diff options
513 files changed, 36223 insertions, 10746 deletions
diff --git a/Documentation/filesystems/gfs2.txt b/Documentation/filesystems/gfs2.txt index 5e3ab8f3bef..0b59c020091 100644 --- a/Documentation/filesystems/gfs2.txt +++ b/Documentation/filesystems/gfs2.txt @@ -1,7 +1,7 @@ Global File System ------------------ -http://sources.redhat.com/cluster/ +http://sources.redhat.com/cluster/wiki/ GFS is a cluster file system. It allows a cluster of computers to simultaneously use a block device that is shared between them (with FC, @@ -36,11 +36,11 @@ GFS2 is not on-disk compatible with previous versions of GFS, but it is pretty close. The following man pages can be found at the URL above: - fsck.gfs2 to repair a filesystem - gfs2_grow to expand a filesystem online - gfs2_jadd to add journals to a filesystem online - gfs2_tool to manipulate, examine and tune a filesystem + fsck.gfs2 to repair a filesystem + gfs2_grow to expand a filesystem online + gfs2_jadd to add journals to a filesystem online + gfs2_tool to manipulate, examine and tune a filesystem gfs2_quota to examine and change quota values in a filesystem gfs2_convert to convert a gfs filesystem to gfs2 in-place mount.gfs2 to help mount(8) mount a filesystem - mkfs.gfs2 to make a filesystem + mkfs.gfs2 to make a filesystem diff --git a/Documentation/filesystems/nilfs2.txt b/Documentation/filesystems/nilfs2.txt index cf6d0d85ca8..d3e7673995e 100644 --- a/Documentation/filesystems/nilfs2.txt +++ b/Documentation/filesystems/nilfs2.txt @@ -50,8 +50,8 @@ NILFS2 supports the following mount options: (*) == default nobarrier Disables barriers. -errors=continue(*) Keep going on a filesystem error. -errors=remount-ro Remount the filesystem read-only on an error. +errors=continue Keep going on a filesystem error. +errors=remount-ro(*) Remount the filesystem read-only on an error. errors=panic Panic and halt the machine if an error occurs. cp=n Specify the checkpoint-number of the snapshot to be mounted. Checkpoints and snapshots are listed by lscp diff --git a/Documentation/filesystems/ocfs2.txt b/Documentation/filesystems/ocfs2.txt index c58b9f5ba00..1f7ae144f6d 100644 --- a/Documentation/filesystems/ocfs2.txt +++ b/Documentation/filesystems/ocfs2.txt @@ -80,3 +80,10 @@ user_xattr (*) Enables Extended User Attributes. nouser_xattr Disables Extended User Attributes. acl Enables POSIX Access Control Lists support. noacl (*) Disables POSIX Access Control Lists support. +resv_level=2 (*) Set how agressive allocation reservations will be. + Valid values are between 0 (reservations off) to 8 + (maximum space for reservations). +dir_resv_level= (*) By default, directory reservations will scale with file + reservations - users should rarely need to change this + value. If allocation reservations are turned off, this + option will have no effect. diff --git a/Documentation/filesystems/sysfs-tagging.txt b/Documentation/filesystems/sysfs-tagging.txt new file mode 100644 index 00000000000..caaaf1266d8 --- /dev/null +++ b/Documentation/filesystems/sysfs-tagging.txt @@ -0,0 +1,42 @@ +Sysfs tagging +------------- + +(Taken almost verbatim from Eric Biederman's netns tagging patch +commit msg) + +The problem. Network devices show up in sysfs and with the network +namespace active multiple devices with the same name can show up in +the same directory, ouch! + +To avoid that problem and allow existing applications in network +namespaces to see the same interface that is currently presented in +sysfs, sysfs now has tagging directory support. + +By using the network namespace pointers as tags to separate out the +the sysfs directory entries we ensure that we don't have conflicts +in the directories and applications only see a limited set of +the network devices. + +Each sysfs directory entry may be tagged with zero or one +namespaces. A sysfs_dirent is augmented with a void *s_ns. If a +directory entry is tagged, then sysfs_dirent->s_flags will have a +flag between KOBJ_NS_TYPE_NONE and KOBJ_NS_TYPES, and s_ns will +point to the namespace to which it belongs. + +Each sysfs superblock's sysfs_super_info contains an array void +*ns[KOBJ_NS_TYPES]. When a a task in a tagging namespace +kobj_nstype first mounts sysfs, a new superblock is created. It +will be differentiated from other sysfs mounts by having its +s_fs_info->ns[kobj_nstype] set to the new namespace. Note that +through bind mounting and mounts propagation, a task can easily view +the contents of other namespaces' sysfs mounts. Therefore, when a +namespace exits, it will call kobj_ns_exit() to invalidate any +sysfs_dirent->s_ns pointers pointing to it. + +Users of this interface: +- define a type in the kobj_ns_type enumeration. +- call kobj_ns_type_register() with its kobj_ns_type_operations which has + - current_ns() which returns current's namespace + - netlink_ns() which returns a socket's namespace + - initial_ns() which returns the initial namesapce +- call kobj_ns_exit() when an individual tag is no longer valid diff --git a/MAINTAINERS b/MAINTAINERS index 22a49e68c8b..a31a717e6dc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -131,19 +131,12 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/typhoon* -3W-9XXX SATA-RAID CONTROLLER DRIVER -M: Adam Radford <linuxraid@amcc.com> +3WARE SAS/SATA-RAID SCSI DRIVERS (3W-XXXX, 3W-9XXX, 3W-SAS) +M: Adam Radford <linuxraid@lsi.com> L: linux-scsi@vger.kernel.org -W: http://www.amcc.com +W: http://www.lsi.com S: Supported -F: drivers/scsi/3w-9xxx* - -3W-XXXX ATA-RAID CONTROLLER DRIVER -M: Adam Radford <linuxraid@amcc.com> -L: linux-scsi@vger.kernel.org -W: http://www.amcc.com -S: Supported -F: drivers/scsi/3w-xxxx* +F: drivers/scsi/3w-* 53C700 AND 53C700-66 SCSI DRIVER M: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com> @@ -4621,6 +4614,14 @@ S: Supported F: Documentation/scsi/LICENSE.qla2xxx F: drivers/scsi/qla2xxx/ +QLOGIC QLA4XXX iSCSI DRIVER +M: Ravi Anand <ravi.anand@qlogic.com> +M: Vikas Chaudhary <vikas.chaudhary@qlogic.com> +M: iscsi-driver@qlogic.com +L: linux-scsi@vger.kernel.org +S: Supported +F: drivers/scsi/qla4xxx/ + QLOGIC QLA3XXX NETWORK DRIVER M: Ron Mercer <ron.mercer@qlogic.com> M: linux-driver@qlogic.com @@ -4761,6 +4762,12 @@ S: Maintained F: Documentation/rfkill.txt F: net/rfkill/ +RICOH SMARTMEDIA/XD DRIVER +M: Maxim Levitsky <maximlevitsky@gmail.com> +S: Maintained +F: drivers/mtd/nand/r822.c +F: drivers/mtd/nand/r822.h + RISCOM8 DRIVER S: Orphan F: Documentation/serial/riscom8.txt diff --git a/arch/alpha/kernel/pci-sysfs.c b/arch/alpha/kernel/pci-sysfs.c index d979e7c7bc4..a5fffc882c7 100644 --- a/arch/alpha/kernel/pci-sysfs.c +++ b/arch/alpha/kernel/pci-sysfs.c @@ -53,6 +53,7 @@ static int __pci_mmap_fits(struct pci_dev *pdev, int num, /** * pci_mmap_resource - map a PCI resource into user memory space + * @filp: open sysfs file * @kobj: kobject for mapping * @attr: struct bin_attribute for the file being mapped * @vma: struct vm_area_struct passed into the mmap @@ -60,7 +61,8 @@ static int __pci_mmap_fits(struct pci_dev *pdev, int num, * * Use the bus mapping routines to map a PCI resource into userspace. */ -static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, +static int pci_mmap_resource(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, struct vm_area_struct *vma, int sparse) { struct pci_dev *pdev = to_pci_dev(container_of(kobj, @@ -89,14 +91,14 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, return hose_mmap_page_range(pdev->sysdata, vma, mmap_type, sparse); } -static int pci_mmap_resource_sparse(struct kobject *kobj, +static int pci_mmap_resource_sparse(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, struct vm_area_struct *vma) { return pci_mmap_resource(kobj, attr, vma, 1); } -static int pci_mmap_resource_dense(struct kobject *kobj, +static int pci_mmap_resource_dense(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, struct vm_area_struct *vma) { diff --git a/arch/arm/mach-ep93xx/include/mach/ts72xx.h b/arch/arm/mach-ep93xx/include/mach/ts72xx.h index 93107d88ff3..0eabec62cd9 100644 --- a/arch/arm/mach-ep93xx/include/mach/ts72xx.h +++ b/arch/arm/mach-ep93xx/include/mach/ts72xx.h @@ -9,9 +9,6 @@ * febff000 22000000 4K model number register * febfe000 22400000 4K options register * febfd000 22800000 4K options register #2 - * febfc000 [67]0000000 4K NAND data register - * febfb000 [67]0400000 4K NAND control register - * febfa000 [67]0800000 4K NAND busy register * febf9000 10800000 4K TS-5620 RTC index register * febf8000 11700000 4K TS-5620 RTC data register */ @@ -41,22 +38,6 @@ #define TS72XX_OPTIONS2_TS9420_BOOT 0x02 -#define TS72XX_NAND1_DATA_PHYS_BASE 0x60000000 -#define TS72XX_NAND2_DATA_PHYS_BASE 0x70000000 -#define TS72XX_NAND_DATA_VIRT_BASE 0xfebfc000 -#define TS72XX_NAND_DATA_SIZE 0x00001000 - -#define TS72XX_NAND1_CONTROL_PHYS_BASE 0x60400000 -#define TS72XX_NAND2_CONTROL_PHYS_BASE 0x70400000 -#define TS72XX_NAND_CONTROL_VIRT_BASE 0xfebfb000 -#define TS72XX_NAND_CONTROL_SIZE 0x00001000 - -#define TS72XX_NAND1_BUSY_PHYS_BASE 0x60800000 -#define TS72XX_NAND2_BUSY_PHYS_BASE 0x70800000 -#define TS72XX_NAND_BUSY_VIRT_BASE 0xfebfa000 -#define TS72XX_NAND_BUSY_SIZE 0x00001000 - - #define TS72XX_RTC_INDEX_VIRT_BASE 0xfebf9000 #define TS72XX_RTC_INDEX_PHYS_BASE 0x10800000 #define TS72XX_RTC_INDEX_SIZE 0x00001000 diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c index 9553031900b..ae7319e588c 100644 --- a/arch/arm/mach-ep93xx/ts72xx.c +++ b/arch/arm/mach-ep93xx/ts72xx.c @@ -10,12 +10,16 @@ * your option) any later version. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/io.h> #include <linux/m48t86.h> #include <linux/mtd/physmap.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> #include <mach/hardware.h> #include <mach/ts72xx.h> @@ -54,92 +58,162 @@ static struct map_desc ts72xx_io_desc[] __initdata = { } }; -static struct map_desc ts72xx_nand_io_desc[] __initdata = { - { - .virtual = TS72XX_NAND_DATA_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND1_DATA_PHYS_BASE), - .length = TS72XX_NAND_DATA_SIZE, - .type = MT_DEVICE, - }, { - .virtual = TS72XX_NAND_CONTROL_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND1_CONTROL_PHYS_BASE), - .length = TS72XX_NAND_CONTROL_SIZE, - .type = MT_DEVICE, - }, { - .virtual = TS72XX_NAND_BUSY_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND1_BUSY_PHYS_BASE), - .length = TS72XX_NAND_BUSY_SIZE, - .type = MT_DEVICE, +static void __init ts72xx_map_io(void) +{ + ep93xx_map_io(); + iotable_init(ts72xx_io_desc, ARRAY_SIZE(ts72xx_io_desc)); +} + + +/************************************************************************* + * NAND flash + *************************************************************************/ +#define TS72XX_NAND_CONTROL_ADDR_LINE 22 /* 0xN0400000 */ +#define TS72XX_NAND_BUSY_ADDR_LINE 23 /* 0xN0800000 */ + +static void ts72xx_nand_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + + if (ctrl & NAND_CTRL_CHANGE) { + void __iomem *addr = chip->IO_ADDR_R; + unsigned char bits; + + addr += (1 << TS72XX_NAND_CONTROL_ADDR_LINE); + + bits = __raw_readb(addr) & ~0x07; + bits |= (ctrl & NAND_NCE) << 2; /* bit 0 -> bit 2 */ + bits |= (ctrl & NAND_CLE); /* bit 1 -> bit 1 */ + bits |= (ctrl & NAND_ALE) >> 2; /* bit 2 -> bit 0 */ + + __raw_writeb(bits, addr); } -}; -static struct map_desc ts72xx_alternate_nand_io_desc[] __initdata = { + if (cmd != NAND_CMD_NONE) + __raw_writeb(cmd, chip->IO_ADDR_W); +} + +static int ts72xx_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + void __iomem *addr = chip->IO_ADDR_R; + + addr += (1 << TS72XX_NAND_BUSY_ADDR_LINE); + + return !!(__raw_readb(addr) & 0x20); +} + +static const char *ts72xx_nand_part_probes[] = { "cmdlinepart", NULL }; + +#define TS72XX_BOOTROM_PART_SIZE (SZ_16K) +#define TS72XX_REDBOOT_PART_SIZE (SZ_2M + SZ_1M) + +static struct mtd_partition ts72xx_nand_parts[] = { { - .virtual = TS72XX_NAND_DATA_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND2_DATA_PHYS_BASE), - .length = TS72XX_NAND_DATA_SIZE, - .type = MT_DEVICE, + .name = "TS-BOOTROM", + .offset = 0, + .size = TS72XX_BOOTROM_PART_SIZE, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - .virtual = TS72XX_NAND_CONTROL_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND2_CONTROL_PHYS_BASE), - .length = TS72XX_NAND_CONTROL_SIZE, - .type = MT_DEVICE, + .name = "Linux", + .offset = MTDPART_OFS_APPEND, + .size = 0, /* filled in later */ }, { - .virtual = TS72XX_NAND_BUSY_VIRT_BASE, - .pfn = __phys_to_pfn(TS72XX_NAND2_BUSY_PHYS_BASE), - .length = TS72XX_NAND_BUSY_SIZE, - .type = MT_DEVICE, - } + .name = "RedBoot", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, }; -static void __init ts72xx_map_io(void) +static void ts72xx_nand_set_parts(uint64_t size, + struct platform_nand_chip *chip) { - ep93xx_map_io(); - iotable_init(ts72xx_io_desc, ARRAY_SIZE(ts72xx_io_desc)); + /* Factory TS-72xx boards only come with 32MiB or 128MiB NAND options */ + if (size == SZ_32M || size == SZ_128M) { + /* Set the "Linux" partition size */ + ts72xx_nand_parts[1].size = size - TS72XX_REDBOOT_PART_SIZE; - /* - * The TS-7200 has NOR flash, the other models have NAND flash. - */ - if (!board_is_ts7200()) { - if (is_ts9420_installed()) { - iotable_init(ts72xx_alternate_nand_io_desc, - ARRAY_SIZE(ts72xx_alternate_nand_io_desc)); - } else { - iotable_init(ts72xx_nand_io_desc, - ARRAY_SIZE(ts72xx_nand_io_desc)); - } + chip->partitions = ts72xx_nand_parts; + chip->nr_partitions = ARRAY_SIZE(ts72xx_nand_parts); + } else { + pr_warning("Unknown nand disk size:%lluMiB\n", size >> 20); } } +static struct platform_nand_data ts72xx_nand_data = { + .chip = { + .nr_chips = 1, + .chip_offset = 0, + .chip_delay = 15, + .part_probe_types = ts72xx_nand_part_probes, + .set_parts = ts72xx_nand_set_parts, + }, + .ctrl = { + .cmd_ctrl = ts72xx_nand_hwcontrol, + .dev_ready = ts72xx_nand_device_ready, + }, +}; + +static struct resource ts72xx_nand_resource[] = { + { + .start = 0, /* filled in later */ + .end = 0, /* filled in later */ + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ts72xx_nand_flash = { + .name = "gen_nand", + .id = -1, + .dev.platform_data = &ts72xx_nand_data, + .resource = ts72xx_nand_resource, + .num_resources = ARRAY_SIZE(ts72xx_nand_resource), +}; + + /************************************************************************* * NOR flash (TS-7200 only) *************************************************************************/ -static struct physmap_flash_data ts72xx_flash_data = { +static struct physmap_flash_data ts72xx_nor_data = { .width = 2, }; -static struct resource ts72xx_flash_resource = { +static struct resource ts72xx_nor_resource = { .start = EP93XX_CS6_PHYS_BASE, .end = EP93XX_CS6_PHYS_BASE + SZ_16M - 1, .flags = IORESOURCE_MEM, }; -static struct platform_device ts72xx_flash = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &ts72xx_flash_data, - }, - .num_resources = 1, - .resource = &ts72xx_flash_resource, +static struct platform_device ts72xx_nor_flash = { + .name = "physmap-flash", + .id = 0, + .dev.platform_data = &ts72xx_nor_data, + .resource = &ts72xx_nor_resource, + .num_resources = 1, }; static void __init ts72xx_register_flash(void) { - if (board_is_ts7200()) - platform_device_register(&ts72xx_flash); + if (board_is_ts7200()) { + platform_device_register(&ts72xx_nor_flash); + } else { + resource_size_t start; + + if (is_ts9420_installed()) + start = EP93XX_CS7_PHYS_BASE; + else + start = EP93XX_CS6_PHYS_BASE; + + ts72xx_nand_resource[0].start = start; + ts72xx_nand_resource[0].end = start + SZ_16M - 1; + + platform_device_register(&ts72xx_nand_flash); + } } + static unsigned char ts72xx_rtc_readbyte(unsigned long addr) { __raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE); diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c index f759ca24392..6072eaa5e66 100644 --- a/arch/arm/mach-kirkwood/common.c +++ b/arch/arm/mach-kirkwood/common.c @@ -305,6 +305,15 @@ void __init kirkwood_nand_init(struct mtd_partition *parts, int nr_parts, platform_device_register(&kirkwood_nand_flash); } +void __init kirkwood_nand_init_rnb(struct mtd_partition *parts, int nr_parts, + int (*dev_ready)(struct mtd_info *)) +{ + kirkwood_clk_ctrl |= CGC_RUNIT; + kirkwood_nand_data.parts = parts; + kirkwood_nand_data.nr_parts = nr_parts; + kirkwood_nand_data.dev_ready = dev_ready; + platform_device_register(&kirkwood_nand_flash); +} /***************************************************************************** * SoC RTC diff --git a/arch/arm/mach-kirkwood/common.h b/arch/arm/mach-kirkwood/common.h index d7de4346435..05e8a8a5692 100644 --- a/arch/arm/mach-kirkwood/common.h +++ b/arch/arm/mach-kirkwood/common.h @@ -16,6 +16,7 @@ struct mv643xx_eth_platform_data; struct mv_sata_platform_data; struct mvsdio_platform_data; struct mtd_partition; +struct mtd_info; /* * Basic Kirkwood init functions used early by machine-setup. @@ -41,6 +42,7 @@ void kirkwood_i2c_init(void); void kirkwood_uart0_init(void); void kirkwood_uart1_init(void); void kirkwood_nand_init(struct mtd_partition *parts, int nr_parts, int delay); +void kirkwood_nand_init_rnb(struct mtd_partition *parts, int nr_parts, int (*dev_ready)(struct mtd_info *)); extern int kirkwood_tclk; extern struct sys_timer kirkwood_timer; diff --git a/arch/arm/plat-orion/include/plat/orion_nand.h b/arch/arm/plat-orion/include/plat/orion_nand.h index d6a4cfa3778..9f3c180834d 100644 --- a/arch/arm/plat-orion/include/plat/orion_nand.h +++ b/arch/arm/plat-orion/include/plat/orion_nand.h @@ -14,6 +14,7 @@ */ struct orion_nand_data { struct mtd_partition *parts; + int (*dev_ready)(struct mtd_info *mtd); u32 nr_parts; u8 ale; /* address line number connected to ALE */ u8 cle; /* address line number connected to CLE */ diff --git a/arch/mips/txx9/generic/setup.c b/arch/mips/txx9/generic/setup.c index adc69291f9e..575d219b800 100644 --- a/arch/mips/txx9/generic/setup.c +++ b/arch/mips/txx9/generic/setup.c @@ -905,7 +905,7 @@ struct txx9_sramc_sysdev { void __iomem *base; }; -static ssize_t txx9_sram_read(struct kobject *kobj, +static ssize_t txx9_sram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { @@ -920,7 +920,7 @@ static ssize_t txx9_sram_read(struct kobject *kobj, return size; } -static ssize_t txx9_sram_write(struct kobject *kobj, +static ssize_t txx9_sram_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { diff --git a/arch/powerpc/sysdev/mv64x60_pci.c b/arch/powerpc/sysdev/mv64x60_pci.c index 1456015a22d..198f288570c 100644 --- a/arch/powerpc/sysdev/mv64x60_pci.c +++ b/arch/powerpc/sysdev/mv64x60_pci.c @@ -24,7 +24,7 @@ #define MV64X60_VAL_LEN_MAX 11 #define MV64X60_PCICFG_CPCI_HOTSWAP 0x68 -static ssize_t mv64x60_hs_reg_read(struct kobject *kobj, +static ssize_t mv64x60_hs_reg_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { @@ -45,7 +45,7 @@ static ssize_t mv64x60_hs_reg_read(struct kobject *kobj, return sprintf(buf, "0x%08x\n", v); } -static ssize_t mv64x60_hs_reg_write(struct kobject *kobj, +static ssize_t mv64x60_hs_reg_write(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 72c8b0d070c..a689070be28 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -403,8 +403,9 @@ static ssize_t sys_ipl_device_show(struct kobject *kobj, static struct kobj_attribute sys_ipl_device_attr = __ATTR(device, S_IRUGO, sys_ipl_device_show, NULL); -static ssize_t ipl_parameter_read(struct kobject *kobj, struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static ssize_t ipl_parameter_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { return memory_read_from_buffer(buf, count, &off, IPL_PARMBLOCK_START, IPL_PARMBLOCK_SIZE); @@ -419,8 +420,9 @@ static struct bin_attribute ipl_parameter_attr = { .read = &ipl_parameter_read, }; -static ssize_t ipl_scp_data_read(struct kobject *kobj, struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static ssize_t ipl_scp_data_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len; void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data; @@ -694,7 +696,7 @@ static struct kobj_attribute sys_reipl_ccw_vmparm_attr = /* FCP reipl device attributes */ -static ssize_t reipl_fcp_scpdata_read(struct kobject *kobj, +static ssize_t reipl_fcp_scpdata_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { @@ -704,7 +706,7 @@ static ssize_t reipl_fcp_scpdata_read(struct kobject *kobj, return memory_read_from_buffer(buf, count, &off, scp_data, size); } -static ssize_t reipl_fcp_scpdata_write(struct kobject *kobj, +static ssize_t reipl_fcp_scpdata_write(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index e35525b39f6..c79e789ed03 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -71,7 +71,7 @@ struct acpi_table_attr { struct list_head node; }; -static ssize_t acpi_table_show(struct kobject *kobj, +static ssize_t acpi_table_show(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t offset, size_t count) { diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index fd52c48ee76..ef38aff737e 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -18,9 +18,9 @@ config UEVENT_HELPER_PATH config DEVTMPFS bool "Maintain a devtmpfs filesystem to mount at /dev" - depends on HOTPLUG && SHMEM && TMPFS + depends on HOTPLUG help - This creates a tmpfs filesystem instance early at bootup. + This creates a tmpfs/ramfs filesystem instance early at bootup. In this filesystem, the kernel driver core maintains device nodes with their default names and permissions for all registered devices with an assigned major/minor number. @@ -33,6 +33,9 @@ config DEVTMPFS functional /dev without any further help. It also allows simple rescue systems, and reliably handles dynamic major/minor numbers. + Notice: if CONFIG_TMPFS isn't enabled, the simpler ramfs + file system will be used instead. + config DEVTMPFS_MOUNT bool "Automount devtmpfs at /dev, after the kernel mounted the rootfs" depends on DEVTMPFS diff --git a/drivers/base/class.c b/drivers/base/class.c index 9c6a0d6408e..8e231d05b40 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -63,6 +63,14 @@ static void class_release(struct kobject *kobj) kfree(cp); } +static const struct kobj_ns_type_operations *class_child_ns_type(struct kobject *kobj) +{ + struct class_private *cp = to_class(kobj); + struct class *class = cp->class; + + return class->ns_type; +} + static const struct sysfs_ops class_sysfs_ops = { .show = class_attr_show, .store = class_attr_store, @@ -71,6 +79,7 @@ static const struct sysfs_ops class_sysfs_ops = { static struct kobj_type class_ktype = { .sysfs_ops = &class_sysfs_ops, .release = class_release, + .child_ns_type = class_child_ns_type, }; /* Hotplug events for classes go to the class class_subsys */ diff --git a/drivers/base/core.c b/drivers/base/core.c index b56a0ba31d4..9630fbdf4e6 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -20,7 +20,6 @@ #include <linux/notifier.h> #include <linux/genhd.h> #include <linux/kallsyms.h> -#include <linux/semaphore.h> #include <linux/mutex.h> #include <linux/async.h> @@ -132,9 +131,21 @@ static void device_release(struct kobject *kobj) kfree(p); } +static const void *device_namespace(struct kobject *kobj) +{ + struct device *dev = to_dev(kobj); + const void *ns = NULL; + + if (dev->class && dev->class->ns_type) + ns = dev->class->namespace(dev); + + return ns; +} + static struct kobj_type device_ktype = { .release = device_release, .sysfs_ops = &dev_sysfs_ops, + .namespace = device_namespace, }; @@ -559,10 +570,10 @@ void device_initialize(struct device *dev) dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); INIT_LIST_HEAD(&dev->dma_pools); - init_MUTEX(&dev->sem); + mutex_init(&dev->mutex); + lockdep_set_novalidate_class(&dev->mutex); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); - device_init_wakeup(dev, 0); device_pm_init(dev); set_dev_node(dev, -1); } @@ -596,11 +607,59 @@ static struct kobject *virtual_device_parent(struct device *dev) return virtual_dir; } -static struct kobject *get_device_parent(struct device *dev, - struct device *parent) +struct class_dir { + struct kobject kobj; + struct class *class; +}; + +#define to_class_dir(obj) container_of(obj, struct class_dir, kobj) + +static void class_dir_release(struct kobject *kobj) +{ + struct class_dir *dir = to_class_dir(kobj); + kfree(dir); +} + +static const +struct kobj_ns_type_operations *class_dir_child_ns_type(struct kobject *kobj) { + struct class_dir *dir = to_class_dir(kobj); + return dir->class->ns_type; +} + +static struct kobj_type class_dir_ktype = { + .release = class_dir_release, + .sysfs_ops = &kobj_sysfs_ops, + .child_ns_type = class_dir_child_ns_type +}; + +static struct kobject * +class_dir_create_and_add(struct class *class, struct kobject *parent_kobj) +{ + struct class_dir *dir; int retval; + dir = kzalloc(sizeof(*dir), GFP_KERNEL); + if (!dir) + return NULL; + + dir->class = class; + kobject_init(&dir->kobj, &class_dir_ktype); + + dir->kobj.kset = &class->p->class_dirs; + + retval = kobject_add(&dir->kobj, parent_kobj, "%s", class->name); + if (retval < 0) { + kobject_put(&dir->kobj); + return NULL; + } + return &dir->kobj; +} + + +static struct kobject *get_device_parent(struct device *dev, + struct device *parent) +{ if (dev->class) { static DEFINE_MUTEX(gdp_mutex); struct kobject *kobj = NULL; @@ -635,18 +694,7 @@ static struct kobject *get_device_parent(struct device *dev, } /* or create a new class-directory at the parent device */ - k = kobject_create(); - if (!k) { - mutex_unlock(&gdp_mutex); - return NULL; - } - k->kset = &dev->class->p->class_dirs; - retval = kobject_add(k, parent_kobj, "%s", dev->class->name); - if (retval < 0) { - mutex_unlock(&gdp_mutex); - kobject_put(k); - return NULL; - } + k = class_dir_create_and_add(dev->class, parent_kobj); /* do not emit an uevent for this simple "glue" directory */ mutex_unlock(&gdp_mutex); return k; @@ -738,7 +786,7 @@ out_device: out_busid: if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && device_is_not_partition(dev)) - sysfs_remove_link(&dev->class->p->class_subsys.kobj, + sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); #else /* link in the class directory pointing to the device */ @@ -756,7 +804,7 @@ out_busid: return 0; out_busid: - sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev)); + sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); #endif out_subsys: @@ -784,13 +832,13 @@ static void device_remove_class_symlinks(struct device *dev) if (dev->kobj.parent != &dev->class->p->class_subsys.kobj && device_is_not_partition(dev)) - sysfs_remove_link(&dev->class->p->class_subsys.kobj, + sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); #else if (dev->parent && device_is_not_partition(dev)) sysfs_remove_link(&dev->kobj, "device"); - sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev)); + sysfs_delete_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev_name(dev)); #endif sysfs_remove_link(&dev->kobj, "subsystem"); @@ -1372,7 +1420,7 @@ struct device *__root_device_register(const char *name, struct module *owner) return ERR_PTR(err); } -#ifdef CONFIG_MODULE /* gotta find a "cleaner" way to do this */ +#ifdef CONFIG_MODULES /* gotta find a "cleaner" way to do this */ if (owner) { struct module_kobject *mk = &owner->mkobj; @@ -1576,6 +1624,14 @@ int device_rename(struct device *dev, char *new_name) goto out; } +#ifndef CONFIG_SYSFS_DEPRECATED + if (dev->class) { + error = sysfs_rename_link(&dev->class->p->class_subsys.kobj, + &dev->kobj, old_device_name, new_name); + if (error) + goto out; + } +#endif error = kobject_rename(&dev->kobj, new_name); if (error) goto out; @@ -1590,11 +1646,6 @@ int device_rename(struct device *dev, char *new_name) new_class_name); } } -#else - if (dev->class) { - error = sysfs_rename_link(&dev->class->p->class_subsys.kobj, - &dev->kobj, old_device_name, new_name); - } #endif out: @@ -1735,10 +1786,25 @@ EXPORT_SYMBOL_GPL(device_move); */ void device_shutdown(void) { - struct device *dev, *devn; + struct device *dev; + + spin_lock(&devices_kset->list_lock); + /* + * Walk the devices list backward, shutting down each in turn. + * Beware that device unplug events may also start pulling + * devices offline, even as the system is shutting down. + */ + while (!list_empty(&devices_kset->list)) { + dev = list_entry(devices_kset->list.prev, struct device, + kobj.entry); + get_device(dev); + /* + * Make sure the device is off the kset list, in the + * event that dev->*->shutdown() doesn't remove it. + */ + list_del_init(&dev->kobj.entry); + spin_unlock(&devices_kset->list_lock); - list_for_each_entry_safe_reverse(dev, devn, &devices_kset->list, - kobj.entry) { if (dev->bus && dev->bus->shutdown) { dev_dbg(dev, "shutdown\n"); dev->bus->shutdown(dev); @@ -1746,6 +1812,10 @@ void device_shutdown(void) dev_dbg(dev, "shutdown\n"); dev->driver->shutdown(dev); } + put_device(dev); + + spin_lock(&devices_kset->list_lock); } + spin_unlock(&devices_kset->list_lock); async_synchronize_full(); } diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index f35719aab3c..251acea3d35 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -186,7 +186,7 @@ static ssize_t print_cpus_offline(struct sysdev_class *class, /* display offline cpus < nr_cpu_ids */ if (!alloc_cpumask_var(&offline, GFP_KERNEL)) return -ENOMEM; - cpumask_complement(offline, cpu_online_mask); + cpumask_andnot(offline, cpu_possible_mask, cpu_online_mask); n = cpulist_scnprintf(buf, len, offline); free_cpumask_var(offline); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index c89291f8a16..503c2620bbc 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -40,11 +40,11 @@ static void driver_bound(struct device *dev) pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev), __func__, dev->driver->name); + klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); + if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_BOUND_DRIVER, dev); - - klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); } static int driver_sysfs_add(struct device *dev) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 057cf11326b..af0600143d1 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -20,6 +20,7 @@ #include <linux/namei.h> #include <linux/fs.h> #include <linux/shmem_fs.h> +#include <linux/ramfs.h> #include <linux/cred.h> #include <linux/sched.h> #include <linux/init_task.h> @@ -45,7 +46,11 @@ __setup("devtmpfs.mount=", mount_param); static int dev_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { +#ifdef CONFIG_TMPFS return get_sb_single(fs_type, flags, data, shmem_fill_super, mnt); +#else + return get_sb_single(fs_type, flags, data, ramfs_fill_super, mnt); +#endif } static struct file_system_type dev_fs_type = { diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 985da11174e..3f093b0dd21 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -27,6 +27,52 @@ MODULE_AUTHOR("Manuel Estrada Sainz"); MODULE_DESCRIPTION("Multi purpose firmware loading support"); MODULE_LICENSE("GPL"); +/* Builtin firmware support */ + +#ifdef CONFIG_FW_LOADER + +extern struct builtin_fw __start_builtin_fw[]; +extern struct builtin_fw __end_builtin_fw[]; + +static bool fw_get_builtin_firmware(struct firmware *fw, const char *name) +{ + struct builtin_fw *b_fw; + + for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) { + if (strcmp(name, b_fw->name) == 0) { + fw->size = b_fw->size; + fw->data = b_fw->data; + return true; + } + } + + return false; +} + +static bool fw_is_builtin_firmware(const struct firmware *fw) +{ + struct builtin_fw *b_fw; + + for (b_fw = __start_builtin_fw; b_fw != __end_builtin_fw; b_fw++) + if (fw->data == b_fw->data) + return true; + + return false; +} + +#else /* Module case - no builtin firmware support */ + +static inline bool fw_get_builtin_firmware(struct firmware *fw, const char *name) +{ + return false; +} + +static inline bool fw_is_builtin_firmware(const struct firmware *fw) +{ + return false; +} +#endif + enum { FW_STATUS_LOADING, FW_STATUS_DONE, @@ -40,7 +86,6 @@ static int loading_timeout = 60; /* In seconds */ static DEFINE_MUTEX(fw_lock); struct firmware_priv { - char *fw_id; struct completion completion; struct bin_attribute attr_data; struct firmware *fw; @@ -48,18 +93,11 @@ struct firmware_priv { struct page **pages; int nr_pages; int page_array_size; - const char *vdata; struct timer_list timeout; + bool nowait; + char fw_id[]; }; -#ifdef CONFIG_FW_LOADER -extern struct builtin_fw __start_builtin_fw[]; -extern struct builtin_fw __end_builtin_fw[]; -#else /* Module case. Avoid ifdefs later; it'll all optimise out */ -static struct builtin_fw *__start_builtin_fw; -static struct builtin_fw *__end_builtin_fw; -#endif - static void fw_load_abort(struct firmware_priv *fw_priv) { @@ -100,9 +138,25 @@ firmware_timeout_store(struct class *class, return count; } -static CLASS_ATTR(timeout, 0644, firmware_timeout_show, firmware_timeout_store); +static struct class_attribute firmware_class_attrs[] = { + __ATTR(timeout, S_IWUSR | S_IRUGO, + firmware_timeout_show, firmware_timeout_store), + __ATTR_NULL +}; + +static void fw_dev_release(struct device *dev) +{ + struct firmware_priv *fw_priv = dev_get_drvdata(dev); + int i; + + for (i = 0; i < fw_priv->nr_pages; i++) + __free_page(fw_priv->pages[i]); + kfree(fw_priv->pages); + kfree(fw_priv); + kfree(dev); -static void fw_dev_release(struct device *dev); + module_put(THIS_MODULE); +} static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env) { @@ -112,12 +166,15 @@ static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env) return -ENOMEM; if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout)) return -ENOMEM; + if (add_uevent_var(env, "ASYNC=%d", fw_priv->nowait)) + return -ENOMEM; return 0; } static struct class firmware_class = { .name = "firmware", + .class_attrs = firmware_class_attrs, .dev_uevent = firmware_uevent, .dev_release = fw_dev_release, }; @@ -130,6 +187,17 @@ static ssize_t firmware_loading_show(struct device *dev, return sprintf(buf, "%d\n", loading); } +static void firmware_free_data(const struct firmware *fw) +{ + int i; + vunmap(fw->data); + if (fw->pages) { + for (i = 0; i < PFN_UP(fw->size); i++) + __free_page(fw->pages[i]); + kfree(fw->pages); + } +} + /* Some architectures don't have PAGE_KERNEL_RO */ #ifndef PAGE_KERNEL_RO #define PAGE_KERNEL_RO PAGE_KERNEL @@ -162,21 +230,21 @@ static ssize_t firmware_loading_store(struct device *dev, mutex_unlock(&fw_lock); break; } - vfree(fw_priv->fw->data); - fw_priv->fw->data = NULL; + firmware_free_data(fw_priv->fw); + memset(fw_priv->fw, 0, sizeof(struct firmware)); + /* If the pages are not owned by 'struct firmware' */ for (i = 0; i < fw_priv->nr_pages; i++) __free_page(fw_priv->pages[i]); kfree(fw_priv->pages); fw_priv->pages = NULL; fw_priv->page_array_size = 0; fw_priv->nr_pages = 0; - fw_priv->fw->size = 0; set_bit(FW_STATUS_LOADING, &fw_priv->status); mutex_unlock(&fw_lock); break; case 0: if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) { - vfree(fw_priv->fw->data); + vunmap(fw_priv->fw->data); fw_priv->fw->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0, PAGE_KERNEL_RO); @@ -184,7 +252,10 @@ static ssize_t firmware_loading_store(struct device *dev, dev_err(dev, "%s: vmap() failed\n", __func__); goto err; } - /* Pages will be freed by vfree() */ + /* Pages are now owned by 'struct firmware' */ + fw_priv->fw->pages = fw_priv->pages; + fw_priv->pages = NULL; + fw_priv->page_array_size = 0; fw_priv->nr_pages = 0; complete(&fw_priv->completion); @@ -207,8 +278,9 @@ static ssize_t firmware_loading_store(struct device *dev, static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); static ssize_t -firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buffer, loff_t offset, size_t count) +firmware_data_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buffer, loff_t offset, + size_t count) { struct device *dev = to_dev(kobj); struct firmware_priv *fw_priv = dev_get_drvdata(dev); @@ -291,6 +363,7 @@ fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) /** * firmware_data_write - write method for firmware + * @filp: open sysfs file * @kobj: kobject for the device * @bin_attr: bin_attr structure * @buffer: buffer being written @@ -301,8 +374,9 @@ fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) * the driver as a firmware image. **/ static ssize_t -firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buffer, loff_t offset, size_t count) +firmware_data_write(struct file* filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buffer, + loff_t offset, size_t count) { struct device *dev = to_dev(kobj); struct firmware_priv *fw_priv = dev_get_drvdata(dev); @@ -353,21 +427,6 @@ static struct bin_attribute firmware_attr_data_tmpl = { .write = firmware_data_write, }; -static void fw_dev_release(struct device *dev) -{ - struct firmware_priv *fw_priv = dev_get_drvdata(dev); - int i; - - for (i = 0; i < fw_priv->nr_pages; i++) - __free_page(fw_priv->pages[i]); - kfree(fw_priv->pages); - kfree(fw_priv->fw_id); - kfree(fw_priv); - kfree(dev); - - module_put(THIS_MODULE); -} - static void firmware_class_timeout(u_long data) { @@ -379,8 +438,8 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, struct device *device) { int retval; - struct firmware_priv *fw_priv = kzalloc(sizeof(*fw_priv), - GFP_KERNEL); + struct firmware_priv *fw_priv = + kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL); struct device *f_dev = kzalloc(sizeof(*f_dev), GFP_KERNEL); *dev_p = NULL; @@ -391,16 +450,9 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, goto error_kfree; } + strcpy(fw_priv->fw_id, fw_name); init_completion(&fw_priv->completion); fw_priv->attr_data = firmware_attr_data_tmpl; - fw_priv->fw_id = kstrdup(fw_name, GFP_KERNEL); - if (!fw_priv->fw_id) { - dev_err(device, "%s: Firmware name allocation failed\n", - __func__); - retval = -ENOMEM; - goto error_kfree; - } - fw_priv->timeout.function = firmware_class_timeout; fw_priv->timeout.data = (u_long) fw_priv; init_timer(&fw_priv->timeout); @@ -427,7 +479,7 @@ error_kfree: static int fw_setup_device(struct firmware *fw, struct device **dev_p, const char *fw_name, struct device *device, - int uevent) + int uevent, bool nowait) { struct device *f_dev; struct firmware_priv *fw_priv; @@ -443,6 +495,8 @@ static int fw_setup_device(struct firmware *fw, struct device **dev_p, fw_priv = dev_get_drvdata(f_dev); + fw_priv->nowait = nowait; + fw_priv->fw = fw; sysfs_bin_attr_init(&fw_priv->attr_data); retval = sysfs_create_bin_file(&f_dev->kobj, &fw_priv->attr_data); @@ -470,12 +524,11 @@ out: static int _request_firmware(const struct firmware **firmware_p, const char *name, - struct device *device, int uevent) + struct device *device, int uevent, bool nowait) { struct device *f_dev; struct firmware_priv *fw_priv; struct firmware *firmware; - struct builtin_fw *builtin; int retval; if (!firmware_p) @@ -489,21 +542,16 @@ _request_firmware(const struct firmware **firmware_p, const char *name, goto out; } - for (builtin = __start_builtin_fw; builtin != __end_builtin_fw; - builtin++) { - if (strcmp(name, builtin->name)) - continue; - dev_info(device, "firmware: using built-in firmware %s\n", - name); - firmware->size = builtin->size; - firmware->data = builtin->data; + if (fw_get_builtin_firmware(firmware, name)) { + dev_dbg(device, "firmware: using built-in firmware %s\n", name); return 0; } if (uevent) - dev_info(device, "firmware: requesting %s\n", name); + dev_dbg(device, "firmware: requesting %s\n", name); - retval = fw_setup_device(firmware, &f_dev, name, device, uevent); + retval = fw_setup_device(firmware, &f_dev, name, device, + uevent, nowait); if (retval) goto error_kfree_fw; @@ -560,26 +608,18 @@ request_firmware(const struct firmware **firmware_p, const char *name, struct device *device) { int uevent = 1; - return _request_firmware(firmware_p, name, device, uevent); + return _request_firmware(firmware_p, name, device, uevent, false); } /** * release_firmware: - release the resource associated with a firmware image * @fw: firmware resource to release **/ -void -release_firmware(const struct firmware *fw) +void release_firmware(const struct firmware *fw) { - struct builtin_fw *builtin; - if (fw) { - for (builtin = __start_builtin_fw; builtin != __end_builtin_fw; - builtin++) { - if (fw->data == builtin->data) - goto free_fw; - } - vfree(fw->data); - free_fw: + if (!fw_is_builtin_firmware(fw)) + firmware_free_data(fw); kfree(fw); } } @@ -606,7 +646,7 @@ request_firmware_work_func(void *arg) return 0; } ret = _request_firmware(&fw, fw_work->name, fw_work->device, - fw_work->uevent); + fw_work->uevent, true); fw_work->cont(fw, fw_work->context); @@ -670,26 +710,12 @@ request_firmware_nowait( return 0; } -static int __init -firmware_class_init(void) +static int __init firmware_class_init(void) { - int error; - error = class_register(&firmware_class); - if (error) { - printk(KERN_ERR "%s: class_register failed\n", __func__); - return error; - } - error = class_create_file(&firmware_class, &class_attr_timeout); - if (error) { - printk(KERN_ERR "%s: class_create_file failed\n", - __func__); - class_unregister(&firmware_class); - } - return error; - + return class_register(&firmware_class); } -static void __exit -firmware_class_exit(void) + +static void __exit firmware_class_exit(void) { class_unregister(&firmware_class); } diff --git a/drivers/base/module.c b/drivers/base/module.c index f32f2f9b7be..db930d3ee31 100644 --- a/drivers/base/module.c +++ b/drivers/base/module.c @@ -15,12 +15,10 @@ static char *make_driver_name(struct device_driver *drv) { char *driver_name; - driver_name = kmalloc(strlen(drv->name) + strlen(drv->bus->name) + 2, - GFP_KERNEL); + driver_name = kasprintf(GFP_KERNEL, "%s:%s", drv->bus->name, drv->name); if (!driver_name) return NULL; - sprintf(driver_name, "%s:%s", drv->bus->name, drv->name); return driver_name; } diff --git a/drivers/base/platform.c b/drivers/base/platform.c index ada6397c23a..4d99c8bdfed 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -735,7 +735,7 @@ static void platform_pm_complete(struct device *dev) #ifdef CONFIG_SUSPEND -static int platform_pm_suspend(struct device *dev) +int __weak platform_pm_suspend(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -753,7 +753,7 @@ static int platform_pm_suspend(struct device *dev) return ret; } -static int platform_pm_suspend_noirq(struct device *dev) +int __weak platform_pm_suspend_noirq(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -769,7 +769,7 @@ static int platform_pm_suspend_noirq(struct device *dev) return ret; } -static int platform_pm_resume(struct device *dev) +int __weak platform_pm_resume(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; @@ -787,7 +787,7 @@ static int platform_pm_resume(struct device *dev) return ret; } -static int platform_pm_resume_noirq(struct device *dev) +int __weak platform_pm_resume_noirq(struct device *dev) { struct device_driver *drv = dev->driver; int ret = 0; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 8546d123b9a..a90e83c9be9 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -835,6 +835,8 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, set_capacity(lo->lo_disk, size); bd_set_size(bdev, size << 9); + /* let user-space know about the new size */ + kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE); set_blocksize(bdev, lo_blocksize); @@ -858,6 +860,7 @@ out_clr: set_capacity(lo->lo_disk, 0); invalidate_bdev(bdev); bd_set_size(bdev, 0); + kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE); mapping_set_gfp_mask(mapping, lo->old_gfp_mask); lo->lo_state = Lo_unbound; out_putf: @@ -944,8 +947,11 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev) if (bdev) invalidate_bdev(bdev); set_capacity(lo->lo_disk, 0); - if (bdev) + if (bdev) { bd_set_size(bdev, 0); + /* let user-space know about this change */ + kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE); + } mapping_set_gfp_mask(filp->f_mapping, gfp); lo->lo_state = Lo_unbound; /* This is safe: open() is still holding a reference. */ @@ -1189,6 +1195,8 @@ static int loop_set_capacity(struct loop_device *lo, struct block_device *bdev) sz <<= 9; mutex_lock(&bdev->bd_mutex); bd_set_size(bdev, sz); + /* let user-space know about the new size */ + kobject_uevent(&disk_to_dev(bdev->bd_disk)->kobj, KOBJ_CHANGE); mutex_unlock(&bdev->bd_mutex); out: diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 3141dd3b6e5..e21175be25d 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -276,11 +276,19 @@ config N_HDLC Allows synchronous HDLC communications with tty device drivers that support synchronous HDLC such as the Microgate SyncLink adapter. - This driver can only be built as a module ( = code which can be + This driver can be built as a module ( = code which can be inserted in and removed from the running kernel whenever you want). The module will be called n_hdlc. If you want to do that, say M here. +config N_GSM + tristate "GSM MUX line discipline support (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on NET + help + This line discipline provides support for the GSM MUX protocol and + presents the mux as a set of 61 individual tty devices. + config RISCOM8 tristate "SDL RISCom/8 card support" depends on SERIAL_NONSTANDARD diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f957edf7e45..d39be4cf1f5 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SYNCLINK) += synclink.o obj-$(CONFIG_SYNCLINKMP) += synclinkmp.o obj-$(CONFIG_SYNCLINK_GT) += synclink_gt.o obj-$(CONFIG_N_HDLC) += n_hdlc.o +obj-$(CONFIG_N_GSM) += n_gsm.o obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o obj-$(CONFIG_SX) += sx.o generic_serial.o obj-$(CONFIG_RIO) += rio/ generic_serial.o diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index c1ab303455c..98310e1aae3 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -1573,11 +1573,16 @@ static int __devinit isicom_probe(struct pci_dev *pdev, dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device); /* allot the first empty slot in the array */ - for (index = 0; index < BOARD_COUNT; index++) + for (index = 0; index < BOARD_COUNT; index++) { if (isi_card[index].base == 0) { board = &isi_card[index]; break; } + } + if (index == BOARD_COUNT) { + retval = -ENODEV; + goto err_disable; + } board->index = index; board->base = pci_resource_start(pdev, 3); @@ -1624,6 +1629,7 @@ errunrr: errdec: board->base = 0; card_count--; +err_disable: pci_disable_device(pdev); err: return retval; diff --git a/drivers/char/n_gsm.c b/drivers/char/n_gsm.c new file mode 100644 index 00000000000..c4161d5e053 --- /dev/null +++ b/drivers/char/n_gsm.c @@ -0,0 +1,2763 @@ +/* + * n_gsm.c GSM 0710 tty multiplexor + * Copyright (c) 2009/10 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * * THIS IS A DEVELOPMENT SNAPSHOT IT IS NOT A FINAL RELEASE * + * + * TO DO: + * Mostly done: ioctls for setting modes/timing + * Partly done: hooks so you can pull off frames to non tty devs + * Restart DLCI 0 when it closes ? + * Test basic encoding + * Improve the tx engine + * Resolve tx side locking by adding a queue_head and routing + * all control traffic via it + * General tidy/document + * Review the locking/move to refcounts more (mux now moved to an + * alloc/free model ready) + * Use newest tty open/close port helpers and install hooks + * What to do about power functions ? + * Termios setting and negotiation + * Do we need a 'which mux are you' ioctl to correlate mux and tty sets + * + */ + +#include <linux/types.h> +#include <linux/major.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/fcntl.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/timer.h> +#include <linux/ctype.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/bitops.h> +#include <linux/file.h> +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/timer.h> +#include <linux/tty_flip.h> +#include <linux/tty_driver.h> +#include <linux/serial.h> +#include <linux/kfifo.h> +#include <linux/skbuff.h> +#include <linux/gsmmux.h> + +static int debug; +module_param(debug, int, 0600); + +#define T1 (HZ/10) +#define T2 (HZ/3) +#define N2 3 + +/* Use long timers for testing at low speed with debug on */ +#ifdef DEBUG_TIMING +#define T1 HZ +#define T2 (2 * HZ) +#endif + +/* Semi-arbitary buffer size limits. 0710 is normally run with 32-64 byte + limits so this is plenty */ +#define MAX_MRU 512 +#define MAX_MTU 512 + +/* + * Each block of data we have queued to go out is in the form of + * a gsm_msg which holds everything we need in a link layer independant + * format + */ + +struct gsm_msg { + struct gsm_msg *next; + u8 addr; /* DLCI address + flags */ + u8 ctrl; /* Control byte + flags */ + unsigned int len; /* Length of data block (can be zero) */ + unsigned char *data; /* Points into buffer but not at the start */ + unsigned char buffer[0]; +}; + +/* + * Each active data link has a gsm_dlci structure associated which ties + * the link layer to an optional tty (if the tty side is open). To avoid + * complexity right now these are only ever freed up when the mux is + * shut down. + * + * At the moment we don't free DLCI objects until the mux is torn down + * this avoid object life time issues but might be worth review later. + */ + +struct gsm_dlci { + struct gsm_mux *gsm; + int addr; + int state; +#define DLCI_CLOSED 0 +#define DLCI_OPENING 1 /* Sending SABM not seen UA */ +#define DLCI_OPEN 2 /* SABM/UA complete */ +#define DLCI_CLOSING 3 /* Sending DISC not seen UA/DM */ + + /* Link layer */ + spinlock_t lock; /* Protects the internal state */ + struct timer_list t1; /* Retransmit timer for SABM and UA */ + int retries; + /* Uplink tty if active */ + struct tty_port port; /* The tty bound to this DLCI if there is one */ + struct kfifo *fifo; /* Queue fifo for the DLCI */ + struct kfifo _fifo; /* For new fifo API porting only */ + int adaption; /* Adaption layer in use */ + u32 modem_rx; /* Our incoming virtual modem lines */ + u32 modem_tx; /* Our outgoing modem lines */ + int dead; /* Refuse re-open */ + /* Flow control */ + int throttled; /* Private copy of throttle state */ + int constipated; /* Throttle status for outgoing */ + /* Packetised I/O */ + struct sk_buff *skb; /* Frame being sent */ + struct sk_buff_head skb_list; /* Queued frames */ + /* Data handling callback */ + void (*data)(struct gsm_dlci *dlci, u8 *data, int len); +}; + +/* DLCI 0, 62/63 are special or reseved see gsmtty_open */ + +#define NUM_DLCI 64 + +/* + * DLCI 0 is used to pass control blocks out of band of the data + * flow (and with a higher link priority). One command can be outstanding + * at a time and we use this structure to manage them. They are created + * and destroyed by the user context, and updated by the receive paths + * and timers + */ + +struct gsm_control { + u8 cmd; /* Command we are issuing */ + u8 *data; /* Data for the command in case we retransmit */ + int len; /* Length of block for retransmission */ + int done; /* Done flag */ + int error; /* Error if any */ +}; + +/* + * Each GSM mux we have is represented by this structure. If we are + * operating as an ldisc then we use this structure as our ldisc + * state. We need to sort out lifetimes and locking with respect + * to the gsm mux array. For now we don't free DLCI objects that + * have been instantiated until the mux itself is terminated. + * + * To consider further: tty open versus mux shutdown. + */ + +struct gsm_mux { + struct tty_struct *tty; /* The tty our ldisc is bound to */ + spinlock_t lock; + + /* Events on the GSM channel */ + wait_queue_head_t event; + + /* Bits for GSM mode decoding */ + + /* Framing Layer */ + unsigned char *buf; + int state; +#define GSM_SEARCH 0 +#define GSM_START 1 +#define GSM_ADDRESS 2 +#define GSM_CONTROL 3 +#define GSM_LEN 4 +#define GSM_DATA 5 +#define GSM_FCS 6 +#define GSM_OVERRUN 7 + unsigned int len; + unsigned int address; + unsigned int count; + int escape; + int encoding; + u8 control; + u8 fcs; + u8 *txframe; /* TX framing buffer */ + + /* Methods for the receiver side */ + void (*receive)(struct gsm_mux *gsm, u8 ch); + void (*error)(struct gsm_mux *gsm, u8 ch, u8 flag); + /* And transmit side */ + int (*output)(struct gsm_mux *mux, u8 *data, int len); + + /* Link Layer */ + unsigned int mru; + unsigned int mtu; + int initiator; /* Did we initiate connection */ + int dead; /* Has the mux been shut down */ + struct gsm_dlci *dlci[NUM_DLCI]; + int constipated; /* Asked by remote to shut up */ + + spinlock_t tx_lock; + unsigned int tx_bytes; /* TX data outstanding */ +#define TX_THRESH_HI 8192 +#define TX_THRESH_LO 2048 + struct gsm_msg *tx_head; /* Pending data packets */ + struct gsm_msg *tx_tail; + + /* Control messages */ + struct timer_list t2_timer; /* Retransmit timer for commands */ + int cretries; /* Command retry counter */ + struct gsm_control *pending_cmd;/* Our current pending command */ + spinlock_t control_lock; /* Protects the pending command */ + + /* Configuration */ + int adaption; /* 1 or 2 supported */ + u8 ftype; /* UI or UIH */ + int t1, t2; /* Timers in 1/100th of a sec */ + int n2; /* Retry count */ + + /* Statistics (not currently exposed) */ + unsigned long bad_fcs; + unsigned long malformed; + unsigned long io_error; + unsigned long bad_size; + unsigned long unsupported; +}; + + +/* + * Mux objects - needed so that we can translate a tty index into the + * relevant mux and DLCI. + */ + +#define MAX_MUX 4 /* 256 minors */ +static struct gsm_mux *gsm_mux[MAX_MUX]; /* GSM muxes */ +static spinlock_t gsm_mux_lock; + +/* + * This section of the driver logic implements the GSM encodings + * both the basic and the 'advanced'. Reliable transport is not + * supported. + */ + +#define CR 0x02 +#define EA 0x01 +#define PF 0x10 + +/* I is special: the rest are ..*/ +#define RR 0x01 +#define UI 0x03 +#define RNR 0x05 +#define REJ 0x09 +#define DM 0x0F +#define SABM 0x2F +#define DISC 0x43 +#define UA 0x63 +#define UIH 0xEF + +/* Channel commands */ +#define CMD_NSC 0x09 +#define CMD_TEST 0x11 +#define CMD_PSC 0x21 +#define CMD_RLS 0x29 +#define CMD_FCOFF 0x31 +#define CMD_PN 0x41 +#define CMD_RPN 0x49 +#define CMD_FCON 0x51 +#define CMD_CLD 0x61 +#define CMD_SNC 0x69 +#define CMD_MSC 0x71 + +/* Virtual modem bits */ +#define MDM_FC 0x01 +#define MDM_RTC 0x02 +#define MDM_RTR 0x04 +#define MDM_IC 0x20 +#define MDM_DV 0x40 + +#define GSM0_SOF 0xF9 +#define GSM1_SOF 0x7E +#define GSM1_ESCAPE 0x7D +#define GSM1_ESCAPE_BITS 0x20 +#define XON 0x11 +#define XOFF 0x13 + +static const struct tty_port_operations gsm_port_ops; + +/* + * CRC table for GSM 0710 + */ + +static const u8 gsm_fcs8[256] = { + 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, + 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, + 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, + 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, + 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, + 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, + 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, + 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, + 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, + 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, + 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, + 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, + 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, + 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, + 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, + 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, + 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, + 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, + 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, + 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, + 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, + 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, + 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, + 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, + 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, + 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, + 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, + 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, + 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, + 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, + 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, + 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF +}; + +#define INIT_FCS 0xFF +#define GOOD_FCS 0xCF + +/** + * gsm_fcs_add - update FCS + * @fcs: Current FCS + * @c: Next data + * + * Update the FCS to include c. Uses the algorithm in the specification + * notes. + */ + +static inline u8 gsm_fcs_add(u8 fcs, u8 c) +{ + return gsm_fcs8[fcs ^ c]; +} + +/** + * gsm_fcs_add_block - update FCS for a block + * @fcs: Current FCS + * @c: buffer of data + * @len: length of buffer + * + * Update the FCS to include c. Uses the algorithm in the specification + * notes. + */ + +static inline u8 gsm_fcs_add_block(u8 fcs, u8 *c, int len) +{ + while (len--) + fcs = gsm_fcs8[fcs ^ *c++]; + return fcs; +} + +/** + * gsm_read_ea - read a byte into an EA + * @val: variable holding value + * c: byte going into the EA + * + * Processes one byte of an EA. Updates the passed variable + * and returns 1 if the EA is now completely read + */ + +static int gsm_read_ea(unsigned int *val, u8 c) +{ + /* Add the next 7 bits into the value */ + *val <<= 7; + *val |= c >> 1; + /* Was this the last byte of the EA 1 = yes*/ + return c & EA; +} + +/** + * gsm_encode_modem - encode modem data bits + * @dlci: DLCI to encode from + * + * Returns the correct GSM encoded modem status bits (6 bit field) for + * the current status of the DLCI and attached tty object + */ + +static u8 gsm_encode_modem(const struct gsm_dlci *dlci) +{ + u8 modembits = 0; + /* FC is true flow control not modem bits */ + if (dlci->throttled) + modembits |= MDM_FC; + if (dlci->modem_tx & TIOCM_DTR) + modembits |= MDM_RTC; + if (dlci->modem_tx & TIOCM_RTS) + modembits |= MDM_RTR; + if (dlci->modem_tx & TIOCM_RI) + modembits |= MDM_IC; + if (dlci->modem_tx & TIOCM_CD) + modembits |= MDM_DV; + return modembits; +} + +/** + * gsm_print_packet - display a frame for debug + * @hdr: header to print before decode + * @addr: address EA from the frame + * @cr: C/R bit from the frame + * @control: control including PF bit + * @data: following data bytes + * @dlen: length of data + * + * Displays a packet in human readable format for debugging purposes. The + * style is based on amateur radio LAP-B dump display. + */ + +static void gsm_print_packet(const char *hdr, int addr, int cr, + u8 control, const u8 *data, int dlen) +{ + if (!(debug & 1)) + return; + + printk(KERN_INFO "%s %d) %c: ", hdr, addr, "RC"[cr]); + + switch (control & ~PF) { + case SABM: + printk(KERN_CONT "SABM"); + break; + case UA: + printk(KERN_CONT "UA"); + break; + case DISC: + printk(KERN_CONT "DISC"); + break; + case DM: + printk(KERN_CONT "DM"); + break; + case UI: + printk(KERN_CONT "UI"); + break; + case UIH: + printk(KERN_CONT "UIH"); + break; + default: + if (!(control & 0x01)) { + printk(KERN_CONT "I N(S)%d N(R)%d", + (control & 0x0E) >> 1, (control & 0xE)>> 5); + } else switch (control & 0x0F) { + case RR: + printk("RR(%d)", (control & 0xE0) >> 5); + break; + case RNR: + printk("RNR(%d)", (control & 0xE0) >> 5); + break; + case REJ: + printk("REJ(%d)", (control & 0xE0) >> 5); + break; + default: + printk(KERN_CONT "[%02X]", control); + } + } + + if (control & PF) + printk(KERN_CONT "(P)"); + else + printk(KERN_CONT "(F)"); + + if (dlen) { + int ct = 0; + while (dlen--) { + if (ct % 8 == 0) + printk(KERN_CONT "\n "); + printk(KERN_CONT "%02X ", *data++); + ct++; + } + } + printk(KERN_CONT "\n"); +} + + +/* + * Link level transmission side + */ + +/** + * gsm_stuff_packet - bytestuff a packet + * @ibuf: input + * @obuf: output + * @len: length of input + * + * Expand a buffer by bytestuffing it. The worst case size change + * is doubling and the caller is responsible for handing out + * suitable sized buffers. + */ + +static int gsm_stuff_frame(const u8 *input, u8 *output, int len) +{ + int olen = 0; + while (len--) { + if (*input == GSM1_SOF || *input == GSM1_ESCAPE + || *input == XON || *input == XOFF) { + *output++ = GSM1_ESCAPE; + *output++ = *input++ ^ GSM1_ESCAPE_BITS; + olen++; + } else + *output++ = *input++; + olen++; + } + return olen; +} + +static void hex_packet(const unsigned char *p, int len) +{ + int i; + for (i = 0; i < len; i++) { + if (i && (i % 16) == 0) + printk("\n"); + printk("%02X ", *p++); + } + printk("\n"); +} + +/** + * gsm_send - send a control frame + * @gsm: our GSM mux + * @addr: address for control frame + * @cr: command/response bit + * @control: control byte including PF bit + * + * Format up and transmit a control frame. These do not go via the + * queueing logic as they should be transmitted ahead of data when + * they are needed. + * + * FIXME: Lock versus data TX path + */ + +static void gsm_send(struct gsm_mux *gsm, int addr, int cr, int control) +{ + int len; + u8 cbuf[10]; + u8 ibuf[3]; + + switch (gsm->encoding) { + case 0: + cbuf[0] = GSM0_SOF; + cbuf[1] = (addr << 2) | (cr << 1) | EA; + cbuf[2] = control; + cbuf[3] = EA; /* Length of data = 0 */ + cbuf[4] = 0xFF - gsm_fcs_add_block(INIT_FCS, cbuf + 1, 3); + cbuf[5] = GSM0_SOF; + len = 6; + break; + case 1: + case 2: + /* Control frame + packing (but not frame stuffing) in mode 1 */ + ibuf[0] = (addr << 2) | (cr << 1) | EA; + ibuf[1] = control; + ibuf[2] = 0xFF - gsm_fcs_add_block(INIT_FCS, ibuf, 2); + /* Stuffing may double the size worst case */ + len = gsm_stuff_frame(ibuf, cbuf + 1, 3); + /* Now add the SOF markers */ + cbuf[0] = GSM1_SOF; + cbuf[len + 1] = GSM1_SOF; + /* FIXME: we can omit the lead one in many cases */ + len += 2; + break; + default: + WARN_ON(1); + return; + } + gsm->output(gsm, cbuf, len); + gsm_print_packet("-->", addr, cr, control, NULL, 0); +} + +/** + * gsm_response - send a control response + * @gsm: our GSM mux + * @addr: address for control frame + * @control: control byte including PF bit + * + * Format up and transmit a link level response frame. + */ + +static inline void gsm_response(struct gsm_mux *gsm, int addr, int control) +{ + gsm_send(gsm, addr, 0, control); +} + +/** + * gsm_command - send a control command + * @gsm: our GSM mux + * @addr: address for control frame + * @control: control byte including PF bit + * + * Format up and transmit a link level command frame. + */ + +static inline void gsm_command(struct gsm_mux *gsm, int addr, int control) +{ + gsm_send(gsm, addr, 1, control); +} + +/* Data transmission */ + +#define HDR_LEN 6 /* ADDR CTRL [LEN.2] DATA FCS */ + +/** + * gsm_data_alloc - allocate data frame + * @gsm: GSM mux + * @addr: DLCI address + * @len: length excluding header and FCS + * @ctrl: control byte + * + * Allocate a new data buffer for sending frames with data. Space is left + * at the front for header bytes but that is treated as an implementation + * detail and not for the high level code to use + */ + +static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, + u8 ctrl) +{ + struct gsm_msg *m = kmalloc(sizeof(struct gsm_msg) + len + HDR_LEN, + GFP_ATOMIC); + if (m == NULL) + return NULL; + m->data = m->buffer + HDR_LEN - 1; /* Allow for FCS */ + m->len = len; + m->addr = addr; + m->ctrl = ctrl; + m->next = NULL; + return m; +} + +/** + * gsm_data_kick - poke the queue + * @gsm: GSM Mux + * + * The tty device has called us to indicate that room has appeared in + * the transmit queue. Ram more data into the pipe if we have any + * + * FIXME: lock against link layer control transmissions + */ + +static void gsm_data_kick(struct gsm_mux *gsm) +{ + struct gsm_msg *msg = gsm->tx_head; + int len; + int skip_sof = 0; + + /* FIXME: We need to apply this solely to data messages */ + if (gsm->constipated) + return; + + while (gsm->tx_head != NULL) { + msg = gsm->tx_head; + if (gsm->encoding != 0) { + gsm->txframe[0] = GSM1_SOF; + len = gsm_stuff_frame(msg->data, + gsm->txframe + 1, msg->len); + gsm->txframe[len + 1] = GSM1_SOF; + len += 2; + } else { + gsm->txframe[0] = GSM0_SOF; + memcpy(gsm->txframe + 1 , msg->data, msg->len); + gsm->txframe[msg->len + 1] = GSM0_SOF; + len = msg->len + 2; + } + + if (debug & 4) { + printk("gsm_data_kick: \n"); + hex_packet(gsm->txframe, len); + } + + if (gsm->output(gsm, gsm->txframe + skip_sof, + len - skip_sof) < 0) + break; + /* FIXME: Can eliminate one SOF in many more cases */ + gsm->tx_head = msg->next; + if (gsm->tx_head == NULL) + gsm->tx_tail = NULL; + gsm->tx_bytes -= msg->len; + kfree(msg); + /* For a burst of frames skip the extra SOF within the + burst */ + skip_sof = 1; + } +} + +/** + * __gsm_data_queue - queue a UI or UIH frame + * @dlci: DLCI sending the data + * @msg: message queued + * + * Add data to the transmit queue and try and get stuff moving + * out of the mux tty if not already doing so. The Caller must hold + * the gsm tx lock. + */ + +static void __gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) +{ + struct gsm_mux *gsm = dlci->gsm; + u8 *dp = msg->data; + u8 *fcs = dp + msg->len; + + /* Fill in the header */ + if (gsm->encoding == 0) { + if (msg->len < 128) + *--dp = (msg->len << 1) | EA; + else { + *--dp = (msg->len >> 6) | EA; + *--dp = (msg->len & 127) << 1; + } + } + + *--dp = msg->ctrl; + if (gsm->initiator) + *--dp = (msg->addr << 2) | 2 | EA; + else + *--dp = (msg->addr << 2) | EA; + *fcs = gsm_fcs_add_block(INIT_FCS, dp , msg->data - dp); + /* Ugly protocol layering violation */ + if (msg->ctrl == UI || msg->ctrl == (UI|PF)) + *fcs = gsm_fcs_add_block(*fcs, msg->data, msg->len); + *fcs = 0xFF - *fcs; + + gsm_print_packet("Q> ", msg->addr, gsm->initiator, msg->ctrl, + msg->data, msg->len); + + /* Move the header back and adjust the length, also allow for the FCS + now tacked on the end */ + msg->len += (msg->data - dp) + 1; + msg->data = dp; + + /* Add to the actual output queue */ + if (gsm->tx_tail) + gsm->tx_tail->next = msg; + else + gsm->tx_head = msg; + gsm->tx_tail = msg; + gsm->tx_bytes += msg->len; + gsm_data_kick(gsm); +} + +/** + * gsm_data_queue - queue a UI or UIH frame + * @dlci: DLCI sending the data + * @msg: message queued + * + * Add data to the transmit queue and try and get stuff moving + * out of the mux tty if not already doing so. Take the + * the gsm tx lock and dlci lock. + */ + +static void gsm_data_queue(struct gsm_dlci *dlci, struct gsm_msg *msg) +{ + unsigned long flags; + spin_lock_irqsave(&dlci->gsm->tx_lock, flags); + __gsm_data_queue(dlci, msg); + spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); +} + +/** + * gsm_dlci_data_output - try and push data out of a DLCI + * @gsm: mux + * @dlci: the DLCI to pull data from + * + * Pull data from a DLCI and send it into the transmit queue if there + * is data. Keep to the MRU of the mux. This path handles the usual tty + * interface which is a byte stream with optional modem data. + * + * Caller must hold the tx_lock of the mux. + */ + +static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci) +{ + struct gsm_msg *msg; + u8 *dp; + int len, size; + int h = dlci->adaption - 1; + + len = kfifo_len(dlci->fifo); + if (len == 0) + return 0; + + /* MTU/MRU count only the data bits */ + if (len > gsm->mtu) + len = gsm->mtu; + + size = len + h; + + msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); + /* FIXME: need a timer or something to kick this so it can't + get stuck with no work outstanding and no buffer free */ + if (msg == NULL) + return -ENOMEM; + dp = msg->data; + switch (dlci->adaption) { + case 1: /* Unstructured */ + break; + case 2: /* Unstructed with modem bits. Always one byte as we never + send inline break data */ + *dp += gsm_encode_modem(dlci); + len--; + break; + } + WARN_ON(kfifo_out_locked(dlci->fifo, dp , len, &dlci->lock) != len); + __gsm_data_queue(dlci, msg); + /* Bytes of data we used up */ + return size; +} + +/** + * gsm_dlci_data_output_framed - try and push data out of a DLCI + * @gsm: mux + * @dlci: the DLCI to pull data from + * + * Pull data from a DLCI and send it into the transmit queue if there + * is data. Keep to the MRU of the mux. This path handles framed data + * queued as skbuffs to the DLCI. + * + * Caller must hold the tx_lock of the mux. + */ + +static int gsm_dlci_data_output_framed(struct gsm_mux *gsm, + struct gsm_dlci *dlci) +{ + struct gsm_msg *msg; + u8 *dp; + int len, size; + int last = 0, first = 0; + int overhead = 0; + + /* One byte per frame is used for B/F flags */ + if (dlci->adaption == 4) + overhead = 1; + + /* dlci->skb is locked by tx_lock */ + if (dlci->skb == NULL) { + dlci->skb = skb_dequeue(&dlci->skb_list); + if (dlci->skb == NULL) + return 0; + first = 1; + } + len = dlci->skb->len + overhead; + + /* MTU/MRU count only the data bits */ + if (len > gsm->mtu) { + if (dlci->adaption == 3) { + /* Over long frame, bin it */ + kfree_skb(dlci->skb); + dlci->skb = NULL; + return 0; + } + len = gsm->mtu; + } else + last = 1; + + size = len + overhead; + msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype); + + /* FIXME: need a timer or something to kick this so it can't + get stuck with no work outstanding and no buffer free */ + if (msg == NULL) + return -ENOMEM; + dp = msg->data; + + if (dlci->adaption == 4) { /* Interruptible framed (Packetised Data) */ + /* Flag byte to carry the start/end info */ + *dp++ = last << 7 | first << 6 | 1; /* EA */ + len--; + } + memcpy(dp, skb_pull(dlci->skb, len), len); + __gsm_data_queue(dlci, msg); + if (last) + dlci->skb = NULL; + return size; +} + +/** + * gsm_dlci_data_sweep - look for data to send + * @gsm: the GSM mux + * + * Sweep the GSM mux channels in priority order looking for ones with + * data to send. We could do with optimising this scan a bit. We aim + * to fill the queue totally or up to TX_THRESH_HI bytes. Once we hit + * TX_THRESH_LO we get called again + * + * FIXME: We should round robin between groups and in theory you can + * renegotiate DLCI priorities with optional stuff. Needs optimising. + */ + +static void gsm_dlci_data_sweep(struct gsm_mux *gsm) +{ + int len; + /* Priority ordering: We should do priority with RR of the groups */ + int i = 1; + unsigned long flags; + + spin_lock_irqsave(&gsm->tx_lock, flags); + while (i < NUM_DLCI) { + struct gsm_dlci *dlci; + + if (gsm->tx_bytes > TX_THRESH_HI) + break; + dlci = gsm->dlci[i]; + if (dlci == NULL || dlci->constipated) { + i++; + continue; + } + if (dlci->adaption < 3) + len = gsm_dlci_data_output(gsm, dlci); + else + len = gsm_dlci_data_output_framed(gsm, dlci); + if (len < 0) + return; + /* DLCI empty - try the next */ + if (len == 0) + i++; + } + spin_unlock_irqrestore(&gsm->tx_lock, flags); +} + +/** + * gsm_dlci_data_kick - transmit if possible + * @dlci: DLCI to kick + * + * Transmit data from this DLCI if the queue is empty. We can't rely on + * a tty wakeup except when we filled the pipe so we need to fire off + * new data ourselves in other cases. + */ + +static void gsm_dlci_data_kick(struct gsm_dlci *dlci) +{ + unsigned long flags; + + spin_lock_irqsave(&dlci->gsm->tx_lock, flags); + /* If we have nothing running then we need to fire up */ + if (dlci->gsm->tx_bytes == 0) + gsm_dlci_data_output(dlci->gsm, dlci); + else if (dlci->gsm->tx_bytes < TX_THRESH_LO) + gsm_dlci_data_sweep(dlci->gsm); + spin_unlock_irqrestore(&dlci->gsm->tx_lock, flags); +} + +/* + * Control message processing + */ + + +/** + * gsm_control_reply - send a response frame to a control + * @gsm: gsm channel + * @cmd: the command to use + * @data: data to follow encoded info + * @dlen: length of data + * + * Encode up and queue a UI/UIH frame containing our response. + */ + +static void gsm_control_reply(struct gsm_mux *gsm, int cmd, u8 *data, + int dlen) +{ + struct gsm_msg *msg; + msg = gsm_data_alloc(gsm, 0, dlen + 2, gsm->ftype); + msg->data[0] = (cmd & 0xFE) << 1 | EA; /* Clear C/R */ + msg->data[1] = (dlen << 1) | EA; + memcpy(msg->data + 2, data, dlen); + gsm_data_queue(gsm->dlci[0], msg); +} + +/** + * gsm_process_modem - process received modem status + * @tty: virtual tty bound to the DLCI + * @dlci: DLCI to affect + * @modem: modem bits (full EA) + * + * Used when a modem control message or line state inline in adaption + * layer 2 is processed. Sort out the local modem state and throttles + */ + +static void gsm_process_modem(struct tty_struct *tty, struct gsm_dlci *dlci, + u32 modem) +{ + int mlines = 0; + u8 brk = modem >> 6; + + /* Flow control/ready to communicate */ + if (modem & MDM_FC) { + /* Need to throttle our output on this device */ + dlci->constipated = 1; + } + if (modem & MDM_RTC) { + mlines |= TIOCM_DSR | TIOCM_DTR; + dlci->constipated = 0; + gsm_dlci_data_kick(dlci); + } + /* Map modem bits */ + if (modem & MDM_RTR) + mlines |= TIOCM_RTS | TIOCM_CTS; + if (modem & MDM_IC) + mlines |= TIOCM_RI; + if (modem & MDM_DV) + mlines |= TIOCM_CD; + + /* Carrier drop -> hangup */ + if (tty) { + if ((mlines & TIOCM_CD) == 0 && (dlci->modem_rx & TIOCM_CD)) + if (!(tty->termios->c_cflag & CLOCAL)) + tty_hangup(tty); + if (brk & 0x01) + tty_insert_flip_char(tty, 0, TTY_BREAK); + } + dlci->modem_rx = mlines; +} + +/** + * gsm_control_modem - modem status received + * @gsm: GSM channel + * @data: data following command + * @clen: command length + * + * We have received a modem status control message. This is used by + * the GSM mux protocol to pass virtual modem line status and optionally + * to indicate break signals. Unpack it, convert to Linux representation + * and if need be stuff a break message down the tty. + */ + +static void gsm_control_modem(struct gsm_mux *gsm, u8 *data, int clen) +{ + unsigned int addr = 0; + unsigned int modem = 0; + struct gsm_dlci *dlci; + int len = clen; + u8 *dp = data; + struct tty_struct *tty; + + while (gsm_read_ea(&addr, *dp++) == 0) { + len--; + if (len == 0) + return; + } + /* Must be at least one byte following the EA */ + len--; + if (len <= 0) + return; + + addr >>= 1; + /* Closed port, or invalid ? */ + if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL) + return; + dlci = gsm->dlci[addr]; + + while (gsm_read_ea(&modem, *dp++) == 0) { + len--; + if (len == 0) + return; + } + tty = tty_port_tty_get(&dlci->port); + gsm_process_modem(tty, dlci, modem); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } + gsm_control_reply(gsm, CMD_MSC, data, clen); +} + +/** + * gsm_control_rls - remote line status + * @gsm: GSM channel + * @data: data bytes + * @clen: data length + * + * The modem sends us a two byte message on the control channel whenever + * it wishes to send us an error state from the virtual link. Stuff + * this into the uplink tty if present + */ + +static void gsm_control_rls(struct gsm_mux *gsm, u8 *data, int clen) +{ + struct tty_struct *tty; + unsigned int addr = 0 ; + u8 bits; + int len = clen; + u8 *dp = data; + + while (gsm_read_ea(&addr, *dp++) == 0) { + len--; + if (len == 0) + return; + } + /* Must be at least one byte following ea */ + len--; + if (len <= 0) + return; + addr >>= 1; + /* Closed port, or invalid ? */ + if (addr == 0 || addr >= NUM_DLCI || gsm->dlci[addr] == NULL) + return; + /* No error ? */ + bits = *dp; + if ((bits & 1) == 0) + return; + /* See if we have an uplink tty */ + tty = tty_port_tty_get(&gsm->dlci[addr]->port); + + if (tty) { + if (bits & 2) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + if (bits & 4) + tty_insert_flip_char(tty, 0, TTY_PARITY); + if (bits & 8) + tty_insert_flip_char(tty, 0, TTY_FRAME); + tty_flip_buffer_push(tty); + tty_kref_put(tty); + } + gsm_control_reply(gsm, CMD_RLS, data, clen); +} + +static void gsm_dlci_begin_close(struct gsm_dlci *dlci); + +/** + * gsm_control_message - DLCI 0 control processing + * @gsm: our GSM mux + * @command: the command EA + * @data: data beyond the command/length EAs + * @clen: length + * + * Input processor for control messages from the other end of the link. + * Processes the incoming request and queues a response frame or an + * NSC response if not supported + */ + +static void gsm_control_message(struct gsm_mux *gsm, unsigned int command, + u8 *data, int clen) +{ + u8 buf[1]; + switch (command) { + case CMD_CLD: { + struct gsm_dlci *dlci = gsm->dlci[0]; + /* Modem wishes to close down */ + if (dlci) { + dlci->dead = 1; + gsm->dead = 1; + gsm_dlci_begin_close(dlci); + } + } + break; + case CMD_TEST: + /* Modem wishes to test, reply with the data */ + gsm_control_reply(gsm, CMD_TEST, data, clen); + break; + case CMD_FCON: + /* Modem wants us to STFU */ + gsm->constipated = 1; + gsm_control_reply(gsm, CMD_FCON, NULL, 0); + break; + case CMD_FCOFF: + /* Modem can accept data again */ + gsm->constipated = 0; + gsm_control_reply(gsm, CMD_FCOFF, NULL, 0); + /* Kick the link in case it is idling */ + gsm_data_kick(gsm); + break; + case CMD_MSC: + /* Out of band modem line change indicator for a DLCI */ + gsm_control_modem(gsm, data, clen); + break; + case CMD_RLS: + /* Out of band error reception for a DLCI */ + gsm_control_rls(gsm, data, clen); + break; + case CMD_PSC: + /* Modem wishes to enter power saving state */ + gsm_control_reply(gsm, CMD_PSC, NULL, 0); + break; + /* Optional unsupported commands */ + case CMD_PN: /* Parameter negotiation */ + case CMD_RPN: /* Remote port negotation */ + case CMD_SNC: /* Service negotation command */ + default: + /* Reply to bad commands with an NSC */ + buf[0] = command; + gsm_control_reply(gsm, CMD_NSC, buf, 1); + break; + } +} + +/** + * gsm_control_response - process a response to our control + * @gsm: our GSM mux + * @command: the command (response) EA + * @data: data beyond the command/length EA + * @clen: length + * + * Process a response to an outstanding command. We only allow a single + * control message in flight so this is fairly easy. All the clean up + * is done by the caller, we just update the fields, flag it as done + * and return + */ + +static void gsm_control_response(struct gsm_mux *gsm, unsigned int command, + u8 *data, int clen) +{ + struct gsm_control *ctrl; + unsigned long flags; + + spin_lock_irqsave(&gsm->control_lock, flags); + + ctrl = gsm->pending_cmd; + /* Does the reply match our command */ + command |= 1; + if (ctrl != NULL && (command == ctrl->cmd || command == CMD_NSC)) { + /* Our command was replied to, kill the retry timer */ + del_timer(&gsm->t2_timer); + gsm->pending_cmd = NULL; + /* Rejected by the other end */ + if (command == CMD_NSC) + ctrl->error = -EOPNOTSUPP; + ctrl->done = 1; + wake_up(&gsm->event); + } + spin_unlock_irqrestore(&gsm->control_lock, flags); +} + +/** + * gsm_control_transmit - send control packet + * @gsm: gsm mux + * @ctrl: frame to send + * + * Send out a pending control command (called under control lock) + */ + +static void gsm_control_transmit(struct gsm_mux *gsm, struct gsm_control *ctrl) +{ + struct gsm_msg *msg = gsm_data_alloc(gsm, 0, ctrl->len + 1, + gsm->ftype|PF); + if (msg == NULL) + return; + msg->data[0] = (ctrl->cmd << 1) | 2 | EA; /* command */ + memcpy(msg->data + 1, ctrl->data, ctrl->len); + gsm_data_queue(gsm->dlci[0], msg); +} + +/** + * gsm_control_retransmit - retransmit a control frame + * @data: pointer to our gsm object + * + * Called off the T2 timer expiry in order to retransmit control frames + * that have been lost in the system somewhere. The control_lock protects + * us from colliding with another sender or a receive completion event. + * In that situation the timer may still occur in a small window but + * gsm->pending_cmd will be NULL and we just let the timer expire. + */ + +static void gsm_control_retransmit(unsigned long data) +{ + struct gsm_mux *gsm = (struct gsm_mux *)data; + struct gsm_control *ctrl; + unsigned long flags; + spin_lock_irqsave(&gsm->control_lock, flags); + ctrl = gsm->pending_cmd; + if (ctrl) { + gsm->cretries--; + if (gsm->cretries == 0) { + gsm->pending_cmd = NULL; + ctrl->error = -ETIMEDOUT; + ctrl->done = 1; + spin_unlock_irqrestore(&gsm->control_lock, flags); + wake_up(&gsm->event); + return; + } + gsm_control_transmit(gsm, ctrl); + mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); + } + spin_unlock_irqrestore(&gsm->control_lock, flags); +} + +/** + * gsm_control_send - send a control frame on DLCI 0 + * @gsm: the GSM channel + * @command: command to send including CR bit + * @data: bytes of data (must be kmalloced) + * @len: length of the block to send + * + * Queue and dispatch a control command. Only one command can be + * active at a time. In theory more can be outstanding but the matching + * gets really complicated so for now stick to one outstanding. + */ + +static struct gsm_control *gsm_control_send(struct gsm_mux *gsm, + unsigned int command, u8 *data, int clen) +{ + struct gsm_control *ctrl = kzalloc(sizeof(struct gsm_control), + GFP_KERNEL); + unsigned long flags; + if (ctrl == NULL) + return NULL; +retry: + wait_event(gsm->event, gsm->pending_cmd == NULL); + spin_lock_irqsave(&gsm->control_lock, flags); + if (gsm->pending_cmd != NULL) { + spin_unlock_irqrestore(&gsm->control_lock, flags); + goto retry; + } + ctrl->cmd = command; + ctrl->data = data; + ctrl->len = clen; + gsm->pending_cmd = ctrl; + gsm->cretries = gsm->n2; + mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100); + gsm_control_transmit(gsm, ctrl); + spin_unlock_irqrestore(&gsm->control_lock, flags); + return ctrl; +} + +/** + * gsm_control_wait - wait for a control to finish + * @gsm: GSM mux + * @control: control we are waiting on + * + * Waits for the control to complete or time out. Frees any used + * resources and returns 0 for success, or an error if the remote + * rejected or ignored the request. + */ + +static int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control) +{ + int err; + wait_event(gsm->event, control->done == 1); + err = control->error; + kfree(control); + return err; +} + + +/* + * DLCI level handling: Needs krefs + */ + +/* + * State transitions and timers + */ + +/** + * gsm_dlci_close - a DLCI has closed + * @dlci: DLCI that closed + * + * Perform processing when moving a DLCI into closed state. If there + * is an attached tty this is hung up + */ + +static void gsm_dlci_close(struct gsm_dlci *dlci) +{ + del_timer(&dlci->t1); + if (debug & 8) + printk("DLCI %d goes closed.\n", dlci->addr); + dlci->state = DLCI_CLOSED; + if (dlci->addr != 0) { + struct tty_struct *tty = tty_port_tty_get(&dlci->port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } + kfifo_reset(dlci->fifo); + } else + dlci->gsm->dead = 1; + wake_up(&dlci->gsm->event); + /* A DLCI 0 close is a MUX termination so we need to kick that + back to userspace somehow */ +} + +/** + * gsm_dlci_open - a DLCI has opened + * @dlci: DLCI that opened + * + * Perform processing when moving a DLCI into open state. + */ + +static void gsm_dlci_open(struct gsm_dlci *dlci) +{ + /* Note that SABM UA .. SABM UA first UA lost can mean that we go + open -> open */ + del_timer(&dlci->t1); + /* This will let a tty open continue */ + dlci->state = DLCI_OPEN; + if (debug & 8) + printk("DLCI %d goes open.\n", dlci->addr); + wake_up(&dlci->gsm->event); +} + +/** + * gsm_dlci_t1 - T1 timer expiry + * @dlci: DLCI that opened + * + * The T1 timer handles retransmits of control frames (essentially of + * SABM and DISC). We resend the command until the retry count runs out + * in which case an opening port goes back to closed and a closing port + * is simply put into closed state (any further frames from the other + * end will get a DM response) + */ + +static void gsm_dlci_t1(unsigned long data) +{ + struct gsm_dlci *dlci = (struct gsm_dlci *)data; + struct gsm_mux *gsm = dlci->gsm; + + switch (dlci->state) { + case DLCI_OPENING: + dlci->retries--; + if (dlci->retries) { + gsm_command(dlci->gsm, dlci->addr, SABM|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); + } else + gsm_dlci_close(dlci); + break; + case DLCI_CLOSING: + dlci->retries--; + if (dlci->retries) { + gsm_command(dlci->gsm, dlci->addr, DISC|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); + } else + gsm_dlci_close(dlci); + break; + } +} + +/** + * gsm_dlci_begin_open - start channel open procedure + * @dlci: DLCI to open + * + * Commence opening a DLCI from the Linux side. We issue SABM messages + * to the modem which should then reply with a UA, at which point we + * will move into open state. Opening is done asynchronously with retry + * running off timers and the responses. + */ + +static void gsm_dlci_begin_open(struct gsm_dlci *dlci) +{ + struct gsm_mux *gsm = dlci->gsm; + if (dlci->state == DLCI_OPEN || dlci->state == DLCI_OPENING) + return; + dlci->retries = gsm->n2; + dlci->state = DLCI_OPENING; + gsm_command(dlci->gsm, dlci->addr, SABM|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); +} + +/** + * gsm_dlci_begin_close - start channel open procedure + * @dlci: DLCI to open + * + * Commence closing a DLCI from the Linux side. We issue DISC messages + * to the modem which should then reply with a UA, at which point we + * will move into closed state. Closing is done asynchronously with retry + * off timers. We may also receive a DM reply from the other end which + * indicates the channel was already closed. + */ + +static void gsm_dlci_begin_close(struct gsm_dlci *dlci) +{ + struct gsm_mux *gsm = dlci->gsm; + if (dlci->state == DLCI_CLOSED || dlci->state == DLCI_CLOSING) + return; + dlci->retries = gsm->n2; + dlci->state = DLCI_CLOSING; + gsm_command(dlci->gsm, dlci->addr, DISC|PF); + mod_timer(&dlci->t1, jiffies + gsm->t1 * HZ / 100); +} + +/** + * gsm_dlci_data - data arrived + * @dlci: channel + * @data: block of bytes received + * @len: length of received block + * + * A UI or UIH frame has arrived which contains data for a channel + * other than the control channel. If the relevant virtual tty is + * open we shovel the bits down it, if not we drop them. + */ + +static void gsm_dlci_data(struct gsm_dlci *dlci, u8 *data, int len) +{ + /* krefs .. */ + struct tty_port *port = &dlci->port; + struct tty_struct *tty = tty_port_tty_get(port); + unsigned int modem = 0; + + if (debug & 16) + printk("%d bytes for tty %p\n", len, tty); + if (tty) { + switch (dlci->adaption) { + /* Unsupported types */ + /* Packetised interruptible data */ + case 4: + break; + /* Packetised uininterruptible voice/data */ + case 3: + break; + /* Asynchronous serial with line state in each frame */ + case 2: + while (gsm_read_ea(&modem, *data++) == 0) { + len--; + if (len == 0) + return; + } + gsm_process_modem(tty, dlci, modem); + /* Line state will go via DLCI 0 controls only */ + case 1: + default: + tty_insert_flip_string(tty, data, len); + tty_flip_buffer_push(tty); + } + tty_kref_put(tty); + } +} + +/** + * gsm_dlci_control - data arrived on control channel + * @dlci: channel + * @data: block of bytes received + * @len: length of received block + * + * A UI or UIH frame has arrived which contains data for DLCI 0 the + * control channel. This should contain a command EA followed by + * control data bytes. The command EA contains a command/response bit + * and we divide up the work accordingly. + */ + +static void gsm_dlci_command(struct gsm_dlci *dlci, u8 *data, int len) +{ + /* See what command is involved */ + unsigned int command = 0; + while (len-- > 0) { + if (gsm_read_ea(&command, *data++) == 1) { + int clen = *data++; + len--; + /* FIXME: this is properly an EA */ + clen >>= 1; + /* Malformed command ? */ + if (clen > len) + return; + if (command & 1) + gsm_control_message(dlci->gsm, command, + data, clen); + else + gsm_control_response(dlci->gsm, command, + data, clen); + return; + } + } +} + +/* + * Allocate/Free DLCI channels + */ + +/** + * gsm_dlci_alloc - allocate a DLCI + * @gsm: GSM mux + * @addr: address of the DLCI + * + * Allocate and install a new DLCI object into the GSM mux. + * + * FIXME: review locking races + */ + +static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr) +{ + struct gsm_dlci *dlci = kzalloc(sizeof(struct gsm_dlci), GFP_ATOMIC); + if (dlci == NULL) + return NULL; + spin_lock_init(&dlci->lock); + dlci->fifo = &dlci->_fifo; + if (kfifo_alloc(&dlci->_fifo, 4096, GFP_KERNEL) < 0) { + kfree(dlci); + return NULL; + } + + skb_queue_head_init(&dlci->skb_list); + init_timer(&dlci->t1); + dlci->t1.function = gsm_dlci_t1; + dlci->t1.data = (unsigned long)dlci; + tty_port_init(&dlci->port); + dlci->port.ops = &gsm_port_ops; + dlci->gsm = gsm; + dlci->addr = addr; + dlci->adaption = gsm->adaption; + dlci->state = DLCI_CLOSED; + if (addr) + dlci->data = gsm_dlci_data; + else + dlci->data = gsm_dlci_command; + gsm->dlci[addr] = dlci; + return dlci; +} + +/** + * gsm_dlci_free - release DLCI + * @dlci: DLCI to destroy + * + * Free up a DLCI. Currently to keep the lifetime rules sane we only + * clean up DLCI objects when the MUX closes rather than as the port + * is closed down on both the tty and mux levels. + * + * Can sleep. + */ +static void gsm_dlci_free(struct gsm_dlci *dlci) +{ + struct tty_struct *tty = tty_port_tty_get(&dlci->port); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } + del_timer_sync(&dlci->t1); + dlci->gsm->dlci[dlci->addr] = NULL; + kfifo_free(dlci->fifo); + kfree(dlci); +} + + +/* + * LAPBish link layer logic + */ + +/** + * gsm_queue - a GSM frame is ready to process + * @gsm: pointer to our gsm mux + * + * At this point in time a frame has arrived and been demangled from + * the line encoding. All the differences between the encodings have + * been handled below us and the frame is unpacked into the structures. + * The fcs holds the header FCS but any data FCS must be added here. + */ + +static void gsm_queue(struct gsm_mux *gsm) +{ + struct gsm_dlci *dlci; + u8 cr; + int address; + /* We have to sneak a look at the packet body to do the FCS. + A somewhat layering violation in the spec */ + + if ((gsm->control & ~PF) == UI) + gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len); + if (gsm->fcs != GOOD_FCS) { + gsm->bad_fcs++; + if (debug & 4) + printk("BAD FCS %02x\n", gsm->fcs); + return; + } + address = gsm->address >> 1; + if (address >= NUM_DLCI) + goto invalid; + + cr = gsm->address & 1; /* C/R bit */ + + gsm_print_packet("<--", address, cr, gsm->control, gsm->buf, gsm->len); + + cr ^= 1 - gsm->initiator; /* Flip so 1 always means command */ + dlci = gsm->dlci[address]; + + switch (gsm->control) { + case SABM|PF: + if (cr == 0) + goto invalid; + if (dlci == NULL) + dlci = gsm_dlci_alloc(gsm, address); + if (dlci == NULL) + return; + if (dlci->dead) + gsm_response(gsm, address, DM); + else { + gsm_response(gsm, address, UA); + gsm_dlci_open(dlci); + } + break; + case DISC|PF: + if (cr == 0) + goto invalid; + if (dlci == NULL || dlci->state == DLCI_CLOSED) { + gsm_response(gsm, address, DM); + return; + } + /* Real close complete */ + gsm_response(gsm, address, UA); + gsm_dlci_close(dlci); + break; + case UA: + case UA|PF: + if (cr == 0 || dlci == NULL) + break; + switch (dlci->state) { + case DLCI_CLOSING: + gsm_dlci_close(dlci); + break; + case DLCI_OPENING: + gsm_dlci_open(dlci); + break; + } + break; + case DM: /* DM can be valid unsolicited */ + case DM|PF: + if (cr) + goto invalid; + if (dlci == NULL) + return; + gsm_dlci_close(dlci); + break; + case UI: + case UI|PF: + case UIH: + case UIH|PF: +#if 0 + if (cr) + goto invalid; +#endif + if (dlci == NULL || dlci->state != DLCI_OPEN) { + gsm_command(gsm, address, DM|PF); + return; + } + dlci->data(dlci, gsm->buf, gsm->len); + break; + default: + goto invalid; + } + return; +invalid: + gsm->malformed++; + return; +} + + +/** + * gsm0_receive - perform processing for non-transparency + * @gsm: gsm data for this ldisc instance + * @c: character + * + * Receive bytes in gsm mode 0 + */ + +static void gsm0_receive(struct gsm_mux *gsm, unsigned char c) +{ + switch (gsm->state) { + case GSM_SEARCH: /* SOF marker */ + if (c == GSM0_SOF) { + gsm->state = GSM_ADDRESS; + gsm->address = 0; + gsm->len = 0; + gsm->fcs = INIT_FCS; + } + break; /* Address EA */ + case GSM_ADDRESS: + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + if (gsm_read_ea(&gsm->address, c)) + gsm->state = GSM_CONTROL; + break; + case GSM_CONTROL: /* Control Byte */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + gsm->control = c; + gsm->state = GSM_LEN; + break; + case GSM_LEN: /* Length EA */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + if (gsm_read_ea(&gsm->len, c)) { + if (gsm->len > gsm->mru) { + gsm->bad_size++; + gsm->state = GSM_SEARCH; + break; + } + gsm->count = 0; + gsm->state = GSM_DATA; + } + break; + case GSM_DATA: /* Data */ + gsm->buf[gsm->count++] = c; + if (gsm->count == gsm->len) + gsm->state = GSM_FCS; + break; + case GSM_FCS: /* FCS follows the packet */ + gsm->fcs = c; + gsm_queue(gsm); + /* And then back for the next frame */ + gsm->state = GSM_SEARCH; + break; + } +} + +/** + * gsm0_receive - perform processing for non-transparency + * @gsm: gsm data for this ldisc instance + * @c: character + * + * Receive bytes in mode 1 (Advanced option) + */ + +static void gsm1_receive(struct gsm_mux *gsm, unsigned char c) +{ + if (c == GSM1_SOF) { + /* EOF is only valid in frame if we have got to the data state + and received at least one byte (the FCS) */ + if (gsm->state == GSM_DATA && gsm->count) { + /* Extract the FCS */ + gsm->count--; + gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]); + gsm->len = gsm->count; + gsm_queue(gsm); + gsm->state = GSM_START; + return; + } + /* Any partial frame was a runt so go back to start */ + if (gsm->state != GSM_START) { + gsm->malformed++; + gsm->state = GSM_START; + } + /* A SOF in GSM_START means we are still reading idling or + framing bytes */ + return; + } + + if (c == GSM1_ESCAPE) { + gsm->escape = 1; + return; + } + + /* Only an unescaped SOF gets us out of GSM search */ + if (gsm->state == GSM_SEARCH) + return; + + if (gsm->escape) { + c ^= GSM1_ESCAPE_BITS; + gsm->escape = 0; + } + switch (gsm->state) { + case GSM_START: /* First byte after SOF */ + gsm->address = 0; + gsm->state = GSM_ADDRESS; + gsm->fcs = INIT_FCS; + /* Drop through */ + case GSM_ADDRESS: /* Address continuation */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + if (gsm_read_ea(&gsm->address, c)) + gsm->state = GSM_CONTROL; + break; + case GSM_CONTROL: /* Control Byte */ + gsm->fcs = gsm_fcs_add(gsm->fcs, c); + gsm->control = c; + gsm->count = 0; + gsm->state = GSM_DATA; + break; + case GSM_DATA: /* Data */ + if (gsm->count > gsm->mru ) { /* Allow one for the FCS */ + gsm->state = GSM_OVERRUN; + gsm->bad_size++; + } else + gsm->buf[gsm->count++] = c; + break; + case GSM_OVERRUN: /* Over-long - eg a dropped SOF */ + break; + } +} + +/** + * gsm_error - handle tty error + * @gsm: ldisc data + * @data: byte received (may be invalid) + * @flag: error received + * + * Handle an error in the receipt of data for a frame. Currently we just + * go back to hunting for a SOF. + * + * FIXME: better diagnostics ? + */ + +static void gsm_error(struct gsm_mux *gsm, + unsigned char data, unsigned char flag) +{ + gsm->state = GSM_SEARCH; + gsm->io_error++; +} + +/** + * gsm_cleanup_mux - generic GSM protocol cleanup + * @gsm: our mux + * + * Clean up the bits of the mux which are the same for all framing + * protocols. Remove the mux from the mux table, stop all the timers + * and then shut down each device hanging up the channels as we go. + */ + +void gsm_cleanup_mux(struct gsm_mux *gsm) +{ + int i; + struct gsm_dlci *dlci = gsm->dlci[0]; + struct gsm_msg *txq; + + gsm->dead = 1; + + spin_lock(&gsm_mux_lock); + for (i = 0; i < MAX_MUX; i++) { + if (gsm_mux[i] == gsm) { + gsm_mux[i] = NULL; + break; + } + } + spin_unlock(&gsm_mux_lock); + WARN_ON(i == MAX_MUX); + + del_timer_sync(&gsm->t2_timer); + /* Now we are sure T2 has stopped */ + if (dlci) { + dlci->dead = 1; + gsm_dlci_begin_close(dlci); + wait_event_interruptible(gsm->event, + dlci->state == DLCI_CLOSED); + } + /* Free up any link layer users */ + for (i = 0; i < NUM_DLCI; i++) + if (gsm->dlci[i]) + gsm_dlci_free(gsm->dlci[i]); + /* Now wipe the queues */ + for (txq = gsm->tx_head; txq != NULL; txq = gsm->tx_head) { + gsm->tx_head = txq->next; + kfree(txq); + } + gsm->tx_tail = NULL; +} +EXPORT_SYMBOL_GPL(gsm_cleanup_mux); + +/** + * gsm_activate_mux - generic GSM setup + * @gsm: our mux + * + * Set up the bits of the mux which are the same for all framing + * protocols. Add the mux to the mux table so it can be opened and + * finally kick off connecting to DLCI 0 on the modem. + */ + +int gsm_activate_mux(struct gsm_mux *gsm) +{ + struct gsm_dlci *dlci; + int i = 0; + + init_timer(&gsm->t2_timer); + gsm->t2_timer.function = gsm_control_retransmit; + gsm->t2_timer.data = (unsigned long)gsm; + init_waitqueue_head(&gsm->event); + spin_lock_init(&gsm->control_lock); + spin_lock_init(&gsm->tx_lock); + + if (gsm->encoding == 0) + gsm->receive = gsm0_receive; + else + gsm->receive = gsm1_receive; + gsm->error = gsm_error; + + spin_lock(&gsm_mux_lock); + for (i = 0; i < MAX_MUX; i++) { + if (gsm_mux[i] == NULL) { + gsm_mux[i] = gsm; + break; + } + } + spin_unlock(&gsm_mux_lock); + if (i == MAX_MUX) + return -EBUSY; + + dlci = gsm_dlci_alloc(gsm, 0); + if (dlci == NULL) + return -ENOMEM; + gsm->dead = 0; /* Tty opens are now permissible */ + return 0; +} +EXPORT_SYMBOL_GPL(gsm_activate_mux); + +/** + * gsm_free_mux - free up a mux + * @mux: mux to free + * + * Dispose of allocated resources for a dead mux. No refcounting + * at present so the mux must be truely dead. + */ +void gsm_free_mux(struct gsm_mux *gsm) +{ + kfree(gsm->txframe); + kfree(gsm->buf); + kfree(gsm); +} +EXPORT_SYMBOL_GPL(gsm_free_mux); + +/** + * gsm_alloc_mux - allocate a mux + * + * Creates a new mux ready for activation. + */ + +struct gsm_mux *gsm_alloc_mux(void) +{ + struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL); + if (gsm == NULL) + return NULL; + gsm->buf = kmalloc(MAX_MRU + 1, GFP_KERNEL); + if (gsm->buf == NULL) { + kfree(gsm); + return NULL; + } + gsm->txframe = kmalloc(2 * MAX_MRU + 2, GFP_KERNEL); + if (gsm->txframe == NULL) { + kfree(gsm->buf); + kfree(gsm); + return NULL; + } + spin_lock_init(&gsm->lock); + + gsm->t1 = T1; + gsm->t2 = T2; + gsm->n2 = N2; + gsm->ftype = UIH; + gsm->initiator = 0; + gsm->adaption = 1; + gsm->encoding = 1; + gsm->mru = 64; /* Default to encoding 1 so these should be 64 */ + gsm->mtu = 64; + gsm->dead = 1; /* Avoid early tty opens */ + + return gsm; +} +EXPORT_SYMBOL_GPL(gsm_alloc_mux); + + + + +/** + * gsmld_output - write to link + * @gsm: our mux + * @data: bytes to output + * @len: size + * + * Write a block of data from the GSM mux to the data channel. This + * will eventually be serialized from above but at the moment isn't. + */ + +static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len) +{ + if (tty_write_room(gsm->tty) < len) { + set_bit(TTY_DO_WRITE_WAKEUP, &gsm->tty->flags); + return -ENOSPC; + } + if (debug & 4) { + printk("-->%d bytes out\n", len); + hex_packet(data, len); + } + gsm->tty->ops->write(gsm->tty, data, len); + return len; +} + +/** + * gsmld_attach_gsm - mode set up + * @tty: our tty structure + * @gsm: our mux + * + * Set up the MUX for basic mode and commence connecting to the + * modem. Currently called from the line discipline set up but + * will need moving to an ioctl path. + */ + +static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) +{ + int ret; + + gsm->tty = tty_kref_get(tty); + gsm->output = gsmld_output; + ret = gsm_activate_mux(gsm); + if (ret != 0) + tty_kref_put(gsm->tty); + return ret; +} + + +/** + * gsmld_detach_gsm - stop doing 0710 mux + * @tty: tty atttached to the mux + * @gsm: mux + * + * Shutdown and then clean up the resources used by the line discipline + */ + +static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm) +{ + WARN_ON(tty != gsm->tty); + gsm_cleanup_mux(gsm); + tty_kref_put(gsm->tty); + gsm->tty = NULL; +} + +static void gsmld_receive_buf(struct tty_struct *tty, const unsigned char *cp, + char *fp, int count) +{ + struct gsm_mux *gsm = tty->disc_data; + const unsigned char *dp; + char *f; + int i; + char buf[64]; + char flags; + + if (debug & 4) { + printk("Inbytes %dd\n", count); + hex_packet(cp, count); + } + + for (i = count, dp = cp, f = fp; i; i--, dp++) { + flags = *f++; + switch (flags) { + case TTY_NORMAL: + gsm->receive(gsm, *dp); + break; + case TTY_OVERRUN: + case TTY_BREAK: + case TTY_PARITY: + case TTY_FRAME: + gsm->error(gsm, *dp, flags); + break; + default: + printk(KERN_ERR "%s: unknown flag %d\n", + tty_name(tty, buf), flags); + break; + } + } + /* FASYNC if needed ? */ + /* If clogged call tty_throttle(tty); */ +} + +/** + * gsmld_chars_in_buffer - report available bytes + * @tty: tty device + * + * Report the number of characters buffered to be delivered to user + * at this instant in time. + * + * Locking: gsm lock + */ + +static ssize_t gsmld_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +/** + * gsmld_flush_buffer - clean input queue + * @tty: terminal device + * + * Flush the input buffer. Called when the line discipline is + * being closed, when the tty layer wants the buffer flushed (eg + * at hangup). + */ + +static void gsmld_flush_buffer(struct tty_struct *tty) +{ +} + +/** + * gsmld_close - close the ldisc for this tty + * @tty: device + * + * Called from the terminal layer when this line discipline is + * being shut down, either because of a close or becsuse of a + * discipline change. The function will not be called while other + * ldisc methods are in progress. + */ + +static void gsmld_close(struct tty_struct *tty) +{ + struct gsm_mux *gsm = tty->disc_data; + + gsmld_detach_gsm(tty, gsm); + + gsmld_flush_buffer(tty); + /* Do other clean up here */ + gsm_free_mux(gsm); +} + +/** + * gsmld_open - open an ldisc + * @tty: terminal to open + * + * Called when this line discipline is being attached to the + * terminal device. Can sleep. Called serialized so that no + * other events will occur in parallel. No further open will occur + * until a close. + */ + +static int gsmld_open(struct tty_struct *tty) +{ + struct gsm_mux *gsm; + + if (tty->ops->write == NULL) + return -EINVAL; + + /* Attach our ldisc data */ + gsm = gsm_alloc_mux(); + if (gsm == NULL) + return -ENOMEM; + + tty->disc_data = gsm; + tty->receive_room = 65536; + + /* Attach the initial passive connection */ + gsm->encoding = 1; + return gsmld_attach_gsm(tty, gsm); +} + +/** + * gsmld_write_wakeup - asynchronous I/O notifier + * @tty: tty device + * + * Required for the ptys, serial driver etc. since processes + * that attach themselves to the master and rely on ASYNC + * IO must be woken up + */ + +static void gsmld_write_wakeup(struct tty_struct *tty) +{ + struct gsm_mux *gsm = tty->disc_data; + + /* Queue poll */ + clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + gsm_data_kick(gsm); + if (gsm->tx_bytes < TX_THRESH_LO) + gsm_dlci_data_sweep(gsm); +} + +/** + * gsmld_read - read function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Perform reads for the line discipline. We are guaranteed that the + * line discipline will not be closed under us but we may get multiple + * parallel readers and must handle this ourselves. We may also get + * a hangup. Always called in user context, may sleep. + * + * This code must be sure never to sleep through a hangup. + */ + +static ssize_t gsmld_read(struct tty_struct *tty, struct file *file, + unsigned char __user *buf, size_t nr) +{ + return -EOPNOTSUPP; +} + +/** + * gsmld_write - write function for tty + * @tty: tty device + * @file: file object + * @buf: userspace buffer pointer + * @nr: size of I/O + * + * Called when the owner of the device wants to send a frame + * itself (or some other control data). The data is transferred + * as-is and must be properly framed and checksummed as appropriate + * by userspace. Frames are either sent whole or not at all as this + * avoids pain user side. + */ + +static ssize_t gsmld_write(struct tty_struct *tty, struct file *file, + const unsigned char *buf, size_t nr) +{ + int space = tty_write_room(tty); + if (space >= nr) + return tty->ops->write(tty, buf, nr); + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); + return -ENOBUFS; +} + +/** + * gsmld_poll - poll method for N_GSM0710 + * @tty: terminal device + * @file: file accessing it + * @wait: poll table + * + * Called when the line discipline is asked to poll() for data or + * for special events. This code is not serialized with respect to + * other events save open/close. + * + * This code must be sure never to sleep through a hangup. + * Called without the kernel lock held - fine + */ + +static unsigned int gsmld_poll(struct tty_struct *tty, struct file *file, + poll_table *wait) +{ + unsigned int mask = 0; + struct gsm_mux *gsm = tty->disc_data; + + poll_wait(file, &tty->read_wait, wait); + poll_wait(file, &tty->write_wait, wait); + if (tty_hung_up_p(file)) + mask |= POLLHUP; + if (!tty_is_writelocked(tty) && tty_write_room(tty) > 0) + mask |= POLLOUT | POLLWRNORM; + if (gsm->dead) + mask |= POLLHUP; + return mask; +} + +static int gsmld_config(struct tty_struct *tty, struct gsm_mux *gsm, + struct gsm_config *c) +{ + int need_close = 0; + int need_restart = 0; + + /* Stuff we don't support yet - UI or I frame transport, windowing */ + if ((c->adaption !=1 && c->adaption != 2) || c->k) + return -EOPNOTSUPP; + /* Check the MRU/MTU range looks sane */ + if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8) + return -EINVAL; + if (c->n2 < 3) + return -EINVAL; + if (c->encapsulation > 1) /* Basic, advanced, no I */ + return -EINVAL; + if (c->initiator > 1) + return -EINVAL; + if (c->i == 0 || c->i > 2) /* UIH and UI only */ + return -EINVAL; + /* + * See what is needed for reconfiguration + */ + + /* Timing fields */ + if (c->t1 != 0 && c->t1 != gsm->t1) + need_restart = 1; + if (c->t2 != 0 && c->t2 != gsm->t2) + need_restart = 1; + if (c->encapsulation != gsm->encoding) + need_restart = 1; + if (c->adaption != gsm->adaption) + need_restart = 1; + /* Requires care */ + if (c->initiator != gsm->initiator) + need_close = 1; + if (c->mru != gsm->mru) + need_restart = 1; + if (c->mtu != gsm->mtu) + need_restart = 1; + + /* + * Close down what is needed, restart and initiate the new + * configuration + */ + + if (need_close || need_restart) { + gsm_dlci_begin_close(gsm->dlci[0]); + /* This will timeout if the link is down due to N2 expiring */ + wait_event_interruptible(gsm->event, + gsm->dlci[0]->state == DLCI_CLOSED); + if (signal_pending(current)) + return -EINTR; + } + if (need_restart) + gsm_cleanup_mux(gsm); + + gsm->initiator = c->initiator; + gsm->mru = c->mru; + gsm->encoding = c->encapsulation; + gsm->adaption = c->adaption; + + if (c->i == 1) + gsm->ftype = UIH; + else if (c->i == 2) + gsm->ftype = UI; + + if (c->t1) + gsm->t1 = c->t1; + if (c->t2) + gsm->t2 = c->t2; + + /* FIXME: We need to separate activation/deactivation from adding + and removing from the mux array */ + if (need_restart) + gsm_activate_mux(gsm); + if (gsm->initiator && need_close) + gsm_dlci_begin_open(gsm->dlci[0]); + return 0; +} + +static int gsmld_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct gsm_config c; + struct gsm_mux *gsm = tty->disc_data; + + switch (cmd) { + case GSMIOC_GETCONF: + memset(&c, 0, sizeof(c)); + c.adaption = gsm->adaption; + c.encapsulation = gsm->encoding; + c.initiator = gsm->initiator; + c.t1 = gsm->t1; + c.t2 = gsm->t2; + c.t3 = 0; /* Not supported */ + c.n2 = gsm->n2; + if (gsm->ftype == UIH) + c.i = 1; + else + c.i = 2; + printk("Ftype %d i %d\n", gsm->ftype, c.i); + c.mru = gsm->mru; + c.mtu = gsm->mtu; + c.k = 0; + if (copy_to_user((void *)arg, &c, sizeof(c))) + return -EFAULT; + return 0; + case GSMIOC_SETCONF: + if (copy_from_user(&c, (void *)arg, sizeof(c))) + return -EFAULT; + return gsmld_config(tty, gsm, &c); + default: + return n_tty_ioctl_helper(tty, file, cmd, arg); + } +} + + +/* Line discipline for real tty */ +struct tty_ldisc_ops tty_ldisc_packet = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = "n_gsm", + .open = gsmld_open, + .close = gsmld_close, + .flush_buffer = gsmld_flush_buffer, + .chars_in_buffer = gsmld_chars_in_buffer, + .read = gsmld_read, + .write = gsmld_write, + .ioctl = gsmld_ioctl, + .poll = gsmld_poll, + .receive_buf = gsmld_receive_buf, + .write_wakeup = gsmld_write_wakeup +}; + +/* + * Virtual tty side + */ + +#define TX_SIZE 512 + +static int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk) +{ + u8 modembits[5]; + struct gsm_control *ctrl; + int len = 2; + + if (brk) + len++; + + modembits[0] = len << 1 | EA; /* Data bytes */ + modembits[1] = dlci->addr << 2 | 3; /* DLCI, EA, 1 */ + modembits[2] = gsm_encode_modem(dlci) << 1 | EA; + if (brk) + modembits[3] = brk << 4 | 2 | EA; /* Valid, EA */ + ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len + 1); + if (ctrl == NULL) + return -ENOMEM; + return gsm_control_wait(dlci->gsm, ctrl); +} + +static int gsm_carrier_raised(struct tty_port *port) +{ + struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); + /* Not yet open so no carrier info */ + if (dlci->state != DLCI_OPEN) + return 0; + if (debug & 2) + return 1; + return dlci->modem_rx & TIOCM_CD; +} + +static void gsm_dtr_rts(struct tty_port *port, int onoff) +{ + struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port); + unsigned int modem_tx = dlci->modem_tx; + if (onoff) + modem_tx |= TIOCM_DTR | TIOCM_RTS; + else + modem_tx &= ~(TIOCM_DTR | TIOCM_RTS); + if (modem_tx != dlci->modem_tx) { + dlci->modem_tx = modem_tx; + gsmtty_modem_update(dlci, 0); + } +} + +static const struct tty_port_operations gsm_port_ops = { + .carrier_raised = gsm_carrier_raised, + .dtr_rts = gsm_dtr_rts, +}; + + +static int gsmtty_open(struct tty_struct *tty, struct file *filp) +{ + struct gsm_mux *gsm; + struct gsm_dlci *dlci; + struct tty_port *port; + unsigned int line = tty->index; + unsigned int mux = line >> 6; + + line = line & 0x3F; + + if (mux >= MAX_MUX) + return -ENXIO; + /* FIXME: we need to lock gsm_mux for lifetimes of ttys eventually */ + if (gsm_mux[mux] == NULL) + return -EUNATCH; + if (line == 0 || line > 61) /* 62/63 reserved */ + return -ECHRNG; + gsm = gsm_mux[mux]; + if (gsm->dead) + return -EL2HLT; + dlci = gsm->dlci[line]; + if (dlci == NULL) + dlci = gsm_dlci_alloc(gsm, line); + if (dlci == NULL) + return -ENOMEM; + port = &dlci->port; + port->count++; + tty->driver_data = dlci; + tty_port_tty_set(port, tty); + + dlci->modem_rx = 0; + /* We could in theory open and close before we wait - eg if we get + a DM straight back. This is ok as that will have caused a hangup */ + set_bit(ASYNCB_INITIALIZED, &port->flags); + /* Start sending off SABM messages */ + gsm_dlci_begin_open(dlci); + /* And wait for virtual carrier */ + return tty_port_block_til_ready(port, tty, filp); +} + +static void gsmtty_close(struct tty_struct *tty, struct file *filp) +{ + struct gsm_dlci *dlci = tty->driver_data; + if (dlci == NULL) + return; + if (tty_port_close_start(&dlci->port, tty, filp) == 0) + return; + gsm_dlci_begin_close(dlci); + tty_port_close_end(&dlci->port, tty); + tty_port_tty_set(&dlci->port, NULL); +} + +static void gsmtty_hangup(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + tty_port_hangup(&dlci->port); + gsm_dlci_begin_close(dlci); +} + +static int gsmtty_write(struct tty_struct *tty, const unsigned char *buf, + int len) +{ + struct gsm_dlci *dlci = tty->driver_data; + /* Stuff the bytes into the fifo queue */ + int sent = kfifo_in_locked(dlci->fifo, buf, len, &dlci->lock); + /* Need to kick the channel */ + gsm_dlci_data_kick(dlci); + return sent; +} + +static int gsmtty_write_room(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + return TX_SIZE - kfifo_len(dlci->fifo); +} + +static int gsmtty_chars_in_buffer(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + return kfifo_len(dlci->fifo); +} + +static void gsmtty_flush_buffer(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + /* Caution needed: If we implement reliable transport classes + then the data being transmitted can't simply be junked once + it has first hit the stack. Until then we can just blow it + away */ + kfifo_reset(dlci->fifo); + /* Need to unhook this DLCI from the transmit queue logic */ +} + +static void gsmtty_wait_until_sent(struct tty_struct *tty, int timeout) +{ + /* The FIFO handles the queue so the kernel will do the right + thing waiting on chars_in_buffer before calling us. No work + to do here */ +} + +static int gsmtty_tiocmget(struct tty_struct *tty, struct file *filp) +{ + struct gsm_dlci *dlci = tty->driver_data; + return dlci->modem_rx; +} + +static int gsmtty_tiocmset(struct tty_struct *tty, struct file *filp, + unsigned int set, unsigned int clear) +{ + struct gsm_dlci *dlci = tty->driver_data; + unsigned int modem_tx = dlci->modem_tx; + + modem_tx &= clear; + modem_tx |= set; + + if (modem_tx != dlci->modem_tx) { + dlci->modem_tx = modem_tx; + return gsmtty_modem_update(dlci, 0); + } + return 0; +} + + +static int gsmtty_ioctl(struct tty_struct *tty, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +static void gsmtty_set_termios(struct tty_struct *tty, struct ktermios *old) +{ + /* For the moment its fixed. In actual fact the speed information + for the virtual channel can be propogated in both directions by + the RPN control message. This however rapidly gets nasty as we + then have to remap modem signals each way according to whether + our virtual cable is null modem etc .. */ + tty_termios_copy_hw(tty->termios, old); +} + +static void gsmtty_throttle(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + if (tty->termios->c_cflag & CRTSCTS) + dlci->modem_tx &= ~TIOCM_DTR; + dlci->throttled = 1; + /* Send an MSC with DTR cleared */ + gsmtty_modem_update(dlci, 0); +} + +static void gsmtty_unthrottle(struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + if (tty->termios->c_cflag & CRTSCTS) + dlci->modem_tx |= TIOCM_DTR; + dlci->throttled = 0; + /* Send an MSC with DTR set */ + gsmtty_modem_update(dlci, 0); +} + +static int gsmtty_break_ctl(struct tty_struct *tty, int state) +{ + struct gsm_dlci *dlci = tty->driver_data; + int encode = 0; /* Off */ + + if (state == -1) /* "On indefinitely" - we can't encode this + properly */ + encode = 0x0F; + else if (state > 0) { + encode = state / 200; /* mS to encoding */ + if (encode > 0x0F) + encode = 0x0F; /* Best effort */ + } + return gsmtty_modem_update(dlci, encode); +} + +static struct tty_driver *gsm_tty_driver; + +/* Virtual ttys for the demux */ +static const struct tty_operations gsmtty_ops = { + .open = gsmtty_open, + .close = gsmtty_close, + .write = gsmtty_write, + .write_room = gsmtty_write_room, + .chars_in_buffer = gsmtty_chars_in_buffer, + .flush_buffer = gsmtty_flush_buffer, + .ioctl = gsmtty_ioctl, + .throttle = gsmtty_throttle, + .unthrottle = gsmtty_unthrottle, + .set_termios = gsmtty_set_termios, + .hangup = gsmtty_hangup, + .wait_until_sent = gsmtty_wait_until_sent, + .tiocmget = gsmtty_tiocmget, + .tiocmset = gsmtty_tiocmset, + .break_ctl = gsmtty_break_ctl, +}; + + + +static int __init gsm_init(void) +{ + /* Fill in our line protocol discipline, and register it */ + int status = tty_register_ldisc(N_GSM0710, &tty_ldisc_packet); + if (status != 0) { + printk(KERN_ERR "n_gsm: can't register line discipline (err = %d)\n", status); + return status; + } + + gsm_tty_driver = alloc_tty_driver(256); + if (!gsm_tty_driver) { + tty_unregister_ldisc(N_GSM0710); + printk(KERN_ERR "gsm_init: tty allocation failed.\n"); + return -EINVAL; + } + gsm_tty_driver->owner = THIS_MODULE; + gsm_tty_driver->driver_name = "gsmtty"; + gsm_tty_driver->name = "gsmtty"; + gsm_tty_driver->major = 0; /* Dynamic */ + gsm_tty_driver->minor_start = 0; + gsm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + gsm_tty_driver->subtype = SERIAL_TYPE_NORMAL; + gsm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV + | TTY_DRIVER_HARDWARE_BREAK; + gsm_tty_driver->init_termios = tty_std_termios; + /* Fixme */ + gsm_tty_driver->init_termios.c_lflag &= ~ECHO; + tty_set_operations(gsm_tty_driver, &gsmtty_ops); + + spin_lock_init(&gsm_mux_lock); + + if (tty_register_driver(gsm_tty_driver)) { + put_tty_driver(gsm_tty_driver); + tty_unregister_ldisc(N_GSM0710); + printk(KERN_ERR "gsm_init: tty registration failed.\n"); + return -EBUSY; + } + printk(KERN_INFO "gsm_init: loaded as %d,%d.\n", gsm_tty_driver->major, gsm_tty_driver->minor_start); + return 0; +} + +static void __exit gsm_exit(void) +{ + int status = tty_unregister_ldisc(N_GSM0710); + if (status != 0) + printk(KERN_ERR "n_gsm: can't unregister line discipline (err = %d)\n", status); + tty_unregister_driver(gsm_tty_driver); + put_tty_driver(gsm_tty_driver); + printk(KERN_INFO "gsm_init: unloaded.\n"); +} + +module_init(gsm_init); +module_exit(gsm_exit); + + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_LDISC(N_GSM0710); diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 78a62ebe75c..ecbe479c7d6 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -176,23 +176,6 @@ static void config_setup(struct cyclades_port *); static void show_status(int); #endif -#ifdef CONFIG_REMOTE_DEBUG -static void debug_setup(void); -void queueDebugChar(int c); -int getDebugChar(void); - -#define DEBUG_PORT 1 -#define DEBUG_LEN 256 - -typedef struct { - int in; - int out; - unsigned char buf[DEBUG_LEN]; -} debugq; - -debugq debugiq; -#endif - /* * I have my own version of udelay(), as it is needed when initialising * the chip, before the delay loop has been calibrated. Should probably @@ -515,11 +498,6 @@ static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id) /* determine the channel and change to that context */ channel = (u_short) (base_addr[CyLICR] >> 2); -#ifdef CONFIG_REMOTE_DEBUG - if (channel == DEBUG_PORT) { - panic("TxInt on debug port!!!"); - } -#endif /* validate the port number (as configured and open) */ if ((channel < 0) || (NR_PORTS <= channel)) { base_addr[CyIER] &= ~(CyTxMpty | CyTxRdy); @@ -634,14 +612,6 @@ static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id) info->last_active = jiffies; save_cnt = char_count = base_addr[CyRFOC]; -#ifdef CONFIG_REMOTE_DEBUG - if (channel == DEBUG_PORT) { - while (char_count--) { - data = base_addr[CyRDR]; - queueDebugChar(data); - } - } else -#endif /* if there is nowhere to put the data, discard it */ if (info->tty == 0) { while (char_count--) { @@ -2195,9 +2165,7 @@ static int __init serial167_init(void) port_num++; info++; } -#ifdef CONFIG_REMOTE_DEBUG - debug_setup(); -#endif + ret = request_irq(MVME167_IRQ_SER_ERR, cd2401_rxerr_interrupt, 0, "cd2401_errors", cd2401_rxerr_interrupt); if (ret) { @@ -2518,193 +2486,4 @@ static int __init serial167_console_init(void) console_initcall(serial167_console_init); -#ifdef CONFIG_REMOTE_DEBUG -void putDebugChar(int c) -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - unsigned long flags; - volatile u_char sink; - u_char ier; - int port; - - local_irq_save(flags); - - /* Ensure transmitter is enabled! */ - - port = DEBUG_PORT; - base_addr[CyCAR] = (u_char) port; - while (base_addr[CyCCR]) - ; - base_addr[CyCCR] = CyENB_XMTR; - - ier = base_addr[CyIER]; - base_addr[CyIER] = CyTxMpty; - - while (1) { - if (pcc2chip[PccSCCTICR] & 0x20) { - /* We have a Tx int. Acknowledge it */ - sink = pcc2chip[PccTPIACKR]; - if ((base_addr[CyLICR] >> 2) == port) { - base_addr[CyTDR] = c; - base_addr[CyTEOIR] = 0; - break; - } else - base_addr[CyTEOIR] = CyNOTRANS; - } - } - - base_addr[CyIER] = ier; - - local_irq_restore(flags); -} - -int getDebugChar() -{ - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - unsigned long flags; - volatile u_char sink; - u_char ier; - int port; - int i, c; - - i = debugiq.out; - if (i != debugiq.in) { - c = debugiq.buf[i]; - if (++i == DEBUG_LEN) - i = 0; - debugiq.out = i; - return c; - } - /* OK, nothing in queue, wait in poll loop */ - - local_irq_save(flags); - - /* Ensure receiver is enabled! */ - - port = DEBUG_PORT; - base_addr[CyCAR] = (u_char) port; -#if 0 - while (base_addr[CyCCR]) - ; - base_addr[CyCCR] = CyENB_RCVR; -#endif - ier = base_addr[CyIER]; - base_addr[CyIER] = CyRxData; - - while (1) { - if (pcc2chip[PccSCCRICR] & 0x20) { - /* We have a Rx int. Acknowledge it */ - sink = pcc2chip[PccRPIACKR]; - if ((base_addr[CyLICR] >> 2) == port) { - int cnt = base_addr[CyRFOC]; - while (cnt-- > 0) { - c = base_addr[CyRDR]; - if (c == 0) - printk - ("!! debug char is null (cnt=%d) !!", - cnt); - else - queueDebugChar(c); - } - base_addr[CyREOIR] = 0; - i = debugiq.out; - if (i == debugiq.in) - panic("Debug input queue empty!"); - c = debugiq.buf[i]; - if (++i == DEBUG_LEN) - i = 0; - debugiq.out = i; - break; - } else - base_addr[CyREOIR] = CyNOTRANS; - } - } - - base_addr[CyIER] = ier; - - local_irq_restore(flags); - - return (c); -} - -void queueDebugChar(int c) -{ - int i; - - i = debugiq.in; - debugiq.buf[i] = c; - if (++i == DEBUG_LEN) - i = 0; - if (i != debugiq.out) - debugiq.in = i; -} - -static void debug_setup() -{ - unsigned long flags; - volatile unsigned char *base_addr = (u_char *) BASE_ADDR; - int i, cflag; - - cflag = B19200; - - local_irq_save(flags); - - for (i = 0; i < 4; i++) { - base_addr[CyCAR] = i; - base_addr[CyLICR] = i << 2; - } - - debugiq.in = debugiq.out = 0; - - base_addr[CyCAR] = DEBUG_PORT; - - /* baud rate */ - i = cflag & CBAUD; - - base_addr[CyIER] = 0; - - base_addr[CyCMR] = CyASYNC; - base_addr[CyLICR] = DEBUG_PORT << 2; - base_addr[CyLIVR] = 0x5c; - - /* tx and rx baud rate */ - - base_addr[CyTCOR] = baud_co[i]; - base_addr[CyTBPR] = baud_bpr[i]; - base_addr[CyRCOR] = baud_co[i] >> 5; - base_addr[CyRBPR] = baud_bpr[i]; - - /* set line characteristics according configuration */ - - base_addr[CySCHR1] = 0; - base_addr[CySCHR2] = 0; - base_addr[CySCRL] = 0; - base_addr[CySCRH] = 0; - base_addr[CyCOR1] = Cy_8_BITS | CyPARITY_NONE; - base_addr[CyCOR2] = 0; - base_addr[CyCOR3] = Cy_1_STOP; - base_addr[CyCOR4] = baud_cor4[i]; - base_addr[CyCOR5] = 0; - base_addr[CyCOR6] = 0; - base_addr[CyCOR7] = 0; - - write_cy_cmd(base_addr, CyINIT_CHAN); - write_cy_cmd(base_addr, CyENB_RCVR); - - base_addr[CyCAR] = DEBUG_PORT; /* !!! Is this needed? */ - - base_addr[CyRTPRL] = 2; - base_addr[CyRTPRH] = 0; - - base_addr[CyMSVR1] = CyRTS; - base_addr[CyMSVR2] = CyDTR; - - base_addr[CyIER] = CyRxData; - - local_irq_restore(flags); - -} /* debug_setup */ - -#endif - MODULE_LICENSE("GPL"); diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c index 7ee52164d47..cc1e9850d65 100644 --- a/drivers/char/tty_buffer.c +++ b/drivers/char/tty_buffer.c @@ -238,7 +238,7 @@ EXPORT_SYMBOL_GPL(tty_buffer_request_room); * @size: size * * Queue a series of bytes to the tty buffering. All the characters - * passed are marked as without error. Returns the number added. + * passed are marked with the supplied flag. Returns the number added. * * Locking: Called functions may take tty->buf.lock */ diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index fb09bb3c0ad..aa9bc9e980e 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -149,7 +149,7 @@ static ssize_t smi_data_buf_size_store(struct device *dev, return count; } -static ssize_t smi_data_read(struct kobject *kobj, +static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { @@ -162,7 +162,7 @@ static ssize_t smi_data_read(struct kobject *kobj, return ret; } -static ssize_t smi_data_write(struct kobject *kobj, +static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c index 3a4460265b1..2f452f1f7c8 100644 --- a/drivers/firmware/dell_rbu.c +++ b/drivers/firmware/dell_rbu.c @@ -522,7 +522,7 @@ static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) rbu_data.image_update_buffer, rbu_data.bios_image_size); } -static ssize_t read_rbu_data(struct kobject *kobj, +static ssize_t read_rbu_data(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { @@ -576,7 +576,7 @@ static void callbackfn_rbu(const struct firmware *fw, void *context) release_firmware(fw); } -static ssize_t read_rbu_image_type(struct kobject *kobj, +static ssize_t read_rbu_image_type(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { @@ -586,7 +586,7 @@ static ssize_t read_rbu_image_type(struct kobject *kobj, return size; } -static ssize_t write_rbu_image_type(struct kobject *kobj, +static ssize_t write_rbu_image_type(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { @@ -647,7 +647,7 @@ static ssize_t write_rbu_image_type(struct kobject *kobj, return rc; } -static ssize_t read_rbu_packet_size(struct kobject *kobj, +static ssize_t read_rbu_packet_size(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { @@ -660,7 +660,7 @@ static ssize_t read_rbu_packet_size(struct kobject *kobj, return size; } -static ssize_t write_rbu_packet_size(struct kobject *kobj, +static ssize_t write_rbu_packet_size(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 81b70bd0758..2a62ec6390e 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -402,7 +402,7 @@ efivar_unregister(struct efivar_entry *var) } -static ssize_t efivar_create(struct kobject *kobj, +static ssize_t efivar_create(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { @@ -461,7 +461,7 @@ static ssize_t efivar_create(struct kobject *kobj, return count; } -static ssize_t efivar_delete(struct kobject *kobj, +static ssize_t efivar_delete(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index eb0c3fe44b2..cae1b8c5b08 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -399,7 +399,7 @@ static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, goto free_id; } - pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, "value"); + pdesc->value_sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value"); if (!pdesc->value_sd) { ret = -ENODEV; goto free_id; diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 25bbd30ed7a..387166d5a10 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -193,8 +193,9 @@ static ssize_t enabled_show(struct device *device, "disabled"); } -static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static ssize_t edid_show(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, + size_t count) { struct device *connector_dev = container_of(kobj, struct device, kobj); struct drm_connector *connector = to_drm_connector(connector_dev); diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 26ac8aad0b1..f084249295d 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -1678,9 +1678,9 @@ int bitmap_create(mddev_t *mddev) bitmap->mddev = mddev; - bm = sysfs_get_dirent(mddev->kobj.sd, "bitmap"); + bm = sysfs_get_dirent(mddev->kobj.sd, NULL, "bitmap"); if (bm) { - bitmap->sysfs_can_clear = sysfs_get_dirent(bm, "can_clear"); + bitmap->sysfs_can_clear = sysfs_get_dirent(bm, NULL, "can_clear"); sysfs_put(bm); } else bitmap->sysfs_can_clear = NULL; diff --git a/drivers/md/md.c b/drivers/md/md.c index cefd63daff3..a9fd491796a 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1766,7 +1766,7 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) kobject_del(&rdev->kobj); goto fail; } - rdev->sysfs_state = sysfs_get_dirent(rdev->kobj.sd, "state"); + rdev->sysfs_state = sysfs_get_dirent(rdev->kobj.sd, NULL, "state"); list_add_rcu(&rdev->same_set, &mddev->disks); bd_claim_by_disk(rdev->bdev, rdev->bdev->bd_holder, mddev->gendisk); @@ -4189,7 +4189,7 @@ static int md_alloc(dev_t dev, char *name) mutex_unlock(&disks_mutex); if (!error) { kobject_uevent(&mddev->kobj, KOBJ_ADD); - mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, "array_state"); + mddev->sysfs_state = sysfs_get_dirent(mddev->kobj.sd, NULL, "array_state"); } mddev_put(mddev); return error; @@ -4398,7 +4398,7 @@ static int do_md_run(mddev_t * mddev) printk(KERN_WARNING "md: cannot register extra attributes for %s\n", mdname(mddev)); - mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, "sync_action"); + mddev->sysfs_action = sysfs_get_dirent(mddev->kobj.sd, NULL, "sync_action"); } else if (mddev->ro == 2) /* auto-readonly not meaningful */ mddev->ro = 0; diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 5382b5a44af..a6a57011ba6 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -5064,7 +5064,7 @@ mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode) if (!timeleft) { printk(KERN_DEBUG "%s: Issuing Reset from %s!!\n", ioc->name, __func__); - mpt_HardResetHandler(ioc, CAN_SLEEP); + mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); mpt_free_msg_frame(ioc, mf); } goto out; @@ -6456,10 +6456,15 @@ out: issue_hard_reset = 0; printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n", ioc->name, __func__); - mpt_HardResetHandler(ioc, CAN_SLEEP); + if (retry_count == 0) { + if (mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP) != 0) + retry_count++; + } else + mpt_HardResetHandler(ioc, CAN_SLEEP); + mpt_free_msg_frame(ioc, mf); /* attempt one retry for a timed out command */ - if (!retry_count) { + if (retry_count < 2) { printk(MYIOC_s_INFO_FMT "Attempting Retry Config request" " type 0x%x, page 0x%x," @@ -6904,6 +6909,172 @@ mpt_halt_firmware(MPT_ADAPTER *ioc) } EXPORT_SYMBOL(mpt_halt_firmware); +/** + * mpt_SoftResetHandler - Issues a less expensive reset + * @ioc: Pointer to MPT_ADAPTER structure + * @sleepFlag: Indicates if sleep or schedule must be called. + + * + * Returns 0 for SUCCESS or -1 if FAILED. + * + * Message Unit Reset - instructs the IOC to reset the Reply Post and + * Free FIFO's. All the Message Frames on Reply Free FIFO are discarded. + * All posted buffers are freed, and event notification is turned off. + * IOC doesnt reply to any outstanding request. This will transfer IOC + * to READY state. + **/ +int +mpt_SoftResetHandler(MPT_ADAPTER *ioc, int sleepFlag) +{ + int rc; + int ii; + u8 cb_idx; + unsigned long flags; + u32 ioc_state; + unsigned long time_count; + + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "SoftResetHandler Entered!\n", + ioc->name)); + + ioc_state = mpt_GetIocState(ioc, 0) & MPI_IOC_STATE_MASK; + + if (mpt_fwfault_debug) + mpt_halt_firmware(ioc); + + if (ioc_state == MPI_IOC_STATE_FAULT || + ioc_state == MPI_IOC_STATE_RESET) { + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "skipping, either in FAULT or RESET state!\n", ioc->name)); + return -1; + } + + if (ioc->bus_type == FC) { + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "skipping, because the bus type is FC!\n", ioc->name)); + return -1; + } + + spin_lock_irqsave(&ioc->taskmgmt_lock, flags); + if (ioc->ioc_reset_in_progress) { + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + return -1; + } + ioc->ioc_reset_in_progress = 1; + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + + rc = -1; + + for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) { + if (MptResetHandlers[cb_idx]) + mpt_signal_reset(cb_idx, ioc, MPT_IOC_SETUP_RESET); + } + + spin_lock_irqsave(&ioc->taskmgmt_lock, flags); + if (ioc->taskmgmt_in_progress) { + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + return -1; + } + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + /* Disable reply interrupts (also blocks FreeQ) */ + CHIPREG_WRITE32(&ioc->chip->IntMask, 0xFFFFFFFF); + ioc->active = 0; + time_count = jiffies; + + rc = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag); + + for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) { + if (MptResetHandlers[cb_idx]) + mpt_signal_reset(cb_idx, ioc, MPT_IOC_PRE_RESET); + } + + if (rc) + goto out; + + ioc_state = mpt_GetIocState(ioc, 0) & MPI_IOC_STATE_MASK; + if (ioc_state != MPI_IOC_STATE_READY) + goto out; + + for (ii = 0; ii < 5; ii++) { + /* Get IOC facts! Allow 5 retries */ + rc = GetIocFacts(ioc, sleepFlag, + MPT_HOSTEVENT_IOC_RECOVER); + if (rc == 0) + break; + if (sleepFlag == CAN_SLEEP) + msleep(100); + else + mdelay(100); + } + if (ii == 5) + goto out; + + rc = PrimeIocFifos(ioc); + if (rc != 0) + goto out; + + rc = SendIocInit(ioc, sleepFlag); + if (rc != 0) + goto out; + + rc = SendEventNotification(ioc, 1, sleepFlag); + if (rc != 0) + goto out; + + if (ioc->hard_resets < -1) + ioc->hard_resets++; + + /* + * At this point, we know soft reset succeeded. + */ + + ioc->active = 1; + CHIPREG_WRITE32(&ioc->chip->IntMask, MPI_HIM_DIM); + + out: + spin_lock_irqsave(&ioc->taskmgmt_lock, flags); + ioc->ioc_reset_in_progress = 0; + ioc->taskmgmt_quiesce_io = 0; + ioc->taskmgmt_in_progress = 0; + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + + if (ioc->active) { /* otherwise, hard reset coming */ + for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) { + if (MptResetHandlers[cb_idx]) + mpt_signal_reset(cb_idx, ioc, + MPT_IOC_POST_RESET); + } + } + + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "SoftResetHandler: completed (%d seconds): %s\n", + ioc->name, jiffies_to_msecs(jiffies - time_count)/1000, + ((rc == 0) ? "SUCCESS" : "FAILED"))); + + return rc; +} + +/** + * mpt_Soft_Hard_ResetHandler - Try less expensive reset + * @ioc: Pointer to MPT_ADAPTER structure + * @sleepFlag: Indicates if sleep or schedule must be called. + + * + * Returns 0 for SUCCESS or -1 if FAILED. + * Try for softreset first, only if it fails go for expensive + * HardReset. + **/ +int +mpt_Soft_Hard_ResetHandler(MPT_ADAPTER *ioc, int sleepFlag) { + int ret = -1; + + ret = mpt_SoftResetHandler(ioc, sleepFlag); + if (ret == 0) + return ret; + ret = mpt_HardResetHandler(ioc, sleepFlag); + return ret; +} +EXPORT_SYMBOL(mpt_Soft_Hard_ResetHandler); + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * Reset Handling diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index 9718c8f2e95..b613eb3d470 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -76,8 +76,8 @@ #define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR #endif -#define MPT_LINUX_VERSION_COMMON "3.04.14" -#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.14" +#define MPT_LINUX_VERSION_COMMON "3.04.15" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.15" #define WHAT_MAGIC_STRING "@" "(" "#" ")" #define show_mptmod_ver(s,ver) \ @@ -940,6 +940,7 @@ extern int mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp); extern u32 mpt_GetIocState(MPT_ADAPTER *ioc, int cooked); extern void mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buf, int *size, int len, int showlan); extern int mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag); +extern int mpt_Soft_Hard_ResetHandler(MPT_ADAPTER *ioc, int sleepFlag); extern int mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *cfg); extern int mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size); extern void mpt_free_fw_memory(MPT_ADAPTER *ioc); diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c index caa8f568a41..f06b29193b4 100644 --- a/drivers/message/fusion/mptctl.c +++ b/drivers/message/fusion/mptctl.c @@ -128,7 +128,6 @@ static MptSge_t *kbuf_alloc_2_sgl(int bytes, u32 dir, int sge_offset, int *frags struct buflist **blp, dma_addr_t *sglbuf_dma, MPT_ADAPTER *ioc); static void kfree_sgl(MptSge_t *sgl, dma_addr_t sgl_dma, struct buflist *buflist, MPT_ADAPTER *ioc); -static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function); /* * Reset Handler cleanup function @@ -275,45 +274,6 @@ mptctl_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply) return 1; } -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* mptctl_timeout_expired - * - * Expecting an interrupt, however timed out. - * - */ -static void -mptctl_timeout_expired(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) -{ - unsigned long flags; - - dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": %s\n", - ioc->name, __func__)); - - if (mpt_fwfault_debug) - mpt_halt_firmware(ioc); - - spin_lock_irqsave(&ioc->taskmgmt_lock, flags); - if (ioc->ioc_reset_in_progress) { - spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); - CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) - mpt_free_msg_frame(ioc, mf); - return; - } - spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); - - - if (!mptctl_bus_reset(ioc, mf->u.hdr.Function)) - return; - - /* Issue a reset for this device. - * The IOC is not responding. - */ - dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling HardReset! \n", - ioc->name)); - CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) - mpt_HardResetHandler(ioc, CAN_SLEEP); - mpt_free_msg_frame(ioc, mf); -} static int mptctl_taskmgmt_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) @@ -343,12 +303,8 @@ mptctl_taskmgmt_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) return 0; } -/* mptctl_bus_reset - * - * Bus reset code. - * - */ -static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function) +static int +mptctl_do_taskmgmt(MPT_ADAPTER *ioc, u8 tm_type, u8 bus_id, u8 target_id) { MPT_FRAME_HDR *mf; SCSITaskMgmt_t *pScsiTm; @@ -359,13 +315,6 @@ static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function) unsigned long time_count; u16 iocstatus; - /* bus reset is only good for SCSI IO, RAID PASSTHRU */ - if (!(function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || - function == MPI_FUNCTION_SCSI_IO_REQUEST)) { - dtmprintk(ioc, printk(MYIOC_s_WARN_FMT - "TaskMgmt, not SCSI_IO!!\n", ioc->name)); - return -EPERM; - } mutex_lock(&ioc->taskmgmt_cmds.mutex); if (mpt_set_taskmgmt_in_progress_flag(ioc) != 0) { @@ -375,15 +324,14 @@ static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function) retval = 0; - /* Send request - */ mf = mpt_get_msg_frame(mptctl_taskmgmt_id, ioc); if (mf == NULL) { - dtmprintk(ioc, printk(MYIOC_s_WARN_FMT - "TaskMgmt, no msg frames!!\n", ioc->name)); + dtmprintk(ioc, + printk(MYIOC_s_WARN_FMT "TaskMgmt, no msg frames!!\n", + ioc->name)); mpt_clear_taskmgmt_in_progress_flag(ioc); retval = -ENOMEM; - goto mptctl_bus_reset_done; + goto tm_done; } dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt request (mf=%p)\n", @@ -392,10 +340,13 @@ static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function) pScsiTm = (SCSITaskMgmt_t *) mf; memset(pScsiTm, 0, sizeof(SCSITaskMgmt_t)); pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; - pScsiTm->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS; - pScsiTm->MsgFlags = MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION; - pScsiTm->TargetID = 0; - pScsiTm->Bus = 0; + pScsiTm->TaskType = tm_type; + if ((tm_type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && + (ioc->bus_type == FC)) + pScsiTm->MsgFlags = + MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION; + pScsiTm->TargetID = target_id; + pScsiTm->Bus = bus_id; pScsiTm->ChainOffset = 0; pScsiTm->Reserved = 0; pScsiTm->Reserved1 = 0; @@ -413,17 +364,16 @@ static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function) timeout = 30; break; case SPI: - default: - timeout = 2; + default: + timeout = 10; break; } - dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT - "TaskMgmt type=%d timeout=%ld\n", - ioc->name, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, timeout)); + dtmprintk(ioc, + printk(MYIOC_s_DEBUG_FMT "TaskMgmt type=%d timeout=%ld\n", + ioc->name, tm_type, timeout)); INITIALIZE_MGMT_STATUS(ioc->taskmgmt_cmds.status) - CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status) time_count = jiffies; if ((ioc->facts.IOCCapabilities & MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q) && (ioc->facts.MsgVersion >= MPI_VERSION_01_05)) @@ -432,17 +382,20 @@ static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function) retval = mpt_send_handshake_request(mptctl_taskmgmt_id, ioc, sizeof(SCSITaskMgmt_t), (u32 *)pScsiTm, CAN_SLEEP); if (retval != 0) { - dfailprintk(ioc, printk(MYIOC_s_ERR_FMT + dfailprintk(ioc, + printk(MYIOC_s_ERR_FMT "TaskMgmt send_handshake FAILED!" " (ioc %p, mf %p, rc=%d) \n", ioc->name, ioc, mf, retval)); + mpt_free_msg_frame(ioc, mf); mpt_clear_taskmgmt_in_progress_flag(ioc); - goto mptctl_bus_reset_done; + goto tm_done; } } /* Now wait for the command to complete */ ii = wait_for_completion_timeout(&ioc->taskmgmt_cmds.done, timeout*HZ); + if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt failed\n", ioc->name)); @@ -452,14 +405,14 @@ static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function) retval = 0; else retval = -1; /* return failure */ - goto mptctl_bus_reset_done; + goto tm_done; } if (!(ioc->taskmgmt_cmds.status & MPT_MGMT_STATUS_RF_VALID)) { dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt failed\n", ioc->name)); retval = -1; /* return failure */ - goto mptctl_bus_reset_done; + goto tm_done; } pScsiTmReply = (SCSITaskMgmtReply_t *) ioc->taskmgmt_cmds.reply; @@ -467,7 +420,7 @@ static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function) "TaskMgmt fw_channel = %d, fw_id = %d, task_type=0x%02X, " "iocstatus=0x%04X\n\tloginfo=0x%08X, response_code=0x%02X, " "term_cmnds=%d\n", ioc->name, pScsiTmReply->Bus, - pScsiTmReply->TargetID, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, + pScsiTmReply->TargetID, tm_type, le16_to_cpu(pScsiTmReply->IOCStatus), le32_to_cpu(pScsiTmReply->IOCLogInfo), pScsiTmReply->ResponseCode, @@ -485,13 +438,71 @@ static int mptctl_bus_reset(MPT_ADAPTER *ioc, u8 function) retval = -1; /* return failure */ } - - mptctl_bus_reset_done: + tm_done: mutex_unlock(&ioc->taskmgmt_cmds.mutex); CLEAR_MGMT_STATUS(ioc->taskmgmt_cmds.status) return retval; } +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* mptctl_timeout_expired + * + * Expecting an interrupt, however timed out. + * + */ +static void +mptctl_timeout_expired(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) +{ + unsigned long flags; + int ret_val = -1; + SCSIIORequest_t *scsi_req = (SCSIIORequest_t *) mf; + u8 function = mf->u.hdr.Function; + + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": %s\n", + ioc->name, __func__)); + + if (mpt_fwfault_debug) + mpt_halt_firmware(ioc); + + spin_lock_irqsave(&ioc->taskmgmt_lock, flags); + if (ioc->ioc_reset_in_progress) { + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) + mpt_free_msg_frame(ioc, mf); + return; + } + spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags); + + + CLEAR_MGMT_PENDING_STATUS(ioc->ioctl_cmds.status) + + if (ioc->bus_type == SAS) { + if (function == MPI_FUNCTION_SCSI_IO_REQUEST) + ret_val = mptctl_do_taskmgmt(ioc, + MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, + scsi_req->Bus, scsi_req->TargetID); + else if (function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) + ret_val = mptctl_do_taskmgmt(ioc, + MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, + scsi_req->Bus, 0); + if (!ret_val) + return; + } else { + if ((function == MPI_FUNCTION_SCSI_IO_REQUEST) || + (function == MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) + ret_val = mptctl_do_taskmgmt(ioc, + MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, + scsi_req->Bus, 0); + if (!ret_val) + return; + } + + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Calling Reset! \n", + ioc->name)); + mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); + mpt_free_msg_frame(ioc, mf); +} + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* mptctl_ioc_reset @@ -1318,6 +1329,8 @@ mptctl_getiocinfo (unsigned long arg, unsigned int data_size) if (ioc->sh) { shost_for_each_device(sdev, ioc->sh) { vdevice = sdev->hostdata; + if (vdevice == NULL || vdevice->vtarget == NULL) + continue; if (vdevice->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT) continue; @@ -1439,6 +1452,8 @@ mptctl_gettargetinfo (unsigned long arg) if (!maxWordsLeft) continue; vdevice = sdev->hostdata; + if (vdevice == NULL || vdevice->vtarget == NULL) + continue; if (vdevice->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT) continue; @@ -1967,6 +1982,9 @@ mptctl_do_mpt_command (struct mpt_ioctl_command karg, void __user *mfPtr) struct scsi_target *starget = scsi_target(sdev); VirtTarget *vtarget = starget->hostdata; + if (vtarget == NULL) + continue; + if ((pScsiReq->TargetID == vtarget->id) && (pScsiReq->Bus == vtarget->channel) && (vtarget->tflags & MPT_TARGET_FLAGS_Q_YES)) @@ -2991,6 +3009,14 @@ static int __init mptctl_init(void) } mptctl_taskmgmt_id = mpt_register(mptctl_taskmgmt_reply, MPTCTL_DRIVER); + if (!mptctl_taskmgmt_id || mptctl_taskmgmt_id >= MPT_MAX_PROTOCOL_DRIVERS) { + printk(KERN_ERR MYNAM ": ERROR: Failed to register with Fusion MPT base driver\n"); + mpt_deregister(mptctl_id); + misc_deregister(&mptctl_miscdev); + err = -EBUSY; + goto out_fail; + } + mpt_reset_register(mptctl_id, mptctl_ioc_reset); mpt_event_register(mptctl_id, mptctl_event_process); @@ -3010,12 +3036,15 @@ static void mptctl_exit(void) printk(KERN_INFO MYNAM ": Deregistered /dev/%s @ (major,minor=%d,%d)\n", mptctl_miscdev.name, MISC_MAJOR, mptctl_miscdev.minor); + /* De-register event handler from base module */ + mpt_event_deregister(mptctl_id); + /* De-register reset handler from base module */ mpt_reset_deregister(mptctl_id); /* De-register callback handler from base module */ + mpt_deregister(mptctl_taskmgmt_id); mpt_deregister(mptctl_id); - mpt_reset_deregister(mptctl_taskmgmt_id); mpt_device_driver_deregister(MPTCTL_DRIVER); diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index 33f7256055b..b5f03ad8156 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -482,6 +482,7 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0) if (vtarget) { vtarget->id = pg0->CurrentTargetID; vtarget->channel = pg0->CurrentBus; + vtarget->deleted = 0; } } *((struct mptfc_rport_info **)rport->dd_data) = ri; @@ -1092,6 +1093,8 @@ mptfc_setup_reset(struct work_struct *work) container_of(work, MPT_ADAPTER, fc_setup_reset_work); u64 pn; struct mptfc_rport_info *ri; + struct scsi_target *starget; + VirtTarget *vtarget; /* reset about to happen, delete (block) all rports */ list_for_each_entry(ri, &ioc->fc_rports, list) { @@ -1099,6 +1102,12 @@ mptfc_setup_reset(struct work_struct *work) ri->flags &= ~MPT_RPORT_INFO_FLAGS_REGISTERED; fc_remote_port_delete(ri->rport); /* won't sleep */ ri->rport = NULL; + starget = ri->starget; + if (starget) { + vtarget = starget->hostdata; + if (vtarget) + vtarget->deleted = 1; + } pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; @@ -1119,6 +1128,8 @@ mptfc_rescan_devices(struct work_struct *work) int ii; u64 pn; struct mptfc_rport_info *ri; + struct scsi_target *starget; + VirtTarget *vtarget; /* start by tagging all ports as missing */ list_for_each_entry(ri, &ioc->fc_rports, list) { @@ -1146,6 +1157,12 @@ mptfc_rescan_devices(struct work_struct *work) MPT_RPORT_INFO_FLAGS_MISSING); fc_remote_port_delete(ri->rport); /* won't sleep */ ri->rport = NULL; + starget = ri->starget; + if (starget) { + vtarget = starget->hostdata; + if (vtarget) + vtarget->deleted = 1; + } pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; @@ -1358,6 +1375,9 @@ mptfc_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) unsigned long flags; int rc=1; + if (ioc->bus_type != FC) + return 0; + devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n", ioc->name, event)); @@ -1396,7 +1416,7 @@ mptfc_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) unsigned long flags; rc = mptscsih_ioc_reset(ioc,reset_phase); - if (rc == 0) + if ((ioc->bus_type != FC) || (!rc)) return rc; diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 76687126b57..ac000e83db0 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -1894,7 +1894,7 @@ static struct scsi_host_template mptsas_driver_template = { .module = THIS_MODULE, .proc_name = "mptsas", .proc_info = mptscsih_proc_info, - .name = "MPT SPI Host", + .name = "MPT SAS Host", .info = mptscsih_info, .queuecommand = mptsas_qcmd, .target_alloc = mptsas_target_alloc, @@ -2038,11 +2038,13 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset) timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ); - if (!timeleft) { - /* On timeout reset the board */ + if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { + error = -ETIME; mpt_free_msg_frame(ioc, mf); - mpt_HardResetHandler(ioc, CAN_SLEEP); - error = -ETIMEDOUT; + if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET) + goto out_unlock; + if (!timeleft) + mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); goto out_unlock; } @@ -2223,11 +2225,14 @@ static int mptsas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf); timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done, 10 * HZ); - if (!timeleft) { - printk(MYIOC_s_ERR_FMT "%s: smp timeout!\n", ioc->name, __func__); - /* On timeout reset the board */ - mpt_HardResetHandler(ioc, CAN_SLEEP); - ret = -ETIMEDOUT; + if (!(ioc->sas_mgmt.status & MPT_MGMT_STATUS_COMMAND_GOOD)) { + ret = -ETIME; + mpt_free_msg_frame(ioc, mf); + mf = NULL; + if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET) + goto unmap; + if (!timeleft) + mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); goto unmap; } mf = NULL; @@ -2518,6 +2523,12 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; error = mpt_config(ioc, &cfg); + + if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) { + error = -ENODEV; + goto out_free_consistent; + } + if (error) goto out_free_consistent; @@ -2594,14 +2605,14 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info, cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; error = mpt_config(ioc, &cfg); - if (error) - goto out_free_consistent; - - if (!buffer->NumPhys) { + if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) { error = -ENODEV; goto out_free_consistent; } + if (error) + goto out_free_consistent; + /* save config data */ port_info->num_phys = (buffer->NumPhys) ? buffer->NumPhys : 1; port_info->phy_info = kcalloc(port_info->num_phys, @@ -2677,7 +2688,7 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, if (error == MPI_IOCSTATUS_CONFIG_INVALID_PAGE) { error = -ENODEV; - goto out; + goto out_free_consistent; } if (error) @@ -2833,7 +2844,7 @@ mptsas_exp_repmanufacture_info(MPT_ADAPTER *ioc, if (ioc->sas_mgmt.status & MPT_MGMT_STATUS_DID_IOCRESET) goto out_free; if (!timeleft) - mpt_HardResetHandler(ioc, CAN_SLEEP); + mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); goto out_free; } @@ -4098,6 +4109,7 @@ mptsas_adding_inactive_raid_components(MPT_ADAPTER *ioc, u8 channel, u8 id) cfg.pageAddr = (channel << 8) + id; cfg.cfghdr.hdr = &hdr; cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT; if (mpt_config(ioc, &cfg) != 0) goto out; @@ -4717,7 +4729,7 @@ mptsas_broadcast_primative_work(struct fw_event_work *fw_event) if (issue_reset) { printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n", ioc->name, __func__); - mpt_HardResetHandler(ioc, CAN_SLEEP); + mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); } mptsas_free_fw_event(ioc, fw_event); } @@ -4779,6 +4791,9 @@ mptsas_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *reply) struct fw_event_work *fw_event; unsigned long delay; + if (ioc->bus_type != SAS) + return 0; + /* events turned off due to host reset or driver unloading */ if (ioc->fw_events_off) return 0; @@ -5073,6 +5088,12 @@ static void __devexit mptsas_remove(struct pci_dev *pdev) struct mptsas_portinfo *p, *n; int i; + if (!ioc->sh) { + printk(MYIOC_s_INFO_FMT "IOC is in Target mode\n", ioc->name); + mpt_detach(pdev); + return; + } + mptsas_shutdown(pdev); mptsas_del_device_components(ioc); diff --git a/drivers/message/fusion/mptsas.h b/drivers/message/fusion/mptsas.h index 953c2bfcf6a..7b249edbda7 100644 --- a/drivers/message/fusion/mptsas.h +++ b/drivers/message/fusion/mptsas.h @@ -110,7 +110,7 @@ struct fw_event_work { MPT_ADAPTER *ioc; u32 event; u8 retries; - u8 event_data[1]; + u8 __attribute__((aligned(4))) event_data[1]; }; struct mptsas_discovery_event { diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index 6796597dcee..7bd4c0fc23c 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -1149,11 +1149,6 @@ mptscsih_remove(struct pci_dev *pdev) MPT_SCSI_HOST *hd; int sz1; - if(!host) { - mpt_detach(pdev); - return; - } - scsi_remove_host(host); if((hd = shost_priv(host)) == NULL) @@ -1711,7 +1706,7 @@ mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, if (issue_hard_reset) { printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n", ioc->name, __func__); - retval = mpt_HardResetHandler(ioc, CAN_SLEEP); + retval = mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); mpt_free_msg_frame(ioc, mf); } @@ -1728,6 +1723,7 @@ mptscsih_get_tm_timeout(MPT_ADAPTER *ioc) case FC: return 40; case SAS: + return 30; case SPI: default: return 10; @@ -1777,7 +1773,7 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) ioc->name, SCpnt)); SCpnt->result = DID_NO_CONNECT << 16; SCpnt->scsi_done(SCpnt); - retval = 0; + retval = SUCCESS; goto out; } @@ -1792,6 +1788,17 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) goto out; } + /* Task aborts are not supported for volumes. + */ + if (vdevice->vtarget->raidVolume) { + dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT + "task abort: raid volume (sc=%p)\n", + ioc->name, SCpnt)); + SCpnt->result = DID_RESET << 16; + retval = FAILED; + goto out; + } + /* Find this command */ if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(ioc, SCpnt)) < 0) { @@ -1991,7 +1998,7 @@ mptscsih_host_reset(struct scsi_cmnd *SCpnt) /* If our attempts to reset the host failed, then return a failed * status. The host will be taken off line by the SCSI mid-layer. */ - retval = mpt_HardResetHandler(ioc, CAN_SLEEP); + retval = mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); if (retval < 0) status = FAILED; else @@ -2344,6 +2351,8 @@ mptscsih_slave_destroy(struct scsi_device *sdev) starget = scsi_target(sdev); vtarget = starget->hostdata; vdevice = sdev->hostdata; + if (!vdevice) + return; mptscsih_search_running_cmds(hd, vdevice); vtarget->num_luns--; @@ -3040,7 +3049,7 @@ mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io) if (!timeleft) { printk(MYIOC_s_WARN_FMT "Issuing Reset from %s!!\n", ioc->name, __func__); - mpt_HardResetHandler(ioc, CAN_SLEEP); + mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); mpt_free_msg_frame(ioc, mf); } goto out; diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c index e44365193fd..1abaa5d01ae 100644 --- a/drivers/message/fusion/mptspi.c +++ b/drivers/message/fusion/mptspi.c @@ -210,6 +210,10 @@ mptspi_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtTarget *target, target->maxOffset = offset; target->maxWidth = width; + spi_min_period(scsi_target(sdev)) = factor; + spi_max_offset(scsi_target(sdev)) = offset; + spi_max_width(scsi_target(sdev)) = width; + target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO; /* Disable unused features. @@ -558,6 +562,7 @@ static int mptspi_read_spi_device_pg0(struct scsi_target *starget, cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; cfg.dir = 0; cfg.pageAddr = starget->id; + cfg.timeout = 60; if (mpt_config(ioc, &cfg)) { starget_printk(KERN_ERR, starget, MYIOC_s_FMT "mpt_config failed\n", ioc->name); @@ -1152,6 +1157,9 @@ mptspi_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) u8 event = le32_to_cpu(pEvReply->Event) & 0xFF; struct _MPT_SCSI_HOST *hd = shost_priv(ioc->sh); + if (ioc->bus_type != SPI) + return 0; + if (hd && event == MPI_EVENT_INTEGRATED_RAID) { int reason = (le32_to_cpu(pEvReply->Data[0]) & 0x00FF0000) >> 16; @@ -1283,6 +1291,8 @@ mptspi_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) int rc; rc = mptscsih_ioc_reset(ioc, reset_phase); + if ((ioc->bus_type != SPI) || (!rc)) + return rc; /* only try to do a renegotiation if we're properly set up * if we get an ioc fault on bringup, ioc->sh will be NULL */ diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c index ed090e77c9c..19fc7c1cb42 100644 --- a/drivers/misc/c2port/core.c +++ b/drivers/misc/c2port/core.c @@ -707,7 +707,7 @@ static ssize_t __c2port_read_flash_data(struct c2port_device *dev, return nread; } -static ssize_t c2port_read_flash_data(struct kobject *kobj, +static ssize_t c2port_read_flash_data(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buffer, loff_t offset, size_t count) { @@ -824,7 +824,7 @@ static ssize_t __c2port_write_flash_data(struct c2port_device *dev, return nwrite; } -static ssize_t c2port_write_flash_data(struct kobject *kobj, +static ssize_t c2port_write_flash_data(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buffer, loff_t offset, size_t count) { diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index 9197cfc5501..a513f0aa643 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -140,7 +140,8 @@ static const struct attribute_group ds1682_group = { /* * User data attribute */ -static ssize_t ds1682_eeprom_read(struct kobject *kobj, struct bin_attribute *attr, +static ssize_t ds1682_eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client = kobj_to_i2c_client(kobj); @@ -163,7 +164,8 @@ static ssize_t ds1682_eeprom_read(struct kobject *kobj, struct bin_attribute *at return count; } -static ssize_t ds1682_eeprom_write(struct kobject *kobj, struct bin_attribute *attr, +static ssize_t ds1682_eeprom_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client = kobj_to_i2c_client(kobj); diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 0c11e411d8e..f7ca3a42b49 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -299,7 +299,8 @@ static ssize_t at24_read(struct at24_data *at24, return retval; } -static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr, +static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct at24_data *at24; @@ -420,7 +421,8 @@ static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off, return retval; } -static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr, +static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct at24_data *at24; diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index d194212a41f..c627e4174cc 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -126,7 +126,8 @@ at25_ee_read( } static ssize_t -at25_bin_read(struct kobject *kobj, struct bin_attribute *bin_attr, +at25_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev; @@ -253,7 +254,8 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off, } static ssize_t -at25_bin_write(struct kobject *kobj, struct bin_attribute *bin_attr, +at25_bin_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev; diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index e306a8cd2f9..45060ddc4e5 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -81,7 +81,8 @@ exit: mutex_unlock(&data->update_lock); } -static ssize_t eeprom_read(struct kobject *kobj, struct bin_attribute *bin_attr, +static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index fe290927850..5653a3ce051 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -107,7 +107,7 @@ exit_up: mutex_unlock(&data->update_lock); } -static ssize_t max6875_read(struct kobject *kobj, +static ssize_t max6875_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index ecf90f5c97c..f8210bf2d24 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -304,6 +304,19 @@ config SSFDC This enables read only access to SmartMedia formatted NAND flash. You can mount it with FAT file system. + +config SM_FTL + tristate "SmartMedia/xD new translation layer" + depends on EXPERIMENTAL && BLOCK + select MTD_BLKDEVS + select MTD_NAND_ECC + help + This enables new and very EXPERMENTAL support for SmartMedia/xD + FTL (Flash translation layer). + Write support isn't yet well tested, therefore this code IS likely to + eat your card, so please don't use it together with valuable data. + Use readonly driver (CONFIG_SSFDC) instead. + config MTD_OOPS tristate "Log panic/oops to an MTD buffer" depends on MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4521b1ecce4..760abc53339 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_NFTL) += nftl.o obj-$(CONFIG_INFTL) += inftl.o obj-$(CONFIG_RFD_FTL) += rfd_ftl.o obj-$(CONFIG_SSFDC) += ssfdc.o +obj-$(CONFIG_SM_FTL) += sm_ftl.o obj-$(CONFIG_MTD_OOPS) += mtdoops.o nftl-objs := nftlcore.o nftlmount.o diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 5fbf29e1e64..62f3ea9de84 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -615,10 +615,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) return mtd; setup_err: - if(mtd) { - kfree(mtd->eraseregions); - kfree(mtd); - } + kfree(mtd->eraseregions); + kfree(mtd); kfree(cfi->cmdset_priv); return NULL; } @@ -727,8 +725,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, /* those should be reset too since they create memory references. */ init_waitqueue_head(&chip->wq); - spin_lock_init(&chip->_spinlock); - chip->mutex = &chip->_spinlock; + mutex_init(&chip->mutex); chip++; } } @@ -774,9 +771,9 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS)) break; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Someone else might have been playing with it. */ return -EAGAIN; } @@ -823,9 +820,9 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long return -EIO; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. So we can just loop here. */ } @@ -852,10 +849,10 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); return -EAGAIN; } } @@ -901,20 +898,20 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr * it'll happily send us to sleep. In any case, when * get_chip returns success we're clear to go ahead. */ - ret = spin_trylock(contender->mutex); + ret = mutex_trylock(&contender->mutex); spin_unlock(&shared->lock); if (!ret) goto retry; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); ret = chip_ready(map, contender, contender->start, mode); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (ret == -EAGAIN) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } if (ret) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); return ret; } spin_lock(&shared->lock); @@ -923,10 +920,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr * in FL_SYNCING state. Put contender and retry. */ if (chip->state == FL_SYNCING) { put_chip(map, contender, contender->start); - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); } /* Check if we already have suspended erase @@ -936,10 +933,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr spin_unlock(&shared->lock); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); goto retry; } @@ -969,12 +966,12 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad if (shared->writing && shared->writing != chip) { /* give back ownership to who we loaned it from */ struct flchip *loaner = shared->writing; - spin_lock(loaner->mutex); + mutex_lock(&loaner->mutex); spin_unlock(&shared->lock); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); put_chip(map, loaner, loaner->start); - spin_lock(chip->mutex); - spin_unlock(loaner->mutex); + mutex_lock(&chip->mutex); + mutex_unlock(&loaner->mutex); wake_up(&chip->wq); return; } @@ -1144,7 +1141,7 @@ static int __xipram xip_wait_for_operation( (void) map_read(map, adr); xip_iprefetch(); local_irq_enable(); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); xip_iprefetch(); cond_resched(); @@ -1154,15 +1151,15 @@ static int __xipram xip_wait_for_operation( * a suspended erase state. If so let's wait * until it's done. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != newstate) { DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } /* Disallow XIP again */ local_irq_disable(); @@ -1218,10 +1215,10 @@ static int inval_cache_and_wait_for_operation( int chip_state = chip->state; unsigned int timeo, sleep_time, reset_timeo; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (inval_len) INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); timeo = chip_op_time_max; if (!timeo) @@ -1241,7 +1238,7 @@ static int inval_cache_and_wait_for_operation( } /* OK Still waiting. Drop the lock, wait a while and retry. */ - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (sleep_time >= 1000000/HZ) { /* * Half of the normal delay still remaining @@ -1256,17 +1253,17 @@ static int inval_cache_and_wait_for_operation( cond_resched(); timeo--; } - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != chip_state) { /* Someone's suspended the operation: sleep */ DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } if (chip->erase_suspended && chip_state == FL_ERASING) { /* Erase suspend occured while sleep: reset timeout */ @@ -1302,7 +1299,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(map_bankwidth(map)-1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_POINT); @@ -1313,7 +1310,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a chip->state = FL_POINT; chip->ref_point_counter++; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1398,7 +1395,7 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len) else thislen = len; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_POINT) { chip->ref_point_counter--; if(chip->ref_point_counter == 0) @@ -1407,7 +1404,7 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len) printk(KERN_ERR "%s: Warning: unpoint called on non pointed region\n", map->name); /* Should this give an error? */ put_chip(map, chip, chip->start); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); len -= thislen; ofs = 0; @@ -1426,10 +1423,10 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(map_bankwidth(map)-1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_READY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1443,7 +1440,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof put_chip(map, chip, cmd_addr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -1506,10 +1503,10 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, return -EINVAL; } - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, mode); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1555,7 +1552,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, xip_enable(map, chip, adr); out: put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1664,10 +1661,10 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, /* Let's determine this according to the interleave only once */ write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1798,7 +1795,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, xip_enable(map, chip, cmd_adr); out: put_chip(map, chip, cmd_adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1877,10 +1874,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, adr += chip->start; retry: - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_ERASING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1936,7 +1933,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, } else if (chipstatus & 0x20 && retries--) { printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus); put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); goto retry; } else { printk(KERN_ERR "%s: block erase failed at 0x%08lx (status 0x%lx)\n", map->name, adr, chipstatus); @@ -1948,7 +1945,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, xip_enable(map, chip, adr); out: put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1981,7 +1978,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd) for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, chip->start, FL_SYNCING); if (!ret) { @@ -1992,7 +1989,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd) * with the chip now anyway. */ } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -2000,14 +1997,14 @@ static void cfi_intelext_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; chip->oldstate = FL_READY; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -2053,10 +2050,10 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip adr += chip->start; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_LOCKING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -2090,7 +2087,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip xip_enable(map, chip, adr); out: put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -2155,10 +2152,10 @@ do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, struct cfi_private *cfi = map->fldrv_priv; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -2177,7 +2174,7 @@ do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, INVALIDATE_CACHED_RANGE(map, chip->start + offset, size); put_chip(map, chip, chip->start); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -2452,7 +2449,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); switch (chip->state) { case FL_READY: @@ -2484,7 +2481,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) case FL_PM_SUSPENDED: break; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -2493,7 +2490,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { /* No need to force it into a known state here, @@ -2503,7 +2500,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) chip->oldstate = FL_READY; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -2544,7 +2541,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { @@ -2553,7 +2550,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } if ((mtd->flags & MTD_POWERUP_LOCK) @@ -2573,14 +2570,14 @@ static int cfi_intelext_reset(struct mtd_info *mtd) /* force the completion of any ongoing operation and switch to array mode so any bootloader in flash is accessible for soft reboot. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); if (!ret) { map_write(map, CMD(0xff), chip->start); chip->state = FL_SHUTDOWN; put_chip(map, chip, chip->start); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } return 0; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index f3600e8d538..d81079ef91a 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -32,6 +32,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/reboot.h> #include <linux/mtd/compatmac.h> #include <linux/mtd/map.h> #include <linux/mtd/mtd.h> @@ -43,10 +44,6 @@ #define MAX_WORD_RETRIES 3 -#define MANUFACTURER_AMD 0x0001 -#define MANUFACTURER_ATMEL 0x001F -#define MANUFACTURER_MACRONIX 0x00C2 -#define MANUFACTURER_SST 0x00BF #define SST49LF004B 0x0060 #define SST49LF040B 0x0050 #define SST49LF008A 0x005a @@ -60,6 +57,7 @@ static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); static void cfi_amdstd_sync (struct mtd_info *); static int cfi_amdstd_suspend (struct mtd_info *); static void cfi_amdstd_resume (struct mtd_info *); +static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *); static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static void cfi_amdstd_destroy(struct mtd_info *); @@ -168,7 +166,7 @@ static void fixup_amd_bootblock(struct mtd_info *mtd, void* param) * This reduces the risk of false detection due to * the 8-bit device ID. */ - (cfi->mfr == MANUFACTURER_MACRONIX)) { + (cfi->mfr == CFI_MFR_MACRONIX)) { DEBUG(MTD_DEBUG_LEVEL1, "%s: Macronix MX29LV400C with bottom boot block" " detected\n", map->name); @@ -260,6 +258,42 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param) mtd->flags |= MTD_POWERUP_LOCK; } +static void fixup_old_sst_eraseregion(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + /* + * These flashes report two seperate eraseblock regions based on the + * sector_erase-size and block_erase-size, although they both operate on the + * same memory. This is not allowed according to CFI, so we just pick the + * sector_erase-size. + */ + cfi->cfiq->NumEraseRegions = 1; +} + +static void fixup_sst39vf(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + fixup_old_sst_eraseregion(mtd); + + cfi->addr_unlock1 = 0x5555; + cfi->addr_unlock2 = 0x2AAA; +} + +static void fixup_sst39vf_rev_b(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + fixup_old_sst_eraseregion(mtd); + + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2AA; +} + static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param) { struct map_info *map = mtd->priv; @@ -282,11 +316,24 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param) } } +/* Used to fix CFI-Tables of chips without Extended Query Tables */ +static struct cfi_fixup cfi_nopri_fixup_table[] = { + { CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, // SST39VF1602 + { CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, // SST39VF1601 + { CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, // SST39VF3202 + { CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, // SST39VF3201 + { CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, // SST39VF3202B + { CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, // SST39VF3201B + { CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, // SST39VF6402B + { CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, // SST39VF6401B + { 0, 0, NULL, NULL } +}; + static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef AMD_BOOTLOC_BUG { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, - { MANUFACTURER_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock, NULL }, + { CFI_MFR_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock, NULL }, #endif { CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, }, @@ -304,9 +351,9 @@ static struct cfi_fixup cfi_fixup_table[] = { { 0, 0, NULL, NULL } }; static struct cfi_fixup jedec_fixup_table[] = { - { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, - { MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, }, - { MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_SST, SST49LF040B, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_SST, SST49LF008A, fixup_use_fwh_lock, NULL, }, { 0, 0, NULL, NULL } }; @@ -355,67 +402,72 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) mtd->name = map->name; mtd->writesize = 1; + mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot; + if (cfi->cfi_mode==CFI_MODE_CFI){ unsigned char bootloc; - /* - * It's a real CFI chip, not one for which the probe - * routine faked a CFI structure. So we read the feature - * table from it. - */ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; struct cfi_pri_amdstd *extp; extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); - if (!extp) { - kfree(mtd); - return NULL; - } - - cfi_fixup_major_minor(cfi, extp); - - if (extp->MajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { - printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " - "version %c.%c.\n", extp->MajorVersion, - extp->MinorVersion); - kfree(extp); - kfree(mtd); - return NULL; - } + if (extp) { + /* + * It's a real CFI chip, not one for which the probe + * routine faked a CFI structure. + */ + cfi_fixup_major_minor(cfi, extp); + + if (extp->MajorVersion != '1' || + (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { + printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " + "version %c.%c.\n", extp->MajorVersion, + extp->MinorVersion); + kfree(extp); + kfree(mtd); + return NULL; + } - /* Install our own private info structure */ - cfi->cmdset_priv = extp; + /* Install our own private info structure */ + cfi->cmdset_priv = extp; - /* Apply cfi device specific fixups */ - cfi_fixup(mtd, cfi_fixup_table); + /* Apply cfi device specific fixups */ + cfi_fixup(mtd, cfi_fixup_table); #ifdef DEBUG_CFI_FEATURES - /* Tell the user about it in lots of lovely detail */ - cfi_tell_features(extp); + /* Tell the user about it in lots of lovely detail */ + cfi_tell_features(extp); #endif - bootloc = extp->TopBottom; - if ((bootloc != 2) && (bootloc != 3)) { - printk(KERN_WARNING "%s: CFI does not contain boot " - "bank location. Assuming top.\n", map->name); - bootloc = 2; - } + bootloc = extp->TopBottom; + if ((bootloc < 2) || (bootloc > 5)) { + printk(KERN_WARNING "%s: CFI contains unrecognised boot " + "bank location (%d). Assuming bottom.\n", + map->name, bootloc); + bootloc = 2; + } - if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { - printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); + if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { + printk(KERN_WARNING "%s: Swapping erase regions for top-boot CFI table.\n", map->name); - for (i=0; i<cfi->cfiq->NumEraseRegions / 2; i++) { - int j = (cfi->cfiq->NumEraseRegions-1)-i; - __u32 swap; + for (i=0; i<cfi->cfiq->NumEraseRegions / 2; i++) { + int j = (cfi->cfiq->NumEraseRegions-1)-i; + __u32 swap; - swap = cfi->cfiq->EraseRegionInfo[i]; - cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j]; - cfi->cfiq->EraseRegionInfo[j] = swap; + swap = cfi->cfiq->EraseRegionInfo[i]; + cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j]; + cfi->cfiq->EraseRegionInfo[j] = swap; + } } + /* Set the default CFI lock/unlock addresses */ + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2aa; + } + cfi_fixup(mtd, cfi_nopri_fixup_table); + + if (!cfi->addr_unlock1 || !cfi->addr_unlock2) { + kfree(mtd); + return NULL; } - /* Set the default CFI lock/unlock addresses */ - cfi->addr_unlock1 = 0x555; - cfi->addr_unlock2 = 0x2aa; } /* CFI mode */ else if (cfi->cfi_mode == CFI_MODE_JEDEC) { @@ -437,7 +489,11 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) return cfi_amdstd_setup(mtd); } +struct mtd_info *cfi_cmdset_0006(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); +struct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); EXPORT_SYMBOL_GPL(cfi_cmdset_0002); +EXPORT_SYMBOL_GPL(cfi_cmdset_0006); +EXPORT_SYMBOL_GPL(cfi_cmdset_0701); static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) { @@ -491,13 +547,12 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) #endif __module_get(THIS_MODULE); + register_reboot_notifier(&mtd->reboot_notifier); return mtd; setup_err: - if(mtd) { - kfree(mtd->eraseregions); - kfree(mtd); - } + kfree(mtd->eraseregions); + kfree(mtd); kfree(cfi->cmdset_priv); kfree(cfi->cfiq); return NULL; @@ -571,9 +626,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); return -EIO; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Someone else might have been playing with it. */ goto retry; } @@ -617,9 +672,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr return -EIO; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. So we can just loop here. */ } @@ -634,6 +689,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr chip->state = FL_READY; return 0; + case FL_SHUTDOWN: + /* The machine is rebooting */ + return -EIO; + case FL_POINT: /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) @@ -643,10 +702,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); goto resettime; } } @@ -778,7 +837,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, (void) map_read(map, adr); xip_iprefetch(); local_irq_enable(); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); xip_iprefetch(); cond_resched(); @@ -788,15 +847,15 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, * a suspended erase state. If so let's wait * until it's done. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != FL_XIP_WHILE_ERASING) { DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } /* Disallow XIP again */ local_irq_disable(); @@ -858,17 +917,17 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, #define UDELAY(map, chip, adr, usec) \ do { \ - spin_unlock(chip->mutex); \ + mutex_unlock(&chip->mutex); \ cfi_udelay(usec); \ - spin_lock(chip->mutex); \ + mutex_lock(&chip->mutex); \ } while (0) #define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \ do { \ - spin_unlock(chip->mutex); \ + mutex_unlock(&chip->mutex); \ INVALIDATE_CACHED_RANGE(map, adr, len); \ cfi_udelay(usec); \ - spin_lock(chip->mutex); \ + mutex_lock(&chip->mutex); \ } while (0) #endif @@ -884,10 +943,10 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(map_bankwidth(map)-1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_READY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -900,7 +959,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof put_chip(map, chip, cmd_addr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -954,7 +1013,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi struct cfi_private *cfi = map->fldrv_priv; retry: - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state != FL_READY){ #if 0 @@ -963,7 +1022,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); @@ -992,7 +1051,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); wake_up(&chip->wq); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -1061,10 +1120,10 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, adr += chip->start; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1107,11 +1166,11 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -1143,7 +1202,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, op_done: chip->state = FL_READY; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1175,7 +1234,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, map_word tmp_buf; retry: - spin_lock(cfi->chips[chipnum].mutex); + mutex_lock(&cfi->chips[chipnum].mutex); if (cfi->chips[chipnum].state != FL_READY) { #if 0 @@ -1184,7 +1243,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&cfi->chips[chipnum].wq, &wait); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); schedule(); remove_wait_queue(&cfi->chips[chipnum].wq, &wait); @@ -1198,7 +1257,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, /* Load 'tmp_buf' with old contents of flash */ tmp_buf = map_read(map, bus_ofs+chipstart); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); /* Number of bytes to copy from buffer */ n = min_t(int, len, map_bankwidth(map)-i); @@ -1253,7 +1312,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, map_word tmp_buf; retry1: - spin_lock(cfi->chips[chipnum].mutex); + mutex_lock(&cfi->chips[chipnum].mutex); if (cfi->chips[chipnum].state != FL_READY) { #if 0 @@ -1262,7 +1321,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&cfi->chips[chipnum].wq, &wait); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); schedule(); remove_wait_queue(&cfi->chips[chipnum].wq, &wait); @@ -1275,7 +1334,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, tmp_buf = map_read(map, ofs + chipstart); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len); @@ -1310,10 +1369,10 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, adr += chip->start; cmd_adr = adr; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1368,11 +1427,11 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -1400,7 +1459,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, op_done: chip->state = FL_READY; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1500,10 +1559,10 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) adr = cfi->addr_unlock1; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1536,10 +1595,10 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } if (chip->erase_suspended) { @@ -1573,7 +1632,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) chip->state = FL_READY; xip_enable(map, chip, adr); put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1588,10 +1647,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, adr += chip->start; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_ERASING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1624,10 +1683,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } if (chip->erase_suspended) { @@ -1663,7 +1722,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, chip->state = FL_READY; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1715,7 +1774,7 @@ static int do_atmel_lock(struct map_info *map, struct flchip *chip, struct cfi_private *cfi = map->fldrv_priv; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr + chip->start, FL_LOCKING); if (ret) goto out_unlock; @@ -1741,7 +1800,7 @@ static int do_atmel_lock(struct map_info *map, struct flchip *chip, ret = 0; out_unlock: - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1751,7 +1810,7 @@ static int do_atmel_unlock(struct map_info *map, struct flchip *chip, struct cfi_private *cfi = map->fldrv_priv; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr + chip->start, FL_UNLOCKING); if (ret) goto out_unlock; @@ -1769,7 +1828,7 @@ static int do_atmel_unlock(struct map_info *map, struct flchip *chip, ret = 0; out_unlock: - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1797,7 +1856,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) chip = &cfi->chips[i]; retry: - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1811,7 +1870,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) * with the chip now anyway. */ case FL_SYNCING: - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); break; default: @@ -1819,7 +1878,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); @@ -1834,13 +1893,13 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1856,7 +1915,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1876,7 +1935,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) ret = -EAGAIN; break; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -1885,13 +1944,13 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1910,7 +1969,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { chip->state = FL_READY; @@ -1920,15 +1979,62 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) else printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n"); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } + +/* + * Ensure that the flash device is put back into read array mode before + * unloading the driver or rebooting. On some systems, rebooting while + * the flash is in query/program/erase mode will prevent the CPU from + * fetching the bootloader code, requiring a hard reset or power cycle. + */ +static int cfi_amdstd_reset(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i, ret; + struct flchip *chip; + + for (i = 0; i < cfi->numchips; i++) { + + chip = &cfi->chips[i]; + + mutex_lock(&chip->mutex); + + ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); + if (!ret) { + map_write(map, CMD(0xF0), chip->start); + chip->state = FL_SHUTDOWN; + put_chip(map, chip, chip->start); + } + + mutex_unlock(&chip->mutex); + } + + return 0; +} + + +static int cfi_amdstd_reboot(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct mtd_info *mtd; + + mtd = container_of(nb, struct mtd_info, reboot_notifier); + cfi_amdstd_reset(mtd); + return NOTIFY_DONE; +} + + static void cfi_amdstd_destroy(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + cfi_amdstd_reset(mtd); + unregister_reboot_notifier(&mtd->reboot_notifier); kfree(cfi->cmdset_priv); kfree(cfi->cfiq); kfree(cfi); @@ -1938,3 +2044,5 @@ static void cfi_amdstd_destroy(struct mtd_info *mtd) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al."); MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); +MODULE_ALIAS("cfi_cmdset_0006"); +MODULE_ALIAS("cfi_cmdset_0701"); diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 0667a671525..e54e8c169d7 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -265,7 +265,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. * If it's in FL_ERASING state, suspend it and make it talk now. @@ -296,15 +296,15 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* make sure we're in 'read status' mode */ map_write(map, CMD(0x70), cmd_addr); chip->state = FL_ERASING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "Chip not ready after erase " "suspended: status = 0x%lx\n", status.x[0]); return -EIO; } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } suspended = 1; @@ -335,13 +335,13 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -351,7 +351,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -376,7 +376,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof } wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -445,7 +445,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, #ifdef DEBUG_CFI_FEATURES printk("%s: chip->state[%d]\n", __func__, chip->state); #endif - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. * Later, we can actually think about interrupting it @@ -470,14 +470,14 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, break; /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n", status.x[0], map_read(map, cmd_adr).x[0]); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -486,7 +486,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -503,16 +503,16 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, if (map_word_andequal(map, status, status_OK, status_OK)) break; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); if (++z > 100) { /* Argh. Not ready for write to buffer */ DISABLE_VPP(map); map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]); return -EIO; } @@ -532,9 +532,9 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(chip->buffer_write_time); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); timeo = jiffies + (HZ/2); z = 0; @@ -543,11 +543,11 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* Someone's suspended the write. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -563,16 +563,16 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); z++; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } if (!z) { chip->buffer_write_time--; @@ -596,11 +596,11 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* put back into read status register mode */ map_write(map, CMD(0x70), adr); wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO; } wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -749,7 +749,7 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. */ switch (chip->state) { @@ -766,13 +766,13 @@ retry: /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -781,7 +781,7 @@ retry: someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -797,9 +797,9 @@ retry: map_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); msleep(1000); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -810,11 +810,11 @@ retry: /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ*20); /* FIXME */ - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -828,14 +828,14 @@ retry: chip->state = FL_STATUS; printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } DISABLE_VPP(map); @@ -878,7 +878,7 @@ retry: printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); timeo = jiffies + HZ; chip->state = FL_STATUS; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); goto retry; } printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); @@ -887,7 +887,7 @@ retry: } wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -995,7 +995,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) chip = &cfi->chips[i]; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1009,7 +1009,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) * with the chip now anyway. */ case FL_SYNCING: - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); break; default: @@ -1017,7 +1017,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); @@ -1030,13 +1030,13 @@ static void cfi_staa_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1054,7 +1054,7 @@ static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, un timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. */ switch (chip->state) { @@ -1071,13 +1071,13 @@ retry: /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in lock\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -1086,7 +1086,7 @@ retry: someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -1098,9 +1098,9 @@ retry: map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); msleep(1000); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -1118,21 +1118,21 @@ retry: chip->state = FL_STATUS; printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; DISABLE_VPP(map); wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -1203,7 +1203,7 @@ static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. */ switch (chip->state) { @@ -1220,13 +1220,13 @@ retry: /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in unlock\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -1235,7 +1235,7 @@ retry: someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -1247,9 +1247,9 @@ retry: map_write(map, CMD(0xD0), adr); chip->state = FL_UNLOCKING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); msleep(1000); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -1267,21 +1267,21 @@ retry: chip->state = FL_STATUS; printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return -EIO; } /* Latency issues. Drop the unlock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; DISABLE_VPP(map); wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -1334,7 +1334,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1354,7 +1354,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) ret = -EAGAIN; break; } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -1363,7 +1363,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { /* No need to force it into a known state here, @@ -1372,7 +1372,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1390,7 +1390,7 @@ static void cfi_staa_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { @@ -1399,7 +1399,7 @@ static void cfi_staa_resume(struct mtd_info *mtd) wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } } diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index e63e6749429..b2acd32f4fb 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -158,6 +158,7 @@ static int __xipram cfi_chip_setup(struct map_info *map, __u32 base = 0; int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor); int i; + int addr_unlock1 = 0x555, addr_unlock2 = 0x2AA; xip_enable(base, map, cfi); #ifdef DEBUG_CFI @@ -181,29 +182,6 @@ static int __xipram cfi_chip_setup(struct map_info *map, for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) ((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor); - /* Note we put the device back into Read Mode BEFORE going into Auto - * Select Mode, as some devices support nesting of modes, others - * don't. This way should always work. - * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and - * so should be treated as nops or illegal (and so put the device - * back into Read Mode, which is a nop in this case). - */ - cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi->mfr = cfi_read_query16(map, base); - cfi->id = cfi_read_query16(map, base + ofs_factor); - - /* Get AMD/Spansion extended JEDEC ID */ - if (cfi->mfr == CFI_MFR_AMD && (cfi->id & 0xff) == 0x7e) - cfi->id = cfi_read_query(map, base + 0xe * ofs_factor) << 8 | - cfi_read_query(map, base + 0xf * ofs_factor); - - /* Put it back into Read Mode */ - cfi_qry_mode_off(base, map, cfi); - xip_allowed(base, map); - /* Do any necessary byteswapping */ cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID); @@ -228,6 +206,35 @@ static int __xipram cfi_chip_setup(struct map_info *map, #endif } + if (cfi->cfiq->P_ID == P_ID_SST_OLD) { + addr_unlock1 = 0x5555; + addr_unlock2 = 0x2AAA; + } + + /* + * Note we put the device back into Read Mode BEFORE going into Auto + * Select Mode, as some devices support nesting of modes, others + * don't. This way should always work. + * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and + * so should be treated as nops or illegal (and so put the device + * back into Read Mode, which is a nop in this case). + */ + cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xaa, addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, addr_unlock2, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi->mfr = cfi_read_query16(map, base); + cfi->id = cfi_read_query16(map, base + ofs_factor); + + /* Get AMD/Spansion extended JEDEC ID */ + if (cfi->mfr == CFI_MFR_AMD && (cfi->id & 0xff) == 0x7e) + cfi->id = cfi_read_query(map, base + 0xe * ofs_factor) << 8 | + cfi_read_query(map, base + 0xf * ofs_factor); + + /* Put it back into Read Mode */ + cfi_qry_mode_off(base, map, cfi); + xip_allowed(base, map); + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, map->bankwidth*8); @@ -269,6 +276,9 @@ static char *vendorname(__u16 vendor) case P_ID_SST_PAGE: return "SST Page Write"; + case P_ID_SST_OLD: + return "SST 39VF160x/39VF320x"; + case P_ID_INTEL_PERFORMANCE: return "Intel Performance Code"; diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index ca584d0380b..d7c2c672757 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -104,10 +104,11 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n int i; struct cfi_extquery *extp = NULL; - printk(" %s Extended Query Table at 0x%4.4X\n", name, adr); if (!adr) goto out; + printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr); + extp = kmalloc(size, GFP_KERNEL); if (!extp) { printk(KERN_ERR "Failed to allocate memory\n"); diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h index 57e0e4e921f..d1806497719 100644 --- a/drivers/mtd/chips/fwh_lock.h +++ b/drivers/mtd/chips/fwh_lock.h @@ -58,10 +58,10 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, * to flash memory - that means that we don't have to check status * and timeout. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_LOCKING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -72,7 +72,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, /* Done and happy. */ chip->state = chip->oldstate; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index e2dc96441e0..3b9a2843c5f 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -155,8 +155,7 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi pchip->start = (i << cfi.chipshift); pchip->state = FL_READY; init_waitqueue_head(&pchip->wq); - spin_lock_init(&pchip->_spinlock); - pchip->mutex = &pchip->_spinlock; + mutex_init(&pchip->mutex); } } @@ -242,17 +241,19 @@ static struct mtd_info *check_cmd_set(struct map_info *map, int primary) /* We need these for the !CONFIG_MODULES case, because symbol_get() doesn't work there */ #ifdef CONFIG_MTD_CFI_INTELEXT - case 0x0001: - case 0x0003: - case 0x0200: + case P_ID_INTEL_EXT: + case P_ID_INTEL_STD: + case P_ID_INTEL_PERFORMANCE: return cfi_cmdset_0001(map, primary); #endif #ifdef CONFIG_MTD_CFI_AMDSTD - case 0x0002: + case P_ID_AMD_STD: + case P_ID_SST_OLD: + case P_ID_WINBOND: return cfi_cmdset_0002(map, primary); #endif #ifdef CONFIG_MTD_CFI_STAA - case 0x0020: + case P_ID_ST_ADV: return cfi_cmdset_0020(map, primary); #endif default: diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 8db1148dfa4..d72a5fb2d04 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -22,24 +22,6 @@ #include <linux/mtd/cfi.h> #include <linux/mtd/gen_probe.h> -/* Manufacturers */ -#define MANUFACTURER_AMD 0x0001 -#define MANUFACTURER_ATMEL 0x001f -#define MANUFACTURER_EON 0x001c -#define MANUFACTURER_FUJITSU 0x0004 -#define MANUFACTURER_HYUNDAI 0x00AD -#define MANUFACTURER_INTEL 0x0089 -#define MANUFACTURER_MACRONIX 0x00C2 -#define MANUFACTURER_NEC 0x0010 -#define MANUFACTURER_PMC 0x009D -#define MANUFACTURER_SHARP 0x00b0 -#define MANUFACTURER_SST 0x00BF -#define MANUFACTURER_ST 0x0020 -#define MANUFACTURER_TOSHIBA 0x0098 -#define MANUFACTURER_WINBOND 0x00da -#define CONTINUATION_CODE 0x007f - - /* AMD */ #define AM29DL800BB 0x22CB #define AM29DL800BT 0x224A @@ -166,6 +148,8 @@ #define SST39LF160 0x2782 #define SST39VF1601 0x234b #define SST39VF3201 0x235b +#define SST39WF1601 0x274b +#define SST39WF1602 0x274a #define SST39LF512 0x00D4 #define SST39LF010 0x00D5 #define SST39LF020 0x00D6 @@ -309,7 +293,7 @@ struct amd_flash_info { */ static const struct amd_flash_info jedec_table[] = { { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F032B, .name = "AMD AM29F032B", .uaddr = MTD_UADDR_0x0555_0x02AA, @@ -321,7 +305,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,64) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV160DT, .name = "AMD AM29LV160DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -336,7 +320,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV160DB, .name = "AMD AM29LV160DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -351,7 +335,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV400BB, .name = "AMD AM29LV400BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -366,7 +350,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV400BT, .name = "AMD AM29LV400BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -381,7 +365,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV800BB, .name = "AMD AM29LV800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -397,7 +381,7 @@ static const struct amd_flash_info jedec_table[] = { } }, { /* add DL */ - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29DL800BB, .name = "AMD AM29DL800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -414,7 +398,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,14) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29DL800BT, .name = "AMD AM29DL800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -431,7 +415,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F800BB, .name = "AMD AM29F800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -446,7 +430,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV800BT, .name = "AMD AM29LV800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -461,7 +445,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F800BT, .name = "AMD AM29F800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -476,7 +460,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F017D, .name = "AMD AM29F017D", .devtypes = CFI_DEVICETYPE_X8, @@ -488,7 +472,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F016D, .name = "AMD AM29F016D", .devtypes = CFI_DEVICETYPE_X8, @@ -500,7 +484,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F080, .name = "AMD AM29F080", .devtypes = CFI_DEVICETYPE_X8, @@ -512,7 +496,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F040, .name = "AMD AM29F040", .devtypes = CFI_DEVICETYPE_X8, @@ -524,7 +508,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV040B, .name = "AMD AM29LV040B", .devtypes = CFI_DEVICETYPE_X8, @@ -536,7 +520,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F002T, .name = "AMD AM29F002T", .devtypes = CFI_DEVICETYPE_X8, @@ -551,7 +535,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29SL800DT, .name = "AMD AM29SL800DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -566,7 +550,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29SL800DB, .name = "AMD AM29SL800DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -581,7 +565,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV512, .name = "Atmel AT49BV512", .devtypes = CFI_DEVICETYPE_X8, @@ -593,7 +577,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,1) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT29LV512, .name = "Atmel AT29LV512", .devtypes = CFI_DEVICETYPE_X8, @@ -606,7 +590,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x80,256) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV16X, .name = "Atmel AT49BV16X", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -619,7 +603,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV16XT, .name = "Atmel AT49BV16XT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -632,7 +616,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV32X, .name = "Atmel AT49BV32X", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -645,7 +629,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,63) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV32XT, .name = "Atmel AT49BV32XT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -658,7 +642,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_EON, + .mfr_id = CFI_MFR_EON, .dev_id = EN29SL800BT, .name = "Eon EN29SL800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -673,7 +657,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_EON, + .mfr_id = CFI_MFR_EON, .dev_id = EN29SL800BB, .name = "Eon EN29SL800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -688,7 +672,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29F040C, .name = "Fujitsu MBM29F040C", .devtypes = CFI_DEVICETYPE_X8, @@ -700,7 +684,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29F800BA, .name = "Fujitsu MBM29F800BA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -715,7 +699,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV650UE, .name = "Fujitsu MBM29LV650UE", .devtypes = CFI_DEVICETYPE_X8, @@ -727,7 +711,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,128) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV320TE, .name = "Fujitsu MBM29LV320TE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -740,7 +724,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV320BE, .name = "Fujitsu MBM29LV320BE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -753,7 +737,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,63) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV160TE, .name = "Fujitsu MBM29LV160TE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -768,7 +752,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV160BE, .name = "Fujitsu MBM29LV160BE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -783,7 +767,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV800BA, .name = "Fujitsu MBM29LV800BA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -798,7 +782,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV800TA, .name = "Fujitsu MBM29LV800TA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -813,7 +797,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV400BC, .name = "Fujitsu MBM29LV400BC", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -828,7 +812,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV400TC, .name = "Fujitsu MBM29LV400TC", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -843,7 +827,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_HYUNDAI, + .mfr_id = CFI_MFR_HYUNDAI, .dev_id = HY29F002T, .name = "Hyundai HY29F002T", .devtypes = CFI_DEVICETYPE_X8, @@ -858,7 +842,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F004B3B, .name = "Intel 28F004B3B", .devtypes = CFI_DEVICETYPE_X8, @@ -871,7 +855,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 7), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F004B3T, .name = "Intel 28F004B3T", .devtypes = CFI_DEVICETYPE_X8, @@ -884,7 +868,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F400B3B, .name = "Intel 28F400B3B", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -897,7 +881,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 7), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F400B3T, .name = "Intel 28F400B3T", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -910,7 +894,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008B3B, .name = "Intel 28F008B3B", .devtypes = CFI_DEVICETYPE_X8, @@ -923,7 +907,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 15), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008B3T, .name = "Intel 28F008B3T", .devtypes = CFI_DEVICETYPE_X8, @@ -936,7 +920,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008S5, .name = "Intel 28F008S5", .devtypes = CFI_DEVICETYPE_X8, @@ -948,7 +932,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016S5, .name = "Intel 28F016S5", .devtypes = CFI_DEVICETYPE_X8, @@ -960,7 +944,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008SA, .name = "Intel 28F008SA", .devtypes = CFI_DEVICETYPE_X8, @@ -972,7 +956,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 16), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F800B3B, .name = "Intel 28F800B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -985,7 +969,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 15), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F800B3T, .name = "Intel 28F800B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -998,7 +982,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016B3B, .name = "Intel 28F016B3B", .devtypes = CFI_DEVICETYPE_X8, @@ -1011,7 +995,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 31), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016S3, .name = "Intel I28F016S3", .devtypes = CFI_DEVICETYPE_X8, @@ -1023,7 +1007,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 32), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016B3T, .name = "Intel 28F016B3T", .devtypes = CFI_DEVICETYPE_X8, @@ -1036,7 +1020,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F160B3B, .name = "Intel 28F160B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1049,7 +1033,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 31), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F160B3T, .name = "Intel 28F160B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -1062,7 +1046,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F320B3B, .name = "Intel 28F320B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1075,7 +1059,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 63), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F320B3T, .name = "Intel 28F320B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -1088,7 +1072,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F640B3B, .name = "Intel 28F640B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1101,7 +1085,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 127), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F640B3T, .name = "Intel 28F640B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -1114,7 +1098,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F640C3B, .name = "Intel 28F640C3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1127,7 +1111,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 127), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I82802AB, .name = "Intel 82802AB", .devtypes = CFI_DEVICETYPE_X8, @@ -1139,7 +1123,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I82802AC, .name = "Intel 82802AC", .devtypes = CFI_DEVICETYPE_X8, @@ -1151,7 +1135,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29LV040C, .name = "Macronix MX29LV040C", .devtypes = CFI_DEVICETYPE_X8, @@ -1163,7 +1147,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29LV160T, .name = "MXIC MX29LV160T", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1178,7 +1162,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_NEC, + .mfr_id = CFI_MFR_NEC, .dev_id = UPD29F064115, .name = "NEC uPD29F064115", .devtypes = CFI_DEVICETYPE_X16, @@ -1192,7 +1176,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x2000,8), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29LV160B, .name = "MXIC MX29LV160B", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1207,7 +1191,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F040, .name = "Macronix MX29F040", .devtypes = CFI_DEVICETYPE_X8, @@ -1219,7 +1203,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F016, .name = "Macronix MX29F016", .devtypes = CFI_DEVICETYPE_X8, @@ -1231,7 +1215,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F004T, .name = "Macronix MX29F004T", .devtypes = CFI_DEVICETYPE_X8, @@ -1246,7 +1230,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F004B, .name = "Macronix MX29F004B", .devtypes = CFI_DEVICETYPE_X8, @@ -1261,7 +1245,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F002T, .name = "Macronix MX29F002T", .devtypes = CFI_DEVICETYPE_X8, @@ -1276,7 +1260,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_PMC, + .mfr_id = CFI_MFR_PMC, .dev_id = PM49FL002, .name = "PMC Pm49FL002", .devtypes = CFI_DEVICETYPE_X8, @@ -1288,7 +1272,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO( 0x01000, 64 ) } }, { - .mfr_id = MANUFACTURER_PMC, + .mfr_id = CFI_MFR_PMC, .dev_id = PM49FL004, .name = "PMC Pm49FL004", .devtypes = CFI_DEVICETYPE_X8, @@ -1300,7 +1284,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO( 0x01000, 128 ) } }, { - .mfr_id = MANUFACTURER_PMC, + .mfr_id = CFI_MFR_PMC, .dev_id = PM49FL008, .name = "PMC Pm49FL008", .devtypes = CFI_DEVICETYPE_X8, @@ -1312,7 +1296,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO( 0x01000, 256 ) } }, { - .mfr_id = MANUFACTURER_SHARP, + .mfr_id = CFI_MFR_SHARP, .dev_id = LH28F640BF, .name = "LH28F640BF", .devtypes = CFI_DEVICETYPE_X8, @@ -1324,7 +1308,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x40000,16), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF512, .name = "SST 39LF512", .devtypes = CFI_DEVICETYPE_X8, @@ -1336,7 +1320,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,16), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF010, .name = "SST 39LF010", .devtypes = CFI_DEVICETYPE_X8, @@ -1348,8 +1332,8 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,32), } }, { - .mfr_id = MANUFACTURER_SST, - .dev_id = SST29EE020, + .mfr_id = CFI_MFR_SST, + .dev_id = SST29EE020, .name = "SST 29EE020", .devtypes = CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x5555_0x2AAA, @@ -1359,9 +1343,9 @@ static const struct amd_flash_info jedec_table[] = { .regions = {ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST29LE020, - .name = "SST 29LE020", + .name = "SST 29LE020", .devtypes = CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x5555_0x2AAA, .dev_size = SIZE_256KiB, @@ -1370,7 +1354,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = {ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF020, .name = "SST 39LF020", .devtypes = CFI_DEVICETYPE_X8, @@ -1382,7 +1366,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF040, .name = "SST 39LF040", .devtypes = CFI_DEVICETYPE_X8, @@ -1394,7 +1378,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39SF010A, .name = "SST 39SF010A", .devtypes = CFI_DEVICETYPE_X8, @@ -1406,7 +1390,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,32), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39SF020A, .name = "SST 39SF020A", .devtypes = CFI_DEVICETYPE_X8, @@ -1418,7 +1402,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39SF040, .name = "SST 39SF040", .devtypes = CFI_DEVICETYPE_X8, @@ -1430,7 +1414,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF040B, .name = "SST 49LF040B", .devtypes = CFI_DEVICETYPE_X8, @@ -1443,7 +1427,7 @@ static const struct amd_flash_info jedec_table[] = { } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF004B, .name = "SST 49LF004B", .devtypes = CFI_DEVICETYPE_X8, @@ -1455,7 +1439,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF008A, .name = "SST 49LF008A", .devtypes = CFI_DEVICETYPE_X8, @@ -1467,7 +1451,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,256), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF030A, .name = "SST 49LF030A", .devtypes = CFI_DEVICETYPE_X8, @@ -1479,7 +1463,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,96), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF040A, .name = "SST 49LF040A", .devtypes = CFI_DEVICETYPE_X8, @@ -1491,7 +1475,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF080A, .name = "SST 49LF080A", .devtypes = CFI_DEVICETYPE_X8, @@ -1503,7 +1487,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,256), } }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .mfr_id = CFI_MFR_SST, /* should be CFI */ .dev_id = SST39LF160, .name = "SST 39LF160", .devtypes = CFI_DEVICETYPE_X16, @@ -1516,7 +1500,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .mfr_id = CFI_MFR_SST, /* should be CFI */ .dev_id = SST39VF1601, .name = "SST 39VF1601", .devtypes = CFI_DEVICETYPE_X16, @@ -1529,7 +1513,35 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ + /* CFI is broken: reports AMD_STD, but needs custom uaddr */ + .mfr_id = CFI_MFR_SST, + .dev_id = SST39WF1601, + .name = "SST 39WF1601", + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_0xAAAA_0x5555, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + }, { + /* CFI is broken: reports AMD_STD, but needs custom uaddr */ + .mfr_id = CFI_MFR_SST, + .dev_id = SST39WF1602, + .name = "SST 39WF1602", + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_0xAAAA_0x5555, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + }, { + .mfr_id = CFI_MFR_SST, /* should be CFI */ .dev_id = SST39VF3201, .name = "SST 39VF3201", .devtypes = CFI_DEVICETYPE_X16, @@ -1544,7 +1556,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST36VF3203, .name = "SST 36VF3203", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1556,7 +1568,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,64), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29F800AB, .name = "ST M29F800AB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1571,7 +1583,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W800DT, .name = "ST M29W800DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1586,7 +1598,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W800DB, .name = "ST M29W800DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1601,7 +1613,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15) } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29W400DT, .name = "ST M29W400DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1616,7 +1628,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,1) } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29W400DB, .name = "ST M29W400DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1631,7 +1643,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7) } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W160DT, .name = "ST M29W160DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1646,7 +1658,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W160DB, .name = "ST M29W160DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1661,7 +1673,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29W040B, .name = "ST M29W040B", .devtypes = CFI_DEVICETYPE_X8, @@ -1673,7 +1685,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FW040, .name = "ST M50FW040", .devtypes = CFI_DEVICETYPE_X8, @@ -1685,7 +1697,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FW080, .name = "ST M50FW080", .devtypes = CFI_DEVICETYPE_X8, @@ -1697,7 +1709,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FW016, .name = "ST M50FW016", .devtypes = CFI_DEVICETYPE_X8, @@ -1709,7 +1721,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50LPW080, .name = "ST M50LPW080", .devtypes = CFI_DEVICETYPE_X8, @@ -1721,7 +1733,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), }, }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FLW080A, .name = "ST M50FLW080A", .devtypes = CFI_DEVICETYPE_X8, @@ -1736,7 +1748,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,16), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FLW080B, .name = "ST M50FLW080B", .devtypes = CFI_DEVICETYPE_X8, @@ -1751,7 +1763,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,16), } }, { - .mfr_id = 0xff00 | MANUFACTURER_ST, + .mfr_id = 0xff00 | CFI_MFR_ST, .dev_id = 0xff00 | PSD4256G6V, .name = "ST PSD4256G6V", .devtypes = CFI_DEVICETYPE_X16, @@ -1763,7 +1775,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVT160, .name = "Toshiba TC58FVT160", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1778,7 +1790,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVB160, .name = "Toshiba TC58FVB160", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1793,7 +1805,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVB321, .name = "Toshiba TC58FVB321", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1806,7 +1818,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,63) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVT321, .name = "Toshiba TC58FVT321", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1819,7 +1831,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVB641, .name = "Toshiba TC58FVB641", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1832,7 +1844,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,127) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVT641, .name = "Toshiba TC58FVT641", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1845,7 +1857,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_WINBOND, + .mfr_id = CFI_MFR_WINBOND, .dev_id = W49V002A, .name = "Winbond W49V002A", .devtypes = CFI_DEVICETYPE_X8, @@ -1878,7 +1890,7 @@ static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base, mask = (1 << (cfi->device_type * 8)) - 1; result = map_read(map, base + ofs); bank++; - } while ((result.x[0] & mask) == CONTINUATION_CODE); + } while ((result.x[0] & mask) == CFI_MFR_CONTINUATION); return result.x[0] & mask; } @@ -1969,7 +1981,7 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 / p_cfi->device_type; p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 / p_cfi->device_type; - return 1; /* ok */ + return 1; /* ok */ } diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index ab5c9b92ac8..f3226b1d38f 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -1,5 +1,5 @@ # -# linux/drivers/devices/Makefile +# linux/drivers/mtd/devices/Makefile # obj-$(CONFIG_MTD_DOC2000) += doc2000.o diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index ce6424008ed..93651865ddb 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -276,12 +276,10 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) /* Setup the MTD structure */ /* make the name contain the block device in */ - name = kmalloc(sizeof("block2mtd: ") + strlen(devname) + 1, - GFP_KERNEL); + name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname); if (!name) goto devinit_err; - sprintf(name, "block2mtd: %s", devname); dev->mtd.name = name; dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index d2fd550f7e0..fc8ea0a57ac 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -668,7 +668,7 @@ static int __init init_pmc551(void) { struct pci_dev *PCI_Device = NULL; struct mypriv *priv; - int count, found = 0; + int found = 0; struct mtd_info *mtd; u32 length = 0; @@ -695,7 +695,7 @@ static int __init init_pmc551(void) /* * PCU-bus chipset probe. */ - for (count = 0; count < MAX_MTD_DEVICES; count++) { + for (;;) { if ((PCI_Device = pci_get_device(PCI_VENDOR_ID_V3_SEMI, PCI_DEVICE_ID_V3_SEMI_V370PDC, diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index fe17054ee2f..ab5d8cd02a1 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -73,15 +73,25 @@ static struct flash_info __initdata sst25l_flash_info[] = { static int sst25l_status(struct sst25l_flash *flash, int *status) { - unsigned char command, response; + struct spi_message m; + struct spi_transfer t; + unsigned char cmd_resp[2]; int err; - command = SST25L_CMD_RDSR; - err = spi_write_then_read(flash->spi, &command, 1, &response, 1); + spi_message_init(&m); + memset(&t, 0, sizeof(struct spi_transfer)); + + cmd_resp[0] = SST25L_CMD_RDSR; + cmd_resp[1] = 0xff; + t.tx_buf = cmd_resp; + t.rx_buf = cmd_resp; + t.len = sizeof(cmd_resp); + spi_message_add_tail(&t, &m); + err = spi_sync(flash->spi, &m); if (err < 0) return err; - *status = response; + *status = cmd_resp[1]; return 0; } @@ -328,33 +338,32 @@ out: static struct flash_info *__init sst25l_match_device(struct spi_device *spi) { struct flash_info *flash_info = NULL; - unsigned char command[4], response; + struct spi_message m; + struct spi_transfer t; + unsigned char cmd_resp[6]; int i, err; uint16_t id; - command[0] = SST25L_CMD_READ_ID; - command[1] = 0; - command[2] = 0; - command[3] = 0; - err = spi_write_then_read(spi, command, sizeof(command), &response, 1); + spi_message_init(&m); + memset(&t, 0, sizeof(struct spi_transfer)); + + cmd_resp[0] = SST25L_CMD_READ_ID; + cmd_resp[1] = 0; + cmd_resp[2] = 0; + cmd_resp[3] = 0; + cmd_resp[4] = 0xff; + cmd_resp[5] = 0xff; + t.tx_buf = cmd_resp; + t.rx_buf = cmd_resp; + t.len = sizeof(cmd_resp); + spi_message_add_tail(&t, &m); + err = spi_sync(spi, &m); if (err < 0) { - dev_err(&spi->dev, "error reading device id msb\n"); + dev_err(&spi->dev, "error reading device id\n"); return NULL; } - id = response << 8; - - command[0] = SST25L_CMD_READ_ID; - command[1] = 0; - command[2] = 0; - command[3] = 1; - err = spi_write_then_read(spi, command, sizeof(command), &response, 1); - if (err < 0) { - dev_err(&spi->dev, "error reading device id lsb\n"); - return NULL; - } - - id |= response; + id = (cmd_resp[4] << 8) | cmd_resp[5]; for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++) if (sst25l_flash_info[i].device_id == id) @@ -411,17 +420,6 @@ static int __init sst25l_probe(struct spi_device *spi) flash->mtd.erasesize, flash->mtd.erasesize / 1024, flash->mtd.numeraseregions); - if (flash->mtd.numeraseregions) - for (i = 0; i < flash->mtd.numeraseregions; i++) - DEBUG(MTD_DEBUG_LEVEL2, - "mtd.eraseregions[%d] = { .offset = 0x%llx, " - ".erasesize = 0x%.8x (%uKiB), " - ".numblocks = %d }\n", - i, (long long)flash->mtd.eraseregions[i].offset, - flash->mtd.eraseregions[i].erasesize, - flash->mtd.eraseregions[i].erasesize / 1024, - flash->mtd.eraseregions[i].numblocks); - if (mtd_has_partitions()) { struct mtd_partition *parts = NULL; int nr_parts = 0; diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index e56d6b42f02..62da9eb7032 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1082,7 +1082,6 @@ static void ftl_remove_dev(struct mtd_blktrans_dev *dev) { del_mtd_blktrans_dev(dev); ftl_freepart((partition_t *)dev); - kfree(dev); } static struct mtd_blktrans_ops ftl_tr = { diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 8aca5523a33..015a7fe1b6e 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -139,7 +139,6 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev) kfree(inftl->PUtable); kfree(inftl->VUtable); - kfree(inftl); } /* diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 32e82aef3e5..8f988d7d3c5 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -100,9 +100,10 @@ static int find_boot_record(struct INFTLrecord *inftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize + - SECTORSIZE + 8, 8, &retlen, - (char *)&h1) < 0)) { + ret = inftl_read_oob(mtd, + block * inftl->EraseSize + SECTORSIZE + 8, + 8, &retlen,(char *)&h1); + if (ret < 0) { printk(KERN_WARNING "INFTL: ANAND header found at " "0x%x in mtd%d, but OOB data read failed " "(err %d)\n", block * inftl->EraseSize, diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index a73ee12aad8..fece5be5871 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -107,8 +107,7 @@ struct mtd_info *lpddr_cmdset(struct map_info *map) /* those should be reset too since they create memory references. */ init_waitqueue_head(&chip->wq); - spin_lock_init(&chip->_spinlock); - chip->mutex = &chip->_spinlock; + mutex_init(&chip->mutex); chip++; } } @@ -144,7 +143,7 @@ static int wait_for_ready(struct map_info *map, struct flchip *chip, } /* OK Still waiting. Drop the lock, wait a while and retry. */ - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (sleep_time >= 1000000/HZ) { /* * Half of the normal delay still remaining @@ -159,17 +158,17 @@ static int wait_for_ready(struct map_info *map, struct flchip *chip, cond_resched(); timeo--; } - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != chip_state) { /* Someone's suspended the operation: sleep */ DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } if (chip->erase_suspended || chip->write_suspended) { /* Suspend has occured while sleep: reset timeout */ @@ -230,20 +229,20 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) * it'll happily send us to sleep. In any case, when * get_chip returns success we're clear to go ahead. */ - ret = spin_trylock(contender->mutex); + ret = mutex_trylock(&contender->mutex); spin_unlock(&shared->lock); if (!ret) goto retry; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); ret = chip_ready(map, contender, mode); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (ret == -EAGAIN) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } if (ret) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); return ret; } spin_lock(&shared->lock); @@ -252,10 +251,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) * state. Put contender and retry. */ if (chip->state == FL_SYNCING) { put_chip(map, contender); - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); } /* Check if we have suspended erase on this chip. @@ -265,10 +264,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) spin_unlock(&shared->lock); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); goto retry; } @@ -337,10 +336,10 @@ static int chip_ready(struct map_info *map, struct flchip *chip, int mode) sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); return -EAGAIN; } } @@ -356,12 +355,12 @@ static void put_chip(struct map_info *map, struct flchip *chip) if (shared->writing && shared->writing != chip) { /* give back the ownership */ struct flchip *loaner = shared->writing; - spin_lock(loaner->mutex); + mutex_lock(&loaner->mutex); spin_unlock(&shared->lock); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); put_chip(map, loaner); - spin_lock(chip->mutex); - spin_unlock(loaner->mutex); + mutex_lock(&chip->mutex); + mutex_unlock(&loaner->mutex); wake_up(&chip->wq); return; } @@ -414,10 +413,10 @@ int do_write_buffer(struct map_info *map, struct flchip *chip, wbufsize = 1 << lpddr->qinfo->BufSizeShift; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } /* Figure out the number of words to write */ @@ -478,7 +477,7 @@ int do_write_buffer(struct map_info *map, struct flchip *chip, } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -490,10 +489,10 @@ int do_erase_oneblock(struct mtd_info *mtd, loff_t adr) struct flchip *chip = &lpddr->chips[chipnum]; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_ERASING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } send_pfow_command(map, LPDDR_BLOCK_ERASE, adr, 0, NULL); @@ -505,7 +504,7 @@ int do_erase_oneblock(struct mtd_info *mtd, loff_t adr) goto out; } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -518,10 +517,10 @@ static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, struct flchip *chip = &lpddr->chips[chipnum]; int ret = 0; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_READY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -529,7 +528,7 @@ static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, *retlen = len; put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -569,9 +568,9 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len, else thislen = len; /* get the chip */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_POINT); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (ret) break; @@ -611,7 +610,7 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len) else thislen = len; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_POINT) { chip->ref_point_counter--; if (chip->ref_point_counter == 0) @@ -621,7 +620,7 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len) "pointed region\n", map->name); put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); len -= thislen; ofs = 0; @@ -727,10 +726,10 @@ int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) int chipnum = adr >> lpddr->chipshift; struct flchip *chip = &lpddr->chips[chipnum]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_LOCKING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -750,7 +749,7 @@ int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) goto out; } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -771,10 +770,10 @@ int word_program(struct map_info *map, loff_t adr, uint32_t curval) int chipnum = adr >> lpddr->chipshift; struct flchip *chip = &lpddr->chips[chipnum]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -788,7 +787,7 @@ int word_program(struct map_info *map, loff_t adr, uint32_t curval) } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c index 79bf40f48b7..dbfe17baf04 100644 --- a/drivers/mtd/lpddr/qinfo_probe.c +++ b/drivers/mtd/lpddr/qinfo_probe.c @@ -134,13 +134,12 @@ out: static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr) { - lpddr->qinfo = kmalloc(sizeof(struct qinfo_chip), GFP_KERNEL); + lpddr->qinfo = kzalloc(sizeof(struct qinfo_chip), GFP_KERNEL); if (!lpddr->qinfo) { printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n", map->name); return 0; } - memset(lpddr->qinfo, 0, sizeof(struct qinfo_chip)); /* Get the ManuID */ lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID)); @@ -185,13 +184,11 @@ static struct lpddr_private *lpddr_probe_chip(struct map_info *map) lpddr.numchips = 1; numvirtchips = lpddr.numchips * lpddr.qinfo->HWPartsNum; - retlpddr = kmalloc(sizeof(struct lpddr_private) + + retlpddr = kzalloc(sizeof(struct lpddr_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL); if (!retlpddr) return NULL; - memset(retlpddr, 0, sizeof(struct lpddr_private) + - numvirtchips * sizeof(struct flchip)); memcpy(retlpddr, &lpddr, sizeof(struct lpddr_private)); retlpddr->numchips = numvirtchips; diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index aa2807d0ce7..f22bc9f05dd 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -435,7 +435,7 @@ config MTD_PCI config MTD_PCMCIA tristate "PCMCIA MTD driver" - depends on PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN + depends on PCMCIA && MTD_COMPLEX_MAPPINGS help Map driver for accessing PCMCIA linear flash memory cards. These cards are usually around 4-16MiB in size. This does not include diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index c0fd99b0c52..85dd18193cf 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -70,7 +70,7 @@ static void switch_back(struct async_state *state) local_irq_restore(state->irq_flags); } -static map_word bfin_read(struct map_info *map, unsigned long ofs) +static map_word bfin_flash_read(struct map_info *map, unsigned long ofs) { struct async_state *state = (struct async_state *)map->map_priv_1; uint16_t word; @@ -86,7 +86,7 @@ static map_word bfin_read(struct map_info *map, unsigned long ofs) return test; } -static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +static void bfin_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { struct async_state *state = (struct async_state *)map->map_priv_1; @@ -97,7 +97,7 @@ static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, s switch_back(state); } -static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs) +static void bfin_flash_write(struct map_info *map, map_word d1, unsigned long ofs) { struct async_state *state = (struct async_state *)map->map_priv_1; uint16_t d; @@ -112,7 +112,7 @@ static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs) switch_back(state); } -static void bfin_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +static void bfin_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) { struct async_state *state = (struct async_state *)map->map_priv_1; @@ -141,10 +141,10 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev) return -ENOMEM; state->map.name = DRIVER_NAME; - state->map.read = bfin_read; - state->map.copy_from = bfin_copy_from; - state->map.write = bfin_write; - state->map.copy_to = bfin_copy_to; + state->map.read = bfin_flash_read; + state->map.copy_from = bfin_flash_copy_from; + state->map.write = bfin_flash_write; + state->map.copy_to = bfin_flash_copy_to; state->map.bankwidth = pdata->width; state->map.size = memory->end - memory->start + 1; state->map.virt = (void __iomem *)memory->start; diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c index d41f34766e5..c09f4f57093 100644 --- a/drivers/mtd/maps/ceiva.c +++ b/drivers/mtd/maps/ceiva.c @@ -253,7 +253,7 @@ static void __exit clps_destroy_mtd(struct clps_info *clps, struct mtd_info *mtd static int __init clps_setup_flash(void) { - int nr; + int nr = 0; #ifdef CONFIG_ARCH_CEIVA if (machine_is_ceiva()) { diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index 1bdf0ee6d0b..9639d83a9d6 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -165,12 +165,11 @@ static int ixp2000_flash_probe(struct platform_device *dev) return -EIO; } - info = kmalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); if(!info) { err = -ENOMEM; goto Error; } - memset(info, 0, sizeof(struct ixp2000_flash_info)); platform_set_drvdata(dev, info); diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 7b051529741..e0a5e0426ea 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -107,8 +107,8 @@ static void ixp4xx_copy_from(struct map_info *map, void *to, return; if (from & 1) { - *dest++ = BYTE1(flash_read16(src)); - src++; + *dest++ = BYTE1(flash_read16(src-1)); + src++; --len; } @@ -196,12 +196,11 @@ static int ixp4xx_flash_probe(struct platform_device *dev) return err; } - info = kmalloc(sizeof(struct ixp4xx_flash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct ixp4xx_flash_info), GFP_KERNEL); if(!info) { err = -ENOMEM; goto Error; } - memset(info, 0, sizeof(struct ixp4xx_flash_info)); platform_set_drvdata(dev, info); diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 87b2b8ff331..e699e6ac23d 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -40,10 +40,7 @@ MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy"); static const int debug = 0; #endif -#define err(format, arg...) printk(KERN_ERR "pcmciamtd: " format "\n" , ## arg) #define info(format, arg...) printk(KERN_INFO "pcmciamtd: " format "\n" , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "pcmciamtd: " format "\n" , ## arg) - #define DRIVER_DESC "PCMCIA Flash memory card driver" @@ -99,7 +96,9 @@ module_param(mem_type, int, 0); MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)"); -/* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */ +/* read/write{8,16} copy_{from,to} routines with window remapping + * to access whole card + */ static caddr_t remap_window(struct map_info *map, unsigned long to) { struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; @@ -136,7 +135,7 @@ static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs) return d; d.x[0] = readb(addr); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02lx", ofs, addr, d.x[0]); return d; } @@ -151,7 +150,7 @@ static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs) return d; d.x[0] = readw(addr); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04lx", ofs, addr, d.x[0]); return d; } @@ -161,7 +160,7 @@ static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; unsigned long win_size = dev->win_size; - DEBUG(3, "to = %p from = %lu len = %u", to, from, len); + DEBUG(3, "to = %p from = %lu len = %zd", to, from, len); while(len) { int toread = win_size - (from & (win_size-1)); caddr_t addr; @@ -189,7 +188,7 @@ static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long if(!addr) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d.x[0]); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02lx", adr, addr, d.x[0]); writeb(d.x[0], addr); } @@ -200,7 +199,7 @@ static void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long if(!addr) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d.x[0]); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04lx", adr, addr, d.x[0]); writew(d.x[0], addr); } @@ -210,7 +209,7 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; unsigned long win_size = dev->win_size; - DEBUG(3, "to = %lu from = %p len = %u", to, from, len); + DEBUG(3, "to = %lu from = %p len = %zd", to, from, len); while(len) { int towrite = win_size - (to & (win_size-1)); caddr_t addr; @@ -244,7 +243,8 @@ static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) return d; d.x[0] = readb(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02lx", + ofs, win_base + ofs, d.x[0]); return d; } @@ -258,7 +258,8 @@ static map_word pcmcia_read16(struct map_info *map, unsigned long ofs) return d; d.x[0] = readw(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04lx", + ofs, win_base + ofs, d.x[0]); return d; } @@ -270,32 +271,34 @@ static void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from, if(DEV_REMOVED(map)) return; - DEBUG(3, "to = %p from = %lu len = %u", to, from, len); + DEBUG(3, "to = %p from = %lu len = %zd", to, from, len); memcpy_fromio(to, win_base + from, len); } -static void pcmcia_write8(struct map_info *map, u8 d, unsigned long adr) +static void pcmcia_write8(struct map_info *map, map_word d, unsigned long adr) { caddr_t win_base = (caddr_t)map->map_priv_2; if(DEV_REMOVED(map)) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, win_base + adr, d); - writeb(d, win_base + adr); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02lx", + adr, win_base + adr, d.x[0]); + writeb(d.x[0], win_base + adr); } -static void pcmcia_write16(struct map_info *map, u16 d, unsigned long adr) +static void pcmcia_write16(struct map_info *map, map_word d, unsigned long adr) { caddr_t win_base = (caddr_t)map->map_priv_2; if(DEV_REMOVED(map)) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, win_base + adr, d); - writew(d, win_base + adr); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04lx", + adr, win_base + adr, d.x[0]); + writew(d.x[0], win_base + adr); } @@ -306,7 +309,7 @@ static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *f if(DEV_REMOVED(map)) return; - DEBUG(3, "to = %lu from = %p len = %u", to, from, len); + DEBUG(3, "to = %lu from = %p len = %zd", to, from, len); memcpy_toio(win_base + to, from, len); } @@ -375,7 +378,8 @@ static int pcmciamtd_cistpl_jedec(struct pcmcia_device *p_dev, if (!pcmcia_parse_tuple(tuple, &parse)) { cistpl_jedec_t *t = &parse.jedec; for (i = 0; i < t->nid; i++) - DEBUG(2, "JEDEC: 0x%02x 0x%02x", t->id[i].mfr, t->id[i].info); + DEBUG(2, "JEDEC: 0x%02x 0x%02x", + t->id[i].mfr, t->id[i].info); } return -ENOSPC; } @@ -431,7 +435,7 @@ static int pcmciamtd_cistpl_geo(struct pcmcia_device *p_dev, } -static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, int *new_name) +static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *p_dev, int *new_name) { int i; @@ -476,7 +480,8 @@ static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, } DEBUG(1, "Device: Size: %lu Width:%d Name: %s", - dev->pcmcia_map.size, dev->pcmcia_map.bankwidth << 3, dev->mtd_name); + dev->pcmcia_map.size, + dev->pcmcia_map.bankwidth << 3, dev->mtd_name); } @@ -489,7 +494,6 @@ static int pcmciamtd_config(struct pcmcia_device *link) { struct pcmciamtd_dev *dev = link->priv; struct mtd_info *mtd = NULL; - cs_status_t status; win_req_t req; int ret; int i; @@ -513,9 +517,11 @@ static int pcmciamtd_config(struct pcmcia_device *link) if(setvpp == 1) dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp; - /* Request a memory window for PCMCIA. Some architeures can map windows upto the maximum - that PCMCIA can support (64MiB) - this is ideal and we aim for a window the size of the - whole card - otherwise we try smaller windows until we succeed */ + /* Request a memory window for PCMCIA. Some architeures can map windows + * upto the maximum that PCMCIA can support (64MiB) - this is ideal and + * we aim for a window the size of the whole card - otherwise we try + * smaller windows until we succeed + */ req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; @@ -543,7 +549,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) DEBUG(2, "dev->win_size = %d", dev->win_size); if(!dev->win_size) { - err("Cant allocate memory window"); + dev_err(&dev->p_dev->dev, "Cannot allocate memory window\n"); pcmciamtd_release(link); return -ENODEV; } @@ -553,7 +559,8 @@ static int pcmciamtd_config(struct pcmcia_device *link) DEBUG(2, "window handle = 0x%8.8lx", (unsigned long)link->win); dev->win_base = ioremap(req.Base, req.Size); if(!dev->win_base) { - err("ioremap(%lu, %u) failed", req.Base, req.Size); + dev_err(&dev->p_dev->dev, "ioremap(%lu, %u) failed\n", + req.Base, req.Size); pcmciamtd_release(link); return -ENODEV; } @@ -564,7 +571,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) dev->pcmcia_map.map_priv_1 = (unsigned long)dev; dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; - dev->vpp = (vpp) ? vpp : link->socket.socket.Vpp; + dev->vpp = (vpp) ? vpp : link->socket->socket.Vpp; link->conf.Attributes = 0; if(setvpp == 2) { link->conf.Vpp = dev->vpp; @@ -600,7 +607,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) } if(!mtd) { - DEBUG(1, "Cant find an MTD"); + DEBUG(1, "Can not find an MTD"); pcmciamtd_release(link); return -ENODEV; } @@ -611,8 +618,9 @@ static int pcmciamtd_config(struct pcmcia_device *link) if(new_name) { int size = 0; char unit = ' '; - /* Since we are using a default name, make it better by adding in the - size */ + /* Since we are using a default name, make it better by adding + * in the size + */ if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */ size = mtd->size >> 10; unit = 'K'; @@ -642,15 +650,15 @@ static int pcmciamtd_config(struct pcmcia_device *link) if(add_mtd_device(mtd)) { map_destroy(mtd); dev->mtd_info = NULL; - err("Couldnt register MTD device"); + dev_err(&dev->p_dev->dev, + "Could not register the MTD device\n"); pcmciamtd_release(link); return -ENODEV; } - info("mtd%d: %s", mtd->index, mtd->name); + dev_info(&dev->p_dev->dev, "mtd%d: %s\n", mtd->index, mtd->name); return 0; - failed: - err("CS Error, exiting"); + dev_err(&dev->p_dev->dev, "CS Error, exiting\n"); pcmciamtd_release(link); return -ENODEV; } @@ -689,8 +697,9 @@ static void pcmciamtd_detach(struct pcmcia_device *link) if(dev->mtd_info) { del_mtd_device(dev->mtd_info); + dev_info(&dev->p_dev->dev, "mtd%d: Removing\n", + dev->mtd_info->index); map_destroy(dev->mtd_info); - info("mtd%d: Removed", dev->mtd_info->index); } pcmciamtd_release(link); @@ -734,8 +743,11 @@ static struct pcmcia_device_id pcmciamtd_ids[] = { PCMCIA_DEVICE_PROD_ID12("intel", "VALUE SERIES 100 ", 0x40ade711, 0xdf8506d8), PCMCIA_DEVICE_PROD_ID12("KINGMAX TECHNOLOGY INC.", "SRAM 256K Bytes", 0x54d0c69c, 0xad12c29c), PCMCIA_DEVICE_PROD_ID12("Maxtor", "MAXFL MobileMax Flash Memory Card", 0xb68968c8, 0x2dfb47b0), + PCMCIA_DEVICE_PROD_ID123("M-Systems", "M-SYS Flash Memory Card", "(c) M-Systems", 0x7ed2ad87, 0x675dc3fb, 0x7aef3965), + PCMCIA_DEVICE_PROD_ID12("PRETEC", " 2MB SRAM CARD", 0xebf91155, 0x805360ca), PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB101EN20", 0xf9876baf, 0xad0b207b), PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB513EN20", 0xf9876baf, 0xe8d884ad), + PCMCIA_DEVICE_PROD_ID12("SMART Modular Technologies", " 4MB FLASH Card", 0x96fd8277, 0x737a5b05), PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-3000", 0x05ddca47, 0xe7d67bca), PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-4100", 0x05ddca47, 0x7bc32944), /* the following was commented out in pcmcia-cs-3.2.7 */ diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index d9603f7f965..426461a5f0d 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -264,8 +264,11 @@ static int __init physmap_init(void) err = platform_driver_register(&physmap_flash_driver); #ifdef CONFIG_MTD_PHYSMAP_COMPAT - if (err == 0) - platform_device_register(&physmap_flash); + if (err == 0) { + err = platform_device_register(&physmap_flash); + if (err) + platform_driver_unregister(&physmap_flash_driver); + } #endif return err; diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 101ee6ead05..36dbcee1ac2 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -173,12 +173,53 @@ static struct mtd_info * __devinit obsolete_probe(struct of_device *dev, } } +#ifdef CONFIG_MTD_PARTITIONS +/* When partitions are set we look for a linux,part-probe property which + specifies the list of partition probers to use. If none is given then the + default is use. These take precedence over other device tree + information. */ +static const char *part_probe_types_def[] = { "cmdlinepart", "RedBoot", NULL }; +static const char ** __devinit of_get_probes(struct device_node *dp) +{ + const char *cp; + int cplen; + unsigned int l; + unsigned int count; + const char **res; + + cp = of_get_property(dp, "linux,part-probe", &cplen); + if (cp == NULL) + return part_probe_types_def; + + count = 0; + for (l = 0; l != cplen; l++) + if (cp[l] == 0) + count++; + + res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL); + count = 0; + while (cplen > 0) { + res[count] = cp; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + count++; + } + return res; +} + +static void __devinit of_free_probes(const char **probes) +{ + if (probes != part_probe_types_def) + kfree(probes); +} +#endif + static int __devinit of_flash_probe(struct of_device *dev, const struct of_device_id *match) { #ifdef CONFIG_MTD_PARTITIONS - static const char *part_probe_types[] - = { "cmdlinepart", "RedBoot", NULL }; + const char **part_probe_types; #endif struct device_node *dp = dev->node; struct resource res; @@ -218,7 +259,7 @@ static int __devinit of_flash_probe(struct of_device *dev, dev_set_drvdata(&dev->dev, info); - mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); + mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL); if (!mtd_list) goto err_flash_remove; @@ -307,12 +348,14 @@ static int __devinit of_flash_probe(struct of_device *dev, goto err_out; #ifdef CONFIG_MTD_PARTITIONS - /* First look for RedBoot table or partitions on the command - * line, these take precedence over device tree information */ + part_probe_types = of_get_probes(dp); err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0); - if (err < 0) + if (err < 0) { + of_free_probes(part_probe_types); return err; + } + of_free_probes(part_probe_types); #ifdef CONFIG_MTD_OF_PARTS if (err == 0) { diff --git a/drivers/mtd/maps/pismo.c b/drivers/mtd/maps/pismo.c index 60c068db452..eb476b7f8d1 100644 --- a/drivers/mtd/maps/pismo.c +++ b/drivers/mtd/maps/pismo.c @@ -234,6 +234,7 @@ static int __devexit pismo_remove(struct i2c_client *client) /* FIXME: set_vpp needs saner arguments */ pismo_setvpp_remove_fix(pismo); + i2c_set_clientdata(client, NULL); kfree(pismo); return 0; @@ -272,7 +273,7 @@ static int __devinit pismo_probe(struct i2c_client *client, ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom)); if (ret < 0) { dev_err(&client->dev, "error reading EEPROM: %d\n", ret); - return ret; + goto exit_free; } dev_info(&client->dev, "%.15s board found\n", eeprom.board); @@ -283,6 +284,11 @@ static int __devinit pismo_probe(struct i2c_client *client, pdata->cs_addrs[i]); return 0; + + exit_free: + i2c_set_clientdata(client, NULL); + kfree(pismo); + return ret; } static const struct i2c_device_id pismo_id[] = { diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 91dc6331053..dd90880048c 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -63,11 +63,10 @@ static int __init pxa2xx_flash_probe(struct platform_device *pdev) if (!res) return -ENODEV; - info = kmalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL); if (!info) return -ENOMEM; - memset(info, 0, sizeof(struct pxa2xx_flash_info)); info->map.name = (char *) flash->name; info->map.bankwidth = flash->width; info->map.phys = res->start; diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index c82e09bbc5f..03e19c1965c 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -14,7 +14,6 @@ #include <linux/mtd/mtd.h> #include <linux/blkdev.h> #include <linux/blkpg.h> -#include <linux/freezer.h> #include <linux/spinlock.h> #include <linux/hdreg.h> #include <linux/init.h> @@ -25,12 +24,42 @@ #include "mtdcore.h" static LIST_HEAD(blktrans_majors); +static DEFINE_MUTEX(blktrans_ref_mutex); + +void blktrans_dev_release(struct kref *kref) +{ + struct mtd_blktrans_dev *dev = + container_of(kref, struct mtd_blktrans_dev, ref); + + dev->disk->private_data = NULL; + blk_cleanup_queue(dev->rq); + put_disk(dev->disk); + list_del(&dev->list); + kfree(dev); +} + +static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk) +{ + struct mtd_blktrans_dev *dev; + + mutex_lock(&blktrans_ref_mutex); + dev = disk->private_data; + + if (!dev) + goto unlock; + kref_get(&dev->ref); +unlock: + mutex_unlock(&blktrans_ref_mutex); + return dev; +} + +void blktrans_dev_put(struct mtd_blktrans_dev *dev) +{ + mutex_lock(&blktrans_ref_mutex); + kref_put(&dev->ref, blktrans_dev_release); + mutex_unlock(&blktrans_ref_mutex); +} -struct mtd_blkcore_priv { - struct task_struct *thread; - struct request_queue *rq; - spinlock_t queue_lock; -}; static int do_blktrans_request(struct mtd_blktrans_ops *tr, struct mtd_blktrans_dev *dev, @@ -61,7 +90,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, return -EIO; rq_flush_dcache_pages(req); return 0; - case WRITE: if (!tr->writesect) return -EIO; @@ -71,7 +99,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, if (tr->writesect(dev, block, buf)) return -EIO; return 0; - default: printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req)); return -EIO; @@ -80,14 +107,13 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, static int mtd_blktrans_thread(void *arg) { - struct mtd_blktrans_ops *tr = arg; - struct request_queue *rq = tr->blkcore_priv->rq; + struct mtd_blktrans_dev *dev = arg; + struct request_queue *rq = dev->rq; struct request *req = NULL; spin_lock_irq(rq->queue_lock); while (!kthread_should_stop()) { - struct mtd_blktrans_dev *dev; int res; if (!req && !(req = blk_fetch_request(rq))) { @@ -98,13 +124,10 @@ static int mtd_blktrans_thread(void *arg) continue; } - dev = req->rq_disk->private_data; - tr = dev->tr; - spin_unlock_irq(rq->queue_lock); mutex_lock(&dev->lock); - res = do_blktrans_request(tr, dev, req); + res = do_blktrans_request(dev->tr, dev, req); mutex_unlock(&dev->lock); spin_lock_irq(rq->queue_lock); @@ -123,81 +146,112 @@ static int mtd_blktrans_thread(void *arg) static void mtd_blktrans_request(struct request_queue *rq) { - struct mtd_blktrans_ops *tr = rq->queuedata; - wake_up_process(tr->blkcore_priv->thread); -} + struct mtd_blktrans_dev *dev; + struct request *req = NULL; + + dev = rq->queuedata; + if (!dev) + while ((req = blk_fetch_request(rq)) != NULL) + __blk_end_request_all(req, -ENODEV); + else + wake_up_process(dev->thread); +} static int blktrans_open(struct block_device *bdev, fmode_t mode) { - struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; - struct mtd_blktrans_ops *tr = dev->tr; - int ret = -ENODEV; - - if (!get_mtd_device(NULL, dev->mtd->index)) - goto out; - - if (!try_module_get(tr->owner)) - goto out_tr; - - /* FIXME: Locking. A hot pluggable device can go away - (del_mtd_device can be called for it) without its module - being unloaded. */ - dev->mtd->usecount++; - - ret = 0; - if (tr->open && (ret = tr->open(dev))) { - dev->mtd->usecount--; - put_mtd_device(dev->mtd); - out_tr: - module_put(tr->owner); + struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); + int ret; + + if (!dev) + return -ERESTARTSYS; + + mutex_lock(&dev->lock); + + if (!dev->mtd) { + ret = -ENXIO; + goto unlock; } - out: + + ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0; + + /* Take another reference on the device so it won't go away till + last release */ + if (!ret) + kref_get(&dev->ref); +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); return ret; } static int blktrans_release(struct gendisk *disk, fmode_t mode) { - struct mtd_blktrans_dev *dev = disk->private_data; - struct mtd_blktrans_ops *tr = dev->tr; - int ret = 0; + struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); + int ret = -ENXIO; - if (tr->release) - ret = tr->release(dev); + if (!dev) + return ret; - if (!ret) { - dev->mtd->usecount--; - put_mtd_device(dev->mtd); - module_put(tr->owner); - } + mutex_lock(&dev->lock); + + /* Release one reference, we sure its not the last one here*/ + kref_put(&dev->ref, blktrans_dev_release); + if (!dev->mtd) + goto unlock; + + ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0; +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); return ret; } static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) { - struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; + struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); + int ret = -ENXIO; + + if (!dev) + return ret; + + mutex_lock(&dev->lock); + + if (!dev->mtd) + goto unlock; - if (dev->tr->getgeo) - return dev->tr->getgeo(dev, geo); - return -ENOTTY; + ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0; +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); + return ret; } static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { - struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; - struct mtd_blktrans_ops *tr = dev->tr; + struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); + int ret = -ENXIO; + + if (!dev) + return ret; + + mutex_lock(&dev->lock); + + if (!dev->mtd) + goto unlock; switch (cmd) { case BLKFLSBUF: - if (tr->flush) - return tr->flush(dev); - /* The core code did the work, we had nothing to do. */ - return 0; + ret = dev->tr->flush ? dev->tr->flush(dev) : 0; default: - return -ENOTTY; + ret = -ENOTTY; } +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); + return ret; } static const struct block_device_operations mtd_blktrans_ops = { @@ -214,12 +268,14 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) struct mtd_blktrans_dev *d; int last_devnum = -1; struct gendisk *gd; + int ret; if (mutex_trylock(&mtd_table_mutex)) { mutex_unlock(&mtd_table_mutex); BUG(); } + mutex_lock(&blktrans_ref_mutex); list_for_each_entry(d, &tr->devs, list) { if (new->devnum == -1) { /* Use first free number */ @@ -231,6 +287,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) } } else if (d->devnum == new->devnum) { /* Required number taken */ + mutex_unlock(&blktrans_ref_mutex); return -EBUSY; } else if (d->devnum > new->devnum) { /* Required number was free */ @@ -239,24 +296,38 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) } last_devnum = d->devnum; } + + ret = -EBUSY; if (new->devnum == -1) new->devnum = last_devnum+1; - if ((new->devnum << tr->part_bits) > 256) { - return -EBUSY; + /* Check that the device and any partitions will get valid + * minor numbers and that the disk naming code below can cope + * with this number. */ + if (new->devnum > (MINORMASK >> tr->part_bits) || + (tr->part_bits && new->devnum >= 27 * 26)) { + mutex_unlock(&blktrans_ref_mutex); + goto error1; } list_add_tail(&new->list, &tr->devs); added: + mutex_unlock(&blktrans_ref_mutex); + mutex_init(&new->lock); + kref_init(&new->ref); if (!tr->writesect) new->readonly = 1; + /* Create gendisk */ + ret = -ENOMEM; gd = alloc_disk(1 << tr->part_bits); - if (!gd) { - list_del(&new->list); - return -ENOMEM; - } + + if (!gd) + goto error2; + + new->disk = gd; + gd->private_data = new; gd->major = tr->major; gd->first_minor = (new->devnum) << tr->part_bits; gd->fops = &mtd_blktrans_ops; @@ -274,13 +345,35 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) snprintf(gd->disk_name, sizeof(gd->disk_name), "%s%d", tr->name, new->devnum); - /* 2.5 has capacity in units of 512 bytes while still - having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ set_capacity(gd, (new->size * tr->blksize) >> 9); - gd->private_data = new; - new->blkcore_priv = gd; - gd->queue = tr->blkcore_priv->rq; + /* Create the request queue */ + spin_lock_init(&new->queue_lock); + new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock); + + if (!new->rq) + goto error3; + + new->rq->queuedata = new; + blk_queue_logical_block_size(new->rq, tr->blksize); + + if (tr->discard) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, + new->rq); + + gd->queue = new->rq; + + __get_mtd_device(new->mtd); + __module_get(tr->owner); + + /* Create processing thread */ + /* TODO: workqueue ? */ + new->thread = kthread_run(mtd_blktrans_thread, new, + "%s%d", tr->name, new->mtd->index); + if (IS_ERR(new->thread)) { + ret = PTR_ERR(new->thread); + goto error4; + } gd->driverfs_dev = &new->mtd->dev; if (new->readonly) @@ -288,21 +381,65 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) add_disk(gd); + if (new->disk_attributes) { + ret = sysfs_create_group(&disk_to_dev(gd)->kobj, + new->disk_attributes); + WARN_ON(ret); + } return 0; +error4: + module_put(tr->owner); + __put_mtd_device(new->mtd); + blk_cleanup_queue(new->rq); +error3: + put_disk(new->disk); +error2: + list_del(&new->list); +error1: + kfree(new); + return ret; } int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) { + unsigned long flags; + if (mutex_trylock(&mtd_table_mutex)) { mutex_unlock(&mtd_table_mutex); BUG(); } - list_del(&old->list); + /* Stop new requests to arrive */ + del_gendisk(old->disk); + + if (old->disk_attributes) + sysfs_remove_group(&disk_to_dev(old->disk)->kobj, + old->disk_attributes); + + /* Stop the thread */ + kthread_stop(old->thread); + + /* Kill current requests */ + spin_lock_irqsave(&old->queue_lock, flags); + old->rq->queuedata = NULL; + blk_start_queue(old->rq); + spin_unlock_irqrestore(&old->queue_lock, flags); + + /* Ask trans driver for release to the mtd device */ + mutex_lock(&old->lock); + if (old->open && old->tr->release) { + old->tr->release(old); + old->open = 0; + } + + __put_mtd_device(old->mtd); + module_put(old->tr->owner); - del_gendisk(old->blkcore_priv); - put_disk(old->blkcore_priv); + /* At that point, we don't touch the mtd anymore */ + old->mtd = NULL; + mutex_unlock(&old->lock); + blktrans_dev_put(old); return 0; } @@ -335,7 +472,8 @@ static struct mtd_notifier blktrans_notifier = { int register_mtd_blktrans(struct mtd_blktrans_ops *tr) { - int ret, i; + struct mtd_info *mtd; + int ret; /* Register the notifier if/when the first device type is registered, to prevent the link/init ordering from fucking @@ -343,9 +481,6 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) if (!blktrans_notifier.list.next) register_mtd_user(&blktrans_notifier); - tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); - if (!tr->blkcore_priv) - return -ENOMEM; mutex_lock(&mtd_table_mutex); @@ -353,49 +488,20 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) if (ret) { printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", tr->name, tr->major, ret); - kfree(tr->blkcore_priv); mutex_unlock(&mtd_table_mutex); return ret; } - spin_lock_init(&tr->blkcore_priv->queue_lock); - - tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); - if (!tr->blkcore_priv->rq) { - unregister_blkdev(tr->major, tr->name); - kfree(tr->blkcore_priv); - mutex_unlock(&mtd_table_mutex); - return -ENOMEM; - } - - tr->blkcore_priv->rq->queuedata = tr; - blk_queue_logical_block_size(tr->blkcore_priv->rq, tr->blksize); - if (tr->discard) - queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, - tr->blkcore_priv->rq); tr->blkshift = ffs(tr->blksize) - 1; - tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, - "%sd", tr->name); - if (IS_ERR(tr->blkcore_priv->thread)) { - ret = PTR_ERR(tr->blkcore_priv->thread); - blk_cleanup_queue(tr->blkcore_priv->rq); - unregister_blkdev(tr->major, tr->name); - kfree(tr->blkcore_priv); - mutex_unlock(&mtd_table_mutex); - return ret; - } - INIT_LIST_HEAD(&tr->devs); list_add(&tr->list, &blktrans_majors); - for (i=0; i<MAX_MTD_DEVICES; i++) { - if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT) - tr->add_mtd(tr, mtd_table[i]); - } + mtd_for_each_device(mtd) + if (mtd->type != MTD_ABSENT) + tr->add_mtd(tr, mtd); mutex_unlock(&mtd_table_mutex); - return 0; } @@ -405,22 +511,15 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) mutex_lock(&mtd_table_mutex); - /* Clean up the kernel thread */ - kthread_stop(tr->blkcore_priv->thread); - /* Remove it from the list of active majors */ list_del(&tr->list); list_for_each_entry_safe(dev, next, &tr->devs, list) tr->remove_dev(dev); - blk_cleanup_queue(tr->blkcore_priv->rq); unregister_blkdev(tr->major, tr->name); - mutex_unlock(&mtd_table_mutex); - kfree(tr->blkcore_priv); - BUG_ON(!list_empty(&tr->devs)); return 0; } diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 9f41b1a853c..e6edbec609f 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -19,15 +19,15 @@ #include <linux/mutex.h> -static struct mtdblk_dev { - struct mtd_info *mtd; +struct mtdblk_dev { + struct mtd_blktrans_dev mbd; int count; struct mutex cache_mutex; unsigned char *cache_data; unsigned long cache_offset; unsigned int cache_size; enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; -} *mtdblks[MAX_MTD_DEVICES]; +}; static struct mutex mtdblks_lock; @@ -98,7 +98,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, static int write_cached_data (struct mtdblk_dev *mtdblk) { - struct mtd_info *mtd = mtdblk->mtd; + struct mtd_info *mtd = mtdblk->mbd.mtd; int ret; if (mtdblk->cache_state != STATE_DIRTY) @@ -128,7 +128,7 @@ static int write_cached_data (struct mtdblk_dev *mtdblk) static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, int len, const char *buf) { - struct mtd_info *mtd = mtdblk->mtd; + struct mtd_info *mtd = mtdblk->mbd.mtd; unsigned int sect_size = mtdblk->cache_size; size_t retlen; int ret; @@ -198,7 +198,7 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, int len, char *buf) { - struct mtd_info *mtd = mtdblk->mtd; + struct mtd_info *mtd = mtdblk->mbd.mtd; unsigned int sect_size = mtdblk->cache_size; size_t retlen; int ret; @@ -244,16 +244,16 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, static int mtdblock_readsect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) { - struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); return do_cached_read(mtdblk, block<<9, 512, buf); } static int mtdblock_writesect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) { - struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { - mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); + mtdblk->cache_data = vmalloc(mtdblk->mbd.mtd->erasesize); if (!mtdblk->cache_data) return -EINTR; /* -EINTR is not really correct, but it is the best match @@ -266,37 +266,26 @@ static int mtdblock_writesect(struct mtd_blktrans_dev *dev, static int mtdblock_open(struct mtd_blktrans_dev *mbd) { - struct mtdblk_dev *mtdblk; - struct mtd_info *mtd = mbd->mtd; - int dev = mbd->devnum; + struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd); DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); mutex_lock(&mtdblks_lock); - if (mtdblks[dev]) { - mtdblks[dev]->count++; + if (mtdblk->count) { + mtdblk->count++; mutex_unlock(&mtdblks_lock); return 0; } /* OK, it's not open. Create cache info for it */ - mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); - if (!mtdblk) { - mutex_unlock(&mtdblks_lock); - return -ENOMEM; - } - mtdblk->count = 1; - mtdblk->mtd = mtd; - mutex_init(&mtdblk->cache_mutex); mtdblk->cache_state = STATE_EMPTY; - if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) { - mtdblk->cache_size = mtdblk->mtd->erasesize; + if (!(mbd->mtd->flags & MTD_NO_ERASE) && mbd->mtd->erasesize) { + mtdblk->cache_size = mbd->mtd->erasesize; mtdblk->cache_data = NULL; } - mtdblks[dev] = mtdblk; mutex_unlock(&mtdblks_lock); DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); @@ -306,8 +295,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) static int mtdblock_release(struct mtd_blktrans_dev *mbd) { - int dev = mbd->devnum; - struct mtdblk_dev *mtdblk = mtdblks[dev]; + struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd); DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); @@ -318,12 +306,10 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd) mutex_unlock(&mtdblk->cache_mutex); if (!--mtdblk->count) { - /* It was the last usage. Free the device */ - mtdblks[dev] = NULL; - if (mtdblk->mtd->sync) - mtdblk->mtd->sync(mtdblk->mtd); + /* It was the last usage. Free the cache */ + if (mbd->mtd->sync) + mbd->mtd->sync(mbd->mtd); vfree(mtdblk->cache_data); - kfree(mtdblk); } mutex_unlock(&mtdblks_lock); @@ -335,40 +321,40 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd) static int mtdblock_flush(struct mtd_blktrans_dev *dev) { - struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); mutex_lock(&mtdblk->cache_mutex); write_cached_data(mtdblk); mutex_unlock(&mtdblk->cache_mutex); - if (mtdblk->mtd->sync) - mtdblk->mtd->sync(mtdblk->mtd); + if (dev->mtd->sync) + dev->mtd->sync(dev->mtd); return 0; } static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { - struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + struct mtdblk_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return; - dev->mtd = mtd; - dev->devnum = mtd->index; + dev->mbd.mtd = mtd; + dev->mbd.devnum = mtd->index; - dev->size = mtd->size >> 9; - dev->tr = tr; + dev->mbd.size = mtd->size >> 9; + dev->mbd.tr = tr; if (!(mtd->flags & MTD_WRITEABLE)) - dev->readonly = 1; + dev->mbd.readonly = 1; - add_mtd_blktrans_dev(dev); + if (add_mtd_blktrans_dev(&dev->mbd)) + kfree(dev); } static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { del_mtd_blktrans_dev(dev); - kfree(dev); } static struct mtd_blktrans_ops mtdblock_tr = { diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 852165f8b1c..d0d3f79f9d0 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -43,13 +43,13 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) dev->tr = tr; dev->readonly = 1; - add_mtd_blktrans_dev(dev); + if (add_mtd_blktrans_dev(dev)) + kfree(dev); } static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { del_mtd_blktrans_dev(dev); - kfree(dev); } static struct mtd_blktrans_ops mtdblock_tr = { diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5b081cb8435..8bb5e4a6632 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -15,12 +15,15 @@ #include <linux/smp_lock.h> #include <linux/backing-dev.h> #include <linux/compat.h> +#include <linux/mount.h> #include <linux/mtd/mtd.h> #include <linux/mtd/compatmac.h> #include <asm/uaccess.h> +#define MTD_INODE_FS_MAGIC 0x11307854 +static struct vfsmount *mtd_inode_mnt __read_mostly; /* * Data structure to hold the pointer to the mtd device as well @@ -28,6 +31,7 @@ */ struct mtd_file_info { struct mtd_info *mtd; + struct inode *ino; enum mtd_file_modes mode; }; @@ -64,12 +68,10 @@ static int mtd_open(struct inode *inode, struct file *file) int ret = 0; struct mtd_info *mtd; struct mtd_file_info *mfi; + struct inode *mtd_ino; DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); - if (devnum >= MAX_MTD_DEVICES) - return -ENODEV; - /* You can't open the RO devices RW */ if ((file->f_mode & FMODE_WRITE) && (minor & 1)) return -EACCES; @@ -88,11 +90,23 @@ static int mtd_open(struct inode *inode, struct file *file) goto out; } - if (mtd->backing_dev_info) - file->f_mapping->backing_dev_info = mtd->backing_dev_info; + mtd_ino = iget_locked(mtd_inode_mnt->mnt_sb, devnum); + if (!mtd_ino) { + put_mtd_device(mtd); + ret = -ENOMEM; + goto out; + } + if (mtd_ino->i_state & I_NEW) { + mtd_ino->i_private = mtd; + mtd_ino->i_mode = S_IFCHR; + mtd_ino->i_data.backing_dev_info = mtd->backing_dev_info; + unlock_new_inode(mtd_ino); + } + file->f_mapping = mtd_ino->i_mapping; /* You can't open it RW if it's not a writeable device */ if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { + iput(mtd_ino); put_mtd_device(mtd); ret = -EACCES; goto out; @@ -100,10 +114,12 @@ static int mtd_open(struct inode *inode, struct file *file) mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); if (!mfi) { + iput(mtd_ino); put_mtd_device(mtd); ret = -ENOMEM; goto out; } + mfi->ino = mtd_ino; mfi->mtd = mtd; file->private_data = mfi; @@ -125,6 +141,8 @@ static int mtd_close(struct inode *inode, struct file *file) if ((file->f_mode & FMODE_WRITE) && mtd->sync) mtd->sync(mtd); + iput(mfi->ino); + put_mtd_device(mtd); file->private_data = NULL; kfree(mfi); @@ -373,7 +391,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd, if (!mtd->write_oob) ret = -EOPNOTSUPP; else - ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT; + ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT; if (ret) return ret; @@ -482,7 +500,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, { uint32_t ur_idx; struct mtd_erase_region_info *kr; - struct region_info_user *ur = (struct region_info_user *) argp; + struct region_info_user __user *ur = argp; if (get_user(ur_idx, &(ur->regionindex))) return -EFAULT; @@ -954,22 +972,81 @@ static const struct file_operations mtd_fops = { #endif }; +static int mtd_inodefs_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, + struct vfsmount *mnt) +{ + return get_sb_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC, + mnt); +} + +static struct file_system_type mtd_inodefs_type = { + .name = "mtd_inodefs", + .get_sb = mtd_inodefs_get_sb, + .kill_sb = kill_anon_super, +}; + +static void mtdchar_notify_add(struct mtd_info *mtd) +{ +} + +static void mtdchar_notify_remove(struct mtd_info *mtd) +{ + struct inode *mtd_ino = ilookup(mtd_inode_mnt->mnt_sb, mtd->index); + + if (mtd_ino) { + /* Destroy the inode if it exists */ + mtd_ino->i_nlink = 0; + iput(mtd_ino); + } +} + +static struct mtd_notifier mtdchar_notifier = { + .add = mtdchar_notify_add, + .remove = mtdchar_notify_remove, +}; + static int __init init_mtdchar(void) { - int status; + int ret; - status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops); - if (status < 0) { - printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", - MTD_CHAR_MAJOR); + ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, + "mtd", &mtd_fops); + if (ret < 0) { + pr_notice("Can't allocate major number %d for " + "Memory Technology Devices.\n", MTD_CHAR_MAJOR); + return ret; } - return status; + ret = register_filesystem(&mtd_inodefs_type); + if (ret) { + pr_notice("Can't register mtd_inodefs filesystem: %d\n", ret); + goto err_unregister_chdev; + } + + mtd_inode_mnt = kern_mount(&mtd_inodefs_type); + if (IS_ERR(mtd_inode_mnt)) { + ret = PTR_ERR(mtd_inode_mnt); + pr_notice("Error mounting mtd_inodefs filesystem: %d\n", ret); + goto err_unregister_filesystem; + } + register_mtd_user(&mtdchar_notifier); + + return ret; + +err_unregister_filesystem: + unregister_filesystem(&mtd_inodefs_type); +err_unregister_chdev: + __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); + return ret; } static void __exit cleanup_mtdchar(void) { - unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + unregister_mtd_user(&mtdchar_notifier); + mntput(mtd_inode_mnt); + unregister_filesystem(&mtd_inodefs_type); + __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); } module_init(init_mtdchar); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index db6de74082a..7e075621bbf 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -183,10 +183,9 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, } /* make a copy of vecs */ - vecs_copy = kmalloc(sizeof(struct kvec) * count, GFP_KERNEL); + vecs_copy = kmemdup(vecs, sizeof(struct kvec) * count, GFP_KERNEL); if (!vecs_copy) return -ENOMEM; - memcpy(vecs_copy, vecs, sizeof(struct kvec) * count); entry_low = 0; for (i = 0; i < concat->num_subdev; i++) { diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index b177e750efc..a1b8b70d2d0 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -19,7 +19,9 @@ #include <linux/init.h> #include <linux/mtd/compatmac.h> #include <linux/proc_fs.h> +#include <linux/idr.h> #include <linux/backing-dev.h> +#include <linux/gfp.h> #include <linux/mtd/mtd.h> @@ -63,13 +65,18 @@ static struct class mtd_class = { .resume = mtd_cls_resume, }; +static DEFINE_IDR(mtd_idr); + /* These are exported solely for the purpose of mtd_blkdevs.c. You should not use them for _anything_ else */ DEFINE_MUTEX(mtd_table_mutex); -struct mtd_info *mtd_table[MAX_MTD_DEVICES]; - EXPORT_SYMBOL_GPL(mtd_table_mutex); -EXPORT_SYMBOL_GPL(mtd_table); + +struct mtd_info *__mtd_next_device(int i) +{ + return idr_get_next(&mtd_idr, &i); +} +EXPORT_SYMBOL_GPL(__mtd_next_device); static LIST_HEAD(mtd_notifiers); @@ -265,13 +272,13 @@ static struct device_type mtd_devtype = { * Add a device to the list of MTD devices present in the system, and * notify each currently active MTD 'user' of its arrival. Returns * zero on success or 1 on failure, which currently will only happen - * if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16) - * or there's a sysfs error. + * if there is insufficient memory or a sysfs error. */ int add_mtd_device(struct mtd_info *mtd) { - int i; + struct mtd_notifier *not; + int i, error; if (!mtd->backing_dev_info) { switch (mtd->type) { @@ -290,70 +297,73 @@ int add_mtd_device(struct mtd_info *mtd) BUG_ON(mtd->writesize == 0); mutex_lock(&mtd_table_mutex); - for (i=0; i < MAX_MTD_DEVICES; i++) - if (!mtd_table[i]) { - struct mtd_notifier *not; - - mtd_table[i] = mtd; - mtd->index = i; - mtd->usecount = 0; - - if (is_power_of_2(mtd->erasesize)) - mtd->erasesize_shift = ffs(mtd->erasesize) - 1; - else - mtd->erasesize_shift = 0; - - if (is_power_of_2(mtd->writesize)) - mtd->writesize_shift = ffs(mtd->writesize) - 1; - else - mtd->writesize_shift = 0; - - mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; - mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; - - /* Some chips always power up locked. Unlock them now */ - if ((mtd->flags & MTD_WRITEABLE) - && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { - if (mtd->unlock(mtd, 0, mtd->size)) - printk(KERN_WARNING - "%s: unlock failed, " - "writes may not work\n", - mtd->name); - } + do { + if (!idr_pre_get(&mtd_idr, GFP_KERNEL)) + goto fail_locked; + error = idr_get_new(&mtd_idr, mtd, &i); + } while (error == -EAGAIN); - /* Caller should have set dev.parent to match the - * physical device. - */ - mtd->dev.type = &mtd_devtype; - mtd->dev.class = &mtd_class; - mtd->dev.devt = MTD_DEVT(i); - dev_set_name(&mtd->dev, "mtd%d", i); - dev_set_drvdata(&mtd->dev, mtd); - if (device_register(&mtd->dev) != 0) { - mtd_table[i] = NULL; - break; - } + if (error) + goto fail_locked; - if (MTD_DEVT(i)) - device_create(&mtd_class, mtd->dev.parent, - MTD_DEVT(i) + 1, - NULL, "mtd%dro", i); - - DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); - /* No need to get a refcount on the module containing - the notifier, since we hold the mtd_table_mutex */ - list_for_each_entry(not, &mtd_notifiers, list) - not->add(mtd); - - mutex_unlock(&mtd_table_mutex); - /* We _know_ we aren't being removed, because - our caller is still holding us here. So none - of this try_ nonsense, and no bitching about it - either. :) */ - __module_get(THIS_MODULE); - return 0; - } + mtd->index = i; + mtd->usecount = 0; + + if (is_power_of_2(mtd->erasesize)) + mtd->erasesize_shift = ffs(mtd->erasesize) - 1; + else + mtd->erasesize_shift = 0; + + if (is_power_of_2(mtd->writesize)) + mtd->writesize_shift = ffs(mtd->writesize) - 1; + else + mtd->writesize_shift = 0; + + mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; + mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; + + /* Some chips always power up locked. Unlock them now */ + if ((mtd->flags & MTD_WRITEABLE) + && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { + if (mtd->unlock(mtd, 0, mtd->size)) + printk(KERN_WARNING + "%s: unlock failed, writes may not work\n", + mtd->name); + } + + /* Caller should have set dev.parent to match the + * physical device. + */ + mtd->dev.type = &mtd_devtype; + mtd->dev.class = &mtd_class; + mtd->dev.devt = MTD_DEVT(i); + dev_set_name(&mtd->dev, "mtd%d", i); + dev_set_drvdata(&mtd->dev, mtd); + if (device_register(&mtd->dev) != 0) + goto fail_added; + + if (MTD_DEVT(i)) + device_create(&mtd_class, mtd->dev.parent, + MTD_DEVT(i) + 1, + NULL, "mtd%dro", i); + + DEBUG(0, "mtd: Giving out device %d to %s\n", i, mtd->name); + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each_entry(not, &mtd_notifiers, list) + not->add(mtd); + + mutex_unlock(&mtd_table_mutex); + /* We _know_ we aren't being removed, because + our caller is still holding us here. So none + of this try_ nonsense, and no bitching about it + either. :) */ + __module_get(THIS_MODULE); + return 0; +fail_added: + idr_remove(&mtd_idr, i); +fail_locked: mutex_unlock(&mtd_table_mutex); return 1; } @@ -371,31 +381,34 @@ int add_mtd_device(struct mtd_info *mtd) int del_mtd_device (struct mtd_info *mtd) { int ret; + struct mtd_notifier *not; mutex_lock(&mtd_table_mutex); - if (mtd_table[mtd->index] != mtd) { + if (idr_find(&mtd_idr, mtd->index) != mtd) { ret = -ENODEV; - } else if (mtd->usecount) { + goto out_error; + } + + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each_entry(not, &mtd_notifiers, list) + not->remove(mtd); + + if (mtd->usecount) { printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", mtd->index, mtd->name, mtd->usecount); ret = -EBUSY; } else { - struct mtd_notifier *not; - device_unregister(&mtd->dev); - /* No need to get a refcount on the module containing - the notifier, since we hold the mtd_table_mutex */ - list_for_each_entry(not, &mtd_notifiers, list) - not->remove(mtd); - - mtd_table[mtd->index] = NULL; + idr_remove(&mtd_idr, mtd->index); module_put(THIS_MODULE); ret = 0; } +out_error: mutex_unlock(&mtd_table_mutex); return ret; } @@ -411,7 +424,7 @@ int del_mtd_device (struct mtd_info *mtd) void register_mtd_user (struct mtd_notifier *new) { - int i; + struct mtd_info *mtd; mutex_lock(&mtd_table_mutex); @@ -419,9 +432,8 @@ void register_mtd_user (struct mtd_notifier *new) __module_get(THIS_MODULE); - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i]) - new->add(mtd_table[i]); + mtd_for_each_device(mtd) + new->add(mtd); mutex_unlock(&mtd_table_mutex); } @@ -438,15 +450,14 @@ void register_mtd_user (struct mtd_notifier *new) int unregister_mtd_user (struct mtd_notifier *old) { - int i; + struct mtd_info *mtd; mutex_lock(&mtd_table_mutex); module_put(THIS_MODULE); - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i]) - old->remove(mtd_table[i]); + mtd_for_each_device(mtd) + old->remove(mtd); list_del(&old->list); mutex_unlock(&mtd_table_mutex); @@ -468,42 +479,56 @@ int unregister_mtd_user (struct mtd_notifier *old) struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) { - struct mtd_info *ret = NULL; - int i, err = -ENODEV; + struct mtd_info *ret = NULL, *other; + int err = -ENODEV; mutex_lock(&mtd_table_mutex); if (num == -1) { - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i] == mtd) - ret = mtd_table[i]; - } else if (num >= 0 && num < MAX_MTD_DEVICES) { - ret = mtd_table[num]; + mtd_for_each_device(other) { + if (other == mtd) { + ret = mtd; + break; + } + } + } else if (num >= 0) { + ret = idr_find(&mtd_idr, num); if (mtd && mtd != ret) ret = NULL; } - if (!ret) - goto out_unlock; - - if (!try_module_get(ret->owner)) - goto out_unlock; - - if (ret->get_device) { - err = ret->get_device(ret); - if (err) - goto out_put; + if (!ret) { + ret = ERR_PTR(err); + goto out; } - ret->usecount++; + err = __get_mtd_device(ret); + if (err) + ret = ERR_PTR(err); +out: mutex_unlock(&mtd_table_mutex); return ret; +} -out_put: - module_put(ret->owner); -out_unlock: - mutex_unlock(&mtd_table_mutex); - return ERR_PTR(err); + +int __get_mtd_device(struct mtd_info *mtd) +{ + int err; + + if (!try_module_get(mtd->owner)) + return -ENODEV; + + if (mtd->get_device) { + + err = mtd->get_device(mtd); + + if (err) { + module_put(mtd->owner); + return err; + } + } + mtd->usecount++; + return 0; } /** @@ -517,14 +542,14 @@ out_unlock: struct mtd_info *get_mtd_device_nm(const char *name) { - int i, err = -ENODEV; - struct mtd_info *mtd = NULL; + int err = -ENODEV; + struct mtd_info *mtd = NULL, *other; mutex_lock(&mtd_table_mutex); - for (i = 0; i < MAX_MTD_DEVICES; i++) { - if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { - mtd = mtd_table[i]; + mtd_for_each_device(other) { + if (!strcmp(name, other->name)) { + mtd = other; break; } } @@ -554,14 +579,19 @@ out_unlock: void put_mtd_device(struct mtd_info *mtd) { - int c; - mutex_lock(&mtd_table_mutex); - c = --mtd->usecount; + __put_mtd_device(mtd); + mutex_unlock(&mtd_table_mutex); + +} + +void __put_mtd_device(struct mtd_info *mtd) +{ + --mtd->usecount; + BUG_ON(mtd->usecount < 0); + if (mtd->put_device) mtd->put_device(mtd); - mutex_unlock(&mtd_table_mutex); - BUG_ON(c < 0); module_put(mtd->owner); } @@ -599,7 +629,9 @@ EXPORT_SYMBOL_GPL(add_mtd_device); EXPORT_SYMBOL_GPL(del_mtd_device); EXPORT_SYMBOL_GPL(get_mtd_device); EXPORT_SYMBOL_GPL(get_mtd_device_nm); +EXPORT_SYMBOL_GPL(__get_mtd_device); EXPORT_SYMBOL_GPL(put_mtd_device); +EXPORT_SYMBOL_GPL(__put_mtd_device); EXPORT_SYMBOL_GPL(register_mtd_user); EXPORT_SYMBOL_GPL(unregister_mtd_user); EXPORT_SYMBOL_GPL(default_mtd_writev); @@ -611,14 +643,9 @@ EXPORT_SYMBOL_GPL(default_mtd_writev); static struct proc_dir_entry *proc_mtd; -static inline int mtd_proc_info (char *buf, int i) +static inline int mtd_proc_info(char *buf, struct mtd_info *this) { - struct mtd_info *this = mtd_table[i]; - - if (!this) - return 0; - - return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", i, + return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", this->index, (unsigned long long)this->size, this->erasesize, this->name); } @@ -626,15 +653,15 @@ static inline int mtd_proc_info (char *buf, int i) static int mtd_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data_unused) { - int len, l, i; + struct mtd_info *mtd; + int len, l; off_t begin = 0; mutex_lock(&mtd_table_mutex); len = sprintf(page, "dev: size erasesize name\n"); - for (i=0; i< MAX_MTD_DEVICES; i++) { - - l = mtd_proc_info(page + len, i); + mtd_for_each_device(mtd) { + l = mtd_proc_info(page + len, mtd); len += l; if (len+begin > off+count) goto done; diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index a33251f4b87..6a64fdebc89 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -8,4 +8,9 @@ should not use them for _anything_ else */ extern struct mutex mtd_table_mutex; -extern struct mtd_info *mtd_table[MAX_MTD_DEVICES]; +extern struct mtd_info *__mtd_next_device(int i); + +#define mtd_for_each_device(mtd) \ + for ((mtd) = __mtd_next_device(0); \ + (mtd) != NULL; \ + (mtd) = __mtd_next_device(mtd->index + 1)) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 92e12df0917..328313c3dcc 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -429,11 +429,6 @@ static int __init mtdoops_init(void) mtd_index = simple_strtoul(mtddev, &endp, 0); if (*endp == '\0') cxt->mtd_index = mtd_index; - if (cxt->mtd_index > MAX_MTD_DEVICES) { - printk(KERN_ERR "mtdoops: invalid mtd device number (%u) given\n", - mtd_index); - return -EINVAL; - } cxt->oops_buf = vmalloc(record_size); if (!cxt->oops_buf) { diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index 7c003191fca..bd9a443ccf6 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c @@ -152,18 +152,12 @@ int get_sb_mtd(struct file_system_type *fs_type, int flags, DEBUG(1, "MTDSB: mtd:%%s, name \"%s\"\n", dev_name + 4); - for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { - mtd = get_mtd_device(NULL, mtdnr); - if (!IS_ERR(mtd)) { - if (!strcmp(mtd->name, dev_name + 4)) - return get_sb_mtd_aux( - fs_type, flags, - dev_name, data, mtd, - fill_super, mnt); - - put_mtd_device(mtd); - } - } + mtd = get_mtd_device_nm(dev_name + 4); + if (!IS_ERR(mtd)) + return get_sb_mtd_aux( + fs_type, flags, + dev_name, data, mtd, + fill_super, mnt); printk(KERN_NOTICE "MTD:" " MTD device with name \"%s\" not found.\n", diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 42e5ea49e97..98a04b3c952 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -2,11 +2,23 @@ menuconfig MTD_NAND tristate "NAND Device Support" depends on MTD select MTD_NAND_IDS + select MTD_NAND_ECC help This enables support for accessing all type of NAND flash devices. For further information see <http://www.linux-mtd.infradead.org/doc/nand.html>. +config MTD_NAND_ECC + tristate + +config MTD_NAND_ECC_SMC + bool "NAND ECC Smart Media byte order" + depends on MTD_NAND_ECC + default n + help + Software ECC according to the Smart Media Specification. + The original Linux implementation had byte 0 and 1 swapped. + if MTD_NAND config MTD_NAND_VERIFY_WRITE @@ -18,12 +30,9 @@ config MTD_NAND_VERIFY_WRITE device thinks the write was successful, a bit could have been flipped accidentally due to device wear or something else. -config MTD_NAND_ECC_SMC - bool "NAND ECC Smart Media byte order" +config MTD_SM_COMMON + tristate default n - help - Software ECC according to the Smart Media Specification. - The original Linux implementation had byte 0 and 1 swapped. config MTD_NAND_MUSEUM_IDS bool "Enable chip ids for obsolete ancient NAND devices" @@ -41,6 +50,23 @@ config MTD_NAND_AUTCPU12 This enables the driver for the autronix autcpu12 board to access the SmartMediaCard. +config MTD_NAND_DENALI + depends on PCI + tristate "Support Denali NAND controller on Intel Moorestown" + help + Enable the driver for NAND flash on Intel Moorestown, using the + Denali NAND controller core. + +config MTD_NAND_DENALI_SCRATCH_REG_ADDR + hex "Denali NAND size scratch register address" + default "0xFF108018" + help + Some platforms place the NAND chip size in a scratch register + because (some versions of) the driver aren't able to automatically + determine the size of certain chips. Set the address of the + scratch register here to enable this feature. On Intel Moorestown + boards, the scratch register is at 0xFF108018. + config MTD_NAND_EDB7312 tristate "Support for Cirrus Logic EBD7312 evaluation board" depends on ARCH_EDB7312 @@ -95,15 +121,21 @@ config MTD_NAND_OMAP_PREFETCH_DMA or in DMA interrupt mode. Say y for DMA mode or MPU mode will be used -config MTD_NAND_TS7250 - tristate "NAND Flash device on TS-7250 board" - depends on MACH_TS72XX - help - Support for NAND flash on Technologic Systems TS-7250 platform. - config MTD_NAND_IDS tristate +config MTD_NAND_RICOH + tristate "Ricoh xD card reader" + default n + depends on PCI + select MTD_SM_COMMON + help + Enable support for Ricoh R5C852 xD card reader + You also need to enable ether + NAND SSFDC (SmartMedia) read only translation layer' or new + expermental, readwrite + 'SmartMedia/xD new translation layer' + config MTD_NAND_AU1550 tristate "Au1550/1200 NAND support" depends on SOC_AU1200 || SOC_AU1550 @@ -358,8 +390,6 @@ config MTD_NAND_ATMEL_ECC_NONE If unsure, say N - endchoice - endchoice config MTD_NAND_PXA3xx @@ -442,6 +472,13 @@ config MTD_NAND_FSL_UPM Enables support for NAND Flash chips wired onto Freescale PowerPC processor localbus with User-Programmable Machine support. +config MTD_NAND_MPC5121_NFC + tristate "MPC5121 built-in NAND Flash Controller support" + depends on PPC_MPC512x + help + This enables the driver for the NAND flash controller on the + MPC5121 SoC. + config MTD_NAND_MXC tristate "MXC NAND support" depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 @@ -481,11 +518,11 @@ config MTD_NAND_SOCRATES help Enables support for NAND Flash chips wired onto Socrates board. -config MTD_NAND_W90P910 - tristate "Support for NAND on w90p910 evaluation board." +config MTD_NAND_NUC900 + tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards." depends on ARCH_W90X900 && MTD_PARTITIONS help This enables the driver for the NAND Flash on evaluation board based - on w90p910. + on w90p910 / NUC9xx. endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1407bd14401..e8ab884ba47 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -2,13 +2,16 @@ # linux/drivers/nand/Makefile # -obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o +obj-$(CONFIG_MTD_NAND) += nand.o +obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o +obj-$(CONFIG_MTD_NAND_DENALI) += denali.o obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o @@ -19,7 +22,6 @@ obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o -obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o @@ -39,8 +41,10 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o -obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o +obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o +obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o +obj-$(CONFIG_MTD_NAND_RICOH) += r852.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 2d6773281fd..8691e0482ed 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -49,7 +49,7 @@ #define TIMEOUT HZ -static struct usb_device_id alauda_table [] = { +static const struct usb_device_id alauda_table[] = { { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */ { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */ { } diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 524e6c9e067..04d30887ca7 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -474,7 +474,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) } /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1)) { + if (nand_scan_ident(mtd, 1, NULL)) { res = -ENXIO; goto err_scan_ident; } diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 43d46e42404..3ffe05db492 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -451,7 +451,7 @@ static int __init au1xxx_nand_init(void) u32 nand_phys; /* Allocate memory for MTD device structure and private data */ - au1550_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + au1550_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!au1550_mtd) { printk("Unable to allocate NAND MTD dev structure.\n"); return -ENOMEM; @@ -460,10 +460,6 @@ static int __init au1xxx_nand_init(void) /* Get pointer to private data */ this = (struct nand_chip *)(&au1550_mtd[1]); - /* Initialize structures */ - memset(au1550_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ au1550_mtd->priv = this; au1550_mtd->owner = THIS_MODULE; @@ -544,7 +540,7 @@ static int __init au1xxx_nand_init(void) } nand_phys = (mem_staddr << 4) & 0xFFFC0000; - p_nand = (void __iomem *)ioremap(nand_phys, 0x1000); + p_nand = ioremap(nand_phys, 0x1000); /* make controller and MTD agree */ if (NAND_CS == 0) @@ -589,7 +585,7 @@ static int __init au1xxx_nand_init(void) return 0; outio: - iounmap((void *)p_nand); + iounmap(p_nand); outmem: kfree(au1550_mtd); @@ -610,7 +606,7 @@ static void __exit au1550_cleanup(void) kfree(au1550_mtd); /* Unmap */ - iounmap((void *)p_nand); + iounmap(p_nand); } module_exit(au1550_cleanup); diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index c997f98eeb3..dfe262c726f 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -13,7 +13,6 @@ *****************************************************************************/ /* ---- Include Files ---------------------------------------------------- */ -#include <linux/version.h> #include <linux/module.h> #include <linux/types.h> #include <linux/init.h> @@ -447,7 +446,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) * layout we'll be using. */ - err = nand_scan_ident(board_mtd, 1); + err = nand_scan_ident(board_mtd, 1, NULL); if (err) { printk(KERN_ERR "nand_scan failed: %d\n", err); iounmap(bcm_umi_io_base); diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 8506e7e606f..2974995e194 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -68,6 +68,27 @@ #define DRV_AUTHOR "Bryan Wu <bryan.wu@analog.com>" #define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver" +/* NFC_STAT Masks */ +#define NBUSY 0x01 /* Not Busy */ +#define WB_FULL 0x02 /* Write Buffer Full */ +#define PG_WR_STAT 0x04 /* Page Write Pending */ +#define PG_RD_STAT 0x08 /* Page Read Pending */ +#define WB_EMPTY 0x10 /* Write Buffer Empty */ + +/* NFC_IRQSTAT Masks */ +#define NBUSYIRQ 0x01 /* Not Busy IRQ */ +#define WB_OVF 0x02 /* Write Buffer Overflow */ +#define WB_EDGE 0x04 /* Write Buffer Edge Detect */ +#define RD_RDY 0x08 /* Read Data Ready */ +#define WR_DONE 0x10 /* Page Write Done */ + +/* NFC_RST Masks */ +#define ECC_RST 0x01 /* ECC (and NFC counters) Reset */ + +/* NFC_PGCTL Masks */ +#define PG_RD_START 0x01 /* Page Read Start */ +#define PG_WR_START 0x02 /* Page Write Start */ + #ifdef CONFIG_MTD_NAND_BF5XX_HWECC static int hardware_ecc = 1; #else @@ -487,7 +508,7 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, * transferred to generate the correct ECC register * values. */ - bfin_write_NFC_RST(0x1); + bfin_write_NFC_RST(ECC_RST); SSYNC(); disable_dma(CH_NFC); @@ -497,7 +518,7 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, set_dma_config(CH_NFC, 0x0); set_dma_start_addr(CH_NFC, (unsigned long) buf); -/* The DMAs have different size on BF52x and BF54x */ + /* The DMAs have different size on BF52x and BF54x */ #ifdef CONFIG_BF52x set_dma_x_count(CH_NFC, (page_size >> 1)); set_dma_x_modify(CH_NFC, 2); @@ -517,9 +538,9 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, /* Start PAGE read/write operation */ if (is_read) - bfin_write_NFC_PGCTL(0x1); + bfin_write_NFC_PGCTL(PG_RD_START); else - bfin_write_NFC_PGCTL(0x2); + bfin_write_NFC_PGCTL(PG_WR_START); wait_for_completion(&info->dma_completion); } diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index e5a9f9ccea6..db1dfc5a1b1 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -762,7 +762,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); /* Scan to find existence of the device */ - if (nand_scan_ident(mtd, 2)) { + if (nand_scan_ident(mtd, 2, NULL)) { err = -ENXIO; goto out_irq; } @@ -849,7 +849,7 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev) kfree(mtd); } -static struct pci_device_id cafe_nand_tbl[] = { +static const struct pci_device_id cafe_nand_tbl[] = { { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, PCI_ANY_ID, PCI_ANY_ID }, { } diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 76e2dc8e62f..9c9d893affe 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -567,8 +567,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) goto err_nomem; } - vaddr = ioremap(res1->start, res1->end - res1->start); - base = ioremap(res2->start, res2->end - res2->start); + vaddr = ioremap(res1->start, resource_size(res1)); + base = ioremap(res2->start, resource_size(res2)); if (!vaddr || !base) { dev_err(&pdev->dev, "ioremap failed\n"); ret = -EINVAL; @@ -691,7 +691,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) spin_unlock_irq(&davinci_nand_lock); /* Scan to find existence of the device(s) */ - ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1); + ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1, NULL); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); goto err_scan; diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c new file mode 100644 index 00000000000..ca03428b59c --- /dev/null +++ b/drivers/mtd/nand/denali.c @@ -0,0 +1,2134 @@ +/* + * NAND Flash Controller Device Driver + * Copyright © 2009-2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/mutex.h> +#include <linux/pci.h> +#include <linux/mtd/mtd.h> +#include <linux/module.h> + +#include "denali.h" + +MODULE_LICENSE("GPL"); + +/* We define a module parameter that allows the user to override + * the hardware and decide what timing mode should be used. + */ +#define NAND_DEFAULT_TIMINGS -1 + +static int onfi_timing_mode = NAND_DEFAULT_TIMINGS; +module_param(onfi_timing_mode, int, S_IRUGO); +MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting. -1 indicates" + " use default timings"); + +#define DENALI_NAND_NAME "denali-nand" + +/* We define a macro here that combines all interrupts this driver uses into + * a single constant value, for convenience. */ +#define DENALI_IRQ_ALL (INTR_STATUS0__DMA_CMD_COMP | \ + INTR_STATUS0__ECC_TRANSACTION_DONE | \ + INTR_STATUS0__ECC_ERR | \ + INTR_STATUS0__PROGRAM_FAIL | \ + INTR_STATUS0__LOAD_COMP | \ + INTR_STATUS0__PROGRAM_COMP | \ + INTR_STATUS0__TIME_OUT | \ + INTR_STATUS0__ERASE_FAIL | \ + INTR_STATUS0__RST_COMP | \ + INTR_STATUS0__ERASE_COMP) + +/* indicates whether or not the internal value for the flash bank is + valid or not */ +#define CHIP_SELECT_INVALID -1 + +#define SUPPORT_8BITECC 1 + +/* This macro divides two integers and rounds fractional values up + * to the nearest integer value. */ +#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y))) + +/* this macro allows us to convert from an MTD structure to our own + * device context (denali) structure. + */ +#define mtd_to_denali(m) container_of(m, struct denali_nand_info, mtd) + +/* These constants are defined by the driver to enable common driver + configuration options. */ +#define SPARE_ACCESS 0x41 +#define MAIN_ACCESS 0x42 +#define MAIN_SPARE_ACCESS 0x43 + +#define DENALI_READ 0 +#define DENALI_WRITE 0x100 + +/* types of device accesses. We can issue commands and get status */ +#define COMMAND_CYCLE 0 +#define ADDR_CYCLE 1 +#define STATUS_CYCLE 2 + +/* this is a helper macro that allows us to + * format the bank into the proper bits for the controller */ +#define BANK(x) ((x) << 24) + +/* List of platforms this NAND controller has be integrated into */ +static const struct pci_device_id denali_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, + { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, + { /* end: all zeroes */ } +}; + + +/* these are static lookup tables that give us easy access to + registers in the NAND controller. + */ +static const uint32_t intr_status_addresses[4] = {INTR_STATUS0, + INTR_STATUS1, + INTR_STATUS2, + INTR_STATUS3}; + +static const uint32_t device_reset_banks[4] = {DEVICE_RESET__BANK0, + DEVICE_RESET__BANK1, + DEVICE_RESET__BANK2, + DEVICE_RESET__BANK3}; + +static const uint32_t operation_timeout[4] = {INTR_STATUS0__TIME_OUT, + INTR_STATUS1__TIME_OUT, + INTR_STATUS2__TIME_OUT, + INTR_STATUS3__TIME_OUT}; + +static const uint32_t reset_complete[4] = {INTR_STATUS0__RST_COMP, + INTR_STATUS1__RST_COMP, + INTR_STATUS2__RST_COMP, + INTR_STATUS3__RST_COMP}; + +/* specifies the debug level of the driver */ +static int nand_debug_level = 0; + +/* forward declarations */ +static void clear_interrupts(struct denali_nand_info *denali); +static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask); +static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask); +static uint32_t read_interrupt_status(struct denali_nand_info *denali); + +#define DEBUG_DENALI 0 + +/* This is a wrapper for writing to the denali registers. + * this allows us to create debug information so we can + * observe how the driver is programming the device. + * it uses standard linux convention for (val, addr) */ +static void denali_write32(uint32_t value, void *addr) +{ + iowrite32(value, addr); + +#if DEBUG_DENALI + printk(KERN_ERR "wrote: 0x%x -> 0x%x\n", value, (uint32_t)((uint32_t)addr & 0x1fff)); +#endif +} + +/* Certain operations for the denali NAND controller use an indexed mode to read/write + data. The operation is performed by writing the address value of the command to + the device memory followed by the data. This function abstracts this common + operation. +*/ +static void index_addr(struct denali_nand_info *denali, uint32_t address, uint32_t data) +{ + denali_write32(address, denali->flash_mem); + denali_write32(data, denali->flash_mem + 0x10); +} + +/* Perform an indexed read of the device */ +static void index_addr_read_data(struct denali_nand_info *denali, + uint32_t address, uint32_t *pdata) +{ + denali_write32(address, denali->flash_mem); + *pdata = ioread32(denali->flash_mem + 0x10); +} + +/* We need to buffer some data for some of the NAND core routines. + * The operations manage buffering that data. */ +static void reset_buf(struct denali_nand_info *denali) +{ + denali->buf.head = denali->buf.tail = 0; +} + +static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte) +{ + BUG_ON(denali->buf.tail >= sizeof(denali->buf.buf)); + denali->buf.buf[denali->buf.tail++] = byte; +} + +/* reads the status of the device */ +static void read_status(struct denali_nand_info *denali) +{ + uint32_t cmd = 0x0; + + /* initialize the data buffer to store status */ + reset_buf(denali); + + /* initiate a device status read */ + cmd = MODE_11 | BANK(denali->flash_bank); + index_addr(denali, cmd | COMMAND_CYCLE, 0x70); + denali_write32(cmd | STATUS_CYCLE, denali->flash_mem); + + /* update buffer with status value */ + write_byte_to_buf(denali, ioread32(denali->flash_mem + 0x10)); + +#if DEBUG_DENALI + printk("device reporting status value of 0x%2x\n", denali->buf.buf[0]); +#endif +} + +/* resets a specific device connected to the core */ +static void reset_bank(struct denali_nand_info *denali) +{ + uint32_t irq_status = 0; + uint32_t irq_mask = reset_complete[denali->flash_bank] | + operation_timeout[denali->flash_bank]; + int bank = 0; + + clear_interrupts(denali); + + bank = device_reset_banks[denali->flash_bank]; + denali_write32(bank, denali->flash_reg + DEVICE_RESET); + + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status & operation_timeout[denali->flash_bank]) + { + printk(KERN_ERR "reset bank failed.\n"); + } +} + +/* Reset the flash controller */ +static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali) +{ + uint32_t i; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) + denali_write32(reset_complete[i] | operation_timeout[i], + denali->flash_reg + intr_status_addresses[i]); + + for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) { + denali_write32(device_reset_banks[i], denali->flash_reg + DEVICE_RESET); + while (!(ioread32(denali->flash_reg + intr_status_addresses[i]) & + (reset_complete[i] | operation_timeout[i]))) + ; + if (ioread32(denali->flash_reg + intr_status_addresses[i]) & + operation_timeout[i]) + nand_dbg_print(NAND_DBG_WARN, + "NAND Reset operation timed out on bank %d\n", i); + } + + for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) + denali_write32(reset_complete[i] | operation_timeout[i], + denali->flash_reg + intr_status_addresses[i]); + + return PASS; +} + +/* this routine calculates the ONFI timing values for a given mode and programs + * the clocking register accordingly. The mode is determined by the get_onfi_nand_para + routine. + */ +static void NAND_ONFi_Timing_Mode(struct denali_nand_info *denali, uint16_t mode) +{ + uint16_t Trea[6] = {40, 30, 25, 20, 20, 16}; + uint16_t Trp[6] = {50, 25, 17, 15, 12, 10}; + uint16_t Treh[6] = {30, 15, 15, 10, 10, 7}; + uint16_t Trc[6] = {100, 50, 35, 30, 25, 20}; + uint16_t Trhoh[6] = {0, 15, 15, 15, 15, 15}; + uint16_t Trloh[6] = {0, 0, 0, 0, 5, 5}; + uint16_t Tcea[6] = {100, 45, 30, 25, 25, 25}; + uint16_t Tadl[6] = {200, 100, 100, 100, 70, 70}; + uint16_t Trhw[6] = {200, 100, 100, 100, 100, 100}; + uint16_t Trhz[6] = {200, 100, 100, 100, 100, 100}; + uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60}; + uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15}; + + uint16_t TclsRising = 1; + uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid; + uint16_t dv_window = 0; + uint16_t en_lo, en_hi; + uint16_t acc_clks; + uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + en_lo = CEIL_DIV(Trp[mode], CLK_X); + en_hi = CEIL_DIV(Treh[mode], CLK_X); +#if ONFI_BLOOM_TIME + if ((en_hi * CLK_X) < (Treh[mode] + 2)) + en_hi++; +#endif + + if ((en_lo + en_hi) * CLK_X < Trc[mode]) + en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X); + + if ((en_lo + en_hi) < CLK_MULTI) + en_lo += CLK_MULTI - en_lo - en_hi; + + while (dv_window < 8) { + data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode]; + + data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode]; + + data_invalid = + data_invalid_rhoh < + data_invalid_rloh ? data_invalid_rhoh : data_invalid_rloh; + + dv_window = data_invalid - Trea[mode]; + + if (dv_window < 8) + en_lo++; + } + + acc_clks = CEIL_DIV(Trea[mode], CLK_X); + + while (((acc_clks * CLK_X) - Trea[mode]) < 3) + acc_clks++; + + if ((data_invalid - acc_clks * CLK_X) < 2) + nand_dbg_print(NAND_DBG_WARN, "%s, Line %d: Warning!\n", + __FILE__, __LINE__); + + addr_2_data = CEIL_DIV(Tadl[mode], CLK_X); + re_2_we = CEIL_DIV(Trhw[mode], CLK_X); + re_2_re = CEIL_DIV(Trhz[mode], CLK_X); + we_2_re = CEIL_DIV(Twhr[mode], CLK_X); + cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X); + if (!TclsRising) + cs_cnt = CEIL_DIV(Tcs[mode], CLK_X); + if (cs_cnt == 0) + cs_cnt = 1; + + if (Tcea[mode]) { + while (((cs_cnt * CLK_X) + Trea[mode]) < Tcea[mode]) + cs_cnt++; + } + +#if MODE5_WORKAROUND + if (mode == 5) + acc_clks = 5; +#endif + + /* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */ + if ((ioread32(denali->flash_reg + MANUFACTURER_ID) == 0) && + (ioread32(denali->flash_reg + DEVICE_ID) == 0x88)) + acc_clks = 6; + + denali_write32(acc_clks, denali->flash_reg + ACC_CLKS); + denali_write32(re_2_we, denali->flash_reg + RE_2_WE); + denali_write32(re_2_re, denali->flash_reg + RE_2_RE); + denali_write32(we_2_re, denali->flash_reg + WE_2_RE); + denali_write32(addr_2_data, denali->flash_reg + ADDR_2_DATA); + denali_write32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT); + denali_write32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT); + denali_write32(cs_cnt, denali->flash_reg + CS_SETUP_CNT); +} + +/* configures the initial ECC settings for the controller */ +static void set_ecc_config(struct denali_nand_info *denali) +{ +#if SUPPORT_8BITECC + if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) < 4096) || + (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) <= 128)) + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + + if ((ioread32(denali->flash_reg + ECC_CORRECTION) & ECC_CORRECTION__VALUE) + == 1) { + denali->dev_info.wECCBytesPerSector = 4; + denali->dev_info.wECCBytesPerSector *= denali->dev_info.wDevicesConnected; + denali->dev_info.wNumPageSpareFlag = + denali->dev_info.wPageSpareSize - + denali->dev_info.wPageDataSize / + (ECC_SECTOR_SIZE * denali->dev_info.wDevicesConnected) * + denali->dev_info.wECCBytesPerSector + - denali->dev_info.wSpareSkipBytes; + } else { + denali->dev_info.wECCBytesPerSector = + (ioread32(denali->flash_reg + ECC_CORRECTION) & + ECC_CORRECTION__VALUE) * 13 / 8; + if ((denali->dev_info.wECCBytesPerSector) % 2 == 0) + denali->dev_info.wECCBytesPerSector += 2; + else + denali->dev_info.wECCBytesPerSector += 1; + + denali->dev_info.wECCBytesPerSector *= denali->dev_info.wDevicesConnected; + denali->dev_info.wNumPageSpareFlag = denali->dev_info.wPageSpareSize - + denali->dev_info.wPageDataSize / + (ECC_SECTOR_SIZE * denali->dev_info.wDevicesConnected) * + denali->dev_info.wECCBytesPerSector + - denali->dev_info.wSpareSkipBytes; + } +} + +/* queries the NAND device to see what ONFI modes it supports. */ +static uint16_t get_onfi_nand_para(struct denali_nand_info *denali) +{ + int i; + uint16_t blks_lun_l, blks_lun_h, n_of_luns; + uint32_t blockperlun, id; + + denali_write32(DEVICE_RESET__BANK0, denali->flash_reg + DEVICE_RESET); + + while (!((ioread32(denali->flash_reg + INTR_STATUS0) & + INTR_STATUS0__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS0) & + INTR_STATUS0__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS0) & INTR_STATUS0__RST_COMP) { + denali_write32(DEVICE_RESET__BANK1, denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__RST_COMP) { + denali_write32(DEVICE_RESET__BANK2, + denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__RST_COMP) { + denali_write32(DEVICE_RESET__BANK3, + denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS3) & + INTR_STATUS3__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS3) & + INTR_STATUS3__TIME_OUT))) + ; + } else { + printk(KERN_ERR "Getting a time out for bank 2!\n"); + } + } else { + printk(KERN_ERR "Getting a time out for bank 1!\n"); + } + } + + denali_write32(INTR_STATUS0__TIME_OUT, denali->flash_reg + INTR_STATUS0); + denali_write32(INTR_STATUS1__TIME_OUT, denali->flash_reg + INTR_STATUS1); + denali_write32(INTR_STATUS2__TIME_OUT, denali->flash_reg + INTR_STATUS2); + denali_write32(INTR_STATUS3__TIME_OUT, denali->flash_reg + INTR_STATUS3); + + denali->dev_info.wONFIDevFeatures = + ioread32(denali->flash_reg + ONFI_DEVICE_FEATURES); + denali->dev_info.wONFIOptCommands = + ioread32(denali->flash_reg + ONFI_OPTIONAL_COMMANDS); + denali->dev_info.wONFITimingMode = + ioread32(denali->flash_reg + ONFI_TIMING_MODE); + denali->dev_info.wONFIPgmCacheTimingMode = + ioread32(denali->flash_reg + ONFI_PGM_CACHE_TIMING_MODE); + + n_of_luns = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) & + ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS; + blks_lun_l = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L); + blks_lun_h = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U); + + blockperlun = (blks_lun_h << 16) | blks_lun_l; + + denali->dev_info.wTotalBlocks = n_of_luns * blockperlun; + + if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) & + ONFI_TIMING_MODE__VALUE)) + return FAIL; + + for (i = 5; i > 0; i--) { + if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) & (0x01 << i)) + break; + } + + NAND_ONFi_Timing_Mode(denali, i); + + index_addr(denali, MODE_11 | 0, 0x90); + index_addr(denali, MODE_11 | 1, 0); + + for (i = 0; i < 3; i++) + index_addr_read_data(denali, MODE_11 | 2, &id); + + nand_dbg_print(NAND_DBG_DEBUG, "3rd ID: 0x%x\n", id); + + denali->dev_info.MLCDevice = id & 0x0C; + + /* By now, all the ONFI devices we know support the page cache */ + /* rw feature. So here we enable the pipeline_rw_ahead feature */ + /* iowrite32(1, denali->flash_reg + CACHE_WRITE_ENABLE); */ + /* iowrite32(1, denali->flash_reg + CACHE_READ_ENABLE); */ + + return PASS; +} + +static void get_samsung_nand_para(struct denali_nand_info *denali) +{ + uint8_t no_of_planes; + uint32_t blk_size; + uint64_t plane_size, capacity; + uint32_t id_bytes[5]; + int i; + + index_addr(denali, (uint32_t)(MODE_11 | 0), 0x90); + index_addr(denali, (uint32_t)(MODE_11 | 1), 0); + for (i = 0; i < 5; i++) + index_addr_read_data(denali, (uint32_t)(MODE_11 | 2), &id_bytes[i]); + + nand_dbg_print(NAND_DBG_DEBUG, + "ID bytes: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + id_bytes[0], id_bytes[1], id_bytes[2], + id_bytes[3], id_bytes[4]); + + if ((id_bytes[1] & 0xff) == 0xd3) { /* Samsung K9WAG08U1A */ + /* Set timing register values according to datasheet */ + denali_write32(5, denali->flash_reg + ACC_CLKS); + denali_write32(20, denali->flash_reg + RE_2_WE); + denali_write32(12, denali->flash_reg + WE_2_RE); + denali_write32(14, denali->flash_reg + ADDR_2_DATA); + denali_write32(3, denali->flash_reg + RDWR_EN_LO_CNT); + denali_write32(2, denali->flash_reg + RDWR_EN_HI_CNT); + denali_write32(2, denali->flash_reg + CS_SETUP_CNT); + } + + no_of_planes = 1 << ((id_bytes[4] & 0x0c) >> 2); + plane_size = (uint64_t)64 << ((id_bytes[4] & 0x70) >> 4); + blk_size = 64 << ((ioread32(denali->flash_reg + DEVICE_PARAM_1) & 0x30) >> 4); + capacity = (uint64_t)128 * plane_size * no_of_planes; + + do_div(capacity, blk_size); + denali->dev_info.wTotalBlocks = capacity; +} + +static void get_toshiba_nand_para(struct denali_nand_info *denali) +{ + void __iomem *scratch_reg; + uint32_t tmp; + + /* Workaround to fix a controller bug which reports a wrong */ + /* spare area size for some kind of Toshiba NAND device */ + if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) && + (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64)) { + denali_write32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + tmp = ioread32(denali->flash_reg + DEVICES_CONNECTED) * + ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + denali_write32(tmp, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); +#if SUPPORT_15BITECC + denali_write32(15, denali->flash_reg + ECC_CORRECTION); +#elif SUPPORT_8BITECC + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + } + + /* As Toshiba NAND can not provide it's block number, */ + /* so here we need user to provide the correct block */ + /* number in a scratch register before the Linux NAND */ + /* driver is loaded. If no valid value found in the scratch */ + /* register, then we use default block number value */ + scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); + if (!scratch_reg) { + printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", + __FILE__, __LINE__); + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } else { + nand_dbg_print(NAND_DBG_WARN, + "Spectra: ioremap reg address: 0x%p\n", scratch_reg); + denali->dev_info.wTotalBlocks = 1 << ioread8(scratch_reg); + if (denali->dev_info.wTotalBlocks < 512) + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + iounmap(scratch_reg); + } +} + +static void get_hynix_nand_para(struct denali_nand_info *denali) +{ + void __iomem *scratch_reg; + uint32_t main_size, spare_size; + + switch (denali->dev_info.wDeviceID) { + case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */ + case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */ + denali_write32(128, denali->flash_reg + PAGES_PER_BLOCK); + denali_write32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE); + denali_write32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + main_size = 4096 * ioread32(denali->flash_reg + DEVICES_CONNECTED); + spare_size = 224 * ioread32(denali->flash_reg + DEVICES_CONNECTED); + denali_write32(main_size, denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); + denali_write32(spare_size, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); + denali_write32(0, denali->flash_reg + DEVICE_WIDTH); +#if SUPPORT_15BITECC + denali_write32(15, denali->flash_reg + ECC_CORRECTION); +#elif SUPPORT_8BITECC + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + denali->dev_info.MLCDevice = 1; + break; + default: + nand_dbg_print(NAND_DBG_WARN, + "Spectra: Unknown Hynix NAND (Device ID: 0x%x)." + "Will use default parameter values instead.\n", + denali->dev_info.wDeviceID); + } + + scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); + if (!scratch_reg) { + printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", + __FILE__, __LINE__); + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } else { + nand_dbg_print(NAND_DBG_WARN, + "Spectra: ioremap reg address: 0x%p\n", scratch_reg); + denali->dev_info.wTotalBlocks = 1 << ioread8(scratch_reg); + if (denali->dev_info.wTotalBlocks < 512) + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + iounmap(scratch_reg); + } +} + +/* determines how many NAND chips are connected to the controller. Note for + Intel CE4100 devices we don't support more than one device. + */ +static void find_valid_banks(struct denali_nand_info *denali) +{ + uint32_t id[LLD_MAX_FLASH_BANKS]; + int i; + + denali->total_used_banks = 1; + for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) { + index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 0), 0x90); + index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 1), 0); + index_addr_read_data(denali, (uint32_t)(MODE_11 | (i << 24) | 2), &id[i]); + + nand_dbg_print(NAND_DBG_DEBUG, + "Return 1st ID for bank[%d]: %x\n", i, id[i]); + + if (i == 0) { + if (!(id[i] & 0x0ff)) + break; /* WTF? */ + } else { + if ((id[i] & 0x0ff) == (id[0] & 0x0ff)) + denali->total_used_banks++; + else + break; + } + } + + if (denali->platform == INTEL_CE4100) + { + /* Platform limitations of the CE4100 device limit + * users to a single chip solution for NAND. + * Multichip support is not enabled. + */ + if (denali->total_used_banks != 1) + { + printk(KERN_ERR "Sorry, Intel CE4100 only supports " + "a single NAND device.\n"); + BUG(); + } + } + nand_dbg_print(NAND_DBG_DEBUG, + "denali->total_used_banks: %d\n", denali->total_used_banks); +} + +static void detect_partition_feature(struct denali_nand_info *denali) +{ + if (ioread32(denali->flash_reg + FEATURES) & FEATURES__PARTITION) { + if ((ioread32(denali->flash_reg + PERM_SRC_ID_1) & + PERM_SRC_ID_1__SRCID) == SPECTRA_PARTITION_ID) { + denali->dev_info.wSpectraStartBlock = + ((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & + MIN_MAX_BANK_1__MIN_VALUE) * + denali->dev_info.wTotalBlocks) + + + (ioread32(denali->flash_reg + MIN_BLK_ADDR_1) & + MIN_BLK_ADDR_1__VALUE); + + denali->dev_info.wSpectraEndBlock = + (((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & + MIN_MAX_BANK_1__MAX_VALUE) >> 2) * + denali->dev_info.wTotalBlocks) + + + (ioread32(denali->flash_reg + MAX_BLK_ADDR_1) & + MAX_BLK_ADDR_1__VALUE); + + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + + if (denali->dev_info.wSpectraEndBlock >= + denali->dev_info.wTotalBlocks) { + denali->dev_info.wSpectraEndBlock = + denali->dev_info.wTotalBlocks - 1; + } + + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } else { + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + denali->dev_info.wSpectraStartBlock = SPECTRA_START_BLOCK; + denali->dev_info.wSpectraEndBlock = + denali->dev_info.wTotalBlocks - 1; + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } + } else { + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + denali->dev_info.wSpectraStartBlock = SPECTRA_START_BLOCK; + denali->dev_info.wSpectraEndBlock = denali->dev_info.wTotalBlocks - 1; + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } +} + +static void dump_device_info(struct denali_nand_info *denali) +{ + nand_dbg_print(NAND_DBG_DEBUG, "denali->dev_info:\n"); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceMaker: 0x%x\n", + denali->dev_info.wDeviceMaker); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceID: 0x%x\n", + denali->dev_info.wDeviceID); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceType: 0x%x\n", + denali->dev_info.wDeviceType); + nand_dbg_print(NAND_DBG_DEBUG, "SpectraStartBlock: %d\n", + denali->dev_info.wSpectraStartBlock); + nand_dbg_print(NAND_DBG_DEBUG, "SpectraEndBlock: %d\n", + denali->dev_info.wSpectraEndBlock); + nand_dbg_print(NAND_DBG_DEBUG, "TotalBlocks: %d\n", + denali->dev_info.wTotalBlocks); + nand_dbg_print(NAND_DBG_DEBUG, "PagesPerBlock: %d\n", + denali->dev_info.wPagesPerBlock); + nand_dbg_print(NAND_DBG_DEBUG, "PageSize: %d\n", + denali->dev_info.wPageSize); + nand_dbg_print(NAND_DBG_DEBUG, "PageDataSize: %d\n", + denali->dev_info.wPageDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "PageSpareSize: %d\n", + denali->dev_info.wPageSpareSize); + nand_dbg_print(NAND_DBG_DEBUG, "NumPageSpareFlag: %d\n", + denali->dev_info.wNumPageSpareFlag); + nand_dbg_print(NAND_DBG_DEBUG, "ECCBytesPerSector: %d\n", + denali->dev_info.wECCBytesPerSector); + nand_dbg_print(NAND_DBG_DEBUG, "BlockSize: %d\n", + denali->dev_info.wBlockSize); + nand_dbg_print(NAND_DBG_DEBUG, "BlockDataSize: %d\n", + denali->dev_info.wBlockDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "DataBlockNum: %d\n", + denali->dev_info.wDataBlockNum); + nand_dbg_print(NAND_DBG_DEBUG, "PlaneNum: %d\n", + denali->dev_info.bPlaneNum); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceMainAreaSize: %d\n", + denali->dev_info.wDeviceMainAreaSize); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceSpareAreaSize: %d\n", + denali->dev_info.wDeviceSpareAreaSize); + nand_dbg_print(NAND_DBG_DEBUG, "DevicesConnected: %d\n", + denali->dev_info.wDevicesConnected); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceWidth: %d\n", + denali->dev_info.wDeviceWidth); + nand_dbg_print(NAND_DBG_DEBUG, "HWRevision: 0x%x\n", + denali->dev_info.wHWRevision); + nand_dbg_print(NAND_DBG_DEBUG, "HWFeatures: 0x%x\n", + denali->dev_info.wHWFeatures); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIDevFeatures: 0x%x\n", + denali->dev_info.wONFIDevFeatures); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIOptCommands: 0x%x\n", + denali->dev_info.wONFIOptCommands); + nand_dbg_print(NAND_DBG_DEBUG, "ONFITimingMode: 0x%x\n", + denali->dev_info.wONFITimingMode); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIPgmCacheTimingMode: 0x%x\n", + denali->dev_info.wONFIPgmCacheTimingMode); + nand_dbg_print(NAND_DBG_DEBUG, "MLCDevice: %s\n", + denali->dev_info.MLCDevice ? "Yes" : "No"); + nand_dbg_print(NAND_DBG_DEBUG, "SpareSkipBytes: %d\n", + denali->dev_info.wSpareSkipBytes); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageNumber: %d\n", + denali->dev_info.nBitsInPageNumber); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageDataSize: %d\n", + denali->dev_info.nBitsInPageDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInBlockDataSize: %d\n", + denali->dev_info.nBitsInBlockDataSize); +} + +static uint16_t NAND_Read_Device_ID(struct denali_nand_info *denali) +{ + uint16_t status = PASS; + uint8_t no_of_planes; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + denali->dev_info.wDeviceMaker = ioread32(denali->flash_reg + MANUFACTURER_ID); + denali->dev_info.wDeviceID = ioread32(denali->flash_reg + DEVICE_ID); + denali->dev_info.bDeviceParam0 = ioread32(denali->flash_reg + DEVICE_PARAM_0); + denali->dev_info.bDeviceParam1 = ioread32(denali->flash_reg + DEVICE_PARAM_1); + denali->dev_info.bDeviceParam2 = ioread32(denali->flash_reg + DEVICE_PARAM_2); + + denali->dev_info.MLCDevice = ioread32(denali->flash_reg + DEVICE_PARAM_0) & 0x0c; + + if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) & + ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */ + if (FAIL == get_onfi_nand_para(denali)) + return FAIL; + } else if (denali->dev_info.wDeviceMaker == 0xEC) { /* Samsung NAND */ + get_samsung_nand_para(denali); + } else if (denali->dev_info.wDeviceMaker == 0x98) { /* Toshiba NAND */ + get_toshiba_nand_para(denali); + } else if (denali->dev_info.wDeviceMaker == 0xAD) { /* Hynix NAND */ + get_hynix_nand_para(denali); + } else { + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } + + nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" + "acc_clks: %d, re_2_we: %d, we_2_re: %d," + "addr_2_data: %d, rdwr_en_lo_cnt: %d, " + "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n", + ioread32(denali->flash_reg + ACC_CLKS), + ioread32(denali->flash_reg + RE_2_WE), + ioread32(denali->flash_reg + WE_2_RE), + ioread32(denali->flash_reg + ADDR_2_DATA), + ioread32(denali->flash_reg + RDWR_EN_LO_CNT), + ioread32(denali->flash_reg + RDWR_EN_HI_CNT), + ioread32(denali->flash_reg + CS_SETUP_CNT)); + + denali->dev_info.wHWRevision = ioread32(denali->flash_reg + REVISION); + denali->dev_info.wHWFeatures = ioread32(denali->flash_reg + FEATURES); + + denali->dev_info.wDeviceMainAreaSize = + ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE); + denali->dev_info.wDeviceSpareAreaSize = + ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + + denali->dev_info.wPageDataSize = + ioread32(denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); + + /* Note: When using the Micon 4K NAND device, the controller will report + * Page Spare Size as 216 bytes. But Micron's Spec say it's 218 bytes. + * And if force set it to 218 bytes, the controller can not work + * correctly. So just let it be. But keep in mind that this bug may + * cause + * other problems in future. - Yunpeng 2008-10-10 + */ + denali->dev_info.wPageSpareSize = + ioread32(denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); + + denali->dev_info.wPagesPerBlock = ioread32(denali->flash_reg + PAGES_PER_BLOCK); + + denali->dev_info.wPageSize = + denali->dev_info.wPageDataSize + denali->dev_info.wPageSpareSize; + denali->dev_info.wBlockSize = + denali->dev_info.wPageSize * denali->dev_info.wPagesPerBlock; + denali->dev_info.wBlockDataSize = + denali->dev_info.wPagesPerBlock * denali->dev_info.wPageDataSize; + + denali->dev_info.wDeviceWidth = ioread32(denali->flash_reg + DEVICE_WIDTH); + denali->dev_info.wDeviceType = + ((ioread32(denali->flash_reg + DEVICE_WIDTH) > 0) ? 16 : 8); + + denali->dev_info.wDevicesConnected = ioread32(denali->flash_reg + DEVICES_CONNECTED); + + denali->dev_info.wSpareSkipBytes = + ioread32(denali->flash_reg + SPARE_AREA_SKIP_BYTES) * + denali->dev_info.wDevicesConnected; + + denali->dev_info.nBitsInPageNumber = + ilog2(denali->dev_info.wPagesPerBlock); + denali->dev_info.nBitsInPageDataSize = + ilog2(denali->dev_info.wPageDataSize); + denali->dev_info.nBitsInBlockDataSize = + ilog2(denali->dev_info.wBlockDataSize); + + set_ecc_config(denali); + + no_of_planes = ioread32(denali->flash_reg + NUMBER_OF_PLANES) & + NUMBER_OF_PLANES__VALUE; + + switch (no_of_planes) { + case 0: + case 1: + case 3: + case 7: + denali->dev_info.bPlaneNum = no_of_planes + 1; + break; + default: + status = FAIL; + break; + } + + find_valid_banks(denali); + + detect_partition_feature(denali); + + dump_device_info(denali); + + /* If the user specified to override the default timings + * with a specific ONFI mode, we apply those changes here. + */ + if (onfi_timing_mode != NAND_DEFAULT_TIMINGS) + { + NAND_ONFi_Timing_Mode(denali, onfi_timing_mode); + } + + return status; +} + +static void NAND_LLD_Enable_Disable_Interrupts(struct denali_nand_info *denali, + uint16_t INT_ENABLE) +{ + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + if (INT_ENABLE) + denali_write32(1, denali->flash_reg + GLOBAL_INT_ENABLE); + else + denali_write32(0, denali->flash_reg + GLOBAL_INT_ENABLE); +} + +/* validation function to verify that the controlling software is making + a valid request + */ +static inline bool is_flash_bank_valid(int flash_bank) +{ + return (flash_bank >= 0 && flash_bank < 4); +} + +static void denali_irq_init(struct denali_nand_info *denali) +{ + uint32_t int_mask = 0; + + /* Disable global interrupts */ + NAND_LLD_Enable_Disable_Interrupts(denali, false); + + int_mask = DENALI_IRQ_ALL; + + /* Clear all status bits */ + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS0); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS1); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS2); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS3); + + denali_irq_enable(denali, int_mask); +} + +static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali) +{ + NAND_LLD_Enable_Disable_Interrupts(denali, false); + free_irq(irqnum, denali); +} + +static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask) +{ + denali_write32(int_mask, denali->flash_reg + INTR_EN0); + denali_write32(int_mask, denali->flash_reg + INTR_EN1); + denali_write32(int_mask, denali->flash_reg + INTR_EN2); + denali_write32(int_mask, denali->flash_reg + INTR_EN3); +} + +/* This function only returns when an interrupt that this driver cares about + * occurs. This is to reduce the overhead of servicing interrupts + */ +static inline uint32_t denali_irq_detected(struct denali_nand_info *denali) +{ + return (read_interrupt_status(denali) & DENALI_IRQ_ALL); +} + +/* Interrupts are cleared by writing a 1 to the appropriate status bit */ +static inline void clear_interrupt(struct denali_nand_info *denali, uint32_t irq_mask) +{ + uint32_t intr_status_reg = 0; + + intr_status_reg = intr_status_addresses[denali->flash_bank]; + + denali_write32(irq_mask, denali->flash_reg + intr_status_reg); +} + +static void clear_interrupts(struct denali_nand_info *denali) +{ + uint32_t status = 0x0; + spin_lock_irq(&denali->irq_lock); + + status = read_interrupt_status(denali); + +#if DEBUG_DENALI + denali->irq_debug_array[denali->idx++] = 0x30000000 | status; + denali->idx %= 32; +#endif + + denali->irq_status = 0x0; + spin_unlock_irq(&denali->irq_lock); +} + +static uint32_t read_interrupt_status(struct denali_nand_info *denali) +{ + uint32_t intr_status_reg = 0; + + intr_status_reg = intr_status_addresses[denali->flash_bank]; + + return ioread32(denali->flash_reg + intr_status_reg); +} + +#if DEBUG_DENALI +static void print_irq_log(struct denali_nand_info *denali) +{ + int i = 0; + + printk("ISR debug log index = %X\n", denali->idx); + for (i = 0; i < 32; i++) + { + printk("%08X: %08X\n", i, denali->irq_debug_array[i]); + } +} +#endif + +/* This is the interrupt service routine. It handles all interrupts + * sent to this device. Note that on CE4100, this is a shared + * interrupt. + */ +static irqreturn_t denali_isr(int irq, void *dev_id) +{ + struct denali_nand_info *denali = dev_id; + uint32_t irq_status = 0x0; + irqreturn_t result = IRQ_NONE; + + spin_lock(&denali->irq_lock); + + /* check to see if a valid NAND chip has + * been selected. + */ + if (is_flash_bank_valid(denali->flash_bank)) + { + /* check to see if controller generated + * the interrupt, since this is a shared interrupt */ + if ((irq_status = denali_irq_detected(denali)) != 0) + { +#if DEBUG_DENALI + denali->irq_debug_array[denali->idx++] = 0x10000000 | irq_status; + denali->idx %= 32; + + printk("IRQ status = 0x%04x\n", irq_status); +#endif + /* handle interrupt */ + /* first acknowledge it */ + clear_interrupt(denali, irq_status); + /* store the status in the device context for someone + to read */ + denali->irq_status |= irq_status; + /* notify anyone who cares that it happened */ + complete(&denali->complete); + /* tell the OS that we've handled this */ + result = IRQ_HANDLED; + } + } + spin_unlock(&denali->irq_lock); + return result; +} +#define BANK(x) ((x) << 24) + +static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask) +{ + unsigned long comp_res = 0; + uint32_t intr_status = 0; + bool retry = false; + unsigned long timeout = msecs_to_jiffies(1000); + + do + { +#if DEBUG_DENALI + printk("waiting for 0x%x\n", irq_mask); +#endif + comp_res = wait_for_completion_timeout(&denali->complete, timeout); + spin_lock_irq(&denali->irq_lock); + intr_status = denali->irq_status; + +#if DEBUG_DENALI + denali->irq_debug_array[denali->idx++] = 0x20000000 | (irq_mask << 16) | intr_status; + denali->idx %= 32; +#endif + + if (intr_status & irq_mask) + { + denali->irq_status &= ~irq_mask; + spin_unlock_irq(&denali->irq_lock); +#if DEBUG_DENALI + if (retry) printk("status on retry = 0x%x\n", intr_status); +#endif + /* our interrupt was detected */ + break; + } + else + { + /* these are not the interrupts you are looking for - + need to wait again */ + spin_unlock_irq(&denali->irq_lock); +#if DEBUG_DENALI + print_irq_log(denali); + printk("received irq nobody cared: irq_status = 0x%x," + " irq_mask = 0x%x, timeout = %ld\n", intr_status, irq_mask, comp_res); +#endif + retry = true; + } + } while (comp_res != 0); + + if (comp_res == 0) + { + /* timeout */ + printk(KERN_ERR "timeout occurred, status = 0x%x, mask = 0x%x\n", + intr_status, irq_mask); + + intr_status = 0; + } + return intr_status; +} + +/* This helper function setups the registers for ECC and whether or not + the spare area will be transfered. */ +static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en, + bool transfer_spare) +{ + int ecc_en_flag = 0, transfer_spare_flag = 0; + + /* set ECC, transfer spare bits if needed */ + ecc_en_flag = ecc_en ? ECC_ENABLE__FLAG : 0; + transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0; + + /* Enable spare area/ECC per user's request. */ + denali_write32(ecc_en_flag, denali->flash_reg + ECC_ENABLE); + denali_write32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG); +} + +/* sends a pipeline command operation to the controller. See the Denali NAND + controller's user guide for more information (section 4.2.3.6). + */ +static int denali_send_pipeline_cmd(struct denali_nand_info *denali, bool ecc_en, + bool transfer_spare, int access_type, + int op) +{ + int status = PASS; + uint32_t addr = 0x0, cmd = 0x0, page_count = 1, irq_status = 0, + irq_mask = 0; + + if (op == DENALI_READ) irq_mask = INTR_STATUS0__LOAD_COMP; + else if (op == DENALI_WRITE) irq_mask = 0; + else BUG(); + + setup_ecc_for_xfer(denali, ecc_en, transfer_spare); + +#if DEBUG_DENALI + spin_lock_irq(&denali->irq_lock); + denali->irq_debug_array[denali->idx++] = 0x40000000 | ioread32(denali->flash_reg + ECC_ENABLE) | (access_type << 4); + denali->idx %= 32; + spin_unlock_irq(&denali->irq_lock); +#endif + + + /* clear interrupts */ + clear_interrupts(denali); + + addr = BANK(denali->flash_bank) | denali->page; + + if (op == DENALI_WRITE && access_type != SPARE_ACCESS) + { + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) + { + /* read spare area */ + cmd = MODE_10 | addr; + index_addr(denali, (uint32_t)cmd, access_type); + + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + else if (op == DENALI_READ) + { + /* setup page read request for access type */ + cmd = MODE_10 | addr; + index_addr(denali, (uint32_t)cmd, access_type); + + /* page 33 of the NAND controller spec indicates we should not + use the pipeline commands in Spare area only mode. So we + don't. + */ + if (access_type == SPARE_ACCESS) + { + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + else + { + index_addr(denali, (uint32_t)cmd, 0x2000 | op | page_count); + + /* wait for command to be accepted + * can always use status0 bit as the mask is identical for each + * bank. */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "cmd, page, addr on timeout " + "(0x%x, 0x%x, 0x%x)\n", cmd, denali->page, addr); + status = FAIL; + } + else + { + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + } + } + return status; +} + +/* helper function that simply writes a buffer to the flash */ +static int write_data_to_flash_mem(struct denali_nand_info *denali, const uint8_t *buf, + int len) +{ + uint32_t i = 0, *buf32; + + /* verify that the len is a multiple of 4. see comment in + * read_data_from_flash_mem() */ + BUG_ON((len % 4) != 0); + + /* write the data to the flash memory */ + buf32 = (uint32_t *)buf; + for (i = 0; i < len / 4; i++) + { + denali_write32(*buf32++, denali->flash_mem + 0x10); + } + return i*4; /* intent is to return the number of bytes read */ +} + +/* helper function that simply reads a buffer from the flash */ +static int read_data_from_flash_mem(struct denali_nand_info *denali, uint8_t *buf, + int len) +{ + uint32_t i = 0, *buf32; + + /* we assume that len will be a multiple of 4, if not + * it would be nice to know about it ASAP rather than + * have random failures... + * + * This assumption is based on the fact that this + * function is designed to be used to read flash pages, + * which are typically multiples of 4... + */ + + BUG_ON((len % 4) != 0); + + /* transfer the data from the flash */ + buf32 = (uint32_t *)buf; + for (i = 0; i < len / 4; i++) + { + *buf32++ = ioread32(denali->flash_mem + 0x10); + } + return i*4; /* intent is to return the number of bytes read */ +} + +/* writes OOB data to the device */ +static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__PROGRAM_COMP | + INTR_STATUS0__PROGRAM_FAIL; + int status = 0; + + denali->page = page; + + if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS, + DENALI_WRITE) == PASS) + { + write_data_to_flash_mem(denali, buf, mtd->oobsize); + +#if DEBUG_DENALI + spin_lock_irq(&denali->irq_lock); + denali->irq_debug_array[denali->idx++] = 0x80000000 | mtd->oobsize; + denali->idx %= 32; + spin_unlock_irq(&denali->irq_lock); +#endif + + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "OOB write failed\n"); + status = -EIO; + } + } + else + { + printk(KERN_ERR "unable to send pipeline command\n"); + status = -EIO; + } + return status; +} + +/* reads OOB data from the device */ +static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_mask = INTR_STATUS0__LOAD_COMP, irq_status = 0, addr = 0x0, cmd = 0x0; + + denali->page = page; + +#if DEBUG_DENALI + printk("read_oob %d\n", page); +#endif + if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS, + DENALI_READ) == PASS) + { + read_data_from_flash_mem(denali, buf, mtd->oobsize); + + /* wait for command to be accepted + * can always use status0 bit as the mask is identical for each + * bank. */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "page on OOB timeout %d\n", denali->page); + } + + /* We set the device back to MAIN_ACCESS here as I observed + * instability with the controller if you do a block erase + * and the last transaction was a SPARE_ACCESS. Block erase + * is reliable (according to the MTD test infrastructure) + * if you are in MAIN_ACCESS. + */ + addr = BANK(denali->flash_bank) | denali->page; + cmd = MODE_10 | addr; + index_addr(denali, (uint32_t)cmd, MAIN_ACCESS); + +#if DEBUG_DENALI + spin_lock_irq(&denali->irq_lock); + denali->irq_debug_array[denali->idx++] = 0x60000000 | mtd->oobsize; + denali->idx %= 32; + spin_unlock_irq(&denali->irq_lock); +#endif + } +} + +/* this function examines buffers to see if they contain data that + * indicate that the buffer is part of an erased region of flash. + */ +bool is_erased(uint8_t *buf, int len) +{ + int i = 0; + for (i = 0; i < len; i++) + { + if (buf[i] != 0xFF) + { + return false; + } + } + return true; +} +#define ECC_SECTOR_SIZE 512 + +#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12) +#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET)) +#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK) +#define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO)) +#define ECC_ERR_DEVICE(x) ((x) & ERR_CORRECTION_INFO__DEVICE_NR >> 8) +#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) + +static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, + uint8_t *oobbuf, uint32_t irq_status) +{ + bool check_erased_page = false; + + if (irq_status & INTR_STATUS0__ECC_ERR) + { + /* read the ECC errors. we'll ignore them for now */ + uint32_t err_address = 0, err_correction_info = 0; + uint32_t err_byte = 0, err_sector = 0, err_device = 0; + uint32_t err_correction_value = 0; + + do + { + err_address = ioread32(denali->flash_reg + + ECC_ERROR_ADDRESS); + err_sector = ECC_SECTOR(err_address); + err_byte = ECC_BYTE(err_address); + + + err_correction_info = ioread32(denali->flash_reg + + ERR_CORRECTION_INFO); + err_correction_value = + ECC_CORRECTION_VALUE(err_correction_info); + err_device = ECC_ERR_DEVICE(err_correction_info); + + if (ECC_ERROR_CORRECTABLE(err_correction_info)) + { + /* offset in our buffer is computed as: + sector number * sector size + offset in + sector + */ + int offset = err_sector * ECC_SECTOR_SIZE + + err_byte; + if (offset < denali->mtd.writesize) + { + /* correct the ECC error */ + buf[offset] ^= err_correction_value; + denali->mtd.ecc_stats.corrected++; + } + else + { + /* bummer, couldn't correct the error */ + printk(KERN_ERR "ECC offset invalid\n"); + denali->mtd.ecc_stats.failed++; + } + } + else + { + /* if the error is not correctable, need to + * look at the page to see if it is an erased page. + * if so, then it's not a real ECC error */ + check_erased_page = true; + } + +#if DEBUG_DENALI + printk("Detected ECC error in page %d: err_addr = 0x%08x," + " info to fix is 0x%08x\n", denali->page, err_address, + err_correction_info); +#endif + } while (!ECC_LAST_ERR(err_correction_info)); + } + return check_erased_page; +} + +/* programs the controller to either enable/disable DMA transfers */ +static void denali_enable_dma(struct denali_nand_info *denali, bool en) +{ + uint32_t reg_val = 0x0; + + if (en) reg_val = DMA_ENABLE__FLAG; + + denali_write32(reg_val, denali->flash_reg + DMA_ENABLE); + ioread32(denali->flash_reg + DMA_ENABLE); +} + +/* setups the HW to perform the data DMA */ +static void denali_setup_dma(struct denali_nand_info *denali, int op) +{ + uint32_t mode = 0x0; + const int page_count = 1; + dma_addr_t addr = denali->buf.dma_buf; + + mode = MODE_10 | BANK(denali->flash_bank); + + /* DMA is a four step process */ + + /* 1. setup transfer type and # of pages */ + index_addr(denali, mode | denali->page, 0x2000 | op | page_count); + + /* 2. set memory high address bits 23:8 */ + index_addr(denali, mode | ((uint16_t)(addr >> 16) << 8), 0x2200); + + /* 3. set memory low address bits 23:8 */ + index_addr(denali, mode | ((uint16_t)addr << 8), 0x2300); + + /* 4. interrupt when complete, burst len = 64 bytes*/ + index_addr(denali, mode | 0x14000, 0x2400); +} + +/* writes a page. user specifies type, and this function handles the + configuration details. */ +static void write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, bool raw_xfer) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct pci_dev *pci_dev = denali->dev; + + dma_addr_t addr = denali->buf.dma_buf; + size_t size = denali->mtd.writesize + denali->mtd.oobsize; + + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP | + INTR_STATUS0__PROGRAM_FAIL; + + /* if it is a raw xfer, we want to disable ecc, and send + * the spare area. + * !raw_xfer - enable ecc + * raw_xfer - transfer spare + */ + setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer); + + /* copy buffer into DMA buffer */ + memcpy(denali->buf.buf, buf, mtd->writesize); + + if (raw_xfer) + { + /* transfer the data to the spare area */ + memcpy(denali->buf.buf + mtd->writesize, + chip->oob_poi, + mtd->oobsize); + } + + pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_TODEVICE); + + clear_interrupts(denali); + denali_enable_dma(denali, true); + + denali_setup_dma(denali, DENALI_WRITE); + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "timeout on write_page (type = %d)\n", raw_xfer); + denali->status = + (irq_status & INTR_STATUS0__PROGRAM_FAIL) ? NAND_STATUS_FAIL : + PASS; + } + + denali_enable_dma(denali, false); + pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_TODEVICE); +} + +/* NAND core entry points */ + +/* this is the callback that the NAND core calls to write a page. Since + writing a page with ECC or without is similar, all the work is done + by write_page above. */ +static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + /* for regular page writes, we let HW handle all the ECC + * data written to the device. */ + write_page(mtd, chip, buf, false); +} + +/* This is the callback that the NAND core calls to write a page without ECC. + raw access is similiar to ECC page writes, so all the work is done in the + write_page() function above. + */ +static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + /* for raw page writes, we want to disable ECC and simply write + whatever data is in the buffer. */ + write_page(mtd, chip, buf, true); +} + +static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return write_oob_data(mtd, chip->oob_poi, page); +} + +static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + read_oob_data(mtd, chip->oob_poi, page); + + return 0; /* notify NAND core to send command to + * NAND device. */ +} + +static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct pci_dev *pci_dev = denali->dev; + + dma_addr_t addr = denali->buf.dma_buf; + size_t size = denali->mtd.writesize + denali->mtd.oobsize; + + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__ECC_TRANSACTION_DONE | + INTR_STATUS0__ECC_ERR; + bool check_erased_page = false; + + setup_ecc_for_xfer(denali, true, false); + + denali_enable_dma(denali, true); + pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + clear_interrupts(denali); + denali_setup_dma(denali, DENALI_READ); + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + memcpy(buf, denali->buf.buf, mtd->writesize); + + check_erased_page = handle_ecc(denali, buf, chip->oob_poi, irq_status); + denali_enable_dma(denali, false); + + if (check_erased_page) + { + read_oob_data(&denali->mtd, chip->oob_poi, denali->page); + + /* check ECC failures that may have occurred on erased pages */ + if (check_erased_page) + { + if (!is_erased(buf, denali->mtd.writesize)) + { + denali->mtd.ecc_stats.failed++; + } + if (!is_erased(buf, denali->mtd.oobsize)) + { + denali->mtd.ecc_stats.failed++; + } + } + } + return 0; +} + +static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct pci_dev *pci_dev = denali->dev; + + dma_addr_t addr = denali->buf.dma_buf; + size_t size = denali->mtd.writesize + denali->mtd.oobsize; + + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP; + + setup_ecc_for_xfer(denali, false, true); + denali_enable_dma(denali, true); + + pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + clear_interrupts(denali); + denali_setup_dma(denali, DENALI_READ); + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + denali_enable_dma(denali, false); + + memcpy(buf, denali->buf.buf, mtd->writesize); + memcpy(chip->oob_poi, denali->buf.buf + mtd->writesize, mtd->oobsize); + + return 0; +} + +static uint8_t denali_read_byte(struct mtd_info *mtd) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint8_t result = 0xff; + + if (denali->buf.head < denali->buf.tail) + { + result = denali->buf.buf[denali->buf.head++]; + } + +#if DEBUG_DENALI + printk("read byte -> 0x%02x\n", result); +#endif + return result; +} + +static void denali_select_chip(struct mtd_info *mtd, int chip) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); +#if DEBUG_DENALI + printk("denali select chip %d\n", chip); +#endif + spin_lock_irq(&denali->irq_lock); + denali->flash_bank = chip; + spin_unlock_irq(&denali->irq_lock); +} + +static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int status = denali->status; + denali->status = 0; + +#if DEBUG_DENALI + printk("waitfunc %d\n", status); +#endif + return status; +} + +static void denali_erase(struct mtd_info *mtd, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + uint32_t cmd = 0x0, irq_status = 0; + +#if DEBUG_DENALI + printk("erase page: %d\n", page); +#endif + /* clear interrupts */ + clear_interrupts(denali); + + /* setup page read request for access type */ + cmd = MODE_10 | BANK(denali->flash_bank) | page; + index_addr(denali, (uint32_t)cmd, 0x1); + + /* wait for erase to complete or failure to occur */ + irq_status = wait_for_irq(denali, INTR_STATUS0__ERASE_COMP | + INTR_STATUS0__ERASE_FAIL); + + denali->status = (irq_status & INTR_STATUS0__ERASE_FAIL) ? NAND_STATUS_FAIL : + PASS; +} + +static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, + int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + +#if DEBUG_DENALI + printk("cmdfunc: 0x%x %d %d\n", cmd, col, page); +#endif + switch (cmd) + { + case NAND_CMD_PAGEPROG: + break; + case NAND_CMD_STATUS: + read_status(denali); + break; + case NAND_CMD_READID: + reset_buf(denali); + if (denali->flash_bank < denali->total_used_banks) + { + /* write manufacturer information into nand + buffer for NAND subsystem to fetch. + */ + write_byte_to_buf(denali, denali->dev_info.wDeviceMaker); + write_byte_to_buf(denali, denali->dev_info.wDeviceID); + write_byte_to_buf(denali, denali->dev_info.bDeviceParam0); + write_byte_to_buf(denali, denali->dev_info.bDeviceParam1); + write_byte_to_buf(denali, denali->dev_info.bDeviceParam2); + } + else + { + int i; + for (i = 0; i < 5; i++) + write_byte_to_buf(denali, 0xff); + } + break; + case NAND_CMD_READ0: + case NAND_CMD_SEQIN: + denali->page = page; + break; + case NAND_CMD_RESET: + reset_bank(denali); + break; + case NAND_CMD_READOOB: + /* TODO: Read OOB data */ + break; + default: + printk(KERN_ERR ": unsupported command received 0x%x\n", cmd); + break; + } +} + +/* stubs for ECC functions not used by the NAND core */ +static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, + uint8_t *ecc_code) +{ + printk(KERN_ERR "denali_ecc_calculate called unexpectedly\n"); + BUG(); + return -EIO; +} + +static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + printk(KERN_ERR "denali_ecc_correct called unexpectedly\n"); + BUG(); + return -EIO; +} + +static void denali_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + printk(KERN_ERR "denali_ecc_hwctl called unexpectedly\n"); + BUG(); +} +/* end NAND core entry points */ + +/* Initialization code to bring the device up to a known good state */ +static void denali_hw_init(struct denali_nand_info *denali) +{ + denali_irq_init(denali); + NAND_Flash_Reset(denali); + denali_write32(0x0F, denali->flash_reg + RB_PIN_ENABLED); + denali_write32(CHIP_EN_DONT_CARE__FLAG, denali->flash_reg + CHIP_ENABLE_DONT_CARE); + + denali_write32(0x0, denali->flash_reg + SPARE_AREA_SKIP_BYTES); + denali_write32(0xffff, denali->flash_reg + SPARE_AREA_MARKER); + + /* Should set value for these registers when init */ + denali_write32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES); + denali_write32(1, denali->flash_reg + ECC_ENABLE); +} + +/* ECC layout for SLC devices. Denali spec indicates SLC fixed at 4 bytes */ +#define ECC_BYTES_SLC 4 * (2048 / ECC_SECTOR_SIZE) +static struct nand_ecclayout nand_oob_slc = { + .eccbytes = 4, + .eccpos = { 0, 1, 2, 3 }, /* not used */ + .oobfree = {{ + .offset = ECC_BYTES_SLC, + .length = 64 - ECC_BYTES_SLC + }} +}; + +#define ECC_BYTES_MLC 14 * (2048 / ECC_SECTOR_SIZE) +static struct nand_ecclayout nand_oob_mlc_14bit = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13 }, /* not used */ + .oobfree = {{ + .offset = ECC_BYTES_MLC, + .length = 64 - ECC_BYTES_MLC + }} +}; + +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/* initalize driver data structures */ +void denali_drv_init(struct denali_nand_info *denali) +{ + denali->idx = 0; + + /* setup interrupt handler */ + /* the completion object will be used to notify + * the callee that the interrupt is done */ + init_completion(&denali->complete); + + /* the spinlock will be used to synchronize the ISR + * with any element that might be access shared + * data (interrupt status) */ + spin_lock_init(&denali->irq_lock); + + /* indicate that MTD has not selected a valid bank yet */ + denali->flash_bank = CHIP_SELECT_INVALID; + + /* initialize our irq_status variable to indicate no interrupts */ + denali->irq_status = 0; +} + +/* driver entry point */ +static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int ret = -ENODEV; + resource_size_t csr_base, mem_base; + unsigned long csr_len, mem_len; + struct denali_nand_info *denali; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + denali = kzalloc(sizeof(*denali), GFP_KERNEL); + if (!denali) + return -ENOMEM; + + ret = pci_enable_device(dev); + if (ret) { + printk(KERN_ERR "Spectra: pci_enable_device failed.\n"); + goto failed_enable; + } + + if (id->driver_data == INTEL_CE4100) { + /* Due to a silicon limitation, we can only support + * ONFI timing mode 1 and below. + */ + if (onfi_timing_mode < -1 || onfi_timing_mode > 1) + { + printk("Intel CE4100 only supports ONFI timing mode 1 " + "or below\n"); + ret = -EINVAL; + goto failed_enable; + } + denali->platform = INTEL_CE4100; + mem_base = pci_resource_start(dev, 0); + mem_len = pci_resource_len(dev, 1); + csr_base = pci_resource_start(dev, 1); + csr_len = pci_resource_len(dev, 1); + } else { + denali->platform = INTEL_MRST; + csr_base = pci_resource_start(dev, 0); + csr_len = pci_resource_start(dev, 0); + mem_base = pci_resource_start(dev, 1); + mem_len = pci_resource_len(dev, 1); + if (!mem_len) { + mem_base = csr_base + csr_len; + mem_len = csr_len; + nand_dbg_print(NAND_DBG_WARN, + "Spectra: No second BAR for PCI device; assuming %08Lx\n", + (uint64_t)csr_base); + } + } + + /* Is 32-bit DMA supported? */ + ret = pci_set_dma_mask(dev, DMA_BIT_MASK(32)); + + if (ret) + { + printk(KERN_ERR "Spectra: no usable DMA configuration\n"); + goto failed_enable; + } + denali->buf.dma_buf = pci_map_single(dev, denali->buf.buf, DENALI_BUF_SIZE, + PCI_DMA_BIDIRECTIONAL); + + if (pci_dma_mapping_error(dev, denali->buf.dma_buf)) + { + printk(KERN_ERR "Spectra: failed to map DMA buffer\n"); + goto failed_enable; + } + + pci_set_master(dev); + denali->dev = dev; + + ret = pci_request_regions(dev, DENALI_NAND_NAME); + if (ret) { + printk(KERN_ERR "Spectra: Unable to request memory regions\n"); + goto failed_req_csr; + } + + denali->flash_reg = ioremap_nocache(csr_base, csr_len); + if (!denali->flash_reg) { + printk(KERN_ERR "Spectra: Unable to remap memory region\n"); + ret = -ENOMEM; + goto failed_remap_csr; + } + nand_dbg_print(NAND_DBG_DEBUG, "Spectra: CSR 0x%08Lx -> 0x%p (0x%lx)\n", + (uint64_t)csr_base, denali->flash_reg, csr_len); + + denali->flash_mem = ioremap_nocache(mem_base, mem_len); + if (!denali->flash_mem) { + printk(KERN_ERR "Spectra: ioremap_nocache failed!"); + iounmap(denali->flash_reg); + ret = -ENOMEM; + goto failed_remap_csr; + } + + nand_dbg_print(NAND_DBG_WARN, + "Spectra: Remapped flash base address: " + "0x%p, len: %ld\n", + denali->flash_mem, csr_len); + + denali_hw_init(denali); + denali_drv_init(denali); + + nand_dbg_print(NAND_DBG_DEBUG, "Spectra: IRQ %d\n", dev->irq); + if (request_irq(dev->irq, denali_isr, IRQF_SHARED, + DENALI_NAND_NAME, denali)) { + printk(KERN_ERR "Spectra: Unable to allocate IRQ\n"); + ret = -ENODEV; + goto failed_request_irq; + } + + /* now that our ISR is registered, we can enable interrupts */ + NAND_LLD_Enable_Disable_Interrupts(denali, true); + + pci_set_drvdata(dev, denali); + + NAND_Read_Device_ID(denali); + + /* MTD supported page sizes vary by kernel. We validate our + kernel supports the device here. + */ + if (denali->dev_info.wPageSize > NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) + { + ret = -ENODEV; + printk(KERN_ERR "Spectra: device size not supported by this " + "version of MTD."); + goto failed_nand; + } + + nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" + "acc_clks: %d, re_2_we: %d, we_2_re: %d," + "addr_2_data: %d, rdwr_en_lo_cnt: %d, " + "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n", + ioread32(denali->flash_reg + ACC_CLKS), + ioread32(denali->flash_reg + RE_2_WE), + ioread32(denali->flash_reg + WE_2_RE), + ioread32(denali->flash_reg + ADDR_2_DATA), + ioread32(denali->flash_reg + RDWR_EN_LO_CNT), + ioread32(denali->flash_reg + RDWR_EN_HI_CNT), + ioread32(denali->flash_reg + CS_SETUP_CNT)); + + denali->mtd.name = "Denali NAND"; + denali->mtd.owner = THIS_MODULE; + denali->mtd.priv = &denali->nand; + + /* register the driver with the NAND core subsystem */ + denali->nand.select_chip = denali_select_chip; + denali->nand.cmdfunc = denali_cmdfunc; + denali->nand.read_byte = denali_read_byte; + denali->nand.waitfunc = denali_waitfunc; + + /* scan for NAND devices attached to the controller + * this is the first stage in a two step process to register + * with the nand subsystem */ + if (nand_scan_ident(&denali->mtd, LLD_MAX_FLASH_BANKS, NULL)) + { + ret = -ENXIO; + goto failed_nand; + } + + /* second stage of the NAND scan + * this stage requires information regarding ECC and + * bad block management. */ + + /* Bad block management */ + denali->nand.bbt_td = &bbt_main_descr; + denali->nand.bbt_md = &bbt_mirror_descr; + + /* skip the scan for now until we have OOB read and write support */ + denali->nand.options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN; + denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + + if (denali->dev_info.MLCDevice) + { + denali->nand.ecc.layout = &nand_oob_mlc_14bit; + denali->nand.ecc.bytes = ECC_BYTES_MLC; + } + else /* SLC */ + { + denali->nand.ecc.layout = &nand_oob_slc; + denali->nand.ecc.bytes = ECC_BYTES_SLC; + } + + /* These functions are required by the NAND core framework, otherwise, + the NAND core will assert. However, we don't need them, so we'll stub + them out. */ + denali->nand.ecc.calculate = denali_ecc_calculate; + denali->nand.ecc.correct = denali_ecc_correct; + denali->nand.ecc.hwctl = denali_ecc_hwctl; + + /* override the default read operations */ + denali->nand.ecc.size = denali->mtd.writesize; + denali->nand.ecc.read_page = denali_read_page; + denali->nand.ecc.read_page_raw = denali_read_page_raw; + denali->nand.ecc.write_page = denali_write_page; + denali->nand.ecc.write_page_raw = denali_write_page_raw; + denali->nand.ecc.read_oob = denali_read_oob; + denali->nand.ecc.write_oob = denali_write_oob; + denali->nand.erase_cmd = denali_erase; + + if (nand_scan_tail(&denali->mtd)) + { + ret = -ENXIO; + goto failed_nand; + } + + ret = add_mtd_device(&denali->mtd); + if (ret) { + printk(KERN_ERR "Spectra: Failed to register MTD device: %d\n", ret); + goto failed_nand; + } + return 0; + + failed_nand: + denali_irq_cleanup(dev->irq, denali); + failed_request_irq: + iounmap(denali->flash_reg); + iounmap(denali->flash_mem); + failed_remap_csr: + pci_release_regions(dev); + failed_req_csr: + pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + PCI_DMA_BIDIRECTIONAL); + failed_enable: + kfree(denali); + return ret; +} + +/* driver exit point */ +static void denali_pci_remove(struct pci_dev *dev) +{ + struct denali_nand_info *denali = pci_get_drvdata(dev); + + nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + nand_release(&denali->mtd); + del_mtd_device(&denali->mtd); + + denali_irq_cleanup(dev->irq, denali); + + iounmap(denali->flash_reg); + iounmap(denali->flash_mem); + pci_release_regions(dev); + pci_disable_device(dev); + pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + PCI_DMA_BIDIRECTIONAL); + pci_set_drvdata(dev, NULL); + kfree(denali); +} + +MODULE_DEVICE_TABLE(pci, denali_pci_ids); + +static struct pci_driver denali_pci_driver = { + .name = DENALI_NAND_NAME, + .id_table = denali_pci_ids, + .probe = denali_pci_probe, + .remove = denali_pci_remove, +}; + +static int __devinit denali_init(void) +{ + printk(KERN_INFO "Spectra MTD driver built on %s @ %s\n", __DATE__, __TIME__); + return pci_register_driver(&denali_pci_driver); +} + +/* Free memory */ +static void __devexit denali_exit(void) +{ + pci_unregister_driver(&denali_pci_driver); +} + +module_init(denali_init); +module_exit(denali_exit); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h new file mode 100644 index 00000000000..422a29ab2f6 --- /dev/null +++ b/drivers/mtd/nand/denali.h @@ -0,0 +1,816 @@ +/* + * NAND Flash Controller Device Driver + * Copyright (c) 2009 - 2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/mtd/nand.h> + +#define DEVICE_RESET 0x0 +#define DEVICE_RESET__BANK0 0x0001 +#define DEVICE_RESET__BANK1 0x0002 +#define DEVICE_RESET__BANK2 0x0004 +#define DEVICE_RESET__BANK3 0x0008 + +#define TRANSFER_SPARE_REG 0x10 +#define TRANSFER_SPARE_REG__FLAG 0x0001 + +#define LOAD_WAIT_CNT 0x20 +#define LOAD_WAIT_CNT__VALUE 0xffff + +#define PROGRAM_WAIT_CNT 0x30 +#define PROGRAM_WAIT_CNT__VALUE 0xffff + +#define ERASE_WAIT_CNT 0x40 +#define ERASE_WAIT_CNT__VALUE 0xffff + +#define INT_MON_CYCCNT 0x50 +#define INT_MON_CYCCNT__VALUE 0xffff + +#define RB_PIN_ENABLED 0x60 +#define RB_PIN_ENABLED__BANK0 0x0001 +#define RB_PIN_ENABLED__BANK1 0x0002 +#define RB_PIN_ENABLED__BANK2 0x0004 +#define RB_PIN_ENABLED__BANK3 0x0008 + +#define MULTIPLANE_OPERATION 0x70 +#define MULTIPLANE_OPERATION__FLAG 0x0001 + +#define MULTIPLANE_READ_ENABLE 0x80 +#define MULTIPLANE_READ_ENABLE__FLAG 0x0001 + +#define COPYBACK_DISABLE 0x90 +#define COPYBACK_DISABLE__FLAG 0x0001 + +#define CACHE_WRITE_ENABLE 0xa0 +#define CACHE_WRITE_ENABLE__FLAG 0x0001 + +#define CACHE_READ_ENABLE 0xb0 +#define CACHE_READ_ENABLE__FLAG 0x0001 + +#define PREFETCH_MODE 0xc0 +#define PREFETCH_MODE__PREFETCH_EN 0x0001 +#define PREFETCH_MODE__PREFETCH_BURST_LENGTH 0xfff0 + +#define CHIP_ENABLE_DONT_CARE 0xd0 +#define CHIP_EN_DONT_CARE__FLAG 0x01 + +#define ECC_ENABLE 0xe0 +#define ECC_ENABLE__FLAG 0x0001 + +#define GLOBAL_INT_ENABLE 0xf0 +#define GLOBAL_INT_EN_FLAG 0x01 + +#define WE_2_RE 0x100 +#define WE_2_RE__VALUE 0x003f + +#define ADDR_2_DATA 0x110 +#define ADDR_2_DATA__VALUE 0x003f + +#define RE_2_WE 0x120 +#define RE_2_WE__VALUE 0x003f + +#define ACC_CLKS 0x130 +#define ACC_CLKS__VALUE 0x000f + +#define NUMBER_OF_PLANES 0x140 +#define NUMBER_OF_PLANES__VALUE 0x0007 + +#define PAGES_PER_BLOCK 0x150 +#define PAGES_PER_BLOCK__VALUE 0xffff + +#define DEVICE_WIDTH 0x160 +#define DEVICE_WIDTH__VALUE 0x0003 + +#define DEVICE_MAIN_AREA_SIZE 0x170 +#define DEVICE_MAIN_AREA_SIZE__VALUE 0xffff + +#define DEVICE_SPARE_AREA_SIZE 0x180 +#define DEVICE_SPARE_AREA_SIZE__VALUE 0xffff + +#define TWO_ROW_ADDR_CYCLES 0x190 +#define TWO_ROW_ADDR_CYCLES__FLAG 0x0001 + +#define MULTIPLANE_ADDR_RESTRICT 0x1a0 +#define MULTIPLANE_ADDR_RESTRICT__FLAG 0x0001 + +#define ECC_CORRECTION 0x1b0 +#define ECC_CORRECTION__VALUE 0x001f + +#define READ_MODE 0x1c0 +#define READ_MODE__VALUE 0x000f + +#define WRITE_MODE 0x1d0 +#define WRITE_MODE__VALUE 0x000f + +#define COPYBACK_MODE 0x1e0 +#define COPYBACK_MODE__VALUE 0x000f + +#define RDWR_EN_LO_CNT 0x1f0 +#define RDWR_EN_LO_CNT__VALUE 0x001f + +#define RDWR_EN_HI_CNT 0x200 +#define RDWR_EN_HI_CNT__VALUE 0x001f + +#define MAX_RD_DELAY 0x210 +#define MAX_RD_DELAY__VALUE 0x000f + +#define CS_SETUP_CNT 0x220 +#define CS_SETUP_CNT__VALUE 0x001f + +#define SPARE_AREA_SKIP_BYTES 0x230 +#define SPARE_AREA_SKIP_BYTES__VALUE 0x003f + +#define SPARE_AREA_MARKER 0x240 +#define SPARE_AREA_MARKER__VALUE 0xffff + +#define DEVICES_CONNECTED 0x250 +#define DEVICES_CONNECTED__VALUE 0x0007 + +#define DIE_MASK 0x260 +#define DIE_MASK__VALUE 0x00ff + +#define FIRST_BLOCK_OF_NEXT_PLANE 0x270 +#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE 0xffff + +#define WRITE_PROTECT 0x280 +#define WRITE_PROTECT__FLAG 0x0001 + +#define RE_2_RE 0x290 +#define RE_2_RE__VALUE 0x003f + +#define MANUFACTURER_ID 0x300 +#define MANUFACTURER_ID__VALUE 0x00ff + +#define DEVICE_ID 0x310 +#define DEVICE_ID__VALUE 0x00ff + +#define DEVICE_PARAM_0 0x320 +#define DEVICE_PARAM_0__VALUE 0x00ff + +#define DEVICE_PARAM_1 0x330 +#define DEVICE_PARAM_1__VALUE 0x00ff + +#define DEVICE_PARAM_2 0x340 +#define DEVICE_PARAM_2__VALUE 0x00ff + +#define LOGICAL_PAGE_DATA_SIZE 0x350 +#define LOGICAL_PAGE_DATA_SIZE__VALUE 0xffff + +#define LOGICAL_PAGE_SPARE_SIZE 0x360 +#define LOGICAL_PAGE_SPARE_SIZE__VALUE 0xffff + +#define REVISION 0x370 +#define REVISION__VALUE 0xffff + +#define ONFI_DEVICE_FEATURES 0x380 +#define ONFI_DEVICE_FEATURES__VALUE 0x003f + +#define ONFI_OPTIONAL_COMMANDS 0x390 +#define ONFI_OPTIONAL_COMMANDS__VALUE 0x003f + +#define ONFI_TIMING_MODE 0x3a0 +#define ONFI_TIMING_MODE__VALUE 0x003f + +#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0 +#define ONFI_PGM_CACHE_TIMING_MODE__VALUE 0x003f + +#define ONFI_DEVICE_NO_OF_LUNS 0x3c0 +#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS 0x00ff +#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE 0x0100 + +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0 +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE 0xffff + +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0 +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE 0xffff + +#define FEATURES 0x3f0 +#define FEATURES__N_BANKS 0x0003 +#define FEATURES__ECC_MAX_ERR 0x003c +#define FEATURES__DMA 0x0040 +#define FEATURES__CMD_DMA 0x0080 +#define FEATURES__PARTITION 0x0100 +#define FEATURES__XDMA_SIDEBAND 0x0200 +#define FEATURES__GPREG 0x0400 +#define FEATURES__INDEX_ADDR 0x0800 + +#define TRANSFER_MODE 0x400 +#define TRANSFER_MODE__VALUE 0x0003 + +#define INTR_STATUS0 0x410 +#define INTR_STATUS0__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS0__ECC_ERR 0x0002 +#define INTR_STATUS0__DMA_CMD_COMP 0x0004 +#define INTR_STATUS0__TIME_OUT 0x0008 +#define INTR_STATUS0__PROGRAM_FAIL 0x0010 +#define INTR_STATUS0__ERASE_FAIL 0x0020 +#define INTR_STATUS0__LOAD_COMP 0x0040 +#define INTR_STATUS0__PROGRAM_COMP 0x0080 +#define INTR_STATUS0__ERASE_COMP 0x0100 +#define INTR_STATUS0__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS0__LOCKED_BLK 0x0400 +#define INTR_STATUS0__UNSUP_CMD 0x0800 +#define INTR_STATUS0__INT_ACT 0x1000 +#define INTR_STATUS0__RST_COMP 0x2000 +#define INTR_STATUS0__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS0__PAGE_XFER_INC 0x8000 + +#define INTR_EN0 0x420 +#define INTR_EN0__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN0__ECC_ERR 0x0002 +#define INTR_EN0__DMA_CMD_COMP 0x0004 +#define INTR_EN0__TIME_OUT 0x0008 +#define INTR_EN0__PROGRAM_FAIL 0x0010 +#define INTR_EN0__ERASE_FAIL 0x0020 +#define INTR_EN0__LOAD_COMP 0x0040 +#define INTR_EN0__PROGRAM_COMP 0x0080 +#define INTR_EN0__ERASE_COMP 0x0100 +#define INTR_EN0__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN0__LOCKED_BLK 0x0400 +#define INTR_EN0__UNSUP_CMD 0x0800 +#define INTR_EN0__INT_ACT 0x1000 +#define INTR_EN0__RST_COMP 0x2000 +#define INTR_EN0__PIPE_CMD_ERR 0x4000 +#define INTR_EN0__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT0 0x430 +#define PAGE_CNT0__VALUE 0x00ff + +#define ERR_PAGE_ADDR0 0x440 +#define ERR_PAGE_ADDR0__VALUE 0xffff + +#define ERR_BLOCK_ADDR0 0x450 +#define ERR_BLOCK_ADDR0__VALUE 0xffff + +#define INTR_STATUS1 0x460 +#define INTR_STATUS1__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS1__ECC_ERR 0x0002 +#define INTR_STATUS1__DMA_CMD_COMP 0x0004 +#define INTR_STATUS1__TIME_OUT 0x0008 +#define INTR_STATUS1__PROGRAM_FAIL 0x0010 +#define INTR_STATUS1__ERASE_FAIL 0x0020 +#define INTR_STATUS1__LOAD_COMP 0x0040 +#define INTR_STATUS1__PROGRAM_COMP 0x0080 +#define INTR_STATUS1__ERASE_COMP 0x0100 +#define INTR_STATUS1__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS1__LOCKED_BLK 0x0400 +#define INTR_STATUS1__UNSUP_CMD 0x0800 +#define INTR_STATUS1__INT_ACT 0x1000 +#define INTR_STATUS1__RST_COMP 0x2000 +#define INTR_STATUS1__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS1__PAGE_XFER_INC 0x8000 + +#define INTR_EN1 0x470 +#define INTR_EN1__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN1__ECC_ERR 0x0002 +#define INTR_EN1__DMA_CMD_COMP 0x0004 +#define INTR_EN1__TIME_OUT 0x0008 +#define INTR_EN1__PROGRAM_FAIL 0x0010 +#define INTR_EN1__ERASE_FAIL 0x0020 +#define INTR_EN1__LOAD_COMP 0x0040 +#define INTR_EN1__PROGRAM_COMP 0x0080 +#define INTR_EN1__ERASE_COMP 0x0100 +#define INTR_EN1__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN1__LOCKED_BLK 0x0400 +#define INTR_EN1__UNSUP_CMD 0x0800 +#define INTR_EN1__INT_ACT 0x1000 +#define INTR_EN1__RST_COMP 0x2000 +#define INTR_EN1__PIPE_CMD_ERR 0x4000 +#define INTR_EN1__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT1 0x480 +#define PAGE_CNT1__VALUE 0x00ff + +#define ERR_PAGE_ADDR1 0x490 +#define ERR_PAGE_ADDR1__VALUE 0xffff + +#define ERR_BLOCK_ADDR1 0x4a0 +#define ERR_BLOCK_ADDR1__VALUE 0xffff + +#define INTR_STATUS2 0x4b0 +#define INTR_STATUS2__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS2__ECC_ERR 0x0002 +#define INTR_STATUS2__DMA_CMD_COMP 0x0004 +#define INTR_STATUS2__TIME_OUT 0x0008 +#define INTR_STATUS2__PROGRAM_FAIL 0x0010 +#define INTR_STATUS2__ERASE_FAIL 0x0020 +#define INTR_STATUS2__LOAD_COMP 0x0040 +#define INTR_STATUS2__PROGRAM_COMP 0x0080 +#define INTR_STATUS2__ERASE_COMP 0x0100 +#define INTR_STATUS2__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS2__LOCKED_BLK 0x0400 +#define INTR_STATUS2__UNSUP_CMD 0x0800 +#define INTR_STATUS2__INT_ACT 0x1000 +#define INTR_STATUS2__RST_COMP 0x2000 +#define INTR_STATUS2__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS2__PAGE_XFER_INC 0x8000 + +#define INTR_EN2 0x4c0 +#define INTR_EN2__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN2__ECC_ERR 0x0002 +#define INTR_EN2__DMA_CMD_COMP 0x0004 +#define INTR_EN2__TIME_OUT 0x0008 +#define INTR_EN2__PROGRAM_FAIL 0x0010 +#define INTR_EN2__ERASE_FAIL 0x0020 +#define INTR_EN2__LOAD_COMP 0x0040 +#define INTR_EN2__PROGRAM_COMP 0x0080 +#define INTR_EN2__ERASE_COMP 0x0100 +#define INTR_EN2__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN2__LOCKED_BLK 0x0400 +#define INTR_EN2__UNSUP_CMD 0x0800 +#define INTR_EN2__INT_ACT 0x1000 +#define INTR_EN2__RST_COMP 0x2000 +#define INTR_EN2__PIPE_CMD_ERR 0x4000 +#define INTR_EN2__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT2 0x4d0 +#define PAGE_CNT2__VALUE 0x00ff + +#define ERR_PAGE_ADDR2 0x4e0 +#define ERR_PAGE_ADDR2__VALUE 0xffff + +#define ERR_BLOCK_ADDR2 0x4f0 +#define ERR_BLOCK_ADDR2__VALUE 0xffff + +#define INTR_STATUS3 0x500 +#define INTR_STATUS3__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS3__ECC_ERR 0x0002 +#define INTR_STATUS3__DMA_CMD_COMP 0x0004 +#define INTR_STATUS3__TIME_OUT 0x0008 +#define INTR_STATUS3__PROGRAM_FAIL 0x0010 +#define INTR_STATUS3__ERASE_FAIL 0x0020 +#define INTR_STATUS3__LOAD_COMP 0x0040 +#define INTR_STATUS3__PROGRAM_COMP 0x0080 +#define INTR_STATUS3__ERASE_COMP 0x0100 +#define INTR_STATUS3__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS3__LOCKED_BLK 0x0400 +#define INTR_STATUS3__UNSUP_CMD 0x0800 +#define INTR_STATUS3__INT_ACT 0x1000 +#define INTR_STATUS3__RST_COMP 0x2000 +#define INTR_STATUS3__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS3__PAGE_XFER_INC 0x8000 + +#define INTR_EN3 0x510 +#define INTR_EN3__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN3__ECC_ERR 0x0002 +#define INTR_EN3__DMA_CMD_COMP 0x0004 +#define INTR_EN3__TIME_OUT 0x0008 +#define INTR_EN3__PROGRAM_FAIL 0x0010 +#define INTR_EN3__ERASE_FAIL 0x0020 +#define INTR_EN3__LOAD_COMP 0x0040 +#define INTR_EN3__PROGRAM_COMP 0x0080 +#define INTR_EN3__ERASE_COMP 0x0100 +#define INTR_EN3__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN3__LOCKED_BLK 0x0400 +#define INTR_EN3__UNSUP_CMD 0x0800 +#define INTR_EN3__INT_ACT 0x1000 +#define INTR_EN3__RST_COMP 0x2000 +#define INTR_EN3__PIPE_CMD_ERR 0x4000 +#define INTR_EN3__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT3 0x520 +#define PAGE_CNT3__VALUE 0x00ff + +#define ERR_PAGE_ADDR3 0x530 +#define ERR_PAGE_ADDR3__VALUE 0xffff + +#define ERR_BLOCK_ADDR3 0x540 +#define ERR_BLOCK_ADDR3__VALUE 0xffff + +#define DATA_INTR 0x550 +#define DATA_INTR__WRITE_SPACE_AV 0x0001 +#define DATA_INTR__READ_DATA_AV 0x0002 + +#define DATA_INTR_EN 0x560 +#define DATA_INTR_EN__WRITE_SPACE_AV 0x0001 +#define DATA_INTR_EN__READ_DATA_AV 0x0002 + +#define GPREG_0 0x570 +#define GPREG_0__VALUE 0xffff + +#define GPREG_1 0x580 +#define GPREG_1__VALUE 0xffff + +#define GPREG_2 0x590 +#define GPREG_2__VALUE 0xffff + +#define GPREG_3 0x5a0 +#define GPREG_3__VALUE 0xffff + +#define ECC_THRESHOLD 0x600 +#define ECC_THRESHOLD__VALUE 0x03ff + +#define ECC_ERROR_BLOCK_ADDRESS 0x610 +#define ECC_ERROR_BLOCK_ADDRESS__VALUE 0xffff + +#define ECC_ERROR_PAGE_ADDRESS 0x620 +#define ECC_ERROR_PAGE_ADDRESS__VALUE 0x0fff +#define ECC_ERROR_PAGE_ADDRESS__BANK 0xf000 + +#define ECC_ERROR_ADDRESS 0x630 +#define ECC_ERROR_ADDRESS__OFFSET 0x0fff +#define ECC_ERROR_ADDRESS__SECTOR_NR 0xf000 + +#define ERR_CORRECTION_INFO 0x640 +#define ERR_CORRECTION_INFO__BYTEMASK 0x00ff +#define ERR_CORRECTION_INFO__DEVICE_NR 0x0f00 +#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000 +#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000 + +#define DMA_ENABLE 0x700 +#define DMA_ENABLE__FLAG 0x0001 + +#define IGNORE_ECC_DONE 0x710 +#define IGNORE_ECC_DONE__FLAG 0x0001 + +#define DMA_INTR 0x720 +#define DMA_INTR__TARGET_ERROR 0x0001 +#define DMA_INTR__DESC_COMP_CHANNEL0 0x0002 +#define DMA_INTR__DESC_COMP_CHANNEL1 0x0004 +#define DMA_INTR__DESC_COMP_CHANNEL2 0x0008 +#define DMA_INTR__DESC_COMP_CHANNEL3 0x0010 +#define DMA_INTR__MEMCOPY_DESC_COMP 0x0020 + +#define DMA_INTR_EN 0x730 +#define DMA_INTR_EN__TARGET_ERROR 0x0001 +#define DMA_INTR_EN__DESC_COMP_CHANNEL0 0x0002 +#define DMA_INTR_EN__DESC_COMP_CHANNEL1 0x0004 +#define DMA_INTR_EN__DESC_COMP_CHANNEL2 0x0008 +#define DMA_INTR_EN__DESC_COMP_CHANNEL3 0x0010 +#define DMA_INTR_EN__MEMCOPY_DESC_COMP 0x0020 + +#define TARGET_ERR_ADDR_LO 0x740 +#define TARGET_ERR_ADDR_LO__VALUE 0xffff + +#define TARGET_ERR_ADDR_HI 0x750 +#define TARGET_ERR_ADDR_HI__VALUE 0xffff + +#define CHNL_ACTIVE 0x760 +#define CHNL_ACTIVE__CHANNEL0 0x0001 +#define CHNL_ACTIVE__CHANNEL1 0x0002 +#define CHNL_ACTIVE__CHANNEL2 0x0004 +#define CHNL_ACTIVE__CHANNEL3 0x0008 + +#define ACTIVE_SRC_ID 0x800 +#define ACTIVE_SRC_ID__VALUE 0x00ff + +#define PTN_INTR 0x810 +#define PTN_INTR__CONFIG_ERROR 0x0001 +#define PTN_INTR__ACCESS_ERROR_BANK0 0x0002 +#define PTN_INTR__ACCESS_ERROR_BANK1 0x0004 +#define PTN_INTR__ACCESS_ERROR_BANK2 0x0008 +#define PTN_INTR__ACCESS_ERROR_BANK3 0x0010 +#define PTN_INTR__REG_ACCESS_ERROR 0x0020 + +#define PTN_INTR_EN 0x820 +#define PTN_INTR_EN__CONFIG_ERROR 0x0001 +#define PTN_INTR_EN__ACCESS_ERROR_BANK0 0x0002 +#define PTN_INTR_EN__ACCESS_ERROR_BANK1 0x0004 +#define PTN_INTR_EN__ACCESS_ERROR_BANK2 0x0008 +#define PTN_INTR_EN__ACCESS_ERROR_BANK3 0x0010 +#define PTN_INTR_EN__REG_ACCESS_ERROR 0x0020 + +#define PERM_SRC_ID_0 0x830 +#define PERM_SRC_ID_0__SRCID 0x00ff +#define PERM_SRC_ID_0__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_0__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_0__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_0__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_0 0x840 +#define MIN_BLK_ADDR_0__VALUE 0xffff + +#define MAX_BLK_ADDR_0 0x850 +#define MAX_BLK_ADDR_0__VALUE 0xffff + +#define MIN_MAX_BANK_0 0x860 +#define MIN_MAX_BANK_0__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_0__MAX_VALUE 0x000c + +#define PERM_SRC_ID_1 0x870 +#define PERM_SRC_ID_1__SRCID 0x00ff +#define PERM_SRC_ID_1__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_1__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_1__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_1__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_1 0x880 +#define MIN_BLK_ADDR_1__VALUE 0xffff + +#define MAX_BLK_ADDR_1 0x890 +#define MAX_BLK_ADDR_1__VALUE 0xffff + +#define MIN_MAX_BANK_1 0x8a0 +#define MIN_MAX_BANK_1__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_1__MAX_VALUE 0x000c + +#define PERM_SRC_ID_2 0x8b0 +#define PERM_SRC_ID_2__SRCID 0x00ff +#define PERM_SRC_ID_2__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_2__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_2__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_2__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_2 0x8c0 +#define MIN_BLK_ADDR_2__VALUE 0xffff + +#define MAX_BLK_ADDR_2 0x8d0 +#define MAX_BLK_ADDR_2__VALUE 0xffff + +#define MIN_MAX_BANK_2 0x8e0 +#define MIN_MAX_BANK_2__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_2__MAX_VALUE 0x000c + +#define PERM_SRC_ID_3 0x8f0 +#define PERM_SRC_ID_3__SRCID 0x00ff +#define PERM_SRC_ID_3__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_3__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_3__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_3__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_3 0x900 +#define MIN_BLK_ADDR_3__VALUE 0xffff + +#define MAX_BLK_ADDR_3 0x910 +#define MAX_BLK_ADDR_3__VALUE 0xffff + +#define MIN_MAX_BANK_3 0x920 +#define MIN_MAX_BANK_3__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_3__MAX_VALUE 0x000c + +#define PERM_SRC_ID_4 0x930 +#define PERM_SRC_ID_4__SRCID 0x00ff +#define PERM_SRC_ID_4__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_4__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_4__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_4__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_4 0x940 +#define MIN_BLK_ADDR_4__VALUE 0xffff + +#define MAX_BLK_ADDR_4 0x950 +#define MAX_BLK_ADDR_4__VALUE 0xffff + +#define MIN_MAX_BANK_4 0x960 +#define MIN_MAX_BANK_4__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_4__MAX_VALUE 0x000c + +#define PERM_SRC_ID_5 0x970 +#define PERM_SRC_ID_5__SRCID 0x00ff +#define PERM_SRC_ID_5__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_5__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_5__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_5__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_5 0x980 +#define MIN_BLK_ADDR_5__VALUE 0xffff + +#define MAX_BLK_ADDR_5 0x990 +#define MAX_BLK_ADDR_5__VALUE 0xffff + +#define MIN_MAX_BANK_5 0x9a0 +#define MIN_MAX_BANK_5__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_5__MAX_VALUE 0x000c + +#define PERM_SRC_ID_6 0x9b0 +#define PERM_SRC_ID_6__SRCID 0x00ff +#define PERM_SRC_ID_6__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_6__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_6__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_6__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_6 0x9c0 +#define MIN_BLK_ADDR_6__VALUE 0xffff + +#define MAX_BLK_ADDR_6 0x9d0 +#define MAX_BLK_ADDR_6__VALUE 0xffff + +#define MIN_MAX_BANK_6 0x9e0 +#define MIN_MAX_BANK_6__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_6__MAX_VALUE 0x000c + +#define PERM_SRC_ID_7 0x9f0 +#define PERM_SRC_ID_7__SRCID 0x00ff +#define PERM_SRC_ID_7__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_7__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_7__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_7__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_7 0xa00 +#define MIN_BLK_ADDR_7__VALUE 0xffff + +#define MAX_BLK_ADDR_7 0xa10 +#define MAX_BLK_ADDR_7__VALUE 0xffff + +#define MIN_MAX_BANK_7 0xa20 +#define MIN_MAX_BANK_7__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_7__MAX_VALUE 0x000c + +/* flash.h */ +struct device_info_tag { + uint16_t wDeviceMaker; + uint16_t wDeviceID; + uint8_t bDeviceParam0; + uint8_t bDeviceParam1; + uint8_t bDeviceParam2; + uint32_t wDeviceType; + uint32_t wSpectraStartBlock; + uint32_t wSpectraEndBlock; + uint32_t wTotalBlocks; + uint16_t wPagesPerBlock; + uint16_t wPageSize; + uint16_t wPageDataSize; + uint16_t wPageSpareSize; + uint16_t wNumPageSpareFlag; + uint16_t wECCBytesPerSector; + uint32_t wBlockSize; + uint32_t wBlockDataSize; + uint32_t wDataBlockNum; + uint8_t bPlaneNum; + uint16_t wDeviceMainAreaSize; + uint16_t wDeviceSpareAreaSize; + uint16_t wDevicesConnected; + uint16_t wDeviceWidth; + uint16_t wHWRevision; + uint16_t wHWFeatures; + + uint16_t wONFIDevFeatures; + uint16_t wONFIOptCommands; + uint16_t wONFITimingMode; + uint16_t wONFIPgmCacheTimingMode; + + uint16_t MLCDevice; + uint16_t wSpareSkipBytes; + + uint8_t nBitsInPageNumber; + uint8_t nBitsInPageDataSize; + uint8_t nBitsInBlockDataSize; +}; + +/* ffsdefs.h */ +#define CLEAR 0 /*use this to clear a field instead of "fail"*/ +#define SET 1 /*use this to set a field instead of "pass"*/ +#define FAIL 1 /*failed flag*/ +#define PASS 0 /*success flag*/ +#define ERR -1 /*error flag*/ + +/* lld.h */ +#define GOOD_BLOCK 0 +#define DEFECTIVE_BLOCK 1 +#define READ_ERROR 2 + +#define CLK_X 5 +#define CLK_MULTI 4 + +/* ffsport.h */ +#define VERBOSE 1 + +#define NAND_DBG_WARN 1 +#define NAND_DBG_DEBUG 2 +#define NAND_DBG_TRACE 3 + +#ifdef VERBOSE +#define nand_dbg_print(level, args...) \ + do { \ + if (level <= nand_debug_level) \ + printk(KERN_ALERT args); \ + } while (0) +#else +#define nand_dbg_print(level, args...) +#endif + + +/* spectraswconfig.h */ +#define CMD_DMA 0 + +#define SPECTRA_PARTITION_ID 0 +/**** Block Table and Reserved Block Parameters *****/ +#define SPECTRA_START_BLOCK 3 +#define NUM_FREE_BLOCKS_GATE 30 + +/* KBV - Updated to LNW scratch register address */ +#define SCRATCH_REG_ADDR CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR +#define SCRATCH_REG_SIZE 64 + +#define GLOB_HWCTL_DEFAULT_BLKS 2048 + +#define SUPPORT_15BITECC 1 +#define SUPPORT_8BITECC 1 + +#define CUSTOM_CONF_PARAMS 0 + +#define ONFI_BLOOM_TIME 1 +#define MODE5_WORKAROUND 0 + +/* lld_nand.h */ +/* + * NAND Flash Controller Device Driver + * Copyright (c) 2009, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _LLD_NAND_ +#define _LLD_NAND_ + +#define MODE_00 0x00000000 +#define MODE_01 0x04000000 +#define MODE_10 0x08000000 +#define MODE_11 0x0C000000 + + +#define DATA_TRANSFER_MODE 0 +#define PROTECTION_PER_BLOCK 1 +#define LOAD_WAIT_COUNT 2 +#define PROGRAM_WAIT_COUNT 3 +#define ERASE_WAIT_COUNT 4 +#define INT_MONITOR_CYCLE_COUNT 5 +#define READ_BUSY_PIN_ENABLED 6 +#define MULTIPLANE_OPERATION_SUPPORT 7 +#define PRE_FETCH_MODE 8 +#define CE_DONT_CARE_SUPPORT 9 +#define COPYBACK_SUPPORT 10 +#define CACHE_WRITE_SUPPORT 11 +#define CACHE_READ_SUPPORT 12 +#define NUM_PAGES_IN_BLOCK 13 +#define ECC_ENABLE_SELECT 14 +#define WRITE_ENABLE_2_READ_ENABLE 15 +#define ADDRESS_2_DATA 16 +#define READ_ENABLE_2_WRITE_ENABLE 17 +#define TWO_ROW_ADDRESS_CYCLES 18 +#define MULTIPLANE_ADDRESS_RESTRICT 19 +#define ACC_CLOCKS 20 +#define READ_WRITE_ENABLE_LOW_COUNT 21 +#define READ_WRITE_ENABLE_HIGH_COUNT 22 + +#define ECC_SECTOR_SIZE 512 +#define LLD_MAX_FLASH_BANKS 4 + +#define DENALI_BUF_SIZE NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE + +struct nand_buf +{ + int head; + int tail; + uint8_t buf[DENALI_BUF_SIZE]; + dma_addr_t dma_buf; +}; + +#define INTEL_CE4100 1 +#define INTEL_MRST 2 + +struct denali_nand_info { + struct mtd_info mtd; + struct nand_chip nand; + struct device_info_tag dev_info; + int flash_bank; /* currently selected chip */ + int status; + int platform; + struct nand_buf buf; + struct pci_dev *dev; + int total_used_banks; + uint32_t block; /* stored for future use */ + uint16_t page; + void __iomem *flash_reg; /* Mapped io reg base address */ + void __iomem *flash_mem; /* Mapped io reg base address */ + + /* elements used by ISR */ + struct completion complete; + spinlock_t irq_lock; + uint32_t irq_status; + int irq_debug_array[32]; + int idx; +}; + +static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali); +static uint16_t NAND_Read_Device_ID(struct denali_nand_info *denali); +static void NAND_LLD_Enable_Disable_Interrupts(struct denali_nand_info *denali, uint16_t INT_ENABLE); + +#endif /*_LLD_NAND_*/ + diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index ae30fb6eed9..3f38fb8e666 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -874,7 +874,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, priv->ctrl = ctrl; priv->dev = ctrl->dev; - priv->vbase = ioremap(res.start, res.end - res.start + 1); + priv->vbase = ioremap(res.start, resource_size(&res)); if (!priv->vbase) { dev_err(ctrl->dev, "failed to map chip region\n"); ret = -ENOMEM; @@ -891,7 +891,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, if (ret) goto err; - ret = nand_scan_ident(&priv->mtd, 1); + ret = nand_scan_ident(&priv->mtd, 1, NULL); if (ret) goto err; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 4b96296af32..2d215ccb564 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -49,7 +49,10 @@ struct fsl_upm_nand { uint32_t wait_flags; }; -#define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd) +static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo) +{ + return container_of(mtdinfo, struct fsl_upm_nand, mtd); +} static int fun_chip_ready(struct mtd_info *mtd) { @@ -303,7 +306,7 @@ static int __devinit fun_probe(struct of_device *ofdev, FSL_UPM_WAIT_WRITE_BYTE; fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, - io_res.end - io_res.start + 1); + resource_size(&io_res)); if (!fun->io_base) { ret = -ENOMEM; goto err2; @@ -350,7 +353,7 @@ static int __devexit fun_remove(struct of_device *ofdev) return 0; } -static struct of_device_id of_fun_match[] = { +static const struct of_device_id of_fun_match[] = { { .compatible = "fsl,upm-nand" }, {}, }; diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 8f902e75aa8..0cde618bcc1 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -181,11 +181,11 @@ static int __devexit gpio_nand_remove(struct platform_device *dev) res = platform_get_resource(dev, IORESOURCE_MEM, 1); iounmap(gpiomtd->io_sync); if (res) - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); res = platform_get_resource(dev, IORESOURCE_MEM, 0); iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); @@ -208,14 +208,14 @@ static void __iomem *request_and_remap(struct resource *res, size_t size, { void __iomem *ptr; - if (!request_mem_region(res->start, res->end - res->start + 1, name)) { + if (!request_mem_region(res->start, resource_size(res), name)) { *err = -EBUSY; return NULL; } ptr = ioremap(res->start, size); if (!ptr) { - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); *err = -ENOMEM; } return ptr; @@ -338,10 +338,10 @@ err_nwp: err_nce: iounmap(gpiomtd->io_sync); if (res1) - release_mem_region(res1->start, res1->end - res1->start + 1); + release_mem_region(res1->start, resource_size(res1)); err_sync: iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res0->start, res0->end - res0->start + 1); + release_mem_region(res0->start, resource_size(res0)); err_map: kfree(gpiomtd); return ret; diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c new file mode 100644 index 00000000000..3d0867d829c --- /dev/null +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -0,0 +1,917 @@ +/* + * Copyright 2004-2008 Freescale Semiconductor, Inc. + * Copyright 2009 Semihalf. + * + * Approved as OSADL project by a majority of OSADL members and funded + * by OSADL membership fees in 2009; for details see www.osadl.org. + * + * Based on original driver from Freescale Semiconductor + * written by John Rigby <jrigby@freescale.com> on basis + * of drivers/mtd/nand/mxc_nand.c. Reworked and extended + * Piotr Ziecik <kosmo@semihalf.com>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/gfp.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> + +#include <asm/mpc5121.h> + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x200) + +/* Addresses for NFC SPARE BUFFER areas */ +#define NFC_SPARE_BUFFERS 8 +#define NFC_SPARE_LEN 0x40 +#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN)) + +/* MPC5121 NFC registers */ +#define NFC_BUF_ADDR 0x1E04 +#define NFC_FLASH_ADDR 0x1E06 +#define NFC_FLASH_CMD 0x1E08 +#define NFC_CONFIG 0x1E0A +#define NFC_ECC_STATUS1 0x1E0C +#define NFC_ECC_STATUS2 0x1E0E +#define NFC_SPAS 0x1E10 +#define NFC_WRPROT 0x1E12 +#define NFC_NF_WRPRST 0x1E18 +#define NFC_CONFIG1 0x1E1A +#define NFC_CONFIG2 0x1E1C +#define NFC_UNLOCKSTART_BLK0 0x1E20 +#define NFC_UNLOCKEND_BLK0 0x1E22 +#define NFC_UNLOCKSTART_BLK1 0x1E24 +#define NFC_UNLOCKEND_BLK1 0x1E26 +#define NFC_UNLOCKSTART_BLK2 0x1E28 +#define NFC_UNLOCKEND_BLK2 0x1E2A +#define NFC_UNLOCKSTART_BLK3 0x1E2C +#define NFC_UNLOCKEND_BLK3 0x1E2E + +/* Bit Definitions: NFC_BUF_ADDR */ +#define NFC_RBA_MASK (7 << 0) +#define NFC_ACTIVE_CS_SHIFT 5 +#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT) + +/* Bit Definitions: NFC_CONFIG */ +#define NFC_BLS_UNLOCKED (1 << 1) + +/* Bit Definitions: NFC_CONFIG1 */ +#define NFC_ECC_4BIT (1 << 0) +#define NFC_FULL_PAGE_DMA (1 << 1) +#define NFC_SPARE_ONLY (1 << 2) +#define NFC_ECC_ENABLE (1 << 3) +#define NFC_INT_MASK (1 << 4) +#define NFC_BIG_ENDIAN (1 << 5) +#define NFC_RESET (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) +#define NFC_PPB_32 (0 << 9) +#define NFC_PPB_64 (1 << 9) +#define NFC_PPB_128 (2 << 9) +#define NFC_PPB_256 (3 << 9) +#define NFC_PPB_MASK (3 << 9) +#define NFC_FULL_PAGE_INT (1 << 11) + +/* Bit Definitions: NFC_CONFIG2 */ +#define NFC_COMMAND (1 << 0) +#define NFC_ADDRESS (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) +#define NFC_CMD_FAIL (1 << 15) +#define NFC_INT (1 << 15) + +/* Bit Definitions: NFC_WRPROT */ +#define NFC_WPC_LOCK_TIGHT (1 << 0) +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) + +#define DRV_NAME "mpc5121_nfc" + +/* Timeouts */ +#define NFC_RESET_TIMEOUT 1000 /* 1 ms */ +#define NFC_TIMEOUT (HZ / 10) /* 1/10 s */ + +struct mpc5121_nfc_prv { + struct mtd_info mtd; + struct nand_chip chip; + int irq; + void __iomem *regs; + struct clk *clk; + wait_queue_head_t irq_waitq; + uint column; + int spareonly; + void __iomem *csreg; + struct device *dev; +}; + +static void mpc5121_nfc_done(struct mtd_info *mtd); + +#ifdef CONFIG_MTD_PARTITIONS +static const char *mpc5121_nfc_pprobes[] = { "cmdlinepart", NULL }; +#endif + +/* Read NFC register */ +static inline u16 nfc_read(struct mtd_info *mtd, uint reg) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + return in_be16(prv->regs + reg); +} + +/* Write NFC register */ +static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + out_be16(prv->regs + reg, val); +} + +/* Set bits in NFC register */ +static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); +} + +/* Clear bits in NFC register */ +static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); +} + +/* Invoke address cycle */ +static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr) +{ + nfc_write(mtd, NFC_FLASH_ADDR, addr); + nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS); + mpc5121_nfc_done(mtd); +} + +/* Invoke command cycle */ +static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd) +{ + nfc_write(mtd, NFC_FLASH_CMD, cmd); + nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND); + mpc5121_nfc_done(mtd); +} + +/* Send data from NFC buffers to NAND flash */ +static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_INPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive data from NAND flash */ +static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive ID from NAND flash */ +static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_ID); + mpc5121_nfc_done(mtd); +} + +/* Receive status from NAND flash */ +static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_STATUS); + mpc5121_nfc_done(mtd); +} + +/* NFC interrupt handler */ +static irqreturn_t mpc5121_nfc_irq(int irq, void *data) +{ + struct mtd_info *mtd = data; + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK); + wake_up(&prv->irq_waitq); + + return IRQ_HANDLED; +} + +/* Wait for operation complete */ +static void mpc5121_nfc_done(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + int rv; + + if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) == 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_INT_MASK); + rv = wait_event_timeout(prv->irq_waitq, + (nfc_read(mtd, NFC_CONFIG2) & NFC_INT), NFC_TIMEOUT); + + if (!rv) + dev_warn(prv->dev, + "Timeout while waiting for interrupt.\n"); + } + + nfc_clear(mtd, NFC_CONFIG2, NFC_INT); +} + +/* Do address cycle(s) */ +static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) +{ + struct nand_chip *chip = mtd->priv; + u32 pagemask = chip->pagemask; + + if (column != -1) { + mpc5121_nfc_send_addr(mtd, column); + if (mtd->writesize > 512) + mpc5121_nfc_send_addr(mtd, column >> 8); + } + + if (page != -1) { + do { + mpc5121_nfc_send_addr(mtd, page & 0xFF); + page >>= 8; + pagemask >>= 8; + } while (pagemask); + } +} + +/* Control chip select signals */ +static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + if (chip < 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_CE); + return; + } + + nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) & + NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_CONFIG1, NFC_CE); +} + +/* Init external chip select logic on ADS5121 board */ +static int ads5121_chipselect_init(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + struct device_node *dn; + + dn = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld"); + if (dn) { + prv->csreg = of_iomap(dn, 0); + of_node_put(dn); + if (!prv->csreg) + return -ENOMEM; + + /* CPLD Register 9 controls NAND /CE Lines */ + prv->csreg += 9; + return 0; + } + + return -EINVAL; +} + +/* Control chips select signal on ADS5121 board */ +static void ads5121_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd->priv; + struct mpc5121_nfc_prv *prv = nand->priv; + u8 v; + + v = in_8(prv->csreg); + v |= 0x0F; + + if (chip >= 0) { + mpc5121_nfc_select_chip(mtd, 0); + v &= ~(1 << chip); + } else + mpc5121_nfc_select_chip(mtd, -1); + + out_8(prv->csreg, v); +} + +/* Read NAND Ready/Busy signal */ +static int mpc5121_nfc_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles ready/busy signal internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +/* Write command to NAND flash */ +static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + prv->column = (column >= 0) ? column : 0; + prv->spareonly = 0; + + switch (command) { + case NAND_CMD_PAGEPROG: + mpc5121_nfc_send_prog_page(mtd); + break; + /* + * NFC does not support sub-page reads and writes, + * so emulate them using full page transfers. + */ + case NAND_CMD_READ0: + column = 0; + break; + + case NAND_CMD_READ1: + prv->column += 256; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_READOOB: + prv->spareonly = 1; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_SEQIN: + mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page); + column = 0; + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_READID: + case NAND_CMD_STATUS: + break; + + default: + return; + } + + mpc5121_nfc_send_cmd(mtd, command); + mpc5121_nfc_addr_cycle(mtd, column, page); + + switch (command) { + case NAND_CMD_READ0: + if (mtd->writesize > 512) + mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART); + mpc5121_nfc_send_read_page(mtd); + break; + + case NAND_CMD_READID: + mpc5121_nfc_send_read_id(mtd); + break; + + case NAND_CMD_STATUS: + mpc5121_nfc_send_read_status(mtd); + if (chip->options & NAND_BUSWIDTH_16) + prv->column = 1; + else + prv->column = 0; + break; + } +} + +/* Copy data from/to NFC spare buffers. */ +static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset, + u8 *buffer, uint size, int wr) +{ + struct nand_chip *nand = mtd->priv; + struct mpc5121_nfc_prv *prv = nand->priv; + uint o, s, sbsize, blksize; + + /* + * NAND spare area is available through NFC spare buffers. + * The NFC divides spare area into (page_size / 512) chunks. + * Each chunk is placed into separate spare memory area, using + * first (spare_size / num_of_chunks) bytes of the buffer. + * + * For NAND device in which the spare area is not divided fully + * by the number of chunks, number of used bytes in each spare + * buffer is rounded down to the nearest even number of bytes, + * and all remaining bytes are added to the last used spare area. + * + * For more information read section 26.6.10 of MPC5121e + * Microcontroller Reference Manual, Rev. 3. + */ + + /* Calculate number of valid bytes in each spare buffer */ + sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1; + + while (size) { + /* Calculate spare buffer number */ + s = offset / sbsize; + if (s > NFC_SPARE_BUFFERS - 1) + s = NFC_SPARE_BUFFERS - 1; + + /* + * Calculate offset to requested data block in selected spare + * buffer and its size. + */ + o = offset - (s * sbsize); + blksize = min(sbsize - o, size); + + if (wr) + memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o, + buffer, blksize); + else + memcpy_fromio(buffer, + prv->regs + NFC_SPARE_AREA(s) + o, blksize); + + buffer += blksize; + offset += blksize; + size -= blksize; + }; +} + +/* Copy data from/to NFC main and spare buffers */ +static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len, + int wr) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + uint c = prv->column; + uint l; + + /* Handle spare area access */ + if (prv->spareonly || c >= mtd->writesize) { + /* Calculate offset from beginning of spare area */ + if (c >= mtd->writesize) + c -= mtd->writesize; + + prv->column += len; + mpc5121_nfc_copy_spare(mtd, c, buf, len, wr); + return; + } + + /* + * Handle main area access - limit copy length to prevent + * crossing main/spare boundary. + */ + l = min((uint)len, mtd->writesize - c); + prv->column += l; + + if (wr) + memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l); + else + memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l); + + /* Handle crossing main/spare boundary */ + if (l != len) { + buf += l; + len -= l; + mpc5121_nfc_buf_copy(mtd, buf, len, wr); + } +} + +/* Read data from NFC buffers */ +static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, buf, len, 0); +} + +/* Write data to NFC buffers */ +static void mpc5121_nfc_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1); +} + +/* Compare buffer with NAND flash */ +static int mpc5121_nfc_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + u_char tmp[256]; + uint bsize; + + while (len) { + bsize = min(len, 256); + mpc5121_nfc_read_buf(mtd, tmp, bsize); + + if (memcmp(buf, tmp, bsize)) + return 1; + + buf += bsize; + len -= bsize; + } + + return 0; +} + +/* Read byte from NFC buffers */ +static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) +{ + u8 tmp; + + mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp)); + + return tmp; +} + +/* Read word from NFC buffers */ +static u16 mpc5121_nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + + mpc5121_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); + + return tmp; +} + +/* + * Read NFC configuration from Reset Config Word + * + * NFC is configured during reset in basis of information stored + * in Reset Config Word. There is no other way to set NAND block + * size, spare size and bus width. + */ +static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + struct mpc512x_reset_module *rm; + struct device_node *rmnode; + uint rcw_pagesize = 0; + uint rcw_sparesize = 0; + uint rcw_width; + uint rcwh; + uint romloc, ps; + + rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset"); + if (!rmnode) { + dev_err(prv->dev, "Missing 'fsl,mpc5121-reset' " + "node in device tree!\n"); + return -ENODEV; + } + + rm = of_iomap(rmnode, 0); + if (!rm) { + dev_err(prv->dev, "Error mapping reset module node!\n"); + return -EBUSY; + } + + rcwh = in_be32(&rm->rcwhr); + + /* Bit 6: NFC bus width */ + rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1; + + /* Bit 7: NFC Page/Spare size */ + ps = (rcwh >> 7) & 0x1; + + /* Bits [22:21]: ROM Location */ + romloc = (rcwh >> 21) & 0x3; + + /* Decode RCW bits */ + switch ((ps << 2) | romloc) { + case 0x00: + case 0x01: + rcw_pagesize = 512; + rcw_sparesize = 16; + break; + case 0x02: + case 0x03: + rcw_pagesize = 4096; + rcw_sparesize = 128; + break; + case 0x04: + case 0x05: + rcw_pagesize = 2048; + rcw_sparesize = 64; + break; + case 0x06: + case 0x07: + rcw_pagesize = 4096; + rcw_sparesize = 218; + break; + } + + mtd->writesize = rcw_pagesize; + mtd->oobsize = rcw_sparesize; + if (rcw_width == 2) + chip->options |= NAND_BUSWIDTH_16; + + dev_notice(prv->dev, "Configured for " + "%u-bit NAND, page size %u " + "with %u spare.\n", + rcw_width * 8, rcw_pagesize, + rcw_sparesize); + iounmap(rm); + of_node_put(rmnode); + return 0; +} + +/* Free driver resources */ +static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + if (prv->clk) { + clk_disable(prv->clk); + clk_put(prv->clk); + } + + if (prv->csreg) + iounmap(prv->csreg); +} + +static int __devinit mpc5121_nfc_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct device_node *rootnode, *dn = op->node; + struct device *dev = &op->dev; + struct mpc5121_nfc_prv *prv; + struct resource res; + struct mtd_info *mtd; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif + struct nand_chip *chip; + unsigned long regs_paddr, regs_size; + const uint *chips_no; + int resettime = 0; + int retval = 0; + int rev, len; + + /* + * Check SoC revision. This driver supports only NFC + * in MPC5121 revision 2 and MPC5123 revision 3. + */ + rev = (mfspr(SPRN_SVR) >> 4) & 0xF; + if ((rev != 2) && (rev != 3)) { + dev_err(dev, "SoC revision %u is not supported!\n", rev); + return -ENXIO; + } + + prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL); + if (!prv) { + dev_err(dev, "Memory exhausted!\n"); + return -ENOMEM; + } + + mtd = &prv->mtd; + chip = &prv->chip; + + mtd->priv = chip; + chip->priv = prv; + prv->dev = dev; + + /* Read NFC configuration from Reset Config Word */ + retval = mpc5121_nfc_read_hw_config(mtd); + if (retval) { + dev_err(dev, "Unable to read NFC config!\n"); + return retval; + } + + prv->irq = irq_of_parse_and_map(dn, 0); + if (prv->irq == NO_IRQ) { + dev_err(dev, "Error mapping IRQ!\n"); + return -EINVAL; + } + + retval = of_address_to_resource(dn, 0, &res); + if (retval) { + dev_err(dev, "Error parsing memory region!\n"); + return retval; + } + + chips_no = of_get_property(dn, "chips", &len); + if (!chips_no || len != sizeof(*chips_no)) { + dev_err(dev, "Invalid/missing 'chips' property!\n"); + return -EINVAL; + } + + regs_paddr = res.start; + regs_size = res.end - res.start + 1; + + if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) { + dev_err(dev, "Error requesting memory region!\n"); + return -EBUSY; + } + + prv->regs = devm_ioremap(dev, regs_paddr, regs_size); + if (!prv->regs) { + dev_err(dev, "Error mapping memory region!\n"); + return -ENOMEM; + } + + mtd->name = "MPC5121 NAND"; + chip->dev_ready = mpc5121_nfc_dev_ready; + chip->cmdfunc = mpc5121_nfc_command; + chip->read_byte = mpc5121_nfc_read_byte; + chip->read_word = mpc5121_nfc_read_word; + chip->read_buf = mpc5121_nfc_read_buf; + chip->write_buf = mpc5121_nfc_write_buf; + chip->verify_buf = mpc5121_nfc_verify_buf; + chip->select_chip = mpc5121_nfc_select_chip; + chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT; + chip->ecc.mode = NAND_ECC_SOFT; + + /* Support external chip-select logic on ADS5121 board */ + rootnode = of_find_node_by_path("/"); + if (of_device_is_compatible(rootnode, "fsl,mpc5121ads")) { + retval = ads5121_chipselect_init(mtd); + if (retval) { + dev_err(dev, "Chipselect init error!\n"); + of_node_put(rootnode); + return retval; + } + + chip->select_chip = ads5121_select_chip; + } + of_node_put(rootnode); + + /* Enable NFC clock */ + prv->clk = clk_get(dev, "nfc_clk"); + if (!prv->clk) { + dev_err(dev, "Unable to acquire NFC clock!\n"); + retval = -ENODEV; + goto error; + } + + clk_enable(prv->clk); + + /* Reset NAND Flash controller */ + nfc_set(mtd, NFC_CONFIG1, NFC_RESET); + while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) { + if (resettime++ >= NFC_RESET_TIMEOUT) { + dev_err(dev, "Timeout while resetting NFC!\n"); + retval = -EINVAL; + goto error; + } + + udelay(1); + } + + /* Enable write to NFC memory */ + nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED); + + /* Enable write to all NAND pages */ + nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000); + nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF); + nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK); + + /* + * Setup NFC: + * - Big Endian transfers, + * - Interrupt after full page read/write. + */ + nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK | + NFC_FULL_PAGE_INT); + + /* Set spare area size */ + nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1); + + init_waitqueue_head(&prv->irq_waitq); + retval = devm_request_irq(dev, prv->irq, &mpc5121_nfc_irq, 0, DRV_NAME, + mtd); + if (retval) { + dev_err(dev, "Error requesting IRQ!\n"); + goto error; + } + + /* Detect NAND chips */ + if (nand_scan(mtd, *chips_no)) { + dev_err(dev, "NAND Flash not found !\n"); + devm_free_irq(dev, prv->irq, mtd); + retval = -ENXIO; + goto error; + } + + /* Set erase block size */ + switch (mtd->erasesize / mtd->writesize) { + case 32: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32); + break; + + case 64: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64); + break; + + case 128: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128); + break; + + case 256: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256); + break; + + default: + dev_err(dev, "Unsupported NAND flash!\n"); + devm_free_irq(dev, prv->irq, mtd); + retval = -ENXIO; + goto error; + } + + dev_set_drvdata(dev, mtd); + + /* Register device in MTD */ +#ifdef CONFIG_MTD_PARTITIONS + retval = parse_mtd_partitions(mtd, mpc5121_nfc_pprobes, &parts, 0); +#ifdef CONFIG_MTD_OF_PARTS + if (retval == 0) + retval = of_mtd_parse_partitions(dev, dn, &parts); +#endif + if (retval < 0) { + dev_err(dev, "Error parsing MTD partitions!\n"); + devm_free_irq(dev, prv->irq, mtd); + retval = -EINVAL; + goto error; + } + + if (retval > 0) + retval = add_mtd_partitions(mtd, parts, retval); + else +#endif + retval = add_mtd_device(mtd); + + if (retval) { + dev_err(dev, "Error adding MTD device!\n"); + devm_free_irq(dev, prv->irq, mtd); + goto error; + } + + return 0; +error: + mpc5121_nfc_free(dev, mtd); + return retval; +} + +static int __devexit mpc5121_nfc_remove(struct of_device *op) +{ + struct device *dev = &op->dev; + struct mtd_info *mtd = dev_get_drvdata(dev); + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + nand_release(mtd); + devm_free_irq(dev, prv->irq, mtd); + mpc5121_nfc_free(dev, mtd); + + return 0; +} + +static struct of_device_id mpc5121_nfc_match[] __devinitdata = { + { .compatible = "fsl,mpc5121-nfc", }, + {}, +}; + +static struct of_platform_driver mpc5121_nfc_driver = { + .match_table = mpc5121_nfc_match, + .probe = mpc5121_nfc_probe, + .remove = __devexit_p(mpc5121_nfc_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init mpc5121_nfc_init(void) +{ + return of_register_platform_driver(&mpc5121_nfc_driver); +} + +module_init(mpc5121_nfc_init); + +static void __exit mpc5121_nfc_cleanup(void) +{ + of_unregister_platform_driver(&mpc5121_nfc_driver); +} + +module_exit(mpc5121_nfc_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MPC5121 NAND MTD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index b2900d8406d..82e94389824 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -38,7 +38,7 @@ #define DRIVER_NAME "mxc_nand" #define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35()) -#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27()) +#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21()) /* Addresses for NFC registers */ #define NFC_BUF_SIZE 0xE00 @@ -168,11 +168,7 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) { struct mxc_nand_host *host = dev_id; - uint16_t tmp; - - tmp = readw(host->regs + NFC_CONFIG1); - tmp |= NFC_INT_MSK; /* Disable interrupt */ - writew(tmp, host->regs + NFC_CONFIG1); + disable_irq_nosync(irq); wake_up(&host->irq_waitq); @@ -184,15 +180,13 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) */ static void wait_op_done(struct mxc_nand_host *host, int useirq) { - uint32_t tmp; - int max_retries = 2000; + uint16_t tmp; + int max_retries = 8000; if (useirq) { if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) { - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_INT_MSK; /* Enable interrupt */ - writew(tmp, host->regs + NFC_CONFIG1); + enable_irq(host->irq); wait_event(host->irq_waitq, readw(host->regs + NFC_CONFIG2) & NFC_INT); @@ -226,8 +220,23 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(cmd, host->regs + NFC_FLASH_CMD); writew(NFC_CMD, host->regs + NFC_CONFIG2); - /* Wait for operation to complete */ - wait_op_done(host, useirq); + if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) { + int max_retries = 100; + /* Reset completion is indicated by NFC_CONFIG2 */ + /* being set to 0 */ + while (max_retries-- > 0) { + if (readw(host->regs + NFC_CONFIG2) == 0) { + break; + } + udelay(1); + } + if (max_retries < 0) + DEBUG(MTD_DEBUG_LEVEL0, "%s: RESET failed\n", + __func__); + } else { + /* Wait for operation to complete */ + wait_op_done(host, useirq); + } } /* This function sends an address (or partial address) to the @@ -542,6 +551,41 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) } } +static void preset(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t tmp; + + /* enable interrupt, disable spare enable */ + tmp = readw(host->regs + NFC_CONFIG1); + tmp &= ~NFC_INT_MSK; + tmp &= ~NFC_SP_EN; + if (nand_chip->ecc.mode == NAND_ECC_HW) { + tmp |= NFC_ECC_EN; + } else { + tmp &= ~NFC_ECC_EN; + } + writew(tmp, host->regs + NFC_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, host->regs + NFC_CONFIG); + + /* Blocks to be unlocked */ + if (nfc_is_v21()) { + writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); + writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); + } else if (nfc_is_v1()) { + writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); + writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); + } else + BUG(); + + /* Unlock Block Command for given address range */ + writew(0x4, host->regs + NFC_WRPROT); +} + /* Used by the upper layer to write command to NAND Flash for * different operations to be carried out on NAND Flash */ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, @@ -559,6 +603,10 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Command pre-processing step */ switch (command) { + case NAND_CMD_RESET: + send_cmd(host, command, false); + preset(mtd); + break; case NAND_CMD_STATUS: host->buf_start = 0; @@ -679,7 +727,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; struct mxc_nand_host *host; struct resource *res; - uint16_t tmp; int err = 0, nr_parts = 0; struct nand_ecclayout *oob_smallpage, *oob_largepage; @@ -743,51 +790,17 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->spare_len = 64; oob_smallpage = &nandv2_hw_eccoob_smallpage; oob_largepage = &nandv2_hw_eccoob_largepage; + this->ecc.bytes = 9; } else if (nfc_is_v1()) { host->regs = host->base; host->spare0 = host->base + 0x800; host->spare_len = 16; oob_smallpage = &nandv1_hw_eccoob_smallpage; oob_largepage = &nandv1_hw_eccoob_largepage; - } else - BUG(); - - /* disable interrupt and spare enable */ - tmp = readw(host->regs + NFC_CONFIG1); - tmp |= NFC_INT_MSK; - tmp &= ~NFC_SP_EN; - writew(tmp, host->regs + NFC_CONFIG1); - - init_waitqueue_head(&host->irq_waitq); - - host->irq = platform_get_irq(pdev, 0); - - err = request_irq(host->irq, mxc_nfc_irq, 0, DRIVER_NAME, host); - if (err) - goto eirq; - - /* Reset NAND */ - this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - /* preset operation */ - /* Unlock the internal RAM Buffer */ - writew(0x2, host->regs + NFC_CONFIG); - - /* Blocks to be unlocked */ - if (nfc_is_v21()) { - writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); - writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); - this->ecc.bytes = 9; - } else if (nfc_is_v1()) { - writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); - writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); this->ecc.bytes = 3; } else BUG(); - /* Unlock Block Command for given address range */ - writew(0x4, host->regs + NFC_WRPROT); - this->ecc.size = 512; this->ecc.layout = oob_smallpage; @@ -796,14 +809,8 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = mxc_nand_correct_data; this->ecc.mode = NAND_ECC_HW; - tmp = readw(host->regs + NFC_CONFIG1); - tmp |= NFC_ECC_EN; - writew(tmp, host->regs + NFC_CONFIG1); } else { this->ecc.mode = NAND_ECC_SOFT; - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_ECC_EN; - writew(tmp, host->regs + NFC_CONFIG1); } /* NAND bus width determines access funtions used by upper layer */ @@ -817,8 +824,16 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->options |= NAND_USE_FLASH_BBT; } + init_waitqueue_head(&host->irq_waitq); + + host->irq = platform_get_irq(pdev, 0); + + err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); + if (err) + goto eirq; + /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1)) { + if (nand_scan_ident(mtd, 1, NULL)) { err = -ENXIO; goto escan; } @@ -886,11 +901,14 @@ static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) int ret = 0; DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); - if (mtd) { - ret = mtd->suspend(mtd); - /* Disable the NFC clock */ - clk_disable(host->clk); - } + + ret = mtd->suspend(mtd); + + /* + * nand_suspend locks the device for exclusive access, so + * the clock must already be off. + */ + BUG_ON(!ret && host->clk_act); return ret; } @@ -904,11 +922,7 @@ static int mxcnd_resume(struct platform_device *pdev) DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); - if (mtd) { - /* Enable the NFC clock */ - clk_enable(host->clk); - mtd->resume(mtd); - } + mtd->resume(mtd); return ret; } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8f2958fe214..4a7b86423ee 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -108,6 +108,35 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, */ DEFINE_LED_TRIGGER(nand_led_trigger); +static int check_offs_len(struct mtd_info *mtd, + loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd->priv; + int ret = 0; + + /* Start address must align on block boundary */ + if (ofs & ((1 << chip->phys_erase_shift) - 1)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + ret = -EINVAL; + } + + /* Length must align on block boundary */ + if (len & ((1 << chip->phys_erase_shift) - 1)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", + __func__); + ret = -EINVAL; + } + + /* Do not allow past end of device */ + if (ofs + len > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + /** * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure @@ -318,6 +347,9 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) struct nand_chip *chip = mtd->priv; u16 bad; + if (chip->options & NAND_BB_LAST_PAGE) + ofs += mtd->erasesize - mtd->writesize; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; if (getchip) { @@ -335,14 +367,18 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) bad = cpu_to_le16(chip->read_word(mtd)); if (chip->badblockpos & 0x1) bad >>= 8; - if ((bad & 0xFF) != 0xff) - res = 1; + else + bad &= 0xFF; } else { chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); - if (chip->read_byte(mtd) != 0xff) - res = 1; + bad = chip->read_byte(mtd); } + if (likely(chip->badblockbits == 8)) + res = bad != 0xFF; + else + res = hweight8(bad) < chip->badblockbits; + if (getchip) nand_release_device(mtd); @@ -363,6 +399,9 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) uint8_t buf[2] = { 0, 0 }; int block, ret; + if (chip->options & NAND_BB_LAST_PAGE) + ofs += mtd->erasesize - mtd->writesize; + /* Get block number */ block = (int)(ofs >> chip->bbt_erase_shift); if (chip->bbt) @@ -401,6 +440,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) static int nand_check_wp(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; + + /* broken xD cards report WP despite being writable */ + if (chip->options & NAND_BROKEN_XD) + return 0; + /* Check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; @@ -744,9 +788,6 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) chip->state = FL_PM_SUSPENDED; spin_unlock(lock); return 0; - } else { - spin_unlock(lock); - return -EAGAIN; } } set_current_state(TASK_UNINTERRUPTIBLE); @@ -835,6 +876,168 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) } /** + * __nand_unlock - [REPLACABLE] unlocks specified locked blockes + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * @invert - when = 0, unlock the range of blocks within the lower and + * upper boundary address + * whne = 1, unlock the range of blocks outside the boundaries + * of the lower and upper boundary address + * + * @return - unlock status + */ +static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, + uint64_t len, int invert) +{ + int ret = 0; + int status, page; + struct nand_chip *chip = mtd->priv; + + /* Submit address of first page to unlock */ + page = ofs >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); + + /* Submit address of last page to unlock */ + page = (ofs + len) >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, + (page | invert) & chip->pagemask); + + /* Call wait ready function */ + status = chip->waitfunc(mtd, chip); + udelay(1000); + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", + __func__, status); + ret = -EIO; + } + + return ret; +} + +/** + * nand_unlock - [REPLACABLE] unlocks specified locked blockes + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * + * @return - unlock status + */ +int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + int chipnr; + struct nand_chip *chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)ofs, len); + + if (check_offs_len(mtd, ofs, len)) + ret = -EINVAL; + + /* Align to last block address if size addresses end of the device */ + if (ofs + len == mtd->size) + len -= mtd->erasesize; + + nand_get_device(chip, mtd, FL_UNLOCKING); + + /* Shift to get chip number */ + chipnr = ofs >> chip->chip_shift; + + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); + ret = -EIO; + goto out; + } + + ret = __nand_unlock(mtd, ofs, len, 0); + +out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + + nand_release_device(mtd); + + return ret; +} + +/** + * nand_lock - [REPLACABLE] locks all blockes present in the device + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * + * @return - lock status + * + * This feature is not support in many NAND parts. 'Micron' NAND parts + * do have this feature, but it allows only to lock all blocks not for + * specified range for block. + * + * Implementing 'lock' feature by making use of 'unlock', for now. + */ +int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + int chipnr, status, page; + struct nand_chip *chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)ofs, len); + + if (check_offs_len(mtd, ofs, len)) + ret = -EINVAL; + + nand_get_device(chip, mtd, FL_LOCKING); + + /* Shift to get chip number */ + chipnr = ofs >> chip->chip_shift; + + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); + status = MTD_ERASE_FAILED; + ret = -EIO; + goto out; + } + + /* Submit address of first page to lock */ + page = ofs >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask); + + /* Call wait ready function */ + status = chip->waitfunc(mtd, chip); + udelay(1000); + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", + __func__, status); + ret = -EIO; + goto out; + } + + ret = __nand_unlock(mtd, ofs, len, 0x1); + +out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + + nand_release_device(mtd); + + return ret; +} + +/** * nand_read_page_raw - [Intern] read raw page data without ecc * @mtd: mtd info structure * @chip: nand chip info structure @@ -1232,6 +1435,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; + uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ? + mtd->oobavail : mtd->oobsize; + uint8_t *bufpoi, *oob, *buf; stats = mtd->ecc_stats; @@ -1282,18 +1488,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf += bytes; if (unlikely(oob)) { - /* Raw mode does data:oob:data:oob */ - if (ops->mode != MTD_OOB_RAW) { - int toread = min(oobreadlen, - chip->ecc.layout->oobavail); - if (toread) { - oob = nand_transfer_oob(chip, - oob, ops, toread); - oobreadlen -= toread; - } - } else - buf = nand_transfer_oob(chip, - buf, ops, mtd->oobsize); + + int toread = min(oobreadlen, max_oobsize); + + if (toread) { + oob = nand_transfer_oob(chip, + oob, ops, toread); + oobreadlen -= toread; + } } if (!(chip->options & NAND_NO_READRDY)) { @@ -1880,11 +2082,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, * @oob: oob data buffer * @ops: oob ops structure */ -static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, - struct mtd_oob_ops *ops) +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, + struct mtd_oob_ops *ops) { - size_t len = ops->ooblen; - switch(ops->mode) { case MTD_OOB_PLACE: @@ -1939,6 +2139,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, int chipnr, realpage, page, blockmask, column; struct nand_chip *chip = mtd->priv; uint32_t writelen = ops->len; + + uint32_t oobwritelen = ops->ooblen; + uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ? + mtd->oobavail : mtd->oobsize; + uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; @@ -1980,6 +2185,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (likely(!oob)) memset(chip->oob_poi, 0xff, mtd->oobsize); + /* Don't allow multipage oob writes with offset */ + if (ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) + return -EINVAL; + while(1) { int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; @@ -1995,8 +2204,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, wbuf = chip->buffers->databuf; } - if (unlikely(oob)) - oob = nand_fill_oob(chip, oob, ops); + if (unlikely(oob)) { + size_t len = min(oobwritelen, oobmaxlen); + oob = nand_fill_oob(chip, oob, len, ops); + oobwritelen -= len; + } ret = chip->write_page(mtd, chip, wbuf, page, cached, (ops->mode == MTD_OOB_RAW)); @@ -2170,7 +2382,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, chip->pagebuf = -1; memset(chip->oob_poi, 0xff, mtd->oobsize); - nand_fill_oob(chip, ops->oobbuf, ops); + nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops); status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); memset(chip->oob_poi, 0xff, mtd->oobsize); @@ -2293,25 +2505,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, __func__, (unsigned long long)instr->addr, (unsigned long long)instr->len); - /* Start address must align on block boundary */ - if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + if (check_offs_len(mtd, instr->addr, instr->len)) return -EINVAL; - } - - /* Length must align on block boundary */ - if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", - __func__); - return -EINVAL; - } - - /* Do not allow erase past end of device */ - if ((instr->len + instr->addr) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Erase past end of device\n", - __func__); - return -EINVAL; - } instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; @@ -2582,11 +2777,11 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) */ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, - int busw, int *maf_id) + int busw, int *maf_id, + struct nand_flash_dev *type) { - struct nand_flash_dev *type = NULL; int i, dev_id, maf_idx; - int tmp_id, tmp_manf; + u8 id_data[8]; /* Select the device */ chip->select_chip(mtd, 0); @@ -2612,27 +2807,26 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - /* Read manufacturer and device IDs */ + /* Read entire ID string */ - tmp_manf = chip->read_byte(mtd); - tmp_id = chip->read_byte(mtd); + for (i = 0; i < 8; i++) + id_data[i] = chip->read_byte(mtd); - if (tmp_manf != *maf_id || tmp_id != dev_id) { + if (id_data[0] != *maf_id || id_data[1] != dev_id) { printk(KERN_INFO "%s: second ID read did not match " "%02x,%02x against %02x,%02x\n", __func__, - *maf_id, dev_id, tmp_manf, tmp_id); + *maf_id, dev_id, id_data[0], id_data[1]); return ERR_PTR(-ENODEV); } - /* Lookup the flash id */ - for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (dev_id == nand_flash_ids[i].id) { - type = &nand_flash_ids[i]; - break; - } - } - if (!type) + type = nand_flash_ids; + + for (; type->name != NULL; type++) + if (dev_id == type->id) + break; + + if (!type->name) return ERR_PTR(-ENODEV); if (!mtd->name) @@ -2644,21 +2838,45 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (!type->pagesize) { int extid; /* The 3rd id byte holds MLC / multichip data */ - chip->cellinfo = chip->read_byte(mtd); + chip->cellinfo = id_data[2]; /* The 4th id byte is the important one */ - extid = chip->read_byte(mtd); - /* Calc pagesize */ - mtd->writesize = 1024 << (extid & 0x3); - extid >>= 2; - /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); - extid >>= 2; - /* Calc blocksize. Blocksize is multiples of 64KiB */ - mtd->erasesize = (64 * 1024) << (extid & 0x03); - extid >>= 2; - /* Get buswidth information */ - busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + extid = id_data[3]; + /* + * Field definitions are in the following datasheets: + * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) + * New style (6 byte ID): Samsung K9GAG08U0D (p.40) + * + * Check for wraparound + Samsung ID + nonzero 6th byte + * to decide what to do. + */ + if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && + id_data[0] == NAND_MFR_SAMSUNG && + id_data[5] != 0x00) { + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (extid & 0x03) == 0x01 ? 128 : 218; + extid >>= 2; + /* Calc blocksize */ + mtd->erasesize = (128 * 1024) << + (((extid >> 1) & 0x04) | (extid & 0x03)); + busw = 0; + } else { + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * + (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + } } else { /* * Old devices have chip data hardcoded in the device id table @@ -2704,6 +2922,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, /* Set the bad block position */ chip->badblockpos = mtd->writesize > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + chip->badblockbits = 8; /* Get chip options, preserve non chip based options */ chip->options &= ~NAND_CHIPOPTIONS_MSK; @@ -2720,6 +2939,15 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; + /* + * Bad block marker is stored in the last page of each block + * on Samsung and Hynix MLC devices + */ + if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + (*maf_id == NAND_MFR_SAMSUNG || + *maf_id == NAND_MFR_HYNIX)) + chip->options |= NAND_BB_LAST_PAGE; + /* Check for AND chips with 4 page planes */ if (chip->options & NAND_4PAGE_ARRAY) chip->erase_cmd = multi_erase_cmd; @@ -2741,13 +2969,15 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, * nand_scan_ident - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * @maxchips: Number of chips to scan for + * @table: Alternative NAND ID table * * This is the first phase of the normal nand_scan() function. It * reads the flash ID and sets up MTD fields accordingly. * * The mtd->owner field must be set to the module of the caller. */ -int nand_scan_ident(struct mtd_info *mtd, int maxchips) +int nand_scan_ident(struct mtd_info *mtd, int maxchips, + struct nand_flash_dev *table) { int i, busw, nand_maf_id; struct nand_chip *chip = mtd->priv; @@ -2759,7 +2989,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) nand_set_defaults(chip, busw); /* Read the flash type */ - type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); + type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table); if (IS_ERR(type)) { if (!(chip->options & NAND_SCAN_SILENT_NODEV)) @@ -2989,7 +3219,8 @@ int nand_scan_tail(struct mtd_info *mtd) /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; - mtd->flags = MTD_CAP_NANDFLASH; + mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : + MTD_CAP_NANDFLASH; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; @@ -3050,7 +3281,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) BUG(); } - ret = nand_scan_ident(mtd, maxchips); + ret = nand_scan_ident(mtd, maxchips, NULL); if (!ret) ret = nand_scan_tail(mtd); return ret; @@ -3077,6 +3308,8 @@ void nand_release(struct mtd_info *mtd) kfree(chip->buffers); } +EXPORT_SYMBOL_GPL(nand_lock); +EXPORT_SYMBOL_GPL(nand_unlock); EXPORT_SYMBOL_GPL(nand_scan); EXPORT_SYMBOL_GPL(nand_scan_ident); EXPORT_SYMBOL_GPL(nand_scan_tail); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 55c23e5cd21..ad97c0ce73b 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -237,15 +237,33 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, size_t len) { struct mtd_oob_ops ops; + int res; ops.mode = MTD_OOB_RAW; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; - ops.oobbuf = buf; - ops.datbuf = buf; - ops.len = len; - return mtd->read_oob(mtd, offs, &ops); + + while (len > 0) { + if (len <= mtd->writesize) { + ops.oobbuf = buf + len; + ops.datbuf = buf; + ops.len = len; + return mtd->read_oob(mtd, offs, &ops); + } else { + ops.oobbuf = buf + mtd->writesize; + ops.datbuf = buf; + ops.len = mtd->writesize; + res = mtd->read_oob(mtd, offs, &ops); + + if (res) + return res; + } + + buf += mtd->oobsize + mtd->writesize; + len -= mtd->writesize; + } + return 0; } /* @@ -414,6 +432,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, from = (loff_t)startblock << (this->bbt_erase_shift - 1); } + if (this->options & NAND_BB_LAST_PAGE) + from += mtd->erasesize - (mtd->writesize * len); + for (i = startblock; i < numblocks;) { int ret; diff --git a/drivers/mtd/nand/nand_bcm_umi.h b/drivers/mtd/nand/nand_bcm_umi.h index 7cec2cd9785..198b304d6f7 100644 --- a/drivers/mtd/nand/nand_bcm_umi.h +++ b/drivers/mtd/nand/nand_bcm_umi.h @@ -167,18 +167,27 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, int numToRead = 16; /* There are 16 bytes per sector in the OOB */ /* ECC is already paused when this function is called */ + if (pageSize != NAND_DATA_ACCESS_SIZE) { + /* skip BI */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + } - if (pageSize == NAND_DATA_ACCESS_SIZE) { - while (numToRead > numEccBytes) { - /* skip free oob region */ + while (numToRead > numEccBytes) { + /* skip free oob region */ #if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; + *oobp++ = REG_NAND_DATA8; #else - REG_NAND_DATA8; + REG_NAND_DATA8; #endif - numToRead--; - } + numToRead--; + } + if (pageSize == NAND_DATA_ACCESS_SIZE) { /* read ECC bytes before BI */ nand_bcm_umi_bch_resume_read_ecc_calc(); @@ -190,6 +199,7 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, #else eccCalc[eccPos++] = REG_NAND_DATA8; #endif + numToRead--; } nand_bcm_umi_bch_pause_read_ecc_calc(); @@ -204,49 +214,18 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, numToRead--; } - /* read ECC bytes */ - nand_bcm_umi_bch_resume_read_ecc_calc(); - while (numToRead) { -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp = REG_NAND_DATA8; - eccCalc[eccPos++] = *oobp; - oobp++; -#else - eccCalc[eccPos++] = REG_NAND_DATA8; -#endif - numToRead--; - } - } else { - /* skip BI */ + } + /* read ECC bytes */ + nand_bcm_umi_bch_resume_read_ecc_calc(); + while (numToRead) { #if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; + *oobp = REG_NAND_DATA8; + eccCalc[eccPos++] = *oobp; + oobp++; #else - REG_NAND_DATA8; + eccCalc[eccPos++] = REG_NAND_DATA8; #endif numToRead--; - - while (numToRead > numEccBytes) { - /* skip free oob region */ -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; -#else - REG_NAND_DATA8; -#endif - numToRead--; - } - - /* read ECC bytes */ - nand_bcm_umi_bch_resume_read_ecc_calc(); - while (numToRead) { -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp = REG_NAND_DATA8; - eccCalc[eccPos++] = *oobp; - oobp++; -#else - eccCalc[eccPos++] = REG_NAND_DATA8; -#endif - numToRead--; - } } } diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 69ee2c90eb0..89907ed9900 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -82,6 +82,7 @@ struct nand_flash_dev nand_flash_ids[] = { /* 1 Gigabit */ {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 3,3V 8-bit", 0xD1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 7281000fef2..261337efe0e 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -80,6 +80,9 @@ #ifndef CONFIG_NANDSIM_DBG #define CONFIG_NANDSIM_DBG 0 #endif +#ifndef CONFIG_NANDSIM_MAX_PARTS +#define CONFIG_NANDSIM_MAX_PARTS 32 +#endif static uint first_id_byte = CONFIG_NANDSIM_FIRST_ID_BYTE; static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE; @@ -94,7 +97,7 @@ static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; static uint log = CONFIG_NANDSIM_LOG; static uint dbg = CONFIG_NANDSIM_DBG; -static unsigned long parts[MAX_MTD_DEVICES]; +static unsigned long parts[CONFIG_NANDSIM_MAX_PARTS]; static unsigned int parts_num; static char *badblocks = NULL; static char *weakblocks = NULL; @@ -135,8 +138,8 @@ MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read I MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)"); MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); -MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); -MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanodeconds)"); +MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanoseconds)"); +MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanoseconds)"); MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); MODULE_PARM_DESC(log, "Perform logging if not zero"); @@ -288,7 +291,7 @@ union ns_mem { * The structure which describes all the internal simulator data. */ struct nandsim { - struct mtd_partition partitions[MAX_MTD_DEVICES]; + struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS]; unsigned int nbparts; uint busw; /* flash chip bus width (8 or 16) */ @@ -312,7 +315,7 @@ struct nandsim { union ns_mem buf; /* NAND flash "geometry" */ - struct nandsin_geometry { + struct { uint64_t totsz; /* total flash size, bytes */ uint32_t secsz; /* flash sector (erase block) size, bytes */ uint pgsz; /* NAND flash page size, bytes */ @@ -331,7 +334,7 @@ struct nandsim { } geom; /* NAND flash internal registers */ - struct nandsim_regs { + struct { unsigned command; /* the command register */ u_char status; /* the status register */ uint row; /* the page number */ @@ -342,7 +345,7 @@ struct nandsim { } regs; /* NAND flash lines state */ - struct ns_lines_status { + struct { int ce; /* chip Enable */ int cle; /* command Latch Enable */ int ale; /* address Latch Enable */ diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c index 1f6f741af5d..8c0b6937522 100644 --- a/drivers/mtd/nand/nomadik_nand.c +++ b/drivers/mtd/nand/nomadik_nand.c @@ -105,21 +105,21 @@ static int nomadik_nand_probe(struct platform_device *pdev) ret = -EIO; goto err_unmap; } - host->addr_va = ioremap(res->start, res->end - res->start + 1); + host->addr_va = ioremap(res->start, resource_size(res)); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); if (!res) { ret = -EIO; goto err_unmap; } - host->data_va = ioremap(res->start, res->end - res->start + 1); + host->data_va = ioremap(res->start, resource_size(res)); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); if (!res) { ret = -EIO; goto err_unmap; } - host->cmd_va = ioremap(res->start, res->end - res->start + 1); + host->cmd_va = ioremap(res->start, resource_size(res)); if (!host->addr_va || !host->data_va || !host->cmd_va) { ret = -ENOMEM; diff --git a/drivers/mtd/nand/w90p910_nand.c b/drivers/mtd/nand/nuc900_nand.c index 7680e731348..6eddf7361ed 100644 --- a/drivers/mtd/nand/w90p910_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Nuvoton technology corporation. + * Copyright © 2009 Nuvoton technology corporation. * * Wan ZongShun <mcuos.com@gmail.com> * @@ -55,7 +55,7 @@ #define write_addr_reg(dev, val) \ __raw_writel((val), (dev)->reg + REG_SMADDR) -struct w90p910_nand { +struct nuc900_nand { struct mtd_info mtd; struct nand_chip chip; void __iomem *reg; @@ -76,49 +76,49 @@ static const struct mtd_partition partitions[] = { } }; -static unsigned char w90p910_nand_read_byte(struct mtd_info *mtd) +static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd) { unsigned char ret; - struct w90p910_nand *nand; + struct nuc900_nand *nand; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); ret = (unsigned char)read_data_reg(nand); return ret; } -static void w90p910_nand_read_buf(struct mtd_info *mtd, - unsigned char *buf, int len) +static void nuc900_nand_read_buf(struct mtd_info *mtd, + unsigned char *buf, int len) { int i; - struct w90p910_nand *nand; + struct nuc900_nand *nand; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); for (i = 0; i < len; i++) buf[i] = (unsigned char)read_data_reg(nand); } -static void w90p910_nand_write_buf(struct mtd_info *mtd, - const unsigned char *buf, int len) +static void nuc900_nand_write_buf(struct mtd_info *mtd, + const unsigned char *buf, int len) { int i; - struct w90p910_nand *nand; + struct nuc900_nand *nand; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); for (i = 0; i < len; i++) write_data_reg(nand, buf[i]); } -static int w90p910_verify_buf(struct mtd_info *mtd, - const unsigned char *buf, int len) +static int nuc900_verify_buf(struct mtd_info *mtd, + const unsigned char *buf, int len) { int i; - struct w90p910_nand *nand; + struct nuc900_nand *nand; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); for (i = 0; i < len; i++) { if (buf[i] != (unsigned char)read_data_reg(nand)) @@ -128,7 +128,7 @@ static int w90p910_verify_buf(struct mtd_info *mtd, return 0; } -static int w90p910_check_rb(struct w90p910_nand *nand) +static int nuc900_check_rb(struct nuc900_nand *nand) { unsigned int val; spin_lock(&nand->lock); @@ -139,24 +139,24 @@ static int w90p910_check_rb(struct w90p910_nand *nand) return val; } -static int w90p910_nand_devready(struct mtd_info *mtd) +static int nuc900_nand_devready(struct mtd_info *mtd) { - struct w90p910_nand *nand; + struct nuc900_nand *nand; int ready; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); - ready = (w90p910_check_rb(nand)) ? 1 : 0; + ready = (nuc900_check_rb(nand)) ? 1 : 0; return ready; } -static void w90p910_nand_command_lp(struct mtd_info *mtd, - unsigned int command, int column, int page_addr) +static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) { register struct nand_chip *chip = mtd->priv; - struct w90p910_nand *nand; + struct nuc900_nand *nand; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); if (command == NAND_CMD_READOOB) { column += mtd->writesize; @@ -212,7 +212,7 @@ static void w90p910_nand_command_lp(struct mtd_info *mtd, write_cmd_reg(nand, NAND_CMD_STATUS); write_cmd_reg(nand, command); - while (!w90p910_check_rb(nand)) + while (!nuc900_check_rb(nand)) ; return; @@ -241,7 +241,7 @@ static void w90p910_nand_command_lp(struct mtd_info *mtd, } -static void w90p910_nand_enable(struct w90p910_nand *nand) +static void nuc900_nand_enable(struct nuc900_nand *nand) { unsigned int val; spin_lock(&nand->lock); @@ -262,37 +262,37 @@ static void w90p910_nand_enable(struct w90p910_nand *nand) spin_unlock(&nand->lock); } -static int __devinit w90p910_nand_probe(struct platform_device *pdev) +static int __devinit nuc900_nand_probe(struct platform_device *pdev) { - struct w90p910_nand *w90p910_nand; + struct nuc900_nand *nuc900_nand; struct nand_chip *chip; int retval; struct resource *res; retval = 0; - w90p910_nand = kzalloc(sizeof(struct w90p910_nand), GFP_KERNEL); - if (!w90p910_nand) + nuc900_nand = kzalloc(sizeof(struct nuc900_nand), GFP_KERNEL); + if (!nuc900_nand) return -ENOMEM; - chip = &(w90p910_nand->chip); + chip = &(nuc900_nand->chip); - w90p910_nand->mtd.priv = chip; - w90p910_nand->mtd.owner = THIS_MODULE; - spin_lock_init(&w90p910_nand->lock); + nuc900_nand->mtd.priv = chip; + nuc900_nand->mtd.owner = THIS_MODULE; + spin_lock_init(&nuc900_nand->lock); - w90p910_nand->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(w90p910_nand->clk)) { + nuc900_nand->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(nuc900_nand->clk)) { retval = -ENOENT; goto fail1; } - clk_enable(w90p910_nand->clk); - - chip->cmdfunc = w90p910_nand_command_lp; - chip->dev_ready = w90p910_nand_devready; - chip->read_byte = w90p910_nand_read_byte; - chip->write_buf = w90p910_nand_write_buf; - chip->read_buf = w90p910_nand_read_buf; - chip->verify_buf = w90p910_verify_buf; + clk_enable(nuc900_nand->clk); + + chip->cmdfunc = nuc900_nand_command_lp; + chip->dev_ready = nuc900_nand_devready; + chip->read_byte = nuc900_nand_read_byte; + chip->write_buf = nuc900_nand_write_buf; + chip->read_buf = nuc900_nand_read_buf; + chip->verify_buf = nuc900_verify_buf; chip->chip_delay = 50; chip->options = 0; chip->ecc.mode = NAND_ECC_SOFT; @@ -308,75 +308,75 @@ static int __devinit w90p910_nand_probe(struct platform_device *pdev) goto fail1; } - w90p910_nand->reg = ioremap(res->start, resource_size(res)); - if (!w90p910_nand->reg) { + nuc900_nand->reg = ioremap(res->start, resource_size(res)); + if (!nuc900_nand->reg) { retval = -ENOMEM; goto fail2; } - w90p910_nand_enable(w90p910_nand); + nuc900_nand_enable(nuc900_nand); - if (nand_scan(&(w90p910_nand->mtd), 1)) { + if (nand_scan(&(nuc900_nand->mtd), 1)) { retval = -ENXIO; goto fail3; } - add_mtd_partitions(&(w90p910_nand->mtd), partitions, + add_mtd_partitions(&(nuc900_nand->mtd), partitions, ARRAY_SIZE(partitions)); - platform_set_drvdata(pdev, w90p910_nand); + platform_set_drvdata(pdev, nuc900_nand); return retval; -fail3: iounmap(w90p910_nand->reg); +fail3: iounmap(nuc900_nand->reg); fail2: release_mem_region(res->start, resource_size(res)); -fail1: kfree(w90p910_nand); +fail1: kfree(nuc900_nand); return retval; } -static int __devexit w90p910_nand_remove(struct platform_device *pdev) +static int __devexit nuc900_nand_remove(struct platform_device *pdev) { - struct w90p910_nand *w90p910_nand = platform_get_drvdata(pdev); + struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); struct resource *res; - iounmap(w90p910_nand->reg); + iounmap(nuc900_nand->reg); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); - clk_disable(w90p910_nand->clk); - clk_put(w90p910_nand->clk); + clk_disable(nuc900_nand->clk); + clk_put(nuc900_nand->clk); - kfree(w90p910_nand); + kfree(nuc900_nand); platform_set_drvdata(pdev, NULL); return 0; } -static struct platform_driver w90p910_nand_driver = { - .probe = w90p910_nand_probe, - .remove = __devexit_p(w90p910_nand_remove), +static struct platform_driver nuc900_nand_driver = { + .probe = nuc900_nand_probe, + .remove = __devexit_p(nuc900_nand_remove), .driver = { - .name = "w90p910-fmi", + .name = "nuc900-fmi", .owner = THIS_MODULE, }, }; -static int __init w90p910_nand_init(void) +static int __init nuc900_nand_init(void) { - return platform_driver_register(&w90p910_nand_driver); + return platform_driver_register(&nuc900_nand_driver); } -static void __exit w90p910_nand_exit(void) +static void __exit nuc900_nand_exit(void) { - platform_driver_unregister(&w90p910_nand_driver); + platform_driver_unregister(&nuc900_nand_driver); } -module_init(w90p910_nand_init); -module_exit(w90p910_nand_exit); +module_init(nuc900_nand_init); +module_exit(nuc900_nand_exit); MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); -MODULE_DESCRIPTION("w90p910 nand driver!"); +MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:w90p910-fmi"); +MODULE_ALIAS("platform:nuc900-fmi"); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 7545568fce4..ee87325c771 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -292,11 +292,14 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) u32 *p = (u32 *)buf; /* take care of subpage reads */ - for (; len % 4 != 0; ) { - *buf++ = __raw_readb(info->nand.IO_ADDR_R); - len--; + if (len % 4) { + if (info->nand.options & NAND_BUSWIDTH_16) + omap_read_buf16(mtd, buf, len % 4); + else + omap_read_buf8(mtd, buf, len % 4); + p = (u32 *) (buf + len % 4); + len -= len % 4; } - p = (u32 *) buf; /* configure and start prefetch transfer */ ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0); @@ -502,7 +505,7 @@ static void omap_write_buf_dma_pref(struct mtd_info *mtd, omap_write_buf_pref(mtd, buf, len); else /* start transfer in DMA mode */ - omap_nand_dma_transfer(mtd, buf, len, 0x1); + omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1); } /** @@ -1028,7 +1031,8 @@ out_free_info: static int omap_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); - struct omap_nand_info *info = mtd->priv; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); platform_set_drvdata(pdev, NULL); if (use_dma) diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index d60fc5719fe..da6e7534305 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -80,6 +80,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) struct mtd_info *mtd; struct nand_chip *nc; struct orion_nand_data *board; + struct resource *res; void __iomem *io_base; int ret = 0; #ifdef CONFIG_MTD_PARTITIONS @@ -95,8 +96,13 @@ static int __init orion_nand_probe(struct platform_device *pdev) } mtd = (struct mtd_info *)(nc + 1); - io_base = ioremap(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto no_res; + } + + io_base = ioremap(res->start, resource_size(res)); if (!io_base) { printk(KERN_ERR "orion_nand: ioremap failed\n"); ret = -EIO; @@ -120,6 +126,9 @@ static int __init orion_nand_probe(struct platform_device *pdev) if (board->width == 16) nc->options |= NAND_BUSWIDTH_16; + if (board->dev_ready) + nc->dev_ready = board->dev_ready; + platform_set_drvdata(pdev, mtd); if (nand_scan(mtd, 1)) { diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index a8b9376cf32..090a05c12cb 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -209,7 +209,7 @@ static int __devexit pasemi_nand_remove(struct of_device *ofdev) return 0; } -static struct of_device_id pasemi_nand_match[] = +static const struct of_device_id pasemi_nand_match[] = { { .compatible = "pasemi,localbus-nand", diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 5d55152162c..e02fa4f0e3c 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1320,6 +1320,17 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) goto fail_free_irq; } + if (mtd_has_cmdlinepart()) { + static const char *probes[] = { "cmdlinepart", NULL }; + struct mtd_partition *parts; + int nr_parts; + + nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0); + + if (nr_parts) + return add_mtd_partitions(mtd, parts, nr_parts); + } + return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); fail_free_irq: diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c new file mode 100644 index 00000000000..78a42329547 --- /dev/null +++ b/drivers/mtd/nand/r852.c @@ -0,0 +1,1140 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * driver for Ricoh xD readers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <asm/byteorder.h> +#include <linux/sched.h> +#include "sm_common.h" +#include "r852.h" + + +static int r852_enable_dma = 1; +module_param(r852_enable_dma, bool, S_IRUGO); +MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)"); + +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +/* read register */ +static inline uint8_t r852_read_reg(struct r852_device *dev, int address) +{ + uint8_t reg = readb(dev->mmio + address); + return reg; +} + +/* write register */ +static inline void r852_write_reg(struct r852_device *dev, + int address, uint8_t value) +{ + writeb(value, dev->mmio + address); + mmiowb(); +} + + +/* read dword sized register */ +static inline uint32_t r852_read_reg_dword(struct r852_device *dev, int address) +{ + uint32_t reg = le32_to_cpu(readl(dev->mmio + address)); + return reg; +} + +/* write dword sized register */ +static inline void r852_write_reg_dword(struct r852_device *dev, + int address, uint32_t value) +{ + writel(cpu_to_le32(value), dev->mmio + address); + mmiowb(); +} + +/* returns pointer to our private structure */ +static inline struct r852_device *r852_get_dev(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + return (struct r852_device *)chip->priv; +} + + +/* check if controller supports dma */ +static void r852_dma_test(struct r852_device *dev) +{ + dev->dma_usable = (r852_read_reg(dev, R852_DMA_CAP) & + (R852_DMA1 | R852_DMA2)) == (R852_DMA1 | R852_DMA2); + + if (!dev->dma_usable) + message("Non dma capable device detected, dma disabled"); + + if (!r852_enable_dma) { + message("disabling dma on user request"); + dev->dma_usable = 0; + } +} + +/* + * Enable dma. Enables ether first or second stage of the DMA, + * Expects dev->dma_dir and dev->dma_state be set + */ +static void r852_dma_enable(struct r852_device *dev) +{ + uint8_t dma_reg, dma_irq_reg; + + /* Set up dma settings */ + dma_reg = r852_read_reg_dword(dev, R852_DMA_SETTINGS); + dma_reg &= ~(R852_DMA_READ | R852_DMA_INTERNAL | R852_DMA_MEMORY); + + if (dev->dma_dir) + dma_reg |= R852_DMA_READ; + + if (dev->dma_state == DMA_INTERNAL) { + dma_reg |= R852_DMA_INTERNAL; + /* Precaution to make sure HW doesn't write */ + /* to random kernel memory */ + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_bounce_buffer)); + } else { + dma_reg |= R852_DMA_MEMORY; + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_dma_addr)); + } + + /* Precaution: make sure write reached the device */ + r852_read_reg_dword(dev, R852_DMA_ADDR); + + r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg); + + /* Set dma irq */ + dma_irq_reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, + dma_irq_reg | + R852_DMA_IRQ_INTERNAL | + R852_DMA_IRQ_ERROR | + R852_DMA_IRQ_MEMORY); +} + +/* + * Disable dma, called from the interrupt handler, which specifies + * success of the operation via 'error' argument + */ +static void r852_dma_done(struct r852_device *dev, int error) +{ + WARN_ON(dev->dma_stage == 0); + + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, + r852_read_reg_dword(dev, R852_DMA_IRQ_STA)); + + r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, 0); + + /* Precaution to make sure HW doesn't write to random kernel memory */ + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_bounce_buffer)); + r852_read_reg_dword(dev, R852_DMA_ADDR); + + dev->dma_error = error; + dev->dma_stage = 0; + + if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer) + pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN, + dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + complete(&dev->dma_done); +} + +/* + * Wait, till dma is done, which includes both phases of it + */ +static int r852_dma_wait(struct r852_device *dev) +{ + long timeout = wait_for_completion_timeout(&dev->dma_done, + msecs_to_jiffies(1000)); + if (!timeout) { + dbg("timeout waiting for DMA interrupt"); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * Read/Write one page using dma. Only pages can be read (512 bytes) +*/ +static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) +{ + int bounce = 0; + unsigned long flags; + int error; + + dev->dma_error = 0; + + /* Set dma direction */ + dev->dma_dir = do_read; + dev->dma_stage = 1; + + dbg_verbose("doing dma %s ", do_read ? "read" : "write"); + + /* Set intial dma state: for reading first fill on board buffer, + from device, for writes first fill the buffer from memory*/ + dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY; + + /* if incoming buffer is not page aligned, we should do bounce */ + if ((unsigned long)buf & (R852_DMA_LEN-1)) + bounce = 1; + + if (!bounce) { + dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf, + R852_DMA_LEN, + (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE)); + + if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr)) + bounce = 1; + } + + if (bounce) { + dbg_verbose("dma: using bounce buffer"); + dev->phys_dma_addr = dev->phys_bounce_buffer; + if (!do_read) + memcpy(dev->bounce_buffer, buf, R852_DMA_LEN); + } + + /* Enable DMA */ + spin_lock_irqsave(&dev->irqlock, flags); + r852_dma_enable(dev); + spin_unlock_irqrestore(&dev->irqlock, flags); + + /* Wait till complete */ + error = r852_dma_wait(dev); + + if (error) { + r852_dma_done(dev, error); + return; + } + + if (do_read && bounce) + memcpy((void *)buf, dev->bounce_buffer, R852_DMA_LEN); +} + +/* + * Program data lines of the nand chip to send data to it + */ +void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + uint32_t reg; + + /* Don't allow any access to hardware if we suspect card removal */ + if (dev->card_unstable) + return; + + /* Special case for whole sector read */ + if (len == R852_DMA_LEN && dev->dma_usable) { + r852_do_dma(dev, (uint8_t *)buf, 0); + return; + } + + /* write DWORD chinks - faster */ + while (len) { + reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; + r852_write_reg_dword(dev, R852_DATALINE, reg); + buf += 4; + len -= 4; + + } + + /* write rest */ + while (len) + r852_write_reg(dev, R852_DATALINE, *buf++); +} + +/* + * Read data lines of the nand chip to retrieve data + */ +void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + uint32_t reg; + + if (dev->card_unstable) { + /* since we can't signal error here, at least, return + predictable buffer */ + memset(buf, 0, len); + return; + } + + /* special case for whole sector read */ + if (len == R852_DMA_LEN && dev->dma_usable) { + r852_do_dma(dev, buf, 1); + return; + } + + /* read in dword sized chunks */ + while (len >= 4) { + + reg = r852_read_reg_dword(dev, R852_DATALINE); + *buf++ = reg & 0xFF; + *buf++ = (reg >> 8) & 0xFF; + *buf++ = (reg >> 16) & 0xFF; + *buf++ = (reg >> 24) & 0xFF; + len -= 4; + } + + /* read the reset by bytes */ + while (len--) + *buf++ = r852_read_reg(dev, R852_DATALINE); +} + +/* + * Read one byte from nand chip + */ +static uint8_t r852_read_byte(struct mtd_info *mtd) +{ + struct r852_device *dev = r852_get_dev(mtd); + + /* Same problem as in r852_read_buf.... */ + if (dev->card_unstable) + return 0; + + return r852_read_reg(dev, R852_DATALINE); +} + + +/* + * Readback the buffer to verify it + */ +int r852_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + + /* We can't be sure about anything here... */ + if (dev->card_unstable) + return -1; + + /* This will never happen, unless you wired up a nand chip + with > 512 bytes page size to the reader */ + if (len > SM_SECTOR_SIZE) + return 0; + + r852_read_buf(mtd, dev->tmp_buffer, len); + return memcmp(buf, dev->tmp_buffer, len); +} + +/* + * Control several chip lines & send commands + */ +void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return; + + if (ctrl & NAND_CTRL_CHANGE) { + + dev->ctlreg &= ~(R852_CTL_DATA | R852_CTL_COMMAND | + R852_CTL_ON | R852_CTL_CARDENABLE); + + if (ctrl & NAND_ALE) + dev->ctlreg |= R852_CTL_DATA; + + if (ctrl & NAND_CLE) + dev->ctlreg |= R852_CTL_COMMAND; + + if (ctrl & NAND_NCE) + dev->ctlreg |= (R852_CTL_CARDENABLE | R852_CTL_ON); + else + dev->ctlreg &= ~R852_CTL_WRITE; + + /* when write is stareted, enable write access */ + if (dat == NAND_CMD_ERASE1) + dev->ctlreg |= R852_CTL_WRITE; + + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } + + /* HACK: NAND_CMD_SEQIN is called without NAND_CTRL_CHANGE, but we need + to set write mode */ + if (dat == NAND_CMD_SEQIN && (dev->ctlreg & R852_CTL_COMMAND)) { + dev->ctlreg |= R852_CTL_WRITE; + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } + + if (dat != NAND_CMD_NONE) + r852_write_reg(dev, R852_DATALINE, dat); +} + +/* + * Wait till card is ready. + * based on nand_wait, but returns errors on DMA error + */ +int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct r852_device *dev = (struct r852_device *)chip->priv; + + unsigned long timeout; + int status; + + timeout = jiffies + (chip->state == FL_ERASING ? + msecs_to_jiffies(400) : msecs_to_jiffies(20)); + + while (time_before(jiffies, timeout)) + if (chip->dev_ready(mtd)) + break; + + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + status = (int)chip->read_byte(mtd); + + /* Unfortunelly, no way to send detailed error status... */ + if (dev->dma_error) { + status |= NAND_STATUS_FAIL; + dev->dma_error = 0; + } + return status; +} + +/* + * Check if card is ready + */ + +int r852_ready(struct mtd_info *mtd) +{ + struct r852_device *dev = r852_get_dev(mtd); + return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY); +} + + +/* + * Set ECC engine mode +*/ + +void r852_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return; + + switch (mode) { + case NAND_ECC_READ: + case NAND_ECC_WRITE: + /* enable ecc generation/check*/ + dev->ctlreg |= R852_CTL_ECC_ENABLE; + + /* flush ecc buffer */ + r852_write_reg(dev, R852_CTL, + dev->ctlreg | R852_CTL_ECC_ACCESS); + + r852_read_reg_dword(dev, R852_DATALINE); + r852_write_reg(dev, R852_CTL, dev->ctlreg); + return; + + case NAND_ECC_READSYN: + /* disable ecc generation */ + dev->ctlreg &= ~R852_CTL_ECC_ENABLE; + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } +} + +/* + * Calculate ECC, only used for writes + */ + +int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct r852_device *dev = r852_get_dev(mtd); + struct sm_oob *oob = (struct sm_oob *)ecc_code; + uint32_t ecc1, ecc2; + + if (dev->card_unstable) + return 0; + + dev->ctlreg &= ~R852_CTL_ECC_ENABLE; + r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); + + ecc1 = r852_read_reg_dword(dev, R852_DATALINE); + ecc2 = r852_read_reg_dword(dev, R852_DATALINE); + + oob->ecc1[0] = (ecc1) & 0xFF; + oob->ecc1[1] = (ecc1 >> 8) & 0xFF; + oob->ecc1[2] = (ecc1 >> 16) & 0xFF; + + oob->ecc2[0] = (ecc2) & 0xFF; + oob->ecc2[1] = (ecc2 >> 8) & 0xFF; + oob->ecc2[2] = (ecc2 >> 16) & 0xFF; + + r852_write_reg(dev, R852_CTL, dev->ctlreg); + return 0; +} + +/* + * Correct the data using ECC, hw did almost everything for us + */ + +int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + uint16_t ecc_reg; + uint8_t ecc_status, err_byte; + int i, error = 0; + + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return 0; + + r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); + ecc_reg = r852_read_reg_dword(dev, R852_DATALINE); + r852_write_reg(dev, R852_CTL, dev->ctlreg); + + for (i = 0 ; i <= 1 ; i++) { + + ecc_status = (ecc_reg >> 8) & 0xFF; + + /* ecc uncorrectable error */ + if (ecc_status & R852_ECC_FAIL) { + dbg("ecc: unrecoverable error, in half %d", i); + error = -1; + goto exit; + } + + /* correctable error */ + if (ecc_status & R852_ECC_CORRECTABLE) { + + err_byte = ecc_reg & 0xFF; + dbg("ecc: recoverable error, " + "in half %d, byte %d, bit %d", i, + err_byte, ecc_status & R852_ECC_ERR_BIT_MSK); + + dat[err_byte] ^= + 1 << (ecc_status & R852_ECC_ERR_BIT_MSK); + error++; + } + + dat += 256; + ecc_reg >>= 16; + } +exit: + return error; +} + +/* + * This is copy of nand_read_oob_std + * nand_read_oob_syndrome assumes we can send column address - we can't + */ +static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return sndcmd; +} + +/* + * Start the nand engine + */ + +void r852_engine_enable(struct r852_device *dev) +{ + if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) { + r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); + r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); + } else { + r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); + r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); + } + msleep(300); + r852_write_reg(dev, R852_CTL, 0); +} + + +/* + * Stop the nand engine + */ + +void r852_engine_disable(struct r852_device *dev) +{ + r852_write_reg_dword(dev, R852_HW, 0); + r852_write_reg(dev, R852_CTL, R852_CTL_RESET); +} + +/* + * Test if card is present + */ + +void r852_card_update_present(struct r852_device *dev) +{ + unsigned long flags; + uint8_t reg; + + spin_lock_irqsave(&dev->irqlock, flags); + reg = r852_read_reg(dev, R852_CARD_STA); + dev->card_detected = !!(reg & R852_CARD_STA_PRESENT); + spin_unlock_irqrestore(&dev->irqlock, flags); +} + +/* + * Update card detection IRQ state according to current card state + * which is read in r852_card_update_present + */ +void r852_update_card_detect(struct r852_device *dev) +{ + int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); + dev->card_unstable = 0; + + card_detect_reg &= ~(R852_CARD_IRQ_REMOVE | R852_CARD_IRQ_INSERT); + card_detect_reg |= R852_CARD_IRQ_GENABLE; + + card_detect_reg |= dev->card_detected ? + R852_CARD_IRQ_REMOVE : R852_CARD_IRQ_INSERT; + + r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg); +} + +ssize_t r852_media_type_show(struct device *sys_dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev); + struct r852_device *dev = r852_get_dev(mtd); + char *data = dev->sm ? "smartmedia" : "xd"; + + strcpy(buf, data); + return strlen(data); +} + +DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); + + +/* Detect properties of card in slot */ +void r852_update_media_status(struct r852_device *dev) +{ + uint8_t reg; + unsigned long flags; + int readonly; + + spin_lock_irqsave(&dev->irqlock, flags); + if (!dev->card_detected) { + message("card removed"); + spin_unlock_irqrestore(&dev->irqlock, flags); + return ; + } + + readonly = r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_RO; + reg = r852_read_reg(dev, R852_DMA_CAP); + dev->sm = (reg & (R852_DMA1 | R852_DMA2)) && (reg & R852_SMBIT); + + message("detected %s %s card in slot", + dev->sm ? "SmartMedia" : "xD", + readonly ? "readonly" : "writeable"); + + dev->readonly = readonly; + spin_unlock_irqrestore(&dev->irqlock, flags); +} + +/* + * Register the nand device + * Called when the card is detected + */ +int r852_register_nand_device(struct r852_device *dev) +{ + dev->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); + + if (!dev->mtd) + goto error1; + + WARN_ON(dev->card_registred); + + dev->mtd->owner = THIS_MODULE; + dev->mtd->priv = dev->chip; + dev->mtd->dev.parent = &dev->pci_dev->dev; + + if (dev->readonly) + dev->chip->options |= NAND_ROM; + + r852_engine_enable(dev); + + if (sm_register_device(dev->mtd, dev->sm)) + goto error2; + + if (device_create_file(&dev->mtd->dev, &dev_attr_media_type)) + message("can't create media type sysfs attribute"); + + dev->card_registred = 1; + return 0; +error2: + kfree(dev->mtd); +error1: + /* Force card redetect */ + dev->card_detected = 0; + return -1; +} + +/* + * Unregister the card + */ + +void r852_unregister_nand_device(struct r852_device *dev) +{ + if (!dev->card_registred) + return; + + device_remove_file(&dev->mtd->dev, &dev_attr_media_type); + nand_release(dev->mtd); + r852_engine_disable(dev); + dev->card_registred = 0; + kfree(dev->mtd); + dev->mtd = NULL; +} + +/* Card state updater */ +void r852_card_detect_work(struct work_struct *work) +{ + struct r852_device *dev = + container_of(work, struct r852_device, card_detect_work.work); + + r852_card_update_present(dev); + dev->card_unstable = 0; + + /* False alarm */ + if (dev->card_detected == dev->card_registred) + goto exit; + + /* Read media properties */ + r852_update_media_status(dev); + + /* Register the card */ + if (dev->card_detected) + r852_register_nand_device(dev); + else + r852_unregister_nand_device(dev); +exit: + /* Update detection logic */ + r852_update_card_detect(dev); +} + +/* Ack + disable IRQ generation */ +static void r852_disable_irqs(struct r852_device *dev) +{ + uint8_t reg; + reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); + r852_write_reg(dev, R852_CARD_IRQ_ENABLE, reg & ~R852_CARD_IRQ_MASK); + + reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, + reg & ~R852_DMA_IRQ_MASK); + + r852_write_reg(dev, R852_CARD_IRQ_STA, R852_CARD_IRQ_MASK); + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, R852_DMA_IRQ_MASK); +} + +/* Interrupt handler */ +static irqreturn_t r852_irq(int irq, void *data) +{ + struct r852_device *dev = (struct r852_device *)data; + + uint8_t card_status, dma_status; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&dev->irqlock, flags); + + /* We can recieve shared interrupt while pci is suspended + in that case reads will return 0xFFFFFFFF.... */ + if (dev->insuspend) + goto out; + + /* handle card detection interrupts first */ + card_status = r852_read_reg(dev, R852_CARD_IRQ_STA); + r852_write_reg(dev, R852_CARD_IRQ_STA, card_status); + + if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) { + + ret = IRQ_HANDLED; + dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT); + + /* we shouldn't recieve any interrupts if we wait for card + to settle */ + WARN_ON(dev->card_unstable); + + /* disable irqs while card is unstable */ + /* this will timeout DMA if active, but better that garbage */ + r852_disable_irqs(dev); + + if (dev->card_unstable) + goto out; + + /* let, card state to settle a bit, and then do the work */ + dev->card_unstable = 1; + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, msecs_to_jiffies(100)); + goto out; + } + + + /* Handle dma interrupts */ + dma_status = r852_read_reg_dword(dev, R852_DMA_IRQ_STA); + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, dma_status); + + if (dma_status & R852_DMA_IRQ_MASK) { + + ret = IRQ_HANDLED; + + if (dma_status & R852_DMA_IRQ_ERROR) { + dbg("recieved dma error IRQ"); + r852_dma_done(dev, -EIO); + goto out; + } + + /* recieved DMA interrupt out of nowhere? */ + WARN_ON_ONCE(dev->dma_stage == 0); + + if (dev->dma_stage == 0) + goto out; + + /* done device access */ + if (dev->dma_state == DMA_INTERNAL && + (dma_status & R852_DMA_IRQ_INTERNAL)) { + + dev->dma_state = DMA_MEMORY; + dev->dma_stage++; + } + + /* done memory DMA */ + if (dev->dma_state == DMA_MEMORY && + (dma_status & R852_DMA_IRQ_MEMORY)) { + dev->dma_state = DMA_INTERNAL; + dev->dma_stage++; + } + + /* Enable 2nd half of dma dance */ + if (dev->dma_stage == 2) + r852_dma_enable(dev); + + /* Operation done */ + if (dev->dma_stage == 3) + r852_dma_done(dev, 0); + goto out; + } + + /* Handle unknown interrupts */ + if (dma_status) + dbg("bad dma IRQ status = %x", dma_status); + + if (card_status & ~R852_CARD_STA_CD) + dbg("strange card status = %x", card_status); + +out: + spin_unlock_irqrestore(&dev->irqlock, flags); + return ret; +} + +int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +{ + int error; + struct nand_chip *chip; + struct r852_device *dev; + + /* pci initialization */ + error = pci_enable_device(pci_dev); + + if (error) + goto error1; + + pci_set_master(pci_dev); + + error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (error) + goto error2; + + error = pci_request_regions(pci_dev, DRV_NAME); + + if (error) + goto error3; + + error = -ENOMEM; + + /* init nand chip, but register it only on card insert */ + chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); + + if (!chip) + goto error4; + + /* commands */ + chip->cmd_ctrl = r852_cmdctl; + chip->waitfunc = r852_wait; + chip->dev_ready = r852_ready; + + /* I/O */ + chip->read_byte = r852_read_byte; + chip->read_buf = r852_read_buf; + chip->write_buf = r852_write_buf; + chip->verify_buf = r852_verify_buf; + + /* ecc */ + chip->ecc.mode = NAND_ECC_HW_SYNDROME; + chip->ecc.size = R852_DMA_LEN; + chip->ecc.bytes = SM_OOB_SIZE; + chip->ecc.hwctl = r852_ecc_hwctl; + chip->ecc.calculate = r852_ecc_calculate; + chip->ecc.correct = r852_ecc_correct; + + /* TODO: hack */ + chip->ecc.read_oob = r852_read_oob; + + /* init our device structure */ + dev = kzalloc(sizeof(struct r852_device), GFP_KERNEL); + + if (!dev) + goto error5; + + chip->priv = dev; + dev->chip = chip; + dev->pci_dev = pci_dev; + pci_set_drvdata(pci_dev, dev); + + dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN, + &dev->phys_bounce_buffer); + + if (!dev->bounce_buffer) + goto error6; + + + error = -ENODEV; + dev->mmio = pci_ioremap_bar(pci_dev, 0); + + if (!dev->mmio) + goto error7; + + error = -ENOMEM; + dev->tmp_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); + + if (!dev->tmp_buffer) + goto error8; + + init_completion(&dev->dma_done); + + dev->card_workqueue = create_freezeable_workqueue(DRV_NAME); + + if (!dev->card_workqueue) + goto error9; + + INIT_DELAYED_WORK(&dev->card_detect_work, r852_card_detect_work); + + /* shutdown everything - precation */ + r852_engine_disable(dev); + r852_disable_irqs(dev); + + r852_dma_test(dev); + + /*register irq handler*/ + error = -ENODEV; + if (request_irq(pci_dev->irq, &r852_irq, IRQF_SHARED, + DRV_NAME, dev)) + goto error10; + + dev->irq = pci_dev->irq; + spin_lock_init(&dev->irqlock); + + /* kick initial present test */ + dev->card_detected = 0; + r852_card_update_present(dev); + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, 0); + + + printk(KERN_NOTICE DRV_NAME ": driver loaded succesfully\n"); + return 0; + +error10: + destroy_workqueue(dev->card_workqueue); +error9: + kfree(dev->tmp_buffer); +error8: + pci_iounmap(pci_dev, dev->mmio); +error7: + pci_free_consistent(pci_dev, R852_DMA_LEN, + dev->bounce_buffer, dev->phys_bounce_buffer); +error6: + kfree(dev); +error5: + kfree(chip); +error4: + pci_release_regions(pci_dev); +error3: +error2: + pci_disable_device(pci_dev); +error1: + return error; +} + +void r852_remove(struct pci_dev *pci_dev) +{ + struct r852_device *dev = pci_get_drvdata(pci_dev); + + /* Stop detect workqueue - + we are going to unregister the device anyway*/ + cancel_delayed_work_sync(&dev->card_detect_work); + destroy_workqueue(dev->card_workqueue); + + /* Unregister the device, this might make more IO */ + r852_unregister_nand_device(dev); + + /* Stop interrupts */ + r852_disable_irqs(dev); + synchronize_irq(dev->irq); + free_irq(dev->irq, dev); + + /* Cleanup */ + kfree(dev->tmp_buffer); + pci_iounmap(pci_dev, dev->mmio); + pci_free_consistent(pci_dev, R852_DMA_LEN, + dev->bounce_buffer, dev->phys_bounce_buffer); + + kfree(dev->chip); + kfree(dev); + + /* Shutdown the PCI device */ + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); +} + +void r852_shutdown(struct pci_dev *pci_dev) +{ + struct r852_device *dev = pci_get_drvdata(pci_dev); + + cancel_delayed_work_sync(&dev->card_detect_work); + r852_disable_irqs(dev); + synchronize_irq(dev->irq); + pci_disable_device(pci_dev); +} + +#ifdef CONFIG_PM +int r852_suspend(struct device *device) +{ + struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + unsigned long flags; + + if (dev->ctlreg & R852_CTL_CARDENABLE) + return -EBUSY; + + /* First make sure the detect work is gone */ + cancel_delayed_work_sync(&dev->card_detect_work); + + /* Turn off the interrupts and stop the device */ + r852_disable_irqs(dev); + r852_engine_disable(dev); + + spin_lock_irqsave(&dev->irqlock, flags); + dev->insuspend = 1; + spin_unlock_irqrestore(&dev->irqlock, flags); + + /* At that point, even if interrupt handler is running, it will quit */ + /* So wait for this to happen explictly */ + synchronize_irq(dev->irq); + + /* If card was pulled off just during the suspend, which is very + unlikely, we will remove it on resume, it too late now + anyway... */ + dev->card_unstable = 0; + + pci_save_state(to_pci_dev(device)); + return pci_prepare_to_sleep(to_pci_dev(device)); +} + +int r852_resume(struct device *device) +{ + struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + unsigned long flags; + + /* Turn on the hardware */ + pci_back_from_sleep(to_pci_dev(device)); + pci_restore_state(to_pci_dev(device)); + + r852_disable_irqs(dev); + r852_card_update_present(dev); + r852_engine_disable(dev); + + + /* Now its safe for IRQ to run */ + spin_lock_irqsave(&dev->irqlock, flags); + dev->insuspend = 0; + spin_unlock_irqrestore(&dev->irqlock, flags); + + + /* If card status changed, just do the work */ + if (dev->card_detected != dev->card_registred) { + dbg("card was %s during low power state", + dev->card_detected ? "added" : "removed"); + + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, 1000); + return 0; + } + + /* Otherwise, initialize the card */ + if (dev->card_registred) { + r852_engine_enable(dev); + dev->chip->select_chip(dev->mtd, 0); + dev->chip->cmdfunc(dev->mtd, NAND_CMD_RESET, -1, -1); + dev->chip->select_chip(dev->mtd, -1); + } + + /* Program card detection IRQ */ + r852_update_card_detect(dev); + return 0; +} +#else +#define r852_suspend NULL +#define r852_resume NULL +#endif + +static const struct pci_device_id r852_pci_id_tbl[] = { + + { PCI_VDEVICE(RICOH, 0x0852), }, + { }, +}; + +MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl); + +SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume); + + +static struct pci_driver r852_pci_driver = { + .name = DRV_NAME, + .id_table = r852_pci_id_tbl, + .probe = r852_probe, + .remove = r852_remove, + .shutdown = r852_shutdown, + .driver.pm = &r852_pm_ops, +}; + +static __init int r852_module_init(void) +{ + return pci_register_driver(&r852_pci_driver); +} + +static void __exit r852_module_exit(void) +{ + pci_unregister_driver(&r852_pci_driver); +} + +module_init(r852_module_init); +module_exit(r852_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); +MODULE_DESCRIPTION("Ricoh 85xx xD/smartmedia card reader driver"); diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/r852.h new file mode 100644 index 00000000000..8096cc280c7 --- /dev/null +++ b/drivers/mtd/nand/r852.h @@ -0,0 +1,163 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * driver for Ricoh xD readers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/pci.h> +#include <linux/completion.h> +#include <linux/workqueue.h> +#include <linux/mtd/nand.h> +#include <linux/spinlock.h> + + +/* nand interface + ecc + byte write/read does one cycle on nand data lines. + dword write/read does 4 cycles + if R852_CTL_ECC_ACCESS is set in R852_CTL, then dword read reads + results of ecc correction, if DMA read was done before. + If write was done two dword reads read generated ecc checksums +*/ +#define R852_DATALINE 0x00 + +/* control register */ +#define R852_CTL 0x04 +#define R852_CTL_COMMAND 0x01 /* send command (#CLE)*/ +#define R852_CTL_DATA 0x02 /* read/write data (#ALE)*/ +#define R852_CTL_ON 0x04 /* only seem to controls the hd led, */ + /* but has to be set on start...*/ +#define R852_CTL_RESET 0x08 /* unknown, set only on start once*/ +#define R852_CTL_CARDENABLE 0x10 /* probably (#CE) - always set*/ +#define R852_CTL_ECC_ENABLE 0x20 /* enable ecc engine */ +#define R852_CTL_ECC_ACCESS 0x40 /* read/write ecc via reg #0*/ +#define R852_CTL_WRITE 0x80 /* set when performing writes (#WP) */ + +/* card detection status */ +#define R852_CARD_STA 0x05 + +#define R852_CARD_STA_CD 0x01 /* state of #CD line, same as 0x04 */ +#define R852_CARD_STA_RO 0x02 /* card is readonly */ +#define R852_CARD_STA_PRESENT 0x04 /* card is present (#CD) */ +#define R852_CARD_STA_ABSENT 0x08 /* card is absent */ +#define R852_CARD_STA_BUSY 0x80 /* card is busy - (#R/B) */ + +/* card detection irq status & enable*/ +#define R852_CARD_IRQ_STA 0x06 /* IRQ status */ +#define R852_CARD_IRQ_ENABLE 0x07 /* IRQ enable */ + +#define R852_CARD_IRQ_CD 0x01 /* fire when #CD lights, same as 0x04*/ +#define R852_CARD_IRQ_REMOVE 0x04 /* detect card removal */ +#define R852_CARD_IRQ_INSERT 0x08 /* detect card insert */ +#define R852_CARD_IRQ_UNK1 0x10 /* unknown */ +#define R852_CARD_IRQ_GENABLE 0x80 /* general enable */ +#define R852_CARD_IRQ_MASK 0x1D + + + +/* hardware enable */ +#define R852_HW 0x08 +#define R852_HW_ENABLED 0x01 /* hw enabled */ +#define R852_HW_UNKNOWN 0x80 + + +/* dma capabilities */ +#define R852_DMA_CAP 0x09 +#define R852_SMBIT 0x20 /* if set with bit #6 or bit #7, then */ + /* hw is smartmedia */ +#define R852_DMA1 0x40 /* if set w/bit #7, dma is supported */ +#define R852_DMA2 0x80 /* if set w/bit #6, dma is supported */ + + +/* physical DMA address - 32 bit value*/ +#define R852_DMA_ADDR 0x0C + + +/* dma settings */ +#define R852_DMA_SETTINGS 0x10 +#define R852_DMA_MEMORY 0x01 /* (memory <-> internal hw buffer) */ +#define R852_DMA_READ 0x02 /* 0 = write, 1 = read */ +#define R852_DMA_INTERNAL 0x04 /* (internal hw buffer <-> card) */ + +/* dma IRQ status */ +#define R852_DMA_IRQ_STA 0x14 + +/* dma IRQ enable */ +#define R852_DMA_IRQ_ENABLE 0x18 + +#define R852_DMA_IRQ_MEMORY 0x01 /* (memory <-> internal hw buffer) */ +#define R852_DMA_IRQ_ERROR 0x02 /* error did happen */ +#define R852_DMA_IRQ_INTERNAL 0x04 /* (internal hw buffer <-> card) */ +#define R852_DMA_IRQ_MASK 0x07 /* mask of all IRQ bits */ + + +/* ECC syndrome format - read from reg #0 will return two copies of these for + each half of the page. + first byte is error byte location, and second, bit location + flags */ +#define R852_ECC_ERR_BIT_MSK 0x07 /* error bit location */ +#define R852_ECC_CORRECT 0x10 /* no errors - (guessed) */ +#define R852_ECC_CORRECTABLE 0x20 /* correctable error exist */ +#define R852_ECC_FAIL 0x40 /* non correctable error detected */ + +#define R852_DMA_LEN 512 + +#define DMA_INTERNAL 0 +#define DMA_MEMORY 1 + +struct r852_device { + void __iomem *mmio; /* mmio */ + struct mtd_info *mtd; /* mtd backpointer */ + struct nand_chip *chip; /* nand chip backpointer */ + struct pci_dev *pci_dev; /* pci backpointer */ + + /* dma area */ + dma_addr_t phys_dma_addr; /* bus address of buffer*/ + struct completion dma_done; /* data transfer done */ + + dma_addr_t phys_bounce_buffer; /* bus address of bounce buffer */ + uint8_t *bounce_buffer; /* virtual address of bounce buffer */ + + int dma_dir; /* 1 = read, 0 = write */ + int dma_stage; /* 0 - idle, 1 - first step, + 2 - second step */ + + int dma_state; /* 0 = internal, 1 = memory */ + int dma_error; /* dma errors */ + int dma_usable; /* is it possible to use dma */ + + /* card status area */ + struct delayed_work card_detect_work; + struct workqueue_struct *card_workqueue; + int card_registred; /* card registered with mtd */ + int card_detected; /* card detected in slot */ + int card_unstable; /* whenever the card is inserted, + is not known yet */ + int readonly; /* card is readonly */ + int sm; /* Is card smartmedia */ + + /* interrupt handling */ + spinlock_t irqlock; /* IRQ protecting lock */ + int irq; /* irq num */ + int insuspend; /* device is suspended */ + + /* misc */ + void *tmp_buffer; /* temporary buffer */ + uint8_t ctlreg; /* cached contents of control reg */ +}; + +#define DRV_NAME "r852" + + +#define dbg(format, ...) \ + if (debug) \ + printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) + +#define dbg_verbose(format, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) + + +#define message(format, ...) \ + printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index fa6e9c7fe51..239aadfd01b 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -929,14 +929,13 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) pr_debug("s3c2410_nand_probe(%p)\n", pdev); - info = kmalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { dev_err(&pdev->dev, "no memory for flash info\n"); err = -ENOMEM; goto exit_error; } - memset(info, 0, sizeof(*info)); platform_set_drvdata(pdev, info); spin_lock_init(&info->controller.lock); @@ -957,7 +956,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) /* currently we assume we have the one resource */ res = pdev->resource; - size = res->end - res->start + 1; + size = resource_size(res); info->area = request_mem_region(res->start, size, pdev->name); @@ -994,15 +993,13 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) /* allocate our information */ size = nr_sets * sizeof(*info->mtds); - info->mtds = kmalloc(size, GFP_KERNEL); + info->mtds = kzalloc(size, GFP_KERNEL); if (info->mtds == NULL) { dev_err(&pdev->dev, "failed to allocate mtd storage\n"); err = -ENOMEM; goto exit_error; } - memset(info->mtds, 0, size); - /* initialise all possible chips */ nmtd = info->mtds; @@ -1013,7 +1010,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) s3c2410_nand_init_chip(info, nmtd, sets); nmtd->scan_res = nand_scan_ident(&nmtd->mtd, - (sets) ? sets->nr_chips : 1); + (sets) ? sets->nr_chips : 1, + NULL); if (nmtd->scan_res == 0) { s3c2410_nand_update_chip(info, nmtd); diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 34752fce079..546c2f0eb2e 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -855,7 +855,7 @@ static int __devinit flctl_probe(struct platform_device *pdev) nand->read_word = flctl_read_word; } - ret = nand_scan_ident(flctl_mtd, 1); + ret = nand_scan_ident(flctl_mtd, 1, NULL); if (ret) goto err; diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c new file mode 100644 index 00000000000..ac80fb362e6 --- /dev/null +++ b/drivers/mtd/nand/sm_common.c @@ -0,0 +1,148 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * Common routines & support for xD format + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/mtd/nand.h> +#include "sm_common.h" + +static struct nand_ecclayout nand_oob_sm = { + .eccbytes = 6, + .eccpos = {8, 9, 10, 13, 14, 15}, + .oobfree = { + {.offset = 0 , .length = 4}, /* reserved */ + {.offset = 6 , .length = 2}, /* LBA1 */ + {.offset = 11, .length = 2} /* LBA2 */ + } +}; + +/* NOTE: This layout is is not compatabable with SmartMedia, */ +/* because the 256 byte devices have page depenent oob layout */ +/* However it does preserve the bad block markers */ +/* If you use smftl, it will bypass this and work correctly */ +/* If you not, then you break SmartMedia compliance anyway */ + +static struct nand_ecclayout nand_oob_sm_small = { + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { + {.offset = 3 , .length = 2}, /* reserved */ + {.offset = 6 , .length = 2}, /* LBA1 */ + } +}; + + +static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_oob_ops ops; + struct sm_oob oob; + int ret, error = 0; + + memset(&oob, -1, SM_OOB_SIZE); + oob.block_status = 0x0F; + + /* As long as this function is called on erase block boundaries + it will work correctly for 256 byte nand */ + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = (void *)&oob; + ops.datbuf = NULL; + + + ret = mtd->write_oob(mtd, ofs, &ops); + if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { + printk(KERN_NOTICE + "sm_common: can't mark sector at %i as bad\n", + (int)ofs); + error = -EIO; + } else + mtd->ecc_stats.badblocks++; + + return error; +} + + +static struct nand_flash_dev nand_smartmedia_flash_ids[] = { + {"SmartMedia 1MiB 5V", 0x6e, 256, 1, 0x1000, 0}, + {"SmartMedia 1MiB 3,3V", 0xe8, 256, 1, 0x1000, 0}, + {"SmartMedia 1MiB 3,3V", 0xec, 256, 1, 0x1000, 0}, + {"SmartMedia 2MiB 3,3V", 0xea, 256, 2, 0x1000, 0}, + {"SmartMedia 2MiB 5V", 0x64, 256, 2, 0x1000, 0}, + {"SmartMedia 2MiB 3,3V ROM", 0x5d, 512, 2, 0x2000, NAND_ROM}, + {"SmartMedia 4MiB 3,3V", 0xe3, 512, 4, 0x2000, 0}, + {"SmartMedia 4MiB 3,3/5V", 0xe5, 512, 4, 0x2000, 0}, + {"SmartMedia 4MiB 5V", 0x6b, 512, 4, 0x2000, 0}, + {"SmartMedia 4MiB 3,3V ROM", 0xd5, 512, 4, 0x2000, NAND_ROM}, + {"SmartMedia 8MiB 3,3V", 0xe6, 512, 8, 0x2000, 0}, + {"SmartMedia 8MiB 3,3V ROM", 0xd6, 512, 8, 0x2000, NAND_ROM}, + {"SmartMedia 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, + {"SmartMedia 16MiB 3,3V ROM", 0x57, 512, 16, 0x4000, NAND_ROM}, + {"SmartMedia 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, + {"SmartMedia 32MiB 3,3V ROM", 0x58, 512, 32, 0x4000, NAND_ROM}, + {"SmartMedia 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, + {"SmartMedia 64MiB 3,3V ROM", 0xd9, 512, 64, 0x4000, NAND_ROM}, + {"SmartMedia 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, + {"SmartMedia 128MiB 3,3V ROM", 0xda, 512, 128, 0x4000, NAND_ROM}, + {"SmartMedia 256MiB 3,3V", 0x71, 512, 256, 0x4000 }, + {"SmartMedia 256MiB 3,3V ROM", 0x5b, 512, 256, 0x4000, NAND_ROM}, + {NULL,} +}; + +#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD) +static struct nand_flash_dev nand_xd_flash_ids[] = { + + {"xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, + {"xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, + {"xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, + {"xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, + {"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM}, + {"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, XD_TYPEM}, + {"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, XD_TYPEM}, + {"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, XD_TYPEM}, + {NULL,} +}; + +int sm_register_device(struct mtd_info *mtd, int smartmedia) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + int ret; + + chip->options |= NAND_SKIP_BBTSCAN; + + /* Scan for card properties */ + ret = nand_scan_ident(mtd, 1, smartmedia ? + nand_smartmedia_flash_ids : nand_xd_flash_ids); + + if (ret) + return ret; + + /* Bad block marker postion */ + chip->badblockpos = 0x05; + chip->badblockbits = 7; + chip->block_markbad = sm_block_markbad; + + /* ECC layout */ + if (mtd->writesize == SM_SECTOR_SIZE) + chip->ecc.layout = &nand_oob_sm; + else if (mtd->writesize == SM_SMALL_PAGE) + chip->ecc.layout = &nand_oob_sm_small; + else + return -ENODEV; + + ret = nand_scan_tail(mtd); + + if (ret) + return ret; + + return add_mtd_device(mtd); +} +EXPORT_SYMBOL_GPL(sm_register_device); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); +MODULE_DESCRIPTION("Common SmartMedia/xD functions"); diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/sm_common.h new file mode 100644 index 00000000000..00f4a83359b --- /dev/null +++ b/drivers/mtd/nand/sm_common.h @@ -0,0 +1,61 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * Common routines & support for SmartMedia/xD format + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/bitops.h> +#include <linux/mtd/mtd.h> + +/* Full oob structure as written on the flash */ +struct sm_oob { + uint32_t reserved; + uint8_t data_status; + uint8_t block_status; + uint8_t lba_copy1[2]; + uint8_t ecc2[3]; + uint8_t lba_copy2[2]; + uint8_t ecc1[3]; +} __attribute__((packed)); + + +/* one sector is always 512 bytes, but it can consist of two nand pages */ +#define SM_SECTOR_SIZE 512 + +/* oob area is also 16 bytes, but might be from two pages */ +#define SM_OOB_SIZE 16 + +/* This is maximum zone size, and all devices that have more that one zone + have this size */ +#define SM_MAX_ZONE_SIZE 1024 + +/* support for small page nand */ +#define SM_SMALL_PAGE 256 +#define SM_SMALL_OOB_SIZE 8 + + +extern int sm_register_device(struct mtd_info *mtd, int smartmedia); + + +static inline int sm_sector_valid(struct sm_oob *oob) +{ + return hweight16(oob->data_status) >= 5; +} + +static inline int sm_block_valid(struct sm_oob *oob) +{ + return hweight16(oob->block_status) >= 7; +} + +static inline int sm_block_erased(struct sm_oob *oob) +{ + static const uint32_t erased_pattern[4] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + + /* First test for erased block */ + if (!memcmp(oob, erased_pattern, sizeof(*oob))) + return 1; + return 0; +} diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index a4519a7bd68..b37cbde6e7d 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -220,7 +220,7 @@ static int __devinit socrates_nand_probe(struct of_device *ofdev, dev_set_drvdata(&ofdev->dev, host); /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1)) { + if (nand_scan_ident(mtd, 1, NULL)) { res = -ENXIO; goto out; } @@ -290,7 +290,7 @@ static int __devexit socrates_nand_remove(struct of_device *ofdev) return 0; } -static struct of_device_id socrates_nand_match[] = +static const struct of_device_id socrates_nand_match[] = { { .compatible = "abb,socrates-nand", diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index fa28f01ae00..3041d1f7ae3 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -319,7 +319,7 @@ static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); int ret; if (cell->enable) { @@ -363,7 +363,7 @@ static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE); if (cell->disable) @@ -372,7 +372,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) static int tmio_probe(struct platform_device *dev) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); struct tmio_nand_data *data = cell->driver_data; struct resource *fcr = platform_get_resource(dev, IORESOURCE_MEM, 0); @@ -405,14 +405,14 @@ static int tmio_probe(struct platform_device *dev) mtd->priv = nand_chip; mtd->name = "tmio-nand"; - tmio->ccr = ioremap(ccr->start, ccr->end - ccr->start + 1); + tmio->ccr = ioremap(ccr->start, resource_size(ccr)); if (!tmio->ccr) { retval = -EIO; goto err_iomap_ccr; } tmio->fcr_base = fcr->start & 0xfffff; - tmio->fcr = ioremap(fcr->start, fcr->end - fcr->start + 1); + tmio->fcr = ioremap(fcr->start, resource_size(fcr)); if (!tmio->fcr) { retval = -EIO; goto err_iomap_fcr; @@ -516,7 +516,7 @@ static int tmio_remove(struct platform_device *dev) #ifdef CONFIG_PM static int tmio_suspend(struct platform_device *dev, pm_message_t state) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); if (cell->suspend) cell->suspend(dev); @@ -527,7 +527,7 @@ static int tmio_suspend(struct platform_device *dev, pm_message_t state) static int tmio_resume(struct platform_device *dev) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); /* FIXME - is this required or merely another attack of the broken * SHARP platform? Looks suspicious. diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c deleted file mode 100644 index 0f5562aeedc..00000000000 --- a/drivers/mtd/nand/ts7250.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * drivers/mtd/nand/ts7250.c - * - * Copyright (C) 2004 Technologic Systems (support@embeddedARM.com) - * - * Derived from drivers/mtd/nand/edb7312.c - * Copyright (C) 2004 Marius Gröger (mag@sysgo.de) - * - * Derived from drivers/mtd/nand/autcpu12.c - * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash device found on the - * TS-7250 board which utilizes a Samsung 32 Mbyte part. - */ - -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> -#include <linux/io.h> - -#include <mach/hardware.h> -#include <mach/ts72xx.h> - -#include <asm/sizes.h> -#include <asm/mach-types.h> - -/* - * MTD structure for TS7250 board - */ -static struct mtd_info *ts7250_mtd = NULL; - -#ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { "cmdlinepart", NULL }; - -#define NUM_PARTITIONS 3 - -/* - * Define static partitions for flash device - */ -static struct mtd_partition partition_info32[] = { - { - .name = "TS-BOOTROM", - .offset = 0x00000000, - .size = 0x00004000, - }, { - .name = "Linux", - .offset = 0x00004000, - .size = 0x01d00000, - }, { - .name = "RedBoot", - .offset = 0x01d04000, - .size = 0x002fc000, - }, -}; - -/* - * Define static partitions for flash device - */ -static struct mtd_partition partition_info128[] = { - { - .name = "TS-BOOTROM", - .offset = 0x00000000, - .size = 0x00004000, - }, { - .name = "Linux", - .offset = 0x00004000, - .size = 0x07d00000, - }, { - .name = "RedBoot", - .offset = 0x07d04000, - .size = 0x002fc000, - }, -}; -#endif - - -/* - * hardware specific access to control-lines - * - * ctrl: - * NAND_NCE: bit 0 -> bit 2 - * NAND_CLE: bit 1 -> bit 1 - * NAND_ALE: bit 2 -> bit 0 - */ -static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { - unsigned long addr = TS72XX_NAND_CONTROL_VIRT_BASE; - unsigned char bits; - - bits = (ctrl & NAND_NCE) << 2; - bits |= ctrl & NAND_CLE; - bits |= (ctrl & NAND_ALE) >> 2; - - __raw_writeb((__raw_readb(addr) & ~0x7) | bits, addr); - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -/* - * read device ready pin - */ -static int ts7250_device_ready(struct mtd_info *mtd) -{ - return __raw_readb(TS72XX_NAND_BUSY_VIRT_BASE) & 0x20; -} - -/* - * Main initialization routine - */ -static int __init ts7250_init(void) -{ - struct nand_chip *this; - const char *part_type = 0; - int mtd_parts_nb = 0; - struct mtd_partition *mtd_parts = 0; - - if (!machine_is_ts72xx() || board_is_ts7200()) - return -ENXIO; - - /* Allocate memory for MTD device structure and private data */ - ts7250_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!ts7250_mtd) { - printk("Unable to allocate TS7250 NAND MTD device structure.\n"); - return -ENOMEM; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&ts7250_mtd[1]); - - /* Initialize structures */ - memset(ts7250_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - ts7250_mtd->priv = this; - ts7250_mtd->owner = THIS_MODULE; - - /* insert callbacks */ - this->IO_ADDR_R = (void *)TS72XX_NAND_DATA_VIRT_BASE; - this->IO_ADDR_W = (void *)TS72XX_NAND_DATA_VIRT_BASE; - this->cmd_ctrl = ts7250_hwcontrol; - this->dev_ready = ts7250_device_ready; - this->chip_delay = 15; - this->ecc.mode = NAND_ECC_SOFT; - - printk("Searching for NAND flash...\n"); - /* Scan to find existence of the device */ - if (nand_scan(ts7250_mtd, 1)) { - kfree(ts7250_mtd); - return -ENXIO; - } -#ifdef CONFIG_MTD_PARTITIONS - ts7250_mtd->name = "ts7250-nand"; - mtd_parts_nb = parse_mtd_partitions(ts7250_mtd, part_probes, &mtd_parts, 0); - if (mtd_parts_nb > 0) - part_type = "command line"; - else - mtd_parts_nb = 0; -#endif - if (mtd_parts_nb == 0) { - mtd_parts = partition_info32; - if (ts7250_mtd->size >= (128 * 0x100000)) - mtd_parts = partition_info128; - mtd_parts_nb = NUM_PARTITIONS; - part_type = "static"; - } - - /* Register the partitions */ - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(ts7250_mtd, mtd_parts, mtd_parts_nb); - - /* Return happy */ - return 0; -} - -module_init(ts7250_init); - -/* - * Clean up routine - */ -static void __exit ts7250_cleanup(void) -{ - /* Unregister the device */ - del_mtd_device(ts7250_mtd); - - /* Free the MTD device structure */ - kfree(ts7250_mtd); -} - -module_exit(ts7250_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jesse Off <joff@embeddedARM.com>"); -MODULE_DESCRIPTION("MTD map driver for Technologic Systems TS-7250 board"); diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 863513c3b69..054a41c0ef4 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -274,7 +274,7 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd) struct nand_chip *chip = mtd->priv; int ret; - ret = nand_scan_ident(mtd, 1); + ret = nand_scan_ident(mtd, 1, NULL); if (!ret) { if (mtd->writesize >= 512) { chip->ecc.size = mtd->writesize; diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 1002e188299..a4578bf903a 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -126,7 +126,6 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev) del_mtd_blktrans_dev(dev); kfree(nftl->ReplUnitTable); kfree(nftl->EUNtable); - kfree(nftl); } /* diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 3a9f1578460..9a49d68ba5f 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -30,6 +30,13 @@ config MTD_ONENAND_OMAP2 Support for a OneNAND flash device connected to an OMAP2/OMAP3 CPU via the GPMC memory controller. +config MTD_ONENAND_SAMSUNG + tristate "OneNAND on Samsung SOC controller support" + depends on MTD_ONENAND && (ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210) + help + Support for a OneNAND flash device connected to an Samsung SOC + S3C64XX/S5PC1XX controller. + config MTD_ONENAND_OTP bool "OneNAND OTP Support" select HAVE_MTD_OTP diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 64b6cc61a52..2b7884c7577 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MTD_ONENAND) += onenand.o # Board specific. obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o obj-$(CONFIG_MTD_ONENAND_OMAP2) += omap2.o +obj-$(CONFIG_MTD_ONENAND_SAMSUNG) += samsung.o # Simulator obj-$(CONFIG_MTD_ONENAND_SIM) += onenand_sim.o diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index fd406348fdf..9f322f1a7f2 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -309,7 +309,7 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, goto out_copy; /* panic_write() may be in an interrupt context */ - if (in_interrupt()) + if (in_interrupt() || oops_in_progress) goto out_copy; if (buf >= high_memory) { @@ -386,7 +386,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, goto out_copy; /* panic_write() may be in an interrupt context */ - if (in_interrupt()) + if (in_interrupt() || oops_in_progress) goto out_copy; if (buf >= high_memory) { @@ -403,7 +403,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE); dma_dst = c->phys_base + bram_offset; - if (dma_mapping_error(&c->pdev->dev, dma_dst)) { + if (dma_mapping_error(&c->pdev->dev, dma_src)) { dev_err(&c->pdev->dev, "Couldn't DMA map a %d byte buffer\n", count); @@ -426,7 +426,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, if (*done) break; - dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_TO_DEVICE); + dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); if (!*done) { dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); @@ -521,7 +521,7 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, dma_src = dma_map_single(&c->pdev->dev, (void *) buffer, count, DMA_TO_DEVICE); dma_dst = c->phys_base + bram_offset; - if (dma_mapping_error(&c->pdev->dev, dma_dst)) { + if (dma_mapping_error(&c->pdev->dev, dma_src)) { dev_err(&c->pdev->dev, "Couldn't DMA map a %d byte buffer\n", count); @@ -539,7 +539,7 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, omap_start_dma(c->dma_channel); wait_for_completion(&c->dma_done); - dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_TO_DEVICE); + dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); return 0; } diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 32f0ed33afe..26caf2590da 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -397,7 +397,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this) || + ONENAND_IS_4KB_PAGE(this)) /* It is always BufferRAM0 */ ONENAND_SET_BUFFERRAM0(this); else @@ -426,7 +427,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le case FLEXONENAND_CMD_RECOVER_LSB: case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: - if (ONENAND_IS_MLC(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) /* It is always BufferRAM0 */ dataram = ONENAND_SET_BUFFERRAM0(this); else @@ -466,11 +467,11 @@ static inline int onenand_read_ecc(struct onenand_chip *this) { int ecc, i, result = 0; - if (!FLEXONENAND(this)) + if (!FLEXONENAND(this) && !ONENAND_IS_4KB_PAGE(this)) return this->read_word(this->base + ONENAND_REG_ECC_STATUS); for (i = 0; i < 4; i++) { - ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i); + ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i*2); if (likely(!ecc)) continue; if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) @@ -1425,7 +1426,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, int ret; onenand_get_device(mtd, FL_READING); - ret = ONENAND_IS_MLC(this) ? + ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ? onenand_mlc_read_ops_nolock(mtd, from, &ops) : onenand_read_ops_nolock(mtd, from, &ops); onenand_release_device(mtd); @@ -1460,7 +1461,7 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, onenand_get_device(mtd, FL_READING); if (ops->datbuf) - ret = ONENAND_IS_MLC(this) ? + ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ? onenand_mlc_read_ops_nolock(mtd, from, ops) : onenand_read_ops_nolock(mtd, from, ops); else @@ -1634,7 +1635,6 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; - void __iomem *dataram; int ret = 0; int thislen, column; @@ -1654,10 +1654,9 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, onenand_update_bufferram(mtd, addr, 1); - dataram = this->base + ONENAND_DATARAM; - dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); + this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize); - if (memcmp(buf, dataram + column, thislen)) + if (memcmp(buf, this->verify_buf, thislen)) return -EBADMSG; len -= thislen; @@ -1926,7 +1925,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, * 2 PLANE, MLC, and Flex-OneNAND do not support * write-while-program feature. */ - if (!ONENAND_IS_2PLANE(this) && !first) { + if (!ONENAND_IS_2PLANE(this) && !ONENAND_IS_4KB_PAGE(this) && !first) { ONENAND_SET_PREV_BUFFERRAM(this); ret = this->wait(mtd, FL_WRITING); @@ -1957,7 +1956,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, /* * 2 PLANE, MLC, and Flex-OneNAND wait here */ - if (ONENAND_IS_2PLANE(this)) { + if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) { ret = this->wait(mtd, FL_WRITING); /* In partial page write we don't update bufferram */ @@ -2084,7 +2083,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, memcpy(oobbuf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); - if (ONENAND_IS_MLC(this)) { + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) { /* Set main area of DataRAM to 0xff*/ memset(this->page_buf, 0xff, mtd->writesize); this->write_bufferram(mtd, ONENAND_DATARAM, @@ -3027,7 +3026,7 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = ONENAND_IS_MLC(this) ? + ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ? onenand_mlc_read_ops_nolock(mtd, from, &ops) : onenand_read_ops_nolock(mtd, from, &ops); @@ -3372,7 +3371,10 @@ static void onenand_check_features(struct mtd_info *mtd) /* Lock scheme */ switch (density) { case ONENAND_DEVICE_DENSITY_4Gb: - this->options |= ONENAND_HAS_2PLANE; + if (ONENAND_IS_DDP(this)) + this->options |= ONENAND_HAS_2PLANE; + else + this->options |= ONENAND_HAS_4KB_PAGE; case ONENAND_DEVICE_DENSITY_2Gb: /* 2Gb DDP does not have 2 plane */ @@ -3393,7 +3395,7 @@ static void onenand_check_features(struct mtd_info *mtd) break; } - if (ONENAND_IS_MLC(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) this->options &= ~ONENAND_HAS_2PLANE; if (FLEXONENAND(this)) { @@ -3407,6 +3409,8 @@ static void onenand_check_features(struct mtd_info *mtd) printk(KERN_DEBUG "Chip support all block unlock\n"); if (this->options & ONENAND_HAS_2PLANE) printk(KERN_DEBUG "Chip has 2 plane\n"); + if (this->options & ONENAND_HAS_4KB_PAGE) + printk(KERN_DEBUG "Chip has 4KiB pagesize\n"); } /** @@ -3759,6 +3763,12 @@ static int onenand_probe(struct mtd_info *mtd) /* Restore system configuration 1 */ this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + /* Workaround */ + if (syscfg & ONENAND_SYS_CFG1_SYNC_WRITE) { + bram_maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); + bram_dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + } + /* Check manufacturer ID */ if (onenand_check_maf(bram_maf_id)) return -ENXIO; @@ -3778,6 +3788,9 @@ static int onenand_probe(struct mtd_info *mtd) this->device_id = dev_id; this->version_id = ver_id; + /* Check OneNAND features */ + onenand_check_features(mtd); + density = onenand_get_density(dev_id); if (FLEXONENAND(this)) { this->dies = ONENAND_IS_DDP(this) ? 2 : 1; @@ -3799,7 +3812,7 @@ static int onenand_probe(struct mtd_info *mtd) /* The data buffer size is equal to page size */ mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); /* We use the full BufferRAM */ - if (ONENAND_IS_MLC(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) mtd->writesize <<= 1; mtd->oobsize = mtd->writesize >> 5; @@ -3829,9 +3842,6 @@ static int onenand_probe(struct mtd_info *mtd) else mtd->size = this->chipsize; - /* Check OneNAND features */ - onenand_check_features(mtd); - /* * We emulate the 4KiB page and 256KiB erase block size * But oobsize is still 64 bytes. @@ -3926,6 +3936,13 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) __func__); return -ENOMEM; } +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL); + if (!this->verify_buf) { + kfree(this->page_buf); + return -ENOMEM; + } +#endif this->options |= ONENAND_PAGEBUF_ALLOC; } if (!this->oob_buf) { @@ -4053,8 +4070,12 @@ void onenand_release(struct mtd_info *mtd) kfree(this->bbm); } /* Buffers allocated by onenand_scan */ - if (this->options & ONENAND_PAGEBUF_ALLOC) + if (this->options & ONENAND_PAGEBUF_ALLOC) { kfree(this->page_buf); +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + kfree(this->verify_buf); +#endif + } if (this->options & ONENAND_OOBBUF_ALLOC) kfree(this->oob_buf); kfree(mtd->eraseregions); diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c new file mode 100644 index 00000000000..2750317cb58 --- /dev/null +++ b/drivers/mtd/onenand/samsung.c @@ -0,0 +1,1071 @@ +/* + * Samsung S3C64XX/S5PC1XX OneNAND driver + * + * Copyright © 2008-2010 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * Marek Szyprowski <m.szyprowski@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Implementation: + * S3C64XX and S5PC100: emulate the pseudo BufferRAM + * S5PC110: use DMA + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> +#include <linux/mtd/partitions.h> +#include <linux/dma-mapping.h> + +#include <asm/mach/flash.h> +#include <plat/regs-onenand.h> + +#include <linux/io.h> + +enum soc_type { + TYPE_S3C6400, + TYPE_S3C6410, + TYPE_S5PC100, + TYPE_S5PC110, +}; + +#define ONENAND_ERASE_STATUS 0x00 +#define ONENAND_MULTI_ERASE_SET 0x01 +#define ONENAND_ERASE_START 0x03 +#define ONENAND_UNLOCK_START 0x08 +#define ONENAND_UNLOCK_END 0x09 +#define ONENAND_LOCK_START 0x0A +#define ONENAND_LOCK_END 0x0B +#define ONENAND_LOCK_TIGHT_START 0x0C +#define ONENAND_LOCK_TIGHT_END 0x0D +#define ONENAND_UNLOCK_ALL 0x0E +#define ONENAND_OTP_ACCESS 0x12 +#define ONENAND_SPARE_ACCESS_ONLY 0x13 +#define ONENAND_MAIN_ACCESS_ONLY 0x14 +#define ONENAND_ERASE_VERIFY 0x15 +#define ONENAND_MAIN_SPARE_ACCESS 0x16 +#define ONENAND_PIPELINE_READ 0x4000 + +#define MAP_00 (0x0) +#define MAP_01 (0x1) +#define MAP_10 (0x2) +#define MAP_11 (0x3) + +#define S3C64XX_CMD_MAP_SHIFT 24 +#define S5PC1XX_CMD_MAP_SHIFT 26 + +#define S3C6400_FBA_SHIFT 10 +#define S3C6400_FPA_SHIFT 4 +#define S3C6400_FSA_SHIFT 2 + +#define S3C6410_FBA_SHIFT 12 +#define S3C6410_FPA_SHIFT 6 +#define S3C6410_FSA_SHIFT 4 + +#define S5PC100_FBA_SHIFT 13 +#define S5PC100_FPA_SHIFT 7 +#define S5PC100_FSA_SHIFT 5 + +/* S5PC110 specific definitions */ +#define S5PC110_DMA_SRC_ADDR 0x400 +#define S5PC110_DMA_SRC_CFG 0x404 +#define S5PC110_DMA_DST_ADDR 0x408 +#define S5PC110_DMA_DST_CFG 0x40C +#define S5PC110_DMA_TRANS_SIZE 0x414 +#define S5PC110_DMA_TRANS_CMD 0x418 +#define S5PC110_DMA_TRANS_STATUS 0x41C +#define S5PC110_DMA_TRANS_DIR 0x420 + +#define S5PC110_DMA_CFG_SINGLE (0x0 << 16) +#define S5PC110_DMA_CFG_4BURST (0x2 << 16) +#define S5PC110_DMA_CFG_8BURST (0x3 << 16) +#define S5PC110_DMA_CFG_16BURST (0x4 << 16) + +#define S5PC110_DMA_CFG_INC (0x0 << 8) +#define S5PC110_DMA_CFG_CNT (0x1 << 8) + +#define S5PC110_DMA_CFG_8BIT (0x0 << 0) +#define S5PC110_DMA_CFG_16BIT (0x1 << 0) +#define S5PC110_DMA_CFG_32BIT (0x2 << 0) + +#define S5PC110_DMA_SRC_CFG_READ (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_16BIT) +#define S5PC110_DMA_DST_CFG_READ (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_32BIT) +#define S5PC110_DMA_SRC_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_32BIT) +#define S5PC110_DMA_DST_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_16BIT) + +#define S5PC110_DMA_TRANS_CMD_TDC (0x1 << 18) +#define S5PC110_DMA_TRANS_CMD_TEC (0x1 << 16) +#define S5PC110_DMA_TRANS_CMD_TR (0x1 << 0) + +#define S5PC110_DMA_TRANS_STATUS_TD (0x1 << 18) +#define S5PC110_DMA_TRANS_STATUS_TB (0x1 << 17) +#define S5PC110_DMA_TRANS_STATUS_TE (0x1 << 16) + +#define S5PC110_DMA_DIR_READ 0x0 +#define S5PC110_DMA_DIR_WRITE 0x1 + +struct s3c_onenand { + struct mtd_info *mtd; + struct platform_device *pdev; + enum soc_type type; + void __iomem *base; + struct resource *base_res; + void __iomem *ahb_addr; + struct resource *ahb_res; + int bootram_command; + void __iomem *page_buf; + void __iomem *oob_buf; + unsigned int (*mem_addr)(int fba, int fpa, int fsa); + unsigned int (*cmd_map)(unsigned int type, unsigned int val); + void __iomem *dma_addr; + struct resource *dma_res; + unsigned long phys_base; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif +}; + +#define CMD_MAP_00(dev, addr) (dev->cmd_map(MAP_00, ((addr) << 1))) +#define CMD_MAP_01(dev, mem_addr) (dev->cmd_map(MAP_01, (mem_addr))) +#define CMD_MAP_10(dev, mem_addr) (dev->cmd_map(MAP_10, (mem_addr))) +#define CMD_MAP_11(dev, addr) (dev->cmd_map(MAP_11, ((addr) << 2))) + +static struct s3c_onenand *onenand; + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "cmdlinepart", NULL, }; +#endif + +static inline int s3c_read_reg(int offset) +{ + return readl(onenand->base + offset); +} + +static inline void s3c_write_reg(int value, int offset) +{ + writel(value, onenand->base + offset); +} + +static inline int s3c_read_cmd(unsigned int cmd) +{ + return readl(onenand->ahb_addr + cmd); +} + +static inline void s3c_write_cmd(int value, unsigned int cmd) +{ + writel(value, onenand->ahb_addr + cmd); +} + +#ifdef SAMSUNG_DEBUG +static void s3c_dump_reg(void) +{ + int i; + + for (i = 0; i < 0x400; i += 0x40) { + printk(KERN_INFO "0x%08X: 0x%08x 0x%08x 0x%08x 0x%08x\n", + (unsigned int) onenand->base + i, + s3c_read_reg(i), s3c_read_reg(i + 0x10), + s3c_read_reg(i + 0x20), s3c_read_reg(i + 0x30)); + } +} +#endif + +static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val) +{ + return (type << S3C64XX_CMD_MAP_SHIFT) | val; +} + +static unsigned int s5pc1xx_cmd_map(unsigned type, unsigned val) +{ + return (type << S5PC1XX_CMD_MAP_SHIFT) | val; +} + +static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S3C6400_FBA_SHIFT) | (fpa << S3C6400_FPA_SHIFT) | + (fsa << S3C6400_FSA_SHIFT); +} + +static unsigned int s3c6410_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S3C6410_FBA_SHIFT) | (fpa << S3C6410_FPA_SHIFT) | + (fsa << S3C6410_FSA_SHIFT); +} + +static unsigned int s5pc100_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S5PC100_FBA_SHIFT) | (fpa << S5PC100_FPA_SHIFT) | + (fsa << S5PC100_FSA_SHIFT); +} + +static void s3c_onenand_reset(void) +{ + unsigned long timeout = 0x10000; + int stat; + + s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); + while (1 && timeout--) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & RST_CMP) + break; + } + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + /* Clear interrupt */ + s3c_write_reg(0x0, INT_ERR_ACK_OFFSET); + /* Clear the ECC status */ + s3c_write_reg(0x0, ECC_ERR_STAT_OFFSET); +} + +static unsigned short s3c_onenand_readw(void __iomem *addr) +{ + struct onenand_chip *this = onenand->mtd->priv; + struct device *dev = &onenand->pdev->dev; + int reg = addr - this->base; + int word_addr = reg >> 1; + int value; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_MANUFACTURER_ID: + return s3c_read_reg(MANUFACT_ID_OFFSET); + case ONENAND_REG_DEVICE_ID: + return s3c_read_reg(DEVICE_ID_OFFSET); + case ONENAND_REG_VERSION_ID: + return s3c_read_reg(FLASH_VER_ID_OFFSET); + case ONENAND_REG_DATA_BUFFER_SIZE: + return s3c_read_reg(DATA_BUF_SIZE_OFFSET); + case ONENAND_REG_TECHNOLOGY: + return s3c_read_reg(TECH_OFFSET); + case ONENAND_REG_SYS_CFG1: + return s3c_read_reg(MEM_CFG_OFFSET); + + /* Used at unlock all status */ + case ONENAND_REG_CTRL_STATUS: + return 0; + + case ONENAND_REG_WP_STATUS: + return ONENAND_WP_US; + + default: + break; + } + + /* BootRAM access control */ + if ((unsigned int) addr < ONENAND_DATARAM && onenand->bootram_command) { + if (word_addr == 0) + return s3c_read_reg(MANUFACT_ID_OFFSET); + if (word_addr == 1) + return s3c_read_reg(DEVICE_ID_OFFSET); + if (word_addr == 2) + return s3c_read_reg(FLASH_VER_ID_OFFSET); + } + + value = s3c_read_cmd(CMD_MAP_11(onenand, word_addr)) & 0xffff; + dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, + word_addr, value); + return value; +} + +static void s3c_onenand_writew(unsigned short value, void __iomem *addr) +{ + struct onenand_chip *this = onenand->mtd->priv; + struct device *dev = &onenand->pdev->dev; + unsigned int reg = addr - this->base; + unsigned int word_addr = reg >> 1; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_SYS_CFG1: + s3c_write_reg(value, MEM_CFG_OFFSET); + return; + + case ONENAND_REG_START_ADDRESS1: + case ONENAND_REG_START_ADDRESS2: + return; + + /* Lock/lock-tight/unlock/unlock_all */ + case ONENAND_REG_START_BLOCK_ADDRESS: + return; + + default: + break; + } + + /* BootRAM access control */ + if ((unsigned int)addr < ONENAND_DATARAM) { + if (value == ONENAND_CMD_READID) { + onenand->bootram_command = 1; + return; + } + if (value == ONENAND_CMD_RESET) { + s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); + onenand->bootram_command = 0; + return; + } + } + + dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, + word_addr, value); + + s3c_write_cmd(value, CMD_MAP_11(onenand, word_addr)); +} + +static int s3c_onenand_wait(struct mtd_info *mtd, int state) +{ + struct device *dev = &onenand->pdev->dev; + unsigned int flags = INT_ACT; + unsigned int stat, ecc; + unsigned long timeout; + + switch (state) { + case FL_READING: + flags |= BLK_RW_CMP | LOAD_CMP; + break; + case FL_WRITING: + flags |= BLK_RW_CMP | PGM_CMP; + break; + case FL_ERASING: + flags |= BLK_RW_CMP | ERS_CMP; + break; + case FL_LOCKING: + flags |= BLK_RW_CMP; + break; + default: + break; + } + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & flags) + break; + + if (state != FL_READING) + cond_resched(); + } + /* To get correct interrupt status in timeout case */ + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + /* + * In the Spec. it checks the controller status first + * However if you get the correct information in case of + * power off recovery (POR) test, it should read ECC status first + */ + if (stat & LOAD_CMP) { + ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); + if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { + dev_info(dev, "%s: ECC error = 0x%04x\n", __func__, + ecc); + mtd->ecc_stats.failed++; + return -EBADMSG; + } + } + + if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) { + dev_info(dev, "%s: controller error = 0x%04x\n", __func__, + stat); + if (stat & LOCKED_BLK) + dev_info(dev, "%s: it's locked error = 0x%04x\n", + __func__, stat); + + return -EIO; + } + + return 0; +} + +static int s3c_onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, + size_t len) +{ + struct onenand_chip *this = mtd->priv; + unsigned int *m, *s; + int fba, fpa, fsa = 0; + unsigned int mem_addr, cmd_map_01, cmd_map_10; + int i, mcount, scount; + int index; + + fba = (int) (addr >> this->erase_shift); + fpa = (int) (addr >> this->page_shift); + fpa &= this->page_mask; + + mem_addr = onenand->mem_addr(fba, fpa, fsa); + cmd_map_01 = CMD_MAP_01(onenand, mem_addr); + cmd_map_10 = CMD_MAP_10(onenand, mem_addr); + + switch (cmd) { + case ONENAND_CMD_READ: + case ONENAND_CMD_READOOB: + case ONENAND_CMD_BUFFERRAM: + ONENAND_SET_NEXT_BUFFERRAM(this); + default: + break; + } + + index = ONENAND_CURRENT_BUFFERRAM(this); + + /* + * Emulate Two BufferRAMs and access with 4 bytes pointer + */ + m = (unsigned int *) onenand->page_buf; + s = (unsigned int *) onenand->oob_buf; + + if (index) { + m += (this->writesize >> 2); + s += (mtd->oobsize >> 2); + } + + mcount = mtd->writesize >> 2; + scount = mtd->oobsize >> 2; + + switch (cmd) { + case ONENAND_CMD_READ: + /* Main */ + for (i = 0; i < mcount; i++) + *m++ = s3c_read_cmd(cmd_map_01); + return 0; + + case ONENAND_CMD_READOOB: + s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); + /* Main */ + for (i = 0; i < mcount; i++) + *m++ = s3c_read_cmd(cmd_map_01); + + /* Spare */ + for (i = 0; i < scount; i++) + *s++ = s3c_read_cmd(cmd_map_01); + + s3c_write_reg(0, TRANS_SPARE_OFFSET); + return 0; + + case ONENAND_CMD_PROG: + /* Main */ + for (i = 0; i < mcount; i++) + s3c_write_cmd(*m++, cmd_map_01); + return 0; + + case ONENAND_CMD_PROGOOB: + s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); + + /* Main - dummy write */ + for (i = 0; i < mcount; i++) + s3c_write_cmd(0xffffffff, cmd_map_01); + + /* Spare */ + for (i = 0; i < scount; i++) + s3c_write_cmd(*s++, cmd_map_01); + + s3c_write_reg(0, TRANS_SPARE_OFFSET); + return 0; + + case ONENAND_CMD_UNLOCK_ALL: + s3c_write_cmd(ONENAND_UNLOCK_ALL, cmd_map_10); + return 0; + + case ONENAND_CMD_ERASE: + s3c_write_cmd(ONENAND_ERASE_START, cmd_map_10); + return 0; + + default: + break; + } + + return 0; +} + +static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area) +{ + struct onenand_chip *this = mtd->priv; + int index = ONENAND_CURRENT_BUFFERRAM(this); + unsigned char *p; + + if (area == ONENAND_DATARAM) { + p = (unsigned char *) onenand->page_buf; + if (index == 1) + p += this->writesize; + } else { + p = (unsigned char *) onenand->oob_buf; + if (index == 1) + p += mtd->oobsize; + } + + return p; +} + +static int onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count) +{ + unsigned char *p; + + p = s3c_get_bufferram(mtd, area); + memcpy(buffer, p + offset, count); + return 0; +} + +static int onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, int offset, + size_t count) +{ + unsigned char *p; + + p = s3c_get_bufferram(mtd, area); + memcpy(p + offset, buffer, count); + return 0; +} + +static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction) +{ + void __iomem *base = onenand->dma_addr; + int status; + + writel(src, base + S5PC110_DMA_SRC_ADDR); + writel(dst, base + S5PC110_DMA_DST_ADDR); + + if (direction == S5PC110_DMA_DIR_READ) { + writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); + } else { + writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); + } + + writel(count, base + S5PC110_DMA_TRANS_SIZE); + writel(direction, base + S5PC110_DMA_TRANS_DIR); + + writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); + + do { + status = readl(base + S5PC110_DMA_TRANS_STATUS); + } while (!(status & S5PC110_DMA_TRANS_STATUS_TD)); + + if (status & S5PC110_DMA_TRANS_STATUS_TE) { + writel(S5PC110_DMA_TRANS_CMD_TEC, base + S5PC110_DMA_TRANS_CMD); + writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); + return -EIO; + } + + writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); + + return 0; +} + +static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + void __iomem *p; + void *buf = (void *) buffer; + dma_addr_t dma_src, dma_dst; + int err; + + p = bufferram = this->base + area; + if (ONENAND_CURRENT_BUFFERRAM(this)) { + if (area == ONENAND_DATARAM) + p += this->writesize; + else + p += mtd->oobsize; + } + + if (offset & 3 || (size_t) buf & 3 || + !onenand->dma_addr || count != mtd->writesize) + goto normal; + + /* Handle vmalloc address */ + if (buf >= high_memory) { + struct page *page; + + if (((size_t) buf & PAGE_MASK) != + ((size_t) (buf + count - 1) & PAGE_MASK)) + goto normal; + page = vmalloc_to_page(buf); + if (!page) + goto normal; + buf = page_address(page) + ((size_t) buf & ~PAGE_MASK); + } + + /* DMA routine */ + dma_src = onenand->phys_base + (p - this->base); + dma_dst = dma_map_single(&onenand->pdev->dev, + buf, count, DMA_FROM_DEVICE); + if (dma_mapping_error(&onenand->pdev->dev, dma_dst)) { + dev_err(&onenand->pdev->dev, + "Couldn't map a %d byte buffer for DMA\n", count); + goto normal; + } + err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src, + count, S5PC110_DMA_DIR_READ); + dma_unmap_single(&onenand->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); + + if (!err) + return 0; + +normal: + if (count != mtd->writesize) { + /* Copy the bufferram to memory to prevent unaligned access */ + memcpy(this->page_buf, bufferram, mtd->writesize); + p = this->page_buf + offset; + } + + memcpy(buffer, p, count); + + return 0; +} + +static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + unsigned int flags = INT_ACT | LOAD_CMP; + unsigned int stat; + unsigned long timeout; + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & flags) + break; + } + /* To get correct interrupt status in timeout case */ + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + if (stat & LD_FAIL_ECC_ERR) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + + if (stat & LOAD_CMP) { + int ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); + if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + } + + return 0; +} + +static void s3c_onenand_check_lock_status(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + struct device *dev = &onenand->pdev->dev; + unsigned int block, end; + int tmp; + + end = this->chipsize >> this->erase_shift; + + for (block = 0; block < end; block++) { + unsigned int mem_addr = onenand->mem_addr(block, 0, 0); + tmp = s3c_read_cmd(CMD_MAP_01(onenand, mem_addr)); + + if (s3c_read_reg(INT_ERR_STAT_OFFSET) & LOCKED_BLK) { + dev_err(dev, "block %d is write-protected!\n", block); + s3c_write_reg(LOCKED_BLK, INT_ERR_ACK_OFFSET); + } + } +} + +static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, + size_t len, int cmd) +{ + struct onenand_chip *this = mtd->priv; + int start, end, start_mem_addr, end_mem_addr; + + start = ofs >> this->erase_shift; + start_mem_addr = onenand->mem_addr(start, 0, 0); + end = start + (len >> this->erase_shift) - 1; + end_mem_addr = onenand->mem_addr(end, 0, 0); + + if (cmd == ONENAND_CMD_LOCK) { + s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(onenand, + start_mem_addr)); + s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(onenand, + end_mem_addr)); + } else { + s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(onenand, + start_mem_addr)); + s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(onenand, + end_mem_addr)); + } + + this->wait(mtd, FL_LOCKING); +} + +static void s3c_unlock_all(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + loff_t ofs = 0; + size_t len = this->chipsize; + + if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); + + /* No need to check return value */ + this->wait(mtd, FL_LOCKING); + + /* Workaround for all block unlock in DDP */ + if (!ONENAND_IS_DDP(this)) { + s3c_onenand_check_lock_status(mtd); + return; + } + + /* All blocks on another chip */ + ofs = this->chipsize >> 1; + len = this->chipsize >> 1; + } + + s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); + + s3c_onenand_check_lock_status(mtd); +} + +static void s3c_onenand_setup(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + + onenand->mtd = mtd; + + if (onenand->type == TYPE_S3C6400) { + onenand->mem_addr = s3c6400_mem_addr; + onenand->cmd_map = s3c64xx_cmd_map; + } else if (onenand->type == TYPE_S3C6410) { + onenand->mem_addr = s3c6410_mem_addr; + onenand->cmd_map = s3c64xx_cmd_map; + } else if (onenand->type == TYPE_S5PC100) { + onenand->mem_addr = s5pc100_mem_addr; + onenand->cmd_map = s5pc1xx_cmd_map; + } else if (onenand->type == TYPE_S5PC110) { + /* Use generic onenand functions */ + onenand->cmd_map = s5pc1xx_cmd_map; + this->read_bufferram = s5pc110_read_bufferram; + return; + } else { + BUG(); + } + + this->read_word = s3c_onenand_readw; + this->write_word = s3c_onenand_writew; + + this->wait = s3c_onenand_wait; + this->bbt_wait = s3c_onenand_bbt_wait; + this->unlock_all = s3c_unlock_all; + this->command = s3c_onenand_command; + + this->read_bufferram = onenand_read_bufferram; + this->write_bufferram = onenand_write_bufferram; +} + +static int s3c_onenand_probe(struct platform_device *pdev) +{ + struct onenand_platform_data *pdata; + struct onenand_chip *this; + struct mtd_info *mtd; + struct resource *r; + int size, err; + unsigned long onenand_ctrl_cfg = 0; + + pdata = pdev->dev.platform_data; + /* No need to check pdata. the platform data is optional */ + + size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); + mtd = kzalloc(size, GFP_KERNEL); + if (!mtd) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL); + if (!onenand) { + err = -ENOMEM; + goto onenand_fail; + } + + this = (struct onenand_chip *) &mtd[1]; + mtd->priv = this; + mtd->dev.parent = &pdev->dev; + mtd->owner = THIS_MODULE; + onenand->pdev = pdev; + onenand->type = platform_get_device_id(pdev)->driver_data; + + s3c_onenand_setup(mtd); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENOENT; + goto ahb_resource_failed; + } + + onenand->base_res = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!onenand->base_res) { + dev_err(&pdev->dev, "failed to request memory resource\n"); + err = -EBUSY; + goto resource_failed; + } + + onenand->base = ioremap(r->start, resource_size(r)); + if (!onenand->base) { + dev_err(&pdev->dev, "failed to map memory resource\n"); + err = -EFAULT; + goto ioremap_failed; + } + /* Set onenand_chip also */ + this->base = onenand->base; + + /* Use runtime badblock check */ + this->options |= ONENAND_SKIP_UNLOCK_CHECK; + + if (onenand->type != TYPE_S5PC110) { + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r) { + dev_err(&pdev->dev, "no buffer memory resource defined\n"); + return -ENOENT; + goto ahb_resource_failed; + } + + onenand->ahb_res = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!onenand->ahb_res) { + dev_err(&pdev->dev, "failed to request buffer memory resource\n"); + err = -EBUSY; + goto ahb_resource_failed; + } + + onenand->ahb_addr = ioremap(r->start, resource_size(r)); + if (!onenand->ahb_addr) { + dev_err(&pdev->dev, "failed to map buffer memory resource\n"); + err = -EINVAL; + goto ahb_ioremap_failed; + } + + /* Allocate 4KiB BufferRAM */ + onenand->page_buf = kzalloc(SZ_4K, GFP_KERNEL); + if (!onenand->page_buf) { + err = -ENOMEM; + goto page_buf_fail; + } + + /* Allocate 128 SpareRAM */ + onenand->oob_buf = kzalloc(128, GFP_KERNEL); + if (!onenand->oob_buf) { + err = -ENOMEM; + goto oob_buf_fail; + } + + /* S3C doesn't handle subpage write */ + mtd->subpage_sft = 0; + this->subpagesize = mtd->writesize; + + } else { /* S5PC110 */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r) { + dev_err(&pdev->dev, "no dma memory resource defined\n"); + return -ENOENT; + goto dma_resource_failed; + } + + onenand->dma_res = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!onenand->dma_res) { + dev_err(&pdev->dev, "failed to request dma memory resource\n"); + err = -EBUSY; + goto dma_resource_failed; + } + + onenand->dma_addr = ioremap(r->start, resource_size(r)); + if (!onenand->dma_addr) { + dev_err(&pdev->dev, "failed to map dma memory resource\n"); + err = -EINVAL; + goto dma_ioremap_failed; + } + + onenand->phys_base = onenand->base_res->start; + + onenand_ctrl_cfg = readl(onenand->dma_addr + 0x100); + if ((onenand_ctrl_cfg & ONENAND_SYS_CFG1_SYNC_WRITE) && + onenand->dma_addr) + writel(onenand_ctrl_cfg & ~ONENAND_SYS_CFG1_SYNC_WRITE, + onenand->dma_addr + 0x100); + else + onenand_ctrl_cfg = 0; + } + + if (onenand_scan(mtd, 1)) { + err = -EFAULT; + goto scan_failed; + } + + if (onenand->type == TYPE_S5PC110) { + if (onenand_ctrl_cfg && onenand->dma_addr) + writel(onenand_ctrl_cfg, onenand->dma_addr + 0x100); + } else { + /* S3C doesn't handle subpage write */ + mtd->subpage_sft = 0; + this->subpagesize = mtd->writesize; + } + + if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ) + dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n"); + +#ifdef CONFIG_MTD_PARTITIONS + err = parse_mtd_partitions(mtd, part_probes, &onenand->parts, 0); + if (err > 0) + add_mtd_partitions(mtd, onenand->parts, err); + else if (err <= 0 && pdata && pdata->parts) + add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); + else +#endif + err = add_mtd_device(mtd); + + platform_set_drvdata(pdev, mtd); + + return 0; + +scan_failed: + if (onenand->dma_addr) + iounmap(onenand->dma_addr); +dma_ioremap_failed: + if (onenand->dma_res) + release_mem_region(onenand->dma_res->start, + resource_size(onenand->dma_res)); + kfree(onenand->oob_buf); +oob_buf_fail: + kfree(onenand->page_buf); +page_buf_fail: + if (onenand->ahb_addr) + iounmap(onenand->ahb_addr); +ahb_ioremap_failed: + if (onenand->ahb_res) + release_mem_region(onenand->ahb_res->start, + resource_size(onenand->ahb_res)); +dma_resource_failed: +ahb_resource_failed: + iounmap(onenand->base); +ioremap_failed: + if (onenand->base_res) + release_mem_region(onenand->base_res->start, + resource_size(onenand->base_res)); +resource_failed: + kfree(onenand); +onenand_fail: + kfree(mtd); + return err; +} + +static int __devexit s3c_onenand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + + onenand_release(mtd); + if (onenand->ahb_addr) + iounmap(onenand->ahb_addr); + if (onenand->ahb_res) + release_mem_region(onenand->ahb_res->start, + resource_size(onenand->ahb_res)); + if (onenand->dma_addr) + iounmap(onenand->dma_addr); + if (onenand->dma_res) + release_mem_region(onenand->dma_res->start, + resource_size(onenand->dma_res)); + + iounmap(onenand->base); + release_mem_region(onenand->base_res->start, + resource_size(onenand->base_res)); + + platform_set_drvdata(pdev, NULL); + kfree(onenand->oob_buf); + kfree(onenand->page_buf); + kfree(onenand); + kfree(mtd); + return 0; +} + +static int s3c_pm_ops_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct onenand_chip *this = mtd->priv; + + this->wait(mtd, FL_PM_SUSPENDED); + return mtd->suspend(mtd); +} + +static int s3c_pm_ops_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct onenand_chip *this = mtd->priv; + + mtd->resume(mtd); + this->unlock_all(mtd); + return 0; +} + +static const struct dev_pm_ops s3c_pm_ops = { + .suspend = s3c_pm_ops_suspend, + .resume = s3c_pm_ops_resume, +}; + +static struct platform_device_id s3c_onenand_driver_ids[] = { + { + .name = "s3c6400-onenand", + .driver_data = TYPE_S3C6400, + }, { + .name = "s3c6410-onenand", + .driver_data = TYPE_S3C6410, + }, { + .name = "s5pc100-onenand", + .driver_data = TYPE_S5PC100, + }, { + .name = "s5pc110-onenand", + .driver_data = TYPE_S5PC110, + }, { }, +}; +MODULE_DEVICE_TABLE(platform, s3c_onenand_driver_ids); + +static struct platform_driver s3c_onenand_driver = { + .driver = { + .name = "samsung-onenand", + .pm = &s3c_pm_ops, + }, + .id_table = s3c_onenand_driver_ids, + .probe = s3c_onenand_probe, + .remove = __devexit_p(s3c_onenand_remove), +}; + +static int __init s3c_onenand_init(void) +{ + return platform_driver_register(&s3c_onenand_driver); +} + +static void __exit s3c_onenand_exit(void) +{ + platform_driver_unregister(&s3c_onenand_driver); +} + +module_init(s3c_onenand_init); +module_exit(s3c_onenand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); +MODULE_DESCRIPTION("Samsung OneNAND controller support"); diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index d2aa9c46530..63b83c0d9a1 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -817,7 +817,6 @@ static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev) vfree(part->sector_map); kfree(part->header_cache); kfree(part->blocks); - kfree(part); } static struct mtd_blktrans_ops rfd_ftl_tr = { diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c new file mode 100644 index 00000000000..67822cf6c02 --- /dev/null +++ b/drivers/mtd/sm_ftl.c @@ -0,0 +1,1284 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * SmartMedia/xD translation layer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/random.h> +#include <linux/hdreg.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/sysfs.h> +#include <linux/bitops.h> +#include <linux/slab.h> +#include <linux/mtd/nand_ecc.h> +#include "nand/sm_common.h" +#include "sm_ftl.h" + + + +struct workqueue_struct *cache_flush_workqueue; + +static int cache_timeout = 1000; +module_param(cache_timeout, bool, S_IRUGO); +MODULE_PARM_DESC(cache_timeout, + "Timeout (in ms) for cache flush (1000 ms default"); + +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + + +/* ------------------- sysfs attributtes ---------------------------------- */ +struct sm_sysfs_attribute { + struct device_attribute dev_attr; + char *data; + int len; +}; + +ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sm_sysfs_attribute *sm_attr = + container_of(attr, struct sm_sysfs_attribute, dev_attr); + + strncpy(buf, sm_attr->data, sm_attr->len); + return sm_attr->len; +} + + +#define NUM_ATTRIBUTES 1 +#define SM_CIS_VENDOR_OFFSET 0x59 +struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) +{ + struct attribute_group *attr_group; + struct attribute **attributes; + struct sm_sysfs_attribute *vendor_attribute; + + int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, + SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET); + + char *vendor = kmalloc(vendor_len, GFP_KERNEL); + memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len); + vendor[vendor_len] = 0; + + /* Initialize sysfs attributes */ + vendor_attribute = + kzalloc(sizeof(struct sm_sysfs_attribute), GFP_KERNEL); + + sysfs_attr_init(&vendor_attribute->dev_attr.attr); + + vendor_attribute->data = vendor; + vendor_attribute->len = vendor_len; + vendor_attribute->dev_attr.attr.name = "vendor"; + vendor_attribute->dev_attr.attr.mode = S_IRUGO; + vendor_attribute->dev_attr.show = sm_attr_show; + + + /* Create array of pointers to the attributes */ + attributes = kzalloc(sizeof(struct attribute *) * (NUM_ATTRIBUTES + 1), + GFP_KERNEL); + attributes[0] = &vendor_attribute->dev_attr.attr; + + /* Finally create the attribute group */ + attr_group = kzalloc(sizeof(struct attribute_group), GFP_KERNEL); + attr_group->attrs = attributes; + return attr_group; +} + +void sm_delete_sysfs_attributes(struct sm_ftl *ftl) +{ + struct attribute **attributes = ftl->disk_attributes->attrs; + int i; + + for (i = 0; attributes[i] ; i++) { + + struct device_attribute *dev_attr = container_of(attributes[i], + struct device_attribute, attr); + + struct sm_sysfs_attribute *sm_attr = + container_of(dev_attr, + struct sm_sysfs_attribute, dev_attr); + + kfree(sm_attr->data); + kfree(sm_attr); + } + + kfree(ftl->disk_attributes->attrs); + kfree(ftl->disk_attributes); +} + + +/* ----------------------- oob helpers -------------------------------------- */ + +static int sm_get_lba(uint8_t *lba) +{ + /* check fixed bits */ + if ((lba[0] & 0xF8) != 0x10) + return -2; + + /* check parity - endianess doesn't matter */ + if (hweight16(*(uint16_t *)lba) & 1) + return -2; + + return (lba[1] >> 1) | ((lba[0] & 0x07) << 7); +} + + +/* + * Read LBA asscociated with block + * returns -1, if block is erased + * returns -2 if error happens + */ +static int sm_read_lba(struct sm_oob *oob) +{ + static const uint32_t erased_pattern[4] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + + uint16_t lba_test; + int lba; + + /* First test for erased block */ + if (!memcmp(oob, erased_pattern, SM_OOB_SIZE)) + return -1; + + /* Now check is both copies of the LBA differ too much */ + lba_test = *(uint16_t *)oob->lba_copy1 ^ *(uint16_t*)oob->lba_copy2; + if (lba_test && !is_power_of_2(lba_test)) + return -2; + + /* And read it */ + lba = sm_get_lba(oob->lba_copy1); + + if (lba == -2) + lba = sm_get_lba(oob->lba_copy2); + + return lba; +} + +static void sm_write_lba(struct sm_oob *oob, uint16_t lba) +{ + uint8_t tmp[2]; + + WARN_ON(lba >= 1000); + + tmp[0] = 0x10 | ((lba >> 7) & 0x07); + tmp[1] = (lba << 1) & 0xFF; + + if (hweight16(*(uint16_t *)tmp) & 0x01) + tmp[1] |= 1; + + oob->lba_copy1[0] = oob->lba_copy2[0] = tmp[0]; + oob->lba_copy1[1] = oob->lba_copy2[1] = tmp[1]; +} + + +/* Make offset from parts */ +static loff_t sm_mkoffset(struct sm_ftl *ftl, int zone, int block, int boffset) +{ + WARN_ON(boffset & (SM_SECTOR_SIZE - 1)); + WARN_ON(zone < 0 || zone >= ftl->zone_count); + WARN_ON(block >= ftl->zone_size); + WARN_ON(boffset >= ftl->block_size); + + if (block == -1) + return -1; + + return (zone * SM_MAX_ZONE_SIZE + block) * ftl->block_size + boffset; +} + +/* Breaks offset into parts */ +static void sm_break_offset(struct sm_ftl *ftl, loff_t offset, + int *zone, int *block, int *boffset) +{ + *boffset = do_div(offset, ftl->block_size); + *block = do_div(offset, ftl->max_lba); + *zone = offset >= ftl->zone_count ? -1 : offset; +} + +/* ---------------------- low level IO ------------------------------------- */ + +static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob) +{ + uint8_t ecc[3]; + + __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc); + if (__nand_correct_data(buffer, ecc, oob->ecc1, SM_SMALL_PAGE) < 0) + return -EIO; + + buffer += SM_SMALL_PAGE; + + __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc); + if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE) < 0) + return -EIO; + return 0; +} + +/* Reads a sector + oob*/ +static int sm_read_sector(struct sm_ftl *ftl, + int zone, int block, int boffset, + uint8_t *buffer, struct sm_oob *oob) +{ + struct mtd_info *mtd = ftl->trans->mtd; + struct mtd_oob_ops ops; + struct sm_oob tmp_oob; + int ret = -EIO; + int try = 0; + + /* FTL can contain -1 entries that are by default filled with bits */ + if (block == -1) { + memset(buffer, 0xFF, SM_SECTOR_SIZE); + return 0; + } + + /* User might not need the oob, but we do for data vertification */ + if (!oob) + oob = &tmp_oob; + + ops.mode = ftl->smallpagenand ? MTD_OOB_RAW : MTD_OOB_PLACE; + ops.ooboffs = 0; + ops.ooblen = SM_OOB_SIZE; + ops.oobbuf = (void *)oob; + ops.len = SM_SECTOR_SIZE; + ops.datbuf = buffer; + +again: + if (try++) { + /* Avoid infinite recursion on CIS reads, sm_recheck_media + won't help anyway */ + if (zone == 0 && block == ftl->cis_block && boffset == + ftl->cis_boffset) + return ret; + + /* Test if media is stable */ + if (try == 3 || sm_recheck_media(ftl)) + return ret; + } + + /* Unfortunelly, oob read will _always_ succeed, + despite card removal..... */ + ret = mtd->read_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); + + /* Test for unknown errors */ + if (ret != 0 && ret != -EUCLEAN && ret != -EBADMSG) { + dbg("read of block %d at zone %d, failed due to error (%d)", + block, zone, ret); + goto again; + } + + /* Do a basic test on the oob, to guard against returned garbage */ + if (oob->reserved != 0xFFFFFFFF && !is_power_of_2(~oob->reserved)) + goto again; + + /* This should never happen, unless there is a bug in the mtd driver */ + WARN_ON(ops.oobretlen != SM_OOB_SIZE); + WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE); + + if (!buffer) + return 0; + + /* Test if sector marked as bad */ + if (!sm_sector_valid(oob)) { + dbg("read of block %d at zone %d, failed because it is marked" + " as bad" , block, zone); + goto again; + } + + /* Test ECC*/ + if (ret == -EBADMSG || + (ftl->smallpagenand && sm_correct_sector(buffer, oob))) { + + dbg("read of block %d at zone %d, failed due to ECC error", + block, zone); + goto again; + } + + return 0; +} + +/* Writes a sector to media */ +static int sm_write_sector(struct sm_ftl *ftl, + int zone, int block, int boffset, + uint8_t *buffer, struct sm_oob *oob) +{ + struct mtd_oob_ops ops; + struct mtd_info *mtd = ftl->trans->mtd; + int ret; + + BUG_ON(ftl->readonly); + + if (zone == 0 && (block == ftl->cis_block || block == 0)) { + dbg("attempted to write the CIS!"); + return -EIO; + } + + if (ftl->unstable) + return -EIO; + + ops.mode = ftl->smallpagenand ? MTD_OOB_RAW : MTD_OOB_PLACE; + ops.len = SM_SECTOR_SIZE; + ops.datbuf = buffer; + ops.ooboffs = 0; + ops.ooblen = SM_OOB_SIZE; + ops.oobbuf = (void *)oob; + + ret = mtd->write_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); + + /* Now we assume that hardware will catch write bitflip errors */ + /* If you are paranoid, use CONFIG_MTD_NAND_VERIFY_WRITE */ + + if (ret) { + dbg("write to block %d at zone %d, failed with error %d", + block, zone, ret); + + sm_recheck_media(ftl); + return ret; + } + + /* This should never happen, unless there is a bug in the driver */ + WARN_ON(ops.oobretlen != SM_OOB_SIZE); + WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE); + + return 0; +} + +/* ------------------------ block IO ------------------------------------- */ + +/* Write a block using data and lba, and invalid sector bitmap */ +static int sm_write_block(struct sm_ftl *ftl, uint8_t *buf, + int zone, int block, int lba, + unsigned long invalid_bitmap) +{ + struct sm_oob oob; + int boffset; + int retry = 0; + + /* Initialize the oob with requested values */ + memset(&oob, 0xFF, SM_OOB_SIZE); + sm_write_lba(&oob, lba); +restart: + if (ftl->unstable) + return -EIO; + + for (boffset = 0; boffset < ftl->block_size; + boffset += SM_SECTOR_SIZE) { + + oob.data_status = 0xFF; + + if (test_bit(boffset / SM_SECTOR_SIZE, &invalid_bitmap)) { + + sm_printk("sector %d of block at LBA %d of zone %d" + " coudn't be read, marking it as invalid", + boffset / SM_SECTOR_SIZE, lba, zone); + + oob.data_status = 0; + } + + if (ftl->smallpagenand) { + __nand_calculate_ecc(buf + boffset, + SM_SMALL_PAGE, oob.ecc1); + + __nand_calculate_ecc(buf + boffset + SM_SMALL_PAGE, + SM_SMALL_PAGE, oob.ecc2); + } + if (!sm_write_sector(ftl, zone, block, boffset, + buf + boffset, &oob)) + continue; + + if (!retry) { + + /* If write fails. try to erase the block */ + /* This is safe, because we never write in blocks + that contain valuable data. + This is intended to repair block that are marked + as erased, but that isn't fully erased*/ + + if (sm_erase_block(ftl, zone, block, 0)) + return -EIO; + + retry = 1; + goto restart; + } else { + sm_mark_block_bad(ftl, zone, block); + return -EIO; + } + } + return 0; +} + + +/* Mark whole block at offset 'offs' as bad. */ +static void sm_mark_block_bad(struct sm_ftl *ftl, int zone, int block) +{ + struct sm_oob oob; + int boffset; + + memset(&oob, 0xFF, SM_OOB_SIZE); + oob.block_status = 0xF0; + + if (ftl->unstable) + return; + + if (sm_recheck_media(ftl)) + return; + + sm_printk("marking block %d of zone %d as bad", block, zone); + + /* We aren't checking the return value, because we don't care */ + /* This also fails on fake xD cards, but I guess these won't expose + any bad blocks till fail completly */ + for (boffset = 0; boffset < ftl->block_size; boffset += SM_SECTOR_SIZE) + sm_write_sector(ftl, zone, block, boffset, NULL, &oob); +} + +/* + * Erase a block within a zone + * If erase succedes, it updates free block fifo, otherwise marks block as bad + */ +static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, + int put_free) +{ + struct ftl_zone *zone = &ftl->zones[zone_num]; + struct mtd_info *mtd = ftl->trans->mtd; + struct erase_info erase; + + erase.mtd = mtd; + erase.callback = sm_erase_callback; + erase.addr = sm_mkoffset(ftl, zone_num, block, 0); + erase.len = ftl->block_size; + erase.priv = (u_long)ftl; + + if (ftl->unstable) + return -EIO; + + BUG_ON(ftl->readonly); + + if (zone_num == 0 && (block == ftl->cis_block || block == 0)) { + sm_printk("attempted to erase the CIS!"); + return -EIO; + } + + if (mtd->erase(mtd, &erase)) { + sm_printk("erase of block %d in zone %d failed", + block, zone_num); + goto error; + } + + if (erase.state == MTD_ERASE_PENDING) + wait_for_completion(&ftl->erase_completion); + + if (erase.state != MTD_ERASE_DONE) { + sm_printk("erase of block %d in zone %d failed after wait", + block, zone_num); + goto error; + } + + if (put_free) + kfifo_in(&zone->free_sectors, + (const unsigned char *)&block, sizeof(block)); + + return 0; +error: + sm_mark_block_bad(ftl, zone_num, block); + return -EIO; +} + +static void sm_erase_callback(struct erase_info *self) +{ + struct sm_ftl *ftl = (struct sm_ftl *)self->priv; + complete(&ftl->erase_completion); +} + +/* Throughtly test that block is valid. */ +static int sm_check_block(struct sm_ftl *ftl, int zone, int block) +{ + int boffset; + struct sm_oob oob; + int lbas[] = { -3, 0, 0, 0 }; + int i = 0; + int test_lba; + + + /* First just check that block doesn't look fishy */ + /* Only blocks that are valid or are sliced in two parts, are + accepted */ + for (boffset = 0; boffset < ftl->block_size; + boffset += SM_SECTOR_SIZE) { + + /* This shoudn't happen anyway */ + if (sm_read_sector(ftl, zone, block, boffset, NULL, &oob)) + return -2; + + test_lba = sm_read_lba(&oob); + + if (lbas[i] != test_lba) + lbas[++i] = test_lba; + + /* If we found three different LBAs, something is fishy */ + if (i == 3) + return -EIO; + } + + /* If the block is sliced (partialy erased usually) erase it */ + if (i == 2) { + sm_erase_block(ftl, zone, block, 1); + return 1; + } + + return 0; +} + +/* ----------------- media scanning --------------------------------- */ +static const struct chs_entry chs_table[] = { + { 1, 125, 4, 4 }, + { 2, 125, 4, 8 }, + { 4, 250, 4, 8 }, + { 8, 250, 4, 16 }, + { 16, 500, 4, 16 }, + { 32, 500, 8, 16 }, + { 64, 500, 8, 32 }, + { 128, 500, 16, 32 }, + { 256, 1000, 16, 32 }, + { 512, 1015, 32, 63 }, + { 1024, 985, 33, 63 }, + { 2048, 985, 33, 63 }, + { 0 }, +}; + + +static const uint8_t cis_signature[] = { + 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 +}; +/* Find out media parameters. + * This ideally has to be based on nand id, but for now device size is enough */ +int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd) +{ + int i; + int size_in_megs = mtd->size / (1024 * 1024); + + ftl->readonly = mtd->type == MTD_ROM; + + /* Manual settings for very old devices */ + ftl->zone_count = 1; + ftl->smallpagenand = 0; + + switch (size_in_megs) { + case 1: + /* 1 MiB flash/rom SmartMedia card (256 byte pages)*/ + ftl->zone_size = 256; + ftl->max_lba = 250; + ftl->block_size = 8 * SM_SECTOR_SIZE; + ftl->smallpagenand = 1; + + break; + case 2: + /* 2 MiB flash SmartMedia (256 byte pages)*/ + if (mtd->writesize == SM_SMALL_PAGE) { + ftl->zone_size = 512; + ftl->max_lba = 500; + ftl->block_size = 8 * SM_SECTOR_SIZE; + ftl->smallpagenand = 1; + /* 2 MiB rom SmartMedia */ + } else { + + if (!ftl->readonly) + return -ENODEV; + + ftl->zone_size = 256; + ftl->max_lba = 250; + ftl->block_size = 16 * SM_SECTOR_SIZE; + } + break; + case 4: + /* 4 MiB flash/rom SmartMedia device */ + ftl->zone_size = 512; + ftl->max_lba = 500; + ftl->block_size = 16 * SM_SECTOR_SIZE; + break; + case 8: + /* 8 MiB flash/rom SmartMedia device */ + ftl->zone_size = 1024; + ftl->max_lba = 1000; + ftl->block_size = 16 * SM_SECTOR_SIZE; + } + + /* Minimum xD size is 16MiB. Also, all xD cards have standard zone + sizes. SmartMedia cards exist up to 128 MiB and have same layout*/ + if (size_in_megs >= 16) { + ftl->zone_count = size_in_megs / 16; + ftl->zone_size = 1024; + ftl->max_lba = 1000; + ftl->block_size = 32 * SM_SECTOR_SIZE; + } + + /* Test for proper write,erase and oob sizes */ + if (mtd->erasesize > ftl->block_size) + return -ENODEV; + + if (mtd->writesize > SM_SECTOR_SIZE) + return -ENODEV; + + if (ftl->smallpagenand && mtd->oobsize < SM_SMALL_OOB_SIZE) + return -ENODEV; + + if (!ftl->smallpagenand && mtd->oobsize < SM_OOB_SIZE) + return -ENODEV; + + /* We use these functions for IO */ + if (!mtd->read_oob || !mtd->write_oob) + return -ENODEV; + + /* Find geometry information */ + for (i = 0 ; i < ARRAY_SIZE(chs_table) ; i++) { + if (chs_table[i].size == size_in_megs) { + ftl->cylinders = chs_table[i].cyl; + ftl->heads = chs_table[i].head; + ftl->sectors = chs_table[i].sec; + return 0; + } + } + + sm_printk("media has unknown size : %dMiB", size_in_megs); + ftl->cylinders = 985; + ftl->heads = 33; + ftl->sectors = 63; + return 0; +} + +/* Validate the CIS */ +static int sm_read_cis(struct sm_ftl *ftl) +{ + struct sm_oob oob; + + if (sm_read_sector(ftl, + 0, ftl->cis_block, ftl->cis_boffset, ftl->cis_buffer, &oob)) + return -EIO; + + if (!sm_sector_valid(&oob) || !sm_block_valid(&oob)) + return -EIO; + + if (!memcmp(ftl->cis_buffer + ftl->cis_page_offset, + cis_signature, sizeof(cis_signature))) { + return 0; + } + + return -EIO; +} + +/* Scan the media for the CIS */ +static int sm_find_cis(struct sm_ftl *ftl) +{ + struct sm_oob oob; + int block, boffset; + int block_found = 0; + int cis_found = 0; + + /* Search for first valid block */ + for (block = 0 ; block < ftl->zone_size - ftl->max_lba ; block++) { + + if (sm_read_sector(ftl, 0, block, 0, NULL, &oob)) + continue; + + if (!sm_block_valid(&oob)) + continue; + block_found = 1; + break; + } + + if (!block_found) + return -EIO; + + /* Search for first valid sector in this block */ + for (boffset = 0 ; boffset < ftl->block_size; + boffset += SM_SECTOR_SIZE) { + + if (sm_read_sector(ftl, 0, block, boffset, NULL, &oob)) + continue; + + if (!sm_sector_valid(&oob)) + continue; + break; + } + + if (boffset == ftl->block_size) + return -EIO; + + ftl->cis_block = block; + ftl->cis_boffset = boffset; + ftl->cis_page_offset = 0; + + cis_found = !sm_read_cis(ftl); + + if (!cis_found) { + ftl->cis_page_offset = SM_SMALL_PAGE; + cis_found = !sm_read_cis(ftl); + } + + if (cis_found) { + dbg("CIS block found at offset %x", + block * ftl->block_size + + boffset + ftl->cis_page_offset); + return 0; + } + return -EIO; +} + +/* Basic test to determine if underlying mtd device if functional */ +static int sm_recheck_media(struct sm_ftl *ftl) +{ + if (sm_read_cis(ftl)) { + + if (!ftl->unstable) { + sm_printk("media unstable, not allowing writes"); + ftl->unstable = 1; + } + return -EIO; + } + return 0; +} + +/* Initialize a FTL zone */ +static int sm_init_zone(struct sm_ftl *ftl, int zone_num) +{ + struct ftl_zone *zone = &ftl->zones[zone_num]; + struct sm_oob oob; + uint16_t block; + int lba; + int i = 0; + int len; + + dbg("initializing zone %d", zone_num); + + /* Allocate memory for FTL table */ + zone->lba_to_phys_table = kmalloc(ftl->max_lba * 2, GFP_KERNEL); + + if (!zone->lba_to_phys_table) + return -ENOMEM; + memset(zone->lba_to_phys_table, -1, ftl->max_lba * 2); + + + /* Allocate memory for free sectors FIFO */ + if (kfifo_alloc(&zone->free_sectors, ftl->zone_size * 2, GFP_KERNEL)) { + kfree(zone->lba_to_phys_table); + return -ENOMEM; + } + + /* Now scan the zone */ + for (block = 0 ; block < ftl->zone_size ; block++) { + + /* Skip blocks till the CIS (including) */ + if (zone_num == 0 && block <= ftl->cis_block) + continue; + + /* Read the oob of first sector */ + if (sm_read_sector(ftl, zone_num, block, 0, NULL, &oob)) + return -EIO; + + /* Test to see if block is erased. It is enough to test + first sector, because erase happens in one shot */ + if (sm_block_erased(&oob)) { + kfifo_in(&zone->free_sectors, + (unsigned char *)&block, 2); + continue; + } + + /* If block is marked as bad, skip it */ + /* This assumes we can trust first sector*/ + /* However the way the block valid status is defined, ensures + very low probability of failure here */ + if (!sm_block_valid(&oob)) { + dbg("PH %04d <-> <marked bad>", block); + continue; + } + + + lba = sm_read_lba(&oob); + + /* Invalid LBA means that block is damaged. */ + /* We can try to erase it, or mark it as bad, but + lets leave that to recovery application */ + if (lba == -2 || lba >= ftl->max_lba) { + dbg("PH %04d <-> LBA %04d(bad)", block, lba); + continue; + } + + + /* If there is no collision, + just put the sector in the FTL table */ + if (zone->lba_to_phys_table[lba] < 0) { + dbg_verbose("PH %04d <-> LBA %04d", block, lba); + zone->lba_to_phys_table[lba] = block; + continue; + } + + sm_printk("collision" + " of LBA %d between blocks %d and %d in zone %d", + lba, zone->lba_to_phys_table[lba], block, zone_num); + + /* Test that this block is valid*/ + if (sm_check_block(ftl, zone_num, block)) + continue; + + /* Test now the old block */ + if (sm_check_block(ftl, zone_num, + zone->lba_to_phys_table[lba])) { + zone->lba_to_phys_table[lba] = block; + continue; + } + + /* If both blocks are valid and share same LBA, it means that + they hold different versions of same data. It not + known which is more recent, thus just erase one of them + */ + sm_printk("both blocks are valid, erasing the later"); + sm_erase_block(ftl, zone_num, block, 1); + } + + dbg("zone initialized"); + zone->initialized = 1; + + /* No free sectors, means that the zone is heavily damaged, write won't + work, but it can still can be (partially) read */ + if (!kfifo_len(&zone->free_sectors)) { + sm_printk("no free blocks in zone %d", zone_num); + return 0; + } + + /* Randomize first block we write to */ + get_random_bytes(&i, 2); + i %= (kfifo_len(&zone->free_sectors) / 2); + + while (i--) { + len = kfifo_out(&zone->free_sectors, + (unsigned char *)&block, 2); + WARN_ON(len != 2); + kfifo_in(&zone->free_sectors, (const unsigned char *)&block, 2); + } + return 0; +} + +/* Get and automaticly initialize an FTL mapping for one zone */ +struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) +{ + struct ftl_zone *zone; + int error; + + BUG_ON(zone_num >= ftl->zone_count); + zone = &ftl->zones[zone_num]; + + if (!zone->initialized) { + error = sm_init_zone(ftl, zone_num); + + if (error) + return ERR_PTR(error); + } + return zone; +} + + +/* ----------------- cache handling ------------------------------------------*/ + +/* Initialize the one block cache */ +void sm_cache_init(struct sm_ftl *ftl) +{ + ftl->cache_data_invalid_bitmap = 0xFFFFFFFF; + ftl->cache_clean = 1; + ftl->cache_zone = -1; + ftl->cache_block = -1; + /*memset(ftl->cache_data, 0xAA, ftl->block_size);*/ +} + +/* Put sector in one block cache */ +void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset) +{ + memcpy(ftl->cache_data + boffset, buffer, SM_SECTOR_SIZE); + clear_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap); + ftl->cache_clean = 0; +} + +/* Read a sector from the cache */ +int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) +{ + if (test_bit(boffset / SM_SECTOR_SIZE, + &ftl->cache_data_invalid_bitmap)) + return -1; + + memcpy(buffer, ftl->cache_data + boffset, SM_SECTOR_SIZE); + return 0; +} + +/* Write the cache to hardware */ +int sm_cache_flush(struct sm_ftl *ftl) +{ + struct ftl_zone *zone; + + int sector_num; + uint16_t write_sector; + int zone_num = ftl->cache_zone; + int block_num; + + if (ftl->cache_clean) + return 0; + + if (ftl->unstable) + return -EIO; + + BUG_ON(zone_num < 0); + zone = &ftl->zones[zone_num]; + block_num = zone->lba_to_phys_table[ftl->cache_block]; + + + /* Try to read all unread areas of the cache block*/ + for_each_set_bit(sector_num, &ftl->cache_data_invalid_bitmap, + ftl->block_size / SM_SECTOR_SIZE) { + + if (!sm_read_sector(ftl, + zone_num, block_num, sector_num * SM_SECTOR_SIZE, + ftl->cache_data + sector_num * SM_SECTOR_SIZE, NULL)) + clear_bit(sector_num, + &ftl->cache_data_invalid_bitmap); + } +restart: + + if (ftl->unstable) + return -EIO; + + /* If there are no spare blocks, */ + /* we could still continue by erasing/writing the current block, + but for such worn out media it doesn't worth the trouble, + and the dangers */ + if (kfifo_out(&zone->free_sectors, + (unsigned char *)&write_sector, 2) != 2) { + dbg("no free sectors for write!"); + return -EIO; + } + + + if (sm_write_block(ftl, ftl->cache_data, zone_num, write_sector, + ftl->cache_block, ftl->cache_data_invalid_bitmap)) + goto restart; + + /* Update the FTL table */ + zone->lba_to_phys_table[ftl->cache_block] = write_sector; + + /* Write succesfull, so erase and free the old block */ + if (block_num > 0) + sm_erase_block(ftl, zone_num, block_num, 1); + + sm_cache_init(ftl); + return 0; +} + + +/* flush timer, runs a second after last write */ +static void sm_cache_flush_timer(unsigned long data) +{ + struct sm_ftl *ftl = (struct sm_ftl *)data; + queue_work(cache_flush_workqueue, &ftl->flush_work); +} + +/* cache flush work, kicked by timer */ +static void sm_cache_flush_work(struct work_struct *work) +{ + struct sm_ftl *ftl = container_of(work, struct sm_ftl, flush_work); + mutex_lock(&ftl->mutex); + sm_cache_flush(ftl); + mutex_unlock(&ftl->mutex); + return; +} + +/* ---------------- outside interface -------------------------------------- */ + +/* outside interface: read a sector */ +static int sm_read(struct mtd_blktrans_dev *dev, + unsigned long sect_no, char *buf) +{ + struct sm_ftl *ftl = dev->priv; + struct ftl_zone *zone; + int error = 0, in_cache = 0; + int zone_num, block, boffset; + + sm_break_offset(ftl, sect_no << 9, &zone_num, &block, &boffset); + mutex_lock(&ftl->mutex); + + + zone = sm_get_zone(ftl, zone_num); + if (IS_ERR(zone)) { + error = PTR_ERR(zone); + goto unlock; + } + + /* Have to look at cache first */ + if (ftl->cache_zone == zone_num && ftl->cache_block == block) { + in_cache = 1; + if (!sm_cache_get(ftl, buf, boffset)) + goto unlock; + } + + /* Translate the block and return if doesn't exist in the table */ + block = zone->lba_to_phys_table[block]; + + if (block == -1) { + memset(buf, 0xFF, SM_SECTOR_SIZE); + goto unlock; + } + + if (sm_read_sector(ftl, zone_num, block, boffset, buf, NULL)) { + error = -EIO; + goto unlock; + } + + if (in_cache) + sm_cache_put(ftl, buf, boffset); +unlock: + mutex_unlock(&ftl->mutex); + return error; +} + +/* outside interface: write a sector */ +static int sm_write(struct mtd_blktrans_dev *dev, + unsigned long sec_no, char *buf) +{ + struct sm_ftl *ftl = dev->priv; + struct ftl_zone *zone; + int error, zone_num, block, boffset; + + BUG_ON(ftl->readonly); + sm_break_offset(ftl, sec_no << 9, &zone_num, &block, &boffset); + + /* No need in flush thread running now */ + del_timer(&ftl->timer); + mutex_lock(&ftl->mutex); + + zone = sm_get_zone(ftl, zone_num); + if (IS_ERR(zone)) { + error = PTR_ERR(zone); + goto unlock; + } + + /* If entry is not in cache, flush it */ + if (ftl->cache_block != block || ftl->cache_zone != zone_num) { + + error = sm_cache_flush(ftl); + if (error) + goto unlock; + + ftl->cache_block = block; + ftl->cache_zone = zone_num; + } + + sm_cache_put(ftl, buf, boffset); +unlock: + mod_timer(&ftl->timer, jiffies + msecs_to_jiffies(cache_timeout)); + mutex_unlock(&ftl->mutex); + return error; +} + +/* outside interface: flush everything */ +static int sm_flush(struct mtd_blktrans_dev *dev) +{ + struct sm_ftl *ftl = dev->priv; + int retval; + + mutex_lock(&ftl->mutex); + retval = sm_cache_flush(ftl); + mutex_unlock(&ftl->mutex); + return retval; +} + +/* outside interface: device is released */ +static int sm_release(struct mtd_blktrans_dev *dev) +{ + struct sm_ftl *ftl = dev->priv; + + mutex_lock(&ftl->mutex); + del_timer_sync(&ftl->timer); + cancel_work_sync(&ftl->flush_work); + sm_cache_flush(ftl); + mutex_unlock(&ftl->mutex); + return 0; +} + +/* outside interface: get geometry */ +static int sm_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) +{ + struct sm_ftl *ftl = dev->priv; + geo->heads = ftl->heads; + geo->sectors = ftl->sectors; + geo->cylinders = ftl->cylinders; + return 0; +} + +/* external interface: main initialization function */ +static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) +{ + struct mtd_blktrans_dev *trans; + struct sm_ftl *ftl; + + /* Allocate & initialize our private structure */ + ftl = kzalloc(sizeof(struct sm_ftl), GFP_KERNEL); + if (!ftl) + goto error1; + + + mutex_init(&ftl->mutex); + setup_timer(&ftl->timer, sm_cache_flush_timer, (unsigned long)ftl); + INIT_WORK(&ftl->flush_work, sm_cache_flush_work); + init_completion(&ftl->erase_completion); + + /* Read media information */ + if (sm_get_media_info(ftl, mtd)) { + dbg("found unsupported mtd device, aborting"); + goto error2; + } + + + /* Allocate temporary CIS buffer for read retry support */ + ftl->cis_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); + if (!ftl->cis_buffer) + goto error2; + + /* Allocate zone array, it will be initialized on demand */ + ftl->zones = kzalloc(sizeof(struct ftl_zone) * ftl->zone_count, + GFP_KERNEL); + if (!ftl->zones) + goto error3; + + /* Allocate the cache*/ + ftl->cache_data = kzalloc(ftl->block_size, GFP_KERNEL); + + if (!ftl->cache_data) + goto error4; + + sm_cache_init(ftl); + + + /* Allocate upper layer structure and initialize it */ + trans = kzalloc(sizeof(struct mtd_blktrans_dev), GFP_KERNEL); + if (!trans) + goto error5; + + ftl->trans = trans; + trans->priv = ftl; + + trans->tr = tr; + trans->mtd = mtd; + trans->devnum = -1; + trans->size = (ftl->block_size * ftl->max_lba * ftl->zone_count) >> 9; + trans->readonly = ftl->readonly; + + if (sm_find_cis(ftl)) { + dbg("CIS not found on mtd device, aborting"); + goto error6; + } + + ftl->disk_attributes = sm_create_sysfs_attributes(ftl); + trans->disk_attributes = ftl->disk_attributes; + + sm_printk("Found %d MiB xD/SmartMedia FTL on mtd%d", + (int)(mtd->size / (1024 * 1024)), mtd->index); + + dbg("FTL layout:"); + dbg("%d zone(s), each consists of %d blocks (+%d spares)", + ftl->zone_count, ftl->max_lba, + ftl->zone_size - ftl->max_lba); + dbg("each block consists of %d bytes", + ftl->block_size); + + + /* Register device*/ + if (add_mtd_blktrans_dev(trans)) { + dbg("error in mtdblktrans layer"); + goto error6; + } + return; +error6: + kfree(trans); +error5: + kfree(ftl->cache_data); +error4: + kfree(ftl->zones); +error3: + kfree(ftl->cis_buffer); +error2: + kfree(ftl); +error1: + return; +} + +/* main interface: device {surprise,} removal */ +static void sm_remove_dev(struct mtd_blktrans_dev *dev) +{ + struct sm_ftl *ftl = dev->priv; + int i; + + del_mtd_blktrans_dev(dev); + ftl->trans = NULL; + + for (i = 0 ; i < ftl->zone_count; i++) { + + if (!ftl->zones[i].initialized) + continue; + + kfree(ftl->zones[i].lba_to_phys_table); + kfifo_free(&ftl->zones[i].free_sectors); + } + + sm_delete_sysfs_attributes(ftl); + kfree(ftl->cis_buffer); + kfree(ftl->zones); + kfree(ftl->cache_data); + kfree(ftl); +} + +static struct mtd_blktrans_ops sm_ftl_ops = { + .name = "smblk", + .major = -1, + .part_bits = SM_FTL_PARTN_BITS, + .blksize = SM_SECTOR_SIZE, + .getgeo = sm_getgeo, + + .add_mtd = sm_add_mtd, + .remove_dev = sm_remove_dev, + + .readsect = sm_read, + .writesect = sm_write, + + .flush = sm_flush, + .release = sm_release, + + .owner = THIS_MODULE, +}; + +static __init int sm_module_init(void) +{ + int error = 0; + cache_flush_workqueue = create_freezeable_workqueue("smflush"); + + if (IS_ERR(cache_flush_workqueue)) + return PTR_ERR(cache_flush_workqueue); + + error = register_mtd_blktrans(&sm_ftl_ops); + if (error) + destroy_workqueue(cache_flush_workqueue); + return error; + +} + +static void __exit sm_module_exit(void) +{ + destroy_workqueue(cache_flush_workqueue); + deregister_mtd_blktrans(&sm_ftl_ops); +} + +module_init(sm_module_init); +module_exit(sm_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); +MODULE_DESCRIPTION("Smartmedia/xD mtd translation layer"); diff --git a/drivers/mtd/sm_ftl.h b/drivers/mtd/sm_ftl.h new file mode 100644 index 00000000000..e30e48e7f63 --- /dev/null +++ b/drivers/mtd/sm_ftl.h @@ -0,0 +1,94 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * SmartMedia/xD translation layer + * + * Based loosly on ssfdc.c which is + * © 2005 Eptar srl + * Author: Claudio Lanconelli <lanconelli.claudio@eptar.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/mtd/blktrans.h> +#include <linux/kfifo.h> +#include <linux/sched.h> +#include <linux/completion.h> +#include <linux/mtd/mtd.h> + + + +struct ftl_zone { + int initialized; + int16_t *lba_to_phys_table; /* LBA to physical table */ + struct kfifo free_sectors; /* queue of free sectors */ +}; + +struct sm_ftl { + struct mtd_blktrans_dev *trans; + + struct mutex mutex; /* protects the structure */ + struct ftl_zone *zones; /* FTL tables for each zone */ + + /* Media information */ + int block_size; /* block size in bytes */ + int zone_size; /* zone size in blocks */ + int zone_count; /* number of zones */ + int max_lba; /* maximum lba in a zone */ + int smallpagenand; /* 256 bytes/page nand */ + int readonly; /* is FS readonly */ + int unstable; + int cis_block; /* CIS block location */ + int cis_boffset; /* CIS offset in the block */ + int cis_page_offset; /* CIS offset in the page */ + void *cis_buffer; /* tmp buffer for cis reads */ + + /* Cache */ + int cache_block; /* block number of cached block */ + int cache_zone; /* zone of cached block */ + unsigned char *cache_data; /* cached block data */ + long unsigned int cache_data_invalid_bitmap; + int cache_clean; + struct work_struct flush_work; + struct timer_list timer; + + /* Async erase stuff */ + struct completion erase_completion; + + /* Geometry stuff */ + int heads; + int sectors; + int cylinders; + + struct attribute_group *disk_attributes; +}; + +struct chs_entry { + unsigned long size; + unsigned short cyl; + unsigned char head; + unsigned char sec; +}; + + +#define SM_FTL_PARTN_BITS 3 + +#define sm_printk(format, ...) \ + printk(KERN_WARNING "sm_ftl" ": " format "\n", ## __VA_ARGS__) + +#define dbg(format, ...) \ + if (debug) \ + printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__) + +#define dbg_verbose(format, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__) + + +static void sm_erase_callback(struct erase_info *self); +static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, + int put_free); +static void sm_mark_block_bad(struct sm_ftl *ftl, int zone_num, int block); + +static int sm_recheck_media(struct sm_ftl *ftl); diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c index 3f67e00d98e..81c4ecdc11f 100644 --- a/drivers/mtd/ssfdc.c +++ b/drivers/mtd/ssfdc.c @@ -375,7 +375,6 @@ static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev) del_mtd_blktrans_dev(dev); kfree(ssfdc->logic_block_map); - kfree(ssfdc); } static int ssfdcr_readsect(struct mtd_blktrans_dev *dev, diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index 921a85df919..6bc1b8276c6 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c @@ -480,12 +480,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); printk(PRINT_PREF "scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c index 7107fccbc7d..afe71aa15c4 100644 --- a/drivers/mtd/tests/mtd_readtest.c +++ b/drivers/mtd/tests/mtd_readtest.c @@ -141,12 +141,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); /* NOR flash does not implement block_isbad */ if (mtd->block_isbad == NULL) diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c index 56ca62bb96b..161feeb7b8b 100644 --- a/drivers/mtd/tests/mtd_speedtest.c +++ b/drivers/mtd/tests/mtd_speedtest.c @@ -295,12 +295,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); /* NOR flash does not implement block_isbad */ if (mtd->block_isbad == NULL) diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c index 3854afec56d..531625fc925 100644 --- a/drivers/mtd/tests/mtd_stresstest.c +++ b/drivers/mtd/tests/mtd_stresstest.c @@ -221,12 +221,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); /* NOR flash does not implement block_isbad */ if (mtd->block_isbad == NULL) diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c index 700237a3d12..11204e8aab5 100644 --- a/drivers/mtd/tests/mtd_subpagetest.c +++ b/drivers/mtd/tests/mtd_subpagetest.c @@ -354,12 +354,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); printk(PRINT_PREF "scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index 0a8c7ea764a..f702a163d8d 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -27,7 +27,7 @@ config MTD_UBI_WL_THRESHOLD The default value should be OK for SLC NAND flashes, NOR flashes and other flashes which have eraseblock life-cycle 100000 or more. However, in case of MLC NAND flashes which typically have eraseblock - life-cycle less then 10000, the threshold should be lessened (e.g., + life-cycle less than 10000, the threshold should be lessened (e.g., to 128 or 256, although it does not have to be power of 2). config MTD_UBI_BEB_RESERVE diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 55c726dde94..13b05cb33b0 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -42,7 +42,6 @@ #include <linux/miscdevice.h> #include <linux/log2.h> #include <linux/kthread.h> -#include <linux/reboot.h> #include <linux/kernel.h> #include <linux/slab.h> #include "ubi.h" @@ -50,6 +49,12 @@ /* Maximum length of the 'mtd=' parameter */ #define MTD_PARAM_LEN_MAX 64 +#ifdef CONFIG_MTD_UBI_MODULE +#define ubi_is_module() 1 +#else +#define ubi_is_module() 0 +#endif + /** * struct mtd_dev_param - MTD device parameter description data structure. * @name: MTD character device node path, MTD device name, or MTD device number @@ -832,34 +837,6 @@ static int autoresize(struct ubi_device *ubi, int vol_id) } /** - * ubi_reboot_notifier - halt UBI transactions immediately prior to a reboot. - * @n: reboot notifier object - * @state: SYS_RESTART, SYS_HALT, or SYS_POWER_OFF - * @cmd: pointer to command string for RESTART2 - * - * This function stops the UBI background thread so that the flash device - * remains quiescent when Linux restarts the system. Any queued work will be - * discarded, but this function will block until do_work() finishes if an - * operation is already in progress. - * - * This function solves a real-life problem observed on NOR flashes when an - * PEB erase operation starts, then the system is rebooted before the erase is - * finishes, and the boot loader gets confused and dies. So we prefer to finish - * the ongoing operation before rebooting. - */ -static int ubi_reboot_notifier(struct notifier_block *n, unsigned long state, - void *cmd) -{ - struct ubi_device *ubi; - - ubi = container_of(n, struct ubi_device, reboot_notifier); - if (ubi->bgt_thread) - kthread_stop(ubi->bgt_thread); - ubi_sync(ubi->ubi_num); - return NOTIFY_DONE; -} - -/** * ubi_attach_mtd_dev - attach an MTD device. * @mtd: MTD device description object * @ubi_num: number to assign to the new UBI device @@ -1016,11 +993,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) wake_up_process(ubi->bgt_thread); spin_unlock(&ubi->wl_lock); - /* Flash device priority is 0 - UBI needs to shut down first */ - ubi->reboot_notifier.priority = 1; - ubi->reboot_notifier.notifier_call = ubi_reboot_notifier; - register_reboot_notifier(&ubi->reboot_notifier); - ubi_devices[ubi_num] = ubi; ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL); return ubi_num; @@ -1091,7 +1063,6 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) * Before freeing anything, we have to stop the background thread to * prevent it from doing anything on this device while we are freeing. */ - unregister_reboot_notifier(&ubi->reboot_notifier); if (ubi->bgt_thread) kthread_stop(ubi->bgt_thread); @@ -1241,9 +1212,24 @@ static int __init ubi_init(void) p->vid_hdr_offs); mutex_unlock(&ubi_devices_mutex); if (err < 0) { - put_mtd_device(mtd); ubi_err("cannot attach mtd%d", mtd->index); - goto out_detach; + put_mtd_device(mtd); + + /* + * Originally UBI stopped initializing on any error. + * However, later on it was found out that this + * behavior is not very good when UBI is compiled into + * the kernel and the MTD devices to attach are passed + * through the command line. Indeed, UBI failure + * stopped whole boot sequence. + * + * To fix this, we changed the behavior for the + * non-module case, but preserved the old behavior for + * the module case, just for compatibility. This is a + * little inconsistent, though. + */ + if (ubi_is_module()) + goto out_detach; } } diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 533b1a4b9af..4b979e34b15 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -64,9 +64,9 @@ * device, e.g., make @ubi->min_io_size = 512 in the example above? * * A: because when writing a sub-page, MTD still writes a full 2K page but the - * bytes which are no relevant to the sub-page are 0xFF. So, basically, writing - * 4x512 sub-pages is 4 times slower then writing one 2KiB NAND page. Thus, we - * prefer to use sub-pages only for EV and VID headers. + * bytes which are not relevant to the sub-page are 0xFF. So, basically, + * writing 4x512 sub-pages is 4 times slower than writing one 2KiB NAND page. + * Thus, we prefer to use sub-pages only for EC and VID headers. * * As it was noted above, the VID header may start at a non-aligned offset. * For example, in case of a 2KiB page NAND flash with a 512 bytes sub-page, diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 17f287decc3..69fa4ef03c5 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -488,7 +488,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_write); * * This function changes the contents of a logical eraseblock atomically. @buf * has to contain new logical eraseblock data, and @len - the length of the - * data, which has to be aligned. The length may be shorter then the logical + * data, which has to be aligned. The length may be shorter than the logical * eraseblock size, ant the logical eraseblock may be appended to more times * later on. This function guarantees that in case of an unclean reboot the old * contents is preserved. Returns zero in case of success and a negative error @@ -571,7 +571,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_erase); * * This function un-maps logical eraseblock @lnum and schedules the * corresponding physical eraseblock for erasure, so that it will eventually be - * physically erased in background. This operation is much faster then the + * physically erased in background. This operation is much faster than the * erase operation. * * Unlike erase, the un-map operation does not guarantee that the logical @@ -590,7 +590,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_erase); * * The main and obvious use-case of this function is when the contents of a * logical eraseblock has to be re-written. Then it is much more efficient to - * first un-map it, then write new data, rather then first erase it, then write + * first un-map it, then write new data, rather than first erase it, then write * new data. Note, once new data has been written to the logical eraseblock, * UBI guarantees that the old contents has gone forever. In other words, if an * unclean reboot happens after the logical eraseblock has been un-mapped and diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index dc5f688699d..aed19f33b8f 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -231,7 +231,7 @@ static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id, * case of success this function returns a positive value, in case of failure, a * negative error code is returned. The success return codes use the following * bits: - * o bit 0 is cleared: the first PEB (described by @seb) is newer then the + * o bit 0 is cleared: the first PEB (described by @seb) is newer than the * second PEB (described by @pnum and @vid_hdr); * o bit 0 is set: the second PEB is newer; * o bit 1 is cleared: no bit-flips were detected in the newer LEB; @@ -452,7 +452,7 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, if (cmp_res & 1) { /* - * This logical eraseblock is newer then the one + * This logical eraseblock is newer than the one * found earlier. */ err = validate_vid_hdr(vid_hdr, sv, pnum); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 5176d488651..a637f0283ad 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -350,7 +350,6 @@ struct ubi_wl_entry; * @bgt_thread: background thread description object * @thread_enabled: if the background thread is enabled * @bgt_name: background thread name - * @reboot_notifier: notifier to terminate background thread before rebooting * * @flash_size: underlying MTD device size (in bytes) * @peb_count: count of physical eraseblocks on the MTD device @@ -436,7 +435,6 @@ struct ubi_device { struct task_struct *bgt_thread; int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; - struct notifier_block reboot_notifier; /* I/O sub-system's stuff */ long long flash_size; diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index cd90ff3b76b..14c10bed94e 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -414,7 +414,7 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, * 0 contains more recent information. * * So the plan is to first check LEB 0. Then - * a. if LEB 0 is OK, it must be containing the most resent data; then + * a. if LEB 0 is OK, it must be containing the most recent data; then * we compare it with LEB 1, and if they are different, we copy LEB * 0 to LEB 1; * b. if LEB 0 is corrupted, but LEB 1 has to be OK, and we copy LEB 1 @@ -848,7 +848,7 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) goto out_free; /* - * Get sure that the scanning information is consistent to the + * Make sure that the scanning information is consistent to the * information stored in the volume table. */ err = check_scanning_info(ubi, si); diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index f64ddabd4ac..ee7b1d8fbb9 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -350,7 +350,7 @@ static void prot_queue_add(struct ubi_device *ubi, struct ubi_wl_entry *e) * @max: highest possible erase counter * * This function looks for a wear leveling entry with erase counter closest to - * @max and less then @max. + * @max and less than @max. */ static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max) { diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h index bc72d6e4919..13343e88499 100644 --- a/drivers/net/mlx4/mlx4.h +++ b/drivers/net/mlx4/mlx4.h @@ -40,6 +40,7 @@ #include <linux/mutex.h> #include <linux/radix-tree.h> #include <linux/timer.h> +#include <linux/semaphore.h> #include <linux/workqueue.h> #include <linux/mlx4/device.h> diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index c61a61f177b..6ce6ce1df6d 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -2560,7 +2560,8 @@ netxen_sysfs_validate_crb(struct netxen_adapter *adapter, } static ssize_t -netxen_sysfs_read_crb(struct kobject *kobj, struct bin_attribute *attr, +netxen_sysfs_read_crb(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { struct device *dev = container_of(kobj, struct device, kobj); @@ -2587,7 +2588,8 @@ netxen_sysfs_read_crb(struct kobject *kobj, struct bin_attribute *attr, } static ssize_t -netxen_sysfs_write_crb(struct kobject *kobj, struct bin_attribute *attr, +netxen_sysfs_write_crb(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { struct device *dev = container_of(kobj, struct device, kobj); @@ -2627,7 +2629,8 @@ netxen_sysfs_validate_mem(struct netxen_adapter *adapter, } static ssize_t -netxen_sysfs_read_mem(struct kobject *kobj, struct bin_attribute *attr, +netxen_sysfs_read_mem(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { struct device *dev = container_of(kobj, struct device, kobj); @@ -2647,7 +2650,7 @@ netxen_sysfs_read_mem(struct kobject *kobj, struct bin_attribute *attr, return size; } -static ssize_t netxen_sysfs_write_mem(struct kobject *kobj, +static ssize_t netxen_sysfs_write_mem(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c index 1003eb76fda..23ea9caa526 100644 --- a/drivers/net/qlcnic/qlcnic_main.c +++ b/drivers/net/qlcnic/qlcnic_main.c @@ -2464,7 +2464,8 @@ qlcnic_sysfs_validate_crb(struct qlcnic_adapter *adapter, } static ssize_t -qlcnic_sysfs_read_crb(struct kobject *kobj, struct bin_attribute *attr, +qlcnic_sysfs_read_crb(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { struct device *dev = container_of(kobj, struct device, kobj); @@ -2488,7 +2489,8 @@ qlcnic_sysfs_read_crb(struct kobject *kobj, struct bin_attribute *attr, } static ssize_t -qlcnic_sysfs_write_crb(struct kobject *kobj, struct bin_attribute *attr, +qlcnic_sysfs_write_crb(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { struct device *dev = container_of(kobj, struct device, kobj); @@ -2525,7 +2527,8 @@ qlcnic_sysfs_validate_mem(struct qlcnic_adapter *adapter, } static ssize_t -qlcnic_sysfs_read_mem(struct kobject *kobj, struct bin_attribute *attr, +qlcnic_sysfs_read_mem(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { struct device *dev = container_of(kobj, struct device, kobj); @@ -2546,7 +2549,8 @@ qlcnic_sysfs_read_mem(struct kobject *kobj, struct bin_attribute *attr, } static ssize_t -qlcnic_sysfs_write_mem(struct kobject *kobj, struct bin_attribute *attr, +qlcnic_sysfs_write_mem(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t offset, size_t size) { struct device *dev = container_of(kobj, struct device, kobj); diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 6ecbfb27db9..e525263210e 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -108,7 +108,7 @@ static int ibm_set_attention_status(struct hotplug_slot *slot, u8 status); static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status); static void ibm_handle_events(acpi_handle handle, u32 event, void *context); static int ibm_get_table_from_acpi(char **bufp); -static ssize_t ibm_read_apci_table(struct kobject *kobj, +static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t size); static acpi_status __init ibm_find_acpi_device(acpi_handle handle, @@ -351,6 +351,7 @@ read_table_done: /** * ibm_read_apci_table - callback for the sysfs apci_table file + * @filp: the open sysfs file * @kobj: the kobject this binary attribute is a part of * @bin_attr: struct bin_attribute for this file * @buffer: the kernel space buffer to fill @@ -364,7 +365,7 @@ read_table_done: * things get really tricky here... * our solution is to only allow reading the table in all at once. */ -static ssize_t ibm_read_apci_table(struct kobject *kobj, +static ssize_t ibm_read_apci_table(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t size) { diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index fad93983bfe..6309c5a2528 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -21,6 +21,7 @@ #include <linux/stat.h> #include <linux/topology.h> #include <linux/mm.h> +#include <linux/fs.h> #include <linux/capability.h> #include <linux/pci-aspm.h> #include <linux/slab.h> @@ -357,7 +358,8 @@ boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf) struct device_attribute vga_attr = __ATTR_RO(boot_vga); static ssize_t -pci_read_config(struct kobject *kobj, struct bin_attribute *bin_attr, +pci_read_config(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); @@ -366,7 +368,7 @@ pci_read_config(struct kobject *kobj, struct bin_attribute *bin_attr, u8 *data = (u8*) buf; /* Several chips lock up trying to read undefined config space */ - if (capable(CAP_SYS_ADMIN)) { + if (cap_raised(filp->f_cred->cap_effective, CAP_SYS_ADMIN)) { size = dev->cfg_size; } else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) { size = 128; @@ -430,7 +432,8 @@ pci_read_config(struct kobject *kobj, struct bin_attribute *bin_attr, } static ssize_t -pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr, +pci_write_config(struct file* filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); @@ -487,7 +490,8 @@ pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr, } static ssize_t -read_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr, +read_vpd_attr(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_dev *dev = @@ -502,7 +506,8 @@ read_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr, } static ssize_t -write_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr, +write_vpd_attr(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_dev *dev = @@ -519,6 +524,7 @@ write_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr, #ifdef HAVE_PCI_LEGACY /** * pci_read_legacy_io - read byte(s) from legacy I/O port space + * @filp: open sysfs file * @kobj: kobject corresponding to file to read from * @bin_attr: struct bin_attribute for this file * @buf: buffer to store results @@ -529,7 +535,8 @@ write_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr, * callback routine (pci_legacy_read). */ static ssize_t -pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, +pci_read_legacy_io(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_bus *bus = to_pci_bus(container_of(kobj, @@ -545,6 +552,7 @@ pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, /** * pci_write_legacy_io - write byte(s) to legacy I/O port space + * @filp: open sysfs file * @kobj: kobject corresponding to file to read from * @bin_attr: struct bin_attribute for this file * @buf: buffer containing value to be written @@ -555,7 +563,8 @@ pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, * callback routine (pci_legacy_write). */ static ssize_t -pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, +pci_write_legacy_io(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_bus *bus = to_pci_bus(container_of(kobj, @@ -570,6 +579,7 @@ pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, /** * pci_mmap_legacy_mem - map legacy PCI memory into user memory space + * @filp: open sysfs file * @kobj: kobject corresponding to device to be mapped * @attr: struct bin_attribute for this file * @vma: struct vm_area_struct passed to mmap @@ -579,7 +589,8 @@ pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, * memory space. */ static int -pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, +pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, struct vm_area_struct *vma) { struct pci_bus *bus = to_pci_bus(container_of(kobj, @@ -591,6 +602,7 @@ pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, /** * pci_mmap_legacy_io - map legacy PCI IO into user memory space + * @filp: open sysfs file * @kobj: kobject corresponding to device to be mapped * @attr: struct bin_attribute for this file * @vma: struct vm_area_struct passed to mmap @@ -600,7 +612,8 @@ pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, * memory space. Returns -ENOSYS if the operation isn't supported */ static int -pci_mmap_legacy_io(struct kobject *kobj, struct bin_attribute *attr, +pci_mmap_legacy_io(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, struct vm_area_struct *vma) { struct pci_bus *bus = to_pci_bus(container_of(kobj, @@ -750,14 +763,16 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, } static int -pci_mmap_resource_uc(struct kobject *kobj, struct bin_attribute *attr, +pci_mmap_resource_uc(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, struct vm_area_struct *vma) { return pci_mmap_resource(kobj, attr, vma, 0); } static int -pci_mmap_resource_wc(struct kobject *kobj, struct bin_attribute *attr, +pci_mmap_resource_wc(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, struct vm_area_struct *vma) { return pci_mmap_resource(kobj, attr, vma, 1); @@ -861,6 +876,7 @@ void __weak pci_remove_resource_files(struct pci_dev *dev) { return; } /** * pci_write_rom - used to enable access to the PCI ROM display + * @filp: sysfs file * @kobj: kernel object handle * @bin_attr: struct bin_attribute for this file * @buf: user input @@ -870,7 +886,8 @@ void __weak pci_remove_resource_files(struct pci_dev *dev) { return; } * writing anything except 0 enables it */ static ssize_t -pci_write_rom(struct kobject *kobj, struct bin_attribute *bin_attr, +pci_write_rom(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj)); @@ -885,6 +902,7 @@ pci_write_rom(struct kobject *kobj, struct bin_attribute *bin_attr, /** * pci_read_rom - read a PCI ROM + * @filp: sysfs file * @kobj: kernel object handle * @bin_attr: struct bin_attribute for this file * @buf: where to put the data we read from the ROM @@ -895,7 +913,8 @@ pci_write_rom(struct kobject *kobj, struct bin_attribute *bin_attr, * device corresponding to @kobj. */ static ssize_t -pci_read_rom(struct kobject *kobj, struct bin_attribute *bin_attr, +pci_read_rom(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj)); diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 60d428be0b0..8844bc3e311 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -1531,7 +1531,7 @@ static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, } -static ssize_t pccard_show_cis(struct kobject *kobj, +static ssize_t pccard_show_cis(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -1562,7 +1562,7 @@ static ssize_t pccard_show_cis(struct kobject *kobj, } -static ssize_t pccard_store_cis(struct kobject *kobj, +static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c index 8fefe5a7355..baefcf1cffc 100644 --- a/drivers/power/olpc_battery.c +++ b/drivers/power/olpc_battery.c @@ -354,7 +354,7 @@ static enum power_supply_property olpc_bat_props[] = { #define EEPROM_END 0x80 #define EEPROM_SIZE (EEPROM_END - EEPROM_START) -static ssize_t olpc_bat_eeprom_read(struct kobject *kobj, +static ssize_t olpc_bat_eeprom_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { uint8_t ec_byte; diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c index ba742e82c57..00b47565835 100644 --- a/drivers/rapidio/rio-sysfs.c +++ b/drivers/rapidio/rio-sysfs.c @@ -68,7 +68,8 @@ struct device_attribute rio_dev_attrs[] = { }; static ssize_t -rio_read_config(struct kobject *kobj, struct bin_attribute *bin_attr, +rio_read_config(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct rio_dev *dev = @@ -139,7 +140,8 @@ rio_read_config(struct kobject *kobj, struct bin_attribute *bin_attr, } static ssize_t -rio_write_config(struct kobject *kobj, struct bin_attribute *bin_attr, +rio_write_config(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct rio_dev *dev = diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index e9aa814ddd2..ece4dbddc0e 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -519,7 +519,8 @@ static const struct rtc_class_ops cmos_rtc_ops = { #define NVRAM_OFFSET (RTC_REG_D + 1) static ssize_t -cmos_nvram_read(struct kobject *kobj, struct bin_attribute *attr, +cmos_nvram_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { int retval; @@ -547,7 +548,8 @@ cmos_nvram_read(struct kobject *kobj, struct bin_attribute *attr, } static ssize_t -cmos_nvram_write(struct kobject *kobj, struct bin_attribute *attr, +cmos_nvram_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct cmos_rtc *cmos; diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index 7836c9cec55..48da85e97ca 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c @@ -542,7 +542,8 @@ static void msg_init(struct spi_message *m, struct spi_transfer *x, } static ssize_t -ds1305_nvram_read(struct kobject *kobj, struct bin_attribute *attr, +ds1305_nvram_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct spi_device *spi; @@ -572,7 +573,8 @@ ds1305_nvram_read(struct kobject *kobj, struct bin_attribute *attr, } static ssize_t -ds1305_nvram_write(struct kobject *kobj, struct bin_attribute *attr, +ds1305_nvram_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct spi_device *spi; diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index c4ec5c158aa..de033b7ac21 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -556,7 +556,8 @@ static const struct rtc_class_ops ds13xx_rtc_ops = { #define NVRAM_SIZE 56 static ssize_t -ds1307_nvram_read(struct kobject *kobj, struct bin_attribute *attr, +ds1307_nvram_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client; @@ -580,7 +581,8 @@ ds1307_nvram_read(struct kobject *kobj, struct bin_attribute *attr, } static ssize_t -ds1307_nvram_write(struct kobject *kobj, struct bin_attribute *attr, +ds1307_nvram_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct i2c_client *client; diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c index 06b8566c453..37268e97de4 100644 --- a/drivers/rtc/rtc-ds1511.c +++ b/drivers/rtc/rtc-ds1511.c @@ -423,8 +423,9 @@ static const struct rtc_class_ops ds1511_rtc_ops = { }; static ssize_t -ds1511_nvram_read(struct kobject *kobj, struct bin_attribute *ba, - char *buf, loff_t pos, size_t size) +ds1511_nvram_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *ba, + char *buf, loff_t pos, size_t size) { ssize_t count; @@ -452,8 +453,9 @@ ds1511_nvram_read(struct kobject *kobj, struct bin_attribute *ba, } static ssize_t -ds1511_nvram_write(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buf, loff_t pos, size_t size) +ds1511_nvram_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) { ssize_t count; diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index 244f9994bcb..ff432e2ca27 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -252,7 +252,7 @@ static const struct rtc_class_ops ds1553_rtc_ops = { .update_irq_enable = ds1553_rtc_update_irq_enable, }; -static ssize_t ds1553_nvram_read(struct kobject *kobj, +static ssize_t ds1553_nvram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { @@ -267,7 +267,7 @@ static ssize_t ds1553_nvram_read(struct kobject *kobj, return count; } -static ssize_t ds1553_nvram_write(struct kobject *kobj, +static ssize_t ds1553_nvram_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index 2b4b0bc42d6..042630c90dd 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -128,7 +128,7 @@ static const struct rtc_class_ops ds1742_rtc_ops = { .set_time = ds1742_rtc_set_time, }; -static ssize_t ds1742_nvram_read(struct kobject *kobj, +static ssize_t ds1742_nvram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { @@ -143,7 +143,7 @@ static ssize_t ds1742_nvram_read(struct kobject *kobj, return count; } -static ssize_t ds1742_nvram_write(struct kobject *kobj, +static ssize_t ds1742_nvram_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index 365ff3ac234..be8359fdb65 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -343,7 +343,7 @@ static const struct rtc_class_ops m48t02_rtc_ops = { .set_time = m48t59_rtc_set_time, }; -static ssize_t m48t59_nvram_read(struct kobject *kobj, +static ssize_t m48t59_nvram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { @@ -363,7 +363,7 @@ static ssize_t m48t59_nvram_read(struct kobject *kobj, return cnt; } -static ssize_t m48t59_nvram_write(struct kobject *kobj, +static ssize_t m48t59_nvram_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c index b53a00198db..3b943673cd3 100644 --- a/drivers/rtc/rtc-stk17ta8.c +++ b/drivers/rtc/rtc-stk17ta8.c @@ -244,7 +244,7 @@ static const struct rtc_class_ops stk17ta8_rtc_ops = { .alarm_irq_enable = stk17ta8_rtc_alarm_irq_enable, }; -static ssize_t stk17ta8_nvram_read(struct kobject *kobj, +static ssize_t stk17ta8_nvram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t pos, size_t size) { @@ -259,7 +259,7 @@ static ssize_t stk17ta8_nvram_read(struct kobject *kobj, return count; } -static ssize_t stk17ta8_nvram_write(struct kobject *kobj, +static ssize_t stk17ta8_nvram_write(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t pos, size_t size) { diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c index 20bfc64a15c..ec6313d1535 100644 --- a/drivers/rtc/rtc-tx4939.c +++ b/drivers/rtc/rtc-tx4939.c @@ -188,7 +188,7 @@ static const struct rtc_class_ops tx4939_rtc_ops = { .alarm_irq_enable = tx4939_rtc_alarm_irq_enable, }; -static ssize_t tx4939_rtc_nvram_read(struct kobject *kobj, +static ssize_t tx4939_rtc_nvram_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { @@ -207,7 +207,7 @@ static ssize_t tx4939_rtc_nvram_read(struct kobject *kobj, return count; } -static ssize_t tx4939_rtc_nvram_write(struct kobject *kobj, +static ssize_t tx4939_rtc_nvram_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) { diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 1d16189f2f2..6c9fa15aac7 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -135,7 +135,8 @@ static int s390_vary_chpid(struct chp_id chpid, int on) /* * Channel measurement related functions */ -static ssize_t chp_measurement_chars_read(struct kobject *kobj, +static ssize_t chp_measurement_chars_read(struct file *filp, + struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -182,7 +183,7 @@ static void chp_measurement_copy_block(struct cmg_entry *buf, } while (reference_buf.values[0] != buf->values[0]); } -static ssize_t chp_measurement_read(struct kobject *kobj, +static ssize_t chp_measurement_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 1e6183a86ce..e331df2122f 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -425,7 +425,8 @@ int zfcp_status_read_refill(struct zfcp_adapter *adapter) { while (atomic_read(&adapter->stat_miss) > 0) if (zfcp_fsf_status_read(adapter->qdio)) { - if (atomic_read(&adapter->stat_miss) >= 16) { + if (atomic_read(&adapter->stat_miss) >= + adapter->stat_read_buf_num) { zfcp_erp_adapter_reopen(adapter, 0, "axsref1", NULL); return 1; @@ -545,6 +546,10 @@ struct zfcp_adapter *zfcp_adapter_enqueue(struct ccw_device *ccw_device) &zfcp_sysfs_adapter_attrs)) goto failed; + /* report size limit per scatter-gather segment */ + adapter->dma_parms.max_segment_size = ZFCP_QDIO_SBALE_LEN; + adapter->ccw_device->dev.dma_parms = &adapter->dma_parms; + if (!zfcp_adapter_scsi_register(adapter)) return adapter; diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h index 7131c7db1f0..9fa1b064893 100644 --- a/drivers/s390/scsi/zfcp_def.h +++ b/drivers/s390/scsi/zfcp_def.h @@ -44,23 +44,6 @@ struct zfcp_reqlist; /********************* SCSI SPECIFIC DEFINES *********************************/ #define ZFCP_SCSI_ER_TIMEOUT (10*HZ) -/********************* CIO/QDIO SPECIFIC DEFINES *****************************/ - -/* DMQ bug workaround: don't use last SBALE */ -#define ZFCP_MAX_SBALES_PER_SBAL (QDIO_MAX_ELEMENTS_PER_BUFFER - 1) - -/* index of last SBALE (with respect to DMQ bug workaround) */ -#define ZFCP_LAST_SBALE_PER_SBAL (ZFCP_MAX_SBALES_PER_SBAL - 1) - -/* max. number of (data buffer) SBALEs in largest SBAL chain */ -#define ZFCP_MAX_SBALES_PER_REQ \ - (FSF_MAX_SBALS_PER_REQ * ZFCP_MAX_SBALES_PER_SBAL - 2) - /* request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */ - -#define ZFCP_MAX_SECTORS (ZFCP_MAX_SBALES_PER_REQ * 8) - /* max. number of (data buffer) SBALEs in largest SBAL chain - multiplied with number of sectors per 4k block */ - /********************* FSF SPECIFIC DEFINES *********************************/ /* ATTENTION: value must not be used by hardware */ @@ -181,6 +164,7 @@ struct zfcp_adapter { stack abort/command completion races */ atomic_t stat_miss; /* # missing status reads*/ + unsigned int stat_read_buf_num; struct work_struct stat_work; atomic_t status; /* status of this adapter */ struct list_head erp_ready_head; /* error recovery for this @@ -205,6 +189,7 @@ struct zfcp_adapter { struct work_struct scan_work; struct service_level service_level; struct workqueue_struct *work_queue; + struct device_dma_parameters dma_parms; }; struct zfcp_port { diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 0be5e7ea282..e3dbeda9717 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -714,7 +714,7 @@ static int zfcp_erp_adapter_strategy_open_fsf(struct zfcp_erp_action *act) if (zfcp_erp_adapter_strategy_open_fsf_xport(act) == ZFCP_ERP_FAILED) return ZFCP_ERP_FAILED; - atomic_set(&act->adapter->stat_miss, 16); + atomic_set(&act->adapter->stat_miss, act->adapter->stat_read_buf_num); if (zfcp_status_read_refill(act->adapter)) return ZFCP_ERP_FAILED; diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 8786a79c7f8..48a8f93b72f 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -3,7 +3,7 @@ * * External function declarations. * - * Copyright IBM Corporation 2002, 2009 + * Copyright IBM Corporation 2002, 2010 */ #ifndef ZFCP_EXT_H @@ -143,9 +143,9 @@ extern void zfcp_fsf_reqid_check(struct zfcp_qdio *, int); /* zfcp_qdio.c */ extern int zfcp_qdio_setup(struct zfcp_adapter *); extern void zfcp_qdio_destroy(struct zfcp_qdio *); +extern int zfcp_qdio_sbal_get(struct zfcp_qdio *); extern int zfcp_qdio_send(struct zfcp_qdio *, struct zfcp_qdio_req *); -extern int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *, - struct zfcp_qdio_req *, unsigned long, +extern int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *, struct zfcp_qdio_req *, struct scatterlist *, int); extern int zfcp_qdio_open(struct zfcp_qdio *); extern void zfcp_qdio_close(struct zfcp_qdio *); diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 2a1cbb74b99..6f8ab43a485 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -400,7 +400,7 @@ static int zfcp_fc_adisc(struct zfcp_port *port) struct zfcp_adapter *adapter = port->adapter; int ret; - adisc = kmem_cache_alloc(zfcp_data.adisc_cache, GFP_ATOMIC); + adisc = kmem_cache_zalloc(zfcp_data.adisc_cache, GFP_ATOMIC); if (!adisc) return -ENOMEM; @@ -493,7 +493,7 @@ static struct zfcp_fc_gpn_ft *zfcp_alloc_sg_env(int buf_num) if (!gpn_ft) return NULL; - req = kmem_cache_alloc(zfcp_data.gpn_ft_cache, GFP_KERNEL); + req = kmem_cache_zalloc(zfcp_data.gpn_ft_cache, GFP_KERNEL); if (!req) { kfree(gpn_ft); gpn_ft = NULL; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index b3b1d2f7939..9ac6a6e4a60 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -496,6 +496,7 @@ static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req) adapter->hydra_version = bottom->adapter_type; adapter->timer_ticks = bottom->timer_interval; + adapter->stat_read_buf_num = max(bottom->status_read_buf_num, (u16)16); if (fc_host_permanent_port_name(shost) == -1) fc_host_permanent_port_name(shost) = fc_host_port_name(shost); @@ -640,37 +641,6 @@ static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req) } } -static int zfcp_fsf_sbal_check(struct zfcp_qdio *qdio) -{ - struct zfcp_qdio_queue *req_q = &qdio->req_q; - - spin_lock_bh(&qdio->req_q_lock); - if (atomic_read(&req_q->count)) - return 1; - spin_unlock_bh(&qdio->req_q_lock); - return 0; -} - -static int zfcp_fsf_req_sbal_get(struct zfcp_qdio *qdio) -{ - struct zfcp_adapter *adapter = qdio->adapter; - long ret; - - spin_unlock_bh(&qdio->req_q_lock); - ret = wait_event_interruptible_timeout(qdio->req_q_wq, - zfcp_fsf_sbal_check(qdio), 5 * HZ); - if (ret > 0) - return 0; - if (!ret) { - atomic_inc(&qdio->req_q_full); - /* assume hanging outbound queue, try queue recovery */ - zfcp_erp_adapter_reopen(adapter, 0, "fsrsg_1", NULL); - } - - spin_lock_bh(&qdio->req_q_lock); - return -EIO; -} - static struct zfcp_fsf_req *zfcp_fsf_alloc(mempool_t *pool) { struct zfcp_fsf_req *req; @@ -705,10 +675,9 @@ static struct fsf_qtcb *zfcp_qtcb_alloc(mempool_t *pool) } static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_qdio *qdio, - u32 fsf_cmd, mempool_t *pool) + u32 fsf_cmd, u32 sbtype, + mempool_t *pool) { - struct qdio_buffer_element *sbale; - struct zfcp_qdio_queue *req_q = &qdio->req_q; struct zfcp_adapter *adapter = qdio->adapter; struct zfcp_fsf_req *req = zfcp_fsf_alloc(pool); @@ -725,14 +694,6 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_qdio *qdio, req->adapter = adapter; req->fsf_command = fsf_cmd; req->req_id = adapter->req_no; - req->qdio_req.sbal_number = 1; - req->qdio_req.sbal_first = req_q->first; - req->qdio_req.sbal_last = req_q->first; - req->qdio_req.sbale_curr = 1; - - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].addr = (void *) req->req_id; - sbale[0].flags |= SBAL_FLAGS0_COMMAND; if (likely(fsf_cmd != FSF_QTCB_UNSOLICITED_STATUS)) { if (likely(pool)) @@ -753,10 +714,11 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_qdio *qdio, req->qtcb->prefix.qtcb_version = FSF_QTCB_CURRENT_VERSION; req->qtcb->header.req_handle = req->req_id; req->qtcb->header.fsf_command = req->fsf_command; - sbale[1].addr = (void *) req->qtcb; - sbale[1].length = sizeof(struct fsf_qtcb); } + zfcp_qdio_req_init(adapter->qdio, &req->qdio_req, req->req_id, sbtype, + req->qtcb, sizeof(struct fsf_qtcb)); + if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)) { zfcp_fsf_req_free(req); return ERR_PTR(-EIO); @@ -803,24 +765,19 @@ int zfcp_fsf_status_read(struct zfcp_qdio *qdio) struct zfcp_adapter *adapter = qdio->adapter; struct zfcp_fsf_req *req; struct fsf_status_read_buffer *sr_buf; - struct qdio_buffer_element *sbale; int retval = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; - req = zfcp_fsf_req_create(qdio, FSF_QTCB_UNSOLICITED_STATUS, + req = zfcp_fsf_req_create(qdio, FSF_QTCB_UNSOLICITED_STATUS, 0, adapter->pool.status_read_req); if (IS_ERR(req)) { retval = PTR_ERR(req); goto out; } - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY; - req->qdio_req.sbale_curr = 2; - sr_buf = mempool_alloc(adapter->pool.status_read_data, GFP_ATOMIC); if (!sr_buf) { retval = -ENOMEM; @@ -828,9 +785,9 @@ int zfcp_fsf_status_read(struct zfcp_qdio *qdio) } memset(sr_buf, 0, sizeof(*sr_buf)); req->data = sr_buf; - sbale = zfcp_qdio_sbale_curr(qdio, &req->qdio_req); - sbale->addr = (void *) sr_buf; - sbale->length = sizeof(*sr_buf); + + zfcp_qdio_fill_next(qdio, &req->qdio_req, sr_buf, sizeof(*sr_buf)); + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); retval = zfcp_fsf_req_send(req); if (retval) @@ -907,14 +864,14 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req) struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long old_req_id, struct zfcp_unit *unit) { - struct qdio_buffer_element *sbale; struct zfcp_fsf_req *req = NULL; struct zfcp_qdio *qdio = unit->port->adapter->qdio; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_ABORT_FCP_CMND, + SBAL_FLAGS0_TYPE_READ, qdio->adapter->pool.scsi_abort); if (IS_ERR(req)) { req = NULL; @@ -925,9 +882,7 @@ struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long old_req_id, ZFCP_STATUS_COMMON_UNBLOCKED))) goto out_error_free; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->data = unit; req->handler = zfcp_fsf_abort_fcp_command_handler; @@ -996,21 +951,14 @@ skip_fsfstatus: ct->handler(ct->handler_data); } -static void zfcp_fsf_setup_ct_els_unchained(struct qdio_buffer_element *sbale, +static void zfcp_fsf_setup_ct_els_unchained(struct zfcp_qdio *qdio, + struct zfcp_qdio_req *q_req, struct scatterlist *sg_req, struct scatterlist *sg_resp) { - sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ; - sbale[2].addr = sg_virt(sg_req); - sbale[2].length = sg_req->length; - sbale[3].addr = sg_virt(sg_resp); - sbale[3].length = sg_resp->length; - sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY; -} - -static int zfcp_fsf_one_sbal(struct scatterlist *sg) -{ - return sg_is_last(sg) && sg->length <= PAGE_SIZE; + zfcp_qdio_fill_next(qdio, q_req, sg_virt(sg_req), sg_req->length); + zfcp_qdio_fill_next(qdio, q_req, sg_virt(sg_resp), sg_resp->length); + zfcp_qdio_set_sbale_last(qdio, q_req); } static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, @@ -1019,35 +967,34 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req, int max_sbals) { struct zfcp_adapter *adapter = req->adapter; - struct qdio_buffer_element *sbale = zfcp_qdio_sbale_req(adapter->qdio, - &req->qdio_req); u32 feat = adapter->adapter_features; int bytes; if (!(feat & FSF_FEATURE_ELS_CT_CHAINED_SBALS)) { - if (!zfcp_fsf_one_sbal(sg_req) || !zfcp_fsf_one_sbal(sg_resp)) + if (!zfcp_qdio_sg_one_sbale(sg_req) || + !zfcp_qdio_sg_one_sbale(sg_resp)) return -EOPNOTSUPP; - zfcp_fsf_setup_ct_els_unchained(sbale, sg_req, sg_resp); + zfcp_fsf_setup_ct_els_unchained(adapter->qdio, &req->qdio_req, + sg_req, sg_resp); return 0; } /* use single, unchained SBAL if it can hold the request */ - if (zfcp_fsf_one_sbal(sg_req) && zfcp_fsf_one_sbal(sg_resp)) { - zfcp_fsf_setup_ct_els_unchained(sbale, sg_req, sg_resp); + if (zfcp_qdio_sg_one_sbale(sg_req) || zfcp_qdio_sg_one_sbale(sg_resp)) { + zfcp_fsf_setup_ct_els_unchained(adapter->qdio, &req->qdio_req, + sg_req, sg_resp); return 0; } bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->qdio_req, - SBAL_FLAGS0_TYPE_WRITE_READ, sg_req, max_sbals); if (bytes <= 0) return -EIO; req->qtcb->bottom.support.req_buf_length = bytes; - req->qdio_req.sbale_curr = ZFCP_LAST_SBALE_PER_SBAL; + zfcp_qdio_skip_to_last_sbale(&req->qdio_req); bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->qdio_req, - SBAL_FLAGS0_TYPE_WRITE_READ, sg_resp, max_sbals); req->qtcb->bottom.support.resp_buf_length = bytes; if (bytes <= 0) @@ -1091,10 +1038,11 @@ int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port, int ret = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; - req = zfcp_fsf_req_create(qdio, FSF_QTCB_SEND_GENERIC, pool); + req = zfcp_fsf_req_create(qdio, FSF_QTCB_SEND_GENERIC, + SBAL_FLAGS0_TYPE_WRITE_READ, pool); if (IS_ERR(req)) { ret = PTR_ERR(req); @@ -1103,7 +1051,7 @@ int zfcp_fsf_send_ct(struct zfcp_fc_wka_port *wka_port, req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; ret = zfcp_fsf_setup_ct_els(req, ct->req, ct->resp, - FSF_MAX_SBALS_PER_REQ, timeout); + ZFCP_FSF_MAX_SBALS_PER_REQ, timeout); if (ret) goto failed_send; @@ -1187,10 +1135,11 @@ int zfcp_fsf_send_els(struct zfcp_adapter *adapter, u32 d_id, int ret = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; - req = zfcp_fsf_req_create(qdio, FSF_QTCB_SEND_ELS, NULL); + req = zfcp_fsf_req_create(qdio, FSF_QTCB_SEND_ELS, + SBAL_FLAGS0_TYPE_WRITE_READ, NULL); if (IS_ERR(req)) { ret = PTR_ERR(req); @@ -1224,16 +1173,16 @@ out: int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action) { - struct qdio_buffer_element *sbale; struct zfcp_fsf_req *req; struct zfcp_qdio *qdio = erp_action->adapter->qdio; int retval = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_CONFIG_DATA, + SBAL_FLAGS0_TYPE_READ, qdio->adapter->pool.erp_req); if (IS_ERR(req)) { @@ -1242,9 +1191,7 @@ int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->qtcb->bottom.config.feature_selection = FSF_FEATURE_CFDC | @@ -1269,24 +1216,22 @@ out: int zfcp_fsf_exchange_config_data_sync(struct zfcp_qdio *qdio, struct fsf_qtcb_bottom_config *data) { - struct qdio_buffer_element *sbale; struct zfcp_fsf_req *req = NULL; int retval = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out_unlock; - req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_CONFIG_DATA, NULL); + req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_CONFIG_DATA, + SBAL_FLAGS0_TYPE_READ, NULL); if (IS_ERR(req)) { retval = PTR_ERR(req); goto out_unlock; } - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->handler = zfcp_fsf_exchange_config_data_handler; req->qtcb->bottom.config.feature_selection = @@ -1320,7 +1265,6 @@ out_unlock: int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action) { struct zfcp_qdio *qdio = erp_action->adapter->qdio; - struct qdio_buffer_element *sbale; struct zfcp_fsf_req *req; int retval = -EIO; @@ -1328,10 +1272,11 @@ int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action) return -EOPNOTSUPP; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_PORT_DATA, + SBAL_FLAGS0_TYPE_READ, qdio->adapter->pool.erp_req); if (IS_ERR(req)) { @@ -1340,9 +1285,7 @@ int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->handler = zfcp_fsf_exchange_port_data_handler; req->erp_action = erp_action; @@ -1368,7 +1311,6 @@ out: int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *qdio, struct fsf_qtcb_bottom_port *data) { - struct qdio_buffer_element *sbale; struct zfcp_fsf_req *req = NULL; int retval = -EIO; @@ -1376,10 +1318,11 @@ int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *qdio, return -EOPNOTSUPP; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out_unlock; - req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_PORT_DATA, NULL); + req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_PORT_DATA, + SBAL_FLAGS0_TYPE_READ, NULL); if (IS_ERR(req)) { retval = PTR_ERR(req); @@ -1389,9 +1332,7 @@ int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *qdio, if (data) req->data = data; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->handler = zfcp_fsf_exchange_port_data_handler; zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT); @@ -1485,17 +1426,17 @@ out: */ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action) { - struct qdio_buffer_element *sbale; struct zfcp_qdio *qdio = erp_action->adapter->qdio; struct zfcp_port *port = erp_action->port; struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_OPEN_PORT_WITH_DID, + SBAL_FLAGS0_TYPE_READ, qdio->adapter->pool.erp_req); if (IS_ERR(req)) { @@ -1504,9 +1445,7 @@ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->handler = zfcp_fsf_open_port_handler; hton24(req->qtcb->bottom.support.d_id, port->d_id); @@ -1556,16 +1495,16 @@ static void zfcp_fsf_close_port_handler(struct zfcp_fsf_req *req) */ int zfcp_fsf_close_port(struct zfcp_erp_action *erp_action) { - struct qdio_buffer_element *sbale; struct zfcp_qdio *qdio = erp_action->adapter->qdio; struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_PORT, + SBAL_FLAGS0_TYPE_READ, qdio->adapter->pool.erp_req); if (IS_ERR(req)) { @@ -1574,9 +1513,7 @@ int zfcp_fsf_close_port(struct zfcp_erp_action *erp_action) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->handler = zfcp_fsf_close_port_handler; req->data = erp_action->port; @@ -1633,16 +1570,16 @@ out: */ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) { - struct qdio_buffer_element *sbale; struct zfcp_qdio *qdio = wka_port->adapter->qdio; struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_OPEN_PORT_WITH_DID, + SBAL_FLAGS0_TYPE_READ, qdio->adapter->pool.erp_req); if (unlikely(IS_ERR(req))) { @@ -1651,9 +1588,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_fc_wka_port *wka_port) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->handler = zfcp_fsf_open_wka_port_handler; hton24(req->qtcb->bottom.support.d_id, wka_port->d_id); @@ -1688,16 +1623,16 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req) */ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) { - struct qdio_buffer_element *sbale; struct zfcp_qdio *qdio = wka_port->adapter->qdio; struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_PORT, + SBAL_FLAGS0_TYPE_READ, qdio->adapter->pool.erp_req); if (unlikely(IS_ERR(req))) { @@ -1706,9 +1641,7 @@ int zfcp_fsf_close_wka_port(struct zfcp_fc_wka_port *wka_port) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->handler = zfcp_fsf_close_wka_port_handler; req->data = wka_port; @@ -1782,16 +1715,16 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req) */ int zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action) { - struct qdio_buffer_element *sbale; struct zfcp_qdio *qdio = erp_action->adapter->qdio; struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_PHYSICAL_PORT, + SBAL_FLAGS0_TYPE_READ, qdio->adapter->pool.erp_req); if (IS_ERR(req)) { @@ -1800,9 +1733,7 @@ int zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->data = erp_action->port; req->qtcb->header.port_handle = erp_action->port->handle; @@ -1954,17 +1885,17 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req) */ int zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action) { - struct qdio_buffer_element *sbale; struct zfcp_adapter *adapter = erp_action->adapter; struct zfcp_qdio *qdio = adapter->qdio; struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_OPEN_LUN, + SBAL_FLAGS0_TYPE_READ, adapter->pool.erp_req); if (IS_ERR(req)) { @@ -1973,9 +1904,7 @@ int zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->qtcb->header.port_handle = erp_action->port->handle; req->qtcb->bottom.support.fcp_lun = erp_action->unit->fcp_lun; @@ -2041,16 +1970,16 @@ static void zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *req) */ int zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action) { - struct qdio_buffer_element *sbale; struct zfcp_qdio *qdio = erp_action->adapter->qdio; struct zfcp_fsf_req *req; int retval = -EIO; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_LUN, + SBAL_FLAGS0_TYPE_READ, qdio->adapter->pool.erp_req); if (IS_ERR(req)) { @@ -2059,9 +1988,7 @@ int zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action) } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_READ; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); req->qtcb->header.port_handle = erp_action->port->handle; req->qtcb->header.lun_handle = erp_action->unit->handle; @@ -2289,8 +2216,11 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, goto out; } + if (scsi_cmnd->sc_data_direction == DMA_TO_DEVICE) + sbtype = SBAL_FLAGS0_TYPE_WRITE; + req = zfcp_fsf_req_create(qdio, FSF_QTCB_FCP_CMND, - adapter->pool.scsi_req); + sbtype, adapter->pool.scsi_req); if (IS_ERR(req)) { retval = PTR_ERR(req); @@ -2298,7 +2228,6 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, } req->status |= ZFCP_STATUS_FSFREQ_CLEANUP; - get_device(&unit->dev); req->unit = unit; req->data = scsi_cmnd; req->handler = zfcp_fsf_send_fcp_command_handler; @@ -2323,20 +2252,21 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit, break; case DMA_TO_DEVICE: req->qtcb->bottom.io.data_direction = FSF_DATADIR_WRITE; - sbtype = SBAL_FLAGS0_TYPE_WRITE; break; case DMA_BIDIRECTIONAL: goto failed_scsi_cmnd; } + get_device(&unit->dev); + fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd; zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd); - real_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, sbtype, + real_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, scsi_sglist(scsi_cmnd), - FSF_MAX_SBALS_PER_REQ); + ZFCP_FSF_MAX_SBALS_PER_REQ); if (unlikely(real_bytes < 0)) { - if (req->qdio_req.sbal_number >= FSF_MAX_SBALS_PER_REQ) { + if (req->qdio_req.sbal_number >= ZFCP_FSF_MAX_SBALS_PER_REQ) { dev_err(&adapter->ccw_device->dev, "Oversize data package, unit 0x%016Lx " "on port 0x%016Lx closed\n", @@ -2371,7 +2301,6 @@ out: */ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags) { - struct qdio_buffer_element *sbale; struct zfcp_fsf_req *req = NULL; struct fcp_cmnd *fcp_cmnd; struct zfcp_qdio *qdio = unit->port->adapter->qdio; @@ -2381,10 +2310,11 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags) return NULL; spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; req = zfcp_fsf_req_create(qdio, FSF_QTCB_FCP_CMND, + SBAL_FLAGS0_TYPE_WRITE, qdio->adapter->pool.scsi_req); if (IS_ERR(req)) { @@ -2401,9 +2331,7 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags) req->qtcb->bottom.io.service_class = FSF_CLASS_3; req->qtcb->bottom.io.fcp_cmnd_length = FCP_CMND_LEN; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE; - sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY; + zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd; zfcp_fc_fcp_tm(fcp_cmnd, unit->device, tm_flags); @@ -2432,7 +2360,6 @@ static void zfcp_fsf_control_file_handler(struct zfcp_fsf_req *req) struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter, struct zfcp_fsf_cfdc *fsf_cfdc) { - struct qdio_buffer_element *sbale; struct zfcp_qdio *qdio = adapter->qdio; struct zfcp_fsf_req *req = NULL; struct fsf_qtcb_bottom_support *bottom; @@ -2453,10 +2380,10 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter, } spin_lock_bh(&qdio->req_q_lock); - if (zfcp_fsf_req_sbal_get(qdio)) + if (zfcp_qdio_sbal_get(qdio)) goto out; - req = zfcp_fsf_req_create(qdio, fsf_cfdc->command, NULL); + req = zfcp_fsf_req_create(qdio, fsf_cfdc->command, direction, NULL); if (IS_ERR(req)) { retval = -EPERM; goto out; @@ -2464,16 +2391,13 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter, req->handler = zfcp_fsf_control_file_handler; - sbale = zfcp_qdio_sbale_req(qdio, &req->qdio_req); - sbale[0].flags |= direction; - bottom = &req->qtcb->bottom.support; bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE; bottom->option = fsf_cfdc->option; bytes = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req, - direction, fsf_cfdc->sg, - FSF_MAX_SBALS_PER_REQ); + fsf_cfdc->sg, + ZFCP_FSF_MAX_SBALS_PER_REQ); if (bytes != ZFCP_CFDC_MAX_SIZE) { zfcp_fsf_req_free(req); goto out; diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index b3de682b64c..519083fd6e8 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -3,7 +3,7 @@ * * Interface to the FSF support functions. * - * Copyright IBM Corporation 2002, 2009 + * Copyright IBM Corporation 2002, 2010 */ #ifndef FSF_H @@ -152,7 +152,12 @@ #define FSF_CLASS_3 0x00000003 /* SBAL chaining */ -#define FSF_MAX_SBALS_PER_REQ 36 +#define ZFCP_FSF_MAX_SBALS_PER_REQ 36 + +/* max. number of (data buffer) SBALEs in largest SBAL chain + * request ID + QTCB in SBALE 0 + 1 of first SBAL in chain */ +#define ZFCP_FSF_MAX_SBALES_PER_REQ \ + (ZFCP_FSF_MAX_SBALS_PER_REQ * ZFCP_QDIO_MAX_SBALES_PER_SBAL - 2) /* logging space behind QTCB */ #define FSF_QTCB_LOG_SIZE 1024 @@ -361,7 +366,7 @@ struct fsf_qtcb_bottom_config { u32 adapter_type; u8 res0; u8 peer_d_id[3]; - u8 res1[2]; + u16 status_read_buf_num; u16 timer_interval; u8 res2[9]; u8 s_id[3]; diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index dbfa312a7f5..28117e130e2 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -3,7 +3,7 @@ * * Setup and helper functions to access QDIO. * - * Copyright IBM Corporation 2002, 2009 + * Copyright IBM Corporation 2002, 2010 */ #define KMSG_COMPONENT "zfcp" @@ -151,8 +151,7 @@ static void zfcp_qdio_sbal_limit(struct zfcp_qdio *qdio, } static struct qdio_buffer_element * -zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, - unsigned long sbtype) +zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) { struct qdio_buffer_element *sbale; @@ -180,17 +179,16 @@ zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, /* set storage-block type for new SBAL */ sbale = zfcp_qdio_sbale_curr(qdio, q_req); - sbale->flags |= sbtype; + sbale->flags |= q_req->sbtype; return sbale; } static struct qdio_buffer_element * -zfcp_qdio_sbale_next(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, - unsigned int sbtype) +zfcp_qdio_sbale_next(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) { - if (q_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL) - return zfcp_qdio_sbal_chain(qdio, q_req, sbtype); + if (q_req->sbale_curr == ZFCP_QDIO_LAST_SBALE_PER_SBAL) + return zfcp_qdio_sbal_chain(qdio, q_req); q_req->sbale_curr++; return zfcp_qdio_sbale_curr(qdio, q_req); } @@ -206,62 +204,38 @@ static void zfcp_qdio_undo_sbals(struct zfcp_qdio *qdio, zfcp_qdio_zero_sbals(sbal, first, count); } -static int zfcp_qdio_fill_sbals(struct zfcp_qdio *qdio, - struct zfcp_qdio_req *q_req, - unsigned int sbtype, void *start_addr, - unsigned int total_length) -{ - struct qdio_buffer_element *sbale; - unsigned long remaining, length; - void *addr; - - /* split segment up */ - for (addr = start_addr, remaining = total_length; remaining > 0; - addr += length, remaining -= length) { - sbale = zfcp_qdio_sbale_next(qdio, q_req, sbtype); - if (!sbale) { - atomic_inc(&qdio->req_q_full); - zfcp_qdio_undo_sbals(qdio, q_req); - return -EINVAL; - } - - /* new piece must not exceed next page boundary */ - length = min(remaining, - (PAGE_SIZE - ((unsigned long)addr & - (PAGE_SIZE - 1)))); - sbale->addr = addr; - sbale->length = length; - } - return 0; -} - /** * zfcp_qdio_sbals_from_sg - fill SBALs from scatter-gather list - * @fsf_req: request to be processed - * @sbtype: SBALE flags + * @qdio: pointer to struct zfcp_qdio + * @q_req: pointer to struct zfcp_qdio_req * @sg: scatter-gather list * @max_sbals: upper bound for number of SBALs to be used * Returns: number of bytes, or error (negativ) */ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, - unsigned long sbtype, struct scatterlist *sg, - int max_sbals) + struct scatterlist *sg, int max_sbals) { struct qdio_buffer_element *sbale; - int retval, bytes = 0; + int bytes = 0; /* figure out last allowed SBAL */ zfcp_qdio_sbal_limit(qdio, q_req, max_sbals); /* set storage-block type for this request */ sbale = zfcp_qdio_sbale_req(qdio, q_req); - sbale->flags |= sbtype; + sbale->flags |= q_req->sbtype; for (; sg; sg = sg_next(sg)) { - retval = zfcp_qdio_fill_sbals(qdio, q_req, sbtype, - sg_virt(sg), sg->length); - if (retval < 0) - return retval; + sbale = zfcp_qdio_sbale_next(qdio, q_req); + if (!sbale) { + atomic_inc(&qdio->req_q_full); + zfcp_qdio_undo_sbals(qdio, q_req); + return -EINVAL; + } + + sbale->addr = sg_virt(sg); + sbale->length = sg->length; + bytes += sg->length; } @@ -272,6 +246,46 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, return bytes; } +static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio) +{ + struct zfcp_qdio_queue *req_q = &qdio->req_q; + + spin_lock_bh(&qdio->req_q_lock); + if (atomic_read(&req_q->count)) + return 1; + spin_unlock_bh(&qdio->req_q_lock); + return 0; +} + +/** + * zfcp_qdio_sbal_get - get free sbal in request queue, wait if necessary + * @qdio: pointer to struct zfcp_qdio + * + * The req_q_lock must be held by the caller of this function, and + * this function may only be called from process context; it will + * sleep when waiting for a free sbal. + * + * Returns: 0 on success, -EIO if there is no free sbal after waiting. + */ +int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) +{ + long ret; + + spin_unlock_bh(&qdio->req_q_lock); + ret = wait_event_interruptible_timeout(qdio->req_q_wq, + zfcp_qdio_sbal_check(qdio), 5 * HZ); + if (ret > 0) + return 0; + if (!ret) { + atomic_inc(&qdio->req_q_full); + /* assume hanging outbound queue, try queue recovery */ + zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1", NULL); + } + + spin_lock_bh(&qdio->req_q_lock); + return -EIO; +} + /** * zfcp_qdio_send - set PCI flag in first SBALE and send req to QDIO * @qdio: pointer to struct zfcp_qdio diff --git a/drivers/s390/scsi/zfcp_qdio.h b/drivers/s390/scsi/zfcp_qdio.h index 8cca54631e1..138fba577b4 100644 --- a/drivers/s390/scsi/zfcp_qdio.h +++ b/drivers/s390/scsi/zfcp_qdio.h @@ -11,6 +11,14 @@ #include <asm/qdio.h> +#define ZFCP_QDIO_SBALE_LEN PAGE_SIZE + +/* DMQ bug workaround: don't use last SBALE */ +#define ZFCP_QDIO_MAX_SBALES_PER_SBAL (QDIO_MAX_ELEMENTS_PER_BUFFER - 1) + +/* index of last SBALE (with respect to DMQ bug workaround) */ +#define ZFCP_QDIO_LAST_SBALE_PER_SBAL (ZFCP_QDIO_MAX_SBALES_PER_SBAL - 1) + /** * struct zfcp_qdio_queue - qdio queue buffer, zfcp index and free count * @sbal: qdio buffers @@ -49,6 +57,7 @@ struct zfcp_qdio { /** * struct zfcp_qdio_req - qdio queue related values for a request + * @sbtype: sbal type flags for sbale 0 * @sbal_number: number of free sbals * @sbal_first: first sbal for this request * @sbal_last: last sbal for this request @@ -59,6 +68,7 @@ struct zfcp_qdio { * @qdio_inb_usage: usage of inbound queue */ struct zfcp_qdio_req { + u32 sbtype; u8 sbal_number; u8 sbal_first; u8 sbal_last; @@ -106,4 +116,98 @@ zfcp_qdio_sbale_curr(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req) q_req->sbale_curr); } +/** + * zfcp_qdio_req_init - initialize qdio request + * @qdio: request queue where to start putting the request + * @q_req: the qdio request to start + * @req_id: The request id + * @sbtype: type flags to set for all sbals + * @data: First data block + * @len: Length of first data block + * + * This is the start of putting the request into the queue, the last + * step is passing the request to zfcp_qdio_send. The request queue + * lock must be held during the whole process from init to send. + */ +static inline +void zfcp_qdio_req_init(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, + unsigned long req_id, u32 sbtype, void *data, u32 len) +{ + struct qdio_buffer_element *sbale; + + q_req->sbal_first = q_req->sbal_last = qdio->req_q.first; + q_req->sbal_number = 1; + q_req->sbtype = sbtype; + + sbale = zfcp_qdio_sbale_req(qdio, q_req); + sbale->addr = (void *) req_id; + sbale->flags |= SBAL_FLAGS0_COMMAND; + sbale->flags |= sbtype; + + q_req->sbale_curr = 1; + sbale++; + sbale->addr = data; + if (likely(data)) + sbale->length = len; +} + +/** + * zfcp_qdio_fill_next - Fill next sbale, only for single sbal requests + * @qdio: pointer to struct zfcp_qdio + * @q_req: pointer to struct zfcp_queue_req + * + * This is only required for single sbal requests, calling it when + * wrapping around to the next sbal is a bug. + */ +static inline +void zfcp_qdio_fill_next(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req, + void *data, u32 len) +{ + struct qdio_buffer_element *sbale; + + BUG_ON(q_req->sbale_curr == ZFCP_QDIO_LAST_SBALE_PER_SBAL); + q_req->sbale_curr++; + sbale = zfcp_qdio_sbale_curr(qdio, q_req); + sbale->addr = data; + sbale->length = len; +} + +/** + * zfcp_qdio_set_sbale_last - set last entry flag in current sbale + * @qdio: pointer to struct zfcp_qdio + * @q_req: pointer to struct zfcp_queue_req + */ +static inline +void zfcp_qdio_set_sbale_last(struct zfcp_qdio *qdio, + struct zfcp_qdio_req *q_req) +{ + struct qdio_buffer_element *sbale; + + sbale = zfcp_qdio_sbale_curr(qdio, q_req); + sbale->flags |= SBAL_FLAGS_LAST_ENTRY; +} + +/** + * zfcp_qdio_sg_one_sbal - check if one sbale is enough for sg data + * @sg: The scatterlist where to check the data size + * + * Returns: 1 when one sbale is enough for the data in the scatterlist, + * 0 if not. + */ +static inline +int zfcp_qdio_sg_one_sbale(struct scatterlist *sg) +{ + return sg_is_last(sg) && sg->length <= ZFCP_QDIO_SBALE_LEN; +} + +/** + * zfcp_qdio_skip_to_last_sbale - skip to last sbale in sbal + * @q_req: The current zfcp_qdio_req + */ +static inline +void zfcp_qdio_skip_to_last_sbale(struct zfcp_qdio_req *q_req) +{ + q_req->sbale_curr = ZFCP_QDIO_LAST_SBALE_PER_SBAL; +} + #endif /* ZFCP_QDIO_H */ diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 174b6d57d57..be5d2c60453 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -175,7 +175,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) struct zfcp_fsf_req *old_req, *abrt_req; unsigned long flags; unsigned long old_reqid = (unsigned long) scpnt->host_scribble; - int retval = SUCCESS; + int retval = SUCCESS, ret; int retry = 3; char *dbf_tag; @@ -200,7 +200,9 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) break; zfcp_erp_wait(adapter); - fc_block_scsi_eh(scpnt); + ret = fc_block_scsi_eh(scpnt); + if (ret) + return ret; if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) { zfcp_dbf_scsi_abort("nres", adapter->dbf, scpnt, NULL, @@ -231,7 +233,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) struct zfcp_unit *unit = scpnt->device->hostdata; struct zfcp_adapter *adapter = unit->port->adapter; struct zfcp_fsf_req *fsf_req = NULL; - int retval = SUCCESS; + int retval = SUCCESS, ret; int retry = 3; while (retry--) { @@ -240,7 +242,10 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) break; zfcp_erp_wait(adapter); - fc_block_scsi_eh(scpnt); + ret = fc_block_scsi_eh(scpnt); + if (ret) + return ret; + if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) { zfcp_dbf_scsi_devreset("nres", tm_flags, unit, scpnt); @@ -276,10 +281,13 @@ static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) { struct zfcp_unit *unit = scpnt->device->hostdata; struct zfcp_adapter *adapter = unit->port->adapter; + int ret; zfcp_erp_adapter_reopen(adapter, 0, "schrh_1", scpnt); zfcp_erp_wait(adapter); - fc_block_scsi_eh(scpnt); + ret = fc_block_scsi_eh(scpnt); + if (ret) + return ret; return SUCCESS; } @@ -669,11 +677,12 @@ struct zfcp_data zfcp_data = { .eh_host_reset_handler = zfcp_scsi_eh_host_reset_handler, .can_queue = 4096, .this_id = -1, - .sg_tablesize = ZFCP_MAX_SBALES_PER_REQ, + .sg_tablesize = ZFCP_FSF_MAX_SBALES_PER_REQ, .cmd_per_lun = 1, .use_clustering = 1, .sdev_attrs = zfcp_sysfs_sdev_attrs, - .max_sectors = (ZFCP_MAX_SBALES_PER_REQ * 8), + .max_sectors = (ZFCP_FSF_MAX_SBALES_PER_REQ * 8), + .dma_boundary = ZFCP_QDIO_SBALE_LEN - 1, .shost_attrs = zfcp_sysfs_shost_attrs, }, }; diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index e9788f55ab1..1bb774becf2 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -1,10 +1,11 @@ /* 3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux. - Written By: Adam Radford <linuxraid@amcc.com> - Modifications By: Tom Couch <linuxraid@amcc.com> + Written By: Adam Radford <linuxraid@lsi.com> + Modifications By: Tom Couch <linuxraid@lsi.com> Copyright (C) 2004-2009 Applied Micro Circuits Corporation. + Copyright (C) 2010 LSI Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,10 +41,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Bugs/Comments/Suggestions should be mailed to: - linuxraid@amcc.com + linuxraid@lsi.com For more information, goto: - http://www.amcc.com + http://www.lsi.com Note: This version of the driver does not contain a bundled firmware image. @@ -77,6 +78,7 @@ Use pci_resource_len() for ioremap(). 2.26.02.012 - Add power management support. 2.26.02.013 - Fix bug in twa_load_sgl(). + 2.26.02.014 - Force 60 second timeout default. */ #include <linux/module.h> @@ -102,14 +104,14 @@ #include "3w-9xxx.h" /* Globals */ -#define TW_DRIVER_VERSION "2.26.02.013" +#define TW_DRIVER_VERSION "2.26.02.014" static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; static unsigned int twa_device_extension_count; static int twa_major = -1; extern struct timezone sys_tz; /* Module parameters */ -MODULE_AUTHOR ("AMCC"); +MODULE_AUTHOR ("LSI"); MODULE_DESCRIPTION ("3ware 9000 Storage Controller Linux Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(TW_DRIVER_VERSION); @@ -1990,6 +1992,15 @@ static void twa_unmap_scsi_data(TW_Device_Extension *tw_dev, int request_id) scsi_dma_unmap(cmd); } /* End twa_unmap_scsi_data() */ +/* This function gets called when a disk is coming on-line */ +static int twa_slave_configure(struct scsi_device *sdev) +{ + /* Force 60 second timeout */ + blk_queue_rq_timeout(sdev->request_queue, 60 * HZ); + + return 0; +} /* End twa_slave_configure() */ + /* scsi_host_template initializer */ static struct scsi_host_template driver_template = { .module = THIS_MODULE, @@ -1999,6 +2010,7 @@ static struct scsi_host_template driver_template = { .bios_param = twa_scsi_biosparam, .change_queue_depth = twa_change_queue_depth, .can_queue = TW_Q_LENGTH-2, + .slave_configure = twa_slave_configure, .this_id = -1, .sg_tablesize = TW_APACHE_MAX_SGL_LENGTH, .max_sectors = TW_MAX_SECTORS, diff --git a/drivers/scsi/3w-9xxx.h b/drivers/scsi/3w-9xxx.h index 2893eec78ed..3343824855d 100644 --- a/drivers/scsi/3w-9xxx.h +++ b/drivers/scsi/3w-9xxx.h @@ -1,10 +1,11 @@ /* 3w-9xxx.h -- 3ware 9000 Storage Controller device driver for Linux. - Written By: Adam Radford <linuxraid@amcc.com> - Modifications By: Tom Couch <linuxraid@amcc.com> + Written By: Adam Radford <linuxraid@lsi.com> + Modifications By: Tom Couch <linuxraid@lsi.com> Copyright (C) 2004-2009 Applied Micro Circuits Corporation. + Copyright (C) 2010 LSI Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,10 +41,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Bugs/Comments/Suggestions should be mailed to: - linuxraid@amcc.com + linuxraid@lsi.com For more information, goto: - http://www.amcc.com + http://www.lsi.com */ #ifndef _3W_9XXX_H diff --git a/drivers/scsi/3w-sas.c b/drivers/scsi/3w-sas.c index 54c5ffb1eaa..d38000db923 100644 --- a/drivers/scsi/3w-sas.c +++ b/drivers/scsi/3w-sas.c @@ -98,7 +98,7 @@ static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_res /* Functions */ /* This function returns AENs through sysfs */ -static ssize_t twl_sysfs_aen_read(struct kobject *kobj, +static ssize_t twl_sysfs_aen_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *outbuf, loff_t offset, size_t count) { @@ -129,7 +129,7 @@ static struct bin_attribute twl_sysfs_aen_read_attr = { }; /* This function returns driver compatibility info through sysfs */ -static ssize_t twl_sysfs_compat_info(struct kobject *kobj, +static ssize_t twl_sysfs_compat_info(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *outbuf, loff_t offset, size_t count) { diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index 5faf903ca8c..d119a614bf7 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -1,12 +1,12 @@ /* 3w-xxxx.c -- 3ware Storage Controller device driver for Linux. - Written By: Adam Radford <linuxraid@amcc.com> + Written By: Adam Radford <linuxraid@lsi.com> Modifications By: Joel Jacobson <linux@3ware.com> Arnaldo Carvalho de Melo <acme@conectiva.com.br> Brad Strand <linux@3ware.com> - Copyright (C) 1999-2009 3ware Inc. + Copyright (C) 1999-2010 3ware Inc. Kernel compatibility By: Andre Hedrick <andre@suse.com> Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com> @@ -47,10 +47,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Bugs/Comments/Suggestions should be mailed to: - linuxraid@amcc.com + linuxraid@lsi.com For more information, goto: - http://www.amcc.com + http://www.lsi.com History ------- @@ -194,6 +194,7 @@ 1.26.02.002 - Free irq handler in __tw_shutdown(). Turn on RCD bit for caching mode page. Serialize reset code. + 1.26.02.003 - Force 60 second timeout default. */ #include <linux/module.h> @@ -219,13 +220,13 @@ #include "3w-xxxx.h" /* Globals */ -#define TW_DRIVER_VERSION "1.26.02.002" +#define TW_DRIVER_VERSION "1.26.02.003" static TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT]; static int tw_device_extension_count = 0; static int twe_major = -1; /* Module parameters */ -MODULE_AUTHOR("AMCC"); +MODULE_AUTHOR("LSI"); MODULE_DESCRIPTION("3ware Storage Controller Linux Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(TW_DRIVER_VERSION); @@ -2245,6 +2246,15 @@ static void tw_shutdown(struct pci_dev *pdev) __tw_shutdown(tw_dev); } /* End tw_shutdown() */ +/* This function gets called when a disk is coming online */ +static int tw_slave_configure(struct scsi_device *sdev) +{ + /* Force 60 second timeout */ + blk_queue_rq_timeout(sdev->request_queue, 60 * HZ); + + return 0; +} /* End tw_slave_configure() */ + static struct scsi_host_template driver_template = { .module = THIS_MODULE, .name = "3ware Storage Controller", @@ -2253,6 +2263,7 @@ static struct scsi_host_template driver_template = { .bios_param = tw_scsi_biosparam, .change_queue_depth = tw_change_queue_depth, .can_queue = TW_Q_LENGTH-2, + .slave_configure = tw_slave_configure, .this_id = -1, .sg_tablesize = TW_MAX_SGL_LENGTH, .max_sectors = TW_MAX_SECTORS, diff --git a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h index a5a2ba2561d..8b9f9d17e7f 100644 --- a/drivers/scsi/3w-xxxx.h +++ b/drivers/scsi/3w-xxxx.h @@ -1,12 +1,12 @@ /* 3w-xxxx.h -- 3ware Storage Controller device driver for Linux. - Written By: Adam Radford <linuxraid@amcc.com> + Written By: Adam Radford <linuxraid@lsi.com> Modifications By: Joel Jacobson <linux@3ware.com> Arnaldo Carvalho de Melo <acme@conectiva.com.br> Brad Strand <linux@3ware.com> - Copyright (C) 1999-2009 3ware Inc. + Copyright (C) 1999-2010 3ware Inc. Kernel compatiblity By: Andre Hedrick <andre@suse.com> Non-Copyright (C) 2000 Andre Hedrick <andre@suse.com> @@ -45,10 +45,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Bugs/Comments/Suggestions should be mailed to: - linuxraid@amcc.com + linuxraid@lsi.com For more information, goto: - http://www.amcc.com + http://www.lsi.com */ #ifndef _3W_XXXX_H diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 92a8c500b23..1c7ac49be64 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -162,6 +162,7 @@ scsi_mod-y += scsi_scan.o scsi_sysfs.o scsi_devinfo.o scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o +scsi_mod-y += scsi_trace.o scsi_tgt-y += scsi_tgt_lib.o scsi_tgt_if.o diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c index d8fe5b76fee..308541ff85c 100644 --- a/drivers/scsi/a2091.c +++ b/drivers/scsi/a2091.c @@ -19,186 +19,190 @@ #include "wd33c93.h" #include "a2091.h" -#include<linux/stat.h> +#include <linux/stat.h> -#define DMA(ptr) ((a2091_scsiregs *)((ptr)->base)) -#define HDATA(ptr) ((struct WD33C93_hostdata *)((ptr)->hostdata)) static int a2091_release(struct Scsi_Host *instance); -static irqreturn_t a2091_intr (int irq, void *_instance) +static irqreturn_t a2091_intr(int irq, void *data) { - unsigned long flags; - unsigned int status; - struct Scsi_Host *instance = (struct Scsi_Host *)_instance; - - status = DMA(instance)->ISTR; - if (!(status & (ISTR_INT_F|ISTR_INT_P)) || !(status & ISTR_INTS)) - return IRQ_NONE; - - spin_lock_irqsave(instance->host_lock, flags); - wd33c93_intr(instance); - spin_unlock_irqrestore(instance->host_lock, flags); - return IRQ_HANDLED; + struct Scsi_Host *instance = data; + a2091_scsiregs *regs = (a2091_scsiregs *)(instance->base); + unsigned int status = regs->ISTR; + unsigned long flags; + + if (!(status & (ISTR_INT_F | ISTR_INT_P)) || !(status & ISTR_INTS)) + return IRQ_NONE; + + spin_lock_irqsave(instance->host_lock, flags); + wd33c93_intr(instance); + spin_unlock_irqrestore(instance->host_lock, flags); + return IRQ_HANDLED; } static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { - unsigned short cntr = CNTR_PDMD | CNTR_INTEN; - unsigned long addr = virt_to_bus(cmd->SCp.ptr); - struct Scsi_Host *instance = cmd->device->host; - - /* don't allow DMA if the physical address is bad */ - if (addr & A2091_XFER_MASK) - { - HDATA(instance)->dma_bounce_len = (cmd->SCp.this_residual + 511) - & ~0x1ff; - HDATA(instance)->dma_bounce_buffer = - kmalloc (HDATA(instance)->dma_bounce_len, GFP_KERNEL); - - /* can't allocate memory; use PIO */ - if (!HDATA(instance)->dma_bounce_buffer) { - HDATA(instance)->dma_bounce_len = 0; - return 1; - } - - /* get the physical address of the bounce buffer */ - addr = virt_to_bus(HDATA(instance)->dma_bounce_buffer); + struct Scsi_Host *instance = cmd->device->host; + struct WD33C93_hostdata *hdata = shost_priv(instance); + a2091_scsiregs *regs = (a2091_scsiregs *)(instance->base); + unsigned short cntr = CNTR_PDMD | CNTR_INTEN; + unsigned long addr = virt_to_bus(cmd->SCp.ptr); - /* the bounce buffer may not be in the first 16M of physmem */ + /* don't allow DMA if the physical address is bad */ if (addr & A2091_XFER_MASK) { - /* we could use chipmem... maybe later */ - kfree (HDATA(instance)->dma_bounce_buffer); - HDATA(instance)->dma_bounce_buffer = NULL; - HDATA(instance)->dma_bounce_len = 0; - return 1; + hdata->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff; + hdata->dma_bounce_buffer = kmalloc(hdata->dma_bounce_len, + GFP_KERNEL); + + /* can't allocate memory; use PIO */ + if (!hdata->dma_bounce_buffer) { + hdata->dma_bounce_len = 0; + return 1; + } + + /* get the physical address of the bounce buffer */ + addr = virt_to_bus(hdata->dma_bounce_buffer); + + /* the bounce buffer may not be in the first 16M of physmem */ + if (addr & A2091_XFER_MASK) { + /* we could use chipmem... maybe later */ + kfree(hdata->dma_bounce_buffer); + hdata->dma_bounce_buffer = NULL; + hdata->dma_bounce_len = 0; + return 1; + } + + if (!dir_in) { + /* copy to bounce buffer for a write */ + memcpy(hdata->dma_bounce_buffer, cmd->SCp.ptr, + cmd->SCp.this_residual); + } } - if (!dir_in) { - /* copy to bounce buffer for a write */ - memcpy (HDATA(instance)->dma_bounce_buffer, - cmd->SCp.ptr, cmd->SCp.this_residual); - } - } + /* setup dma direction */ + if (!dir_in) + cntr |= CNTR_DDIR; - /* setup dma direction */ - if (!dir_in) - cntr |= CNTR_DDIR; + /* remember direction */ + hdata->dma_dir = dir_in; - /* remember direction */ - HDATA(cmd->device->host)->dma_dir = dir_in; + regs->CNTR = cntr; - DMA(cmd->device->host)->CNTR = cntr; + /* setup DMA *physical* address */ + regs->ACR = addr; - /* setup DMA *physical* address */ - DMA(cmd->device->host)->ACR = addr; - - if (dir_in){ - /* invalidate any cache */ - cache_clear (addr, cmd->SCp.this_residual); - }else{ - /* push any dirty cache */ - cache_push (addr, cmd->SCp.this_residual); - } - /* start DMA */ - DMA(cmd->device->host)->ST_DMA = 1; + if (dir_in) { + /* invalidate any cache */ + cache_clear(addr, cmd->SCp.this_residual); + } else { + /* push any dirty cache */ + cache_push(addr, cmd->SCp.this_residual); + } + /* start DMA */ + regs->ST_DMA = 1; - /* return success */ - return 0; + /* return success */ + return 0; } static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, - int status) + int status) { - /* disable SCSI interrupts */ - unsigned short cntr = CNTR_PDMD; - - if (!HDATA(instance)->dma_dir) - cntr |= CNTR_DDIR; - - /* disable SCSI interrupts */ - DMA(instance)->CNTR = cntr; - - /* flush if we were reading */ - if (HDATA(instance)->dma_dir) { - DMA(instance)->FLUSH = 1; - while (!(DMA(instance)->ISTR & ISTR_FE_FLG)) - ; - } - - /* clear a possible interrupt */ - DMA(instance)->CINT = 1; - - /* stop DMA */ - DMA(instance)->SP_DMA = 1; - - /* restore the CONTROL bits (minus the direction flag) */ - DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN; - - /* copy from a bounce buffer, if necessary */ - if (status && HDATA(instance)->dma_bounce_buffer) { - if( HDATA(instance)->dma_dir ) - memcpy (SCpnt->SCp.ptr, - HDATA(instance)->dma_bounce_buffer, - SCpnt->SCp.this_residual); - kfree (HDATA(instance)->dma_bounce_buffer); - HDATA(instance)->dma_bounce_buffer = NULL; - HDATA(instance)->dma_bounce_len = 0; - } + struct WD33C93_hostdata *hdata = shost_priv(instance); + a2091_scsiregs *regs = (a2091_scsiregs *)(instance->base); + + /* disable SCSI interrupts */ + unsigned short cntr = CNTR_PDMD; + + if (!hdata->dma_dir) + cntr |= CNTR_DDIR; + + /* disable SCSI interrupts */ + regs->CNTR = cntr; + + /* flush if we were reading */ + if (hdata->dma_dir) { + regs->FLUSH = 1; + while (!(regs->ISTR & ISTR_FE_FLG)) + ; + } + + /* clear a possible interrupt */ + regs->CINT = 1; + + /* stop DMA */ + regs->SP_DMA = 1; + + /* restore the CONTROL bits (minus the direction flag) */ + regs->CNTR = CNTR_PDMD | CNTR_INTEN; + + /* copy from a bounce buffer, if necessary */ + if (status && hdata->dma_bounce_buffer) { + if (hdata->dma_dir) + memcpy(SCpnt->SCp.ptr, hdata->dma_bounce_buffer, + SCpnt->SCp.this_residual); + kfree(hdata->dma_bounce_buffer); + hdata->dma_bounce_buffer = NULL; + hdata->dma_bounce_len = 0; + } } static int __init a2091_detect(struct scsi_host_template *tpnt) { - static unsigned char called = 0; - struct Scsi_Host *instance; - unsigned long address; - struct zorro_dev *z = NULL; - wd33c93_regs regs; - int num_a2091 = 0; - - if (!MACH_IS_AMIGA || called) - return 0; - called = 1; - - tpnt->proc_name = "A2091"; - tpnt->proc_info = &wd33c93_proc_info; - - while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { - if (z->id != ZORRO_PROD_CBM_A590_A2091_1 && - z->id != ZORRO_PROD_CBM_A590_A2091_2) - continue; - address = z->resource.start; - if (!request_mem_region(address, 256, "wd33c93")) - continue; - - instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata)); - if (instance == NULL) - goto release; - instance->base = ZTWO_VADDR(address); - instance->irq = IRQ_AMIGA_PORTS; - instance->unique_id = z->slotaddr; - DMA(instance)->DAWR = DAWR_A2091; - regs.SASR = &(DMA(instance)->SASR); - regs.SCMD = &(DMA(instance)->SCMD); - HDATA(instance)->no_sync = 0xff; - HDATA(instance)->fast = 0; - HDATA(instance)->dma_mode = CTRL_DMA; - wd33c93_init(instance, regs, dma_setup, dma_stop, WD33C93_FS_8_10); - if (request_irq(IRQ_AMIGA_PORTS, a2091_intr, IRQF_SHARED, "A2091 SCSI", - instance)) - goto unregister; - DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN; - num_a2091++; - continue; + static unsigned char called = 0; + struct Scsi_Host *instance; + unsigned long address; + struct zorro_dev *z = NULL; + wd33c93_regs wdregs; + a2091_scsiregs *regs; + struct WD33C93_hostdata *hdata; + int num_a2091 = 0; + + if (!MACH_IS_AMIGA || called) + return 0; + called = 1; + + tpnt->proc_name = "A2091"; + tpnt->proc_info = &wd33c93_proc_info; + + while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { + if (z->id != ZORRO_PROD_CBM_A590_A2091_1 && + z->id != ZORRO_PROD_CBM_A590_A2091_2) + continue; + address = z->resource.start; + if (!request_mem_region(address, 256, "wd33c93")) + continue; + + instance = scsi_register(tpnt, sizeof(struct WD33C93_hostdata)); + if (instance == NULL) + goto release; + instance->base = ZTWO_VADDR(address); + instance->irq = IRQ_AMIGA_PORTS; + instance->unique_id = z->slotaddr; + regs = (a2091_scsiregs *)(instance->base); + regs->DAWR = DAWR_A2091; + wdregs.SASR = ®s->SASR; + wdregs.SCMD = ®s->SCMD; + hdata = shost_priv(instance); + hdata->no_sync = 0xff; + hdata->fast = 0; + hdata->dma_mode = CTRL_DMA; + wd33c93_init(instance, wdregs, dma_setup, dma_stop, + WD33C93_FS_8_10); + if (request_irq(IRQ_AMIGA_PORTS, a2091_intr, IRQF_SHARED, + "A2091 SCSI", instance)) + goto unregister; + regs->CNTR = CNTR_PDMD | CNTR_INTEN; + num_a2091++; + continue; unregister: - scsi_unregister(instance); - wd33c93_release(); + scsi_unregister(instance); release: - release_mem_region(address, 256); - } + release_mem_region(address, 256); + } - return num_a2091; + return num_a2091; } static int a2091_bus_reset(struct scsi_cmnd *cmd) @@ -239,10 +243,11 @@ static struct scsi_host_template driver_template = { static int a2091_release(struct Scsi_Host *instance) { #ifdef MODULE - DMA(instance)->CNTR = 0; + a2091_scsiregs *regs = (a2091_scsiregs *)(instance->base); + + regs->CNTR = 0; release_mem_region(ZTWO_PADDR(instance->base), 256); free_irq(IRQ_AMIGA_PORTS, instance); - wd33c93_release(); #endif return 1; } diff --git a/drivers/scsi/a2091.h b/drivers/scsi/a2091.h index 252528f2672..1c3daa1fd75 100644 --- a/drivers/scsi/a2091.h +++ b/drivers/scsi/a2091.h @@ -12,38 +12,38 @@ #include <linux/types.h> #ifndef CMD_PER_LUN -#define CMD_PER_LUN 2 +#define CMD_PER_LUN 2 #endif #ifndef CAN_QUEUE -#define CAN_QUEUE 16 +#define CAN_QUEUE 16 #endif /* * if the transfer address ANDed with this results in a non-zero * result, then we can't use DMA. */ -#define A2091_XFER_MASK (0xff000001) +#define A2091_XFER_MASK (0xff000001) typedef struct { - unsigned char pad1[64]; - volatile unsigned short ISTR; - volatile unsigned short CNTR; - unsigned char pad2[60]; - volatile unsigned int WTC; - volatile unsigned long ACR; - unsigned char pad3[6]; - volatile unsigned short DAWR; - unsigned char pad4; - volatile unsigned char SASR; - unsigned char pad5; - volatile unsigned char SCMD; - unsigned char pad6[76]; - volatile unsigned short ST_DMA; - volatile unsigned short SP_DMA; - volatile unsigned short CINT; - unsigned char pad7[2]; - volatile unsigned short FLUSH; + unsigned char pad1[64]; + volatile unsigned short ISTR; + volatile unsigned short CNTR; + unsigned char pad2[60]; + volatile unsigned int WTC; + volatile unsigned long ACR; + unsigned char pad3[6]; + volatile unsigned short DAWR; + unsigned char pad4; + volatile unsigned char SASR; + unsigned char pad5; + volatile unsigned char SCMD; + unsigned char pad6[76]; + volatile unsigned short ST_DMA; + volatile unsigned short SP_DMA; + volatile unsigned short CINT; + unsigned char pad7[2]; + volatile unsigned short FLUSH; } a2091_scsiregs; #define DAWR_A2091 (3) diff --git a/drivers/scsi/a3000.c b/drivers/scsi/a3000.c index c35fc55f1c9..bc6eb69f5fd 100644 --- a/drivers/scsi/a3000.c +++ b/drivers/scsi/a3000.c @@ -19,26 +19,25 @@ #include "wd33c93.h" #include "a3000.h" -#include<linux/stat.h> +#include <linux/stat.h> -#define DMA(ptr) ((a3000_scsiregs *)((ptr)->base)) -#define HDATA(ptr) ((struct WD33C93_hostdata *)((ptr)->hostdata)) + +#define DMA(ptr) ((a3000_scsiregs *)((ptr)->base)) static struct Scsi_Host *a3000_host = NULL; static int a3000_release(struct Scsi_Host *instance); -static irqreturn_t a3000_intr (int irq, void *dummy) +static irqreturn_t a3000_intr(int irq, void *dummy) { unsigned long flags; unsigned int status = DMA(a3000_host)->ISTR; if (!(status & ISTR_INT_P)) return IRQ_NONE; - if (status & ISTR_INTS) - { + if (status & ISTR_INTS) { spin_lock_irqsave(a3000_host->host_lock, flags); - wd33c93_intr (a3000_host); + wd33c93_intr(a3000_host); spin_unlock_irqrestore(a3000_host->host_lock, flags); return IRQ_HANDLED; } @@ -48,162 +47,165 @@ static irqreturn_t a3000_intr (int irq, void *dummy) static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { - unsigned short cntr = CNTR_PDMD | CNTR_INTEN; - unsigned long addr = virt_to_bus(cmd->SCp.ptr); - - /* - * if the physical address has the wrong alignment, or if - * physical address is bad, or if it is a write and at the - * end of a physical memory chunk, then allocate a bounce - * buffer - */ - if (addr & A3000_XFER_MASK) - { - HDATA(a3000_host)->dma_bounce_len = (cmd->SCp.this_residual + 511) - & ~0x1ff; - HDATA(a3000_host)->dma_bounce_buffer = - kmalloc (HDATA(a3000_host)->dma_bounce_len, GFP_KERNEL); - - /* can't allocate memory; use PIO */ - if (!HDATA(a3000_host)->dma_bounce_buffer) { - HDATA(a3000_host)->dma_bounce_len = 0; - return 1; - } - - if (!dir_in) { - /* copy to bounce buffer for a write */ - memcpy (HDATA(a3000_host)->dma_bounce_buffer, - cmd->SCp.ptr, cmd->SCp.this_residual); + struct WD33C93_hostdata *hdata = shost_priv(a3000_host); + unsigned short cntr = CNTR_PDMD | CNTR_INTEN; + unsigned long addr = virt_to_bus(cmd->SCp.ptr); + + /* + * if the physical address has the wrong alignment, or if + * physical address is bad, or if it is a write and at the + * end of a physical memory chunk, then allocate a bounce + * buffer + */ + if (addr & A3000_XFER_MASK) { + hdata->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff; + hdata->dma_bounce_buffer = kmalloc(hdata->dma_bounce_len, + GFP_KERNEL); + + /* can't allocate memory; use PIO */ + if (!hdata->dma_bounce_buffer) { + hdata->dma_bounce_len = 0; + return 1; + } + + if (!dir_in) { + /* copy to bounce buffer for a write */ + memcpy(hdata->dma_bounce_buffer, cmd->SCp.ptr, + cmd->SCp.this_residual); + } + + addr = virt_to_bus(hdata->dma_bounce_buffer); } - addr = virt_to_bus(HDATA(a3000_host)->dma_bounce_buffer); - } + /* setup dma direction */ + if (!dir_in) + cntr |= CNTR_DDIR; - /* setup dma direction */ - if (!dir_in) - cntr |= CNTR_DDIR; + /* remember direction */ + hdata->dma_dir = dir_in; - /* remember direction */ - HDATA(a3000_host)->dma_dir = dir_in; + DMA(a3000_host)->CNTR = cntr; - DMA(a3000_host)->CNTR = cntr; + /* setup DMA *physical* address */ + DMA(a3000_host)->ACR = addr; - /* setup DMA *physical* address */ - DMA(a3000_host)->ACR = addr; - - if (dir_in) - /* invalidate any cache */ - cache_clear (addr, cmd->SCp.this_residual); - else - /* push any dirty cache */ - cache_push (addr, cmd->SCp.this_residual); + if (dir_in) { + /* invalidate any cache */ + cache_clear(addr, cmd->SCp.this_residual); + } else { + /* push any dirty cache */ + cache_push(addr, cmd->SCp.this_residual); + } - /* start DMA */ - mb(); /* make sure setup is completed */ - DMA(a3000_host)->ST_DMA = 1; - mb(); /* make sure DMA has started before next IO */ + /* start DMA */ + mb(); /* make sure setup is completed */ + DMA(a3000_host)->ST_DMA = 1; + mb(); /* make sure DMA has started before next IO */ - /* return success */ - return 0; + /* return success */ + return 0; } static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, int status) { - /* disable SCSI interrupts */ - unsigned short cntr = CNTR_PDMD; - - if (!HDATA(instance)->dma_dir) - cntr |= CNTR_DDIR; - - DMA(instance)->CNTR = cntr; - mb(); /* make sure CNTR is updated before next IO */ - - /* flush if we were reading */ - if (HDATA(instance)->dma_dir) { - DMA(instance)->FLUSH = 1; - mb(); /* don't allow prefetch */ - while (!(DMA(instance)->ISTR & ISTR_FE_FLG)) - barrier(); - mb(); /* no IO until FLUSH is done */ - } - - /* clear a possible interrupt */ - /* I think that this CINT is only necessary if you are - * using the terminal count features. HM 7 Mar 1994 - */ - DMA(instance)->CINT = 1; - - /* stop DMA */ - DMA(instance)->SP_DMA = 1; - mb(); /* make sure DMA is stopped before next IO */ - - /* restore the CONTROL bits (minus the direction flag) */ - DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN; - mb(); /* make sure CNTR is updated before next IO */ - - /* copy from a bounce buffer, if necessary */ - if (status && HDATA(instance)->dma_bounce_buffer) { - if (SCpnt) { - if (HDATA(instance)->dma_dir && SCpnt) - memcpy (SCpnt->SCp.ptr, - HDATA(instance)->dma_bounce_buffer, - SCpnt->SCp.this_residual); - kfree (HDATA(instance)->dma_bounce_buffer); - HDATA(instance)->dma_bounce_buffer = NULL; - HDATA(instance)->dma_bounce_len = 0; - } else { - kfree (HDATA(instance)->dma_bounce_buffer); - HDATA(instance)->dma_bounce_buffer = NULL; - HDATA(instance)->dma_bounce_len = 0; + struct WD33C93_hostdata *hdata = shost_priv(instance); + + /* disable SCSI interrupts */ + unsigned short cntr = CNTR_PDMD; + + if (!hdata->dma_dir) + cntr |= CNTR_DDIR; + + DMA(instance)->CNTR = cntr; + mb(); /* make sure CNTR is updated before next IO */ + + /* flush if we were reading */ + if (hdata->dma_dir) { + DMA(instance)->FLUSH = 1; + mb(); /* don't allow prefetch */ + while (!(DMA(instance)->ISTR & ISTR_FE_FLG)) + barrier(); + mb(); /* no IO until FLUSH is done */ + } + + /* clear a possible interrupt */ + /* I think that this CINT is only necessary if you are + * using the terminal count features. HM 7 Mar 1994 + */ + DMA(instance)->CINT = 1; + + /* stop DMA */ + DMA(instance)->SP_DMA = 1; + mb(); /* make sure DMA is stopped before next IO */ + + /* restore the CONTROL bits (minus the direction flag) */ + DMA(instance)->CNTR = CNTR_PDMD | CNTR_INTEN; + mb(); /* make sure CNTR is updated before next IO */ + + /* copy from a bounce buffer, if necessary */ + if (status && hdata->dma_bounce_buffer) { + if (SCpnt) { + if (hdata->dma_dir && SCpnt) + memcpy(SCpnt->SCp.ptr, + hdata->dma_bounce_buffer, + SCpnt->SCp.this_residual); + kfree(hdata->dma_bounce_buffer); + hdata->dma_bounce_buffer = NULL; + hdata->dma_bounce_len = 0; + } else { + kfree(hdata->dma_bounce_buffer); + hdata->dma_bounce_buffer = NULL; + hdata->dma_bounce_len = 0; + } } - } } static int __init a3000_detect(struct scsi_host_template *tpnt) { - wd33c93_regs regs; - - if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(A3000_SCSI)) - return 0; - if (!request_mem_region(0xDD0000, 256, "wd33c93")) - return 0; - - tpnt->proc_name = "A3000"; - tpnt->proc_info = &wd33c93_proc_info; - - a3000_host = scsi_register (tpnt, sizeof(struct WD33C93_hostdata)); - if (a3000_host == NULL) - goto fail_register; - - a3000_host->base = ZTWO_VADDR(0xDD0000); - a3000_host->irq = IRQ_AMIGA_PORTS; - DMA(a3000_host)->DAWR = DAWR_A3000; - regs.SASR = &(DMA(a3000_host)->SASR); - regs.SCMD = &(DMA(a3000_host)->SCMD); - HDATA(a3000_host)->no_sync = 0xff; - HDATA(a3000_host)->fast = 0; - HDATA(a3000_host)->dma_mode = CTRL_DMA; - wd33c93_init(a3000_host, regs, dma_setup, dma_stop, WD33C93_FS_12_15); - if (request_irq(IRQ_AMIGA_PORTS, a3000_intr, IRQF_SHARED, "A3000 SCSI", - a3000_intr)) - goto fail_irq; - DMA(a3000_host)->CNTR = CNTR_PDMD | CNTR_INTEN; - - return 1; + wd33c93_regs regs; + struct WD33C93_hostdata *hdata; + + if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(A3000_SCSI)) + return 0; + if (!request_mem_region(0xDD0000, 256, "wd33c93")) + return 0; + + tpnt->proc_name = "A3000"; + tpnt->proc_info = &wd33c93_proc_info; + + a3000_host = scsi_register(tpnt, sizeof(struct WD33C93_hostdata)); + if (a3000_host == NULL) + goto fail_register; + + a3000_host->base = ZTWO_VADDR(0xDD0000); + a3000_host->irq = IRQ_AMIGA_PORTS; + DMA(a3000_host)->DAWR = DAWR_A3000; + regs.SASR = &(DMA(a3000_host)->SASR); + regs.SCMD = &(DMA(a3000_host)->SCMD); + hdata = shost_priv(a3000_host); + hdata->no_sync = 0xff; + hdata->fast = 0; + hdata->dma_mode = CTRL_DMA; + wd33c93_init(a3000_host, regs, dma_setup, dma_stop, WD33C93_FS_12_15); + if (request_irq(IRQ_AMIGA_PORTS, a3000_intr, IRQF_SHARED, "A3000 SCSI", + a3000_intr)) + goto fail_irq; + DMA(a3000_host)->CNTR = CNTR_PDMD | CNTR_INTEN; + + return 1; fail_irq: - wd33c93_release(); - scsi_unregister(a3000_host); + scsi_unregister(a3000_host); fail_register: - release_mem_region(0xDD0000, 256); - return 0; + release_mem_region(0xDD0000, 256); + return 0; } static int a3000_bus_reset(struct scsi_cmnd *cmd) { /* FIXME perform bus-specific reset */ - + /* FIXME 2: kill this entire function, which should cause mid-layer to call wd33c93_host_reset anyway? */ @@ -237,11 +239,10 @@ static struct scsi_host_template driver_template = { static int a3000_release(struct Scsi_Host *instance) { - wd33c93_release(); - DMA(instance)->CNTR = 0; - release_mem_region(0xDD0000, 256); - free_irq(IRQ_AMIGA_PORTS, a3000_intr); - return 1; + DMA(instance)->CNTR = 0; + release_mem_region(0xDD0000, 256); + free_irq(IRQ_AMIGA_PORTS, a3000_intr); + return 1; } MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/a3000.h b/drivers/scsi/a3000.h index c7afe16fd6e..684813ee378 100644 --- a/drivers/scsi/a3000.h +++ b/drivers/scsi/a3000.h @@ -12,40 +12,40 @@ #include <linux/types.h> #ifndef CMD_PER_LUN -#define CMD_PER_LUN 2 +#define CMD_PER_LUN 2 #endif #ifndef CAN_QUEUE -#define CAN_QUEUE 16 +#define CAN_QUEUE 16 #endif /* * if the transfer address ANDed with this results in a non-zero * result, then we can't use DMA. */ -#define A3000_XFER_MASK (0x00000003) +#define A3000_XFER_MASK (0x00000003) typedef struct { - unsigned char pad1[2]; - volatile unsigned short DAWR; - volatile unsigned int WTC; - unsigned char pad2[2]; - volatile unsigned short CNTR; - volatile unsigned long ACR; - unsigned char pad3[2]; - volatile unsigned short ST_DMA; - unsigned char pad4[2]; - volatile unsigned short FLUSH; - unsigned char pad5[2]; - volatile unsigned short CINT; - unsigned char pad6[2]; - volatile unsigned short ISTR; - unsigned char pad7[30]; - volatile unsigned short SP_DMA; - unsigned char pad8; - volatile unsigned char SASR; - unsigned char pad9; - volatile unsigned char SCMD; + unsigned char pad1[2]; + volatile unsigned short DAWR; + volatile unsigned int WTC; + unsigned char pad2[2]; + volatile unsigned short CNTR; + volatile unsigned long ACR; + unsigned char pad3[2]; + volatile unsigned short ST_DMA; + unsigned char pad4[2]; + volatile unsigned short FLUSH; + unsigned char pad5[2]; + volatile unsigned short CINT; + unsigned char pad6[2]; + volatile unsigned short ISTR; + unsigned char pad7[30]; + volatile unsigned short SP_DMA; + unsigned char pad8; + volatile unsigned char SASR; + unsigned char pad9; + volatile unsigned char SCMD; } a3000_scsiregs; #define DAWR_A3000 (3) diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 7e26ebc2666..7df2dd1d2c6 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -328,6 +328,16 @@ int aac_get_config_status(struct aac_dev *dev, int commit_flag) return status; } +static void aac_expose_phy_device(struct scsi_cmnd *scsicmd) +{ + char inq_data; + scsi_sg_copy_to_buffer(scsicmd, &inq_data, sizeof(inq_data)); + if ((inq_data & 0x20) && (inq_data & 0x1f) == TYPE_DISK) { + inq_data &= 0xdf; + scsi_sg_copy_from_buffer(scsicmd, &inq_data, sizeof(inq_data)); + } +} + /** * aac_get_containers - list containers * @common: adapter to probe @@ -1598,6 +1608,7 @@ static int aac_read(struct scsi_cmnd * scsicmd) int status; struct aac_dev *dev; struct fib * cmd_fibcontext; + int cid; dev = (struct aac_dev *)scsicmd->device->host->hostdata; /* @@ -1647,6 +1658,22 @@ static int aac_read(struct scsi_cmnd * scsicmd) count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8]; break; } + + if ((lba + count) > (dev->fsa_dev[scmd_id(scsicmd)].size)) { + cid = scmd_id(scsicmd); + dprintk((KERN_DEBUG "aacraid: Illegal lba\n")); + scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | + SAM_STAT_CHECK_CONDITION; + set_sense(&dev->fsa_dev[cid].sense_data, + HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE, + ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0); + memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data, + min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data), + SCSI_SENSE_BUFFERSIZE)); + scsicmd->scsi_done(scsicmd); + return 1; + } + dprintk((KERN_DEBUG "aac_read[cpu %d]: lba = %llu, t = %ld.\n", smp_processor_id(), (unsigned long long)lba, jiffies)); if (aac_adapter_bounds(dev,scsicmd,lba)) @@ -1688,6 +1715,7 @@ static int aac_write(struct scsi_cmnd * scsicmd) int status; struct aac_dev *dev; struct fib * cmd_fibcontext; + int cid; dev = (struct aac_dev *)scsicmd->device->host->hostdata; /* @@ -1727,6 +1755,22 @@ static int aac_write(struct scsi_cmnd * scsicmd) count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8]; fua = scsicmd->cmnd[1] & 0x8; } + + if ((lba + count) > (dev->fsa_dev[scmd_id(scsicmd)].size)) { + cid = scmd_id(scsicmd); + dprintk((KERN_DEBUG "aacraid: Illegal lba\n")); + scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | + SAM_STAT_CHECK_CONDITION; + set_sense(&dev->fsa_dev[cid].sense_data, + HARDWARE_ERROR, SENCODE_INTERNAL_TARGET_FAILURE, + ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0); + memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data, + min_t(size_t, sizeof(dev->fsa_dev[cid].sense_data), + SCSI_SENSE_BUFFERSIZE)); + scsicmd->scsi_done(scsicmd); + return 1; + } + dprintk((KERN_DEBUG "aac_write[cpu %d]: lba = %llu, t = %ld.\n", smp_processor_id(), (unsigned long long)lba, jiffies)); if (aac_adapter_bounds(dev,scsicmd,lba)) @@ -2573,6 +2617,11 @@ static void aac_srb_callback(void *context, struct fib * fibptr) scsi_dma_unmap(scsicmd); + /* expose physical device if expose_physicald flag is on */ + if (scsicmd->cmnd[0] == INQUIRY && !(scsicmd->cmnd[1] & 0x01) + && expose_physicals > 0) + aac_expose_phy_device(scsicmd); + /* * First check the fib status */ @@ -2678,8 +2727,22 @@ static void aac_srb_callback(void *context, struct fib * fibptr) scsicmd->cmnd[0], le32_to_cpu(srbreply->scsi_status)); #endif - scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8; - break; + if ((scsicmd->cmnd[0] == ATA_12) + || (scsicmd->cmnd[0] == ATA_16)) { + if (scsicmd->cmnd[2] & (0x01 << 5)) { + scsicmd->result = DID_OK << 16 + | COMMAND_COMPLETE << 8; + break; + } else { + scsicmd->result = DID_ERROR << 16 + | COMMAND_COMPLETE << 8; + break; + } + } else { + scsicmd->result = DID_ERROR << 16 + | COMMAND_COMPLETE << 8; + break; + } } if (le32_to_cpu(srbreply->scsi_status) == SAM_STAT_CHECK_CONDITION) { int len; diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 619c02d9c86..4dbcc055ac7 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -12,7 +12,7 @@ *----------------------------------------------------------------------------*/ #ifndef AAC_DRIVER_BUILD -# define AAC_DRIVER_BUILD 24702 +# define AAC_DRIVER_BUILD 26400 # define AAC_DRIVER_BRANCH "-ms" #endif #define MAXIMUM_NUM_CONTAINERS 32 @@ -26,6 +26,8 @@ #define AAC_MAX_HOSTPHYSMEMPAGES (0xfffff) #define AAC_MAX_32BIT_SGBCOUNT ((unsigned short)256) +#define AAC_DEBUG_INSTRUMENT_AIF_DELETE + /* * These macros convert from physical channels to virtual channels */ diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 94d2954d79a..70079146e20 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -966,6 +966,16 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) device_config_needed = (((__le32 *)aifcmd->data)[0] == cpu_to_le32(AifEnAddJBOD)) ? ADD : DELETE; + if (device_config_needed == ADD) { + device = scsi_device_lookup(dev->scsi_host_ptr, + channel, + id, + lun); + if (device) { + scsi_remove_device(device); + scsi_device_put(device); + } + } break; case AifEnEnclosureManagement: @@ -1123,6 +1133,9 @@ retry_next: if (device) { switch (device_config_needed) { case DELETE: +#if (defined(AAC_DEBUG_INSTRUMENT_AIF_DELETE)) + scsi_remove_device(device); +#else if (scsi_device_online(device)) { scsi_device_set_state(device, SDEV_OFFLINE); sdev_printk(KERN_INFO, device, @@ -1131,6 +1144,7 @@ retry_next: "array deleted" : "enclosure services event"); } +#endif break; case ADD: if (!scsi_device_online(device)) { @@ -1145,12 +1159,16 @@ retry_next: case CHANGE: if ((channel == CONTAINER_CHANNEL) && (!dev->fsa_dev[container].valid)) { +#if (defined(AAC_DEBUG_INSTRUMENT_AIF_DELETE)) + scsi_remove_device(device); +#else if (!scsi_device_online(device)) break; scsi_device_set_state(device, SDEV_OFFLINE); sdev_printk(KERN_INFO, device, "Device offlined - %s\n", "array failed"); +#endif break; } scsi_rescan_device(&device->sdev_gendev); diff --git a/drivers/scsi/arcmsr/arcmsr_attr.c b/drivers/scsi/arcmsr/arcmsr_attr.c index 5877f29a600..a4e04c50c43 100644 --- a/drivers/scsi/arcmsr/arcmsr_attr.c +++ b/drivers/scsi/arcmsr/arcmsr_attr.c @@ -59,7 +59,8 @@ struct device_attribute *arcmsr_host_attrs[]; -static ssize_t arcmsr_sysfs_iop_message_read(struct kobject *kobj, +static ssize_t arcmsr_sysfs_iop_message_read(struct file *filp, + struct kobject *kobj, struct bin_attribute *bin, char *buf, loff_t off, size_t count) @@ -105,7 +106,8 @@ static ssize_t arcmsr_sysfs_iop_message_read(struct kobject *kobj, return (allxfer_len); } -static ssize_t arcmsr_sysfs_iop_message_write(struct kobject *kobj, +static ssize_t arcmsr_sysfs_iop_message_write(struct file *filp, + struct kobject *kobj, struct bin_attribute *bin, char *buf, loff_t off, size_t count) @@ -153,7 +155,8 @@ static ssize_t arcmsr_sysfs_iop_message_write(struct kobject *kobj, } } -static ssize_t arcmsr_sysfs_iop_message_clear(struct kobject *kobj, +static ssize_t arcmsr_sysfs_iop_message_clear(struct file *filp, + struct kobject *kobj, struct bin_attribute *bin, char *buf, loff_t off, size_t count) diff --git a/drivers/scsi/bfa/bfa_cb_ioim_macros.h b/drivers/scsi/bfa/bfa_cb_ioim_macros.h index 961fe439daa..53a616f5f50 100644 --- a/drivers/scsi/bfa/bfa_cb_ioim_macros.h +++ b/drivers/scsi/bfa/bfa_cb_ioim_macros.h @@ -117,35 +117,6 @@ bfa_cb_ioim_get_timeout(struct bfad_ioim_s *dio) } /** - * Get SG element for the I/O request given the SG element index - */ -static inline union bfi_addr_u -bfa_cb_ioim_get_sgaddr(struct bfad_ioim_s *dio, int sgeid) -{ - struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; - struct scatterlist *sge; - u64 addr; - - sge = (struct scatterlist *)scsi_sglist(cmnd) + sgeid; - addr = (u64) sg_dma_address(sge); - - return *((union bfi_addr_u *) &addr); -} - -static inline u32 -bfa_cb_ioim_get_sglen(struct bfad_ioim_s *dio, int sgeid) -{ - struct scsi_cmnd *cmnd = (struct scsi_cmnd *)dio; - struct scatterlist *sge; - u32 len; - - sge = (struct scatterlist *)scsi_sglist(cmnd) + sgeid; - len = sg_dma_len(sge); - - return len; -} - -/** * Get Command Reference Number for the I/O request. 0 if none. */ static inline u8 diff --git a/drivers/scsi/bfa/bfa_ioim.c b/drivers/scsi/bfa/bfa_ioim.c index 5b107abe46e..687f3d6e252 100644 --- a/drivers/scsi/bfa/bfa_ioim.c +++ b/drivers/scsi/bfa/bfa_ioim.c @@ -731,6 +731,9 @@ bfa_ioim_send_ioreq(struct bfa_ioim_s *ioim) static struct fcp_cmnd_s cmnd_z0 = { 0 }; struct bfi_sge_s *sge; u32 pgdlen = 0; + u64 addr; + struct scatterlist *sg; + struct scsi_cmnd *cmnd = (struct scsi_cmnd *) ioim->dio; /** * check for room in queue to send request now @@ -754,8 +757,10 @@ bfa_ioim_send_ioreq(struct bfa_ioim_s *ioim) */ sge = &m->sges[0]; if (ioim->nsges) { - sge->sga = bfa_cb_ioim_get_sgaddr(ioim->dio, 0); - pgdlen = bfa_cb_ioim_get_sglen(ioim->dio, 0); + sg = (struct scatterlist *)scsi_sglist(cmnd); + addr = bfa_os_sgaddr(sg_dma_address(sg)); + sge->sga = *(union bfi_addr_u *) &addr; + pgdlen = sg_dma_len(sg); sge->sg_len = pgdlen; sge->flags = (ioim->nsges > BFI_SGE_INLINE) ? BFI_SGE_DATA_CPL : BFI_SGE_DATA_LAST; @@ -868,10 +873,16 @@ bfa_ioim_sgpg_setup(struct bfa_ioim_s *ioim) struct bfi_sge_s *sge; struct bfa_sgpg_s *sgpg; u32 pgcumsz; + u64 addr; + struct scatterlist *sg; + struct scsi_cmnd *cmnd = (struct scsi_cmnd *) ioim->dio; sgeid = BFI_SGE_INLINE; ioim->sgpg = sgpg = bfa_q_first(&ioim->sgpg_q); + sg = scsi_sglist(cmnd); + sg = sg_next(sg); + do { sge = sgpg->sgpg->sges; nsges = ioim->nsges - sgeid; @@ -879,9 +890,10 @@ bfa_ioim_sgpg_setup(struct bfa_ioim_s *ioim) nsges = BFI_SGPG_DATA_SGES; pgcumsz = 0; - for (i = 0; i < nsges; i++, sge++, sgeid++) { - sge->sga = bfa_cb_ioim_get_sgaddr(ioim->dio, sgeid); - sge->sg_len = bfa_cb_ioim_get_sglen(ioim->dio, sgeid); + for (i = 0; i < nsges; i++, sge++, sgeid++, sg = sg_next(sg)) { + addr = bfa_os_sgaddr(sg_dma_address(sg)); + sge->sga = *(union bfi_addr_u *) &addr; + sge->sg_len = sg_dma_len(sg); pgcumsz += sge->sg_len; /** diff --git a/drivers/scsi/bfa/bfa_os_inc.h b/drivers/scsi/bfa/bfa_os_inc.h index 10a89f75fa9..bd1cd3ee302 100644 --- a/drivers/scsi/bfa/bfa_os_inc.h +++ b/drivers/scsi/bfa/bfa_os_inc.h @@ -50,6 +50,10 @@ #include <scsi/scsi_transport_fc.h> #include <scsi/scsi_transport.h> +#ifdef __BIG_ENDIAN +#define __BIGENDIAN +#endif + #define BFA_ERR KERN_ERR #define BFA_WARNING KERN_WARNING #define BFA_NOTICE KERN_NOTICE @@ -123,6 +127,15 @@ int bfa_os_MWB(void *); (((_x) & 0x00ff0000) >> 8) | \ (((_x) & 0xff000000) >> 24)) +#define bfa_os_swap_sgaddr(_x) ((u64)( \ + (((u64)(_x) & (u64)0x00000000000000ffull) << 32) | \ + (((u64)(_x) & (u64)0x000000000000ff00ull) << 32) | \ + (((u64)(_x) & (u64)0x0000000000ff0000ull) << 32) | \ + (((u64)(_x) & (u64)0x00000000ff000000ull) << 32) | \ + (((u64)(_x) & (u64)0x000000ff00000000ull) >> 32) | \ + (((u64)(_x) & (u64)0x0000ff0000000000ull) >> 32) | \ + (((u64)(_x) & (u64)0x00ff000000000000ull) >> 32) | \ + (((u64)(_x) & (u64)0xff00000000000000ull) >> 32))) #ifndef __BIGENDIAN #define bfa_os_htons(_x) ((u16)((((_x) & 0xff00) >> 8) | \ @@ -133,6 +146,7 @@ int bfa_os_MWB(void *); #define bfa_os_hton3b(_x) bfa_swap_3b(_x) #define bfa_os_wtole(_x) (_x) +#define bfa_os_sgaddr(_x) (_x) #else @@ -141,6 +155,7 @@ int bfa_os_MWB(void *); #define bfa_os_hton3b(_x) (_x) #define bfa_os_htonll(_x) (_x) #define bfa_os_wtole(_x) bfa_os_swap32(_x) +#define bfa_os_sgaddr(_x) bfa_os_swap_sgaddr(_x) #endif @@ -161,12 +176,12 @@ int bfa_os_MWB(void *); #define bfa_os_addr_t char __iomem * #define bfa_os_panic() -#define bfa_os_reg_read(_raddr) bfa_os_wtole(readl(_raddr)) -#define bfa_os_reg_write(_raddr, _val) writel(bfa_os_wtole((_val)), (_raddr)) +#define bfa_os_reg_read(_raddr) readl(_raddr) +#define bfa_os_reg_write(_raddr, _val) writel((_val), (_raddr)) #define bfa_os_mem_read(_raddr, _off) \ - bfa_os_ntohl(readl(((_raddr) + (_off)))) + bfa_os_swap32(readl(((_raddr) + (_off)))) #define bfa_os_mem_write(_raddr, _off, _val) \ - writel(bfa_os_htonl((_val)), ((_raddr) + (_off))) + writel(bfa_os_swap32((_val)), ((_raddr) + (_off))) #define BFA_TRC_TS(_trcm) \ ({ \ diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c index 13f5feb308c..d4fc4287ebd 100644 --- a/drivers/scsi/bfa/bfad.c +++ b/drivers/scsi/bfa/bfad.c @@ -33,7 +33,7 @@ #include <fcb/bfa_fcb.h> BFA_TRC_FILE(LDRV, BFAD); -static DEFINE_MUTEX(bfad_mutex); +DEFINE_MUTEX(bfad_mutex); LIST_HEAD(bfad_list); static int bfad_inst; int bfad_supported_fc4s; @@ -299,8 +299,6 @@ bfa_fcb_vport_delete(struct bfad_vport_s *vport_drv) complete(vport_drv->comp_del); return; } - - kfree(vport_drv); } /** @@ -483,7 +481,7 @@ ext: */ bfa_status_t bfad_vport_create(struct bfad_s *bfad, u16 vf_id, - struct bfa_port_cfg_s *port_cfg) + struct bfa_port_cfg_s *port_cfg, struct device *dev) { struct bfad_vport_s *vport; int rc = BFA_STATUS_OK; @@ -506,7 +504,8 @@ bfad_vport_create(struct bfad_s *bfad, u16 vf_id, goto ext_free_vport; if (port_cfg->roles & BFA_PORT_ROLE_FCP_IM) { - rc = bfad_im_scsi_host_alloc(bfad, vport->drv_port.im_port); + rc = bfad_im_scsi_host_alloc(bfad, vport->drv_port.im_port, + dev); if (rc != BFA_STATUS_OK) goto ext_free_fcs_vport; } @@ -591,7 +590,6 @@ bfad_init_timer(struct bfad_s *bfad) int bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad) { - unsigned long bar0_len; int rc = -ENODEV; if (pci_enable_device(pdev)) { @@ -611,9 +609,7 @@ bfad_pci_init(struct pci_dev *pdev, struct bfad_s *bfad) goto out_release_region; } - bfad->pci_bar0_map = pci_resource_start(pdev, 0); - bar0_len = pci_resource_len(pdev, 0); - bfad->pci_bar0_kva = ioremap(bfad->pci_bar0_map, bar0_len); + bfad->pci_bar0_kva = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); if (bfad->pci_bar0_kva == NULL) { BFA_PRINTF(BFA_ERR, "Fail to map bar0\n"); @@ -646,11 +642,7 @@ out: void bfad_pci_uninit(struct pci_dev *pdev, struct bfad_s *bfad) { -#if defined(__ia64__) pci_iounmap(pdev, bfad->pci_bar0_kva); -#else - iounmap(bfad->pci_bar0_kva); -#endif pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); @@ -848,7 +840,8 @@ bfad_cfg_pport(struct bfad_s *bfad, enum bfa_port_role role) goto out; } - rc = bfad_im_scsi_host_alloc(bfad, bfad->pport.im_port); + rc = bfad_im_scsi_host_alloc(bfad, bfad->pport.im_port, + &bfad->pcidev->dev); if (rc != BFA_STATUS_OK) goto out; diff --git a/drivers/scsi/bfa/bfad_attr.c b/drivers/scsi/bfa/bfad_attr.c index 6a2efdd5ef2..e477bfbfa7d 100644 --- a/drivers/scsi/bfa/bfad_attr.c +++ b/drivers/scsi/bfa/bfad_attr.c @@ -364,6 +364,152 @@ bfad_im_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout) } +static int +bfad_im_vport_create(struct fc_vport *fc_vport, bool disable) +{ + char *vname = fc_vport->symbolic_name; + struct Scsi_Host *shost = fc_vport->shost; + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) shost->hostdata[0]; + struct bfad_s *bfad = im_port->bfad; + struct bfa_port_cfg_s port_cfg; + int status = 0, rc; + unsigned long flags; + + memset(&port_cfg, 0, sizeof(port_cfg)); + + port_cfg.pwwn = wwn_to_u64((u8 *) &fc_vport->port_name); + port_cfg.nwwn = wwn_to_u64((u8 *) &fc_vport->node_name); + + if (strlen(vname) > 0) + strcpy((char *)&port_cfg.sym_name, vname); + + port_cfg.roles = BFA_PORT_ROLE_FCP_IM; + rc = bfad_vport_create(bfad, 0, &port_cfg, &fc_vport->dev); + + if (rc == BFA_STATUS_OK) { + struct bfad_vport_s *vport; + struct bfa_fcs_vport_s *fcs_vport; + struct Scsi_Host *vshost; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + fcs_vport = bfa_fcs_vport_lookup(&bfad->bfa_fcs, 0, + port_cfg.pwwn); + if (fcs_vport == NULL) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + return VPCERR_BAD_WWN; + } + + fc_vport_set_state(fc_vport, FC_VPORT_ACTIVE); + if (disable) { + bfa_fcs_vport_stop(fcs_vport); + fc_vport_set_state(fc_vport, FC_VPORT_DISABLED); + } + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + vport = fcs_vport->vport_drv; + vshost = vport->drv_port.im_port->shost; + fc_host_node_name(vshost) = wwn_to_u64((u8 *) &port_cfg.nwwn); + fc_host_port_name(vshost) = wwn_to_u64((u8 *) &port_cfg.pwwn); + fc_vport->dd_data = vport; + vport->drv_port.im_port->fc_vport = fc_vport; + + } else if (rc == BFA_STATUS_INVALID_WWN) + return VPCERR_BAD_WWN; + else if (rc == BFA_STATUS_VPORT_EXISTS) + return VPCERR_BAD_WWN; + else if (rc == BFA_STATUS_VPORT_MAX) + return VPCERR_NO_FABRIC_SUPP; + else if (rc == BFA_STATUS_VPORT_WWN_BP) + return VPCERR_BAD_WWN; + else + return FC_VPORT_FAILED; + + return status; +} + +static int +bfad_im_vport_delete(struct fc_vport *fc_vport) +{ + struct bfad_vport_s *vport = (struct bfad_vport_s *)fc_vport->dd_data; + struct bfad_im_port_s *im_port = + (struct bfad_im_port_s *) vport->drv_port.im_port; + struct bfad_s *bfad = im_port->bfad; + struct bfad_port_s *port; + struct bfa_fcs_vport_s *fcs_vport; + struct Scsi_Host *vshost; + wwn_t pwwn; + int rc; + unsigned long flags; + struct completion fcomp; + + if (im_port->flags & BFAD_PORT_DELETE) + goto free_scsi_host; + + port = im_port->port; + + vshost = vport->drv_port.im_port->shost; + pwwn = wwn_to_u64((u8 *) &fc_host_port_name(vshost)); + + spin_lock_irqsave(&bfad->bfad_lock, flags); + fcs_vport = bfa_fcs_vport_lookup(&bfad->bfa_fcs, 0, pwwn); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (fcs_vport == NULL) + return VPCERR_BAD_WWN; + + vport->drv_port.flags |= BFAD_PORT_DELETE; + + vport->comp_del = &fcomp; + init_completion(vport->comp_del); + + spin_lock_irqsave(&bfad->bfad_lock, flags); + rc = bfa_fcs_vport_delete(&vport->fcs_vport); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + wait_for_completion(vport->comp_del); + +free_scsi_host: + bfad_os_scsi_host_free(bfad, im_port); + + kfree(vport); + + return 0; +} + +static int +bfad_im_vport_disable(struct fc_vport *fc_vport, bool disable) +{ + struct bfad_vport_s *vport; + struct bfad_s *bfad; + struct bfa_fcs_vport_s *fcs_vport; + struct Scsi_Host *vshost; + wwn_t pwwn; + unsigned long flags; + + vport = (struct bfad_vport_s *)fc_vport->dd_data; + bfad = vport->drv_port.bfad; + vshost = vport->drv_port.im_port->shost; + pwwn = wwn_to_u64((u8 *) &fc_vport->port_name); + + spin_lock_irqsave(&bfad->bfad_lock, flags); + fcs_vport = bfa_fcs_vport_lookup(&bfad->bfa_fcs, 0, pwwn); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (fcs_vport == NULL) + return VPCERR_BAD_WWN; + + if (disable) { + bfa_fcs_vport_stop(fcs_vport); + fc_vport_set_state(fc_vport, FC_VPORT_DISABLED); + } else { + bfa_fcs_vport_start(fcs_vport); + fc_vport_set_state(fc_vport, FC_VPORT_ACTIVE); + } + + return 0; +} + struct fc_function_template bfad_im_fc_function_template = { /* Target dynamic attributes */ @@ -413,6 +559,61 @@ struct fc_function_template bfad_im_fc_function_template = { .show_rport_dev_loss_tmo = 1, .get_rport_dev_loss_tmo = bfad_im_get_rport_loss_tmo, .set_rport_dev_loss_tmo = bfad_im_set_rport_loss_tmo, + + .vport_create = bfad_im_vport_create, + .vport_delete = bfad_im_vport_delete, + .vport_disable = bfad_im_vport_disable, +}; + +struct fc_function_template bfad_im_vport_fc_function_template = { + + /* Target dynamic attributes */ + .get_starget_port_id = bfad_im_get_starget_port_id, + .show_starget_port_id = 1, + .get_starget_node_name = bfad_im_get_starget_node_name, + .show_starget_node_name = 1, + .get_starget_port_name = bfad_im_get_starget_port_name, + .show_starget_port_name = 1, + + /* Host dynamic attribute */ + .get_host_port_id = bfad_im_get_host_port_id, + .show_host_port_id = 1, + + /* Host fixed attributes */ + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_supported_speeds = 1, + .show_host_maxframe_size = 1, + + /* More host dynamic attributes */ + .show_host_port_type = 1, + .get_host_port_type = bfad_im_get_host_port_type, + .show_host_port_state = 1, + .get_host_port_state = bfad_im_get_host_port_state, + .show_host_active_fc4s = 1, + .get_host_active_fc4s = bfad_im_get_host_active_fc4s, + .show_host_speed = 1, + .get_host_speed = bfad_im_get_host_speed, + .show_host_fabric_name = 1, + .get_host_fabric_name = bfad_im_get_host_fabric_name, + + .show_host_symbolic_name = 1, + + /* Statistics */ + .get_fc_host_stats = bfad_im_get_stats, + .reset_fc_host_stats = bfad_im_reset_stats, + + /* Allocation length for host specific data */ + .dd_fcrport_size = sizeof(struct bfad_itnim_data_s *), + + /* Remote port fixed attributes */ + .show_rport_maxframe_size = 1, + .show_rport_supported_classes = 1, + .show_rport_dev_loss_tmo = 1, + .get_rport_dev_loss_tmo = bfad_im_get_rport_loss_tmo, + .set_rport_dev_loss_tmo = bfad_im_set_rport_loss_tmo, }; /** diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h index 107848cd3b6..6c920c1b53a 100644 --- a/drivers/scsi/bfa/bfad_drv.h +++ b/drivers/scsi/bfa/bfad_drv.h @@ -162,7 +162,6 @@ struct bfad_s { const char *pci_name; struct bfa_pcidev_s hal_pcidev; struct bfa_ioc_pci_attr_s pci_attr; - unsigned long pci_bar0_map; void __iomem *pci_bar0_kva; struct completion comp; struct completion suspend; @@ -254,7 +253,7 @@ do { \ bfa_status_t bfad_vport_create(struct bfad_s *bfad, u16 vf_id, - struct bfa_port_cfg_s *port_cfg); + struct bfa_port_cfg_s *port_cfg, struct device *dev); bfa_status_t bfad_vf_create(struct bfad_s *bfad, u16 vf_id, struct bfa_port_cfg_s *port_cfg); bfa_status_t bfad_cfg_pport(struct bfad_s *bfad, enum bfa_port_role role); @@ -294,5 +293,6 @@ extern struct list_head bfad_list; extern int bfa_lun_queue_depth; extern int bfad_supported_fc4s; extern int bfa_linkup_delay; +extern struct mutex bfad_mutex; #endif /* __BFAD_DRV_H__ */ diff --git a/drivers/scsi/bfa/bfad_im.c b/drivers/scsi/bfa/bfad_im.c index 78f42aa5736..5b7cf539e50 100644 --- a/drivers/scsi/bfa/bfad_im.c +++ b/drivers/scsi/bfa/bfad_im.c @@ -30,6 +30,7 @@ BFA_TRC_FILE(LDRV, IM); DEFINE_IDR(bfad_im_port_index); struct scsi_transport_template *bfad_im_scsi_transport_template; +struct scsi_transport_template *bfad_im_scsi_vport_transport_template; static void bfad_im_itnim_work_handler(struct work_struct *work); static int bfad_im_queuecommand(struct scsi_cmnd *cmnd, void (*done)(struct scsi_cmnd *)); @@ -252,7 +253,6 @@ bfad_im_target_reset_send(struct bfad_s *bfad, struct scsi_cmnd *cmnd, struct bfa_itnim_s *bfa_itnim; bfa_status_t rc = BFA_STATUS_OK; - bfa_itnim = bfa_fcs_itnim_get_halitn(&itnim->fcs_itnim); tskim = bfa_tskim_alloc(&bfad->bfa, (struct bfad_tskim_s *) cmnd); if (!tskim) { BFA_DEV_PRINTF(bfad, BFA_ERR, @@ -513,11 +513,14 @@ void bfa_fcb_itnim_tov(struct bfad_itnim_s *itnim) * Allocate a Scsi_Host for a port. */ int -bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port) +bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port, + struct device *dev) { int error = 1; + mutex_lock(&bfad_mutex); if (!idr_pre_get(&bfad_im_port_index, GFP_KERNEL)) { + mutex_unlock(&bfad_mutex); printk(KERN_WARNING "idr_pre_get failure\n"); goto out; } @@ -525,10 +528,13 @@ bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port) error = idr_get_new(&bfad_im_port_index, im_port, &im_port->idr_id); if (error) { + mutex_unlock(&bfad_mutex); printk(KERN_WARNING "idr_get_new failure\n"); goto out; } + mutex_unlock(&bfad_mutex); + im_port->shost = bfad_os_scsi_host_alloc(im_port, bfad); if (!im_port->shost) { error = 1; @@ -542,12 +548,15 @@ bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port) im_port->shost->max_lun = MAX_FCP_LUN; im_port->shost->max_cmd_len = 16; im_port->shost->can_queue = bfad->cfg_data.ioc_queue_depth; - im_port->shost->transportt = bfad_im_scsi_transport_template; + if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE) + im_port->shost->transportt = bfad_im_scsi_transport_template; + else + im_port->shost->transportt = + bfad_im_scsi_vport_transport_template; - error = bfad_os_scsi_add_host(im_port->shost, im_port, bfad); + error = scsi_add_host(im_port->shost, dev); if (error) { - printk(KERN_WARNING "bfad_os_scsi_add_host failure %d\n", - error); + printk(KERN_WARNING "scsi_add_host failure %d\n", error); goto out_fc_rel; } @@ -559,7 +568,9 @@ bfad_im_scsi_host_alloc(struct bfad_s *bfad, struct bfad_im_port_s *im_port) out_fc_rel: scsi_host_put(im_port->shost); out_free_idr: + mutex_lock(&bfad_mutex); idr_remove(&bfad_im_port_index, im_port->idr_id); + mutex_unlock(&bfad_mutex); out: return error; } @@ -567,8 +578,6 @@ out: void bfad_im_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) { - unsigned long flags; - bfa_trc(bfad, bfad->inst_no); bfa_log(bfad->logmod, BFA_LOG_LINUX_SCSI_HOST_FREE, im_port->shost->host_no); @@ -578,9 +587,9 @@ bfad_im_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) scsi_remove_host(im_port->shost); scsi_host_put(im_port->shost); - spin_lock_irqsave(&bfad->bfad_lock, flags); + mutex_lock(&bfad_mutex); idr_remove(&bfad_im_port_index, im_port->idr_id); - spin_unlock_irqrestore(&bfad->bfad_lock, flags); + mutex_unlock(&bfad_mutex); } static void @@ -589,9 +598,11 @@ bfad_im_port_delete_handler(struct work_struct *work) struct bfad_im_port_s *im_port = container_of(work, struct bfad_im_port_s, port_delete_work); - bfad_im_scsi_host_free(im_port->bfad, im_port); - bfad_im_port_clean(im_port); - kfree(im_port); + if (im_port->port->pvb_type != BFAD_PORT_PHYS_BASE) { + im_port->flags |= BFAD_PORT_DELETE; + fc_vport_terminate(im_port->fc_vport); + } + } bfa_status_t @@ -690,23 +701,6 @@ bfad_im_probe_undo(struct bfad_s *bfad) } } - - - -int -bfad_os_scsi_add_host(struct Scsi_Host *shost, struct bfad_im_port_s *im_port, - struct bfad_s *bfad) -{ - struct device *dev; - - if (im_port->port->pvb_type == BFAD_PORT_PHYS_BASE) - dev = &bfad->pcidev->dev; - else - dev = &bfad->pport.im_port->shost->shost_gendev; - - return scsi_add_host(shost, dev); -} - struct Scsi_Host * bfad_os_scsi_host_alloc(struct bfad_im_port_s *im_port, struct bfad_s *bfad) { @@ -725,7 +719,8 @@ bfad_os_scsi_host_alloc(struct bfad_im_port_s *im_port, struct bfad_s *bfad) void bfad_os_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port) { - flush_workqueue(bfad->im->drv_workq); + if (!(im_port->flags & BFAD_PORT_DELETE)) + flush_workqueue(bfad->im->drv_workq); bfad_im_scsi_host_free(im_port->bfad, im_port); bfad_im_port_clean(im_port); kfree(im_port); @@ -830,6 +825,13 @@ bfad_im_module_init(void) if (!bfad_im_scsi_transport_template) return BFA_STATUS_ENOMEM; + bfad_im_scsi_vport_transport_template = + fc_attach_transport(&bfad_im_vport_fc_function_template); + if (!bfad_im_scsi_vport_transport_template) { + fc_release_transport(bfad_im_scsi_transport_template); + return BFA_STATUS_ENOMEM; + } + return BFA_STATUS_OK; } @@ -838,6 +840,8 @@ bfad_im_module_exit(void) { if (bfad_im_scsi_transport_template) fc_release_transport(bfad_im_scsi_transport_template); + if (bfad_im_scsi_vport_transport_template) + fc_release_transport(bfad_im_scsi_vport_transport_template); } void @@ -938,6 +942,7 @@ bfad_os_fc_host_init(struct bfad_im_port_s *im_port) bfa_os_htonll((bfa_fcs_port_get_nwwn(port->fcs_port))); fc_host_port_name(host) = bfa_os_htonll((bfa_fcs_port_get_pwwn(port->fcs_port))); + fc_host_max_npiv_vports(host) = bfa_lps_get_max_vport(&bfad->bfa); fc_host_supported_classes(host) = FC_COS_CLASS3; diff --git a/drivers/scsi/bfa/bfad_im.h b/drivers/scsi/bfa/bfad_im.h index 85ab2da2132..973cab4d09c 100644 --- a/drivers/scsi/bfa/bfad_im.h +++ b/drivers/scsi/bfa/bfad_im.h @@ -34,7 +34,7 @@ void bfad_im_port_online(struct bfad_s *bfad, struct bfad_port_s *port); void bfad_im_port_offline(struct bfad_s *bfad, struct bfad_port_s *port); void bfad_im_port_clean(struct bfad_im_port_s *im_port); int bfad_im_scsi_host_alloc(struct bfad_s *bfad, - struct bfad_im_port_s *im_port); + struct bfad_im_port_s *im_port, struct device *dev); void bfad_im_scsi_host_free(struct bfad_s *bfad, struct bfad_im_port_s *im_port); @@ -64,9 +64,11 @@ struct bfad_im_port_s { struct work_struct port_delete_work; int idr_id; u16 cur_scsi_id; + u16 flags; struct list_head binding_list; struct Scsi_Host *shost; struct list_head itnim_mapped_list; + struct fc_vport *fc_vport; }; enum bfad_itnim_state { @@ -140,6 +142,8 @@ void bfad_im_itnim_unmap(struct bfad_im_port_s *im_port, extern struct scsi_host_template bfad_im_scsi_host_template; extern struct scsi_host_template bfad_im_vport_template; extern struct fc_function_template bfad_im_fc_function_template; +extern struct fc_function_template bfad_im_vport_fc_function_template; extern struct scsi_transport_template *bfad_im_scsi_transport_template; +extern struct scsi_transport_template *bfad_im_scsi_vport_transport_template; #endif diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index 18352ff8210..3a66ca24c7b 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -347,6 +347,7 @@ int bnx2i_send_iscsi_login(struct bnx2i_conn *bnx2i_conn, login_wqe->cmd_sn = be32_to_cpu(login_hdr->cmdsn); login_wqe->exp_stat_sn = be32_to_cpu(login_hdr->exp_statsn); + login_wqe->flags = ISCSI_LOGIN_REQUEST_UPDATE_EXP_STAT_SN; login_wqe->resp_bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.resp_bd_dma; login_wqe->resp_bd_list_addr_hi = @@ -356,7 +357,6 @@ int bnx2i_send_iscsi_login(struct bnx2i_conn *bnx2i_conn, (bnx2i_conn->gen_pdu.resp_buf_size << ISCSI_LOGIN_REQUEST_RESP_BUFFER_LENGTH_SHIFT)); login_wqe->resp_buffer = dword; - login_wqe->flags = 0; login_wqe->bd_list_addr_lo = (u32) bnx2i_conn->gen_pdu.req_bd_dma; login_wqe->bd_list_addr_hi = (u32) ((u64) bnx2i_conn->gen_pdu.req_bd_dma >> 32); diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c index 5d9296c599f..af6a00a600f 100644 --- a/drivers/scsi/bnx2i/bnx2i_init.c +++ b/drivers/scsi/bnx2i/bnx2i_init.c @@ -17,8 +17,8 @@ static struct list_head adapter_list = LIST_HEAD_INIT(adapter_list); static u32 adapter_count; #define DRV_MODULE_NAME "bnx2i" -#define DRV_MODULE_VERSION "2.1.0" -#define DRV_MODULE_RELDATE "Dec 06, 2009" +#define DRV_MODULE_VERSION "2.1.1" +#define DRV_MODULE_RELDATE "Mar 24, 2010" static char version[] __devinitdata = "Broadcom NetXtreme II iSCSI Driver " DRV_MODULE_NAME \ @@ -26,7 +26,8 @@ static char version[] __devinitdata = MODULE_AUTHOR("Anil Veerabhadrappa <anilgv@broadcom.com>"); -MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709 iSCSI Driver"); +MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709/57710/57711" + " iSCSI Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); @@ -289,6 +290,7 @@ static int bnx2i_init_one(struct bnx2i_hba *hba, struct cnic_dev *cnic) int rc; mutex_lock(&bnx2i_dev_lock); + hba->cnic = cnic; rc = cnic->register_device(cnic, CNIC_ULP_ISCSI, hba); if (!rc) { hba->age++; @@ -335,8 +337,7 @@ void bnx2i_ulp_init(struct cnic_dev *dev) if (bnx2i_init_one(hba, dev)) { printk(KERN_ERR "bnx2i - hba %p init failed\n", hba); bnx2i_free_hba(hba); - } else - hba->cnic = dev; + } } diff --git a/drivers/scsi/cxgb3i/cxgb3i_init.c b/drivers/scsi/cxgb3i/cxgb3i_init.c index d0ab23a5835..685af369851 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_init.c +++ b/drivers/scsi/cxgb3i/cxgb3i_init.c @@ -104,8 +104,10 @@ static int __init cxgb3i_init_module(void) return err; err = cxgb3i_pdu_init(); - if (err < 0) + if (err < 0) { + cxgb3i_iscsi_cleanup(); return err; + } cxgb3_register_client(&t3c_client); diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index e8a0bc3efd4..6faf472f753 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -285,13 +285,11 @@ static struct request *get_req(struct scsi_device *sdev, int cmd, switch (cmd) { case MODE_SELECT: len = sizeof(short_trespass); - rq->cmd_flags |= REQ_RW; rq->cmd[1] = 0x10; rq->cmd[4] = len; break; case MODE_SELECT_10: len = sizeof(long_trespass); - rq->cmd_flags |= REQ_RW; rq->cmd[1] = 0x10; rq->cmd[8] = len; break; diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 54c870b8c32..9276121db1e 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -74,6 +74,7 @@ static int fcoe_rcv(struct sk_buff *, struct net_device *, static int fcoe_percpu_receive_thread(void *); static void fcoe_clean_pending_queue(struct fc_lport *); static void fcoe_percpu_clean(struct fc_lport *); +static int fcoe_link_speed_update(struct fc_lport *); static int fcoe_link_ok(struct fc_lport *); static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); @@ -146,6 +147,7 @@ static int fcoe_vport_destroy(struct fc_vport *); static int fcoe_vport_create(struct fc_vport *, bool disabled); static int fcoe_vport_disable(struct fc_vport *, bool disable); static void fcoe_set_vport_symbolic_name(struct fc_vport *); +static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *); static struct libfc_function_template fcoe_libfc_fcn_templ = { .frame_send = fcoe_xmit, @@ -153,6 +155,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = { .ddp_done = fcoe_ddp_done, .elsct_send = fcoe_elsct_send, .get_lesb = fcoe_get_lesb, + .lport_set_port_id = fcoe_set_port_id, }; struct fc_function_template fcoe_transport_function = { @@ -629,6 +632,8 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) port->fcoe_pending_queue_active = 0; setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport); + fcoe_link_speed_update(lport); + if (!lport->vport) { /* * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN: @@ -653,15 +658,13 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) /** * fcoe_shost_config() - Set up the SCSI host associated with a local port * @lport: The local port - * @shost: The SCSI host to associate with the local port * @dev: The device associated with the SCSI host * * Must be called after fcoe_lport_config() and fcoe_netdev_config() * * Returns: 0 for success */ -static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost, - struct device *dev) +static int fcoe_shost_config(struct fc_lport *lport, struct device *dev) { int rc = 0; @@ -669,6 +672,8 @@ static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost, lport->host->max_lun = FCOE_MAX_LUN; lport->host->max_id = FCOE_MAX_FCP_TARGET; lport->host->max_channel = 0; + lport->host->max_cmd_len = FCOE_MAX_CMD_LEN; + if (lport->vport) lport->host->transportt = fcoe_vport_transport_template; else @@ -796,6 +801,12 @@ skip_oem: /** * fcoe_if_destroy() - Tear down a SW FCoE instance * @lport: The local port to be destroyed + * + * Locking: must be called with the RTNL mutex held and RTNL mutex + * needed to be dropped by this function since not dropping RTNL + * would cause circular locking warning on synchronous fip worker + * cancelling thru fcoe_interface_put invoked by this function. + * */ static void fcoe_if_destroy(struct fc_lport *lport) { @@ -818,7 +829,6 @@ static void fcoe_if_destroy(struct fc_lport *lport) /* Free existing transmit skbs */ fcoe_clean_pending_queue(lport); - rtnl_lock(); if (!is_zero_ether_addr(port->data_src_addr)) dev_uc_del(netdev, port->data_src_addr); rtnl_unlock(); @@ -841,6 +851,7 @@ static void fcoe_if_destroy(struct fc_lport *lport) /* Release the Scsi_Host */ scsi_host_put(lport->host); + module_put(THIS_MODULE); } /** @@ -897,7 +908,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, struct net_device *netdev = fcoe->netdev; struct fc_lport *lport = NULL; struct fcoe_port *port; - struct Scsi_Host *shost; int rc; /* * parent is only a vport if npiv is 1, @@ -919,7 +929,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, rc = -ENOMEM; goto out; } - shost = lport->host; port = lport_priv(lport); port->lport = lport; port->fcoe = fcoe; @@ -934,7 +943,8 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, } if (npiv) { - FCOE_NETDEV_DBG(netdev, "Setting vport names, 0x%llX 0x%llX\n", + FCOE_NETDEV_DBG(netdev, "Setting vport names, " + "%16.16llx %16.16llx\n", vport->node_name, vport->port_name); fc_set_wwnn(lport, vport->node_name); fc_set_wwpn(lport, vport->port_name); @@ -949,7 +959,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, } /* configure lport scsi host properties */ - rc = fcoe_shost_config(lport, shost, parent); + rc = fcoe_shost_config(lport, parent); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure shost for the " "interface\n"); @@ -1073,7 +1083,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu) struct sk_buff *skb; #ifdef CONFIG_SMP struct fcoe_percpu_s *p0; - unsigned targ_cpu = smp_processor_id(); + unsigned targ_cpu = get_cpu(); #endif /* CONFIG_SMP */ FCOE_DBG("Destroying receive thread for CPU %d\n", cpu); @@ -1129,6 +1139,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu) kfree_skb(skb); spin_unlock_bh(&p->fcoe_rx_list.lock); } + put_cpu(); #else /* * This a non-SMP scenario where the singular Rx thread is @@ -1297,8 +1308,8 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, return 0; err: - fc_lport_get_stats(lport)->ErrorFrames++; - + per_cpu_ptr(lport->dev_stats, get_cpu())->ErrorFrames++; + put_cpu(); err2: kfree_skb(skb); return -1; @@ -1444,7 +1455,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) return 0; } - if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) && + if (unlikely(fh->fh_type == FC_TYPE_ELS) && fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb)) return 0; @@ -1527,9 +1538,10 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) skb_shinfo(skb)->gso_size = 0; } /* update tx stats: regardless if LLD fails */ - stats = fc_lport_get_stats(lport); + stats = per_cpu_ptr(lport->dev_stats, get_cpu()); stats->TxFrames++; stats->TxWords += wlen; + put_cpu(); /* send down to lld */ fr_dev(fp) = lport; @@ -1563,7 +1575,6 @@ static void fcoe_recv_frame(struct sk_buff *skb) struct fc_frame_header *fh; struct fcoe_crc_eof crc_eof; struct fc_frame *fp; - u8 *mac = NULL; struct fcoe_port *port; struct fcoe_hdr *hp; @@ -1583,13 +1594,9 @@ static void fcoe_recv_frame(struct sk_buff *skb) skb_end_pointer(skb), skb->csum, skb->dev ? skb->dev->name : "<NULL>"); - /* - * Save source MAC address before discarding header. - */ port = lport_priv(lport); if (skb_is_nonlinear(skb)) skb_linearize(skb); /* not ideal */ - mac = eth_hdr(skb)->h_source; /* * Frame length checks and setting up the header pointers @@ -1598,7 +1605,7 @@ static void fcoe_recv_frame(struct sk_buff *skb) hp = (struct fcoe_hdr *) skb_network_header(skb); fh = (struct fc_frame_header *) skb_transport_header(skb); - stats = fc_lport_get_stats(lport); + stats = per_cpu_ptr(lport->dev_stats, get_cpu()); if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { if (stats->ErrorFrames < 5) printk(KERN_WARNING "fcoe: FCoE version " @@ -1607,9 +1614,7 @@ static void fcoe_recv_frame(struct sk_buff *skb) "initiator supports version " "%x\n", FC_FCOE_DECAPS_VER(hp), FC_FCOE_VER); - stats->ErrorFrames++; - kfree_skb(skb); - return; + goto drop; } skb_pull(skb, sizeof(struct fcoe_hdr)); @@ -1624,16 +1629,12 @@ static void fcoe_recv_frame(struct sk_buff *skb) fr_sof(fp) = hp->fcoe_sof; /* Copy out the CRC and EOF trailer for access */ - if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { - kfree_skb(skb); - return; - } + if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) + goto drop; fr_eof(fp) = crc_eof.fcoe_eof; fr_crc(fp) = crc_eof.fcoe_crc32; - if (pskb_trim(skb, fr_len)) { - kfree_skb(skb); - return; - } + if (pskb_trim(skb, fr_len)) + goto drop; /* * We only check CRC if no offload is available and if it is @@ -1647,25 +1648,27 @@ static void fcoe_recv_frame(struct sk_buff *skb) fr_flags(fp) |= FCPHF_CRC_UNCHECKED; fh = fc_frame_header_get(fp); - if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && - fh->fh_type == FC_TYPE_FCP) { - fc_exch_recv(lport, fp); - return; - } - if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { + if ((fh->fh_r_ctl != FC_RCTL_DD_SOL_DATA || + fh->fh_type != FC_TYPE_FCP) && + (fr_flags(fp) & FCPHF_CRC_UNCHECKED)) { if (le32_to_cpu(fr_crc(fp)) != ~crc32(~0, skb->data, fr_len)) { if (stats->InvalidCRCCount < 5) printk(KERN_WARNING "fcoe: dropping " "frame with CRC error\n"); stats->InvalidCRCCount++; - stats->ErrorFrames++; - fc_frame_free(fp); - return; + goto drop; } fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; } + put_cpu(); fc_exch_recv(lport, fp); + return; + +drop: + stats->ErrorFrames++; + put_cpu(); + kfree_skb(skb); } /** @@ -1835,11 +1838,15 @@ static int fcoe_device_notification(struct notifier_block *notifier, FCOE_NETDEV_DBG(netdev, "Unknown event %ld " "from netdev netlink\n", event); } + + fcoe_link_speed_update(lport); + if (link_possible && !fcoe_link_ok(lport)) fcoe_ctlr_link_up(&fcoe->ctlr); else if (fcoe_ctlr_link_down(&fcoe->ctlr)) { - stats = fc_lport_get_stats(lport); + stats = per_cpu_ptr(lport->dev_stats, get_cpu()); stats->LinkFailureCount++; + put_cpu(); fcoe_clean_pending_queue(lport); } out: @@ -1901,13 +1908,19 @@ static int fcoe_disable(const char *buffer, struct kernel_param *kp) goto out_nodev; } - rtnl_lock(); + if (!rtnl_trylock()) { + dev_put(netdev); + mutex_unlock(&fcoe_config_mutex); + return restart_syscall(); + } + fcoe = fcoe_hostlist_lookup_port(netdev); rtnl_unlock(); - if (fcoe) + if (fcoe) { fc_fabric_logoff(fcoe->ctlr.lp); - else + fcoe_ctlr_link_down(&fcoe->ctlr); + } else rc = -ENODEV; dev_put(netdev); @@ -1950,13 +1963,20 @@ static int fcoe_enable(const char *buffer, struct kernel_param *kp) goto out_nodev; } - rtnl_lock(); + if (!rtnl_trylock()) { + dev_put(netdev); + mutex_unlock(&fcoe_config_mutex); + return restart_syscall(); + } + fcoe = fcoe_hostlist_lookup_port(netdev); rtnl_unlock(); - if (fcoe) + if (fcoe) { + if (!fcoe_link_ok(fcoe->ctlr.lp)) + fcoe_ctlr_link_up(&fcoe->ctlr); rc = fc_fabric_login(fcoe->ctlr.lp); - else + } else rc = -ENODEV; dev_put(netdev); @@ -1999,7 +2019,12 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) goto out_nodev; } - rtnl_lock(); + if (!rtnl_trylock()) { + dev_put(netdev); + mutex_unlock(&fcoe_config_mutex); + return restart_syscall(); + } + fcoe = fcoe_hostlist_lookup_port(netdev); if (!fcoe) { rtnl_unlock(); @@ -2008,9 +2033,8 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) } list_del(&fcoe->list); fcoe_interface_cleanup(fcoe); - rtnl_unlock(); + /* RTNL mutex is dropped by fcoe_if_destroy */ fcoe_if_destroy(fcoe->ctlr.lp); - module_put(THIS_MODULE); out_putdev: dev_put(netdev); @@ -2029,6 +2053,8 @@ static void fcoe_destroy_work(struct work_struct *work) port = container_of(work, struct fcoe_port, destroy_work); mutex_lock(&fcoe_config_mutex); + rtnl_lock(); + /* RTNL mutex is dropped by fcoe_if_destroy */ fcoe_if_destroy(port->lport); mutex_unlock(&fcoe_config_mutex); } @@ -2050,6 +2076,12 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) struct net_device *netdev; mutex_lock(&fcoe_config_mutex); + + if (!rtnl_trylock()) { + mutex_unlock(&fcoe_config_mutex); + return restart_syscall(); + } + #ifdef CONFIG_FCOE_MODULE /* * Make sure the module has been initialized, and is not about to be @@ -2058,7 +2090,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) */ if (THIS_MODULE->state != MODULE_STATE_LIVE) { rc = -ENODEV; - goto out_nodev; + goto out_nomod; } #endif @@ -2067,7 +2099,6 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) goto out_nomod; } - rtnl_lock(); netdev = fcoe_if_to_netdev(buffer); if (!netdev) { rc = -ENODEV; @@ -2122,35 +2153,27 @@ out_free: out_putdev: dev_put(netdev); out_nodev: - rtnl_unlock(); module_put(THIS_MODULE); out_nomod: + rtnl_unlock(); mutex_unlock(&fcoe_config_mutex); return rc; } /** - * fcoe_link_ok() - Check if the link is OK for a local port - * @lport: The local port to check link on - * - * Any permanently-disqualifying conditions have been previously checked. - * This also updates the speed setting, which may change with link for 100/1000. - * - * This function should probably be checking for PAUSE support at some point - * in the future. Currently Per-priority-pause is not determinable using - * ethtool, so we shouldn't be restrictive until that problem is resolved. - * - * Returns: 0 if link is OK for use by FCoE. + * fcoe_link_speed_update() - Update the supported and actual link speeds + * @lport: The local port to update speeds for * + * Returns: 0 if the ethtool query was successful + * -1 if the ethtool query failed */ -int fcoe_link_ok(struct fc_lport *lport) +int fcoe_link_speed_update(struct fc_lport *lport) { struct fcoe_port *port = lport_priv(lport); struct net_device *netdev = port->fcoe->netdev; struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - if ((netdev->flags & IFF_UP) && netif_carrier_ok(netdev) && - (!dev_ethtool_get_settings(netdev, &ecmd))) { + if (!dev_ethtool_get_settings(netdev, &ecmd)) { lport->link_supported_speeds &= ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); if (ecmd.supported & (SUPPORTED_1000baseT_Half | @@ -2170,6 +2193,23 @@ int fcoe_link_ok(struct fc_lport *lport) } /** + * fcoe_link_ok() - Check if the link is OK for a local port + * @lport: The local port to check link on + * + * Returns: 0 if link is UP and OK, -1 if not + * + */ +int fcoe_link_ok(struct fc_lport *lport) +{ + struct fcoe_port *port = lport_priv(lport); + struct net_device *netdev = port->fcoe->netdev; + + if (netif_oper_up(netdev)) + return 0; + return -1; +} + +/** * fcoe_percpu_clean() - Clear all pending skbs for an local port * @lport: The local port whose skbs are to be cleared * @@ -2631,3 +2671,25 @@ static void fcoe_get_lesb(struct fc_lport *lport, lesb->lesb_miss_fka = htonl(mdac); lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors); } + +/** + * fcoe_set_port_id() - Callback from libfc when Port_ID is set. + * @lport: the local port + * @port_id: the port ID + * @fp: the received frame, if any, that caused the port_id to be set. + * + * This routine handles the case where we received a FLOGI and are + * entering point-to-point mode. We need to call fcoe_ctlr_recv_flogi() + * so it can set the non-mapped mode and gateway address. + * + * The FLOGI LS_ACC is handled by fcoe_flogi_resp(). + */ +static void fcoe_set_port_id(struct fc_lport *lport, + u32 port_id, struct fc_frame *fp) +{ + struct fcoe_port *port = lport_priv(lport); + struct fcoe_interface *fcoe = port->fcoe; + + if (fp && fc_frame_payload_op(fp) == ELS_FLOGI) + fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp); +} diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 3440da48d16..50aaa4bcfc5 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -51,7 +51,7 @@ MODULE_LICENSE("GPL v2"); #define FCOE_CTLR_DEF_FKA FIP_DEF_FKA /* default keep alive (mS) */ static void fcoe_ctlr_timeout(unsigned long); -static void fcoe_ctlr_link_work(struct work_struct *); +static void fcoe_ctlr_timer_work(struct work_struct *); static void fcoe_ctlr_recv_work(struct work_struct *); static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS; @@ -116,7 +116,7 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip) spin_lock_init(&fip->lock); fip->flogi_oxid = FC_XID_UNKNOWN; setup_timer(&fip->timer, fcoe_ctlr_timeout, (unsigned long)fip); - INIT_WORK(&fip->link_work, fcoe_ctlr_link_work); + INIT_WORK(&fip->timer_work, fcoe_ctlr_timer_work); INIT_WORK(&fip->recv_work, fcoe_ctlr_recv_work); skb_queue_head_init(&fip->fip_recv_list); } @@ -164,7 +164,7 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip) fcoe_ctlr_reset_fcfs(fip); spin_unlock_bh(&fip->lock); del_timer_sync(&fip->timer); - cancel_work_sync(&fip->link_work); + cancel_work_sync(&fip->timer_work); } EXPORT_SYMBOL(fcoe_ctlr_destroy); @@ -257,14 +257,10 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip) { spin_lock_bh(&fip->lock); if (fip->state == FIP_ST_NON_FIP || fip->state == FIP_ST_AUTO) { - fip->last_link = 1; - fip->link = 1; spin_unlock_bh(&fip->lock); fc_linkup(fip->lp); } else if (fip->state == FIP_ST_LINK_WAIT) { fip->state = fip->mode; - fip->last_link = 1; - fip->link = 1; spin_unlock_bh(&fip->lock); if (fip->state == FIP_ST_AUTO) LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n"); @@ -306,9 +302,7 @@ int fcoe_ctlr_link_down(struct fcoe_ctlr *fip) LIBFCOE_FIP_DBG(fip, "link down.\n"); spin_lock_bh(&fip->lock); fcoe_ctlr_reset(fip); - link_dropped = fip->link; - fip->link = 0; - fip->last_link = 0; + link_dropped = fip->state != FIP_ST_LINK_WAIT; fip->state = FIP_ST_LINK_WAIT; spin_unlock_bh(&fip->lock); @@ -349,7 +343,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, fcf = fip->sel_fcf; lp = fip->lp; - if (!fcf || !fc_host_port_id(lp->host)) + if (!fcf || !lp->port_id) return; len = sizeof(*kal) + ports * sizeof(*vn); @@ -380,8 +374,8 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, vn->fd_desc.fip_dtype = FIP_DT_VN_ID; vn->fd_desc.fip_dlen = sizeof(*vn) / FIP_BPW; memcpy(vn->fd_mac, fip->get_src_addr(lport), ETH_ALEN); - hton24(vn->fd_fc_id, fc_host_port_id(lp->host)); - put_unaligned_be64(lp->wwpn, &vn->fd_wwpn); + hton24(vn->fd_fc_id, lport->port_id); + put_unaligned_be64(lport->wwpn, &vn->fd_wwpn); } skb_put(skb, len); skb->protocol = htons(ETH_P_FIP); @@ -445,13 +439,18 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport, cap->encaps.fd_desc.fip_dlen = dlen / FIP_BPW; mac = (struct fip_mac_desc *)skb_put(skb, sizeof(*mac)); - memset(mac, 0, sizeof(mac)); + memset(mac, 0, sizeof(*mac)); mac->fd_desc.fip_dtype = FIP_DT_MAC; mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW; - if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) + if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) { memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN); - else if (fip->spma) + } else if (fip_flags & FIP_FL_SPMA) { + LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with SPMA\n"); memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN); + } else { + LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with FPMA\n"); + /* FPMA only FLOGI must leave the MAC desc set to all 0s */ + } skb->protocol = htons(ETH_P_FIP); skb_reset_mac_header(skb); @@ -556,7 +555,7 @@ EXPORT_SYMBOL(fcoe_ctlr_els_send); * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller * @fip: The FCoE controller to free FCFs on * - * Called with lock held. + * Called with lock held and preemption disabled. * * An FCF is considered old if we have missed three advertisements. * That is, there have been no valid advertisement from it for three @@ -573,17 +572,20 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) struct fcoe_fcf *next; unsigned long sel_time = 0; unsigned long mda_time = 0; + struct fcoe_dev_stats *stats; list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { mda_time = fcf->fka_period + (fcf->fka_period >> 1); if ((fip->sel_fcf == fcf) && (time_after(jiffies, fcf->time + mda_time))) { mod_timer(&fip->timer, jiffies + mda_time); - fc_lport_get_stats(fip->lp)->MissDiscAdvCount++; + stats = per_cpu_ptr(fip->lp->dev_stats, + smp_processor_id()); + stats->MissDiscAdvCount++; printk(KERN_INFO "libfcoe: host%d: Missing Discovery " - "Advertisement for fab %llx count %lld\n", + "Advertisement for fab %16.16llx count %lld\n", fip->lp->host->host_no, fcf->fabric_name, - fc_lport_get_stats(fip->lp)->MissDiscAdvCount); + stats->MissDiscAdvCount); } if (time_after(jiffies, fcf->time + fcf->fka_period * 3 + msecs_to_jiffies(FIP_FCF_FUZZ * 3))) { @@ -593,7 +595,9 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) WARN_ON(!fip->fcf_count); fip->fcf_count--; kfree(fcf); - fc_lport_get_stats(fip->lp)->VLinkFailureCount++; + stats = per_cpu_ptr(fip->lp->dev_stats, + smp_processor_id()); + stats->VLinkFailureCount++; } else if (fcoe_ctlr_mtu_valid(fcf) && (!sel_time || time_before(sel_time, fcf->time))) { sel_time = fcf->time; @@ -776,7 +780,8 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) mtu_valid = fcoe_ctlr_mtu_valid(fcf); fcf->time = jiffies; if (!found) { - LIBFCOE_FIP_DBG(fip, "New FCF for fab %llx map %x val %d\n", + LIBFCOE_FIP_DBG(fip, "New FCF for fab %16.16llx " + "map %x val %d\n", fcf->fabric_name, fcf->fc_map, mtu_valid); } @@ -906,9 +911,10 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) fr_eof(fp) = FC_EOF_T; fr_dev(fp) = lport; - stats = fc_lport_get_stats(lport); + stats = per_cpu_ptr(lport->dev_stats, get_cpu()); stats->RxFrames++; stats->RxWords += skb->len / FIP_BPW; + put_cpu(); fc_exch_recv(lport, fp); return; @@ -942,9 +948,8 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, u32 desc_mask; LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n"); - if (!fcf) - return; - if (!fcf || !fc_host_port_id(lport->host)) + + if (!fcf || !lport->port_id) return; /* @@ -982,8 +987,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, if (compare_ether_addr(vp->fd_mac, fip->get_src_addr(lport)) == 0 && get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn && - ntoh24(vp->fd_fc_id) == - fc_host_port_id(lport->host)) + ntoh24(vp->fd_fc_id) == lport->port_id) desc_mask &= ~BIT(FIP_DT_VN_ID); break; default: @@ -1006,7 +1010,8 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n"); spin_lock_bh(&fip->lock); - fc_lport_get_stats(lport)->VLinkFailureCount++; + per_cpu_ptr(lport->dev_stats, + smp_processor_id())->VLinkFailureCount++; fcoe_ctlr_reset(fip); spin_unlock_bh(&fip->lock); @@ -1102,15 +1107,17 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) struct fcoe_fcf *best = NULL; list_for_each_entry(fcf, &fip->fcfs, list) { - LIBFCOE_FIP_DBG(fip, "consider FCF for fab %llx VFID %d map %x " - "val %d\n", fcf->fabric_name, fcf->vfid, + LIBFCOE_FIP_DBG(fip, "consider FCF for fab %16.16llx " + "VFID %d map %x val %d\n", + fcf->fabric_name, fcf->vfid, fcf->fc_map, fcoe_ctlr_mtu_valid(fcf)); if (!fcoe_ctlr_fcf_usable(fcf)) { - LIBFCOE_FIP_DBG(fip, "FCF for fab %llx map %x %svalid " - "%savailable\n", fcf->fabric_name, - fcf->fc_map, (fcf->flags & FIP_FL_SOL) - ? "" : "in", (fcf->flags & FIP_FL_AVAIL) - ? "" : "un"); + LIBFCOE_FIP_DBG(fip, "FCF for fab %16.16llx " + "map %x %svalid %savailable\n", + fcf->fabric_name, fcf->fc_map, + (fcf->flags & FIP_FL_SOL) ? "" : "in", + (fcf->flags & FIP_FL_AVAIL) ? + "" : "un"); continue; } if (!best) { @@ -1175,7 +1182,7 @@ static void fcoe_ctlr_timeout(unsigned long arg) "Starting FCF discovery.\n", fip->lp->host->host_no); fip->reset_req = 1; - schedule_work(&fip->link_work); + schedule_work(&fip->timer_work); } } @@ -1201,43 +1208,31 @@ static void fcoe_ctlr_timeout(unsigned long arg) mod_timer(&fip->timer, next_timer); } if (fip->send_ctlr_ka || fip->send_port_ka) - schedule_work(&fip->link_work); + schedule_work(&fip->timer_work); spin_unlock_bh(&fip->lock); } /** - * fcoe_ctlr_link_work() - Worker thread function for link changes + * fcoe_ctlr_timer_work() - Worker thread function for timer work * @work: Handle to a FCoE controller * - * See if the link status has changed and if so, report it. - * - * This is here because fc_linkup() and fc_linkdown() must not + * Sends keep-alives and resets which must not * be called from the timer directly, since they use a mutex. */ -static void fcoe_ctlr_link_work(struct work_struct *work) +static void fcoe_ctlr_timer_work(struct work_struct *work) { struct fcoe_ctlr *fip; struct fc_lport *vport; u8 *mac; - int link; - int last_link; int reset; - fip = container_of(work, struct fcoe_ctlr, link_work); + fip = container_of(work, struct fcoe_ctlr, timer_work); spin_lock_bh(&fip->lock); - last_link = fip->last_link; - link = fip->link; - fip->last_link = link; reset = fip->reset_req; fip->reset_req = 0; spin_unlock_bh(&fip->lock); - if (last_link != link) { - if (link) - fc_linkup(fip->lp); - else - fc_linkdown(fip->lp); - } else if (reset && link) + if (reset) fc_lport_reset(fip->lp); if (fip->send_ctlr_ka) { @@ -1334,9 +1329,9 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport, if (fip->state == FIP_ST_AUTO || fip->state == FIP_ST_NON_FIP) { memcpy(fip->dest_addr, sa, ETH_ALEN); fip->map_dest = 0; - if (fip->state == FIP_ST_NON_FIP) - LIBFCOE_FIP_DBG(fip, "received FLOGI REQ, " - "using non-FIP mode\n"); + if (fip->state == FIP_ST_AUTO) + LIBFCOE_FIP_DBG(fip, "received non-FIP FLOGI. " + "Setting non-FIP mode\n"); fip->state = FIP_ST_NON_FIP; } spin_unlock_bh(&fip->lock); diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index 3966c71d009..19338e0ba2c 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -36,7 +36,7 @@ #define DRV_NAME "fnic" #define DRV_DESCRIPTION "Cisco FCoE HBA Driver" -#define DRV_VERSION "1.4.0.98" +#define DRV_VERSION "1.4.0.145" #define PFX DRV_NAME ": " #define DFX DRV_NAME "%d: " @@ -45,7 +45,7 @@ #define FNIC_IO_LOCKS 64 /* IO locks: power of 2 */ #define FNIC_DFLT_QUEUE_DEPTH 32 #define FNIC_STATS_RATE_LIMIT 4 /* limit rate at which stats are pulled up */ -#define FNIC_MAX_CMD_LEN 16 /* Supported CDB length */ + /* * Tag bits used for special requests. */ diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index 5259888fbfb..2b48d79bad9 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -617,7 +617,7 @@ void fnic_flush_tx(struct fnic *fnic) struct sk_buff *skb; struct fc_frame *fp; - while ((skb = skb_dequeue(&fnic->frame_queue))) { + while ((skb = skb_dequeue(&fnic->tx_queue))) { fp = (struct fc_frame *)skb; fnic_send_frame(fnic, fp); } diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 97b212570bc..265e73d9cd6 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -556,7 +556,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev, } host->max_lun = fnic->config.luns_per_tgt; host->max_id = FNIC_MAX_FCP_TARGET; - host->max_cmd_len = FNIC_MAX_CMD_LEN; + host->max_cmd_len = FCOE_MAX_CMD_LEN; fnic_get_res_counts(fnic); diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 35a4b3073ec..a765fe7a55c 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -3842,7 +3842,7 @@ int __init option_setup(char *str) TRACE2(("option_setup() str %s\n", str ? str:"NULL")); - while (cur && isdigit(*cur) && i <= MAXHA) { + while (cur && isdigit(*cur) && i < MAXHA) { ints[i++] = simple_strtoul(cur, NULL, 0); if ((cur = strchr(cur, ',')) != NULL) cur++; } diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c index 48f406850c6..18b7102bb80 100644 --- a/drivers/scsi/gvp11.c +++ b/drivers/scsi/gvp11.c @@ -19,332 +19,334 @@ #include "wd33c93.h" #include "gvp11.h" -#include<linux/stat.h> +#include <linux/stat.h> -#define DMA(ptr) ((gvp11_scsiregs *)((ptr)->base)) -#define HDATA(ptr) ((struct WD33C93_hostdata *)((ptr)->hostdata)) -static irqreturn_t gvp11_intr (int irq, void *_instance) +#define DMA(ptr) ((gvp11_scsiregs *)((ptr)->base)) + +static irqreturn_t gvp11_intr(int irq, void *_instance) { - unsigned long flags; - unsigned int status; - struct Scsi_Host *instance = (struct Scsi_Host *)_instance; - - status = DMA(instance)->CNTR; - if (!(status & GVP11_DMAC_INT_PENDING)) - return IRQ_NONE; - - spin_lock_irqsave(instance->host_lock, flags); - wd33c93_intr(instance); - spin_unlock_irqrestore(instance->host_lock, flags); - return IRQ_HANDLED; + unsigned long flags; + unsigned int status; + struct Scsi_Host *instance = (struct Scsi_Host *)_instance; + + status = DMA(instance)->CNTR; + if (!(status & GVP11_DMAC_INT_PENDING)) + return IRQ_NONE; + + spin_lock_irqsave(instance->host_lock, flags); + wd33c93_intr(instance); + spin_unlock_irqrestore(instance->host_lock, flags); + return IRQ_HANDLED; } static int gvp11_xfer_mask = 0; -void gvp11_setup (char *str, int *ints) +void gvp11_setup(char *str, int *ints) { - gvp11_xfer_mask = ints[1]; + gvp11_xfer_mask = ints[1]; } static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { - unsigned short cntr = GVP11_DMAC_INT_ENABLE; - unsigned long addr = virt_to_bus(cmd->SCp.ptr); - int bank_mask; - static int scsi_alloc_out_of_range = 0; - - /* use bounce buffer if the physical address is bad */ - if (addr & HDATA(cmd->device->host)->dma_xfer_mask) - { - HDATA(cmd->device->host)->dma_bounce_len = (cmd->SCp.this_residual + 511) - & ~0x1ff; - - if( !scsi_alloc_out_of_range ) { - HDATA(cmd->device->host)->dma_bounce_buffer = - kmalloc (HDATA(cmd->device->host)->dma_bounce_len, GFP_KERNEL); - HDATA(cmd->device->host)->dma_buffer_pool = BUF_SCSI_ALLOCED; - } + struct Scsi_Host *instance = cmd->device->host; + struct WD33C93_hostdata *hdata = shost_priv(instance); + unsigned short cntr = GVP11_DMAC_INT_ENABLE; + unsigned long addr = virt_to_bus(cmd->SCp.ptr); + int bank_mask; + static int scsi_alloc_out_of_range = 0; + + /* use bounce buffer if the physical address is bad */ + if (addr & hdata->dma_xfer_mask) { + hdata->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff; + + if (!scsi_alloc_out_of_range) { + hdata->dma_bounce_buffer = + kmalloc(hdata->dma_bounce_len, GFP_KERNEL); + hdata->dma_buffer_pool = BUF_SCSI_ALLOCED; + } - if (scsi_alloc_out_of_range || - !HDATA(cmd->device->host)->dma_bounce_buffer) { - HDATA(cmd->device->host)->dma_bounce_buffer = - amiga_chip_alloc(HDATA(cmd->device->host)->dma_bounce_len, - "GVP II SCSI Bounce Buffer"); + if (scsi_alloc_out_of_range || + !hdata->dma_bounce_buffer) { + hdata->dma_bounce_buffer = + amiga_chip_alloc(hdata->dma_bounce_len, + "GVP II SCSI Bounce Buffer"); - if(!HDATA(cmd->device->host)->dma_bounce_buffer) - { - HDATA(cmd->device->host)->dma_bounce_len = 0; - return 1; - } + if (!hdata->dma_bounce_buffer) { + hdata->dma_bounce_len = 0; + return 1; + } - HDATA(cmd->device->host)->dma_buffer_pool = BUF_CHIP_ALLOCED; - } + hdata->dma_buffer_pool = BUF_CHIP_ALLOCED; + } - /* check if the address of the bounce buffer is OK */ - addr = virt_to_bus(HDATA(cmd->device->host)->dma_bounce_buffer); - - if (addr & HDATA(cmd->device->host)->dma_xfer_mask) { - /* fall back to Chip RAM if address out of range */ - if( HDATA(cmd->device->host)->dma_buffer_pool == BUF_SCSI_ALLOCED) { - kfree (HDATA(cmd->device->host)->dma_bounce_buffer); - scsi_alloc_out_of_range = 1; - } else { - amiga_chip_free (HDATA(cmd->device->host)->dma_bounce_buffer); - } - - HDATA(cmd->device->host)->dma_bounce_buffer = - amiga_chip_alloc(HDATA(cmd->device->host)->dma_bounce_len, - "GVP II SCSI Bounce Buffer"); - - if(!HDATA(cmd->device->host)->dma_bounce_buffer) - { - HDATA(cmd->device->host)->dma_bounce_len = 0; - return 1; - } - - addr = virt_to_bus(HDATA(cmd->device->host)->dma_bounce_buffer); - HDATA(cmd->device->host)->dma_buffer_pool = BUF_CHIP_ALLOCED; - } - - if (!dir_in) { - /* copy to bounce buffer for a write */ - memcpy (HDATA(cmd->device->host)->dma_bounce_buffer, - cmd->SCp.ptr, cmd->SCp.this_residual); + /* check if the address of the bounce buffer is OK */ + addr = virt_to_bus(hdata->dma_bounce_buffer); + + if (addr & hdata->dma_xfer_mask) { + /* fall back to Chip RAM if address out of range */ + if (hdata->dma_buffer_pool == BUF_SCSI_ALLOCED) { + kfree(hdata->dma_bounce_buffer); + scsi_alloc_out_of_range = 1; + } else { + amiga_chip_free(hdata->dma_bounce_buffer); + } + + hdata->dma_bounce_buffer = + amiga_chip_alloc(hdata->dma_bounce_len, + "GVP II SCSI Bounce Buffer"); + + if (!hdata->dma_bounce_buffer) { + hdata->dma_bounce_len = 0; + return 1; + } + + addr = virt_to_bus(hdata->dma_bounce_buffer); + hdata->dma_buffer_pool = BUF_CHIP_ALLOCED; + } + + if (!dir_in) { + /* copy to bounce buffer for a write */ + memcpy(hdata->dma_bounce_buffer, cmd->SCp.ptr, + cmd->SCp.this_residual); + } } - } - /* setup dma direction */ - if (!dir_in) - cntr |= GVP11_DMAC_DIR_WRITE; + /* setup dma direction */ + if (!dir_in) + cntr |= GVP11_DMAC_DIR_WRITE; - HDATA(cmd->device->host)->dma_dir = dir_in; - DMA(cmd->device->host)->CNTR = cntr; + hdata->dma_dir = dir_in; + DMA(cmd->device->host)->CNTR = cntr; - /* setup DMA *physical* address */ - DMA(cmd->device->host)->ACR = addr; + /* setup DMA *physical* address */ + DMA(cmd->device->host)->ACR = addr; - if (dir_in) - /* invalidate any cache */ - cache_clear (addr, cmd->SCp.this_residual); - else - /* push any dirty cache */ - cache_push (addr, cmd->SCp.this_residual); + if (dir_in) { + /* invalidate any cache */ + cache_clear(addr, cmd->SCp.this_residual); + } else { + /* push any dirty cache */ + cache_push(addr, cmd->SCp.this_residual); + } - if ((bank_mask = (~HDATA(cmd->device->host)->dma_xfer_mask >> 18) & 0x01c0)) - DMA(cmd->device->host)->BANK = bank_mask & (addr >> 18); + bank_mask = (~hdata->dma_xfer_mask >> 18) & 0x01c0; + if (bank_mask) + DMA(cmd->device->host)->BANK = bank_mask & (addr >> 18); - /* start DMA */ - DMA(cmd->device->host)->ST_DMA = 1; + /* start DMA */ + DMA(cmd->device->host)->ST_DMA = 1; - /* return success */ - return 0; + /* return success */ + return 0; } static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, int status) { - /* stop DMA */ - DMA(instance)->SP_DMA = 1; - /* remove write bit from CONTROL bits */ - DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE; - - /* copy from a bounce buffer, if necessary */ - if (status && HDATA(instance)->dma_bounce_buffer) { - if (HDATA(instance)->dma_dir && SCpnt) - memcpy (SCpnt->SCp.ptr, - HDATA(instance)->dma_bounce_buffer, - SCpnt->SCp.this_residual); - - if (HDATA(instance)->dma_buffer_pool == BUF_SCSI_ALLOCED) - kfree (HDATA(instance)->dma_bounce_buffer); - else - amiga_chip_free(HDATA(instance)->dma_bounce_buffer); - - HDATA(instance)->dma_bounce_buffer = NULL; - HDATA(instance)->dma_bounce_len = 0; - } + struct WD33C93_hostdata *hdata = shost_priv(instance); + + /* stop DMA */ + DMA(instance)->SP_DMA = 1; + /* remove write bit from CONTROL bits */ + DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE; + + /* copy from a bounce buffer, if necessary */ + if (status && hdata->dma_bounce_buffer) { + if (hdata->dma_dir && SCpnt) + memcpy(SCpnt->SCp.ptr, hdata->dma_bounce_buffer, + SCpnt->SCp.this_residual); + + if (hdata->dma_buffer_pool == BUF_SCSI_ALLOCED) + kfree(hdata->dma_bounce_buffer); + else + amiga_chip_free(hdata->dma_bounce_buffer); + + hdata->dma_bounce_buffer = NULL; + hdata->dma_bounce_len = 0; + } } #define CHECK_WD33C93 int __init gvp11_detect(struct scsi_host_template *tpnt) { - static unsigned char called = 0; - struct Scsi_Host *instance; - unsigned long address; - unsigned int epc; - struct zorro_dev *z = NULL; - unsigned int default_dma_xfer_mask; - wd33c93_regs regs; - int num_gvp11 = 0; + static unsigned char called = 0; + struct Scsi_Host *instance; + unsigned long address; + unsigned int epc; + struct zorro_dev *z = NULL; + unsigned int default_dma_xfer_mask; + struct WD33C93_hostdata *hdata; + wd33c93_regs regs; + int num_gvp11 = 0; #ifdef CHECK_WD33C93 - volatile unsigned char *sasr_3393, *scmd_3393; - unsigned char save_sasr; - unsigned char q, qq; + volatile unsigned char *sasr_3393, *scmd_3393; + unsigned char save_sasr; + unsigned char q, qq; #endif - if (!MACH_IS_AMIGA || called) - return 0; - called = 1; - - tpnt->proc_name = "GVP11"; - tpnt->proc_info = &wd33c93_proc_info; - - while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { - /* - * This should (hopefully) be the correct way to identify - * all the different GVP SCSI controllers (except for the - * SERIES I though). - */ - - if (z->id == ZORRO_PROD_GVP_COMBO_030_R3_SCSI || - z->id == ZORRO_PROD_GVP_SERIES_II) - default_dma_xfer_mask = ~0x00ffffff; - else if (z->id == ZORRO_PROD_GVP_GFORCE_030_SCSI || - z->id == ZORRO_PROD_GVP_A530_SCSI || - z->id == ZORRO_PROD_GVP_COMBO_030_R4_SCSI) - default_dma_xfer_mask = ~0x01ffffff; - else if (z->id == ZORRO_PROD_GVP_A1291 || - z->id == ZORRO_PROD_GVP_GFORCE_040_SCSI_1) - default_dma_xfer_mask = ~0x07ffffff; - else - continue; - - /* - * Rumors state that some GVP ram boards use the same product - * code as the SCSI controllers. Therefore if the board-size - * is not 64KB we asume it is a ram board and bail out. - */ - if (z->resource.end-z->resource.start != 0xffff) - continue; - - address = z->resource.start; - if (!request_mem_region(address, 256, "wd33c93")) - continue; + if (!MACH_IS_AMIGA || called) + return 0; + called = 1; + + tpnt->proc_name = "GVP11"; + tpnt->proc_info = &wd33c93_proc_info; + + while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { + /* + * This should (hopefully) be the correct way to identify + * all the different GVP SCSI controllers (except for the + * SERIES I though). + */ + + if (z->id == ZORRO_PROD_GVP_COMBO_030_R3_SCSI || + z->id == ZORRO_PROD_GVP_SERIES_II) + default_dma_xfer_mask = ~0x00ffffff; + else if (z->id == ZORRO_PROD_GVP_GFORCE_030_SCSI || + z->id == ZORRO_PROD_GVP_A530_SCSI || + z->id == ZORRO_PROD_GVP_COMBO_030_R4_SCSI) + default_dma_xfer_mask = ~0x01ffffff; + else if (z->id == ZORRO_PROD_GVP_A1291 || + z->id == ZORRO_PROD_GVP_GFORCE_040_SCSI_1) + default_dma_xfer_mask = ~0x07ffffff; + else + continue; + + /* + * Rumors state that some GVP ram boards use the same product + * code as the SCSI controllers. Therefore if the board-size + * is not 64KB we asume it is a ram board and bail out. + */ + if (z->resource.end - z->resource.start != 0xffff) + continue; + + address = z->resource.start; + if (!request_mem_region(address, 256, "wd33c93")) + continue; #ifdef CHECK_WD33C93 - /* - * These darn GVP boards are a problem - it can be tough to tell - * whether or not they include a SCSI controller. This is the - * ultimate Yet-Another-GVP-Detection-Hack in that it actually - * probes for a WD33c93 chip: If we find one, it's extremely - * likely that this card supports SCSI, regardless of Product_ - * Code, Board_Size, etc. - */ - - /* Get pointers to the presumed register locations and save contents */ - - sasr_3393 = &(((gvp11_scsiregs *)(ZTWO_VADDR(address)))->SASR); - scmd_3393 = &(((gvp11_scsiregs *)(ZTWO_VADDR(address)))->SCMD); - save_sasr = *sasr_3393; - - /* First test the AuxStatus Reg */ - - q = *sasr_3393; /* read it */ - if (q & 0x08) /* bit 3 should always be clear */ - goto release; - *sasr_3393 = WD_AUXILIARY_STATUS; /* setup indirect address */ - if (*sasr_3393 == WD_AUXILIARY_STATUS) { /* shouldn't retain the write */ - *sasr_3393 = save_sasr; /* Oops - restore this byte */ - goto release; + /* + * These darn GVP boards are a problem - it can be tough to tell + * whether or not they include a SCSI controller. This is the + * ultimate Yet-Another-GVP-Detection-Hack in that it actually + * probes for a WD33c93 chip: If we find one, it's extremely + * likely that this card supports SCSI, regardless of Product_ + * Code, Board_Size, etc. + */ + + /* Get pointers to the presumed register locations and save contents */ + + sasr_3393 = &(((gvp11_scsiregs *)(ZTWO_VADDR(address)))->SASR); + scmd_3393 = &(((gvp11_scsiregs *)(ZTWO_VADDR(address)))->SCMD); + save_sasr = *sasr_3393; + + /* First test the AuxStatus Reg */ + + q = *sasr_3393; /* read it */ + if (q & 0x08) /* bit 3 should always be clear */ + goto release; + *sasr_3393 = WD_AUXILIARY_STATUS; /* setup indirect address */ + if (*sasr_3393 == WD_AUXILIARY_STATUS) { /* shouldn't retain the write */ + *sasr_3393 = save_sasr; /* Oops - restore this byte */ + goto release; } - if (*sasr_3393 != q) { /* should still read the same */ - *sasr_3393 = save_sasr; /* Oops - restore this byte */ - goto release; + if (*sasr_3393 != q) { /* should still read the same */ + *sasr_3393 = save_sasr; /* Oops - restore this byte */ + goto release; } - if (*scmd_3393 != q) /* and so should the image at 0x1f */ - goto release; - - - /* Ok, we probably have a wd33c93, but let's check a few other places - * for good measure. Make sure that this works for both 'A and 'B - * chip versions. - */ - - *sasr_3393 = WD_SCSI_STATUS; - q = *scmd_3393; - *sasr_3393 = WD_SCSI_STATUS; - *scmd_3393 = ~q; - *sasr_3393 = WD_SCSI_STATUS; - qq = *scmd_3393; - *sasr_3393 = WD_SCSI_STATUS; - *scmd_3393 = q; - if (qq != q) /* should be read only */ - goto release; - *sasr_3393 = 0x1e; /* this register is unimplemented */ - q = *scmd_3393; - *sasr_3393 = 0x1e; - *scmd_3393 = ~q; - *sasr_3393 = 0x1e; - qq = *scmd_3393; - *sasr_3393 = 0x1e; - *scmd_3393 = q; - if (qq != q || qq != 0xff) /* should be read only, all 1's */ - goto release; - *sasr_3393 = WD_TIMEOUT_PERIOD; - q = *scmd_3393; - *sasr_3393 = WD_TIMEOUT_PERIOD; - *scmd_3393 = ~q; - *sasr_3393 = WD_TIMEOUT_PERIOD; - qq = *scmd_3393; - *sasr_3393 = WD_TIMEOUT_PERIOD; - *scmd_3393 = q; - if (qq != (~q & 0xff)) /* should be read/write */ - goto release; + if (*scmd_3393 != q) /* and so should the image at 0x1f */ + goto release; + + /* + * Ok, we probably have a wd33c93, but let's check a few other places + * for good measure. Make sure that this works for both 'A and 'B + * chip versions. + */ + + *sasr_3393 = WD_SCSI_STATUS; + q = *scmd_3393; + *sasr_3393 = WD_SCSI_STATUS; + *scmd_3393 = ~q; + *sasr_3393 = WD_SCSI_STATUS; + qq = *scmd_3393; + *sasr_3393 = WD_SCSI_STATUS; + *scmd_3393 = q; + if (qq != q) /* should be read only */ + goto release; + *sasr_3393 = 0x1e; /* this register is unimplemented */ + q = *scmd_3393; + *sasr_3393 = 0x1e; + *scmd_3393 = ~q; + *sasr_3393 = 0x1e; + qq = *scmd_3393; + *sasr_3393 = 0x1e; + *scmd_3393 = q; + if (qq != q || qq != 0xff) /* should be read only, all 1's */ + goto release; + *sasr_3393 = WD_TIMEOUT_PERIOD; + q = *scmd_3393; + *sasr_3393 = WD_TIMEOUT_PERIOD; + *scmd_3393 = ~q; + *sasr_3393 = WD_TIMEOUT_PERIOD; + qq = *scmd_3393; + *sasr_3393 = WD_TIMEOUT_PERIOD; + *scmd_3393 = q; + if (qq != (~q & 0xff)) /* should be read/write */ + goto release; #endif - instance = scsi_register (tpnt, sizeof (struct WD33C93_hostdata)); - if(instance == NULL) - goto release; - instance->base = ZTWO_VADDR(address); - instance->irq = IRQ_AMIGA_PORTS; - instance->unique_id = z->slotaddr; - - if (gvp11_xfer_mask) - HDATA(instance)->dma_xfer_mask = gvp11_xfer_mask; - else - HDATA(instance)->dma_xfer_mask = default_dma_xfer_mask; - - - DMA(instance)->secret2 = 1; - DMA(instance)->secret1 = 0; - DMA(instance)->secret3 = 15; - while (DMA(instance)->CNTR & GVP11_DMAC_BUSY) ; - DMA(instance)->CNTR = 0; - - DMA(instance)->BANK = 0; - - epc = *(unsigned short *)(ZTWO_VADDR(address) + 0x8000); - - /* - * Check for 14MHz SCSI clock - */ - regs.SASR = &(DMA(instance)->SASR); - regs.SCMD = &(DMA(instance)->SCMD); - HDATA(instance)->no_sync = 0xff; - HDATA(instance)->fast = 0; - HDATA(instance)->dma_mode = CTRL_DMA; - wd33c93_init(instance, regs, dma_setup, dma_stop, - (epc & GVP_SCSICLKMASK) ? WD33C93_FS_8_10 - : WD33C93_FS_12_15); - - if (request_irq(IRQ_AMIGA_PORTS, gvp11_intr, IRQF_SHARED, "GVP11 SCSI", - instance)) - goto unregister; - DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE; - num_gvp11++; - continue; + instance = scsi_register(tpnt, sizeof(struct WD33C93_hostdata)); + if (instance == NULL) + goto release; + instance->base = ZTWO_VADDR(address); + instance->irq = IRQ_AMIGA_PORTS; + instance->unique_id = z->slotaddr; + + hdata = shost_priv(instance); + if (gvp11_xfer_mask) + hdata->dma_xfer_mask = gvp11_xfer_mask; + else + hdata->dma_xfer_mask = default_dma_xfer_mask; + + DMA(instance)->secret2 = 1; + DMA(instance)->secret1 = 0; + DMA(instance)->secret3 = 15; + while (DMA(instance)->CNTR & GVP11_DMAC_BUSY) + ; + DMA(instance)->CNTR = 0; + + DMA(instance)->BANK = 0; + + epc = *(unsigned short *)(ZTWO_VADDR(address) + 0x8000); + + /* + * Check for 14MHz SCSI clock + */ + regs.SASR = &(DMA(instance)->SASR); + regs.SCMD = &(DMA(instance)->SCMD); + hdata->no_sync = 0xff; + hdata->fast = 0; + hdata->dma_mode = CTRL_DMA; + wd33c93_init(instance, regs, dma_setup, dma_stop, + (epc & GVP_SCSICLKMASK) ? WD33C93_FS_8_10 + : WD33C93_FS_12_15); + + if (request_irq(IRQ_AMIGA_PORTS, gvp11_intr, IRQF_SHARED, + "GVP11 SCSI", instance)) + goto unregister; + DMA(instance)->CNTR = GVP11_DMAC_INT_ENABLE; + num_gvp11++; + continue; unregister: - scsi_unregister(instance); - wd33c93_release(); + scsi_unregister(instance); release: - release_mem_region(address, 256); - } + release_mem_region(address, 256); + } - return num_gvp11; + return num_gvp11; } static int gvp11_bus_reset(struct scsi_cmnd *cmd) @@ -389,12 +391,11 @@ static struct scsi_host_template driver_template = { int gvp11_release(struct Scsi_Host *instance) { #ifdef MODULE - DMA(instance)->CNTR = 0; - release_mem_region(ZTWO_PADDR(instance->base), 256); - free_irq(IRQ_AMIGA_PORTS, instance); - wd33c93_release(); + DMA(instance)->CNTR = 0; + release_mem_region(ZTWO_PADDR(instance->base), 256); + free_irq(IRQ_AMIGA_PORTS, instance); #endif - return 1; + return 1; } MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/gvp11.h b/drivers/scsi/gvp11.h index bf22859a503..e2efdf9601e 100644 --- a/drivers/scsi/gvp11.h +++ b/drivers/scsi/gvp11.h @@ -15,11 +15,11 @@ int gvp11_detect(struct scsi_host_template *); int gvp11_release(struct Scsi_Host *); #ifndef CMD_PER_LUN -#define CMD_PER_LUN 2 +#define CMD_PER_LUN 2 #endif #ifndef CAN_QUEUE -#define CAN_QUEUE 16 +#define CAN_QUEUE 16 #endif #ifndef HOSTS_C @@ -28,24 +28,24 @@ int gvp11_release(struct Scsi_Host *); * if the transfer address ANDed with this results in a non-zero * result, then we can't use DMA. */ -#define GVP11_XFER_MASK (0xff000001) +#define GVP11_XFER_MASK (0xff000001) typedef struct { - unsigned char pad1[64]; - volatile unsigned short CNTR; - unsigned char pad2[31]; - volatile unsigned char SASR; - unsigned char pad3; - volatile unsigned char SCMD; - unsigned char pad4[4]; - volatile unsigned short BANK; - unsigned char pad5[6]; - volatile unsigned long ACR; - volatile unsigned short secret1; /* store 0 here */ - volatile unsigned short ST_DMA; - volatile unsigned short SP_DMA; - volatile unsigned short secret2; /* store 1 here */ - volatile unsigned short secret3; /* store 15 here */ + unsigned char pad1[64]; + volatile unsigned short CNTR; + unsigned char pad2[31]; + volatile unsigned char SASR; + unsigned char pad3; + volatile unsigned char SCMD; + unsigned char pad4[4]; + volatile unsigned short BANK; + unsigned char pad5[6]; + volatile unsigned long ACR; + volatile unsigned short secret1; /* store 0 here */ + volatile unsigned short ST_DMA; + volatile unsigned short SP_DMA; + volatile unsigned short secret2; /* store 1 here */ + volatile unsigned short secret3; /* store 15 here */ } gvp11_scsiregs; /* bits in CNTR */ diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 183d3a43c28..c016426b31b 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -2708,14 +2708,6 @@ static void fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h, c->Request.CDB[8] = (size >> 8) & 0xFF; c->Request.CDB[9] = size & 0xFF; break; - - case HPSA_READ_CAPACITY: - c->Request.CDBLen = 10; - c->Request.Type.Attribute = ATTR_SIMPLE; - c->Request.Type.Direction = XFER_READ; - c->Request.Timeout = 0; - c->Request.CDB[0] = cmd; - break; case HPSA_CACHE_FLUSH: c->Request.CDBLen = 12; c->Request.Type.Attribute = ATTR_SIMPLE; diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h index 56fb9827681..78de9b6d1e0 100644 --- a/drivers/scsi/hpsa_cmd.h +++ b/drivers/scsi/hpsa_cmd.h @@ -152,21 +152,6 @@ struct SenseSubsystem_info { u8 reserved1[1108]; }; -#define HPSA_READ_CAPACITY 0x25 /* Read Capacity */ -struct ReadCapdata { - u8 total_size[4]; /* Total size in blocks */ - u8 block_size[4]; /* Size of blocks in bytes */ -}; - -#if 0 -/* 12 byte commands not implemented in firmware yet. */ -#define HPSA_READ 0xa8 -#define HPSA_WRITE 0xaa -#endif - -#define HPSA_READ 0x28 /* Read(10) */ -#define HPSA_WRITE 0x2a /* Write(10) */ - /* BMIC commands */ #define BMIC_READ 0x26 #define BMIC_WRITE 0x27 diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index c2eea711a5c..3eb2b7b3d8b 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -2245,7 +2245,7 @@ static int ibmvfc_wait_for_ops(struct ibmvfc_host *vhost, void *device, DECLARE_COMPLETION_ONSTACK(comp); int wait; unsigned long flags; - signed long timeout = init_timeout * HZ; + signed long timeout = IBMVFC_ABORT_WAIT_TIMEOUT * HZ; ENTER; do { @@ -2919,6 +2919,7 @@ static DEVICE_ATTR(log_level, S_IRUGO | S_IWUSR, #ifdef CONFIG_SCSI_IBMVFC_TRACE /** * ibmvfc_read_trace - Dump the adapter trace + * @filp: open sysfs file * @kobj: kobject struct * @bin_attr: bin_attribute struct * @buf: buffer @@ -2928,7 +2929,7 @@ static DEVICE_ATTR(log_level, S_IRUGO | S_IWUSR, * Return value: * number of bytes printed to buffer **/ -static ssize_t ibmvfc_read_trace(struct kobject *kobj, +static ssize_t ibmvfc_read_trace(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -3013,6 +3014,7 @@ static struct ibmvfc_async_crq *ibmvfc_next_async_crq(struct ibmvfc_host *vhost) if (crq->valid & 0x80) { if (++async_crq->cur == async_crq->size) async_crq->cur = 0; + rmb(); } else crq = NULL; @@ -3035,6 +3037,7 @@ static struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost) if (crq->valid & 0x80) { if (++queue->cur == queue->size) queue->cur = 0; + rmb(); } else crq = NULL; @@ -3083,12 +3086,14 @@ static void ibmvfc_tasklet(void *data) while ((async = ibmvfc_next_async_crq(vhost)) != NULL) { ibmvfc_handle_async(async, vhost); async->valid = 0; + wmb(); } /* Pull all the valid messages off the CRQ */ while ((crq = ibmvfc_next_crq(vhost)) != NULL) { ibmvfc_handle_crq(crq, vhost); crq->valid = 0; + wmb(); } vio_enable_interrupts(vdev); @@ -3096,10 +3101,12 @@ static void ibmvfc_tasklet(void *data) vio_disable_interrupts(vdev); ibmvfc_handle_async(async, vhost); async->valid = 0; + wmb(); } else if ((crq = ibmvfc_next_crq(vhost)) != NULL) { vio_disable_interrupts(vdev); ibmvfc_handle_crq(crq, vhost); crq->valid = 0; + wmb(); } else done = 1; } diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index d25106a958d..7e9742764e4 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -38,6 +38,7 @@ #define IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT \ (IBMVFC_ADISC_TIMEOUT + IBMVFC_ADISC_CANCEL_TIMEOUT) #define IBMVFC_INIT_TIMEOUT 120 +#define IBMVFC_ABORT_WAIT_TIMEOUT 40 #define IBMVFC_MAX_REQUESTS_DEFAULT 100 #define IBMVFC_DEBUG 0 diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index b90c118119d..6a6661c35b2 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3120,6 +3120,7 @@ restart: #ifdef CONFIG_SCSI_IPR_TRACE /** * ipr_read_trace - Dump the adapter trace + * @filp: open sysfs file * @kobj: kobject struct * @bin_attr: bin_attribute struct * @buf: buffer @@ -3129,7 +3130,7 @@ restart: * Return value: * number of bytes printed to buffer **/ -static ssize_t ipr_read_trace(struct kobject *kobj, +static ssize_t ipr_read_trace(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -3764,6 +3765,7 @@ static struct device_attribute *ipr_ioa_attrs[] = { #ifdef CONFIG_SCSI_IPR_DUMP /** * ipr_read_dump - Dump the adapter + * @filp: open sysfs file * @kobj: kobject struct * @bin_attr: bin_attribute struct * @buf: buffer @@ -3773,7 +3775,7 @@ static struct device_attribute *ipr_ioa_attrs[] = { * Return value: * number of bytes printed to buffer **/ -static ssize_t ipr_read_dump(struct kobject *kobj, +static ssize_t ipr_read_dump(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -3927,6 +3929,7 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) /** * ipr_write_dump - Setup dump state of adapter + * @filp: open sysfs file * @kobj: kobject struct * @bin_attr: bin_attribute struct * @buf: buffer @@ -3936,7 +3939,7 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) * Return value: * number of bytes printed to buffer **/ -static ssize_t ipr_write_dump(struct kobject *kobj, +static ssize_t ipr_write_dump(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 9eae04afa9a..bf55d305741 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -206,8 +206,10 @@ static void iscsi_sw_tcp_conn_set_callbacks(struct iscsi_conn *conn) } static void -iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_sw_tcp_conn *tcp_sw_conn) +iscsi_sw_tcp_conn_restore_callbacks(struct iscsi_conn *conn) { + struct iscsi_tcp_conn *tcp_conn = conn->dd_data; + struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; struct sock *sk = tcp_sw_conn->sock->sk; /* restore socket callbacks, see also: iscsi_conn_set_callbacks() */ @@ -555,7 +557,7 @@ static void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn) return; sock_hold(sock->sk); - iscsi_sw_tcp_conn_restore_callbacks(tcp_sw_conn); + iscsi_sw_tcp_conn_restore_callbacks(conn); sock_put(sock->sk); spin_lock_bh(&session->lock); @@ -599,7 +601,7 @@ static void iscsi_sw_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); write_unlock_bh(&tcp_sw_conn->sock->sk->sk_callback_lock); - if (sk_sleep(sock->sk) && waitqueue_active(sk_sleep(sock->sk))) { + if (sk_sleep(sock->sk)) { sock->sk->sk_err = EIO; wake_up_interruptible(sk_sleep(sock->sk)); } diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h index ca6b7bc64de..94644bad0ed 100644 --- a/drivers/scsi/iscsi_tcp.h +++ b/drivers/scsi/iscsi_tcp.h @@ -36,7 +36,6 @@ struct iscsi_sw_tcp_send { }; struct iscsi_sw_tcp_conn { - struct iscsi_conn *iscsi_conn; struct socket *sock; struct iscsi_sw_tcp_send out; diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index 1087a7f18e8..c7985da8809 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -132,7 +132,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp, switch (fmt) { case ELS_ADDR_FMT_PORT: FC_DISC_DBG(disc, "Port address format for port " - "(%6x)\n", ntoh24(pp->rscn_fid)); + "(%6.6x)\n", ntoh24(pp->rscn_fid)); dp = kzalloc(sizeof(*dp), GFP_KERNEL); if (!dp) { redisc = 1; @@ -440,7 +440,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len) ids.port_id = ntoh24(np->fp_fid); ids.port_name = ntohll(np->fp_wwpn); - if (ids.port_id != fc_host_port_id(lport->host) && + if (ids.port_id != lport->port_id && ids.port_name != lport->wwpn) { rdata = lport->tt.rport_create(lport, ids.port_id); if (rdata) { @@ -449,7 +449,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len) } else { printk(KERN_WARNING "libfc: Failed to allocate " "memory for the newly discovered port " - "(%6x)\n", ids.port_id); + "(%6.6x)\n", ids.port_id); error = -ENOMEM; } } @@ -607,7 +607,7 @@ static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp, rdata->ids.port_name = port_name; else if (rdata->ids.port_name != port_name) { FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. " - "Port-id %x wwpn %llx\n", + "Port-id %6.6x wwpn %16.16llx\n", rdata->ids.port_id, port_name); lport->tt.rport_logoff(rdata); diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c index 53748724f2c..e9412b710fa 100644 --- a/drivers/scsi/libfc/fc_elsct.c +++ b/drivers/scsi/libfc/fc_elsct.c @@ -63,7 +63,7 @@ struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did, return NULL; } - fc_fill_fc_hdr(fp, r_ctl, did, fc_host_port_id(lport->host), fh_type, + fc_fill_fc_hdr(fp, r_ctl, did, lport->port_id, fh_type, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec); diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index e5df0d4db67..104e0fba7c4 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -488,7 +488,7 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, */ spin_lock_bh(&ep->ex_lock); ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */ - if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT)) + if (f_ctl & FC_FC_SEQ_INIT) ep->esb_stat &= ~ESB_ST_SEQ_INIT; spin_unlock_bh(&ep->ex_lock); return error; @@ -676,9 +676,10 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport, } memset(ep, 0, sizeof(*ep)); - cpu = smp_processor_id(); + cpu = get_cpu(); pool = per_cpu_ptr(mp->pool, cpu); spin_lock_bh(&pool->lock); + put_cpu(); index = pool->next_index; /* allocate new exch from pool */ while (fc_exch_ptr_get(pool, index)) { @@ -734,19 +735,14 @@ err: * EM is selected when a NULL match function pointer is encountered * or when a call to a match function returns true. */ -static struct fc_exch *fc_exch_alloc(struct fc_lport *lport, - struct fc_frame *fp) +static inline struct fc_exch *fc_exch_alloc(struct fc_lport *lport, + struct fc_frame *fp) { struct fc_exch_mgr_anchor *ema; - struct fc_exch *ep; - list_for_each_entry(ema, &lport->ema_list, ema_list) { - if (!ema->match || ema->match(fp)) { - ep = fc_exch_em_alloc(lport, ema->mp); - if (ep) - return ep; - } - } + list_for_each_entry(ema, &lport->ema_list, ema_list) + if (!ema->match || ema->match(fp)) + return fc_exch_em_alloc(lport, ema->mp); return NULL; } @@ -920,13 +916,9 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport, * Find or create the sequence. */ if (fc_sof_is_init(fr_sof(fp))) { - sp = fc_seq_start_next(&ep->seq); - if (!sp) { - reject = FC_RJT_SEQ_XS; /* exchange shortage */ - goto rel; - } - sp->id = fh->fh_seq_id; + sp = &ep->seq; sp->ssb_stat |= SSB_ST_RESP; + sp->id = fh->fh_seq_id; } else { sp = &ep->seq; if (sp->id != fh->fh_seq_id) { @@ -1250,9 +1242,6 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp, struct fc_frame_header *fh = fc_frame_header_get(fp); struct fc_seq *sp = NULL; struct fc_exch *ep = NULL; - enum fc_sof sof; - enum fc_eof eof; - u32 f_ctl; enum fc_pf_rjt_reason reject; /* We can have the wrong fc_lport at this point with NPIV, which is a @@ -1269,9 +1258,6 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp, if (reject == FC_RJT_NONE) { sp = fr_seq(fp); /* sequence will be held */ ep = fc_seq_exch(sp); - sof = fr_sof(fp); - eof = fr_eof(fp); - f_ctl = ntoh24(fh->fh_f_ctl); fc_seq_send_ack(sp, fp); /* @@ -1336,17 +1322,15 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) goto rel; } sof = fr_sof(fp); + sp = &ep->seq; if (fc_sof_is_init(sof)) { - sp = fc_seq_start_next(&ep->seq); - sp->id = fh->fh_seq_id; sp->ssb_stat |= SSB_ST_RESP; - } else { - sp = &ep->seq; - if (sp->id != fh->fh_seq_id) { - atomic_inc(&mp->stats.seq_not_found); - goto rel; - } + sp->id = fh->fh_seq_id; + } else if (sp->id != fh->fh_seq_id) { + atomic_inc(&mp->stats.seq_not_found); + goto rel; } + f_ctl = ntoh24(fh->fh_f_ctl); fr_seq(fp) = sp; if (f_ctl & FC_FC_SEQ_INIT) @@ -1763,7 +1747,6 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp) fc_exch_done(sp); goto out; } - sp = fc_seq_start_next(sp); acc = fc_frame_payload_get(fp, sizeof(*acc)); memset(acc, 0, sizeof(*acc)); acc->reca_cmd = ELS_LS_ACC; @@ -1944,7 +1927,7 @@ static void fc_exch_rrq(struct fc_exch *ep) did = ep->sid; fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, did, - fc_host_port_id(lport->host), FC_TYPE_ELS, + lport->port_id, FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); if (fc_exch_seq_send(lport, fp, fc_exch_rrq_resp, NULL, ep, diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index 17396c708b0..ec1f66c4a9d 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -97,7 +97,7 @@ static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *); static void fc_fcp_complete_locked(struct fc_fcp_pkt *); static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *); static void fc_fcp_error(struct fc_fcp_pkt *, struct fc_frame *); -static void fc_timeout_error(struct fc_fcp_pkt *); +static void fc_fcp_recovery(struct fc_fcp_pkt *); static void fc_fcp_timeout(unsigned long); static void fc_fcp_rec(struct fc_fcp_pkt *); static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *); @@ -121,7 +121,7 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *); #define FC_DATA_UNDRUN 7 #define FC_ERROR 8 #define FC_HRD_ERROR 9 -#define FC_CMD_TIME_OUT 10 +#define FC_CMD_RECOVERY 10 /* * Error recovery timeout values. @@ -446,9 +446,16 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp) len = fr_len(fp) - sizeof(*fh); buf = fc_frame_payload_get(fp, 0); - /* if this I/O is ddped, update xfer len */ - fc_fcp_ddp_done(fsp); - + /* + * if this I/O is ddped then clear it + * and initiate recovery since data + * frames are expected to be placed + * directly in that case. + */ + if (fsp->xfer_ddp != FC_XID_UNKNOWN) { + fc_fcp_ddp_done(fsp); + goto err; + } if (offset + len > fsp->data_len) { /* this should never happen */ if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) && @@ -456,8 +463,7 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp) goto crc_err; FC_FCP_DBG(fsp, "data received past end. len %zx offset %zx " "data_len %x\n", len, offset, fsp->data_len); - fc_fcp_retry_cmd(fsp); - return; + goto err; } if (offset != fsp->xfer_len) fsp->state |= FC_SRB_DISCONTIG; @@ -478,13 +484,14 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp) if (~crc != le32_to_cpu(fr_crc(fp))) { crc_err: - stats = fc_lport_get_stats(lport); + stats = per_cpu_ptr(lport->dev_stats, get_cpu()); stats->ErrorFrames++; - /* FIXME - per cpu count, not total count! */ + /* per cpu count, not total count, but OK for limit */ if (stats->InvalidCRCCount++ < 5) printk(KERN_WARNING "libfc: CRC error on data " - "frame for port (%6x)\n", - fc_host_port_id(lport->host)); + "frame for port (%6.6x)\n", + lport->port_id); + put_cpu(); /* * Assume the frame is total garbage. * We may have copied it over the good part @@ -493,7 +500,7 @@ crc_err: * Otherwise, ignore it. */ if (fsp->state & FC_SRB_DISCONTIG) - fc_fcp_retry_cmd(fsp); + goto err; return; } } @@ -509,6 +516,9 @@ crc_err: if (unlikely(fsp->state & FC_SRB_RCV_STATUS) && fsp->xfer_len == fsp->data_len - fsp->scsi_resid) fc_fcp_complete_locked(fsp); + return; +err: + fc_fcp_recovery(fsp); } /** @@ -834,8 +844,7 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) * exit here */ return; - } else - goto err; + } } if (flags & FCP_SNS_LEN_VAL) { snsl = ntohl(rp_ex->fr_sns_len); @@ -885,7 +894,7 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) return; } fsp->status_code = FC_DATA_OVRRUN; - FC_FCP_DBG(fsp, "tgt %6x xfer len %zx greater than expected, " + FC_FCP_DBG(fsp, "tgt %6.6x xfer len %zx greater than expected, " "len %x, data len %x\n", fsp->rport->port_id, fsp->xfer_len, expected_len, fsp->data_len); @@ -1100,7 +1109,7 @@ static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp, rpriv = rport->dd_data; fc_fill_fc_hdr(fp, FC_RCTL_DD_UNSOL_CMD, rport->port_id, - fc_host_port_id(rpriv->local_port->host), FC_TYPE_FCP, + rpriv->local_port->port_id, FC_TYPE_FCP, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); seq = lport->tt.exch_seq_send(lport, fp, resp, fc_fcp_pkt_destroy, @@ -1341,7 +1350,7 @@ static void fc_fcp_timeout(unsigned long data) else if (fsp->state & FC_SRB_RCV_STATUS) fc_fcp_complete_locked(fsp); else - fc_timeout_error(fsp); + fc_fcp_recovery(fsp); fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO; unlock: fc_fcp_unlock_pkt(fsp); @@ -1373,7 +1382,7 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp) fr_seq(fp) = fsp->seq_ptr; fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id, - fc_host_port_id(rpriv->local_port->host), FC_TYPE_ELS, + rpriv->local_port->port_id, FC_TYPE_ELS, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); if (lport->tt.elsct_send(lport, rport->port_id, fp, ELS_REC, fc_fcp_rec_resp, fsp, @@ -1385,7 +1394,7 @@ retry: if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); else - fc_timeout_error(fsp); + fc_fcp_recovery(fsp); } /** @@ -1454,7 +1463,7 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) fc_fcp_retry_cmd(fsp); break; } - fc_timeout_error(fsp); + fc_fcp_recovery(fsp); break; } } else if (opcode == ELS_LS_ACC) { @@ -1553,7 +1562,7 @@ static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) break; default: - FC_FCP_DBG(fsp, "REC %p fid %x error unexpected error %d\n", + FC_FCP_DBG(fsp, "REC %p fid %6.6x error unexpected error %d\n", fsp, fsp->rport->port_id, error); fsp->status_code = FC_CMD_PLOGO; /* fall through */ @@ -1563,13 +1572,13 @@ static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) * Assume REC or LS_ACC was lost. * The exchange manager will have aborted REC, so retry. */ - FC_FCP_DBG(fsp, "REC fid %x error error %d retry %d/%d\n", + FC_FCP_DBG(fsp, "REC fid %6.6x error error %d retry %d/%d\n", fsp->rport->port_id, error, fsp->recov_retry, FC_MAX_RECOV_RETRY); if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) fc_fcp_rec(fsp); else - fc_timeout_error(fsp); + fc_fcp_recovery(fsp); break; } fc_fcp_unlock_pkt(fsp); @@ -1578,12 +1587,12 @@ out: } /** - * fc_timeout_error() - Handler for fcp_pkt timeouts - * @fsp: The FCP packt that has timed out + * fc_fcp_recovery() - Handler for fcp_pkt recovery + * @fsp: The FCP pkt that needs to be aborted */ -static void fc_timeout_error(struct fc_fcp_pkt *fsp) +static void fc_fcp_recovery(struct fc_fcp_pkt *fsp) { - fsp->status_code = FC_CMD_TIME_OUT; + fsp->status_code = FC_CMD_RECOVERY; fsp->cdb_status = 0; fsp->io_status = 0; /* @@ -1631,7 +1640,7 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset) srr->srr_rel_off = htonl(offset); fc_fill_fc_hdr(fp, FC_RCTL_ELS4_REQ, rport->port_id, - fc_host_port_id(rpriv->local_port->host), FC_TYPE_FCP, + rpriv->local_port->port_id, FC_TYPE_FCP, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, NULL, @@ -1689,7 +1698,7 @@ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) break; case ELS_LS_RJT: default: - fc_timeout_error(fsp); + fc_fcp_recovery(fsp); break; } fc_fcp_unlock_pkt(fsp); @@ -1715,7 +1724,7 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) fc_fcp_rec(fsp); else - fc_timeout_error(fsp); + fc_fcp_recovery(fsp); break; case -FC_EX_CLOSED: /* e.g., link failure */ /* fall through */ @@ -1810,7 +1819,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *)) /* * setup the data direction */ - stats = fc_lport_get_stats(lport); + stats = per_cpu_ptr(lport->dev_stats, get_cpu()); if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) { fsp->req_flags = FC_SRB_READ; stats->InputRequests++; @@ -1823,6 +1832,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *)) fsp->req_flags = 0; stats->ControlRequests++; } + put_cpu(); fsp->tgt_flags = rpriv->flags; @@ -1907,6 +1917,8 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp) } break; case FC_ERROR: + FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml " + "due to FC_ERROR\n"); sc_cmd->result = DID_ERROR << 16; break; case FC_DATA_UNDRUN: @@ -1915,12 +1927,19 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp) * scsi status is good but transport level * underrun. */ - sc_cmd->result = (fsp->state & FC_SRB_RCV_STATUS ? - DID_OK : DID_ERROR) << 16; + if (fsp->state & FC_SRB_RCV_STATUS) { + sc_cmd->result = DID_OK << 16; + } else { + FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml" + " due to FC_DATA_UNDRUN (trans)\n"); + sc_cmd->result = DID_ERROR << 16; + } } else { /* * scsi got underrun, this is an error */ + FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml " + "due to FC_DATA_UNDRUN (scsi)\n"); CMD_RESID_LEN(sc_cmd) = fsp->scsi_resid; sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status; } @@ -1929,12 +1948,16 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp) /* * overrun is an error */ + FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml " + "due to FC_DATA_OVRRUN\n"); sc_cmd->result = (DID_ERROR << 16) | fsp->cdb_status; break; case FC_CMD_ABORTED: + FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml " + "due to FC_CMD_ABORTED\n"); sc_cmd->result = (DID_ERROR << 16) | fsp->io_status; break; - case FC_CMD_TIME_OUT: + case FC_CMD_RECOVERY: sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status; break; case FC_CMD_RESET: @@ -1944,6 +1967,8 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp) sc_cmd->result = (DID_NO_CONNECT << 16); break; default: + FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml " + "due to unknown error\n"); sc_cmd->result = (DID_ERROR << 16); break; } @@ -2028,7 +2053,7 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd) if (lport->state != LPORT_ST_READY) return rc; - FC_SCSI_DBG(lport, "Resetting rport (%6x)\n", rport->port_id); + FC_SCSI_DBG(lport, "Resetting rport (%6.6x)\n", rport->port_id); fsp = fc_fcp_pkt_alloc(lport, GFP_NOIO); if (fsp == NULL) { @@ -2076,12 +2101,12 @@ int fc_eh_host_reset(struct scsi_cmnd *sc_cmd) if (fc_fcp_lport_queue_ready(lport)) { shost_printk(KERN_INFO, shost, "libfc: Host reset succeeded " - "on port (%6x)\n", fc_host_port_id(lport->host)); + "on port (%6.6x)\n", lport->port_id); return SUCCESS; } else { shost_printk(KERN_INFO, shost, "libfc: Host reset failed, " - "port (%6x) is not ready.\n", - fc_host_port_id(lport->host)); + "port (%6.6x) is not ready.\n", + lport->port_id); return FAILED; } } @@ -2166,7 +2191,7 @@ void fc_fcp_destroy(struct fc_lport *lport) if (!list_empty(&si->scsi_pkt_queue)) printk(KERN_ERR "libfc: Leaked SCSI packets when destroying " - "port (%6x)\n", fc_host_port_id(lport->host)); + "port (%6.6x)\n", lport->port_id); mempool_destroy(si->scsi_pkt_pool); kfree(si); diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h index 741fd5c72e1..f5c0ca4b6ef 100644 --- a/drivers/scsi/libfc/fc_libfc.h +++ b/drivers/scsi/libfc/fc_libfc.h @@ -45,9 +45,9 @@ extern unsigned int fc_debug_logging; #define FC_LPORT_DBG(lport, fmt, args...) \ FC_CHECK_LOGGING(FC_LPORT_LOGGING, \ - printk(KERN_INFO "host%u: lport %6x: " fmt, \ + printk(KERN_INFO "host%u: lport %6.6x: " fmt, \ (lport)->host->host_no, \ - fc_host_port_id((lport)->host), ##args)) + (lport)->port_id, ##args)) #define FC_DISC_DBG(disc, fmt, args...) \ FC_CHECK_LOGGING(FC_DISC_LOGGING, \ @@ -57,7 +57,7 @@ extern unsigned int fc_debug_logging; #define FC_RPORT_ID_DBG(lport, port_id, fmt, args...) \ FC_CHECK_LOGGING(FC_RPORT_LOGGING, \ - printk(KERN_INFO "host%u: rport %6x: " fmt, \ + printk(KERN_INFO "host%u: rport %6.6x: " fmt, \ (lport)->host->host_no, \ (port_id), ##args)) @@ -66,7 +66,7 @@ extern unsigned int fc_debug_logging; #define FC_FCP_DBG(pkt, fmt, args...) \ FC_CHECK_LOGGING(FC_FCP_LOGGING, \ - printk(KERN_INFO "host%u: fcp: %6x: " fmt, \ + printk(KERN_INFO "host%u: fcp: %6.6x: " fmt, \ (pkt)->lp->host->host_no, \ pkt->rport->port_id, ##args)) diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index d126ecfff70..79c9e3ccd34 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -172,7 +172,7 @@ static void fc_lport_rport_callback(struct fc_lport *lport, struct fc_rport_priv *rdata, enum fc_rport_event event) { - FC_LPORT_DBG(lport, "Received a %d event for port (%6x)\n", event, + FC_LPORT_DBG(lport, "Received a %d event for port (%6.6x)\n", event, rdata->ids.port_id); mutex_lock(&lport->lp_mutex); @@ -183,7 +183,7 @@ static void fc_lport_rport_callback(struct fc_lport *lport, fc_lport_enter_ns(lport, LPORT_ST_RNN_ID); } else { FC_LPORT_DBG(lport, "Received an READY event " - "on port (%6x) for the directory " + "on port (%6.6x) for the directory " "server, but the lport is not " "in the DNS state, it's in the " "%d state", rdata->ids.port_id, @@ -228,9 +228,12 @@ static void fc_lport_ptp_setup(struct fc_lport *lport, u64 remote_wwnn) { mutex_lock(&lport->disc.disc_mutex); - if (lport->ptp_rdata) + if (lport->ptp_rdata) { lport->tt.rport_logoff(lport->ptp_rdata); + kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy); + } lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid); + kref_get(&lport->ptp_rdata->kref); lport->ptp_rdata->ids.port_name = remote_wwpn; lport->ptp_rdata->ids.node_name = remote_wwnn; mutex_unlock(&lport->disc.disc_mutex); @@ -241,17 +244,6 @@ static void fc_lport_ptp_setup(struct fc_lport *lport, } /** - * fc_get_host_port_type() - Return the port type of the given Scsi_Host - * @shost: The SCSI host whose port type is to be determined - */ -void fc_get_host_port_type(struct Scsi_Host *shost) -{ - /* TODO - currently just NPORT */ - fc_host_port_type(shost) = FC_PORTTYPE_NPORT; -} -EXPORT_SYMBOL(fc_get_host_port_type); - -/** * fc_get_host_port_state() - Return the port state of the given Scsi_Host * @shost: The SCSI host whose port state is to be determined */ @@ -572,8 +564,8 @@ void __fc_linkup(struct fc_lport *lport) */ void fc_linkup(struct fc_lport *lport) { - printk(KERN_INFO "host%d: libfc: Link up on port (%6x)\n", - lport->host->host_no, fc_host_port_id(lport->host)); + printk(KERN_INFO "host%d: libfc: Link up on port (%6.6x)\n", + lport->host->host_no, lport->port_id); mutex_lock(&lport->lp_mutex); __fc_linkup(lport); @@ -602,8 +594,8 @@ void __fc_linkdown(struct fc_lport *lport) */ void fc_linkdown(struct fc_lport *lport) { - printk(KERN_INFO "host%d: libfc: Link down on port (%6x)\n", - lport->host->host_no, fc_host_port_id(lport->host)); + printk(KERN_INFO "host%d: libfc: Link down on port (%6.6x)\n", + lport->host->host_no, lport->port_id); mutex_lock(&lport->lp_mutex); __fc_linkdown(lport); @@ -704,8 +696,8 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event) break; case DISC_EV_FAILED: printk(KERN_ERR "host%d: libfc: " - "Discovery failed for port (%6x)\n", - lport->host->host_no, fc_host_port_id(lport->host)); + "Discovery failed for port (%6.6x)\n", + lport->host->host_no, lport->port_id); mutex_lock(&lport->lp_mutex); fc_lport_enter_reset(lport); mutex_unlock(&lport->lp_mutex); @@ -750,10 +742,14 @@ static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp) { if (port_id) - printk(KERN_INFO "host%d: Assigned Port ID %6x\n", + printk(KERN_INFO "host%d: Assigned Port ID %6.6x\n", lport->host->host_no, port_id); + lport->port_id = port_id; + + /* Update the fc_host */ fc_host_port_id(lport->host) = port_id; + if (lport->tt.lport_set_port_id) lport->tt.lport_set_port_id(lport, port_id, fp); } @@ -797,11 +793,11 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, remote_wwpn = get_unaligned_be64(&flp->fl_wwpn); if (remote_wwpn == lport->wwpn) { printk(KERN_WARNING "host%d: libfc: Received FLOGI from port " - "with same WWPN %llx\n", + "with same WWPN %16.16llx\n", lport->host->host_no, remote_wwpn); goto out; } - FC_LPORT_DBG(lport, "FLOGI from port WWPN %llx\n", remote_wwpn); + FC_LPORT_DBG(lport, "FLOGI from port WWPN %16.16llx\n", remote_wwpn); /* * XXX what is the right thing to do for FIDs? @@ -832,7 +828,7 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, */ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ; ep = fc_seq_exch(sp); - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, + fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, remote_fid, local_fid, FC_TYPE_ELS, f_ctl, 0); lport->tt.seq_send(lport, sp, fp); @@ -947,14 +943,18 @@ static void fc_lport_reset_locked(struct fc_lport *lport) if (lport->dns_rdata) lport->tt.rport_logoff(lport->dns_rdata); - lport->ptp_rdata = NULL; + if (lport->ptp_rdata) { + lport->tt.rport_logoff(lport->ptp_rdata); + kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy); + lport->ptp_rdata = NULL; + } lport->tt.disc_stop(lport); lport->tt.exch_mgr_reset(lport, 0, 0); fc_host_fabric_name(lport->host) = 0; - if (fc_host_port_id(lport->host)) + if (lport->port_id) fc_lport_set_port_id(lport, 0, NULL); } @@ -1492,7 +1492,7 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, lport->r_a_tov = 2 * e_d_tov; fc_lport_set_port_id(lport, did, fp); printk(KERN_INFO "host%d: libfc: " - "Port (%6x) entered " + "Port (%6.6x) entered " "point-to-point mode\n", lport->host->host_no, did); fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id), @@ -1699,7 +1699,7 @@ static int fc_lport_els_request(struct fc_bsg_job *job, fh = fc_frame_header_get(fp); fh->fh_r_ctl = FC_RCTL_ELS_REQ; hton24(fh->fh_d_id, did); - hton24(fh->fh_s_id, fc_host_port_id(lport->host)); + hton24(fh->fh_s_id, lport->port_id); fh->fh_type = FC_TYPE_ELS; hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT); @@ -1759,7 +1759,7 @@ static int fc_lport_ct_request(struct fc_bsg_job *job, fh = fc_frame_header_get(fp); fh->fh_r_ctl = FC_RCTL_DD_UNSOL_CTL; hton24(fh->fh_d_id, did); - hton24(fh->fh_s_id, fc_host_port_id(lport->host)); + hton24(fh->fh_s_id, lport->port_id); fh->fh_type = FC_TYPE_CT; hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT); diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c index c68f6c7341c..dd2b43bb1c7 100644 --- a/drivers/scsi/libfc/fc_npiv.c +++ b/drivers/scsi/libfc/fc_npiv.c @@ -69,12 +69,15 @@ struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id) struct fc_lport *lport = NULL; struct fc_lport *vn_port; - if (fc_host_port_id(n_port->host) == port_id) + if (n_port->port_id == port_id) return n_port; + if (port_id == FC_FID_FLOGI) + return n_port; /* for point-to-point */ + mutex_lock(&n_port->lp_mutex); list_for_each_entry(vn_port, &n_port->vports, list) { - if (fc_host_port_id(vn_port->host) == port_id) { + if (vn_port->port_id == port_id) { lport = vn_port; break; } diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index b37d0ff28b3..39e440f0f54 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -1442,136 +1442,115 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, struct fc_els_spp *spp; /* response spp */ unsigned int len; unsigned int plen; - enum fc_els_rjt_reason reason = ELS_RJT_UNAB; - enum fc_els_rjt_explan explan = ELS_EXPL_NONE; enum fc_els_spp_resp resp; struct fc_seq_els_data rjt_data; u32 f_ctl; u32 fcp_parm; u32 roles = FC_RPORT_ROLE_UNKNOWN; - rjt_data.fp = NULL; + rjt_data.fp = NULL; fh = fc_frame_header_get(rx_fp); FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n", fc_rport_state(rdata)); - switch (rdata->rp_state) { - case RPORT_ST_PRLI: - case RPORT_ST_RTV: - case RPORT_ST_READY: - case RPORT_ST_ADISC: - reason = ELS_RJT_NONE; - break; - default: - fc_frame_free(rx_fp); - return; - break; - } len = fr_len(rx_fp) - sizeof(*fh); pp = fc_frame_payload_get(rx_fp, sizeof(*pp)); - if (pp == NULL) { - reason = ELS_RJT_PROT; - explan = ELS_EXPL_INV_LEN; - } else { - plen = ntohs(pp->prli.prli_len); - if ((plen % 4) != 0 || plen > len) { - reason = ELS_RJT_PROT; - explan = ELS_EXPL_INV_LEN; - } else if (plen < len) { - len = plen; - } - plen = pp->prli.prli_spp_len; - if ((plen % 4) != 0 || plen < sizeof(*spp) || - plen > len || len < sizeof(*pp)) { - reason = ELS_RJT_PROT; - explan = ELS_EXPL_INV_LEN; - } - rspp = &pp->spp; + if (!pp) + goto reject_len; + plen = ntohs(pp->prli.prli_len); + if ((plen % 4) != 0 || plen > len || plen < 16) + goto reject_len; + if (plen < len) + len = plen; + plen = pp->prli.prli_spp_len; + if ((plen % 4) != 0 || plen < sizeof(*spp) || + plen > len || len < sizeof(*pp) || plen < 12) + goto reject_len; + rspp = &pp->spp; + + fp = fc_frame_alloc(lport, len); + if (!fp) { + rjt_data.reason = ELS_RJT_UNAB; + rjt_data.explan = ELS_EXPL_INSUF_RES; + goto reject; } - if (reason != ELS_RJT_NONE || - (fp = fc_frame_alloc(lport, len)) == NULL) { - rjt_data.reason = reason; - rjt_data.explan = explan; - lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); - } else { - sp = lport->tt.seq_start_next(sp); - WARN_ON(!sp); - pp = fc_frame_payload_get(fp, len); - WARN_ON(!pp); - memset(pp, 0, len); - pp->prli.prli_cmd = ELS_LS_ACC; - pp->prli.prli_spp_len = plen; - pp->prli.prli_len = htons(len); - len -= sizeof(struct fc_els_prli); - - /* reinitialize remote port roles */ - rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; - - /* - * Go through all the service parameter pages and build - * response. If plen indicates longer SPP than standard, - * use that. The entire response has been pre-cleared above. - */ - spp = &pp->spp; - while (len >= plen) { - spp->spp_type = rspp->spp_type; - spp->spp_type_ext = rspp->spp_type_ext; - spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR; - resp = FC_SPP_RESP_ACK; - if (rspp->spp_flags & FC_SPP_RPA_VAL) - resp = FC_SPP_RESP_NO_PA; - switch (rspp->spp_type) { - case 0: /* common to all FC-4 types */ - break; - case FC_TYPE_FCP: - fcp_parm = ntohl(rspp->spp_params); - if (fcp_parm & FCP_SPPF_RETRY) - rdata->flags |= FC_RP_FLAGS_RETRY; - rdata->supported_classes = FC_COS_CLASS3; - if (fcp_parm & FCP_SPPF_INIT_FCN) - roles |= FC_RPORT_ROLE_FCP_INITIATOR; - if (fcp_parm & FCP_SPPF_TARG_FCN) - roles |= FC_RPORT_ROLE_FCP_TARGET; - rdata->ids.roles = roles; - - spp->spp_params = - htonl(lport->service_params); - break; - default: - resp = FC_SPP_RESP_INVL; - break; - } - spp->spp_flags |= resp; - len -= plen; - rspp = (struct fc_els_spp *)((char *)rspp + plen); - spp = (struct fc_els_spp *)((char *)spp + plen); - } + sp = lport->tt.seq_start_next(sp); + WARN_ON(!sp); + pp = fc_frame_payload_get(fp, len); + WARN_ON(!pp); + memset(pp, 0, len); + pp->prli.prli_cmd = ELS_LS_ACC; + pp->prli.prli_spp_len = plen; + pp->prli.prli_len = htons(len); + len -= sizeof(struct fc_els_prli); - /* - * Send LS_ACC. If this fails, the originator should retry. - */ - f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ; - f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT; - ep = fc_seq_exch(sp); - fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, - FC_TYPE_ELS, f_ctl, 0); - lport->tt.seq_send(lport, sp, fp); + /* reinitialize remote port roles */ + rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; - /* - * Get lock and re-check state. - */ - switch (rdata->rp_state) { - case RPORT_ST_PRLI: - fc_rport_enter_ready(rdata); + /* + * Go through all the service parameter pages and build + * response. If plen indicates longer SPP than standard, + * use that. The entire response has been pre-cleared above. + */ + spp = &pp->spp; + while (len >= plen) { + spp->spp_type = rspp->spp_type; + spp->spp_type_ext = rspp->spp_type_ext; + spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR; + resp = FC_SPP_RESP_ACK; + + switch (rspp->spp_type) { + case 0: /* common to all FC-4 types */ break; - case RPORT_ST_READY: - case RPORT_ST_ADISC: + case FC_TYPE_FCP: + fcp_parm = ntohl(rspp->spp_params); + if (fcp_parm & FCP_SPPF_RETRY) + rdata->flags |= FC_RP_FLAGS_RETRY; + rdata->supported_classes = FC_COS_CLASS3; + if (fcp_parm & FCP_SPPF_INIT_FCN) + roles |= FC_RPORT_ROLE_FCP_INITIATOR; + if (fcp_parm & FCP_SPPF_TARG_FCN) + roles |= FC_RPORT_ROLE_FCP_TARGET; + rdata->ids.roles = roles; + + spp->spp_params = htonl(lport->service_params); break; default: + resp = FC_SPP_RESP_INVL; break; } + spp->spp_flags |= resp; + len -= plen; + rspp = (struct fc_els_spp *)((char *)rspp + plen); + spp = (struct fc_els_spp *)((char *)spp + plen); + } + + /* + * Send LS_ACC. If this fails, the originator should retry. + */ + f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ; + f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT; + ep = fc_seq_exch(sp); + fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, + FC_TYPE_ELS, f_ctl, 0); + lport->tt.seq_send(lport, sp, fp); + + switch (rdata->rp_state) { + case RPORT_ST_PRLI: + fc_rport_enter_ready(rdata); + break; + default: + break; } + goto drop; + +reject_len: + rjt_data.reason = ELS_RJT_PROT; + rjt_data.explan = ELS_EXPL_INV_LEN; +reject: + lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data); +drop: fc_frame_free(rx_fp); } diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index 5c92620292f..8eeb39ffa37 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -421,7 +421,7 @@ iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn) struct iscsi_conn *conn = tcp_conn->iscsi_conn; struct hash_desc *rx_hash = NULL; - if (conn->datadgst_en & + if (conn->datadgst_en && !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD)) rx_hash = tcp_conn->rx_hash; diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 88f74467257..8c496b56556 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -395,12 +395,13 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, void sas_ata_task_abort(struct sas_task *task) { struct ata_queued_cmd *qc = task->uldd_task; - struct request_queue *q = qc->scsicmd->device->request_queue; struct completion *waiting; - unsigned long flags; /* Bounce SCSI-initiated commands to the SCSI EH */ if (qc->scsicmd) { + struct request_queue *q = qc->scsicmd->device->request_queue; + unsigned long flags; + spin_lock_irqsave(q->queue_lock, flags); blk_abort_request(qc->scsicmd->request); spin_unlock_irqrestore(q->queue_lock, flags); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index b71b6d41baa..a7890c6d878 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -1030,8 +1030,6 @@ int __sas_task_abort(struct sas_task *task) void sas_task_abort(struct sas_task *task) { struct scsi_cmnd *sc = task->uldd_task; - struct request_queue *q = sc->device->request_queue; - unsigned long flags; /* Escape for libsas internal commands */ if (!sc) { @@ -1043,13 +1041,15 @@ void sas_task_abort(struct sas_task *task) if (dev_is_sata(task->dev)) { sas_ata_task_abort(task); - return; - } + } else { + struct request_queue *q = sc->device->request_queue; + unsigned long flags; - spin_lock_irqsave(q->queue_lock, flags); - blk_abort_request(sc->request); - spin_unlock_irqrestore(q->queue_lock, flags); - scsi_schedule_eh(sc->device->host); + spin_lock_irqsave(q->queue_lock, flags); + blk_abort_request(sc->request); + spin_unlock_irqrestore(q->queue_lock, flags); + scsi_schedule_eh(sc->device->host); + } } int sas_slave_alloc(struct scsi_device *scsi_dev) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 565e16dd74f..e35a4c71eb9 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -310,7 +310,9 @@ struct lpfc_vport { #define FC_NLP_MORE 0x40 /* More node to process in node tbl */ #define FC_OFFLINE_MODE 0x80 /* Interface is offline for diag */ #define FC_FABRIC 0x100 /* We are fabric attached */ +#define FC_VPORT_LOGO_RCVD 0x200 /* LOGO received on vport */ #define FC_RSCN_DISCOVERY 0x400 /* Auth all devices after RSCN */ +#define FC_LOGO_RCVD_DID_CHNG 0x800 /* FDISC on phys port detect DID chng*/ #define FC_SCSI_SCAN_TMO 0x4000 /* scsi scan timer running */ #define FC_ABORT_DISCOVERY 0x8000 /* we want to abort discovery */ #define FC_NDISC_ACTIVE 0x10000 /* NPort discovery active */ @@ -554,6 +556,7 @@ struct lpfc_hba { struct lpfc_dmabuf slim2p; MAILBOX_t *mbox; + uint32_t *mbox_ext; uint32_t *inb_ha_copy; uint32_t *inb_counter; uint32_t inb_last_counter; @@ -622,6 +625,7 @@ struct lpfc_hba { uint32_t cfg_enable_hba_reset; uint32_t cfg_enable_hba_heartbeat; uint32_t cfg_enable_bg; + uint32_t cfg_hostmem_hgp; uint32_t cfg_log_verbose; uint32_t cfg_aer_support; uint32_t cfg_suppress_link_up; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 1849e33e68f..bf33b315f93 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -869,6 +869,7 @@ lpfc_get_hba_info(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq; MAILBOX_t *pmb; int rc = 0; + uint32_t max_vpi; /* * prevent udev from issuing mailbox commands until the port is @@ -916,11 +917,17 @@ lpfc_get_hba_info(struct lpfc_hba *phba, if (axri) *axri = bf_get(lpfc_mbx_rd_conf_xri_count, rd_config) - phba->sli4_hba.max_cfg_param.xri_used; + + /* Account for differences with SLI-3. Get vpi count from + * mailbox data and subtract one for max vpi value. + */ + max_vpi = (bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) > 0) ? + (bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) - 1) : 0; + if (mvpi) - *mvpi = bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config); + *mvpi = max_vpi; if (avpi) - *avpi = bf_get(lpfc_mbx_rd_conf_vpi_count, rd_config) - - phba->sli4_hba.max_cfg_param.vpi_used; + *avpi = max_vpi - phba->sli4_hba.max_cfg_param.vpi_used; } else { if (mrpi) *mrpi = pmb->un.varRdConfig.max_rpi; @@ -1925,13 +1932,12 @@ MODULE_PARM_DESC(lpfc_sli_mode, "SLI mode selector:" " 2 - select SLI-2 even on SLI-3 capable HBAs," " 3 - select SLI-3"); -int lpfc_enable_npiv = 0; +int lpfc_enable_npiv = 1; module_param(lpfc_enable_npiv, int, 0); MODULE_PARM_DESC(lpfc_enable_npiv, "Enable NPIV functionality"); lpfc_param_show(enable_npiv); -lpfc_param_init(enable_npiv, 0, 0, 1); -static DEVICE_ATTR(lpfc_enable_npiv, S_IRUGO, - lpfc_enable_npiv_show, NULL); +lpfc_param_init(enable_npiv, 1, 0, 1); +static DEVICE_ATTR(lpfc_enable_npiv, S_IRUGO, lpfc_enable_npiv_show, NULL); /* # lpfc_suppress_link_up: Bring link up at initialization @@ -2637,6 +2643,7 @@ static DEVICE_ATTR(lpfc_stat_data_ctrl, S_IRUGO | S_IWUSR, /** * sysfs_drvr_stat_data_read - Read function for lpfc_drvr_stat_data attribute + * @filp: sysfs file * @kobj: Pointer to the kernel object * @bin_attr: Attribute object * @buff: Buffer pointer @@ -2648,7 +2655,8 @@ static DEVICE_ATTR(lpfc_stat_data_ctrl, S_IRUGO | S_IWUSR, * applications. **/ static ssize_t -sysfs_drvr_stat_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, +sysfs_drvr_stat_data_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, @@ -3356,6 +3364,7 @@ struct device_attribute *lpfc_vport_attrs[] = { /** * sysfs_ctlreg_write - Write method for writing to ctlreg + * @filp: open sysfs file * @kobj: kernel kobject that contains the kernel class device. * @bin_attr: kernel attributes passed to us. * @buf: contains the data to be written to the adapter IOREG space. @@ -3373,7 +3382,8 @@ struct device_attribute *lpfc_vport_attrs[] = { * value of count, buf contents written **/ static ssize_t -sysfs_ctlreg_write(struct kobject *kobj, struct bin_attribute *bin_attr, +sysfs_ctlreg_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { size_t buf_off; @@ -3409,6 +3419,7 @@ sysfs_ctlreg_write(struct kobject *kobj, struct bin_attribute *bin_attr, /** * sysfs_ctlreg_read - Read method for reading from ctlreg + * @filp: open sysfs file * @kobj: kernel kobject that contains the kernel class device. * @bin_attr: kernel attributes passed to us. * @buf: if successful contains the data from the adapter IOREG space. @@ -3425,7 +3436,8 @@ sysfs_ctlreg_write(struct kobject *kobj, struct bin_attribute *bin_attr, * value of count, buf contents read **/ static ssize_t -sysfs_ctlreg_read(struct kobject *kobj, struct bin_attribute *bin_attr, +sysfs_ctlreg_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { size_t buf_off; @@ -3490,6 +3502,7 @@ sysfs_mbox_idle(struct lpfc_hba *phba) /** * sysfs_mbox_write - Write method for writing information via mbox + * @filp: open sysfs file * @kobj: kernel kobject that contains the kernel class device. * @bin_attr: kernel attributes passed to us. * @buf: contains the data to be written to sysfs mbox. @@ -3510,7 +3523,8 @@ sysfs_mbox_idle(struct lpfc_hba *phba) * count number of bytes transferred **/ static ssize_t -sysfs_mbox_write(struct kobject *kobj, struct bin_attribute *bin_attr, +sysfs_mbox_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); @@ -3565,6 +3579,7 @@ sysfs_mbox_write(struct kobject *kobj, struct bin_attribute *bin_attr, /** * sysfs_mbox_read - Read method for reading information via mbox + * @filp: open sysfs file * @kobj: kernel kobject that contains the kernel class device. * @bin_attr: kernel attributes passed to us. * @buf: contains the data to be read from sysfs mbox. @@ -3587,7 +3602,8 @@ sysfs_mbox_write(struct kobject *kobj, struct bin_attribute *bin_attr, * count number of bytes transferred **/ static ssize_t -sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr, +sysfs_mbox_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index d62b3e46792..dcf088262b2 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -79,6 +79,12 @@ struct lpfc_bsg_iocb { struct lpfc_bsg_mbox { LPFC_MBOXQ_t *pmboxq; MAILBOX_t *mb; + struct lpfc_dmabuf *rxbmp; /* for BIU diags */ + struct lpfc_dmabufext *dmp; /* for BIU diags */ + uint8_t *ext; /* extended mailbox data */ + uint32_t mbOffset; /* from app */ + uint32_t inExtWLen; /* from app */ + uint32_t outExtWLen; /* from app */ /* job waiting for this mbox command to finish */ struct fc_bsg_job *set_job; @@ -1708,21 +1714,26 @@ static int lpfcdiag_loop_get_xri(struct lpfc_hba *phba, uint16_t rpi, dmabuf = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); if (dmabuf) { dmabuf->virt = lpfc_mbuf_alloc(phba, 0, &dmabuf->phys); - INIT_LIST_HEAD(&dmabuf->list); - bpl = (struct ulp_bde64 *) dmabuf->virt; - memset(bpl, 0, sizeof(*bpl)); - ctreq = (struct lpfc_sli_ct_request *)(bpl + 1); - bpl->addrHigh = - le32_to_cpu(putPaddrHigh(dmabuf->phys + sizeof(*bpl))); - bpl->addrLow = - le32_to_cpu(putPaddrLow(dmabuf->phys + sizeof(*bpl))); - bpl->tus.f.bdeFlags = 0; - bpl->tus.f.bdeSize = ELX_LOOPBACK_HEADER_SZ; - bpl->tus.w = le32_to_cpu(bpl->tus.w); + if (dmabuf->virt) { + INIT_LIST_HEAD(&dmabuf->list); + bpl = (struct ulp_bde64 *) dmabuf->virt; + memset(bpl, 0, sizeof(*bpl)); + ctreq = (struct lpfc_sli_ct_request *)(bpl + 1); + bpl->addrHigh = + le32_to_cpu(putPaddrHigh(dmabuf->phys + + sizeof(*bpl))); + bpl->addrLow = + le32_to_cpu(putPaddrLow(dmabuf->phys + + sizeof(*bpl))); + bpl->tus.f.bdeFlags = 0; + bpl->tus.f.bdeSize = ELX_LOOPBACK_HEADER_SZ; + bpl->tus.w = le32_to_cpu(bpl->tus.w); + } } if (cmdiocbq == NULL || rspiocbq == NULL || - dmabuf == NULL || bpl == NULL || ctreq == NULL) { + dmabuf == NULL || bpl == NULL || ctreq == NULL || + dmabuf->virt == NULL) { ret_val = ENOMEM; goto err_get_xri_exit; } @@ -1918,9 +1929,11 @@ static int lpfcdiag_loop_post_rxbufs(struct lpfc_hba *phba, uint16_t rxxri, rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); if (rxbmp != NULL) { rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); - INIT_LIST_HEAD(&rxbmp->list); - rxbpl = (struct ulp_bde64 *) rxbmp->virt; - rxbuffer = diag_cmd_data_alloc(phba, rxbpl, len, 0); + if (rxbmp->virt) { + INIT_LIST_HEAD(&rxbmp->list); + rxbpl = (struct ulp_bde64 *) rxbmp->virt; + rxbuffer = diag_cmd_data_alloc(phba, rxbpl, len, 0); + } } if (!cmdiocbq || !rxbmp || !rxbpl || !rxbuffer) { @@ -2174,14 +2187,16 @@ lpfc_bsg_diag_test(struct fc_bsg_job *job) if (txbmp) { txbmp->virt = lpfc_mbuf_alloc(phba, 0, &txbmp->phys); - INIT_LIST_HEAD(&txbmp->list); - txbpl = (struct ulp_bde64 *) txbmp->virt; - if (txbpl) + if (txbmp->virt) { + INIT_LIST_HEAD(&txbmp->list); + txbpl = (struct ulp_bde64 *) txbmp->virt; txbuffer = diag_cmd_data_alloc(phba, txbpl, full_size, 0); + } } - if (!cmdiocbq || !rspiocbq || !txbmp || !txbpl || !txbuffer) { + if (!cmdiocbq || !rspiocbq || !txbmp || !txbpl || !txbuffer || + !txbmp->virt) { rc = -ENOMEM; goto err_loopback_test_exit; } @@ -2377,35 +2392,90 @@ void lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) { struct bsg_job_data *dd_data; - MAILBOX_t *pmb; - MAILBOX_t *mb; struct fc_bsg_job *job; uint32_t size; unsigned long flags; + uint8_t *to; + uint8_t *from; spin_lock_irqsave(&phba->ct_ev_lock, flags); dd_data = pmboxq->context1; + /* job already timed out? */ if (!dd_data) { spin_unlock_irqrestore(&phba->ct_ev_lock, flags); return; } - pmb = &dd_data->context_un.mbox.pmboxq->u.mb; - mb = dd_data->context_un.mbox.mb; + /* build the outgoing buffer to do an sg copy + * the format is the response mailbox followed by any extended + * mailbox data + */ + from = (uint8_t *)&pmboxq->u.mb; + to = (uint8_t *)dd_data->context_un.mbox.mb; + memcpy(to, from, sizeof(MAILBOX_t)); + if (pmboxq->u.mb.mbxStatus == MBX_SUCCESS) { + /* copy the extended data if any, count is in words */ + if (dd_data->context_un.mbox.outExtWLen) { + from = (uint8_t *)dd_data->context_un.mbox.ext; + to += sizeof(MAILBOX_t); + size = dd_data->context_un.mbox.outExtWLen * + sizeof(uint32_t); + memcpy(to, from, size); + } else if (pmboxq->u.mb.mbxCommand == MBX_RUN_BIU_DIAG64) { + from = (uint8_t *)dd_data->context_un.mbox. + dmp->dma.virt; + to += sizeof(MAILBOX_t); + size = dd_data->context_un.mbox.dmp->size; + memcpy(to, from, size); + } else if ((phba->sli_rev == LPFC_SLI_REV4) && + (pmboxq->u.mb.mbxCommand == MBX_DUMP_MEMORY)) { + from = (uint8_t *)dd_data->context_un.mbox.dmp->dma. + virt; + to += sizeof(MAILBOX_t); + size = pmboxq->u.mb.un.varWords[5]; + memcpy(to, from, size); + } else if (pmboxq->u.mb.mbxCommand == MBX_READ_EVENT_LOG) { + from = (uint8_t *)dd_data->context_un. + mbox.dmp->dma.virt; + to += sizeof(MAILBOX_t); + size = dd_data->context_un.mbox.dmp->size; + memcpy(to, from, size); + } + } + + from = (uint8_t *)dd_data->context_un.mbox.mb; job = dd_data->context_un.mbox.set_job; - memcpy(mb, pmb, sizeof(*pmb)); - size = job->request_payload.payload_len; + size = job->reply_payload.payload_len; job->reply->reply_payload_rcv_len = sg_copy_from_buffer(job->reply_payload.sg_list, job->reply_payload.sg_cnt, - mb, size); + from, size); job->reply->result = 0; + dd_data->context_un.mbox.set_job = NULL; job->dd_data = NULL; job->job_done(job); + /* need to hold the lock until we call job done to hold off + * the timeout handler returning to the midlayer while + * we are stillprocessing the job + */ spin_unlock_irqrestore(&phba->ct_ev_lock, flags); + + kfree(dd_data->context_un.mbox.mb); mempool_free(dd_data->context_un.mbox.pmboxq, phba->mbox_mem_pool); - kfree(mb); + kfree(dd_data->context_un.mbox.ext); + if (dd_data->context_un.mbox.dmp) { + dma_free_coherent(&phba->pcidev->dev, + dd_data->context_un.mbox.dmp->size, + dd_data->context_un.mbox.dmp->dma.virt, + dd_data->context_un.mbox.dmp->dma.phys); + kfree(dd_data->context_un.mbox.dmp); + } + if (dd_data->context_un.mbox.rxbmp) { + lpfc_mbuf_free(phba, dd_data->context_un.mbox.rxbmp->virt, + dd_data->context_un.mbox.rxbmp->phys); + kfree(dd_data->context_un.mbox.rxbmp); + } kfree(dd_data); return; } @@ -2464,10 +2534,12 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba, case MBX_SET_DEBUG: case MBX_WRITE_WWN: case MBX_SLI4_CONFIG: + case MBX_READ_EVENT_LOG: case MBX_READ_EVENT_LOG_STATUS: case MBX_WRITE_EVENT_LOG: case MBX_PORT_CAPABILITIES: case MBX_PORT_IOV_CONTROL: + case MBX_RUN_BIU_DIAG64: break; case MBX_SET_VARIABLE: lpfc_printf_log(phba, KERN_INFO, LOG_INIT, @@ -2482,8 +2554,6 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba, phba->fc_topology = TOPOLOGY_PT_PT; } break; - case MBX_RUN_BIU_DIAG64: - case MBX_READ_EVENT_LOG: case MBX_READ_SPARM64: case MBX_READ_LA: case MBX_READ_LA64: @@ -2518,97 +2588,365 @@ static uint32_t lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job, struct lpfc_vport *vport) { - LPFC_MBOXQ_t *pmboxq; - MAILBOX_t *pmb; - MAILBOX_t *mb; - struct bsg_job_data *dd_data; + LPFC_MBOXQ_t *pmboxq = NULL; /* internal mailbox queue */ + MAILBOX_t *pmb; /* shortcut to the pmboxq mailbox */ + /* a 4k buffer to hold the mb and extended data from/to the bsg */ + MAILBOX_t *mb = NULL; + struct bsg_job_data *dd_data = NULL; /* bsg data tracking structure */ uint32_t size; + struct lpfc_dmabuf *rxbmp = NULL; /* for biu diag */ + struct lpfc_dmabufext *dmp = NULL; /* for biu diag */ + struct ulp_bde64 *rxbpl = NULL; + struct dfc_mbox_req *mbox_req = (struct dfc_mbox_req *) + job->request->rqst_data.h_vendor.vendor_cmd; + uint8_t *ext = NULL; int rc = 0; + uint8_t *from; + + /* in case no data is transferred */ + job->reply->reply_payload_rcv_len = 0; + + /* check if requested extended data lengths are valid */ + if ((mbox_req->inExtWLen > MAILBOX_EXT_SIZE) || + (mbox_req->outExtWLen > MAILBOX_EXT_SIZE)) { + rc = -ERANGE; + goto job_done; + } /* allocate our bsg tracking structure */ dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); if (!dd_data) { lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, "2727 Failed allocation of dd_data\n"); - return -ENOMEM; + rc = -ENOMEM; + goto job_done; } - mb = kzalloc(PAGE_SIZE, GFP_KERNEL); + mb = kzalloc(BSG_MBOX_SIZE, GFP_KERNEL); if (!mb) { - kfree(dd_data); - return -ENOMEM; + rc = -ENOMEM; + goto job_done; } pmboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmboxq) { - kfree(dd_data); - kfree(mb); - return -ENOMEM; + rc = -ENOMEM; + goto job_done; } + memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t)); size = job->request_payload.payload_len; - job->reply->reply_payload_rcv_len = - sg_copy_to_buffer(job->request_payload.sg_list, - job->request_payload.sg_cnt, - mb, size); + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, + mb, size); rc = lpfc_bsg_check_cmd_access(phba, mb, vport); - if (rc != 0) { - kfree(dd_data); - kfree(mb); - mempool_free(pmboxq, phba->mbox_mem_pool); - return rc; /* must be negative */ - } + if (rc != 0) + goto job_done; /* must be negative */ - memset(pmboxq, 0, sizeof(LPFC_MBOXQ_t)); pmb = &pmboxq->u.mb; memcpy(pmb, mb, sizeof(*pmb)); pmb->mbxOwner = OWN_HOST; - pmboxq->context1 = NULL; pmboxq->vport = vport; - if ((vport->fc_flag & FC_OFFLINE_MODE) || - (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) { - rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL); - if (rc != MBX_SUCCESS) { - if (rc != MBX_TIMEOUT) { - kfree(dd_data); - kfree(mb); - mempool_free(pmboxq, phba->mbox_mem_pool); + /* If HBA encountered an error attention, allow only DUMP + * or RESTART mailbox commands until the HBA is restarted. + */ + if (phba->pport->stopped && + pmb->mbxCommand != MBX_DUMP_MEMORY && + pmb->mbxCommand != MBX_RESTART && + pmb->mbxCommand != MBX_WRITE_VPARMS && + pmb->mbxCommand != MBX_WRITE_WWN) + lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX, + "2797 mbox: Issued mailbox cmd " + "0x%x while in stopped state.\n", + pmb->mbxCommand); + + /* Don't allow mailbox commands to be sent when blocked + * or when in the middle of discovery + */ + if (phba->sli.sli_flag & LPFC_BLOCK_MGMT_IO) { + rc = -EAGAIN; + goto job_done; + } + + /* extended mailbox commands will need an extended buffer */ + if (mbox_req->inExtWLen || mbox_req->outExtWLen) { + ext = kzalloc(MAILBOX_EXT_SIZE, GFP_KERNEL); + if (!ext) { + rc = -ENOMEM; + goto job_done; + } + + /* any data for the device? */ + if (mbox_req->inExtWLen) { + from = (uint8_t *)mb; + from += sizeof(MAILBOX_t); + memcpy((uint8_t *)ext, from, + mbox_req->inExtWLen * sizeof(uint32_t)); + } + + pmboxq->context2 = ext; + pmboxq->in_ext_byte_len = + mbox_req->inExtWLen * + sizeof(uint32_t); + pmboxq->out_ext_byte_len = + mbox_req->outExtWLen * + sizeof(uint32_t); + pmboxq->mbox_offset_word = + mbox_req->mbOffset; + pmboxq->context2 = ext; + pmboxq->in_ext_byte_len = + mbox_req->inExtWLen * sizeof(uint32_t); + pmboxq->out_ext_byte_len = + mbox_req->outExtWLen * sizeof(uint32_t); + pmboxq->mbox_offset_word = mbox_req->mbOffset; + } + + /* biu diag will need a kernel buffer to transfer the data + * allocate our own buffer and setup the mailbox command to + * use ours + */ + if (pmb->mbxCommand == MBX_RUN_BIU_DIAG64) { + uint32_t transmit_length = pmb->un.varWords[1]; + uint32_t receive_length = pmb->un.varWords[4]; + /* transmit length cannot be greater than receive length or + * mailbox extension size + */ + if ((transmit_length > receive_length) || + (transmit_length > MAILBOX_EXT_SIZE)) { + rc = -ERANGE; + goto job_done; + } + + rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (!rxbmp) { + rc = -ENOMEM; + goto job_done; + } + + rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); + if (!rxbmp->virt) { + rc = -ENOMEM; + goto job_done; + } + + INIT_LIST_HEAD(&rxbmp->list); + rxbpl = (struct ulp_bde64 *) rxbmp->virt; + dmp = diag_cmd_data_alloc(phba, rxbpl, transmit_length, 0); + if (!dmp) { + rc = -ENOMEM; + goto job_done; + } + + INIT_LIST_HEAD(&dmp->dma.list); + pmb->un.varBIUdiag.un.s2.xmit_bde64.addrHigh = + putPaddrHigh(dmp->dma.phys); + pmb->un.varBIUdiag.un.s2.xmit_bde64.addrLow = + putPaddrLow(dmp->dma.phys); + + pmb->un.varBIUdiag.un.s2.rcv_bde64.addrHigh = + putPaddrHigh(dmp->dma.phys + + pmb->un.varBIUdiag.un.s2. + xmit_bde64.tus.f.bdeSize); + pmb->un.varBIUdiag.un.s2.rcv_bde64.addrLow = + putPaddrLow(dmp->dma.phys + + pmb->un.varBIUdiag.un.s2. + xmit_bde64.tus.f.bdeSize); + + /* copy the transmit data found in the mailbox extension area */ + from = (uint8_t *)mb; + from += sizeof(MAILBOX_t); + memcpy((uint8_t *)dmp->dma.virt, from, transmit_length); + } else if (pmb->mbxCommand == MBX_READ_EVENT_LOG) { + struct READ_EVENT_LOG_VAR *rdEventLog = + &pmb->un.varRdEventLog ; + uint32_t receive_length = rdEventLog->rcv_bde64.tus.f.bdeSize; + uint32_t mode = bf_get(lpfc_event_log, rdEventLog); + + /* receive length cannot be greater than mailbox + * extension size + */ + if (receive_length > MAILBOX_EXT_SIZE) { + rc = -ERANGE; + goto job_done; + } + + /* mode zero uses a bde like biu diags command */ + if (mode == 0) { + + /* rebuild the command for sli4 using our own buffers + * like we do for biu diags + */ + + rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (!rxbmp) { + rc = -ENOMEM; + goto job_done; } - return (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV; + + rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); + rxbpl = (struct ulp_bde64 *) rxbmp->virt; + if (rxbpl) { + INIT_LIST_HEAD(&rxbmp->list); + dmp = diag_cmd_data_alloc(phba, rxbpl, + receive_length, 0); + } + + if (!dmp) { + rc = -ENOMEM; + goto job_done; + } + + INIT_LIST_HEAD(&dmp->dma.list); + pmb->un.varWords[3] = putPaddrLow(dmp->dma.phys); + pmb->un.varWords[4] = putPaddrHigh(dmp->dma.phys); } + } else if (phba->sli_rev == LPFC_SLI_REV4) { + if (pmb->mbxCommand == MBX_DUMP_MEMORY) { + /* rebuild the command for sli4 using our own buffers + * like we do for biu diags + */ + uint32_t receive_length = pmb->un.varWords[2]; + /* receive length cannot be greater than mailbox + * extension size + */ + if ((receive_length == 0) || + (receive_length > MAILBOX_EXT_SIZE)) { + rc = -ERANGE; + goto job_done; + } - memcpy(mb, pmb, sizeof(*pmb)); - job->reply->reply_payload_rcv_len = - sg_copy_from_buffer(job->reply_payload.sg_list, - job->reply_payload.sg_cnt, - mb, size); - kfree(dd_data); - kfree(mb); - mempool_free(pmboxq, phba->mbox_mem_pool); - /* not waiting mbox already done */ - return 0; + rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (!rxbmp) { + rc = -ENOMEM; + goto job_done; + } + + rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); + if (!rxbmp->virt) { + rc = -ENOMEM; + goto job_done; + } + + INIT_LIST_HEAD(&rxbmp->list); + rxbpl = (struct ulp_bde64 *) rxbmp->virt; + dmp = diag_cmd_data_alloc(phba, rxbpl, receive_length, + 0); + if (!dmp) { + rc = -ENOMEM; + goto job_done; + } + + INIT_LIST_HEAD(&dmp->dma.list); + pmb->un.varWords[3] = putPaddrLow(dmp->dma.phys); + pmb->un.varWords[4] = putPaddrHigh(dmp->dma.phys); + } else if ((pmb->mbxCommand == MBX_UPDATE_CFG) && + pmb->un.varUpdateCfg.co) { + struct ulp_bde64 *bde = + (struct ulp_bde64 *)&pmb->un.varWords[4]; + + /* bde size cannot be greater than mailbox ext size */ + if (bde->tus.f.bdeSize > MAILBOX_EXT_SIZE) { + rc = -ERANGE; + goto job_done; + } + + rxbmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); + if (!rxbmp) { + rc = -ENOMEM; + goto job_done; + } + + rxbmp->virt = lpfc_mbuf_alloc(phba, 0, &rxbmp->phys); + if (!rxbmp->virt) { + rc = -ENOMEM; + goto job_done; + } + + INIT_LIST_HEAD(&rxbmp->list); + rxbpl = (struct ulp_bde64 *) rxbmp->virt; + dmp = diag_cmd_data_alloc(phba, rxbpl, + bde->tus.f.bdeSize, 0); + if (!dmp) { + rc = -ENOMEM; + goto job_done; + } + + INIT_LIST_HEAD(&dmp->dma.list); + bde->addrHigh = putPaddrHigh(dmp->dma.phys); + bde->addrLow = putPaddrLow(dmp->dma.phys); + + /* copy the transmit data found in the mailbox + * extension area + */ + from = (uint8_t *)mb; + from += sizeof(MAILBOX_t); + memcpy((uint8_t *)dmp->dma.virt, from, + bde->tus.f.bdeSize); + } } + dd_data->context_un.mbox.rxbmp = rxbmp; + dd_data->context_un.mbox.dmp = dmp; + /* setup wake call as IOCB callback */ pmboxq->mbox_cmpl = lpfc_bsg_wake_mbox_wait; + /* setup context field to pass wait_queue pointer to wake function */ pmboxq->context1 = dd_data; dd_data->type = TYPE_MBOX; dd_data->context_un.mbox.pmboxq = pmboxq; dd_data->context_un.mbox.mb = mb; dd_data->context_un.mbox.set_job = job; + dd_data->context_un.mbox.ext = ext; + dd_data->context_un.mbox.mbOffset = mbox_req->mbOffset; + dd_data->context_un.mbox.inExtWLen = mbox_req->inExtWLen; + dd_data->context_un.mbox.outExtWLen = mbox_req->outExtWLen; job->dd_data = dd_data; + + if ((vport->fc_flag & FC_OFFLINE_MODE) || + (!(phba->sli.sli_flag & LPFC_SLI_ACTIVE))) { + rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_POLL); + if (rc != MBX_SUCCESS) { + rc = (rc == MBX_TIMEOUT) ? -ETIME : -ENODEV; + goto job_done; + } + + /* job finished, copy the data */ + memcpy(mb, pmb, sizeof(*pmb)); + job->reply->reply_payload_rcv_len = + sg_copy_from_buffer(job->reply_payload.sg_list, + job->reply_payload.sg_cnt, + mb, size); + /* not waiting mbox already done */ + rc = 0; + goto job_done; + } + rc = lpfc_sli_issue_mbox(phba, pmboxq, MBX_NOWAIT); - if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) { - kfree(dd_data); - kfree(mb); + if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) + return 1; /* job started */ + +job_done: + /* common exit for error or job completed inline */ + kfree(mb); + if (pmboxq) mempool_free(pmboxq, phba->mbox_mem_pool); - return -EIO; + kfree(ext); + if (dmp) { + dma_free_coherent(&phba->pcidev->dev, + dmp->size, dmp->dma.virt, + dmp->dma.phys); + kfree(dmp); } + if (rxbmp) { + lpfc_mbuf_free(phba, rxbmp->virt, rxbmp->phys); + kfree(rxbmp); + } + kfree(dd_data); - return 1; + return rc; } /** @@ -2633,7 +2971,12 @@ lpfc_bsg_mbox_cmd(struct fc_bsg_job *job) goto job_error; } - if (job->request_payload.payload_len != PAGE_SIZE) { + if (job->request_payload.payload_len != BSG_MBOX_SIZE) { + rc = -EINVAL; + goto job_error; + } + + if (job->reply_payload.payload_len != BSG_MBOX_SIZE) { rc = -EINVAL; goto job_error; } @@ -3094,6 +3437,7 @@ lpfc_bsg_timeout(struct fc_bsg_job *job) job->dd_data = NULL; job->reply->reply_payload_rcv_len = 0; job->reply->result = -EAGAIN; + /* the mbox completion handler can now be run */ spin_unlock_irqrestore(&phba->ct_ev_lock, flags); job->job_done(job); break; diff --git a/drivers/scsi/lpfc/lpfc_bsg.h b/drivers/scsi/lpfc/lpfc_bsg.h index 5bc630819b9..a2c33e7c915 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.h +++ b/drivers/scsi/lpfc/lpfc_bsg.h @@ -91,11 +91,12 @@ struct get_mgmt_rev_reply { struct MgmtRevInfo info; }; +#define BSG_MBOX_SIZE 4096 /* mailbox command plus extended data */ struct dfc_mbox_req { uint32_t command; + uint32_t mbOffset; uint32_t inExtWLen; uint32_t outExtWLen; - uint8_t mbOffset; }; /* Used for menlo command or menlo data. The xri is only used for menlo data */ diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 5087c4211b4..fbc9baeb604 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -65,6 +65,7 @@ void lpfc_mbx_cmpl_read_la(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_init_vpi_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_cancel_all_vport_retry_delay_timer(struct lpfc_hba *); void lpfc_retry_pport_discovery(struct lpfc_hba *); +void lpfc_release_rpi(struct lpfc_hba *, struct lpfc_vport *, uint16_t); void lpfc_mbx_cmpl_reg_login(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *, LPFC_MBOXQ_t *); diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index 2851d75ffc6..36257a68550 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -38,6 +38,7 @@ enum lpfc_work_type { LPFC_EVT_ELS_RETRY, LPFC_EVT_DEV_LOSS, LPFC_EVT_FASTPATH_MGMT_EVT, + LPFC_EVT_RESET_HBA, }; /* structure used to queue event to the discovery tasklet */ diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 5fbdb22c189..c4c7f0ad746 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -584,6 +584,7 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, spin_unlock_irq(shost->host_lock); lpfc_unreg_rpi(vport, np); } + lpfc_cleanup_pending_mbox(vport); if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED) { lpfc_mbx_unreg_vpi(vport); spin_lock_irq(shost->host_lock); @@ -864,6 +865,7 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, } spin_lock_irq(shost->host_lock); vport->fc_flag &= ~FC_VPORT_CVL_RCVD; + vport->fc_flag &= ~FC_VPORT_LOGO_RCVD; spin_unlock_irq(shost->host_lock); /* @@ -893,11 +895,14 @@ lpfc_cmpl_els_flogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, if (!rc) { /* Mark the FCF discovery process done */ - lpfc_printf_vlog(vport, KERN_INFO, LOG_FIP | LOG_ELS, - "2769 FLOGI successful on FCF record: " - "current_fcf_index:x%x, terminate FCF " - "round robin failover process\n", - phba->fcf.current_rec.fcf_indx); + if (phba->hba_flag & HBA_FIP_SUPPORT) + lpfc_printf_vlog(vport, KERN_INFO, LOG_FIP | + LOG_ELS, + "2769 FLOGI successful on FCF " + "record: current_fcf_index:" + "x%x, terminate FCF round " + "robin failover process\n", + phba->fcf.current_rec.fcf_indx); spin_lock_irq(&phba->hbalock); phba->fcf.fcf_flag &= ~FCF_DISCOVERY; spin_unlock_irq(&phba->hbalock); @@ -5366,7 +5371,7 @@ lpfc_send_els_failure_event(struct lpfc_hba *phba, sizeof(struct lpfc_name)); pcmd = (uint32_t *) (((struct lpfc_dmabuf *) cmdiocbp->context2)->virt); - lsrjt_event.command = *pcmd; + lsrjt_event.command = (pcmd != NULL) ? *pcmd : 0; stat.un.lsRjtError = be32_to_cpu(rspiocbp->iocb.un.ulpWord[4]); lsrjt_event.reason_code = stat.un.b.lsRjtRsnCode; lsrjt_event.explanation = stat.un.b.lsRjtRsnCodeExp; @@ -6050,7 +6055,8 @@ lpfc_cmpl_reg_new_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) spin_lock_irq(shost->host_lock); vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; spin_unlock_irq(shost->host_lock); - if (vport->port_type == LPFC_PHYSICAL_PORT) + if (vport->port_type == LPFC_PHYSICAL_PORT + && !(vport->fc_flag & FC_LOGO_RCVD_DID_CHNG)) lpfc_initial_flogi(vport); else lpfc_initial_fdisc(vport); @@ -6286,6 +6292,7 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, } spin_lock_irq(shost->host_lock); vport->fc_flag &= ~FC_VPORT_CVL_RCVD; + vport->fc_flag &= ~FC_VPORT_LOGO_RCVD; vport->fc_flag |= FC_FABRIC; if (vport->phba->fc_topology == TOPOLOGY_LOOP) vport->fc_flag |= FC_PUBLIC_LOOP; @@ -6310,11 +6317,14 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, spin_unlock_irq(shost->host_lock); lpfc_unreg_rpi(vport, np); } + lpfc_cleanup_pending_mbox(vport); lpfc_mbx_unreg_vpi(vport); spin_lock_irq(shost->host_lock); vport->fc_flag |= FC_VPORT_NEEDS_REG_VPI; if (phba->sli_rev == LPFC_SLI_REV4) vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI; + else + vport->fc_flag |= FC_LOGO_RCVD_DID_CHNG; spin_unlock_irq(shost->host_lock); } diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index e1466eec56b..1f87b4fb8b5 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -475,6 +475,10 @@ lpfc_work_list_done(struct lpfc_hba *phba) lpfc_send_fastpath_evt(phba, evtp); free_evt = 0; break; + case LPFC_EVT_RESET_HBA: + if (!(phba->pport->load_flag & FC_UNLOADING)) + lpfc_reset_hba(phba); + break; } if (free_evt) kfree(evtp); @@ -1531,7 +1535,37 @@ lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf) } /** - * lpfc_sli4_fcf_rec_mbox_parse - parse non-embedded fcf record mailbox command + * lpfc_sli4_new_fcf_random_select - Randomly select an eligible new fcf record + * @phba: pointer to lpfc hba data structure. + * @fcf_cnt: number of eligible fcf record seen so far. + * + * This function makes an running random selection decision on FCF record to + * use through a sequence of @fcf_cnt eligible FCF records with equal + * probability. To perform integer manunipulation of random numbers with + * size unit32_t, the lower 16 bits of the 32-bit random number returned + * from random32() are taken as the random random number generated. + * + * Returns true when outcome is for the newly read FCF record should be + * chosen; otherwise, return false when outcome is for keeping the previously + * chosen FCF record. + **/ +static bool +lpfc_sli4_new_fcf_random_select(struct lpfc_hba *phba, uint32_t fcf_cnt) +{ + uint32_t rand_num; + + /* Get 16-bit uniform random number */ + rand_num = (0xFFFF & random32()); + + /* Decision with probability 1/fcf_cnt */ + if ((fcf_cnt * rand_num) < 0xFFFF) + return true; + else + return false; +} + +/** + * lpfc_mbx_cmpl_read_fcf_record - Completion handler for read_fcf mbox. * @phba: pointer to lpfc hba data structure. * @mboxq: pointer to mailbox object. * @next_fcf_index: pointer to holder of next fcf index. @@ -1592,7 +1626,9 @@ lpfc_sli4_fcf_rec_mbox_parse(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq, new_fcf_record = (struct fcf_record *)(virt_addr + sizeof(struct lpfc_mbx_read_fcf_tbl)); lpfc_sli_pcimem_bcopy(new_fcf_record, new_fcf_record, - sizeof(struct fcf_record)); + offsetof(struct fcf_record, vlan_bitmap)); + new_fcf_record->word137 = le32_to_cpu(new_fcf_record->word137); + new_fcf_record->word138 = le32_to_cpu(new_fcf_record->word138); return new_fcf_record; } @@ -1679,6 +1715,8 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) uint16_t fcf_index, next_fcf_index; struct lpfc_fcf_rec *fcf_rec = NULL; uint16_t vlan_id; + uint32_t seed; + bool select_new_fcf; int rc; /* If there is pending FCoE event restart FCF table scan */ @@ -1809,9 +1847,21 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) * than the driver FCF record, use the new record. */ if (new_fcf_record->fip_priority < fcf_rec->priority) { - /* Choose this FCF record */ + /* Choose the new FCF record with lower priority */ __lpfc_update_fcf_record(phba, fcf_rec, new_fcf_record, addr_mode, vlan_id, 0); + /* Reset running random FCF selection count */ + phba->fcf.eligible_fcf_cnt = 1; + } else if (new_fcf_record->fip_priority == fcf_rec->priority) { + /* Update running random FCF selection count */ + phba->fcf.eligible_fcf_cnt++; + select_new_fcf = lpfc_sli4_new_fcf_random_select(phba, + phba->fcf.eligible_fcf_cnt); + if (select_new_fcf) + /* Choose the new FCF by random selection */ + __lpfc_update_fcf_record(phba, fcf_rec, + new_fcf_record, + addr_mode, vlan_id, 0); } spin_unlock_irq(&phba->hbalock); goto read_next_fcf; @@ -1825,6 +1875,11 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) addr_mode, vlan_id, (boot_flag ? BOOT_ENABLE : 0)); phba->fcf.fcf_flag |= FCF_AVAILABLE; + /* Setup initial running random FCF selection count */ + phba->fcf.eligible_fcf_cnt = 1; + /* Seeding the random number generator for random selection */ + seed = (uint32_t)(0xFFFFFFFF & jiffies); + srandom32(seed); } spin_unlock_irq(&phba->hbalock); goto read_next_fcf; @@ -2686,11 +2741,18 @@ lpfc_mbx_cmpl_unreg_vpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) switch (mb->mbxStatus) { case 0x0011: case 0x0020: - case 0x9700: lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE, "0911 cmpl_unreg_vpi, mb status = 0x%x\n", mb->mbxStatus); break; + /* If VPI is busy, reset the HBA */ + case 0x9700: + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE, + "2798 Unreg_vpi failed vpi 0x%x, mb status = 0x%x\n", + vport->vpi, mb->mbxStatus); + if (!(phba->pport->load_flag & FC_UNLOADING)) + lpfc_workq_post_event(phba, NULL, NULL, + LPFC_EVT_RESET_HBA); } spin_lock_irq(shost->host_lock); vport->vpi_state &= ~LPFC_VPI_REGISTERED; @@ -2965,7 +3027,12 @@ lpfc_mbx_cmpl_fabric_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); if (vport->port_state == LPFC_FABRIC_CFG_LINK) { - lpfc_start_fdiscs(phba); + /* when physical port receive logo donot start + * vport discovery */ + if (!(vport->fc_flag & FC_LOGO_RCVD_DID_CHNG)) + lpfc_start_fdiscs(phba); + else + vport->fc_flag &= ~FC_LOGO_RCVD_DID_CHNG ; lpfc_do_scr_ns_plogi(phba, vport); } @@ -3177,7 +3244,6 @@ lpfc_nlp_state_cleanup(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct Scsi_Host *shost = lpfc_shost_from_vport(vport); if (new_state == NLP_STE_UNMAPPED_NODE) { - ndlp->nlp_type &= ~(NLP_FCP_TARGET | NLP_FCP_INITIATOR); ndlp->nlp_flag &= ~NLP_NODEV_REMOVE; ndlp->nlp_type |= NLP_FC_NODE; } @@ -4935,6 +5001,7 @@ lpfc_unregister_fcf_prep(struct lpfc_hba *phba) ndlp = lpfc_findnode_did(vports[i], Fabric_DID); if (ndlp) lpfc_cancel_retry_delay_tmo(vports[i], ndlp); + lpfc_cleanup_pending_mbox(vports[i]); lpfc_mbx_unreg_vpi(vports[i]); shost = lpfc_shost_from_vport(vports[i]); spin_lock_irq(shost->host_lock); diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 89ff7c09e29..e654d01dad2 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1565,95 +1565,83 @@ enum lpfc_protgrp_type { }; /* PDE Descriptors */ -#define LPFC_PDE1_DESCRIPTOR 0x81 -#define LPFC_PDE2_DESCRIPTOR 0x82 -#define LPFC_PDE3_DESCRIPTOR 0x83 - -/* BlockGuard Profiles */ -enum lpfc_bg_prof_codes { - LPFC_PROF_INVALID, - LPFC_PROF_A1 = 128, /* Full Protection */ - LPFC_PROF_A2, /* Disabled Protection Checks:A2~A4 */ - LPFC_PROF_A3, - LPFC_PROF_A4, - LPFC_PROF_B1, /* Embedded DIFs: B1~B3 */ - LPFC_PROF_B2, - LPFC_PROF_B3, - LPFC_PROF_C1, /* Separate DIFs: C1~C3 */ - LPFC_PROF_C2, - LPFC_PROF_C3, - LPFC_PROF_D1, /* Full Protection */ - LPFC_PROF_D2, /* Partial Protection & Check Disabling */ - LPFC_PROF_D3, - LPFC_PROF_E1, /* E1~E4:out - check-only, in - update apptag */ - LPFC_PROF_E2, - LPFC_PROF_E3, - LPFC_PROF_E4, - LPFC_PROF_F1, /* Full Translation - F1 Prot Descriptor */ - /* F1 Translation BDE */ - LPFC_PROF_ANT1, /* TCP checksum, DIF inline with data buffers */ - LPFC_PROF_AST1, /* TCP checksum, DIF split from data buffer */ - LPFC_PROF_ANT2, - LPFC_PROF_AST2 +#define LPFC_PDE5_DESCRIPTOR 0x85 +#define LPFC_PDE6_DESCRIPTOR 0x86 +#define LPFC_PDE7_DESCRIPTOR 0x87 + +/* BlockGuard Opcodes */ +#define BG_OP_IN_NODIF_OUT_CRC 0x0 +#define BG_OP_IN_CRC_OUT_NODIF 0x1 +#define BG_OP_IN_NODIF_OUT_CSUM 0x2 +#define BG_OP_IN_CSUM_OUT_NODIF 0x3 +#define BG_OP_IN_CRC_OUT_CRC 0x4 +#define BG_OP_IN_CSUM_OUT_CSUM 0x5 +#define BG_OP_IN_CRC_OUT_CSUM 0x6 +#define BG_OP_IN_CSUM_OUT_CRC 0x7 + +struct lpfc_pde5 { + uint32_t word0; +#define pde5_type_SHIFT 24 +#define pde5_type_MASK 0x000000ff +#define pde5_type_WORD word0 +#define pde5_rsvd0_SHIFT 0 +#define pde5_rsvd0_MASK 0x00ffffff +#define pde5_rsvd0_WORD word0 + uint32_t reftag; /* Reference Tag Value */ + uint32_t reftagtr; /* Reference Tag Translation Value */ }; -/* BlockGuard error-control defines */ -#define BG_EC_STOP_ERR 0x00 -#define BG_EC_CONT_ERR 0x01 -#define BG_EC_IGN_UNINIT_STOP_ERR 0x10 -#define BG_EC_IGN_UNINIT_CONT_ERR 0x11 - -/* PDE (Protection Descriptor Entry) word 0 bit masks and shifts */ -#define PDE_DESC_TYPE_MASK 0xff000000 -#define PDE_DESC_TYPE_SHIFT 24 -#define PDE_BG_PROFILE_MASK 0x00ff0000 -#define PDE_BG_PROFILE_SHIFT 16 -#define PDE_BLOCK_LEN_MASK 0x0000fffc -#define PDE_BLOCK_LEN_SHIFT 2 -#define PDE_ERR_CTRL_MASK 0x00000003 -#define PDE_ERR_CTRL_SHIFT 0 -/* PDE word 1 bit masks and shifts */ -#define PDE_APPTAG_MASK_MASK 0xffff0000 -#define PDE_APPTAG_MASK_SHIFT 16 -#define PDE_APPTAG_VAL_MASK 0x0000ffff -#define PDE_APPTAG_VAL_SHIFT 0 -struct lpfc_pde { - uint32_t parms; /* bitfields of descriptor, prof, len, and ec */ - uint32_t apptag; /* bitfields of app tag maskand app tag value */ - uint32_t reftag; /* reference tag occupying all 32 bits */ +struct lpfc_pde6 { + uint32_t word0; +#define pde6_type_SHIFT 24 +#define pde6_type_MASK 0x000000ff +#define pde6_type_WORD word0 +#define pde6_rsvd0_SHIFT 0 +#define pde6_rsvd0_MASK 0x00ffffff +#define pde6_rsvd0_WORD word0 + uint32_t word1; +#define pde6_rsvd1_SHIFT 26 +#define pde6_rsvd1_MASK 0x0000003f +#define pde6_rsvd1_WORD word1 +#define pde6_na_SHIFT 25 +#define pde6_na_MASK 0x00000001 +#define pde6_na_WORD word1 +#define pde6_rsvd2_SHIFT 16 +#define pde6_rsvd2_MASK 0x000001FF +#define pde6_rsvd2_WORD word1 +#define pde6_apptagtr_SHIFT 0 +#define pde6_apptagtr_MASK 0x0000ffff +#define pde6_apptagtr_WORD word1 + uint32_t word2; +#define pde6_optx_SHIFT 28 +#define pde6_optx_MASK 0x0000000f +#define pde6_optx_WORD word2 +#define pde6_oprx_SHIFT 24 +#define pde6_oprx_MASK 0x0000000f +#define pde6_oprx_WORD word2 +#define pde6_nr_SHIFT 23 +#define pde6_nr_MASK 0x00000001 +#define pde6_nr_WORD word2 +#define pde6_ce_SHIFT 22 +#define pde6_ce_MASK 0x00000001 +#define pde6_ce_WORD word2 +#define pde6_re_SHIFT 21 +#define pde6_re_MASK 0x00000001 +#define pde6_re_WORD word2 +#define pde6_ae_SHIFT 20 +#define pde6_ae_MASK 0x00000001 +#define pde6_ae_WORD word2 +#define pde6_ai_SHIFT 19 +#define pde6_ai_MASK 0x00000001 +#define pde6_ai_WORD word2 +#define pde6_bs_SHIFT 16 +#define pde6_bs_MASK 0x00000007 +#define pde6_bs_WORD word2 +#define pde6_apptagval_SHIFT 0 +#define pde6_apptagval_MASK 0x0000ffff +#define pde6_apptagval_WORD word2 }; -/* inline function to set fields in parms of PDE */ -static inline void -lpfc_pde_set_bg_parms(struct lpfc_pde *p, u8 desc, u8 prof, u16 len, u8 ec) -{ - uint32_t *wp = &p->parms; - - /* spec indicates that adapter appends two 0's to length field */ - len = len >> 2; - - *wp &= 0; - *wp |= ((desc << PDE_DESC_TYPE_SHIFT) & PDE_DESC_TYPE_MASK); - *wp |= ((prof << PDE_BG_PROFILE_SHIFT) & PDE_BG_PROFILE_MASK); - *wp |= ((len << PDE_BLOCK_LEN_SHIFT) & PDE_BLOCK_LEN_MASK); - *wp |= ((ec << PDE_ERR_CTRL_SHIFT) & PDE_ERR_CTRL_MASK); - *wp = le32_to_cpu(*wp); -} - -/* inline function to set apptag and reftag fields of PDE */ -static inline void -lpfc_pde_set_dif_parms(struct lpfc_pde *p, u16 apptagmask, u16 apptagval, - u32 reftag) -{ - uint32_t *wp = &p->apptag; - *wp &= 0; - *wp |= ((apptagmask << PDE_APPTAG_MASK_SHIFT) & PDE_APPTAG_MASK_MASK); - *wp |= ((apptagval << PDE_APPTAG_VAL_SHIFT) & PDE_APPTAG_VAL_MASK); - *wp = le32_to_cpu(*wp); - wp = &p->reftag; - *wp = le32_to_cpu(reftag); -} - /* Structure for MB Command LOAD_SM and DOWN_LOAD */ @@ -1744,6 +1732,17 @@ typedef struct { } un; } BIU_DIAG_VAR; +/* Structure for MB command READ_EVENT_LOG (0x38) */ +struct READ_EVENT_LOG_VAR { + uint32_t word1; +#define lpfc_event_log_SHIFT 29 +#define lpfc_event_log_MASK 0x00000001 +#define lpfc_event_log_WORD word1 +#define USE_MAILBOX_RESPONSE 1 + uint32_t offset; + struct ulp_bde64 rcv_bde64; +}; + /* Structure for MB Command INIT_LINK (05) */ typedef struct { @@ -2487,8 +2486,8 @@ typedef struct { #define DMP_VPORT_REGION_SIZE 0x200 #define DMP_MBOX_OFFSET_WORD 0x5 -#define DMP_REGION_23 0x17 /* fcoe param and port state region */ -#define DMP_RGN23_SIZE 0x400 +#define DMP_REGION_23 0x17 /* fcoe param and port state region */ +#define DMP_RGN23_SIZE 0x400 #define WAKE_UP_PARMS_REGION_ID 4 #define WAKE_UP_PARMS_WORD_SIZE 15 @@ -2503,9 +2502,9 @@ struct vport_rec { #define VPORT_INFO_REV 0x1 #define MAX_STATIC_VPORT_COUNT 16 struct static_vport_info { - uint32_t signature; + uint32_t signature; uint32_t rev; - struct vport_rec vport_list[MAX_STATIC_VPORT_COUNT]; + struct vport_rec vport_list[MAX_STATIC_VPORT_COUNT]; uint32_t resvd[66]; }; @@ -2934,6 +2933,12 @@ typedef struct { /* Union of all Mailbox Command types */ #define MAILBOX_CMD_WSIZE 32 #define MAILBOX_CMD_SIZE (MAILBOX_CMD_WSIZE * sizeof(uint32_t)) +/* ext_wsize times 4 bytes should not be greater than max xmit size */ +#define MAILBOX_EXT_WSIZE 512 +#define MAILBOX_EXT_SIZE (MAILBOX_EXT_WSIZE * sizeof(uint32_t)) +#define MAILBOX_HBA_EXT_OFFSET 0x100 +/* max mbox xmit size is a page size for sysfs IO operations */ +#define MAILBOX_MAX_XMIT_SIZE PAGE_SIZE typedef union { uint32_t varWords[MAILBOX_CMD_WSIZE - 1]; /* first word is type/ @@ -2972,6 +2977,9 @@ typedef union { REG_VPI_VAR varRegVpi; /* cmd = 0x96 (REG_VPI) */ UNREG_VPI_VAR varUnregVpi; /* cmd = 0x97 (UNREG_VPI) */ ASYNCEVT_ENABLE_VAR varCfgAsyncEvent; /*cmd = x33 (CONFIG_ASYNC) */ + struct READ_EVENT_LOG_VAR varRdEventLog; /* cmd = 0x38 + * (READ_EVENT_LOG) + */ struct config_msi_var varCfgMSI;/* cmd = x30 (CONFIG_MSI) */ } MAILVARIANTS; @@ -3652,7 +3660,8 @@ typedef struct _IOCB { /* IOCB structure */ /* Maximum IOCBs that will fit in SLI2 slim */ #define MAX_SLI2_IOCB 498 #define MAX_SLIM_IOCB_SIZE (SLI2_SLIM_SIZE - \ - (sizeof(MAILBOX_t) + sizeof(PCB_t))) + (sizeof(MAILBOX_t) + sizeof(PCB_t) + \ + sizeof(uint32_t) * MAILBOX_EXT_WSIZE)) /* HBQ entries are 4 words each = 4k */ #define LPFC_TOTAL_HBQ_SIZE (sizeof(struct lpfc_hbq_entry) * \ @@ -3660,6 +3669,7 @@ typedef struct _IOCB { /* IOCB structure */ struct lpfc_sli2_slim { MAILBOX_t mbx; + uint32_t mbx_ext_words[MAILBOX_EXT_WSIZE]; PCB_t pcb; IOCB_t IOCBs[MAX_SLIM_IOCB_SIZE]; }; diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 820015fbc4d..bbdcf96800f 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -41,8 +41,14 @@ * Or clear that bit field: * bf_set(example_bit_field, &t1, 0); */ +#define bf_get_le32(name, ptr) \ + ((le32_to_cpu((ptr)->name##_WORD) >> name##_SHIFT) & name##_MASK) #define bf_get(name, ptr) \ (((ptr)->name##_WORD >> name##_SHIFT) & name##_MASK) +#define bf_set_le32(name, ptr, value) \ + ((ptr)->name##_WORD = cpu_to_le32(((((value) & \ + name##_MASK) << name##_SHIFT) | (le32_to_cpu((ptr)->name##_WORD) & \ + ~(name##_MASK << name##_SHIFT))))) #define bf_set(name, ptr, value) \ ((ptr)->name##_WORD = ((((value) & name##_MASK) << name##_SHIFT) | \ ((ptr)->name##_WORD & ~(name##_MASK << name##_SHIFT)))) @@ -781,6 +787,7 @@ struct mbox_header { #define LPFC_MBOX_OPCODE_EQ_DESTROY 0x37 #define LPFC_MBOX_OPCODE_QUERY_FW_CFG 0x3A #define LPFC_MBOX_OPCODE_FUNCTION_RESET 0x3D +#define LPFC_MBOX_OPCODE_MQ_CREATE_EXT 0x5A /* FCoE Opcodes */ #define LPFC_MBOX_OPCODE_FCOE_WQ_CREATE 0x01 @@ -1102,6 +1109,39 @@ struct lpfc_mbx_mq_create { } u; }; +struct lpfc_mbx_mq_create_ext { + struct mbox_header header; + union { + struct { + uint32_t word0; +#define lpfc_mbx_mq_create_ext_num_pages_SHIFT 0 +#define lpfc_mbx_mq_create_ext_num_pages_MASK 0x0000FFFF +#define lpfc_mbx_mq_create_ext_num_pages_WORD word0 + uint32_t async_evt_bmap; +#define lpfc_mbx_mq_create_ext_async_evt_link_SHIFT LPFC_TRAILER_CODE_LINK +#define lpfc_mbx_mq_create_ext_async_evt_link_MASK 0x00000001 +#define lpfc_mbx_mq_create_ext_async_evt_link_WORD async_evt_bmap +#define lpfc_mbx_mq_create_ext_async_evt_fcfste_SHIFT LPFC_TRAILER_CODE_FCOE +#define lpfc_mbx_mq_create_ext_async_evt_fcfste_MASK 0x00000001 +#define lpfc_mbx_mq_create_ext_async_evt_fcfste_WORD async_evt_bmap +#define lpfc_mbx_mq_create_ext_async_evt_group5_SHIFT LPFC_TRAILER_CODE_GRP5 +#define lpfc_mbx_mq_create_ext_async_evt_group5_MASK 0x00000001 +#define lpfc_mbx_mq_create_ext_async_evt_group5_WORD async_evt_bmap + struct mq_context context; + struct dma_address page[LPFC_MAX_MQ_PAGE]; + } request; + struct { + uint32_t word0; +#define lpfc_mbx_mq_create_q_id_SHIFT 0 +#define lpfc_mbx_mq_create_q_id_MASK 0x0000FFFF +#define lpfc_mbx_mq_create_q_id_WORD word0 + } response; + } u; +#define LPFC_ASYNC_EVENT_LINK_STATE 0x2 +#define LPFC_ASYNC_EVENT_FCF_STATE 0x4 +#define LPFC_ASYNC_EVENT_GROUP5 0x20 +}; + struct lpfc_mbx_mq_destroy { struct mbox_header header; union { @@ -1428,8 +1468,8 @@ struct lpfc_mbx_reg_vfi { #define lpfc_reg_vfi_fcfi_WORD word2 uint32_t wwn[2]; struct ulp_bde64 bde; - uint32_t word8_rsvd; - uint32_t word9_rsvd; + uint32_t e_d_tov; + uint32_t r_a_tov; uint32_t word10; #define lpfc_reg_vfi_nport_id_SHIFT 0 #define lpfc_reg_vfi_nport_id_MASK 0x00FFFFFF @@ -1940,6 +1980,7 @@ struct lpfc_mbx_sli4_params { #define rdma_MASK 0x00000001 #define rdma_WORD word3 uint32_t sge_supp_len; +#define SLI4_PAGE_SIZE 4096 uint32_t word5; #define if_page_sz_SHIFT 0 #define if_page_sz_MASK 0x0000ffff @@ -2041,6 +2082,7 @@ struct lpfc_mqe { struct lpfc_mbx_reg_fcfi reg_fcfi; struct lpfc_mbx_unreg_fcfi unreg_fcfi; struct lpfc_mbx_mq_create mq_create; + struct lpfc_mbx_mq_create_ext mq_create_ext; struct lpfc_mbx_eq_create eq_create; struct lpfc_mbx_cq_create cq_create; struct lpfc_mbx_wq_create wq_create; @@ -2099,6 +2141,7 @@ struct lpfc_mcqe { #define LPFC_TRAILER_CODE_LINK 0x1 #define LPFC_TRAILER_CODE_FCOE 0x2 #define LPFC_TRAILER_CODE_DCBX 0x3 +#define LPFC_TRAILER_CODE_GRP5 0x5 }; struct lpfc_acqe_link { @@ -2168,6 +2211,19 @@ struct lpfc_acqe_dcbx { uint32_t trailer; }; +struct lpfc_acqe_grp5 { + uint32_t word0; +#define lpfc_acqe_grp5_pport_SHIFT 0 +#define lpfc_acqe_grp5_pport_MASK 0x000000FF +#define lpfc_acqe_grp5_pport_WORD word0 + uint32_t word1; +#define lpfc_acqe_grp5_llink_spd_SHIFT 16 +#define lpfc_acqe_grp5_llink_spd_MASK 0x0000FFFF +#define lpfc_acqe_grp5_llink_spd_WORD word1 + uint32_t event_tag; + uint32_t trailer; +}; + /* * Define the bootstrap mailbox (bmbx) region used to communicate * mailbox command between the host and port. The mailbox consists diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 774663e8e1f..cd9697edf86 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -2566,7 +2566,7 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) shost->max_cmd_len = 16; if (phba->sli_rev == LPFC_SLI_REV4) { shost->dma_boundary = - phba->sli4_hba.pc_sli4_params.sge_supp_len; + phba->sli4_hba.pc_sli4_params.sge_supp_len-1; shost->sg_tablesize = phba->cfg_sg_seg_cnt; } @@ -2600,15 +2600,6 @@ lpfc_create_port(struct lpfc_hba *phba, int instance, struct device *dev) init_timer(&vport->els_tmofunc); vport->els_tmofunc.function = lpfc_els_timeout; vport->els_tmofunc.data = (unsigned long)vport; - if (phba->pcidev->device == PCI_DEVICE_ID_HORNET) { - phba->menlo_flag |= HBA_MENLO_SUPPORT; - /* check for menlo minimum sg count */ - if (phba->cfg_sg_seg_cnt < LPFC_DEFAULT_MENLO_SG_SEG_CNT) { - phba->cfg_sg_seg_cnt = LPFC_DEFAULT_MENLO_SG_SEG_CNT; - shost->sg_tablesize = phba->cfg_sg_seg_cnt; - } - } - error = scsi_add_host_with_dma(shost, dev, &phba->pcidev->dev); if (error) goto out_put_shost; @@ -3236,12 +3227,26 @@ lpfc_sli4_perform_vport_cvl(struct lpfc_vport *vport) if (!vport) return NULL; - ndlp = lpfc_findnode_did(vport, Fabric_DID); - if (!ndlp) - return NULL; phba = vport->phba; if (!phba) return NULL; + ndlp = lpfc_findnode_did(vport, Fabric_DID); + if (!ndlp) { + /* Cannot find existing Fabric ndlp, so allocate a new one */ + ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_KERNEL); + if (!ndlp) + return 0; + lpfc_nlp_init(vport, ndlp, Fabric_DID); + /* Set the node type */ + ndlp->nlp_type |= NLP_FABRIC; + /* Put ndlp onto node list */ + lpfc_enqueue_node(vport, ndlp); + } else if (!NLP_CHK_NODE_ACT(ndlp)) { + /* re-setup ndlp without removing from node list */ + ndlp = lpfc_enable_node(vport, ndlp, NLP_STE_UNUSED_NODE); + if (!ndlp) + return 0; + } if (phba->pport->port_state <= LPFC_FLOGI) return NULL; /* If virtual link is not yet instantiated ignore CVL */ @@ -3304,11 +3309,20 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba, switch (event_type) { case LPFC_FCOE_EVENT_TYPE_NEW_FCF: case LPFC_FCOE_EVENT_TYPE_FCF_PARAM_MOD: - lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY, - "2546 New FCF found/FCF parameter modified event: " - "evt_tag:x%x, fcf_index:x%x\n", - acqe_fcoe->event_tag, acqe_fcoe->index); - + if (event_type == LPFC_FCOE_EVENT_TYPE_NEW_FCF) + lpfc_printf_log(phba, KERN_ERR, LOG_FIP | + LOG_DISCOVERY, + "2546 New FCF found event: " + "evt_tag:x%x, fcf_index:x%x\n", + acqe_fcoe->event_tag, + acqe_fcoe->index); + else + lpfc_printf_log(phba, KERN_WARNING, LOG_FIP | + LOG_DISCOVERY, + "2788 FCF parameter modified event: " + "evt_tag:x%x, fcf_index:x%x\n", + acqe_fcoe->event_tag, + acqe_fcoe->index); spin_lock_irq(&phba->hbalock); if ((phba->fcf.fcf_flag & FCF_SCAN_DONE) || (phba->hba_flag & FCF_DISC_INPROGRESS)) { @@ -3517,6 +3531,32 @@ lpfc_sli4_async_dcbx_evt(struct lpfc_hba *phba, } /** + * lpfc_sli4_async_grp5_evt - Process the asynchronous group5 event + * @phba: pointer to lpfc hba data structure. + * @acqe_link: pointer to the async grp5 completion queue entry. + * + * This routine is to handle the SLI4 asynchronous grp5 event. A grp5 event + * is an asynchronous notified of a logical link speed change. The Port + * reports the logical link speed in units of 10Mbps. + **/ +static void +lpfc_sli4_async_grp5_evt(struct lpfc_hba *phba, + struct lpfc_acqe_grp5 *acqe_grp5) +{ + uint16_t prev_ll_spd; + + phba->fc_eventTag = acqe_grp5->event_tag; + phba->fcoe_eventtag = acqe_grp5->event_tag; + prev_ll_spd = phba->sli4_hba.link_state.logical_speed; + phba->sli4_hba.link_state.logical_speed = + (bf_get(lpfc_acqe_grp5_llink_spd, acqe_grp5)); + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "2789 GRP5 Async Event: Updating logical link speed " + "from %dMbps to %dMbps\n", (prev_ll_spd * 10), + (phba->sli4_hba.link_state.logical_speed*10)); +} + +/** * lpfc_sli4_async_event_proc - Process all the pending asynchronous event * @phba: pointer to lpfc hba data structure. * @@ -3552,6 +3592,10 @@ void lpfc_sli4_async_event_proc(struct lpfc_hba *phba) lpfc_sli4_async_dcbx_evt(phba, &cq_event->cqe.acqe_dcbx); break; + case LPFC_TRAILER_CODE_GRP5: + lpfc_sli4_async_grp5_evt(phba, + &cq_event->cqe.acqe_grp5); + break; default: lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "1804 Invalid asynchrous event code: " @@ -3813,6 +3857,13 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) /* Get all the module params for configuring this host */ lpfc_get_cfgparam(phba); + if (phba->pcidev->device == PCI_DEVICE_ID_HORNET) { + phba->menlo_flag |= HBA_MENLO_SUPPORT; + /* check for menlo minimum sg count */ + if (phba->cfg_sg_seg_cnt < LPFC_DEFAULT_MENLO_SG_SEG_CNT) + phba->cfg_sg_seg_cnt = LPFC_DEFAULT_MENLO_SG_SEG_CNT; + } + /* * Since the sg_tablesize is module parameter, the sg_dma_buf_size * used to create the sg_dma_buf_pool must be dynamically calculated. @@ -4030,6 +4081,43 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) if (unlikely(rc)) goto out_free_bsmbx; + mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_KERNEL); + if (!mboxq) { + rc = -ENOMEM; + goto out_free_bsmbx; + } + + /* Get the Supported Pages. It is always available. */ + lpfc_supported_pages(mboxq); + rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); + if (unlikely(rc)) { + rc = -EIO; + mempool_free(mboxq, phba->mbox_mem_pool); + goto out_free_bsmbx; + } + + mqe = &mboxq->u.mqe; + memcpy(&pn_page[0], ((uint8_t *)&mqe->un.supp_pages.word3), + LPFC_MAX_SUPPORTED_PAGES); + for (i = 0; i < LPFC_MAX_SUPPORTED_PAGES; i++) { + switch (pn_page[i]) { + case LPFC_SLI4_PARAMETERS: + phba->sli4_hba.pc_sli4_params.supported = 1; + break; + default: + break; + } + } + + /* Read the port's SLI4 Parameters capabilities if supported. */ + if (phba->sli4_hba.pc_sli4_params.supported) + rc = lpfc_pc_sli4_params_get(phba, mboxq); + mempool_free(mboxq, phba->mbox_mem_pool); + if (rc) { + rc = -EIO; + goto out_free_bsmbx; + } /* Create all the SLI4 queues */ rc = lpfc_sli4_queue_create(phba); if (rc) @@ -4090,43 +4178,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_free_fcp_eq_hdl; } - mboxq = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, - GFP_KERNEL); - if (!mboxq) { - rc = -ENOMEM; - goto out_free_fcp_eq_hdl; - } - - /* Get the Supported Pages. It is always available. */ - lpfc_supported_pages(mboxq); - rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL); - if (unlikely(rc)) { - rc = -EIO; - mempool_free(mboxq, phba->mbox_mem_pool); - goto out_free_fcp_eq_hdl; - } - - mqe = &mboxq->u.mqe; - memcpy(&pn_page[0], ((uint8_t *)&mqe->un.supp_pages.word3), - LPFC_MAX_SUPPORTED_PAGES); - for (i = 0; i < LPFC_MAX_SUPPORTED_PAGES; i++) { - switch (pn_page[i]) { - case LPFC_SLI4_PARAMETERS: - phba->sli4_hba.pc_sli4_params.supported = 1; - break; - default: - break; - } - } - - /* Read the port's SLI4 Parameters capabilities if supported. */ - if (phba->sli4_hba.pc_sli4_params.supported) - rc = lpfc_pc_sli4_params_get(phba, mboxq); - mempool_free(mboxq, phba->mbox_mem_pool); - if (rc) { - rc = -EIO; - goto out_free_fcp_eq_hdl; - } return rc; out_free_fcp_eq_hdl: @@ -5050,6 +5101,8 @@ lpfc_sli_pci_mem_setup(struct lpfc_hba *phba) memset(phba->slim2p.virt, 0, SLI2_SLIM_SIZE); phba->mbox = phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, mbx); + phba->mbox_ext = (phba->slim2p.virt + + offsetof(struct lpfc_sli2_slim, mbx_ext_words)); phba->pcb = (phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, pcb)); phba->IOCBs = (phba->slim2p.virt + offsetof(struct lpfc_sli2_slim, IOCBs)); @@ -7753,21 +7806,23 @@ lpfc_pci_resume_one_s3(struct pci_dev *pdev) * @phba: pointer to lpfc hba data structure. * * This routine is called to prepare the SLI3 device for PCI slot recover. It - * aborts and stops all the on-going I/Os on the pci device. + * aborts all the outstanding SCSI I/Os to the pci device. **/ static void lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba) { + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring; + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2723 PCI channel I/O abort preparing for recovery\n"); - /* Prepare for bringing HBA offline */ - lpfc_offline_prep(phba); - /* Clear sli active flag to prevent sysfs access to HBA */ - spin_lock_irq(&phba->hbalock); - phba->sli.sli_flag &= ~LPFC_SLI_ACTIVE; - spin_unlock_irq(&phba->hbalock); - /* Stop and flush all I/Os and bring HBA offline */ - lpfc_offline(phba); + + /* + * There may be errored I/Os through HBA, abort all I/Os on txcmplq + * and let the SCSI mid-layer to retry them to recover. + */ + pring = &psli->ring[psli->fcp_ring]; + lpfc_sli_abort_iocb_ring(phba, pring); } /** @@ -7781,21 +7836,20 @@ lpfc_sli_prep_dev_for_recover(struct lpfc_hba *phba) static void lpfc_sli_prep_dev_for_reset(struct lpfc_hba *phba) { - struct lpfc_sli *psli = &phba->sli; - struct lpfc_sli_ring *pring; - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2710 PCI channel disable preparing for reset\n"); + + /* Block all SCSI devices' I/Os on the host */ + lpfc_scsi_dev_block(phba); + + /* stop all timers */ + lpfc_stop_hba_timers(phba); + /* Disable interrupt and pci device */ lpfc_sli_disable_intr(phba); pci_disable_device(phba->pcidev); - /* - * There may be I/Os dropped by the firmware. - * Error iocb (I/O) on txcmplq and let the SCSI layer - * retry it after re-establishing link. - */ - pring = &psli->ring[psli->fcp_ring]; - lpfc_sli_abort_iocb_ring(phba, pring); + /* Flush all driver's outstanding SCSI I/Os as we are to reset */ + lpfc_sli_flush_fcp_rings(phba); } /** @@ -7811,6 +7865,12 @@ lpfc_prep_dev_for_perm_failure(struct lpfc_hba *phba) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2711 PCI channel permanent disable for failure\n"); + /* Block all SCSI devices' I/Os on the host */ + lpfc_scsi_dev_block(phba); + + /* stop all timers */ + lpfc_stop_hba_timers(phba); + /* Clean up all driver's outstanding SCSI I/Os */ lpfc_sli_flush_fcp_rings(phba); } @@ -7839,9 +7899,6 @@ lpfc_io_error_detected_s3(struct pci_dev *pdev, pci_channel_state_t state) struct Scsi_Host *shost = pci_get_drvdata(pdev); struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; - /* Block all SCSI devices' I/Os on the host */ - lpfc_scsi_dev_block(phba); - switch (state) { case pci_channel_io_normal: /* Non-fatal error, prepare for recovery */ @@ -7948,7 +8005,7 @@ lpfc_io_resume_s3(struct pci_dev *pdev) struct Scsi_Host *shost = pci_get_drvdata(pdev); struct lpfc_hba *phba = ((struct lpfc_vport *)shost->hostdata)->phba; - /* Bring the device online */ + /* Bring device online, it will be no-op for non-fatal error resume */ lpfc_online(phba); /* Clean up Advanced Error Reporting (AER) if needed */ diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index 72e6adb0643..e84dc33ca20 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -1216,7 +1216,7 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) phba->pcb->feature = FEATURE_INITIAL_SLI2; /* Setup Mailbox pointers */ - phba->pcb->mailBoxSize = sizeof(MAILBOX_t); + phba->pcb->mailBoxSize = sizeof(MAILBOX_t) + MAILBOX_EXT_SIZE; offset = (uint8_t *)phba->mbox - (uint8_t *)phba->slim2p.virt; pdma_addr = phba->slim2p.phys + offset; phba->pcb->mbAddrHigh = putPaddrHigh(pdma_addr); @@ -1272,28 +1272,41 @@ lpfc_config_port(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) * */ - if (phba->sli_rev == 3) { - phba->host_gp = &mb_slim->us.s3.host[0]; - phba->hbq_put = &mb_slim->us.s3.hbq_put[0]; - } else { - phba->host_gp = &mb_slim->us.s2.host[0]; + if (phba->cfg_hostmem_hgp && phba->sli_rev != 3) { + phba->host_gp = &phba->mbox->us.s2.host[0]; phba->hbq_put = NULL; - } + offset = (uint8_t *)&phba->mbox->us.s2.host - + (uint8_t *)phba->slim2p.virt; + pdma_addr = phba->slim2p.phys + offset; + phba->pcb->hgpAddrHigh = putPaddrHigh(pdma_addr); + phba->pcb->hgpAddrLow = putPaddrLow(pdma_addr); + } else { + /* Always Host Group Pointer is in SLIM */ + mb->un.varCfgPort.hps = 1; - /* mask off BAR0's flag bits 0 - 3 */ - phba->pcb->hgpAddrLow = (bar_low & PCI_BASE_ADDRESS_MEM_MASK) + - (void __iomem *)phba->host_gp - - (void __iomem *)phba->MBslimaddr; - if (bar_low & PCI_BASE_ADDRESS_MEM_TYPE_64) - phba->pcb->hgpAddrHigh = bar_high; - else - phba->pcb->hgpAddrHigh = 0; - /* write HGP data to SLIM at the required longword offset */ - memset(&hgp, 0, sizeof(struct lpfc_hgp)); + if (phba->sli_rev == 3) { + phba->host_gp = &mb_slim->us.s3.host[0]; + phba->hbq_put = &mb_slim->us.s3.hbq_put[0]; + } else { + phba->host_gp = &mb_slim->us.s2.host[0]; + phba->hbq_put = NULL; + } - for (i=0; i < phba->sli.num_rings; i++) { - lpfc_memcpy_to_slim(phba->host_gp + i, &hgp, + /* mask off BAR0's flag bits 0 - 3 */ + phba->pcb->hgpAddrLow = (bar_low & PCI_BASE_ADDRESS_MEM_MASK) + + (void __iomem *)phba->host_gp - + (void __iomem *)phba->MBslimaddr; + if (bar_low & PCI_BASE_ADDRESS_MEM_TYPE_64) + phba->pcb->hgpAddrHigh = bar_high; + else + phba->pcb->hgpAddrHigh = 0; + /* write HGP data to SLIM at the required longword offset */ + memset(&hgp, 0, sizeof(struct lpfc_hgp)); + + for (i = 0; i < phba->sli.num_rings; i++) { + lpfc_memcpy_to_slim(phba->host_gp + i, &hgp, sizeof(*phba->host_gp)); + } } /* Setup Port Group offset */ @@ -1598,7 +1611,7 @@ lpfc_sli4_mbox_cmd_free(struct lpfc_hba *phba, struct lpfcMboxq *mbox) for (sgentry = 0; sgentry < sgecount; sgentry++) { lpfc_sli4_mbx_sge_get(mbox, sgentry, &sge); phyaddr = getPaddr(sge.pa_hi, sge.pa_lo); - dma_free_coherent(&phba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&phba->pcidev->dev, SLI4_PAGE_SIZE, mbox->sge_array->addr[sgentry], phyaddr); } /* Free the sge address array memory */ @@ -1656,7 +1669,7 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox, } /* Setup for the none-embedded mbox command */ - pcount = (PAGE_ALIGN(length))/PAGE_SIZE; + pcount = (PAGE_ALIGN(length))/SLI4_PAGE_SIZE; pcount = (pcount > LPFC_SLI4_MBX_SGE_MAX_PAGES) ? LPFC_SLI4_MBX_SGE_MAX_PAGES : pcount; /* Allocate record for keeping SGE virtual addresses */ @@ -1671,24 +1684,24 @@ lpfc_sli4_config(struct lpfc_hba *phba, struct lpfcMboxq *mbox, for (pagen = 0, alloc_len = 0; pagen < pcount; pagen++) { /* The DMA memory is always allocated in the length of a * page even though the last SGE might not fill up to a - * page, this is used as a priori size of PAGE_SIZE for + * page, this is used as a priori size of SLI4_PAGE_SIZE for * the later DMA memory free. */ - viraddr = dma_alloc_coherent(&phba->pcidev->dev, PAGE_SIZE, + viraddr = dma_alloc_coherent(&phba->pcidev->dev, SLI4_PAGE_SIZE, &phyaddr, GFP_KERNEL); /* In case of malloc fails, proceed with whatever we have */ if (!viraddr) break; - memset(viraddr, 0, PAGE_SIZE); + memset(viraddr, 0, SLI4_PAGE_SIZE); mbox->sge_array->addr[pagen] = viraddr; /* Keep the first page for later sub-header construction */ if (pagen == 0) cfg_shdr = (union lpfc_sli4_cfg_shdr *)viraddr; resid_len = length - alloc_len; - if (resid_len > PAGE_SIZE) { + if (resid_len > SLI4_PAGE_SIZE) { lpfc_sli4_mbx_sge_set(mbox, pagen, phyaddr, - PAGE_SIZE); - alloc_len += PAGE_SIZE; + SLI4_PAGE_SIZE); + alloc_len += SLI4_PAGE_SIZE; } else { lpfc_sli4_mbx_sge_set(mbox, pagen, phyaddr, resid_len); @@ -1886,6 +1899,8 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys) memcpy(reg_vfi->wwn, &vport->fc_portname, sizeof(struct lpfc_name)); reg_vfi->wwn[0] = cpu_to_le32(reg_vfi->wwn[0]); reg_vfi->wwn[1] = cpu_to_le32(reg_vfi->wwn[1]); + reg_vfi->e_d_tov = vport->phba->fc_edtov; + reg_vfi->r_a_tov = vport->phba->fc_ratov; reg_vfi->bde.addrHigh = putPaddrHigh(phys); reg_vfi->bde.addrLow = putPaddrLow(phys); reg_vfi->bde.tus.f.bdeSize = sizeof(vport->fc_sparam); diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index e331204a4d5..b90820a699f 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -493,6 +493,9 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, struct lpfc_iocbq *cmdiocb, uint32_t els_cmd) { struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + struct lpfc_hba *phba = vport->phba; + struct lpfc_vport **vports; + int i, active_vlink_present = 0 ; /* Put ndlp in NPR state with 1 sec timeout for plogi, ACC logo */ /* Only call LOGO ACC for first LOGO, this avoids sending unnecessary @@ -505,15 +508,44 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, lpfc_els_rsp_acc(vport, ELS_CMD_PRLO, cmdiocb, ndlp, NULL); else lpfc_els_rsp_acc(vport, ELS_CMD_ACC, cmdiocb, ndlp, NULL); - if ((ndlp->nlp_DID == Fabric_DID) && - vport->port_type == LPFC_NPIV_PORT) { + if (ndlp->nlp_DID == Fabric_DID) { + if (vport->port_state <= LPFC_FDISC) + goto out; lpfc_linkdown_port(vport); - mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); spin_lock_irq(shost->host_lock); - ndlp->nlp_flag |= NLP_DELAY_TMO; + vport->fc_flag |= FC_VPORT_LOGO_RCVD; spin_unlock_irq(shost->host_lock); + vports = lpfc_create_vport_work_array(phba); + if (vports) { + for (i = 0; i <= phba->max_vports && vports[i] != NULL; + i++) { + if ((!(vports[i]->fc_flag & + FC_VPORT_LOGO_RCVD)) && + (vports[i]->port_state > LPFC_FDISC)) { + active_vlink_present = 1; + break; + } + } + lpfc_destroy_vport_work_array(phba, vports); + } - ndlp->nlp_last_elscmd = ELS_CMD_FDISC; + if (active_vlink_present) { + /* + * If there are other active VLinks present, + * re-instantiate the Vlink using FDISC. + */ + mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ); + spin_lock_irq(shost->host_lock); + ndlp->nlp_flag |= NLP_DELAY_TMO; + spin_unlock_irq(shost->host_lock); + ndlp->nlp_last_elscmd = ELS_CMD_FDISC; + vport->port_state = LPFC_FDISC; + } else { + spin_lock_irq(shost->host_lock); + phba->pport->fc_flag &= ~FC_LOGO_RCVD_DID_CHNG; + spin_unlock_irq(shost->host_lock); + lpfc_retry_pport_discovery(phba); + } } else if ((!(ndlp->nlp_type & NLP_FABRIC) && ((ndlp->nlp_type & NLP_FCP_TARGET) || !(ndlp->nlp_type & NLP_FCP_INITIATOR))) || @@ -526,6 +558,7 @@ lpfc_rcv_logo(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, ndlp->nlp_last_elscmd = ELS_CMD_PLOGI; } +out: ndlp->nlp_prev_state = ndlp->nlp_state; lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); @@ -604,11 +637,55 @@ lpfc_disc_set_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) lpfc_unreg_rpi(vport, ndlp); return 0; } +/** + * lpfc_release_rpi - Release a RPI by issueing unreg_login mailbox cmd. + * @phba : Pointer to lpfc_hba structure. + * @vport: Pointer to lpfc_vport structure. + * @rpi : rpi to be release. + * + * This function will send a unreg_login mailbox command to the firmware + * to release a rpi. + **/ +void +lpfc_release_rpi(struct lpfc_hba *phba, + struct lpfc_vport *vport, + uint16_t rpi) +{ + LPFC_MBOXQ_t *pmb; + int rc; + + pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_KERNEL); + if (!pmb) + lpfc_printf_vlog(vport, KERN_ERR, LOG_MBOX, + "2796 mailbox memory allocation failed \n"); + else { + lpfc_unreg_login(phba, vport->vpi, rpi, pmb); + pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT); + if (rc == MBX_NOT_FINISHED) + mempool_free(pmb, phba->mbox_mem_pool); + } +} static uint32_t lpfc_disc_illegal(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, void *arg, uint32_t evt) { + struct lpfc_hba *phba; + LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg; + MAILBOX_t *mb; + uint16_t rpi; + + phba = vport->phba; + /* Release the RPI if reglogin completing */ + if (!(phba->pport->load_flag & FC_UNLOADING) && + (evt == NLP_EVT_CMPL_REG_LOGIN) && + (!pmb->u.mb.mbxStatus)) { + mb = &pmb->u.mb; + rpi = pmb->u.mb.un.varWords[0]; + lpfc_release_rpi(phba, vport, rpi); + } lpfc_printf_vlog(vport, KERN_ERR, LOG_DISCOVERY, "0271 Illegal State Transition: node x%x " "event x%x, state x%x Data: x%x x%x\n", @@ -944,6 +1021,18 @@ static uint32_t lpfc_cmpl_reglogin_plogi_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, void *arg, uint32_t evt) { + struct lpfc_hba *phba; + LPFC_MBOXQ_t *pmb = (LPFC_MBOXQ_t *) arg; + MAILBOX_t *mb = &pmb->u.mb; + uint16_t rpi; + + phba = vport->phba; + /* Release the RPI */ + if (!(phba->pport->load_flag & FC_UNLOADING) && + !mb->mbxStatus) { + rpi = pmb->u.mb.un.varWords[0]; + lpfc_release_rpi(phba, vport, rpi); + } return ndlp->nlp_state; } diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index dccdb822328..f4a3b2e79ee 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1141,37 +1141,47 @@ lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) } /* - * Given a scsi cmnd, determine the BlockGuard profile to be used - * with the cmd + * Given a scsi cmnd, determine the BlockGuard opcodes to be used with it + * @sc: The SCSI command to examine + * @txopt: (out) BlockGuard operation for transmitted data + * @rxopt: (out) BlockGuard operation for received data + * + * Returns: zero on success; non-zero if tx and/or rx op cannot be determined + * */ static int -lpfc_sc_to_sli_prof(struct lpfc_hba *phba, struct scsi_cmnd *sc) +lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc, + uint8_t *txop, uint8_t *rxop) { uint8_t guard_type = scsi_host_get_guard(sc->device->host); - uint8_t ret_prof = LPFC_PROF_INVALID; + uint8_t ret = 0; if (guard_type == SHOST_DIX_GUARD_IP) { switch (scsi_get_prot_op(sc)) { case SCSI_PROT_READ_INSERT: case SCSI_PROT_WRITE_STRIP: - ret_prof = LPFC_PROF_AST2; + *txop = BG_OP_IN_CSUM_OUT_NODIF; + *rxop = BG_OP_IN_NODIF_OUT_CSUM; break; case SCSI_PROT_READ_STRIP: case SCSI_PROT_WRITE_INSERT: - ret_prof = LPFC_PROF_A1; + *txop = BG_OP_IN_NODIF_OUT_CRC; + *rxop = BG_OP_IN_CRC_OUT_NODIF; break; case SCSI_PROT_READ_PASS: case SCSI_PROT_WRITE_PASS: - ret_prof = LPFC_PROF_AST1; + *txop = BG_OP_IN_CSUM_OUT_CRC; + *rxop = BG_OP_IN_CRC_OUT_CSUM; break; case SCSI_PROT_NORMAL: default: lpfc_printf_log(phba, KERN_ERR, LOG_BG, - "9063 BLKGRD:Bad op/guard:%d/%d combination\n", + "9063 BLKGRD: Bad op/guard:%d/%d combination\n", scsi_get_prot_op(sc), guard_type); + ret = 1; break; } @@ -1179,12 +1189,14 @@ lpfc_sc_to_sli_prof(struct lpfc_hba *phba, struct scsi_cmnd *sc) switch (scsi_get_prot_op(sc)) { case SCSI_PROT_READ_STRIP: case SCSI_PROT_WRITE_INSERT: - ret_prof = LPFC_PROF_A1; + *txop = BG_OP_IN_NODIF_OUT_CRC; + *rxop = BG_OP_IN_CRC_OUT_NODIF; break; case SCSI_PROT_READ_PASS: case SCSI_PROT_WRITE_PASS: - ret_prof = LPFC_PROF_C1; + *txop = BG_OP_IN_CRC_OUT_CRC; + *rxop = BG_OP_IN_CRC_OUT_CRC; break; case SCSI_PROT_READ_INSERT: @@ -1194,6 +1206,7 @@ lpfc_sc_to_sli_prof(struct lpfc_hba *phba, struct scsi_cmnd *sc) lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9075 BLKGRD: Bad op/guard:%d/%d combination\n", scsi_get_prot_op(sc), guard_type); + ret = 1; break; } } else { @@ -1201,7 +1214,7 @@ lpfc_sc_to_sli_prof(struct lpfc_hba *phba, struct scsi_cmnd *sc) BUG(); } - return ret_prof; + return ret; } struct scsi_dif_tuple { @@ -1266,7 +1279,9 @@ lpfc_get_cmd_dif_parms(struct scsi_cmnd *sc, uint16_t *apptagmask, * The buffer list consists of just one protection group described * below: * +-------------------------+ - * start of prot group --> | PDE_1 | + * start of prot group --> | PDE_5 | + * +-------------------------+ + * | PDE_6 | * +-------------------------+ * | Data BDE | * +-------------------------+ @@ -1284,30 +1299,49 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc, struct ulp_bde64 *bpl, int datasegcnt) { struct scatterlist *sgde = NULL; /* s/g data entry */ - struct lpfc_pde *pde1 = NULL; + struct lpfc_pde5 *pde5 = NULL; + struct lpfc_pde6 *pde6 = NULL; dma_addr_t physaddr; - int i = 0, num_bde = 0; + int i = 0, num_bde = 0, status; int datadir = sc->sc_data_direction; - int prof = LPFC_PROF_INVALID; unsigned blksize; uint32_t reftag; uint16_t apptagmask, apptagval; + uint8_t txop, rxop; - pde1 = (struct lpfc_pde *) bpl; - prof = lpfc_sc_to_sli_prof(phba, sc); - - if (prof == LPFC_PROF_INVALID) + status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop); + if (status) goto out; - /* extract some info from the scsi command for PDE1*/ + /* extract some info from the scsi command for pde*/ blksize = lpfc_cmd_blksize(sc); lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag); - /* setup PDE1 with what we have */ - lpfc_pde_set_bg_parms(pde1, LPFC_PDE1_DESCRIPTOR, prof, blksize, - BG_EC_STOP_ERR); - lpfc_pde_set_dif_parms(pde1, apptagmask, apptagval, reftag); + /* setup PDE5 with what we have */ + pde5 = (struct lpfc_pde5 *) bpl; + memset(pde5, 0, sizeof(struct lpfc_pde5)); + bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR); + pde5->reftag = reftag; + /* advance bpl and increment bde count */ + num_bde++; + bpl++; + pde6 = (struct lpfc_pde6 *) bpl; + + /* setup PDE6 with the rest of the info */ + memset(pde6, 0, sizeof(struct lpfc_pde6)); + bf_set(pde6_type, pde6, LPFC_PDE6_DESCRIPTOR); + bf_set(pde6_optx, pde6, txop); + bf_set(pde6_oprx, pde6, rxop); + if (datadir == DMA_FROM_DEVICE) { + bf_set(pde6_ce, pde6, 1); + bf_set(pde6_re, pde6, 1); + bf_set(pde6_ae, pde6, 1); + } + bf_set(pde6_ai, pde6, 1); + bf_set(pde6_apptagval, pde6, apptagval); + + /* advance bpl and increment bde count */ num_bde++; bpl++; @@ -1342,15 +1376,17 @@ out: * The buffer list for this type consists of one or more of the * protection groups described below: * +-------------------------+ - * start of first prot group --> | PDE_1 | + * start of first prot group --> | PDE_5 | + * +-------------------------+ + * | PDE_6 | * +-------------------------+ - * | PDE_3 (Prot BDE) | + * | PDE_7 (Prot BDE) | * +-------------------------+ * | Data BDE | * +-------------------------+ * |more Data BDE's ... (opt)| * +-------------------------+ - * start of new prot group --> | PDE_1 | + * start of new prot group --> | PDE_5 | * +-------------------------+ * | ... | * +-------------------------+ @@ -1369,19 +1405,21 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, { struct scatterlist *sgde = NULL; /* s/g data entry */ struct scatterlist *sgpe = NULL; /* s/g prot entry */ - struct lpfc_pde *pde1 = NULL; + struct lpfc_pde5 *pde5 = NULL; + struct lpfc_pde6 *pde6 = NULL; struct ulp_bde64 *prot_bde = NULL; dma_addr_t dataphysaddr, protphysaddr; unsigned short curr_data = 0, curr_prot = 0; unsigned int split_offset, protgroup_len; unsigned int protgrp_blks, protgrp_bytes; unsigned int remainder, subtotal; - int prof = LPFC_PROF_INVALID; + int status; int datadir = sc->sc_data_direction; unsigned char pgdone = 0, alldone = 0; unsigned blksize; uint32_t reftag; uint16_t apptagmask, apptagval; + uint8_t txop, rxop; int num_bde = 0; sgpe = scsi_prot_sglist(sc); @@ -1394,31 +1432,47 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, return 0; } - prof = lpfc_sc_to_sli_prof(phba, sc); - if (prof == LPFC_PROF_INVALID) + status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop); + if (status) goto out; - /* extract some info from the scsi command for PDE1*/ + /* extract some info from the scsi command */ blksize = lpfc_cmd_blksize(sc); lpfc_get_cmd_dif_parms(sc, &apptagmask, &apptagval, &reftag); split_offset = 0; do { - /* setup the first PDE_1 */ - pde1 = (struct lpfc_pde *) bpl; - - lpfc_pde_set_bg_parms(pde1, LPFC_PDE1_DESCRIPTOR, prof, blksize, - BG_EC_STOP_ERR); - lpfc_pde_set_dif_parms(pde1, apptagmask, apptagval, reftag); + /* setup PDE5 with what we have */ + pde5 = (struct lpfc_pde5 *) bpl; + memset(pde5, 0, sizeof(struct lpfc_pde5)); + bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR); + pde5->reftag = reftag; + /* advance bpl and increment bde count */ + num_bde++; + bpl++; + pde6 = (struct lpfc_pde6 *) bpl; + + /* setup PDE6 with the rest of the info */ + memset(pde6, 0, sizeof(struct lpfc_pde6)); + bf_set(pde6_type, pde6, LPFC_PDE6_DESCRIPTOR); + bf_set(pde6_optx, pde6, txop); + bf_set(pde6_oprx, pde6, rxop); + bf_set(pde6_ce, pde6, 1); + bf_set(pde6_re, pde6, 1); + bf_set(pde6_ae, pde6, 1); + bf_set(pde6_ai, pde6, 1); + bf_set(pde6_apptagval, pde6, apptagval); + + /* advance bpl and increment bde count */ num_bde++; bpl++; /* setup the first BDE that points to protection buffer */ prot_bde = (struct ulp_bde64 *) bpl; protphysaddr = sg_dma_address(sgpe); - prot_bde->addrLow = le32_to_cpu(putPaddrLow(protphysaddr)); - prot_bde->addrHigh = le32_to_cpu(putPaddrHigh(protphysaddr)); + prot_bde->addrHigh = le32_to_cpu(putPaddrLow(protphysaddr)); + prot_bde->addrLow = le32_to_cpu(putPaddrHigh(protphysaddr)); protgroup_len = sg_dma_len(sgpe); @@ -1429,10 +1483,7 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, protgrp_bytes = protgrp_blks * blksize; prot_bde->tus.f.bdeSize = protgroup_len; - if (datadir == DMA_TO_DEVICE) - prot_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64; - else - prot_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; + prot_bde->tus.f.bdeFlags = LPFC_PDE7_DESCRIPTOR; prot_bde->tus.w = le32_to_cpu(bpl->tus.w); curr_prot++; @@ -1484,6 +1535,7 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, /* Move to the next s/g segment if possible */ sgde = sg_next(sgde); + } /* are we done ? */ @@ -1506,7 +1558,6 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, out: - return num_bde; } /* @@ -1828,8 +1879,8 @@ out: * field of @lpfc_cmd for device with SLI-4 interface spec. * * Return codes: - * 1 - Error - * 0 - Success + * 1 - Error + * 0 - Success **/ static int lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) @@ -1937,8 +1988,8 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) * lpfc_hba struct. * * Return codes: - * 1 - Error - * 0 - Success + * 1 - Error + * 0 - Success **/ static inline int lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 049fb9a17b3..7a61455140b 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -212,7 +212,7 @@ lpfc_sli4_eq_get(struct lpfc_queue *q) struct lpfc_eqe *eqe = q->qe[q->hba_index].eqe; /* If the next EQE is not valid then we are done */ - if (!bf_get(lpfc_eqe_valid, eqe)) + if (!bf_get_le32(lpfc_eqe_valid, eqe)) return NULL; /* If the host has not yet processed the next entry then we are done */ if (((q->hba_index + 1) % q->entry_count) == q->host_index) @@ -247,7 +247,7 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm) /* while there are valid entries */ while (q->hba_index != q->host_index) { temp_eqe = q->qe[q->host_index].eqe; - bf_set(lpfc_eqe_valid, temp_eqe, 0); + bf_set_le32(lpfc_eqe_valid, temp_eqe, 0); released++; q->host_index = ((q->host_index + 1) % q->entry_count); } @@ -285,7 +285,7 @@ lpfc_sli4_cq_get(struct lpfc_queue *q) struct lpfc_cqe *cqe; /* If the next CQE is not valid then we are done */ - if (!bf_get(lpfc_cqe_valid, q->qe[q->hba_index].cqe)) + if (!bf_get_le32(lpfc_cqe_valid, q->qe[q->hba_index].cqe)) return NULL; /* If the host has not yet processed the next entry then we are done */ if (((q->hba_index + 1) % q->entry_count) == q->host_index) @@ -321,7 +321,7 @@ lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm) /* while there are valid entries */ while (q->hba_index != q->host_index) { temp_qe = q->qe[q->host_index].cqe; - bf_set(lpfc_cqe_valid, temp_qe, 0); + bf_set_le32(lpfc_cqe_valid, temp_qe, 0); released++; q->host_index = ((q->host_index + 1) % q->entry_count); } @@ -1659,6 +1659,8 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand) case MBX_INIT_VPI: case MBX_INIT_VFI: case MBX_RESUME_RPI: + case MBX_READ_EVENT_LOG_STATUS: + case MBX_READ_EVENT_LOG: ret = mbxCommand; break; default: @@ -4296,7 +4298,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) "2570 Failed to read FCoE parameters\n"); /* Issue READ_REV to collect vpd and FW information. */ - vpd_size = PAGE_SIZE; + vpd_size = SLI4_PAGE_SIZE; vpd = kzalloc(vpd_size, GFP_KERNEL); if (!vpd) { rc = -ENOMEM; @@ -4891,9 +4893,34 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, mb->mbxOwner = OWN_CHIP; if (psli->sli_flag & LPFC_SLI_ACTIVE) { - /* First copy command data to host SLIM area */ + /* Populate mbox extension offset word. */ + if (pmbox->in_ext_byte_len || pmbox->out_ext_byte_len) { + *(((uint32_t *)mb) + pmbox->mbox_offset_word) + = (uint8_t *)phba->mbox_ext + - (uint8_t *)phba->mbox; + } + + /* Copy the mailbox extension data */ + if (pmbox->in_ext_byte_len && pmbox->context2) { + lpfc_sli_pcimem_bcopy(pmbox->context2, + (uint8_t *)phba->mbox_ext, + pmbox->in_ext_byte_len); + } + /* Copy command data to host SLIM area */ lpfc_sli_pcimem_bcopy(mb, phba->mbox, MAILBOX_CMD_SIZE); } else { + /* Populate mbox extension offset word. */ + if (pmbox->in_ext_byte_len || pmbox->out_ext_byte_len) + *(((uint32_t *)mb) + pmbox->mbox_offset_word) + = MAILBOX_HBA_EXT_OFFSET; + + /* Copy the mailbox extension data */ + if (pmbox->in_ext_byte_len && pmbox->context2) { + lpfc_memcpy_to_slim(phba->MBslimaddr + + MAILBOX_HBA_EXT_OFFSET, + pmbox->context2, pmbox->in_ext_byte_len); + + } if (mb->mbxCommand == MBX_CONFIG_PORT) { /* copy command data into host mbox for cmpl */ lpfc_sli_pcimem_bcopy(mb, phba->mbox, MAILBOX_CMD_SIZE); @@ -5003,15 +5030,22 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, if (psli->sli_flag & LPFC_SLI_ACTIVE) { /* copy results back to user */ lpfc_sli_pcimem_bcopy(phba->mbox, mb, MAILBOX_CMD_SIZE); + /* Copy the mailbox extension data */ + if (pmbox->out_ext_byte_len && pmbox->context2) { + lpfc_sli_pcimem_bcopy(phba->mbox_ext, + pmbox->context2, + pmbox->out_ext_byte_len); + } } else { /* First copy command data */ lpfc_memcpy_from_slim(mb, phba->MBslimaddr, MAILBOX_CMD_SIZE); - if ((mb->mbxCommand == MBX_DUMP_MEMORY) && - pmbox->context2) { - lpfc_memcpy_from_slim((void *)pmbox->context2, - phba->MBslimaddr + DMP_RSP_OFFSET, - mb->un.varDmp.word_cnt); + /* Copy the mailbox extension data */ + if (pmbox->out_ext_byte_len && pmbox->context2) { + lpfc_memcpy_from_slim(pmbox->context2, + phba->MBslimaddr + + MAILBOX_HBA_EXT_OFFSET, + pmbox->out_ext_byte_len); } } @@ -7104,13 +7138,11 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, */ list_del_init(&abort_iocb->list); pring->txcmplq_cnt--; - spin_unlock_irq(&phba->hbalock); /* Firmware could still be in progress of DMAing * payload, so don't free data buffer till after * a hbeat. */ - spin_lock_irq(&phba->hbalock); abort_iocb->iocb_flag |= LPFC_DELAY_MEM_FREE; abort_iocb->iocb_flag &= ~LPFC_DRIVER_ABORTED; spin_unlock_irq(&phba->hbalock); @@ -7118,7 +7150,8 @@ lpfc_sli_abort_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, abort_iocb->iocb.ulpStatus = IOSTAT_LOCAL_REJECT; abort_iocb->iocb.un.ulpWord[4] = IOERR_ABORT_REQUESTED; (abort_iocb->iocb_cmpl)(phba, abort_iocb, abort_iocb); - } + } else + spin_unlock_irq(&phba->hbalock); } lpfc_sli_release_iocbq(phba, cmdiocb); @@ -8133,6 +8166,12 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id) if (pmb->mbox_cmpl) { lpfc_sli_pcimem_bcopy(mbox, pmbox, MAILBOX_CMD_SIZE); + if (pmb->out_ext_byte_len && + pmb->context2) + lpfc_sli_pcimem_bcopy( + phba->mbox_ext, + pmb->context2, + pmb->out_ext_byte_len); } if (pmb->mbox_flag & LPFC_MBX_IMED_UNREG) { pmb->mbox_flag &= ~LPFC_MBX_IMED_UNREG; @@ -8983,17 +9022,17 @@ lpfc_sli4_sp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe) int ecount = 0; uint16_t cqid; - if (bf_get(lpfc_eqe_major_code, eqe) != 0) { + if (bf_get_le32(lpfc_eqe_major_code, eqe) != 0) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0359 Not a valid slow-path completion " "event: majorcode=x%x, minorcode=x%x\n", - bf_get(lpfc_eqe_major_code, eqe), - bf_get(lpfc_eqe_minor_code, eqe)); + bf_get_le32(lpfc_eqe_major_code, eqe), + bf_get_le32(lpfc_eqe_minor_code, eqe)); return; } /* Get the reference to the corresponding CQ */ - cqid = bf_get(lpfc_eqe_resource_id, eqe); + cqid = bf_get_le32(lpfc_eqe_resource_id, eqe); /* Search for completion queue pointer matching this cqid */ speq = phba->sli4_hba.sp_eq; @@ -9221,12 +9260,12 @@ lpfc_sli4_fp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, uint16_t cqid; int ecount = 0; - if (unlikely(bf_get(lpfc_eqe_major_code, eqe) != 0)) { + if (unlikely(bf_get_le32(lpfc_eqe_major_code, eqe) != 0)) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0366 Not a valid fast-path completion " "event: majorcode=x%x, minorcode=x%x\n", - bf_get(lpfc_eqe_major_code, eqe), - bf_get(lpfc_eqe_minor_code, eqe)); + bf_get_le32(lpfc_eqe_major_code, eqe), + bf_get_le32(lpfc_eqe_minor_code, eqe)); return; } @@ -9239,7 +9278,7 @@ lpfc_sli4_fp_handle_eqe(struct lpfc_hba *phba, struct lpfc_eqe *eqe, } /* Get the reference to the corresponding CQ */ - cqid = bf_get(lpfc_eqe_resource_id, eqe); + cqid = bf_get_le32(lpfc_eqe_resource_id, eqe); if (unlikely(cqid != cq->queue_id)) { lpfc_printf_log(phba, KERN_ERR, LOG_SLI, "0368 Miss-matched fast-path completion " @@ -9506,7 +9545,7 @@ lpfc_sli4_queue_free(struct lpfc_queue *queue) while (!list_empty(&queue->page_list)) { list_remove_head(&queue->page_list, dmabuf, struct lpfc_dmabuf, list); - dma_free_coherent(&queue->phba->pcidev->dev, PAGE_SIZE, + dma_free_coherent(&queue->phba->pcidev->dev, SLI4_PAGE_SIZE, dmabuf->virt, dmabuf->phys); kfree(dmabuf); } @@ -9532,13 +9571,17 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t entry_size, struct lpfc_dmabuf *dmabuf; int x, total_qe_count; void *dma_pointer; + uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz; + if (!phba->sli4_hba.pc_sli4_params.supported) + hw_page_size = SLI4_PAGE_SIZE; queue = kzalloc(sizeof(struct lpfc_queue) + (sizeof(union sli4_qe) * entry_count), GFP_KERNEL); if (!queue) return NULL; - queue->page_count = (PAGE_ALIGN(entry_size * entry_count))/PAGE_SIZE; + queue->page_count = (ALIGN(entry_size * entry_count, + hw_page_size))/hw_page_size; INIT_LIST_HEAD(&queue->list); INIT_LIST_HEAD(&queue->page_list); INIT_LIST_HEAD(&queue->child_list); @@ -9547,19 +9590,19 @@ lpfc_sli4_queue_alloc(struct lpfc_hba *phba, uint32_t entry_size, if (!dmabuf) goto out_fail; dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev, - PAGE_SIZE, &dmabuf->phys, + hw_page_size, &dmabuf->phys, GFP_KERNEL); if (!dmabuf->virt) { kfree(dmabuf); goto out_fail; } - memset(dmabuf->virt, 0, PAGE_SIZE); + memset(dmabuf->virt, 0, hw_page_size); dmabuf->buffer_tag = x; list_add_tail(&dmabuf->list, &queue->page_list); /* initialize queue's entry array */ dma_pointer = dmabuf->virt; for (; total_qe_count < entry_count && - dma_pointer < (PAGE_SIZE + dmabuf->virt); + dma_pointer < (hw_page_size + dmabuf->virt); total_qe_count++, dma_pointer += entry_size) { queue->qe[total_qe_count].address = dma_pointer; } @@ -9604,6 +9647,10 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax) uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; uint16_t dmult; + uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz; + + if (!phba->sli4_hba.pc_sli4_params.supported) + hw_page_size = SLI4_PAGE_SIZE; mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) @@ -9653,6 +9700,7 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax) break; } list_for_each_entry(dmabuf, &eq->page_list, list) { + memset(dmabuf->virt, 0, hw_page_size); eq_create->u.request.page[dmabuf->buffer_tag].addr_lo = putPaddrLow(dmabuf->phys); eq_create->u.request.page[dmabuf->buffer_tag].addr_hi = @@ -9715,6 +9763,11 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq, int rc, length, status = 0; uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; + uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz; + + if (!phba->sli4_hba.pc_sli4_params.supported) + hw_page_size = SLI4_PAGE_SIZE; + mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) @@ -9752,6 +9805,7 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq, break; } list_for_each_entry(dmabuf, &cq->page_list, list) { + memset(dmabuf->virt, 0, hw_page_size); cq_create->u.request.page[dmabuf->buffer_tag].addr_lo = putPaddrLow(dmabuf->phys); cq_create->u.request.page[dmabuf->buffer_tag].addr_hi = @@ -9791,9 +9845,70 @@ out: } /** + * lpfc_mq_create_fb_init - Send MCC_CREATE without async events registration + * @phba: HBA structure that indicates port to create a queue on. + * @mq: The queue structure to use to create the mailbox queue. + * @mbox: An allocated pointer to type LPFC_MBOXQ_t + * @cq: The completion queue to associate with this cq. + * + * This function provides failback (fb) functionality when the + * mq_create_ext fails on older FW generations. It's purpose is identical + * to mq_create_ext otherwise. + * + * This routine cannot fail as all attributes were previously accessed and + * initialized in mq_create_ext. + **/ +static void +lpfc_mq_create_fb_init(struct lpfc_hba *phba, struct lpfc_queue *mq, + LPFC_MBOXQ_t *mbox, struct lpfc_queue *cq) +{ + struct lpfc_mbx_mq_create *mq_create; + struct lpfc_dmabuf *dmabuf; + int length; + + length = (sizeof(struct lpfc_mbx_mq_create) - + sizeof(struct lpfc_sli4_cfg_mhdr)); + lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON, + LPFC_MBOX_OPCODE_MQ_CREATE, + length, LPFC_SLI4_MBX_EMBED); + mq_create = &mbox->u.mqe.un.mq_create; + bf_set(lpfc_mbx_mq_create_num_pages, &mq_create->u.request, + mq->page_count); + bf_set(lpfc_mq_context_cq_id, &mq_create->u.request.context, + cq->queue_id); + bf_set(lpfc_mq_context_valid, &mq_create->u.request.context, 1); + switch (mq->entry_count) { + case 16: + bf_set(lpfc_mq_context_count, &mq_create->u.request.context, + LPFC_MQ_CNT_16); + break; + case 32: + bf_set(lpfc_mq_context_count, &mq_create->u.request.context, + LPFC_MQ_CNT_32); + break; + case 64: + bf_set(lpfc_mq_context_count, &mq_create->u.request.context, + LPFC_MQ_CNT_64); + break; + case 128: + bf_set(lpfc_mq_context_count, &mq_create->u.request.context, + LPFC_MQ_CNT_128); + break; + } + list_for_each_entry(dmabuf, &mq->page_list, list) { + mq_create->u.request.page[dmabuf->buffer_tag].addr_lo = + putPaddrLow(dmabuf->phys); + mq_create->u.request.page[dmabuf->buffer_tag].addr_hi = + putPaddrHigh(dmabuf->phys); + } +} + +/** * lpfc_mq_create - Create a mailbox Queue on the HBA * @phba: HBA structure that indicates port to create a queue on. * @mq: The queue structure to use to create the mailbox queue. + * @cq: The completion queue to associate with this cq. + * @subtype: The queue's subtype. * * This function creates a mailbox queue, as detailed in @mq, on a port, * described by @phba by sending a MQ_CREATE mailbox command to the HBA. @@ -9809,31 +9924,43 @@ out: * memory this function will return ENOMEM. If the queue create mailbox command * fails this function will return ENXIO. **/ -uint32_t +int32_t lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq, struct lpfc_queue *cq, uint32_t subtype) { struct lpfc_mbx_mq_create *mq_create; + struct lpfc_mbx_mq_create_ext *mq_create_ext; struct lpfc_dmabuf *dmabuf; LPFC_MBOXQ_t *mbox; int rc, length, status = 0; uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; + uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz; + + if (!phba->sli4_hba.pc_sli4_params.supported) + hw_page_size = SLI4_PAGE_SIZE; mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) return -ENOMEM; - length = (sizeof(struct lpfc_mbx_mq_create) - + length = (sizeof(struct lpfc_mbx_mq_create_ext) - sizeof(struct lpfc_sli4_cfg_mhdr)); lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_COMMON, - LPFC_MBOX_OPCODE_MQ_CREATE, + LPFC_MBOX_OPCODE_MQ_CREATE_EXT, length, LPFC_SLI4_MBX_EMBED); - mq_create = &mbox->u.mqe.un.mq_create; - bf_set(lpfc_mbx_mq_create_num_pages, &mq_create->u.request, + + mq_create_ext = &mbox->u.mqe.un.mq_create_ext; + bf_set(lpfc_mbx_mq_create_ext_num_pages, &mq_create_ext->u.request, mq->page_count); - bf_set(lpfc_mq_context_cq_id, &mq_create->u.request.context, - cq->queue_id); - bf_set(lpfc_mq_context_valid, &mq_create->u.request.context, 1); + bf_set(lpfc_mbx_mq_create_ext_async_evt_link, &mq_create_ext->u.request, + 1); + bf_set(lpfc_mbx_mq_create_ext_async_evt_fcfste, + &mq_create_ext->u.request, 1); + bf_set(lpfc_mbx_mq_create_ext_async_evt_group5, + &mq_create_ext->u.request, 1); + bf_set(lpfc_mq_context_cq_id, &mq_create_ext->u.request.context, + cq->queue_id); + bf_set(lpfc_mq_context_valid, &mq_create_ext->u.request.context, 1); switch (mq->entry_count) { default: lpfc_printf_log(phba, KERN_ERR, LOG_SLI, @@ -9843,31 +9970,47 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq, return -EINVAL; /* otherwise default to smallest count (drop through) */ case 16: - bf_set(lpfc_mq_context_count, &mq_create->u.request.context, + bf_set(lpfc_mq_context_count, &mq_create_ext->u.request.context, LPFC_MQ_CNT_16); break; case 32: - bf_set(lpfc_mq_context_count, &mq_create->u.request.context, + bf_set(lpfc_mq_context_count, &mq_create_ext->u.request.context, LPFC_MQ_CNT_32); break; case 64: - bf_set(lpfc_mq_context_count, &mq_create->u.request.context, + bf_set(lpfc_mq_context_count, &mq_create_ext->u.request.context, LPFC_MQ_CNT_64); break; case 128: - bf_set(lpfc_mq_context_count, &mq_create->u.request.context, + bf_set(lpfc_mq_context_count, &mq_create_ext->u.request.context, LPFC_MQ_CNT_128); break; } list_for_each_entry(dmabuf, &mq->page_list, list) { - mq_create->u.request.page[dmabuf->buffer_tag].addr_lo = + memset(dmabuf->virt, 0, hw_page_size); + mq_create_ext->u.request.page[dmabuf->buffer_tag].addr_lo = putPaddrLow(dmabuf->phys); - mq_create->u.request.page[dmabuf->buffer_tag].addr_hi = + mq_create_ext->u.request.page[dmabuf->buffer_tag].addr_hi = putPaddrHigh(dmabuf->phys); } rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + shdr = (union lpfc_sli4_cfg_shdr *) &mq_create_ext->header.cfg_shdr; + mq->queue_id = bf_get(lpfc_mbx_mq_create_q_id, + &mq_create_ext->u.response); + if (rc != MBX_SUCCESS) { + lpfc_printf_log(phba, KERN_INFO, LOG_INIT, + "2795 MQ_CREATE_EXT failed with " + "status x%x. Failback to MQ_CREATE.\n", + rc); + lpfc_mq_create_fb_init(phba, mq, mbox, cq); + mq_create = &mbox->u.mqe.un.mq_create; + rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); + shdr = (union lpfc_sli4_cfg_shdr *) &mq_create->header.cfg_shdr; + mq->queue_id = bf_get(lpfc_mbx_mq_create_q_id, + &mq_create->u.response); + } + /* The IOCTL status is embedded in the mailbox subheader. */ - shdr = (union lpfc_sli4_cfg_shdr *) &mq_create->header.cfg_shdr; shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response); shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response); if (shdr_status || shdr_add_status || rc) { @@ -9878,7 +10021,6 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq, status = -ENXIO; goto out; } - mq->queue_id = bf_get(lpfc_mbx_mq_create_q_id, &mq_create->u.response); if (mq->queue_id == 0xFFFF) { status = -ENXIO; goto out; @@ -9927,6 +10069,10 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, int rc, length, status = 0; uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; + uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz; + + if (!phba->sli4_hba.pc_sli4_params.supported) + hw_page_size = SLI4_PAGE_SIZE; mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!mbox) @@ -9942,6 +10088,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, bf_set(lpfc_mbx_wq_create_cq_id, &wq_create->u.request, cq->queue_id); list_for_each_entry(dmabuf, &wq->page_list, list) { + memset(dmabuf->virt, 0, hw_page_size); wq_create->u.request.page[dmabuf->buffer_tag].addr_lo = putPaddrLow(dmabuf->phys); wq_create->u.request.page[dmabuf->buffer_tag].addr_hi = @@ -10010,6 +10157,10 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq, int rc, length, status = 0; uint32_t shdr_status, shdr_add_status; union lpfc_sli4_cfg_shdr *shdr; + uint32_t hw_page_size = phba->sli4_hba.pc_sli4_params.if_page_sz; + + if (!phba->sli4_hba.pc_sli4_params.supported) + hw_page_size = SLI4_PAGE_SIZE; if (hrq->entry_count != drq->entry_count) return -EINVAL; @@ -10054,6 +10205,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq, bf_set(lpfc_rq_context_buf_size, &rq_create->u.request.context, LPFC_HDR_BUF_SIZE); list_for_each_entry(dmabuf, &hrq->page_list, list) { + memset(dmabuf->virt, 0, hw_page_size); rq_create->u.request.page[dmabuf->buffer_tag].addr_lo = putPaddrLow(dmabuf->phys); rq_create->u.request.page[dmabuf->buffer_tag].addr_hi = @@ -10626,7 +10778,7 @@ lpfc_sli4_post_sgl_list(struct lpfc_hba *phba) reqlen = els_xri_cnt * sizeof(struct sgl_page_pairs) + sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t); - if (reqlen > PAGE_SIZE) { + if (reqlen > SLI4_PAGE_SIZE) { lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, "2559 Block sgl registration required DMA " "size (%d) great than a page\n", reqlen); @@ -10732,7 +10884,7 @@ lpfc_sli4_post_scsi_sgl_block(struct lpfc_hba *phba, struct list_head *sblist, /* Calculate the requested length of the dma memory */ reqlen = cnt * sizeof(struct sgl_page_pairs) + sizeof(union lpfc_sli4_cfg_shdr) + sizeof(uint32_t); - if (reqlen > PAGE_SIZE) { + if (reqlen > SLI4_PAGE_SIZE) { lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, "0217 Block sgl registration required DMA " "size (%d) great than a page\n", reqlen); @@ -11568,8 +11720,8 @@ lpfc_sli4_handle_received_buffer(struct lpfc_hba *phba, * * This routine is invoked to post rpi header templates to the * HBA consistent with the SLI-4 interface spec. This routine - * posts a PAGE_SIZE memory region to the port to hold up to - * PAGE_SIZE modulo 64 rpi context headers. + * posts a SLI4_PAGE_SIZE memory region to the port to hold up to + * SLI4_PAGE_SIZE modulo 64 rpi context headers. * * This routine does not require any locks. It's usage is expected * to be driver load or reset recovery when the driver is @@ -11672,8 +11824,8 @@ lpfc_sli4_post_rpi_hdr(struct lpfc_hba *phba, struct lpfc_rpi_hdr *rpi_page) * * This routine is invoked to post rpi header templates to the * HBA consistent with the SLI-4 interface spec. This routine - * posts a PAGE_SIZE memory region to the port to hold up to - * PAGE_SIZE modulo 64 rpi context headers. + * posts a SLI4_PAGE_SIZE memory region to the port to hold up to + * SLI4_PAGE_SIZE modulo 64 rpi context headers. * * Returns * A nonzero rpi defined as rpi_base <= rpi < max_rpi if successful @@ -12040,9 +12192,11 @@ lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index) phba->hba_flag |= FCF_DISC_INPROGRESS; spin_unlock_irq(&phba->hbalock); /* Reset FCF round robin index bmask for new scan */ - if (fcf_index == LPFC_FCOE_FCF_GET_FIRST) + if (fcf_index == LPFC_FCOE_FCF_GET_FIRST) { memset(phba->fcf.fcf_rr_bmask, 0, sizeof(*phba->fcf.fcf_rr_bmask)); + phba->fcf.eligible_fcf_cnt = 0; + } error = 0; } fail_fcf_scan: @@ -12507,6 +12661,7 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport) struct lpfc_hba *phba = vport->phba; LPFC_MBOXQ_t *mb, *nextmb; struct lpfc_dmabuf *mp; + struct lpfc_nodelist *ndlp; spin_lock_irq(&phba->hbalock); list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) { @@ -12523,6 +12678,11 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport) __lpfc_mbuf_free(phba, mp->virt, mp->phys); kfree(mp); } + ndlp = (struct lpfc_nodelist *) mb->context2; + if (ndlp) { + lpfc_nlp_put(ndlp); + mb->context2 = NULL; + } } list_del(&mb->list); mempool_free(mb, phba->mbox_mem_pool); @@ -12532,6 +12692,15 @@ lpfc_cleanup_pending_mbox(struct lpfc_vport *vport) if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) || (mb->u.mb.mbxCommand == MBX_REG_VPI)) mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + if (mb->u.mb.mbxCommand == MBX_REG_LOGIN64) { + ndlp = (struct lpfc_nodelist *) mb->context2; + if (ndlp) { + lpfc_nlp_put(ndlp); + mb->context2 = NULL; + } + /* Unregister the RPI when mailbox complete */ + mb->mbox_flag |= LPFC_MBX_IMED_UNREG; + } } spin_unlock_irq(&phba->hbalock); } diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index b4a639c4761..e3792151ca0 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -36,6 +36,7 @@ struct lpfc_cq_event { struct lpfc_acqe_link acqe_link; struct lpfc_acqe_fcoe acqe_fcoe; struct lpfc_acqe_dcbx acqe_dcbx; + struct lpfc_acqe_grp5 acqe_grp5; struct lpfc_rcqe rcqe_cmpl; struct sli4_wcqe_xri_aborted wcqe_axri; struct lpfc_wcqe_complete wcqe_cmpl; @@ -110,6 +111,9 @@ typedef struct lpfcMboxq { void (*mbox_cmpl) (struct lpfc_hba *, struct lpfcMboxq *); uint8_t mbox_flag; + uint16_t in_ext_byte_len; + uint16_t out_ext_byte_len; + uint8_t mbox_offset_word; struct lpfc_mcqe mcqe; struct lpfc_mbx_nembed_sge_virt *sge_array; } LPFC_MBOXQ_t; diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 4a35e7b9bc5..58bb4c81b54 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -162,6 +162,7 @@ struct lpfc_fcf { #define FCF_REDISC_FOV 0x200 /* Post FCF rediscovery fast failover */ uint32_t addr_mode; uint16_t fcf_rr_init_indx; + uint32_t eligible_fcf_cnt; struct lpfc_fcf_rec current_rec; struct lpfc_fcf_rec failover_rec; struct timer_list redisc_wait; @@ -492,8 +493,8 @@ void lpfc_sli4_queue_free(struct lpfc_queue *); uint32_t lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint16_t); uint32_t lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_queue *, uint32_t, uint32_t); -uint32_t lpfc_mq_create(struct lpfc_hba *, struct lpfc_queue *, - struct lpfc_queue *, uint32_t); +int32_t lpfc_mq_create(struct lpfc_hba *, struct lpfc_queue *, + struct lpfc_queue *, uint32_t); uint32_t lpfc_wq_create(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_queue *, uint32_t); uint32_t lpfc_rq_create(struct lpfc_hba *, struct lpfc_queue *, diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 013deec5dae..5294c3a515a 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.3.10" +#define LPFC_DRIVER_VERSION "8.3.12" #define LPFC_DRIVER_NAME "lpfc" #define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp" #define LPFC_FP_DRIVER_HANDLER_NAME "lpfc:fp" diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c index ffd575c379f..ab91359bde2 100644 --- a/drivers/scsi/lpfc/lpfc_vport.c +++ b/drivers/scsi/lpfc/lpfc_vport.c @@ -763,7 +763,9 @@ lpfc_create_vport_work_array(struct lpfc_hba *phba) spin_lock_irq(&phba->hbalock); list_for_each_entry(port_iterator, &phba->port_list, listentry) { if (!scsi_host_get(lpfc_shost_from_vport(port_iterator))) { - lpfc_printf_vlog(port_iterator, KERN_WARNING, LOG_VPORT, + if (!(port_iterator->load_flag & FC_UNLOADING)) + lpfc_printf_vlog(port_iterator, KERN_ERR, + LOG_VPORT, "1801 Create vport work array FAILED: " "cannot do scsi_host_get\n"); continue; diff --git a/drivers/scsi/mpt2sas/Kconfig b/drivers/scsi/mpt2sas/Kconfig index ba8e128de23..bbb7e4bf30a 100644 --- a/drivers/scsi/mpt2sas/Kconfig +++ b/drivers/scsi/mpt2sas/Kconfig @@ -2,7 +2,7 @@ # Kernel configuration file for the MPT2SAS # # This code is based on drivers/scsi/mpt2sas/Kconfig -# Copyright (C) 2007-2009 LSI Corporation +# Copyright (C) 2007-2010 LSI Corporation # (mailto:DL-MPTFusionLinux@lsi.com) # This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpi/mpi2.h b/drivers/scsi/mpt2sas/mpi/mpi2.h index 9958d847a88..dada0a13223 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2010 LSI Corporation. * * * Name: mpi2.h diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h index cf0ac9f40c9..d4e9d6f8452 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_cnfg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2010 LSI Corporation. * * * Name: mpi2_cnfg.h diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_history.txt b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt index c4adf76b49d..bd6c92b5fae 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_history.txt +++ b/drivers/scsi/mpt2sas/mpi/mpi2_history.txt @@ -2,7 +2,7 @@ Fusion-MPT MPI 2.0 Header File Change History ============================== - Copyright (c) 2000-2009 LSI Corporation. + Copyright (c) 2000-2010 LSI Corporation. --------------------------------------- Header Set Release Version: 02.00.14 diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_init.h b/drivers/scsi/mpt2sas/mpi/mpi2_init.h index 6541945e97c..220bf65a921 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_init.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_init.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2010 LSI Corporation. * * * Name: mpi2_init.h diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h index 754938422f6..f18f114922b 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_ioc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2010 LSI Corporation. * * * Name: mpi2_ioc.h diff --git a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h index 73fcdbf9263..686b09b8121 100644 --- a/drivers/scsi/mpt2sas/mpi/mpi2_tool.h +++ b/drivers/scsi/mpt2sas/mpi/mpi2_tool.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 LSI Corporation. + * Copyright (c) 2000-2010 LSI Corporation. * * * Name: mpi2_tool.h diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index 88e6eebc315..b830d61684d 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -3,7 +3,7 @@ * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -58,6 +58,7 @@ #include <linux/sort.h> #include <linux/io.h> #include <linux/time.h> +#include <linux/aer.h> #include "mpt2sas_base.h" @@ -285,6 +286,9 @@ _base_sas_ioc_info(struct MPT2SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, request_hdr->Function == MPI2_FUNCTION_EVENT_NOTIFICATION) return; + if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) + return; + switch (ioc_status) { /**************************************************************************** @@ -517,8 +521,18 @@ _base_display_event_data(struct MPT2SAS_ADAPTER *ioc, desc = "IR Operation Status"; break; case MPI2_EVENT_SAS_DISCOVERY: - desc = "Discovery"; - break; + { + Mpi2EventDataSasDiscovery_t *event_data = + (Mpi2EventDataSasDiscovery_t *)mpi_reply->EventData; + printk(MPT2SAS_INFO_FMT "Discovery: (%s)", ioc->name, + (event_data->ReasonCode == MPI2_EVENT_SAS_DISC_RC_STARTED) ? + "start" : "stop"); + if (event_data->DiscoveryStatus) + printk("discovery_status(0x%08x)", + le32_to_cpu(event_data->DiscoveryStatus)); + printk("\n"); + return; + } case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: desc = "SAS Broadcast Primitive"; break; @@ -1243,6 +1257,9 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) goto out_fail; } + /* AER (Advanced Error Reporting) hooks */ + pci_enable_pcie_error_reporting(pdev); + pci_set_master(pdev); if (_base_config_dma_addressing(ioc, pdev) != 0) { @@ -1253,7 +1270,7 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) } for (i = 0, memap_sz = 0, pio_sz = 0 ; i < DEVICE_COUNT_RESOURCE; i++) { - if (pci_resource_flags(pdev, i) & PCI_BASE_ADDRESS_SPACE_IO) { + if (pci_resource_flags(pdev, i) & IORESOURCE_IO) { if (pio_sz) continue; pio_chip = (u64)pci_resource_start(pdev, i); @@ -1261,15 +1278,18 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) } else { if (memap_sz) continue; - ioc->chip_phys = pci_resource_start(pdev, i); - chip_phys = (u64)ioc->chip_phys; - memap_sz = pci_resource_len(pdev, i); - ioc->chip = ioremap(ioc->chip_phys, memap_sz); - if (ioc->chip == NULL) { - printk(MPT2SAS_ERR_FMT "unable to map adapter " - "memory!\n", ioc->name); - r = -EINVAL; - goto out_fail; + /* verify memory resource is valid before using */ + if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { + ioc->chip_phys = pci_resource_start(pdev, i); + chip_phys = (u64)ioc->chip_phys; + memap_sz = pci_resource_len(pdev, i); + ioc->chip = ioremap(ioc->chip_phys, memap_sz); + if (ioc->chip == NULL) { + printk(MPT2SAS_ERR_FMT "unable to map " + "adapter memory!\n", ioc->name); + r = -EINVAL; + goto out_fail; + } } } } @@ -1295,6 +1315,7 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) ioc->chip_phys = 0; ioc->pci_irq = -1; pci_release_selected_regions(ioc->pdev, ioc->bars); + pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); return r; } @@ -1898,7 +1919,10 @@ _base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc) ioc->config_page, ioc->config_page_dma); } - kfree(ioc->scsi_lookup); + if (ioc->scsi_lookup) { + free_pages((ulong)ioc->scsi_lookup, ioc->scsi_lookup_pages); + ioc->scsi_lookup = NULL; + } kfree(ioc->hpr_lookup); kfree(ioc->internal_lookup); } @@ -2110,11 +2134,13 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) ioc->name, (unsigned long long) ioc->request_dma)); total_sz += sz; - ioc->scsi_lookup = kcalloc(ioc->scsiio_depth, - sizeof(struct request_tracker), GFP_KERNEL); + sz = ioc->scsiio_depth * sizeof(struct request_tracker); + ioc->scsi_lookup_pages = get_order(sz); + ioc->scsi_lookup = (struct request_tracker *)__get_free_pages( + GFP_KERNEL, ioc->scsi_lookup_pages); if (!ioc->scsi_lookup) { - printk(MPT2SAS_ERR_FMT "scsi_lookup: kcalloc failed\n", - ioc->name); + printk(MPT2SAS_ERR_FMT "scsi_lookup: get_free_pages failed, " + "sz(%d)\n", ioc->name, (int)sz); goto out; } @@ -3006,8 +3032,8 @@ _base_send_ioc_init(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) * since epoch ~ midnight January 1, 1970. */ do_gettimeofday(¤t_time); - mpi_request.TimeStamp = (current_time.tv_sec * 1000) + - (current_time.tv_usec >> 3); + mpi_request.TimeStamp = cpu_to_le64((u64)current_time.tv_sec * 1000 + + (current_time.tv_usec / 1000)); if (ioc->logging_level & MPT_DEBUG_INIT) { u32 *mfp; @@ -3179,7 +3205,7 @@ _base_event_notification(struct MPT2SAS_ADAPTER *ioc, int sleep_flag) mpi_request->VP_ID = 0; for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) mpi_request->EventMasks[i] = - le32_to_cpu(ioc->event_masks[i]); + cpu_to_le32(ioc->event_masks[i]); mpt2sas_base_put_smid_default(ioc, smid); init_completion(&ioc->base_cmds.done); timeleft = wait_for_completion_timeout(&ioc->base_cmds.done, 30*HZ); @@ -3516,7 +3542,9 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc) __func__)); _base_mask_interrupts(ioc); + ioc->shost_recovery = 1; _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); + ioc->shost_recovery = 0; if (ioc->pci_irq) { synchronize_irq(pdev->irq); free_irq(ioc->pci_irq, ioc); @@ -3527,6 +3555,7 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc) ioc->pci_irq = -1; ioc->chip_phys = 0; pci_release_selected_regions(ioc->pdev, ioc->bars); + pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); return; } @@ -3560,8 +3589,10 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->pfacts = kcalloc(ioc->facts.NumberOfPorts, sizeof(Mpi2PortFactsReply_t), GFP_KERNEL); - if (!ioc->pfacts) + if (!ioc->pfacts) { + r = -ENOMEM; goto out_free_resources; + } for (i = 0 ; i < ioc->facts.NumberOfPorts; i++) { r = _base_get_port_facts(ioc, i, CAN_SLEEP); @@ -3607,6 +3638,15 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->ctl_cmds.status = MPT2_CMD_NOT_USED; mutex_init(&ioc->ctl_cmds.mutex); + if (!ioc->base_cmds.reply || !ioc->transport_cmds.reply || + !ioc->scsih_cmds.reply || !ioc->tm_cmds.reply || + !ioc->config_cmds.reply || !ioc->ctl_cmds.reply) { + r = -ENOMEM; + goto out_free_resources; + } + + init_completion(&ioc->shost_recovery_done); + for (i = 0; i < MPI2_EVENT_NOTIFY_EVENTMASK_WORDS; i++) ioc->event_masks[i] = -1; @@ -3639,6 +3679,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) pci_set_drvdata(ioc->pdev, NULL); kfree(ioc->tm_cmds.reply); kfree(ioc->transport_cmds.reply); + kfree(ioc->scsih_cmds.reply); kfree(ioc->config_cmds.reply); kfree(ioc->base_cmds.reply); kfree(ioc->ctl_cmds.reply); @@ -3646,6 +3687,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->ctl_cmds.reply = NULL; ioc->base_cmds.reply = NULL; ioc->tm_cmds.reply = NULL; + ioc->scsih_cmds.reply = NULL; ioc->transport_cmds.reply = NULL; ioc->config_cmds.reply = NULL; ioc->pfacts = NULL; @@ -3675,6 +3717,7 @@ mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc) kfree(ioc->base_cmds.reply); kfree(ioc->tm_cmds.reply); kfree(ioc->transport_cmds.reply); + kfree(ioc->scsih_cmds.reply); kfree(ioc->config_cmds.reply); } @@ -3811,9 +3854,8 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag, spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); ioc->shost_recovery = 0; + complete(&ioc->shost_recovery_done); spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); - if (!r) - _base_reset_handler(ioc, MPT2_IOC_RUNNING); return r; } diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index e18b0544c38..b4afe431ac1 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -3,7 +3,7 @@ * for access to MPT (Message Passing Technology) firmware. * * This code is based on drivers/scsi/mpt2sas/mpt2_base.h - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -69,11 +69,11 @@ #define MPT2SAS_DRIVER_NAME "mpt2sas" #define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>" #define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" -#define MPT2SAS_DRIVER_VERSION "04.100.01.00" -#define MPT2SAS_MAJOR_VERSION 04 +#define MPT2SAS_DRIVER_VERSION "05.100.00.02" +#define MPT2SAS_MAJOR_VERSION 05 #define MPT2SAS_MINOR_VERSION 100 -#define MPT2SAS_BUILD_VERSION 01 -#define MPT2SAS_RELEASE_VERSION 00 +#define MPT2SAS_BUILD_VERSION 00 +#define MPT2SAS_RELEASE_VERSION 02 /* * Set MPT2SAS_SG_DEPTH value based on user input. @@ -119,7 +119,6 @@ #define MPT2_IOC_PRE_RESET 1 /* prior to host reset */ #define MPT2_IOC_AFTER_RESET 2 /* just after host reset */ #define MPT2_IOC_DONE_RESET 3 /* links re-initialized */ -#define MPT2_IOC_RUNNING 4 /* shost running */ /* * logging format @@ -260,16 +259,6 @@ struct _internal_cmd { u16 smid; }; -/* - * SAS Topology Structures - */ - -#define MPTSAS_STATE_TR_SEND 0x0001 -#define MPTSAS_STATE_TR_COMPLETE 0x0002 -#define MPTSAS_STATE_CNTRL_SEND 0x0004 -#define MPTSAS_STATE_CNTRL_COMPLETE 0x0008 - -#define MPT2SAS_REQ_SAS_CNTRL 0x0010 /** * struct _sas_device - attached device information @@ -307,7 +296,6 @@ struct _sas_device { u16 slot; u8 hidden_raid_component; u8 responding; - u16 state; }; /** @@ -378,6 +366,7 @@ struct _sas_port { * @phy_id: unique phy id * @handle: device handle for this phy * @attached_handle: device handle for attached device + * @phy_belongs_to_port: port has been created for this phy */ struct _sas_phy { struct list_head port_siblings; @@ -387,6 +376,7 @@ struct _sas_phy { u8 phy_id; u16 handle; u16 attached_handle; + u8 phy_belongs_to_port; }; /** @@ -603,7 +593,6 @@ struct MPT2SAS_ADAPTER { /* fw event handler */ char firmware_event_name[20]; struct workqueue_struct *firmware_event_thread; - u8 fw_events_off; spinlock_t fw_event_lock; struct list_head fw_event_list; @@ -611,6 +600,7 @@ struct MPT2SAS_ADAPTER { int aen_event_read_flag; u8 broadcast_aen_busy; u8 shost_recovery; + struct completion shost_recovery_done; spinlock_t ioc_reset_in_progress_lock; u8 ioc_link_reset_in_progress; u8 ignore_loginfos; @@ -688,7 +678,8 @@ struct MPT2SAS_ADAPTER { dma_addr_t request_dma; u32 request_dma_sz; struct request_tracker *scsi_lookup; - spinlock_t scsi_lookup_lock; + ulong scsi_lookup_pages; + spinlock_t scsi_lookup_lock; struct list_head free_list; int pending_io_count; wait_queue_head_t reset_wq; @@ -700,7 +691,7 @@ struct MPT2SAS_ADAPTER { u16 max_sges_in_chain_message; u16 chains_needed_per_io; u16 chain_offset_value_for_main_message; - u16 chain_depth; + u32 chain_depth; /* hi-priority queue */ u16 hi_priority_smid; @@ -814,8 +805,9 @@ void mpt2sas_halt_firmware(struct MPT2SAS_ADAPTER *ioc); /* scsih shared API */ u8 mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, u32 reply); -void mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, - u8 type, u16 smid_task, ulong timeout); +int mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, + uint channel, uint id, uint lun, u8 type, u16 smid_task, + ulong timeout, struct scsi_cmnd *scmd); void mpt2sas_scsih_set_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); void mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle); struct _sas_node *mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *ioc, diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c index cf44b355bc9..e762dd3e2fc 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_config.c +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -2,7 +2,7 @@ * This module provides common API for accessing firmware configuration pages * * This code is based on drivers/scsi/mpt2sas/mpt2_base.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -1390,12 +1390,12 @@ mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle, if (ioc_status != MPI2_IOCSTATUS_SUCCESS) goto out; for (i = 0; i < config_page->NumElements; i++) { - if ((config_page->ConfigElement[i].ElementFlags & + if ((le16_to_cpu(config_page->ConfigElement[i].ElementFlags) & MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE) != MPI2_RAIDCONFIG0_EFLAGS_VOL_PHYS_DISK_ELEMENT) continue; - if (config_page->ConfigElement[i].PhysDiskDevHandle == - pd_handle) { + if (le16_to_cpu(config_page->ConfigElement[i]. + PhysDiskDevHandle) == pd_handle) { *volume_handle = le16_to_cpu(config_page-> ConfigElement[i].VolDevHandle); r = 0; diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index fa9bf83819d..d88e9756d8f 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -3,7 +3,7 @@ * controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -533,7 +533,7 @@ _ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, if (!found) { dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "handle(0x%04x), lun(%d), no active mid!!\n", ioc->name, - desc, tm_request->DevHandle, lun)); + desc, le16_to_cpu(tm_request->DevHandle), lun)); tm_reply = ioc->ctl_cmds.reply; tm_reply->DevHandle = tm_request->DevHandle; tm_reply->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; @@ -551,7 +551,8 @@ _ctl_set_task_mid(struct MPT2SAS_ADAPTER *ioc, struct mpt2_ioctl_command *karg, dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "handle(0x%04x), lun(%d), task_mid(%d)\n", ioc->name, - desc, tm_request->DevHandle, lun, tm_request->TaskMID)); + desc, le16_to_cpu(tm_request->DevHandle), lun, + le16_to_cpu(tm_request->TaskMID))); return 0; } @@ -647,9 +648,9 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { - if (!mpi_request->FunctionDependent1 || - mpi_request->FunctionDependent1 > - cpu_to_le16(ioc->facts.MaxDevHandle)) { + if (!le16_to_cpu(mpi_request->FunctionDependent1) || + le16_to_cpu(mpi_request->FunctionDependent1) > + ioc->facts.MaxDevHandle) { ret = -EINVAL; mpt2sas_base_free_smid(ioc, smid); goto out; @@ -743,8 +744,11 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, mpt2sas_base_get_sense_buffer_dma(ioc, smid); priv_sense = mpt2sas_base_get_sense_buffer(ioc, smid); memset(priv_sense, 0, SCSI_SENSE_BUFFERSIZE); - mpt2sas_base_put_smid_scsi_io(ioc, smid, - le16_to_cpu(mpi_request->FunctionDependent1)); + if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) + mpt2sas_base_put_smid_scsi_io(ioc, smid, + le16_to_cpu(mpi_request->FunctionDependent1)); + else + mpt2sas_base_put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SCSI_TASK_MGMT: @@ -752,6 +756,10 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, Mpi2SCSITaskManagementRequest_t *tm_request = (Mpi2SCSITaskManagementRequest_t *)mpi_request; + dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "TASK_MGMT: " + "handle(0x%04x), task_type(0x%02x)\n", ioc->name, + le16_to_cpu(tm_request->DevHandle), tm_request->TaskType)); + if (tm_request->TaskType == MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK || tm_request->TaskType == @@ -762,7 +770,6 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, } } - mutex_lock(&ioc->tm_cmds.mutex); mpt2sas_scsih_set_tm_flag(ioc, le16_to_cpu( tm_request->DevHandle)); mpt2sas_base_put_smid_hi_priority(ioc, smid); @@ -818,7 +825,6 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, if (mpi_request->Function == MPI2_FUNCTION_SCSI_TASK_MGMT) { Mpi2SCSITaskManagementRequest_t *tm_request = (Mpi2SCSITaskManagementRequest_t *)mpi_request; - mutex_unlock(&ioc->tm_cmds.mutex); mpt2sas_scsih_clear_tm_flag(ioc, le16_to_cpu( tm_request->DevHandle)); } else if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH || @@ -897,14 +903,13 @@ _ctl_do_mpt_command(struct MPT2SAS_ADAPTER *ioc, MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { printk(MPT2SAS_INFO_FMT "issue target reset: handle " "= (0x%04x)\n", ioc->name, - mpi_request->FunctionDependent1); + le16_to_cpu(mpi_request->FunctionDependent1)); mpt2sas_halt_firmware(ioc); - mutex_lock(&ioc->tm_cmds.mutex); mpt2sas_scsih_issue_tm(ioc, - mpi_request->FunctionDependent1, 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10); + le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, + 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10, + NULL); ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); } else mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); @@ -1373,7 +1378,8 @@ _ctl_diag_register_2(struct MPT2SAS_ADAPTER *ioc, dctlprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: diag_buffer(0x%p), " "dma(0x%llx), sz(%d)\n", ioc->name, __func__, request_data, - (unsigned long long)request_data_dma, mpi_request->BufferLength)); + (unsigned long long)request_data_dma, + le32_to_cpu(mpi_request->BufferLength))); for (i = 0; i < MPT2_PRODUCT_SPECIFIC_DWORDS; i++) mpi_request->ProductSpecific[i] = @@ -2334,8 +2340,8 @@ _ctl_version_nvdata_persistent_show(struct device *cdev, struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - return snprintf(buf, PAGE_SIZE, "%02xh\n", - le16_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); + return snprintf(buf, PAGE_SIZE, "%08xh\n", + le32_to_cpu(ioc->iounit_pg0.NvdataVersionPersistent.Word)); } static DEVICE_ATTR(version_nvdata_persistent, S_IRUGO, _ctl_version_nvdata_persistent_show, NULL); @@ -2354,8 +2360,8 @@ _ctl_version_nvdata_default_show(struct device *cdev, struct Scsi_Host *shost = class_to_shost(cdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); - return snprintf(buf, PAGE_SIZE, "%02xh\n", - le16_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); + return snprintf(buf, PAGE_SIZE, "%08xh\n", + le32_to_cpu(ioc->iounit_pg0.NvdataVersionDefault.Word)); } static DEVICE_ATTR(version_nvdata_default, S_IRUGO, _ctl_version_nvdata_default_show, NULL); diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.h b/drivers/scsi/mpt2sas/mpt2sas_ctl.h index 8a5eeb1a5c8..69916e46e04 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.h +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.h @@ -3,7 +3,7 @@ * controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_ctl.h - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_debug.h b/drivers/scsi/mpt2sas/mpt2sas_debug.h index 5308a25cb30..3dcddfeb6f4 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_debug.h +++ b/drivers/scsi/mpt2sas/mpt2sas_debug.h @@ -2,7 +2,7 @@ * Logging Support for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_debug.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index be171ed682e..c5ff26a2a51 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -2,7 +2,7 @@ * Scsi Host Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -52,6 +52,7 @@ #include <linux/delay.h> #include <linux/pci.h> #include <linux/interrupt.h> +#include <linux/aer.h> #include <linux/raid_class.h> #include <linux/slab.h> @@ -109,14 +110,16 @@ struct sense_info { }; +#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF) + /** * struct fw_event_work - firmware event struct * @list: link list framework * @work: work object (ioc->fault_reset_work_q) + * @cancel_pending_work: flag set during reset handling * @ioc: per adapter object * @VF_ID: virtual function id * @VP_ID: virtual port id - * @host_reset_handling: handling events during host reset * @ignore: flag meaning this event has been marked to ignore * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h * @event_data: reply event data payload follows @@ -125,11 +128,11 @@ struct sense_info { */ struct fw_event_work { struct list_head list; - struct work_struct work; + u8 cancel_pending_work; + struct delayed_work delayed_work; struct MPT2SAS_ADAPTER *ioc; u8 VF_ID; u8 VP_ID; - u8 host_reset_handling; u8 ignore; u16 event; void *event_data; @@ -482,27 +485,17 @@ struct _sas_device * mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) { - struct _sas_device *sas_device, *r; + struct _sas_device *sas_device; - r = NULL; - /* check the sas_device_init_list */ - list_for_each_entry(sas_device, &ioc->sas_device_init_list, - list) { - if (sas_device->sas_address != sas_address) - continue; - r = sas_device; - goto out; - } + list_for_each_entry(sas_device, &ioc->sas_device_list, list) + if (sas_device->sas_address == sas_address) + return sas_device; - /* then check the sas_device_list */ - list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (sas_device->sas_address != sas_address) - continue; - r = sas_device; - goto out; - } - out: - return r; + list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) + if (sas_device->sas_address == sas_address) + return sas_device; + + return NULL; } /** @@ -517,28 +510,17 @@ mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc, static struct _sas_device * _scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle) { - struct _sas_device *sas_device, *r; + struct _sas_device *sas_device; - r = NULL; - if (ioc->wait_for_port_enable_to_complete) { - list_for_each_entry(sas_device, &ioc->sas_device_init_list, - list) { - if (sas_device->handle != handle) - continue; - r = sas_device; - goto out; - } - } else { - list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if (sas_device->handle != handle) - continue; - r = sas_device; - goto out; - } - } + list_for_each_entry(sas_device, &ioc->sas_device_list, list) + if (sas_device->handle == handle) + return sas_device; - out: - return r; + list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) + if (sas_device->handle == handle) + return sas_device; + + return NULL; } /** @@ -555,10 +537,15 @@ _scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc, { unsigned long flags; + if (!sas_device) + return; + spin_lock_irqsave(&ioc->sas_device_lock, flags); - list_del(&sas_device->list); - memset(sas_device, 0, sizeof(struct _sas_device)); - kfree(sas_device); + if (mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_device->sas_address)) { + list_del(&sas_device->list); + kfree(sas_device); + } spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } @@ -988,7 +975,7 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, u32 chain_offset; u32 chain_length; u32 chain_flags; - u32 sges_left; + int sges_left; u32 sges_in_segment; u32 sgl_flags; u32 sgl_flags_last_element; @@ -1009,7 +996,7 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc, sg_scmd = scsi_sglist(scmd); sges_left = scsi_dma_map(scmd); - if (!sges_left) { + if (sges_left < 0) { sdev_printk(KERN_ERR, scmd->device, "pci_map_sg" " failed: request for %d bytes!\n", scsi_bufflen(scmd)); return -ENOMEM; @@ -1395,7 +1382,7 @@ _scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc, } flags = le16_to_cpu(sas_device_pg0.Flags); - device_info = le16_to_cpu(sas_device_pg0.DeviceInfo); + device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); sdev_printk(KERN_INFO, sdev, "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), " @@ -1963,65 +1950,78 @@ mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle) } } + /** * mpt2sas_scsih_issue_tm - main routine for sending tm requests * @ioc: per adapter struct * @device_handle: device handle + * @channel: the channel assigned by the OS + * @id: the id assigned by the OS * @lun: lun number * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h) * @smid_task: smid assigned to the task * @timeout: timeout in seconds - * Context: The calling function needs to acquire the tm_cmds.mutex + * Context: user * * A generic API for sending task management requests to firmware. * - * The ioc->tm_cmds.status flag should be MPT2_CMD_NOT_USED before calling - * this API. - * * The callback index is set inside `ioc->tm_cb_idx`. * - * Return nothing. + * Return SUCCESS or FAILED. */ -void -mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, - u8 type, u16 smid_task, ulong timeout) +int +mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint channel, + uint id, uint lun, u8 type, u16 smid_task, ulong timeout, + struct scsi_cmnd *scmd) { Mpi2SCSITaskManagementRequest_t *mpi_request; Mpi2SCSITaskManagementReply_t *mpi_reply; u16 smid = 0; u32 ioc_state; unsigned long timeleft; + struct scsi_cmnd *scmd_lookup; + int rc; + mutex_lock(&ioc->tm_cmds.mutex); if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_INFO_FMT "%s: tm_cmd busy!!!\n", __func__, ioc->name); - return; + rc = FAILED; + goto err_out; } - if (ioc->shost_recovery) { + if (ioc->shost_recovery || ioc->remove_host) { printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", __func__, ioc->name); - return; + rc = FAILED; + goto err_out; } ioc_state = mpt2sas_base_get_iocstate(ioc, 0); if (ioc_state & MPI2_DOORBELL_USED) { dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "unexpected doorbell " "active!\n", ioc->name)); - goto issue_host_reset; + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + rc = SUCCESS; + goto err_out; } if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) { mpt2sas_base_fault_info(ioc, ioc_state & MPI2_DOORBELL_DATA_MASK); - goto issue_host_reset; + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + rc = SUCCESS; + goto err_out; } smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx); if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); - return; + rc = FAILED; + goto err_out; } dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x)," @@ -2035,21 +2035,24 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = type; mpi_request->TaskMID = cpu_to_le16(smid_task); - mpi_request->VP_ID = 0; /* TODO */ - mpi_request->VF_ID = 0; int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); mpt2sas_scsih_set_tm_flag(ioc, handle); init_completion(&ioc->tm_cmds.done); mpt2sas_base_put_smid_hi_priority(ioc, smid); timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); - mpt2sas_scsih_clear_tm_flag(ioc, handle); if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) { printk(MPT2SAS_ERR_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, sizeof(Mpi2SCSITaskManagementRequest_t)/4); - if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) - goto issue_host_reset; + if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) { + mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + rc = SUCCESS; + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mpt2sas_scsih_clear_tm_flag(ioc, handle); + goto err_out; + } } if (ioc->tm_cmds.status & MPT2_CMD_REPLY_VALID) { @@ -2059,12 +2062,57 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, ioc->name, le16_to_cpu(mpi_reply->IOCStatus), le32_to_cpu(mpi_reply->IOCLogInfo), le32_to_cpu(mpi_reply->TerminationCount))); - if (ioc->logging_level & MPT_DEBUG_TM) + if (ioc->logging_level & MPT_DEBUG_TM) { _scsih_response_code(ioc, mpi_reply->ResponseCode); + if (mpi_reply->IOCStatus) + _debug_dump_mf(mpi_request, + sizeof(Mpi2SCSITaskManagementRequest_t)/4); + } } - return; - issue_host_reset: - mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); + + /* sanity check: + * Check to see the commands were terminated. + * This is only needed for eh callbacks, hence the scmd check. + */ + rc = FAILED; + if (scmd == NULL) + goto bypass_sanity_checks; + switch (type) { + case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK: + scmd_lookup = _scsih_scsi_lookup_get(ioc, smid_task); + if (scmd_lookup && (scmd_lookup->serial_number == + scmd->serial_number)) + rc = FAILED; + else + rc = SUCCESS; + break; + + case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: + if (_scsih_scsi_lookup_find_by_target(ioc, id, channel)) + rc = FAILED; + else + rc = SUCCESS; + break; + + case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: + if (_scsih_scsi_lookup_find_by_lun(ioc, id, lun, channel)) + rc = FAILED; + else + rc = SUCCESS; + break; + } + + bypass_sanity_checks: + + mpt2sas_scsih_clear_tm_flag(ioc, handle); + ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mutex_unlock(&ioc->tm_cmds.mutex); + + return rc; + + err_out: + mutex_unlock(&ioc->tm_cmds.mutex); + return rc; } /** @@ -2081,7 +2129,6 @@ _scsih_abort(struct scsi_cmnd *scmd) u16 smid; u16 handle; int r; - struct scsi_cmnd *scmd_lookup; printk(MPT2SAS_INFO_FMT "attempting task abort! scmd(%p)\n", ioc->name, scmd); @@ -2116,19 +2163,10 @@ _scsih_abort(struct scsi_cmnd *scmd) mpt2sas_halt_firmware(ioc); - mutex_lock(&ioc->tm_cmds.mutex); handle = sas_device_priv_data->sas_target->handle; - mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30); - - /* sanity check - see whether command actually completed */ - scmd_lookup = _scsih_scsi_lookup_get(ioc, smid); - if (scmd_lookup && (scmd_lookup->serial_number == scmd->serial_number)) - r = FAILED; - else - r = SUCCESS; - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); + r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + scmd->device->id, scmd->device->lun, + MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, scmd); out: printk(MPT2SAS_INFO_FMT "task abort: %s scmd(%p)\n", @@ -2185,22 +2223,9 @@ _scsih_dev_reset(struct scsi_cmnd *scmd) goto out; } - mutex_lock(&ioc->tm_cmds.mutex); - mpt2sas_scsih_issue_tm(ioc, handle, 0, - MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, scmd->device->lun, - 30); - - /* - * sanity check see whether all commands to this device been - * completed - */ - if (_scsih_scsi_lookup_find_by_lun(ioc, scmd->device->id, - scmd->device->lun, scmd->device->channel)) - r = FAILED; - else - r = SUCCESS; - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); + r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + scmd->device->id, scmd->device->lun, + MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, scmd); out: printk(MPT2SAS_INFO_FMT "device reset: %s scmd(%p)\n", @@ -2257,21 +2282,9 @@ _scsih_target_reset(struct scsi_cmnd *scmd) goto out; } - mutex_lock(&ioc->tm_cmds.mutex); - mpt2sas_scsih_issue_tm(ioc, handle, 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30); - - /* - * sanity check see whether all commands to this target been - * completed - */ - if (_scsih_scsi_lookup_find_by_target(ioc, scmd->device->id, - scmd->device->channel)) - r = FAILED; - else - r = SUCCESS; - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); + r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, + scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, + 30, scmd); out: printk(MPT2SAS_INFO_FMT "target reset: %s scmd(%p)\n", @@ -2325,8 +2338,9 @@ _scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event) spin_lock_irqsave(&ioc->fw_event_lock, flags); list_add_tail(&fw_event->list, &ioc->fw_event_list); - INIT_WORK(&fw_event->work, _firmware_event_work); - queue_work(ioc->firmware_event_thread, &fw_event->work); + INIT_DELAYED_WORK(&fw_event->delayed_work, _firmware_event_work); + queue_delayed_work(ioc->firmware_event_thread, + &fw_event->delayed_work, 0); spin_unlock_irqrestore(&ioc->fw_event_lock, flags); } @@ -2353,61 +2367,53 @@ _scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work spin_unlock_irqrestore(&ioc->fw_event_lock, flags); } + /** - * _scsih_fw_event_add - requeue an event + * _scsih_queue_rescan - queue a topology rescan from user context * @ioc: per adapter object - * @fw_event: object describing the event - * Context: This function will acquire ioc->fw_event_lock. * * Return nothing. */ static void -_scsih_fw_event_requeue(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work - *fw_event, unsigned long delay) +_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc) { - unsigned long flags; - if (ioc->firmware_event_thread == NULL) - return; + struct fw_event_work *fw_event; - spin_lock_irqsave(&ioc->fw_event_lock, flags); - queue_work(ioc->firmware_event_thread, &fw_event->work); - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (ioc->wait_for_port_enable_to_complete) + return; + fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); + if (!fw_event) + return; + fw_event->event = MPT2SAS_RESCAN_AFTER_HOST_RESET; + fw_event->ioc = ioc; + _scsih_fw_event_add(ioc, fw_event); } /** - * _scsih_fw_event_off - turn flag off preventing event handling + * _scsih_fw_event_cleanup_queue - cleanup event queue * @ioc: per adapter object * - * Used to prevent handling of firmware events during adapter reset - * driver unload. + * Walk the firmware event queue, either killing timers, or waiting + * for outstanding events to complete * * Return nothing. */ static void -_scsih_fw_event_off(struct MPT2SAS_ADAPTER *ioc) +_scsih_fw_event_cleanup_queue(struct MPT2SAS_ADAPTER *ioc) { - unsigned long flags; + struct fw_event_work *fw_event, *next; - spin_lock_irqsave(&ioc->fw_event_lock, flags); - ioc->fw_events_off = 1; - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - -} - -/** - * _scsih_fw_event_on - turn flag on allowing firmware event handling - * @ioc: per adapter object - * - * Returns nothing. - */ -static void -_scsih_fw_event_on(struct MPT2SAS_ADAPTER *ioc) -{ - unsigned long flags; + if (list_empty(&ioc->fw_event_list) || + !ioc->firmware_event_thread || in_interrupt()) + return; - spin_lock_irqsave(&ioc->fw_event_lock, flags); - ioc->fw_events_off = 0; - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) { + if (cancel_delayed_work(&fw_event->delayed_work)) { + _scsih_fw_event_free(ioc, fw_event); + continue; + } + fw_event->cancel_pending_work = 1; + } } /** @@ -2571,25 +2577,24 @@ static void _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) { Mpi2SCSITaskManagementRequest_t *mpi_request; - struct MPT2SAS_TARGET *sas_target_priv_data; u16 smid; struct _sas_device *sas_device; unsigned long flags; struct _tr_list *delayed_tr; - if (ioc->shost_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); + if (ioc->shost_recovery || ioc->remove_host) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " + "progress!\n", __func__, ioc->name)); return; } spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - /* skip is hidden raid component */ - if (sas_device && sas_device->hidden_raid_component) + if (sas_device && sas_device->hidden_raid_component) { + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); return; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx); if (!smid) { @@ -2598,36 +2603,16 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle) return; INIT_LIST_HEAD(&delayed_tr->list); delayed_tr->handle = handle; - delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL; - list_add_tail(&delayed_tr->list, - &ioc->delayed_tr_list); - if (sas_device && sas_device->starget) { - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, "DELAYED:tr:handle(0x%04x), " - "(open)\n", handle)); - } else { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "DELAYED:tr:handle(0x%04x), (open)\n", - ioc->name, handle)); - } - return; - } - - if (sas_device) { - sas_device->state |= MPTSAS_STATE_TR_SEND; - sas_device->state |= MPT2SAS_REQ_SAS_CNTRL; - if (sas_device->starget && sas_device->starget->hostdata) { - sas_target_priv_data = sas_device->starget->hostdata; - sas_target_priv_data->tm_busy = 1; - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, "tr:handle(0x%04x), (open)\n", - handle)); - } - } else { + list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list); dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "tr:handle(0x%04x), (open)\n", ioc->name, handle)); + "DELAYED:tr:handle(0x%04x), (open)\n", + ioc->name, handle)); + return; } + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), " + "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid, + ioc->tm_tr_cb_idx)); mpi_request = mpt2sas_base_get_msg_frame(ioc, smid); memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t)); mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; @@ -2657,35 +2642,15 @@ static u8 _scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { - unsigned long flags; - u16 handle; - struct _sas_device *sas_device; Mpi2SasIoUnitControlReply_t *mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); - handle = le16_to_cpu(mpi_reply->DevHandle); - - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (sas_device) { - sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE; - if (sas_device->starget) - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, - "sc_complete:handle(0x%04x), " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - handle, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo))); - } else { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "sc_complete:handle(0x%04x), " - "ioc_status(0x%04x), loginfo(0x%08x)\n", - ioc->name, handle, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo))); - } - + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "sc_complete:handle(0x%04x), (open) " + "smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n", + ioc->name, le16_to_cpu(mpi_reply->DevHandle), smid, + le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo))); return 1; } @@ -2709,87 +2674,63 @@ static u8 _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) { - unsigned long flags; u16 handle; - struct _sas_device *sas_device; + Mpi2SCSITaskManagementRequest_t *mpi_request_tm; Mpi2SCSITaskManagementReply_t *mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); Mpi2SasIoUnitControlRequest_t *mpi_request; u16 smid_sas_ctrl; - struct MPT2SAS_TARGET *sas_target_priv_data; struct _tr_list *delayed_tr; - u8 rc; - handle = le16_to_cpu(mpi_reply->DevHandle); - spin_lock_irqsave(&ioc->sas_device_lock, flags); - sas_device = _scsih_sas_device_find_by_handle(ioc, handle); - spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - - if (sas_device) { - sas_device->state |= MPTSAS_STATE_TR_COMPLETE; - if (sas_device->starget) { - dewtprintk(ioc, starget_printk(KERN_INFO, - sas_device->starget, "tr_complete:handle(0x%04x), " - "(%s) ioc_status(0x%04x), loginfo(0x%08x), " - "completed(%d)\n", sas_device->handle, - (sas_device->state & MPT2SAS_REQ_SAS_CNTRL) ? - "open" : "active", - le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo), - le32_to_cpu(mpi_reply->TerminationCount))); - if (sas_device->starget->hostdata) { - sas_target_priv_data = - sas_device->starget->hostdata; - sas_target_priv_data->tm_busy = 0; - } - } - } else { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT - "tr_complete:handle(0x%04x), (open) ioc_status(0x%04x), " - "loginfo(0x%08x), completed(%d)\n", ioc->name, - handle, le16_to_cpu(mpi_reply->IOCStatus), - le32_to_cpu(mpi_reply->IOCLogInfo), - le32_to_cpu(mpi_reply->TerminationCount))); + if (ioc->shost_recovery || ioc->remove_host) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " + "progress!\n", __func__, ioc->name)); + return 1; } - if (!list_empty(&ioc->delayed_tr_list)) { - delayed_tr = list_entry(ioc->delayed_tr_list.next, - struct _tr_list, list); - mpt2sas_base_free_smid(ioc, smid); - if (delayed_tr->state & MPT2SAS_REQ_SAS_CNTRL) - _scsih_tm_tr_send(ioc, delayed_tr->handle); - list_del(&delayed_tr->list); - kfree(delayed_tr); - rc = 0; /* tells base_interrupt not to free mf */ - } else - rc = 1; - - if (sas_device && !(sas_device->state & MPT2SAS_REQ_SAS_CNTRL)) - return rc; - - if (ioc->shost_recovery) { - printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", - __func__, ioc->name); - return rc; + mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); + handle = le16_to_cpu(mpi_request_tm->DevHandle); + if (handle != le16_to_cpu(mpi_reply->DevHandle)) { + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "spurious interrupt: " + "handle(0x%04x:0x%04x), smid(%d)!!!\n", ioc->name, handle, + le16_to_cpu(mpi_reply->DevHandle), smid)); + return 0; } + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT + "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " + "loginfo(0x%08x), completed(%d)\n", ioc->name, + handle, smid, le16_to_cpu(mpi_reply->IOCStatus), + le32_to_cpu(mpi_reply->IOCLogInfo), + le32_to_cpu(mpi_reply->TerminationCount))); + smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx); if (!smid_sas_ctrl) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); - return rc; + return 1; } - if (sas_device) - sas_device->state |= MPTSAS_STATE_CNTRL_SEND; - + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sc_send:handle(0x%04x), " + "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid_sas_ctrl, + ioc->tm_sas_control_cb_idx)); mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl); memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; - mpi_request->DevHandle = mpi_reply->DevHandle; + mpi_request->DevHandle = mpi_request_tm->DevHandle; mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl); - return rc; + + if (!list_empty(&ioc->delayed_tr_list)) { + delayed_tr = list_entry(ioc->delayed_tr_list.next, + struct _tr_list, list); + mpt2sas_base_free_smid(ioc, smid); + _scsih_tm_tr_send(ioc, delayed_tr->handle); + list_del(&delayed_tr->list); + kfree(delayed_tr); + return 0; /* tells base_interrupt not to free mf */ + } + return 1; } /** @@ -3021,25 +2962,32 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) scmd->scsi_done = done; sas_device_priv_data = scmd->device->hostdata; - if (!sas_device_priv_data) { + if (!sas_device_priv_data || !sas_device_priv_data->sas_target) { scmd->result = DID_NO_CONNECT << 16; scmd->scsi_done(scmd); return 0; } sas_target_priv_data = sas_device_priv_data->sas_target; - if (!sas_target_priv_data || sas_target_priv_data->handle == - MPT2SAS_INVALID_DEVICE_HANDLE || sas_target_priv_data->deleted) { + /* invalid device handle */ + if (sas_target_priv_data->handle == MPT2SAS_INVALID_DEVICE_HANDLE) { scmd->result = DID_NO_CONNECT << 16; scmd->scsi_done(scmd); return 0; } - /* see if we are busy with task managment stuff */ - if (sas_device_priv_data->block || sas_target_priv_data->tm_busy) - return SCSI_MLQUEUE_DEVICE_BUSY; - else if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) + /* host recovery or link resets sent via IOCTLs */ + if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) return SCSI_MLQUEUE_HOST_BUSY; + /* device busy with task managment */ + else if (sas_device_priv_data->block || sas_target_priv_data->tm_busy) + return SCSI_MLQUEUE_DEVICE_BUSY; + /* device has been deleted */ + else if (sas_target_priv_data->deleted) { + scmd->result = DID_NO_CONNECT << 16; + scmd->scsi_done(scmd); + return 0; + } if (scmd->sc_data_direction == DMA_FROM_DEVICE) mpi_control = MPI2_SCSIIO_CONTROL_READ; @@ -3110,8 +3058,11 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *)) } } - mpt2sas_base_put_smid_scsi_io(ioc, smid, - sas_device_priv_data->sas_target->handle); + if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) + mpt2sas_base_put_smid_scsi_io(ioc, smid, + sas_device_priv_data->sas_target->handle); + else + mpt2sas_base_put_smid_default(ioc, smid); return 0; out: @@ -3301,8 +3252,8 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, struct sense_info data; _scsih_normalize_sense(scmd->sense_buffer, &data); printk(MPT2SAS_WARN_FMT "\t[sense_key,asc,ascq]: " - "[0x%02x,0x%02x,0x%02x]\n", ioc->name, data.skey, - data.asc, data.ascq); + "[0x%02x,0x%02x,0x%02x], count(%d)\n", ioc->name, data.skey, + data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount)); } if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { @@ -3356,7 +3307,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle) mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS; mpi_request.SlotStatus = - MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT; + cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT); mpi_request.DevHandle = cpu_to_le16(handle); mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS; if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, @@ -4008,6 +3959,134 @@ _scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address) } /** + * _scsih_check_access_status - check access flags + * @ioc: per adapter object + * @sas_address: sas address + * @handle: sas device handle + * @access_flags: errors returned during discovery of the device + * + * Return 0 for success, else failure + */ +static u8 +_scsih_check_access_status(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, + u16 handle, u8 access_status) +{ + u8 rc = 1; + char *desc = NULL; + + switch (access_status) { + case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS: + case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION: + rc = 0; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED: + desc = "sata capability failed"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT: + desc = "sata affiliation conflict"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE: + desc = "route not addressable"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE: + desc = "smp error not addressable"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED: + desc = "device blocked"; + break; + case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE: + case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX: + desc = "sata initialization failed"; + break; + default: + desc = "unknown"; + break; + } + + if (!rc) + return 0; + + printk(MPT2SAS_ERR_FMT "discovery errors(%s): sas_address(0x%016llx), " + "handle(0x%04x)\n", ioc->name, desc, + (unsigned long long)sas_address, handle); + return rc; +} + +static void +_scsih_check_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ + Mpi2ConfigReply_t mpi_reply; + Mpi2SasDevicePage0_t sas_device_pg0; + struct _sas_device *sas_device; + u32 ioc_status; + unsigned long flags; + u64 sas_address; + struct scsi_target *starget; + struct MPT2SAS_TARGET *sas_target_priv_data; + u32 device_info; + + if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) + return; + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) + return; + + /* check if this is end device */ + device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); + if (!(_scsih_is_end_device(device_info))) + return; + + spin_lock_irqsave(&ioc->sas_device_lock, flags); + sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, + sas_address); + + if (!sas_device) { + printk(MPT2SAS_ERR_FMT "device is not present " + "handle(0x%04x), no sas_device!!!\n", ioc->name, handle); + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + return; + } + + if (unlikely(sas_device->handle != handle)) { + starget = sas_device->starget; + sas_target_priv_data = starget->hostdata; + starget_printk(KERN_INFO, starget, "handle changed from(0x%04x)" + " to (0x%04x)!!!\n", sas_device->handle, handle); + sas_target_priv_data->handle = handle; + sas_device->handle = handle; + } + spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + + /* check if device is present */ + if (!(le16_to_cpu(sas_device_pg0.Flags) & + MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { + printk(MPT2SAS_ERR_FMT "device is not present " + "handle(0x%04x), flags!!!\n", ioc->name, handle); + return; + } + + /* check if there were any issues with discovery */ + if (_scsih_check_access_status(ioc, sas_address, handle, + sas_device_pg0.AccessStatus)) + return; + _scsih_ublock_io_device(ioc, handle); + +} + +/** * _scsih_add_device - creating sas device object * @ioc: per adapter object * @handle: sas device handle @@ -4045,6 +4124,8 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } + sas_address = le64_to_cpu(sas_device_pg0.SASAddress); + /* check if device is present */ if (!(le16_to_cpu(sas_device_pg0.Flags) & MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { @@ -4055,15 +4136,10 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } - /* check if there were any issus with discovery */ - if (sas_device_pg0.AccessStatus == - MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED) { - printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", - ioc->name, __FILE__, __LINE__, __func__); - printk(MPT2SAS_ERR_FMT "AccessStatus = 0x%02x\n", - ioc->name, sas_device_pg0.AccessStatus); + /* check if there were any issues with discovery */ + if (_scsih_check_access_status(ioc, sas_address, handle, + sas_device_pg0.AccessStatus)) return -1; - } /* check if this is end device */ device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); @@ -4073,17 +4149,14 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) return -1; } - sas_address = le64_to_cpu(sas_device_pg0.SASAddress); spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, sas_address); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) { - _scsih_ublock_io_device(ioc, handle); + if (sas_device) return 0; - } sas_device = kzalloc(sizeof(struct _sas_device), GFP_KERNEL); @@ -4126,67 +4199,38 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) } /** - * _scsih_remove_device - removing sas device object + * _scsih_remove_pd_device - removing sas device pd object * @ioc: per adapter object - * @sas_device: the sas_device object + * @sas_device_delete: the sas_device object * + * For hidden raid components, we do driver-fw handshake from + * hotplug work threads. * Return nothing. */ static void -_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device - *sas_device) +_scsih_remove_pd_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device + sas_device) { - struct MPT2SAS_TARGET *sas_target_priv_data; Mpi2SasIoUnitControlReply_t mpi_reply; Mpi2SasIoUnitControlRequest_t mpi_request; - u16 device_handle, handle; - - if (!sas_device) - return; + u16 vol_handle, handle; - handle = sas_device->handle; + handle = sas_device.handle; dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle(0x%04x)," " sas_addr(0x%016llx)\n", ioc->name, __func__, handle, - (unsigned long long) sas_device->sas_address)); - - if (sas_device->starget && sas_device->starget->hostdata) { - sas_target_priv_data = sas_device->starget->hostdata; - sas_target_priv_data->deleted = 1; - } - - if (ioc->remove_host || ioc->shost_recovery || !handle) - goto out; + (unsigned long long) sas_device.sas_address)); - if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " - "target_reset handle(0x%04x)\n", ioc->name, - handle)); - goto skip_tr; - } - - /* Target Reset to flush out all the outstanding IO */ - device_handle = (sas_device->hidden_raid_component) ? - sas_device->volume_handle : handle; - if (device_handle) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset: " - "handle(0x%04x)\n", ioc->name, device_handle)); - mutex_lock(&ioc->tm_cmds.mutex); - mpt2sas_scsih_issue_tm(ioc, device_handle, 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10); - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; - mutex_unlock(&ioc->tm_cmds.mutex); - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset " - "done: handle(0x%04x)\n", ioc->name, device_handle)); - if (ioc->shost_recovery) - goto out; - } - skip_tr: - - if ((sas_device->state & MPTSAS_STATE_CNTRL_COMPLETE)) { - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " - "sas_cntrl handle(0x%04x)\n", ioc->name, handle)); - goto out; - } + vol_handle = sas_device.volume_handle; + if (!vol_handle) + return; + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset: " + "handle(0x%04x)\n", ioc->name, vol_handle)); + mpt2sas_scsih_issue_tm(ioc, vol_handle, 0, 0, 0, + MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30, NULL); + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset " + "done: handle(0x%04x)\n", ioc->name, vol_handle)); + if (ioc->shost_recovery) + return; /* SAS_IO_UNIT_CNTR - send REMOVE_DEVICE */ dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: handle" @@ -4194,34 +4238,68 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request.Operation = MPI2_SAS_OP_REMOVE_DEVICE; - mpi_request.DevHandle = handle; - mpi_request.VF_ID = 0; /* TODO */ - mpi_request.VP_ID = 0; + mpi_request.DevHandle = cpu_to_le16(handle); if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, - &mpi_request)) != 0) { + &mpi_request)) != 0) printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); - } dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: ioc_status" "(0x%04x), loginfo(0x%08x)\n", ioc->name, le16_to_cpu(mpi_reply.IOCStatus), le32_to_cpu(mpi_reply.IOCLogInfo))); - out: + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: handle(0x%04x)," + " sas_addr(0x%016llx)\n", ioc->name, __func__, handle, + (unsigned long long) sas_device.sas_address)); +} - _scsih_ublock_io_device(ioc, handle); +/** + * _scsih_remove_device - removing sas device object + * @ioc: per adapter object + * @sas_device_delete: the sas_device object + * + * Return nothing. + */ +static void +_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, + struct _sas_device *sas_device) +{ + struct _sas_device sas_device_backup; + struct MPT2SAS_TARGET *sas_target_priv_data; - mpt2sas_transport_port_remove(ioc, sas_device->sas_address, - sas_device->sas_address_parent); + if (!sas_device) + return; - printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" - "(0x%016llx)\n", ioc->name, handle, - (unsigned long long) sas_device->sas_address); + memcpy(&sas_device_backup, sas_device, sizeof(struct _sas_device)); _scsih_sas_device_remove(ioc, sas_device); - dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: handle" - "(0x%04x)\n", ioc->name, __func__, handle)); + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: " + "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, + sas_device_backup.handle, (unsigned long long) + sas_device_backup.sas_address)); + + if (sas_device_backup.starget && sas_device_backup.starget->hostdata) { + sas_target_priv_data = sas_device_backup.starget->hostdata; + sas_target_priv_data->deleted = 1; + } + + if (sas_device_backup.hidden_raid_component) + _scsih_remove_pd_device(ioc, sas_device_backup); + + _scsih_ublock_io_device(ioc, sas_device_backup.handle); + + mpt2sas_transport_port_remove(ioc, sas_device_backup.sas_address, + sas_device_backup.sas_address_parent); + + printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" + "(0x%016llx)\n", ioc->name, sas_device_backup.handle, + (unsigned long long) sas_device_backup.sas_address); + + dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: " + "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, + sas_device_backup.handle, (unsigned long long) + sas_device_backup.sas_address)); } #ifdef CONFIG_SCSI_MPT2SAS_LOGGING @@ -4331,7 +4409,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, _scsih_sas_topology_change_event_debug(ioc, event_data); #endif - if (ioc->shost_recovery) + if (ioc->shost_recovery || ioc->remove_host) return; if (!ioc->sas_hba.num_phys) @@ -4370,7 +4448,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, "expander event\n", ioc->name)); return; } - if (ioc->shost_recovery) + if (ioc->shost_recovery || ioc->remove_host) return; phy_number = event_data->StartPhyNum + i; reason_code = event_data->PHY[i].PhyStatus & @@ -4393,8 +4471,10 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, mpt2sas_transport_update_links(ioc, sas_address, handle, phy_number, link_rate); - if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5) - _scsih_ublock_io_device(ioc, handle); + if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) + break; + + _scsih_check_device(ioc, handle); break; case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: @@ -4520,10 +4600,10 @@ _scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc, event_data); #endif - if (!(event_data->ReasonCode == + if (event_data->ReasonCode != MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET && - event_data->ReasonCode == - MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET)) + event_data->ReasonCode != + MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET) return; spin_lock_irqsave(&ioc->sas_device_lock, flags); @@ -4630,7 +4710,6 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name, __func__)); - mutex_lock(&ioc->tm_cmds.mutex); termination_count = 0; query_count = 0; mpi_reply = ioc->tm_cmds.reply; @@ -4654,8 +4733,8 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, lun = sas_device_priv_data->lun; query_count++; - mpt2sas_scsih_issue_tm(ioc, handle, lun, - MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30); + mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, + MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, NULL); ioc->tm_cmds.status = MPT2_CMD_NOT_USED; ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; @@ -4666,13 +4745,11 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc, MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC)) continue; - mpt2sas_scsih_issue_tm(ioc, handle, lun, - MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, 0, 30); - ioc->tm_cmds.status = MPT2_CMD_NOT_USED; + mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, + MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, 0, 30, NULL); termination_count += le32_to_cpu(mpi_reply->TerminationCount); } ioc->broadcast_aen_busy = 0; - mutex_unlock(&ioc->tm_cmds.mutex); dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - exit, query_count = %d termination_count = %d\n", @@ -5442,6 +5519,26 @@ _scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work } /** + * _scsih_prep_device_scan - initialize parameters prior to device scan + * @ioc: per adapter object + * + * Set the deleted flag prior to device scan. If the device is found during + * the scan, then we clear the deleted flag. + */ +static void +_scsih_prep_device_scan(struct MPT2SAS_ADAPTER *ioc) +{ + struct MPT2SAS_DEVICE *sas_device_priv_data; + struct scsi_device *sdev; + + shost_for_each_device(sdev, ioc->shost) { + sas_device_priv_data = sdev->hostdata; + if (sas_device_priv_data && sas_device_priv_data->sas_target) + sas_device_priv_data->sas_target->deleted = 1; + } +} + +/** * _scsih_mark_responding_sas_device - mark a sas_devices as responding * @ioc: per adapter object * @sas_address: sas address @@ -5467,10 +5564,13 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, if (sas_device->sas_address == sas_address && sas_device->slot == slot && sas_device->starget) { sas_device->responding = 1; - sas_device->state = 0; starget = sas_device->starget; - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->tm_busy = 0; + if (starget && starget->hostdata) { + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->tm_busy = 0; + sas_target_priv_data->deleted = 0; + } else + sas_target_priv_data = NULL; starget_printk(KERN_INFO, sas_device->starget, "handle(0x%04x), sas_addr(0x%016llx), enclosure " "logical id(0x%016llx), slot(%d)\n", handle, @@ -5483,7 +5583,8 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", sas_device->handle); sas_device->handle = handle; - sas_target_priv_data->handle = handle; + if (sas_target_priv_data) + sas_target_priv_data->handle = handle; goto out; } } @@ -5558,6 +5659,12 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid, spin_lock_irqsave(&ioc->raid_device_lock, flags); list_for_each_entry(raid_device, &ioc->raid_device_list, list) { if (raid_device->wwid == wwid && raid_device->starget) { + starget = raid_device->starget; + if (starget && starget->hostdata) { + sas_target_priv_data = starget->hostdata; + sas_target_priv_data->deleted = 0; + } else + sas_target_priv_data = NULL; raid_device->responding = 1; starget_printk(KERN_INFO, raid_device->starget, "handle(0x%04x), wwid(0x%016llx)\n", handle, @@ -5567,9 +5674,8 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid, printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n", raid_device->handle); raid_device->handle = handle; - starget = raid_device->starget; - sas_target_priv_data = starget->hostdata; - sas_target_priv_data->handle = handle; + if (sas_target_priv_data) + sas_target_priv_data->handle = handle; goto out; } } @@ -5694,13 +5800,13 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc) } /** - * _scsih_remove_unresponding_devices - removing unresponding devices + * _scsih_remove_unresponding_sas_devices - removing unresponding devices * @ioc: per adapter object * * Return nothing. */ static void -_scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc) +_scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc) { struct _sas_device *sas_device, *sas_device_next; struct _sas_node *sas_expander; @@ -5722,8 +5828,6 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc) (unsigned long long) sas_device->enclosure_logical_id, sas_device->slot); - /* invalidate the device handle */ - sas_device->handle = 0; _scsih_remove_device(ioc, sas_device); } @@ -5774,32 +5878,33 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase) case MPT2_IOC_PRE_RESET: dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); - _scsih_fw_event_off(ioc); break; case MPT2_IOC_AFTER_RESET: dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); + if (ioc->scsih_cmds.status & MPT2_CMD_PENDING) { + ioc->scsih_cmds.status |= MPT2_CMD_RESET; + mpt2sas_base_free_smid(ioc, ioc->scsih_cmds.smid); + complete(&ioc->scsih_cmds.done); + } if (ioc->tm_cmds.status & MPT2_CMD_PENDING) { ioc->tm_cmds.status |= MPT2_CMD_RESET; mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid); complete(&ioc->tm_cmds.done); } - _scsih_fw_event_on(ioc); + _scsih_fw_event_cleanup_queue(ioc); _scsih_flush_running_cmds(ioc); + _scsih_queue_rescan(ioc); break; case MPT2_IOC_DONE_RESET: dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " "MPT2_IOC_DONE_RESET\n", ioc->name, __func__)); _scsih_sas_host_refresh(ioc); + _scsih_prep_device_scan(ioc); _scsih_search_responding_sas_devices(ioc); _scsih_search_responding_raid_devices(ioc); _scsih_search_responding_expanders(ioc); break; - case MPT2_IOC_RUNNING: - dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " - "MPT2_IOC_RUNNING\n", ioc->name, __func__)); - _scsih_remove_unresponding_devices(ioc); - break; } } @@ -5815,21 +5920,28 @@ static void _firmware_event_work(struct work_struct *work) { struct fw_event_work *fw_event = container_of(work, - struct fw_event_work, work); + struct fw_event_work, delayed_work.work); unsigned long flags; struct MPT2SAS_ADAPTER *ioc = fw_event->ioc; /* the queue is being flushed so ignore this event */ - spin_lock_irqsave(&ioc->fw_event_lock, flags); - if (ioc->fw_events_off || ioc->remove_host) { - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (ioc->remove_host || fw_event->cancel_pending_work) { _scsih_fw_event_free(ioc, fw_event); return; } - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - if (ioc->shost_recovery) { - _scsih_fw_event_requeue(ioc, fw_event, 1000); + if (fw_event->event == MPT2SAS_RESCAN_AFTER_HOST_RESET) { + _scsih_fw_event_free(ioc, fw_event); + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->shost_recovery) { + init_completion(&ioc->shost_recovery_done); + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, + flags); + wait_for_completion(&ioc->shost_recovery_done); + } else + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, + flags); + _scsih_remove_unresponding_sas_devices(ioc); return; } @@ -5891,16 +6003,12 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, { struct fw_event_work *fw_event; Mpi2EventNotificationReply_t *mpi_reply; - unsigned long flags; u16 event; + u16 sz; /* events turned off due to host reset or driver unloading */ - spin_lock_irqsave(&ioc->fw_event_lock, flags); - if (ioc->fw_events_off || ioc->remove_host) { - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (ioc->remove_host) return 1; - } - spin_unlock_irqrestore(&ioc->fw_event_lock, flags); mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply); event = le16_to_cpu(mpi_reply->Event); @@ -5947,8 +6055,8 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, ioc->name, __FILE__, __LINE__, __func__); return 1; } - fw_event->event_data = - kzalloc(mpi_reply->EventDataLength*4, GFP_ATOMIC); + sz = le16_to_cpu(mpi_reply->EventDataLength) * 4; + fw_event->event_data = kzalloc(sz, GFP_ATOMIC); if (!fw_event->event_data) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); @@ -5957,7 +6065,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index, } memcpy(fw_event->event_data, mpi_reply->EventData, - mpi_reply->EventDataLength*4); + sz); fw_event->ioc = ioc; fw_event->VF_ID = mpi_reply->VF_ID; fw_event->VP_ID = mpi_reply->VP_ID; @@ -6158,6 +6266,18 @@ _scsih_shutdown(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + struct workqueue_struct *wq; + unsigned long flags; + + ioc->remove_host = 1; + _scsih_fw_event_cleanup_queue(ioc); + + spin_lock_irqsave(&ioc->fw_event_lock, flags); + wq = ioc->firmware_event_thread; + ioc->firmware_event_thread = NULL; + spin_unlock_irqrestore(&ioc->fw_event_lock, flags); + if (wq) + destroy_workqueue(wq); _scsih_ir_shutdown(ioc); mpt2sas_base_detach(ioc); @@ -6184,7 +6304,7 @@ _scsih_remove(struct pci_dev *pdev) unsigned long flags; ioc->remove_host = 1; - _scsih_fw_event_off(ioc); + _scsih_fw_event_cleanup_queue(ioc); spin_lock_irqsave(&ioc->fw_event_lock, flags); wq = ioc->firmware_event_thread; @@ -6557,6 +6677,122 @@ _scsih_resume(struct pci_dev *pdev) } #endif /* CONFIG_PM */ +/** + * _scsih_pci_error_detected - Called when a PCI error is detected. + * @pdev: PCI device struct + * @state: PCI channel state + * + * Description: Called when a PCI error is detected. + * + * Return value: + * PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT + */ +static pci_ers_result_t +_scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + printk(MPT2SAS_INFO_FMT "PCI error: detected callback, state(%d)!!\n", + ioc->name, state); + + switch (state) { + case pci_channel_io_normal: + return PCI_ERS_RESULT_CAN_RECOVER; + case pci_channel_io_frozen: + scsi_block_requests(ioc->shost); + mpt2sas_base_stop_watchdog(ioc); + mpt2sas_base_free_resources(ioc); + return PCI_ERS_RESULT_NEED_RESET; + case pci_channel_io_perm_failure: + _scsih_remove(pdev); + return PCI_ERS_RESULT_DISCONNECT; + } + return PCI_ERS_RESULT_NEED_RESET; +} + +/** + * _scsih_pci_slot_reset - Called when PCI slot has been reset. + * @pdev: PCI device struct + * + * Description: This routine is called by the pci error recovery + * code after the PCI slot has been reset, just before we + * should resume normal operations. + */ +static pci_ers_result_t +_scsih_pci_slot_reset(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + int rc; + + printk(MPT2SAS_INFO_FMT "PCI error: slot reset callback!!\n", + ioc->name); + + ioc->pdev = pdev; + rc = mpt2sas_base_map_resources(ioc); + if (rc) + return PCI_ERS_RESULT_DISCONNECT; + + + rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, + FORCE_BIG_HAMMER); + + printk(MPT2SAS_WARN_FMT "hard reset: %s\n", ioc->name, + (rc == 0) ? "success" : "failed"); + + if (!rc) + return PCI_ERS_RESULT_RECOVERED; + else + return PCI_ERS_RESULT_DISCONNECT; +} + +/** + * _scsih_pci_resume() - resume normal ops after PCI reset + * @pdev: pointer to PCI device + * + * Called when the error recovery driver tells us that its + * OK to resume normal operation. Use completion to allow + * halted scsi ops to resume. + */ +static void +_scsih_pci_resume(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + printk(MPT2SAS_INFO_FMT "PCI error: resume callback!!\n", ioc->name); + + pci_cleanup_aer_uncorrect_error_status(pdev); + mpt2sas_base_start_watchdog(ioc); + scsi_unblock_requests(ioc->shost); +} + +/** + * _scsih_pci_mmio_enabled - Enable MMIO and dump debug registers + * @pdev: pointer to PCI device + */ +static pci_ers_result_t +_scsih_pci_mmio_enabled(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + + printk(MPT2SAS_INFO_FMT "PCI error: mmio enabled callback!!\n", + ioc->name); + + /* TODO - dump whatever for debugging purposes */ + + /* Request a slot reset. */ + return PCI_ERS_RESULT_NEED_RESET; +} + +static struct pci_error_handlers _scsih_err_handler = { + .error_detected = _scsih_pci_error_detected, + .mmio_enabled = _scsih_pci_mmio_enabled, + .slot_reset = _scsih_pci_slot_reset, + .resume = _scsih_pci_resume, +}; static struct pci_driver scsih_driver = { .name = MPT2SAS_DRIVER_NAME, @@ -6564,6 +6800,7 @@ static struct pci_driver scsih_driver = { .probe = _scsih_probe, .remove = __devexit_p(_scsih_remove), .shutdown = _scsih_shutdown, + .err_handler = &_scsih_err_handler, #ifdef CONFIG_PM .suspend = _scsih_suspend, .resume = _scsih_resume, diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c index bd7ca2b49f8..2727c3b6510 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_transport.c +++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c @@ -2,7 +2,7 @@ * SAS Transport Layer for MPT (Message Passing Technology) based controllers * * This code is based on drivers/scsi/mpt2sas/mpt2_transport.c - * Copyright (C) 2007-2009 LSI Corporation + * Copyright (C) 2007-2010 LSI Corporation * (mailto:DL-MPTFusionLinux@lsi.com) * * This program is free software; you can redistribute it and/or @@ -465,6 +465,85 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc, return rc; } + +/** + * _transport_delete_duplicate_port - (see below description) + * @ioc: per adapter object + * @sas_node: sas node object (either expander or sas host) + * @sas_address: sas address of device being added + * @phy_num: phy number + * + * This function is called when attempting to add a new port that is claiming + * the same phy resources already in use by another port. If we don't release + * the claimed phy resources, the sas transport layer will hang from the BUG + * in sas_port_add_phy. + * + * The reason we would hit this issue is becuase someone is changing the + * sas address of a device on the fly, meanwhile controller firmware sends + * EVENTs out of order when removing the previous instance of the device. + */ +static void +_transport_delete_duplicate_port(struct MPT2SAS_ADAPTER *ioc, + struct _sas_node *sas_node, u64 sas_address, int phy_num) +{ + struct _sas_port *mpt2sas_port, *mpt2sas_port_duplicate; + struct _sas_phy *mpt2sas_phy; + + printk(MPT2SAS_ERR_FMT "new device located at sas_addr(0x%016llx), " + "phy_id(%d)\n", ioc->name, (unsigned long long)sas_address, + phy_num); + + mpt2sas_port_duplicate = NULL; + list_for_each_entry(mpt2sas_port, &sas_node->sas_port_list, port_list) { + dev_printk(KERN_ERR, &mpt2sas_port->port->dev, + "existing device at sas_addr(0x%016llx), num_phys(%d)\n", + (unsigned long long) + mpt2sas_port->remote_identify.sas_address, + mpt2sas_port->num_phys); + list_for_each_entry(mpt2sas_phy, &mpt2sas_port->phy_list, + port_siblings) { + dev_printk(KERN_ERR, &mpt2sas_phy->phy->dev, + "phy_number(%d)\n", mpt2sas_phy->phy_id); + if (mpt2sas_phy->phy_id == phy_num) + mpt2sas_port_duplicate = mpt2sas_port; + } + } + + if (!mpt2sas_port_duplicate) + return; + + dev_printk(KERN_ERR, &mpt2sas_port_duplicate->port->dev, + "deleting duplicate device at sas_addr(0x%016llx), phy(%d)!!!!\n", + (unsigned long long) + mpt2sas_port_duplicate->remote_identify.sas_address, phy_num); + ioc->logging_level |= MPT_DEBUG_TRANSPORT; + mpt2sas_transport_port_remove(ioc, + mpt2sas_port_duplicate->remote_identify.sas_address, + sas_node->sas_address); + ioc->logging_level &= ~MPT_DEBUG_TRANSPORT; +} + +/** + * _transport_sanity_check - sanity check when adding a new port + * @ioc: per adapter object + * @sas_node: sas node object (either expander or sas host) + * @sas_address: sas address of device being added + * + * See the explanation above from _transport_delete_duplicate_port + */ +static void +_transport_sanity_check(struct MPT2SAS_ADAPTER *ioc, struct _sas_node *sas_node, + u64 sas_address) +{ + int i; + + for (i = 0; i < sas_node->num_phys; i++) + if (sas_node->phy[i].remote_identify.sas_address == sas_address) + if (sas_node->phy[i].phy_belongs_to_port) + _transport_delete_duplicate_port(ioc, sas_node, + sas_address, i); +} + /** * mpt2sas_transport_port_add - insert port to the list * @ioc: per adapter object @@ -522,6 +601,9 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, goto out_fail; } + _transport_sanity_check(ioc, sas_node, + mpt2sas_port->remote_identify.sas_address); + for (i = 0; i < sas_node->num_phys; i++) { if (sas_node->phy[i].remote_identify.sas_address != mpt2sas_port->remote_identify.sas_address) @@ -553,6 +635,7 @@ mpt2sas_transport_port_add(struct MPT2SAS_ADAPTER *ioc, u16 handle, mpt2sas_port->remote_identify.sas_address, mpt2sas_phy->phy_id); sas_port_add_phy(port, mpt2sas_phy->phy); + mpt2sas_phy->phy_belongs_to_port = 1; } mpt2sas_port->port = port; @@ -651,6 +734,7 @@ mpt2sas_transport_port_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, (unsigned long long) mpt2sas_port->remote_identify.sas_address, mpt2sas_phy->phy_id); + mpt2sas_phy->phy_belongs_to_port = 0; sas_port_delete_phy(mpt2sas_port->port, mpt2sas_phy->phy); list_del(&mpt2sas_phy->port_siblings); } @@ -1341,7 +1425,8 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, memcpy(req->sense, mpi_reply, sizeof(*mpi_reply)); req->sense_len = sizeof(*mpi_reply); req->resid_len = 0; - rsp->resid_len -= mpi_reply->ResponseDataLength; + rsp->resid_len -= + le16_to_cpu(mpi_reply->ResponseDataLength); } else { dtransportprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s - no reply\n", ioc->name, __func__)); diff --git a/drivers/scsi/mvme147.c b/drivers/scsi/mvme147.c index d722235111a..716d1785cda 100644 --- a/drivers/scsi/mvme147.c +++ b/drivers/scsi/mvme147.c @@ -13,112 +13,116 @@ #include "wd33c93.h" #include "mvme147.h" -#include<linux/stat.h> +#include <linux/stat.h> -#define HDATA(ptr) ((struct WD33C93_hostdata *)((ptr)->hostdata)) static struct Scsi_Host *mvme147_host = NULL; -static irqreturn_t mvme147_intr (int irq, void *dummy) +static irqreturn_t mvme147_intr(int irq, void *dummy) { - if (irq == MVME147_IRQ_SCSI_PORT) - wd33c93_intr (mvme147_host); - else - m147_pcc->dma_intr = 0x89; /* Ack and enable ints */ - return IRQ_HANDLED; + if (irq == MVME147_IRQ_SCSI_PORT) + wd33c93_intr(mvme147_host); + else + m147_pcc->dma_intr = 0x89; /* Ack and enable ints */ + return IRQ_HANDLED; } static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { - unsigned char flags = 0x01; - unsigned long addr = virt_to_bus(cmd->SCp.ptr); - - /* setup dma direction */ - if (!dir_in) - flags |= 0x04; - - /* remember direction */ - HDATA(mvme147_host)->dma_dir = dir_in; - - if (dir_in) - /* invalidate any cache */ - cache_clear (addr, cmd->SCp.this_residual); - else - /* push any dirty cache */ - cache_push (addr, cmd->SCp.this_residual); - - /* start DMA */ - m147_pcc->dma_bcr = cmd->SCp.this_residual | (1<<24); - m147_pcc->dma_dadr = addr; - m147_pcc->dma_cntrl = flags; - - /* return success */ - return 0; + struct WD33C93_hostdata *hdata = shost_priv(mvme147_host); + unsigned char flags = 0x01; + unsigned long addr = virt_to_bus(cmd->SCp.ptr); + + /* setup dma direction */ + if (!dir_in) + flags |= 0x04; + + /* remember direction */ + hdata->dma_dir = dir_in; + + if (dir_in) { + /* invalidate any cache */ + cache_clear(addr, cmd->SCp.this_residual); + } else { + /* push any dirty cache */ + cache_push(addr, cmd->SCp.this_residual); + } + + /* start DMA */ + m147_pcc->dma_bcr = cmd->SCp.this_residual | (1 << 24); + m147_pcc->dma_dadr = addr; + m147_pcc->dma_cntrl = flags; + + /* return success */ + return 0; } static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, - int status) + int status) { - m147_pcc->dma_cntrl = 0; + m147_pcc->dma_cntrl = 0; } int mvme147_detect(struct scsi_host_template *tpnt) { - static unsigned char called = 0; - wd33c93_regs regs; - - if (!MACH_IS_MVME147 || called) - return 0; - called++; - - tpnt->proc_name = "MVME147"; - tpnt->proc_info = &wd33c93_proc_info; - - mvme147_host = scsi_register (tpnt, sizeof(struct WD33C93_hostdata)); - if (!mvme147_host) - goto err_out; - - mvme147_host->base = 0xfffe4000; - mvme147_host->irq = MVME147_IRQ_SCSI_PORT; - regs.SASR = (volatile unsigned char *)0xfffe4000; - regs.SCMD = (volatile unsigned char *)0xfffe4001; - HDATA(mvme147_host)->no_sync = 0xff; - HDATA(mvme147_host)->fast = 0; - HDATA(mvme147_host)->dma_mode = CTRL_DMA; - wd33c93_init(mvme147_host, regs, dma_setup, dma_stop, WD33C93_FS_8_10); - - if (request_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr, 0, "MVME147 SCSI PORT", mvme147_intr)) - goto err_unregister; - if (request_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr, 0, "MVME147 SCSI DMA", mvme147_intr)) - goto err_free_irq; + static unsigned char called = 0; + wd33c93_regs regs; + struct WD33C93_hostdata *hdata; + + if (!MACH_IS_MVME147 || called) + return 0; + called++; + + tpnt->proc_name = "MVME147"; + tpnt->proc_info = &wd33c93_proc_info; + + mvme147_host = scsi_register(tpnt, sizeof(struct WD33C93_hostdata)); + if (!mvme147_host) + goto err_out; + + mvme147_host->base = 0xfffe4000; + mvme147_host->irq = MVME147_IRQ_SCSI_PORT; + regs.SASR = (volatile unsigned char *)0xfffe4000; + regs.SCMD = (volatile unsigned char *)0xfffe4001; + hdata = shost_priv(mvme147_host); + hdata->no_sync = 0xff; + hdata->fast = 0; + hdata->dma_mode = CTRL_DMA; + wd33c93_init(mvme147_host, regs, dma_setup, dma_stop, WD33C93_FS_8_10); + + if (request_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr, 0, + "MVME147 SCSI PORT", mvme147_intr)) + goto err_unregister; + if (request_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr, 0, + "MVME147 SCSI DMA", mvme147_intr)) + goto err_free_irq; #if 0 /* Disabled; causes problems booting */ - m147_pcc->scsi_interrupt = 0x10; /* Assert SCSI bus reset */ - udelay(100); - m147_pcc->scsi_interrupt = 0x00; /* Negate SCSI bus reset */ - udelay(2000); - m147_pcc->scsi_interrupt = 0x40; /* Clear bus reset interrupt */ + m147_pcc->scsi_interrupt = 0x10; /* Assert SCSI bus reset */ + udelay(100); + m147_pcc->scsi_interrupt = 0x00; /* Negate SCSI bus reset */ + udelay(2000); + m147_pcc->scsi_interrupt = 0x40; /* Clear bus reset interrupt */ #endif - m147_pcc->scsi_interrupt = 0x09; /* Enable interrupt */ + m147_pcc->scsi_interrupt = 0x09; /* Enable interrupt */ - m147_pcc->dma_cntrl = 0x00; /* ensure DMA is stopped */ - m147_pcc->dma_intr = 0x89; /* Ack and enable ints */ + m147_pcc->dma_cntrl = 0x00; /* ensure DMA is stopped */ + m147_pcc->dma_intr = 0x89; /* Ack and enable ints */ - return 1; + return 1; - err_free_irq: - free_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr); - err_unregister: - wd33c93_release(); - scsi_unregister(mvme147_host); - err_out: - return 0; +err_free_irq: + free_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr); +err_unregister: + scsi_unregister(mvme147_host); +err_out: + return 0; } static int mvme147_bus_reset(struct scsi_cmnd *cmd) { /* FIXME perform bus-specific reset */ - /* FIXME 2: kill this function, and let midlayer fallback to + /* FIXME 2: kill this function, and let midlayer fallback to the same result, calling wd33c93_host_reset() */ spin_lock_irq(cmd->device->host->host_lock); @@ -154,10 +158,9 @@ static struct scsi_host_template driver_template = { int mvme147_release(struct Scsi_Host *instance) { #ifdef MODULE - /* XXX Make sure DMA is stopped! */ - wd33c93_release(); - free_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr); - free_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr); + /* XXX Make sure DMA is stopped! */ + free_irq(MVME147_IRQ_SCSI_PORT, mvme147_intr); + free_irq(MVME147_IRQ_SCSI_DMA, mvme147_intr); #endif - return 1; + return 1; } diff --git a/drivers/scsi/mvme147.h b/drivers/scsi/mvme147.h index 32aee85434d..bfd4566ef05 100644 --- a/drivers/scsi/mvme147.h +++ b/drivers/scsi/mvme147.h @@ -14,11 +14,11 @@ int mvme147_detect(struct scsi_host_template *); int mvme147_release(struct Scsi_Host *); #ifndef CMD_PER_LUN -#define CMD_PER_LUN 2 +#define CMD_PER_LUN 2 #endif #ifndef CAN_QUEUE -#define CAN_QUEUE 16 +#define CAN_QUEUE 16 #endif #endif /* MVME147_H */ diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c index 10a5077b6ae..afc7f6f3a13 100644 --- a/drivers/scsi/mvsas/mv_64xx.c +++ b/drivers/scsi/mvsas/mv_64xx.c @@ -132,9 +132,9 @@ static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard) tmp &= ~PHYEV_RDY_CH; mvs_write_port_irq_stat(mvi, phy_id, tmp); tmp = mvs_read_phy_ctl(mvi, phy_id); - if (hard) + if (hard == 1) tmp |= PHY_RST_HARD; - else + else if (hard == 0) tmp |= PHY_RST; mvs_write_phy_ctl(mvi, phy_id, tmp); if (hard) { @@ -144,6 +144,26 @@ static void mvs_64xx_phy_reset(struct mvs_info *mvi, u32 phy_id, int hard) } } +void mvs_64xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, u8 clear_all) +{ + void __iomem *regs = mvi->regs; + u32 tmp; + if (clear_all) { + tmp = mr32(MVS_INT_STAT_SRS_0); + if (tmp) { + printk(KERN_DEBUG "check SRS 0 %08X.\n", tmp); + mw32(MVS_INT_STAT_SRS_0, tmp); + } + } else { + tmp = mr32(MVS_INT_STAT_SRS_0); + if (tmp & (1 << (reg_set % 32))) { + printk(KERN_DEBUG "register set 0x%x was stopped.\n", + reg_set); + mw32(MVS_INT_STAT_SRS_0, 1 << (reg_set % 32)); + } + } +} + static int __devinit mvs_64xx_chip_reset(struct mvs_info *mvi) { void __iomem *regs = mvi->regs; @@ -761,6 +781,7 @@ const struct mvs_dispatch mvs_64xx_dispatch = { mvs_write_port_irq_mask, mvs_get_sas_addr, mvs_64xx_command_active, + mvs_64xx_clear_srs_irq, mvs_64xx_issue_stop, mvs_start_delivery, mvs_rx_update, diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c index 0940fae19d2..eed4c5c7201 100644 --- a/drivers/scsi/mvsas/mv_94xx.c +++ b/drivers/scsi/mvsas/mv_94xx.c @@ -616,6 +616,15 @@ void mvs_94xx_fix_dma(dma_addr_t buf_dma, int buf_len, int from, void *prd) } #endif +/* + * FIXME JEJB: temporary nop clear_srs_irq to make 94xx still work + * with 64xx fixes + */ +static void mvs_94xx_clear_srs_irq(struct mvs_info *mvi, u8 reg_set, + u8 clear_all) +{ +} + const struct mvs_dispatch mvs_94xx_dispatch = { "mv94xx", mvs_94xx_init, @@ -640,6 +649,7 @@ const struct mvs_dispatch mvs_94xx_dispatch = { mvs_write_port_irq_mask, mvs_get_sas_addr, mvs_94xx_command_active, + mvs_94xx_clear_srs_irq, mvs_94xx_issue_stop, mvs_start_delivery, mvs_rx_update, diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index cae6b2cf492..19ad34f381a 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -37,6 +37,7 @@ static const struct mvs_chip_info mvs_chips[] = { }; #define SOC_SAS_NUM 2 +#define SG_MX 64 static struct scsi_host_template mvs_sht = { .module = THIS_MODULE, @@ -53,10 +54,10 @@ static struct scsi_host_template mvs_sht = { .can_queue = 1, .cmd_per_lun = 1, .this_id = -1, - .sg_tablesize = SG_ALL, + .sg_tablesize = SG_MX, .max_sectors = SCSI_DEFAULT_MAX_SECTORS, .use_clustering = ENABLE_CLUSTERING, - .eh_device_reset_handler = sas_eh_device_reset_handler, + .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, .slave_alloc = mvs_slave_alloc, .target_destroy = sas_target_destroy, @@ -65,19 +66,17 @@ static struct scsi_host_template mvs_sht = { static struct sas_domain_function_template mvs_transport_ops = { .lldd_dev_found = mvs_dev_found, - .lldd_dev_gone = mvs_dev_gone, - + .lldd_dev_gone = mvs_dev_gone, .lldd_execute_task = mvs_queue_command, .lldd_control_phy = mvs_phy_control, .lldd_abort_task = mvs_abort_task, .lldd_abort_task_set = mvs_abort_task_set, .lldd_clear_aca = mvs_clear_aca, - .lldd_clear_task_set = mvs_clear_task_set, + .lldd_clear_task_set = mvs_clear_task_set, .lldd_I_T_nexus_reset = mvs_I_T_nexus_reset, .lldd_lu_reset = mvs_lu_reset, .lldd_query_task = mvs_query_task, - .lldd_port_formed = mvs_port_formed, .lldd_port_deformed = mvs_port_deformed, @@ -213,7 +212,7 @@ static irqreturn_t mvs_interrupt(int irq, void *opaque) static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost) { - int i, slot_nr; + int i = 0, slot_nr; if (mvi->flags & MVF_FLAG_SOC) slot_nr = MVS_SOC_SLOTS; @@ -232,6 +231,7 @@ static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost) mvi->devices[i].dev_type = NO_DEVICE; mvi->devices[i].device_id = i; mvi->devices[i].dev_status = MVS_DEV_NORMAL; + init_timer(&mvi->devices[i].timer); } /* @@ -437,6 +437,7 @@ static int __devinit mvs_prep_sas_ha_init(struct Scsi_Host *shost, sha->sas_phy = arr_phy; sha->sas_port = arr_port; + sha->core.shost = shost; sha->lldd_ha = kzalloc(sizeof(struct mvs_prv_info), GFP_KERNEL); if (!sha->lldd_ha) @@ -574,6 +575,10 @@ static int __devinit mvs_pci_init(struct pci_dev *pdev, } nhost++; } while (nhost < chip->n_host); +#ifdef MVS_USE_TASKLET + tasklet_init(&mv_tasklet, mvs_tasklet, + (unsigned long)SHOST_TO_SAS_HA(shost)); +#endif mvs_post_sas_ha_init(shost, chip); diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index 0d213864121..f5e32179190 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -259,8 +259,6 @@ static inline void mvs_free_reg_set(struct mvs_info *mvi, mv_printk("device has been free.\n"); return; } - if (dev->runing_req != 0) - return; if (dev->taskfileset == MVS_ID_NOT_MAPPED) return; MVS_CHIP_DISP->free_reg_set(mvi, &dev->taskfileset); @@ -762,8 +760,6 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi, } if (is_tmf) flags |= (MCH_SSP_FR_TASK << MCH_SSP_FR_TYPE_SHIFT); - else - flags |= (MCH_SSP_FR_CMD << MCH_SSP_FR_TYPE_SHIFT); hdr->flags = cpu_to_le32(flags | (tei->n_elem << MCH_PRD_LEN_SHIFT)); hdr->tags = cpu_to_le32(tag); hdr->data_len = cpu_to_le32(task->total_xfer_len); @@ -878,14 +874,15 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags, struct mvs_slot_info *slot; u32 tag = 0xdeadbeef, rc, n_elem = 0; u32 n = num, pass = 0; - unsigned long flags = 0; + unsigned long flags = 0, flags_libsas = 0; if (!dev->port) { struct task_status_struct *tsm = &t->task_status; tsm->resp = SAS_TASK_UNDELIVERED; tsm->stat = SAS_PHY_DOWN; - t->task_done(t); + if (dev->dev_type != SATA_DEV) + t->task_done(t); return 0; } @@ -910,12 +907,25 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags, else tei.port = &mvi->port[dev->port->id]; - if (!tei.port->port_attached) { + if (tei.port && !tei.port->port_attached) { if (sas_protocol_ata(t->task_proto)) { + struct task_status_struct *ts = &t->task_status; + mv_dprintk("port %d does not" "attached device.\n", dev->port->id); - rc = SAS_PHY_DOWN; - goto out_done; + ts->stat = SAS_PROTO_RESPONSE; + ts->stat = SAS_PHY_DOWN; + spin_unlock_irqrestore(dev->sata_dev.ap->lock, + flags_libsas); + spin_unlock_irqrestore(&mvi->lock, flags); + t->task_done(t); + spin_lock_irqsave(&mvi->lock, flags); + spin_lock_irqsave(dev->sata_dev.ap->lock, + flags_libsas); + if (n > 1) + t = list_entry(t->list.next, + struct sas_task, list); + continue; } else { struct task_status_struct *ts = &t->task_status; ts->resp = SAS_TASK_UNDELIVERED; @@ -973,8 +983,8 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags, break; default: dev_printk(KERN_ERR, mvi->dev, - "unknown sas_task proto: 0x%x\n", - t->task_proto); + "unknown sas_task proto: 0x%x\n", + t->task_proto); rc = -EINVAL; break; } @@ -993,11 +1003,15 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags, spin_unlock(&t->task_state_lock); mvs_hba_memory_dump(mvi, tag, t->task_proto); - mvi_dev->runing_req++; + mvi_dev->running_req++; ++pass; mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1); if (n > 1) t = list_entry(t->list.next, struct sas_task, list); + if (likely(pass)) + MVS_CHIP_DISP->start_delivery(mvi, (mvi->tx_prod - 1) & + (MVS_CHIP_SLOT_SZ - 1)); + } while (--n); rc = 0; goto out_done; @@ -1012,10 +1026,6 @@ err_out: dma_unmap_sg(mvi->dev, t->scatter, n_elem, t->data_dir); out_done: - if (likely(pass)) { - MVS_CHIP_DISP->start_delivery(mvi, - (mvi->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1)); - } spin_unlock_irqrestore(&mvi->lock, flags); return rc; } @@ -1187,7 +1197,7 @@ void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st) MVS_CHIP_DISP->phy_reset(mvi, i, 0); goto out_done; } - } else if (phy->phy_type & PORT_TYPE_SAS + } else if (phy->phy_type & PORT_TYPE_SAS || phy->att_dev_info & PORT_SSP_INIT_MASK) { phy->phy_attached = 1; phy->identify.device_type = @@ -1256,7 +1266,20 @@ static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock) static void mvs_port_notify_deformed(struct asd_sas_phy *sas_phy, int lock) { - /*Nothing*/ + struct domain_device *dev; + struct mvs_phy *phy = sas_phy->lldd_phy; + struct mvs_info *mvi = phy->mvi; + struct asd_sas_port *port = sas_phy->port; + int phy_no = 0; + + while (phy != &mvi->phy[phy_no]) { + phy_no++; + if (phy_no >= MVS_MAX_PHYS) + return; + } + list_for_each_entry(dev, &port->dev_list, dev_list_node) + mvs_do_release_task(phy->mvi, phy_no, NULL); + } @@ -1316,6 +1339,7 @@ int mvs_dev_found_notify(struct domain_device *dev, int lock) goto found_out; } dev->lldd_dev = mvi_device; + mvi_device->dev_status = MVS_DEV_NORMAL; mvi_device->dev_type = dev->dev_type; mvi_device->mvi_info = mvi; if (parent_dev && DEV_IS_EXPANDER(parent_dev->dev_type)) { @@ -1351,18 +1375,18 @@ int mvs_dev_found(struct domain_device *dev) return mvs_dev_found_notify(dev, 1); } -void mvs_dev_gone_notify(struct domain_device *dev, int lock) +void mvs_dev_gone_notify(struct domain_device *dev) { unsigned long flags = 0; struct mvs_device *mvi_dev = dev->lldd_dev; struct mvs_info *mvi = mvi_dev->mvi_info; - if (lock) - spin_lock_irqsave(&mvi->lock, flags); + spin_lock_irqsave(&mvi->lock, flags); if (mvi_dev) { mv_dprintk("found dev[%d:%x] is gone.\n", mvi_dev->device_id, mvi_dev->dev_type); + mvs_release_task(mvi, dev); mvs_free_reg_set(mvi, mvi_dev); mvs_free_dev(mvi_dev); } else { @@ -1370,14 +1394,13 @@ void mvs_dev_gone_notify(struct domain_device *dev, int lock) } dev->lldd_dev = NULL; - if (lock) - spin_unlock_irqrestore(&mvi->lock, flags); + spin_unlock_irqrestore(&mvi->lock, flags); } void mvs_dev_gone(struct domain_device *dev) { - mvs_dev_gone_notify(dev, 1); + mvs_dev_gone_notify(dev); } static struct sas_task *mvs_alloc_task(void) @@ -1540,7 +1563,7 @@ int mvs_lu_reset(struct domain_device *dev, u8 *lun) num = mvs_find_dev_phyno(dev, phyno); spin_lock_irqsave(&mvi->lock, flags); for (i = 0; i < num; i++) - mvs_release_task(mvi, phyno[i], dev); + mvs_release_task(mvi, dev); spin_unlock_irqrestore(&mvi->lock, flags); } /* If failed, fall-through I_T_Nexus reset */ @@ -1552,8 +1575,8 @@ int mvs_lu_reset(struct domain_device *dev, u8 *lun) int mvs_I_T_nexus_reset(struct domain_device *dev) { unsigned long flags; - int i, phyno[WIDE_PORT_MAX_PHY], num , rc = TMF_RESP_FUNC_FAILED; - struct mvs_device * mvi_dev = (struct mvs_device *)dev->lldd_dev; + int rc = TMF_RESP_FUNC_FAILED; + struct mvs_device * mvi_dev = (struct mvs_device *)dev->lldd_dev; struct mvs_info *mvi = mvi_dev->mvi_info; if (mvi_dev->dev_status != MVS_DEV_EH) @@ -1563,10 +1586,8 @@ int mvs_I_T_nexus_reset(struct domain_device *dev) __func__, mvi_dev->device_id, rc); /* housekeeper */ - num = mvs_find_dev_phyno(dev, phyno); spin_lock_irqsave(&mvi->lock, flags); - for (i = 0; i < num; i++) - mvs_release_task(mvi, phyno[i], dev); + mvs_release_task(mvi, dev); spin_unlock_irqrestore(&mvi->lock, flags); return rc; @@ -1603,6 +1624,9 @@ int mvs_query_task(struct sas_task *task) case TMF_RESP_FUNC_FAILED: case TMF_RESP_FUNC_COMPLETE: break; + default: + rc = TMF_RESP_FUNC_COMPLETE; + break; } } mv_printk("%s:rc= %d\n", __func__, rc); @@ -1621,8 +1645,11 @@ int mvs_abort_task(struct sas_task *task) unsigned long flags; u32 tag; - if (mvi->exp_req) - mvi->exp_req--; + if (!mvi_dev) { + mv_printk("%s:%d TMF_RESP_FUNC_FAILED\n", __func__, __LINE__); + rc = TMF_RESP_FUNC_FAILED; + } + spin_lock_irqsave(&task->task_state_lock, flags); if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); @@ -1630,6 +1657,7 @@ int mvs_abort_task(struct sas_task *task) goto out; } spin_unlock_irqrestore(&task->task_state_lock, flags); + mvi_dev->dev_status = MVS_DEV_EH; if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) { struct scsi_cmnd * cmnd = (struct scsi_cmnd *)task->uldd_task; @@ -1654,12 +1682,31 @@ int mvs_abort_task(struct sas_task *task) if (task->lldd_task) { slot = task->lldd_task; slot_no = (u32) (slot - mvi->slot_info); + spin_lock_irqsave(&mvi->lock, flags); mvs_slot_complete(mvi, slot_no, 1); + spin_unlock_irqrestore(&mvi->lock, flags); } } + } else if (task->task_proto & SAS_PROTOCOL_SATA || task->task_proto & SAS_PROTOCOL_STP) { /* to do free register_set */ + if (SATA_DEV == dev->dev_type) { + struct mvs_slot_info *slot = task->lldd_task; + struct task_status_struct *tstat; + u32 slot_idx = (u32)(slot - mvi->slot_info); + tstat = &task->task_status; + mv_dprintk(KERN_DEBUG "mv_abort_task() mvi=%p task=%p " + "slot=%p slot_idx=x%x\n", + mvi, task, slot, slot_idx); + tstat->stat = SAS_ABORTED_TASK; + if (mvi_dev && mvi_dev->running_req) + mvi_dev->running_req--; + if (sas_protocol_ata(task->task_proto)) + mvs_free_reg_set(mvi, mvi_dev); + mvs_slot_task_free(mvi, task, slot, slot_idx); + return -1; + } } else { /* SMP */ @@ -1717,8 +1764,13 @@ static int mvs_sata_done(struct mvs_info *mvi, struct sas_task *task, SATA_RECEIVED_D2H_FIS(mvi_dev->taskfileset), sizeof(struct dev_to_host_fis)); tstat->buf_valid_size = sizeof(*resp); - if (unlikely(err)) - stat = SAS_PROTO_RESPONSE; + if (unlikely(err)) { + if (unlikely(err & CMD_ISS_STPD)) + stat = SAS_OPEN_REJECT; + else + stat = SAS_PROTO_RESPONSE; + } + return stat; } @@ -1753,9 +1805,7 @@ static int mvs_slot_err(struct mvs_info *mvi, struct sas_task *task, mv_printk("find reserved error, why?\n"); task->ata_task.use_ncq = 0; - stat = SAS_PROTO_RESPONSE; - mvs_sata_done(mvi, task, slot_idx, 1); - + mvs_sata_done(mvi, task, slot_idx, err_dw0); } break; default: @@ -1772,18 +1822,20 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags) struct sas_task *task = slot->task; struct mvs_device *mvi_dev = NULL; struct task_status_struct *tstat; + struct domain_device *dev; + u32 aborted; - bool aborted; void *to; enum exec_status sts; if (mvi->exp_req) mvi->exp_req--; - if (unlikely(!task || !task->lldd_task)) + if (unlikely(!task || !task->lldd_task || !task->dev)) return -1; tstat = &task->task_status; - mvi_dev = task->dev->lldd_dev; + dev = task->dev; + mvi_dev = dev->lldd_dev; mvs_hba_cq_dump(mvi); @@ -1800,8 +1852,8 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags) if (unlikely(aborted)) { tstat->stat = SAS_ABORTED_TASK; - if (mvi_dev) - mvi_dev->runing_req--; + if (mvi_dev && mvi_dev->running_req) + mvi_dev->running_req--; if (sas_protocol_ata(task->task_proto)) mvs_free_reg_set(mvi, mvi_dev); @@ -1809,24 +1861,17 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags) return -1; } - if (unlikely(!mvi_dev || !slot->port->port_attached || flags)) { - mv_dprintk("port has not device.\n"); + if (unlikely(!mvi_dev || flags)) { + if (!mvi_dev) + mv_dprintk("port has not device.\n"); tstat->stat = SAS_PHY_DOWN; goto out; } - /* - if (unlikely((rx_desc & RXQ_ERR) || (*(u64 *) slot->response))) { - mv_dprintk("Find device[%016llx] RXQ_ERR %X, - err info:%016llx\n", - SAS_ADDR(task->dev->sas_addr), - rx_desc, (u64)(*(u64 *) slot->response)); - } - */ - /* error info record present */ if (unlikely((rx_desc & RXQ_ERR) && (*(u64 *) slot->response))) { tstat->stat = mvs_slot_err(mvi, task, slot_idx); + tstat->resp = SAS_TASK_COMPLETE; goto out; } @@ -1868,11 +1913,16 @@ int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags) tstat->stat = SAM_CHECK_COND; break; } + if (!slot->port->port_attached) { + mv_dprintk("port %d has removed.\n", slot->port->sas_port.id); + tstat->stat = SAS_PHY_DOWN; + } + out: - if (mvi_dev) { - mvi_dev->runing_req--; - if (sas_protocol_ata(task->task_proto)) + if (mvi_dev && mvi_dev->running_req) { + mvi_dev->running_req--; + if (sas_protocol_ata(task->task_proto) && !mvi_dev->running_req) mvs_free_reg_set(mvi, mvi_dev); } mvs_slot_task_free(mvi, task, slot, slot_idx); @@ -1888,10 +1938,10 @@ out: return sts; } -void mvs_release_task(struct mvs_info *mvi, +void mvs_do_release_task(struct mvs_info *mvi, int phy_no, struct domain_device *dev) { - int i = 0; u32 slot_idx; + u32 slot_idx; struct mvs_phy *phy; struct mvs_port *port; struct mvs_slot_info *slot, *slot2; @@ -1900,6 +1950,10 @@ void mvs_release_task(struct mvs_info *mvi, port = phy->port; if (!port) return; + /* clean cmpl queue in case request is already finished */ + mvs_int_rx(mvi, false); + + list_for_each_entry_safe(slot, slot2, &port->list, entry) { struct sas_task *task; @@ -1911,18 +1965,22 @@ void mvs_release_task(struct mvs_info *mvi, mv_printk("Release slot [%x] tag[%x], task [%p]:\n", slot_idx, slot->slot_tag, task); - - if (task->task_proto & SAS_PROTOCOL_SSP) { - mv_printk("attached with SSP task CDB["); - for (i = 0; i < 16; i++) - mv_printk(" %02x", task->ssp_task.cdb[i]); - mv_printk(" ]\n"); - } + MVS_CHIP_DISP->command_active(mvi, slot_idx); mvs_slot_complete(mvi, slot_idx, 1); } } +void mvs_release_task(struct mvs_info *mvi, + struct domain_device *dev) +{ + int i, phyno[WIDE_PORT_MAX_PHY], num; + /* housekeeper */ + num = mvs_find_dev_phyno(dev, phyno); + for (i = 0; i < num; i++) + mvs_do_release_task(mvi, phyno[i], dev); +} + static void mvs_phy_disconnected(struct mvs_phy *phy) { phy->phy_attached = 0; @@ -2029,16 +2087,18 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events) * we need check the interrupt status which belongs to per port. */ - if (phy->irq_status & PHYEV_DCDR_ERR) + if (phy->irq_status & PHYEV_DCDR_ERR) { mv_dprintk("port %d STP decoding error.\n", - phy_no+mvi->id*mvi->chip->n_phy); + phy_no + mvi->id*mvi->chip->n_phy); + } if (phy->irq_status & PHYEV_POOF) { if (!(phy->phy_event & PHY_PLUG_OUT)) { int dev_sata = phy->phy_type & PORT_TYPE_SATA; int ready; - mvs_release_task(mvi, phy_no, NULL); + mvs_do_release_task(mvi, phy_no, NULL); phy->phy_event |= PHY_PLUG_OUT; + MVS_CHIP_DISP->clear_srs_irq(mvi, 0, 1); mvs_handle_event(mvi, (void *)(unsigned long)phy_no, PHY_PLUG_EVENT); @@ -2085,6 +2145,11 @@ void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events) phy_no, tmp); } mvs_update_phyinfo(mvi, phy_no, 0); + if (phy->phy_type & PORT_TYPE_SAS) { + MVS_CHIP_DISP->phy_reset(mvi, phy_no, 2); + mdelay(10); + } + mvs_bytes_dmaed(mvi, phy_no); /* whether driver is going to handle hot plug */ if (phy->phy_event & PHY_PLUG_OUT) { diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h index 885858bcc40..77ddc7c1e5f 100644 --- a/drivers/scsi/mvsas/mv_sas.h +++ b/drivers/scsi/mvsas/mv_sas.h @@ -39,6 +39,7 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <scsi/libsas.h> +#include <scsi/scsi.h> #include <scsi/scsi_tcq.h> #include <scsi/sas_ata.h> #include <linux/version.h> @@ -49,7 +50,7 @@ #define _MV_DUMP 0 #define MVS_ID_NOT_MAPPED 0x7f /* #define DISABLE_HOTPLUG_DMA_FIX */ -#define MAX_EXP_RUNNING_REQ 2 +// #define MAX_EXP_RUNNING_REQ 2 #define WIDE_PORT_MAX_PHY 4 #define MV_DISABLE_NCQ 0 #define mv_printk(fmt, arg ...) \ @@ -129,6 +130,7 @@ struct mvs_dispatch { void (*get_sas_addr)(void *buf, u32 buflen); void (*command_active)(struct mvs_info *mvi, u32 slot_idx); + void (*clear_srs_irq)(struct mvs_info *mvi, u8 reg_set, u8 clear_all); void (*issue_stop)(struct mvs_info *mvi, enum mvs_port_type type, u32 tfs); void (*start_delivery)(struct mvs_info *mvi, u32 tx); @@ -236,9 +238,10 @@ struct mvs_device { enum sas_dev_type dev_type; struct mvs_info *mvi_info; struct domain_device *sas_device; + struct timer_list timer; u32 attached_phy; u32 device_id; - u32 runing_req; + u32 running_req; u8 taskfileset; u8 dev_status; u16 reserved; @@ -397,7 +400,9 @@ int mvs_lu_reset(struct domain_device *dev, u8 *lun); int mvs_slot_complete(struct mvs_info *mvi, u32 rx_desc, u32 flags); int mvs_I_T_nexus_reset(struct domain_device *dev); int mvs_query_task(struct sas_task *task); -void mvs_release_task(struct mvs_info *mvi, int phy_no, +void mvs_release_task(struct mvs_info *mvi, + struct domain_device *dev); +void mvs_do_release_task(struct mvs_info *mvi, int phy_no, struct domain_device *dev); void mvs_int_port(struct mvs_info *mvi, int phy_no, u32 events); void mvs_update_phyinfo(struct mvs_info *mvi, int i, int get_st); diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 909c00ec044..5ff8261c5d6 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -4390,7 +4390,6 @@ pm8001_chip_fw_flash_update_req(struct pm8001_hba_info *pm8001_ha, return -ENOMEM; } } - memset(buffer, 0, fw_control->len); memcpy(buffer, fw_control->buffer, fw_control->len); flash_update_info.sgl.addr = cpu_to_le64(phys_addr); flash_update_info.sgl.im_len.len = cpu_to_le32(fw_control->len); diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index bff4f5139b9..cd02ceaf67f 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -885,11 +885,13 @@ static void pm8001_dev_gone_notify(struct domain_device *dev) u32 tag; struct pm8001_hba_info *pm8001_ha; struct pm8001_device *pm8001_dev = dev->lldd_dev; - u32 device_id = pm8001_dev->device_id; + pm8001_ha = pm8001_find_ha_by_dev(dev); spin_lock_irqsave(&pm8001_ha->lock, flags); pm8001_tag_alloc(pm8001_ha, &tag); if (pm8001_dev) { + u32 device_id = pm8001_dev->device_id; + PM8001_DISC_DBG(pm8001_ha, pm8001_printk("found dev[%d:%x] is gone.\n", pm8001_dev->device_id, pm8001_dev->dev_type)); diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 53aefffbaea..c44e4ab4e93 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -3751,12 +3751,6 @@ static int pmcraid_check_ioctl_buffer( return -EINVAL; } - /* buffer length can't be negetive */ - if (hdr->buffer_length < 0) { - pmcraid_err("ioctl: invalid buffer length specified\n"); - return -EINVAL; - } - /* check for appropriate buffer access */ if ((_IOC_DIR(cmd) & _IOC_READ) == _IOC_READ) access = VERIFY_WRITE; diff --git a/drivers/scsi/qla2xxx/Makefile b/drivers/scsi/qla2xxx/Makefile index c51fd1f8663..5df782f4a09 100644 --- a/drivers/scsi/qla2xxx/Makefile +++ b/drivers/scsi/qla2xxx/Makefile @@ -1,4 +1,5 @@ qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \ - qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o + qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o qla_bsg.o \ + qla_nx.o obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 1c7ef55966f..1e4cafabba1 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -12,13 +12,11 @@ #include <linux/delay.h> static int qla24xx_vport_disable(struct fc_vport *, bool); -static int qla84xx_reset(scsi_qla_host_t *, struct msg_echo_lb *, struct fc_bsg_job *); -int qla84xx_reset_chip(scsi_qla_host_t *, uint16_t, uint16_t *); -static int qla84xx_mgmt_cmd(scsi_qla_host_t *, struct msg_echo_lb *, struct fc_bsg_job *); + /* SYSFS attributes --------------------------------------------------------- */ static ssize_t -qla2x00_sysfs_read_fw_dump(struct kobject *kobj, +qla2x00_sysfs_read_fw_dump(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -34,7 +32,7 @@ qla2x00_sysfs_read_fw_dump(struct kobject *kobj, } static ssize_t -qla2x00_sysfs_write_fw_dump(struct kobject *kobj, +qla2x00_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -43,6 +41,12 @@ qla2x00_sysfs_write_fw_dump(struct kobject *kobj, struct qla_hw_data *ha = vha->hw; int reading; + if (IS_QLA82XX(ha)) { + DEBUG2(qla_printk(KERN_INFO, ha, + "Firmware dump not supported for ISP82xx\n")); + return count; + } + if (off != 0) return (0); @@ -88,7 +92,7 @@ static struct bin_attribute sysfs_fw_dump_attr = { }; static ssize_t -qla2x00_sysfs_read_nvram(struct kobject *kobj, +qla2x00_sysfs_read_nvram(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -107,7 +111,7 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj, } static ssize_t -qla2x00_sysfs_write_nvram(struct kobject *kobj, +qla2x00_sysfs_write_nvram(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -173,7 +177,7 @@ static struct bin_attribute sysfs_nvram_attr = { }; static ssize_t -qla2x00_sysfs_read_optrom(struct kobject *kobj, +qla2x00_sysfs_read_optrom(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -189,7 +193,7 @@ qla2x00_sysfs_read_optrom(struct kobject *kobj, } static ssize_t -qla2x00_sysfs_write_optrom(struct kobject *kobj, +qla2x00_sysfs_write_optrom(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -220,7 +224,7 @@ static struct bin_attribute sysfs_optrom_attr = { }; static ssize_t -qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, +qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -277,6 +281,12 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, return count; } + if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { + qla_printk(KERN_WARNING, ha, + "HBA not online, failing NVRAM update.\n"); + return -EAGAIN; + } + DEBUG2(qla_printk(KERN_INFO, ha, "Reading flash region -- 0x%x/0x%x.\n", ha->optrom_region_start, ha->optrom_region_size)); @@ -315,8 +325,8 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, else if (start == (ha->flt_region_boot * 4) || start == (ha->flt_region_fw * 4)) valid = 1; - else if (IS_QLA25XX(ha) || IS_QLA81XX(ha)) - valid = 1; + else if (IS_QLA25XX(ha) || IS_QLA8XXX_TYPE(ha)) + valid = 1; if (!valid) { qla_printk(KERN_WARNING, ha, "Invalid start region 0x%x/0x%x.\n", start, size); @@ -377,7 +387,7 @@ static struct bin_attribute sysfs_optrom_ctl_attr = { }; static ssize_t -qla2x00_sysfs_read_vpd(struct kobject *kobj, +qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -398,7 +408,7 @@ qla2x00_sysfs_read_vpd(struct kobject *kobj, } static ssize_t -qla2x00_sysfs_write_vpd(struct kobject *kobj, +qla2x00_sysfs_write_vpd(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -451,7 +461,7 @@ static struct bin_attribute sysfs_vpd_attr = { }; static ssize_t -qla2x00_sysfs_read_sfp(struct kobject *kobj, +qla2x00_sysfs_read_sfp(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -512,13 +522,14 @@ static struct bin_attribute sysfs_sfp_attr = { }; static ssize_t -qla2x00_sysfs_write_reset(struct kobject *kobj, +qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); int type; if (off != 0) @@ -553,6 +564,20 @@ qla2x00_sysfs_write_reset(struct kobject *kobj, "MPI reset failed on (%ld).\n", vha->host_no); scsi_unblock_requests(vha->host); break; + case 0x2025e: + if (!IS_QLA82XX(ha) || vha != base_vha) { + qla_printk(KERN_INFO, ha, + "FCoE ctx reset not supported for host%ld.\n", + vha->host_no); + return count; + } + + qla_printk(KERN_INFO, ha, + "Issuing FCoE CTX reset on host%ld.\n", vha->host_no); + set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_fcoe_ctx_reset(vha); + break; } return count; } @@ -567,7 +592,7 @@ static struct bin_attribute sysfs_reset_attr = { }; static ssize_t -qla2x00_sysfs_write_edc(struct kobject *kobj, +qla2x00_sysfs_write_edc(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -625,7 +650,7 @@ static struct bin_attribute sysfs_edc_attr = { }; static ssize_t -qla2x00_sysfs_write_edc_status(struct kobject *kobj, +qla2x00_sysfs_write_edc_status(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -675,7 +700,7 @@ qla2x00_sysfs_write_edc_status(struct kobject *kobj, } static ssize_t -qla2x00_sysfs_read_edc_status(struct kobject *kobj, +qla2x00_sysfs_read_edc_status(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -705,7 +730,7 @@ static struct bin_attribute sysfs_edc_status_attr = { }; static ssize_t -qla2x00_sysfs_read_xgmac_stats(struct kobject *kobj, +qla2x00_sysfs_read_xgmac_stats(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -757,7 +782,7 @@ static struct bin_attribute sysfs_xgmac_stats_attr = { }; static ssize_t -qla2x00_sysfs_read_dcbx_tlv(struct kobject *kobj, +qla2x00_sysfs_read_dcbx_tlv(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -838,7 +863,7 @@ qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha) continue; if (iter->is4GBp_only == 2 && !IS_QLA25XX(vha->hw)) continue; - if (iter->is4GBp_only == 3 && !IS_QLA81XX(vha->hw)) + if (iter->is4GBp_only == 3 && !(IS_QLA8XXX_TYPE(vha->hw))) continue; ret = sysfs_create_bin_file(&host->shost_gendev.kobj, @@ -862,7 +887,7 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *vha) continue; if (iter->is4GBp_only == 2 && !IS_QLA25XX(ha)) continue; - if (iter->is4GBp_only == 3 && !IS_QLA81XX(ha)) + if (iter->is4GBp_only == 3 && !!(IS_QLA8XXX_TYPE(vha->hw))) continue; sysfs_remove_bin_file(&host->shost_gendev.kobj, @@ -968,7 +993,8 @@ qla2x00_link_state_show(struct device *dev, struct device_attribute *attr, int len = 0; if (atomic_read(&vha->loop_state) == LOOP_DOWN || - atomic_read(&vha->loop_state) == LOOP_DEAD) + atomic_read(&vha->loop_state) == LOOP_DEAD || + vha->device_flags & DFLG_NO_CABLE) len = snprintf(buf, PAGE_SIZE, "Link Down\n"); else if (atomic_read(&vha->loop_state) != LOOP_READY || test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || @@ -1179,15 +1205,15 @@ qla24xx_84xx_fw_version_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; - if (IS_QLA84XX(ha) && ha->cs84xx) { - if (ha->cs84xx->op_fw_version == 0) { - rval = qla84xx_verify_chip(vha, status); - } + if (!IS_QLA84XX(ha)) + return snprintf(buf, PAGE_SIZE, "\n"); + + if (ha->cs84xx && ha->cs84xx->op_fw_version == 0) + rval = qla84xx_verify_chip(vha, status); if ((rval == QLA_SUCCESS) && (status[0] == 0)) return snprintf(buf, PAGE_SIZE, "%u\n", (uint32_t)ha->cs84xx->op_fw_version); - } return snprintf(buf, PAGE_SIZE, "\n"); } @@ -1237,7 +1263,7 @@ qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr, { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); - if (!IS_QLA81XX(vha->hw)) + if (!IS_QLA8XXX_TYPE(vha->hw)) return snprintf(buf, PAGE_SIZE, "\n"); return snprintf(buf, PAGE_SIZE, "%d\n", vha->fcoe_vlan_id); @@ -1249,7 +1275,7 @@ qla2x00_vn_port_mac_address_show(struct device *dev, { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); - if (!IS_QLA81XX(vha->hw)) + if (!IS_QLA8XXX_TYPE(vha->hw)) return snprintf(buf, PAGE_SIZE, "\n"); return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n", @@ -1706,6 +1732,22 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN); } + if (IS_QLA25XX(ha) && ql2xenabledif) { + if (ha->fw_attributes & BIT_4) { + vha->flags.difdix_supported = 1; + DEBUG18(qla_printk(KERN_INFO, ha, + "Registering for DIF/DIX type 1 and 3" + " protection.\n")); + scsi_host_set_prot(vha->host, + SHOST_DIF_TYPE1_PROTECTION + | SHOST_DIF_TYPE3_PROTECTION + | SHOST_DIX_TYPE1_PROTECTION + | SHOST_DIX_TYPE3_PROTECTION); + scsi_host_set_guard(vha->host, SHOST_DIX_GUARD_CRC); + } else + vha->flags.difdix_supported = 0; + } + if (scsi_add_host_with_dma(vha->host, &fc_vport->dev, &ha->pdev->dev)) { DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n", @@ -1825,582 +1867,6 @@ qla24xx_vport_disable(struct fc_vport *fc_vport, bool disable) return 0; } -/* BSG support for ELS/CT pass through */ -inline srb_t * -qla2x00_get_ctx_bsg_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size) -{ - srb_t *sp; - struct qla_hw_data *ha = vha->hw; - struct srb_bsg_ctx *ctx; - - sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL); - if (!sp) - goto done; - ctx = kzalloc(size, GFP_KERNEL); - if (!ctx) { - mempool_free(sp, ha->srb_mempool); - goto done; - } - - memset(sp, 0, sizeof(*sp)); - sp->fcport = fcport; - sp->ctx = ctx; -done: - return sp; -} - -static int -qla2x00_process_els(struct fc_bsg_job *bsg_job) -{ - struct fc_rport *rport; - fc_port_t *fcport; - struct Scsi_Host *host; - scsi_qla_host_t *vha; - struct qla_hw_data *ha; - srb_t *sp; - const char *type; - int req_sg_cnt, rsp_sg_cnt; - int rval = (DRIVER_ERROR << 16); - uint16_t nextlid = 0; - struct srb_bsg *els; - - /* Multiple SG's are not supported for ELS requests */ - if (bsg_job->request_payload.sg_cnt > 1 || - bsg_job->reply_payload.sg_cnt > 1) { - DEBUG2(printk(KERN_INFO - "multiple SG's are not supported for ELS requests" - " [request_sg_cnt: %x reply_sg_cnt: %x]\n", - bsg_job->request_payload.sg_cnt, - bsg_job->reply_payload.sg_cnt)); - rval = -EPERM; - goto done; - } - - /* ELS request for rport */ - if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { - rport = bsg_job->rport; - fcport = *(fc_port_t **) rport->dd_data; - host = rport_to_shost(rport); - vha = shost_priv(host); - ha = vha->hw; - type = "FC_BSG_RPT_ELS"; - - /* make sure the rport is logged in, - * if not perform fabric login - */ - if (qla2x00_fabric_login(vha, fcport, &nextlid)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "failed to login port %06X for ELS passthru\n", - fcport->d_id.b24)); - rval = -EIO; - goto done; - } - } else { - host = bsg_job->shost; - vha = shost_priv(host); - ha = vha->hw; - type = "FC_BSG_HST_ELS_NOLOGIN"; - - /* Allocate a dummy fcport structure, since functions - * preparing the IOCB and mailbox command retrieves port - * specific information from fcport structure. For Host based - * ELS commands there will be no fcport structure allocated - */ - fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); - if (!fcport) { - rval = -ENOMEM; - goto done; - } - - /* Initialize all required fields of fcport */ - fcport->vha = vha; - fcport->vp_idx = vha->vp_idx; - fcport->d_id.b.al_pa = - bsg_job->request->rqst_data.h_els.port_id[0]; - fcport->d_id.b.area = - bsg_job->request->rqst_data.h_els.port_id[1]; - fcport->d_id.b.domain = - bsg_job->request->rqst_data.h_els.port_id[2]; - fcport->loop_id = - (fcport->d_id.b.al_pa == 0xFD) ? - NPH_FABRIC_CONTROLLER : NPH_F_PORT; - } - - if (!vha->flags.online) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "host not online\n")); - rval = -EIO; - goto done; - } - - req_sg_cnt = - dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, - bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); - if (!req_sg_cnt) { - rval = -ENOMEM; - goto done_free_fcport; - } - rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); - if (!rsp_sg_cnt) { - rval = -ENOMEM; - goto done_free_fcport; - } - - if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || - (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) - { - DEBUG2(printk(KERN_INFO - "dma mapping resulted in different sg counts \ - [request_sg_cnt: %x dma_request_sg_cnt: %x\ - reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n", - bsg_job->request_payload.sg_cnt, req_sg_cnt, - bsg_job->reply_payload.sg_cnt, rsp_sg_cnt)); - rval = -EAGAIN; - goto done_unmap_sg; - } - - /* Alloc SRB structure */ - sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_bsg)); - if (!sp) { - rval = -ENOMEM; - goto done_unmap_sg; - } - - els = sp->ctx; - els->ctx.type = - (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? - SRB_ELS_CMD_RPT : SRB_ELS_CMD_HST); - els->bsg_job = bsg_job; - - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x " - "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type, - bsg_job->request->rqst_data.h_els.command_code, - fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa)); - - rval = qla2x00_start_sp(sp); - if (rval != QLA_SUCCESS) { - kfree(sp->ctx); - mempool_free(sp, ha->srb_mempool); - rval = -EIO; - goto done_unmap_sg; - } - return rval; - -done_unmap_sg: - dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, - bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); - dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); - goto done_free_fcport; - -done_free_fcport: - if (bsg_job->request->msgcode == FC_BSG_HST_ELS_NOLOGIN) - kfree(fcport); -done: - return rval; -} - -static int -qla2x00_process_ct(struct fc_bsg_job *bsg_job) -{ - srb_t *sp; - struct Scsi_Host *host = bsg_job->shost; - scsi_qla_host_t *vha = shost_priv(host); - struct qla_hw_data *ha = vha->hw; - int rval = (DRIVER_ERROR << 16); - int req_sg_cnt, rsp_sg_cnt; - uint16_t loop_id; - struct fc_port *fcport; - char *type = "FC_BSG_HST_CT"; - struct srb_bsg *ct; - - /* pass through is supported only for ISP 4Gb or higher */ - if (!IS_FWI2_CAPABLE(ha)) { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld):Firmware is not capable to support FC " - "CT pass thru\n", vha->host_no)); - rval = -EPERM; - goto done; - } - - req_sg_cnt = - dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, - bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); - if (!req_sg_cnt) { - rval = -ENOMEM; - goto done; - } - - rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); - if (!rsp_sg_cnt) { - rval = -ENOMEM; - goto done; - } - - if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || - (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) - { - DEBUG2(qla_printk(KERN_WARNING, ha, - "dma mapping resulted in different sg counts \ - [request_sg_cnt: %x dma_request_sg_cnt: %x\ - reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n", - bsg_job->request_payload.sg_cnt, req_sg_cnt, - bsg_job->reply_payload.sg_cnt, rsp_sg_cnt)); - rval = -EAGAIN; - goto done_unmap_sg; - } - - if (!vha->flags.online) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "host not online\n")); - rval = -EIO; - goto done_unmap_sg; - } - - loop_id = - (bsg_job->request->rqst_data.h_ct.preamble_word1 & 0xFF000000) - >> 24; - switch (loop_id) { - case 0xFC: - loop_id = cpu_to_le16(NPH_SNS); - break; - case 0xFA: - loop_id = vha->mgmt_svr_loop_id; - break; - default: - DEBUG2(qla_printk(KERN_INFO, ha, - "Unknown loop id: %x\n", loop_id)); - rval = -EINVAL; - goto done_unmap_sg; - } - - /* Allocate a dummy fcport structure, since functions preparing the - * IOCB and mailbox command retrieves port specific information - * from fcport structure. For Host based ELS commands there will be - * no fcport structure allocated - */ - fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); - if (!fcport) - { - rval = -ENOMEM; - goto done_unmap_sg; - } - - /* Initialize all required fields of fcport */ - fcport->vha = vha; - fcport->vp_idx = vha->vp_idx; - fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_ct.port_id[0]; - fcport->d_id.b.area = bsg_job->request->rqst_data.h_ct.port_id[1]; - fcport->d_id.b.domain = bsg_job->request->rqst_data.h_ct.port_id[2]; - fcport->loop_id = loop_id; - - /* Alloc SRB structure */ - sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_bsg)); - if (!sp) { - rval = -ENOMEM; - goto done_free_fcport; - } - - ct = sp->ctx; - ct->ctx.type = SRB_CT_CMD; - ct->bsg_job = bsg_job; - - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x " - "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type, - (bsg_job->request->rqst_data.h_ct.preamble_word2 >> 16), - fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa)); - - rval = qla2x00_start_sp(sp); - if (rval != QLA_SUCCESS) { - kfree(sp->ctx); - mempool_free(sp, ha->srb_mempool); - rval = -EIO; - goto done_free_fcport; - } - return rval; - -done_free_fcport: - kfree(fcport); -done_unmap_sg: - dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, - bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); - dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); -done: - return rval; -} - -static int -qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job) -{ - struct Scsi_Host *host = bsg_job->shost; - scsi_qla_host_t *vha = shost_priv(host); - struct qla_hw_data *ha = vha->hw; - int rval; - uint8_t command_sent; - uint32_t vendor_cmd; - char *type; - struct msg_echo_lb elreq; - uint16_t response[MAILBOX_REGISTER_COUNT]; - uint8_t* fw_sts_ptr; - uint8_t *req_data; - dma_addr_t req_data_dma; - uint32_t req_data_len; - uint8_t *rsp_data; - dma_addr_t rsp_data_dma; - uint32_t rsp_data_len; - - if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || - test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || - test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { - rval = -EBUSY; - goto done; - } - - if (!vha->flags.online) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "host not online\n")); - rval = -EIO; - goto done; - } - - elreq.req_sg_cnt = - dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, - bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); - if (!elreq.req_sg_cnt) { - rval = -ENOMEM; - goto done; - } - elreq.rsp_sg_cnt = - dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); - if (!elreq.rsp_sg_cnt) { - rval = -ENOMEM; - goto done; - } - - if ((elreq.req_sg_cnt != bsg_job->request_payload.sg_cnt) || - (elreq.rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) - { - DEBUG2(printk(KERN_INFO - "dma mapping resulted in different sg counts \ - [request_sg_cnt: %x dma_request_sg_cnt: %x\ - reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n", - bsg_job->request_payload.sg_cnt, elreq.req_sg_cnt, - bsg_job->reply_payload.sg_cnt, elreq.rsp_sg_cnt)); - rval = -EAGAIN; - goto done_unmap_sg; - } - req_data_len = rsp_data_len = bsg_job->request_payload.payload_len; - req_data = dma_alloc_coherent(&ha->pdev->dev, req_data_len, - &req_data_dma, GFP_KERNEL); - - rsp_data = dma_alloc_coherent(&ha->pdev->dev, rsp_data_len, - &rsp_data_dma, GFP_KERNEL); - - /* Copy the request buffer in req_data now */ - sg_copy_to_buffer(bsg_job->request_payload.sg_list, - bsg_job->request_payload.sg_cnt, req_data, - req_data_len); - - elreq.send_dma = req_data_dma; - elreq.rcv_dma = rsp_data_dma; - elreq.transfer_size = req_data_len; - - /* Vendor cmd : loopback or ECHO diagnostic - * Options: - * Loopback : Either internal or external loopback - * ECHO: ECHO ELS or Vendor specific FC4 link data - */ - vendor_cmd = bsg_job->request->rqst_data.h_vendor.vendor_cmd[0]; - elreq.options = - *(((uint32_t *)bsg_job->request->rqst_data.h_vendor.vendor_cmd) - + 1); - - switch (bsg_job->request->rqst_data.h_vendor.vendor_cmd[0]) { - case QL_VND_LOOPBACK: - if (ha->current_topology != ISP_CFG_F) { - type = "FC_BSG_HST_VENDOR_LOOPBACK"; - - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld) bsg rqst type: %s vendor rqst type: %x options: %x.\n", - vha->host_no, type, vendor_cmd, elreq.options)); - - command_sent = INT_DEF_LB_LOOPBACK_CMD; - rval = qla2x00_loopback_test(vha, &elreq, response); - if (IS_QLA81XX(ha)) { - if (response[0] == MBS_COMMAND_ERROR && response[1] == MBS_LB_RESET) { - DEBUG2(printk(KERN_ERR "%s(%ld): ABORTing " - "ISP\n", __func__, vha->host_no)); - set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); - qla2xxx_wake_dpc(vha); - } - } - } else { - type = "FC_BSG_HST_VENDOR_ECHO_DIAG"; - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld) bsg rqst type: %s vendor rqst type: %x options: %x.\n", - vha->host_no, type, vendor_cmd, elreq.options)); - - command_sent = INT_DEF_LB_ECHO_CMD; - rval = qla2x00_echo_test(vha, &elreq, response); - } - break; - case QLA84_RESET: - if (!IS_QLA84XX(vha->hw)) { - rval = -EINVAL; - DEBUG16(printk( - "%s(%ld): 8xxx exiting.\n", - __func__, vha->host_no)); - return rval; - } - rval = qla84xx_reset(vha, &elreq, bsg_job); - break; - case QLA84_MGMT_CMD: - if (!IS_QLA84XX(vha->hw)) { - rval = -EINVAL; - DEBUG16(printk( - "%s(%ld): 8xxx exiting.\n", - __func__, vha->host_no)); - return rval; - } - rval = qla84xx_mgmt_cmd(vha, &elreq, bsg_job); - break; - default: - rval = -ENOSYS; - } - - if (rval != QLA_SUCCESS) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "scsi(%ld) Vendor request %s failed\n", vha->host_no, type)); - rval = 0; - bsg_job->reply->result = (DID_ERROR << 16); - bsg_job->reply->reply_payload_rcv_len = 0; - fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply); - memcpy( fw_sts_ptr, response, sizeof(response)); - fw_sts_ptr += sizeof(response); - *fw_sts_ptr = command_sent; - } else { - DEBUG2(qla_printk(KERN_WARNING, ha, - "scsi(%ld) Vendor request %s completed\n", vha->host_no, type)); - rval = bsg_job->reply->result = 0; - bsg_job->reply_len = sizeof(struct fc_bsg_reply) + sizeof(response) + sizeof(uint8_t); - bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len; - fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply); - memcpy(fw_sts_ptr, response, sizeof(response)); - fw_sts_ptr += sizeof(response); - *fw_sts_ptr = command_sent; - sg_copy_from_buffer(bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, rsp_data, - rsp_data_len); - } - bsg_job->job_done(bsg_job); - -done_unmap_sg: - - if(req_data) - dma_free_coherent(&ha->pdev->dev, req_data_len, - req_data, req_data_dma); - dma_unmap_sg(&ha->pdev->dev, - bsg_job->request_payload.sg_list, - bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); - dma_unmap_sg(&ha->pdev->dev, - bsg_job->reply_payload.sg_list, - bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); - -done: - return rval; -} - -static int -qla24xx_bsg_request(struct fc_bsg_job *bsg_job) -{ - int ret = -EINVAL; - - switch (bsg_job->request->msgcode) { - case FC_BSG_RPT_ELS: - case FC_BSG_HST_ELS_NOLOGIN: - ret = qla2x00_process_els(bsg_job); - break; - case FC_BSG_HST_CT: - ret = qla2x00_process_ct(bsg_job); - break; - case FC_BSG_HST_VENDOR: - ret = qla2x00_process_vendor_specific(bsg_job); - break; - case FC_BSG_HST_ADD_RPORT: - case FC_BSG_HST_DEL_RPORT: - case FC_BSG_RPT_CT: - default: - DEBUG2(printk("qla2xxx: unsupported BSG request\n")); - break; - } - return ret; -} - -static int -qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job) -{ - scsi_qla_host_t *vha = shost_priv(bsg_job->shost); - struct qla_hw_data *ha = vha->hw; - srb_t *sp; - int cnt, que; - unsigned long flags; - struct req_que *req; - struct srb_bsg *sp_bsg; - - /* find the bsg job from the active list of commands */ - spin_lock_irqsave(&ha->hardware_lock, flags); - for (que = 0; que < ha->max_req_queues; que++) { - req = ha->req_q_map[que]; - if (!req) - continue; - - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++ ) { - sp = req->outstanding_cmds[cnt]; - - if (sp) { - sp_bsg = (struct srb_bsg*)sp->ctx; - - if (((sp_bsg->ctx.type == SRB_CT_CMD) || - (sp_bsg->ctx.type == SRB_ELS_CMD_RPT) - || ( sp_bsg->ctx.type == SRB_ELS_CMD_HST)) && - (sp_bsg->bsg_job == bsg_job)) { - if (ha->isp_ops->abort_command(sp)) { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld): mbx abort_command failed\n", vha->host_no)); - bsg_job->req->errors = bsg_job->reply->result = -EIO; - } else { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld): mbx abort_command success\n", vha->host_no)); - bsg_job->req->errors = bsg_job->reply->result = 0; - } - goto done; - } - } - } - } - spin_unlock_irqrestore(&ha->hardware_lock, flags); - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld) SRB not found to abort\n", vha->host_no)); - bsg_job->req->errors = bsg_job->reply->result = -ENXIO; - return 0; - -done: - spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (bsg_job->request->msgcode == FC_BSG_HST_CT) - kfree(sp->fcport); - kfree(sp->ctx); - mempool_free(sp, ha->srb_mempool); - return 0; -} - struct fc_function_template qla2xxx_transport_functions = { .show_host_node_name = 1, @@ -2502,7 +1968,7 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha) fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports; fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count; - if (IS_QLA81XX(ha)) + if (IS_QLA8XXX_TYPE(ha)) speed = FC_PORTSPEED_10GBIT; else if (IS_QLA25XX(ha)) speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT | @@ -2516,125 +1982,3 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha) speed = FC_PORTSPEED_1GBIT; fc_host_supported_speeds(vha->host) = speed; } -static int -qla84xx_reset(scsi_qla_host_t *ha, struct msg_echo_lb *mreq, struct fc_bsg_job *bsg_job) -{ - int ret = 0; - int cmd; - uint16_t cmd_status; - - DEBUG16(printk("%s(%ld): entered.\n", __func__, ha->host_no)); - - cmd = (*((bsg_job->request->rqst_data.h_vendor.vendor_cmd) + 2)) - == A84_RESET_FLAG_ENABLE_DIAG_FW ? - A84_ISSUE_RESET_DIAG_FW : A84_ISSUE_RESET_OP_FW; - ret = qla84xx_reset_chip(ha, cmd == A84_ISSUE_RESET_DIAG_FW, - &cmd_status); - return ret; -} - -static int -qla84xx_mgmt_cmd(scsi_qla_host_t *ha, struct msg_echo_lb *mreq, struct fc_bsg_job *bsg_job) -{ - struct access_chip_84xx *mn; - dma_addr_t mn_dma, mgmt_dma; - void *mgmt_b = NULL; - int ret = 0; - int rsp_hdr_len, len = 0; - struct qla84_msg_mgmt *ql84_mgmt; - - ql84_mgmt = (struct qla84_msg_mgmt *) vmalloc(sizeof(struct qla84_msg_mgmt)); - ql84_mgmt->cmd = - *((uint16_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 2)); - ql84_mgmt->mgmtp.u.mem.start_addr = - *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 3)); - ql84_mgmt->len = - *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 4)); - ql84_mgmt->mgmtp.u.config.id = - *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 5)); - ql84_mgmt->mgmtp.u.config.param0 = - *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 6)); - ql84_mgmt->mgmtp.u.config.param1 = - *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 7)); - ql84_mgmt->mgmtp.u.info.type = - *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 8)); - ql84_mgmt->mgmtp.u.info.context = - *((uint32_t *)(bsg_job->request->rqst_data.h_vendor.vendor_cmd + 9)); - - rsp_hdr_len = bsg_job->request_payload.payload_len; - - mn = dma_pool_alloc(ha->hw->s_dma_pool, GFP_KERNEL, &mn_dma); - if (mn == NULL) { - DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer " - "failed%lu\n", __func__, ha->host_no)); - return -ENOMEM; - } - - memset(mn, 0, sizeof (struct access_chip_84xx)); - - mn->entry_type = ACCESS_CHIP_IOCB_TYPE; - mn->entry_count = 1; - - switch (ql84_mgmt->cmd) { - case QLA84_MGMT_READ_MEM: - mn->options = cpu_to_le16(ACO_DUMP_MEMORY); - mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.mem.start_addr); - break; - case QLA84_MGMT_WRITE_MEM: - mn->options = cpu_to_le16(ACO_LOAD_MEMORY); - mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.mem.start_addr); - break; - case QLA84_MGMT_CHNG_CONFIG: - mn->options = cpu_to_le16(ACO_CHANGE_CONFIG_PARAM); - mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.config.id); - mn->parameter2 = cpu_to_le32(ql84_mgmt->mgmtp.u.config.param0); - mn->parameter3 = cpu_to_le32(ql84_mgmt->mgmtp.u.config.param1); - break; - case QLA84_MGMT_GET_INFO: - mn->options = cpu_to_le16(ACO_REQUEST_INFO); - mn->parameter1 = cpu_to_le32(ql84_mgmt->mgmtp.u.info.type); - mn->parameter2 = cpu_to_le32(ql84_mgmt->mgmtp.u.info.context); - break; - default: - ret = -EIO; - goto exit_mgmt0; - } - - if ((len == ql84_mgmt->len) && - ql84_mgmt->cmd != QLA84_MGMT_CHNG_CONFIG) { - mgmt_b = dma_alloc_coherent(&ha->hw->pdev->dev, len, - &mgmt_dma, GFP_KERNEL); - if (mgmt_b == NULL) { - DEBUG2(printk(KERN_ERR "%s: dma alloc mgmt_b " - "failed%lu\n", __func__, ha->host_no)); - ret = -ENOMEM; - goto exit_mgmt0; - } - mn->total_byte_cnt = cpu_to_le32(ql84_mgmt->len); - mn->dseg_count = cpu_to_le16(1); - mn->dseg_address[0] = cpu_to_le32(LSD(mgmt_dma)); - mn->dseg_address[1] = cpu_to_le32(MSD(mgmt_dma)); - mn->dseg_length = cpu_to_le32(len); - - if (ql84_mgmt->cmd == QLA84_MGMT_WRITE_MEM) { - memcpy(mgmt_b, ql84_mgmt->payload, len); - } - } - - ret = qla2x00_issue_iocb(ha, mn, mn_dma, 0); - if ((ret != QLA_SUCCESS) || (ql84_mgmt->cmd == QLA84_MGMT_WRITE_MEM) - || (ql84_mgmt->cmd == QLA84_MGMT_CHNG_CONFIG)) { - if (ret != QLA_SUCCESS) - DEBUG2(printk(KERN_ERR "%s(%lu): failed\n", - __func__, ha->host_no)); - } else if ((ql84_mgmt->cmd == QLA84_MGMT_READ_MEM) || - (ql84_mgmt->cmd == QLA84_MGMT_GET_INFO)) { - } - - if (mgmt_b) - dma_free_coherent(&ha->hw->pdev->dev, len, mgmt_b, mgmt_dma); - -exit_mgmt0: - dma_pool_free(ha->hw->s_dma_pool, mn, mn_dma); - return ret; -} diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c new file mode 100644 index 00000000000..b905dfe5ea6 --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -0,0 +1,1212 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2008 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#include "qla_def.h" + +#include <linux/kthread.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> + +/* BSG support for ELS/CT pass through */ +inline srb_t * +qla2x00_get_ctx_bsg_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size) +{ + srb_t *sp; + struct qla_hw_data *ha = vha->hw; + struct srb_ctx *ctx; + + sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL); + if (!sp) + goto done; + ctx = kzalloc(size, GFP_KERNEL); + if (!ctx) { + mempool_free(sp, ha->srb_mempool); + sp = NULL; + goto done; + } + + memset(sp, 0, sizeof(*sp)); + sp->fcport = fcport; + sp->ctx = ctx; +done: + return sp; +} + +int +qla24xx_fcp_prio_cfg_valid(struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag) +{ + int i, ret, num_valid; + uint8_t *bcode; + struct qla_fcp_prio_entry *pri_entry; + + ret = 1; + num_valid = 0; + bcode = (uint8_t *)pri_cfg; + + if (bcode[0x0] != 'H' || bcode[0x1] != 'Q' || bcode[0x2] != 'O' || + bcode[0x3] != 'S') { + return 0; + } + if (flag != 1) + return ret; + + pri_entry = &pri_cfg->entry[0]; + for (i = 0; i < pri_cfg->num_entries; i++) { + if (pri_entry->flags & FCP_PRIO_ENTRY_TAG_VALID) + num_valid++; + pri_entry++; + } + + if (num_valid == 0) + ret = 0; + + return ret; +} + +static int +qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int ret = 0; + uint32_t len; + uint32_t oper; + + bsg_job->reply->reply_payload_rcv_len = 0; + + if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { + ret = -EBUSY; + goto exit_fcp_prio_cfg; + } + + /* Get the sub command */ + oper = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + /* Only set config is allowed if config memory is not allocated */ + if (!ha->fcp_prio_cfg && (oper != QLFC_FCP_PRIO_SET_CONFIG)) { + ret = -EINVAL; + goto exit_fcp_prio_cfg; + } + switch (oper) { + case QLFC_FCP_PRIO_DISABLE: + if (ha->flags.fcp_prio_enabled) { + ha->flags.fcp_prio_enabled = 0; + ha->fcp_prio_cfg->attributes &= + ~FCP_PRIO_ATTR_ENABLE; + qla24xx_update_all_fcp_prio(vha); + bsg_job->reply->result = DID_OK; + } else { + ret = -EINVAL; + bsg_job->reply->result = (DID_ERROR << 16); + goto exit_fcp_prio_cfg; + } + break; + + case QLFC_FCP_PRIO_ENABLE: + if (!ha->flags.fcp_prio_enabled) { + if (ha->fcp_prio_cfg) { + ha->flags.fcp_prio_enabled = 1; + ha->fcp_prio_cfg->attributes |= + FCP_PRIO_ATTR_ENABLE; + qla24xx_update_all_fcp_prio(vha); + bsg_job->reply->result = DID_OK; + } else { + ret = -EINVAL; + bsg_job->reply->result = (DID_ERROR << 16); + goto exit_fcp_prio_cfg; + } + } + break; + + case QLFC_FCP_PRIO_GET_CONFIG: + len = bsg_job->reply_payload.payload_len; + if (!len || len > FCP_PRIO_CFG_SIZE) { + ret = -EINVAL; + bsg_job->reply->result = (DID_ERROR << 16); + goto exit_fcp_prio_cfg; + } + + bsg_job->reply->result = DID_OK; + bsg_job->reply->reply_payload_rcv_len = + sg_copy_from_buffer( + bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, ha->fcp_prio_cfg, + len); + + break; + + case QLFC_FCP_PRIO_SET_CONFIG: + len = bsg_job->request_payload.payload_len; + if (!len || len > FCP_PRIO_CFG_SIZE) { + bsg_job->reply->result = (DID_ERROR << 16); + ret = -EINVAL; + goto exit_fcp_prio_cfg; + } + + if (!ha->fcp_prio_cfg) { + ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE); + if (!ha->fcp_prio_cfg) { + qla_printk(KERN_WARNING, ha, + "Unable to allocate memory " + "for fcp prio config data (%x).\n", + FCP_PRIO_CFG_SIZE); + bsg_job->reply->result = (DID_ERROR << 16); + ret = -ENOMEM; + goto exit_fcp_prio_cfg; + } + } + + memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE); + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, ha->fcp_prio_cfg, + FCP_PRIO_CFG_SIZE); + + /* validate fcp priority data */ + if (!qla24xx_fcp_prio_cfg_valid( + (struct qla_fcp_prio_cfg *) + ha->fcp_prio_cfg, 1)) { + bsg_job->reply->result = (DID_ERROR << 16); + ret = -EINVAL; + /* If buffer was invalidatic int + * fcp_prio_cfg is of no use + */ + vfree(ha->fcp_prio_cfg); + ha->fcp_prio_cfg = NULL; + goto exit_fcp_prio_cfg; + } + + ha->flags.fcp_prio_enabled = 0; + if (ha->fcp_prio_cfg->attributes & FCP_PRIO_ATTR_ENABLE) + ha->flags.fcp_prio_enabled = 1; + qla24xx_update_all_fcp_prio(vha); + bsg_job->reply->result = DID_OK; + break; + default: + ret = -EINVAL; + break; + } +exit_fcp_prio_cfg: + bsg_job->job_done(bsg_job); + return ret; +} +static int +qla2x00_process_els(struct fc_bsg_job *bsg_job) +{ + struct fc_rport *rport; + fc_port_t *fcport; + struct Scsi_Host *host; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + srb_t *sp; + const char *type; + int req_sg_cnt, rsp_sg_cnt; + int rval = (DRIVER_ERROR << 16); + uint16_t nextlid = 0; + struct srb_ctx *els; + + /* Multiple SG's are not supported for ELS requests */ + if (bsg_job->request_payload.sg_cnt > 1 || + bsg_job->reply_payload.sg_cnt > 1) { + DEBUG2(printk(KERN_INFO + "multiple SG's are not supported for ELS requests" + " [request_sg_cnt: %x reply_sg_cnt: %x]\n", + bsg_job->request_payload.sg_cnt, + bsg_job->reply_payload.sg_cnt)); + rval = -EPERM; + goto done; + } + + /* ELS request for rport */ + if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { + rport = bsg_job->rport; + fcport = *(fc_port_t **) rport->dd_data; + host = rport_to_shost(rport); + vha = shost_priv(host); + ha = vha->hw; + type = "FC_BSG_RPT_ELS"; + + /* make sure the rport is logged in, + * if not perform fabric login + */ + if (qla2x00_fabric_login(vha, fcport, &nextlid)) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "failed to login port %06X for ELS passthru\n", + fcport->d_id.b24)); + rval = -EIO; + goto done; + } + } else { + host = bsg_job->shost; + vha = shost_priv(host); + ha = vha->hw; + type = "FC_BSG_HST_ELS_NOLOGIN"; + + /* Allocate a dummy fcport structure, since functions + * preparing the IOCB and mailbox command retrieves port + * specific information from fcport structure. For Host based + * ELS commands there will be no fcport structure allocated + */ + fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); + if (!fcport) { + rval = -ENOMEM; + goto done; + } + + /* Initialize all required fields of fcport */ + fcport->vha = vha; + fcport->vp_idx = vha->vp_idx; + fcport->d_id.b.al_pa = + bsg_job->request->rqst_data.h_els.port_id[0]; + fcport->d_id.b.area = + bsg_job->request->rqst_data.h_els.port_id[1]; + fcport->d_id.b.domain = + bsg_job->request->rqst_data.h_els.port_id[2]; + fcport->loop_id = + (fcport->d_id.b.al_pa == 0xFD) ? + NPH_FABRIC_CONTROLLER : NPH_F_PORT; + } + + if (!vha->flags.online) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "host not online\n")); + rval = -EIO; + goto done; + } + + req_sg_cnt = + dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + if (!req_sg_cnt) { + rval = -ENOMEM; + goto done_free_fcport; + } + + rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + if (!rsp_sg_cnt) { + rval = -ENOMEM; + goto done_free_fcport; + } + + if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || + (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { + DEBUG2(printk(KERN_INFO + "dma mapping resulted in different sg counts \ + [request_sg_cnt: %x dma_request_sg_cnt: %x\ + reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n", + bsg_job->request_payload.sg_cnt, req_sg_cnt, + bsg_job->reply_payload.sg_cnt, rsp_sg_cnt)); + rval = -EAGAIN; + goto done_unmap_sg; + } + + /* Alloc SRB structure */ + sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx)); + if (!sp) { + rval = -ENOMEM; + goto done_unmap_sg; + } + + els = sp->ctx; + els->type = + (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? + SRB_ELS_CMD_RPT : SRB_ELS_CMD_HST); + els->name = + (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? + "bsg_els_rpt" : "bsg_els_hst"); + els->u.bsg_job = bsg_job; + + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x " + "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type, + bsg_job->request->rqst_data.h_els.command_code, + fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa)); + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) { + kfree(sp->ctx); + mempool_free(sp, ha->srb_mempool); + rval = -EIO; + goto done_unmap_sg; + } + return rval; + +done_unmap_sg: + dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + goto done_free_fcport; + +done_free_fcport: + if (bsg_job->request->msgcode == FC_BSG_HST_ELS_NOLOGIN) + kfree(fcport); +done: + return rval; +} + +static int +qla2x00_process_ct(struct fc_bsg_job *bsg_job) +{ + srb_t *sp; + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = (DRIVER_ERROR << 16); + int req_sg_cnt, rsp_sg_cnt; + uint16_t loop_id; + struct fc_port *fcport; + char *type = "FC_BSG_HST_CT"; + struct srb_ctx *ct; + + /* pass through is supported only for ISP 4Gb or higher */ + if (!IS_FWI2_CAPABLE(ha)) { + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld):Firmware is not capable to support FC " + "CT pass thru\n", vha->host_no)); + rval = -EPERM; + goto done; + } + + req_sg_cnt = + dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + if (!req_sg_cnt) { + rval = -ENOMEM; + goto done; + } + + rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + if (!rsp_sg_cnt) { + rval = -ENOMEM; + goto done; + } + + if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || + (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "[request_sg_cnt: %x dma_request_sg_cnt: %x\ + reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n", + bsg_job->request_payload.sg_cnt, req_sg_cnt, + bsg_job->reply_payload.sg_cnt, rsp_sg_cnt)); + rval = -EAGAIN; + goto done_unmap_sg; + } + + if (!vha->flags.online) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "host not online\n")); + rval = -EIO; + goto done_unmap_sg; + } + + loop_id = + (bsg_job->request->rqst_data.h_ct.preamble_word1 & 0xFF000000) + >> 24; + switch (loop_id) { + case 0xFC: + loop_id = cpu_to_le16(NPH_SNS); + break; + case 0xFA: + loop_id = vha->mgmt_svr_loop_id; + break; + default: + DEBUG2(qla_printk(KERN_INFO, ha, + "Unknown loop id: %x\n", loop_id)); + rval = -EINVAL; + goto done_unmap_sg; + } + + /* Allocate a dummy fcport structure, since functions preparing the + * IOCB and mailbox command retrieves port specific information + * from fcport structure. For Host based ELS commands there will be + * no fcport structure allocated + */ + fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); + if (!fcport) { + rval = -ENOMEM; + goto done_unmap_sg; + } + + /* Initialize all required fields of fcport */ + fcport->vha = vha; + fcport->vp_idx = vha->vp_idx; + fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_ct.port_id[0]; + fcport->d_id.b.area = bsg_job->request->rqst_data.h_ct.port_id[1]; + fcport->d_id.b.domain = bsg_job->request->rqst_data.h_ct.port_id[2]; + fcport->loop_id = loop_id; + + /* Alloc SRB structure */ + sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx)); + if (!sp) { + rval = -ENOMEM; + goto done_free_fcport; + } + + ct = sp->ctx; + ct->type = SRB_CT_CMD; + ct->name = "bsg_ct"; + ct->u.bsg_job = bsg_job; + + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x " + "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, type, + (bsg_job->request->rqst_data.h_ct.preamble_word2 >> 16), + fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa)); + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) { + kfree(sp->ctx); + mempool_free(sp, ha->srb_mempool); + rval = -EIO; + goto done_free_fcport; + } + return rval; + +done_free_fcport: + kfree(fcport); +done_unmap_sg: + dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); +done: + return rval; +} + +static int +qla2x00_process_loopback(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval; + uint8_t command_sent; + char *type; + struct msg_echo_lb elreq; + uint16_t response[MAILBOX_REGISTER_COUNT]; + uint8_t *fw_sts_ptr; + uint8_t *req_data = NULL; + dma_addr_t req_data_dma; + uint32_t req_data_len; + uint8_t *rsp_data = NULL; + dma_addr_t rsp_data_dma; + uint32_t rsp_data_len; + + if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) + return -EBUSY; + + if (!vha->flags.online) { + DEBUG2(qla_printk(KERN_WARNING, ha, "host not online\n")); + return -EIO; + } + + elreq.req_sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, + DMA_TO_DEVICE); + + if (!elreq.req_sg_cnt) + return -ENOMEM; + + elreq.rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, + DMA_FROM_DEVICE); + + if (!elreq.rsp_sg_cnt) { + rval = -ENOMEM; + goto done_unmap_req_sg; + } + + if ((elreq.req_sg_cnt != bsg_job->request_payload.sg_cnt) || + (elreq.rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { + DEBUG2(printk(KERN_INFO + "dma mapping resulted in different sg counts " + "[request_sg_cnt: %x dma_request_sg_cnt: %x " + "reply_sg_cnt: %x dma_reply_sg_cnt: %x]\n", + bsg_job->request_payload.sg_cnt, elreq.req_sg_cnt, + bsg_job->reply_payload.sg_cnt, elreq.rsp_sg_cnt)); + rval = -EAGAIN; + goto done_unmap_sg; + } + req_data_len = rsp_data_len = bsg_job->request_payload.payload_len; + req_data = dma_alloc_coherent(&ha->pdev->dev, req_data_len, + &req_data_dma, GFP_KERNEL); + if (!req_data) { + DEBUG2(printk(KERN_ERR "%s: dma alloc for req_data " + "failed for host=%lu\n", __func__, vha->host_no)); + rval = -ENOMEM; + goto done_unmap_sg; + } + + rsp_data = dma_alloc_coherent(&ha->pdev->dev, rsp_data_len, + &rsp_data_dma, GFP_KERNEL); + if (!rsp_data) { + DEBUG2(printk(KERN_ERR "%s: dma alloc for rsp_data " + "failed for host=%lu\n", __func__, vha->host_no)); + rval = -ENOMEM; + goto done_free_dma_req; + } + + /* Copy the request buffer in req_data now */ + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, req_data, req_data_len); + + elreq.send_dma = req_data_dma; + elreq.rcv_dma = rsp_data_dma; + elreq.transfer_size = req_data_len; + + elreq.options = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + if (ha->current_topology != ISP_CFG_F) { + type = "FC_BSG_HST_VENDOR_LOOPBACK"; + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld) bsg rqst type: %s\n", + vha->host_no, type)); + + command_sent = INT_DEF_LB_LOOPBACK_CMD; + rval = qla2x00_loopback_test(vha, &elreq, response); + if (IS_QLA81XX(ha)) { + if (response[0] == MBS_COMMAND_ERROR && + response[1] == MBS_LB_RESET) { + DEBUG2(printk(KERN_ERR "%s(%ld): ABORTing " + "ISP\n", __func__, vha->host_no)); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } + } + } else { + type = "FC_BSG_HST_VENDOR_ECHO_DIAG"; + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld) bsg rqst type: %s\n", vha->host_no, type)); + command_sent = INT_DEF_LB_ECHO_CMD; + rval = qla2x00_echo_test(vha, &elreq, response); + } + + if (rval) { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " + "request %s failed\n", vha->host_no, type)); + + fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) + + sizeof(struct fc_bsg_reply); + + memcpy(fw_sts_ptr, response, sizeof(response)); + fw_sts_ptr += sizeof(response); + *fw_sts_ptr = command_sent; + rval = 0; + bsg_job->reply->reply_payload_rcv_len = 0; + bsg_job->reply->result = (DID_ERROR << 16); + } else { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " + "request %s completed\n", vha->host_no, type)); + + bsg_job->reply_len = sizeof(struct fc_bsg_reply) + + sizeof(response) + sizeof(uint8_t); + bsg_job->reply->reply_payload_rcv_len = + bsg_job->reply_payload.payload_len; + fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) + + sizeof(struct fc_bsg_reply); + memcpy(fw_sts_ptr, response, sizeof(response)); + fw_sts_ptr += sizeof(response); + *fw_sts_ptr = command_sent; + bsg_job->reply->result = DID_OK; + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, rsp_data, + rsp_data_len); + } + bsg_job->job_done(bsg_job); + + dma_free_coherent(&ha->pdev->dev, rsp_data_len, + rsp_data, rsp_data_dma); +done_free_dma_req: + dma_free_coherent(&ha->pdev->dev, req_data_len, + req_data, req_data_dma); +done_unmap_sg: + dma_unmap_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); +done_unmap_req_sg: + dma_unmap_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + return rval; +} + +static int +qla84xx_reset(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = 0; + uint32_t flag; + + if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) + return -EBUSY; + + if (!IS_QLA84XX(ha)) { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " + "exiting.\n", vha->host_no)); + return -EINVAL; + } + + flag = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW); + + if (rval) { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " + "request 84xx reset failed\n", vha->host_no)); + rval = bsg_job->reply->reply_payload_rcv_len = 0; + bsg_job->reply->result = (DID_ERROR << 16); + + } else { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " + "request 84xx reset completed\n", vha->host_no)); + bsg_job->reply->result = DID_OK; + } + + bsg_job->job_done(bsg_job); + return rval; +} + +static int +qla84xx_updatefw(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + struct verify_chip_entry_84xx *mn = NULL; + dma_addr_t mn_dma, fw_dma; + void *fw_buf = NULL; + int rval = 0; + uint32_t sg_cnt; + uint32_t data_len; + uint16_t options; + uint32_t flag; + uint32_t fw_ver; + + if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) + return -EBUSY; + + if (!IS_QLA84XX(ha)) { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " + "exiting.\n", vha->host_no)); + return -EINVAL; + } + + sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + if (!sg_cnt) + return -ENOMEM; + + if (sg_cnt != bsg_job->request_payload.sg_cnt) { + DEBUG2(printk(KERN_INFO + "dma mapping resulted in different sg counts " + "request_sg_cnt: %x dma_request_sg_cnt: %x ", + bsg_job->request_payload.sg_cnt, sg_cnt)); + rval = -EAGAIN; + goto done_unmap_sg; + } + + data_len = bsg_job->request_payload.payload_len; + fw_buf = dma_alloc_coherent(&ha->pdev->dev, data_len, + &fw_dma, GFP_KERNEL); + if (!fw_buf) { + DEBUG2(printk(KERN_ERR "%s: dma alloc for fw_buf " + "failed for host=%lu\n", __func__, vha->host_no)); + rval = -ENOMEM; + goto done_unmap_sg; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, fw_buf, data_len); + + mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); + if (!mn) { + DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer " + "failed for host=%lu\n", __func__, vha->host_no)); + rval = -ENOMEM; + goto done_free_fw_buf; + } + + flag = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + fw_ver = le32_to_cpu(*((uint32_t *)((uint32_t *)fw_buf + 2))); + + memset(mn, 0, sizeof(struct access_chip_84xx)); + mn->entry_type = VERIFY_CHIP_IOCB_TYPE; + mn->entry_count = 1; + + options = VCO_FORCE_UPDATE | VCO_END_OF_DATA; + if (flag == A84_ISSUE_UPDATE_DIAGFW_CMD) + options |= VCO_DIAG_FW; + + mn->options = cpu_to_le16(options); + mn->fw_ver = cpu_to_le32(fw_ver); + mn->fw_size = cpu_to_le32(data_len); + mn->fw_seq_size = cpu_to_le32(data_len); + mn->dseg_address[0] = cpu_to_le32(LSD(fw_dma)); + mn->dseg_address[1] = cpu_to_le32(MSD(fw_dma)); + mn->dseg_length = cpu_to_le32(data_len); + mn->data_seg_cnt = cpu_to_le16(1); + + rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120); + + if (rval) { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " + "request 84xx updatefw failed\n", vha->host_no)); + + rval = bsg_job->reply->reply_payload_rcv_len = 0; + bsg_job->reply->result = (DID_ERROR << 16); + + } else { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " + "request 84xx updatefw completed\n", vha->host_no)); + + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->result = DID_OK; + } + + bsg_job->job_done(bsg_job); + dma_pool_free(ha->s_dma_pool, mn, mn_dma); + +done_free_fw_buf: + dma_free_coherent(&ha->pdev->dev, data_len, fw_buf, fw_dma); + +done_unmap_sg: + dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + + return rval; +} + +static int +qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + struct access_chip_84xx *mn = NULL; + dma_addr_t mn_dma, mgmt_dma; + void *mgmt_b = NULL; + int rval = 0; + struct qla_bsg_a84_mgmt *ql84_mgmt; + uint32_t sg_cnt; + uint32_t data_len = 0; + uint32_t dma_direction = DMA_NONE; + + if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) + return -EBUSY; + + if (!IS_QLA84XX(ha)) { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " + "exiting.\n", vha->host_no)); + return -EINVAL; + } + + ql84_mgmt = (struct qla_bsg_a84_mgmt *)((char *)bsg_job->request + + sizeof(struct fc_bsg_request)); + if (!ql84_mgmt) { + DEBUG2(printk("%s(%ld): mgmt header not provided, exiting.\n", + __func__, vha->host_no)); + return -EINVAL; + } + + mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); + if (!mn) { + DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer " + "failed for host=%lu\n", __func__, vha->host_no)); + return -ENOMEM; + } + + memset(mn, 0, sizeof(struct access_chip_84xx)); + mn->entry_type = ACCESS_CHIP_IOCB_TYPE; + mn->entry_count = 1; + + switch (ql84_mgmt->mgmt.cmd) { + case QLA84_MGMT_READ_MEM: + case QLA84_MGMT_GET_INFO: + sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + if (!sg_cnt) { + rval = -ENOMEM; + goto exit_mgmt; + } + + dma_direction = DMA_FROM_DEVICE; + + if (sg_cnt != bsg_job->reply_payload.sg_cnt) { + DEBUG2(printk(KERN_INFO + "dma mapping resulted in different sg counts " + "reply_sg_cnt: %x dma_reply_sg_cnt: %x\n", + bsg_job->reply_payload.sg_cnt, sg_cnt)); + rval = -EAGAIN; + goto done_unmap_sg; + } + + data_len = bsg_job->reply_payload.payload_len; + + mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len, + &mgmt_dma, GFP_KERNEL); + if (!mgmt_b) { + DEBUG2(printk(KERN_ERR "%s: dma alloc for mgmt_b " + "failed for host=%lu\n", + __func__, vha->host_no)); + rval = -ENOMEM; + goto done_unmap_sg; + } + + if (ql84_mgmt->mgmt.cmd == QLA84_MGMT_READ_MEM) { + mn->options = cpu_to_le16(ACO_DUMP_MEMORY); + mn->parameter1 = + cpu_to_le32( + ql84_mgmt->mgmt.mgmtp.u.mem.start_addr); + + } else if (ql84_mgmt->mgmt.cmd == QLA84_MGMT_GET_INFO) { + mn->options = cpu_to_le16(ACO_REQUEST_INFO); + mn->parameter1 = + cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.info.type); + + mn->parameter2 = + cpu_to_le32( + ql84_mgmt->mgmt.mgmtp.u.info.context); + } + break; + + case QLA84_MGMT_WRITE_MEM: + sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + + if (!sg_cnt) { + rval = -ENOMEM; + goto exit_mgmt; + } + + dma_direction = DMA_TO_DEVICE; + + if (sg_cnt != bsg_job->request_payload.sg_cnt) { + DEBUG2(printk(KERN_INFO + "dma mapping resulted in different sg counts " + "request_sg_cnt: %x dma_request_sg_cnt: %x ", + bsg_job->request_payload.sg_cnt, sg_cnt)); + rval = -EAGAIN; + goto done_unmap_sg; + } + + data_len = bsg_job->request_payload.payload_len; + mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len, + &mgmt_dma, GFP_KERNEL); + if (!mgmt_b) { + DEBUG2(printk(KERN_ERR "%s: dma alloc for mgmt_b " + "failed for host=%lu\n", + __func__, vha->host_no)); + rval = -ENOMEM; + goto done_unmap_sg; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, mgmt_b, data_len); + + mn->options = cpu_to_le16(ACO_LOAD_MEMORY); + mn->parameter1 = + cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.mem.start_addr); + break; + + case QLA84_MGMT_CHNG_CONFIG: + mn->options = cpu_to_le16(ACO_CHANGE_CONFIG_PARAM); + mn->parameter1 = + cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.id); + + mn->parameter2 = + cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.param0); + + mn->parameter3 = + cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.param1); + break; + + default: + rval = -EIO; + goto exit_mgmt; + } + + if (ql84_mgmt->mgmt.cmd != QLA84_MGMT_CHNG_CONFIG) { + mn->total_byte_cnt = cpu_to_le32(ql84_mgmt->mgmt.len); + mn->dseg_count = cpu_to_le16(1); + mn->dseg_address[0] = cpu_to_le32(LSD(mgmt_dma)); + mn->dseg_address[1] = cpu_to_le32(MSD(mgmt_dma)); + mn->dseg_length = cpu_to_le32(ql84_mgmt->mgmt.len); + } + + rval = qla2x00_issue_iocb(vha, mn, mn_dma, 0); + + if (rval) { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " + "request 84xx mgmt failed\n", vha->host_no)); + + rval = bsg_job->reply->reply_payload_rcv_len = 0; + bsg_job->reply->result = (DID_ERROR << 16); + + } else { + DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " + "request 84xx mgmt completed\n", vha->host_no)); + + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->result = DID_OK; + + if ((ql84_mgmt->mgmt.cmd == QLA84_MGMT_READ_MEM) || + (ql84_mgmt->mgmt.cmd == QLA84_MGMT_GET_INFO)) { + bsg_job->reply->reply_payload_rcv_len = + bsg_job->reply_payload.payload_len; + + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, mgmt_b, + data_len); + } + } + + bsg_job->job_done(bsg_job); + +done_unmap_sg: + if (mgmt_b) + dma_free_coherent(&ha->pdev->dev, data_len, mgmt_b, mgmt_dma); + + if (dma_direction == DMA_TO_DEVICE) + dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + else if (dma_direction == DMA_FROM_DEVICE) + dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + +exit_mgmt: + dma_pool_free(ha->s_dma_pool, mn, mn_dma); + + return rval; +} + +static int +qla24xx_iidma(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = 0; + struct qla_port_param *port_param = NULL; + fc_port_t *fcport = NULL; + uint16_t mb[MAILBOX_REGISTER_COUNT]; + uint8_t *rsp_ptr = NULL; + + bsg_job->reply->reply_payload_rcv_len = 0; + + if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) + return -EBUSY; + + if (!IS_IIDMA_CAPABLE(vha->hw)) { + DEBUG2(qla_printk(KERN_WARNING, ha, "%s(%lu): iiDMA not " + "supported\n", __func__, vha->host_no)); + return -EINVAL; + } + + port_param = (struct qla_port_param *)((char *)bsg_job->request + + sizeof(struct fc_bsg_request)); + if (!port_param) { + DEBUG2(printk("%s(%ld): port_param header not provided, " + "exiting.\n", __func__, vha->host_no)); + return -EINVAL; + } + + if (port_param->fc_scsi_addr.dest_type != EXT_DEF_TYPE_WWPN) { + DEBUG2(printk(KERN_ERR "%s(%ld): Invalid destination type\n", + __func__, vha->host_no)); + return -EINVAL; + } + + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (fcport->port_type != FCT_TARGET) + continue; + + if (memcmp(port_param->fc_scsi_addr.dest_addr.wwpn, + fcport->port_name, sizeof(fcport->port_name))) + continue; + break; + } + + if (!fcport) { + DEBUG2(printk(KERN_ERR "%s(%ld): Failed to find port\n", + __func__, vha->host_no)); + return -EINVAL; + } + + if (port_param->mode) + rval = qla2x00_set_idma_speed(vha, fcport->loop_id, + port_param->speed, mb); + else + rval = qla2x00_get_idma_speed(vha, fcport->loop_id, + &port_param->speed, mb); + + if (rval) { + DEBUG16(printk(KERN_ERR "scsi(%ld): iIDMA cmd failed for " + "%02x%02x%02x%02x%02x%02x%02x%02x -- " + "%04x %x %04x %04x.\n", + vha->host_no, fcport->port_name[0], + fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7], rval, + fcport->fp_speed, mb[0], mb[1])); + rval = 0; + bsg_job->reply->result = (DID_ERROR << 16); + + } else { + if (!port_param->mode) { + bsg_job->reply_len = sizeof(struct fc_bsg_reply) + + sizeof(struct qla_port_param); + + rsp_ptr = ((uint8_t *)bsg_job->reply) + + sizeof(struct fc_bsg_reply); + + memcpy(rsp_ptr, port_param, + sizeof(struct qla_port_param)); + } + + bsg_job->reply->result = DID_OK; + } + + bsg_job->job_done(bsg_job); + return rval; +} + +static int +qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job) +{ + switch (bsg_job->request->rqst_data.h_vendor.vendor_cmd[0]) { + case QL_VND_LOOPBACK: + return qla2x00_process_loopback(bsg_job); + + case QL_VND_A84_RESET: + return qla84xx_reset(bsg_job); + + case QL_VND_A84_UPDATE_FW: + return qla84xx_updatefw(bsg_job); + + case QL_VND_A84_MGMT_CMD: + return qla84xx_mgmt_cmd(bsg_job); + + case QL_VND_IIDMA: + return qla24xx_iidma(bsg_job); + + case QL_VND_FCP_PRIO_CFG_CMD: + return qla24xx_proc_fcp_prio_cfg_cmd(bsg_job); + + default: + bsg_job->reply->result = (DID_ERROR << 16); + bsg_job->job_done(bsg_job); + return -ENOSYS; + } +} + +int +qla24xx_bsg_request(struct fc_bsg_job *bsg_job) +{ + int ret = -EINVAL; + + switch (bsg_job->request->msgcode) { + case FC_BSG_RPT_ELS: + case FC_BSG_HST_ELS_NOLOGIN: + ret = qla2x00_process_els(bsg_job); + break; + case FC_BSG_HST_CT: + ret = qla2x00_process_ct(bsg_job); + break; + case FC_BSG_HST_VENDOR: + ret = qla2x00_process_vendor_specific(bsg_job); + break; + case FC_BSG_HST_ADD_RPORT: + case FC_BSG_HST_DEL_RPORT: + case FC_BSG_RPT_CT: + default: + DEBUG2(printk("qla2xxx: unsupported BSG request\n")); + break; + } + return ret; +} + +int +qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job) +{ + scsi_qla_host_t *vha = shost_priv(bsg_job->shost); + struct qla_hw_data *ha = vha->hw; + srb_t *sp; + int cnt, que; + unsigned long flags; + struct req_que *req; + struct srb_ctx *sp_bsg; + + /* find the bsg job from the active list of commands */ + spin_lock_irqsave(&ha->hardware_lock, flags); + for (que = 0; que < ha->max_req_queues; que++) { + req = ha->req_q_map[que]; + if (!req) + continue; + + for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + sp = req->outstanding_cmds[cnt]; + if (sp) { + sp_bsg = sp->ctx; + + if (((sp_bsg->type == SRB_CT_CMD) || + (sp_bsg->type == SRB_ELS_CMD_HST)) + && (sp_bsg->u.bsg_job == bsg_job)) { + if (ha->isp_ops->abort_command(sp)) { + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld): mbx " + "abort_command failed\n", + vha->host_no)); + bsg_job->req->errors = + bsg_job->reply->result = -EIO; + } else { + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld): mbx " + "abort_command success\n", + vha->host_no)); + bsg_job->req->errors = + bsg_job->reply->result = 0; + } + goto done; + } + } + } + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld) SRB not found to abort\n", vha->host_no)); + bsg_job->req->errors = bsg_job->reply->result = -ENXIO; + return 0; + +done: + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (bsg_job->request->msgcode == FC_BSG_HST_CT) + kfree(sp->fcport); + kfree(sp->ctx); + mempool_free(sp, ha->srb_mempool); + return 0; +} diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h new file mode 100644 index 00000000000..76ed92dd2ef --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_bsg.h @@ -0,0 +1,135 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2008 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#ifndef __QLA_BSG_H +#define __QLA_BSG_H + +/* BSG Vendor specific commands */ +#define QL_VND_LOOPBACK 0x01 +#define QL_VND_A84_RESET 0x02 +#define QL_VND_A84_UPDATE_FW 0x03 +#define QL_VND_A84_MGMT_CMD 0x04 +#define QL_VND_IIDMA 0x05 +#define QL_VND_FCP_PRIO_CFG_CMD 0x06 + +/* BSG definations for interpreting CommandSent field */ +#define INT_DEF_LB_LOOPBACK_CMD 0 +#define INT_DEF_LB_ECHO_CMD 1 + +/* BSG Vendor specific definations */ +#define A84_ISSUE_WRITE_TYPE_CMD 0 +#define A84_ISSUE_READ_TYPE_CMD 1 +#define A84_CLEANUP_CMD 2 +#define A84_ISSUE_RESET_OP_FW 3 +#define A84_ISSUE_RESET_DIAG_FW 4 +#define A84_ISSUE_UPDATE_OPFW_CMD 5 +#define A84_ISSUE_UPDATE_DIAGFW_CMD 6 + +struct qla84_mgmt_param { + union { + struct { + uint32_t start_addr; + } mem; /* for QLA84_MGMT_READ/WRITE_MEM */ + struct { + uint32_t id; +#define QLA84_MGMT_CONFIG_ID_UIF 1 +#define QLA84_MGMT_CONFIG_ID_FCOE_COS 2 +#define QLA84_MGMT_CONFIG_ID_PAUSE 3 +#define QLA84_MGMT_CONFIG_ID_TIMEOUTS 4 + + uint32_t param0; + uint32_t param1; + } config; /* for QLA84_MGMT_CHNG_CONFIG */ + + struct { + uint32_t type; +#define QLA84_MGMT_INFO_CONFIG_LOG_DATA 1 /* Get Config Log Data */ +#define QLA84_MGMT_INFO_LOG_DATA 2 /* Get Log Data */ +#define QLA84_MGMT_INFO_PORT_STAT 3 /* Get Port Statistics */ +#define QLA84_MGMT_INFO_LIF_STAT 4 /* Get LIF Statistics */ +#define QLA84_MGMT_INFO_ASIC_STAT 5 /* Get ASIC Statistics */ +#define QLA84_MGMT_INFO_CONFIG_PARAMS 6 /* Get Config Parameters */ +#define QLA84_MGMT_INFO_PANIC_LOG 7 /* Get Panic Log */ + + uint32_t context; +/* +* context definitions for QLA84_MGMT_INFO_CONFIG_LOG_DATA +*/ +#define IC_LOG_DATA_LOG_ID_DEBUG_LOG 0 +#define IC_LOG_DATA_LOG_ID_LEARN_LOG 1 +#define IC_LOG_DATA_LOG_ID_FC_ACL_INGRESS_LOG 2 +#define IC_LOG_DATA_LOG_ID_FC_ACL_EGRESS_LOG 3 +#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_INGRESS_LOG 4 +#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_EGRESS_LOG 5 +#define IC_LOG_DATA_LOG_ID_MESSAGE_TRANSMIT_LOG 6 +#define IC_LOG_DATA_LOG_ID_MESSAGE_RECEIVE_LOG 7 +#define IC_LOG_DATA_LOG_ID_LINK_EVENT_LOG 8 +#define IC_LOG_DATA_LOG_ID_DCX_LOG 9 + +/* +* context definitions for QLA84_MGMT_INFO_PORT_STAT +*/ +#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT0 0 +#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT1 1 +#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT0 2 +#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT1 3 +#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT0 4 +#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT1 5 + + +/* +* context definitions for QLA84_MGMT_INFO_LIF_STAT +*/ +#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT0 0 +#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT1 1 +#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT0 2 +#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT1 3 +#define IC_LIF_STATISTICS_LIF_NUMBER_CPU 6 + + } info; /* for QLA84_MGMT_GET_INFO */ + } u; +}; + +struct qla84_msg_mgmt { + uint16_t cmd; +#define QLA84_MGMT_READ_MEM 0x00 +#define QLA84_MGMT_WRITE_MEM 0x01 +#define QLA84_MGMT_CHNG_CONFIG 0x02 +#define QLA84_MGMT_GET_INFO 0x03 + uint16_t rsrvd; + struct qla84_mgmt_param mgmtp;/* parameters for cmd */ + uint32_t len; /* bytes in payload following this struct */ + uint8_t payload[0]; /* payload for cmd */ +}; + +struct qla_bsg_a84_mgmt { + struct qla84_msg_mgmt mgmt; +} __attribute__ ((packed)); + +struct qla_scsi_addr { + uint16_t bus; + uint16_t target; +} __attribute__ ((packed)); + +struct qla_ext_dest_addr { + union { + uint8_t wwnn[8]; + uint8_t wwpn[8]; + uint8_t id[4]; + struct qla_scsi_addr scsi_addr; + } dest_addr; + uint16_t dest_type; +#define EXT_DEF_TYPE_WWPN 2 + uint16_t lun; + uint16_t padding[2]; +} __attribute__ ((packed)); + +struct qla_port_param { + struct qla_ext_dest_addr fc_scsi_addr; + uint16_t mode; + uint16_t speed; +} __attribute__ ((packed)); +#endif diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index cb2eca4c26d..2afc8a362f2 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -769,6 +769,9 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) void *nxt; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); + if (IS_QLA82XX(ha)) + return; + risc_address = ext_mem_cnt = 0; flags = 0; @@ -1660,4 +1663,62 @@ qla2x00_dump_buffer(uint8_t * b, uint32_t size) printk("\n"); } +void +qla2x00_dump_buffer_zipped(uint8_t *b, uint32_t size) +{ + uint32_t cnt; + uint8_t c; + uint8_t last16[16], cur16[16]; + uint32_t lc = 0, num_same16 = 0, j; + + printk(KERN_DEBUG " 0 1 2 3 4 5 6 7 8 9 " + "Ah Bh Ch Dh Eh Fh\n"); + printk(KERN_DEBUG "----------------------------------------" + "----------------------\n"); + + for (cnt = 0; cnt < size;) { + c = *b++; + cur16[lc++] = c; + + cnt++; + if (cnt % 16) + continue; + + /* We have 16 now */ + lc = 0; + if (num_same16 == 0) { + memcpy(last16, cur16, 16); + num_same16++; + continue; + } + if (memcmp(cur16, last16, 16) == 0) { + num_same16++; + continue; + } + for (j = 0; j < 16; j++) + printk(KERN_DEBUG "%02x ", (uint32_t)last16[j]); + printk(KERN_DEBUG "\n"); + + if (num_same16 > 1) + printk(KERN_DEBUG "> prev pattern repeats (%u)" + "more times\n", num_same16-1); + memcpy(last16, cur16, 16); + num_same16 = 1; + } + + if (num_same16) { + for (j = 0; j < 16; j++) + printk(KERN_DEBUG "%02x ", (uint32_t)last16[j]); + printk(KERN_DEBUG "\n"); + + if (num_same16 > 1) + printk(KERN_DEBUG "> prev pattern repeats (%u)" + "more times\n", num_same16-1); + } + if (lc) { + for (j = 0; j < lc; j++) + printk(KERN_DEBUG "%02x ", (uint32_t)cur16[j]); + printk(KERN_DEBUG "\n"); + } +} diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index d6d9c86cb05..916c81f3f55 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -27,6 +27,9 @@ /* #define QL_DEBUG_LEVEL_15 */ /* Output NPIV trace msgs */ /* #define QL_DEBUG_LEVEL_16 */ /* Output ISP84XX trace msgs */ /* #define QL_DEBUG_LEVEL_17 */ /* Output EEH trace messages */ +/* #define QL_DEBUG_LEVEL_18 */ /* Output T10 CRC trace messages */ + +/* #define QL_PRINTK_BUF */ /* Captures printk to buffer */ /* * Macros use for debugging the driver. @@ -139,6 +142,13 @@ #define DEBUG17(x) do {} while (0) #endif +#if defined(QL_DEBUG_LEVEL_18) +#define DEBUG18(x) do {if (ql2xextended_error_logging) x; } while (0) +#else +#define DEBUG18(x) do {} while (0) +#endif + + /* * Firmware Dump structure definition */ diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index afa95614aaf..83961090901 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -33,7 +33,10 @@ #include <scsi/scsi_transport_fc.h> #include <scsi/scsi_bsg_fc.h> -#define QLA2XXX_DRIVER_NAME "qla2xxx" +#include "qla_bsg.h" +#include "qla_nx.h" +#define QLA2XXX_DRIVER_NAME "qla2xxx" +#define QLA2XXX_APIDEV "ql2xapidev" /* * We have MAILBOX_REGISTER_COUNT sized arrays in a few places, @@ -186,6 +189,16 @@ struct req_que; /* + * (sd.h is not exported, hence local inclusion) + * Data Integrity Field tuple. + */ +struct sd_dif_tuple { + __be16 guard_tag; /* Checksum */ + __be16 app_tag; /* Opaque storage */ + __be32 ref_tag; /* Target LBA or indirect LBA */ +}; + +/* * SCSI Request Block */ typedef struct srb { @@ -205,40 +218,73 @@ typedef struct srb { /* * SRB flag definitions */ -#define SRB_DMA_VALID BIT_0 /* Command sent to ISP */ +#define SRB_DMA_VALID BIT_0 /* Command sent to ISP */ +#define SRB_FCP_CMND_DMA_VALID BIT_12 /* DIF: DSD List valid */ +#define SRB_CRC_CTX_DMA_VALID BIT_2 /* DIF: context DMA valid */ +#define SRB_CRC_PROT_DMA_VALID BIT_4 /* DIF: prot DMA valid */ +#define SRB_CRC_CTX_DSD_VALID BIT_5 /* DIF: dsd_list valid */ + +/* To identify if a srb is of T10-CRC type. @sp => srb_t pointer */ +#define IS_PROT_IO(sp) (sp->flags & SRB_CRC_CTX_DSD_VALID) /* * SRB extensions. */ -struct srb_ctx { -#define SRB_LOGIN_CMD 1 -#define SRB_LOGOUT_CMD 2 - uint16_t type; - struct timer_list timer; - - void (*free)(srb_t *sp); - void (*timeout)(srb_t *sp); -}; - -struct srb_logio { - struct srb_ctx ctx; - +struct srb_iocb { + union { + struct { + uint16_t flags; #define SRB_LOGIN_RETRIED BIT_0 #define SRB_LOGIN_COND_PLOGI BIT_1 #define SRB_LOGIN_SKIP_PRLI BIT_2 - uint16_t flags; + uint16_t data[2]; + } logio; + struct { + /* + * Values for flags field below are as + * defined in tsk_mgmt_entry struct + * for control_flags field in qla_fw.h. + */ + uint32_t flags; + uint32_t lun; + uint32_t data; + } tmf; + struct { + /* + * values for modif field below are as + * defined in mrk_entry_24xx struct + * for the modifier field in qla_fw.h. + */ + uint8_t modif; + uint16_t lun; + uint32_t data; + } marker; + } u; + + struct timer_list timer; + + void (*done)(srb_t *); + void (*free)(srb_t *); + void (*timeout)(srb_t *); }; -struct srb_bsg_ctx { +/* Values for srb_ctx type */ +#define SRB_LOGIN_CMD 1 +#define SRB_LOGOUT_CMD 2 #define SRB_ELS_CMD_RPT 3 #define SRB_ELS_CMD_HST 4 -#define SRB_CT_CMD 5 - uint16_t type; -}; +#define SRB_CT_CMD 5 +#define SRB_ADISC_CMD 6 +#define SRB_TM_CMD 7 +#define SRB_MARKER_CMD 8 -struct srb_bsg { - struct srb_bsg_ctx ctx; - struct fc_bsg_job *bsg_job; +struct srb_ctx { + uint16_t type; + char *name; + union { + struct srb_iocb *iocb_cmd; + struct fc_bsg_job *bsg_job; + } u; }; struct msg_echo_lb { @@ -416,6 +462,7 @@ typedef union { struct device_reg_2xxx isp; struct device_reg_24xx isp24; struct device_reg_25xxmq isp25mq; + struct device_reg_82xx isp82; } device_reg_t; #define ISP_REQ_Q_IN(ha, reg) \ @@ -1299,6 +1346,66 @@ typedef struct { uint32_t dseg_4_length; /* Data segment 4 length. */ } cont_a64_entry_t; +#define PO_MODE_DIF_INSERT 0 +#define PO_MODE_DIF_REMOVE BIT_0 +#define PO_MODE_DIF_PASS BIT_1 +#define PO_MODE_DIF_REPLACE (BIT_0 + BIT_1) +#define PO_ENABLE_DIF_BUNDLING BIT_8 +#define PO_ENABLE_INCR_GUARD_SEED BIT_3 +#define PO_DISABLE_INCR_REF_TAG BIT_5 +#define PO_DISABLE_GUARD_CHECK BIT_4 +/* + * ISP queue - 64-Bit addressing, continuation crc entry structure definition. + */ +struct crc_context { + uint32_t handle; /* System handle. */ + uint32_t ref_tag; + uint16_t app_tag; + uint8_t ref_tag_mask[4]; /* Validation/Replacement Mask*/ + uint8_t app_tag_mask[2]; /* Validation/Replacement Mask*/ + uint16_t guard_seed; /* Initial Guard Seed */ + uint16_t prot_opts; /* Requested Data Protection Mode */ + uint16_t blk_size; /* Data size in bytes */ + uint16_t runt_blk_guard; /* Guard value for runt block (tape + * only) */ + uint32_t byte_count; /* Total byte count/ total data + * transfer count */ + union { + struct { + uint32_t reserved_1; + uint16_t reserved_2; + uint16_t reserved_3; + uint32_t reserved_4; + uint32_t data_address[2]; + uint32_t data_length; + uint32_t reserved_5[2]; + uint32_t reserved_6; + } nobundling; + struct { + uint32_t dif_byte_count; /* Total DIF byte + * count */ + uint16_t reserved_1; + uint16_t dseg_count; /* Data segment count */ + uint32_t reserved_2; + uint32_t data_address[2]; + uint32_t data_length; + uint32_t dif_address[2]; + uint32_t dif_length; /* Data segment 0 + * length */ + } bundling; + } u; + + struct fcp_cmnd fcp_cmnd; + dma_addr_t crc_ctx_dma; + /* List of DMA context transfers */ + struct list_head dsd_list; + + /* This structure should not exceed 512 bytes */ +}; + +#define CRC_CONTEXT_LEN_FW (offsetof(struct crc_context, fcp_cmnd.lun)) +#define CRC_CONTEXT_FCPCMND_OFF (offsetof(struct crc_context, fcp_cmnd.lun)) + /* * ISP queue - status entry structure definition. */ @@ -1359,6 +1466,7 @@ typedef struct { #define CS_ABORTED 0x5 /* System aborted command. */ #define CS_TIMEOUT 0x6 /* Timeout error. */ #define CS_DATA_OVERRUN 0x7 /* Data overrun. */ +#define CS_DIF_ERROR 0xC /* DIF error detected */ #define CS_DATA_UNDERRUN 0x15 /* Data Underrun. */ #define CS_QUEUE_FULL 0x1C /* Queue Full. */ @@ -1579,6 +1687,8 @@ typedef struct fc_port { uint16_t loop_id; uint16_t old_loop_id; + uint8_t fcp_prio; + uint8_t fabric_port_name[WWN_SIZE]; uint16_t fp_speed; @@ -1611,6 +1721,7 @@ typedef struct fc_port { #define FCF_FABRIC_DEVICE BIT_0 #define FCF_LOGIN_NEEDED BIT_1 #define FCF_FCP2_DEVICE BIT_2 +#define FCF_ASYNC_SENT BIT_3 /* No loop ID flag. */ #define FC_NO_LOOP_ID 0x1000 @@ -2109,6 +2220,7 @@ struct isp_operations { int (*get_flash_version) (struct scsi_qla_host *, void *); int (*start_scsi) (srb_t *); + int (*abort_isp) (struct scsi_qla_host *); }; /* MSI-X Support *************************************************************/ @@ -2143,6 +2255,8 @@ enum qla_work_type { QLA_EVT_ASYNC_LOGIN_DONE, QLA_EVT_ASYNC_LOGOUT, QLA_EVT_ASYNC_LOGOUT_DONE, + QLA_EVT_ASYNC_ADISC, + QLA_EVT_ASYNC_ADISC_DONE, QLA_EVT_UEVENT, }; @@ -2295,6 +2409,7 @@ struct qla_hw_data { uint32_t eeh_busy :1; uint32_t cpu_affinity_enabled :1; uint32_t disable_msix_handshake :1; + uint32_t fcp_prio_enabled :1; } flags; /* This spinlock is used to protect "io transactions", you must @@ -2382,7 +2497,8 @@ struct qla_hw_data { #define DT_ISP2532 BIT_11 #define DT_ISP8432 BIT_12 #define DT_ISP8001 BIT_13 -#define DT_ISP_LAST (DT_ISP8001 << 1) +#define DT_ISP8021 BIT_14 +#define DT_ISP_LAST (DT_ISP8021 << 1) #define DT_IIDMA BIT_26 #define DT_FWI2 BIT_27 @@ -2405,6 +2521,7 @@ struct qla_hw_data { #define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532) #define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432) #define IS_QLA8001(ha) (DT_MASK(ha) & DT_ISP8001) +#define IS_QLA82XX(ha) (DT_MASK(ha) & DT_ISP8021) #define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \ IS_QLA6312(ha) || IS_QLA6322(ha)) @@ -2415,8 +2532,10 @@ struct qla_hw_data { #define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \ IS_QLA84XX(ha)) #define IS_QLA81XX(ha) (IS_QLA8001(ha)) +#define IS_QLA8XXX_TYPE(ha) (IS_QLA81XX(ha) || IS_QLA82XX(ha)) #define IS_QLA2XXX_MIDTYPE(ha) (IS_QLA24XX(ha) || IS_QLA84XX(ha) || \ - IS_QLA25XX(ha) || IS_QLA81XX(ha)) + IS_QLA25XX(ha) || IS_QLA81XX(ha) || \ + IS_QLA82XX(ha)) #define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha)) #define IS_NOPOLLING_TYPE(ha) ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && \ (ha)->flags.msix_enabled) @@ -2496,6 +2615,9 @@ struct qla_hw_data { dma_addr_t ex_init_cb_dma; struct ex_init_cb_81xx *ex_init_cb; + void *async_pd; + dma_addr_t async_pd_dma; + /* These are used by mailbox operations. */ volatile uint16_t mailbox_out[MAILBOX_REGISTER_COUNT]; @@ -2598,6 +2720,8 @@ struct qla_hw_data { uint32_t flt_region_nvram; uint32_t flt_region_npiv_conf; uint32_t flt_region_gold_fw; + uint32_t flt_region_fcp_prio; + uint32_t flt_region_bootload; /* Needed for BEACON */ uint16_t beacon_blink_led; @@ -2626,6 +2750,39 @@ struct qla_hw_data { struct isp_operations *isp_ops; struct workqueue_struct *wq; struct qlfc_fw fw_buf; + + /* FCP_CMND priority support */ + struct qla_fcp_prio_cfg *fcp_prio_cfg; + + struct dma_pool *dl_dma_pool; +#define DSD_LIST_DMA_POOL_SIZE 512 + + struct dma_pool *fcp_cmnd_dma_pool; + mempool_t *ctx_mempool; +#define FCP_CMND_DMA_POOL_SIZE 512 + + unsigned long nx_pcibase; /* Base I/O address */ + uint8_t *nxdb_rd_ptr; /* Doorbell read pointer */ + unsigned long nxdb_wr_ptr; /* Door bell write pointer */ + + uint32_t crb_win; + uint32_t curr_window; + uint32_t ddr_mn_window; + unsigned long mn_win_crb; + unsigned long ms_win_crb; + int qdr_sn_window; + uint32_t nx_dev_init_timeout; + uint32_t nx_reset_timeout; + rwlock_t hw_lock; + uint16_t portnum; /* port number */ + int link_width; + struct fw_blob *hablob; + struct qla82xx_legacy_intr_set nx_legacy_intr; + + uint16_t gbl_dsd_inuse; + uint16_t gbl_dsd_avail; + struct list_head gbl_dsd_list; +#define NUM_DSD_CHAIN 4096 }; /* @@ -2650,6 +2807,7 @@ typedef struct scsi_qla_host { uint32_t management_server_logged_in :1; uint32_t process_response_queue :1; + uint32_t difdix_supported:1; } flags; atomic_t loop_state; @@ -2678,10 +2836,13 @@ typedef struct scsi_qla_host { #define VP_DPC_NEEDED 14 /* wake up for VP dpc handling */ #define UNLOADING 15 #define NPIV_CONFIG_NEEDED 16 +#define ISP_UNRECOVERABLE 17 +#define FCOE_CTX_RESET_NEEDED 18 /* Initiate FCoE context reset */ uint32_t device_flags; #define SWITCH_FOUND BIT_0 #define DFLG_NO_CABLE BIT_1 +#define DFLG_DEV_FAILED BIT_5 /* ISP configuration data. */ uint16_t loop_id; /* Host adapter loop id */ @@ -2739,6 +2900,8 @@ typedef struct scsi_qla_host { #define VP_ERR_ADAP_NORESOURCES 5 struct qla_hw_data *hw; struct req_que *req; + int fw_heartbeat_counter; + int seconds_since_last_heartbeat; } scsi_qla_host_t; /* @@ -2791,134 +2954,16 @@ typedef struct scsi_qla_host { #define OPTROM_SIZE_24XX 0x100000 #define OPTROM_SIZE_25XX 0x200000 #define OPTROM_SIZE_81XX 0x400000 +#define OPTROM_SIZE_82XX 0x800000 + +#define OPTROM_BURST_SIZE 0x1000 +#define OPTROM_BURST_DWORDS (OPTROM_BURST_SIZE / 4) + +#define QLA_DSDS_PER_IOCB 37 #include "qla_gbl.h" #include "qla_dbg.h" #include "qla_inline.h" #define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr) - -/* - * BSG Vendor specific commands - */ - -#define QL_VND_LOOPBACK 0x01 -#define QLA84_RESET 0x02 -#define QLA84_UPDATE_FW 0x03 -#define QLA84_MGMT_CMD 0x04 - -/* BSG definations for interpreting CommandSent field */ -#define INT_DEF_LB_LOOPBACK_CMD 0 -#define INT_DEF_LB_ECHO_CMD 1 - -/* BSG Vendor specific definations */ -typedef struct _A84_RESET { - uint16_t Flags; - uint16_t Reserved; -#define A84_RESET_FLAG_ENABLE_DIAG_FW 1 -} __attribute__((packed)) A84_RESET, *PA84_RESET; - -#define A84_ISSUE_WRITE_TYPE_CMD 0 -#define A84_ISSUE_READ_TYPE_CMD 1 -#define A84_CLEANUP_CMD 2 -#define A84_ISSUE_RESET_OP_FW 3 -#define A84_ISSUE_RESET_DIAG_FW 4 -#define A84_ISSUE_UPDATE_OPFW_CMD 5 -#define A84_ISSUE_UPDATE_DIAGFW_CMD 6 - -struct qla84_mgmt_param { - union { - struct { - uint32_t start_addr; - } mem; /* for QLA84_MGMT_READ/WRITE_MEM */ - struct { - uint32_t id; -#define QLA84_MGMT_CONFIG_ID_UIF 1 -#define QLA84_MGMT_CONFIG_ID_FCOE_COS 2 -#define QLA84_MGMT_CONFIG_ID_PAUSE 3 -#define QLA84_MGMT_CONFIG_ID_TIMEOUTS 4 - - uint32_t param0; - uint32_t param1; - } config; /* for QLA84_MGMT_CHNG_CONFIG */ - - struct { - uint32_t type; -#define QLA84_MGMT_INFO_CONFIG_LOG_DATA 1 /* Get Config Log Data */ -#define QLA84_MGMT_INFO_LOG_DATA 2 /* Get Log Data */ -#define QLA84_MGMT_INFO_PORT_STAT 3 /* Get Port Statistics */ -#define QLA84_MGMT_INFO_LIF_STAT 4 /* Get LIF Statistics */ -#define QLA84_MGMT_INFO_ASIC_STAT 5 /* Get ASIC Statistics */ -#define QLA84_MGMT_INFO_CONFIG_PARAMS 6 /* Get Config Parameters */ -#define QLA84_MGMT_INFO_PANIC_LOG 7 /* Get Panic Log */ - - uint32_t context; -/* -* context definitions for QLA84_MGMT_INFO_CONFIG_LOG_DATA -*/ -#define IC_LOG_DATA_LOG_ID_DEBUG_LOG 0 -#define IC_LOG_DATA_LOG_ID_LEARN_LOG 1 -#define IC_LOG_DATA_LOG_ID_FC_ACL_INGRESS_LOG 2 -#define IC_LOG_DATA_LOG_ID_FC_ACL_EGRESS_LOG 3 -#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_INGRESS_LOG 4 -#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_EGRESS_LOG 5 -#define IC_LOG_DATA_LOG_ID_MESSAGE_TRANSMIT_LOG 6 -#define IC_LOG_DATA_LOG_ID_MESSAGE_RECEIVE_LOG 7 -#define IC_LOG_DATA_LOG_ID_LINK_EVENT_LOG 8 -#define IC_LOG_DATA_LOG_ID_DCX_LOG 9 - -/* -* context definitions for QLA84_MGMT_INFO_PORT_STAT -*/ -#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT0 0 -#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT1 1 -#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT0 2 -#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT1 3 -#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT0 4 -#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT1 5 - - -/* -* context definitions for QLA84_MGMT_INFO_LIF_STAT -*/ -#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT0 0 -#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT1 1 -#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT0 2 -#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT1 3 -#define IC_LIF_STATISTICS_LIF_NUMBER_CPU 6 - - } info; /* for QLA84_MGMT_GET_INFO */ - } u; -}; - -struct qla84_msg_mgmt { - uint16_t cmd; -#define QLA84_MGMT_READ_MEM 0x00 -#define QLA84_MGMT_WRITE_MEM 0x01 -#define QLA84_MGMT_CHNG_CONFIG 0x02 -#define QLA84_MGMT_GET_INFO 0x03 - uint16_t rsrvd; - struct qla84_mgmt_param mgmtp;/* parameters for cmd */ - uint32_t len; /* bytes in payload following this struct */ - uint8_t payload[0]; /* payload for cmd */ -}; - -struct msg_update_fw { - /* - * diag_fw = 0 operational fw - * otherwise diagnostic fw - * offset, len, fw_len are present to overcome the current limitation - * of 128Kb xfer size. The fw is sent in smaller chunks. Each chunk - * specifies the byte "offset" where it fits in the fw buffer. The - * number of bytes in each chunk is specified in "len". "fw_len" - * is the total size of fw. The first chunk should start at offset = 0. - * When offset+len == fw_len, the fw is written to the HBA. - */ - uint32_t diag_fw; - uint32_t offset;/* start offset */ - uint32_t len; /* num bytes in cur xfer */ - uint32_t fw_len; /* size of fw in bytes */ - uint8_t fw_bytes[0]; -}; - #endif diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 42c5587cc50..93f83396014 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -400,6 +400,7 @@ struct cmd_type_6 { struct scsi_lun lun; /* FCP LUN (BE). */ uint16_t control_flags; /* Control flags. */ +#define CF_DIF_SEG_DESCR_ENABLE BIT_3 #define CF_DATA_SEG_DESCR_ENABLE BIT_2 #define CF_READ_DATA BIT_1 #define CF_WRITE_DATA BIT_0 @@ -466,6 +467,43 @@ struct cmd_type_7 { uint32_t dseg_0_len; /* Data segment 0 length. */ }; +#define COMMAND_TYPE_CRC_2 0x6A /* Command Type CRC_2 (Type 6) + * (T10-DIF) */ +struct cmd_type_crc_2 { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + uint16_t nport_handle; /* N_PORT handle. */ + uint16_t timeout; /* Command timeout. */ + + uint16_t dseg_count; /* Data segment count. */ + + uint16_t fcp_rsp_dseg_len; /* FCP_RSP DSD length. */ + + struct scsi_lun lun; /* FCP LUN (BE). */ + + uint16_t control_flags; /* Control flags. */ + + uint16_t fcp_cmnd_dseg_len; /* Data segment length. */ + uint32_t fcp_cmnd_dseg_address[2]; /* Data segment address. */ + + uint32_t fcp_rsp_dseg_address[2]; /* Data segment address. */ + + uint32_t byte_count; /* Total byte count. */ + + uint8_t port_id[3]; /* PortID of destination port. */ + uint8_t vp_index; + + uint32_t crc_context_address[2]; /* Data segment address. */ + uint16_t crc_context_len; /* Data segment length. */ + uint16_t reserved_1; /* MUST be set to 0. */ +}; + + /* * ISP queue - status entry structure definition. */ @@ -496,10 +534,17 @@ struct sts_entry_24xx { uint32_t sense_len; /* FCP SENSE length. */ uint32_t rsp_data_len; /* FCP response data length. */ - uint8_t data[28]; /* FCP response/sense information. */ + /* + * If DIF Error is set in comp_status, these additional fields are + * defined: + * &data[10] : uint8_t report_runt_bg[2]; - computed guard + * &data[12] : uint8_t actual_dif[8]; - DIF Data recieved + * &data[20] : uint8_t expected_dif[8]; - DIF Data computed + */ }; + /* * Status entry completion status */ @@ -841,6 +886,8 @@ struct device_reg_24xx { #define FA_HW_EVENT_ENTRY_SIZE 4 #define FA_NPIV_CONF0_ADDR 0x5C000 #define FA_NPIV_CONF1_ADDR 0x5D000 +#define FA_FCP_PRIO0_ADDR 0x10000 +#define FA_FCP_PRIO1_ADDR 0x12000 /* * Flash Error Log Event Codes. @@ -1274,6 +1321,8 @@ struct qla_flt_header { #define FLT_REG_NPIV_CONF_0 0x29 #define FLT_REG_NPIV_CONF_1 0x2a #define FLT_REG_GOLD_FW 0x2f +#define FLT_REG_FCP_PRIO_0 0x87 +#define FLT_REG_FCP_PRIO_1 0x88 struct qla_flt_region { uint32_t code; @@ -1750,6 +1799,61 @@ struct ex_init_cb_81xx { #define FARX_ACCESS_FLASH_CONF_81XX 0x7FFD0000 #define FARX_ACCESS_FLASH_DATA_81XX 0x7F800000 +/* FCP priority config defines *************************************/ +/* operations */ +#define QLFC_FCP_PRIO_DISABLE 0x0 +#define QLFC_FCP_PRIO_ENABLE 0x1 +#define QLFC_FCP_PRIO_GET_CONFIG 0x2 +#define QLFC_FCP_PRIO_SET_CONFIG 0x3 + +struct qla_fcp_prio_entry { + uint16_t flags; /* Describes parameter(s) in FCP */ + /* priority entry that are valid */ +#define FCP_PRIO_ENTRY_VALID 0x1 +#define FCP_PRIO_ENTRY_TAG_VALID 0x2 +#define FCP_PRIO_ENTRY_SPID_VALID 0x4 +#define FCP_PRIO_ENTRY_DPID_VALID 0x8 +#define FCP_PRIO_ENTRY_LUNB_VALID 0x10 +#define FCP_PRIO_ENTRY_LUNE_VALID 0x20 +#define FCP_PRIO_ENTRY_SWWN_VALID 0x40 +#define FCP_PRIO_ENTRY_DWWN_VALID 0x80 + uint8_t tag; /* Priority value */ + uint8_t reserved; /* Reserved for future use */ + uint32_t src_pid; /* Src port id. high order byte */ + /* unused; -1 (wild card) */ + uint32_t dst_pid; /* Src port id. high order byte */ + /* unused; -1 (wild card) */ + uint16_t lun_beg; /* 1st lun num of lun range. */ + /* -1 (wild card) */ + uint16_t lun_end; /* 2nd lun num of lun range. */ + /* -1 (wild card) */ + uint8_t src_wwpn[8]; /* Source WWPN: -1 (wild card) */ + uint8_t dst_wwpn[8]; /* Destination WWPN: -1 (wild card) */ +}; + +struct qla_fcp_prio_cfg { + uint8_t signature[4]; /* "HQOS" signature of config data */ + uint16_t version; /* 1: Initial version */ + uint16_t length; /* config data size in num bytes */ + uint16_t checksum; /* config data bytes checksum */ + uint16_t num_entries; /* Number of entries */ + uint16_t size_of_entry; /* Size of each entry in num bytes */ + uint8_t attributes; /* enable/disable, persistence */ +#define FCP_PRIO_ATTR_DISABLE 0x0 +#define FCP_PRIO_ATTR_ENABLE 0x1 +#define FCP_PRIO_ATTR_PERSIST 0x2 + uint8_t reserved; /* Reserved for future use */ +#define FCP_PRIO_CFG_HDR_SIZE 0x10 + struct qla_fcp_prio_entry entry[1]; /* fcp priority entries */ +#define FCP_PRIO_CFG_ENTRY_SIZE 0x20 +}; + +#define FCP_PRIO_CFG_SIZE (32*1024) /* fcp prio data per port*/ + +/* 25XX Support ****************************************************/ +#define FA_FCP_PRIO0_ADDR_25 0x3C000 +#define FA_FCP_PRIO1_ADDR_25 0x3E000 + /* 81XX Flash locations -- occupies second 2MB region. */ #define FA_BOOT_CODE_ADDR_81 0x80000 #define FA_RISC_CODE_ADDR_81 0xA0000 diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 3a89bc514e2..8217c3bcbc2 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -44,6 +44,7 @@ extern int qla2x00_local_device_login(scsi_qla_host_t *, fc_port_t *); extern void qla2x00_update_fcports(scsi_qla_host_t *); extern int qla2x00_abort_isp(scsi_qla_host_t *); +extern void qla2x00_abort_isp_cleanup(scsi_qla_host_t *); extern void qla2x00_update_fcport(scsi_qla_host_t *, fc_port_t *); @@ -55,10 +56,20 @@ extern void qla84xx_put_chip(struct scsi_qla_host *); extern int qla2x00_async_login(struct scsi_qla_host *, fc_port_t *, uint16_t *); extern int qla2x00_async_logout(struct scsi_qla_host *, fc_port_t *); -extern int qla2x00_async_login_done(struct scsi_qla_host *, fc_port_t *, +extern int qla2x00_async_adisc(struct scsi_qla_host *, fc_port_t *, uint16_t *); -extern int qla2x00_async_logout_done(struct scsi_qla_host *, fc_port_t *, +extern int qla2x00_async_tm_cmd(fc_port_t *, uint32_t, uint32_t, uint32_t); +extern int qla2x00_async_marker(fc_port_t *, uint16_t, uint8_t); +extern void qla2x00_async_login_done(struct scsi_qla_host *, fc_port_t *, uint16_t *); +extern void qla2x00_async_logout_done(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern void qla2x00_async_adisc_done(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern void qla2x00_async_tm_cmd_done(struct scsi_qla_host *, fc_port_t *, + struct srb_iocb *); +extern void qla2x00_async_marker_done(struct scsi_qla_host *, fc_port_t *, + struct srb_iocb *); extern fc_port_t * qla2x00_alloc_fcport(scsi_qla_host_t *, gfp_t ); @@ -79,6 +90,13 @@ extern int ql2xmaxqueues; extern int ql2xmultique_tag; extern int ql2xfwloadbin; extern int ql2xetsenable; +extern int ql2xshiftctondsd; +extern int ql2xdbwr; +extern int ql2xdontresethba; +extern int ql2xasynctmfenable; +extern int ql2xenabledif; +extern int ql2xenablehba_err_chk; +extern int ql2xtargetreset; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); @@ -93,6 +111,10 @@ extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); +extern int qla2x00_post_async_adisc_work(struct scsi_qla_host *, fc_port_t *, + uint16_t *); +extern int qla2x00_post_async_adisc_done_work(struct scsi_qla_host *, + fc_port_t *, uint16_t *); extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *); @@ -135,6 +157,7 @@ extern struct fw_blob *qla2x00_request_firmware(scsi_qla_host_t *); extern int qla2x00_wait_for_hba_online(scsi_qla_host_t *); extern int qla2x00_wait_for_chip_reset(scsi_qla_host_t *); +extern int qla2x00_wait_for_fcoe_ctx_reset(scsi_qla_host_t *); extern void qla2xxx_wake_dpc(struct scsi_qla_host *); extern void qla2x00_alert_all_vps(struct rsp_que *, uint16_t *); @@ -157,6 +180,10 @@ int __qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *, uint16_t, uint16_t, uint8_t); extern int qla2x00_start_sp(srb_t *); extern void qla2x00_ctx_sp_free(srb_t *); +extern uint16_t qla24xx_calc_iocbs(uint16_t); +extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *, uint16_t); +extern int qla24xx_dif_start_scsi(srb_t *); + /* * Global Function Prototypes in qla_mbx.c source file. @@ -328,6 +355,9 @@ extern int qla2x00_write_ram_word(scsi_qla_host_t *, uint32_t, uint32_t); extern int qla2x00_get_data_rate(scsi_qla_host_t *); +extern int qla24xx_set_fcp_prio(scsi_qla_host_t *, uint16_t, uint16_t, + uint16_t *); + /* * Global Function Prototypes in qla_isr.c source file. */ @@ -340,6 +370,7 @@ qla24xx_process_response_queue(struct scsi_qla_host *, struct rsp_que *); extern int qla2x00_request_irqs(struct qla_hw_data *, struct rsp_que *); extern void qla2x00_free_irqs(scsi_qla_host_t *); +extern int qla2x00_get_data_rate(scsi_qla_host_t *); /* * Global Function Prototypes in qla_sup.c source file. */ @@ -384,6 +415,7 @@ 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 *); +extern int qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *); /* * Global Function Prototypes in qla_dbg.c source file. @@ -395,6 +427,7 @@ extern void qla25xx_fw_dump(scsi_qla_host_t *, int); extern void qla81xx_fw_dump(scsi_qla_host_t *, int); extern void qla2x00_dump_regs(scsi_qla_host_t *); extern void qla2x00_dump_buffer(uint8_t *, uint32_t); +extern void qla2x00_dump_buffer_zipped(uint8_t *, uint32_t); /* * Global Function Prototypes in qla_gs.c source file. @@ -430,7 +463,10 @@ extern void qla2x00_init_host_attr(scsi_qla_host_t *); extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *); extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *); extern int qla2x00_loopback_test(scsi_qla_host_t *, struct msg_echo_lb *, uint16_t *); -extern int qla2x00_echo_test(scsi_qla_host_t *, struct msg_echo_lb *, uint16_t *); +extern int qla2x00_echo_test(scsi_qla_host_t *, + struct msg_echo_lb *, uint16_t *); +extern int qla24xx_update_all_fcp_prio(scsi_qla_host_t *); +extern int qla24xx_fcp_prio_cfg_valid(struct qla_fcp_prio_cfg *, uint8_t); /* * Global Function Prototypes in qla_dfs.c source file. @@ -459,4 +495,88 @@ extern void qla25xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t); extern void qla25xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t); extern void qla24xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t); +/* qla82xx related functions */ + +/* PCI related functions */ +extern int qla82xx_pci_config(struct scsi_qla_host *); +extern int qla82xx_pci_mem_read_2M(struct qla_hw_data *, u64, void *, int); +extern int qla82xx_pci_mem_write_2M(struct qla_hw_data *, u64, void *, int); +extern char *qla82xx_pci_info_str(struct scsi_qla_host *, char *); +extern int qla82xx_pci_region_offset(struct pci_dev *, int); +extern int qla82xx_pci_region_len(struct pci_dev *, int); +extern int qla82xx_iospace_config(struct qla_hw_data *); + +/* Initialization related functions */ +extern void qla82xx_reset_chip(struct scsi_qla_host *); +extern void qla82xx_config_rings(struct scsi_qla_host *); +extern int qla82xx_nvram_config(struct scsi_qla_host *); +extern int qla82xx_pinit_from_rom(scsi_qla_host_t *); +extern int qla82xx_load_firmware(scsi_qla_host_t *); +extern int qla82xx_reset_hw(scsi_qla_host_t *); +extern int qla82xx_load_risc_blob(scsi_qla_host_t *, uint32_t *); +extern void qla82xx_watchdog(scsi_qla_host_t *); + +/* Firmware and flash related functions */ +extern int qla82xx_load_risc(scsi_qla_host_t *, uint32_t *); +extern uint8_t *qla82xx_read_optrom_data(struct scsi_qla_host *, uint8_t *, + uint32_t, uint32_t); +extern int qla82xx_write_optrom_data(struct scsi_qla_host *, uint8_t *, + uint32_t, uint32_t); + +/* Mailbox related functions */ +extern int qla82xx_abort_isp(scsi_qla_host_t *); +extern int qla82xx_restart_isp(scsi_qla_host_t *); + +/* IOCB related functions */ +extern int qla82xx_start_scsi(srb_t *); + +/* Interrupt related */ +extern irqreturn_t qla82xx_intr_handler(int, void *); +extern irqreturn_t qla82xx_msi_handler(int, void *); +extern irqreturn_t qla82xx_msix_default(int, void *); +extern irqreturn_t qla82xx_msix_rsp_q(int, void *); +extern void qla82xx_enable_intrs(struct qla_hw_data *); +extern void qla82xx_disable_intrs(struct qla_hw_data *); +extern void qla82xx_mbx_completion(scsi_qla_host_t *, uint16_t); +extern void qla82xx_poll(int, void *); +extern void qla82xx_init_flags(struct qla_hw_data *); + +/* ISP 8021 hardware related */ +extern int qla82xx_crb_win_lock(struct qla_hw_data *); +extern void qla82xx_crb_win_unlock(struct qla_hw_data *); +extern int qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *, ulong *); +extern int qla82xx_wr_32(struct qla_hw_data *, ulong, u32); +extern int qla82xx_rd_32(struct qla_hw_data *, ulong); +extern int qla82xx_rdmem(struct qla_hw_data *, u64, void *, int); +extern int qla82xx_wrmem(struct qla_hw_data *, u64, void *, int); +extern int qla82xx_check_for_bad_spd(struct qla_hw_data *); +extern int qla82xx_load_fw(scsi_qla_host_t *); +extern int qla82xx_rom_lock(struct qla_hw_data *); +extern void qla82xx_rom_unlock(struct qla_hw_data *); +extern int qla82xx_rom_fast_read(struct qla_hw_data *, int , int *); +extern int qla82xx_do_rom_fast_read(struct qla_hw_data *, int, int *); +extern unsigned long qla82xx_decode_crb_addr(unsigned long); + +/* ISP 8021 IDC */ +extern void qla82xx_clear_drv_active(struct qla_hw_data *); +extern int qla82xx_idc_lock(struct qla_hw_data *); +extern void qla82xx_idc_unlock(struct qla_hw_data *); +extern int qla82xx_device_state_handler(scsi_qla_host_t *); + +extern void qla2x00_set_model_info(scsi_qla_host_t *, uint8_t *, + size_t, char *); +extern int qla82xx_mbx_intr_enable(scsi_qla_host_t *); +extern int qla82xx_mbx_intr_disable(scsi_qla_host_t *); +extern void qla82xx_start_iocbs(srb_t *); +extern int qla82xx_fcoe_ctx_reset(scsi_qla_host_t *); +extern void qla82xx_wait_for_pending_commands(scsi_qla_host_t *); + +/* BSG related functions */ +extern int qla24xx_bsg_request(struct fc_bsg_job *); +extern int qla24xx_bsg_timeout(struct fc_bsg_job *); +extern int qla84xx_reset_chip(scsi_qla_host_t *, uint16_t); +extern int qla2x00_issue_iocb_timeout(scsi_qla_host_t *, void *, + dma_addr_t, size_t, uint32_t); +extern int qla2x00_get_idma_speed(scsi_qla_host_t *, uint16_t, + uint16_t *, uint16_t *); #endif /* _QLA_GBL_H */ diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 4647015eba6..872c55f049a 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -1535,7 +1535,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) eiter = (struct ct_fdmi_port_attr *) (entries + size); eiter->type = __constant_cpu_to_be16(FDMI_PORT_SUPPORT_SPEED); eiter->len = __constant_cpu_to_be16(4 + 4); - if (IS_QLA81XX(ha)) + if (IS_QLA8XXX_TYPE(ha)) eiter->a.sup_speed = __constant_cpu_to_be32( FDMI_PORT_SPEED_10GB); else if (IS_QLA25XX(ha)) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 4229bb483c5..ab2cc71994c 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -48,6 +48,7 @@ qla2x00_ctx_sp_timeout(unsigned long __data) { srb_t *sp = (srb_t *)__data; struct srb_ctx *ctx; + struct srb_iocb *iocb; fc_port_t *fcport = sp->fcport; struct qla_hw_data *ha = fcport->vha->hw; struct req_que *req; @@ -57,17 +58,21 @@ qla2x00_ctx_sp_timeout(unsigned long __data) req = ha->req_q_map[0]; req->outstanding_cmds[sp->handle] = NULL; ctx = sp->ctx; - ctx->timeout(sp); + iocb = ctx->u.iocb_cmd; + iocb->timeout(sp); spin_unlock_irqrestore(&ha->hardware_lock, flags); - ctx->free(sp); + iocb->free(sp); } void qla2x00_ctx_sp_free(srb_t *sp) { struct srb_ctx *ctx = sp->ctx; + struct srb_iocb *iocb = ctx->u.iocb_cmd; + del_timer_sync(&iocb->timer); + kfree(iocb); kfree(ctx); mempool_free(sp, sp->fcport->vha->hw->srb_mempool); } @@ -79,6 +84,7 @@ qla2x00_get_ctx_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size, srb_t *sp; struct qla_hw_data *ha = vha->hw; struct srb_ctx *ctx; + struct srb_iocb *iocb; sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL); if (!sp) @@ -86,21 +92,30 @@ qla2x00_get_ctx_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size, ctx = kzalloc(size, GFP_KERNEL); if (!ctx) { mempool_free(sp, ha->srb_mempool); + sp = NULL; + goto done; + } + iocb = kzalloc(sizeof(struct srb_iocb), GFP_KERNEL); + if (!iocb) { + mempool_free(sp, ha->srb_mempool); + sp = NULL; + kfree(ctx); goto done; } memset(sp, 0, sizeof(*sp)); sp->fcport = fcport; sp->ctx = ctx; - ctx->free = qla2x00_ctx_sp_free; + ctx->u.iocb_cmd = iocb; + iocb->free = qla2x00_ctx_sp_free; - init_timer(&ctx->timer); + init_timer(&iocb->timer); if (!tmo) goto done; - ctx->timer.expires = jiffies + tmo * HZ; - ctx->timer.data = (unsigned long)sp; - ctx->timer.function = qla2x00_ctx_sp_timeout; - add_timer(&ctx->timer); + iocb->timer.expires = jiffies + tmo * HZ; + iocb->timer.data = (unsigned long)sp; + iocb->timer.function = qla2x00_ctx_sp_timeout; + add_timer(&iocb->timer); done: return sp; } @@ -110,41 +125,56 @@ done: #define ELS_TMO_2_RATOV(ha) ((ha)->r_a_tov / 10 * 2) static void -qla2x00_async_logio_timeout(srb_t *sp) +qla2x00_async_iocb_timeout(srb_t *sp) { fc_port_t *fcport = sp->fcport; - struct srb_logio *lio = sp->ctx; + struct srb_ctx *ctx = sp->ctx; DEBUG2(printk(KERN_WARNING "scsi(%ld:%x): Async-%s timeout.\n", - fcport->vha->host_no, sp->handle, - lio->ctx.type == SRB_LOGIN_CMD ? "login": "logout")); + fcport->vha->host_no, sp->handle, ctx->name)); - if (lio->ctx.type == SRB_LOGIN_CMD) + fcport->flags &= ~FCF_ASYNC_SENT; + if (ctx->type == SRB_LOGIN_CMD) qla2x00_post_async_logout_work(fcport->vha, fcport, NULL); } +static void +qla2x00_async_login_ctx_done(srb_t *sp) +{ + struct srb_ctx *ctx = sp->ctx; + struct srb_iocb *lio = ctx->u.iocb_cmd; + + qla2x00_post_async_login_done_work(sp->fcport->vha, sp->fcport, + lio->u.logio.data); + lio->free(sp); +} + int qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, uint16_t *data) { struct qla_hw_data *ha = vha->hw; srb_t *sp; - struct srb_logio *lio; + struct srb_ctx *ctx; + struct srb_iocb *lio; int rval; rval = QLA_FUNCTION_FAILED; - sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_logio), + sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx), ELS_TMO_2_RATOV(ha) + 2); if (!sp) goto done; - lio = sp->ctx; - lio->ctx.type = SRB_LOGIN_CMD; - lio->ctx.timeout = qla2x00_async_logio_timeout; - lio->flags |= SRB_LOGIN_COND_PLOGI; + ctx = sp->ctx; + ctx->type = SRB_LOGIN_CMD; + ctx->name = "login"; + lio = ctx->u.iocb_cmd; + lio->timeout = qla2x00_async_iocb_timeout; + lio->done = qla2x00_async_login_ctx_done; + lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI; if (data[1] & QLA_LOGIO_LOGIN_RETRIED) - lio->flags |= SRB_LOGIN_RETRIED; + lio->u.logio.flags |= SRB_LOGIN_RETRIED; rval = qla2x00_start_sp(sp); if (rval != QLA_SUCCESS) goto done_free_sp; @@ -157,29 +187,43 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, return rval; done_free_sp: - del_timer_sync(&lio->ctx.timer); - lio->ctx.free(sp); + lio->free(sp); done: return rval; } +static void +qla2x00_async_logout_ctx_done(srb_t *sp) +{ + struct srb_ctx *ctx = sp->ctx; + struct srb_iocb *lio = ctx->u.iocb_cmd; + + qla2x00_post_async_logout_done_work(sp->fcport->vha, sp->fcport, + lio->u.logio.data); + lio->free(sp); +} + int qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport) { struct qla_hw_data *ha = vha->hw; srb_t *sp; - struct srb_logio *lio; + struct srb_ctx *ctx; + struct srb_iocb *lio; int rval; rval = QLA_FUNCTION_FAILED; - sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_logio), + sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx), ELS_TMO_2_RATOV(ha) + 2); if (!sp) goto done; - lio = sp->ctx; - lio->ctx.type = SRB_LOGOUT_CMD; - lio->ctx.timeout = qla2x00_async_logio_timeout; + ctx = sp->ctx; + ctx->type = SRB_LOGOUT_CMD; + ctx->name = "logout"; + lio = ctx->u.iocb_cmd; + lio->timeout = qla2x00_async_iocb_timeout; + lio->done = qla2x00_async_logout_ctx_done; rval = qla2x00_start_sp(sp); if (rval != QLA_SUCCESS) goto done_free_sp; @@ -191,30 +235,186 @@ qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport) return rval; done_free_sp: - del_timer_sync(&lio->ctx.timer); - lio->ctx.free(sp); + lio->free(sp); done: return rval; } +static void +qla2x00_async_adisc_ctx_done(srb_t *sp) +{ + struct srb_ctx *ctx = sp->ctx; + struct srb_iocb *lio = ctx->u.iocb_cmd; + + qla2x00_post_async_adisc_done_work(sp->fcport->vha, sp->fcport, + lio->u.logio.data); + lio->free(sp); +} + int +qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport, + uint16_t *data) +{ + struct qla_hw_data *ha = vha->hw; + srb_t *sp; + struct srb_ctx *ctx; + struct srb_iocb *lio; + int rval; + + rval = QLA_FUNCTION_FAILED; + sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx), + ELS_TMO_2_RATOV(ha) + 2); + if (!sp) + goto done; + + ctx = sp->ctx; + ctx->type = SRB_ADISC_CMD; + ctx->name = "adisc"; + lio = ctx->u.iocb_cmd; + lio->timeout = qla2x00_async_iocb_timeout; + lio->done = qla2x00_async_adisc_ctx_done; + if (data[1] & QLA_LOGIO_LOGIN_RETRIED) + lio->u.logio.flags |= SRB_LOGIN_RETRIED; + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + DEBUG2(printk(KERN_DEBUG + "scsi(%ld:%x): Async-adisc - loop-id=%x portid=%02x%02x%02x.\n", + fcport->vha->host_no, sp->handle, fcport->loop_id, + fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa)); + + return rval; + +done_free_sp: + lio->free(sp); +done: + return rval; +} + +static void +qla2x00_async_tm_cmd_ctx_done(srb_t *sp) +{ + struct srb_ctx *ctx = sp->ctx; + struct srb_iocb *iocb = (struct srb_iocb *)ctx->u.iocb_cmd; + + qla2x00_async_tm_cmd_done(sp->fcport->vha, sp->fcport, iocb); + iocb->free(sp); +} + +int +qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint32_t lun, + uint32_t tag) +{ + struct scsi_qla_host *vha = fcport->vha; + struct qla_hw_data *ha = vha->hw; + srb_t *sp; + struct srb_ctx *ctx; + struct srb_iocb *tcf; + int rval; + + rval = QLA_FUNCTION_FAILED; + sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx), + ELS_TMO_2_RATOV(ha) + 2); + if (!sp) + goto done; + + ctx = sp->ctx; + ctx->type = SRB_TM_CMD; + ctx->name = "tmf"; + tcf = ctx->u.iocb_cmd; + tcf->u.tmf.flags = flags; + tcf->u.tmf.lun = lun; + tcf->u.tmf.data = tag; + tcf->timeout = qla2x00_async_iocb_timeout; + tcf->done = qla2x00_async_tm_cmd_ctx_done; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + DEBUG2(printk(KERN_DEBUG + "scsi(%ld:%x): Async-tmf - loop-id=%x portid=%02x%02x%02x.\n", + fcport->vha->host_no, sp->handle, fcport->loop_id, + fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa)); + + return rval; + +done_free_sp: + tcf->free(sp); +done: + return rval; +} + +static void +qla2x00_async_marker_ctx_done(srb_t *sp) +{ + struct srb_ctx *ctx = sp->ctx; + struct srb_iocb *iocb = (struct srb_iocb *)ctx->u.iocb_cmd; + + qla2x00_async_marker_done(sp->fcport->vha, sp->fcport, iocb); + iocb->free(sp); +} + +int +qla2x00_async_marker(fc_port_t *fcport, uint16_t lun, uint8_t modif) +{ + struct scsi_qla_host *vha = fcport->vha; + srb_t *sp; + struct srb_ctx *ctx; + struct srb_iocb *mrk; + int rval; + + rval = QLA_FUNCTION_FAILED; + sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx), 0); + if (!sp) + goto done; + + ctx = sp->ctx; + ctx->type = SRB_MARKER_CMD; + ctx->name = "marker"; + mrk = ctx->u.iocb_cmd; + mrk->u.marker.lun = lun; + mrk->u.marker.modif = modif; + mrk->timeout = qla2x00_async_iocb_timeout; + mrk->done = qla2x00_async_marker_ctx_done; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + DEBUG2(printk(KERN_DEBUG + "scsi(%ld:%x): Async-marker - loop-id=%x " + "portid=%02x%02x%02x.\n", + fcport->vha->host_no, sp->handle, fcport->loop_id, + fcport->d_id.b.domain, fcport->d_id.b.area, + fcport->d_id.b.al_pa)); + + return rval; + +done_free_sp: + mrk->free(sp); +done: + return rval; +} + +void qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport, uint16_t *data) { int rval; - uint8_t opts = 0; switch (data[0]) { case MBS_COMMAND_COMPLETE: - if (fcport->flags & FCF_FCP2_DEVICE) - opts |= BIT_1; - rval = qla2x00_get_port_database(vha, fcport, opts); - if (rval != QLA_SUCCESS) - qla2x00_mark_device_lost(vha, fcport, 1, 0); - else - qla2x00_update_fcport(vha, fcport); + if (fcport->flags & FCF_FCP2_DEVICE) { + fcport->flags |= FCF_ASYNC_SENT; + qla2x00_post_async_adisc_work(vha, fcport, data); + break; + } + qla2x00_update_fcport(vha, fcport); break; case MBS_COMMAND_ERROR: + fcport->flags &= ~FCF_ASYNC_SENT; if (data[1] & QLA_LOGIO_LOGIN_RETRIED) set_bit(RELOGIN_NEEDED, &vha->dpc_flags); else @@ -228,21 +428,84 @@ qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport, fcport->loop_id++; rval = qla2x00_find_new_loop_id(vha, fcport); if (rval != QLA_SUCCESS) { + fcport->flags &= ~FCF_ASYNC_SENT; qla2x00_mark_device_lost(vha, fcport, 1, 0); break; } qla2x00_post_async_login_work(vha, fcport, NULL); break; } - return QLA_SUCCESS; + return; } -int +void qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport, uint16_t *data) { qla2x00_mark_device_lost(vha, fcport, 1, 0); - return QLA_SUCCESS; + return; +} + +void +qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport, + uint16_t *data) +{ + if (data[0] == MBS_COMMAND_COMPLETE) { + qla2x00_update_fcport(vha, fcport); + + return; + } + + /* Retry login. */ + fcport->flags &= ~FCF_ASYNC_SENT; + if (data[1] & QLA_LOGIO_LOGIN_RETRIED) + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); + else + qla2x00_mark_device_lost(vha, fcport, 1, 0); + + return; +} + +void +qla2x00_async_tm_cmd_done(struct scsi_qla_host *vha, fc_port_t *fcport, + struct srb_iocb *iocb) +{ + int rval; + uint32_t flags; + uint16_t lun; + + flags = iocb->u.tmf.flags; + lun = (uint16_t)iocb->u.tmf.lun; + + /* Issue Marker IOCB */ + rval = qla2x00_async_marker(fcport, lun, + flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID); + + if ((rval != QLA_SUCCESS) || iocb->u.tmf.data) { + DEBUG2_3_11(printk(KERN_WARNING + "%s(%ld): TM IOCB failed (%x).\n", + __func__, vha->host_no, rval)); + } + + return; +} + +void +qla2x00_async_marker_done(struct scsi_qla_host *vha, fc_port_t *fcport, + struct srb_iocb *iocb) +{ + /* + * Currently we dont have any specific post response processing + * for this IOCB. We'll just return success or failed + * depending on whether the IOCB command succeeded or failed. + */ + if (iocb->u.tmf.data) { + DEBUG2_3_11(printk(KERN_WARNING + "%s(%ld): Marker IOCB failed (%x).\n", + __func__, vha->host_no, iocb->u.tmf.data)); + } + + return; } /****************************************************************************/ @@ -328,6 +591,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) if (rval) return (rval); } + if (IS_QLA84XX(ha)) { ha->cs84xx = qla84xx_get_chip(vha); if (!ha->cs84xx) { @@ -340,7 +604,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) ha->flags.chip_reset_done = 1; if (rval == QLA_SUCCESS && IS_QLA84XX(ha)) { - /* Issue verify 84xx FW IOCB to complete 84xx initialization */ + /* Issue verify 84xx FW IOCB to complete 84xx initialization */ rval = qla84xx_init_chip(vha); if (rval != QLA_SUCCESS) { qla_printk(KERN_ERR, ha, @@ -349,6 +613,12 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) } } + if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha)) { + if (qla24xx_read_fcp_prio_cfg(vha)) + qla_printk(KERN_ERR, ha, + "Unable to read FCP priority data.\n"); + } + return (rval); } @@ -955,6 +1225,9 @@ qla24xx_chip_diag(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; + if (IS_QLA82XX(ha)) + return QLA_SUCCESS; + ha->fw_transfer_size = REQUEST_ENTRY_SIZE * req->length; rval = qla2x00_mbx_reg_test(vha); @@ -1177,6 +1450,12 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) unsigned long flags; uint16_t fw_major_version; + if (IS_QLA82XX(ha)) { + rval = ha->isp_ops->load_risc(vha, &srisc_address); + if (rval == QLA_SUCCESS) + goto enable_82xx_npiv; + } + if (!IS_FWI2_CAPABLE(ha) && !IS_QLA2100(ha) && !IS_QLA2200(ha)) { /* Disable SRAM, Instruction RAM and GP RAM parity. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -1202,6 +1481,7 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) rval = qla2x00_execute_fw(vha, srisc_address); /* Retrieve firmware information. */ if (rval == QLA_SUCCESS) { +enable_82xx_npiv: fw_major_version = ha->fw_major_version; rval = qla2x00_get_fw_version(vha, &ha->fw_major_version, @@ -1226,8 +1506,10 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) &ha->fw_xcb_count, NULL, NULL, &ha->max_npiv_vports, NULL); - if (!fw_major_version && ql2xallocfwdump) - qla2x00_alloc_fw_dump(vha); + if (!fw_major_version && ql2xallocfwdump) { + if (!IS_QLA82XX(ha)) + qla2x00_alloc_fw_dump(vha); + } } } else { DEBUG2(printk(KERN_INFO @@ -1384,6 +1666,9 @@ qla24xx_update_fw_options(scsi_qla_host_t *vha) int rval; struct qla_hw_data *ha = vha->hw; + if (IS_QLA82XX(ha)) + return; + /* Update Serial Link options. */ if ((le16_to_cpu(ha->fw_seriallink_options24[0]) & BIT_0) == 0) return; @@ -1818,7 +2103,7 @@ qla2x00_configure_hba(scsi_qla_host_t *vha) return(rval); } -static inline void +inline void qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len, char *def) { @@ -1826,7 +2111,7 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len, uint16_t index; struct qla_hw_data *ha = vha->hw; int use_tbl = !IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && - !IS_QLA81XX(ha); + !IS_QLA8XXX_TYPE(ha); if (memcmp(model, BINZERO, len) != 0) { strncpy(ha->model_number, model, len); @@ -2017,6 +2302,7 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) if (IS_QLA23XX(ha)) { nv->firmware_options[0] |= BIT_2; nv->firmware_options[0] &= ~BIT_3; + nv->firmware_options[0] &= ~BIT_6; nv->add_firmware_options[1] |= BIT_5 | BIT_4; if (IS_QLA2300(ha)) { @@ -2635,7 +2921,7 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) PORT_RETRY_TIME; atomic_set(&fcport->port_down_timer, ha->port_down_retry_count * PORT_RETRY_TIME); - fcport->flags &= ~FCF_LOGIN_NEEDED; + fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); qla2x00_iidma_fcport(vha, fcport); @@ -2864,7 +3150,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, sw_info_t *swl; int swl_idx; int first_dev, last_dev; - port_id_t wrap, nxt_d_id; + port_id_t wrap = {}, nxt_d_id; struct qla_hw_data *ha = vha->hw; struct scsi_qla_host *vp, *base_vha = pci_get_drvdata(ha->pdev); struct scsi_qla_host *tvp; @@ -3167,7 +3453,7 @@ qla2x00_device_resync(scsi_qla_host_t *vha) uint32_t rscn_entry; uint8_t rscn_out_iter; uint8_t format; - port_id_t d_id; + port_id_t d_id = {}; rval = QLA_RSCNS_HANDLED; @@ -3281,11 +3567,15 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport, retry = 0; if (IS_ALOGIO_CAPABLE(ha)) { + if (fcport->flags & FCF_ASYNC_SENT) + return rval; + fcport->flags |= FCF_ASYNC_SENT; rval = qla2x00_post_async_login_work(vha, fcport, NULL); if (!rval) return rval; } + fcport->flags &= ~FCF_ASYNC_SENT; rval = qla2x00_fabric_login(vha, fcport, next_loopid); if (rval == QLA_SUCCESS) { /* Send an ADISC to FCP2 devices.*/ @@ -3546,6 +3836,45 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) qla2x00_rport_del(fcport); } +void +qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct scsi_qla_host *vp, *base_vha = pci_get_drvdata(ha->pdev); + struct scsi_qla_host *tvp; + + vha->flags.online = 0; + ha->flags.chip_reset_done = 0; + clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + ha->qla_stats.total_isp_aborts++; + + qla_printk(KERN_INFO, ha, + "Performing ISP error recovery - ha= %p.\n", ha); + + /* Chip reset does not apply to 82XX */ + if (!IS_QLA82XX(ha)) + ha->isp_ops->reset_chip(vha); + + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + qla2x00_mark_all_devices_lost(vha, 0); + list_for_each_entry_safe(vp, tvp, &base_vha->hw->vp_list, list) + qla2x00_mark_all_devices_lost(vp, 0); + } else { + if (!atomic_read(&vha->loop_down_timer)) + atomic_set(&vha->loop_down_timer, + LOOP_DOWN_TIME); + } + + /* Make sure for ISP 82XX IO DMA is complete */ + if (IS_QLA82XX(ha)) + qla82xx_wait_for_pending_commands(vha); + + /* Requeue all commands in outstanding command list. */ + qla2x00_abort_all_cmds(vha, DID_RESET << 16); +} + /* * qla2x00_abort_isp * Resets ISP and aborts all outstanding commands. @@ -3567,27 +3896,7 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) struct req_que *req = ha->req_q_map[0]; if (vha->flags.online) { - vha->flags.online = 0; - ha->flags.chip_reset_done = 0; - clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); - ha->qla_stats.total_isp_aborts++; - - qla_printk(KERN_INFO, ha, - "Performing ISP error recovery - ha= %p.\n", ha); - ha->isp_ops->reset_chip(vha); - - atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); - if (atomic_read(&vha->loop_state) != LOOP_DOWN) { - atomic_set(&vha->loop_state, LOOP_DOWN); - qla2x00_mark_all_devices_lost(vha, 0); - } else { - if (!atomic_read(&vha->loop_down_timer)) - atomic_set(&vha->loop_down_timer, - LOOP_DOWN_TIME); - } - - /* Requeue all commands in outstanding command list. */ - qla2x00_abort_all_cmds(vha, DID_RESET << 16); + qla2x00_abort_isp_cleanup(vha); if (unlikely(pci_channel_offline(ha->pdev) && ha->flags.pci_channel_io_perm_failure)) { @@ -3843,6 +4152,9 @@ qla24xx_reset_adapter(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + if (IS_QLA82XX(ha)) + return; + vha->flags.online = 0; ha->isp_ops->disable_intrs(ha); @@ -3906,6 +4218,8 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) } ha->nvram_size = sizeof(struct nvram_24xx); ha->vpd_size = FA_NVRAM_VPD_SIZE; + if (IS_QLA82XX(ha)) + ha->vpd_size = FA_VPD_SIZE_82XX; /* Get VPD data into cache */ ha->vpd = ha->nvram + VPD_OFFSET; @@ -4769,7 +5083,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) * Setup driver NVRAM options. */ qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name), - "QLE81XX"); + "QLE8XXX"); /* Use alternate WWN? */ if (nv->host_p & __constant_cpu_to_le32(BIT_15)) { @@ -4892,6 +5206,114 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) return (rval); } +int +qla82xx_restart_isp(scsi_qla_host_t *vha) +{ + int status, rval; + uint32_t wait_time; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + struct rsp_que *rsp = ha->rsp_q_map[0]; + struct scsi_qla_host *vp; + struct scsi_qla_host *tvp; + + status = qla2x00_init_rings(vha); + if (!status) { + clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); + ha->flags.chip_reset_done = 1; + + status = qla2x00_fw_ready(vha); + if (!status) { + qla_printk(KERN_INFO, ha, + "%s(): Start configure loop, " + "status = %d\n", __func__, status); + + /* Issue a marker after FW becomes ready. */ + qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL); + + vha->flags.online = 1; + /* Wait at most MAX_TARGET RSCNs for a stable link. */ + wait_time = 256; + do { + clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + qla2x00_configure_loop(vha); + wait_time--; + } while (!atomic_read(&vha->loop_down_timer) && + !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) && + wait_time && + (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))); + } + + /* if no cable then assume it's good */ + if ((vha->device_flags & DFLG_NO_CABLE)) + status = 0; + + qla_printk(KERN_INFO, ha, + "%s(): Configure loop done, status = 0x%x\n", + __func__, status); + } + + if (!status) { + clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags); + + if (!atomic_read(&vha->loop_down_timer)) { + /* + * Issue marker command only when we are going + * to start the I/O . + */ + vha->marker_needed = 1; + } + + vha->flags.online = 1; + + ha->isp_ops->enable_intrs(ha); + + ha->isp_abort_cnt = 0; + clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags); + + if (ha->fce) { + ha->flags.fce_enabled = 1; + memset(ha->fce, 0, + fce_calc_size(ha->fce_bufs)); + rval = qla2x00_enable_fce_trace(vha, + ha->fce_dma, ha->fce_bufs, ha->fce_mb, + &ha->fce_bufs); + if (rval) { + qla_printk(KERN_WARNING, ha, + "Unable to reinitialize FCE " + "(%d).\n", rval); + ha->flags.fce_enabled = 0; + } + } + + if (ha->eft) { + memset(ha->eft, 0, EFT_SIZE); + rval = qla2x00_enable_eft_trace(vha, + ha->eft_dma, EFT_NUM_BUFFERS); + if (rval) { + qla_printk(KERN_WARNING, ha, + "Unable to reinitialize EFT " + "(%d).\n", rval); + } + } + } + + if (!status) { + DEBUG(printk(KERN_INFO + "qla82xx_restart_isp(%ld): succeeded.\n", + vha->host_no)); + list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) { + if (vp->vp_idx) + qla2x00_vp_abort_isp(vp); + } + } else { + qla_printk(KERN_INFO, ha, + "qla82xx_restart_isp: **** FAILED ****\n"); + } + + return status; +} + void qla81xx_update_fw_options(scsi_qla_host_t *vha) { @@ -4905,3 +5327,165 @@ qla81xx_update_fw_options(scsi_qla_host_t *vha) ha->fw_options[2] |= BIT_9; qla2x00_set_fw_options(vha, ha->fw_options); } + +/* + * qla24xx_get_fcp_prio + * Gets the fcp cmd priority value for the logged in port. + * Looks for a match of the port descriptors within + * each of the fcp prio config entries. If a match is found, + * the tag (priority) value is returned. + * + * Input: + * ha = adapter block po + * fcport = port structure pointer. + * + * Return: + * non-zero (if found) + * 0 (if not found) + * + * Context: + * Kernel context + */ +uint8_t +qla24xx_get_fcp_prio(scsi_qla_host_t *vha, fc_port_t *fcport) +{ + int i, entries; + uint8_t pid_match, wwn_match; + uint8_t priority; + uint32_t pid1, pid2; + uint64_t wwn1, wwn2; + struct qla_fcp_prio_entry *pri_entry; + struct qla_hw_data *ha = vha->hw; + + if (!ha->fcp_prio_cfg || !ha->flags.fcp_prio_enabled) + return 0; + + priority = 0; + entries = ha->fcp_prio_cfg->num_entries; + pri_entry = &ha->fcp_prio_cfg->entry[0]; + + for (i = 0; i < entries; i++) { + pid_match = wwn_match = 0; + + if (!(pri_entry->flags & FCP_PRIO_ENTRY_VALID)) { + pri_entry++; + continue; + } + + /* check source pid for a match */ + if (pri_entry->flags & FCP_PRIO_ENTRY_SPID_VALID) { + pid1 = pri_entry->src_pid & INVALID_PORT_ID; + pid2 = vha->d_id.b24 & INVALID_PORT_ID; + if (pid1 == INVALID_PORT_ID) + pid_match++; + else if (pid1 == pid2) + pid_match++; + } + + /* check destination pid for a match */ + if (pri_entry->flags & FCP_PRIO_ENTRY_DPID_VALID) { + pid1 = pri_entry->dst_pid & INVALID_PORT_ID; + pid2 = fcport->d_id.b24 & INVALID_PORT_ID; + if (pid1 == INVALID_PORT_ID) + pid_match++; + else if (pid1 == pid2) + pid_match++; + } + + /* check source WWN for a match */ + if (pri_entry->flags & FCP_PRIO_ENTRY_SWWN_VALID) { + wwn1 = wwn_to_u64(vha->port_name); + wwn2 = wwn_to_u64(pri_entry->src_wwpn); + if (wwn2 == (uint64_t)-1) + wwn_match++; + else if (wwn1 == wwn2) + wwn_match++; + } + + /* check destination WWN for a match */ + if (pri_entry->flags & FCP_PRIO_ENTRY_DWWN_VALID) { + wwn1 = wwn_to_u64(fcport->port_name); + wwn2 = wwn_to_u64(pri_entry->dst_wwpn); + if (wwn2 == (uint64_t)-1) + wwn_match++; + else if (wwn1 == wwn2) + wwn_match++; + } + + if (pid_match == 2 || wwn_match == 2) { + /* Found a matching entry */ + if (pri_entry->flags & FCP_PRIO_ENTRY_TAG_VALID) + priority = pri_entry->tag; + break; + } + + pri_entry++; + } + + return priority; +} + +/* + * qla24xx_update_fcport_fcp_prio + * Activates fcp priority for the logged in fc port + * + * Input: + * ha = adapter block pointer. + * fcp = port structure pointer. + * + * Return: + * QLA_SUCCESS or QLA_FUNCTION_FAILED + * + * Context: + * Kernel context. + */ +int +qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *ha, fc_port_t *fcport) +{ + int ret; + uint8_t priority; + uint16_t mb[5]; + + if (atomic_read(&fcport->state) == FCS_UNCONFIGURED || + fcport->port_type != FCT_TARGET || + fcport->loop_id == FC_NO_LOOP_ID) + return QLA_FUNCTION_FAILED; + + priority = qla24xx_get_fcp_prio(ha, fcport); + ret = qla24xx_set_fcp_prio(ha, fcport->loop_id, priority, mb); + if (ret == QLA_SUCCESS) + fcport->fcp_prio = priority; + else + DEBUG2(printk(KERN_WARNING + "scsi(%ld): Unable to activate fcp priority, " + " ret=0x%x\n", ha->host_no, ret)); + + return ret; +} + +/* + * qla24xx_update_all_fcp_prio + * Activates fcp priority for all the logged in ports + * + * Input: + * ha = adapter block pointer. + * + * Return: + * QLA_SUCCESS or QLA_FUNCTION_FAILED + * + * Context: + * Kernel context. + */ +int +qla24xx_update_all_fcp_prio(scsi_qla_host_t *vha) +{ + int ret; + fc_port_t *fcport; + + ret = QLA_FUNCTION_FAILED; + /* We need to set priority for all logged in ports */ + list_for_each_entry(fcport, &vha->vp_fcports, list) + ret = qla24xx_update_fcport_fcp_prio(vha, fcport); + + return ret; +} diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 5e0a7095c9f..84c2fea154d 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -37,7 +37,10 @@ qla2x00_poll(struct rsp_que *rsp) unsigned long flags; struct qla_hw_data *ha = rsp->hw; local_irq_save(flags); - ha->isp_ops->intr_handler(0, rsp); + if (IS_QLA82XX(ha)) + qla82xx_poll(0, rsp); + else + ha->isp_ops->intr_handler(0, rsp); local_irq_restore(flags); } @@ -64,3 +67,19 @@ qla2x00_is_reserved_id(scsi_qla_host_t *vha, uint16_t loop_id) return ((loop_id > ha->max_loop_id && loop_id < SNS_FIRST_LOOP_ID) || loop_id == MANAGEMENT_SERVER || loop_id == BROADCAST); } + +static inline void +qla2x00_clean_dsd_pool(struct qla_hw_data *ha, srb_t *sp) +{ + struct dsd_dma *dsd_ptr, *tdsd_ptr; + + /* clean up allocated prev pool */ + list_for_each_entry_safe(dsd_ptr, tdsd_ptr, + &((struct crc_context *)sp->ctx)->dsd_list, list) { + dma_pool_free(ha->dl_dma_pool, dsd_ptr->dsd_addr, + dsd_ptr->dsd_list_dma); + list_del(&dsd_ptr->list); + kfree(dsd_ptr); + } + INIT_LIST_HEAD(&((struct crc_context *)sp->ctx)->dsd_list); +} diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 8299a9891bf..8ef94536541 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -145,7 +145,49 @@ qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha) return (cont_pkt); } -/** +static inline int +qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts) +{ + uint8_t guard = scsi_host_get_guard(sp->cmd->device->host); + + /* We only support T10 DIF right now */ + if (guard != SHOST_DIX_GUARD_CRC) { + DEBUG2(printk(KERN_ERR "Unsupported guard: %d\n", guard)); + return 0; + } + + /* We always use DIFF Bundling for best performance */ + *fw_prot_opts = 0; + + /* Translate SCSI opcode to a protection opcode */ + switch (scsi_get_prot_op(sp->cmd)) { + case SCSI_PROT_READ_STRIP: + *fw_prot_opts |= PO_MODE_DIF_REMOVE; + break; + case SCSI_PROT_WRITE_INSERT: + *fw_prot_opts |= PO_MODE_DIF_INSERT; + break; + case SCSI_PROT_READ_INSERT: + *fw_prot_opts |= PO_MODE_DIF_INSERT; + break; + case SCSI_PROT_WRITE_STRIP: + *fw_prot_opts |= PO_MODE_DIF_REMOVE; + break; + case SCSI_PROT_READ_PASS: + *fw_prot_opts |= PO_MODE_DIF_PASS; + break; + case SCSI_PROT_WRITE_PASS: + *fw_prot_opts |= PO_MODE_DIF_PASS; + break; + default: /* Normal Request */ + *fw_prot_opts |= PO_MODE_DIF_PASS; + break; + } + + return scsi_prot_sg_count(sp->cmd); +} + +/* * qla2x00_build_scsi_iocbs_32() - Build IOCB command utilizing 32bit * capable IOCB types. * @@ -506,7 +548,10 @@ qla2x00_req_pkt(struct scsi_qla_host *vha, struct req_que *req, cnt = (uint16_t) RD_REG_DWORD(®->isp25mq.req_q_out); else { - if (IS_FWI2_CAPABLE(ha)) + if (IS_QLA82XX(ha)) + cnt = (uint16_t)RD_REG_DWORD( + ®->isp82.req_q_out); + else if (IS_FWI2_CAPABLE(ha)) cnt = (uint16_t)RD_REG_DWORD( ®->isp24.req_q_out); else @@ -579,11 +624,29 @@ qla2x00_isp_cmd(struct scsi_qla_host *vha, struct req_que *req) req->ring_ptr++; /* Set chip new ring index. */ - if (ha->mqenable) { + if (IS_QLA82XX(ha)) { + uint32_t dbval = 0x04 | (ha->portnum << 5); + + /* write, read and verify logic */ + dbval = dbval | (req->id << 8) | (req->ring_index << 16); + if (ql2xdbwr) + qla82xx_wr_32(ha, ha->nxdb_wr_ptr, dbval); + else { + WRT_REG_DWORD( + (unsigned long __iomem *)ha->nxdb_wr_ptr, + dbval); + wmb(); + while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) { + WRT_REG_DWORD((unsigned long __iomem *) + ha->nxdb_wr_ptr, dbval); + wmb(); + } + } + } else if (ha->mqenable) { + /* Set chip new ring index. */ WRT_REG_DWORD(®->isp25mq.req_q_in, req->ring_index); RD_REG_DWORD(&ioreg->hccr); - } - else { + } else { if (IS_FWI2_CAPABLE(ha)) { WRT_REG_DWORD(®->isp24.req_q_in, req->ring_index); RD_REG_DWORD_RELAXED(®->isp24.req_q_in); @@ -604,7 +667,7 @@ qla2x00_isp_cmd(struct scsi_qla_host *vha, struct req_que *req) * * Returns the number of IOCB entries needed to store @dsds. */ -static inline uint16_t +inline uint16_t qla24xx_calc_iocbs(uint16_t dsds) { uint16_t iocbs; @@ -615,6 +678,8 @@ qla24xx_calc_iocbs(uint16_t dsds) if ((dsds - 1) % 5) iocbs++; } + DEBUG3(printk(KERN_DEBUG "%s(): Required PKT(s) = %d\n", + __func__, iocbs)); return iocbs; } @@ -626,7 +691,7 @@ qla24xx_calc_iocbs(uint16_t dsds) * @cmd_pkt: Command type 3 IOCB * @tot_dsds: Total number of segments to transfer */ -static inline void +inline void qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, uint16_t tot_dsds) { @@ -695,6 +760,453 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, } } +struct fw_dif_context { + uint32_t ref_tag; + uint16_t app_tag; + uint8_t ref_tag_mask[4]; /* Validation/Replacement Mask*/ + uint8_t app_tag_mask[2]; /* Validation/Replacement Mask*/ +}; + +/* + * qla24xx_set_t10dif_tags_from_cmd - Extract Ref and App tags from SCSI command + * + */ +static inline void +qla24xx_set_t10dif_tags(struct scsi_cmnd *cmd, struct fw_dif_context *pkt, + unsigned int protcnt) +{ + struct sd_dif_tuple *spt; + unsigned char op = scsi_get_prot_op(cmd); + + switch (scsi_get_prot_type(cmd)) { + /* For TYPE 0 protection: no checking */ + case SCSI_PROT_DIF_TYPE0: + pkt->ref_tag_mask[0] = 0x00; + pkt->ref_tag_mask[1] = 0x00; + pkt->ref_tag_mask[2] = 0x00; + pkt->ref_tag_mask[3] = 0x00; + break; + + /* + * For TYPE 2 protection: 16 bit GUARD + 32 bit REF tag has to + * match LBA in CDB + N + */ + case SCSI_PROT_DIF_TYPE2: + break; + + /* For Type 3 protection: 16 bit GUARD only */ + case SCSI_PROT_DIF_TYPE3: + pkt->ref_tag_mask[0] = pkt->ref_tag_mask[1] = + pkt->ref_tag_mask[2] = pkt->ref_tag_mask[3] = + 0x00; + break; + + /* + * For TYpe 1 protection: 16 bit GUARD tag, 32 bit REF tag, and + * 16 bit app tag. + */ + case SCSI_PROT_DIF_TYPE1: + if (!ql2xenablehba_err_chk) + break; + + if (protcnt && (op == SCSI_PROT_WRITE_STRIP || + op == SCSI_PROT_WRITE_PASS)) { + spt = page_address(sg_page(scsi_prot_sglist(cmd))) + + scsi_prot_sglist(cmd)[0].offset; + DEBUG18(printk(KERN_DEBUG + "%s(): LBA from user %p, lba = 0x%x\n", + __func__, spt, (int)spt->ref_tag)); + pkt->ref_tag = swab32(spt->ref_tag); + pkt->app_tag_mask[0] = 0x0; + pkt->app_tag_mask[1] = 0x0; + } else { + pkt->ref_tag = cpu_to_le32((uint32_t) + (0xffffffff & scsi_get_lba(cmd))); + pkt->app_tag = __constant_cpu_to_le16(0); + pkt->app_tag_mask[0] = 0x0; + pkt->app_tag_mask[1] = 0x0; + } + /* enable ALL bytes of the ref tag */ + pkt->ref_tag_mask[0] = 0xff; + pkt->ref_tag_mask[1] = 0xff; + pkt->ref_tag_mask[2] = 0xff; + pkt->ref_tag_mask[3] = 0xff; + break; + } + + DEBUG18(printk(KERN_DEBUG + "%s(): Setting protection Tags: (BIG) ref tag = 0x%x," + " app tag = 0x%x, prot SG count %d , cmd lba 0x%x," + " prot_type=%u\n", __func__, pkt->ref_tag, pkt->app_tag, protcnt, + (int)scsi_get_lba(cmd), scsi_get_prot_type(cmd))); +} + + +static int +qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, + uint16_t tot_dsds) +{ + void *next_dsd; + uint8_t avail_dsds = 0; + uint32_t dsd_list_len; + struct dsd_dma *dsd_ptr; + struct scatterlist *sg; + uint32_t *cur_dsd = dsd; + int i; + uint16_t used_dsds = tot_dsds; + + uint8_t *cp; + + scsi_for_each_sg(sp->cmd, sg, tot_dsds, i) { + dma_addr_t sle_dma; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : used_dsds; + dsd_list_len = (avail_dsds + 1) * 12; + used_dsds -= avail_dsds; + + /* allocate tracking DS */ + dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC); + if (!dsd_ptr) + return 1; + + /* allocate new list */ + dsd_ptr->dsd_addr = next_dsd = + dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, + &dsd_ptr->dsd_list_dma); + + if (!next_dsd) { + /* + * Need to cleanup only this dsd_ptr, rest + * will be done by sp_free_dma() + */ + kfree(dsd_ptr); + return 1; + } + + list_add_tail(&dsd_ptr->list, + &((struct crc_context *)sp->ctx)->dsd_list); + + sp->flags |= SRB_CRC_CTX_DSD_VALID; + + /* add new list to cmd iocb or last list */ + *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = dsd_list_len; + cur_dsd = (uint32_t *)next_dsd; + } + sle_dma = sg_dma_address(sg); + DEBUG18(printk("%s(): %p, sg entry %d - addr =0x%x 0x%x," + " len =%d\n", __func__ , cur_dsd, i, LSD(sle_dma), + MSD(sle_dma), sg_dma_len(sg))); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + avail_dsds--; + + if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) { + cp = page_address(sg_page(sg)) + sg->offset; + DEBUG18(printk("%s(): User Data buffer= %p:\n", + __func__ , cp)); + } + } + /* Null termination */ + *cur_dsd++ = 0; + *cur_dsd++ = 0; + *cur_dsd++ = 0; + return 0; +} + +static int +qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, + uint32_t *dsd, + uint16_t tot_dsds) +{ + void *next_dsd; + uint8_t avail_dsds = 0; + uint32_t dsd_list_len; + struct dsd_dma *dsd_ptr; + struct scatterlist *sg; + int i; + struct scsi_cmnd *cmd; + uint32_t *cur_dsd = dsd; + uint16_t used_dsds = tot_dsds; + + uint8_t *cp; + + + cmd = sp->cmd; + scsi_for_each_prot_sg(cmd, sg, tot_dsds, i) { + dma_addr_t sle_dma; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : used_dsds; + dsd_list_len = (avail_dsds + 1) * 12; + used_dsds -= avail_dsds; + + /* allocate tracking DS */ + dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC); + if (!dsd_ptr) + return 1; + + /* allocate new list */ + dsd_ptr->dsd_addr = next_dsd = + dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, + &dsd_ptr->dsd_list_dma); + + if (!next_dsd) { + /* + * Need to cleanup only this dsd_ptr, rest + * will be done by sp_free_dma() + */ + kfree(dsd_ptr); + return 1; + } + + list_add_tail(&dsd_ptr->list, + &((struct crc_context *)sp->ctx)->dsd_list); + + sp->flags |= SRB_CRC_CTX_DSD_VALID; + + /* add new list to cmd iocb or last list */ + *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = dsd_list_len; + cur_dsd = (uint32_t *)next_dsd; + } + sle_dma = sg_dma_address(sg); + if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) { + DEBUG18(printk(KERN_DEBUG + "%s(): %p, sg entry %d - addr =0x%x" + "0x%x, len =%d\n", __func__ , cur_dsd, i, + LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg))); + } + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + + if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) { + cp = page_address(sg_page(sg)) + sg->offset; + DEBUG18(printk("%s(): Protection Data buffer = %p:\n", + __func__ , cp)); + } + avail_dsds--; + } + /* Null termination */ + *cur_dsd++ = 0; + *cur_dsd++ = 0; + *cur_dsd++ = 0; + return 0; +} + +/** + * qla24xx_build_scsi_crc_2_iocbs() - Build IOCB command utilizing Command + * Type 6 IOCB types. + * + * @sp: SRB command to process + * @cmd_pkt: Command type 3 IOCB + * @tot_dsds: Total number of segments to transfer + */ +static inline int +qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, + uint16_t tot_dsds, uint16_t tot_prot_dsds, uint16_t fw_prot_opts) +{ + uint32_t *cur_dsd, *fcp_dl; + scsi_qla_host_t *vha; + struct scsi_cmnd *cmd; + struct scatterlist *cur_seg; + int sgc; + uint32_t total_bytes; + uint32_t data_bytes; + uint32_t dif_bytes; + uint8_t bundling = 1; + uint16_t blk_size; + uint8_t *clr_ptr; + struct crc_context *crc_ctx_pkt = NULL; + struct qla_hw_data *ha; + uint8_t additional_fcpcdb_len; + uint16_t fcp_cmnd_len; + struct fcp_cmnd *fcp_cmnd; + dma_addr_t crc_ctx_dma; + + cmd = sp->cmd; + + sgc = 0; + /* Update entry type to indicate Command Type CRC_2 IOCB */ + *((uint32_t *)(&cmd_pkt->entry_type)) = + __constant_cpu_to_le32(COMMAND_TYPE_CRC_2); + + /* No data transfer */ + data_bytes = scsi_bufflen(cmd); + if (!data_bytes || cmd->sc_data_direction == DMA_NONE) { + DEBUG18(printk(KERN_INFO "%s: Zero data bytes or DMA-NONE %d\n", + __func__, data_bytes)); + cmd_pkt->byte_count = __constant_cpu_to_le32(0); + return QLA_SUCCESS; + } + + vha = sp->fcport->vha; + ha = vha->hw; + + DEBUG18(printk(KERN_DEBUG + "%s(%ld): Executing cmd sp %p, pid=%ld, prot_op=%u.\n", __func__, + vha->host_no, sp, cmd->serial_number, scsi_get_prot_op(sp->cmd))); + + cmd_pkt->vp_index = sp->fcport->vp_idx; + + /* Set transfer direction */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) { + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_WRITE_DATA); + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_READ_DATA); + } + + tot_prot_dsds = scsi_prot_sg_count(cmd); + if (!tot_prot_dsds) + bundling = 0; + + /* Allocate CRC context from global pool */ + crc_ctx_pkt = sp->ctx = dma_pool_alloc(ha->dl_dma_pool, + GFP_ATOMIC, &crc_ctx_dma); + + if (!crc_ctx_pkt) + goto crc_queuing_error; + + /* Zero out CTX area. */ + clr_ptr = (uint8_t *)crc_ctx_pkt; + memset(clr_ptr, 0, sizeof(*crc_ctx_pkt)); + + crc_ctx_pkt->crc_ctx_dma = crc_ctx_dma; + + sp->flags |= SRB_CRC_CTX_DMA_VALID; + + /* Set handle */ + crc_ctx_pkt->handle = cmd_pkt->handle; + + INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list); + + qla24xx_set_t10dif_tags(cmd, (struct fw_dif_context *) + &crc_ctx_pkt->ref_tag, tot_prot_dsds); + + cmd_pkt->crc_context_address[0] = cpu_to_le32(LSD(crc_ctx_dma)); + cmd_pkt->crc_context_address[1] = cpu_to_le32(MSD(crc_ctx_dma)); + cmd_pkt->crc_context_len = CRC_CONTEXT_LEN_FW; + + /* Determine SCSI command length -- align to 4 byte boundary */ + if (cmd->cmd_len > 16) { + DEBUG18(printk(KERN_INFO "%s(): **** SCSI CMD > 16\n", + __func__)); + additional_fcpcdb_len = cmd->cmd_len - 16; + if ((cmd->cmd_len % 4) != 0) { + /* SCSI cmd > 16 bytes must be multiple of 4 */ + goto crc_queuing_error; + } + fcp_cmnd_len = 12 + cmd->cmd_len + 4; + } else { + additional_fcpcdb_len = 0; + fcp_cmnd_len = 12 + 16 + 4; + } + + fcp_cmnd = &crc_ctx_pkt->fcp_cmnd; + + fcp_cmnd->additional_cdb_len = additional_fcpcdb_len; + if (cmd->sc_data_direction == DMA_TO_DEVICE) + fcp_cmnd->additional_cdb_len |= 1; + else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + fcp_cmnd->additional_cdb_len |= 2; + + int_to_scsilun(sp->cmd->device->lun, &fcp_cmnd->lun); + memcpy(fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len); + cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(fcp_cmnd_len); + cmd_pkt->fcp_cmnd_dseg_address[0] = cpu_to_le32( + LSD(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF)); + cmd_pkt->fcp_cmnd_dseg_address[1] = cpu_to_le32( + MSD(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF)); + fcp_cmnd->task_attribute = 0; + fcp_cmnd->task_managment = 0; + + cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */ + + DEBUG18(printk(KERN_INFO "%s(%ld): Total SG(s) Entries %d, Data" + "entries %d, data bytes %d, Protection entries %d\n", + __func__, vha->host_no, tot_dsds, (tot_dsds-tot_prot_dsds), + data_bytes, tot_prot_dsds)); + + /* Compute dif len and adjust data len to incude protection */ + total_bytes = data_bytes; + dif_bytes = 0; + blk_size = cmd->device->sector_size; + if (scsi_get_prot_type(cmd) == SCSI_PROT_DIF_TYPE1) { + dif_bytes = (data_bytes / blk_size) * 8; + total_bytes += dif_bytes; + } + + if (!ql2xenablehba_err_chk) + fw_prot_opts |= 0x10; /* Disable Guard tag checking */ + + if (!bundling) { + cur_dsd = (uint32_t *) &crc_ctx_pkt->u.nobundling.data_address; + } else { + /* + * Configure Bundling if we need to fetch interlaving + * protection PCI accesses + */ + fw_prot_opts |= PO_ENABLE_DIF_BUNDLING; + crc_ctx_pkt->u.bundling.dif_byte_count = cpu_to_le32(dif_bytes); + crc_ctx_pkt->u.bundling.dseg_count = cpu_to_le16(tot_dsds - + tot_prot_dsds); + cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.data_address; + } + + /* Finish the common fields of CRC pkt */ + crc_ctx_pkt->blk_size = cpu_to_le16(blk_size); + crc_ctx_pkt->prot_opts = cpu_to_le16(fw_prot_opts); + crc_ctx_pkt->byte_count = cpu_to_le32(data_bytes); + crc_ctx_pkt->guard_seed = __constant_cpu_to_le16(0); + /* Fibre channel byte count */ + cmd_pkt->byte_count = cpu_to_le32(total_bytes); + fcp_dl = (uint32_t *)(crc_ctx_pkt->fcp_cmnd.cdb + 16 + + additional_fcpcdb_len); + *fcp_dl = htonl(total_bytes); + + DEBUG18(printk(KERN_INFO "%s(%ld): dif bytes = 0x%x (%d), total bytes" + " = 0x%x (%d), dat block size =0x%x (%d)\n", __func__, + vha->host_no, dif_bytes, dif_bytes, total_bytes, total_bytes, + crc_ctx_pkt->blk_size, crc_ctx_pkt->blk_size)); + + /* Walks data segments */ + + cmd_pkt->control_flags |= + __constant_cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE); + if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd, + (tot_dsds - tot_prot_dsds))) + goto crc_queuing_error; + + if (bundling && tot_prot_dsds) { + /* Walks dif segments */ + cur_seg = scsi_prot_sglist(cmd); + cmd_pkt->control_flags |= + __constant_cpu_to_le16(CF_DIF_SEG_DESCR_ENABLE); + cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.dif_address; + if (qla24xx_walk_and_build_prot_sglist(ha, sp, cur_dsd, + tot_prot_dsds)) + goto crc_queuing_error; + } + return QLA_SUCCESS; + +crc_queuing_error: + DEBUG18(qla_printk(KERN_INFO, ha, + "CMD sent FAILED crc_q error:sp = %p\n", sp)); + /* Cleanup will be performed by the caller */ + + return QLA_FUNCTION_FAILED; +} /** * qla24xx_start_scsi() - Send a SCSI command to the ISP @@ -848,6 +1360,191 @@ queuing_error: return QLA_FUNCTION_FAILED; } + +/** + * qla24xx_dif_start_scsi() - Send a SCSI command to the ISP + * @sp: command to send to the ISP + * + * Returns non-zero if a failure occurred, else zero. + */ +int +qla24xx_dif_start_scsi(srb_t *sp) +{ + int nseg; + unsigned long flags; + uint32_t *clr_ptr; + uint32_t index; + uint32_t handle; + uint16_t cnt; + uint16_t req_cnt = 0; + uint16_t tot_dsds; + uint16_t tot_prot_dsds; + uint16_t fw_prot_opts = 0; + struct req_que *req = NULL; + struct rsp_que *rsp = NULL; + struct scsi_cmnd *cmd = sp->cmd; + struct scsi_qla_host *vha = sp->fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct cmd_type_crc_2 *cmd_pkt; + uint32_t status = 0; + +#define QDSS_GOT_Q_SPACE BIT_0 + + /* Only process protection in this routine */ + if (scsi_get_prot_op(cmd) == SCSI_PROT_NORMAL) + return qla24xx_start_scsi(sp); + + /* Setup device pointers. */ + + qla25xx_set_que(sp, &rsp); + req = vha->req; + + /* So we know we haven't pci_map'ed anything yet */ + tot_dsds = 0; + + /* Send marker if required */ + if (vha->marker_needed != 0) { + if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) != + QLA_SUCCESS) + return QLA_FUNCTION_FAILED; + vha->marker_needed = 0; + } + + /* Acquire ring specific lock */ + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* Check for room in outstanding command list. */ + handle = req->current_outstanding_cmd; + for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { + handle++; + if (handle == MAX_OUTSTANDING_COMMANDS) + handle = 1; + if (!req->outstanding_cmds[handle]) + break; + } + + if (index == MAX_OUTSTANDING_COMMANDS) + goto queuing_error; + + /* Compute number of required data segments */ + /* Map the sg table so we have an accurate count of sg entries needed */ + if (scsi_sg_count(cmd)) { + nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd), + scsi_sg_count(cmd), cmd->sc_data_direction); + if (unlikely(!nseg)) + goto queuing_error; + else + sp->flags |= SRB_DMA_VALID; + } else + nseg = 0; + + /* number of required data segments */ + tot_dsds = nseg; + + /* Compute number of required protection segments */ + if (qla24xx_configure_prot_mode(sp, &fw_prot_opts)) { + nseg = dma_map_sg(&ha->pdev->dev, scsi_prot_sglist(cmd), + scsi_prot_sg_count(cmd), cmd->sc_data_direction); + if (unlikely(!nseg)) + goto queuing_error; + else + sp->flags |= SRB_CRC_PROT_DMA_VALID; + } else { + nseg = 0; + } + + req_cnt = 1; + /* Total Data and protection sg segment(s) */ + tot_prot_dsds = nseg; + tot_dsds += nseg; + if (req->cnt < (req_cnt + 2)) { + cnt = RD_REG_DWORD_RELAXED(req->req_q_out); + + if (req->ring_index < cnt) + req->cnt = cnt - req->ring_index; + else + req->cnt = req->length - + (req->ring_index - cnt); + } + + if (req->cnt < (req_cnt + 2)) + goto queuing_error; + + status |= QDSS_GOT_Q_SPACE; + + /* Build header part of command packet (excluding the OPCODE). */ + req->current_outstanding_cmd = handle; + req->outstanding_cmds[handle] = sp; + sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle; + req->cnt -= req_cnt; + + /* Fill-in common area */ + cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr; + cmd_pkt->handle = MAKE_HANDLE(req->id, handle); + + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + + /* Set NPORT-ID and LUN number*/ + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + + int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); + + /* Total Data and protection segment(s) */ + cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + + /* Build IOCB segments and adjust for data protection segments */ + if (qla24xx_build_scsi_crc_2_iocbs(sp, (struct cmd_type_crc_2 *) + req->ring_ptr, tot_dsds, tot_prot_dsds, fw_prot_opts) != + QLA_SUCCESS) + goto queuing_error; + + cmd_pkt->entry_count = (uint8_t)req_cnt; + /* Specify response queue number where completion should happen */ + cmd_pkt->entry_status = (uint8_t) rsp->id; + cmd_pkt->timeout = __constant_cpu_to_le16(0); + wmb(); + + /* Adjust ring index. */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else + req->ring_ptr++; + + /* Set chip new ring index. */ + WRT_REG_DWORD(req->req_q_in, req->ring_index); + RD_REG_DWORD_RELAXED(&ha->iobase->isp24.hccr); + + /* Manage unprocessed RIO/ZIO commands in response queue. */ + if (vha->flags.process_response_queue && + rsp->ring_ptr->signature != RESPONSE_PROCESSED) + qla24xx_process_response_queue(vha, rsp); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return QLA_SUCCESS; + +queuing_error: + if (status & QDSS_GOT_Q_SPACE) { + req->outstanding_cmds[handle] = NULL; + req->cnt += req_cnt; + } + /* Cleanup will be performed by the caller (queuecommand) */ + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + DEBUG18(qla_printk(KERN_INFO, ha, + "CMD sent FAILED SCSI prot_op:%02x\n", scsi_get_prot_op(cmd))); + return QLA_FUNCTION_FAILED; +} + + static void qla25xx_set_que(srb_t *sp, struct rsp_que **rsp) { struct scsi_cmnd *cmd = sp->cmd; @@ -931,37 +1628,45 @@ qla2x00_start_iocbs(srb_t *sp) device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id); struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp; - /* Adjust ring index. */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else - req->ring_ptr++; - - /* Set chip new ring index. */ - if (ha->mqenable) { - WRT_REG_DWORD(®->isp25mq.req_q_in, req->ring_index); - RD_REG_DWORD(&ioreg->hccr); - } else if (IS_FWI2_CAPABLE(ha)) { - WRT_REG_DWORD(®->isp24.req_q_in, req->ring_index); - RD_REG_DWORD_RELAXED(®->isp24.req_q_in); + if (IS_QLA82XX(ha)) { + qla82xx_start_iocbs(sp); } else { - WRT_REG_WORD(ISP_REQ_Q_IN(ha, ®->isp), req->ring_index); - RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, ®->isp)); + /* Adjust ring index. */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else + req->ring_ptr++; + + /* Set chip new ring index. */ + if (ha->mqenable) { + WRT_REG_DWORD(®->isp25mq.req_q_in, req->ring_index); + RD_REG_DWORD(&ioreg->hccr); + } else if (IS_QLA82XX(ha)) { + qla82xx_start_iocbs(sp); + } else if (IS_FWI2_CAPABLE(ha)) { + WRT_REG_DWORD(®->isp24.req_q_in, req->ring_index); + RD_REG_DWORD_RELAXED(®->isp24.req_q_in); + } else { + WRT_REG_WORD(ISP_REQ_Q_IN(ha, ®->isp), + req->ring_index); + RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, ®->isp)); + } } } static void qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio) { - struct srb_logio *lio = sp->ctx; + struct srb_ctx *ctx = sp->ctx; + struct srb_iocb *lio = ctx->u.iocb_cmd; logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; logio->control_flags = cpu_to_le16(LCF_COMMAND_PLOGI); - if (lio->flags & SRB_LOGIN_COND_PLOGI) + if (lio->u.logio.flags & SRB_LOGIN_COND_PLOGI) logio->control_flags |= cpu_to_le16(LCF_COND_PLOGI); - if (lio->flags & SRB_LOGIN_SKIP_PRLI) + if (lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI) logio->control_flags |= cpu_to_le16(LCF_SKIP_PRLI); logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); logio->port_id[0] = sp->fcport->d_id.b.al_pa; @@ -974,14 +1679,15 @@ static void qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx) { struct qla_hw_data *ha = sp->fcport->vha->hw; - struct srb_logio *lio = sp->ctx; + struct srb_ctx *ctx = sp->ctx; + struct srb_iocb *lio = ctx->u.iocb_cmd; uint16_t opts; mbx->entry_type = MBX_IOCB_TYPE;; SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id); mbx->mb0 = cpu_to_le16(MBC_LOGIN_FABRIC_PORT); - opts = lio->flags & SRB_LOGIN_COND_PLOGI ? BIT_0: 0; - opts |= lio->flags & SRB_LOGIN_SKIP_PRLI ? BIT_1: 0; + opts = lio->u.logio.flags & SRB_LOGIN_COND_PLOGI ? BIT_0 : 0; + opts |= lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI ? BIT_1 : 0; if (HAS_EXTENDED_IDS(ha)) { mbx->mb1 = cpu_to_le16(sp->fcport->loop_id); mbx->mb10 = cpu_to_le16(opts); @@ -1026,9 +1732,97 @@ qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx) } static void +qla24xx_adisc_iocb(srb_t *sp, struct logio_entry_24xx *logio) +{ + logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; + logio->control_flags = cpu_to_le16(LCF_COMMAND_ADISC); + logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); + logio->vp_index = sp->fcport->vp_idx; +} + +static void +qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx) +{ + struct qla_hw_data *ha = sp->fcport->vha->hw; + + mbx->entry_type = MBX_IOCB_TYPE; + SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id); + mbx->mb0 = cpu_to_le16(MBC_GET_PORT_DATABASE); + if (HAS_EXTENDED_IDS(ha)) { + mbx->mb1 = cpu_to_le16(sp->fcport->loop_id); + mbx->mb10 = cpu_to_le16(BIT_0); + } else { + mbx->mb1 = cpu_to_le16((sp->fcport->loop_id << 8) | BIT_0); + } + mbx->mb2 = cpu_to_le16(MSW(ha->async_pd_dma)); + mbx->mb3 = cpu_to_le16(LSW(ha->async_pd_dma)); + mbx->mb6 = cpu_to_le16(MSW(MSD(ha->async_pd_dma))); + mbx->mb7 = cpu_to_le16(LSW(MSD(ha->async_pd_dma))); + mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx); +} + +static void +qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk) +{ + uint32_t flags; + unsigned int lun; + struct fc_port *fcport = sp->fcport; + scsi_qla_host_t *vha = fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct srb_ctx *ctx = sp->ctx; + struct srb_iocb *iocb = ctx->u.iocb_cmd; + struct req_que *req = vha->req; + + flags = iocb->u.tmf.flags; + lun = iocb->u.tmf.lun; + + tsk->entry_type = TSK_MGMT_IOCB_TYPE; + tsk->entry_count = 1; + tsk->handle = MAKE_HANDLE(req->id, tsk->handle); + tsk->nport_handle = cpu_to_le16(fcport->loop_id); + tsk->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); + tsk->control_flags = cpu_to_le32(flags); + tsk->port_id[0] = fcport->d_id.b.al_pa; + tsk->port_id[1] = fcport->d_id.b.area; + tsk->port_id[2] = fcport->d_id.b.domain; + tsk->vp_index = fcport->vp_idx; + + if (flags == TCF_LUN_RESET) { + int_to_scsilun(lun, &tsk->lun); + host_to_fcp_swap((uint8_t *)&tsk->lun, + sizeof(tsk->lun)); + } +} + +static void +qla24xx_marker_iocb(srb_t *sp, struct mrk_entry_24xx *mrk) +{ + uint16_t lun; + uint8_t modif; + struct fc_port *fcport = sp->fcport; + scsi_qla_host_t *vha = fcport->vha; + struct srb_ctx *ctx = sp->ctx; + struct srb_iocb *iocb = ctx->u.iocb_cmd; + struct req_que *req = vha->req; + + lun = iocb->u.marker.lun; + modif = iocb->u.marker.modif; + mrk->entry_type = MARKER_TYPE; + mrk->modifier = modif; + if (modif != MK_SYNC_ALL) { + mrk->nport_handle = cpu_to_le16(fcport->loop_id); + mrk->lun[1] = LSB(lun); + mrk->lun[2] = MSB(lun); + host_to_fcp_swap(mrk->lun, sizeof(mrk->lun)); + mrk->vp_index = vha->vp_idx; + mrk->handle = MAKE_HANDLE(req->id, mrk->handle); + } +} + +static void qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) { - struct fc_bsg_job *bsg_job = ((struct srb_bsg*)sp->ctx)->bsg_job; + struct fc_bsg_job *bsg_job = ((struct srb_ctx *)sp->ctx)->u.bsg_job; els_iocb->entry_type = ELS_IOCB_TYPE; els_iocb->entry_count = 1; @@ -1041,8 +1835,10 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) els_iocb->sof_type = EST_SOFI3; els_iocb->rx_dsd_count = __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt); - els_iocb->opcode =(((struct srb_bsg*)sp->ctx)->ctx.type == SRB_ELS_CMD_RPT) ? - bsg_job->request->rqst_data.r_els.els_code : bsg_job->request->rqst_data.h_els.command_code; + els_iocb->opcode = + (((struct srb_ctx *)sp->ctx)->type == SRB_ELS_CMD_RPT) ? + bsg_job->request->rqst_data.r_els.els_code : + bsg_job->request->rqst_data.h_els.command_code; els_iocb->port_id[0] = sp->fcport->d_id.b.al_pa; els_iocb->port_id[1] = sp->fcport->d_id.b.area; els_iocb->port_id[2] = sp->fcport->d_id.b.domain; @@ -1076,7 +1872,7 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) int index; uint16_t tot_dsds; scsi_qla_host_t *vha = sp->fcport->vha; - struct fc_bsg_job *bsg_job = ((struct srb_bsg*)sp->ctx)->bsg_job; + struct fc_bsg_job *bsg_job = ((struct srb_ctx *)sp->ctx)->u.bsg_job; int loop_iterartion = 0; int cont_iocb_prsnt = 0; int entry_count = 1; @@ -1157,12 +1953,12 @@ qla2x00_start_sp(srb_t *sp) switch (ctx->type) { case SRB_LOGIN_CMD: IS_FWI2_CAPABLE(ha) ? - qla24xx_login_iocb(sp, pkt): + qla24xx_login_iocb(sp, pkt) : qla2x00_login_iocb(sp, pkt); break; case SRB_LOGOUT_CMD: IS_FWI2_CAPABLE(ha) ? - qla24xx_logout_iocb(sp, pkt): + qla24xx_logout_iocb(sp, pkt) : qla2x00_logout_iocb(sp, pkt); break; case SRB_ELS_CMD_RPT: @@ -1172,6 +1968,17 @@ qla2x00_start_sp(srb_t *sp) case SRB_CT_CMD: qla24xx_ct_iocb(sp, pkt); break; + case SRB_ADISC_CMD: + IS_FWI2_CAPABLE(ha) ? + qla24xx_adisc_iocb(sp, pkt) : + qla2x00_adisc_iocb(sp, pkt); + break; + case SRB_TM_CMD: + qla24xx_tm_iocb(sp, pkt); + break; + case SRB_MARKER_CMD: + qla24xx_marker_iocb(sp, pkt); + break; default: break; } diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index db539b0c3da..be3d8bed2ec 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -10,6 +10,7 @@ #include <linux/slab.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_bsg_fc.h> +#include <scsi/scsi_eh.h> static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t); static void qla2x00_process_completed_request(struct scsi_qla_host *, @@ -326,7 +327,7 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) /* Setup to process RIO completion. */ handle_cnt = 0; - if (IS_QLA81XX(ha)) + if (IS_QLA8XXX_TYPE(ha)) goto skip_rio; switch (mb[0]) { case MBA_SCSI_COMPLETION: @@ -544,7 +545,7 @@ skip_rio: if (IS_QLA2100(ha)) break; - if (IS_QLA81XX(ha)) + if (IS_QLA8XXX_TYPE(ha)) DEBUG2(printk("scsi(%ld): DCBX Completed -- %04x %04x " "%04x\n", vha->host_no, mb[1], mb[2], mb[3])); else @@ -845,7 +846,7 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha, qla2x00_sp_compl(ha, sp); } else { DEBUG2(printk("scsi(%ld) Req:%d: Invalid ISP SCSI completion" - " handle(%d)\n", vha->host_no, req->id, index)); + " handle(0x%x)\n", vha->host_no, req->id, index)); qla_printk(KERN_WARNING, ha, "Invalid ISP SCSI completion handle\n"); @@ -895,36 +896,26 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, { const char func[] = "MBX-IOCB"; const char *type; - struct qla_hw_data *ha = vha->hw; fc_port_t *fcport; srb_t *sp; - struct srb_logio *lio; - uint16_t data[2]; + struct srb_iocb *lio; + struct srb_ctx *ctx; + uint16_t *data; + uint16_t status; sp = qla2x00_get_sp_from_handle(vha, func, req, mbx); if (!sp) return; - type = NULL; - lio = sp->ctx; - switch (lio->ctx.type) { - case SRB_LOGIN_CMD: - type = "login"; - break; - case SRB_LOGOUT_CMD: - type = "logout"; - break; - default: - qla_printk(KERN_WARNING, ha, - "%s: Unrecognized SRB: (%p) type=%d.\n", func, sp, - lio->ctx.type); - return; - } - - del_timer(&lio->ctx.timer); + ctx = sp->ctx; + lio = ctx->u.iocb_cmd; + type = ctx->name; fcport = sp->fcport; + data = lio->u.logio.data; - data[0] = data[1] = 0; + data[0] = MBS_COMMAND_ERROR; + data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? + QLA_LOGIO_LOGIN_RETRIED : 0; if (mbx->entry_status) { DEBUG2(printk(KERN_WARNING "scsi(%ld:%x): Async-%s error entry - entry-status=%x " @@ -935,23 +926,28 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, le16_to_cpu(mbx->status_flags))); DEBUG2(qla2x00_dump_buffer((uint8_t *)mbx, sizeof(*mbx))); - data[0] = MBS_COMMAND_ERROR; - data[1] = lio->flags & SRB_LOGIN_RETRIED ? - QLA_LOGIO_LOGIN_RETRIED: 0; - goto done_post_logio_done_work; + goto logio_done; } - if (!mbx->status && le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE) { + status = le16_to_cpu(mbx->status); + if (status == 0x30 && ctx->type == SRB_LOGIN_CMD && + le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE) + status = 0; + if (!status && le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE) { DEBUG2(printk(KERN_DEBUG "scsi(%ld:%x): Async-%s complete - mbx1=%x.\n", fcport->vha->host_no, sp->handle, type, le16_to_cpu(mbx->mb1))); data[0] = MBS_COMMAND_COMPLETE; - if (lio->ctx.type == SRB_LOGIN_CMD && le16_to_cpu(mbx->mb1) & BIT_1) - fcport->flags |= FCF_FCP2_DEVICE; - - goto done_post_logio_done_work; + if (ctx->type == SRB_LOGIN_CMD) { + fcport->port_type = FCT_TARGET; + if (le16_to_cpu(mbx->mb1) & BIT_0) + fcport->port_type = FCT_INITIATOR; + if (le16_to_cpu(mbx->mb1) & BIT_1) + fcport->flags |= FCF_FCP2_DEVICE; + } + goto logio_done; } data[0] = le16_to_cpu(mbx->mb0); @@ -963,25 +959,19 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, break; default: data[0] = MBS_COMMAND_ERROR; - data[1] = lio->flags & SRB_LOGIN_RETRIED ? - QLA_LOGIO_LOGIN_RETRIED: 0; break; } DEBUG2(printk(KERN_WARNING "scsi(%ld:%x): Async-%s failed - status=%x mb0=%x mb1=%x mb2=%x " "mb6=%x mb7=%x.\n", - fcport->vha->host_no, sp->handle, type, le16_to_cpu(mbx->status), + fcport->vha->host_no, sp->handle, type, status, le16_to_cpu(mbx->mb0), le16_to_cpu(mbx->mb1), le16_to_cpu(mbx->mb2), le16_to_cpu(mbx->mb6), le16_to_cpu(mbx->mb7))); -done_post_logio_done_work: - lio->ctx.type == SRB_LOGIN_CMD ? - qla2x00_post_async_login_done_work(fcport->vha, fcport, data): - qla2x00_post_async_logout_done_work(fcport->vha, fcport, data); - - lio->ctx.free(sp); +logio_done: + lio->done(sp); } static void @@ -992,7 +982,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, const char *type; struct qla_hw_data *ha = vha->hw; srb_t *sp; - struct srb_bsg *sp_bsg; + struct srb_ctx *sp_bsg; struct fc_bsg_job *bsg_job; uint16_t comp_status; uint32_t fw_status[3]; @@ -1001,11 +991,11 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); if (!sp) return; - sp_bsg = (struct srb_bsg*)sp->ctx; - bsg_job = sp_bsg->bsg_job; + sp_bsg = sp->ctx; + bsg_job = sp_bsg->u.bsg_job; type = NULL; - switch (sp_bsg->ctx.type) { + switch (sp_bsg->type) { case SRB_ELS_CMD_RPT: case SRB_ELS_CMD_HST: type = "els"; @@ -1016,7 +1006,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, default: qla_printk(KERN_WARNING, ha, "%s: Unrecognized SRB: (%p) type=%d.\n", func, sp, - sp_bsg->ctx.type); + sp_bsg->type); return; } @@ -1070,8 +1060,8 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); - if ((sp_bsg->ctx.type == SRB_ELS_CMD_HST) || - (sp_bsg->ctx.type == SRB_CT_CMD)) + if ((sp_bsg->type == SRB_ELS_CMD_HST) || + (sp_bsg->type == SRB_CT_CMD)) kfree(sp->fcport); kfree(sp->ctx); mempool_free(sp, ha->srb_mempool); @@ -1084,37 +1074,26 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, { const char func[] = "LOGIO-IOCB"; const char *type; - struct qla_hw_data *ha = vha->hw; fc_port_t *fcport; srb_t *sp; - struct srb_logio *lio; - uint16_t data[2]; + struct srb_iocb *lio; + struct srb_ctx *ctx; + uint16_t *data; uint32_t iop[2]; sp = qla2x00_get_sp_from_handle(vha, func, req, logio); if (!sp) return; - type = NULL; - lio = sp->ctx; - switch (lio->ctx.type) { - case SRB_LOGIN_CMD: - type = "login"; - break; - case SRB_LOGOUT_CMD: - type = "logout"; - break; - default: - qla_printk(KERN_WARNING, ha, - "%s: Unrecognized SRB: (%p) type=%d.\n", func, sp, - lio->ctx.type); - return; - } - - del_timer(&lio->ctx.timer); + ctx = sp->ctx; + lio = ctx->u.iocb_cmd; + type = ctx->name; fcport = sp->fcport; + data = lio->u.logio.data; - data[0] = data[1] = 0; + data[0] = MBS_COMMAND_ERROR; + data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? + QLA_LOGIO_LOGIN_RETRIED : 0; if (logio->entry_status) { DEBUG2(printk(KERN_WARNING "scsi(%ld:%x): Async-%s error entry - entry-status=%x.\n", @@ -1122,10 +1101,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, logio->entry_status)); DEBUG2(qla2x00_dump_buffer((uint8_t *)logio, sizeof(*logio))); - data[0] = MBS_COMMAND_ERROR; - data[1] = lio->flags & SRB_LOGIN_RETRIED ? - QLA_LOGIO_LOGIN_RETRIED: 0; - goto done_post_logio_done_work; + goto logio_done; } if (le16_to_cpu(logio->comp_status) == CS_COMPLETE) { @@ -1135,8 +1111,8 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, le32_to_cpu(logio->io_parameter[0]))); data[0] = MBS_COMMAND_COMPLETE; - if (lio->ctx.type == SRB_LOGOUT_CMD) - goto done_post_logio_done_work; + if (ctx->type != SRB_LOGIN_CMD) + goto logio_done; iop[0] = le32_to_cpu(logio->io_parameter[0]); if (iop[0] & BIT_4) { @@ -1151,7 +1127,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, if (logio->io_parameter[9] || logio->io_parameter[10]) fcport->supported_classes |= FC_COS_CLASS3; - goto done_post_logio_done_work; + goto logio_done; } iop[0] = le32_to_cpu(logio->io_parameter[0]); @@ -1172,8 +1148,6 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, /* Fall through. */ default: data[0] = MBS_COMMAND_ERROR; - data[1] = lio->flags & SRB_LOGIN_RETRIED ? - QLA_LOGIO_LOGIN_RETRIED: 0; break; } @@ -1184,12 +1158,101 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, le32_to_cpu(logio->io_parameter[0]), le32_to_cpu(logio->io_parameter[1]))); -done_post_logio_done_work: - lio->ctx.type == SRB_LOGIN_CMD ? - qla2x00_post_async_login_done_work(fcport->vha, fcport, data): - qla2x00_post_async_logout_done_work(fcport->vha, fcport, data); +logio_done: + lio->done(sp); +} - lio->ctx.free(sp); +static void +qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, + struct tsk_mgmt_entry *tsk) +{ + const char func[] = "TMF-IOCB"; + const char *type; + fc_port_t *fcport; + srb_t *sp; + struct srb_iocb *iocb; + struct srb_ctx *ctx; + struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk; + int error = 1; + + sp = qla2x00_get_sp_from_handle(vha, func, req, tsk); + if (!sp) + return; + + ctx = sp->ctx; + iocb = ctx->u.iocb_cmd; + type = ctx->name; + fcport = sp->fcport; + + if (sts->entry_status) { + DEBUG2(printk(KERN_WARNING + "scsi(%ld:%x): Async-%s error - entry-status(%x).\n", + fcport->vha->host_no, sp->handle, type, + sts->entry_status)); + } else if (sts->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { + DEBUG2(printk(KERN_WARNING + "scsi(%ld:%x): Async-%s error - completion status(%x).\n", + fcport->vha->host_no, sp->handle, type, + sts->comp_status)); + } else if (!(le16_to_cpu(sts->scsi_status) & + SS_RESPONSE_INFO_LEN_VALID)) { + DEBUG2(printk(KERN_WARNING + "scsi(%ld:%x): Async-%s error - no response info(%x).\n", + fcport->vha->host_no, sp->handle, type, + sts->scsi_status)); + } else if (le32_to_cpu(sts->rsp_data_len) < 4) { + DEBUG2(printk(KERN_WARNING + "scsi(%ld:%x): Async-%s error - not enough response(%d).\n", + fcport->vha->host_no, sp->handle, type, + sts->rsp_data_len)); + } else if (sts->data[3]) { + DEBUG2(printk(KERN_WARNING + "scsi(%ld:%x): Async-%s error - response(%x).\n", + fcport->vha->host_no, sp->handle, type, + sts->data[3])); + } else { + error = 0; + } + + if (error) { + iocb->u.tmf.data = error; + DEBUG2(qla2x00_dump_buffer((uint8_t *)sts, sizeof(*sts))); + } + + iocb->done(sp); +} + +static void +qla24xx_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, + struct mrk_entry_24xx *mrk) +{ + const char func[] = "MRK-IOCB"; + const char *type; + fc_port_t *fcport; + srb_t *sp; + struct srb_iocb *iocb; + struct srb_ctx *ctx; + struct sts_entry_24xx *sts = (struct sts_entry_24xx *)mrk; + + sp = qla2x00_get_sp_from_handle(vha, func, req, mrk); + if (!sp) + return; + + ctx = sp->ctx; + iocb = ctx->u.iocb_cmd; + type = ctx->name; + fcport = sp->fcport; + + if (sts->entry_status) { + iocb->u.marker.data = 1; + DEBUG2(printk(KERN_WARNING + "scsi(%ld:%x): Async-%s error entry - entry-status=%x.\n", + fcport->vha->host_no, sp->handle, type, + sts->entry_status)); + DEBUG2(qla2x00_dump_buffer((uint8_t *)mrk, sizeof(*sts))); + } + + iocb->done(sp); } /** @@ -1256,6 +1319,7 @@ qla2x00_process_response_queue(struct rsp_que *rsp) case MBX_IOCB_TYPE: qla2x00_mbx_iocb_entry(vha, rsp->req, (struct mbx_entry *)pkt); + break; default: /* Type Not Supported. */ DEBUG4(printk(KERN_WARNING @@ -1301,6 +1365,78 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t sense_len, DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, sense_len)); } +struct scsi_dif_tuple { + __be16 guard; /* Checksum */ + __be16 app_tag; /* APPL identifer */ + __be32 ref_tag; /* Target LBA or indirect LBA */ +}; + +/* + * Checks the guard or meta-data for the type of error + * detected by the HBA. In case of errors, we set the + * ASC/ASCQ fields in the sense buffer with ILLEGAL_REQUEST + * to indicate to the kernel that the HBA detected error. + */ +static inline void +qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24) +{ + struct scsi_cmnd *cmd = sp->cmd; + struct scsi_dif_tuple *ep = + (struct scsi_dif_tuple *)&sts24->data[20]; + struct scsi_dif_tuple *ap = + (struct scsi_dif_tuple *)&sts24->data[12]; + uint32_t e_ref_tag, a_ref_tag; + uint16_t e_app_tag, a_app_tag; + uint16_t e_guard, a_guard; + + e_ref_tag = be32_to_cpu(ep->ref_tag); + a_ref_tag = be32_to_cpu(ap->ref_tag); + e_app_tag = be16_to_cpu(ep->app_tag); + a_app_tag = be16_to_cpu(ap->app_tag); + e_guard = be16_to_cpu(ep->guard); + a_guard = be16_to_cpu(ap->guard); + + DEBUG18(printk(KERN_DEBUG + "%s(): iocb(s) %p Returned STATUS\n", __func__, sts24)); + + DEBUG18(printk(KERN_ERR "DIF ERROR in cmd 0x%x lba 0x%llx act ref" + " tag=0x%x, exp ref_tag=0x%x, act app tag=0x%x, exp app" + " tag=0x%x, act guard=0x%x, exp guard=0x%x\n", + cmd->cmnd[0], (u64)scsi_get_lba(cmd), a_ref_tag, e_ref_tag, + a_app_tag, e_app_tag, a_guard, e_guard)); + + + /* check guard */ + if (e_guard != a_guard) { + scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST, + 0x10, 0x1); + set_driver_byte(cmd, DRIVER_SENSE); + set_host_byte(cmd, DID_ABORT); + cmd->result |= SAM_STAT_CHECK_CONDITION << 1; + return; + } + + /* check appl tag */ + if (e_app_tag != a_app_tag) { + scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST, + 0x10, 0x2); + set_driver_byte(cmd, DRIVER_SENSE); + set_host_byte(cmd, DID_ABORT); + cmd->result |= SAM_STAT_CHECK_CONDITION << 1; + return; + } + + /* check ref tag */ + if (e_ref_tag != a_ref_tag) { + scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST, + 0x10, 0x3); + set_driver_byte(cmd, DRIVER_SENSE); + set_host_byte(cmd, DID_ABORT); + cmd->result |= SAM_STAT_CHECK_CONDITION << 1; + return; + } +} + /** * qla2x00_status_entry() - Process a Status IOCB entry. * @ha: SCSI driver HA context @@ -1316,6 +1452,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) struct sts_entry_24xx *sts24; uint16_t comp_status; uint16_t scsi_status; + uint16_t ox_id; uint8_t lscsi_status; int32_t resid; uint32_t sense_len, rsp_info_len, resid_len, fw_resid_len; @@ -1324,6 +1461,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) uint32_t handle; uint16_t que; struct req_que *req; + int logit = 1; sts = (sts_entry_t *) pkt; sts24 = (struct sts_entry_24xx *) pkt; @@ -1337,6 +1475,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) handle = (uint32_t) LSW(sts->handle); que = MSW(sts->handle); req = ha->req_q_map[que]; + /* Fast path completion. */ if (comp_status == CS_COMPLETE && scsi_status == 0) { qla2x00_process_completed_request(vha, req, handle); @@ -1352,9 +1491,9 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) sp = NULL; if (sp == NULL) { - DEBUG2(printk("scsi(%ld): Status Entry invalid handle.\n", - vha->host_no)); - qla_printk(KERN_WARNING, ha, "Status Entry invalid handle.\n"); + qla_printk(KERN_WARNING, ha, + "scsi(%ld): Invalid status handle (0x%x).\n", vha->host_no, + sts->handle); set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); qla2xxx_wake_dpc(vha); @@ -1362,10 +1501,9 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) } cp = sp->cmd; if (cp == NULL) { - DEBUG2(printk("scsi(%ld): Command already returned back to OS " - "pkt->handle=%d sp=%p.\n", vha->host_no, handle, sp)); qla_printk(KERN_WARNING, ha, - "Command is NULL: already returned to OS (sp=%p)\n", sp); + "scsi(%ld): Command already returned (0x%x/%p).\n", + vha->host_no, sts->handle, sp); return; } @@ -1374,6 +1512,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) fcport = sp->fcport; + ox_id = 0; sense_len = rsp_info_len = resid_len = fw_resid_len = 0; if (IS_FWI2_CAPABLE(ha)) { if (scsi_status & SS_SENSE_LEN_VALID) @@ -1387,6 +1526,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) rsp_info = sts24->data; sense_data = sts24->data; host_to_fcp_swap(sts24->data, sizeof(sts24->data)); + ox_id = le16_to_cpu(sts24->ox_id); } else { if (scsi_status & SS_SENSE_LEN_VALID) sense_len = le16_to_cpu(sts->req_sense_length); @@ -1403,17 +1543,13 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) if (IS_FWI2_CAPABLE(ha)) sense_data += rsp_info_len; if (rsp_info_len > 3 && rsp_info[3]) { - DEBUG2(printk("scsi(%ld:%d:%d:%d) FCP I/O protocol " - "failure (%x/%02x%02x%02x%02x%02x%02x%02x%02x)..." - "retrying command\n", vha->host_no, - cp->device->channel, cp->device->id, - cp->device->lun, rsp_info_len, rsp_info[0], - rsp_info[1], rsp_info[2], rsp_info[3], rsp_info[4], - rsp_info[5], rsp_info[6], rsp_info[7])); + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld:%d:%d): FCP I/O protocol failure " + "(0x%x/0x%x).\n", vha->host_no, cp->device->id, + cp->device->lun, rsp_info_len, rsp_info[3])); cp->result = DID_BUS_BUSY << 16; - qla2x00_sp_compl(ha, sp); - return; + goto out; } } @@ -1440,12 +1576,10 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) ((unsigned)(scsi_bufflen(cp) - resid) < cp->underflow)) { qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d:%d): Mid-layer underflow " - "detected (%x of %x bytes)...returning " - "error status.\n", vha->host_no, - cp->device->channel, cp->device->id, - cp->device->lun, resid, - scsi_bufflen(cp)); + "scsi(%ld:%d:%d): Mid-layer underflow " + "detected (0x%x of 0x%x bytes).\n", + vha->host_no, cp->device->id, + cp->device->lun, resid, scsi_bufflen(cp)); cp->result = DID_ERROR << 16; break; @@ -1454,12 +1588,12 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) cp->result = DID_OK << 16 | lscsi_status; if (lscsi_status == SAM_STAT_TASK_SET_FULL) { - DEBUG2(printk(KERN_INFO - "scsi(%ld): QUEUE FULL status detected " - "0x%x-0x%x.\n", vha->host_no, comp_status, - scsi_status)); + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld:%d:%d) QUEUE FULL detected.\n", + vha->host_no, cp->device->id, cp->device->lun)); break; } + logit = 0; if (lscsi_status != SS_CHECK_CONDITION) break; @@ -1471,23 +1605,14 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) break; case CS_DATA_UNDERRUN: - DEBUG2(printk(KERN_INFO - "scsi(%ld:%d:%d) UNDERRUN status detected 0x%x-0x%x. " - "resid=0x%x fw_resid=0x%x cdb=0x%x os_underflow=0x%x\n", - vha->host_no, cp->device->id, cp->device->lun, comp_status, - scsi_status, resid_len, fw_resid_len, cp->cmnd[0], - cp->underflow)); - /* Use F/W calculated residual length. */ resid = IS_FWI2_CAPABLE(ha) ? fw_resid_len : resid_len; scsi_set_resid(cp, resid); if (scsi_status & SS_RESIDUAL_UNDER) { if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) { - DEBUG2(printk( - "scsi(%ld:%d:%d:%d) Dropped frame(s) " - "detected (%x of %x bytes)...residual " - "length mismatch...retrying command.\n", - vha->host_no, cp->device->channel, + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld:%d:%d) Dropped frame(s) detected " + "(0x%x of 0x%x bytes).\n", vha->host_no, cp->device->id, cp->device->lun, resid, scsi_bufflen(cp))); @@ -1499,21 +1624,18 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) ((unsigned)(scsi_bufflen(cp) - resid) < cp->underflow)) { qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d:%d): Mid-layer underflow " - "detected (%x of %x bytes)...returning " - "error status.\n", vha->host_no, - cp->device->channel, cp->device->id, + "scsi(%ld:%d:%d): Mid-layer underflow " + "detected (0x%x of 0x%x bytes).\n", + vha->host_no, cp->device->id, cp->device->lun, resid, scsi_bufflen(cp)); cp->result = DID_ERROR << 16; break; } } else if (!lscsi_status) { - DEBUG2(printk( - "scsi(%ld:%d:%d:%d) Dropped frame(s) detected " - "(%x of %x bytes)...firmware reported underrun..." - "retrying command.\n", vha->host_no, - cp->device->channel, cp->device->id, + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld:%d:%d) Dropped frame(s) detected (0x%x " + "of 0x%x bytes).\n", vha->host_no, cp->device->id, cp->device->lun, resid, scsi_bufflen(cp))); cp->result = DID_ERROR << 16; @@ -1521,6 +1643,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) } cp->result = DID_OK << 16 | lscsi_status; + logit = 0; /* * Check to see if SCSI Status is non zero. If so report SCSI @@ -1528,10 +1651,11 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) */ if (lscsi_status != 0) { if (lscsi_status == SAM_STAT_TASK_SET_FULL) { - DEBUG2(printk(KERN_INFO - "scsi(%ld): QUEUE FULL status detected " - "0x%x-0x%x.\n", vha->host_no, comp_status, - scsi_status)); + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld:%d:%d) QUEUE FULL detected.\n", + vha->host_no, cp->device->id, + cp->device->lun)); + logit = 1; break; } if (lscsi_status != SS_CHECK_CONDITION) @@ -1545,109 +1669,60 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) } break; - case CS_DATA_OVERRUN: - DEBUG2(printk(KERN_INFO - "scsi(%ld:%d:%d): OVERRUN status detected 0x%x-0x%x\n", - vha->host_no, cp->device->id, cp->device->lun, comp_status, - scsi_status)); - DEBUG2(printk(KERN_INFO - "CDB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", - cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3], - cp->cmnd[4], cp->cmnd[5])); - DEBUG2(printk(KERN_INFO - "PID=0x%lx req=0x%x xtra=0x%x -- returning DID_ERROR " - "status!\n", - cp->serial_number, scsi_bufflen(cp), resid_len)); - - cp->result = DID_ERROR << 16; - break; - case CS_PORT_LOGGED_OUT: case CS_PORT_CONFIG_CHG: case CS_PORT_BUSY: case CS_INCOMPLETE: case CS_PORT_UNAVAILABLE: - /* - * If the port is in Target Down state, return all IOs for this - * Target with DID_NO_CONNECT ELSE Queue the IOs in the - * retry_queue. - */ - DEBUG2(printk("scsi(%ld:%d:%d): status_entry: Port Down " - "pid=%ld, compl status=0x%x, port state=0x%x\n", - vha->host_no, cp->device->id, cp->device->lun, - cp->serial_number, comp_status, - atomic_read(&fcport->state))); - + case CS_TIMEOUT: /* * We are going to have the fc class block the rport * while we try to recover so instruct the mid layer * to requeue until the class decides how to handle this. */ cp->result = DID_TRANSPORT_DISRUPTED << 16; + + if (comp_status == CS_TIMEOUT) { + if (IS_FWI2_CAPABLE(ha)) + break; + else if ((le16_to_cpu(sts->status_flags) & + SF_LOGOUT_SENT) == 0) + break; + } + + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld:%d:%d) Port down status: port-state=0x%x\n", + vha->host_no, cp->device->id, cp->device->lun, + atomic_read(&fcport->state))); + if (atomic_read(&fcport->state) == FCS_ONLINE) qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1); break; case CS_RESET: - DEBUG2(printk(KERN_INFO - "scsi(%ld): RESET status detected 0x%x-0x%x.\n", - vha->host_no, comp_status, scsi_status)); - - cp->result = DID_RESET << 16; - break; - case CS_ABORTED: - /* - * hv2.19.12 - DID_ABORT does not retry the request if we - * aborted this request then abort otherwise it must be a - * reset. - */ - DEBUG2(printk(KERN_INFO - "scsi(%ld): ABORT status detected 0x%x-0x%x.\n", - vha->host_no, comp_status, scsi_status)); - cp->result = DID_RESET << 16; break; - case CS_TIMEOUT: - /* - * We are going to have the fc class block the rport - * while we try to recover so instruct the mid layer - * to requeue until the class decides how to handle this. - */ - cp->result = DID_TRANSPORT_DISRUPTED << 16; - - if (IS_FWI2_CAPABLE(ha)) { - DEBUG2(printk(KERN_INFO - "scsi(%ld:%d:%d:%d): TIMEOUT status detected " - "0x%x-0x%x\n", vha->host_no, cp->device->channel, - cp->device->id, cp->device->lun, comp_status, - scsi_status)); - break; - } - DEBUG2(printk(KERN_INFO - "scsi(%ld:%d:%d:%d): TIMEOUT status detected 0x%x-0x%x " - "sflags=%x.\n", vha->host_no, cp->device->channel, - cp->device->id, cp->device->lun, comp_status, scsi_status, - le16_to_cpu(sts->status_flags))); - - /* Check to see if logout occurred. */ - if ((le16_to_cpu(sts->status_flags) & SF_LOGOUT_SENT)) - qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1); + case CS_DIF_ERROR: + qla2x00_handle_dif_error(sp, sts24); break; - default: - DEBUG3(printk("scsi(%ld): Error detected (unknown status) " - "0x%x-0x%x.\n", vha->host_no, comp_status, scsi_status)); - qla_printk(KERN_INFO, ha, - "Unknown status detected 0x%x-0x%x.\n", - comp_status, scsi_status); - cp->result = DID_ERROR << 16; break; } - /* Place command on done queue. */ +out: + if (logit) + DEBUG2(qla_printk(KERN_INFO, ha, + "scsi(%ld:%d:%d) FCP command status: 0x%x-0x%x (0x%x) " + "oxid=0x%x ser=0x%lx cdb=%02x%02x%02x len=0x%x " + "rsp_info=0x%x resid=0x%x fw_resid=0x%x\n", vha->host_no, + cp->device->id, cp->device->lun, comp_status, scsi_status, + cp->result, ox_id, cp->serial_number, cp->cmnd[0], + cp->cmnd[1], cp->cmnd[2], scsi_bufflen(cp), rsp_info_len, + resid_len, fw_resid_len)); + if (rsp->status_srb == NULL) qla2x00_sp_compl(ha, sp); } @@ -1806,6 +1881,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, struct rsp_que *rsp) { struct sts_entry_24xx *pkt; + struct qla_hw_data *ha = vha->hw; if (!vha->flags.online) return; @@ -1846,6 +1922,14 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, qla24xx_logio_entry(vha, rsp->req, (struct logio_entry_24xx *)pkt); break; + case TSK_MGMT_IOCB_TYPE: + qla24xx_tm_iocb_entry(vha, rsp->req, + (struct tsk_mgmt_entry *)pkt); + break; + case MARKER_TYPE: + qla24xx_marker_iocb_entry(vha, rsp->req, + (struct mrk_entry_24xx *)pkt); + break; case CT_IOCB_TYPE: qla24xx_els_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE); clear_bit(MBX_INTERRUPT, &vha->hw->mbx_cmd_flags); @@ -1866,7 +1950,11 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, } /* Adjust ring index */ - WRT_REG_DWORD(rsp->rsp_q_out, rsp->ring_index); + if (IS_QLA82XX(ha)) { + struct device_reg_82xx __iomem *reg = &ha->iobase->isp82; + WRT_REG_DWORD(®->rsp_q_out[0], rsp->ring_index); + } else + WRT_REG_DWORD(rsp->rsp_q_out, rsp->ring_index); } static void @@ -2169,6 +2257,11 @@ static struct qla_init_msix_entry msix_entries[3] = { { "qla2xxx (multiq)", qla25xx_msix_rsp_q }, }; +static struct qla_init_msix_entry qla82xx_msix_entries[2] = { + { "qla2xxx (default)", qla82xx_msix_default }, + { "qla2xxx (rsp_q)", qla82xx_msix_rsp_q }, +}; + static void qla24xx_disable_msix(struct qla_hw_data *ha) { @@ -2195,7 +2288,7 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) struct qla_msix_entry *qentry; entries = kzalloc(sizeof(struct msix_entry) * ha->msix_count, - GFP_KERNEL); + GFP_KERNEL); if (!entries) return -ENOMEM; @@ -2240,8 +2333,15 @@ msix_failed: /* Enable MSI-X vectors for the base queue */ for (i = 0; i < 2; i++) { qentry = &ha->msix_entries[i]; - ret = request_irq(qentry->vector, msix_entries[i].handler, - 0, msix_entries[i].name, rsp); + if (IS_QLA82XX(ha)) { + ret = request_irq(qentry->vector, + qla82xx_msix_entries[i].handler, + 0, qla82xx_msix_entries[i].name, rsp); + } else { + ret = request_irq(qentry->vector, + msix_entries[i].handler, + 0, msix_entries[i].name, rsp); + } if (ret) { qla_printk(KERN_WARNING, ha, "MSI-X: Unable to register handler -- %x/%d.\n", @@ -2272,7 +2372,7 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) /* If possible, enable MSI-X. */ if (!IS_QLA2432(ha) && !IS_QLA2532(ha) && - !IS_QLA8432(ha) && !IS_QLA8001(ha)) + !IS_QLA8432(ha) && !IS_QLA8XXX_TYPE(ha)) goto skip_msi; if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_HP && @@ -2302,7 +2402,7 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) goto clear_risc_ints; } qla_printk(KERN_WARNING, ha, - "MSI-X: Falling back-to INTa mode -- %d.\n", ret); + "MSI-X: Falling back-to MSI mode -- %d.\n", ret); skip_msix: if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) && @@ -2313,7 +2413,9 @@ skip_msix: if (!ret) { DEBUG2(qla_printk(KERN_INFO, ha, "MSI: Enabled.\n")); ha->flags.msi_enabled = 1; - } + } else + qla_printk(KERN_WARNING, ha, + "MSI-X: Falling back-to INTa mode -- %d.\n", ret); skip_msi: ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler, @@ -2331,7 +2433,7 @@ clear_risc_ints: * FIXME: Noted that 8014s were being dropped during NK testing. * Timing deltas during MSI-X/INTa transitions? */ - if (IS_QLA81XX(ha)) + if (IS_QLA81XX(ha) || IS_QLA82XX(ha)) goto fail; spin_lock_irq(&ha->hardware_lock); if (IS_FWI2_CAPABLE(ha)) { diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 42eb7ffd594..f3650d0434c 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -49,6 +49,14 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) if (ha->pdev->error_state > pci_channel_io_frozen) return QLA_FUNCTION_TIMEOUT; + if (vha->device_flags & DFLG_DEV_FAILED) { + DEBUG2_3_11(qla_printk(KERN_WARNING, ha, + "%s(%ld): Device in failed state, " + "timeout MBX Exiting.\n", + __func__, base_vha->host_no)); + return QLA_FUNCTION_TIMEOUT; + } + reg = ha->iobase; io_lock_on = base_vha->flags.init_done; @@ -85,7 +93,9 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) spin_lock_irqsave(&ha->hardware_lock, flags); /* Load mailbox registers. */ - if (IS_FWI2_CAPABLE(ha)) + if (IS_QLA82XX(ha)) + optr = (uint16_t __iomem *)®->isp82.mailbox_in[0]; + else if (IS_FWI2_CAPABLE(ha) && !IS_QLA82XX(ha)) optr = (uint16_t __iomem *)®->isp24.mailbox0; else optr = (uint16_t __iomem *)MAILBOX_REG(ha, ®->isp, 0); @@ -133,7 +143,18 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) if ((!abort_active && io_lock_on) || IS_NOPOLLING_TYPE(ha)) { set_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); - if (IS_FWI2_CAPABLE(ha)) + if (IS_QLA82XX(ha)) { + if (RD_REG_DWORD(®->isp82.hint) & + HINT_MBX_INT_PENDING) { + spin_unlock_irqrestore(&ha->hardware_lock, + flags); + DEBUG2_3_11(printk(KERN_INFO + "%s(%ld): Pending Mailbox timeout. " + "Exiting.\n", __func__, base_vha->host_no)); + return QLA_FUNCTION_TIMEOUT; + } + WRT_REG_DWORD(®->isp82.hint, HINT_MBX_INT_PENDING); + } else if (IS_FWI2_CAPABLE(ha)) WRT_REG_DWORD(®->isp24.hccr, HCCRX_SET_HOST_INT); else WRT_REG_WORD(®->isp.hccr, HCCR_SET_HOST_INT); @@ -147,7 +168,18 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) DEBUG3_11(printk("%s(%ld): cmd=%x POLLING MODE.\n", __func__, base_vha->host_no, command)); - if (IS_FWI2_CAPABLE(ha)) + if (IS_QLA82XX(ha)) { + if (RD_REG_DWORD(®->isp82.hint) & + HINT_MBX_INT_PENDING) { + spin_unlock_irqrestore(&ha->hardware_lock, + flags); + DEBUG2_3_11(printk(KERN_INFO + "%s(%ld): Pending Mailbox timeout. " + "Exiting.\n", __func__, base_vha->host_no)); + return QLA_FUNCTION_TIMEOUT; + } + WRT_REG_DWORD(®->isp82.hint, HINT_MBX_INT_PENDING); + } else if (IS_FWI2_CAPABLE(ha)) WRT_REG_DWORD(®->isp24.hccr, HCCRX_SET_HOST_INT); else WRT_REG_WORD(®->isp.hccr, HCCR_SET_HOST_INT); @@ -264,7 +296,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); clear_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); - if (qla2x00_abort_isp(base_vha)) { + if (ha->isp_ops->abort_isp(base_vha)) { /* Failed. retry later. */ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); } @@ -711,7 +743,7 @@ qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr) * Context: * Kernel context. */ -static int +int qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer, dma_addr_t phys_addr, size_t size, uint32_t tov) { @@ -952,7 +984,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa, mcp->mb[9] = vha->vp_idx; mcp->out_mb = MBX_9|MBX_0; mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; - if (IS_QLA81XX(vha->hw)) + if (IS_QLA8XXX_TYPE(vha->hw)) mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; @@ -978,7 +1010,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa, DEBUG11(printk("qla2x00_get_adapter_id(%ld): done.\n", vha->host_no)); - if (IS_QLA81XX(vha->hw)) { + if (IS_QLA8XXX_TYPE(vha->hw)) { vha->fcoe_vlan_id = mcp->mb[9] & 0xfff; vha->fcoe_fcf_idx = mcp->mb[10]; vha->fcoe_vn_port_mac[5] = mcp->mb[11] >> 8; @@ -1076,6 +1108,10 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n", vha->host_no)); + if (IS_QLA82XX(ha) && ql2xdbwr) + qla82xx_wr_32(ha, ha->nxdb_wr_ptr, + (0x04 | (ha->portnum << 5) | (0 << 8) | (0 << 16))); + if (ha->flags.npiv_supported) mcp->mb[0] = MBC_MID_INITIALIZE_FIRMWARE; else @@ -1408,7 +1444,7 @@ qla2x00_lip_reset(scsi_qla_host_t *vha) DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - if (IS_QLA81XX(vha->hw)) { + if (IS_QLA8XXX_TYPE(vha->hw)) { /* Logout across all FCFs. */ mcp->mb[0] = MBC_LIP_FULL_LOGIN; mcp->mb[1] = BIT_1; @@ -2428,12 +2464,22 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, int qla24xx_abort_target(struct fc_port *fcport, unsigned int l, int tag) { + struct qla_hw_data *ha = fcport->vha->hw; + + if ((ql2xasynctmfenable) && IS_FWI2_CAPABLE(ha)) + return qla2x00_async_tm_cmd(fcport, TCF_TARGET_RESET, l, tag); + return __qla24xx_issue_tmf("Target", TCF_TARGET_RESET, fcport, l, tag); } int qla24xx_lun_reset(struct fc_port *fcport, unsigned int l, int tag) { + struct qla_hw_data *ha = fcport->vha->hw; + + if ((ql2xasynctmfenable) && IS_FWI2_CAPABLE(ha)) + return qla2x00_async_tm_cmd(fcport, TCF_LUN_RESET, l, tag); + return __qla24xx_issue_tmf("Lun", TCF_LUN_RESET, fcport, l, tag); } @@ -2740,6 +2786,48 @@ qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint16_t addr, } int +qla2x00_get_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, + uint16_t *port_speed, uint16_t *mb) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_IIDMA_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + + mcp->mb[0] = MBC_PORT_PARAMS; + mcp->mb[1] = loop_id; + mcp->mb[2] = mcp->mb[3] = 0; + mcp->mb[9] = vha->vp_idx; + mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_3|MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + /* Return mailbox statuses. */ + if (mb != NULL) { + mb[0] = mcp->mb[0]; + mb[1] = mcp->mb[1]; + mb[3] = mcp->mb[3]; + } + + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, + vha->host_no, rval)); + } else { + DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + if (port_speed) + *port_speed = mcp->mb[3]; + } + + return rval; +} + +int qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t port_speed, uint16_t *mb) { @@ -2755,7 +2843,7 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, mcp->mb[0] = MBC_PORT_PARAMS; mcp->mb[1] = loop_id; mcp->mb[2] = BIT_0; - if (IS_QLA81XX(vha->hw)) + if (IS_QLA8XXX_TYPE(vha->hw)) mcp->mb[3] = port_speed & (BIT_5|BIT_4|BIT_3|BIT_2|BIT_1|BIT_0); else mcp->mb[3] = port_speed & (BIT_2|BIT_1|BIT_0); @@ -3544,7 +3632,7 @@ qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA81XX(vha->hw)) + if (!IS_QLA8XXX_TYPE(vha->hw)) return QLA_FUNCTION_FAILED; DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); @@ -3582,7 +3670,7 @@ qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA81XX(vha->hw)) + if (!IS_QLA8XXX_TYPE(vha->hw)) return QLA_FUNCTION_FAILED; DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); @@ -3643,7 +3731,8 @@ qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data) } int -qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t *mresp) +qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, + uint16_t *mresp) { int rval; mbx_cmd_t mc; @@ -3678,7 +3767,7 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t * mcp->out_mb = MBX_21|MBX_20|MBX_19|MBX_18|MBX_17|MBX_16|MBX_15| MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0; - if (IS_QLA81XX(vha->hw)) + if (IS_QLA8XXX_TYPE(vha->hw)) mcp->out_mb |= MBX_2; mcp->in_mb = MBX_19|MBX_18|MBX_3|MBX_2|MBX_1|MBX_0; @@ -3690,9 +3779,11 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t * if (rval != QLA_SUCCESS) { DEBUG2(printk(KERN_WARNING - "(%ld): failed=%x mb[0]=0x%x " - "mb[1]=0x%x mb[2]=0x%x mb[3]=0x%x mb[18]=0x%x mb[19]=0x%x. \n", vha->host_no, rval, - mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], mcp->mb[18], mcp->mb[19])); + "(%ld): failed=%x mb[0]=0x%x " + "mb[1]=0x%x mb[2]=0x%x mb[3]=0x%x mb[18]=0x%x " + "mb[19]=0x%x.\n", + vha->host_no, rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], + mcp->mb[3], mcp->mb[18], mcp->mb[19])); } else { DEBUG2(printk(KERN_WARNING "scsi(%ld): done.\n", vha->host_no)); @@ -3706,7 +3797,8 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t * } int -qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t *mresp) +qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, + uint16_t *mresp) { int rval; mbx_cmd_t mc; @@ -3718,9 +3810,10 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t *mres memset(mcp->mb, 0 , sizeof(mcp->mb)); mcp->mb[0] = MBC_DIAGNOSTIC_ECHO; mcp->mb[1] = mreq->options | BIT_6; /* BIT_6 specifies 64bit address */ - if (IS_QLA81XX(ha)) + if (IS_QLA8XXX_TYPE(ha)) { mcp->mb[1] |= BIT_15; - mcp->mb[2] = IS_QLA81XX(ha) ? vha->fcoe_fcf_idx : 0; + mcp->mb[2] = vha->fcoe_fcf_idx; + } mcp->mb[16] = LSW(mreq->rcv_dma); mcp->mb[17] = MSW(mreq->rcv_dma); mcp->mb[6] = LSW(MSD(mreq->rcv_dma)); @@ -3735,13 +3828,13 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t *mres mcp->out_mb = MBX_21|MBX_20|MBX_17|MBX_16|MBX_15| MBX_14|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0; - if (IS_QLA81XX(ha)) + if (IS_QLA8XXX_TYPE(ha)) mcp->out_mb |= MBX_2; mcp->in_mb = MBX_0; - if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha)) + if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_QLA8XXX_TYPE(ha)) mcp->in_mb |= MBX_1; - if (IS_QLA81XX(ha)) + if (IS_QLA8XXX_TYPE(ha)) mcp->in_mb |= MBX_3; mcp->tov = MBX_TOV_SECONDS; @@ -3764,8 +3857,7 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, uint16_t *mres return rval; } int -qla84xx_reset_chip(scsi_qla_host_t *ha, uint16_t enable_diagnostic, - uint16_t *cmd_status) +qla84xx_reset_chip(scsi_qla_host_t *ha, uint16_t enable_diagnostic) { int rval; mbx_cmd_t mc; @@ -3782,8 +3874,6 @@ qla84xx_reset_chip(scsi_qla_host_t *ha, uint16_t enable_diagnostic, mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD; rval = qla2x00_mailbox_command(ha, mcp); - /* Return mailbox statuses. */ - *cmd_status = mcp->mb[0]; if (rval != QLA_SUCCESS) DEBUG16(printk("%s(%ld): failed=%x.\n", __func__, ha->host_no, rval)); @@ -3801,7 +3891,7 @@ qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data) mbx_cmd_t *mcp = &mc; if (!IS_FWI2_CAPABLE(vha->hw)) - return QLA_FUNCTION_FAILED; + return QLA_FUNCTION_FAILED; DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); @@ -3836,7 +3926,8 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha) if (!IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; - DEBUG11(printk(KERN_INFO "%s(%ld): entered.\n", __func__, vha->host_no)); + DEBUG11(qla_printk(KERN_INFO, ha, + "%s(%ld): entered.\n", __func__, vha->host_no)); mcp->mb[0] = MBC_DATA_RATE; mcp->mb[1] = 0; @@ -3857,3 +3948,122 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha) return rval; } + +int +qla24xx_set_fcp_prio(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t priority, + uint16_t *mb) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha)) + return QLA_FUNCTION_FAILED; + + DEBUG11(printk(KERN_INFO + "%s(%ld): entered.\n", __func__, ha->host_no)); + + mcp->mb[0] = MBC_PORT_PARAMS; + mcp->mb[1] = loop_id; + if (ha->flags.fcp_prio_enabled) + mcp->mb[2] = BIT_1; + else + mcp->mb[2] = BIT_2; + mcp->mb[4] = priority & 0xf; + mcp->mb[9] = vha->vp_idx; + mcp->out_mb = MBX_9|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_4|MBX_3|MBX_1|MBX_0; + mcp->tov = 30; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + if (mb != NULL) { + mb[0] = mcp->mb[0]; + mb[1] = mcp->mb[1]; + mb[3] = mcp->mb[3]; + mb[4] = mcp->mb[4]; + } + + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk(KERN_WARNING + "%s(%ld): failed=%x.\n", __func__, + vha->host_no, rval)); + } else { + DEBUG11(printk(KERN_INFO + "%s(%ld): done.\n", __func__, vha->host_no)); + } + + return rval; +} + +int +qla82xx_mbx_intr_enable(scsi_qla_host_t *vha) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_FWI2_CAPABLE(ha)) + return QLA_FUNCTION_FAILED; + + DEBUG11(qla_printk(KERN_INFO, ha, + "%s(%ld): entered.\n", __func__, vha->host_no)); + + memset(mcp, 0, sizeof(mbx_cmd_t)); + mcp->mb[0] = MBC_TOGGLE_INTR; + mcp->mb[1] = 1; + + mcp->out_mb = MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = 30; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(qla_printk(KERN_WARNING, ha, + "%s(%ld): failed=%x mb[0]=%x.\n", __func__, + vha->host_no, rval, mcp->mb[0])); + } else { + DEBUG11(qla_printk(KERN_INFO, ha, + "%s(%ld): done.\n", __func__, vha->host_no)); + } + + return rval; +} + +int +qla82xx_mbx_intr_disable(scsi_qla_host_t *vha) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA82XX(ha)) + return QLA_FUNCTION_FAILED; + + DEBUG11(qla_printk(KERN_INFO, ha, + "%s(%ld): entered.\n", __func__, vha->host_no)); + + memset(mcp, 0, sizeof(mbx_cmd_t)); + mcp->mb[0] = MBC_TOGGLE_INTR; + mcp->mb[1] = 0; + + mcp->out_mb = MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = 30; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(qla_printk(KERN_WARNING, ha, + "%s(%ld): failed=%x mb[0]=%x.\n", __func__, + vha->host_no, rval, mcp->mb[0])); + } else { + DEBUG11(qla_printk(KERN_INFO, ha, + "%s(%ld): done.\n", __func__, vha->host_no)); + } + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c new file mode 100644 index 00000000000..ff562de0e8e --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -0,0 +1,3636 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2008 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#include "qla_def.h" +#include <linux/delay.h> +#include <linux/pci.h> + +#define MASK(n) ((1ULL<<(n))-1) +#define MN_WIN(addr) (((addr & 0x1fc0000) >> 1) | \ + ((addr >> 25) & 0x3ff)) +#define OCM_WIN(addr) (((addr & 0x1ff0000) >> 1) | \ + ((addr >> 25) & 0x3ff)) +#define MS_WIN(addr) (addr & 0x0ffc0000) +#define QLA82XX_PCI_MN_2M (0) +#define QLA82XX_PCI_MS_2M (0x80000) +#define QLA82XX_PCI_OCM0_2M (0xc0000) +#define VALID_OCM_ADDR(addr) (((addr) & 0x3f800) != 0x3f800) +#define GET_MEM_OFFS_2M(addr) (addr & MASK(18)) + +/* CRB window related */ +#define CRB_BLK(off) ((off >> 20) & 0x3f) +#define CRB_SUBBLK(off) ((off >> 16) & 0xf) +#define CRB_WINDOW_2M (0x130060) +#define QLA82XX_PCI_CAMQM_2M_END (0x04800800UL) +#define CRB_HI(off) ((qla82xx_crb_hub_agt[CRB_BLK(off)] << 20) | \ + ((off) & 0xf0000)) +#define QLA82XX_PCI_CAMQM_2M_BASE (0x000ff800UL) +#define CRB_INDIRECT_2M (0x1e0000UL) + +#define MAX_CRB_XFORM 60 +static unsigned long crb_addr_xform[MAX_CRB_XFORM]; +int qla82xx_crb_table_initialized; + +#define qla82xx_crb_addr_transform(name) \ + (crb_addr_xform[QLA82XX_HW_PX_MAP_CRB_##name] = \ + QLA82XX_HW_CRB_HUB_AGT_ADR_##name << 20) + +static void qla82xx_crb_addr_transform_setup(void) +{ + qla82xx_crb_addr_transform(XDMA); + qla82xx_crb_addr_transform(TIMR); + qla82xx_crb_addr_transform(SRE); + qla82xx_crb_addr_transform(SQN3); + qla82xx_crb_addr_transform(SQN2); + qla82xx_crb_addr_transform(SQN1); + qla82xx_crb_addr_transform(SQN0); + qla82xx_crb_addr_transform(SQS3); + qla82xx_crb_addr_transform(SQS2); + qla82xx_crb_addr_transform(SQS1); + qla82xx_crb_addr_transform(SQS0); + qla82xx_crb_addr_transform(RPMX7); + qla82xx_crb_addr_transform(RPMX6); + qla82xx_crb_addr_transform(RPMX5); + qla82xx_crb_addr_transform(RPMX4); + qla82xx_crb_addr_transform(RPMX3); + qla82xx_crb_addr_transform(RPMX2); + qla82xx_crb_addr_transform(RPMX1); + qla82xx_crb_addr_transform(RPMX0); + qla82xx_crb_addr_transform(ROMUSB); + qla82xx_crb_addr_transform(SN); + qla82xx_crb_addr_transform(QMN); + qla82xx_crb_addr_transform(QMS); + qla82xx_crb_addr_transform(PGNI); + qla82xx_crb_addr_transform(PGND); + qla82xx_crb_addr_transform(PGN3); + qla82xx_crb_addr_transform(PGN2); + qla82xx_crb_addr_transform(PGN1); + qla82xx_crb_addr_transform(PGN0); + qla82xx_crb_addr_transform(PGSI); + qla82xx_crb_addr_transform(PGSD); + qla82xx_crb_addr_transform(PGS3); + qla82xx_crb_addr_transform(PGS2); + qla82xx_crb_addr_transform(PGS1); + qla82xx_crb_addr_transform(PGS0); + qla82xx_crb_addr_transform(PS); + qla82xx_crb_addr_transform(PH); + qla82xx_crb_addr_transform(NIU); + qla82xx_crb_addr_transform(I2Q); + qla82xx_crb_addr_transform(EG); + qla82xx_crb_addr_transform(MN); + qla82xx_crb_addr_transform(MS); + qla82xx_crb_addr_transform(CAS2); + qla82xx_crb_addr_transform(CAS1); + qla82xx_crb_addr_transform(CAS0); + qla82xx_crb_addr_transform(CAM); + qla82xx_crb_addr_transform(C2C1); + qla82xx_crb_addr_transform(C2C0); + qla82xx_crb_addr_transform(SMB); + qla82xx_crb_addr_transform(OCM0); + /* + * Used only in P3 just define it for P2 also. + */ + qla82xx_crb_addr_transform(I2C0); + + qla82xx_crb_table_initialized = 1; +} + +struct crb_128M_2M_block_map crb_128M_2M_map[64] = { + {{{0, 0, 0, 0} } }, + {{{1, 0x0100000, 0x0102000, 0x120000}, + {1, 0x0110000, 0x0120000, 0x130000}, + {1, 0x0120000, 0x0122000, 0x124000}, + {1, 0x0130000, 0x0132000, 0x126000}, + {1, 0x0140000, 0x0142000, 0x128000}, + {1, 0x0150000, 0x0152000, 0x12a000}, + {1, 0x0160000, 0x0170000, 0x110000}, + {1, 0x0170000, 0x0172000, 0x12e000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x01e0000, 0x01e0800, 0x122000}, + {0, 0x0000000, 0x0000000, 0x000000} } } , + {{{1, 0x0200000, 0x0210000, 0x180000} } }, + {{{0, 0, 0, 0} } }, + {{{1, 0x0400000, 0x0401000, 0x169000} } }, + {{{1, 0x0500000, 0x0510000, 0x140000} } }, + {{{1, 0x0600000, 0x0610000, 0x1c0000} } }, + {{{1, 0x0700000, 0x0704000, 0x1b8000} } }, + {{{1, 0x0800000, 0x0802000, 0x170000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x08f0000, 0x08f2000, 0x172000} } }, + {{{1, 0x0900000, 0x0902000, 0x174000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x09f0000, 0x09f2000, 0x176000} } }, + {{{0, 0x0a00000, 0x0a02000, 0x178000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x0af0000, 0x0af2000, 0x17a000} } }, + {{{0, 0x0b00000, 0x0b02000, 0x17c000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {1, 0x0bf0000, 0x0bf2000, 0x17e000} } }, + {{{1, 0x0c00000, 0x0c04000, 0x1d4000} } }, + {{{1, 0x0d00000, 0x0d04000, 0x1a4000} } }, + {{{1, 0x0e00000, 0x0e04000, 0x1a0000} } }, + {{{1, 0x0f00000, 0x0f01000, 0x164000} } }, + {{{0, 0x1000000, 0x1004000, 0x1a8000} } }, + {{{1, 0x1100000, 0x1101000, 0x160000} } }, + {{{1, 0x1200000, 0x1201000, 0x161000} } }, + {{{1, 0x1300000, 0x1301000, 0x162000} } }, + {{{1, 0x1400000, 0x1401000, 0x163000} } }, + {{{1, 0x1500000, 0x1501000, 0x165000} } }, + {{{1, 0x1600000, 0x1601000, 0x166000} } }, + {{{0, 0, 0, 0} } }, + {{{0, 0, 0, 0} } }, + {{{0, 0, 0, 0} } }, + {{{0, 0, 0, 0} } }, + {{{0, 0, 0, 0} } }, + {{{0, 0, 0, 0} } }, + {{{1, 0x1d00000, 0x1d10000, 0x190000} } }, + {{{1, 0x1e00000, 0x1e01000, 0x16a000} } }, + {{{1, 0x1f00000, 0x1f10000, 0x150000} } }, + {{{0} } }, + {{{1, 0x2100000, 0x2102000, 0x120000}, + {1, 0x2110000, 0x2120000, 0x130000}, + {1, 0x2120000, 0x2122000, 0x124000}, + {1, 0x2130000, 0x2132000, 0x126000}, + {1, 0x2140000, 0x2142000, 0x128000}, + {1, 0x2150000, 0x2152000, 0x12a000}, + {1, 0x2160000, 0x2170000, 0x110000}, + {1, 0x2170000, 0x2172000, 0x12e000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000}, + {0, 0x0000000, 0x0000000, 0x000000} } }, + {{{1, 0x2200000, 0x2204000, 0x1b0000} } }, + {{{0} } }, + {{{0} } }, + {{{0} } }, + {{{0} } }, + {{{0} } }, + {{{1, 0x2800000, 0x2804000, 0x1a4000} } }, + {{{1, 0x2900000, 0x2901000, 0x16b000} } }, + {{{1, 0x2a00000, 0x2a00400, 0x1ac400} } }, + {{{1, 0x2b00000, 0x2b00400, 0x1ac800} } }, + {{{1, 0x2c00000, 0x2c00400, 0x1acc00} } }, + {{{1, 0x2d00000, 0x2d00400, 0x1ad000} } }, + {{{1, 0x2e00000, 0x2e00400, 0x1ad400} } }, + {{{1, 0x2f00000, 0x2f00400, 0x1ad800} } }, + {{{1, 0x3000000, 0x3000400, 0x1adc00} } }, + {{{0, 0x3100000, 0x3104000, 0x1a8000} } }, + {{{1, 0x3200000, 0x3204000, 0x1d4000} } }, + {{{1, 0x3300000, 0x3304000, 0x1a0000} } }, + {{{0} } }, + {{{1, 0x3500000, 0x3500400, 0x1ac000} } }, + {{{1, 0x3600000, 0x3600400, 0x1ae000} } }, + {{{1, 0x3700000, 0x3700400, 0x1ae400} } }, + {{{1, 0x3800000, 0x3804000, 0x1d0000} } }, + {{{1, 0x3900000, 0x3904000, 0x1b4000} } }, + {{{1, 0x3a00000, 0x3a04000, 0x1d8000} } }, + {{{0} } }, + {{{0} } }, + {{{1, 0x3d00000, 0x3d04000, 0x1dc000} } }, + {{{1, 0x3e00000, 0x3e01000, 0x167000} } }, + {{{1, 0x3f00000, 0x3f01000, 0x168000} } } +}; + +/* + * top 12 bits of crb internal address (hub, agent) + */ +unsigned qla82xx_crb_hub_agt[64] = { + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PS, + QLA82XX_HW_CRB_HUB_AGT_ADR_MN, + QLA82XX_HW_CRB_HUB_AGT_ADR_MS, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_SRE, + QLA82XX_HW_CRB_HUB_AGT_ADR_NIU, + QLA82XX_HW_CRB_HUB_AGT_ADR_QMN, + QLA82XX_HW_CRB_HUB_AGT_ADR_SQN0, + QLA82XX_HW_CRB_HUB_AGT_ADR_SQN1, + QLA82XX_HW_CRB_HUB_AGT_ADR_SQN2, + QLA82XX_HW_CRB_HUB_AGT_ADR_SQN3, + QLA82XX_HW_CRB_HUB_AGT_ADR_I2Q, + QLA82XX_HW_CRB_HUB_AGT_ADR_TIMR, + QLA82XX_HW_CRB_HUB_AGT_ADR_ROMUSB, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGN4, + QLA82XX_HW_CRB_HUB_AGT_ADR_XDMA, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGN0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGN1, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGN2, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGN3, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGND, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGNI, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGS0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGS1, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGS2, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGS3, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGSI, + QLA82XX_HW_CRB_HUB_AGT_ADR_SN, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_EG, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PS, + QLA82XX_HW_CRB_HUB_AGT_ADR_CAM, + 0, + 0, + 0, + 0, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_TIMR, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX1, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX2, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX3, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX4, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX5, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX6, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX7, + QLA82XX_HW_CRB_HUB_AGT_ADR_XDMA, + QLA82XX_HW_CRB_HUB_AGT_ADR_I2Q, + QLA82XX_HW_CRB_HUB_AGT_ADR_ROMUSB, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX0, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX8, + QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX9, + QLA82XX_HW_CRB_HUB_AGT_ADR_OCM0, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_SMB, + QLA82XX_HW_CRB_HUB_AGT_ADR_I2C0, + QLA82XX_HW_CRB_HUB_AGT_ADR_I2C1, + 0, + QLA82XX_HW_CRB_HUB_AGT_ADR_PGNC, + 0, +}; + +/* Device states */ +char *qdev_state[] = { + "Unknown", + "Cold", + "Initializing", + "Ready", + "Need Reset", + "Need Quiescent", + "Failed", + "Quiescent", +}; + +/* + * In: 'off' is offset from CRB space in 128M pci map + * Out: 'off' is 2M pci map addr + * side effect: lock crb window + */ +static void +qla82xx_pci_set_crbwindow_2M(struct qla_hw_data *ha, ulong *off) +{ + u32 win_read; + + ha->crb_win = CRB_HI(*off); + writel(ha->crb_win, + (void *)(CRB_WINDOW_2M + ha->nx_pcibase)); + + /* Read back value to make sure write has gone through before trying + * to use it. + */ + win_read = RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase)); + if (win_read != ha->crb_win) { + DEBUG2(qla_printk(KERN_INFO, ha, + "%s: Written crbwin (0x%x) != Read crbwin (0x%x), " + "off=0x%lx\n", __func__, ha->crb_win, win_read, *off)); + } + *off = (*off & MASK(16)) + CRB_INDIRECT_2M + ha->nx_pcibase; +} + +static inline unsigned long +qla82xx_pci_set_crbwindow(struct qla_hw_data *ha, u64 off) +{ + /* See if we are currently pointing to the region we want to use next */ + if ((off >= QLA82XX_CRB_PCIX_HOST) && (off < QLA82XX_CRB_DDR_NET)) { + /* No need to change window. PCIX and PCIEregs are in both + * regs are in both windows. + */ + return off; + } + + if ((off >= QLA82XX_CRB_PCIX_HOST) && (off < QLA82XX_CRB_PCIX_HOST2)) { + /* We are in first CRB window */ + if (ha->curr_window != 0) + WARN_ON(1); + return off; + } + + if ((off > QLA82XX_CRB_PCIX_HOST2) && (off < QLA82XX_CRB_MAX)) { + /* We are in second CRB window */ + off = off - QLA82XX_CRB_PCIX_HOST2 + QLA82XX_CRB_PCIX_HOST; + + if (ha->curr_window != 1) + return off; + + /* We are in the QM or direct access + * register region - do nothing + */ + if ((off >= QLA82XX_PCI_DIRECT_CRB) && + (off < QLA82XX_PCI_CAMQM_MAX)) + return off; + } + /* strange address given */ + qla_printk(KERN_WARNING, ha, + "%s: Warning: unm_nic_pci_set_crbwindow called with" + " an unknown address(%llx)\n", QLA2XXX_DRIVER_NAME, off); + return off; +} + +int +qla82xx_wr_32(struct qla_hw_data *ha, ulong off, u32 data) +{ + unsigned long flags = 0; + int rv; + + rv = qla82xx_pci_get_crb_addr_2M(ha, &off); + + BUG_ON(rv == -1); + + if (rv == 1) { + write_lock_irqsave(&ha->hw_lock, flags); + qla82xx_crb_win_lock(ha); + qla82xx_pci_set_crbwindow_2M(ha, &off); + } + + writel(data, (void __iomem *)off); + + if (rv == 1) { + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_UNLOCK)); + write_unlock_irqrestore(&ha->hw_lock, flags); + } + return 0; +} + +int +qla82xx_rd_32(struct qla_hw_data *ha, ulong off) +{ + unsigned long flags = 0; + int rv; + u32 data; + + rv = qla82xx_pci_get_crb_addr_2M(ha, &off); + + BUG_ON(rv == -1); + + if (rv == 1) { + write_lock_irqsave(&ha->hw_lock, flags); + qla82xx_crb_win_lock(ha); + qla82xx_pci_set_crbwindow_2M(ha, &off); + } + data = RD_REG_DWORD((void __iomem *)off); + + if (rv == 1) { + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_UNLOCK)); + write_unlock_irqrestore(&ha->hw_lock, flags); + } + return data; +} + +#define CRB_WIN_LOCK_TIMEOUT 100000000 +int qla82xx_crb_win_lock(struct qla_hw_data *ha) +{ + int done = 0, timeout = 0; + + while (!done) { + /* acquire semaphore3 from PCI HW block */ + done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_LOCK)); + if (done == 1) + break; + if (timeout >= CRB_WIN_LOCK_TIMEOUT) + return -1; + timeout++; + } + qla82xx_wr_32(ha, QLA82XX_CRB_WIN_LOCK_ID, ha->portnum); + return 0; +} + +#define IDC_LOCK_TIMEOUT 100000000 +int qla82xx_idc_lock(struct qla_hw_data *ha) +{ + int i; + int done = 0, timeout = 0; + + while (!done) { + /* acquire semaphore5 from PCI HW block */ + done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM5_LOCK)); + if (done == 1) + break; + if (timeout >= IDC_LOCK_TIMEOUT) + return -1; + + timeout++; + + /* Yield CPU */ + if (!in_interrupt()) + schedule(); + else { + for (i = 0; i < 20; i++) + cpu_relax(); + } + } + + return 0; +} + +void qla82xx_idc_unlock(struct qla_hw_data *ha) +{ + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM5_UNLOCK)); +} + +int +qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *ha, ulong *off) +{ + struct crb_128M_2M_sub_block_map *m; + + if (*off >= QLA82XX_CRB_MAX) + return -1; + + if (*off >= QLA82XX_PCI_CAMQM && (*off < QLA82XX_PCI_CAMQM_2M_END)) { + *off = (*off - QLA82XX_PCI_CAMQM) + + QLA82XX_PCI_CAMQM_2M_BASE + ha->nx_pcibase; + return 0; + } + + if (*off < QLA82XX_PCI_CRBSPACE) + return -1; + + *off -= QLA82XX_PCI_CRBSPACE; + + /* Try direct map */ + m = &crb_128M_2M_map[CRB_BLK(*off)].sub_block[CRB_SUBBLK(*off)]; + + if (m->valid && (m->start_128M <= *off) && (m->end_128M > *off)) { + *off = *off + m->start_2M - m->start_128M + ha->nx_pcibase; + return 0; + } + /* Not in direct map, use crb window */ + return 1; +} + +/* PCI Windowing for DDR regions. */ +#define QLA82XX_ADDR_IN_RANGE(addr, low, high) \ + (((addr) <= (high)) && ((addr) >= (low))) +/* + * check memory access boundary. + * used by test agent. support ddr access only for now + */ +static unsigned long +qla82xx_pci_mem_bound_check(struct qla_hw_data *ha, + unsigned long long addr, int size) +{ + if (!QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET, + QLA82XX_ADDR_DDR_NET_MAX) || + !QLA82XX_ADDR_IN_RANGE(addr + size - 1, QLA82XX_ADDR_DDR_NET, + QLA82XX_ADDR_DDR_NET_MAX) || + ((size != 1) && (size != 2) && (size != 4) && (size != 8))) + return 0; + else + return 1; +} + +int qla82xx_pci_set_window_warning_count; + +unsigned long +qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr) +{ + int window; + u32 win_read; + + if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET, + QLA82XX_ADDR_DDR_NET_MAX)) { + /* DDR network side */ + window = MN_WIN(addr); + ha->ddr_mn_window = window; + qla82xx_wr_32(ha, + ha->mn_win_crb | QLA82XX_PCI_CRBSPACE, window); + win_read = qla82xx_rd_32(ha, + ha->mn_win_crb | QLA82XX_PCI_CRBSPACE); + if ((win_read << 17) != window) { + qla_printk(KERN_WARNING, ha, + "%s: Written MNwin (0x%x) != Read MNwin (0x%x)\n", + __func__, window, win_read); + } + addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_DDR_NET; + } else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM0, + QLA82XX_ADDR_OCM0_MAX)) { + unsigned int temp1; + if ((addr & 0x00ff800) == 0xff800) { + qla_printk(KERN_WARNING, ha, + "%s: QM access not handled.\n", __func__); + addr = -1UL; + } + window = OCM_WIN(addr); + ha->ddr_mn_window = window; + qla82xx_wr_32(ha, + ha->mn_win_crb | QLA82XX_PCI_CRBSPACE, window); + win_read = qla82xx_rd_32(ha, + ha->mn_win_crb | QLA82XX_PCI_CRBSPACE); + temp1 = ((window & 0x1FF) << 7) | + ((window & 0x0FFFE0000) >> 17); + if (win_read != temp1) { + qla_printk(KERN_WARNING, ha, + "%s: Written OCMwin (0x%x) != Read OCMwin (0x%x)\n", + __func__, temp1, win_read); + } + addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_OCM0_2M; + + } else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_QDR_NET, + QLA82XX_P3_ADDR_QDR_NET_MAX)) { + /* QDR network side */ + window = MS_WIN(addr); + ha->qdr_sn_window = window; + qla82xx_wr_32(ha, + ha->ms_win_crb | QLA82XX_PCI_CRBSPACE, window); + win_read = qla82xx_rd_32(ha, + ha->ms_win_crb | QLA82XX_PCI_CRBSPACE); + if (win_read != window) { + qla_printk(KERN_WARNING, ha, + "%s: Written MSwin (0x%x) != Read MSwin (0x%x)\n", + __func__, window, win_read); + } + addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_QDR_NET; + } else { + /* + * peg gdb frequently accesses memory that doesn't exist, + * this limits the chit chat so debugging isn't slowed down. + */ + if ((qla82xx_pci_set_window_warning_count++ < 8) || + (qla82xx_pci_set_window_warning_count%64 == 0)) { + qla_printk(KERN_WARNING, ha, + "%s: Warning:%s Unknown address range!\n", __func__, + QLA2XXX_DRIVER_NAME); + } + addr = -1UL; + } + return addr; +} + +/* check if address is in the same windows as the previous access */ +static int qla82xx_pci_is_same_window(struct qla_hw_data *ha, + unsigned long long addr) +{ + int window; + unsigned long long qdr_max; + + qdr_max = QLA82XX_P3_ADDR_QDR_NET_MAX; + + /* DDR network side */ + if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET, + QLA82XX_ADDR_DDR_NET_MAX)) + BUG(); + else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM0, + QLA82XX_ADDR_OCM0_MAX)) + return 1; + else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM1, + QLA82XX_ADDR_OCM1_MAX)) + return 1; + else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_QDR_NET, qdr_max)) { + /* QDR network side */ + window = ((addr - QLA82XX_ADDR_QDR_NET) >> 22) & 0x3f; + if (ha->qdr_sn_window == window) + return 1; + } + return 0; +} + +static int qla82xx_pci_mem_read_direct(struct qla_hw_data *ha, + u64 off, void *data, int size) +{ + unsigned long flags; + void *addr = NULL; + int ret = 0; + u64 start; + uint8_t *mem_ptr = NULL; + unsigned long mem_base; + unsigned long mem_page; + + write_lock_irqsave(&ha->hw_lock, flags); + + /* + * If attempting to access unknown address or straddle hw windows, + * do not access. + */ + start = qla82xx_pci_set_window(ha, off); + if ((start == -1UL) || + (qla82xx_pci_is_same_window(ha, off + size - 1) == 0)) { + write_unlock_irqrestore(&ha->hw_lock, flags); + qla_printk(KERN_ERR, ha, + "%s out of bound pci memory access. " + "offset is 0x%llx\n", QLA2XXX_DRIVER_NAME, off); + return -1; + } + + write_unlock_irqrestore(&ha->hw_lock, flags); + mem_base = pci_resource_start(ha->pdev, 0); + mem_page = start & PAGE_MASK; + /* Map two pages whenever user tries to access addresses in two + * consecutive pages. + */ + if (mem_page != ((start + size - 1) & PAGE_MASK)) + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE * 2); + else + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE); + if (mem_ptr == 0UL) { + *(u8 *)data = 0; + return -1; + } + addr = mem_ptr; + addr += start & (PAGE_SIZE - 1); + write_lock_irqsave(&ha->hw_lock, flags); + + switch (size) { + case 1: + *(u8 *)data = readb(addr); + break; + case 2: + *(u16 *)data = readw(addr); + break; + case 4: + *(u32 *)data = readl(addr); + break; + case 8: + *(u64 *)data = readq(addr); + break; + default: + ret = -1; + break; + } + write_unlock_irqrestore(&ha->hw_lock, flags); + + if (mem_ptr) + iounmap(mem_ptr); + return ret; +} + +static int +qla82xx_pci_mem_write_direct(struct qla_hw_data *ha, + u64 off, void *data, int size) +{ + unsigned long flags; + void *addr = NULL; + int ret = 0; + u64 start; + uint8_t *mem_ptr = NULL; + unsigned long mem_base; + unsigned long mem_page; + + write_lock_irqsave(&ha->hw_lock, flags); + + /* + * If attempting to access unknown address or straddle hw windows, + * do not access. + */ + start = qla82xx_pci_set_window(ha, off); + if ((start == -1UL) || + (qla82xx_pci_is_same_window(ha, off + size - 1) == 0)) { + write_unlock_irqrestore(&ha->hw_lock, flags); + qla_printk(KERN_ERR, ha, + "%s out of bound pci memory access. " + "offset is 0x%llx\n", QLA2XXX_DRIVER_NAME, off); + return -1; + } + + write_unlock_irqrestore(&ha->hw_lock, flags); + mem_base = pci_resource_start(ha->pdev, 0); + mem_page = start & PAGE_MASK; + /* Map two pages whenever user tries to access addresses in two + * consecutive pages. + */ + if (mem_page != ((start + size - 1) & PAGE_MASK)) + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE*2); + else + mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE); + if (mem_ptr == 0UL) + return -1; + + addr = mem_ptr; + addr += start & (PAGE_SIZE - 1); + write_lock_irqsave(&ha->hw_lock, flags); + + switch (size) { + case 1: + writeb(*(u8 *)data, addr); + break; + case 2: + writew(*(u16 *)data, addr); + break; + case 4: + writel(*(u32 *)data, addr); + break; + case 8: + writeq(*(u64 *)data, addr); + break; + default: + ret = -1; + break; + } + write_unlock_irqrestore(&ha->hw_lock, flags); + if (mem_ptr) + iounmap(mem_ptr); + return ret; +} + +int +qla82xx_wrmem(struct qla_hw_data *ha, u64 off, void *data, int size) +{ + int i, j, ret = 0, loop, sz[2], off0; + u32 temp; + u64 off8, mem_crb, tmpw, word[2] = {0, 0}; +#define MAX_CTL_CHECK 1000 + /* + * If not MN, go check for MS or invalid. + */ + if (off >= QLA82XX_ADDR_QDR_NET && off <= QLA82XX_P3_ADDR_QDR_NET_MAX) { + mem_crb = QLA82XX_CRB_QDR_NET; + } else { + mem_crb = QLA82XX_CRB_DDR_NET; + if (qla82xx_pci_mem_bound_check(ha, off, size) == 0) + return qla82xx_pci_mem_write_direct(ha, off, + data, size); + } + + off8 = off & 0xfffffff8; + off0 = off & 0x7; + sz[0] = (size < (8 - off0)) ? size : (8 - off0); + sz[1] = size - sz[0]; + loop = ((off0 + size - 1) >> 3) + 1; + + if ((size != 8) || (off0 != 0)) { + for (i = 0; i < loop; i++) { + if (qla82xx_rdmem(ha, off8 + (i << 3), &word[i], 8)) + return -1; + } + } + + switch (size) { + case 1: + tmpw = *((u8 *)data); + break; + case 2: + tmpw = *((u16 *)data); + break; + case 4: + tmpw = *((u32 *)data); + break; + case 8: + default: + tmpw = *((u64 *)data); + break; + } + + word[0] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8)); + word[0] |= tmpw << (off0 * 8); + + if (loop == 2) { + word[1] &= ~(~0ULL << (sz[1] * 8)); + word[1] |= tmpw >> (sz[0] * 8); + } + + for (i = 0; i < loop; i++) { + temp = off8 + (i << 3); + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_LO, temp); + temp = 0; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_HI, temp); + temp = word[i] & 0xffffffff; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_LO, temp); + temp = (word[i] >> 32) & 0xffffffff; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_HI, temp); + temp = MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_CTRL, temp); + temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_CTRL, temp); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = qla82xx_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL); + if ((temp & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + qla_printk(KERN_WARNING, ha, + "%s: Fail to write through agent\n", + QLA2XXX_DRIVER_NAME); + ret = -1; + break; + } + } + return ret; +} + +int +qla82xx_rdmem(struct qla_hw_data *ha, u64 off, void *data, int size) +{ + int i, j = 0, k, start, end, loop, sz[2], off0[2]; + u32 temp; + u64 off8, val, mem_crb, word[2] = {0, 0}; +#define MAX_CTL_CHECK 1000 + + /* + * If not MN, go check for MS or invalid. + */ + if (off >= QLA82XX_ADDR_QDR_NET && off <= QLA82XX_P3_ADDR_QDR_NET_MAX) + mem_crb = QLA82XX_CRB_QDR_NET; + else { + mem_crb = QLA82XX_CRB_DDR_NET; + if (qla82xx_pci_mem_bound_check(ha, off, size) == 0) + return qla82xx_pci_mem_read_direct(ha, off, + data, size); + } + + off8 = off & 0xfffffff8; + off0[0] = off & 0x7; + off0[1] = 0; + sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]); + sz[1] = size - sz[0]; + loop = ((off0[0] + size - 1) >> 3) + 1; + + for (i = 0; i < loop; i++) { + temp = off8 + (i << 3); + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_LO, temp); + temp = 0; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_HI, temp); + temp = MIU_TA_CTL_ENABLE; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); + temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = qla82xx_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL); + if ((temp & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + qla_printk(KERN_INFO, ha, + "%s: Fail to read through agent\n", + QLA2XXX_DRIVER_NAME); + break; + } + + start = off0[i] >> 2; + end = (off0[i] + sz[i] - 1) >> 2; + for (k = start; k <= end; k++) { + temp = qla82xx_rd_32(ha, + mem_crb + MIU_TEST_AGT_RDDATA(k)); + word[i] |= ((u64)temp << (32 * k)); + } + } + + if (j >= MAX_CTL_CHECK) + return -1; + + if (sz[0] == 8) { + val = word[0]; + } else { + val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) | + ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8)); + } + + switch (size) { + case 1: + *(u8 *)data = val; + break; + case 2: + *(u16 *)data = val; + break; + case 4: + *(u32 *)data = val; + break; + case 8: + *(u64 *)data = val; + break; + } + return 0; +} + +#define MTU_FUDGE_FACTOR 100 +unsigned long qla82xx_decode_crb_addr(unsigned long addr) +{ + int i; + unsigned long base_addr, offset, pci_base; + + if (!qla82xx_crb_table_initialized) + qla82xx_crb_addr_transform_setup(); + + pci_base = ADDR_ERROR; + base_addr = addr & 0xfff00000; + offset = addr & 0x000fffff; + + for (i = 0; i < MAX_CRB_XFORM; i++) { + if (crb_addr_xform[i] == base_addr) { + pci_base = i << 20; + break; + } + } + if (pci_base == ADDR_ERROR) + return pci_base; + return pci_base + offset; +} + +static long rom_max_timeout = 100; +static long qla82xx_rom_lock_timeout = 100; + +int +qla82xx_rom_lock(struct qla_hw_data *ha) +{ + int done = 0, timeout = 0; + + while (!done) { + /* acquire semaphore2 from PCI HW block */ + done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_LOCK)); + if (done == 1) + break; + if (timeout >= qla82xx_rom_lock_timeout) + return -1; + timeout++; + } + qla82xx_wr_32(ha, QLA82XX_ROM_LOCK_ID, ROM_LOCK_DRIVER); + return 0; +} + +int +qla82xx_wait_rom_busy(struct qla_hw_data *ha) +{ + long timeout = 0; + long done = 0 ; + + while (done == 0) { + done = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_STATUS); + done &= 4; + timeout++; + if (timeout >= rom_max_timeout) { + DEBUG(qla_printk(KERN_INFO, ha, + "%s: Timeout reached waiting for rom busy", + QLA2XXX_DRIVER_NAME)); + return -1; + } + } + return 0; +} + +int +qla82xx_wait_rom_done(struct qla_hw_data *ha) +{ + long timeout = 0; + long done = 0 ; + + while (done == 0) { + done = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_STATUS); + done &= 2; + timeout++; + if (timeout >= rom_max_timeout) { + DEBUG(qla_printk(KERN_INFO, ha, + "%s: Timeout reached waiting for rom done", + QLA2XXX_DRIVER_NAME)); + return -1; + } + } + return 0; +} + +int +qla82xx_do_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp) +{ + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, addr); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 3); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, 0xb); + qla82xx_wait_rom_busy(ha); + if (qla82xx_wait_rom_done(ha)) { + qla_printk(KERN_WARNING, ha, + "%s: Error waiting for rom done\n", + QLA2XXX_DRIVER_NAME); + return -1; + } + /* Reset abyte_cnt and dummy_byte_cnt */ + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); + udelay(10); + cond_resched(); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 0); + *valp = qla82xx_rd_32(ha, QLA82XX_ROMUSB_ROM_RDATA); + return 0; +} + +int +qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp) +{ + int ret, loops = 0; + + while ((qla82xx_rom_lock(ha) != 0) && (loops < 50000)) { + udelay(100); + schedule(); + loops++; + } + if (loops >= 50000) { + qla_printk(KERN_INFO, ha, + "%s: qla82xx_rom_lock failed\n", + QLA2XXX_DRIVER_NAME); + return -1; + } + ret = qla82xx_do_rom_fast_read(ha, addr, valp); + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK)); + return ret; +} + +int +qla82xx_read_status_reg(struct qla_hw_data *ha, uint32_t *val) +{ + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_RDSR); + qla82xx_wait_rom_busy(ha); + if (qla82xx_wait_rom_done(ha)) { + qla_printk(KERN_WARNING, ha, + "Error waiting for rom done\n"); + return -1; + } + *val = qla82xx_rd_32(ha, QLA82XX_ROMUSB_ROM_RDATA); + return 0; +} + +int +qla82xx_flash_wait_write_finish(struct qla_hw_data *ha) +{ + long timeout = 0; + uint32_t done = 1 ; + uint32_t val; + int ret = 0; + + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 0); + while ((done != 0) && (ret == 0)) { + ret = qla82xx_read_status_reg(ha, &val); + done = val & 1; + timeout++; + udelay(10); + cond_resched(); + if (timeout >= 50000) { + qla_printk(KERN_WARNING, ha, + "Timeout reached waiting for write finish"); + return -1; + } + } + return ret; +} + +int +qla82xx_flash_set_write_enable(struct qla_hw_data *ha) +{ + uint32_t val; + qla82xx_wait_rom_busy(ha); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 0); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_WREN); + qla82xx_wait_rom_busy(ha); + if (qla82xx_wait_rom_done(ha)) + return -1; + if (qla82xx_read_status_reg(ha, &val) != 0) + return -1; + if ((val & 2) != 2) + return -1; + return 0; +} + +int +qla82xx_write_status_reg(struct qla_hw_data *ha, uint32_t val) +{ + if (qla82xx_flash_set_write_enable(ha)) + return -1; + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_WDATA, val); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, 0x1); + if (qla82xx_wait_rom_done(ha)) { + qla_printk(KERN_WARNING, ha, + "Error waiting for rom done\n"); + return -1; + } + return qla82xx_flash_wait_write_finish(ha); +} + +int +qla82xx_write_disable_flash(struct qla_hw_data *ha) +{ + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_WRDI); + if (qla82xx_wait_rom_done(ha)) { + qla_printk(KERN_WARNING, ha, + "Error waiting for rom done\n"); + return -1; + } + return 0; +} + +int +ql82xx_rom_lock_d(struct qla_hw_data *ha) +{ + int loops = 0; + while ((qla82xx_rom_lock(ha) != 0) && (loops < 50000)) { + udelay(100); + cond_resched(); + loops++; + } + if (loops >= 50000) { + qla_printk(KERN_WARNING, ha, "ROM lock failed\n"); + return -1; + } + return 0;; +} + +int +qla82xx_write_flash_dword(struct qla_hw_data *ha, uint32_t flashaddr, + uint32_t data) +{ + int ret = 0; + + ret = ql82xx_rom_lock_d(ha); + if (ret < 0) { + qla_printk(KERN_WARNING, ha, "ROM Lock failed\n"); + return ret; + } + + if (qla82xx_flash_set_write_enable(ha)) + goto done_write; + + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_WDATA, data); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, flashaddr); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 3); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_PP); + qla82xx_wait_rom_busy(ha); + if (qla82xx_wait_rom_done(ha)) { + qla_printk(KERN_WARNING, ha, + "Error waiting for rom done\n"); + ret = -1; + goto done_write; + } + + ret = qla82xx_flash_wait_write_finish(ha); + +done_write: + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK)); + return ret; +} + +/* This routine does CRB initialize sequence + * to put the ISP into operational state + */ +int qla82xx_pinit_from_rom(scsi_qla_host_t *vha) +{ + int addr, val; + int i ; + struct crb_addr_pair *buf; + unsigned long off; + unsigned offset, n; + struct qla_hw_data *ha = vha->hw; + + struct crb_addr_pair { + long addr; + long data; + }; + + /* Halt all the indiviual PEGs and other blocks of the ISP */ + qla82xx_rom_lock(ha); + if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) + /* don't reset CAM block on reset */ + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xfeffffff); + else + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xffffffff); + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK)); + + /* Read the signature value from the flash. + * Offset 0: Contain signature (0xcafecafe) + * Offset 4: Offset and number of addr/value pairs + * that present in CRB initialize sequence + */ + if (qla82xx_rom_fast_read(ha, 0, &n) != 0 || n != 0xcafecafeUL || + qla82xx_rom_fast_read(ha, 4, &n) != 0) { + qla_printk(KERN_WARNING, ha, + "[ERROR] Reading crb_init area: n: %08x\n", n); + return -1; + } + + /* Offset in flash = lower 16 bits + * Number of enteries = upper 16 bits + */ + offset = n & 0xffffU; + n = (n >> 16) & 0xffffU; + + /* number of addr/value pair should not exceed 1024 enteries */ + if (n >= 1024) { + qla_printk(KERN_WARNING, ha, + "%s: %s:n=0x%x [ERROR] Card flash not initialized.\n", + QLA2XXX_DRIVER_NAME, __func__, n); + return -1; + } + + qla_printk(KERN_INFO, ha, + "%s: %d CRB init values found in ROM.\n", QLA2XXX_DRIVER_NAME, n); + + buf = kmalloc(n * sizeof(struct crb_addr_pair), GFP_KERNEL); + if (buf == NULL) { + qla_printk(KERN_WARNING, ha, + "%s: [ERROR] Unable to malloc memory.\n", + QLA2XXX_DRIVER_NAME); + return -1; + } + + for (i = 0; i < n; i++) { + if (qla82xx_rom_fast_read(ha, 8*i + 4*offset, &val) != 0 || + qla82xx_rom_fast_read(ha, 8*i + 4*offset + 4, &addr) != 0) { + kfree(buf); + return -1; + } + + buf[i].addr = addr; + buf[i].data = val; + } + + for (i = 0; i < n; i++) { + /* Translate internal CRB initialization + * address to PCI bus address + */ + off = qla82xx_decode_crb_addr((unsigned long)buf[i].addr) + + QLA82XX_PCI_CRBSPACE; + /* Not all CRB addr/value pair to be written, + * some of them are skipped + */ + + /* skipping cold reboot MAGIC */ + if (off == QLA82XX_CAM_RAM(0x1fc)) + continue; + + /* do not reset PCI */ + if (off == (ROMUSB_GLB + 0xbc)) + continue; + + /* skip core clock, so that firmware can increase the clock */ + if (off == (ROMUSB_GLB + 0xc8)) + continue; + + /* skip the function enable register */ + if (off == QLA82XX_PCIE_REG(PCIE_SETUP_FUNCTION)) + continue; + + if (off == QLA82XX_PCIE_REG(PCIE_SETUP_FUNCTION2)) + continue; + + if ((off & 0x0ff00000) == QLA82XX_CRB_SMB) + continue; + + if ((off & 0x0ff00000) == QLA82XX_CRB_DDR_NET) + continue; + + if (off == ADDR_ERROR) { + qla_printk(KERN_WARNING, ha, + "%s: [ERROR] Unknown addr: 0x%08lx\n", + QLA2XXX_DRIVER_NAME, buf[i].addr); + continue; + } + + if (off == (QLA82XX_CRB_PEG_NET_1 + 0x18)) { + if (!QLA82XX_IS_REVISION_P3PLUS(ha->chip_revision)) + buf[i].data = 0x1020; + } + + qla82xx_wr_32(ha, off, buf[i].data); + + /* ISP requires much bigger delay to settle down, + * else crb_window returns 0xffffffff + */ + if (off == QLA82XX_ROMUSB_GLB_SW_RESET) + msleep(1000); + + /* ISP requires millisec delay between + * successive CRB register updation + */ + msleep(1); + } + + kfree(buf); + + /* Resetting the data and instruction cache */ + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_D+0xec, 0x1e); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_D+0x4c, 8); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_I+0x4c, 8); + + /* Clear all protocol processing engines */ + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0+0x8, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0+0xc, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_1+0x8, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_1+0xc, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_2+0x8, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_2+0xc, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_3+0x8, 0); + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_3+0xc, 0); + return 0; +} + +int qla82xx_check_for_bad_spd(struct qla_hw_data *ha) +{ + u32 val = 0; + val = qla82xx_rd_32(ha, BOOT_LOADER_DIMM_STATUS); + val &= QLA82XX_BOOT_LOADER_MN_ISSUE; + if (val & QLA82XX_PEG_TUNE_MN_SPD_ZEROED) { + qla_printk(KERN_INFO, ha, + "Memory DIMM SPD not programmed. " + " Assumed valid.\n"); + return 1; + } else if (val) { + qla_printk(KERN_INFO, ha, + "Memory DIMM type incorrect.Info:%08X.\n", val); + return 2; + } + return 0; +} + +int +qla82xx_fw_load_from_flash(struct qla_hw_data *ha) +{ + int i; + long size = 0; + long flashaddr = BOOTLD_START, memaddr = BOOTLD_START; + u64 data; + u32 high, low; + size = (IMAGE_START - BOOTLD_START) / 8; + + for (i = 0; i < size; i++) { + if ((qla82xx_rom_fast_read(ha, flashaddr, (int *)&low)) || + (qla82xx_rom_fast_read(ha, flashaddr + 4, (int *)&high))) { + return -1; + } + data = ((u64)high << 32) | low ; + qla82xx_pci_mem_write_2M(ha, memaddr, &data, 8); + flashaddr += 8; + memaddr += 8; + + if (i % 0x1000 == 0) + msleep(1); + } + udelay(100); + read_lock(&ha->hw_lock); + if (QLA82XX_IS_REVISION_P3PLUS(ha->chip_revision)) { + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x18, 0x1020); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0x80001e); + } else { + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0x80001d); + } + read_unlock(&ha->hw_lock); + return 0; +} + +int +qla82xx_pci_mem_read_2M(struct qla_hw_data *ha, + u64 off, void *data, int size) +{ + int i, j = 0, k, start, end, loop, sz[2], off0[2]; + int shift_amount; + uint32_t temp; + uint64_t off8, val, mem_crb, word[2] = {0, 0}; + + /* + * If not MN, go check for MS or invalid. + */ + + if (off >= QLA82XX_ADDR_QDR_NET && off <= QLA82XX_P3_ADDR_QDR_NET_MAX) + mem_crb = QLA82XX_CRB_QDR_NET; + else { + mem_crb = QLA82XX_CRB_DDR_NET; + if (qla82xx_pci_mem_bound_check(ha, off, size) == 0) + return qla82xx_pci_mem_read_direct(ha, + off, data, size); + } + + if (QLA82XX_IS_REVISION_P3PLUS(ha->chip_revision)) { + off8 = off & 0xfffffff0; + off0[0] = off & 0xf; + sz[0] = (size < (16 - off0[0])) ? size : (16 - off0[0]); + shift_amount = 4; + } else { + off8 = off & 0xfffffff8; + off0[0] = off & 0x7; + sz[0] = (size < (8 - off0[0])) ? size : (8 - off0[0]); + shift_amount = 4; + } + loop = ((off0[0] + size - 1) >> shift_amount) + 1; + off0[1] = 0; + sz[1] = size - sz[0]; + + /* + * don't lock here - write_wx gets the lock if each time + * write_lock_irqsave(&adapter->adapter_lock, flags); + * netxen_nic_pci_change_crbwindow_128M(adapter, 0); + */ + + for (i = 0; i < loop; i++) { + temp = off8 + (i << shift_amount); + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_LO, temp); + temp = 0; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_HI, temp); + temp = MIU_TA_CTL_ENABLE; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); + temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = qla82xx_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL); + if ((temp & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + if (printk_ratelimit()) + dev_err(&ha->pdev->dev, + "failed to read through agent\n"); + break; + } + + start = off0[i] >> 2; + end = (off0[i] + sz[i] - 1) >> 2; + for (k = start; k <= end; k++) { + temp = qla82xx_rd_32(ha, + mem_crb + MIU_TEST_AGT_RDDATA(k)); + word[i] |= ((uint64_t)temp << (32 * (k & 1))); + } + } + + /* + * netxen_nic_pci_change_crbwindow_128M(adapter, 1); + * write_unlock_irqrestore(&adapter->adapter_lock, flags); + */ + + if (j >= MAX_CTL_CHECK) + return -1; + + if ((off0[0] & 7) == 0) { + val = word[0]; + } else { + val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) | + ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8)); + } + + switch (size) { + case 1: + *(uint8_t *)data = val; + break; + case 2: + *(uint16_t *)data = val; + break; + case 4: + *(uint32_t *)data = val; + break; + case 8: + *(uint64_t *)data = val; + break; + } + return 0; +} + +int +qla82xx_pci_mem_write_2M(struct qla_hw_data *ha, + u64 off, void *data, int size) +{ + int i, j, ret = 0, loop, sz[2], off0; + int scale, shift_amount, p3p, startword; + uint32_t temp; + uint64_t off8, mem_crb, tmpw, word[2] = {0, 0}; + + /* + * If not MN, go check for MS or invalid. + */ + if (off >= QLA82XX_ADDR_QDR_NET && off <= QLA82XX_P3_ADDR_QDR_NET_MAX) + mem_crb = QLA82XX_CRB_QDR_NET; + else { + mem_crb = QLA82XX_CRB_DDR_NET; + if (qla82xx_pci_mem_bound_check(ha, off, size) == 0) + return qla82xx_pci_mem_write_direct(ha, + off, data, size); + } + + off0 = off & 0x7; + sz[0] = (size < (8 - off0)) ? size : (8 - off0); + sz[1] = size - sz[0]; + + if (QLA82XX_IS_REVISION_P3PLUS(ha->chip_revision)) { + off8 = off & 0xfffffff0; + loop = (((off & 0xf) + size - 1) >> 4) + 1; + shift_amount = 4; + scale = 2; + p3p = 1; + startword = (off & 0xf)/8; + } else { + off8 = off & 0xfffffff8; + loop = ((off0 + size - 1) >> 3) + 1; + shift_amount = 3; + scale = 1; + p3p = 0; + startword = 0; + } + + if (p3p || (size != 8) || (off0 != 0)) { + for (i = 0; i < loop; i++) { + if (qla82xx_pci_mem_read_2M(ha, off8 + + (i << shift_amount), &word[i * scale], 8)) + return -1; + } + } + + switch (size) { + case 1: + tmpw = *((uint8_t *)data); + break; + case 2: + tmpw = *((uint16_t *)data); + break; + case 4: + tmpw = *((uint32_t *)data); + break; + case 8: + default: + tmpw = *((uint64_t *)data); + break; + } + + if (QLA82XX_IS_REVISION_P3PLUS(ha->chip_revision)) { + if (sz[0] == 8) { + word[startword] = tmpw; + } else { + word[startword] &= + ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8)); + word[startword] |= tmpw << (off0 * 8); + } + if (sz[1] != 0) { + word[startword+1] &= ~(~0ULL << (sz[1] * 8)); + word[startword+1] |= tmpw >> (sz[0] * 8); + } + } else { + word[startword] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8)); + word[startword] |= tmpw << (off0 * 8); + + if (loop == 2) { + word[1] &= ~(~0ULL << (sz[1] * 8)); + word[1] |= tmpw >> (sz[0] * 8); + } + } + + /* + * don't lock here - write_wx gets the lock if each time + * write_lock_irqsave(&adapter->adapter_lock, flags); + * netxen_nic_pci_change_crbwindow_128M(adapter, 0); + */ + for (i = 0; i < loop; i++) { + temp = off8 + (i << shift_amount); + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_LO, temp); + temp = 0; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_HI, temp); + temp = word[i * scale] & 0xffffffff; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_LO, temp); + temp = (word[i * scale] >> 32) & 0xffffffff; + qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_HI, temp); + if (QLA82XX_IS_REVISION_P3PLUS(ha->chip_revision)) { + temp = word[i*scale + 1] & 0xffffffff; + qla82xx_wr_32(ha, mem_crb + + MIU_TEST_AGT_WRDATA_UPPER_LO, temp); + temp = (word[i*scale + 1] >> 32) & 0xffffffff; + qla82xx_wr_32(ha, mem_crb + + MIU_TEST_AGT_WRDATA_UPPER_HI, temp); + } + + temp = MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); + temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; + qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + temp = qla82xx_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL); + if ((temp & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + if (printk_ratelimit()) + dev_err(&ha->pdev->dev, + "failed to write through agent\n"); + ret = -1; + break; + } + } + + return ret; +} + +/* PCI related functions */ +char * +qla82xx_pci_info_str(struct scsi_qla_host *vha, char *str) +{ + int pcie_reg; + struct qla_hw_data *ha = vha->hw; + char lwstr[6]; + uint16_t lnk; + + pcie_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP); + pci_read_config_word(ha->pdev, pcie_reg + PCI_EXP_LNKSTA, &lnk); + ha->link_width = (lnk >> 4) & 0x3f; + + strcpy(str, "PCIe ("); + strcat(str, "2.5Gb/s "); + snprintf(lwstr, sizeof(lwstr), "x%d)", ha->link_width); + strcat(str, lwstr); + return str; +} + +int qla82xx_pci_region_offset(struct pci_dev *pdev, int region) +{ + unsigned long val = 0; + u32 control; + + switch (region) { + case 0: + val = 0; + break; + case 1: + pci_read_config_dword(pdev, QLA82XX_PCI_REG_MSIX_TBL, &control); + val = control + QLA82XX_MSIX_TBL_SPACE; + break; + } + return val; +} + +int qla82xx_pci_region_len(struct pci_dev *pdev, int region) +{ + unsigned long val = 0; + u32 control; + switch (region) { + case 0: + pci_read_config_dword(pdev, QLA82XX_PCI_REG_MSIX_TBL, &control); + val = control; + break; + case 1: + val = pci_resource_len(pdev, 0) - + qla82xx_pci_region_offset(pdev, 1); + break; + } + return val; +} + +int +qla82xx_iospace_config(struct qla_hw_data *ha) +{ + uint32_t len = 0; + + if (pci_request_regions(ha->pdev, QLA2XXX_DRIVER_NAME)) { + qla_printk(KERN_WARNING, ha, + "Failed to reserve selected regions (%s)\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + + /* Use MMIO operations for all accesses. */ + if (!(pci_resource_flags(ha->pdev, 0) & IORESOURCE_MEM)) { + qla_printk(KERN_ERR, ha, + "region #0 not an MMIO resource (%s), aborting\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + + len = pci_resource_len(ha->pdev, 0); + ha->nx_pcibase = + (unsigned long)ioremap(pci_resource_start(ha->pdev, 0), len); + if (!ha->nx_pcibase) { + qla_printk(KERN_ERR, ha, + "cannot remap pcibase MMIO (%s), aborting\n", + pci_name(ha->pdev)); + pci_release_regions(ha->pdev); + goto iospace_error_exit; + } + + /* Mapping of IO base pointer */ + ha->iobase = (device_reg_t __iomem *)((uint8_t *)ha->nx_pcibase + + 0xbc000 + (ha->pdev->devfn << 11)); + + if (!ql2xdbwr) { + ha->nxdb_wr_ptr = + (unsigned long)ioremap((pci_resource_start(ha->pdev, 4) + + (ha->pdev->devfn << 12)), 4); + if (!ha->nxdb_wr_ptr) { + qla_printk(KERN_ERR, ha, + "cannot remap MMIO (%s), aborting\n", + pci_name(ha->pdev)); + pci_release_regions(ha->pdev); + goto iospace_error_exit; + } + + /* Mapping of IO base pointer, + * door bell read and write pointer + */ + ha->nxdb_rd_ptr = (uint8_t *) ha->nx_pcibase + (512 * 1024) + + (ha->pdev->devfn * 8); + } else { + ha->nxdb_wr_ptr = (ha->pdev->devfn == 6 ? + QLA82XX_CAMRAM_DB1 : + QLA82XX_CAMRAM_DB2); + } + + ha->max_req_queues = ha->max_rsp_queues = 1; + ha->msix_count = ha->max_rsp_queues + 1; + return 0; + +iospace_error_exit: + return -ENOMEM; +} + +/* GS related functions */ + +/* Initialization related functions */ + +/** + * qla82xx_pci_config() - Setup ISP82xx PCI configuration registers. + * @ha: HA context + * + * Returns 0 on success. +*/ +int +qla82xx_pci_config(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + int ret; + + pci_set_master(ha->pdev); + ret = pci_set_mwi(ha->pdev); + ha->chip_revision = ha->pdev->revision; + return 0; +} + +/** + * qla82xx_reset_chip() - Setup ISP82xx PCI configuration registers. + * @ha: HA context + * + * Returns 0 on success. + */ +void +qla82xx_reset_chip(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + ha->isp_ops->disable_intrs(ha); +} + +void qla82xx_config_rings(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct device_reg_82xx __iomem *reg = &ha->iobase->isp82; + struct init_cb_81xx *icb; + struct req_que *req = ha->req_q_map[0]; + struct rsp_que *rsp = ha->rsp_q_map[0]; + + /* Setup ring parameters in initialization control block. */ + icb = (struct init_cb_81xx *)ha->init_cb; + icb->request_q_outpointer = __constant_cpu_to_le16(0); + icb->response_q_inpointer = __constant_cpu_to_le16(0); + icb->request_q_length = cpu_to_le16(req->length); + icb->response_q_length = cpu_to_le16(rsp->length); + icb->request_q_address[0] = cpu_to_le32(LSD(req->dma)); + icb->request_q_address[1] = cpu_to_le32(MSD(req->dma)); + icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma)); + icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma)); + + icb->version = 1; + icb->frame_payload_size = 2112; + icb->execution_throttle = 8; + icb->exchange_count = 128; + icb->login_retry_count = 8; + + WRT_REG_DWORD((unsigned long __iomem *)®->req_q_out[0], 0); + WRT_REG_DWORD((unsigned long __iomem *)®->rsp_q_in[0], 0); + WRT_REG_DWORD((unsigned long __iomem *)®->rsp_q_out[0], 0); +} + +void qla82xx_reset_adapter(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + vha->flags.online = 0; + qla2x00_try_to_stop_firmware(vha); + ha->isp_ops->disable_intrs(ha); +} + +int qla82xx_fw_load_from_blob(struct qla_hw_data *ha) +{ + u64 *ptr64; + u32 i, flashaddr, size; + __le64 data; + + size = (IMAGE_START - BOOTLD_START) / 8; + + ptr64 = (u64 *)&ha->hablob->fw->data[BOOTLD_START]; + flashaddr = BOOTLD_START; + + for (i = 0; i < size; i++) { + data = cpu_to_le64(ptr64[i]); + qla82xx_pci_mem_write_2M(ha, flashaddr, &data, 8); + flashaddr += 8; + } + + size = *(u32 *)&ha->hablob->fw->data[FW_SIZE_OFFSET]; + size = (__force u32)cpu_to_le32(size) / 8; + ptr64 = (u64 *)&ha->hablob->fw->data[IMAGE_START]; + flashaddr = FLASH_ADDR_START; + + for (i = 0; i < size; i++) { + data = cpu_to_le64(ptr64[i]); + + if (qla82xx_pci_mem_write_2M(ha, flashaddr, &data, 8)) + return -EIO; + flashaddr += 8; + } + + /* Write a magic value to CAMRAM register + * at a specified offset to indicate + * that all data is written and + * ready for firmware to initialize. + */ + qla82xx_wr_32(ha, QLA82XX_CAM_RAM(0x1fc), 0x12345678); + + if (QLA82XX_IS_REVISION_P3PLUS(ha->chip_revision)) { + qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x18, 0x1020); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0x80001e); + } else + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0x80001d); + return 0; +} + +int qla82xx_check_cmdpeg_state(struct qla_hw_data *ha) +{ + u32 val = 0; + int retries = 60; + + do { + read_lock(&ha->hw_lock); + val = qla82xx_rd_32(ha, CRB_CMDPEG_STATE); + read_unlock(&ha->hw_lock); + + switch (val) { + case PHAN_INITIALIZE_COMPLETE: + case PHAN_INITIALIZE_ACK: + return QLA_SUCCESS; + case PHAN_INITIALIZE_FAILED: + break; + default: + break; + } + qla_printk(KERN_WARNING, ha, + "CRB_CMDPEG_STATE: 0x%x and retries: 0x%x\n", + val, retries); + + msleep(500); + + } while (--retries); + + qla_printk(KERN_INFO, ha, + "Cmd Peg initialization failed: 0x%x.\n", val); + + qla82xx_check_for_bad_spd(ha); + val = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_PEGTUNE_DONE); + read_lock(&ha->hw_lock); + qla82xx_wr_32(ha, CRB_CMDPEG_STATE, PHAN_INITIALIZE_FAILED); + read_unlock(&ha->hw_lock); + return QLA_FUNCTION_FAILED; +} + +int qla82xx_check_rcvpeg_state(struct qla_hw_data *ha) +{ + u32 val = 0; + int retries = 60; + + do { + read_lock(&ha->hw_lock); + val = qla82xx_rd_32(ha, CRB_RCVPEG_STATE); + read_unlock(&ha->hw_lock); + + switch (val) { + case PHAN_INITIALIZE_COMPLETE: + case PHAN_INITIALIZE_ACK: + return QLA_SUCCESS; + case PHAN_INITIALIZE_FAILED: + break; + default: + break; + } + + qla_printk(KERN_WARNING, ha, + "CRB_RCVPEG_STATE: 0x%x and retries: 0x%x\n", + val, retries); + + msleep(500); + + } while (--retries); + + qla_printk(KERN_INFO, ha, + "Rcv Peg initialization failed: 0x%x.\n", val); + read_lock(&ha->hw_lock); + qla82xx_wr_32(ha, CRB_RCVPEG_STATE, PHAN_INITIALIZE_FAILED); + read_unlock(&ha->hw_lock); + return QLA_FUNCTION_FAILED; +} + +/* ISR related functions */ +uint32_t qla82xx_isr_int_target_mask_enable[8] = { + ISR_INT_TARGET_MASK, ISR_INT_TARGET_MASK_F1, + ISR_INT_TARGET_MASK_F2, ISR_INT_TARGET_MASK_F3, + ISR_INT_TARGET_MASK_F4, ISR_INT_TARGET_MASK_F5, + ISR_INT_TARGET_MASK_F7, ISR_INT_TARGET_MASK_F7 +}; + +uint32_t qla82xx_isr_int_target_status[8] = { + ISR_INT_TARGET_STATUS, ISR_INT_TARGET_STATUS_F1, + ISR_INT_TARGET_STATUS_F2, ISR_INT_TARGET_STATUS_F3, + ISR_INT_TARGET_STATUS_F4, ISR_INT_TARGET_STATUS_F5, + ISR_INT_TARGET_STATUS_F7, ISR_INT_TARGET_STATUS_F7 +}; + +static struct qla82xx_legacy_intr_set legacy_intr[] = \ + QLA82XX_LEGACY_INTR_CONFIG; + +/* + * qla82xx_mbx_completion() - Process mailbox command completions. + * @ha: SCSI driver HA context + * @mb0: Mailbox0 register + */ +void +qla82xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) +{ + uint16_t cnt; + uint16_t __iomem *wptr; + struct qla_hw_data *ha = vha->hw; + struct device_reg_82xx __iomem *reg = &ha->iobase->isp82; + wptr = (uint16_t __iomem *)®->mailbox_out[1]; + + /* Load return mailbox registers. */ + ha->flags.mbox_int = 1; + ha->mailbox_out[0] = mb0; + + for (cnt = 1; cnt < ha->mbx_count; cnt++) { + ha->mailbox_out[cnt] = RD_REG_WORD(wptr); + wptr++; + } + + if (ha->mcp) { + DEBUG3_11(printk(KERN_INFO "%s(%ld): " + "Got mailbox completion. cmd=%x.\n", + __func__, vha->host_no, ha->mcp->mb[0])); + } else { + qla_printk(KERN_INFO, ha, + "%s(%ld): MBX pointer ERROR!\n", + __func__, vha->host_no); + } +} + +/* + * qla82xx_intr_handler() - Process interrupts for the ISP23xx and ISP63xx. + * @irq: + * @dev_id: SCSI driver HA context + * @regs: + * + * Called by system whenever the host adapter generates an interrupt. + * + * Returns handled flag. + */ +irqreturn_t +qla82xx_intr_handler(int irq, void *dev_id) +{ + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct rsp_que *rsp; + struct device_reg_82xx __iomem *reg; + int status = 0, status1 = 0; + unsigned long flags; + unsigned long iter; + uint32_t stat; + uint16_t mb[4]; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + printk(KERN_INFO + "%s(): NULL response queue pointer\n", __func__); + return IRQ_NONE; + } + ha = rsp->hw; + + if (!ha->flags.msi_enabled) { + status = qla82xx_rd_32(ha, ISR_INT_VECTOR); + if (!(status & ha->nx_legacy_intr.int_vec_bit)) + return IRQ_NONE; + + status1 = qla82xx_rd_32(ha, ISR_INT_STATE_REG); + if (!ISR_IS_LEGACY_INTR_TRIGGERED(status1)) + return IRQ_NONE; + } + + /* clear the interrupt */ + qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_status_reg, 0xffffffff); + + /* read twice to ensure write is flushed */ + qla82xx_rd_32(ha, ISR_INT_VECTOR); + qla82xx_rd_32(ha, ISR_INT_VECTOR); + + reg = &ha->iobase->isp82; + + spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); + for (iter = 1; iter--; ) { + + if (RD_REG_DWORD(®->host_int)) { + stat = RD_REG_DWORD(®->host_status); + if ((stat & HSRX_RISC_INT) == 0) + break; + + switch (stat & 0xff) { + case 0x1: + case 0x2: + case 0x10: + case 0x11: + qla82xx_mbx_completion(vha, MSW(stat)); + status |= MBX_INTERRUPT; + break; + case 0x12: + mb[0] = MSW(stat); + mb[1] = RD_REG_WORD(®->mailbox_out[1]); + mb[2] = RD_REG_WORD(®->mailbox_out[2]); + mb[3] = RD_REG_WORD(®->mailbox_out[3]); + qla2x00_async_event(vha, rsp, mb); + break; + case 0x13: + qla24xx_process_response_queue(vha, rsp); + break; + default: + DEBUG2(printk("scsi(%ld): " + " Unrecognized interrupt type (%d).\n", + vha->host_no, stat & 0xff)); + break; + } + } + WRT_REG_DWORD(®->host_int, 0); + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (!ha->flags.msi_enabled) + qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0xfbff); + +#ifdef QL_DEBUG_LEVEL_17 + if (!irq && ha->flags.eeh_busy) + qla_printk(KERN_WARNING, ha, + "isr: status %x, cmd_flags %lx, mbox_int %x, stat %x\n", + status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat); +#endif + + if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && + (status & MBX_INTERRUPT) && ha->flags.mbox_int) { + set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + complete(&ha->mbx_intr_comp); + } + return IRQ_HANDLED; +} + +irqreturn_t +qla82xx_msix_default(int irq, void *dev_id) +{ + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct rsp_que *rsp; + struct device_reg_82xx __iomem *reg; + int status = 0; + unsigned long flags; + uint32_t stat; + uint16_t mb[4]; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + printk(KERN_INFO + "%s(): NULL response queue pointer\n", __func__); + return IRQ_NONE; + } + ha = rsp->hw; + + reg = &ha->iobase->isp82; + + spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); + do { + if (RD_REG_DWORD(®->host_int)) { + stat = RD_REG_DWORD(®->host_status); + if ((stat & HSRX_RISC_INT) == 0) + break; + + switch (stat & 0xff) { + case 0x1: + case 0x2: + case 0x10: + case 0x11: + qla82xx_mbx_completion(vha, MSW(stat)); + status |= MBX_INTERRUPT; + break; + case 0x12: + mb[0] = MSW(stat); + mb[1] = RD_REG_WORD(®->mailbox_out[1]); + mb[2] = RD_REG_WORD(®->mailbox_out[2]); + mb[3] = RD_REG_WORD(®->mailbox_out[3]); + qla2x00_async_event(vha, rsp, mb); + break; + case 0x13: + qla24xx_process_response_queue(vha, rsp); + break; + default: + DEBUG2(printk("scsi(%ld): " + " Unrecognized interrupt type (%d).\n", + vha->host_no, stat & 0xff)); + break; + } + } + WRT_REG_DWORD(®->host_int, 0); + } while (0); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + +#ifdef QL_DEBUG_LEVEL_17 + if (!irq && ha->flags.eeh_busy) + qla_printk(KERN_WARNING, ha, + "isr: status %x, cmd_flags %lx, mbox_int %x, stat %x\n", + status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat); +#endif + + if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && + (status & MBX_INTERRUPT) && ha->flags.mbox_int) { + set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + complete(&ha->mbx_intr_comp); + } + return IRQ_HANDLED; +} + +irqreturn_t +qla82xx_msix_rsp_q(int irq, void *dev_id) +{ + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct rsp_que *rsp; + struct device_reg_82xx __iomem *reg; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + printk(KERN_INFO + "%s(): NULL response queue pointer\n", __func__); + return IRQ_NONE; + } + + ha = rsp->hw; + reg = &ha->iobase->isp82; + spin_lock_irq(&ha->hardware_lock); + vha = pci_get_drvdata(ha->pdev); + qla24xx_process_response_queue(vha, rsp); + WRT_REG_DWORD(®->host_int, 0); + spin_unlock_irq(&ha->hardware_lock); + return IRQ_HANDLED; +} + +void +qla82xx_poll(int irq, void *dev_id) +{ + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct rsp_que *rsp; + struct device_reg_82xx __iomem *reg; + int status = 0; + uint32_t stat; + uint16_t mb[4]; + unsigned long flags; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + printk(KERN_INFO + "%s(): NULL response queue pointer\n", __func__); + return; + } + ha = rsp->hw; + + reg = &ha->iobase->isp82; + spin_lock_irqsave(&ha->hardware_lock, flags); + vha = pci_get_drvdata(ha->pdev); + + if (RD_REG_DWORD(®->host_int)) { + stat = RD_REG_DWORD(®->host_status); + switch (stat & 0xff) { + case 0x1: + case 0x2: + case 0x10: + case 0x11: + qla82xx_mbx_completion(vha, MSW(stat)); + status |= MBX_INTERRUPT; + break; + case 0x12: + mb[0] = MSW(stat); + mb[1] = RD_REG_WORD(®->mailbox_out[1]); + mb[2] = RD_REG_WORD(®->mailbox_out[2]); + mb[3] = RD_REG_WORD(®->mailbox_out[3]); + qla2x00_async_event(vha, rsp, mb); + break; + case 0x13: + qla24xx_process_response_queue(vha, rsp); + break; + default: + DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " + "(%d).\n", + vha->host_no, stat & 0xff)); + break; + } + } + WRT_REG_DWORD(®->host_int, 0); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +void +qla82xx_enable_intrs(struct qla_hw_data *ha) +{ + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + qla82xx_mbx_intr_enable(vha); + spin_lock_irq(&ha->hardware_lock); + qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0xfbff); + spin_unlock_irq(&ha->hardware_lock); + ha->interrupts_on = 1; +} + +void +qla82xx_disable_intrs(struct qla_hw_data *ha) +{ + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + qla82xx_mbx_intr_disable(vha); + spin_lock_irq(&ha->hardware_lock); + qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0x0400); + spin_unlock_irq(&ha->hardware_lock); + ha->interrupts_on = 0; +} + +void qla82xx_init_flags(struct qla_hw_data *ha) +{ + struct qla82xx_legacy_intr_set *nx_legacy_intr; + + /* ISP 8021 initializations */ + rwlock_init(&ha->hw_lock); + ha->qdr_sn_window = -1; + ha->ddr_mn_window = -1; + ha->curr_window = 255; + ha->portnum = PCI_FUNC(ha->pdev->devfn); + nx_legacy_intr = &legacy_intr[ha->portnum]; + ha->nx_legacy_intr.int_vec_bit = nx_legacy_intr->int_vec_bit; + ha->nx_legacy_intr.tgt_status_reg = nx_legacy_intr->tgt_status_reg; + ha->nx_legacy_intr.tgt_mask_reg = nx_legacy_intr->tgt_mask_reg; + ha->nx_legacy_intr.pci_int_reg = nx_legacy_intr->pci_int_reg; +} + +static inline void +qla82xx_set_drv_active(scsi_qla_host_t *vha) +{ + uint32_t drv_active; + struct qla_hw_data *ha = vha->hw; + + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + + /* If reset value is all FF's, initialize DRV_ACTIVE */ + if (drv_active == 0xffffffff) { + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, 0); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + } + drv_active |= (1 << (ha->portnum * 4)); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active); +} + +inline void +qla82xx_clear_drv_active(struct qla_hw_data *ha) +{ + uint32_t drv_active; + + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + drv_active &= ~(1 << (ha->portnum * 4)); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active); +} + +static inline int +qla82xx_need_reset(struct qla_hw_data *ha) +{ + uint32_t drv_state; + int rval; + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + rval = drv_state & (1 << (ha->portnum * 4)); + return rval; +} + +static inline void +qla82xx_set_rst_ready(struct qla_hw_data *ha) +{ + uint32_t drv_state; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + + /* If reset value is all FF's, initialize DRV_STATE */ + if (drv_state == 0xffffffff) { + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, 0); + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + } + drv_state |= (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); + qla_printk(KERN_INFO, ha, + "%s(%ld):drv_state = 0x%x\n", + __func__, vha->host_no, drv_state); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state); +} + +static inline void +qla82xx_clear_rst_ready(struct qla_hw_data *ha) +{ + uint32_t drv_state; + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + drv_state &= ~(QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state); +} + +static inline void +qla82xx_set_qsnt_ready(struct qla_hw_data *ha) +{ + uint32_t qsnt_state; + + qsnt_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + qsnt_state |= (QLA82XX_DRVST_QSNT_RDY << (ha->portnum * 4)); + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, qsnt_state); +} + +int qla82xx_load_fw(scsi_qla_host_t *vha) +{ + int rst; + struct fw_blob *blob; + struct qla_hw_data *ha = vha->hw; + + /* Put both the PEG CMD and RCV PEG to default state + * of 0 before resetting the hardware + */ + qla82xx_wr_32(ha, CRB_CMDPEG_STATE, 0); + qla82xx_wr_32(ha, CRB_RCVPEG_STATE, 0); + + if (qla82xx_pinit_from_rom(vha) != QLA_SUCCESS) { + qla_printk(KERN_ERR, ha, + "%s: Error during CRB Initialization\n", __func__); + return QLA_FUNCTION_FAILED; + } + udelay(500); + + /* Bring QM and CAMRAM out of reset */ + rst = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET); + rst &= ~((1 << 28) | (1 << 24)); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, rst); + + /* + * FW Load priority: + * 1) Operational firmware residing in flash. + * 2) Firmware via request-firmware interface (.bin file). + */ + if (ql2xfwloadbin == 2) + goto try_blob_fw; + + qla_printk(KERN_INFO, ha, + "Attempting to load firmware from flash\n"); + + if (qla82xx_fw_load_from_flash(ha) == QLA_SUCCESS) { + qla_printk(KERN_ERR, ha, + "Firmware loaded successfully from flash\n"); + return QLA_SUCCESS; + } +try_blob_fw: + qla_printk(KERN_INFO, ha, + "Attempting to load firmware from blob\n"); + + /* Load firmware blob. */ + blob = ha->hablob = qla2x00_request_firmware(vha); + if (!blob) { + qla_printk(KERN_ERR, ha, + "Firmware image not present.\n"); + goto fw_load_failed; + } + + if (qla82xx_fw_load_from_blob(ha) == QLA_SUCCESS) { + qla_printk(KERN_ERR, ha, + "%s: Firmware loaded successfully " + " from binary blob\n", __func__); + return QLA_SUCCESS; + } else { + qla_printk(KERN_ERR, ha, + "Firmware load failed from binary blob\n"); + blob->fw = NULL; + blob = NULL; + goto fw_load_failed; + } + return QLA_SUCCESS; + +fw_load_failed: + return QLA_FUNCTION_FAILED; +} + +static int +qla82xx_start_firmware(scsi_qla_host_t *vha) +{ + int pcie_cap; + uint16_t lnk; + struct qla_hw_data *ha = vha->hw; + + /* scrub dma mask expansion register */ + qla82xx_wr_32(ha, CRB_DMA_SHIFT, 0x55555555); + + /* Overwrite stale initialization register values */ + qla82xx_wr_32(ha, QLA82XX_PEG_HALT_STATUS1, 0); + qla82xx_wr_32(ha, QLA82XX_PEG_HALT_STATUS2, 0); + + if (qla82xx_load_fw(vha) != QLA_SUCCESS) { + qla_printk(KERN_INFO, ha, + "%s: Error trying to start fw!\n", __func__); + return QLA_FUNCTION_FAILED; + } + + /* Handshake with the card before we register the devices. */ + if (qla82xx_check_cmdpeg_state(ha) != QLA_SUCCESS) { + qla_printk(KERN_INFO, ha, + "%s: Error during card handshake!\n", __func__); + return QLA_FUNCTION_FAILED; + } + + /* Negotiated Link width */ + pcie_cap = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP); + pci_read_config_word(ha->pdev, pcie_cap + PCI_EXP_LNKSTA, &lnk); + ha->link_width = (lnk >> 4) & 0x3f; + + /* Synchronize with Receive peg */ + return qla82xx_check_rcvpeg_state(ha); +} + +static inline int +qla2xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt, + uint16_t tot_dsds) +{ + uint32_t *cur_dsd = NULL; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct scsi_cmnd *cmd; + struct scatterlist *cur_seg; + uint32_t *dsd_seg; + void *next_dsd; + uint8_t avail_dsds; + uint8_t first_iocb = 1; + uint32_t dsd_list_len; + struct dsd_dma *dsd_ptr; + struct ct6_dsd *ctx; + + cmd = sp->cmd; + + /* Update entry type to indicate Command Type 3 IOCB */ + *((uint32_t *)(&cmd_pkt->entry_type)) = + __constant_cpu_to_le32(COMMAND_TYPE_6); + + /* No data transfer */ + if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { + cmd_pkt->byte_count = __constant_cpu_to_le32(0); + return 0; + } + + vha = sp->fcport->vha; + ha = vha->hw; + + /* Set transfer direction */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) { + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_WRITE_DATA); + ha->qla_stats.output_bytes += scsi_bufflen(cmd); + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_READ_DATA); + ha->qla_stats.input_bytes += scsi_bufflen(cmd); + } + + cur_seg = scsi_sglist(cmd); + ctx = sp->ctx; + + while (tot_dsds) { + avail_dsds = (tot_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : tot_dsds; + tot_dsds -= avail_dsds; + dsd_list_len = (avail_dsds + 1) * QLA_DSD_SIZE; + + dsd_ptr = list_first_entry(&ha->gbl_dsd_list, + struct dsd_dma, list); + next_dsd = dsd_ptr->dsd_addr; + list_del(&dsd_ptr->list); + ha->gbl_dsd_avail--; + list_add_tail(&dsd_ptr->list, &ctx->dsd_list); + ctx->dsd_use_cnt++; + ha->gbl_dsd_inuse++; + + if (first_iocb) { + first_iocb = 0; + dsd_seg = (uint32_t *)&cmd_pkt->fcp_data_dseg_address; + *dsd_seg++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *dsd_seg++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *dsd_seg++ = dsd_list_len; + } else { + *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma)); + *cur_dsd++ = dsd_list_len; + } + cur_dsd = (uint32_t *)next_dsd; + while (avail_dsds) { + dma_addr_t sle_dma; + + sle_dma = sg_dma_address(cur_seg); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg)); + cur_seg++; + avail_dsds--; + } + } + + /* Null termination */ + *cur_dsd++ = 0; + *cur_dsd++ = 0; + *cur_dsd++ = 0; + cmd_pkt->control_flags |= CF_DATA_SEG_DESCR_ENABLE; + return 0; +} + +/* + * qla82xx_calc_dsd_lists() - Determine number of DSD list required + * for Command Type 6. + * + * @dsds: number of data segment decriptors needed + * + * Returns the number of dsd list needed to store @dsds. + */ +inline uint16_t +qla82xx_calc_dsd_lists(uint16_t dsds) +{ + uint16_t dsd_lists = 0; + + dsd_lists = (dsds/QLA_DSDS_PER_IOCB); + if (dsds % QLA_DSDS_PER_IOCB) + dsd_lists++; + return dsd_lists; +} + +/* + * qla82xx_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. + */ +int +qla82xx_start_scsi(srb_t *sp) +{ + int ret, nseg; + unsigned long flags; + struct scsi_cmnd *cmd; + uint32_t *clr_ptr; + uint32_t index; + uint32_t handle; + uint16_t cnt; + uint16_t req_cnt; + uint16_t tot_dsds; + struct device_reg_82xx __iomem *reg; + uint32_t dbval; + uint32_t *fcp_dl; + uint8_t additional_cdb_len; + struct ct6_dsd *ctx; + struct scsi_qla_host *vha = sp->fcport->vha; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = NULL; + struct rsp_que *rsp = NULL; + + /* Setup device pointers. */ + ret = 0; + reg = &ha->iobase->isp82; + cmd = sp->cmd; + req = vha->req; + rsp = ha->rsp_q_map[0]; + + /* So we know we haven't pci_map'ed anything yet */ + tot_dsds = 0; + + dbval = 0x04 | (ha->portnum << 5); + + /* Send marker if required */ + if (vha->marker_needed != 0) { + if (qla2x00_marker(vha, req, + rsp, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) + return QLA_FUNCTION_FAILED; + vha->marker_needed = 0; + } + + /* Acquire ring specific lock */ + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* Check for room in outstanding command list. */ + handle = req->current_outstanding_cmd; + for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { + handle++; + if (handle == MAX_OUTSTANDING_COMMANDS) + handle = 1; + if (!req->outstanding_cmds[handle]) + break; + } + if (index == MAX_OUTSTANDING_COMMANDS) + goto queuing_error; + + /* Map the sg table so we have an accurate count of sg entries needed */ + if (scsi_sg_count(cmd)) { + nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd), + scsi_sg_count(cmd), cmd->sc_data_direction); + if (unlikely(!nseg)) + goto queuing_error; + } else + nseg = 0; + + tot_dsds = nseg; + + if (tot_dsds > ql2xshiftctondsd) { + struct cmd_type_6 *cmd_pkt; + uint16_t more_dsd_lists = 0; + struct dsd_dma *dsd_ptr; + uint16_t i; + + more_dsd_lists = qla82xx_calc_dsd_lists(tot_dsds); + if ((more_dsd_lists + ha->gbl_dsd_inuse) >= NUM_DSD_CHAIN) + goto queuing_error; + + if (more_dsd_lists <= ha->gbl_dsd_avail) + goto sufficient_dsds; + else + more_dsd_lists -= ha->gbl_dsd_avail; + + for (i = 0; i < more_dsd_lists; i++) { + dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC); + if (!dsd_ptr) + goto queuing_error; + + dsd_ptr->dsd_addr = dma_pool_alloc(ha->dl_dma_pool, + GFP_ATOMIC, &dsd_ptr->dsd_list_dma); + if (!dsd_ptr->dsd_addr) { + kfree(dsd_ptr); + goto queuing_error; + } + list_add_tail(&dsd_ptr->list, &ha->gbl_dsd_list); + ha->gbl_dsd_avail++; + } + +sufficient_dsds: + req_cnt = 1; + + ctx = sp->ctx = mempool_alloc(ha->ctx_mempool, GFP_ATOMIC); + if (!sp->ctx) { + DEBUG(printk(KERN_INFO + "%s(%ld): failed to allocate" + " ctx.\n", __func__, vha->host_no)); + goto queuing_error; + } + memset(ctx, 0, sizeof(struct ct6_dsd)); + ctx->fcp_cmnd = dma_pool_alloc(ha->fcp_cmnd_dma_pool, + GFP_ATOMIC, &ctx->fcp_cmnd_dma); + if (!ctx->fcp_cmnd) { + DEBUG2_3(printk("%s(%ld): failed to allocate" + " fcp_cmnd.\n", __func__, vha->host_no)); + goto queuing_error_fcp_cmnd; + } + + /* Initialize the DSD list and dma handle */ + INIT_LIST_HEAD(&ctx->dsd_list); + ctx->dsd_use_cnt = 0; + + if (cmd->cmd_len > 16) { + additional_cdb_len = cmd->cmd_len - 16; + if ((cmd->cmd_len % 4) != 0) { + /* SCSI command bigger than 16 bytes must be + * multiple of 4 + */ + goto queuing_error_fcp_cmnd; + } + ctx->fcp_cmnd_len = 12 + cmd->cmd_len + 4; + } else { + additional_cdb_len = 0; + ctx->fcp_cmnd_len = 12 + 16 + 4; + } + + cmd_pkt = (struct cmd_type_6 *)req->ring_ptr; + cmd_pkt->handle = MAKE_HANDLE(req->id, handle); + + /* Zero out remaining portion of packet. */ + /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + + /* Set NPORT-ID and LUN number*/ + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->fcport->vp_idx; + + /* Build IOCB segments */ + if (qla2xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds)) + goto queuing_error_fcp_cmnd; + + int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun); + + /* build FCP_CMND IU */ + memset(ctx->fcp_cmnd, 0, sizeof(struct fcp_cmnd)); + int_to_scsilun(sp->cmd->device->lun, &ctx->fcp_cmnd->lun); + ctx->fcp_cmnd->additional_cdb_len = additional_cdb_len; + + if (cmd->sc_data_direction == DMA_TO_DEVICE) + ctx->fcp_cmnd->additional_cdb_len |= 1; + else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + ctx->fcp_cmnd->additional_cdb_len |= 2; + + memcpy(ctx->fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len); + + fcp_dl = (uint32_t *)(ctx->fcp_cmnd->cdb + 16 + + additional_cdb_len); + *fcp_dl = htonl((uint32_t)scsi_bufflen(cmd)); + + cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(ctx->fcp_cmnd_len); + cmd_pkt->fcp_cmnd_dseg_address[0] = + cpu_to_le32(LSD(ctx->fcp_cmnd_dma)); + cmd_pkt->fcp_cmnd_dseg_address[1] = + cpu_to_le32(MSD(ctx->fcp_cmnd_dma)); + + sp->flags |= SRB_FCP_CMND_DMA_VALID; + cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + /* Set total data segment count. */ + cmd_pkt->entry_count = (uint8_t)req_cnt; + /* Specify response queue number where + * completion should happen + */ + cmd_pkt->entry_status = (uint8_t) rsp->id; + } else { + struct cmd_type_7 *cmd_pkt; + req_cnt = qla24xx_calc_iocbs(tot_dsds); + if (req->cnt < (req_cnt + 2)) { + cnt = (uint16_t)RD_REG_DWORD_RELAXED( + ®->req_q_out[0]); + if (req->ring_index < cnt) + req->cnt = cnt - req->ring_index; + else + req->cnt = req->length - + (req->ring_index - cnt); + } + if (req->cnt < (req_cnt + 2)) + goto queuing_error; + + cmd_pkt = (struct cmd_type_7 *)req->ring_ptr; + cmd_pkt->handle = MAKE_HANDLE(req->id, handle); + + /* Zero out remaining portion of packet. */ + /* tagged queuing modifier -- default is TSK_SIMPLE (0).*/ + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + + /* Set NPORT-ID and LUN number*/ + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->fcport->vp_idx; + + int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, + sizeof(cmd_pkt->lun)); + + /* Load SCSI command packet. */ + memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); + host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb)); + + cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + + /* Build IOCB segments */ + qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds); + + /* Set total data segment count. */ + cmd_pkt->entry_count = (uint8_t)req_cnt; + /* Specify response queue number where + * completion should happen. + */ + cmd_pkt->entry_status = (uint8_t) rsp->id; + + } + /* Build command packet. */ + req->current_outstanding_cmd = handle; + req->outstanding_cmds[handle] = sp; + sp->handle = handle; + sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle; + req->cnt -= req_cnt; + wmb(); + + /* Adjust ring index. */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else + req->ring_ptr++; + + sp->flags |= SRB_DMA_VALID; + + /* Set chip new ring index. */ + /* write, read and verify logic */ + dbval = dbval | (req->id << 8) | (req->ring_index << 16); + if (ql2xdbwr) + qla82xx_wr_32(ha, ha->nxdb_wr_ptr, dbval); + else { + WRT_REG_DWORD( + (unsigned long __iomem *)ha->nxdb_wr_ptr, + dbval); + wmb(); + while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) { + WRT_REG_DWORD( + (unsigned long __iomem *)ha->nxdb_wr_ptr, + dbval); + wmb(); + } + } + + /* Manage unprocessed RIO/ZIO commands in response queue. */ + if (vha->flags.process_response_queue && + rsp->ring_ptr->signature != RESPONSE_PROCESSED) + qla24xx_process_response_queue(vha, rsp); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return QLA_SUCCESS; + +queuing_error_fcp_cmnd: + dma_pool_free(ha->fcp_cmnd_dma_pool, ctx->fcp_cmnd, ctx->fcp_cmnd_dma); +queuing_error: + if (tot_dsds) + scsi_dma_unmap(cmd); + + if (sp->ctx) { + mempool_free(sp->ctx, ha->ctx_mempool); + sp->ctx = NULL; + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return QLA_FUNCTION_FAILED; +} + +uint32_t * +qla82xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, + uint32_t length) +{ + uint32_t i; + uint32_t val; + struct qla_hw_data *ha = vha->hw; + + /* Dword reads to flash. */ + for (i = 0; i < length/4; i++, faddr += 4) { + if (qla82xx_rom_fast_read(ha, faddr, &val)) { + qla_printk(KERN_WARNING, ha, + "Do ROM fast read failed\n"); + goto done_read; + } + dwptr[i] = __constant_cpu_to_le32(val); + } +done_read: + return dwptr; +} + +int +qla82xx_unprotect_flash(struct qla_hw_data *ha) +{ + int ret; + uint32_t val; + + ret = ql82xx_rom_lock_d(ha); + if (ret < 0) { + qla_printk(KERN_WARNING, ha, "ROM Lock failed\n"); + return ret; + } + + ret = qla82xx_read_status_reg(ha, &val); + if (ret < 0) + goto done_unprotect; + + val &= ~(0x7 << 2); + ret = qla82xx_write_status_reg(ha, val); + if (ret < 0) { + val |= (0x7 << 2); + qla82xx_write_status_reg(ha, val); + } + + if (qla82xx_write_disable_flash(ha) != 0) + qla_printk(KERN_WARNING, ha, "Write disable failed\n"); + +done_unprotect: + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK)); + return ret; +} + +int +qla82xx_protect_flash(struct qla_hw_data *ha) +{ + int ret; + uint32_t val; + + ret = ql82xx_rom_lock_d(ha); + if (ret < 0) { + qla_printk(KERN_WARNING, ha, "ROM Lock failed\n"); + return ret; + } + + ret = qla82xx_read_status_reg(ha, &val); + if (ret < 0) + goto done_protect; + + val |= (0x7 << 2); + /* LOCK all sectors */ + ret = qla82xx_write_status_reg(ha, val); + if (ret < 0) + qla_printk(KERN_WARNING, ha, "Write status register failed\n"); + + if (qla82xx_write_disable_flash(ha) != 0) + qla_printk(KERN_WARNING, ha, "Write disable failed\n"); +done_protect: + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK)); + return ret; +} + +int +qla82xx_erase_sector(struct qla_hw_data *ha, int addr) +{ + int ret = 0; + + ret = ql82xx_rom_lock_d(ha); + if (ret < 0) { + qla_printk(KERN_WARNING, ha, "ROM Lock failed\n"); + return ret; + } + + qla82xx_flash_set_write_enable(ha); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, addr); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 3); + qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_SE); + + if (qla82xx_wait_rom_done(ha)) { + qla_printk(KERN_WARNING, ha, + "Error waiting for rom done\n"); + ret = -1; + goto done; + } + ret = qla82xx_flash_wait_write_finish(ha); +done: + qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK)); + return ret; +} + +/* + * Address and length are byte address + */ +uint8_t * +qla82xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, + uint32_t offset, uint32_t length) +{ + scsi_block_requests(vha->host); + qla82xx_read_flash_data(vha, (uint32_t *)buf, offset, length); + scsi_unblock_requests(vha->host); + return buf; +} + +static int +qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr, + uint32_t faddr, uint32_t dwords) +{ + int ret; + uint32_t liter; + uint32_t sec_mask, rest_addr; + dma_addr_t optrom_dma; + void *optrom = NULL; + int page_mode = 0; + struct qla_hw_data *ha = vha->hw; + + ret = -1; + + /* Prepare burst-capable write on supported ISPs. */ + if (page_mode && !(faddr & 0xfff) && + dwords > OPTROM_BURST_DWORDS) { + optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, + &optrom_dma, GFP_KERNEL); + if (!optrom) { + qla_printk(KERN_DEBUG, ha, + "Unable to allocate memory for optrom " + "burst write (%x KB).\n", + OPTROM_BURST_SIZE / 1024); + } + } + + rest_addr = ha->fdt_block_size - 1; + sec_mask = ~rest_addr; + + ret = qla82xx_unprotect_flash(ha); + if (ret) { + qla_printk(KERN_WARNING, ha, + "Unable to unprotect flash for update.\n"); + goto write_done; + } + + for (liter = 0; liter < dwords; liter++, faddr += 4, dwptr++) { + /* Are we at the beginning of a sector? */ + if ((faddr & rest_addr) == 0) { + + ret = qla82xx_erase_sector(ha, faddr); + if (ret) { + DEBUG9(qla_printk(KERN_ERR, ha, + "Unable to erase sector: " + "address=%x.\n", faddr)); + break; + } + } + + /* Go with burst-write. */ + if (optrom && (liter + OPTROM_BURST_DWORDS) <= dwords) { + /* Copy data to DMA'ble buffer. */ + memcpy(optrom, dwptr, OPTROM_BURST_SIZE); + + ret = qla2x00_load_ram(vha, optrom_dma, + (ha->flash_data_off | faddr), + OPTROM_BURST_DWORDS); + if (ret != QLA_SUCCESS) { + qla_printk(KERN_WARNING, ha, + "Unable to burst-write optrom segment " + "(%x/%x/%llx).\n", ret, + (ha->flash_data_off | faddr), + (unsigned long long)optrom_dma); + qla_printk(KERN_WARNING, ha, + "Reverting to slow-write.\n"); + + dma_free_coherent(&ha->pdev->dev, + OPTROM_BURST_SIZE, optrom, optrom_dma); + optrom = NULL; + } else { + liter += OPTROM_BURST_DWORDS - 1; + faddr += OPTROM_BURST_DWORDS - 1; + dwptr += OPTROM_BURST_DWORDS - 1; + continue; + } + } + + ret = qla82xx_write_flash_dword(ha, faddr, + cpu_to_le32(*dwptr)); + if (ret) { + DEBUG9(printk(KERN_DEBUG "%s(%ld) Unable to program" + "flash address=%x data=%x.\n", __func__, + ha->host_no, faddr, *dwptr)); + break; + } + } + + ret = qla82xx_protect_flash(ha); + if (ret) + qla_printk(KERN_WARNING, ha, + "Unable to protect flash after update.\n"); +write_done: + if (optrom) + dma_free_coherent(&ha->pdev->dev, + OPTROM_BURST_SIZE, optrom, optrom_dma); + return ret; +} + +int +qla82xx_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, + uint32_t offset, uint32_t length) +{ + int rval; + + /* Suspend HBA. */ + scsi_block_requests(vha->host); + rval = qla82xx_write_flash_data(vha, (uint32_t *)buf, offset, + length >> 2); + scsi_unblock_requests(vha->host); + + /* Convert return ISP82xx to generic */ + if (rval) + rval = QLA_FUNCTION_FAILED; + else + rval = QLA_SUCCESS; + return rval; +} + +void +qla82xx_start_iocbs(srb_t *sp) +{ + struct qla_hw_data *ha = sp->fcport->vha->hw; + struct req_que *req = ha->req_q_map[0]; + struct device_reg_82xx __iomem *reg; + uint32_t dbval; + + /* Adjust ring index. */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else + req->ring_ptr++; + + reg = &ha->iobase->isp82; + dbval = 0x04 | (ha->portnum << 5); + + dbval = dbval | (req->id << 8) | (req->ring_index << 16); + WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr, dbval); + wmb(); + while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) { + WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr, dbval); + wmb(); + } +} + +/* + * qla82xx_device_bootstrap + * Initialize device, set DEV_READY, start fw + * + * Note: + * IDC lock must be held upon entry + * + * Return: + * Success : 0 + * Failed : 1 + */ +static int +qla82xx_device_bootstrap(scsi_qla_host_t *vha) +{ + int rval, i, timeout; + uint32_t old_count, count; + struct qla_hw_data *ha = vha->hw; + + if (qla82xx_need_reset(ha)) + goto dev_initialize; + + old_count = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER); + + for (i = 0; i < 10; i++) { + timeout = msleep_interruptible(200); + if (timeout) { + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_FAILED); + return QLA_FUNCTION_FAILED; + } + + count = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER); + if (count != old_count) + goto dev_ready; + } + +dev_initialize: + /* set to DEV_INITIALIZING */ + qla_printk(KERN_INFO, ha, "HW State: INITIALIZING\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_INITIALIZING); + + /* Driver that sets device state to initializating sets IDC version */ + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION); + + qla82xx_idc_unlock(ha); + rval = qla82xx_start_firmware(vha); + qla82xx_idc_lock(ha); + + if (rval != QLA_SUCCESS) { + qla_printk(KERN_INFO, ha, "HW State: FAILED\n"); + qla82xx_clear_drv_active(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_FAILED); + return rval; + } + +dev_ready: + qla_printk(KERN_INFO, ha, "HW State: READY\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_READY); + + return QLA_SUCCESS; +} + +static void +qla82xx_dev_failed_handler(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + /* Disable the board */ + qla_printk(KERN_INFO, ha, "Disabling the board\n"); + + /* Set DEV_FAILED flag to disable timer */ + vha->device_flags |= DFLG_DEV_FAILED; + qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16); + qla2x00_mark_all_devices_lost(vha, 0); + vha->flags.online = 0; + vha->flags.init_done = 0; +} + +/* + * qla82xx_need_reset_handler + * Code to start reset sequence + * + * Note: + * IDC lock must be held upon entry + * + * Return: + * Success : 0 + * Failed : 1 + */ +static void +qla82xx_need_reset_handler(scsi_qla_host_t *vha) +{ + uint32_t dev_state, drv_state, drv_active; + unsigned long reset_timeout; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + + if (vha->flags.online) { + qla82xx_idc_unlock(ha); + qla2x00_abort_isp_cleanup(vha); + ha->isp_ops->get_flash_version(vha, req->ring); + ha->isp_ops->nvram_config(vha); + qla82xx_idc_lock(ha); + } + + qla82xx_set_rst_ready(ha); + + /* wait for 10 seconds for reset ack from all functions */ + reset_timeout = jiffies + (ha->nx_reset_timeout * HZ); + + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + + while (drv_state != drv_active) { + if (time_after_eq(jiffies, reset_timeout)) { + qla_printk(KERN_INFO, ha, + "%s: RESET TIMEOUT!\n", QLA2XXX_DRIVER_NAME); + break; + } + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + } + + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + qla_printk(KERN_INFO, ha, "3:Device state is 0x%x = %s\n", dev_state, + dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown"); + + /* Force to DEV_COLD unless someone else is starting a reset */ + if (dev_state != QLA82XX_DEV_INITIALIZING) { + qla_printk(KERN_INFO, ha, "HW State: COLD/RE-INIT\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD); + } +} + +static void +qla82xx_check_fw_alive(scsi_qla_host_t *vha) +{ + uint32_t fw_heartbeat_counter, halt_status; + struct qla_hw_data *ha = vha->hw; + + fw_heartbeat_counter = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER); + if (vha->fw_heartbeat_counter == fw_heartbeat_counter) { + vha->seconds_since_last_heartbeat++; + /* FW not alive after 2 seconds */ + if (vha->seconds_since_last_heartbeat == 2) { + vha->seconds_since_last_heartbeat = 0; + halt_status = qla82xx_rd_32(ha, + QLA82XX_PEG_HALT_STATUS1); + if (halt_status & HALT_STATUS_UNRECOVERABLE) { + set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags); + } else { + qla_printk(KERN_INFO, ha, + "scsi(%ld): %s - detect abort needed\n", + vha->host_no, __func__); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + } + qla2xxx_wake_dpc(vha); + } + } + vha->fw_heartbeat_counter = fw_heartbeat_counter; +} + +/* + * qla82xx_device_state_handler + * Main state handler + * + * Note: + * IDC lock must be held upon entry + * + * Return: + * Success : 0 + * Failed : 1 + */ +int +qla82xx_device_state_handler(scsi_qla_host_t *vha) +{ + uint32_t dev_state; + int rval = QLA_SUCCESS; + unsigned long dev_init_timeout; + struct qla_hw_data *ha = vha->hw; + + qla82xx_idc_lock(ha); + if (!vha->flags.init_done) + qla82xx_set_drv_active(vha); + + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + qla_printk(KERN_INFO, ha, "1:Device state is 0x%x = %s\n", dev_state, + dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown"); + + /* wait for 30 seconds for device to go ready */ + dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ); + + while (1) { + + if (time_after_eq(jiffies, dev_init_timeout)) { + DEBUG(qla_printk(KERN_INFO, ha, + "%s: device init failed!\n", + QLA2XXX_DRIVER_NAME)); + rval = QLA_FUNCTION_FAILED; + break; + } + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + qla_printk(KERN_INFO, ha, + "2:Device state is 0x%x = %s\n", dev_state, + dev_state < MAX_STATES ? + qdev_state[dev_state] : "Unknown"); + + switch (dev_state) { + case QLA82XX_DEV_READY: + goto exit; + case QLA82XX_DEV_COLD: + rval = qla82xx_device_bootstrap(vha); + goto exit; + case QLA82XX_DEV_INITIALIZING: + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + break; + case QLA82XX_DEV_NEED_RESET: + if (!ql2xdontresethba) + qla82xx_need_reset_handler(vha); + break; + case QLA82XX_DEV_NEED_QUIESCENT: + qla82xx_set_qsnt_ready(ha); + case QLA82XX_DEV_QUIESCENT: + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + break; + case QLA82XX_DEV_FAILED: + qla82xx_dev_failed_handler(vha); + rval = QLA_FUNCTION_FAILED; + goto exit; + default: + qla82xx_idc_unlock(ha); + msleep(1000); + qla82xx_idc_lock(ha); + } + } +exit: + qla82xx_idc_unlock(ha); + return rval; +} + +void qla82xx_watchdog(scsi_qla_host_t *vha) +{ + uint32_t dev_state; + struct qla_hw_data *ha = vha->hw; + + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + + /* don't poll if reset is going on */ + if (!(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &vha->dpc_flags))) { + if (dev_state == QLA82XX_DEV_NEED_RESET) { + qla_printk(KERN_WARNING, ha, + "%s(): Adapter reset needed!\n", __func__); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } else { + qla82xx_check_fw_alive(vha); + } + } +} + +int qla82xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) +{ + int rval; + rval = qla82xx_device_state_handler(vha); + return rval; +} + +/* + * qla82xx_abort_isp + * Resets ISP and aborts all outstanding commands. + * + * Input: + * ha = adapter block pointer. + * + * Returns: + * 0 = success + */ +int +qla82xx_abort_isp(scsi_qla_host_t *vha) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + uint32_t dev_state; + + if (vha->device_flags & DFLG_DEV_FAILED) { + qla_printk(KERN_WARNING, ha, + "%s(%ld): Device in failed state, " + "Exiting.\n", __func__, vha->host_no); + return QLA_SUCCESS; + } + + qla82xx_idc_lock(ha); + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + if (dev_state == QLA82XX_DEV_READY) { + qla_printk(KERN_INFO, ha, "HW State: NEED RESET\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_NEED_RESET); + } else + qla_printk(KERN_INFO, ha, "HW State: %s\n", + dev_state < MAX_STATES ? + qdev_state[dev_state] : "Unknown"); + qla82xx_idc_unlock(ha); + + rval = qla82xx_device_state_handler(vha); + + qla82xx_idc_lock(ha); + qla82xx_clear_rst_ready(ha); + qla82xx_idc_unlock(ha); + + if (rval == QLA_SUCCESS) + qla82xx_restart_isp(vha); + + if (rval) { + vha->flags.online = 1; + if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { + if (ha->isp_abort_cnt == 0) { + qla_printk(KERN_WARNING, ha, + "ISP error recovery failed - " + "board disabled\n"); + /* + * The next call disables the board + * completely. + */ + ha->isp_ops->reset_adapter(vha); + vha->flags.online = 0; + clear_bit(ISP_ABORT_RETRY, + &vha->dpc_flags); + rval = QLA_SUCCESS; + } else { /* schedule another ISP abort */ + ha->isp_abort_cnt--; + DEBUG(qla_printk(KERN_INFO, ha, + "qla%ld: ISP abort - retry remaining %d\n", + vha->host_no, ha->isp_abort_cnt)); + rval = QLA_FUNCTION_FAILED; + } + } else { + ha->isp_abort_cnt = MAX_RETRIES_OF_ISP_ABORT; + DEBUG(qla_printk(KERN_INFO, ha, + "(%ld): ISP error recovery - retrying (%d) " + "more times\n", vha->host_no, ha->isp_abort_cnt)); + set_bit(ISP_ABORT_RETRY, &vha->dpc_flags); + rval = QLA_FUNCTION_FAILED; + } + } + return rval; +} + +/* + * qla82xx_fcoe_ctx_reset + * Perform a quick reset and aborts all outstanding commands. + * This will only perform an FCoE context reset and avoids a full blown + * chip reset. + * + * Input: + * ha = adapter block pointer. + * is_reset_path = flag for identifying the reset path. + * + * Returns: + * 0 = success + */ +int qla82xx_fcoe_ctx_reset(scsi_qla_host_t *vha) +{ + int rval = QLA_FUNCTION_FAILED; + + if (vha->flags.online) { + /* Abort all outstanding commands, so as to be requeued later */ + qla2x00_abort_isp_cleanup(vha); + } + + /* Stop currently executing firmware. + * This will destroy existing FCoE context at the F/W end. + */ + qla2x00_try_to_stop_firmware(vha); + + /* Restart. Creates a new FCoE context on INIT_FIRMWARE. */ + rval = qla82xx_restart_isp(vha); + + return rval; +} + +/* + * qla2x00_wait_for_fcoe_ctx_reset + * Wait till the FCoE context is reset. + * + * Note: + * Does context switching here. + * Release SPIN_LOCK (if any) before calling this routine. + * + * Return: + * Success (fcoe_ctx reset is done) : 0 + * Failed (fcoe_ctx reset not completed within max loop timout ) : 1 + */ +int qla2x00_wait_for_fcoe_ctx_reset(scsi_qla_host_t *vha) +{ + int status = QLA_FUNCTION_FAILED; + unsigned long wait_reset; + + wait_reset = jiffies + (MAX_LOOP_TIMEOUT * HZ); + while ((test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) + && time_before(jiffies, wait_reset)) { + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ); + + if (!test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) && + !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) { + status = QLA_SUCCESS; + break; + } + } + DEBUG2(printk(KERN_INFO + "%s status=%d\n", __func__, status)); + + return status; +} diff --git a/drivers/scsi/qla2xxx/qla_nx.h b/drivers/scsi/qla2xxx/qla_nx.h new file mode 100644 index 00000000000..f8f99a5ea53 --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_nx.h @@ -0,0 +1,889 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2008 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#ifndef __QLA_NX_H +#define __QLA_NX_H + +/* + * Following are the states of the Phantom. Phantom will set them and + * Host will read to check if the fields are correct. +*/ +#define PHAN_INITIALIZE_FAILED 0xffff +#define PHAN_INITIALIZE_COMPLETE 0xff01 + +/* Host writes the following to notify that it has done the init-handshake */ +#define PHAN_INITIALIZE_ACK 0xf00f +#define PHAN_PEG_RCV_INITIALIZED 0xff01 + +/*CRB_RELATED*/ +#define QLA82XX_CRB_BASE QLA82XX_CAM_RAM(0x200) +#define QLA82XX_REG(X) (QLA82XX_CRB_BASE+(X)) + +#define CRB_CMDPEG_STATE QLA82XX_REG(0x50) +#define CRB_RCVPEG_STATE QLA82XX_REG(0x13c) +#define BOOT_LOADER_DIMM_STATUS QLA82XX_REG(0x54) +#define CRB_DMA_SHIFT QLA82XX_REG(0xcc) + +#define QLA82XX_HW_H0_CH_HUB_ADR 0x05 +#define QLA82XX_HW_H1_CH_HUB_ADR 0x0E +#define QLA82XX_HW_H2_CH_HUB_ADR 0x03 +#define QLA82XX_HW_H3_CH_HUB_ADR 0x01 +#define QLA82XX_HW_H4_CH_HUB_ADR 0x06 +#define QLA82XX_HW_H5_CH_HUB_ADR 0x07 +#define QLA82XX_HW_H6_CH_HUB_ADR 0x08 + +/* Hub 0 */ +#define QLA82XX_HW_MN_CRB_AGT_ADR 0x15 +#define QLA82XX_HW_MS_CRB_AGT_ADR 0x25 + +/* Hub 1 */ +#define QLA82XX_HW_PS_CRB_AGT_ADR 0x73 +#define QLA82XX_HW_QMS_CRB_AGT_ADR 0x00 +#define QLA82XX_HW_RPMX3_CRB_AGT_ADR 0x0b +#define QLA82XX_HW_SQGS0_CRB_AGT_ADR 0x01 +#define QLA82XX_HW_SQGS1_CRB_AGT_ADR 0x02 +#define QLA82XX_HW_SQGS2_CRB_AGT_ADR 0x03 +#define QLA82XX_HW_SQGS3_CRB_AGT_ADR 0x04 +#define QLA82XX_HW_C2C0_CRB_AGT_ADR 0x58 +#define QLA82XX_HW_C2C1_CRB_AGT_ADR 0x59 +#define QLA82XX_HW_C2C2_CRB_AGT_ADR 0x5a +#define QLA82XX_HW_RPMX2_CRB_AGT_ADR 0x0a +#define QLA82XX_HW_RPMX4_CRB_AGT_ADR 0x0c +#define QLA82XX_HW_RPMX7_CRB_AGT_ADR 0x0f +#define QLA82XX_HW_RPMX9_CRB_AGT_ADR 0x12 +#define QLA82XX_HW_SMB_CRB_AGT_ADR 0x18 + +/* Hub 2 */ +#define QLA82XX_HW_NIU_CRB_AGT_ADR 0x31 +#define QLA82XX_HW_I2C0_CRB_AGT_ADR 0x19 +#define QLA82XX_HW_I2C1_CRB_AGT_ADR 0x29 + +#define QLA82XX_HW_SN_CRB_AGT_ADR 0x10 +#define QLA82XX_HW_I2Q_CRB_AGT_ADR 0x20 +#define QLA82XX_HW_LPC_CRB_AGT_ADR 0x22 +#define QLA82XX_HW_ROMUSB_CRB_AGT_ADR 0x21 +#define QLA82XX_HW_QM_CRB_AGT_ADR 0x66 +#define QLA82XX_HW_SQG0_CRB_AGT_ADR 0x60 +#define QLA82XX_HW_SQG1_CRB_AGT_ADR 0x61 +#define QLA82XX_HW_SQG2_CRB_AGT_ADR 0x62 +#define QLA82XX_HW_SQG3_CRB_AGT_ADR 0x63 +#define QLA82XX_HW_RPMX1_CRB_AGT_ADR 0x09 +#define QLA82XX_HW_RPMX5_CRB_AGT_ADR 0x0d +#define QLA82XX_HW_RPMX6_CRB_AGT_ADR 0x0e +#define QLA82XX_HW_RPMX8_CRB_AGT_ADR 0x11 + +/* Hub 3 */ +#define QLA82XX_HW_PH_CRB_AGT_ADR 0x1A +#define QLA82XX_HW_SRE_CRB_AGT_ADR 0x50 +#define QLA82XX_HW_EG_CRB_AGT_ADR 0x51 +#define QLA82XX_HW_RPMX0_CRB_AGT_ADR 0x08 + +/* Hub 4 */ +#define QLA82XX_HW_PEGN0_CRB_AGT_ADR 0x40 +#define QLA82XX_HW_PEGN1_CRB_AGT_ADR 0x41 +#define QLA82XX_HW_PEGN2_CRB_AGT_ADR 0x42 +#define QLA82XX_HW_PEGN3_CRB_AGT_ADR 0x43 +#define QLA82XX_HW_PEGNI_CRB_AGT_ADR 0x44 +#define QLA82XX_HW_PEGND_CRB_AGT_ADR 0x45 +#define QLA82XX_HW_PEGNC_CRB_AGT_ADR 0x46 +#define QLA82XX_HW_PEGR0_CRB_AGT_ADR 0x47 +#define QLA82XX_HW_PEGR1_CRB_AGT_ADR 0x48 +#define QLA82XX_HW_PEGR2_CRB_AGT_ADR 0x49 +#define QLA82XX_HW_PEGR3_CRB_AGT_ADR 0x4a +#define QLA82XX_HW_PEGN4_CRB_AGT_ADR 0x4b + +/* Hub 5 */ +#define QLA82XX_HW_PEGS0_CRB_AGT_ADR 0x40 +#define QLA82XX_HW_PEGS1_CRB_AGT_ADR 0x41 +#define QLA82XX_HW_PEGS2_CRB_AGT_ADR 0x42 +#define QLA82XX_HW_PEGS3_CRB_AGT_ADR 0x43 +#define QLA82XX_HW_PEGSI_CRB_AGT_ADR 0x44 +#define QLA82XX_HW_PEGSD_CRB_AGT_ADR 0x45 +#define QLA82XX_HW_PEGSC_CRB_AGT_ADR 0x46 + +/* Hub 6 */ +#define QLA82XX_HW_CAS0_CRB_AGT_ADR 0x46 +#define QLA82XX_HW_CAS1_CRB_AGT_ADR 0x47 +#define QLA82XX_HW_CAS2_CRB_AGT_ADR 0x48 +#define QLA82XX_HW_CAS3_CRB_AGT_ADR 0x49 +#define QLA82XX_HW_NCM_CRB_AGT_ADR 0x16 +#define QLA82XX_HW_TMR_CRB_AGT_ADR 0x17 +#define QLA82XX_HW_XDMA_CRB_AGT_ADR 0x05 +#define QLA82XX_HW_OCM0_CRB_AGT_ADR 0x06 +#define QLA82XX_HW_OCM1_CRB_AGT_ADR 0x07 + +/* This field defines PCI/X adr [25:20] of agents on the CRB */ +/* */ +#define QLA82XX_HW_PX_MAP_CRB_PH 0 +#define QLA82XX_HW_PX_MAP_CRB_PS 1 +#define QLA82XX_HW_PX_MAP_CRB_MN 2 +#define QLA82XX_HW_PX_MAP_CRB_MS 3 +#define QLA82XX_HW_PX_MAP_CRB_SRE 5 +#define QLA82XX_HW_PX_MAP_CRB_NIU 6 +#define QLA82XX_HW_PX_MAP_CRB_QMN 7 +#define QLA82XX_HW_PX_MAP_CRB_SQN0 8 +#define QLA82XX_HW_PX_MAP_CRB_SQN1 9 +#define QLA82XX_HW_PX_MAP_CRB_SQN2 10 +#define QLA82XX_HW_PX_MAP_CRB_SQN3 11 +#define QLA82XX_HW_PX_MAP_CRB_QMS 12 +#define QLA82XX_HW_PX_MAP_CRB_SQS0 13 +#define QLA82XX_HW_PX_MAP_CRB_SQS1 14 +#define QLA82XX_HW_PX_MAP_CRB_SQS2 15 +#define QLA82XX_HW_PX_MAP_CRB_SQS3 16 +#define QLA82XX_HW_PX_MAP_CRB_PGN0 17 +#define QLA82XX_HW_PX_MAP_CRB_PGN1 18 +#define QLA82XX_HW_PX_MAP_CRB_PGN2 19 +#define QLA82XX_HW_PX_MAP_CRB_PGN3 20 +#define QLA82XX_HW_PX_MAP_CRB_PGN4 QLA82XX_HW_PX_MAP_CRB_SQS2 +#define QLA82XX_HW_PX_MAP_CRB_PGND 21 +#define QLA82XX_HW_PX_MAP_CRB_PGNI 22 +#define QLA82XX_HW_PX_MAP_CRB_PGS0 23 +#define QLA82XX_HW_PX_MAP_CRB_PGS1 24 +#define QLA82XX_HW_PX_MAP_CRB_PGS2 25 +#define QLA82XX_HW_PX_MAP_CRB_PGS3 26 +#define QLA82XX_HW_PX_MAP_CRB_PGSD 27 +#define QLA82XX_HW_PX_MAP_CRB_PGSI 28 +#define QLA82XX_HW_PX_MAP_CRB_SN 29 +#define QLA82XX_HW_PX_MAP_CRB_EG 31 +#define QLA82XX_HW_PX_MAP_CRB_PH2 32 +#define QLA82XX_HW_PX_MAP_CRB_PS2 33 +#define QLA82XX_HW_PX_MAP_CRB_CAM 34 +#define QLA82XX_HW_PX_MAP_CRB_CAS0 35 +#define QLA82XX_HW_PX_MAP_CRB_CAS1 36 +#define QLA82XX_HW_PX_MAP_CRB_CAS2 37 +#define QLA82XX_HW_PX_MAP_CRB_C2C0 38 +#define QLA82XX_HW_PX_MAP_CRB_C2C1 39 +#define QLA82XX_HW_PX_MAP_CRB_TIMR 40 +#define QLA82XX_HW_PX_MAP_CRB_RPMX1 42 +#define QLA82XX_HW_PX_MAP_CRB_RPMX2 43 +#define QLA82XX_HW_PX_MAP_CRB_RPMX3 44 +#define QLA82XX_HW_PX_MAP_CRB_RPMX4 45 +#define QLA82XX_HW_PX_MAP_CRB_RPMX5 46 +#define QLA82XX_HW_PX_MAP_CRB_RPMX6 47 +#define QLA82XX_HW_PX_MAP_CRB_RPMX7 48 +#define QLA82XX_HW_PX_MAP_CRB_XDMA 49 +#define QLA82XX_HW_PX_MAP_CRB_I2Q 50 +#define QLA82XX_HW_PX_MAP_CRB_ROMUSB 51 +#define QLA82XX_HW_PX_MAP_CRB_CAS3 52 +#define QLA82XX_HW_PX_MAP_CRB_RPMX0 53 +#define QLA82XX_HW_PX_MAP_CRB_RPMX8 54 +#define QLA82XX_HW_PX_MAP_CRB_RPMX9 55 +#define QLA82XX_HW_PX_MAP_CRB_OCM0 56 +#define QLA82XX_HW_PX_MAP_CRB_OCM1 57 +#define QLA82XX_HW_PX_MAP_CRB_SMB 58 +#define QLA82XX_HW_PX_MAP_CRB_I2C0 59 +#define QLA82XX_HW_PX_MAP_CRB_I2C1 60 +#define QLA82XX_HW_PX_MAP_CRB_LPC 61 +#define QLA82XX_HW_PX_MAP_CRB_PGNC 62 +#define QLA82XX_HW_PX_MAP_CRB_PGR0 63 +#define QLA82XX_HW_PX_MAP_CRB_PGR1 4 +#define QLA82XX_HW_PX_MAP_CRB_PGR2 30 +#define QLA82XX_HW_PX_MAP_CRB_PGR3 41 + +/* This field defines CRB adr [31:20] of the agents */ +/* */ + +#define QLA82XX_HW_CRB_HUB_AGT_ADR_MN ((QLA82XX_HW_H0_CH_HUB_ADR << 7) | \ + QLA82XX_HW_MN_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PH ((QLA82XX_HW_H0_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PH_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_MS ((QLA82XX_HW_H0_CH_HUB_ADR << 7) | \ + QLA82XX_HW_MS_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PS ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PS_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SS ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SS_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX3 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_RPMX3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_QMS ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_QMS_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS0 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQGS0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS1 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQGS1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS2 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQGS2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS3 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQGS3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_C2C0 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_C2C0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_C2C1 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_C2C1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX2 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_RPMX2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX4 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_RPMX4_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX7 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_RPMX7_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX9 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_RPMX9_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SMB ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SMB_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_NIU ((QLA82XX_HW_H2_CH_HUB_ADR << 7) | \ + QLA82XX_HW_NIU_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_I2C0 ((QLA82XX_HW_H2_CH_HUB_ADR << 7) | \ + QLA82XX_HW_I2C0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_I2C1 ((QLA82XX_HW_H2_CH_HUB_ADR << 7) | \ + QLA82XX_HW_I2C1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SRE ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SRE_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_EG ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_EG_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX0 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_RPMX0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_QMN ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_QM_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN0 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQG0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN1 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQG1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN2 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQG2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN3 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SQG3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX1 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_RPMX1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX5 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_RPMX5_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX6 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_RPMX6_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX8 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_RPMX8_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS0 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_CAS0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS1 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_CAS1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS2 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_CAS2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS3 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \ + QLA82XX_HW_CAS3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGNI ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGNI_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGND ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGND_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN0 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGN0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN1 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGN1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN2 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGN2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN3 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGN3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN4 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGN4_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGNC ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGNC_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR0 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGR0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR1 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGR1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR2 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGR2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR3 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGR3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGSI ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGSI_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGSD ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGSD_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS0 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGS0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS1 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGS1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS2 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGS2_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS3 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGS3_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGSC ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \ + QLA82XX_HW_PEGSC_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAM ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_NCM_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_TIMR ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_TMR_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_XDMA ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_XDMA_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_SN ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_SN_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_I2Q ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_I2Q_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_ROMUSB ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_ROMUSB_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_OCM0 ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_OCM0_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_OCM1 ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_OCM1_CRB_AGT_ADR) +#define QLA82XX_HW_CRB_HUB_AGT_ADR_LPC ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \ + QLA82XX_HW_LPC_CRB_AGT_ADR) + +#define ROMUSB_GLB (QLA82XX_CRB_ROMUSB + 0x00000) +#define QLA82XX_ROMUSB_GLB_PEGTUNE_DONE (ROMUSB_GLB + 0x005c) +#define QLA82XX_ROMUSB_GLB_STATUS (ROMUSB_GLB + 0x0004) +#define QLA82XX_ROMUSB_GLB_SW_RESET (ROMUSB_GLB + 0x0008) +#define QLA82XX_ROMUSB_ROM_ADDRESS (ROMUSB_ROM + 0x0008) +#define QLA82XX_ROMUSB_ROM_WDATA (ROMUSB_ROM + 0x000c) +#define QLA82XX_ROMUSB_ROM_ABYTE_CNT (ROMUSB_ROM + 0x0010) +#define QLA82XX_ROMUSB_ROM_DUMMY_BYTE_CNT (ROMUSB_ROM + 0x0014) +#define QLA82XX_ROMUSB_ROM_RDATA (ROMUSB_ROM + 0x0018) + +#define ROMUSB_ROM (QLA82XX_CRB_ROMUSB + 0x10000) +#define QLA82XX_ROMUSB_ROM_INSTR_OPCODE (ROMUSB_ROM + 0x0004) +#define QLA82XX_ROMUSB_GLB_CAS_RST (ROMUSB_GLB + 0x0038) + +/* Lock IDs for ROM lock */ +#define ROM_LOCK_DRIVER 0x0d417340 + +#define QLA82XX_PCI_CRB_WINDOWSIZE 0x00100000 /* all are 1MB windows */ +#define QLA82XX_PCI_CRB_WINDOW(A) \ + (QLA82XX_PCI_CRBSPACE + (A)*QLA82XX_PCI_CRB_WINDOWSIZE) +#define QLA82XX_CRB_C2C_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_C2C0) +#define QLA82XX_CRB_C2C_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_C2C1) +#define QLA82XX_CRB_C2C_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_C2C2) +#define QLA82XX_CRB_CAM \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAM) +#define QLA82XX_CRB_CASPER \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS) +#define QLA82XX_CRB_CASPER_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS0) +#define QLA82XX_CRB_CASPER_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS1) +#define QLA82XX_CRB_CASPER_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS2) +#define QLA82XX_CRB_DDR_MD \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_MS) +#define QLA82XX_CRB_DDR_NET \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_MN) +#define QLA82XX_CRB_EPG \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_EG) +#define QLA82XX_CRB_I2Q \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_I2Q) +#define QLA82XX_CRB_NIU \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_NIU) + +#define QLA82XX_CRB_PCIX_HOST \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PH) +#define QLA82XX_CRB_PCIX_HOST2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PH2) +#define QLA82XX_CRB_PCIX_MD \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PS) +#define QLA82XX_CRB_PCIE \ + QLA82XX_CRB_PCIX_MD + +/* window 1 pcie slot */ +#define QLA82XX_CRB_PCIE2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PS2) +#define QLA82XX_CRB_PEG_MD_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS0) +#define QLA82XX_CRB_PEG_MD_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS1) +#define QLA82XX_CRB_PEG_MD_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS2) +#define QLA82XX_CRB_PEG_MD_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS3) +#define QLA82XX_CRB_PEG_MD_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS3) +#define QLA82XX_CRB_PEG_MD_D \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGSD) +#define QLA82XX_CRB_PEG_MD_I \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGSI) +#define QLA82XX_CRB_PEG_NET_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN0) +#define QLA82XX_CRB_PEG_NET_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN1) +#define QLA82XX_CRB_PEG_NET_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN2) +#define QLA82XX_CRB_PEG_NET_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN3) +#define QLA82XX_CRB_PEG_NET_4 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN4) +#define QLA82XX_CRB_PEG_NET_D \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGND) +#define QLA82XX_CRB_PEG_NET_I \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGNI) +#define QLA82XX_CRB_PQM_MD \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_QMS) +#define QLA82XX_CRB_PQM_NET \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_QMN) +#define QLA82XX_CRB_QDR_MD \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SS) +#define QLA82XX_CRB_QDR_NET \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SN) +#define QLA82XX_CRB_ROMUSB \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_ROMUSB) +#define QLA82XX_CRB_RPMX_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX0) +#define QLA82XX_CRB_RPMX_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX1) +#define QLA82XX_CRB_RPMX_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX2) +#define QLA82XX_CRB_RPMX_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX3) +#define QLA82XX_CRB_RPMX_4 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX4) +#define QLA82XX_CRB_RPMX_5 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX5) +#define QLA82XX_CRB_RPMX_6 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX6) +#define QLA82XX_CRB_RPMX_7 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX7) +#define QLA82XX_CRB_SQM_MD_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS0) +#define QLA82XX_CRB_SQM_MD_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS1) +#define QLA82XX_CRB_SQM_MD_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS2) +#define QLA82XX_CRB_SQM_MD_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS3) +#define QLA82XX_CRB_SQM_NET_0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN0) +#define QLA82XX_CRB_SQM_NET_1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN1) +#define QLA82XX_CRB_SQM_NET_2 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN2) +#define QLA82XX_CRB_SQM_NET_3 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN3) +#define QLA82XX_CRB_SRE \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SRE) +#define QLA82XX_CRB_TIMER \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_TIMR) +#define QLA82XX_CRB_XDMA \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_XDMA) +#define QLA82XX_CRB_I2C0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_I2C0) +#define QLA82XX_CRB_I2C1 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_I2C1) +#define QLA82XX_CRB_OCM0 \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_OCM0) +#define QLA82XX_CRB_SMB \ + QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SMB) +#define QLA82XX_CRB_MAX \ + QLA82XX_PCI_CRB_WINDOW(64) + +/* + * ====================== BASE ADDRESSES ON-CHIP ====================== + * Base addresses of major components on-chip. + * ====================== BASE ADDRESSES ON-CHIP ====================== + */ +#define QLA82XX_ADDR_DDR_NET (0x0000000000000000ULL) +#define QLA82XX_ADDR_DDR_NET_MAX (0x000000000fffffffULL) + +/* Imbus address bit used to indicate a host address. This bit is + * eliminated by the pcie bar and bar select before presentation + * over pcie. */ +/* host memory via IMBUS */ +#define QLA82XX_P2_ADDR_PCIE (0x0000000800000000ULL) +#define QLA82XX_P3_ADDR_PCIE (0x0000008000000000ULL) +#define QLA82XX_ADDR_PCIE_MAX (0x0000000FFFFFFFFFULL) +#define QLA82XX_ADDR_OCM0 (0x0000000200000000ULL) +#define QLA82XX_ADDR_OCM0_MAX (0x00000002000fffffULL) +#define QLA82XX_ADDR_OCM1 (0x0000000200400000ULL) +#define QLA82XX_ADDR_OCM1_MAX (0x00000002004fffffULL) +#define QLA82XX_ADDR_QDR_NET (0x0000000300000000ULL) + +#define QLA82XX_P2_ADDR_QDR_NET_MAX (0x00000003001fffffULL) +#define QLA82XX_P3_ADDR_QDR_NET_MAX (0x0000000303ffffffULL) + +#define QLA82XX_PCI_CRBSPACE (unsigned long)0x06000000 +#define QLA82XX_PCI_DIRECT_CRB (unsigned long)0x04400000 +#define QLA82XX_PCI_CAMQM (unsigned long)0x04800000 +#define QLA82XX_PCI_CAMQM_MAX (unsigned long)0x04ffffff +#define QLA82XX_PCI_DDR_NET (unsigned long)0x00000000 +#define QLA82XX_PCI_QDR_NET (unsigned long)0x04000000 +#define QLA82XX_PCI_QDR_NET_MAX (unsigned long)0x043fffff + +/* + * Register offsets for MN + */ +#define MIU_CONTROL (0x000) +#define MIU_TAG (0x004) +#define MIU_TEST_AGT_CTRL (0x090) +#define MIU_TEST_AGT_ADDR_LO (0x094) +#define MIU_TEST_AGT_ADDR_HI (0x098) +#define MIU_TEST_AGT_WRDATA_LO (0x0a0) +#define MIU_TEST_AGT_WRDATA_HI (0x0a4) +#define MIU_TEST_AGT_WRDATA(i) (0x0a0+(4*(i))) +#define MIU_TEST_AGT_RDDATA_LO (0x0a8) +#define MIU_TEST_AGT_RDDATA_HI (0x0ac) +#define MIU_TEST_AGT_RDDATA(i) (0x0a8+(4*(i))) +#define MIU_TEST_AGT_ADDR_MASK 0xfffffff8 +#define MIU_TEST_AGT_UPPER_ADDR(off) (0) + +/* MIU_TEST_AGT_CTRL flags. work for SIU as well */ +#define MIU_TA_CTL_START 1 +#define MIU_TA_CTL_ENABLE 2 +#define MIU_TA_CTL_WRITE 4 +#define MIU_TA_CTL_BUSY 8 + +/*CAM RAM */ +# define QLA82XX_CAM_RAM_BASE (QLA82XX_CRB_CAM + 0x02000) +# define QLA82XX_CAM_RAM(reg) (QLA82XX_CAM_RAM_BASE + (reg)) + +#define QLA82XX_PEG_TUNE_MN_SPD_ZEROED 0x80000000 +#define QLA82XX_BOOT_LOADER_MN_ISSUE 0xff00ffff +#define QLA82XX_PORT_MODE_ADDR (QLA82XX_CAM_RAM(0x24)) +#define QLA82XX_PEG_HALT_STATUS1 (QLA82XX_CAM_RAM(0xa8)) +#define QLA82XX_PEG_HALT_STATUS2 (QLA82XX_CAM_RAM(0xac)) +#define QLA82XX_PEG_ALIVE_COUNTER (QLA82XX_CAM_RAM(0xb0)) + +#define QLA82XX_CAMRAM_DB1 (QLA82XX_CAM_RAM(0x1b8)) +#define QLA82XX_CAMRAM_DB2 (QLA82XX_CAM_RAM(0x1bc)) + +#define HALT_STATUS_UNRECOVERABLE 0x80000000 +#define HALT_STATUS_RECOVERABLE 0x40000000 + +/* Driver Coexistence Defines */ +#define QLA82XX_CRB_DRV_ACTIVE (QLA82XX_CAM_RAM(0x138)) +#define QLA82XX_CRB_DEV_STATE (QLA82XX_CAM_RAM(0x140)) +#define QLA82XX_CRB_DEV_PART_INFO (QLA82XX_CAM_RAM(0x14c)) +#define QLA82XX_CRB_DRV_IDC_VERSION (QLA82XX_CAM_RAM(0x174)) +#define QLA82XX_CRB_DRV_STATE (QLA82XX_CAM_RAM(0x144)) +#define QLA82XX_CRB_DRV_SCRATCH (QLA82XX_CAM_RAM(0x148)) +#define QLA82XX_CRB_DEV_PART_INFO (QLA82XX_CAM_RAM(0x14c)) + +/* Every driver should use these Device State */ +#define QLA82XX_DEV_COLD 1 +#define QLA82XX_DEV_INITIALIZING 2 +#define QLA82XX_DEV_READY 3 +#define QLA82XX_DEV_NEED_RESET 4 +#define QLA82XX_DEV_NEED_QUIESCENT 5 +#define QLA82XX_DEV_FAILED 6 +#define QLA82XX_DEV_QUIESCENT 7 +#define MAX_STATES 8 /* Increment if new state added */ + +#define QLA82XX_IDC_VERSION 1 +#define QLA82XX_ROM_DEV_INIT_TIMEOUT 30 +#define QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT 10 + +#define QLA82XX_ROM_LOCK_ID (QLA82XX_CAM_RAM(0x100)) +#define QLA82XX_CRB_WIN_LOCK_ID (QLA82XX_CAM_RAM(0x124)) +#define QLA82XX_FW_VERSION_MAJOR (QLA82XX_CAM_RAM(0x150)) +#define QLA82XX_FW_VERSION_MINOR (QLA82XX_CAM_RAM(0x154)) +#define QLA82XX_FW_VERSION_SUB (QLA82XX_CAM_RAM(0x158)) +#define QLA82XX_PCIE_REG(reg) (QLA82XX_CRB_PCIE + (reg)) + +#define PCIE_CHICKEN3 (0x120c8) +#define PCIE_SETUP_FUNCTION (0x12040) +#define PCIE_SETUP_FUNCTION2 (0x12048) + +#define QLA82XX_PCIX_PS_REG(reg) (QLA82XX_CRB_PCIX_MD + (reg)) +#define QLA82XX_PCIX_PS2_REG(reg) (QLA82XX_CRB_PCIE2 + (reg)) + +#define PCIE_SEM2_LOCK (0x1c010) /* Flash lock */ +#define PCIE_SEM2_UNLOCK (0x1c014) /* Flash unlock */ +#define PCIE_SEM5_LOCK (0x1c028) /* Coexistence lock */ +#define PCIE_SEM5_UNLOCK (0x1c02c) /* Coexistence unlock */ +#define PCIE_SEM7_LOCK (0x1c038) /* crb win lock */ +#define PCIE_SEM7_UNLOCK (0x1c03c) /* crbwin unlock*/ + +/* Different drive state */ +#define QLA82XX_DRVST_NOT_RDY 0 +#define QLA82XX_DRVST_RST_RDY 1 +#define QLA82XX_DRVST_QSNT_RDY 2 + +/* + * The PCI VendorID and DeviceID for our board. + */ +#define PCI_DEVICE_ID_QLOGIC_ISP8021 0x8021 + +#define QLA82XX_MSIX_TBL_SPACE 8192 +#define QLA82XX_PCI_REG_MSIX_TBL 0x44 +#define QLA82XX_PCI_MSIX_CONTROL 0x40 + +struct crb_128M_2M_sub_block_map { + unsigned valid; + unsigned start_128M; + unsigned end_128M; + unsigned start_2M; +}; + +struct crb_128M_2M_block_map { + struct crb_128M_2M_sub_block_map sub_block[16]; +}; + +struct crb_addr_pair { + long addr; + long data; +}; + +#define ADDR_ERROR ((unsigned long) 0xffffffff) +#define MAX_CTL_CHECK 1000 + +/*************************************************************************** + * PCI related defines. + **************************************************************************/ + +/* + * Interrupt related defines. + */ +#define PCIX_TARGET_STATUS (0x10118) +#define PCIX_TARGET_STATUS_F1 (0x10160) +#define PCIX_TARGET_STATUS_F2 (0x10164) +#define PCIX_TARGET_STATUS_F3 (0x10168) +#define PCIX_TARGET_STATUS_F4 (0x10360) +#define PCIX_TARGET_STATUS_F5 (0x10364) +#define PCIX_TARGET_STATUS_F6 (0x10368) +#define PCIX_TARGET_STATUS_F7 (0x1036c) + +#define PCIX_TARGET_MASK (0x10128) +#define PCIX_TARGET_MASK_F1 (0x10170) +#define PCIX_TARGET_MASK_F2 (0x10174) +#define PCIX_TARGET_MASK_F3 (0x10178) +#define PCIX_TARGET_MASK_F4 (0x10370) +#define PCIX_TARGET_MASK_F5 (0x10374) +#define PCIX_TARGET_MASK_F6 (0x10378) +#define PCIX_TARGET_MASK_F7 (0x1037c) + +/* + * Message Signaled Interrupts + */ +#define PCIX_MSI_F0 (0x13000) +#define PCIX_MSI_F1 (0x13004) +#define PCIX_MSI_F2 (0x13008) +#define PCIX_MSI_F3 (0x1300c) +#define PCIX_MSI_F4 (0x13010) +#define PCIX_MSI_F5 (0x13014) +#define PCIX_MSI_F6 (0x13018) +#define PCIX_MSI_F7 (0x1301c) +#define PCIX_MSI_F(FUNC) (0x13000 + ((FUNC) * 4)) +#define PCIX_INT_VECTOR (0x10100) +#define PCIX_INT_MASK (0x10104) + +/* + * Interrupt state machine and other bits. + */ +#define PCIE_MISCCFG_RC (0x1206c) + +#define ISR_INT_TARGET_STATUS \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS)) +#define ISR_INT_TARGET_STATUS_F1 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F1)) +#define ISR_INT_TARGET_STATUS_F2 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F2)) +#define ISR_INT_TARGET_STATUS_F3 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F3)) +#define ISR_INT_TARGET_STATUS_F4 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F4)) +#define ISR_INT_TARGET_STATUS_F5 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F5)) +#define ISR_INT_TARGET_STATUS_F6 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F6)) +#define ISR_INT_TARGET_STATUS_F7 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F7)) + +#define ISR_INT_TARGET_MASK \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK)) +#define ISR_INT_TARGET_MASK_F1 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F1)) +#define ISR_INT_TARGET_MASK_F2 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F2)) +#define ISR_INT_TARGET_MASK_F3 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F3)) +#define ISR_INT_TARGET_MASK_F4 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F4)) +#define ISR_INT_TARGET_MASK_F5 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F5)) +#define ISR_INT_TARGET_MASK_F6 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F6)) +#define ISR_INT_TARGET_MASK_F7 \ + (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F7)) + +#define ISR_INT_VECTOR \ + (QLA82XX_PCIX_PS_REG(PCIX_INT_VECTOR)) +#define ISR_INT_MASK \ + (QLA82XX_PCIX_PS_REG(PCIX_INT_MASK)) +#define ISR_INT_STATE_REG \ + (QLA82XX_PCIX_PS_REG(PCIE_MISCCFG_RC)) + +#define ISR_MSI_INT_TRIGGER(FUNC) \ + (QLA82XX_PCIX_PS_REG(PCIX_MSI_F(FUNC))) + +#define ISR_IS_LEGACY_INTR_IDLE(VAL) (((VAL) & 0x300) == 0) +#define ISR_IS_LEGACY_INTR_TRIGGERED(VAL) (((VAL) & 0x300) == 0x200) + +/* + * PCI Interrupt Vector Values. + */ +#define PCIX_INT_VECTOR_BIT_F0 0x0080 +#define PCIX_INT_VECTOR_BIT_F1 0x0100 +#define PCIX_INT_VECTOR_BIT_F2 0x0200 +#define PCIX_INT_VECTOR_BIT_F3 0x0400 +#define PCIX_INT_VECTOR_BIT_F4 0x0800 +#define PCIX_INT_VECTOR_BIT_F5 0x1000 +#define PCIX_INT_VECTOR_BIT_F6 0x2000 +#define PCIX_INT_VECTOR_BIT_F7 0x4000 + +struct qla82xx_legacy_intr_set { + uint32_t int_vec_bit; + uint32_t tgt_status_reg; + uint32_t tgt_mask_reg; + uint32_t pci_int_reg; +}; + +#define QLA82XX_LEGACY_INTR_CONFIG \ +{ \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F0, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(0) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F1, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F1, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F1, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(1) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F2, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F2, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F2, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(2) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F3, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F3, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F3, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(3) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F4, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F4, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F4, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(4) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F5, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F5, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F5, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(5) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F6, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F6, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F6, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(6) }, \ + \ + { \ + .int_vec_bit = PCIX_INT_VECTOR_BIT_F7, \ + .tgt_status_reg = ISR_INT_TARGET_STATUS_F7, \ + .tgt_mask_reg = ISR_INT_TARGET_MASK_F7, \ + .pci_int_reg = ISR_MSI_INT_TRIGGER(7) }, \ +} + +#define BOOTLD_START 0x10000 +#define IMAGE_START 0x100000 +#define FLASH_ADDR_START 0x43000 + +/* Magic number to let user know flash is programmed */ +#define QLA82XX_BDINFO_MAGIC 0x12345678 +#define FW_SIZE_OFFSET (0x3e840c) + +#define QLA82XX_IS_REVISION_P3PLUS(_rev_) ((_rev_) >= 0x50) +#define MIU_TEST_AGT_WRDATA_UPPER_LO (0x0b0) +#define MIU_TEST_AGT_WRDATA_UPPER_HI (0x0b4) + +#ifndef readq +static inline u64 readq(void __iomem *addr) +{ + return readl(addr) | (((u64) readl(addr + 4)) << 32LL); +} +#endif + +#ifndef writeq +static inline void writeq(u64 val, void __iomem *addr) +{ + writel(((u32) (val)), (addr)); + writel(((u32) (val >> 32)), (addr + 4)); +} +#endif + +/* Request and response queue size */ +#define REQUEST_ENTRY_CNT_82XX 128 /* Number of request entries. */ +#define RESPONSE_ENTRY_CNT_82XX 128 /* Number of response entries.*/ + +/* + * ISP 8021 I/O Register Set structure definitions. + */ +struct device_reg_82xx { + uint32_t req_q_out[64]; /* Request Queue out-Pointer (64 * 4) */ + uint32_t rsp_q_in[64]; /* Response Queue In-Pointer. */ + uint32_t rsp_q_out[64]; /* Response Queue Out-Pointer. */ + + uint16_t mailbox_in[32]; /* Mail box In registers */ + uint16_t unused_1[32]; + uint32_t hint; /* Host interrupt register */ +#define HINT_MBX_INT_PENDING BIT_0 + uint16_t unused_2[62]; + uint16_t mailbox_out[32]; /* Mail box Out registers */ + uint32_t unused_3[48]; + + uint32_t host_status; /* host status */ +#define HSRX_RISC_INT BIT_15 /* RISC to Host interrupt. */ +#define HSRX_RISC_PAUSED BIT_8 /* RISC Paused. */ + uint32_t host_int; /* Interrupt status. */ +#define ISRX_NX_RISC_INT BIT_0 /* RISC interrupt. */ +}; + +struct fcp_cmnd { + struct scsi_lun lun; + uint8_t crn; + uint8_t task_attribute; + uint8_t task_managment; + uint8_t additional_cdb_len; + uint8_t cdb[260]; /* 256 for CDB len and 4 for FCP_DL */ +}; + +struct dsd_dma { + struct list_head list; + dma_addr_t dsd_list_dma; + void *dsd_addr; +}; + +#define QLA_DSDS_PER_IOCB 37 +#define QLA_DSD_SIZE 12 +struct ct6_dsd { + uint16_t fcp_cmnd_len; + dma_addr_t fcp_cmnd_dma; + struct fcp_cmnd *fcp_cmnd; + int dsd_use_cnt; + struct list_head dsd_list; +}; + +#define MBC_TOGGLE_INTR 0x10 + +/* Flash offset */ +#define FLT_REG_BOOTLOAD_82XX 0x72 +#define FLT_REG_BOOT_CODE_82XX 0x78 +#define FLT_REG_FW_82XX 0x74 +#define FLT_REG_GOLD_FW_82XX 0x75 +#define FLT_REG_VPD_82XX 0x81 + +#define FA_VPD_SIZE_82XX 0x400 + +#define FA_FLASH_LAYOUT_ADDR_82 0xFC400 + +/****************************************************************************** +* +* Definitions specific to M25P flash +* +******************************************************************************* +* Instructions +*/ +#define M25P_INSTR_WREN 0x06 +#define M25P_INSTR_WRDI 0x04 +#define M25P_INSTR_RDID 0x9f +#define M25P_INSTR_RDSR 0x05 +#define M25P_INSTR_WRSR 0x01 +#define M25P_INSTR_READ 0x03 +#define M25P_INSTR_FAST_READ 0x0b +#define M25P_INSTR_PP 0x02 +#define M25P_INSTR_SE 0xd8 +#define M25P_INSTR_BE 0xc7 +#define M25P_INSTR_DP 0xb9 +#define M25P_INSTR_RES 0xab + +#endif diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 48c37e38ed0..be1a8fcbb1f 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -24,11 +24,18 @@ */ char qla2x00_version_str[40]; +static int apidev_major; + /* * SRB allocation cache */ static struct kmem_cache *srb_cachep; +/* + * CT6 CTX allocation cache + */ +static struct kmem_cache *ctx_cachep; + int ql2xlogintimeout = 20; module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xlogintimeout, @@ -65,13 +72,19 @@ MODULE_PARM_DESC(ql2xextended_error_logging, "Option to enable extended error logging, " "Default is 0 - no logging. 1 - log errors."); +int ql2xshiftctondsd = 6; +module_param(ql2xshiftctondsd, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(ql2xshiftctondsd, + "Set to control shifting of command type processing " + "based on total number of SG elements."); + static void qla2x00_free_device(scsi_qla_host_t *); int ql2xfdmienable=1; module_param(ql2xfdmienable, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xfdmienable, - "Enables FDMI registratons " - "Default is 0 - no FDMI. 1 - perfom FDMI."); + "Enables FDMI registrations. " + "0 - no FDMI. Default is 1 - perform FDMI."); #define MAX_Q_DEPTH 32 static int ql2xmaxqdepth = MAX_Q_DEPTH; @@ -79,6 +92,19 @@ module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(ql2xmaxqdepth, "Maximum queue depth to report for target devices."); +/* Do not change the value of this after module load */ +int ql2xenabledif = 1; +module_param(ql2xenabledif, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xenabledif, + " Enable T10-CRC-DIF " + " Default is 0 - No DIF Support. 1 - Enable it"); + +int ql2xenablehba_err_chk; +module_param(ql2xenablehba_err_chk, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(ql2xenablehba_err_chk, + " Enable T10-CRC-DIF Error isolation by HBA" + " Default is 0 - Error isolation disabled, 1 - Enable it"); + int ql2xiidmaenable=1; module_param(ql2xiidmaenable, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xiidmaenable, @@ -114,6 +140,32 @@ MODULE_PARM_DESC(ql2xetsenable, "Enables firmware ETS burst." "Default is 0 - skip ETS enablement."); +int ql2xdbwr; +module_param(ql2xdbwr, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(ql2xdbwr, + "Option to specify scheme for request queue posting\n" + " 0 -- Regular doorbell.\n" + " 1 -- CAMRAM doorbell (faster).\n"); + +int ql2xdontresethba; +module_param(ql2xdontresethba, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(ql2xdontresethba, + "Option to specify reset behaviour\n" + " 0 (Default) -- Reset on failure.\n" + " 1 -- Do not reset on failure.\n"); + +int ql2xtargetreset = 1; +module_param(ql2xtargetreset, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(ql2xtargetreset, + "Enable target reset." + "Default is 1 - use hw defaults."); + + +int ql2xasynctmfenable; +module_param(ql2xasynctmfenable, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(ql2xasynctmfenable, + "Enables issue of TM IOCBs asynchronously via IOCB mechanism" + "Default is 0 - Issue TM IOCBs via mailbox mechanism."); /* * SCSI host template entry points */ @@ -183,6 +235,10 @@ qla2x00_start_timer(scsi_qla_host_t *vha, void *func, unsigned long interval) static inline void qla2x00_restart_timer(scsi_qla_host_t *vha, unsigned long interval) { + /* Currently used for 82XX only. */ + if (vha->device_flags & DFLG_DEV_FAILED) + return; + mod_timer(&vha->timer, jiffies + interval * HZ); } @@ -500,6 +556,14 @@ qla2xxx_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) if (fcport->drport) goto qc24_target_busy; + if (!vha->flags.difdix_supported && + scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) { + DEBUG2(qla_printk(KERN_ERR, ha, + "DIF Cap Not Reg, fail DIF capable cmd's:%x\n", + cmd->cmnd[0])); + cmd->result = DID_NO_CONNECT << 16; + goto qc24_fail_command; + } if (atomic_read(&fcport->state) != FCS_ONLINE) { if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD || atomic_read(&base_vha->loop_state) == LOOP_DEAD) { @@ -618,6 +682,50 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *vha) return (return_status); } +/* + * qla2x00_wait_for_reset_ready + * Wait till the HBA is online after going through + * <= MAX_RETRIES_OF_ISP_ABORT or + * finally HBA is disabled ie marked offline or flash + * operations are in progress. + * + * Input: + * ha - pointer to host adapter structure + * + * Note: + * Does context switching-Release SPIN_LOCK + * (if any) before calling this routine. + * + * Return: + * Success (Adapter is online/no flash ops) : 0 + * Failed (Adapter is offline/disabled/flash ops in progress) : 1 + */ +int +qla2x00_wait_for_reset_ready(scsi_qla_host_t *vha) +{ + int return_status; + unsigned long wait_online; + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + + wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ); + while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) || + test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) || + ha->optrom_state != QLA_SWAITING || + ha->dpc_active) && time_before(jiffies, wait_online)) + msleep(1000); + + if (base_vha->flags.online && ha->optrom_state == QLA_SWAITING) + return_status = QLA_SUCCESS; + else + return_status = QLA_FUNCTION_FAILED; + + DEBUG2(printk("%s return_status=%d\n", __func__, return_status)); + + return return_status; +} + int qla2x00_wait_for_chip_reset(scsi_qla_host_t *vha) { @@ -739,7 +847,8 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) if (sp == NULL) continue; - if (sp->ctx) + if ((sp->ctx) && !(sp->flags & SRB_FCP_CMND_DMA_VALID) && + !IS_PROT_IO(sp)) continue; if (sp->cmd != cmd) continue; @@ -805,7 +914,7 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t, sp = req->outstanding_cmds[cnt]; if (!sp) continue; - if (sp->ctx) + if ((sp->ctx) && !IS_PROT_IO(sp)) continue; if (vha->vp_idx != sp->fcport->vha->vp_idx) continue; @@ -834,6 +943,24 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t, return status; } +void qla82xx_wait_for_pending_commands(scsi_qla_host_t *vha) +{ + int cnt; + srb_t *sp; + struct req_que *req = vha->req; + + DEBUG2(qla_printk(KERN_INFO, vha->hw, + "Waiting for pending commands\n")); + for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + sp = req->outstanding_cmds[cnt]; + if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, + sp, WAIT_HOST) == QLA_SUCCESS) { + DEBUG2(qla_printk(KERN_INFO, vha->hw, + "Done wait for pending commands\n")); + } + } +} + static char *reset_errors[] = { "HBA not online", "HBA not ready", @@ -1004,7 +1131,7 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", vha->host_no, id, lun); - if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) + if (qla2x00_wait_for_reset_ready(vha) != QLA_SUCCESS) goto eh_host_reset_lock; /* @@ -1020,11 +1147,19 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) if (qla2x00_vp_abort_isp(vha)) goto eh_host_reset_lock; } else { + if (IS_QLA82XX(vha->hw)) { + if (!qla82xx_fcoe_ctx_reset(vha)) { + /* Ctx reset success */ + ret = SUCCESS; + goto eh_host_reset_lock; + } + /* fall thru if ctx reset failed */ + } if (ha->wq) flush_workqueue(ha->wq); set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); - if (qla2x00_abort_isp(base_vha)) { + if (ha->isp_ops->abort_isp(base_vha)) { clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); /* failed. schedule dpc to try */ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); @@ -1064,7 +1199,7 @@ qla2x00_loop_reset(scsi_qla_host_t *vha) struct fc_port *fcport; struct qla_hw_data *ha = vha->hw; - if (ha->flags.enable_target_reset) { + if (ql2xtargetreset == 1 && ha->flags.enable_target_reset) { list_for_each_entry(fcport, &vha->vp_fcports, list) { if (fcport->port_type != FCT_TARGET) continue; @@ -1078,7 +1213,7 @@ qla2x00_loop_reset(scsi_qla_host_t *vha) } } - if (ha->flags.enable_lip_full_login && !IS_QLA81XX(ha)) { + if (ha->flags.enable_lip_full_login && !IS_QLA8XXX_TYPE(ha)) { ret = qla2x00_full_login_lip(vha); if (ret != QLA_SUCCESS) { DEBUG2_3(printk("%s(%ld): failed: " @@ -1125,23 +1260,28 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res) sp = req->outstanding_cmds[cnt]; if (sp) { req->outstanding_cmds[cnt] = NULL; - if (!sp->ctx) { + if (!sp->ctx || + (sp->flags & SRB_FCP_CMND_DMA_VALID) || + IS_PROT_IO(sp)) { sp->cmd->result = res; qla2x00_sp_compl(ha, sp); } else { ctx = sp->ctx; - if (ctx->type == SRB_LOGIN_CMD || ctx->type == SRB_LOGOUT_CMD) { - del_timer_sync(&ctx->timer); - ctx->free(sp); + if (ctx->type == SRB_LOGIN_CMD || + ctx->type == SRB_LOGOUT_CMD) { + ctx->u.iocb_cmd->free(sp); } else { - struct srb_bsg* sp_bsg = (struct srb_bsg*)sp->ctx; - if (sp_bsg->bsg_job->request->msgcode == FC_BSG_HST_CT) + struct fc_bsg_job *bsg_job = + ctx->u.bsg_job; + if (bsg_job->request->msgcode + == FC_BSG_HST_CT) kfree(sp->fcport); - sp_bsg->bsg_job->req->errors = 0; - sp_bsg->bsg_job->reply->result = res; - sp_bsg->bsg_job->job_done(sp_bsg->bsg_job); + bsg_job->req->errors = 0; + bsg_job->reply->result = res; + bsg_job->job_done(bsg_job); kfree(sp->ctx); - mempool_free(sp, ha->srb_mempool); + mempool_free(sp, + ha->srb_mempool); } } } @@ -1379,6 +1519,7 @@ static struct isp_operations qla2100_isp_ops = { .write_optrom = qla2x00_write_optrom_data, .get_flash_version = qla2x00_get_flash_version, .start_scsi = qla2x00_start_scsi, + .abort_isp = qla2x00_abort_isp, }; static struct isp_operations qla2300_isp_ops = { @@ -1414,6 +1555,7 @@ static struct isp_operations qla2300_isp_ops = { .write_optrom = qla2x00_write_optrom_data, .get_flash_version = qla2x00_get_flash_version, .start_scsi = qla2x00_start_scsi, + .abort_isp = qla2x00_abort_isp, }; static struct isp_operations qla24xx_isp_ops = { @@ -1449,6 +1591,7 @@ static struct isp_operations qla24xx_isp_ops = { .write_optrom = qla24xx_write_optrom_data, .get_flash_version = qla24xx_get_flash_version, .start_scsi = qla24xx_start_scsi, + .abort_isp = qla2x00_abort_isp, }; static struct isp_operations qla25xx_isp_ops = { @@ -1483,7 +1626,8 @@ static struct isp_operations qla25xx_isp_ops = { .read_optrom = qla25xx_read_optrom_data, .write_optrom = qla24xx_write_optrom_data, .get_flash_version = qla24xx_get_flash_version, - .start_scsi = qla24xx_start_scsi, + .start_scsi = qla24xx_dif_start_scsi, + .abort_isp = qla2x00_abort_isp, }; static struct isp_operations qla81xx_isp_ops = { @@ -1519,6 +1663,43 @@ static struct isp_operations qla81xx_isp_ops = { .write_optrom = qla24xx_write_optrom_data, .get_flash_version = qla24xx_get_flash_version, .start_scsi = qla24xx_start_scsi, + .abort_isp = qla2x00_abort_isp, +}; + +static struct isp_operations qla82xx_isp_ops = { + .pci_config = qla82xx_pci_config, + .reset_chip = qla82xx_reset_chip, + .chip_diag = qla24xx_chip_diag, + .config_rings = qla82xx_config_rings, + .reset_adapter = qla24xx_reset_adapter, + .nvram_config = qla81xx_nvram_config, + .update_fw_options = qla24xx_update_fw_options, + .load_risc = qla82xx_load_risc, + .pci_info_str = qla82xx_pci_info_str, + .fw_version_str = qla24xx_fw_version_str, + .intr_handler = qla82xx_intr_handler, + .enable_intrs = qla82xx_enable_intrs, + .disable_intrs = qla82xx_disable_intrs, + .abort_command = qla24xx_abort_command, + .target_reset = qla24xx_abort_target, + .lun_reset = qla24xx_lun_reset, + .fabric_login = qla24xx_login_fabric, + .fabric_logout = qla24xx_fabric_logout, + .calc_req_entries = NULL, + .build_iocbs = NULL, + .prep_ms_iocb = qla24xx_prep_ms_iocb, + .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb, + .read_nvram = qla24xx_read_nvram_data, + .write_nvram = qla24xx_write_nvram_data, + .fw_dump = qla24xx_fw_dump, + .beacon_on = qla24xx_beacon_on, + .beacon_off = qla24xx_beacon_off, + .beacon_blink = qla24xx_beacon_blink, + .read_optrom = qla82xx_read_optrom_data, + .write_optrom = qla82xx_write_optrom_data, + .get_flash_version = qla24xx_get_flash_version, + .start_scsi = qla82xx_start_scsi, + .abort_isp = qla82xx_abort_isp, }; static inline void @@ -1607,10 +1788,22 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha) ha->device_type |= DT_IIDMA; ha->fw_srisc_address = RISC_START_ADDRESS_2400; break; + case PCI_DEVICE_ID_QLOGIC_ISP8021: + ha->device_type |= DT_ISP8021; + ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->fw_srisc_address = RISC_START_ADDRESS_2400; + /* Initialize 82XX ISP flags */ + qla82xx_init_flags(ha); + break; } - /* Get adapter physical port no from interrupt pin register. */ - pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no); + if (IS_QLA82XX(ha)) + ha->port_no = !(ha->portnum & 1); + else + /* Get adapter physical port no from interrupt pin register. */ + pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no); + if (ha->port_no & 1) ha->flags.port0 = 1; else @@ -1624,6 +1817,9 @@ qla2x00_iospace_config(struct qla_hw_data *ha) uint16_t msix; int cpus; + if (IS_QLA82XX(ha)) + return qla82xx_iospace_config(ha); + if (pci_request_selected_regions(ha->pdev, ha->bars, QLA2XXX_DRIVER_NAME)) { qla_printk(KERN_WARNING, ha, @@ -1767,7 +1963,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5422 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532 || - pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8001) { + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8001 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8021) { bars = pci_select_bars(pdev, IORESOURCE_MEM); mem_only = 1; } @@ -1897,6 +2094,19 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX; ha->nvram_conf_off = ~0; ha->nvram_data_off = ~0; + } else if (IS_QLA82XX(ha)) { + ha->mbx_count = MAILBOX_REGISTER_COUNT; + req_length = REQUEST_ENTRY_CNT_82XX; + rsp_length = RESPONSE_ENTRY_CNT_82XX; + ha->max_loop_id = SNS_LAST_LOOP_ID_2300; + ha->init_cb_size = sizeof(struct mid_init_cb_81xx); + ha->gid_list_info_size = 8; + ha->optrom_size = OPTROM_SIZE_82XX; + ha->isp_ops = &qla82xx_isp_ops; + ha->flash_conf_off = FARX_ACCESS_FLASH_CONF; + ha->flash_data_off = FARX_ACCESS_FLASH_DATA; + ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; + ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; } mutex_init(&ha->vport_lock); @@ -1969,6 +2179,7 @@ que_init: " pointers\n"); goto probe_init_failed; } + ha->rsp_q_map[0] = rsp; ha->req_q_map[0] = req; rsp->req = req; @@ -1987,6 +2198,12 @@ que_init: rsp->rsp_q_out = &ha->mqiobase->isp25mq.rsp_q_out; } + if (IS_QLA82XX(ha)) { + req->req_q_out = &ha->iobase->isp82.req_q_out[0]; + rsp->rsp_q_in = &ha->iobase->isp82.rsp_q_in[0]; + rsp->rsp_q_out = &ha->iobase->isp82.rsp_q_out[0]; + } + if (qla2x00_initialize_adapter(base_vha)) { qla_printk(KERN_WARNING, ha, "Failed to initialize adapter\n"); @@ -1995,6 +2212,14 @@ que_init: "Adapter flags %x.\n", base_vha->host_no, base_vha->device_flags)); + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_FAILED); + qla82xx_idc_unlock(ha); + qla_printk(KERN_INFO, ha, "HW State: FAILED\n"); + } + ret = -ENODEV; goto probe_failed; } @@ -2033,6 +2258,24 @@ skip_dpc: DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n", base_vha->host_no, ha)); + if (IS_QLA25XX(ha) && ql2xenabledif) { + if (ha->fw_attributes & BIT_4) { + base_vha->flags.difdix_supported = 1; + DEBUG18(qla_printk(KERN_INFO, ha, + "Registering for DIF/DIX type 1 and 3" + " protection.\n")); + scsi_host_set_prot(host, + SHOST_DIF_TYPE1_PROTECTION + | SHOST_DIF_TYPE3_PROTECTION + | SHOST_DIX_TYPE1_PROTECTION + | SHOST_DIX_TYPE3_PROTECTION); + scsi_host_set_guard(host, SHOST_DIX_GUARD_CRC); + } else + base_vha->flags.difdix_supported = 0; + } + + ha->isp_ops->enable_intrs(ha); + ret = scsi_add_host(host, &pdev->dev); if (ret) goto probe_failed; @@ -2040,8 +2283,6 @@ skip_dpc: base_vha->flags.init_done = 1; base_vha->flags.online = 1; - ha->isp_ops->enable_intrs(ha); - scsi_scan_host(host); qla2x00_alloc_sysfs_attr(base_vha); @@ -2083,9 +2324,17 @@ probe_failed: scsi_host_put(base_vha->host); probe_hw_failed: - if (ha->iobase) - iounmap(ha->iobase); - + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_clear_drv_active(ha); + qla82xx_idc_unlock(ha); + iounmap((device_reg_t __iomem *)ha->nx_pcibase); + if (!ql2xdbwr) + iounmap((device_reg_t __iomem *)ha->nxdb_wr_ptr); + } else { + if (ha->iobase) + iounmap(ha->iobase); + } pci_release_selected_regions(ha->pdev, ha->bars); kfree(ha); ha = NULL; @@ -2152,11 +2401,17 @@ qla2x00_remove_one(struct pci_dev *pdev) scsi_host_put(base_vha->host); - if (ha->iobase) - iounmap(ha->iobase); + if (IS_QLA82XX(ha)) { + iounmap((device_reg_t __iomem *)ha->nx_pcibase); + if (!ql2xdbwr) + iounmap((device_reg_t __iomem *)ha->nxdb_wr_ptr); + } else { + if (ha->iobase) + iounmap(ha->iobase); - if (ha->mqiobase) - iounmap(ha->mqiobase); + if (ha->mqiobase) + iounmap(ha->mqiobase); + } pci_release_selected_regions(ha->pdev, ha->bars); kfree(ha); @@ -2205,8 +2460,10 @@ qla2x00_free_device(scsi_qla_host_t *vha) vha->flags.online = 0; /* turn-off interrupts on the card */ - if (ha->interrupts_on) + if (ha->interrupts_on) { + vha->flags.init_done = 0; ha->isp_ops->disable_intrs(ha); + } qla2x00_free_irqs(vha); @@ -2351,10 +2608,25 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, if (!ha->srb_mempool) goto fail_free_gid_list; + if (IS_QLA82XX(ha)) { + /* Allocate cache for CT6 Ctx. */ + if (!ctx_cachep) { + ctx_cachep = kmem_cache_create("qla2xxx_ctx", + sizeof(struct ct6_dsd), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!ctx_cachep) + goto fail_free_gid_list; + } + ha->ctx_mempool = mempool_create_slab_pool(SRB_MIN_REQ, + ctx_cachep); + if (!ha->ctx_mempool) + goto fail_free_srb_mempool; + } + /* Get memory for cached NVRAM */ ha->nvram = kzalloc(MAX_NVRAM_SIZE, GFP_KERNEL); if (!ha->nvram) - goto fail_free_srb_mempool; + goto fail_free_ctx_mempool; snprintf(name, sizeof(name), "%s_%d", QLA2XXX_DRIVER_NAME, ha->pdev->device); @@ -2363,6 +2635,24 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, if (!ha->s_dma_pool) goto fail_free_nvram; + if (IS_QLA82XX(ha) || ql2xenabledif) { + ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev, + DSD_LIST_DMA_POOL_SIZE, 8, 0); + if (!ha->dl_dma_pool) { + qla_printk(KERN_WARNING, ha, + "Memory Allocation failed - dl_dma_pool\n"); + goto fail_s_dma_pool; + } + + ha->fcp_cmnd_dma_pool = dma_pool_create(name, &ha->pdev->dev, + FCP_CMND_DMA_POOL_SIZE, 8, 0); + if (!ha->fcp_cmnd_dma_pool) { + qla_printk(KERN_WARNING, ha, + "Memory Allocation failed - fcp_cmnd_dma_pool\n"); + goto fail_dl_dma_pool; + } + } + /* Allocate memory for SNS commands */ if (IS_QLA2100(ha) || IS_QLA2200(ha)) { /* Get consistent memory allocated for SNS commands */ @@ -2429,16 +2719,28 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, ha->npiv_info = NULL; /* Get consistent memory allocated for EX-INIT-CB. */ - if (IS_QLA81XX(ha)) { + if (IS_QLA8XXX_TYPE(ha)) { ha->ex_init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &ha->ex_init_cb_dma); if (!ha->ex_init_cb) goto fail_ex_init_cb; } + INIT_LIST_HEAD(&ha->gbl_dsd_list); + + /* Get consistent memory allocated for Async Port-Database. */ + if (!IS_FWI2_CAPABLE(ha)) { + ha->async_pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, + &ha->async_pd_dma); + if (!ha->async_pd) + goto fail_async_pd; + } + INIT_LIST_HEAD(&ha->vp_list); return 1; +fail_async_pd: + dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma); fail_ex_init_cb: kfree(ha->npiv_info); fail_npiv_info: @@ -2465,11 +2767,24 @@ fail_free_ms_iocb: ha->ms_iocb = NULL; ha->ms_iocb_dma = 0; fail_dma_pool: + if (IS_QLA82XX(ha) || ql2xenabledif) { + dma_pool_destroy(ha->fcp_cmnd_dma_pool); + ha->fcp_cmnd_dma_pool = NULL; + } +fail_dl_dma_pool: + if (IS_QLA82XX(ha) || ql2xenabledif) { + dma_pool_destroy(ha->dl_dma_pool); + ha->dl_dma_pool = NULL; + } +fail_s_dma_pool: dma_pool_destroy(ha->s_dma_pool); ha->s_dma_pool = NULL; fail_free_nvram: kfree(ha->nvram); ha->nvram = NULL; +fail_free_ctx_mempool: + mempool_destroy(ha->ctx_mempool); + ha->ctx_mempool = NULL; fail_free_srb_mempool: mempool_destroy(ha->srb_mempool); ha->srb_mempool = NULL; @@ -2538,7 +2853,11 @@ qla2x00_mem_free(struct qla_hw_data *ha) dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); if (ha->ex_init_cb) - dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma); + dma_pool_free(ha->s_dma_pool, + ha->ex_init_cb, ha->ex_init_cb_dma); + + if (ha->async_pd) + dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma); if (ha->s_dma_pool) dma_pool_destroy(ha->s_dma_pool); @@ -2547,14 +2866,39 @@ qla2x00_mem_free(struct qla_hw_data *ha) dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list, ha->gid_list_dma); + if (IS_QLA82XX(ha)) { + if (!list_empty(&ha->gbl_dsd_list)) { + struct dsd_dma *dsd_ptr, *tdsd_ptr; + + /* clean up allocated prev pool */ + list_for_each_entry_safe(dsd_ptr, + tdsd_ptr, &ha->gbl_dsd_list, list) { + dma_pool_free(ha->dl_dma_pool, + dsd_ptr->dsd_addr, dsd_ptr->dsd_list_dma); + list_del(&dsd_ptr->list); + kfree(dsd_ptr); + } + } + } + + if (ha->dl_dma_pool) + dma_pool_destroy(ha->dl_dma_pool); + + if (ha->fcp_cmnd_dma_pool) + dma_pool_destroy(ha->fcp_cmnd_dma_pool); + + if (ha->ctx_mempool) + mempool_destroy(ha->ctx_mempool); + if (ha->init_cb) dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, - ha->init_cb, ha->init_cb_dma); + ha->init_cb, ha->init_cb_dma); vfree(ha->optrom_buffer); kfree(ha->nvram); kfree(ha->npiv_info); ha->srb_mempool = NULL; + ha->ctx_mempool = NULL; ha->eft = NULL; ha->eft_dma = 0; ha->sns_cmd = NULL; @@ -2567,8 +2911,12 @@ qla2x00_mem_free(struct qla_hw_data *ha) ha->init_cb_dma = 0; ha->ex_init_cb = NULL; ha->ex_init_cb_dma = 0; + ha->async_pd = NULL; + ha->async_pd_dma = 0; ha->s_dma_pool = NULL; + ha->dl_dma_pool = NULL; + ha->fcp_cmnd_dma_pool = NULL; ha->gid_list = NULL; ha->gid_list_dma = 0; @@ -2691,6 +3039,8 @@ qla2x00_post_async_work(login, QLA_EVT_ASYNC_LOGIN); qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE); qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT); qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE); +qla2x00_post_async_work(adisc, QLA_EVT_ASYNC_ADISC); +qla2x00_post_async_work(adisc_done, QLA_EVT_ASYNC_ADISC_DONE); int qla2x00_post_uevent_work(struct scsi_qla_host *vha, u32 code) @@ -2760,6 +3110,14 @@ qla2x00_do_work(struct scsi_qla_host *vha) qla2x00_async_logout_done(vha, e->u.logio.fcport, e->u.logio.data); break; + case QLA_EVT_ASYNC_ADISC: + qla2x00_async_adisc(vha, e->u.logio.fcport, + e->u.logio.data); + break; + case QLA_EVT_ASYNC_ADISC_DONE: + qla2x00_async_adisc_done(vha, e->u.logio.fcport, + e->u.logio.data); + break; case QLA_EVT_UEVENT: qla2x00_uevent_emit(vha, e->u.uevent.code); break; @@ -2785,9 +3143,8 @@ void qla2x00_relogin(struct scsi_qla_host *vha) * If the port is not ONLINE then try to login * to it if we haven't run out of retries. */ - if (atomic_read(&fcport->state) != - FCS_ONLINE && fcport->login_retry) { - + if (atomic_read(&fcport->state) != FCS_ONLINE && + fcport->login_retry && !(fcport->flags & FCF_ASYNC_SENT)) { fcport->login_retry--; if (fcport->flags & FCF_FABRIC_DEVICE) { if (fcport->flags & FCF_FCP2_DEVICE) @@ -2798,6 +3155,7 @@ void qla2x00_relogin(struct scsi_qla_host *vha) fcport->d_id.b.al_pa); if (IS_ALOGIO_CAPABLE(ha)) { + fcport->flags |= FCF_ASYNC_SENT; data[0] = 0; data[1] = QLA_LOGIO_LOGIN_RETRIED; status = qla2x00_post_async_login_work( @@ -2896,6 +3254,45 @@ qla2x00_do_dpc(void *data) qla2x00_do_work(base_vha); + if (IS_QLA82XX(ha)) { + if (test_and_clear_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags)) { + qla82xx_idc_lock(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA82XX_DEV_FAILED); + qla82xx_idc_unlock(ha); + qla_printk(KERN_INFO, ha, + "HW State: FAILED\n"); + qla82xx_device_state_handler(base_vha); + continue; + } + + if (test_and_clear_bit(FCOE_CTX_RESET_NEEDED, + &base_vha->dpc_flags)) { + + DEBUG(printk(KERN_INFO + "scsi(%ld): dpc: sched " + "qla82xx_fcoe_ctx_reset ha = %p\n", + base_vha->host_no, ha)); + if (!(test_and_set_bit(ABORT_ISP_ACTIVE, + &base_vha->dpc_flags))) { + if (qla82xx_fcoe_ctx_reset(base_vha)) { + /* FCoE-ctx reset failed. + * Escalate to chip-reset + */ + set_bit(ISP_ABORT_NEEDED, + &base_vha->dpc_flags); + } + clear_bit(ABORT_ISP_ACTIVE, + &base_vha->dpc_flags); + } + + DEBUG(printk("scsi(%ld): dpc:" + " qla82xx_fcoe_ctx_reset end\n", + base_vha->host_no)); + } + } + if (test_and_clear_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) { @@ -2905,7 +3302,7 @@ qla2x00_do_dpc(void *data) if (!(test_and_set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags))) { - if (qla2x00_abort_isp(base_vha)) { + if (ha->isp_ops->abort_isp(base_vha)) { /* failed. retry later */ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); @@ -3038,11 +3435,31 @@ static void qla2x00_sp_free_dma(srb_t *sp) { struct scsi_cmnd *cmd = sp->cmd; + struct qla_hw_data *ha = sp->fcport->vha->hw; if (sp->flags & SRB_DMA_VALID) { scsi_dma_unmap(cmd); sp->flags &= ~SRB_DMA_VALID; } + + if (sp->flags & SRB_CRC_PROT_DMA_VALID) { + dma_unmap_sg(&ha->pdev->dev, scsi_prot_sglist(cmd), + scsi_prot_sg_count(cmd), cmd->sc_data_direction); + sp->flags &= ~SRB_CRC_PROT_DMA_VALID; + } + + if (sp->flags & SRB_CRC_CTX_DSD_VALID) { + /* List assured to be having elements */ + qla2x00_clean_dsd_pool(ha, sp); + sp->flags &= ~SRB_CRC_CTX_DSD_VALID; + } + + if (sp->flags & SRB_CRC_CTX_DMA_VALID) { + dma_pool_free(ha->dl_dma_pool, sp->ctx, + ((struct crc_context *)sp->ctx)->crc_ctx_dma); + sp->flags &= ~SRB_CRC_CTX_DMA_VALID; + } + CMD_SP(cmd) = NULL; } @@ -3053,8 +3470,18 @@ qla2x00_sp_compl(struct qla_hw_data *ha, srb_t *sp) qla2x00_sp_free_dma(sp); - mempool_free(sp, ha->srb_mempool); + if (sp->flags & SRB_FCP_CMND_DMA_VALID) { + struct ct6_dsd *ctx = sp->ctx; + dma_pool_free(ha->fcp_cmnd_dma_pool, ctx->fcp_cmnd, + ctx->fcp_cmnd_dma); + list_splice(&ctx->dsd_list, &ha->gbl_dsd_list); + ha->gbl_dsd_inuse -= ctx->dsd_use_cnt; + ha->gbl_dsd_avail += ctx->dsd_use_cnt; + mempool_free(sp->ctx, ha->ctx_mempool); + sp->ctx = NULL; + } + mempool_free(sp, ha->srb_mempool); cmd->scsi_done(cmd); } @@ -3079,6 +3506,9 @@ qla2x00_timer(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct req_que *req; + if (IS_QLA82XX(ha)) + qla82xx_watchdog(vha); + /* Hardware read to raise pending EEH errors during mailbox waits. */ if (!pci_channel_offline(ha->pdev)) pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w); @@ -3143,7 +3573,7 @@ qla2x00_timer(scsi_qla_host_t *vha) sp = req->outstanding_cmds[index]; if (!sp) continue; - if (sp->ctx) + if (sp->ctx && !IS_PROT_IO(sp)) continue; sfcp = sp->fcport; if (!(sfcp->flags & FCF_FCP2_DEVICE)) @@ -3193,6 +3623,8 @@ qla2x00_timer(scsi_qla_host_t *vha) start_dpc || test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) || test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags) || + test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags) || + test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) || test_bit(VP_DPC_NEEDED, &vha->dpc_flags) || test_bit(RELOGIN_NEEDED, &vha->dpc_flags))) qla2xxx_wake_dpc(vha); @@ -3202,7 +3634,7 @@ qla2x00_timer(scsi_qla_host_t *vha) /* Firmware interface routines. */ -#define FW_BLOBS 7 +#define FW_BLOBS 8 #define FW_ISP21XX 0 #define FW_ISP22XX 1 #define FW_ISP2300 2 @@ -3210,6 +3642,7 @@ qla2x00_timer(scsi_qla_host_t *vha) #define FW_ISP24XX 4 #define FW_ISP25XX 5 #define FW_ISP81XX 6 +#define FW_ISP82XX 7 #define FW_FILE_ISP21XX "ql2100_fw.bin" #define FW_FILE_ISP22XX "ql2200_fw.bin" @@ -3218,6 +3651,7 @@ qla2x00_timer(scsi_qla_host_t *vha) #define FW_FILE_ISP24XX "ql2400_fw.bin" #define FW_FILE_ISP25XX "ql2500_fw.bin" #define FW_FILE_ISP81XX "ql8100_fw.bin" +#define FW_FILE_ISP82XX "ql8200_fw.bin" static DEFINE_MUTEX(qla_fw_lock); @@ -3229,6 +3663,7 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = { { .name = FW_FILE_ISP24XX, }, { .name = FW_FILE_ISP25XX, }, { .name = FW_FILE_ISP81XX, }, + { .name = FW_FILE_ISP82XX, }, }; struct fw_blob * @@ -3252,6 +3687,8 @@ qla2x00_request_firmware(scsi_qla_host_t *vha) blob = &qla_fw_blobs[FW_ISP25XX]; } else if (IS_QLA81XX(ha)) { blob = &qla_fw_blobs[FW_ISP81XX]; + } else if (IS_QLA82XX(ha)) { + blob = &qla_fw_blobs[FW_ISP82XX]; } mutex_lock(&qla_fw_lock); @@ -3392,11 +3829,10 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev) msleep(1000); set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); - if (qla2x00_abort_isp(base_vha) == QLA_SUCCESS) + if (ha->isp_ops->abort_isp(base_vha) == QLA_SUCCESS) ret = PCI_ERS_RESULT_RECOVERED; clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); - pci_cleanup_aer_uncorrect_error_status(pdev); DEBUG17(qla_printk(KERN_WARNING, ha, "slot_reset-return:ret=%x\n", ret)); @@ -3420,6 +3856,8 @@ qla2xxx_pci_resume(struct pci_dev *pdev) "from slot/link_reset"); } + pci_cleanup_aer_uncorrect_error_status(pdev); + ha->flags.eeh_busy = 0; } @@ -3445,6 +3883,7 @@ static struct pci_device_id qla2xxx_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8001) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8021) }, { 0 }, }; MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl); @@ -3460,6 +3899,10 @@ static struct pci_driver qla2xxx_pci_driver = { .err_handler = &qla2xxx_err_handler, }; +static struct file_operations apidev_fops = { + .owner = THIS_MODULE, +}; + /** * qla2x00_module_init - Module initialization. **/ @@ -3488,6 +3931,13 @@ qla2x00_module_init(void) kmem_cache_destroy(srb_cachep); return -ENODEV; } + + apidev_major = register_chrdev(0, QLA2XXX_APIDEV, &apidev_fops); + if (apidev_major < 0) { + printk(KERN_WARNING "qla2xxx: Unable to register char device " + "%s\n", QLA2XXX_APIDEV); + } + qla2xxx_transport_vport_template = fc_attach_transport(&qla2xxx_transport_vport_functions); if (!qla2xxx_transport_vport_template) { @@ -3513,9 +3963,12 @@ qla2x00_module_init(void) static void __exit qla2x00_module_exit(void) { + unregister_chrdev(apidev_major, QLA2XXX_APIDEV); pci_unregister_driver(&qla2xxx_pci_driver); qla2x00_release_firmware(); kmem_cache_destroy(srb_cachep); + if (ctx_cachep) + kmem_cache_destroy(ctx_cachep); fc_release_transport(qla2xxx_transport_template); fc_release_transport(qla2xxx_transport_vport_template); } diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 8b3de4e54c2..de92504d758 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -423,9 +423,6 @@ qla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat) /* Flash Manipulation Routines */ /*****************************************************************************/ -#define OPTROM_BURST_SIZE 0x1000 -#define OPTROM_BURST_DWORDS (OPTROM_BURST_SIZE / 4) - static inline uint32_t flash_conf_addr(struct qla_hw_data *ha, uint32_t faddr) { @@ -565,6 +562,10 @@ qla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start) *start = FA_FLASH_LAYOUT_ADDR; else if (IS_QLA81XX(ha)) *start = FA_FLASH_LAYOUT_ADDR_81; + else if (IS_QLA82XX(ha)) { + *start = FA_FLASH_LAYOUT_ADDR_82; + goto end; + } /* Begin with first PCI expansion ROM header. */ buf = (uint8_t *)req->ring; dcode = (uint32_t *)req->ring; @@ -648,6 +649,12 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) const uint32_t def_npiv_conf1[] = { FA_NPIV_CONF1_ADDR_24, FA_NPIV_CONF1_ADDR, FA_NPIV_CONF1_ADDR_81 }; + const uint32_t fcp_prio_cfg0[] = + { FA_FCP_PRIO0_ADDR, FA_FCP_PRIO0_ADDR_25, + 0 }; + const uint32_t fcp_prio_cfg1[] = + { FA_FCP_PRIO1_ADDR, FA_FCP_PRIO1_ADDR_25, + 0 }; uint32_t def; uint16_t *wptr; uint16_t cnt, chksum; @@ -703,10 +710,14 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) break; case FLT_REG_VPD_0: ha->flt_region_vpd_nvram = start; + if (IS_QLA82XX(ha)) + break; if (ha->flags.port0) ha->flt_region_vpd = start; break; case FLT_REG_VPD_1: + if (IS_QLA82XX(ha)) + break; if (!ha->flags.port0) ha->flt_region_vpd = start; break; @@ -732,6 +743,29 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) case FLT_REG_GOLD_FW: ha->flt_region_gold_fw = start; break; + case FLT_REG_FCP_PRIO_0: + if (ha->flags.port0) + ha->flt_region_fcp_prio = start; + break; + case FLT_REG_FCP_PRIO_1: + if (!ha->flags.port0) + ha->flt_region_fcp_prio = start; + break; + case FLT_REG_BOOT_CODE_82XX: + ha->flt_region_boot = start; + break; + case FLT_REG_FW_82XX: + ha->flt_region_fw = start; + break; + case FLT_REG_GOLD_FW_82XX: + ha->flt_region_gold_fw = start; + break; + case FLT_REG_BOOTLOAD_82XX: + ha->flt_region_bootload = start; + break; + case FLT_REG_VPD_82XX: + ha->flt_region_vpd = start; + break; } } goto done; @@ -750,12 +784,14 @@ no_flash_data: ha->flt_region_boot = def_boot[def]; ha->flt_region_vpd_nvram = def_vpd_nvram[def]; ha->flt_region_vpd = ha->flags.port0 ? - def_vpd0[def]: def_vpd1[def]; + def_vpd0[def] : def_vpd1[def]; ha->flt_region_nvram = ha->flags.port0 ? - def_nvram0[def]: def_nvram1[def]; + def_nvram0[def] : def_nvram1[def]; ha->flt_region_fdt = def_fdt[def]; ha->flt_region_npiv_conf = ha->flags.port0 ? - def_npiv_conf0[def]: def_npiv_conf1[def]; + def_npiv_conf0[def] : def_npiv_conf1[def]; + ha->flt_region_fcp_prio = ha->flags.port0 ? + fcp_prio_cfg0[def] : fcp_prio_cfg1[def]; done: DEBUG2(qla_printk(KERN_DEBUG, ha, "FLT[%s]: boot=0x%x fw=0x%x " "vpd_nvram=0x%x vpd=0x%x nvram=0x%x fdt=0x%x flt=0x%x " @@ -775,7 +811,7 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *vha) uint16_t *wptr; struct qla_fdt_layout *fdt; uint8_t man_id, flash_id; - uint16_t mid, fid; + uint16_t mid = 0, fid = 0; struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; @@ -816,6 +852,10 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *vha) goto done; no_flash_data: loc = locations[0]; + if (IS_QLA82XX(ha)) { + ha->fdt_block_size = FLASH_BLK_SIZE_64K; + goto done; + } qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id); mid = man_id; fid = flash_id; @@ -853,6 +893,31 @@ done: ha->fdt_block_size)); } +static void +qla2xxx_get_idc_param(scsi_qla_host_t *vha) +{ +#define QLA82XX_IDC_PARAM_ADDR 0x003e885c + uint32_t *wptr; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + + if (!IS_QLA82XX(ha)) + return; + + wptr = (uint32_t *)req->ring; + ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring, + QLA82XX_IDC_PARAM_ADDR , 8); + + if (*wptr == __constant_cpu_to_le32(0xffffffff)) { + ha->nx_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT; + ha->nx_reset_timeout = QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT; + } else { + ha->nx_dev_init_timeout = le32_to_cpu(*wptr++); + ha->nx_reset_timeout = le32_to_cpu(*wptr); + } + return; +} + int qla2xxx_get_flash_info(scsi_qla_host_t *vha) { @@ -860,7 +925,7 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha) uint32_t flt_addr; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_QLA81XX(ha)) + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_QLA8XXX_TYPE(ha)) return QLA_SUCCESS; ret = qla2xxx_find_flt_start(vha, &flt_addr); @@ -869,6 +934,7 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha) qla2xxx_get_flt_info(vha, flt_addr); qla2xxx_get_fdt_info(vha); + qla2xxx_get_idc_param(vha); return QLA_SUCCESS; } @@ -885,7 +951,7 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) struct qla_npiv_entry *entry; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_QLA81XX(ha)) + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_QLA8XXX_TYPE(ha)) return; ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr, @@ -1178,6 +1244,9 @@ qla24xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, uint32_t *dwptr; struct qla_hw_data *ha = vha->hw; + if (IS_QLA82XX(ha)) + return buf; + /* Dword reads to flash. */ dwptr = (uint32_t *)buf; for (i = 0; i < bytes >> 2; i++, naddr++) @@ -1233,6 +1302,9 @@ qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, ret = QLA_SUCCESS; + if (IS_QLA82XX(ha)) + return ret; + /* Enable flash write. */ WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) | CSRX_FLASH_ENABLE); @@ -1344,6 +1416,9 @@ qla2x00_beacon_blink(struct scsi_qla_host *vha) struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + if (IS_QLA82XX(ha)) + return; + spin_lock_irqsave(&ha->hardware_lock, flags); /* Save the Original GPIOE. */ @@ -1525,6 +1600,9 @@ qla24xx_beacon_on(struct scsi_qla_host *vha) struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + if (IS_QLA82XX(ha)) + return QLA_SUCCESS; + if (ha->beacon_blink_led == 0) { /* Enable firmware for update */ ha->fw_options[1] |= ADD_FO1_DISABLE_GPIO_LED_CTRL; @@ -1567,6 +1645,9 @@ qla24xx_beacon_off(struct scsi_qla_host *vha) struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; + if (IS_QLA82XX(ha)) + return QLA_SUCCESS; + ha->beacon_blink_led = 0; ha->beacon_color_state = QLA_LED_ALL_ON; @@ -2576,6 +2657,9 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) int i; struct qla_hw_data *ha = vha->hw; + if (IS_QLA82XX(ha)) + return ret; + if (!mbuf) return QLA_FUNCTION_FAILED; @@ -2722,3 +2806,50 @@ qla2xxx_get_vpd_field(scsi_qla_host_t *vha, char *key, char *str, size_t size) return 0; } + +int +qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *vha) +{ + int len, max_len; + uint32_t fcp_prio_addr; + struct qla_hw_data *ha = vha->hw; + + if (!ha->fcp_prio_cfg) { + ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE); + if (!ha->fcp_prio_cfg) { + qla_printk(KERN_WARNING, ha, + "Unable to allocate memory for fcp priority data " + "(%x).\n", FCP_PRIO_CFG_SIZE); + return QLA_FUNCTION_FAILED; + } + } + memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE); + + fcp_prio_addr = ha->flt_region_fcp_prio; + + /* first read the fcp priority data header from flash */ + ha->isp_ops->read_optrom(vha, (uint8_t *)ha->fcp_prio_cfg, + fcp_prio_addr << 2, FCP_PRIO_CFG_HDR_SIZE); + + if (!qla24xx_fcp_prio_cfg_valid(ha->fcp_prio_cfg, 0)) + goto fail; + + /* read remaining FCP CMD config data from flash */ + fcp_prio_addr += (FCP_PRIO_CFG_HDR_SIZE >> 2); + len = ha->fcp_prio_cfg->num_entries * FCP_PRIO_CFG_ENTRY_SIZE; + max_len = FCP_PRIO_CFG_SIZE - FCP_PRIO_CFG_HDR_SIZE; + + ha->isp_ops->read_optrom(vha, (uint8_t *)&ha->fcp_prio_cfg->entry[0], + fcp_prio_addr << 2, (len < max_len ? len : max_len)); + + /* revalidate the entire FCP priority config data, including entries */ + if (!qla24xx_fcp_prio_cfg_valid(ha->fcp_prio_cfg, 1)) + goto fail; + + ha->flags.fcp_prio_enabled = 1; + return QLA_SUCCESS; +fail: + vfree(ha->fcp_prio_cfg); + ha->fcp_prio_cfg = NULL; + return QLA_FUNCTION_FAILED; +} diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index 81b5f29254e..428802616e3 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -114,6 +114,7 @@ */ #define MAC_ADDR_LEN 6 /* in bytes */ #define IP_ADDR_LEN 4 /* in bytes */ +#define IPv6_ADDR_LEN 16 /* IPv6 address size */ #define DRIVER_NAME "qla4xxx" #define MAX_LINKED_CMDS_PER_LUN 3 @@ -147,6 +148,8 @@ #define MAX_RESET_HA_RETRIES 2 +#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr) + /* * SCSI Request Block structure (srb) that is placed * on cmd->SCp location of every I/O [We have 22 bytes available] @@ -169,7 +172,7 @@ struct srb { struct scsi_cmnd *cmd; /* (4) SCSI command block */ dma_addr_t dma_handle; /* (4) for unmap of single transfers */ - atomic_t ref_count; /* reference count for this srb */ + struct kref srb_ref; /* reference count for this srb */ uint32_t fw_ddb_index; uint8_t err_id; /* error id */ #define SRB_ERR_PORT 1 /* Request failed because "port down" */ @@ -220,7 +223,7 @@ struct ddb_entry { uint16_t os_target_id; /* Target ID */ uint16_t fw_ddb_index; /* DDB firmware index */ - uint8_t reserved[2]; + uint16_t options; uint32_t fw_ddb_device_state; /* F/W Device State -- see ql4_fw.h */ uint32_t CmdSn; @@ -245,10 +248,18 @@ struct ddb_entry { uint16_t port; uint32_t tpgt; - uint8_t ip_addr[ISCSI_IPADDR_SIZE]; + uint8_t ip_addr[IP_ADDR_LEN]; uint8_t iscsi_name[ISCSI_NAME_SIZE]; /* 72 x48 */ uint8_t iscsi_alias[0x20]; uint8_t isid[6]; + uint16_t iscsi_max_burst_len; + uint16_t iscsi_max_outsnd_r2t; + uint16_t iscsi_first_burst_len; + uint16_t iscsi_max_rcv_data_seg_len; + uint16_t iscsi_max_snd_data_seg_len; + + struct in6_addr remote_ipv6_addr; + struct in6_addr link_local_ipv6_addr; }; /* @@ -301,6 +312,7 @@ struct scsi_qla_host { #define DPC_ISNS_RESTART 7 /* 0x00000080 */ #define DPC_AEN 9 /* 0x00000200 */ #define DPC_GET_DHCP_IP_ADDR 15 /* 0x00008000 */ +#define DPC_LINK_CHANGED 18 /* 0x00040000 */ struct Scsi_Host *host; /* pointer to host data */ uint32_t tot_ddbs; @@ -320,8 +332,7 @@ struct scsi_qla_host { #define MIN_IOBASE_LEN 0x100 uint16_t req_q_count; - uint8_t marker_needed; - uint8_t rsvd1; + uint8_t rsvd1[2]; unsigned long host_no; @@ -441,8 +452,35 @@ struct scsi_qla_host { /* Saved srb for status continuation entry processing */ struct srb *status_srb; + + /* IPv6 support info from InitFW */ + uint8_t acb_version; + uint8_t ipv4_addr_state; + uint16_t ipv4_options; + + uint32_t resvd2; + uint32_t ipv6_options; + uint32_t ipv6_addl_options; + uint8_t ipv6_link_local_state; + uint8_t ipv6_addr0_state; + uint8_t ipv6_addr1_state; + uint8_t ipv6_default_router_state; + struct in6_addr ipv6_link_local_addr; + struct in6_addr ipv6_addr0; + struct in6_addr ipv6_addr1; + struct in6_addr ipv6_default_router_addr; }; +static inline int is_ipv4_enabled(struct scsi_qla_host *ha) +{ + return ((ha->ipv4_options & IPOPT_IPv4_PROTOCOL_ENABLE) != 0); +} + +static inline int is_ipv6_enabled(struct scsi_qla_host *ha) +{ + return ((ha->ipv6_options & IPV6_OPT_IPV6_PROTOCOL_ENABLE) != 0); +} + static inline int is_qla4010(struct scsi_qla_host *ha) { return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP4010; diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index 9cd7a608df3..855226e0866 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -215,6 +215,7 @@ union external_hw_config_reg { /* Mailbox command definitions */ #define MBOX_CMD_ABOUT_FW 0x0009 #define MBOX_CMD_PING 0x000B +#define MBOX_CMD_ABORT_TASK 0x0015 #define MBOX_CMD_LUN_RESET 0x0016 #define MBOX_CMD_TARGET_WARM_RESET 0x0017 #define MBOX_CMD_GET_MANAGEMENT_DATA 0x001E @@ -258,13 +259,15 @@ union external_hw_config_reg { /* Mailbox 1 */ #define FW_STATE_READY 0x0000 #define FW_STATE_CONFIG_WAIT 0x0001 -#define FW_STATE_WAIT_LOGIN 0x0002 +#define FW_STATE_WAIT_AUTOCONNECT 0x0002 #define FW_STATE_ERROR 0x0004 -#define FW_STATE_DHCP_IN_PROGRESS 0x0008 +#define FW_STATE_CONFIGURING_IP 0x0008 /* Mailbox 3 */ #define FW_ADDSTATE_OPTICAL_MEDIA 0x0001 -#define FW_ADDSTATE_DHCP_ENABLED 0x0002 +#define FW_ADDSTATE_DHCPv4_ENABLED 0x0002 +#define FW_ADDSTATE_DHCPv4_LEASE_ACQUIRED 0x0004 +#define FW_ADDSTATE_DHCPv4_LEASE_EXPIRED 0x0008 #define FW_ADDSTATE_LINK_UP 0x0010 #define FW_ADDSTATE_ISNS_SVC_ENABLED 0x0020 #define MBOX_CMD_GET_DATABASE_ENTRY_DEFAULTS 0x006B @@ -320,6 +323,8 @@ union external_hw_config_reg { /* Host Adapter Initialization Control Block (from host) */ struct addr_ctrl_blk { uint8_t version; /* 00 */ +#define IFCB_VER_MIN 0x01 +#define IFCB_VER_MAX 0x02 uint8_t control; /* 01 */ uint16_t fw_options; /* 02-03 */ @@ -351,11 +356,16 @@ struct addr_ctrl_blk { uint16_t iscsi_opts; /* 30-31 */ uint16_t ipv4_tcp_opts; /* 32-33 */ uint16_t ipv4_ip_opts; /* 34-35 */ +#define IPOPT_IPv4_PROTOCOL_ENABLE 0x8000 uint16_t iscsi_max_pdu_size; /* 36-37 */ uint8_t ipv4_tos; /* 38 */ uint8_t ipv4_ttl; /* 39 */ uint8_t acb_version; /* 3A */ +#define ACB_NOT_SUPPORTED 0x00 +#define ACB_SUPPORTED 0x02 /* Capable of ACB Version 2 + Features */ + uint8_t res2; /* 3B */ uint16_t def_timeout; /* 3C-3D */ uint16_t iscsi_fburst_len; /* 3E-3F */ @@ -397,16 +407,35 @@ struct addr_ctrl_blk { uint32_t cookie; /* 200-203 */ uint16_t ipv6_port; /* 204-205 */ uint16_t ipv6_opts; /* 206-207 */ +#define IPV6_OPT_IPV6_PROTOCOL_ENABLE 0x8000 + uint16_t ipv6_addtl_opts; /* 208-209 */ +#define IPV6_ADDOPT_NEIGHBOR_DISCOVERY_ADDR_ENABLE 0x0002 /* Pri ACB + Only */ +#define IPV6_ADDOPT_AUTOCONFIG_LINK_LOCAL_ADDR 0x0001 + uint16_t ipv6_tcp_opts; /* 20A-20B */ uint8_t ipv6_tcp_wsf; /* 20C */ uint16_t ipv6_flow_lbl; /* 20D-20F */ - uint8_t ipv6_gw_addr[16]; /* 210-21F */ + uint8_t ipv6_dflt_rtr_addr[16]; /* 210-21F */ uint16_t ipv6_vlan_tag; /* 220-221 */ uint8_t ipv6_lnk_lcl_addr_state;/* 222 */ uint8_t ipv6_addr0_state; /* 223 */ uint8_t ipv6_addr1_state; /* 224 */ - uint8_t ipv6_gw_state; /* 225 */ +#define IP_ADDRSTATE_UNCONFIGURED 0 +#define IP_ADDRSTATE_INVALID 1 +#define IP_ADDRSTATE_ACQUIRING 2 +#define IP_ADDRSTATE_TENTATIVE 3 +#define IP_ADDRSTATE_DEPRICATED 4 +#define IP_ADDRSTATE_PREFERRED 5 +#define IP_ADDRSTATE_DISABLING 6 + + uint8_t ipv6_dflt_rtr_state; /* 225 */ +#define IPV6_RTRSTATE_UNKNOWN 0 +#define IPV6_RTRSTATE_MANUAL 1 +#define IPV6_RTRSTATE_ADVERTISED 3 +#define IPV6_RTRSTATE_STALE 4 + uint8_t ipv6_traffic_class; /* 226 */ uint8_t ipv6_hop_limit; /* 227 */ uint8_t ipv6_if_id[8]; /* 228-22F */ @@ -424,7 +453,7 @@ struct addr_ctrl_blk { struct init_fw_ctrl_blk { struct addr_ctrl_blk pri; - struct addr_ctrl_blk sec; +/* struct addr_ctrl_blk sec;*/ }; /*************************************************************************/ @@ -433,6 +462,9 @@ struct dev_db_entry { uint16_t options; /* 00-01 */ #define DDB_OPT_DISC_SESSION 0x10 #define DDB_OPT_TARGET 0x02 /* device is a target */ +#define DDB_OPT_IPV6_DEVICE 0x100 +#define DDB_OPT_IPV6_NULL_LINK_LOCAL 0x800 /* post connection */ +#define DDB_OPT_IPV6_FW_DEFINED_LINK_LOCAL 0x800 /* pre connection */ uint16_t exec_throttle; /* 02-03 */ uint16_t exec_count; /* 04-05 */ @@ -468,7 +500,7 @@ struct dev_db_entry { * pointer to a string so we * don't have to reserve soooo * much RAM */ - uint8_t ipv6_addr[0x10];/* 1A0-1AF */ + uint8_t link_local_ipv6_addr[0x10]; /* 1A0-1AF */ uint8_t res5[0x10]; /* 1B0-1BF */ uint16_t ddb_link; /* 1C0-1C1 */ uint16_t chap_tbl_idx; /* 1C2-1C3 */ diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index 96ebfb021f6..c4636f6cb3c 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -25,6 +25,7 @@ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen); int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha); int qla4xxx_relogin_device(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry); +int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb); int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry, int lun); int qla4xxx_reset_target(struct scsi_qla_host * ha, @@ -65,13 +66,14 @@ void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha, int qla4xxx_init_rings(struct scsi_qla_host * ha); struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t index); -void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb); +void qla4xxx_srb_compl(struct kref *ref); int qla4xxx_reinitialize_ddb_list(struct scsi_qla_host * ha); -int qla4xxx_process_ddb_changed(struct scsi_qla_host * ha, - uint32_t fw_ddb_index, uint32_t state); +int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, + uint32_t state, uint32_t conn_error); void qla4xxx_dump_buffer(void *b, uint32_t size); int qla4xxx_send_marker_iocb(struct scsi_qla_host *ha, struct ddb_entry *ddb_entry, int lun, uint16_t mrkr_mod); +int qla4_is_relogin_allowed(struct scsi_qla_host *ha, uint32_t conn_err); extern int ql4xextended_error_logging; extern int ql4xdiscoverywait; diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 92329a461c6..5510df8a7fa 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -189,6 +189,78 @@ static int qla4xxx_init_local_data(struct scsi_qla_host *ha) return qla4xxx_get_firmware_status(ha); } +static uint8_t +qla4xxx_wait_for_ip_config(struct scsi_qla_host *ha) +{ + uint8_t ipv4_wait = 0; + uint8_t ipv6_wait = 0; + int8_t ip_address[IPv6_ADDR_LEN] = {0} ; + + /* If both IPv4 & IPv6 are enabled, possibly only one + * IP address may be acquired, so check to see if we + * need to wait for another */ + if (is_ipv4_enabled(ha) && is_ipv6_enabled(ha)) { + if (((ha->addl_fw_state & FW_ADDSTATE_DHCPv4_ENABLED) != 0) && + ((ha->addl_fw_state & + FW_ADDSTATE_DHCPv4_LEASE_ACQUIRED) == 0)) { + ipv4_wait = 1; + } + if (((ha->ipv6_addl_options & + IPV6_ADDOPT_NEIGHBOR_DISCOVERY_ADDR_ENABLE) != 0) && + ((ha->ipv6_link_local_state == IP_ADDRSTATE_ACQUIRING) || + (ha->ipv6_addr0_state == IP_ADDRSTATE_ACQUIRING) || + (ha->ipv6_addr1_state == IP_ADDRSTATE_ACQUIRING))) { + + ipv6_wait = 1; + + if ((ha->ipv6_link_local_state == + IP_ADDRSTATE_PREFERRED) || + (ha->ipv6_addr0_state == IP_ADDRSTATE_PREFERRED) || + (ha->ipv6_addr1_state == IP_ADDRSTATE_PREFERRED)) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: " + "Preferred IP configured." + " Don't wait!\n", ha->host_no, + __func__)); + ipv6_wait = 0; + } + if (memcmp(&ha->ipv6_default_router_addr, ip_address, + IPv6_ADDR_LEN) == 0) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: " + "No Router configured. " + "Don't wait!\n", ha->host_no, + __func__)); + ipv6_wait = 0; + } + if ((ha->ipv6_default_router_state == + IPV6_RTRSTATE_MANUAL) && + (ha->ipv6_link_local_state == + IP_ADDRSTATE_TENTATIVE) && + (memcmp(&ha->ipv6_link_local_addr, + &ha->ipv6_default_router_addr, 4) == 0)) { + DEBUG2(printk("scsi%ld: %s: LinkLocal Router & " + "IP configured. Don't wait!\n", + ha->host_no, __func__)); + ipv6_wait = 0; + } + } + if (ipv4_wait || ipv6_wait) { + DEBUG2(printk("scsi%ld: %s: Wait for additional " + "IP(s) \"", ha->host_no, __func__)); + if (ipv4_wait) + DEBUG2(printk("IPv4 ")); + if (ha->ipv6_link_local_state == IP_ADDRSTATE_ACQUIRING) + DEBUG2(printk("IPv6LinkLocal ")); + if (ha->ipv6_addr0_state == IP_ADDRSTATE_ACQUIRING) + DEBUG2(printk("IPv6Addr0 ")); + if (ha->ipv6_addr1_state == IP_ADDRSTATE_ACQUIRING) + DEBUG2(printk("IPv6Addr1 ")); + DEBUG2(printk("\"\n")); + } + } + + return ipv4_wait|ipv6_wait; +} + static int qla4xxx_fw_ready(struct scsi_qla_host *ha) { uint32_t timeout_count; @@ -226,38 +298,80 @@ static int qla4xxx_fw_ready(struct scsi_qla_host *ha) continue; } + if (ha->firmware_state & FW_STATE_WAIT_AUTOCONNECT) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: fwstate:" + "AUTOCONNECT in progress\n", + ha->host_no, __func__)); + } + + if (ha->firmware_state & FW_STATE_CONFIGURING_IP) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: fwstate:" + " CONFIGURING IP\n", + ha->host_no, __func__)); + /* + * Check for link state after 15 secs and if link is + * still DOWN then, cable is unplugged. Ignore "DHCP + * in Progress/CONFIGURING IP" bit to check if firmware + * is in ready state or not after 15 secs. + * This is applicable for both 2.x & 3.x firmware + */ + if (timeout_count <= (ADAPTER_INIT_TOV - 15)) { + if (ha->addl_fw_state & FW_ADDSTATE_LINK_UP) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s:" + " LINK UP (Cable plugged)\n", + ha->host_no, __func__)); + } else if (ha->firmware_state & + (FW_STATE_CONFIGURING_IP | + FW_STATE_READY)) { + DEBUG2(printk(KERN_INFO "scsi%ld: %s: " + "LINK DOWN (Cable unplugged)\n", + ha->host_no, __func__)); + ha->firmware_state = FW_STATE_READY; + } + } + } + if (ha->firmware_state == FW_STATE_READY) { - DEBUG2(dev_info(&ha->pdev->dev, "Firmware Ready..\n")); - /* The firmware is ready to process SCSI commands. */ - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: MEDIA TYPE - %s\n", - ha->host_no, - __func__, (ha->addl_fw_state & - FW_ADDSTATE_OPTICAL_MEDIA) - != 0 ? "OPTICAL" : "COPPER")); - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: DHCP STATE Enabled " - "%s\n", - ha->host_no, __func__, - (ha->addl_fw_state & - FW_ADDSTATE_DHCP_ENABLED) != 0 ? - "YES" : "NO")); - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: LINK %s\n", - ha->host_no, __func__, - (ha->addl_fw_state & - FW_ADDSTATE_LINK_UP) != 0 ? - "UP" : "DOWN")); - DEBUG2(dev_info(&ha->pdev->dev, - "scsi%ld: %s: iSNS Service " - "Started %s\n", - ha->host_no, __func__, - (ha->addl_fw_state & - FW_ADDSTATE_ISNS_SVC_ENABLED) != 0 ? - "YES" : "NO")); - - ready = 1; - break; + /* If DHCP IP Addr is available, retrieve it now. */ + if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, + &ha->dpc_flags)) + qla4xxx_get_dhcp_ip_address(ha); + + if (!qla4xxx_wait_for_ip_config(ha) || + timeout_count == 1) { + DEBUG2(dev_info(&ha->pdev->dev, + "Firmware Ready..\n")); + /* The firmware is ready to process SCSI + commands. */ + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: MEDIA TYPE" + " - %s\n", ha->host_no, + __func__, (ha->addl_fw_state & + FW_ADDSTATE_OPTICAL_MEDIA) + != 0 ? "OPTICAL" : "COPPER")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: DHCPv4 STATE" + " Enabled %s\n", ha->host_no, + __func__, (ha->addl_fw_state & + FW_ADDSTATE_DHCPv4_ENABLED) != 0 ? + "YES" : "NO")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: LINK %s\n", + ha->host_no, __func__, + (ha->addl_fw_state & + FW_ADDSTATE_LINK_UP) != 0 ? + "UP" : "DOWN")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: iSNS Service " + "Started %s\n", + ha->host_no, __func__, + (ha->addl_fw_state & + FW_ADDSTATE_ISNS_SVC_ENABLED) != 0 ? + "YES" : "NO")); + + ready = 1; + break; + } } DEBUG2(printk("scsi%ld: %s: waiting on fw, state=%x:%x - " "seconds expired= %d\n", ha->host_no, __func__, @@ -272,15 +386,19 @@ static int qla4xxx_fw_ready(struct scsi_qla_host *ha) msleep(1000); } /* end of for */ - if (timeout_count == 0) + if (timeout_count <= 0) DEBUG2(printk("scsi%ld: %s: FW Initialization timed out!\n", ha->host_no, __func__)); - if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS) { - DEBUG2(printk("scsi%ld: %s: FW is reporting its waiting to" - " grab an IP address from DHCP server\n", - ha->host_no, __func__)); + if (ha->firmware_state & FW_STATE_CONFIGURING_IP) { + DEBUG2(printk("scsi%ld: %s: FW initialized, but is reporting " + "it's waiting to configure an IP address\n", + ha->host_no, __func__)); ready = 1; + } else if (ha->firmware_state & FW_STATE_WAIT_AUTOCONNECT) { + DEBUG2(printk("scsi%ld: %s: FW initialized, but " + "auto-discovery still in process\n", + ha->host_no, __func__)); } return ready; @@ -387,6 +505,7 @@ static int qla4xxx_update_ddb_entry(struct scsi_qla_host *ha, struct dev_db_entry *fw_ddb_entry = NULL; dma_addr_t fw_ddb_entry_dma; int status = QLA_ERROR; + uint32_t conn_err; if (ddb_entry == NULL) { DEBUG2(printk("scsi%ld: %s: ddb_entry is NULL\n", ha->host_no, @@ -407,7 +526,7 @@ static int qla4xxx_update_ddb_entry(struct scsi_qla_host *ha, if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, fw_ddb_entry_dma, NULL, NULL, - &ddb_entry->fw_ddb_device_state, NULL, + &ddb_entry->fw_ddb_device_state, &conn_err, &ddb_entry->tcp_source_port_num, &ddb_entry->connection_id) == QLA_ERROR) { @@ -419,6 +538,7 @@ static int qla4xxx_update_ddb_entry(struct scsi_qla_host *ha, } status = QLA_SUCCESS; + ddb_entry->options = le16_to_cpu(fw_ddb_entry->options); ddb_entry->target_session_id = le16_to_cpu(fw_ddb_entry->tsid); ddb_entry->task_mgmt_timeout = le16_to_cpu(fw_ddb_entry->def_timeout); @@ -442,11 +562,44 @@ static int qla4xxx_update_ddb_entry(struct scsi_qla_host *ha, memcpy(&ddb_entry->ip_addr[0], &fw_ddb_entry->ip_addr[0], min(sizeof(ddb_entry->ip_addr), sizeof(fw_ddb_entry->ip_addr))); - DEBUG2(printk("scsi%ld: %s: ddb[%d] - State= %x status= %d.\n", - ha->host_no, __func__, fw_ddb_index, - ddb_entry->fw_ddb_device_state, status)); - - exit_update_ddb: + ddb_entry->iscsi_max_burst_len = fw_ddb_entry->iscsi_max_burst_len; + ddb_entry->iscsi_max_outsnd_r2t = fw_ddb_entry->iscsi_max_outsnd_r2t; + ddb_entry->iscsi_first_burst_len = fw_ddb_entry->iscsi_first_burst_len; + ddb_entry->iscsi_max_rcv_data_seg_len = + fw_ddb_entry->iscsi_max_rcv_data_seg_len; + ddb_entry->iscsi_max_snd_data_seg_len = + fw_ddb_entry->iscsi_max_snd_data_seg_len; + + if (ddb_entry->options & DDB_OPT_IPV6_DEVICE) { + memcpy(&ddb_entry->remote_ipv6_addr, + fw_ddb_entry->ip_addr, + min(sizeof(ddb_entry->remote_ipv6_addr), + sizeof(fw_ddb_entry->ip_addr))); + memcpy(&ddb_entry->link_local_ipv6_addr, + fw_ddb_entry->link_local_ipv6_addr, + min(sizeof(ddb_entry->link_local_ipv6_addr), + sizeof(fw_ddb_entry->link_local_ipv6_addr))); + + DEBUG2(dev_info(&ha->pdev->dev, "%s: DDB[%d] osIdx = %d " + "State %04x ConnErr %08x IP %pI6 " + ":%04d \"%s\"\n", + __func__, fw_ddb_index, + ddb_entry->os_target_id, + ddb_entry->fw_ddb_device_state, + conn_err, fw_ddb_entry->ip_addr, + le16_to_cpu(fw_ddb_entry->port), + fw_ddb_entry->iscsi_name)); + } else + DEBUG2(dev_info(&ha->pdev->dev, "%s: DDB[%d] osIdx = %d " + "State %04x ConnErr %08x IP %pI4 " + ":%04d \"%s\"\n", + __func__, fw_ddb_index, + ddb_entry->os_target_id, + ddb_entry->fw_ddb_device_state, + conn_err, fw_ddb_entry->ip_addr, + le16_to_cpu(fw_ddb_entry->port), + fw_ddb_entry->iscsi_name)); +exit_update_ddb: if (fw_ddb_entry) dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry, fw_ddb_entry_dma); @@ -492,6 +645,40 @@ static struct ddb_entry * qla4xxx_alloc_ddb(struct scsi_qla_host *ha, } /** + * qla4_is_relogin_allowed - Are we allowed to login? + * @ha: Pointer to host adapter structure. + * @conn_err: Last connection error associated with the ddb + * + * This routine tests the given connection error to determine if + * we are allowed to login. + **/ +int qla4_is_relogin_allowed(struct scsi_qla_host *ha, uint32_t conn_err) +{ + uint32_t err_code, login_rsp_sts_class; + int relogin = 1; + + err_code = ((conn_err & 0x00ff0000) >> 16); + login_rsp_sts_class = ((conn_err & 0x0000ff00) >> 8); + if (err_code == 0x1c || err_code == 0x06) { + DEBUG2(dev_info(&ha->pdev->dev, + ": conn_err=0x%08x, send target completed" + " or access denied failure\n", conn_err)); + relogin = 0; + } + if ((err_code == 0x08) && (login_rsp_sts_class == 0x02)) { + /* Login Response PDU returned an error. + Login Response Status in Error Code Detail + indicates login should not be retried.*/ + DEBUG2(dev_info(&ha->pdev->dev, + ": conn_err=0x%08x, do not retry relogin\n", + conn_err)); + relogin = 0; + } + + return relogin; +} + +/** * qla4xxx_configure_ddbs - builds driver ddb list * @ha: Pointer to host adapter structure. * @@ -505,18 +692,30 @@ static int qla4xxx_build_ddb_list(struct scsi_qla_host *ha) uint32_t fw_ddb_index = 0; uint32_t next_fw_ddb_index = 0; uint32_t ddb_state; - uint32_t conn_err, err_code; + uint32_t conn_err; struct ddb_entry *ddb_entry; + struct dev_db_entry *fw_ddb_entry = NULL; + dma_addr_t fw_ddb_entry_dma; + uint32_t ipv6_device; uint32_t new_tgt; + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), + &fw_ddb_entry_dma, GFP_KERNEL); + if (fw_ddb_entry == NULL) { + DEBUG2(dev_info(&ha->pdev->dev, "%s: DMA alloc failed\n", + __func__)); + return QLA_ERROR; + } + dev_info(&ha->pdev->dev, "Initializing DDBs ...\n"); for (fw_ddb_index = 0; fw_ddb_index < MAX_DDB_ENTRIES; fw_ddb_index = next_fw_ddb_index) { /* First, let's see if a device exists here */ - if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL, - &next_fw_ddb_index, &ddb_state, - &conn_err, NULL, NULL) == - QLA_ERROR) { + if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, + 0, NULL, &next_fw_ddb_index, + &ddb_state, &conn_err, + NULL, NULL) == + QLA_ERROR) { DEBUG2(printk("scsi%ld: %s: get_ddb_entry, " "fw_ddb_index %d failed", ha->host_no, __func__, fw_ddb_index)); @@ -533,18 +732,19 @@ static int qla4xxx_build_ddb_list(struct scsi_qla_host *ha) /* Try and login to device */ DEBUG2(printk("scsi%ld: %s: Login to DDB[%d]\n", ha->host_no, __func__, fw_ddb_index)); - err_code = ((conn_err & 0x00ff0000) >> 16); - if (err_code == 0x1c || err_code == 0x06) { - DEBUG2(printk("scsi%ld: %s send target " - "completed " - "or access denied failure\n", - ha->host_no, __func__)); - } else { + ipv6_device = le16_to_cpu(fw_ddb_entry->options) & + DDB_OPT_IPV6_DEVICE; + if (qla4_is_relogin_allowed(ha, conn_err) && + ((!ipv6_device && + *((uint32_t *)fw_ddb_entry->ip_addr)) + || ipv6_device)) { qla4xxx_set_ddb_entry(ha, fw_ddb_index, 0); if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, - NULL, 0, NULL, &next_fw_ddb_index, - &ddb_state, &conn_err, NULL, NULL) - == QLA_ERROR) { + NULL, 0, NULL, + &next_fw_ddb_index, + &ddb_state, &conn_err, + NULL, NULL) + == QLA_ERROR) { DEBUG2(printk("scsi%ld: %s:" "get_ddb_entry %d failed\n", ha->host_no, @@ -599,7 +799,6 @@ next_one: struct qla4_relog_scan { int halt_wait; uint32_t conn_err; - uint32_t err_code; uint32_t fw_ddb_index; uint32_t next_fw_ddb_index; uint32_t fw_ddb_device_state; @@ -609,18 +808,7 @@ static int qla4_test_rdy(struct scsi_qla_host *ha, struct qla4_relog_scan *rs) { struct ddb_entry *ddb_entry; - /* - * Don't want to do a relogin if connection - * error is 0x1c. - */ - rs->err_code = ((rs->conn_err & 0x00ff0000) >> 16); - if (rs->err_code == 0x1c || rs->err_code == 0x06) { - DEBUG2(printk( - "scsi%ld: %s send target" - " completed or " - "access denied failure\n", - ha->host_no, __func__)); - } else { + if (qla4_is_relogin_allowed(ha, rs->conn_err)) { /* We either have a device that is in * the process of relogging in or a * device that is waiting to be @@ -908,7 +1096,7 @@ static void qla4x00_pci_config(struct scsi_qla_host *ha) static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) { int status = QLA_ERROR; - uint32_t max_wait_time; + unsigned long max_wait_time; unsigned long flags; uint32_t mbox_status; @@ -940,7 +1128,10 @@ static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) spin_unlock_irqrestore(&ha->hardware_lock, flags); /* Wait for firmware to come UP. */ - max_wait_time = FIRMWARE_UP_TOV * 4; + DEBUG2(printk(KERN_INFO "scsi%ld: %s: Wait up to %d seconds for " + "boot firmware to complete...\n", + ha->host_no, __func__, FIRMWARE_UP_TOV)); + max_wait_time = jiffies + (FIRMWARE_UP_TOV * HZ); do { uint32_t ctrl_status; @@ -954,16 +1145,15 @@ static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) if (mbox_status == MBOX_STS_COMMAND_COMPLETE) break; - DEBUG2(printk("scsi%ld: %s: Waiting for boot firmware to " - "complete... ctrl_sts=0x%x, remaining=%d\n", - ha->host_no, __func__, ctrl_status, - max_wait_time)); + DEBUG2(printk(KERN_INFO "scsi%ld: %s: Waiting for boot " + "firmware to complete... ctrl_sts=0x%x\n", + ha->host_no, __func__, ctrl_status)); - msleep(250); - } while ((max_wait_time--)); + msleep_interruptible(250); + } while (!time_after_eq(jiffies, max_wait_time)); if (mbox_status == MBOX_STS_COMMAND_COMPLETE) { - DEBUG(printk("scsi%ld: %s: Firmware has started\n", + DEBUG(printk(KERN_INFO "scsi%ld: %s: Firmware has started\n", ha->host_no, __func__)); spin_lock_irqsave(&ha->hardware_lock, flags); @@ -1141,6 +1331,7 @@ int qla4xxx_initialize_adapter(struct scsi_qla_host *ha, int status = QLA_ERROR; int8_t ip_address[IP_ADDR_LEN] = {0} ; + clear_bit(AF_ONLINE, &ha->flags); ha->eeprom_cmd_data = 0; qla4x00_pci_config(ha); @@ -1166,7 +1357,7 @@ int qla4xxx_initialize_adapter(struct scsi_qla_host *ha, * the ddb_list and wait for DHCP lease acquired aen to come in * followed by 0x8014 aen" to trigger the tgt discovery process. */ - if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS) + if (ha->firmware_state & FW_STATE_CONFIGURING_IP) goto exit_init_online; /* Skip device discovery if ip and subnet is zero */ @@ -1270,8 +1461,8 @@ static void qla4xxx_add_device_dynamically(struct scsi_qla_host *ha, * * This routine processes a Decive Database Changed AEN Event. **/ -int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, - uint32_t fw_ddb_index, uint32_t state) +int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, + uint32_t state, uint32_t conn_err) { struct ddb_entry * ddb_entry; uint32_t old_fw_ddb_device_state; @@ -1318,19 +1509,24 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, * the device came back. */ } else { - /* Device went away, try to relogin. */ - /* Mark device missing */ - if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) + /* Device went away, mark device missing */ + if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) { + DEBUG2(dev_info(&ha->pdev->dev, "%s mark missing " + "ddb_entry 0x%p sess 0x%p conn 0x%p\n", + __func__, ddb_entry, + ddb_entry->sess, ddb_entry->conn)); qla4xxx_mark_device_missing(ha, ddb_entry); + } + /* * Relogin if device state changed to a not active state. - * However, do not relogin if this aen is a result of an IOCTL - * logout (DF_NO_RELOGIN) or if this is a discovered device. + * However, do not relogin if a RELOGIN is in process, or + * we are not allowed to relogin to this DDB. */ if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_FAILED && !test_bit(DF_RELOGIN, &ddb_entry->flags) && !test_bit(DF_NO_RELOGIN, &ddb_entry->flags) && - !test_bit(DF_ISNS_DISCOVERED, &ddb_entry->flags)) { + qla4_is_relogin_allowed(ha, conn_err)) { /* * This triggers a relogin. After the relogin_timer * expires, the relogin gets scheduled. We must wait a @@ -1338,7 +1534,7 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, * with failed device_state or a logout response before * we can issue another relogin. */ - /* Firmware padds this timeout: (time2wait +1). + /* Firmware pads this timeout: (time2wait +1). * Driver retry to login should be longer than F/W. * Otherwise F/W will fail * set_ddb() mbx cmd with 0x4005 since it still diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c index e0c32159749..e66f3f263f4 100644 --- a/drivers/scsi/qla4xxx/ql4_iocb.c +++ b/drivers/scsi/qla4xxx/ql4_iocb.c @@ -299,7 +299,7 @@ int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb) qla4xxx_build_scsi_iocbs(srb, cmd_entry, tot_dsds); wmb(); - srb->cmd->host_scribble = (unsigned char *)srb; + srb->cmd->host_scribble = (unsigned char *)(unsigned long)index; /* update counters */ srb->state = SRB_ACTIVE_STATE; diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index c196d55eae3..596c3031483 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -97,7 +97,7 @@ qla4xxx_status_cont_entry(struct scsi_qla_host *ha, /* Place command on done queue. */ if (srb->req_sense_len == 0) { - qla4xxx_srb_compl(ha, srb); + kref_put(&srb->srb_ref, qla4xxx_srb_compl); ha->status_srb = NULL; } } @@ -329,7 +329,7 @@ status_entry_exit: /* complete the request, if not waiting for status_continuation pkt */ srb->cc_stat = sts_entry->completionStatus; if (ha->status_srb == NULL) - qla4xxx_srb_compl(ha, srb); + kref_put(&srb->srb_ref, qla4xxx_srb_compl); } /** @@ -393,7 +393,7 @@ static void qla4xxx_process_response_queue(struct scsi_qla_host * ha) /* ETRY normally by sending it back with * DID_BUS_BUSY */ srb->cmd->result = DID_BUS_BUSY << 16; - qla4xxx_srb_compl(ha, srb); + kref_put(&srb->srb_ref, qla4xxx_srb_compl); break; case ET_CONTINUE: @@ -498,15 +498,22 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, break; case MBOX_ASTS_LINK_UP: - DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK UP\n", - ha->host_no, mbox_status)); set_bit(AF_LINK_UP, &ha->flags); + if (test_bit(AF_INIT_DONE, &ha->flags)) + set_bit(DPC_LINK_CHANGED, &ha->dpc_flags); + + DEBUG2(printk(KERN_INFO "scsi%ld: AEN %04x Adapter" + " LINK UP\n", ha->host_no, + mbox_status)); break; case MBOX_ASTS_LINK_DOWN: - DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK DOWN\n", - ha->host_no, mbox_status)); clear_bit(AF_LINK_UP, &ha->flags); + set_bit(DPC_LINK_CHANGED, &ha->dpc_flags); + + DEBUG2(printk(KERN_INFO "scsi%ld: AEN %04x Adapter" + " LINK DOWN\n", ha->host_no, + mbox_status)); break; case MBOX_ASTS_HEARTBEAT: @@ -831,7 +838,7 @@ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen) qla4xxx_reinitialize_ddb_list(ha); } else if (mbox_sts[1] == 1) { /* Specific device. */ qla4xxx_process_ddb_changed(ha, mbox_sts[2], - mbox_sts[3]); + mbox_sts[3], mbox_sts[4]); } break; } diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index caeb7d10ae0..75496fb0ae7 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -172,108 +172,207 @@ mbox_exit: return status; } +uint8_t +qla4xxx_set_ifcb(struct scsi_qla_host *ha, uint32_t *mbox_cmd, + uint32_t *mbox_sts, dma_addr_t init_fw_cb_dma) +{ + memset(mbox_cmd, 0, sizeof(mbox_cmd[0]) * MBOX_REG_COUNT); + memset(mbox_sts, 0, sizeof(mbox_sts[0]) * MBOX_REG_COUNT); + mbox_cmd[0] = MBOX_CMD_INITIALIZE_FIRMWARE; + mbox_cmd[1] = 0; + mbox_cmd[2] = LSDW(init_fw_cb_dma); + mbox_cmd[3] = MSDW(init_fw_cb_dma); + mbox_cmd[4] = sizeof(struct addr_ctrl_blk); + mbox_cmd[5] = (IFCB_VER_MAX << 8) | IFCB_VER_MIN; + + if (qla4xxx_mailbox_command(ha, 6, 6, mbox_cmd, mbox_sts) != + QLA_SUCCESS) { + DEBUG2(printk(KERN_WARNING "scsi%ld: %s: " + "MBOX_CMD_INITIALIZE_FIRMWARE" + " failed w/ status %04X\n", + ha->host_no, __func__, mbox_sts[0])); + return QLA_ERROR; + } + return QLA_SUCCESS; +} + +uint8_t +qla4xxx_get_ifcb(struct scsi_qla_host *ha, uint32_t *mbox_cmd, + uint32_t *mbox_sts, dma_addr_t init_fw_cb_dma) +{ + memset(mbox_cmd, 0, sizeof(mbox_cmd[0]) * MBOX_REG_COUNT); + memset(mbox_sts, 0, sizeof(mbox_sts[0]) * MBOX_REG_COUNT); + mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; + mbox_cmd[2] = LSDW(init_fw_cb_dma); + mbox_cmd[3] = MSDW(init_fw_cb_dma); + mbox_cmd[4] = sizeof(struct addr_ctrl_blk); + + if (qla4xxx_mailbox_command(ha, 5, 5, mbox_cmd, mbox_sts) != + QLA_SUCCESS) { + DEBUG2(printk(KERN_WARNING "scsi%ld: %s: " + "MBOX_CMD_GET_INIT_FW_CTRL_BLOCK" + " failed w/ status %04X\n", + ha->host_no, __func__, mbox_sts[0])); + return QLA_ERROR; + } + return QLA_SUCCESS; +} + +void +qla4xxx_update_local_ip(struct scsi_qla_host *ha, + struct addr_ctrl_blk *init_fw_cb) +{ + /* Save IPv4 Address Info */ + memcpy(ha->ip_address, init_fw_cb->ipv4_addr, + min(sizeof(ha->ip_address), sizeof(init_fw_cb->ipv4_addr))); + memcpy(ha->subnet_mask, init_fw_cb->ipv4_subnet, + min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->ipv4_subnet))); + memcpy(ha->gateway, init_fw_cb->ipv4_gw_addr, + min(sizeof(ha->gateway), sizeof(init_fw_cb->ipv4_gw_addr))); + + if (is_ipv6_enabled(ha)) { + /* Save IPv6 Address */ + ha->ipv6_link_local_state = init_fw_cb->ipv6_lnk_lcl_addr_state; + ha->ipv6_addr0_state = init_fw_cb->ipv6_addr0_state; + ha->ipv6_addr1_state = init_fw_cb->ipv6_addr1_state; + ha->ipv6_default_router_state = init_fw_cb->ipv6_dflt_rtr_state; + ha->ipv6_link_local_addr.in6_u.u6_addr8[0] = 0xFE; + ha->ipv6_link_local_addr.in6_u.u6_addr8[1] = 0x80; + + memcpy(&ha->ipv6_link_local_addr.in6_u.u6_addr8[8], + init_fw_cb->ipv6_if_id, + min(sizeof(ha->ipv6_link_local_addr)/2, + sizeof(init_fw_cb->ipv6_if_id))); + memcpy(&ha->ipv6_addr0, init_fw_cb->ipv6_addr0, + min(sizeof(ha->ipv6_addr0), + sizeof(init_fw_cb->ipv6_addr0))); + memcpy(&ha->ipv6_addr1, init_fw_cb->ipv6_addr1, + min(sizeof(ha->ipv6_addr1), + sizeof(init_fw_cb->ipv6_addr1))); + memcpy(&ha->ipv6_default_router_addr, + init_fw_cb->ipv6_dflt_rtr_addr, + min(sizeof(ha->ipv6_default_router_addr), + sizeof(init_fw_cb->ipv6_dflt_rtr_addr))); + } +} + +uint8_t +qla4xxx_update_local_ifcb(struct scsi_qla_host *ha, + uint32_t *mbox_cmd, + uint32_t *mbox_sts, + struct addr_ctrl_blk *init_fw_cb, + dma_addr_t init_fw_cb_dma) +{ + if (qla4xxx_get_ifcb(ha, mbox_cmd, mbox_sts, init_fw_cb_dma) + != QLA_SUCCESS) { + DEBUG2(printk(KERN_WARNING + "scsi%ld: %s: Failed to get init_fw_ctrl_blk\n", + ha->host_no, __func__)); + return QLA_ERROR; + } + + DEBUG2(qla4xxx_dump_buffer(init_fw_cb, sizeof(struct addr_ctrl_blk))); + + /* Save some info in adapter structure. */ + ha->acb_version = init_fw_cb->acb_version; + ha->firmware_options = le16_to_cpu(init_fw_cb->fw_options); + ha->tcp_options = le16_to_cpu(init_fw_cb->ipv4_tcp_opts); + ha->ipv4_options = le16_to_cpu(init_fw_cb->ipv4_ip_opts); + ha->ipv4_addr_state = le16_to_cpu(init_fw_cb->ipv4_addr_state); + ha->heartbeat_interval = init_fw_cb->hb_interval; + memcpy(ha->name_string, init_fw_cb->iscsi_name, + min(sizeof(ha->name_string), + sizeof(init_fw_cb->iscsi_name))); + /*memcpy(ha->alias, init_fw_cb->Alias, + min(sizeof(ha->alias), sizeof(init_fw_cb->Alias)));*/ + + /* Save Command Line Paramater info */ + ha->port_down_retry_count = le16_to_cpu(init_fw_cb->conn_ka_timeout); + ha->discovery_wait = ql4xdiscoverywait; + + if (ha->acb_version == ACB_SUPPORTED) { + ha->ipv6_options = init_fw_cb->ipv6_opts; + ha->ipv6_addl_options = init_fw_cb->ipv6_addtl_opts; + } + qla4xxx_update_local_ip(ha, init_fw_cb); + + return QLA_SUCCESS; +} + /** * qla4xxx_initialize_fw_cb - initializes firmware control block. * @ha: Pointer to host adapter structure. **/ int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha) { - struct init_fw_ctrl_blk *init_fw_cb; + struct addr_ctrl_blk *init_fw_cb; dma_addr_t init_fw_cb_dma; uint32_t mbox_cmd[MBOX_REG_COUNT]; uint32_t mbox_sts[MBOX_REG_COUNT]; int status = QLA_ERROR; init_fw_cb = dma_alloc_coherent(&ha->pdev->dev, - sizeof(struct init_fw_ctrl_blk), + sizeof(struct addr_ctrl_blk), &init_fw_cb_dma, GFP_KERNEL); if (init_fw_cb == NULL) { DEBUG2(printk("scsi%ld: %s: Unable to alloc init_cb\n", ha->host_no, __func__)); return 10; } - memset(init_fw_cb, 0, sizeof(struct init_fw_ctrl_blk)); + memset(init_fw_cb, 0, sizeof(struct addr_ctrl_blk)); /* Get Initialize Firmware Control Block. */ memset(&mbox_cmd, 0, sizeof(mbox_cmd)); memset(&mbox_sts, 0, sizeof(mbox_sts)); - mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; - mbox_cmd[2] = LSDW(init_fw_cb_dma); - mbox_cmd[3] = MSDW(init_fw_cb_dma); - mbox_cmd[4] = sizeof(struct init_fw_ctrl_blk); - - if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]) != + if (qla4xxx_get_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) != QLA_SUCCESS) { dma_free_coherent(&ha->pdev->dev, - sizeof(struct init_fw_ctrl_blk), + sizeof(struct addr_ctrl_blk), init_fw_cb, init_fw_cb_dma); - return status; + goto exit_init_fw_cb; } /* Initialize request and response queues. */ qla4xxx_init_rings(ha); /* Fill in the request and response queue information. */ - init_fw_cb->pri.rqq_consumer_idx = cpu_to_le16(ha->request_out); - init_fw_cb->pri.compq_producer_idx = cpu_to_le16(ha->response_in); - init_fw_cb->pri.rqq_len = __constant_cpu_to_le16(REQUEST_QUEUE_DEPTH); - init_fw_cb->pri.compq_len = __constant_cpu_to_le16(RESPONSE_QUEUE_DEPTH); - init_fw_cb->pri.rqq_addr_lo = cpu_to_le32(LSDW(ha->request_dma)); - init_fw_cb->pri.rqq_addr_hi = cpu_to_le32(MSDW(ha->request_dma)); - init_fw_cb->pri.compq_addr_lo = cpu_to_le32(LSDW(ha->response_dma)); - init_fw_cb->pri.compq_addr_hi = cpu_to_le32(MSDW(ha->response_dma)); - init_fw_cb->pri.shdwreg_addr_lo = - cpu_to_le32(LSDW(ha->shadow_regs_dma)); - init_fw_cb->pri.shdwreg_addr_hi = - cpu_to_le32(MSDW(ha->shadow_regs_dma)); + init_fw_cb->rqq_consumer_idx = cpu_to_le16(ha->request_out); + init_fw_cb->compq_producer_idx = cpu_to_le16(ha->response_in); + init_fw_cb->rqq_len = __constant_cpu_to_le16(REQUEST_QUEUE_DEPTH); + init_fw_cb->compq_len = __constant_cpu_to_le16(RESPONSE_QUEUE_DEPTH); + init_fw_cb->rqq_addr_lo = cpu_to_le32(LSDW(ha->request_dma)); + init_fw_cb->rqq_addr_hi = cpu_to_le32(MSDW(ha->request_dma)); + init_fw_cb->compq_addr_lo = cpu_to_le32(LSDW(ha->response_dma)); + init_fw_cb->compq_addr_hi = cpu_to_le32(MSDW(ha->response_dma)); + init_fw_cb->shdwreg_addr_lo = cpu_to_le32(LSDW(ha->shadow_regs_dma)); + init_fw_cb->shdwreg_addr_hi = cpu_to_le32(MSDW(ha->shadow_regs_dma)); /* Set up required options. */ - init_fw_cb->pri.fw_options |= + init_fw_cb->fw_options |= __constant_cpu_to_le16(FWOPT_SESSION_MODE | FWOPT_INITIATOR_MODE); - init_fw_cb->pri.fw_options &= __constant_cpu_to_le16(~FWOPT_TARGET_MODE); - - /* Save some info in adapter structure. */ - ha->firmware_options = le16_to_cpu(init_fw_cb->pri.fw_options); - ha->tcp_options = le16_to_cpu(init_fw_cb->pri.ipv4_tcp_opts); - ha->heartbeat_interval = init_fw_cb->pri.hb_interval; - memcpy(ha->ip_address, init_fw_cb->pri.ipv4_addr, - min(sizeof(ha->ip_address), sizeof(init_fw_cb->pri.ipv4_addr))); - memcpy(ha->subnet_mask, init_fw_cb->pri.ipv4_subnet, - min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->pri.ipv4_subnet))); - memcpy(ha->gateway, init_fw_cb->pri.ipv4_gw_addr, - min(sizeof(ha->gateway), sizeof(init_fw_cb->pri.ipv4_gw_addr))); - memcpy(ha->name_string, init_fw_cb->pri.iscsi_name, - min(sizeof(ha->name_string), - sizeof(init_fw_cb->pri.iscsi_name))); - /*memcpy(ha->alias, init_fw_cb->Alias, - min(sizeof(ha->alias), sizeof(init_fw_cb->Alias)));*/ - - /* Save Command Line Paramater info */ - ha->port_down_retry_count = le16_to_cpu(init_fw_cb->pri.conn_ka_timeout); - ha->discovery_wait = ql4xdiscoverywait; + init_fw_cb->fw_options &= __constant_cpu_to_le16(~FWOPT_TARGET_MODE); - /* Send Initialize Firmware Control Block. */ - memset(&mbox_cmd, 0, sizeof(mbox_cmd)); - memset(&mbox_sts, 0, sizeof(mbox_sts)); - - mbox_cmd[0] = MBOX_CMD_INITIALIZE_FIRMWARE; - mbox_cmd[1] = 0; - mbox_cmd[2] = LSDW(init_fw_cb_dma); - mbox_cmd[3] = MSDW(init_fw_cb_dma); - mbox_cmd[4] = sizeof(struct init_fw_ctrl_blk); + if (qla4xxx_set_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) + != QLA_SUCCESS) { + DEBUG2(printk(KERN_WARNING + "scsi%ld: %s: Failed to set init_fw_ctrl_blk\n", + ha->host_no, __func__)); + goto exit_init_fw_cb; + } - if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]) == - QLA_SUCCESS) - status = QLA_SUCCESS; - else { - DEBUG2(printk("scsi%ld: %s: MBOX_CMD_INITIALIZE_FIRMWARE " - "failed w/ status %04X\n", ha->host_no, __func__, - mbox_sts[0])); + if (qla4xxx_update_local_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], + init_fw_cb, init_fw_cb_dma) != QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: Failed to update local ifcb\n", + ha->host_no, __func__)); + goto exit_init_fw_cb; } - dma_free_coherent(&ha->pdev->dev, sizeof(struct init_fw_ctrl_blk), - init_fw_cb, init_fw_cb_dma); + status = QLA_SUCCESS; + +exit_init_fw_cb: + dma_free_coherent(&ha->pdev->dev, sizeof(struct addr_ctrl_blk), + init_fw_cb, init_fw_cb_dma); return status; } @@ -284,13 +383,13 @@ int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha) **/ int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha) { - struct init_fw_ctrl_blk *init_fw_cb; + struct addr_ctrl_blk *init_fw_cb; dma_addr_t init_fw_cb_dma; uint32_t mbox_cmd[MBOX_REG_COUNT]; uint32_t mbox_sts[MBOX_REG_COUNT]; init_fw_cb = dma_alloc_coherent(&ha->pdev->dev, - sizeof(struct init_fw_ctrl_blk), + sizeof(struct addr_ctrl_blk), &init_fw_cb_dma, GFP_KERNEL); if (init_fw_cb == NULL) { printk("scsi%ld: %s: Unable to alloc init_cb\n", ha->host_no, @@ -299,35 +398,21 @@ int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha) } /* Get Initialize Firmware Control Block. */ - memset(&mbox_cmd, 0, sizeof(mbox_cmd)); - memset(&mbox_sts, 0, sizeof(mbox_sts)); - - memset(init_fw_cb, 0, sizeof(struct init_fw_ctrl_blk)); - mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; - mbox_cmd[2] = LSDW(init_fw_cb_dma); - mbox_cmd[3] = MSDW(init_fw_cb_dma); - mbox_cmd[4] = sizeof(struct init_fw_ctrl_blk); - - if (qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 1, &mbox_cmd[0], &mbox_sts[0]) != + memset(init_fw_cb, 0, sizeof(struct addr_ctrl_blk)); + if (qla4xxx_get_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) != QLA_SUCCESS) { DEBUG2(printk("scsi%ld: %s: Failed to get init_fw_ctrl_blk\n", ha->host_no, __func__)); dma_free_coherent(&ha->pdev->dev, - sizeof(struct init_fw_ctrl_blk), + sizeof(struct addr_ctrl_blk), init_fw_cb, init_fw_cb_dma); return QLA_ERROR; } /* Save IP Address. */ - memcpy(ha->ip_address, init_fw_cb->pri.ipv4_addr, - min(sizeof(ha->ip_address), sizeof(init_fw_cb->pri.ipv4_addr))); - memcpy(ha->subnet_mask, init_fw_cb->pri.ipv4_subnet, - min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->pri.ipv4_subnet))); - memcpy(ha->gateway, init_fw_cb->pri.ipv4_gw_addr, - min(sizeof(ha->gateway), sizeof(init_fw_cb->pri.ipv4_gw_addr))); - - dma_free_coherent(&ha->pdev->dev, sizeof(struct init_fw_ctrl_blk), - init_fw_cb, init_fw_cb_dma); + qla4xxx_update_local_ip(ha, init_fw_cb); + dma_free_coherent(&ha->pdev->dev, sizeof(struct addr_ctrl_blk), + init_fw_cb, init_fw_cb_dma); return QLA_SUCCESS; } @@ -409,6 +494,7 @@ int qla4xxx_get_fwddb_entry(struct scsi_qla_host *ha, uint16_t *connection_id) { int status = QLA_ERROR; + uint16_t options; uint32_t mbox_cmd[MBOX_REG_COUNT]; uint32_t mbox_sts[MBOX_REG_COUNT]; @@ -441,14 +527,26 @@ int qla4xxx_get_fwddb_entry(struct scsi_qla_host *ha, goto exit_get_fwddb; } if (fw_ddb_entry) { - dev_info(&ha->pdev->dev, "DDB[%d] MB0 %04x Tot %d Next %d " - "State %04x ConnErr %08x %d.%d.%d.%d:%04d \"%s\"\n", - fw_ddb_index, mbox_sts[0], mbox_sts[2], mbox_sts[3], - mbox_sts[4], mbox_sts[5], fw_ddb_entry->ip_addr[0], - fw_ddb_entry->ip_addr[1], fw_ddb_entry->ip_addr[2], - fw_ddb_entry->ip_addr[3], - le16_to_cpu(fw_ddb_entry->port), - fw_ddb_entry->iscsi_name); + options = le16_to_cpu(fw_ddb_entry->options); + if (options & DDB_OPT_IPV6_DEVICE) { + dev_info(&ha->pdev->dev, "%s: DDB[%d] MB0 %04x Tot %d " + "Next %d State %04x ConnErr %08x %pI6 " + ":%04d \"%s\"\n", __func__, fw_ddb_index, + mbox_sts[0], mbox_sts[2], mbox_sts[3], + mbox_sts[4], mbox_sts[5], + fw_ddb_entry->ip_addr, + le16_to_cpu(fw_ddb_entry->port), + fw_ddb_entry->iscsi_name); + } else { + dev_info(&ha->pdev->dev, "%s: DDB[%d] MB0 %04x Tot %d " + "Next %d State %04x ConnErr %08x %pI4 " + ":%04d \"%s\"\n", __func__, fw_ddb_index, + mbox_sts[0], mbox_sts[2], mbox_sts[3], + mbox_sts[4], mbox_sts[5], + fw_ddb_entry->ip_addr, + le16_to_cpu(fw_ddb_entry->port), + fw_ddb_entry->iscsi_name); + } } if (num_valid_ddb_entries) *num_valid_ddb_entries = mbox_sts[2]; @@ -664,6 +762,59 @@ exit_get_event_log: } /** + * qla4xxx_abort_task - issues Abort Task + * @ha: Pointer to host adapter structure. + * @srb: Pointer to srb entry + * + * This routine performs a LUN RESET on the specified target/lun. + * The caller must ensure that the ddb_entry and lun_entry pointers + * are valid before calling this routine. + **/ +int qla4xxx_abort_task(struct scsi_qla_host *ha, struct srb *srb) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + struct scsi_cmnd *cmd = srb->cmd; + int status = QLA_SUCCESS; + unsigned long flags = 0; + uint32_t index; + + /* + * Send abort task command to ISP, so that the ISP will return + * request with ABORT status + */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + + spin_lock_irqsave(&ha->hardware_lock, flags); + index = (unsigned long)(unsigned char *)cmd->host_scribble; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* Firmware already posted completion on response queue */ + if (index == MAX_SRBS) + return status; + + mbox_cmd[0] = MBOX_CMD_ABORT_TASK; + mbox_cmd[1] = srb->fw_ddb_index; + mbox_cmd[2] = index; + /* Immediate Command Enable */ + mbox_cmd[5] = 0x01; + + qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 5, &mbox_cmd[0], + &mbox_sts[0]); + if (mbox_sts[0] != MBOX_STS_COMMAND_COMPLETE) { + status = QLA_ERROR; + + DEBUG2(printk(KERN_WARNING "scsi%ld:%d:%d: abort task FAILED: " + "mbx0=%04X, mb1=%04X, mb2=%04X, mb3=%04X, mb4=%04X\n", + ha->host_no, cmd->device->id, cmd->device->lun, mbox_sts[0], + mbox_sts[1], mbox_sts[2], mbox_sts[3], mbox_sts[4])); + } + + return status; +} + +/** * qla4xxx_reset_lun - issues LUN Reset * @ha: Pointer to host adapter structure. * @db_entry: Pointer to device database entry diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 2ccad36bee9..38b1d38afca 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -74,6 +74,7 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc); */ static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, void (*done) (struct scsi_cmnd *)); +static int qla4xxx_eh_abort(struct scsi_cmnd *cmd); static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd); static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd); static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); @@ -88,6 +89,7 @@ static struct scsi_host_template qla4xxx_driver_template = { .proc_name = DRIVER_NAME, .queuecommand = qla4xxx_queuecommand, + .eh_abort_handler = qla4xxx_eh_abort, .eh_device_reset_handler = qla4xxx_eh_device_reset, .eh_target_reset_handler = qla4xxx_eh_target_reset, .eh_host_reset_handler = qla4xxx_eh_host_reset, @@ -384,12 +386,12 @@ static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha, if (!srb) return srb; - atomic_set(&srb->ref_count, 1); + kref_init(&srb->srb_ref); srb->ha = ha; srb->ddb = ddb_entry; srb->cmd = cmd; srb->flags = 0; - cmd->SCp.ptr = (void *)srb; + CMD_SP(cmd) = (void *)srb; cmd->scsi_done = done; return srb; @@ -403,12 +405,14 @@ static void qla4xxx_srb_free_dma(struct scsi_qla_host *ha, struct srb *srb) scsi_dma_unmap(cmd); srb->flags &= ~SRB_DMA_VALID; } - cmd->SCp.ptr = NULL; + CMD_SP(cmd) = NULL; } -void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb) +void qla4xxx_srb_compl(struct kref *ref) { + struct srb *srb = container_of(ref, struct srb, srb_ref); struct scsi_cmnd *cmd = srb->cmd; + struct scsi_qla_host *ha = srb->ha; qla4xxx_srb_free_dma(ha, srb); @@ -685,6 +689,7 @@ static void qla4xxx_timer(struct scsi_qla_host *ha) test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags) || test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) || test_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags) || + test_bit(DPC_LINK_CHANGED, &ha->dpc_flags) || test_bit(DPC_AEN, &ha->dpc_flags)) && ha->dpc_thread) { DEBUG2(printk("scsi%ld: %s: scheduling dpc routine" @@ -886,11 +891,10 @@ static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha) srb = qla4xxx_del_from_active_array(ha, i); if (srb != NULL) { srb->cmd->result = DID_RESET << 16; - qla4xxx_srb_compl(ha, srb); + kref_put(&srb->srb_ref, qla4xxx_srb_compl); } } spin_unlock_irqrestore(&ha->hardware_lock, flags); - } /** @@ -1069,6 +1073,54 @@ static void qla4xxx_do_dpc(struct work_struct *work) if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags)) qla4xxx_get_dhcp_ip_address(ha); + /* ---- link change? --- */ + if (test_and_clear_bit(DPC_LINK_CHANGED, &ha->dpc_flags)) { + if (!test_bit(AF_LINK_UP, &ha->flags)) { + /* ---- link down? --- */ + list_for_each_entry_safe(ddb_entry, dtemp, + &ha->ddb_list, list) { + if (atomic_read(&ddb_entry->state) == + DDB_STATE_ONLINE) + qla4xxx_mark_device_missing(ha, + ddb_entry); + } + } else { + /* ---- link up? --- * + * F/W will auto login to all devices ONLY ONCE after + * link up during driver initialization and runtime + * fatal error recovery. Therefore, the driver must + * manually relogin to devices when recovering from + * connection failures, logouts, expired KATO, etc. */ + + list_for_each_entry_safe(ddb_entry, dtemp, + &ha->ddb_list, list) { + if ((atomic_read(&ddb_entry->state) == + DDB_STATE_MISSING) || + (atomic_read(&ddb_entry->state) == + DDB_STATE_DEAD)) { + if (ddb_entry->fw_ddb_device_state == + DDB_DS_SESSION_ACTIVE) { + atomic_set(&ddb_entry->state, + DDB_STATE_ONLINE); + dev_info(&ha->pdev->dev, + "scsi%ld: %s: ddb[%d]" + " os[%d] marked" + " ONLINE\n", + ha->host_no, __func__, + ddb_entry->fw_ddb_index, + ddb_entry->os_target_id); + + iscsi_unblock_session( + ddb_entry->sess); + } else + qla4xxx_relogin_device( + ha, ddb_entry); + } + + } + } + } + /* ---- relogin device? --- */ if (adapter_up(ha) && test_and_clear_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags)) { @@ -1430,12 +1482,14 @@ static void qla4xxx_slave_destroy(struct scsi_device *sdev) struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t index) { struct srb *srb = NULL; - struct scsi_cmnd *cmd; + struct scsi_cmnd *cmd = NULL; - if (!(cmd = scsi_host_find_tag(ha->host, index))) + cmd = scsi_host_find_tag(ha->host, index); + if (!cmd) return srb; - if (!(srb = (struct srb *)cmd->host_scribble)) + srb = (struct srb *)CMD_SP(cmd); + if (!srb) return srb; /* update counters */ @@ -1443,14 +1497,15 @@ struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t in ha->req_q_count += srb->iocb_cnt; ha->iocb_cnt -= srb->iocb_cnt; if (srb->cmd) - srb->cmd->host_scribble = NULL; + srb->cmd->host_scribble = + (unsigned char *)(unsigned long) MAX_SRBS; } return srb; } /** * qla4xxx_eh_wait_on_command - waits for command to be returned by firmware - * @ha: actual ha whose done queue will contain the comd returned by firmware. + * @ha: Pointer to host adapter structure. * @cmd: Scsi Command to wait on. * * This routine waits for the command to be returned by the Firmware @@ -1465,7 +1520,7 @@ static int qla4xxx_eh_wait_on_command(struct scsi_qla_host *ha, do { /* Checking to see if its returned to OS */ - rp = (struct srb *) cmd->SCp.ptr; + rp = (struct srb *) CMD_SP(cmd); if (rp == NULL) { done++; break; @@ -1534,6 +1589,62 @@ static int qla4xxx_eh_wait_for_commands(struct scsi_qla_host *ha, } /** + * qla4xxx_eh_abort - callback for abort task. + * @cmd: Pointer to Linux's SCSI command structure + * + * This routine is called by the Linux OS to abort the specified + * command. + **/ +static int qla4xxx_eh_abort(struct scsi_cmnd *cmd) +{ + struct scsi_qla_host *ha = to_qla_host(cmd->device->host); + unsigned int id = cmd->device->id; + unsigned int lun = cmd->device->lun; + unsigned long serial = cmd->serial_number; + struct srb *srb = NULL; + int ret = SUCCESS; + int wait = 0; + + dev_info(&ha->pdev->dev, + "scsi%ld:%d:%d: Abort command issued cmd=%p, pid=%ld\n", + ha->host_no, id, lun, cmd, serial); + + srb = (struct srb *) CMD_SP(cmd); + + if (!srb) + return SUCCESS; + + kref_get(&srb->srb_ref); + + if (qla4xxx_abort_task(ha, srb) != QLA_SUCCESS) { + DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx failed.\n", + ha->host_no, id, lun)); + ret = FAILED; + } else { + DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx success.\n", + ha->host_no, id, lun)); + wait = 1; + } + + kref_put(&srb->srb_ref, qla4xxx_srb_compl); + + /* Wait for command to complete */ + if (wait) { + if (!qla4xxx_eh_wait_on_command(ha, cmd)) { + DEBUG2(printk("scsi%ld:%d:%d: Abort handler timed out\n", + ha->host_no, id, lun)); + ret = FAILED; + } + } + + dev_info(&ha->pdev->dev, + "scsi%ld:%d:%d: Abort command - %s\n", + ha->host_no, id, lun, (ret == SUCCESS) ? "succeded" : "failed"); + + return ret; +} + +/** * qla4xxx_eh_device_reset - callback for target reset. * @cmd: Pointer to Linux's SCSI command structure * diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h index 6980cb279c8..28a6c494a2e 100644 --- a/drivers/scsi/qla4xxx/ql4_version.h +++ b/drivers/scsi/qla4xxx/ql4_version.h @@ -5,5 +5,4 @@ * See LICENSE.qla4xxx for copyright and licensing details. */ -#define QLA4XXX_DRIVER_VERSION "5.01.00-k9" - +#define QLA4XXX_DRIVER_VERSION "5.02.00-k1" diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 1c08f616465..ad0ed212db4 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -67,6 +67,9 @@ #include "scsi_priv.h" #include "scsi_logging.h" +#define CREATE_TRACE_POINTS +#include <trace/events/scsi.h> + static void scsi_done(struct scsi_cmnd *cmd); /* @@ -747,10 +750,12 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) cmd->result = (DID_NO_CONNECT << 16); scsi_done(cmd); } else { + trace_scsi_dispatch_cmd_start(cmd); rtn = host->hostt->queuecommand(cmd, scsi_done); } spin_unlock_irqrestore(host->host_lock, flags); if (rtn) { + trace_scsi_dispatch_cmd_error(cmd, rtn); if (rtn != SCSI_MLQUEUE_DEVICE_BUSY && rtn != SCSI_MLQUEUE_TARGET_BUSY) rtn = SCSI_MLQUEUE_HOST_BUSY; @@ -781,6 +786,7 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) */ static void scsi_done(struct scsi_cmnd *cmd) { + trace_scsi_dispatch_cmd_done(cmd); blk_complete_request(cmd->request); } diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 3a5bfd10b2c..136329b4027 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -12,7 +12,7 @@ * SAS disks. * * - * For documentation see http://www.torque.net/sg/sdebug26.html + * For documentation see http://sg.danny.cz/sg/sdebug26.html * * D. Gilbert (dpg) work for Magneto-Optical device test [20010421] * dpg: work for devfs large number of disks [20010809] @@ -58,8 +58,8 @@ #include "sd.h" #include "scsi_logging.h" -#define SCSI_DEBUG_VERSION "1.81" -static const char * scsi_debug_version_date = "20070104"; +#define SCSI_DEBUG_VERSION "1.82" +static const char * scsi_debug_version_date = "20100324"; /* Additional Sense Code (ASC) */ #define NO_ADDITIONAL_SENSE 0x0 @@ -108,6 +108,7 @@ static const char * scsi_debug_version_date = "20070104"; #define DEF_ATO 1 #define DEF_PHYSBLK_EXP 0 #define DEF_LOWEST_ALIGNED 0 +#define DEF_OPT_BLKS 64 #define DEF_UNMAP_MAX_BLOCKS 0 #define DEF_UNMAP_MAX_DESC 0 #define DEF_UNMAP_GRANULARITY 0 @@ -147,12 +148,18 @@ static const char * scsi_debug_version_date = "20070104"; #define SAM2_LUN_ADDRESS_METHOD 0 #define SAM2_WLUN_REPORT_LUNS 0xc101 +/* Can queue up to this number of commands. Typically commands that + * that have a non-zero delay are queued. */ +#define SCSI_DEBUG_CANQUEUE 255 + static int scsi_debug_add_host = DEF_NUM_HOST; static int scsi_debug_delay = DEF_DELAY; static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB; static int scsi_debug_every_nth = DEF_EVERY_NTH; static int scsi_debug_max_luns = DEF_MAX_LUNS; +static int scsi_debug_max_queue = SCSI_DEBUG_CANQUEUE; static int scsi_debug_num_parts = DEF_NUM_PARTS; +static int scsi_debug_no_uld = 0; static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */ static int scsi_debug_opts = DEF_OPTS; static int scsi_debug_scsi_level = DEF_SCSI_LEVEL; @@ -169,6 +176,7 @@ static int scsi_debug_guard = DEF_GUARD; static int scsi_debug_ato = DEF_ATO; static int scsi_debug_physblk_exp = DEF_PHYSBLK_EXP; static int scsi_debug_lowest_aligned = DEF_LOWEST_ALIGNED; +static int scsi_debug_opt_blks = DEF_OPT_BLKS; static int scsi_debug_unmap_max_desc = DEF_UNMAP_MAX_DESC; static int scsi_debug_unmap_max_blocks = DEF_UNMAP_MAX_BLOCKS; static int scsi_debug_unmap_granularity = DEF_UNMAP_GRANULARITY; @@ -192,7 +200,6 @@ static int sdebug_sectors_per; /* sectors per cylinder */ #define SDEBUG_SENSE_LEN 32 -#define SCSI_DEBUG_CANQUEUE 255 #define SCSI_DEBUG_MAX_CMD_LEN 32 struct sdebug_dev_info { @@ -699,9 +706,13 @@ static int inquiry_evpd_b0(unsigned char * arr) unsigned int gran; memcpy(arr, vpdb0_data, sizeof(vpdb0_data)); + + /* Optimal transfer length granularity */ gran = 1 << scsi_debug_physblk_exp; arr[2] = (gran >> 8) & 0xff; arr[3] = gran & 0xff; + + /* Maximum Transfer Length */ if (sdebug_store_sectors > 0x400) { arr[4] = (sdebug_store_sectors >> 24) & 0xff; arr[5] = (sdebug_store_sectors >> 16) & 0xff; @@ -709,6 +720,9 @@ static int inquiry_evpd_b0(unsigned char * arr) arr[7] = sdebug_store_sectors & 0xff; } + /* Optimal Transfer Length */ + put_unaligned_be32(scsi_debug_opt_blks, &arr[8]); + if (scsi_debug_unmap_max_desc) { unsigned int blocks; @@ -717,15 +731,20 @@ static int inquiry_evpd_b0(unsigned char * arr) else blocks = 0xffffffff; + /* Maximum Unmap LBA Count */ put_unaligned_be32(blocks, &arr[16]); + + /* Maximum Unmap Block Descriptor Count */ put_unaligned_be32(scsi_debug_unmap_max_desc, &arr[20]); } + /* Unmap Granularity Alignment */ if (scsi_debug_unmap_alignment) { put_unaligned_be32(scsi_debug_unmap_alignment, &arr[28]); arr[28] |= 0x80; /* UGAVALID */ } + /* Optimal Unmap Granularity */ if (scsi_debug_unmap_granularity) { put_unaligned_be32(scsi_debug_unmap_granularity, &arr[24]); return 0x3c; /* Mandatory page length for thin provisioning */ @@ -2266,7 +2285,7 @@ static void timer_intr_handler(unsigned long indx) struct sdebug_queued_cmd * sqcp; unsigned long iflags; - if (indx >= SCSI_DEBUG_CANQUEUE) { + if (indx >= scsi_debug_max_queue) { printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too " "large\n"); return; @@ -2380,6 +2399,8 @@ static int scsi_debug_slave_configure(struct scsi_device *sdp) scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING, sdp->host->cmd_per_lun); blk_queue_max_segment_size(sdp->request_queue, 256 * 1024); + if (scsi_debug_no_uld) + sdp->no_uld_attach = 1; return 0; } @@ -2406,7 +2427,7 @@ static int stop_queued_cmnd(struct scsi_cmnd *cmnd) struct sdebug_queued_cmd *sqcp; spin_lock_irqsave(&queued_arr_lock, iflags); - for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) { + for (k = 0; k < scsi_debug_max_queue; ++k) { sqcp = &queued_arr[k]; if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) { del_timer_sync(&sqcp->cmnd_timer); @@ -2416,7 +2437,7 @@ static int stop_queued_cmnd(struct scsi_cmnd *cmnd) } } spin_unlock_irqrestore(&queued_arr_lock, iflags); - return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0; + return (k < scsi_debug_max_queue) ? 1 : 0; } /* Deletes (stops) timers of all queued commands */ @@ -2427,7 +2448,7 @@ static void stop_all_queued(void) struct sdebug_queued_cmd *sqcp; spin_lock_irqsave(&queued_arr_lock, iflags); - for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) { + for (k = 0; k < scsi_debug_max_queue; ++k) { sqcp = &queued_arr[k]; if (sqcp->in_use && sqcp->a_cmnd) { del_timer_sync(&sqcp->cmnd_timer); @@ -2533,7 +2554,7 @@ static void __init init_all_queued(void) struct sdebug_queued_cmd * sqcp; spin_lock_irqsave(&queued_arr_lock, iflags); - for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) { + for (k = 0; k < scsi_debug_max_queue; ++k) { sqcp = &queued_arr[k]; init_timer(&sqcp->cmnd_timer); sqcp->in_use = 0; @@ -2625,12 +2646,12 @@ static int schedule_resp(struct scsi_cmnd * cmnd, struct sdebug_queued_cmd * sqcp = NULL; spin_lock_irqsave(&queued_arr_lock, iflags); - for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) { + for (k = 0; k < scsi_debug_max_queue; ++k) { sqcp = &queued_arr[k]; if (! sqcp->in_use) break; } - if (k >= SCSI_DEBUG_CANQUEUE) { + if (k >= scsi_debug_max_queue) { spin_unlock_irqrestore(&queued_arr_lock, iflags); printk(KERN_WARNING "scsi_debug: can_queue exceeded\n"); return 1; /* report busy to mid level */ @@ -2662,7 +2683,9 @@ module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR); module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR); module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR); module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR); +module_param_named(max_queue, scsi_debug_max_queue, int, S_IRUGO | S_IWUSR); module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR); +module_param_named(no_uld, scsi_debug_no_uld, int, S_IRUGO); module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO); module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR); module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR); @@ -2677,6 +2700,7 @@ module_param_named(dif, scsi_debug_dif, int, S_IRUGO); module_param_named(guard, scsi_debug_guard, int, S_IRUGO); module_param_named(ato, scsi_debug_ato, int, S_IRUGO); module_param_named(physblk_exp, scsi_debug_physblk_exp, int, S_IRUGO); +module_param_named(opt_blks, scsi_debug_opt_blks, int, S_IRUGO); module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO); module_param_named(unmap_max_blocks, scsi_debug_unmap_max_blocks, int, S_IRUGO); module_param_named(unmap_max_desc, scsi_debug_unmap_max_desc, int, S_IRUGO); @@ -2695,7 +2719,9 @@ MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)"); MODULE_PARM_DESC(every_nth, "timeout every nth command(def=0)"); MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)"); MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)"); +MODULE_PARM_DESC(max_queue, "max number of queued commands (1 to 255(def))"); MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)"); +MODULE_PARM_DESC(no_uld, "stop ULD (e.g. sd driver) attaching (def=0))"); MODULE_PARM_DESC(num_parts, "number of partitions(def=0)"); MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)"); MODULE_PARM_DESC(opts, "1->noise, 2->medium_err, 4->timeout, 8->recovered_err... (def=0)"); @@ -2705,6 +2731,7 @@ MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)") MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)"); MODULE_PARM_DESC(sector_size, "logical block size in bytes (def=512)"); MODULE_PARM_DESC(physblk_exp, "physical block exponent (def=0)"); +MODULE_PARM_DESC(opt_blks, "optimal transfer length in block (def=64)"); MODULE_PARM_DESC(lowest_aligned, "lowest aligned lba (def=0)"); MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)"); MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)"); @@ -2970,6 +2997,31 @@ static ssize_t sdebug_max_luns_store(struct device_driver * ddp, DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show, sdebug_max_luns_store); +static ssize_t sdebug_max_queue_show(struct device_driver * ddp, char * buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_queue); +} +static ssize_t sdebug_max_queue_store(struct device_driver * ddp, + const char * buf, size_t count) +{ + int n; + + if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n > 0) && + (n <= SCSI_DEBUG_CANQUEUE)) { + scsi_debug_max_queue = n; + return count; + } + return -EINVAL; +} +DRIVER_ATTR(max_queue, S_IRUGO | S_IWUSR, sdebug_max_queue_show, + sdebug_max_queue_store); + +static ssize_t sdebug_no_uld_show(struct device_driver * ddp, char * buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_uld); +} +DRIVER_ATTR(no_uld, S_IRUGO, sdebug_no_uld_show, NULL); + static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf) { return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level); @@ -3107,7 +3159,9 @@ static int do_create_driverfs_files(void) ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns); + ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_queue); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0); + ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_uld); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts); ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype); @@ -3139,7 +3193,9 @@ static void do_remove_driverfs_files(void) driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts); + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_uld); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0); + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_queue); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw); driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth); @@ -3830,12 +3886,13 @@ static int sdebug_driver_probe(struct device * dev) sdbg_host = to_sdebug_host(dev); - hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host)); - if (NULL == hpnt) { - printk(KERN_ERR "%s: scsi_register failed\n", __func__); - error = -ENODEV; + sdebug_driver_template.can_queue = scsi_debug_max_queue; + hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host)); + if (NULL == hpnt) { + printk(KERN_ERR "%s: scsi_register failed\n", __func__); + error = -ENODEV; return error; - } + } sdbg_host->shost = hpnt; *((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host; diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 7ad53fa4276..a5d630f5f51 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -39,6 +39,8 @@ #include "scsi_logging.h" #include "scsi_transport_api.h" +#include <trace/events/scsi.h> + #define SENSE_TIMEOUT (10*HZ) /* @@ -52,6 +54,7 @@ void scsi_eh_wakeup(struct Scsi_Host *shost) { if (shost->host_busy == shost->host_failed) { + trace_scsi_eh_wakeup(shost); wake_up_process(shost->ehandler); SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread\n")); @@ -127,6 +130,7 @@ enum blk_eh_timer_return scsi_times_out(struct request *req) struct scsi_cmnd *scmd = req->special; enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED; + trace_scsi_dispatch_cmd_timeout(scmd); scsi_log_completion(scmd, TIMEOUT_ERROR); if (scmd->device->host->transportt->eh_timed_out) @@ -970,9 +974,10 @@ static int scsi_eh_abort_cmds(struct list_head *work_q, "0x%p\n", current->comm, scmd)); rtn = scsi_try_to_abort_cmd(scmd); - if (rtn == SUCCESS) { + if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { scmd->eh_eflags &= ~SCSI_EH_CANCEL_CMD; if (!scsi_device_online(scmd->device) || + rtn == FAST_IO_FAIL || !scsi_eh_tur(scmd)) { scsi_eh_finish_cmd(scmd, done_q); } @@ -1099,8 +1104,9 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost, " 0x%p\n", current->comm, sdev)); rtn = scsi_try_bus_device_reset(bdr_scmd); - if (rtn == SUCCESS) { + if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { if (!scsi_device_online(sdev) || + rtn == FAST_IO_FAIL || !scsi_eh_tur(bdr_scmd)) { list_for_each_entry_safe(scmd, next, work_q, eh_entry) { @@ -1163,10 +1169,11 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost, "to target %d\n", current->comm, id)); rtn = scsi_try_target_reset(tgtr_scmd); - if (rtn == SUCCESS) { + if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { list_for_each_entry_safe(scmd, next, work_q, eh_entry) { if (id == scmd_id(scmd)) if (!scsi_device_online(scmd->device) || + rtn == FAST_IO_FAIL || !scsi_eh_tur(tgtr_scmd)) scsi_eh_finish_cmd(scmd, done_q); @@ -1222,10 +1229,11 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost, " %d\n", current->comm, channel)); rtn = scsi_try_bus_reset(chan_scmd); - if (rtn == SUCCESS) { + if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { list_for_each_entry_safe(scmd, next, work_q, eh_entry) { if (channel == scmd_channel(scmd)) if (!scsi_device_online(scmd->device) || + rtn == FAST_IO_FAIL || !scsi_eh_tur(scmd)) scsi_eh_finish_cmd(scmd, done_q); @@ -1259,9 +1267,10 @@ static int scsi_eh_host_reset(struct list_head *work_q, , current->comm)); rtn = scsi_try_host_reset(scmd); - if (rtn == SUCCESS) { + if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { list_for_each_entry_safe(scmd, next, work_q, eh_entry) { if (!scsi_device_online(scmd->device) || + rtn == FAST_IO_FAIL || (!scsi_eh_try_stu(scmd) && !scsi_eh_tur(scmd)) || !scsi_eh_tur(scmd)) scsi_eh_finish_cmd(scmd, done_q); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 38518b08807..c992ecf4e37 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -459,8 +459,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, found_target->reap_ref++; spin_unlock_irqrestore(shost->host_lock, flags); if (found_target->state != STARGET_DEL) { - put_device(parent); - kfree(starget); + put_device(dev); return found_target; } /* Unfortunately, we found a dying target; need to diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 429c9b73e3e..c23ab978c3b 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -474,7 +474,7 @@ static DEVICE_ATTR(field, S_IRUGO, sdev_show_##field, NULL); /* - * sdev_rd_attr: create a function and attribute variable for a + * sdev_rw_attr: create a function and attribute variable for a * read/write field. */ #define sdev_rw_attr(field, format_string) \ @@ -486,7 +486,7 @@ sdev_store_##field (struct device *dev, struct device_attribute *attr, \ { \ struct scsi_device *sdev; \ sdev = to_scsi_device(dev); \ - snscanf (buf, 20, format_string, &sdev->field); \ + sscanf (buf, format_string, &sdev->field); \ return count; \ } \ static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, sdev_show_##field, sdev_store_##field); @@ -853,9 +853,6 @@ static int scsi_target_add(struct scsi_target *starget) error = device_add(&starget->dev); if (error) { dev_err(&starget->dev, "target device_add failed, error %d\n", error); - get_device(&starget->dev); - scsi_target_reap(starget); - put_device(&starget->dev); return error; } transport_add_device(&starget->dev); diff --git a/drivers/scsi/scsi_trace.c b/drivers/scsi/scsi_trace.c new file mode 100644 index 00000000000..b587289cfac --- /dev/null +++ b/drivers/scsi/scsi_trace.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2010 FUJITSU LIMITED + * Copyright (C) 2010 Tomohiro Kusumi <kusumi.tomohiro@jp.fujitsu.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include <linux/kernel.h> +#include <linux/trace_seq.h> +#include <trace/events/scsi.h> + +#define SERVICE_ACTION16(cdb) (cdb[1] & 0x1f) +#define SERVICE_ACTION32(cdb) ((cdb[8] << 8) | cdb[9]) + +static const char * +scsi_trace_misc(struct trace_seq *, unsigned char *, int); + +static const char * +scsi_trace_rw6(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= ((cdb[1] & 0x1F) << 16); + lba |= (cdb[2] << 8); + lba |= cdb[3]; + txlen = cdb[4]; + + trace_seq_printf(p, "lba=%llu txlen=%llu", + (unsigned long long)lba, (unsigned long long)txlen); + trace_seq_putc(p, 0); + + return ret; +} + +static const char * +scsi_trace_rw10(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= (cdb[2] << 24); + lba |= (cdb[3] << 16); + lba |= (cdb[4] << 8); + lba |= cdb[5]; + txlen |= (cdb[7] << 8); + txlen |= cdb[8]; + + trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", + (unsigned long long)lba, (unsigned long long)txlen, + cdb[1] >> 5); + trace_seq_putc(p, 0); + + return ret; +} + +static const char * +scsi_trace_rw12(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= (cdb[2] << 24); + lba |= (cdb[3] << 16); + lba |= (cdb[4] << 8); + lba |= cdb[5]; + txlen |= (cdb[6] << 24); + txlen |= (cdb[7] << 16); + txlen |= (cdb[8] << 8); + txlen |= cdb[9]; + + trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", + (unsigned long long)lba, (unsigned long long)txlen, + cdb[1] >> 5); + trace_seq_putc(p, 0); + + return ret; +} + +static const char * +scsi_trace_rw16(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= ((u64)cdb[2] << 56); + lba |= ((u64)cdb[3] << 48); + lba |= ((u64)cdb[4] << 40); + lba |= ((u64)cdb[5] << 32); + lba |= (cdb[6] << 24); + lba |= (cdb[7] << 16); + lba |= (cdb[8] << 8); + lba |= cdb[9]; + txlen |= (cdb[10] << 24); + txlen |= (cdb[11] << 16); + txlen |= (cdb[12] << 8); + txlen |= cdb[13]; + + trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", + (unsigned long long)lba, (unsigned long long)txlen, + cdb[1] >> 5); + + if (cdb[0] == WRITE_SAME_16) + trace_seq_printf(p, " unmap=%u", cdb[1] >> 3 & 1); + + trace_seq_putc(p, 0); + + return ret; +} + +static const char * +scsi_trace_rw32(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len, *cmd; + sector_t lba = 0, txlen = 0; + u32 ei_lbrt = 0; + + switch (SERVICE_ACTION32(cdb)) { + case READ_32: + cmd = "READ"; + break; + case VERIFY_32: + cmd = "VERIFY"; + break; + case WRITE_32: + cmd = "WRITE"; + break; + case WRITE_SAME_32: + cmd = "WRITE_SAME"; + break; + default: + trace_seq_printf(p, "UNKNOWN"); + goto out; + } + + lba |= ((u64)cdb[12] << 56); + lba |= ((u64)cdb[13] << 48); + lba |= ((u64)cdb[14] << 40); + lba |= ((u64)cdb[15] << 32); + lba |= (cdb[16] << 24); + lba |= (cdb[17] << 16); + lba |= (cdb[18] << 8); + lba |= cdb[19]; + ei_lbrt |= (cdb[20] << 24); + ei_lbrt |= (cdb[21] << 16); + ei_lbrt |= (cdb[22] << 8); + ei_lbrt |= cdb[23]; + txlen |= (cdb[28] << 24); + txlen |= (cdb[29] << 16); + txlen |= (cdb[30] << 8); + txlen |= cdb[31]; + + trace_seq_printf(p, "%s_32 lba=%llu txlen=%llu protect=%u ei_lbrt=%u", + cmd, (unsigned long long)lba, + (unsigned long long)txlen, cdb[10] >> 5, ei_lbrt); + + if (SERVICE_ACTION32(cdb) == WRITE_SAME_32) + trace_seq_printf(p, " unmap=%u", cdb[10] >> 3 & 1); + +out: + trace_seq_putc(p, 0); + + return ret; +} + +static const char * +scsi_trace_unmap(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + unsigned int regions = cdb[7] << 8 | cdb[8]; + + trace_seq_printf(p, "regions=%u", (regions - 8) / 16); + trace_seq_putc(p, 0); + + return ret; +} + +static const char * +scsi_trace_service_action_in(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len, *cmd; + sector_t lba = 0; + u32 alloc_len = 0; + + switch (SERVICE_ACTION16(cdb)) { + case SAI_READ_CAPACITY_16: + cmd = "READ_CAPACITY_16"; + break; + case SAI_GET_LBA_STATUS: + cmd = "GET_LBA_STATUS"; + break; + default: + trace_seq_printf(p, "UNKNOWN"); + goto out; + } + + lba |= ((u64)cdb[2] << 56); + lba |= ((u64)cdb[3] << 48); + lba |= ((u64)cdb[4] << 40); + lba |= ((u64)cdb[5] << 32); + lba |= (cdb[6] << 24); + lba |= (cdb[7] << 16); + lba |= (cdb[8] << 8); + lba |= cdb[9]; + alloc_len |= (cdb[10] << 24); + alloc_len |= (cdb[11] << 16); + alloc_len |= (cdb[12] << 8); + alloc_len |= cdb[13]; + + trace_seq_printf(p, "%s lba=%llu alloc_len=%u", cmd, + (unsigned long long)lba, alloc_len); + +out: + trace_seq_putc(p, 0); + + return ret; +} + +static const char * +scsi_trace_varlen(struct trace_seq *p, unsigned char *cdb, int len) +{ + switch (SERVICE_ACTION32(cdb)) { + case READ_32: + case VERIFY_32: + case WRITE_32: + case WRITE_SAME_32: + return scsi_trace_rw32(p, cdb, len); + default: + return scsi_trace_misc(p, cdb, len); + } +} + +static const char * +scsi_trace_misc(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + + trace_seq_printf(p, "-"); + trace_seq_putc(p, 0); + + return ret; +} + +const char * +scsi_trace_parse_cdb(struct trace_seq *p, unsigned char *cdb, int len) +{ + switch (cdb[0]) { + case READ_6: + case WRITE_6: + return scsi_trace_rw6(p, cdb, len); + case READ_10: + case VERIFY: + case WRITE_10: + case WRITE_SAME: + return scsi_trace_rw10(p, cdb, len); + case READ_12: + case VERIFY_12: + case WRITE_12: + return scsi_trace_rw12(p, cdb, len); + case READ_16: + case VERIFY_16: + case WRITE_16: + case WRITE_SAME_16: + return scsi_trace_rw16(p, cdb, len); + case UNMAP: + return scsi_trace_unmap(p, cdb, len); + case SERVICE_ACTION_IN: + return scsi_trace_service_action_in(p, cdb, len); + case VARIABLE_LENGTH_CMD: + return scsi_trace_varlen(p, cdb, len); + default: + return scsi_trace_misc(p, cdb, len); + } +} diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 6cfffc88022..06813789145 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -834,7 +834,7 @@ static ssize_t store_fc_rport_dev_loss_tmo(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int val; + unsigned long val; struct fc_rport *rport = transport_class_to_rport(dev); struct Scsi_Host *shost = rport_to_shost(rport); struct fc_internal *i = to_fc_internal(shost->transportt); @@ -848,6 +848,12 @@ store_fc_rport_dev_loss_tmo(struct device *dev, struct device_attribute *attr, return -EINVAL; /* + * Check for overflow; dev_loss_tmo is u32 + */ + if (val > UINT_MAX) + return -EINVAL; + + /* * If fast_io_fail is off we have to cap * dev_loss_tmo at SCSI_DEVICE_BLOCK_MAX_TIMEOUT */ @@ -2865,7 +2871,7 @@ void fc_remote_port_delete(struct fc_rport *rport) { struct Scsi_Host *shost = rport_to_shost(rport); - int timeout = rport->dev_loss_tmo; + unsigned long timeout = rport->dev_loss_tmo; unsigned long flags; /* @@ -3191,23 +3197,33 @@ fc_scsi_scan_rport(struct work_struct *work) * * This routine can be called from a FC LLD scsi_eh callback. It * blocks the scsi_eh thread until the fc_rport leaves the - * FC_PORTSTATE_BLOCKED. This is necessary to avoid the scsi_eh - * failing recovery actions for blocked rports which would lead to - * offlined SCSI devices. + * FC_PORTSTATE_BLOCKED, or the fast_io_fail_tmo fires. This is + * necessary to avoid the scsi_eh failing recovery actions for blocked + * rports which would lead to offlined SCSI devices. + * + * Returns: 0 if the fc_rport left the state FC_PORTSTATE_BLOCKED. + * FAST_IO_FAIL if the fast_io_fail_tmo fired, this should be + * passed back to scsi_eh. */ -void fc_block_scsi_eh(struct scsi_cmnd *cmnd) +int fc_block_scsi_eh(struct scsi_cmnd *cmnd) { struct Scsi_Host *shost = cmnd->device->host; struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); - while (rport->port_state == FC_PORTSTATE_BLOCKED) { + while (rport->port_state == FC_PORTSTATE_BLOCKED && + !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)) { spin_unlock_irqrestore(shost->host_lock, flags); msleep(1000); spin_lock_irqsave(shost->host_lock, flags); } spin_unlock_irqrestore(shost->host_lock, flags); + + if (rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT) + return FAST_IO_FAIL; + + return 0; } EXPORT_SYMBOL(fc_block_scsi_eh); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index de6c60320f6..829cc37abc4 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1434,6 +1434,8 @@ static void read_capacity_error(struct scsi_disk *sdkp, struct scsi_device *sdp, #error RC16_LEN must not be more than SD_BUF_SIZE #endif +#define READ_CAPACITY_RETRIES_ON_RESET 10 + static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, unsigned char *buffer) { @@ -1441,7 +1443,7 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, struct scsi_sense_hdr sshdr; int sense_valid = 0; int the_result; - int retries = 3; + int retries = 3, reset_retries = READ_CAPACITY_RETRIES_ON_RESET; unsigned int alignment; unsigned long long lba; unsigned sector_size; @@ -1470,6 +1472,13 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, * Invalid Field in CDB, just retry * silently with RC10 */ return -EINVAL; + if (sense_valid && + sshdr.sense_key == UNIT_ATTENTION && + sshdr.asc == 0x29 && sshdr.ascq == 0x00) + /* Device reset might occur several times, + * give it one more chance */ + if (--reset_retries > 0) + continue; } retries--; @@ -1528,7 +1537,7 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp, struct scsi_sense_hdr sshdr; int sense_valid = 0; int the_result; - int retries = 3; + int retries = 3, reset_retries = READ_CAPACITY_RETRIES_ON_RESET; sector_t lba; unsigned sector_size; @@ -1544,8 +1553,16 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp, if (media_not_present(sdkp, &sshdr)) return -ENODEV; - if (the_result) + if (the_result) { sense_valid = scsi_sense_valid(&sshdr); + if (sense_valid && + sshdr.sense_key == UNIT_ATTENTION && + sshdr.asc == 0x29 && sshdr.ascq == 0x00) + /* Device reset might occur several times, + * give it one more chance */ + if (--reset_retries > 0) + continue; + } retries--; } while (the_result && retries); @@ -1574,6 +1591,8 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp, static int sd_try_rc16_first(struct scsi_device *sdp) { + if (sdp->host->max_cmd_len < 16) + return 0; if (sdp->scsi_level > SCSI_SPC_2) return 1; if (scsi_device_protection(sdp)) diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 5fda881c247..b701bf2cc18 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -2224,14 +2224,8 @@ wd33c93_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off } -void -wd33c93_release(void) -{ -} - EXPORT_SYMBOL(wd33c93_host_reset); EXPORT_SYMBOL(wd33c93_init); -EXPORT_SYMBOL(wd33c93_release); EXPORT_SYMBOL(wd33c93_abort); EXPORT_SYMBOL(wd33c93_queuecommand); EXPORT_SYMBOL(wd33c93_intr); diff --git a/drivers/scsi/wd33c93.h b/drivers/scsi/wd33c93.h index 00123f2383d..1ed5f3bf388 100644 --- a/drivers/scsi/wd33c93.h +++ b/drivers/scsi/wd33c93.h @@ -348,6 +348,5 @@ int wd33c93_queuecommand (struct scsi_cmnd *cmd, void wd33c93_intr (struct Scsi_Host *instance); int wd33c93_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int); int wd33c93_host_reset (struct scsi_cmnd *); -void wd33c93_release(void); #endif /* WD33C93_H */ diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 302836a8069..8b23165bc5d 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1423,8 +1423,8 @@ config SERIAL_SC26XX_CONSOLE Support for Console on SC2681/SC2692 serial ports. config SERIAL_BFIN_SPORT - tristate "Blackfin SPORT emulate UART (EXPERIMENTAL)" - depends on BLACKFIN && EXPERIMENTAL + tristate "Blackfin SPORT emulate UART" + depends on BLACKFIN select SERIAL_CORE help Enable SPORT emulate UART on Blackfin series. @@ -1439,28 +1439,52 @@ config SERIAL_BFIN_SPORT_CONSOLE config SERIAL_BFIN_SPORT0_UART bool "Enable UART over SPORT0" - depends on SERIAL_BFIN_SPORT && !(BF542 || BF542M || BF544 || BF544M) + depends on SERIAL_BFIN_SPORT && !(BF542 || BF544) help Enable UART over SPORT0 +config SERIAL_BFIN_SPORT0_UART_CTSRTS + bool "Enable UART over SPORT0 hardware flow control" + depends on SERIAL_BFIN_SPORT0_UART + help + Enable hardware flow control in the driver. + config SERIAL_BFIN_SPORT1_UART bool "Enable UART over SPORT1" depends on SERIAL_BFIN_SPORT help Enable UART over SPORT1 +config SERIAL_BFIN_SPORT1_UART_CTSRTS + bool "Enable UART over SPORT1 hardware flow control" + depends on SERIAL_BFIN_SPORT1_UART + help + Enable hardware flow control in the driver. + config SERIAL_BFIN_SPORT2_UART bool "Enable UART over SPORT2" depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) help Enable UART over SPORT2 +config SERIAL_BFIN_SPORT2_UART_CTSRTS + bool "Enable UART over SPORT2 hardware flow control" + depends on SERIAL_BFIN_SPORT2_UART + help + Enable hardware flow control in the driver. + config SERIAL_BFIN_SPORT3_UART bool "Enable UART over SPORT3" depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) help Enable UART over SPORT3 +config SERIAL_BFIN_SPORT3_UART_CTSRTS + bool "Enable UART over SPORT3 hardware flow control" + depends on SERIAL_BFIN_SPORT3_UART + help + Enable hardware flow control in the driver. + config SERIAL_TIMBERDALE tristate "Support for timberdale UART" select SERIAL_CORE @@ -1499,4 +1523,56 @@ config SERIAL_GRLIB_GAISLER_APBUART_CONSOLE help Support for running a console on the GRLIB APBUART +config SERIAL_ALTERA_JTAGUART + tristate "Altera JTAG UART support" + select SERIAL_CORE + help + This driver supports the Altera JTAG UART port. + +config SERIAL_ALTERA_JTAGUART_CONSOLE + bool "Altera JTAG UART console support" + depends on SERIAL_ALTERA_JTAGUART=y + select SERIAL_CORE_CONSOLE + help + Enable a Altera JTAG UART port to be the system console. + +config SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS + bool "Bypass output when no connection" + depends on SERIAL_ALTERA_JTAGUART_CONSOLE + select SERIAL_CORE_CONSOLE + help + Bypass console output and keep going even if there is no + JTAG terminal connection with the host. + +config SERIAL_ALTERA_UART + tristate "Altera UART support" + select SERIAL_CORE + help + This driver supports the Altera softcore UART port. + +config SERIAL_ALTERA_UART_MAXPORTS + int "Maximum number of Altera UART ports" + depends on SERIAL_ALTERA_UART + default 4 + help + This setting lets you define the maximum number of the Altera + UART ports. The usual default varies from board to board, and + this setting is a way of catering for that. + +config SERIAL_ALTERA_UART_BAUDRATE + int "Default baudrate for Altera UART ports" + depends on SERIAL_ALTERA_UART + default 115200 + help + This setting lets you define what the default baudrate is for the + Altera UART ports. The usual default varies from board to board, + and this setting is a way of catering for that. + +config SERIAL_ALTERA_UART_CONSOLE + bool "Altera UART console support" + depends on SERIAL_ALTERA_UART=y + select SERIAL_CORE_CONSOLE + help + Enable a Altera UART port to be the system console. + endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 328f107346c..208a85572c3 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -82,3 +82,5 @@ obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o +obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o +obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o diff --git a/drivers/serial/altera_jtaguart.c b/drivers/serial/altera_jtaguart.c new file mode 100644 index 00000000000..f9b49b5ff5e --- /dev/null +++ b/drivers/serial/altera_jtaguart.c @@ -0,0 +1,504 @@ +/* + * altera_jtaguart.c -- Altera JTAG UART driver + * + * Based on mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com> + * (C) Copyright 2008, Thomas Chou <thomas@wytron.com.tw> + * (C) Copyright 2010, Tobias Klauser <tklauser@distanz.ch> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/altera_jtaguart.h> + +#define DRV_NAME "altera_jtaguart" + +/* + * Altera JTAG UART register definitions according to the Altera JTAG UART + * datasheet: http://www.altera.com/literature/hb/nios2/n2cpu_nii51009.pdf + */ + +#define ALTERA_JTAGUART_SIZE 8 + +#define ALTERA_JTAGUART_DATA_REG 0 + +#define ALTERA_JTAGUART_DATA_DATA_MSK 0x000000FF +#define ALTERA_JTAGUART_DATA_RVALID_MSK 0x00008000 +#define ALTERA_JTAGUART_DATA_RAVAIL_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_DATA_RAVAIL_OFF 16 + +#define ALTERA_JTAGUART_CONTROL_REG 4 + +#define ALTERA_JTAGUART_CONTROL_RE_MSK 0x00000001 +#define ALTERA_JTAGUART_CONTROL_WE_MSK 0x00000002 +#define ALTERA_JTAGUART_CONTROL_RI_MSK 0x00000100 +#define ALTERA_JTAGUART_CONTROL_RI_OFF 8 +#define ALTERA_JTAGUART_CONTROL_WI_MSK 0x00000200 +#define ALTERA_JTAGUART_CONTROL_AC_MSK 0x00000400 +#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_CONTROL_WSPACE_OFF 16 + +/* + * Local per-uart structure. + */ +struct altera_jtaguart { + struct uart_port port; + unsigned int sigs; /* Local copy of line sigs */ + unsigned long imr; /* Local IMR mirror */ +}; + +static unsigned int altera_jtaguart_tx_empty(struct uart_port *port) +{ + return (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) ? TIOCSER_TEMT : 0; +} + +static unsigned int altera_jtaguart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void altera_jtaguart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ +} + +static void altera_jtaguart_start_tx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr |= ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_stop_tx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_stop_rx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr &= ~ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_break_ctl(struct uart_port *port, int break_state) +{ +} + +static void altera_jtaguart_enable_ms(struct uart_port *port) +{ +} + +static void altera_jtaguart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + /* Just copy the old termios settings back */ + if (old) + tty_termios_copy_hw(termios, old); +} + +static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + unsigned long status; + + while ((status = readl(port->membase + ALTERA_JTAGUART_DATA_REG)) & + ALTERA_JTAGUART_DATA_RVALID_MSK) { + ch = status & ALTERA_JTAGUART_DATA_DATA_MSK; + flag = TTY_NORMAL; + port->icount.rx++; + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, 0, 0, ch, flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned int pending, count; + + if (port->x_char) { + /* Send special char - probably flow control */ + writel(port->x_char, port->membase + ALTERA_JTAGUART_DATA_REG); + port->x_char = 0; + port->icount.tx++; + return; + } + + pending = uart_circ_chars_pending(xmit); + if (pending > 0) { + count = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) >> + ALTERA_JTAGUART_CONTROL_WSPACE_OFF; + if (count > pending) + count = pending; + if (count > 0) { + pending -= count; + while (count--) { + writel(xmit->buf[xmit->tail], + port->membase + ALTERA_JTAGUART_DATA_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + if (pending < WAKEUP_CHARS) + uart_write_wakeup(port); + } + } + + if (pending == 0) { + pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + } +} + +static irqreturn_t altera_jtaguart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned int isr; + + isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >> + ALTERA_JTAGUART_CONTROL_RI_OFF) & pp->imr; + + spin_lock(&port->lock); + + if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK) + altera_jtaguart_rx_chars(pp); + if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK) + altera_jtaguart_tx_chars(pp); + + spin_unlock(&port->lock); + + return IRQ_RETVAL(isr); +} + +static void altera_jtaguart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ALTERA_JTAGUART; + + /* Clear mask, so no surprise interrupts. */ + writel(0, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static int altera_jtaguart_startup(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned long flags; + int ret; + + ret = request_irq(port->irq, altera_jtaguart_interrupt, IRQF_DISABLED, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void altera_jtaguart_shutdown(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +static const char *altera_jtaguart_type(struct uart_port *port) +{ + return (port->type == PORT_ALTERA_JTAGUART) ? "Altera JTAG UART" : NULL; +} + +static int altera_jtaguart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void altera_jtaguart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int altera_jtaguart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != PORT_ALTERA_JTAGUART) + return -EINVAL; + return 0; +} + +/* + * Define the basic serial functions we support. + */ +static struct uart_ops altera_jtaguart_ops = { + .tx_empty = altera_jtaguart_tx_empty, + .get_mctrl = altera_jtaguart_get_mctrl, + .set_mctrl = altera_jtaguart_set_mctrl, + .start_tx = altera_jtaguart_start_tx, + .stop_tx = altera_jtaguart_stop_tx, + .stop_rx = altera_jtaguart_stop_rx, + .enable_ms = altera_jtaguart_enable_ms, + .break_ctl = altera_jtaguart_break_ctl, + .startup = altera_jtaguart_startup, + .shutdown = altera_jtaguart_shutdown, + .set_termios = altera_jtaguart_set_termios, + .type = altera_jtaguart_type, + .request_port = altera_jtaguart_request_port, + .release_port = altera_jtaguart_release_port, + .config_port = altera_jtaguart_config_port, + .verify_port = altera_jtaguart_verify_port, +}; + +#define ALTERA_JTAGUART_MAXPORTS 1 +static struct altera_jtaguart altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS]; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) + +int __init early_altera_jtaguart_setup(struct altera_jtaguart_platform_uart + *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_jtaguart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_JTAGUART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->flags = ASYNC_BOOT_AUTOCONF; + port->ops = &altera_jtaguart_ops; + } + + return 0; +} + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) +static void altera_jtaguart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; + unsigned long status; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while (((status = readl(port->membase + ALTERA_JTAGUART_CONTROL_REG)) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { + if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) { + spin_unlock_irqrestore(&port->lock, flags); + return; /* no connection activity */ + } + spin_unlock_irqrestore(&port->lock, flags); + cpu_relax(); + spin_lock_irqsave(&port->lock, flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); + spin_unlock_irqrestore(&port->lock, flags); +} +#else +static void altera_jtaguart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while ((readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { + spin_unlock_irqrestore(&port->lock, flags); + cpu_relax(); + spin_lock_irqsave(&port->lock, flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); + spin_unlock_irqrestore(&port->lock, flags); +} +#endif + +static void altera_jtaguart_console_write(struct console *co, const char *s, + unsigned int count) +{ + for (; count; count--, s++) { + altera_jtaguart_console_putc(co, *s); + if (*s == '\n') + altera_jtaguart_console_putc(co, '\r'); + } +} + +static int __init altera_jtaguart_console_setup(struct console *co, + char *options) +{ + struct uart_port *port; + + if (co->index < 0 || co->index >= ALTERA_JTAGUART_MAXPORTS) + return -EINVAL; + port = &altera_jtaguart_ports[co->index].port; + if (port->membase == 0) + return -ENODEV; + return 0; +} + +static struct uart_driver altera_jtaguart_driver; + +static struct console altera_jtaguart_console = { + .name = "ttyJ", + .write = altera_jtaguart_console_write, + .device = uart_console_device, + .setup = altera_jtaguart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &altera_jtaguart_driver, +}; + +static int __init altera_jtaguart_console_init(void) +{ + register_console(&altera_jtaguart_console); + return 0; +} + +console_initcall(altera_jtaguart_console_init); + +#define ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console) + +#else + +#define ALTERA_JTAGUART_CONSOLE NULL + +#endif /* CONFIG_ALTERA_JTAGUART_CONSOLE */ + +static struct uart_driver altera_jtaguart_driver = { + .owner = THIS_MODULE, + .driver_name = "altera_jtaguart", + .dev_name = "ttyJ", + .major = ALTERA_JTAGUART_MAJOR, + .minor = ALTERA_JTAGUART_MINOR, + .nr = ALTERA_JTAGUART_MAXPORTS, + .cons = ALTERA_JTAGUART_CONSOLE, +}; + +static int __devinit altera_jtaguart_probe(struct platform_device *pdev) +{ + struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_jtaguart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_JTAGUART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->ops = &altera_jtaguart_ops; + port->flags = ASYNC_BOOT_AUTOCONF; + + uart_add_one_port(&altera_jtaguart_driver, port); + } + + return 0; +} + +static int __devexit altera_jtaguart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS; i++) { + port = &altera_jtaguart_ports[i].port; + if (port) + uart_remove_one_port(&altera_jtaguart_driver, port); + } + + return 0; +} + +static struct platform_driver altera_jtaguart_platform_driver = { + .probe = altera_jtaguart_probe, + .remove = __devexit_p(altera_jtaguart_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init altera_jtaguart_init(void) +{ + int rc; + + rc = uart_register_driver(&altera_jtaguart_driver); + if (rc) + return rc; + rc = platform_driver_register(&altera_jtaguart_platform_driver); + if (rc) { + uart_unregister_driver(&altera_jtaguart_driver); + return rc; + } + return 0; +} + +static void __exit altera_jtaguart_exit(void) +{ + platform_driver_unregister(&altera_jtaguart_platform_driver); + uart_unregister_driver(&altera_jtaguart_driver); +} + +module_init(altera_jtaguart_init); +module_exit(altera_jtaguart_exit); + +MODULE_DESCRIPTION("Altera JTAG UART driver"); +MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/serial/altera_uart.c b/drivers/serial/altera_uart.c new file mode 100644 index 00000000000..bcee156d2f2 --- /dev/null +++ b/drivers/serial/altera_uart.c @@ -0,0 +1,570 @@ +/* + * altera_uart.c -- Altera UART driver + * + * Based on mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com> + * (C) Copyright 2008, Thomas Chou <thomas@wytron.com.tw> + * (C) Copyright 2010, Tobias Klauser <tklauser@distanz.ch> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/altera_uart.h> + +#define DRV_NAME "altera_uart" + +/* + * Altera UART register definitions according to the Nios UART datasheet: + * http://www.altera.com/literature/ds/ds_nios_uart.pdf + */ + +#define ALTERA_UART_SIZE 32 + +#define ALTERA_UART_RXDATA_REG 0 +#define ALTERA_UART_TXDATA_REG 4 +#define ALTERA_UART_STATUS_REG 8 +#define ALTERA_UART_CONTROL_REG 12 +#define ALTERA_UART_DIVISOR_REG 16 +#define ALTERA_UART_EOP_REG 20 + +#define ALTERA_UART_STATUS_PE_MSK 0x0001 /* parity error */ +#define ALTERA_UART_STATUS_FE_MSK 0x0002 /* framing error */ +#define ALTERA_UART_STATUS_BRK_MSK 0x0004 /* break */ +#define ALTERA_UART_STATUS_ROE_MSK 0x0008 /* RX overrun error */ +#define ALTERA_UART_STATUS_TOE_MSK 0x0010 /* TX overrun error */ +#define ALTERA_UART_STATUS_TMT_MSK 0x0020 /* TX shift register state */ +#define ALTERA_UART_STATUS_TRDY_MSK 0x0040 /* TX ready */ +#define ALTERA_UART_STATUS_RRDY_MSK 0x0080 /* RX ready */ +#define ALTERA_UART_STATUS_E_MSK 0x0100 /* exception condition */ +#define ALTERA_UART_STATUS_DCTS_MSK 0x0400 /* CTS logic-level change */ +#define ALTERA_UART_STATUS_CTS_MSK 0x0800 /* CTS logic state */ +#define ALTERA_UART_STATUS_EOP_MSK 0x1000 /* EOP written/read */ + + /* Enable interrupt on... */ +#define ALTERA_UART_CONTROL_PE_MSK 0x0001 /* ...parity error */ +#define ALTERA_UART_CONTROL_FE_MSK 0x0002 /* ...framing error */ +#define ALTERA_UART_CONTROL_BRK_MSK 0x0004 /* ...break */ +#define ALTERA_UART_CONTROL_ROE_MSK 0x0008 /* ...RX overrun */ +#define ALTERA_UART_CONTROL_TOE_MSK 0x0010 /* ...TX overrun */ +#define ALTERA_UART_CONTROL_TMT_MSK 0x0020 /* ...TX shift register empty */ +#define ALTERA_UART_CONTROL_TRDY_MSK 0x0040 /* ...TX ready */ +#define ALTERA_UART_CONTROL_RRDY_MSK 0x0080 /* ...RX ready */ +#define ALTERA_UART_CONTROL_E_MSK 0x0100 /* ...exception*/ + +#define ALTERA_UART_CONTROL_TRBK_MSK 0x0200 /* TX break */ +#define ALTERA_UART_CONTROL_DCTS_MSK 0x0400 /* Interrupt on CTS change */ +#define ALTERA_UART_CONTROL_RTS_MSK 0x0800 /* RTS signal */ +#define ALTERA_UART_CONTROL_EOP_MSK 0x1000 /* Interrupt on EOP */ + +/* + * Local per-uart structure. + */ +struct altera_uart { + struct uart_port port; + unsigned int sigs; /* Local copy of line sigs */ + unsigned short imr; /* Local IMR mirror */ +}; + +static unsigned int altera_uart_tx_empty(struct uart_port *port) +{ + return (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TMT_MSK) ? TIOCSER_TEMT : 0; +} + +static unsigned int altera_uart_get_mctrl(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + unsigned int sigs; + + spin_lock_irqsave(&port->lock, flags); + sigs = + (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_CTS_MSK) ? TIOCM_CTS : 0; + sigs |= (pp->sigs & TIOCM_RTS); + spin_unlock_irqrestore(&port->lock, flags); + + return sigs; +} + +static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->sigs = sigs; + if (sigs & TIOCM_RTS) + pp->imr |= ALTERA_UART_CONTROL_RTS_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_start_tx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_stop_tx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_stop_rx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_break_ctl(struct uart_port *port, int break_state) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (break_state == -1) + pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_enable_ms(struct uart_port *port) +{ +} + +static void altera_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, baudclk; + + baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + baudclk = port->uartclk / baud; + + if (old) + tty_termios_copy_hw(termios, old); + tty_termios_encode_baud_rate(termios, baud, baud); + + spin_lock_irqsave(&port->lock, flags); + writel(baudclk, port->membase + ALTERA_UART_DIVISOR_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_rx_chars(struct altera_uart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + unsigned short status; + + while ((status = readl(port->membase + ALTERA_UART_STATUS_REG)) & + ALTERA_UART_STATUS_RRDY_MSK) { + ch = readl(port->membase + ALTERA_UART_RXDATA_REG); + flag = TTY_NORMAL; + port->icount.rx++; + + if (status & ALTERA_UART_STATUS_E_MSK) { + writel(status, port->membase + ALTERA_UART_STATUS_REG); + + if (status & ALTERA_UART_STATUS_BRK_MSK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (status & ALTERA_UART_STATUS_PE_MSK) { + port->icount.parity++; + } else if (status & ALTERA_UART_STATUS_ROE_MSK) { + port->icount.overrun++; + } else if (status & ALTERA_UART_STATUS_FE_MSK) { + port->icount.frame++; + } + + status &= port->read_status_mask; + + if (status & ALTERA_UART_STATUS_BRK_MSK) + flag = TTY_BREAK; + else if (status & ALTERA_UART_STATUS_PE_MSK) + flag = TTY_PARITY; + else if (status & ALTERA_UART_STATUS_FE_MSK) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, status, ALTERA_UART_STATUS_ROE_MSK, ch, + flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +static void altera_uart_tx_chars(struct altera_uart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + /* Send special char - probably flow control */ + writel(port->x_char, port->membase + ALTERA_UART_TXDATA_REG); + port->x_char = 0; + port->icount.tx++; + return; + } + + while (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) { + if (xmit->head == xmit->tail) + break; + writel(xmit->buf[xmit->tail], + port->membase + ALTERA_UART_TXDATA_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (xmit->head == xmit->tail) { + pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + } +} + +static irqreturn_t altera_uart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned int isr; + + isr = readl(port->membase + ALTERA_UART_STATUS_REG) & pp->imr; + if (isr & ALTERA_UART_STATUS_RRDY_MSK) + altera_uart_rx_chars(pp); + if (isr & ALTERA_UART_STATUS_TRDY_MSK) + altera_uart_tx_chars(pp); + return IRQ_RETVAL(isr); +} + +static void altera_uart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ALTERA_UART; + + /* Clear mask, so no surprise interrupts. */ + writel(0, port->membase + ALTERA_UART_CONTROL_REG); + /* Clear status register */ + writel(0, port->membase + ALTERA_UART_STATUS_REG); +} + +static int altera_uart_startup(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + int ret; + + ret = request_irq(port->irq, altera_uart_interrupt, IRQF_DISABLED, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_UART_CONTROL_RRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void altera_uart_shutdown(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +static const char *altera_uart_type(struct uart_port *port) +{ + return (port->type == PORT_ALTERA_UART) ? "Altera UART" : NULL; +} + +static int altera_uart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void altera_uart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int altera_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_ALTERA_UART)) + return -EINVAL; + return 0; +} + +/* + * Define the basic serial functions we support. + */ +static struct uart_ops altera_uart_ops = { + .tx_empty = altera_uart_tx_empty, + .get_mctrl = altera_uart_get_mctrl, + .set_mctrl = altera_uart_set_mctrl, + .start_tx = altera_uart_start_tx, + .stop_tx = altera_uart_stop_tx, + .stop_rx = altera_uart_stop_rx, + .enable_ms = altera_uart_enable_ms, + .break_ctl = altera_uart_break_ctl, + .startup = altera_uart_startup, + .shutdown = altera_uart_shutdown, + .set_termios = altera_uart_set_termios, + .type = altera_uart_type, + .request_port = altera_uart_request_port, + .release_port = altera_uart_release_port, + .config_port = altera_uart_config_port, + .verify_port = altera_uart_verify_port, +}; + +static struct altera_uart altera_uart_ports[CONFIG_SERIAL_ALTERA_UART_MAXPORTS]; + +#if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + +int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_uart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_UART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->uartclk = platp[i].uartclk; + port->flags = ASYNC_BOOT_AUTOCONF; + port->ops = &altera_uart_ops; + } + + return 0; +} + +static void altera_uart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_uart_ports + co->index)->port; + int i; + + for (i = 0; i < 0x10000; i++) { + if (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) + break; + } + writel(c, port->membase + ALTERA_UART_TXDATA_REG); + for (i = 0; i < 0x10000; i++) { + if (readl(port->membase + ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) + break; + } +} + +static void altera_uart_console_write(struct console *co, const char *s, + unsigned int count) +{ + for (; count; count--, s++) { + altera_uart_console_putc(co, *s); + if (*s == '\n') + altera_uart_console_putc(co, '\r'); + } +} + +static int __init altera_uart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) + return -EINVAL; + port = &altera_uart_ports[co->index].port; + if (port->membase == 0) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver altera_uart_driver; + +static struct console altera_uart_console = { + .name = "ttyS", + .write = altera_uart_console_write, + .device = uart_console_device, + .setup = altera_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &altera_uart_driver, +}; + +static int __init altera_uart_console_init(void) +{ + register_console(&altera_uart_console); + return 0; +} + +console_initcall(altera_uart_console_init); + +#define ALTERA_UART_CONSOLE (&altera_uart_console) + +#else + +#define ALTERA_UART_CONSOLE NULL + +#endif /* CONFIG_ALTERA_UART_CONSOLE */ + +/* + * Define the altera_uart UART driver structure. + */ +static struct uart_driver altera_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = CONFIG_SERIAL_ALTERA_UART_MAXPORTS, + .cons = ALTERA_UART_CONSOLE, +}; + +static int __devinit altera_uart_probe(struct platform_device *pdev) +{ + struct altera_uart_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_uart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_UART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->uartclk = platp[i].uartclk; + port->ops = &altera_uart_ops; + port->flags = ASYNC_BOOT_AUTOCONF; + + uart_add_one_port(&altera_uart_driver, port); + } + + return 0; +} + +static int altera_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS; i++) { + port = &altera_uart_ports[i].port; + if (port) + uart_remove_one_port(&altera_uart_driver, port); + } + + return 0; +} + +static struct platform_driver altera_uart_platform_driver = { + .probe = altera_uart_probe, + .remove = __devexit_p(altera_uart_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = NULL, + }, +}; + +static int __init altera_uart_init(void) +{ + int rc; + + rc = uart_register_driver(&altera_uart_driver); + if (rc) + return rc; + rc = platform_driver_register(&altera_uart_platform_driver); + if (rc) { + uart_unregister_driver(&altera_uart_driver); + return rc; + } + return 0; +} + +static void __exit altera_uart_exit(void) +{ + platform_driver_unregister(&altera_uart_platform_driver); + uart_unregister_driver(&altera_uart_driver); +} + +module_init(altera_uart_init); +module_exit(altera_uart_exit); + +MODULE_DESCRIPTION("Altera UART driver"); +MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index c88f8ad3ff8..e57fb3d228e 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -34,32 +34,12 @@ #include <linux/tty_flip.h> #include <linux/serial_core.h> +#include <asm/bfin_sport.h> #include <asm/delay.h> #include <asm/portmux.h> #include "bfin_sport_uart.h" -#ifdef CONFIG_SERIAL_BFIN_SPORT0_UART -unsigned short bfin_uart_pin_req_sport0[] = - {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ - P_SPORT0_DRPRI, P_SPORT0_RSCLK, P_SPORT0_DRSEC, P_SPORT0_DTSEC, 0}; -#endif -#ifdef CONFIG_SERIAL_BFIN_SPORT1_UART -unsigned short bfin_uart_pin_req_sport1[] = - {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ - P_SPORT1_DRPRI, P_SPORT1_RSCLK, P_SPORT1_DRSEC, P_SPORT1_DTSEC, 0}; -#endif -#ifdef CONFIG_SERIAL_BFIN_SPORT2_UART -unsigned short bfin_uart_pin_req_sport2[] = - {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, P_SPORT2_RFS, \ - P_SPORT2_DRPRI, P_SPORT2_RSCLK, P_SPORT2_DRSEC, P_SPORT2_DTSEC, 0}; -#endif -#ifdef CONFIG_SERIAL_BFIN_SPORT3_UART -unsigned short bfin_uart_pin_req_sport3[] = - {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, P_SPORT3_RFS, \ - P_SPORT3_DRPRI, P_SPORT3_RSCLK, P_SPORT3_DRSEC, P_SPORT3_DTSEC, 0}; -#endif - struct sport_uart_port { struct uart_port port; int err_irq; @@ -69,9 +49,13 @@ struct sport_uart_port { unsigned short txmask2; unsigned char stopb; /* unsigned char parib; */ +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + int cts_pin; + int rts_pin; +#endif }; -static void sport_uart_tx_chars(struct sport_uart_port *up); +static int sport_uart_tx_chars(struct sport_uart_port *up); static void sport_stop_tx(struct uart_port *port); static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value) @@ -219,6 +203,59 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) return IRQ_HANDLED; } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + if (up->cts_pin < 0) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + + /* CTS PIN is negative assertive. */ + if (SPORT_UART_GET_CTS(up)) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + else + return TIOCM_DSR | TIOCM_CAR; +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + if (up->rts_pin < 0) + return; + + /* RTS PIN is negative assertive. */ + if (mctrl & TIOCM_RTS) + SPORT_UART_ENABLE_RTS(up); + else + SPORT_UART_DISABLE_RTS(up); +} + +/* + * Handle any change of modem status signal. + */ +static irqreturn_t sport_mctrl_cts_int(int irq, void *dev_id) +{ + struct sport_uart_port *up = (struct sport_uart_port *)dev_id; + unsigned int status; + + status = sport_get_mctrl(&up->port); + uart_handle_cts_change(&up->port, status & TIOCM_CTS); + + return IRQ_HANDLED; +} +#else +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + pr_debug("%s enter\n", __func__); + return TIOCM_CTS | TIOCM_CD | TIOCM_DSR; +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + pr_debug("%s enter\n", __func__); +} +#endif + /* Reqeust IRQ, Setup clock */ static int sport_startup(struct uart_port *port) { @@ -247,6 +284,21 @@ static int sport_startup(struct uart_port *port) goto fail2; } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + if (up->cts_pin >= 0) { + if (request_irq(gpio_to_irq(up->cts_pin), + sport_mctrl_cts_int, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_DISABLED, "BFIN_SPORT_UART_CTS", up)) { + up->cts_pin = -1; + dev_info(port->dev, "Unable to attach BlackFin UART \ + over SPORT CTS interrupt. So, disable it.\n"); + } + } + if (up->rts_pin >= 0) + gpio_direction_output(up->rts_pin, 0); +#endif + return 0; fail2: free_irq(up->port.irq+1, up); @@ -256,23 +308,35 @@ static int sport_startup(struct uart_port *port) return ret; } -static void sport_uart_tx_chars(struct sport_uart_port *up) +/* + * sport_uart_tx_chars + * + * ret 1 means need to enable sport. + * ret 0 means do nothing. + */ +static int sport_uart_tx_chars(struct sport_uart_port *up) { struct circ_buf *xmit = &up->port.state->xmit; if (SPORT_GET_STAT(up) & TXF) - return; + return 0; if (up->port.x_char) { tx_one_byte(up, up->port.x_char); up->port.icount.tx++; up->port.x_char = 0; - return; + return 1; } if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - sport_stop_tx(&up->port); - return; + /* The waiting loop to stop SPORT TX from TX interrupt is + * too long. This may block SPORT RX interrupts and cause + * RX FIFO overflow. So, do stop sport TX only after the last + * char in TX FIFO is moved into the shift register. + */ + if (SPORT_GET_STAT(up) & TXHRE) + sport_stop_tx(&up->port); + return 0; } while(!(SPORT_GET_STAT(up) & TXF) && !uart_circ_empty(xmit)) { @@ -283,6 +347,8 @@ static void sport_uart_tx_chars(struct sport_uart_port *up) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&up->port); + + return 1; } static unsigned int sport_tx_empty(struct uart_port *port) @@ -298,23 +364,15 @@ static unsigned int sport_tx_empty(struct uart_port *port) return 0; } -static unsigned int sport_get_mctrl(struct uart_port *port) -{ - pr_debug("%s enter\n", __func__); - return (TIOCM_CTS | TIOCM_CD | TIOCM_DSR); -} - -static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - pr_debug("%s enter\n", __func__); -} - static void sport_stop_tx(struct uart_port *port) { struct sport_uart_port *up = (struct sport_uart_port *)port; pr_debug("%s enter\n", __func__); + if (!(SPORT_GET_TCR1(up) & TSPEN)) + return; + /* Although the hold register is empty, last byte is still in shift * register and not sent out yet. So, put a dummy data into TX FIFO. * Then, sport tx stops when last byte is shift out and the dummy @@ -337,11 +395,12 @@ static void sport_start_tx(struct uart_port *port) pr_debug("%s enter\n", __func__); /* Write data into SPORT FIFO before enable SPROT to transmit */ - sport_uart_tx_chars(up); + if (sport_uart_tx_chars(up)) { + /* Enable transmit, then an interrupt will generated */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + } - /* Enable transmit, then an interrupt will generated */ - SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); - SSYNC(); pr_debug("%s exit\n", __func__); } @@ -379,6 +438,10 @@ static void sport_shutdown(struct uart_port *port) free_irq(up->port.irq, up); free_irq(up->port.irq+1, up); free_irq(up->err_irq, up); +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + if (up->cts_pin >= 0) + free_irq(gpio_to_irq(up->cts_pin), up); +#endif } static const char *sport_type(struct uart_port *port) @@ -448,27 +511,14 @@ static void sport_set_termios(struct uart_port *port, /* up->parib = 1; */ } - port->read_status_mask = OE; - if (termios->c_iflag & INPCK) - port->read_status_mask |= (FE | PE); - if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= BI; + spin_lock_irqsave(&up->port.lock, flags); + + port->read_status_mask = 0; /* * Characters to ignore */ port->ignore_status_mask = 0; - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= FE | PE; - if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= BI; - /* - * If we're ignoring parity and break indicators, - * ignore overruns too (for real raw support). - */ - if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= OE; - } /* RX extract mask */ up->rxmask = 0x01 | (((up->csize + up->stopb) * 2 - 1) << 0x8); @@ -488,8 +538,6 @@ static void sport_set_termios(struct uart_port *port, /* uart baud rate */ port->uartclk = uart_get_baud_rate(port, termios, old, 0, get_sclk()/16); - spin_lock_irqsave(&up->port.lock, flags); - /* Disable UART */ SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); @@ -542,6 +590,8 @@ struct uart_ops sport_uart_ops = { static struct sport_uart_port *bfin_sport_uart_ports[BFIN_SPORT_UART_MAX_PORTS]; #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE +#define CLASS_BFIN_SPORT_CONSOLE "bfin-sport-console" + static int __init sport_uart_console_setup(struct console *co, char *options) { @@ -549,7 +599,11 @@ sport_uart_console_setup(struct console *co, char *options) int baud = 57600; int bits = 8; int parity = 'n'; +# ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + int flow = 'r'; +# else int flow = 'n'; +# endif /* Check whether an invalid uart number has been specified */ if (co->index < 0 || co->index >= BFIN_SPORT_UART_MAX_PORTS) @@ -690,11 +744,11 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) if (bfin_sport_uart_ports[pdev->id] == NULL) { bfin_sport_uart_ports[pdev->id] = - kmalloc(sizeof(struct sport_uart_port), GFP_KERNEL); + kzalloc(sizeof(struct sport_uart_port), GFP_KERNEL); sport = bfin_sport_uart_ports[pdev->id]; if (!sport) { dev_err(&pdev->dev, - "Fail to kmalloc sport_uart_port\n"); + "Fail to malloc sport_uart_port\n"); return -ENOMEM; } @@ -720,13 +774,13 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) goto out_error_free_peripherals; } - sport->port.membase = ioremap(res->start, - res->end - res->start); + sport->port.membase = ioremap(res->start, resource_size(res)); if (!sport->port.membase) { dev_err(&pdev->dev, "Cannot map sport IO\n"); ret = -ENXIO; goto out_error_free_peripherals; } + sport->port.mapbase = res->start; sport->port.irq = platform_get_irq(pdev, 0); if (sport->port.irq < 0) { @@ -741,6 +795,22 @@ static int __devinit sport_uart_probe(struct platform_device *pdev) ret = -ENOENT; goto out_error_unmap; } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) + sport->cts_pin = -1; + else + sport->cts_pin = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (res == NULL) + sport->rts_pin = -1; + else + sport->rts_pin = res->start; + + if (sport->rts_pin >= 0) + gpio_request(sport->rts_pin, DRV_NAME); +#endif } #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE @@ -779,6 +849,10 @@ static int __devexit sport_uart_remove(struct platform_device *pdev) if (sport) { uart_remove_one_port(&sport_uart_reg, &sport->port); +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (sport->rts_pin >= 0) + gpio_free(sport->rts_pin); +#endif iounmap(sport->port.membase); peripheral_free_list( (unsigned short *)pdev->dev.platform_data); @@ -802,7 +876,7 @@ static struct platform_driver sport_uart_driver = { #ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE static __initdata struct early_platform_driver early_sport_uart_driver = { - .class_str = DRV_NAME, + .class_str = CLASS_BFIN_SPORT_CONSOLE, .pdrv = &sport_uart_driver, .requested_id = EARLY_PLATFORM_ID_UNSET, }; @@ -811,7 +885,8 @@ static int __init sport_uart_rs_console_init(void) { early_platform_driver_register(&early_sport_uart_driver, DRV_NAME); - early_platform_driver_probe(DRV_NAME, BFIN_SPORT_UART_MAX_PORTS, 0); + early_platform_driver_probe(CLASS_BFIN_SPORT_CONSOLE, + BFIN_SPORT_UART_MAX_PORTS, 0); register_console(&sport_uart_console); @@ -824,7 +899,7 @@ static int __init sport_uart_init(void) { int ret; - pr_info("Serial: Blackfin uart over sport driver\n"); + pr_info("Blackfin uart over sport driver\n"); ret = uart_register_driver(&sport_uart_reg); if (ret) { diff --git a/drivers/serial/bfin_sport_uart.h b/drivers/serial/bfin_sport_uart.h index abe03614e4d..9ce253e381d 100644 --- a/drivers/serial/bfin_sport_uart.h +++ b/drivers/serial/bfin_sport_uart.h @@ -37,7 +37,21 @@ #define SPORT_GET_TFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TFSDIV)) #define SPORT_GET_TX(sport) bfin_read16(((sport)->port.membase + OFFSET_TX)) #define SPORT_GET_RX(sport) bfin_read16(((sport)->port.membase + OFFSET_RX)) -#define SPORT_GET_RX32(sport) bfin_read32(((sport)->port.membase + OFFSET_RX)) +/* + * If another interrupt fires while doing a 32-bit read from RX FIFO, + * a fake RX underflow error will be generated. So disable interrupts + * to prevent interruption while reading the FIFO. + */ +#define SPORT_GET_RX32(sport) \ +({ \ + unsigned int __ret; \ + if (ANOMALY_05000473) \ + local_irq_disable(); \ + __ret = bfin_read32((sport)->port.membase + OFFSET_RX); \ + if (ANOMALY_05000473) \ + local_irq_enable(); \ + __ret; \ +}) #define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1)) #define SPORT_GET_RCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR2)) #define SPORT_GET_RCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RCLKDIV)) @@ -58,4 +72,15 @@ #define SPORT_TX_FIFO_SIZE 8 +#define SPORT_UART_GET_CTS(x) gpio_get_value(x->cts_pin) +#define SPORT_UART_DISABLE_RTS(x) gpio_set_value(x->rts_pin, 1) +#define SPORT_UART_ENABLE_RTS(x) gpio_set_value(x->rts_pin, 0) + +#if defined(CONFIG_SERIAL_BFIN_SPORT0_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT1_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT2_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT3_UART_CTSRTS) +# define CONFIG_SERIAL_BFIN_SPORT_CTSRTS +#endif + #endif /* _BFIN_SPORT_UART_H */ diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c index 786ba85c170..67ca642713b 100644 --- a/drivers/serial/timbuart.c +++ b/drivers/serial/timbuart.c @@ -68,12 +68,22 @@ static void timbuart_start_tx(struct uart_port *port) tasklet_schedule(&uart->tasklet); } +static unsigned int timbuart_tx_empty(struct uart_port *port) +{ + u32 isr = ioread32(port->membase + TIMBUART_ISR); + + return (isr & TXBE) ? TIOCSER_TEMT : 0; +} + static void timbuart_flush_buffer(struct uart_port *port) { - u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | TIMBUART_CTRL_FLSHTX; + if (!timbuart_tx_empty(port)) { + u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | + TIMBUART_CTRL_FLSHTX; - iowrite8(ctl, port->membase + TIMBUART_CTRL); - iowrite32(TXBF, port->membase + TIMBUART_ISR); + iowrite8(ctl, port->membase + TIMBUART_CTRL); + iowrite32(TXBF, port->membase + TIMBUART_ISR); + } } static void timbuart_rx_chars(struct uart_port *port) @@ -195,13 +205,6 @@ void timbuart_tasklet(unsigned long arg) dev_dbg(uart->port.dev, "%s leaving\n", __func__); } -static unsigned int timbuart_tx_empty(struct uart_port *port) -{ - u32 isr = ioread32(port->membase + TIMBUART_ISR); - - return (isr & TXBE) ? TIOCSER_TEMT : 0; -} - static unsigned int timbuart_get_mctrl(struct uart_port *port) { u8 cts = ioread8(port->membase + TIMBUART_CTRL); @@ -220,7 +223,7 @@ static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) if (mctrl & TIOCM_RTS) iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); else - iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); + iowrite8(0, port->membase + TIMBUART_CTRL); } static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier) diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index f0a6c61b17f..e6639a95d27 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -86,7 +86,7 @@ static int ulite_receive(struct uart_port *port, int stat) /* stats */ if (stat & ULITE_STATUS_RXVALID) { port->icount.rx++; - ch = readb(port->membase + ULITE_RX); + ch = ioread32be(port->membase + ULITE_RX); if (stat & ULITE_STATUS_PARITY) port->icount.parity++; @@ -131,7 +131,7 @@ static int ulite_transmit(struct uart_port *port, int stat) return 0; if (port->x_char) { - writeb(port->x_char, port->membase + ULITE_TX); + iowrite32be(port->x_char, port->membase + ULITE_TX); port->x_char = 0; port->icount.tx++; return 1; @@ -140,7 +140,7 @@ static int ulite_transmit(struct uart_port *port, int stat) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return 0; - writeb(xmit->buf[xmit->tail], port->membase + ULITE_TX); + iowrite32be(xmit->buf[xmit->tail], port->membase + ULITE_TX); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); port->icount.tx++; @@ -157,7 +157,7 @@ static irqreturn_t ulite_isr(int irq, void *dev_id) int busy, n = 0; do { - int stat = readb(port->membase + ULITE_STATUS); + int stat = ioread32be(port->membase + ULITE_STATUS); busy = ulite_receive(port, stat); busy |= ulite_transmit(port, stat); n++; @@ -178,7 +178,7 @@ static unsigned int ulite_tx_empty(struct uart_port *port) unsigned int ret; spin_lock_irqsave(&port->lock, flags); - ret = readb(port->membase + ULITE_STATUS); + ret = ioread32be(port->membase + ULITE_STATUS); spin_unlock_irqrestore(&port->lock, flags); return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; @@ -201,7 +201,7 @@ static void ulite_stop_tx(struct uart_port *port) static void ulite_start_tx(struct uart_port *port) { - ulite_transmit(port, readb(port->membase + ULITE_STATUS)); + ulite_transmit(port, ioread32be(port->membase + ULITE_STATUS)); } static void ulite_stop_rx(struct uart_port *port) @@ -230,17 +230,17 @@ static int ulite_startup(struct uart_port *port) if (ret) return ret; - writeb(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, + iowrite32be(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, port->membase + ULITE_CONTROL); - writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); return 0; } static void ulite_shutdown(struct uart_port *port) { - writeb(0, port->membase + ULITE_CONTROL); - readb(port->membase + ULITE_CONTROL); /* dummy */ + iowrite32be(0, port->membase + ULITE_CONTROL); + ioread32be(port->membase + ULITE_CONTROL); /* dummy */ free_irq(port->irq, port); } @@ -352,7 +352,7 @@ static void ulite_console_wait_tx(struct uart_port *port) /* Spin waiting for TX fifo to have space available */ for (i = 0; i < 100000; i++) { - val = readb(port->membase + ULITE_STATUS); + val = ioread32be(port->membase + ULITE_STATUS); if ((val & ULITE_STATUS_TXFULL) == 0) break; cpu_relax(); @@ -362,7 +362,7 @@ static void ulite_console_wait_tx(struct uart_port *port) static void ulite_console_putchar(struct uart_port *port, int ch) { ulite_console_wait_tx(port); - writeb(ch, port->membase + ULITE_TX); + iowrite32be(ch, port->membase + ULITE_TX); } static void ulite_console_write(struct console *co, const char *s, @@ -379,8 +379,8 @@ static void ulite_console_write(struct console *co, const char *s, spin_lock_irqsave(&port->lock, flags); /* save and disable interrupt */ - ier = readb(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; - writeb(0, port->membase + ULITE_CONTROL); + ier = ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; + iowrite32be(0, port->membase + ULITE_CONTROL); uart_console_write(port, s, count, ulite_console_putchar); @@ -388,7 +388,7 @@ static void ulite_console_write(struct console *co, const char *s, /* restore interrupt state */ if (ier) - writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); if (locked) spin_unlock_irqrestore(&port->lock, flags); @@ -601,7 +601,7 @@ ulite_of_probe(struct of_device *op, const struct of_device_id *match) id = of_get_property(op->node, "port-number", NULL); - return ulite_assign(&op->dev, id ? *id : -1, res.start+3, irq); + return ulite_assign(&op->dev, id ? *id : -1, res.start, irq); } static int __devexit ulite_of_remove(struct of_device *op) diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c index 1786db2f337..8b274b70825 100644 --- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c @@ -48,6 +48,7 @@ Devices: [Quatech] DAQP-208 (daqp), DAQP-308 */ #include "../comedidev.h" +#include <linux/semaphore.h> #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> diff --git a/drivers/staging/udlfb/udlfb.c b/drivers/staging/udlfb/udlfb.c index aa8195199a2..577f2bf6eb2 100644 --- a/drivers/staging/udlfb/udlfb.c +++ b/drivers/staging/udlfb/udlfb.c @@ -1063,7 +1063,8 @@ static ssize_t metrics_misc_show(struct device *fbdev, atomic_read(&dev->lost_pixels) ? "yes" : "no"); } -static ssize_t edid_show(struct kobject *kobj, struct bin_attribute *a, +static ssize_t edid_show(struct file *filp, struct kobject *kobj, + struct bin_attribute *a, char *buf, loff_t off, size_t count) { struct device *fbdev = container_of(kobj, struct device, kobj); struct fb_info *fb_info = dev_get_drvdata(fbdev); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 06863befaf3..448f5b47fc4 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -658,7 +658,8 @@ const struct attribute_group *usb_device_groups[] = { /* Binary descriptors */ static ssize_t -read_descriptors(struct kobject *kobj, struct bin_attribute *attr, +read_descriptors(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 397b678f1c4..5ae14f6c1e7 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -573,7 +573,7 @@ int usb_lock_device_for_reset(struct usb_device *udev, iface->condition == USB_INTERFACE_UNBOUND)) return -EINTR; - while (usb_trylock_device(udev) != 0) { + while (!usb_trylock_device(udev)) { /* If we can't acquire the lock after waiting one second, * we're probably deadlocked */ diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 6c37e8ee5ef..3c1e13ed1cb 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -2099,7 +2099,7 @@ static ssize_t radeon_show_one_edid(char *buf, loff_t off, size_t count, const u } -static ssize_t radeon_show_edid1(struct kobject *kobj, +static ssize_t radeon_show_edid1(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -2112,7 +2112,7 @@ static ssize_t radeon_show_edid1(struct kobject *kobj, } -static ssize_t radeon_show_edid2(struct kobject *kobj, +static ssize_t radeon_show_edid2(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/w1/slaves/w1_ds2431.c b/drivers/w1/slaves/w1_ds2431.c index 2c6c0cf6a20..84e2410aec1 100644 --- a/drivers/w1/slaves/w1_ds2431.c +++ b/drivers/w1/slaves/w1_ds2431.c @@ -96,7 +96,7 @@ static int w1_f2d_readblock(struct w1_slave *sl, int off, int count, char *buf) return -1; } -static ssize_t w1_f2d_read_bin(struct kobject *kobj, +static ssize_t w1_f2d_read_bin(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -202,7 +202,7 @@ retry: return 0; } -static ssize_t w1_f2d_write_bin(struct kobject *kobj, +static ssize_t w1_f2d_write_bin(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index d2bf32118a9..0f7b8f9c509 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -92,7 +92,7 @@ static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, } #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ -static ssize_t w1_f23_read_bin(struct kobject *kobj, +static ssize_t w1_f23_read_bin(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -206,7 +206,7 @@ static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) return 0; } -static ssize_t w1_f23_write_bin(struct kobject *kobj, +static ssize_t w1_f23_write_bin(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c index 6e153343e11..483d4518091 100644 --- a/drivers/w1/slaves/w1_ds2760.c +++ b/drivers/w1/slaves/w1_ds2760.c @@ -97,7 +97,7 @@ int w1_ds2760_recall_eeprom(struct device *dev, int addr) return w1_ds2760_eeprom_cmd(dev, addr, W1_DS2760_RECALL_DATA); } -static ssize_t w1_ds2760_read_bin(struct kobject *kobj, +static ssize_t w1_ds2760_read_bin(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index ad5897dc449..2839e281cd6 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -120,7 +120,7 @@ static struct device_attribute w1_slave_attr_id = /* Default family */ -static ssize_t w1_default_write(struct kobject *kobj, +static ssize_t w1_default_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { @@ -139,7 +139,7 @@ out_up: return count; } -static ssize_t w1_default_read(struct kobject *kobj, +static ssize_t w1_default_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c index eb924e0a64c..26f7184ef9e 100644 --- a/drivers/zorro/zorro-sysfs.c +++ b/drivers/zorro/zorro-sysfs.c @@ -49,7 +49,7 @@ static ssize_t zorro_show_resource(struct device *dev, struct device_attribute * static DEVICE_ATTR(resource, S_IRUGO, zorro_show_resource, NULL); -static ssize_t zorro_read_config(struct kobject *kobj, +static ssize_t zorro_read_config(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/fs/dlm/lock.c b/fs/dlm/lock.c index 17903b49129..031dbe3a15c 100644 --- a/fs/dlm/lock.c +++ b/fs/dlm/lock.c @@ -733,10 +733,7 @@ static void lkb_add_ordered(struct list_head *new, struct list_head *head, if (lkb->lkb_rqmode < mode) break; - if (!lkb) - list_add_tail(new, head); - else - __list_add(new, lkb->lkb_statequeue.prev, &lkb->lkb_statequeue); + __list_add(new, lkb->lkb_statequeue.prev, &lkb->lkb_statequeue); } /* add/remove lkb to rsb's grant/convert/wait queue */ diff --git a/fs/dlm/user.c b/fs/dlm/user.c index 8b6e73c4743..b6272853130 100644 --- a/fs/dlm/user.c +++ b/fs/dlm/user.c @@ -215,6 +215,7 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode) if (!ast_type) { kref_get(&lkb->lkb_ref); list_add_tail(&lkb->lkb_astqueue, &proc->asts); + lkb->lkb_ast_first = type; wake_up_interruptible(&proc->wait); } if (type == AST_COMP && (ast_type & AST_COMP)) @@ -223,7 +224,6 @@ void dlm_user_add_ast(struct dlm_lkb *lkb, int type, int mode) eol = lkb_is_endoflife(lkb, ua->lksb.sb_status, type); if (eol) { - lkb->lkb_ast_type &= ~AST_BAST; lkb->lkb_flags |= DLM_IFL_ENDOFLIFE; } @@ -706,7 +706,7 @@ static int device_close(struct inode *inode, struct file *file) } static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, - int bmode, char __user *buf, size_t count) + int mode, char __user *buf, size_t count) { #ifdef CONFIG_COMPAT struct dlm_lock_result32 result32; @@ -733,7 +733,7 @@ static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type, if (type == AST_BAST) { result.user_astaddr = ua->bastaddr; result.user_astparam = ua->bastparam; - result.bast_mode = bmode; + result.bast_mode = mode; } else { result.user_astaddr = ua->castaddr; result.user_astparam = ua->castparam; @@ -801,7 +801,9 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, struct dlm_user_proc *proc = file->private_data; struct dlm_lkb *lkb; DECLARE_WAITQUEUE(wait, current); - int error, type=0, bmode=0, removed = 0; + int error = 0, removed; + int ret_type, ret_mode; + int bastmode, castmode, do_bast, do_cast; if (count == sizeof(struct dlm_device_version)) { error = copy_version_to_user(buf, count); @@ -820,6 +822,8 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, #endif return -EINVAL; + try_another: + /* do we really need this? can a read happen after a close? */ if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags)) return -EINVAL; @@ -855,13 +859,55 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue); - if (lkb->lkb_ast_type & AST_COMP) { - lkb->lkb_ast_type &= ~AST_COMP; - type = AST_COMP; - } else if (lkb->lkb_ast_type & AST_BAST) { - lkb->lkb_ast_type &= ~AST_BAST; - type = AST_BAST; - bmode = lkb->lkb_bastmode; + removed = 0; + ret_type = 0; + ret_mode = 0; + do_bast = lkb->lkb_ast_type & AST_BAST; + do_cast = lkb->lkb_ast_type & AST_COMP; + bastmode = lkb->lkb_bastmode; + castmode = lkb->lkb_castmode; + + /* when both are queued figure out which to do first and + switch first so the other goes in the next read */ + + if (do_cast && do_bast) { + if (lkb->lkb_ast_first == AST_COMP) { + ret_type = AST_COMP; + ret_mode = castmode; + lkb->lkb_ast_type &= ~AST_COMP; + lkb->lkb_ast_first = AST_BAST; + } else { + ret_type = AST_BAST; + ret_mode = bastmode; + lkb->lkb_ast_type &= ~AST_BAST; + lkb->lkb_ast_first = AST_COMP; + } + } else { + ret_type = lkb->lkb_ast_first; + ret_mode = (ret_type == AST_COMP) ? castmode : bastmode; + lkb->lkb_ast_type &= ~ret_type; + lkb->lkb_ast_first = 0; + } + + /* if we're doing a bast but the bast is unnecessary, then + switch to do nothing or do a cast if that was needed next */ + + if ((ret_type == AST_BAST) && + dlm_modes_compat(bastmode, lkb->lkb_castmode_done)) { + ret_type = 0; + ret_mode = 0; + + if (do_cast) { + ret_type = AST_COMP; + ret_mode = castmode; + lkb->lkb_ast_type &= ~AST_COMP; + lkb->lkb_ast_first = 0; + } + } + + if (lkb->lkb_ast_first != lkb->lkb_ast_type) { + log_print("device_read %x ast_first %x ast_type %x", + lkb->lkb_id, lkb->lkb_ast_first, lkb->lkb_ast_type); } if (!lkb->lkb_ast_type) { @@ -870,15 +916,29 @@ static ssize_t device_read(struct file *file, char __user *buf, size_t count, } spin_unlock(&proc->asts_spin); - error = copy_result_to_user(lkb->lkb_ua, - test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), - type, bmode, buf, count); + if (ret_type) { + error = copy_result_to_user(lkb->lkb_ua, + test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags), + ret_type, ret_mode, buf, count); + + if (ret_type == AST_COMP) + lkb->lkb_castmode_done = castmode; + if (ret_type == AST_BAST) + lkb->lkb_bastmode_done = bastmode; + } /* removes reference for the proc->asts lists added by dlm_user_add_ast() and may result in the lkb being freed */ + if (removed) dlm_put_lkb(lkb); + /* the bast that was queued was eliminated (see unnecessary above), + leaving nothing to return */ + + if (!ret_type) + goto try_another; + return error; } diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 0c1d0b82dcf..a739a0a4806 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -418,6 +418,7 @@ static int gfs2_jdata_writepages(struct address_space *mapping, static int stuffed_readpage(struct gfs2_inode *ip, struct page *page) { struct buffer_head *dibh; + u64 dsize = i_size_read(&ip->i_inode); void *kaddr; int error; @@ -437,9 +438,10 @@ static int stuffed_readpage(struct gfs2_inode *ip, struct page *page) return error; kaddr = kmap_atomic(page, KM_USER0); - memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), - ip->i_disksize); - memset(kaddr + ip->i_disksize, 0, PAGE_CACHE_SIZE - ip->i_disksize); + if (dsize > (dibh->b_size - sizeof(struct gfs2_dinode))) + dsize = (dibh->b_size - sizeof(struct gfs2_dinode)); + memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), dsize); + memset(kaddr + dsize, 0, PAGE_CACHE_SIZE - dsize); kunmap_atomic(kaddr, KM_USER0); flush_dcache_page(page); brelse(dibh); diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 5e411d5f469..4a48c0f4b40 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -71,11 +71,13 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh, if (!PageUptodate(page)) { void *kaddr = kmap(page); + u64 dsize = i_size_read(inode); + + if (dsize > (dibh->b_size - sizeof(struct gfs2_dinode))) + dsize = dibh->b_size - sizeof(struct gfs2_dinode); - memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), - ip->i_disksize); - memset(kaddr + ip->i_disksize, 0, - PAGE_CACHE_SIZE - ip->i_disksize); + memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), dsize); + memset(kaddr + dsize, 0, PAGE_CACHE_SIZE - dsize); kunmap(page); SetPageUptodate(page); @@ -1038,13 +1040,14 @@ static int trunc_start(struct gfs2_inode *ip, u64 size) goto out; if (gfs2_is_stuffed(ip)) { - ip->i_disksize = size; + u64 dsize = size + sizeof(struct gfs2_inode); ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); - gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + size); + if (dsize > dibh->b_size) + dsize = dibh->b_size; + gfs2_buffer_clear_tail(dibh, dsize); error = 1; - } else { if (size & (u64)(sdp->sd_sb.sb_bsize - 1)) error = gfs2_block_truncate_page(ip->i_inode.i_mapping); diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 25fddc100f1..8295c5b5d4a 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -1475,7 +1475,7 @@ struct inode *gfs2_dir_search(struct inode *dir, const struct qstr *name) inode = gfs2_inode_lookup(dir->i_sb, be16_to_cpu(dent->de_type), be64_to_cpu(dent->de_inum.no_addr), - be64_to_cpu(dent->de_inum.no_formal_ino), 0); + be64_to_cpu(dent->de_inum.no_formal_ino)); brelse(bh); return inode; } diff --git a/fs/gfs2/export.c b/fs/gfs2/export.c index c22c2117483..dfe237a3f8a 100644 --- a/fs/gfs2/export.c +++ b/fs/gfs2/export.c @@ -168,7 +168,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, if (error) goto fail; - inode = gfs2_inode_lookup(sb, DT_UNKNOWN, inum->no_addr, 0, 0); + inode = gfs2_inode_lookup(sb, DT_UNKNOWN, inum->no_addr, 0); if (IS_ERR(inode)) { error = PTR_ERR(inode); goto fail; diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 454d4b4eb36..ddcdbf49353 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -855,6 +855,9 @@ void gfs2_holder_reinit(unsigned int state, unsigned flags, struct gfs2_holder * gh->gh_flags = flags; gh->gh_iflags = 0; gh->gh_ip = (unsigned long)__builtin_return_address(0); + if (gh->gh_owner_pid) + put_pid(gh->gh_owner_pid); + gh->gh_owner_pid = get_pid(task_pid(current)); } /** diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 3aac46f6853..b5d7363b22d 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -439,9 +439,6 @@ struct gfs2_args { struct gfs2_tune { spinlock_t gt_spin; - unsigned int gt_incore_log_blocks; - unsigned int gt_log_flush_secs; - unsigned int gt_logd_secs; unsigned int gt_quota_simul_sync; /* Max quotavals to sync at once */ @@ -462,6 +459,7 @@ enum { SDF_SHUTDOWN = 2, SDF_NOBARRIERS = 3, SDF_NORECOVERY = 4, + SDF_DEMOTE = 5, }; #define GFS2_FSNAME_LEN 256 @@ -618,6 +616,7 @@ struct gfs2_sbd { unsigned int sd_log_commited_databuf; int sd_log_commited_revoke; + atomic_t sd_log_pinned; unsigned int sd_log_num_buf; unsigned int sd_log_num_revoke; unsigned int sd_log_num_rg; @@ -629,15 +628,17 @@ struct gfs2_sbd { struct list_head sd_log_le_databuf; struct list_head sd_log_le_ordered; + atomic_t sd_log_thresh1; + atomic_t sd_log_thresh2; atomic_t sd_log_blks_free; - struct mutex sd_log_reserve_mutex; + wait_queue_head_t sd_log_waitq; + wait_queue_head_t sd_logd_waitq; u64 sd_log_sequence; unsigned int sd_log_head; unsigned int sd_log_tail; int sd_log_idle; - unsigned long sd_log_flush_time; struct rw_semaphore sd_log_flush_lock; atomic_t sd_log_in_flight; wait_queue_head_t sd_log_flush_wait; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index b1bf2694fb2..51d8061fa07 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -158,7 +158,6 @@ void gfs2_set_iop(struct inode *inode) * @sb: The super block * @no_addr: The inode number * @type: The type of the inode - * @skip_freeing: set this not return an inode if it is currently being freed. * * Returns: A VFS inode, or an error */ @@ -166,17 +165,14 @@ void gfs2_set_iop(struct inode *inode) struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type, u64 no_addr, - u64 no_formal_ino, int skip_freeing) + u64 no_formal_ino) { struct inode *inode; struct gfs2_inode *ip; struct gfs2_glock *io_gl; int error; - if (skip_freeing) - inode = gfs2_iget_skip(sb, no_addr); - else - inode = gfs2_iget(sb, no_addr); + inode = gfs2_iget(sb, no_addr); ip = GFS2_I(inode); if (!inode) @@ -234,13 +230,100 @@ fail_glock: fail_iopen: gfs2_glock_put(io_gl); fail_put: - ip->i_gl->gl_object = NULL; + if (inode->i_state & I_NEW) + ip->i_gl->gl_object = NULL; gfs2_glock_put(ip->i_gl); fail: - iget_failed(inode); + if (inode->i_state & I_NEW) + iget_failed(inode); + else + iput(inode); return ERR_PTR(error); } +/** + * gfs2_unlinked_inode_lookup - Lookup an unlinked inode for reclamation + * @sb: The super block + * no_addr: The inode number + * @@inode: A pointer to the inode found, if any + * + * Returns: 0 and *inode if no errors occurred. If an error occurs, + * the resulting *inode may or may not be NULL. + */ + +int gfs2_unlinked_inode_lookup(struct super_block *sb, u64 no_addr, + struct inode **inode) +{ + struct gfs2_sbd *sdp; + struct gfs2_inode *ip; + struct gfs2_glock *io_gl; + int error; + struct gfs2_holder gh; + + *inode = gfs2_iget_skip(sb, no_addr); + + if (!(*inode)) + return -ENOBUFS; + + if (!((*inode)->i_state & I_NEW)) + return -ENOBUFS; + + ip = GFS2_I(*inode); + sdp = GFS2_SB(*inode); + ip->i_no_formal_ino = -1; + + error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &ip->i_gl); + if (unlikely(error)) + goto fail; + ip->i_gl->gl_object = ip; + + error = gfs2_glock_get(sdp, no_addr, &gfs2_iopen_glops, CREATE, &io_gl); + if (unlikely(error)) + goto fail_put; + + set_bit(GIF_INVALID, &ip->i_flags); + error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, LM_FLAG_TRY | GL_EXACT, + &ip->i_iopen_gh); + if (unlikely(error)) { + if (error == GLR_TRYFAILED) + error = 0; + goto fail_iopen; + } + ip->i_iopen_gh.gh_gl->gl_object = ip; + gfs2_glock_put(io_gl); + + (*inode)->i_mode = DT2IF(DT_UNKNOWN); + + /* + * We must read the inode in order to work out its type in + * this case. Note that this doesn't happen often as we normally + * know the type beforehand. This code path only occurs during + * unlinked inode recovery (where it is safe to do this glock, + * which is not true in the general case). + */ + error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, LM_FLAG_TRY, + &gh); + if (unlikely(error)) { + if (error == GLR_TRYFAILED) + error = 0; + goto fail_glock; + } + /* Inode is now uptodate */ + gfs2_glock_dq_uninit(&gh); + gfs2_set_iop(*inode); + + return 0; +fail_glock: + gfs2_glock_dq(&ip->i_iopen_gh); +fail_iopen: + gfs2_glock_put(io_gl); +fail_put: + ip->i_gl->gl_object = NULL; + gfs2_glock_put(ip->i_gl); +fail: + return error; +} + static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) { const struct gfs2_dinode *str = buf; @@ -862,7 +945,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, goto fail_gunlock2; inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr, - inum.no_formal_ino, 0); + inum.no_formal_ino); if (IS_ERR(inode)) goto fail_gunlock2; diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h index c341aaf67ad..e161461d4c5 100644 --- a/fs/gfs2/inode.h +++ b/fs/gfs2/inode.h @@ -83,8 +83,9 @@ static inline void gfs2_inum_out(const struct gfs2_inode *ip, extern void gfs2_set_iop(struct inode *inode); extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, - u64 no_addr, u64 no_formal_ino, - int skip_freeing); + u64 no_addr, u64 no_formal_ino); +extern int gfs2_unlinked_inode_lookup(struct super_block *sb, u64 no_addr, + struct inode **inode); extern struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr); extern int gfs2_inode_refresh(struct gfs2_inode *ip); diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index e5bf4b59d46..b593f0e28f2 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -168,12 +168,11 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl return list_empty(&ai->ai_ail1_list); } -static void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags) +static void gfs2_ail1_start(struct gfs2_sbd *sdp) { struct list_head *head; u64 sync_gen; - struct list_head *first; - struct gfs2_ail *first_ai, *ai, *tmp; + struct gfs2_ail *ai; int done = 0; gfs2_log_lock(sdp); @@ -184,21 +183,9 @@ static void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags) } sync_gen = sdp->sd_ail_sync_gen++; - first = head->prev; - first_ai = list_entry(first, struct gfs2_ail, ai_list); - first_ai->ai_sync_gen = sync_gen; - gfs2_ail1_start_one(sdp, first_ai); /* This may drop log lock */ - - if (flags & DIO_ALL) - first = NULL; - while(!done) { - if (first && (head->prev != first || - gfs2_ail1_empty_one(sdp, first_ai, 0))) - break; - done = 1; - list_for_each_entry_safe_reverse(ai, tmp, head, ai_list) { + list_for_each_entry_reverse(ai, head, ai_list) { if (ai->ai_sync_gen >= sync_gen) continue; ai->ai_sync_gen = sync_gen; @@ -290,58 +277,57 @@ static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail) * flush time, so we ensure that we have just enough free blocks at all * times to avoid running out during a log flush. * + * We no longer flush the log here, instead we wake up logd to do that + * for us. To avoid the thundering herd and to ensure that we deal fairly + * with queued waiters, we use an exclusive wait. This means that when we + * get woken with enough journal space to get our reservation, we need to + * wake the next waiter on the list. + * * Returns: errno */ int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks) { - unsigned int try = 0; unsigned reserved_blks = 6 * (4096 / sdp->sd_vfs->s_blocksize); + unsigned wanted = blks + reserved_blks; + DEFINE_WAIT(wait); + int did_wait = 0; + unsigned int free_blocks; if (gfs2_assert_warn(sdp, blks) || gfs2_assert_warn(sdp, blks <= sdp->sd_jdesc->jd_blocks)) return -EINVAL; - - mutex_lock(&sdp->sd_log_reserve_mutex); - gfs2_log_lock(sdp); - while(atomic_read(&sdp->sd_log_blks_free) <= (blks + reserved_blks)) { - gfs2_log_unlock(sdp); - gfs2_ail1_empty(sdp, 0); - gfs2_log_flush(sdp, NULL); - - if (try++) - gfs2_ail1_start(sdp, 0); - gfs2_log_lock(sdp); +retry: + free_blocks = atomic_read(&sdp->sd_log_blks_free); + if (unlikely(free_blocks <= wanted)) { + do { + prepare_to_wait_exclusive(&sdp->sd_log_waitq, &wait, + TASK_UNINTERRUPTIBLE); + wake_up(&sdp->sd_logd_waitq); + did_wait = 1; + if (atomic_read(&sdp->sd_log_blks_free) <= wanted) + io_schedule(); + free_blocks = atomic_read(&sdp->sd_log_blks_free); + } while(free_blocks <= wanted); + finish_wait(&sdp->sd_log_waitq, &wait); } - atomic_sub(blks, &sdp->sd_log_blks_free); + if (atomic_cmpxchg(&sdp->sd_log_blks_free, free_blocks, + free_blocks - blks) != free_blocks) + goto retry; trace_gfs2_log_blocks(sdp, -blks); - gfs2_log_unlock(sdp); - mutex_unlock(&sdp->sd_log_reserve_mutex); + + /* + * If we waited, then so might others, wake them up _after_ we get + * our share of the log. + */ + if (unlikely(did_wait)) + wake_up(&sdp->sd_log_waitq); down_read(&sdp->sd_log_flush_lock); return 0; } -/** - * gfs2_log_release - Release a given number of log blocks - * @sdp: The GFS2 superblock - * @blks: The number of blocks - * - */ - -void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks) -{ - - gfs2_log_lock(sdp); - atomic_add(blks, &sdp->sd_log_blks_free); - trace_gfs2_log_blocks(sdp, blks); - gfs2_assert_withdraw(sdp, - atomic_read(&sdp->sd_log_blks_free) <= sdp->sd_jdesc->jd_blocks); - gfs2_log_unlock(sdp); - up_read(&sdp->sd_log_flush_lock); -} - static u64 log_bmap(struct gfs2_sbd *sdp, unsigned int lbn) { struct gfs2_journal_extent *je; @@ -559,11 +545,10 @@ static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail) ail2_empty(sdp, new_tail); - gfs2_log_lock(sdp); atomic_add(dist, &sdp->sd_log_blks_free); trace_gfs2_log_blocks(sdp, dist); - gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <= sdp->sd_jdesc->jd_blocks); - gfs2_log_unlock(sdp); + gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <= + sdp->sd_jdesc->jd_blocks); sdp->sd_log_tail = new_tail; } @@ -615,6 +600,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) if (buffer_eopnotsupp(bh)) { clear_buffer_eopnotsupp(bh); set_buffer_uptodate(bh); + fs_info(sdp, "barrier sync failed - disabling barriers\n"); set_bit(SDF_NOBARRIERS, &sdp->sd_flags); lock_buffer(bh); skip_barrier: @@ -822,6 +808,13 @@ static void buf_lo_incore_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) * @sdp: the filesystem * @tr: the transaction * + * We wake up gfs2_logd if the number of pinned blocks exceed thresh1 + * or the total number of used blocks (pinned blocks plus AIL blocks) + * is greater than thresh2. + * + * At mount time thresh1 is 1/3rd of journal size, thresh2 is 2/3rd of + * journal size. + * * Returns: errno */ @@ -832,10 +825,10 @@ void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) up_read(&sdp->sd_log_flush_lock); - gfs2_log_lock(sdp); - if (sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks)) - wake_up_process(sdp->sd_logd_process); - gfs2_log_unlock(sdp); + if (atomic_read(&sdp->sd_log_pinned) > atomic_read(&sdp->sd_log_thresh1) || + ((sdp->sd_jdesc->jd_blocks - atomic_read(&sdp->sd_log_blks_free)) > + atomic_read(&sdp->sd_log_thresh2))) + wake_up(&sdp->sd_logd_waitq); } /** @@ -882,13 +875,23 @@ void gfs2_meta_syncfs(struct gfs2_sbd *sdp) { gfs2_log_flush(sdp, NULL); for (;;) { - gfs2_ail1_start(sdp, DIO_ALL); + gfs2_ail1_start(sdp); if (gfs2_ail1_empty(sdp, DIO_ALL)) break; msleep(10); } } +static inline int gfs2_jrnl_flush_reqd(struct gfs2_sbd *sdp) +{ + return (atomic_read(&sdp->sd_log_pinned) >= atomic_read(&sdp->sd_log_thresh1)); +} + +static inline int gfs2_ail_flush_reqd(struct gfs2_sbd *sdp) +{ + unsigned int used_blocks = sdp->sd_jdesc->jd_blocks - atomic_read(&sdp->sd_log_blks_free); + return used_blocks >= atomic_read(&sdp->sd_log_thresh2); +} /** * gfs2_logd - Update log tail as Active Items get flushed to in-place blocks @@ -901,28 +904,43 @@ void gfs2_meta_syncfs(struct gfs2_sbd *sdp) int gfs2_logd(void *data) { struct gfs2_sbd *sdp = data; - unsigned long t; - int need_flush; + unsigned long t = 1; + DEFINE_WAIT(wait); + unsigned preflush; while (!kthread_should_stop()) { - /* Advance the log tail */ - t = sdp->sd_log_flush_time + - gfs2_tune_get(sdp, gt_log_flush_secs) * HZ; + preflush = atomic_read(&sdp->sd_log_pinned); + if (gfs2_jrnl_flush_reqd(sdp) || t == 0) { + gfs2_ail1_empty(sdp, DIO_ALL); + gfs2_log_flush(sdp, NULL); + gfs2_ail1_empty(sdp, DIO_ALL); + } - gfs2_ail1_empty(sdp, DIO_ALL); - gfs2_log_lock(sdp); - need_flush = sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks); - gfs2_log_unlock(sdp); - if (need_flush || time_after_eq(jiffies, t)) { + if (gfs2_ail_flush_reqd(sdp)) { + gfs2_ail1_start(sdp); + io_schedule(); + gfs2_ail1_empty(sdp, 0); gfs2_log_flush(sdp, NULL); - sdp->sd_log_flush_time = jiffies; + gfs2_ail1_empty(sdp, DIO_ALL); } + wake_up(&sdp->sd_log_waitq); t = gfs2_tune_get(sdp, gt_logd_secs) * HZ; if (freezing(current)) refrigerator(); - schedule_timeout_interruptible(t); + + do { + prepare_to_wait(&sdp->sd_logd_waitq, &wait, + TASK_UNINTERRUPTIBLE); + if (!gfs2_ail_flush_reqd(sdp) && + !gfs2_jrnl_flush_reqd(sdp) && + !kthread_should_stop()) + t = schedule_timeout(t); + } while(t && !gfs2_ail_flush_reqd(sdp) && + !gfs2_jrnl_flush_reqd(sdp) && + !kthread_should_stop()); + finish_wait(&sdp->sd_logd_waitq, &wait); } return 0; diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h index 7c64510ccfd..eb570b4ad44 100644 --- a/fs/gfs2/log.h +++ b/fs/gfs2/log.h @@ -51,7 +51,6 @@ unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct, unsigned int ssize); int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks); -void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks); void gfs2_log_incr_head(struct gfs2_sbd *sdp); struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp); diff --git a/fs/gfs2/lops.c b/fs/gfs2/lops.c index adc260fbea9..bf33f822058 100644 --- a/fs/gfs2/lops.c +++ b/fs/gfs2/lops.c @@ -54,6 +54,7 @@ static void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh) if (bd->bd_ail) list_move(&bd->bd_ail_st_list, &bd->bd_ail->ai_ail2_list); get_bh(bh); + atomic_inc(&sdp->sd_log_pinned); trace_gfs2_pin(bd, 1); } @@ -94,6 +95,7 @@ static void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh, trace_gfs2_pin(bd, 0); gfs2_log_unlock(sdp); unlock_buffer(bh); + atomic_dec(&sdp->sd_log_pinned); } diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index a88fadc704b..fb2a5f93b7c 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -94,7 +94,7 @@ static int __init init_gfs2_fs(void) if (!gfs2_glock_cachep) goto fail; - gfs2_glock_aspace_cachep = kmem_cache_create("gfs2_glock (aspace)", + gfs2_glock_aspace_cachep = kmem_cache_create("gfs2_glock(aspace)", sizeof(struct gfs2_glock) + sizeof(struct address_space), 0, 0, gfs2_init_gl_aspace_once); diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index 0bb12c80937..18176d0b75d 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -34,7 +34,6 @@ static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wbc) { - int err; struct buffer_head *bh, *head; int nr_underway = 0; int write_op = (1 << BIO_RW_META) | ((wbc->sync_mode == WB_SYNC_ALL ? @@ -86,11 +85,10 @@ static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wb } while (bh != head); unlock_page(page); - err = 0; if (nr_underway == 0) end_page_writeback(page); - return err; + return 0; } const struct address_space_operations gfs2_meta_aops = { @@ -313,6 +311,7 @@ void gfs2_remove_from_journal(struct buffer_head *bh, struct gfs2_trans *tr, int struct gfs2_bufdata *bd = bh->b_private; if (test_clear_buffer_pinned(bh)) { + atomic_dec(&sdp->sd_log_pinned); list_del_init(&bd->bd_le.le_list); if (meta) { gfs2_assert_warn(sdp, sdp->sd_log_num_buf); diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index c1309ed1c49..3593b3a7290 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -57,8 +57,6 @@ static void gfs2_tune_init(struct gfs2_tune *gt) { spin_lock_init(>->gt_spin); - gt->gt_incore_log_blocks = 1024; - gt->gt_logd_secs = 1; gt->gt_quota_simul_sync = 64; gt->gt_quota_warn_period = 10; gt->gt_quota_scale_num = 1; @@ -101,14 +99,15 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) spin_lock_init(&sdp->sd_trunc_lock); spin_lock_init(&sdp->sd_log_lock); - + atomic_set(&sdp->sd_log_pinned, 0); INIT_LIST_HEAD(&sdp->sd_log_le_buf); INIT_LIST_HEAD(&sdp->sd_log_le_revoke); INIT_LIST_HEAD(&sdp->sd_log_le_rg); INIT_LIST_HEAD(&sdp->sd_log_le_databuf); INIT_LIST_HEAD(&sdp->sd_log_le_ordered); - mutex_init(&sdp->sd_log_reserve_mutex); + init_waitqueue_head(&sdp->sd_log_waitq); + init_waitqueue_head(&sdp->sd_logd_waitq); INIT_LIST_HEAD(&sdp->sd_ail1_list); INIT_LIST_HEAD(&sdp->sd_ail2_list); @@ -487,7 +486,7 @@ static int gfs2_lookup_root(struct super_block *sb, struct dentry **dptr, struct dentry *dentry; struct inode *inode; - inode = gfs2_inode_lookup(sb, DT_DIR, no_addr, 0, 0); + inode = gfs2_inode_lookup(sb, DT_DIR, no_addr, 0); if (IS_ERR(inode)) { fs_err(sdp, "can't read in %s inode: %ld\n", name, PTR_ERR(inode)); return PTR_ERR(inode); @@ -733,6 +732,8 @@ static int init_journal(struct gfs2_sbd *sdp, int undo) if (sdp->sd_args.ar_spectator) { sdp->sd_jdesc = gfs2_jdesc_find(sdp, 0); atomic_set(&sdp->sd_log_blks_free, sdp->sd_jdesc->jd_blocks); + atomic_set(&sdp->sd_log_thresh1, 2*sdp->sd_jdesc->jd_blocks/5); + atomic_set(&sdp->sd_log_thresh2, 4*sdp->sd_jdesc->jd_blocks/5); } else { if (sdp->sd_lockstruct.ls_jid >= gfs2_jindex_size(sdp)) { fs_err(sdp, "can't mount journal #%u\n", @@ -770,6 +771,8 @@ static int init_journal(struct gfs2_sbd *sdp, int undo) goto fail_jinode_gh; } atomic_set(&sdp->sd_log_blks_free, sdp->sd_jdesc->jd_blocks); + atomic_set(&sdp->sd_log_thresh1, 2*sdp->sd_jdesc->jd_blocks/5); + atomic_set(&sdp->sd_log_thresh2, 4*sdp->sd_jdesc->jd_blocks/5); /* Map the extents for this journal's blocks */ map_journal_extents(sdp); @@ -951,8 +954,6 @@ static int init_threads(struct gfs2_sbd *sdp, int undo) if (undo) goto fail_quotad; - sdp->sd_log_flush_time = jiffies; - p = kthread_run(gfs2_logd, sdp, "gfs2_logd"); error = IS_ERR(p); if (error) { @@ -1160,7 +1161,7 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent GFS2_BASIC_BLOCK_SHIFT; sdp->sd_fsb2bb = 1 << sdp->sd_fsb2bb_shift; - sdp->sd_tune.gt_log_flush_secs = sdp->sd_args.ar_commit; + sdp->sd_tune.gt_logd_secs = sdp->sd_args.ar_commit; sdp->sd_tune.gt_quota_quantum = sdp->sd_args.ar_quota_quantum; if (sdp->sd_args.ar_statfs_quantum) { sdp->sd_tune.gt_statfs_slow = 0; @@ -1323,7 +1324,7 @@ static int gfs2_get_sb(struct file_system_type *fs_type, int flags, memset(&args, 0, sizeof(args)); args.ar_quota = GFS2_QUOTA_DEFAULT; args.ar_data = GFS2_DATA_DEFAULT; - args.ar_commit = 60; + args.ar_commit = 30; args.ar_statfs_quantum = 30; args.ar_quota_quantum = 60; args.ar_errors = GFS2_ERRORS_DEFAULT; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 6dbcbad6ab1..d5f4661287f 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -637,15 +637,40 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, unsigned blocksize, iblock, pos; struct buffer_head *bh, *dibh; struct page *page; - void *kaddr; - struct gfs2_quota *qp; - s64 value; - int err = -EIO; + void *kaddr, *ptr; + struct gfs2_quota q, *qp; + int err, nbytes; u64 size; if (gfs2_is_stuffed(ip)) gfs2_unstuff_dinode(ip, NULL); - + + memset(&q, 0, sizeof(struct gfs2_quota)); + err = gfs2_internal_read(ip, NULL, (char *)&q, &loc, sizeof(q)); + if (err < 0) + return err; + + err = -EIO; + qp = &q; + qp->qu_value = be64_to_cpu(qp->qu_value); + qp->qu_value += change; + qp->qu_value = cpu_to_be64(qp->qu_value); + qd->qd_qb.qb_value = qp->qu_value; + if (fdq) { + if (fdq->d_fieldmask & FS_DQ_BSOFT) { + qp->qu_warn = cpu_to_be64(fdq->d_blk_softlimit); + qd->qd_qb.qb_warn = qp->qu_warn; + } + if (fdq->d_fieldmask & FS_DQ_BHARD) { + qp->qu_limit = cpu_to_be64(fdq->d_blk_hardlimit); + qd->qd_qb.qb_limit = qp->qu_limit; + } + } + + /* Write the quota into the quota file on disk */ + ptr = qp; + nbytes = sizeof(struct gfs2_quota); +get_a_page: page = grab_cache_page(mapping, index); if (!page) return -ENOMEM; @@ -667,7 +692,12 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, if (!buffer_mapped(bh)) { gfs2_block_map(inode, iblock, bh, 1); if (!buffer_mapped(bh)) - goto unlock; + goto unlock_out; + /* If it's a newly allocated disk block for quota, zero it */ + if (buffer_new(bh)) { + memset(bh->b_data, 0, bh->b_size); + set_buffer_uptodate(bh); + } } if (PageUptodate(page)) @@ -677,32 +707,34 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, ll_rw_block(READ_META, 1, &bh); wait_on_buffer(bh); if (!buffer_uptodate(bh)) - goto unlock; + goto unlock_out; } gfs2_trans_add_bh(ip->i_gl, bh, 0); kaddr = kmap_atomic(page, KM_USER0); - qp = kaddr + offset; - value = (s64)be64_to_cpu(qp->qu_value) + change; - qp->qu_value = cpu_to_be64(value); - qd->qd_qb.qb_value = qp->qu_value; - if (fdq) { - if (fdq->d_fieldmask & FS_DQ_BSOFT) { - qp->qu_warn = cpu_to_be64(fdq->d_blk_softlimit); - qd->qd_qb.qb_warn = qp->qu_warn; - } - if (fdq->d_fieldmask & FS_DQ_BHARD) { - qp->qu_limit = cpu_to_be64(fdq->d_blk_hardlimit); - qd->qd_qb.qb_limit = qp->qu_limit; - } - } + if (offset + sizeof(struct gfs2_quota) > PAGE_CACHE_SIZE) + nbytes = PAGE_CACHE_SIZE - offset; + memcpy(kaddr + offset, ptr, nbytes); flush_dcache_page(page); kunmap_atomic(kaddr, KM_USER0); + unlock_page(page); + page_cache_release(page); + /* If quota straddles page boundary, we need to update the rest of the + * quota at the beginning of the next page */ + if (offset != 0) { /* first page, offset is closer to PAGE_CACHE_SIZE */ + ptr = ptr + nbytes; + nbytes = sizeof(struct gfs2_quota) - nbytes; + offset = 0; + index++; + goto get_a_page; + } + + /* Update the disk inode timestamp and size (if extended) */ err = gfs2_meta_inode_buffer(ip, &dibh); if (err) - goto unlock; + goto out; size = loc + sizeof(struct gfs2_quota); if (size > inode->i_size) { @@ -715,7 +747,9 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, brelse(dibh); mark_inode_dirty(inode); -unlock: +out: + return err; +unlock_out: unlock_page(page); page_cache_release(page); return err; @@ -779,8 +813,10 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) * rgrp since it won't be allocated during the transaction */ al->al_requested = 1; - /* +1 in the end for block requested above for unstuffing */ - blocks = num_qd * data_blocks + RES_DINODE + num_qd + 1; + /* +3 in the end for unstuffing block, inode size update block + * and another block in case quota straddles page boundary and + * two blocks need to be updated instead of 1 */ + blocks = num_qd * data_blocks + RES_DINODE + num_qd + 3; if (nalloc) al->al_requested += nalloc * (data_blocks + ind_blocks); @@ -1418,10 +1454,18 @@ static int gfs2_quota_get_xstate(struct super_block *sb, memset(fqs, 0, sizeof(struct fs_quota_stat)); fqs->qs_version = FS_QSTAT_VERSION; - if (sdp->sd_args.ar_quota == GFS2_QUOTA_ON) - fqs->qs_flags = (XFS_QUOTA_UDQ_ENFD | XFS_QUOTA_GDQ_ENFD); - else if (sdp->sd_args.ar_quota == GFS2_QUOTA_ACCOUNT) - fqs->qs_flags = (XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_GDQ_ACCT); + + switch (sdp->sd_args.ar_quota) { + case GFS2_QUOTA_ON: + fqs->qs_flags |= (XFS_QUOTA_UDQ_ENFD | XFS_QUOTA_GDQ_ENFD); + /*FALLTHRU*/ + case GFS2_QUOTA_ACCOUNT: + fqs->qs_flags |= (XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_GDQ_ACCT); + break; + case GFS2_QUOTA_OFF: + break; + } + if (sdp->sd_quota_inode) { fqs->qs_uquota.qfs_ino = GFS2_I(sdp->sd_quota_inode)->i_no_addr; fqs->qs_uquota.qfs_nblks = sdp->sd_quota_inode->i_blocks; diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 503b842f3ba..8bce73ed4d8 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -948,13 +948,13 @@ static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_alloc *al) * try_rgrp_unlink - Look for any unlinked, allocated, but unused inodes * @rgd: The rgrp * - * Returns: The inode, if one has been found + * Returns: 0 if no error + * The inode, if one has been found, in inode. */ -static struct inode *try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, - u64 skip) +static u64 try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, + u64 skip) { - struct inode *inode; u32 goal = 0, block; u64 no_addr; struct gfs2_sbd *sdp = rgd->rd_sbd; @@ -979,14 +979,11 @@ static struct inode *try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, if (no_addr == skip) continue; *last_unlinked = no_addr; - inode = gfs2_inode_lookup(rgd->rd_sbd->sd_vfs, DT_UNKNOWN, - no_addr, -1, 1); - if (!IS_ERR(inode)) - return inode; + return no_addr; } rgd->rd_flags &= ~GFS2_RDF_CHECK; - return NULL; + return 0; } /** @@ -1067,11 +1064,12 @@ static void forward_rgrp_set(struct gfs2_sbd *sdp, struct gfs2_rgrpd *rgd) * Try to acquire rgrp in way which avoids contending with others. * * Returns: errno + * unlinked: the block address of an unlinked block to be reclaimed */ -static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) +static int get_local_rgrp(struct gfs2_inode *ip, u64 *unlinked, + u64 *last_unlinked) { - struct inode *inode = NULL; struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_rgrpd *rgd, *begin = NULL; struct gfs2_alloc *al = ip->i_alloc; @@ -1080,6 +1078,7 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) int loops = 0; int error, rg_locked; + *unlinked = 0; rgd = gfs2_blk2rgrpd(sdp, ip->i_goal); while (rgd) { @@ -1096,19 +1095,24 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) case 0: if (try_rgrp_fit(rgd, al)) goto out; - if (rgd->rd_flags & GFS2_RDF_CHECK) - inode = try_rgrp_unlink(rgd, last_unlinked, ip->i_no_addr); + /* If the rg came in already locked, there's no + way we can recover from a failed try_rgrp_unlink + because that would require an iput which can only + happen after the rgrp is unlocked. */ + if (!rg_locked && rgd->rd_flags & GFS2_RDF_CHECK) + *unlinked = try_rgrp_unlink(rgd, last_unlinked, + ip->i_no_addr); if (!rg_locked) gfs2_glock_dq_uninit(&al->al_rgd_gh); - if (inode) - return inode; + if (*unlinked) + return -EAGAIN; /* fall through */ case GLR_TRYFAILED: rgd = recent_rgrp_next(rgd); break; default: - return ERR_PTR(error); + return error; } } @@ -1130,12 +1134,13 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) case 0: if (try_rgrp_fit(rgd, al)) goto out; - if (rgd->rd_flags & GFS2_RDF_CHECK) - inode = try_rgrp_unlink(rgd, last_unlinked, ip->i_no_addr); + if (!rg_locked && rgd->rd_flags & GFS2_RDF_CHECK) + *unlinked = try_rgrp_unlink(rgd, last_unlinked, + ip->i_no_addr); if (!rg_locked) gfs2_glock_dq_uninit(&al->al_rgd_gh); - if (inode) - return inode; + if (*unlinked) + return -EAGAIN; break; case GLR_TRYFAILED: @@ -1143,7 +1148,7 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) break; default: - return ERR_PTR(error); + return error; } rgd = gfs2_rgrpd_get_next(rgd); @@ -1152,7 +1157,7 @@ static struct inode *get_local_rgrp(struct gfs2_inode *ip, u64 *last_unlinked) if (rgd == begin) { if (++loops >= 3) - return ERR_PTR(-ENOSPC); + return -ENOSPC; if (!skipped) loops++; flags = 0; @@ -1172,7 +1177,7 @@ out: forward_rgrp_set(sdp, rgd); } - return NULL; + return 0; } /** @@ -1188,7 +1193,7 @@ int gfs2_inplace_reserve_i(struct gfs2_inode *ip, char *file, unsigned int line) struct gfs2_alloc *al = ip->i_alloc; struct inode *inode; int error = 0; - u64 last_unlinked = NO_BLOCK; + u64 last_unlinked = NO_BLOCK, unlinked; if (gfs2_assert_warn(sdp, al->al_requested)) return -EINVAL; @@ -1204,14 +1209,19 @@ try_again: if (error) return error; - inode = get_local_rgrp(ip, &last_unlinked); - if (inode) { + error = get_local_rgrp(ip, &unlinked, &last_unlinked); + if (error) { if (ip != GFS2_I(sdp->sd_rindex)) gfs2_glock_dq_uninit(&al->al_ri_gh); - if (IS_ERR(inode)) - return PTR_ERR(inode); - iput(inode); + if (error != -EAGAIN) + return error; + error = gfs2_unlinked_inode_lookup(ip->i_inode.i_sb, + unlinked, &inode); + if (inode) + iput(inode); gfs2_log_flush(sdp, NULL); + if (error == GLR_TRYFAILED) + error = 0; goto try_again; } diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 50aac606b99..4d1aad38f1b 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1113,7 +1113,7 @@ static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data) int error; spin_lock(>->gt_spin); - args.ar_commit = gt->gt_log_flush_secs; + args.ar_commit = gt->gt_logd_secs; args.ar_quota_quantum = gt->gt_quota_quantum; if (gt->gt_statfs_slow) args.ar_statfs_quantum = 0; @@ -1160,7 +1160,7 @@ static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data) else clear_bit(SDF_NOBARRIERS, &sdp->sd_flags); spin_lock(>->gt_spin); - gt->gt_log_flush_secs = args.ar_commit; + gt->gt_logd_secs = args.ar_commit; gt->gt_quota_quantum = args.ar_quota_quantum; if (args.ar_statfs_quantum) { gt->gt_statfs_slow = 0; @@ -1305,8 +1305,8 @@ static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt) } if (args->ar_discard) seq_printf(s, ",discard"); - val = sdp->sd_tune.gt_log_flush_secs; - if (val != 60) + val = sdp->sd_tune.gt_logd_secs; + if (val != 30) seq_printf(s, ",commit=%d", val); val = sdp->sd_tune.gt_statfs_quantum; if (val != 30) @@ -1334,7 +1334,8 @@ static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt) } if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags)) seq_printf(s, ",nobarrier"); - + if (test_bit(SDF_DEMOTE, &sdp->sd_flags)) + seq_printf(s, ",demote_interface_used"); return 0; } diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 54fd9842599..37f5393e68e 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -232,6 +232,8 @@ static ssize_t demote_rq_store(struct gfs2_sbd *sdp, const char *buf, size_t len glops = gfs2_glops_list[gltype]; if (glops == NULL) return -EINVAL; + if (!test_and_set_bit(SDF_DEMOTE, &sdp->sd_flags)) + fs_info(sdp, "demote interface used\n"); rv = gfs2_glock_get(sdp, glnum, glops, 0, &gl); if (rv) return rv; @@ -468,8 +470,6 @@ static ssize_t name##_store(struct gfs2_sbd *sdp, const char *buf, size_t len)\ } \ TUNE_ATTR_2(name, name##_store) -TUNE_ATTR(incore_log_blocks, 0); -TUNE_ATTR(log_flush_secs, 0); TUNE_ATTR(quota_warn_period, 0); TUNE_ATTR(quota_quantum, 0); TUNE_ATTR(max_readahead, 0); @@ -481,8 +481,6 @@ TUNE_ATTR(statfs_quantum, 1); TUNE_ATTR_3(quota_scale, quota_scale_show, quota_scale_store); static struct attribute *tune_attrs[] = { - &tune_attr_incore_log_blocks.attr, - &tune_attr_log_flush_secs.attr, &tune_attr_quota_warn_period.attr, &tune_attr_quota_quantum.attr, &tune_attr_max_readahead.attr, diff --git a/fs/gfs2/trans.c b/fs/gfs2/trans.c index 4ef0e9fa354..9ec73a85411 100644 --- a/fs/gfs2/trans.c +++ b/fs/gfs2/trans.c @@ -23,6 +23,7 @@ #include "meta_io.h" #include "trans.h" #include "util.h" +#include "trace_gfs2.h" int gfs2_trans_begin(struct gfs2_sbd *sdp, unsigned int blocks, unsigned int revokes) @@ -75,6 +76,23 @@ fail_holder_uninit: return error; } +/** + * gfs2_log_release - Release a given number of log blocks + * @sdp: The GFS2 superblock + * @blks: The number of blocks + * + */ + +static void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks) +{ + + atomic_add(blks, &sdp->sd_log_blks_free); + trace_gfs2_log_blocks(sdp, blks); + gfs2_assert_withdraw(sdp, atomic_read(&sdp->sd_log_blks_free) <= + sdp->sd_jdesc->jd_blocks); + up_read(&sdp->sd_log_flush_lock); +} + void gfs2_trans_end(struct gfs2_sbd *sdp) { struct gfs2_trans *tr = current->journal_info; diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 3ff50da9478..55f1dde2fa8 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -23,10 +23,9 @@ static int jffs2_garbage_collect_thread(void *); void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) { - spin_lock(&c->erase_completion_lock); + assert_spin_locked(&c->erase_completion_lock); if (c->gc_task && jffs2_thread_should_wake(c)) send_sig(SIGHUP, c->gc_task, 1); - spin_unlock(&c->erase_completion_lock); } /* This must only ever be called when no GC thread is currently running */ diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index b47679be118..6286ad9b00f 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -103,9 +103,10 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, jffs2_erase_failed(c, jeb, bad_offset); } -void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) +int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) { struct jffs2_eraseblock *jeb; + int work_done = 0; mutex_lock(&c->erase_free_sem); @@ -121,6 +122,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) mutex_unlock(&c->erase_free_sem); jffs2_mark_erased_block(c, jeb); + work_done++; if (!--count) { D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n")); goto done; @@ -157,6 +159,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) mutex_unlock(&c->erase_free_sem); done: D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); + return work_done; } static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) @@ -165,10 +168,11 @@ static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblo mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); list_move_tail(&jeb->list, &c->erase_complete_list); + /* Wake the GC thread to mark them clean */ + jffs2_garbage_collect_trigger(c); spin_unlock(&c->erase_completion_lock); mutex_unlock(&c->erase_free_sem); - /* Ensure that kupdated calls us again to mark them clean */ - jffs2_erase_pending_trigger(c); + wake_up(&c->erase_wait); } static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset) @@ -487,9 +491,9 @@ filebad: refile: /* Stick it back on the list from whence it came and come back later */ - jffs2_erase_pending_trigger(c); mutex_lock(&c->erase_free_sem); spin_lock(&c->erase_completion_lock); + jffs2_garbage_collect_trigger(c); list_move(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); mutex_unlock(&c->erase_free_sem); diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 3451a81b214..86e0821fc98 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -313,8 +313,8 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) case S_IFBLK: case S_IFCHR: /* Read the device numbers from the media */ - if (f->metadata->size != sizeof(jdev.old) && - f->metadata->size != sizeof(jdev.new)) { + if (f->metadata->size != sizeof(jdev.old_id) && + f->metadata->size != sizeof(jdev.new_id)) { printk(KERN_NOTICE "Device node has strange size %d\n", f->metadata->size); goto error_io; } @@ -325,10 +325,10 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino) printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); goto error; } - if (f->metadata->size == sizeof(jdev.old)) - rdev = old_decode_dev(je16_to_cpu(jdev.old)); + if (f->metadata->size == sizeof(jdev.old_id)) + rdev = old_decode_dev(je16_to_cpu(jdev.old_id)); else - rdev = new_decode_dev(je32_to_cpu(jdev.new)); + rdev = new_decode_dev(je32_to_cpu(jdev.new_id)); case S_IFSOCK: case S_IFIFO: diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 3b6f2fa12cf..f5e96bd656e 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -214,6 +214,19 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) return ret; } + /* If there are any blocks which need erasing, erase them now */ + if (!list_empty(&c->erase_complete_list) || + !list_empty(&c->erase_pending_list)) { + spin_unlock(&c->erase_completion_lock); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() erasing pending blocks\n")); + if (jffs2_erase_pending_blocks(c, 1)) { + mutex_unlock(&c->alloc_sem); + return 0; + } + D1(printk(KERN_DEBUG "No progress from erasing blocks; doing GC anyway\n")); + spin_lock(&c->erase_completion_lock); + } + /* First, work out which block we're garbage-collecting */ jeb = c->gcblock; @@ -222,7 +235,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) if (!jeb) { /* Couldn't find a free block. But maybe we can just erase one and make 'progress'? */ - if (!list_empty(&c->erase_pending_list)) { + if (c->nr_erasing_blocks) { spin_unlock(&c->erase_completion_lock); mutex_unlock(&c->alloc_sem); return -EAGAIN; @@ -435,7 +448,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) list_add_tail(&c->gcblock->list, &c->erase_pending_list); c->gcblock = NULL; c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); } spin_unlock(&c->erase_completion_lock); diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 507ed6ec184..a881a42f19e 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -312,11 +312,11 @@ static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c) static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev) { if (old_valid_dev(rdev)) { - jdev->old = cpu_to_je16(old_encode_dev(rdev)); - return sizeof(jdev->old); + jdev->old_id = cpu_to_je16(old_encode_dev(rdev)); + return sizeof(jdev->old_id); } else { - jdev->new = cpu_to_je32(new_encode_dev(rdev)); - return sizeof(jdev->new); + jdev->new_id = cpu_to_je32(new_encode_dev(rdev)); + return sizeof(jdev->new_id); } } @@ -464,7 +464,7 @@ int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb int jffs2_do_mount_fs(struct jffs2_sb_info *c); /* erase.c */ -void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); +int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 191359dde4e..694aa5b0350 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -116,9 +116,21 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ret = jffs2_garbage_collect_pass(c); - if (ret == -EAGAIN) - jffs2_erase_pending_blocks(c, 1); - else if (ret) + if (ret == -EAGAIN) { + spin_lock(&c->erase_completion_lock); + if (c->nr_erasing_blocks && + list_empty(&c->erase_pending_list) && + list_empty(&c->erase_complete_list)) { + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&c->erase_wait, &wait); + D1(printk(KERN_DEBUG "%s waiting for erase to complete\n", __func__)); + spin_unlock(&c->erase_completion_lock); + + schedule(); + } else + spin_unlock(&c->erase_completion_lock); + } else if (ret) return ret; cond_resched(); @@ -217,7 +229,7 @@ static int jffs2_find_nextblock(struct jffs2_sb_info *c) ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); list_move_tail(&ejeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); D1(printk(KERN_DEBUG "jffs2_find_nextblock: Triggering erase of erasable block at 0x%08x\n", ejeb->offset)); } @@ -469,7 +481,9 @@ struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c, void jffs2_complete_reservation(struct jffs2_sb_info *c) { D1(printk(KERN_DEBUG "jffs2_complete_reservation()\n")); + spin_lock(&c->erase_completion_lock); jffs2_garbage_collect_trigger(c); + spin_unlock(&c->erase_completion_lock); mutex_unlock(&c->alloc_sem); } @@ -611,7 +625,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); list_add_tail(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); } else { /* Sometimes, however, we leave it elsewhere so it doesn't get immediately reused, and we spread the load a bit. */ @@ -732,6 +746,10 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c) int nr_very_dirty = 0; struct jffs2_eraseblock *jeb; + if (!list_empty(&c->erase_complete_list) || + !list_empty(&c->erase_pending_list)) + return 1; + if (c->unchecked_size) { D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", c->unchecked_size, c->checked_ino)); diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index a7f03b7ebcb..035a767f958 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -140,8 +140,7 @@ void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c); #endif /* WRITEBUFFER */ -/* erase.c */ -static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) +static inline void jffs2_dirty_trigger(struct jffs2_sb_info *c) { OFNI_BS_2SFFJ(c)->s_dirt = 1; } diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 696686cc206..46f870d1cc3 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -260,7 +260,9 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) ret = -EIO; goto out; } - jffs2_erase_pending_trigger(c); + spin_lock(&c->erase_completion_lock); + jffs2_garbage_collect_trigger(c); + spin_unlock(&c->erase_completion_lock); } ret = 0; out: diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 9a80e8e595d..511e2d609d1 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -63,8 +63,6 @@ static void jffs2_write_super(struct super_block *sb) if (!(sb->s_flags & MS_RDONLY)) { D1(printk(KERN_DEBUG "jffs2_write_super()\n")); - jffs2_garbage_collect_trigger(c); - jffs2_erase_pending_blocks(c, 0); jffs2_flush_wbuf_gc(c, 0); } diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 5ef7bac265e..07ee1546b2f 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -84,7 +84,7 @@ static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino) struct jffs2_inodirty *new; /* Mark the superblock dirty so that kupdated will flush... */ - jffs2_erase_pending_trigger(c); + jffs2_dirty_trigger(c); if (jffs2_wbuf_pending_for_ino(c, ino)) return; @@ -121,7 +121,7 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); list_add_tail(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); } else { /* Sometimes, however, we leave it elsewhere so it doesn't get immediately reused, and we spread the load a bit. */ @@ -152,7 +152,7 @@ static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset)); list_add(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); } if (!jffs2_prealloc_raw_node_refs(c, jeb, 1)) { @@ -543,7 +543,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset)); list_move(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; - jffs2_erase_pending_trigger(c); + jffs2_garbage_collect_trigger(c); } jffs2_dbg_acct_sanity_check_nolock(c, jeb); diff --git a/fs/nilfs2/alloc.c b/fs/nilfs2/alloc.c index 7cfb87e692d..d7fd696e595 100644 --- a/fs/nilfs2/alloc.c +++ b/fs/nilfs2/alloc.c @@ -31,6 +31,11 @@ #include "alloc.h" +/** + * nilfs_palloc_groups_per_desc_block - get the number of groups that a group + * descriptor block can maintain + * @inode: inode of metadata file using this allocator + */ static inline unsigned long nilfs_palloc_groups_per_desc_block(const struct inode *inode) { @@ -38,12 +43,21 @@ nilfs_palloc_groups_per_desc_block(const struct inode *inode) sizeof(struct nilfs_palloc_group_desc); } +/** + * nilfs_palloc_groups_count - get maximum number of groups + * @inode: inode of metadata file using this allocator + */ static inline unsigned long nilfs_palloc_groups_count(const struct inode *inode) { return 1UL << (BITS_PER_LONG - (inode->i_blkbits + 3 /* log2(8) */)); } +/** + * nilfs_palloc_init_blockgroup - initialize private variables for allocator + * @inode: inode of metadata file using this allocator + * @entry_size: size of the persistent object + */ int nilfs_palloc_init_blockgroup(struct inode *inode, unsigned entry_size) { struct nilfs_mdt_info *mi = NILFS_MDT(inode); @@ -69,6 +83,12 @@ int nilfs_palloc_init_blockgroup(struct inode *inode, unsigned entry_size) return 0; } +/** + * nilfs_palloc_group - get group number and offset from an entry number + * @inode: inode of metadata file using this allocator + * @nr: serial number of the entry (e.g. inode number) + * @offset: pointer to store offset number in the group + */ static unsigned long nilfs_palloc_group(const struct inode *inode, __u64 nr, unsigned long *offset) { @@ -78,6 +98,14 @@ static unsigned long nilfs_palloc_group(const struct inode *inode, __u64 nr, return group; } +/** + * nilfs_palloc_desc_blkoff - get block offset of a group descriptor block + * @inode: inode of metadata file using this allocator + * @group: group number + * + * nilfs_palloc_desc_blkoff() returns block offset of the descriptor + * block which contains a descriptor of the specified group. + */ static unsigned long nilfs_palloc_desc_blkoff(const struct inode *inode, unsigned long group) { @@ -86,6 +114,14 @@ nilfs_palloc_desc_blkoff(const struct inode *inode, unsigned long group) return desc_block * NILFS_MDT(inode)->mi_blocks_per_desc_block; } +/** + * nilfs_palloc_bitmap_blkoff - get block offset of a bitmap block + * @inode: inode of metadata file using this allocator + * @group: group number + * + * nilfs_palloc_bitmap_blkoff() returns block offset of the bitmap + * block used to allocate/deallocate entries in the specified group. + */ static unsigned long nilfs_palloc_bitmap_blkoff(const struct inode *inode, unsigned long group) { @@ -95,6 +131,12 @@ nilfs_palloc_bitmap_blkoff(const struct inode *inode, unsigned long group) desc_offset * NILFS_MDT(inode)->mi_blocks_per_group; } +/** + * nilfs_palloc_group_desc_nfrees - get the number of free entries in a group + * @inode: inode of metadata file using this allocator + * @group: group number + * @desc: pointer to descriptor structure for the group + */ static unsigned long nilfs_palloc_group_desc_nfrees(struct inode *inode, unsigned long group, const struct nilfs_palloc_group_desc *desc) @@ -107,6 +149,13 @@ nilfs_palloc_group_desc_nfrees(struct inode *inode, unsigned long group, return nfree; } +/** + * nilfs_palloc_group_desc_add_entries - adjust count of free entries + * @inode: inode of metadata file using this allocator + * @group: group number + * @desc: pointer to descriptor structure for the group + * @n: delta to be added + */ static void nilfs_palloc_group_desc_add_entries(struct inode *inode, unsigned long group, @@ -118,6 +167,11 @@ nilfs_palloc_group_desc_add_entries(struct inode *inode, spin_unlock(nilfs_mdt_bgl_lock(inode, group)); } +/** + * nilfs_palloc_entry_blkoff - get block offset of an entry block + * @inode: inode of metadata file using this allocator + * @nr: serial number of the entry (e.g. inode number) + */ static unsigned long nilfs_palloc_entry_blkoff(const struct inode *inode, __u64 nr) { @@ -129,6 +183,12 @@ nilfs_palloc_entry_blkoff(const struct inode *inode, __u64 nr) group_offset / NILFS_MDT(inode)->mi_entries_per_block; } +/** + * nilfs_palloc_desc_block_init - initialize buffer of a group descriptor block + * @inode: inode of metadata file + * @bh: buffer head of the buffer to be initialized + * @kaddr: kernel address mapped for the page including the buffer + */ static void nilfs_palloc_desc_block_init(struct inode *inode, struct buffer_head *bh, void *kaddr) { @@ -179,6 +239,13 @@ static int nilfs_palloc_get_block(struct inode *inode, unsigned long blkoff, return ret; } +/** + * nilfs_palloc_get_desc_block - get buffer head of a group descriptor block + * @inode: inode of metadata file using this allocator + * @group: group number + * @create: create flag + * @bhp: pointer to store the resultant buffer head + */ static int nilfs_palloc_get_desc_block(struct inode *inode, unsigned long group, int create, struct buffer_head **bhp) @@ -191,6 +258,13 @@ static int nilfs_palloc_get_desc_block(struct inode *inode, bhp, &cache->prev_desc, &cache->lock); } +/** + * nilfs_palloc_get_bitmap_block - get buffer head of a bitmap block + * @inode: inode of metadata file using this allocator + * @group: group number + * @create: create flag + * @bhp: pointer to store the resultant buffer head + */ static int nilfs_palloc_get_bitmap_block(struct inode *inode, unsigned long group, int create, struct buffer_head **bhp) @@ -203,6 +277,13 @@ static int nilfs_palloc_get_bitmap_block(struct inode *inode, &cache->prev_bitmap, &cache->lock); } +/** + * nilfs_palloc_get_entry_block - get buffer head of an entry block + * @inode: inode of metadata file using this allocator + * @nr: serial number of the entry (e.g. inode number) + * @create: create flag + * @bhp: pointer to store the resultant buffer head + */ int nilfs_palloc_get_entry_block(struct inode *inode, __u64 nr, int create, struct buffer_head **bhp) { @@ -214,6 +295,13 @@ int nilfs_palloc_get_entry_block(struct inode *inode, __u64 nr, &cache->prev_entry, &cache->lock); } +/** + * nilfs_palloc_block_get_group_desc - get kernel address of a group descriptor + * @inode: inode of metadata file using this allocator + * @group: group number + * @bh: buffer head of the buffer storing the group descriptor block + * @kaddr: kernel address mapped for the page including the buffer + */ static struct nilfs_palloc_group_desc * nilfs_palloc_block_get_group_desc(const struct inode *inode, unsigned long group, @@ -223,6 +311,13 @@ nilfs_palloc_block_get_group_desc(const struct inode *inode, group % nilfs_palloc_groups_per_desc_block(inode); } +/** + * nilfs_palloc_block_get_entry - get kernel address of an entry + * @inode: inode of metadata file using this allocator + * @nr: serial number of the entry (e.g. inode number) + * @bh: buffer head of the buffer storing the entry block + * @kaddr: kernel address mapped for the page including the buffer + */ void *nilfs_palloc_block_get_entry(const struct inode *inode, __u64 nr, const struct buffer_head *bh, void *kaddr) { @@ -235,11 +330,19 @@ void *nilfs_palloc_block_get_entry(const struct inode *inode, __u64 nr, entry_offset * NILFS_MDT(inode)->mi_entry_size; } +/** + * nilfs_palloc_find_available_slot - find available slot in a group + * @inode: inode of metadata file using this allocator + * @group: group number + * @target: offset number of an entry in the group (start point) + * @bitmap: bitmap of the group + * @bsize: size in bits + */ static int nilfs_palloc_find_available_slot(struct inode *inode, unsigned long group, unsigned long target, unsigned char *bitmap, - int bsize) /* size in bits */ + int bsize) { int curr, pos, end, i; @@ -277,6 +380,13 @@ static int nilfs_palloc_find_available_slot(struct inode *inode, return -ENOSPC; } +/** + * nilfs_palloc_rest_groups_in_desc_block - get the remaining number of groups + * in a group descriptor block + * @inode: inode of metadata file using this allocator + * @curr: current group number + * @max: maximum number of groups + */ static unsigned long nilfs_palloc_rest_groups_in_desc_block(const struct inode *inode, unsigned long curr, unsigned long max) @@ -287,6 +397,11 @@ nilfs_palloc_rest_groups_in_desc_block(const struct inode *inode, max - curr + 1); } +/** + * nilfs_palloc_prepare_alloc_entry - prepare to allocate a persistent object + * @inode: inode of metadata file using this allocator + * @req: nilfs_palloc_req structure exchanged for the allocation + */ int nilfs_palloc_prepare_alloc_entry(struct inode *inode, struct nilfs_palloc_req *req) { @@ -366,6 +481,11 @@ int nilfs_palloc_prepare_alloc_entry(struct inode *inode, return ret; } +/** + * nilfs_palloc_commit_alloc_entry - finish allocation of a persistent object + * @inode: inode of metadata file using this allocator + * @req: nilfs_palloc_req structure exchanged for the allocation + */ void nilfs_palloc_commit_alloc_entry(struct inode *inode, struct nilfs_palloc_req *req) { @@ -377,6 +497,11 @@ void nilfs_palloc_commit_alloc_entry(struct inode *inode, brelse(req->pr_desc_bh); } +/** + * nilfs_palloc_commit_free_entry - finish deallocating a persistent object + * @inode: inode of metadata file using this allocator + * @req: nilfs_palloc_req structure exchanged for the removal + */ void nilfs_palloc_commit_free_entry(struct inode *inode, struct nilfs_palloc_req *req) { @@ -410,6 +535,11 @@ void nilfs_palloc_commit_free_entry(struct inode *inode, brelse(req->pr_desc_bh); } +/** + * nilfs_palloc_abort_alloc_entry - cancel allocation of a persistent object + * @inode: inode of metadata file using this allocator + * @req: nilfs_palloc_req structure exchanged for the allocation + */ void nilfs_palloc_abort_alloc_entry(struct inode *inode, struct nilfs_palloc_req *req) { @@ -442,6 +572,11 @@ void nilfs_palloc_abort_alloc_entry(struct inode *inode, req->pr_desc_bh = NULL; } +/** + * nilfs_palloc_prepare_free_entry - prepare to deallocate a persistent object + * @inode: inode of metadata file using this allocator + * @req: nilfs_palloc_req structure exchanged for the removal + */ int nilfs_palloc_prepare_free_entry(struct inode *inode, struct nilfs_palloc_req *req) { @@ -464,6 +599,11 @@ int nilfs_palloc_prepare_free_entry(struct inode *inode, return 0; } +/** + * nilfs_palloc_abort_free_entry - cancel deallocating a persistent object + * @inode: inode of metadata file using this allocator + * @req: nilfs_palloc_req structure exchanged for the removal + */ void nilfs_palloc_abort_free_entry(struct inode *inode, struct nilfs_palloc_req *req) { @@ -475,6 +615,12 @@ void nilfs_palloc_abort_free_entry(struct inode *inode, req->pr_desc_bh = NULL; } +/** + * nilfs_palloc_group_is_in - judge if an entry is in a group + * @inode: inode of metadata file using this allocator + * @group: group number + * @nr: serial number of the entry (e.g. inode number) + */ static int nilfs_palloc_group_is_in(struct inode *inode, unsigned long group, __u64 nr) { @@ -485,6 +631,12 @@ nilfs_palloc_group_is_in(struct inode *inode, unsigned long group, __u64 nr) return (nr >= first) && (nr <= last); } +/** + * nilfs_palloc_freev - deallocate a set of persistent objects + * @inode: inode of metadata file using this allocator + * @entry_nrs: array of entry numbers to be deallocated + * @nitems: number of entries stored in @entry_nrs + */ int nilfs_palloc_freev(struct inode *inode, __u64 *entry_nrs, size_t nitems) { struct buffer_head *desc_bh, *bitmap_bh; diff --git a/fs/nilfs2/alloc.h b/fs/nilfs2/alloc.h index 5cccf874d69..9af34a7e6e1 100644 --- a/fs/nilfs2/alloc.h +++ b/fs/nilfs2/alloc.h @@ -29,6 +29,13 @@ #include <linux/buffer_head.h> #include <linux/fs.h> +/** + * nilfs_palloc_entries_per_group - get the number of entries per group + * @inode: inode of metadata file using this allocator + * + * The number of entries per group is defined by the number of bits + * that a bitmap block can maintain. + */ static inline unsigned long nilfs_palloc_entries_per_group(const struct inode *inode) { diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 76c38e3e19d..b27a342c5af 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -31,63 +31,16 @@ #include "alloc.h" #include "dat.h" -/** - * struct nilfs_btree_path - A path on which B-tree operations are executed - * @bp_bh: buffer head of node block - * @bp_sib_bh: buffer head of sibling node block - * @bp_index: index of child node - * @bp_oldreq: ptr end request for old ptr - * @bp_newreq: ptr alloc request for new ptr - * @bp_op: rebalance operation - */ -struct nilfs_btree_path { - struct buffer_head *bp_bh; - struct buffer_head *bp_sib_bh; - int bp_index; - union nilfs_bmap_ptr_req bp_oldreq; - union nilfs_bmap_ptr_req bp_newreq; - struct nilfs_btnode_chkey_ctxt bp_ctxt; - void (*bp_op)(struct nilfs_btree *, struct nilfs_btree_path *, - int, __u64 *, __u64 *); -}; - -/* - * B-tree path operations - */ - -static struct kmem_cache *nilfs_btree_path_cache; - -int __init nilfs_btree_path_cache_init(void) -{ - nilfs_btree_path_cache = - kmem_cache_create("nilfs2_btree_path_cache", - sizeof(struct nilfs_btree_path) * - NILFS_BTREE_LEVEL_MAX, 0, 0, NULL); - return (nilfs_btree_path_cache != NULL) ? 0 : -ENOMEM; -} - -void nilfs_btree_path_cache_destroy(void) -{ - kmem_cache_destroy(nilfs_btree_path_cache); -} - -static inline struct nilfs_btree_path *nilfs_btree_alloc_path(void) -{ - return kmem_cache_alloc(nilfs_btree_path_cache, GFP_NOFS); -} - -static inline void nilfs_btree_free_path(struct nilfs_btree_path *path) +static struct nilfs_btree_path *nilfs_btree_alloc_path(void) { - kmem_cache_free(nilfs_btree_path_cache, path); -} + struct nilfs_btree_path *path; + int level = NILFS_BTREE_LEVEL_DATA; -static void nilfs_btree_init_path(struct nilfs_btree_path *path) -{ - int level; + path = kmem_cache_alloc(nilfs_btree_path_cache, GFP_NOFS); + if (path == NULL) + goto out; - for (level = NILFS_BTREE_LEVEL_DATA; - level < NILFS_BTREE_LEVEL_MAX; - level++) { + for (; level < NILFS_BTREE_LEVEL_MAX; level++) { path[level].bp_bh = NULL; path[level].bp_sib_bh = NULL; path[level].bp_index = 0; @@ -95,15 +48,19 @@ static void nilfs_btree_init_path(struct nilfs_btree_path *path) path[level].bp_newreq.bpr_ptr = NILFS_BMAP_INVALID_PTR; path[level].bp_op = NULL; } + +out: + return path; } -static void nilfs_btree_release_path(struct nilfs_btree_path *path) +static void nilfs_btree_free_path(struct nilfs_btree_path *path) { - int level; + int level = NILFS_BTREE_LEVEL_DATA; - for (level = NILFS_BTREE_LEVEL_DATA; level < NILFS_BTREE_LEVEL_MAX; - level++) + for (; level < NILFS_BTREE_LEVEL_MAX; level++) brelse(path[level].bp_bh); + + kmem_cache_free(nilfs_btree_path_cache, path); } /* @@ -566,14 +523,12 @@ static int nilfs_btree_lookup(const struct nilfs_bmap *bmap, path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; - nilfs_btree_init_path(path); ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level); if (ptrp != NULL) *ptrp = ptr; - nilfs_btree_release_path(path); nilfs_btree_free_path(path); return ret; @@ -594,7 +549,7 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *bmap, path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; - nilfs_btree_init_path(path); + ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level); if (ret < 0) goto out; @@ -655,7 +610,6 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *bmap, *ptrp = ptr; ret = cnt; out: - nilfs_btree_release_path(path); nilfs_btree_free_path(path); return ret; } @@ -1123,7 +1077,6 @@ static int nilfs_btree_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; - nilfs_btree_init_path(path); ret = nilfs_btree_do_lookup(btree, path, key, NULL, NILFS_BTREE_LEVEL_NODE_MIN); @@ -1140,7 +1093,6 @@ static int nilfs_btree_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) nilfs_bmap_add_blocks(bmap, stats.bs_nblocks); out: - nilfs_btree_release_path(path); nilfs_btree_free_path(path); return ret; } @@ -1456,7 +1408,7 @@ static int nilfs_btree_delete(struct nilfs_bmap *bmap, __u64 key) path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; - nilfs_btree_init_path(path); + ret = nilfs_btree_do_lookup(btree, path, key, NULL, NILFS_BTREE_LEVEL_NODE_MIN); if (ret < 0) @@ -1473,7 +1425,6 @@ static int nilfs_btree_delete(struct nilfs_bmap *bmap, __u64 key) nilfs_bmap_sub_blocks(bmap, stats.bs_nblocks); out: - nilfs_btree_release_path(path); nilfs_btree_free_path(path); return ret; } @@ -1488,11 +1439,9 @@ static int nilfs_btree_last_key(const struct nilfs_bmap *bmap, __u64 *keyp) path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; - nilfs_btree_init_path(path); ret = nilfs_btree_do_lookup_last(btree, path, keyp, NULL); - nilfs_btree_release_path(path); nilfs_btree_free_path(path); return ret; @@ -1923,7 +1872,6 @@ static int nilfs_btree_propagate(const struct nilfs_bmap *bmap, path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; - nilfs_btree_init_path(path); if (buffer_nilfs_node(bh)) { node = (struct nilfs_btree_node *)bh->b_data; @@ -1947,7 +1895,6 @@ static int nilfs_btree_propagate(const struct nilfs_bmap *bmap, nilfs_btree_propagate_p(btree, path, level, bh); out: - nilfs_btree_release_path(path); nilfs_btree_free_path(path); return ret; @@ -2108,7 +2055,6 @@ static int nilfs_btree_assign(struct nilfs_bmap *bmap, path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; - nilfs_btree_init_path(path); if (buffer_nilfs_node(*bh)) { node = (struct nilfs_btree_node *)(*bh)->b_data; @@ -2130,7 +2076,6 @@ static int nilfs_btree_assign(struct nilfs_bmap *bmap, nilfs_btree_assign_p(btree, path, level, bh, blocknr, binfo); out: - nilfs_btree_release_path(path); nilfs_btree_free_path(path); return ret; @@ -2175,7 +2120,6 @@ static int nilfs_btree_mark(struct nilfs_bmap *bmap, __u64 key, int level) path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; - nilfs_btree_init_path(path); ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level + 1); if (ret < 0) { @@ -2195,7 +2139,6 @@ static int nilfs_btree_mark(struct nilfs_bmap *bmap, __u64 key, int level) nilfs_bmap_set_dirty(&btree->bt_bmap); out: - nilfs_btree_release_path(path); nilfs_btree_free_path(path); return ret; } diff --git a/fs/nilfs2/btree.h b/fs/nilfs2/btree.h index 4b82d84ade7..af638d59e3b 100644 --- a/fs/nilfs2/btree.h +++ b/fs/nilfs2/btree.h @@ -30,9 +30,6 @@ #include "btnode.h" #include "bmap.h" -struct nilfs_btree; -struct nilfs_btree_path; - /** * struct nilfs_btree - B-tree structure * @bt_bmap: bmap base structure @@ -41,6 +38,25 @@ struct nilfs_btree { struct nilfs_bmap bt_bmap; }; +/** + * struct nilfs_btree_path - A path on which B-tree operations are executed + * @bp_bh: buffer head of node block + * @bp_sib_bh: buffer head of sibling node block + * @bp_index: index of child node + * @bp_oldreq: ptr end request for old ptr + * @bp_newreq: ptr alloc request for new ptr + * @bp_op: rebalance operation + */ +struct nilfs_btree_path { + struct buffer_head *bp_bh; + struct buffer_head *bp_sib_bh; + int bp_index; + union nilfs_bmap_ptr_req bp_oldreq; + union nilfs_bmap_ptr_req bp_newreq; + struct nilfs_btnode_chkey_ctxt bp_ctxt; + void (*bp_op)(struct nilfs_btree *, struct nilfs_btree_path *, + int, __u64 *, __u64 *); +}; #define NILFS_BTREE_ROOT_SIZE NILFS_BMAP_SIZE #define NILFS_BTREE_ROOT_NCHILDREN_MAX \ @@ -57,6 +73,7 @@ struct nilfs_btree { #define NILFS_BTREE_KEY_MIN ((__u64)0) #define NILFS_BTREE_KEY_MAX (~(__u64)0) +extern struct kmem_cache *nilfs_btree_path_cache; int nilfs_btree_path_cache_init(void); void nilfs_btree_path_cache_destroy(void); diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 0957b58f909..5e226d4b41d 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -451,7 +451,7 @@ static int __nilfs_read_inode(struct super_block *sb, unsigned long ino, inode->i_op = &nilfs_special_inode_operations; init_special_inode( inode, inode->i_mode, - new_decode_dev(le64_to_cpu(raw_inode->i_device_code))); + huge_decode_dev(le64_to_cpu(raw_inode->i_device_code))); } nilfs_ifile_unmap_inode(sbi->s_ifile, ino, bh); brelse(bh); @@ -511,7 +511,7 @@ void nilfs_write_inode_common(struct inode *inode, nilfs_bmap_write(ii->i_bmap, raw_inode); else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) raw_inode->i_device_code = - cpu_to_le64(new_encode_dev(inode->i_rdev)); + cpu_to_le64(huge_encode_dev(inode->i_rdev)); /* When extending inode, nilfs->ns_inode_size should be checked for substitutions of appended fields */ } diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index ba43146f3c3..bae2a516b4e 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -105,6 +105,8 @@ static void store_segsum_info(struct nilfs_segsum_info *ssi, ssi->nsumblk = DIV_ROUND_UP(ssi->sumbytes, blocksize); ssi->nfileblk = ssi->nblocks - ssi->nsumblk - !!NILFS_SEG_HAS_SR(ssi); + + /* need to verify ->ss_bytes field if read ->ss_cno */ } /** diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index 17851f77f73..2e6a2723b8f 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -40,35 +40,10 @@ struct nilfs_write_info { sector_t blocknr; }; - static int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf, struct the_nilfs *nilfs); static int nilfs_segbuf_wait(struct nilfs_segment_buffer *segbuf); - -static struct kmem_cache *nilfs_segbuf_cachep; - -static void nilfs_segbuf_init_once(void *obj) -{ - memset(obj, 0, sizeof(struct nilfs_segment_buffer)); -} - -int __init nilfs_init_segbuf_cache(void) -{ - nilfs_segbuf_cachep = - kmem_cache_create("nilfs2_segbuf_cache", - sizeof(struct nilfs_segment_buffer), - 0, SLAB_RECLAIM_ACCOUNT, - nilfs_segbuf_init_once); - - return (nilfs_segbuf_cachep == NULL) ? -ENOMEM : 0; -} - -void nilfs_destroy_segbuf_cache(void) -{ - kmem_cache_destroy(nilfs_segbuf_cachep); -} - struct nilfs_segment_buffer *nilfs_segbuf_new(struct super_block *sb) { struct nilfs_segment_buffer *segbuf; @@ -81,6 +56,7 @@ struct nilfs_segment_buffer *nilfs_segbuf_new(struct super_block *sb) INIT_LIST_HEAD(&segbuf->sb_list); INIT_LIST_HEAD(&segbuf->sb_segsum_buffers); INIT_LIST_HEAD(&segbuf->sb_payload_buffers); + segbuf->sb_super_root = NULL; init_completion(&segbuf->sb_bio_event); atomic_set(&segbuf->sb_err, 0); @@ -158,7 +134,7 @@ int nilfs_segbuf_extend_payload(struct nilfs_segment_buffer *segbuf, } int nilfs_segbuf_reset(struct nilfs_segment_buffer *segbuf, unsigned flags, - time_t ctime) + time_t ctime, __u64 cno) { int err; @@ -171,6 +147,7 @@ int nilfs_segbuf_reset(struct nilfs_segment_buffer *segbuf, unsigned flags, segbuf->sb_sum.sumbytes = sizeof(struct nilfs_segment_summary); segbuf->sb_sum.nfinfo = segbuf->sb_sum.nfileblk = 0; segbuf->sb_sum.ctime = ctime; + segbuf->sb_sum.cno = cno; return 0; } @@ -196,13 +173,14 @@ void nilfs_segbuf_fill_in_segsum(struct nilfs_segment_buffer *segbuf) raw_sum->ss_nfinfo = cpu_to_le32(segbuf->sb_sum.nfinfo); raw_sum->ss_sumbytes = cpu_to_le32(segbuf->sb_sum.sumbytes); raw_sum->ss_pad = 0; + raw_sum->ss_cno = cpu_to_le64(segbuf->sb_sum.cno); } /* * CRC calculation routines */ -void nilfs_segbuf_fill_in_segsum_crc(struct nilfs_segment_buffer *segbuf, - u32 seed) +static void +nilfs_segbuf_fill_in_segsum_crc(struct nilfs_segment_buffer *segbuf, u32 seed) { struct buffer_head *bh; struct nilfs_segment_summary *raw_sum; @@ -229,8 +207,8 @@ void nilfs_segbuf_fill_in_segsum_crc(struct nilfs_segment_buffer *segbuf, raw_sum->ss_sumsum = cpu_to_le32(crc); } -void nilfs_segbuf_fill_in_data_crc(struct nilfs_segment_buffer *segbuf, - u32 seed) +static void nilfs_segbuf_fill_in_data_crc(struct nilfs_segment_buffer *segbuf, + u32 seed) { struct buffer_head *bh; struct nilfs_segment_summary *raw_sum; @@ -256,6 +234,20 @@ void nilfs_segbuf_fill_in_data_crc(struct nilfs_segment_buffer *segbuf, raw_sum->ss_datasum = cpu_to_le32(crc); } +static void +nilfs_segbuf_fill_in_super_root_crc(struct nilfs_segment_buffer *segbuf, + u32 seed) +{ + struct nilfs_super_root *raw_sr; + u32 crc; + + raw_sr = (struct nilfs_super_root *)segbuf->sb_super_root->b_data; + crc = crc32_le(seed, + (unsigned char *)raw_sr + sizeof(raw_sr->sr_sum), + NILFS_SR_BYTES - sizeof(raw_sr->sr_sum)); + raw_sr->sr_sum = cpu_to_le32(crc); +} + static void nilfs_release_buffers(struct list_head *list) { struct buffer_head *bh, *n; @@ -282,6 +274,7 @@ static void nilfs_segbuf_clear(struct nilfs_segment_buffer *segbuf) { nilfs_release_buffers(&segbuf->sb_segsum_buffers); nilfs_release_buffers(&segbuf->sb_payload_buffers); + segbuf->sb_super_root = NULL; } /* @@ -334,6 +327,23 @@ int nilfs_wait_on_logs(struct list_head *logs) return ret; } +/** + * nilfs_add_checksums_on_logs - add checksums on the logs + * @logs: list of segment buffers storing target logs + * @seed: checksum seed value + */ +void nilfs_add_checksums_on_logs(struct list_head *logs, u32 seed) +{ + struct nilfs_segment_buffer *segbuf; + + list_for_each_entry(segbuf, logs, sb_list) { + if (segbuf->sb_super_root) + nilfs_segbuf_fill_in_super_root_crc(segbuf, seed); + nilfs_segbuf_fill_in_segsum_crc(segbuf, seed); + nilfs_segbuf_fill_in_data_crc(segbuf, seed); + } +} + /* * BIO operations */ diff --git a/fs/nilfs2/segbuf.h b/fs/nilfs2/segbuf.h index 94dfd3517bc..fdf1c3b6d67 100644 --- a/fs/nilfs2/segbuf.h +++ b/fs/nilfs2/segbuf.h @@ -37,6 +37,7 @@ * @sumbytes: Byte count of segment summary * @nfileblk: Total number of file blocks * @seg_seq: Segment sequence number + * @cno: Checkpoint number * @ctime: Creation time * @next: Block number of the next full segment */ @@ -48,6 +49,7 @@ struct nilfs_segsum_info { unsigned long sumbytes; unsigned long nfileblk; u64 seg_seq; + __u64 cno; time_t ctime; sector_t next; }; @@ -76,6 +78,7 @@ struct nilfs_segsum_info { * @sb_rest_blocks: Number of residual blocks in the current segment * @sb_segsum_buffers: List of buffers for segment summaries * @sb_payload_buffers: List of buffers for segment payload + * @sb_super_root: Pointer to buffer storing a super root block (if exists) * @sb_nbio: Number of flying bio requests * @sb_err: I/O error status * @sb_bio_event: Completion event of log writing @@ -95,6 +98,7 @@ struct nilfs_segment_buffer { /* Buffers */ struct list_head sb_segsum_buffers; struct list_head sb_payload_buffers; /* including super root */ + struct buffer_head *sb_super_root; /* io status */ int sb_nbio; @@ -121,6 +125,7 @@ struct nilfs_segment_buffer { b_assoc_buffers)) #define NILFS_SEGBUF_BH_IS_LAST(bh, head) ((bh)->b_assoc_buffers.next == head) +extern struct kmem_cache *nilfs_segbuf_cachep; int __init nilfs_init_segbuf_cache(void); void nilfs_destroy_segbuf_cache(void); @@ -132,13 +137,11 @@ void nilfs_segbuf_map_cont(struct nilfs_segment_buffer *segbuf, struct nilfs_segment_buffer *prev); void nilfs_segbuf_set_next_segnum(struct nilfs_segment_buffer *, __u64, struct the_nilfs *); -int nilfs_segbuf_reset(struct nilfs_segment_buffer *, unsigned, time_t); +int nilfs_segbuf_reset(struct nilfs_segment_buffer *, unsigned, time_t, __u64); int nilfs_segbuf_extend_segsum(struct nilfs_segment_buffer *); int nilfs_segbuf_extend_payload(struct nilfs_segment_buffer *, struct buffer_head **); void nilfs_segbuf_fill_in_segsum(struct nilfs_segment_buffer *); -void nilfs_segbuf_fill_in_segsum_crc(struct nilfs_segment_buffer *, u32); -void nilfs_segbuf_fill_in_data_crc(struct nilfs_segment_buffer *, u32); static inline void nilfs_segbuf_add_segsum_buffer(struct nilfs_segment_buffer *segbuf, @@ -171,6 +174,7 @@ void nilfs_truncate_logs(struct list_head *logs, struct nilfs_segment_buffer *last); int nilfs_write_logs(struct list_head *logs, struct the_nilfs *nilfs); int nilfs_wait_on_logs(struct list_head *logs); +void nilfs_add_checksums_on_logs(struct list_head *logs, u32 seed); static inline void nilfs_destroy_logs(struct list_head *logs) { diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 6a7dbd8451d..c9201649cc4 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -116,42 +116,6 @@ static void nilfs_dispose_list(struct nilfs_sb_info *, struct list_head *, #define nilfs_cnt32_lt(a, b) nilfs_cnt32_gt(b, a) #define nilfs_cnt32_le(a, b) nilfs_cnt32_ge(b, a) -/* - * Transaction - */ -static struct kmem_cache *nilfs_transaction_cachep; - -/** - * nilfs_init_transaction_cache - create a cache for nilfs_transaction_info - * - * nilfs_init_transaction_cache() creates a slab cache for the struct - * nilfs_transaction_info. - * - * Return Value: On success, it returns 0. On error, one of the following - * negative error code is returned. - * - * %-ENOMEM - Insufficient memory available. - */ -int nilfs_init_transaction_cache(void) -{ - nilfs_transaction_cachep = - kmem_cache_create("nilfs2_transaction_cache", - sizeof(struct nilfs_transaction_info), - 0, SLAB_RECLAIM_ACCOUNT, NULL); - return (nilfs_transaction_cachep == NULL) ? -ENOMEM : 0; -} - -/** - * nilfs_destroy_transaction_cache - destroy the cache for transaction info - * - * nilfs_destroy_transaction_cache() frees the slab cache for the struct - * nilfs_transaction_info. - */ -void nilfs_destroy_transaction_cache(void) -{ - kmem_cache_destroy(nilfs_transaction_cachep); -} - static int nilfs_prepare_segment_lock(struct nilfs_transaction_info *ti) { struct nilfs_transaction_info *cur_ti = current->journal_info; @@ -402,7 +366,8 @@ static int nilfs_segctor_reset_segment_buffer(struct nilfs_sc_info *sci) if (nilfs_doing_gc()) flags = NILFS_SS_GC; - err = nilfs_segbuf_reset(segbuf, flags, sci->sc_seg_ctime); + err = nilfs_segbuf_reset(segbuf, flags, sci->sc_seg_ctime, + sci->sc_sbi->s_nilfs->ns_cno); if (unlikely(err)) return err; @@ -435,7 +400,7 @@ static int nilfs_segctor_add_super_root(struct nilfs_sc_info *sci) return err; segbuf = sci->sc_curseg; } - err = nilfs_segbuf_extend_payload(segbuf, &sci->sc_super_root); + err = nilfs_segbuf_extend_payload(segbuf, &segbuf->sb_super_root); if (likely(!err)) segbuf->sb_sum.flags |= NILFS_SS_SR; return err; @@ -599,7 +564,7 @@ static void nilfs_write_file_node_binfo(struct nilfs_sc_info *sci, *vblocknr = binfo->bi_v.bi_vblocknr; } -struct nilfs_sc_operations nilfs_sc_file_ops = { +static struct nilfs_sc_operations nilfs_sc_file_ops = { .collect_data = nilfs_collect_file_data, .collect_node = nilfs_collect_file_node, .collect_bmap = nilfs_collect_file_bmap, @@ -649,7 +614,7 @@ static void nilfs_write_dat_node_binfo(struct nilfs_sc_info *sci, *binfo_dat = binfo->bi_dat; } -struct nilfs_sc_operations nilfs_sc_dat_ops = { +static struct nilfs_sc_operations nilfs_sc_dat_ops = { .collect_data = nilfs_collect_dat_data, .collect_node = nilfs_collect_file_node, .collect_bmap = nilfs_collect_dat_bmap, @@ -657,7 +622,7 @@ struct nilfs_sc_operations nilfs_sc_dat_ops = { .write_node_binfo = nilfs_write_dat_node_binfo, }; -struct nilfs_sc_operations nilfs_sc_dsync_ops = { +static struct nilfs_sc_operations nilfs_sc_dsync_ops = { .collect_data = nilfs_collect_file_data, .collect_node = NULL, .collect_bmap = NULL, @@ -932,43 +897,16 @@ static void nilfs_segctor_fill_in_file_bmap(struct nilfs_sc_info *sci, } } -/* - * CRC calculation routines - */ -static void nilfs_fill_in_super_root_crc(struct buffer_head *bh_sr, u32 seed) -{ - struct nilfs_super_root *raw_sr = - (struct nilfs_super_root *)bh_sr->b_data; - u32 crc; - - crc = crc32_le(seed, - (unsigned char *)raw_sr + sizeof(raw_sr->sr_sum), - NILFS_SR_BYTES - sizeof(raw_sr->sr_sum)); - raw_sr->sr_sum = cpu_to_le32(crc); -} - -static void nilfs_segctor_fill_in_checksums(struct nilfs_sc_info *sci, - u32 seed) -{ - struct nilfs_segment_buffer *segbuf; - - if (sci->sc_super_root) - nilfs_fill_in_super_root_crc(sci->sc_super_root, seed); - - list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) { - nilfs_segbuf_fill_in_segsum_crc(segbuf, seed); - nilfs_segbuf_fill_in_data_crc(segbuf, seed); - } -} - static void nilfs_segctor_fill_in_super_root(struct nilfs_sc_info *sci, struct the_nilfs *nilfs) { - struct buffer_head *bh_sr = sci->sc_super_root; - struct nilfs_super_root *raw_sr = - (struct nilfs_super_root *)bh_sr->b_data; + struct buffer_head *bh_sr; + struct nilfs_super_root *raw_sr; unsigned isz = nilfs->ns_inode_size; + bh_sr = NILFS_LAST_SEGBUF(&sci->sc_segbufs)->sb_super_root; + raw_sr = (struct nilfs_super_root *)bh_sr->b_data; + raw_sr->sr_bytes = cpu_to_le16(NILFS_SR_BYTES); raw_sr->sr_nongc_ctime = cpu_to_le64(nilfs_doing_gc() ? @@ -1491,7 +1429,6 @@ static int nilfs_segctor_collect(struct nilfs_sc_info *sci, /* Collection retry loop */ for (;;) { - sci->sc_super_root = NULL; sci->sc_nblk_this_inc = 0; sci->sc_curseg = NILFS_FIRST_SEGBUF(&sci->sc_segbufs); @@ -1568,7 +1505,7 @@ nilfs_segctor_update_payload_blocknr(struct nilfs_sc_info *sci, ssp.offset = sizeof(struct nilfs_segment_summary); list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) { - if (bh == sci->sc_super_root) + if (bh == segbuf->sb_super_root) break; if (!finfo) { finfo = nilfs_segctor_map_segsum_entry( @@ -1729,7 +1666,7 @@ static int nilfs_segctor_prepare_write(struct nilfs_sc_info *sci, list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) { - if (bh == sci->sc_super_root) { + if (bh == segbuf->sb_super_root) { if (bh->b_page != bd_page) { lock_page(bd_page); clear_page_dirty_for_io(bd_page); @@ -1848,7 +1785,7 @@ static void nilfs_clear_copied_buffers(struct list_head *list, int err) } static void nilfs_abort_logs(struct list_head *logs, struct page *failed_page, - struct buffer_head *bh_sr, int err) + int err) { struct nilfs_segment_buffer *segbuf; struct page *bd_page = NULL, *fs_page = NULL; @@ -1869,7 +1806,7 @@ static void nilfs_abort_logs(struct list_head *logs, struct page *failed_page, list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) { - if (bh == bh_sr) { + if (bh == segbuf->sb_super_root) { if (bh->b_page != bd_page) { end_page_writeback(bd_page); bd_page = bh->b_page; @@ -1898,7 +1835,7 @@ static void nilfs_segctor_abort_construction(struct nilfs_sc_info *sci, list_splice_tail_init(&sci->sc_write_logs, &logs); ret = nilfs_wait_on_logs(&logs); - nilfs_abort_logs(&logs, NULL, sci->sc_super_root, ret ? : err); + nilfs_abort_logs(&logs, NULL, ret ? : err); list_splice_tail_init(&sci->sc_segbufs, &logs); nilfs_cancel_segusage(&logs, nilfs->ns_sufile); @@ -1914,7 +1851,6 @@ static void nilfs_segctor_abort_construction(struct nilfs_sc_info *sci, } nilfs_destroy_logs(&logs); - sci->sc_super_root = NULL; } static void nilfs_set_next_segment(struct the_nilfs *nilfs, @@ -1933,7 +1869,7 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci) struct nilfs_segment_buffer *segbuf; struct page *bd_page = NULL, *fs_page = NULL; struct the_nilfs *nilfs = sci->sc_sbi->s_nilfs; - int update_sr = (sci->sc_super_root != NULL); + int update_sr = false; list_for_each_entry(segbuf, &sci->sc_write_logs, sb_list) { struct buffer_head *bh; @@ -1964,11 +1900,12 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci) set_buffer_uptodate(bh); clear_buffer_dirty(bh); clear_buffer_nilfs_volatile(bh); - if (bh == sci->sc_super_root) { + if (bh == segbuf->sb_super_root) { if (bh->b_page != bd_page) { end_page_writeback(bd_page); bd_page = bh->b_page; } + update_sr = true; break; } if (bh->b_page != fs_page) { @@ -2115,7 +2052,7 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) struct nilfs_sb_info *sbi = sci->sc_sbi; struct the_nilfs *nilfs = sbi->s_nilfs; struct page *failed_page; - int err, has_sr = 0; + int err; sci->sc_stage.scnt = NILFS_ST_INIT; @@ -2143,8 +2080,6 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) if (unlikely(err)) goto failed; - has_sr = (sci->sc_super_root != NULL); - /* Avoid empty segment */ if (sci->sc_stage.scnt == NILFS_ST_DONE && NILFS_SEG_EMPTY(&sci->sc_curseg->sb_sum)) { @@ -2159,7 +2094,8 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) if (sci->sc_stage.flags & NILFS_CF_IFILE_STARTED) nilfs_segctor_fill_in_file_bmap(sci, sbi->s_ifile); - if (has_sr) { + if (mode == SC_LSEG_SR && + sci->sc_stage.scnt >= NILFS_ST_CPFILE) { err = nilfs_segctor_fill_in_checkpoint(sci); if (unlikely(err)) goto failed_to_write; @@ -2171,11 +2107,12 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) /* Write partial segments */ err = nilfs_segctor_prepare_write(sci, &failed_page); if (err) { - nilfs_abort_logs(&sci->sc_segbufs, failed_page, - sci->sc_super_root, err); + nilfs_abort_logs(&sci->sc_segbufs, failed_page, err); goto failed_to_write; } - nilfs_segctor_fill_in_checksums(sci, nilfs->ns_crc_seed); + + nilfs_add_checksums_on_logs(&sci->sc_segbufs, + nilfs->ns_crc_seed); err = nilfs_segctor_write(sci, nilfs); if (unlikely(err)) @@ -2196,8 +2133,6 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) } } while (sci->sc_stage.scnt != NILFS_ST_DONE); - sci->sc_super_root = NULL; - out: nilfs_segctor_check_out_files(sci, sbi); return err; @@ -2224,9 +2159,9 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) static void nilfs_segctor_start_timer(struct nilfs_sc_info *sci) { spin_lock(&sci->sc_state_lock); - if (sci->sc_timer && !(sci->sc_state & NILFS_SEGCTOR_COMMIT)) { - sci->sc_timer->expires = jiffies + sci->sc_interval; - add_timer(sci->sc_timer); + if (!(sci->sc_state & NILFS_SEGCTOR_COMMIT)) { + sci->sc_timer.expires = jiffies + sci->sc_interval; + add_timer(&sci->sc_timer); sci->sc_state |= NILFS_SEGCTOR_COMMIT; } spin_unlock(&sci->sc_state_lock); @@ -2431,9 +2366,7 @@ static void nilfs_segctor_accept(struct nilfs_sc_info *sci) spin_lock(&sci->sc_state_lock); sci->sc_seq_accepted = sci->sc_seq_request; spin_unlock(&sci->sc_state_lock); - - if (sci->sc_timer) - del_timer_sync(sci->sc_timer); + del_timer_sync(&sci->sc_timer); } /** @@ -2459,9 +2392,9 @@ static void nilfs_segctor_notify(struct nilfs_sc_info *sci, int mode, int err) sci->sc_flush_request &= ~FLUSH_DAT_BIT; /* re-enable timer if checkpoint creation was not done */ - if (sci->sc_timer && (sci->sc_state & NILFS_SEGCTOR_COMMIT) && - time_before(jiffies, sci->sc_timer->expires)) - add_timer(sci->sc_timer); + if ((sci->sc_state & NILFS_SEGCTOR_COMMIT) && + time_before(jiffies, sci->sc_timer.expires)) + add_timer(&sci->sc_timer); } spin_unlock(&sci->sc_state_lock); } @@ -2640,13 +2573,10 @@ static int nilfs_segctor_thread(void *arg) { struct nilfs_sc_info *sci = (struct nilfs_sc_info *)arg; struct the_nilfs *nilfs = sci->sc_sbi->s_nilfs; - struct timer_list timer; int timeout = 0; - init_timer(&timer); - timer.data = (unsigned long)current; - timer.function = nilfs_construction_timeout; - sci->sc_timer = &timer; + sci->sc_timer.data = (unsigned long)current; + sci->sc_timer.function = nilfs_construction_timeout; /* start sync. */ sci->sc_task = current; @@ -2695,7 +2625,7 @@ static int nilfs_segctor_thread(void *arg) should_sleep = 0; else if (sci->sc_state & NILFS_SEGCTOR_COMMIT) should_sleep = time_before(jiffies, - sci->sc_timer->expires); + sci->sc_timer.expires); if (should_sleep) { spin_unlock(&sci->sc_state_lock); @@ -2704,7 +2634,7 @@ static int nilfs_segctor_thread(void *arg) } finish_wait(&sci->sc_wait_daemon, &wait); timeout = ((sci->sc_state & NILFS_SEGCTOR_COMMIT) && - time_after_eq(jiffies, sci->sc_timer->expires)); + time_after_eq(jiffies, sci->sc_timer.expires)); if (nilfs_sb_dirty(nilfs) && nilfs_sb_need_update(nilfs)) set_nilfs_discontinued(nilfs); @@ -2713,8 +2643,6 @@ static int nilfs_segctor_thread(void *arg) end_thread: spin_unlock(&sci->sc_state_lock); - del_timer_sync(sci->sc_timer); - sci->sc_timer = NULL; /* end sync. */ sci->sc_task = NULL; @@ -2750,13 +2678,6 @@ static void nilfs_segctor_kill_thread(struct nilfs_sc_info *sci) } } -static int nilfs_segctor_init(struct nilfs_sc_info *sci) -{ - sci->sc_seq_done = sci->sc_seq_request; - - return nilfs_segctor_start_thread(sci); -} - /* * Setup & clean-up functions */ @@ -2780,6 +2701,7 @@ static struct nilfs_sc_info *nilfs_segctor_new(struct nilfs_sb_info *sbi) INIT_LIST_HEAD(&sci->sc_write_logs); INIT_LIST_HEAD(&sci->sc_gc_inodes); INIT_LIST_HEAD(&sci->sc_copied_buffers); + init_timer(&sci->sc_timer); sci->sc_interval = HZ * NILFS_SC_DEFAULT_TIMEOUT; sci->sc_mjcp_freq = HZ * NILFS_SC_DEFAULT_SR_FREQ; @@ -2846,6 +2768,7 @@ static void nilfs_segctor_destroy(struct nilfs_sc_info *sci) down_write(&sbi->s_nilfs->ns_segctor_sem); + del_timer_sync(&sci->sc_timer); kfree(sci); } @@ -2880,7 +2803,7 @@ int nilfs_attach_segment_constructor(struct nilfs_sb_info *sbi) return -ENOMEM; nilfs_attach_writer(nilfs, sbi); - err = nilfs_segctor_init(NILFS_SC(sbi)); + err = nilfs_segctor_start_thread(NILFS_SC(sbi)); if (err) { nilfs_detach_writer(nilfs, sbi); kfree(sbi->s_sc_info); diff --git a/fs/nilfs2/segment.h b/fs/nilfs2/segment.h index 82dfd6a686b..dca142361cc 100644 --- a/fs/nilfs2/segment.h +++ b/fs/nilfs2/segment.h @@ -100,7 +100,6 @@ struct nilfs_segsum_pointer { * @sc_write_logs: List of segment buffers to hold logs under writing * @sc_segbuf_nblocks: Number of available blocks in segment buffers. * @sc_curseg: Current segment buffer - * @sc_super_root: Pointer to the super root buffer * @sc_stage: Collection stage * @sc_finfo_ptr: pointer to the current finfo struct in the segment summary * @sc_binfo_ptr: pointer to the current binfo struct in the segment summary @@ -148,7 +147,6 @@ struct nilfs_sc_info { struct list_head sc_write_logs; unsigned long sc_segbuf_nblocks; struct nilfs_segment_buffer *sc_curseg; - struct buffer_head *sc_super_root; struct nilfs_cstage sc_stage; @@ -179,7 +177,7 @@ struct nilfs_sc_info { unsigned long sc_lseg_stime; /* in 1/HZ seconds */ unsigned long sc_watermark; - struct timer_list *sc_timer; + struct timer_list sc_timer; struct task_struct *sc_task; }; @@ -219,6 +217,8 @@ enum { */ #define NILFS_SC_DEFAULT_WATERMARK 3600 +/* super.c */ +extern struct kmem_cache *nilfs_transaction_cachep; /* segment.c */ extern int nilfs_init_transaction_cache(void); diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 48145f505a6..03b34b73899 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -67,6 +67,11 @@ MODULE_DESCRIPTION("A New Implementation of the Log-structured Filesystem " "(NILFS)"); MODULE_LICENSE("GPL"); +struct kmem_cache *nilfs_inode_cachep; +struct kmem_cache *nilfs_transaction_cachep; +struct kmem_cache *nilfs_segbuf_cachep; +struct kmem_cache *nilfs_btree_path_cache; + static int nilfs_remount(struct super_block *sb, int *flags, char *data); /** @@ -129,7 +134,6 @@ void nilfs_warning(struct super_block *sb, const char *function, va_end(args); } -static struct kmem_cache *nilfs_inode_cachep; struct inode *nilfs_alloc_inode_common(struct the_nilfs *nilfs) { @@ -155,34 +159,6 @@ void nilfs_destroy_inode(struct inode *inode) kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode)); } -static void init_once(void *obj) -{ - struct nilfs_inode_info *ii = obj; - - INIT_LIST_HEAD(&ii->i_dirty); -#ifdef CONFIG_NILFS_XATTR - init_rwsem(&ii->xattr_sem); -#endif - nilfs_btnode_cache_init_once(&ii->i_btnode_cache); - ii->i_bmap = (struct nilfs_bmap *)&ii->i_bmap_union; - inode_init_once(&ii->vfs_inode); -} - -static int nilfs_init_inode_cache(void) -{ - nilfs_inode_cachep = kmem_cache_create("nilfs2_inode_cache", - sizeof(struct nilfs_inode_info), - 0, SLAB_RECLAIM_ACCOUNT, - init_once); - - return (nilfs_inode_cachep == NULL) ? -ENOMEM : 0; -} - -static inline void nilfs_destroy_inode_cache(void) -{ - kmem_cache_destroy(nilfs_inode_cachep); -} - static void nilfs_clear_inode(struct inode *inode) { struct nilfs_inode_info *ii = NILFS_I(inode); @@ -266,8 +242,8 @@ int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb) int err; /* nilfs->sem must be locked by the caller. */ - if (sbp[0]->s_magic != NILFS_SUPER_MAGIC) { - if (sbp[1] && sbp[1]->s_magic == NILFS_SUPER_MAGIC) + if (sbp[0]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) { + if (sbp[1] && sbp[1]->s_magic == cpu_to_le16(NILFS_SUPER_MAGIC)) nilfs_swap_super_block(nilfs); else { printk(KERN_CRIT "NILFS: superblock broke on dev %s\n", @@ -470,10 +446,10 @@ static int nilfs_show_options(struct seq_file *seq, struct vfsmount *vfs) if (nilfs_test_opt(sbi, SNAPSHOT)) seq_printf(seq, ",cp=%llu", (unsigned long long int)sbi->s_snapshot_cno); - if (nilfs_test_opt(sbi, ERRORS_RO)) - seq_printf(seq, ",errors=remount-ro"); if (nilfs_test_opt(sbi, ERRORS_PANIC)) seq_printf(seq, ",errors=panic"); + if (nilfs_test_opt(sbi, ERRORS_CONT)) + seq_printf(seq, ",errors=continue"); if (nilfs_test_opt(sbi, STRICT_ORDER)) seq_printf(seq, ",order=strict"); if (nilfs_test_opt(sbi, NORECOVERY)) @@ -631,7 +607,7 @@ nilfs_set_default_options(struct nilfs_sb_info *sbi, struct nilfs_super_block *sbp) { sbi->s_mount_opt = - NILFS_MOUNT_ERRORS_CONT | NILFS_MOUNT_BARRIER; + NILFS_MOUNT_ERRORS_RO | NILFS_MOUNT_BARRIER; } static int nilfs_setup_super(struct nilfs_sb_info *sbi) @@ -778,9 +754,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, goto failed_sbi; } cno = sbi->s_snapshot_cno; - } else - /* Read-only mount */ - sbi->s_snapshot_cno = cno; + } } err = nilfs_attach_checkpoint(sbi, cno); @@ -849,7 +823,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) struct the_nilfs *nilfs = sbi->s_nilfs; unsigned long old_sb_flags; struct nilfs_mount_options old_opts; - int err; + int was_snapshot, err; lock_kernel(); @@ -857,6 +831,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) old_sb_flags = sb->s_flags; old_opts.mount_opt = sbi->s_mount_opt; old_opts.snapshot_cno = sbi->s_snapshot_cno; + was_snapshot = nilfs_test_opt(sbi, SNAPSHOT); if (!parse_options(data, sb)) { err = -EINVAL; @@ -864,20 +839,32 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) } sb->s_flags = (sb->s_flags & ~MS_POSIXACL); - if ((*flags & MS_RDONLY) && - sbi->s_snapshot_cno != old_opts.snapshot_cno) { - printk(KERN_WARNING "NILFS (device %s): couldn't " - "remount to a different snapshot.\n", - sb->s_id); - err = -EINVAL; - goto restore_opts; + err = -EINVAL; + if (was_snapshot) { + if (!(*flags & MS_RDONLY)) { + printk(KERN_ERR "NILFS (device %s): cannot remount " + "snapshot read/write.\n", + sb->s_id); + goto restore_opts; + } else if (sbi->s_snapshot_cno != old_opts.snapshot_cno) { + printk(KERN_ERR "NILFS (device %s): cannot " + "remount to a different snapshot.\n", + sb->s_id); + goto restore_opts; + } + } else { + if (nilfs_test_opt(sbi, SNAPSHOT)) { + printk(KERN_ERR "NILFS (device %s): cannot change " + "a regular mount to a snapshot.\n", + sb->s_id); + goto restore_opts; + } } if (!nilfs_valid_fs(nilfs)) { printk(KERN_WARNING "NILFS (device %s): couldn't " "remount because the filesystem is in an " "incomplete recovery state.\n", sb->s_id); - err = -EINVAL; goto restore_opts; } @@ -888,9 +875,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) nilfs_detach_segment_constructor(sbi); sb->s_flags |= MS_RDONLY; - sbi->s_snapshot_cno = nilfs_last_cno(nilfs); - /* nilfs_set_opt(sbi, SNAPSHOT); */ - /* * Remounting a valid RW partition RDONLY, so set * the RDONLY flag and then mark the partition as valid again. @@ -909,24 +893,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) * store the current valid flag. (It may have been changed * by fsck since we originally mounted the partition.) */ - if (nilfs->ns_current && nilfs->ns_current != sbi) { - printk(KERN_WARNING "NILFS (device %s): couldn't " - "remount because an RW-mount exists.\n", - sb->s_id); - err = -EBUSY; - goto restore_opts; - } - if (sbi->s_snapshot_cno != nilfs_last_cno(nilfs)) { - printk(KERN_WARNING "NILFS (device %s): couldn't " - "remount because the current RO-mount is not " - "the latest one.\n", - sb->s_id); - err = -EINVAL; - goto restore_opts; - } sb->s_flags &= ~MS_RDONLY; - nilfs_clear_opt(sbi, SNAPSHOT); - sbi->s_snapshot_cno = 0; err = nilfs_attach_segment_constructor(sbi); if (err) @@ -935,8 +902,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) down_write(&nilfs->ns_sem); nilfs_setup_super(sbi); up_write(&nilfs->ns_sem); - - nilfs->ns_current = sbi; } out: up_write(&nilfs->ns_super_sem); @@ -1022,10 +987,14 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, { struct nilfs_super_data sd; struct super_block *s; + fmode_t mode = FMODE_READ; struct the_nilfs *nilfs; int err, need_to_close = 1; - sd.bdev = open_bdev_exclusive(dev_name, flags, fs_type); + if (!(flags & MS_RDONLY)) + mode |= FMODE_WRITE; + + sd.bdev = open_bdev_exclusive(dev_name, mode, fs_type); if (IS_ERR(sd.bdev)) return PTR_ERR(sd.bdev); @@ -1092,10 +1061,12 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, /* New superblock instance created */ s->s_flags = flags; + s->s_mode = mode; strlcpy(s->s_id, bdevname(sd.bdev, b), sizeof(s->s_id)); sb_set_blocksize(s, block_size(sd.bdev)); - err = nilfs_fill_super(s, data, flags & MS_VERBOSE, nilfs); + err = nilfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0, + nilfs); if (err) goto cancel_new; @@ -1106,7 +1077,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, mutex_unlock(&nilfs->ns_mount_mutex); put_nilfs(nilfs); if (need_to_close) - close_bdev_exclusive(sd.bdev, flags); + close_bdev_exclusive(sd.bdev, mode); simple_set_mnt(mnt, s); return 0; @@ -1114,7 +1085,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, mutex_unlock(&nilfs->ns_mount_mutex); put_nilfs(nilfs); failed: - close_bdev_exclusive(sd.bdev, flags); + close_bdev_exclusive(sd.bdev, mode); return err; @@ -1124,7 +1095,7 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags, put_nilfs(nilfs); deactivate_locked_super(s); /* - * deactivate_super() invokes close_bdev_exclusive(). + * deactivate_locked_super() invokes close_bdev_exclusive(). * We must finish all post-cleaning before this call; * put_nilfs() needs the block device. */ @@ -1139,54 +1110,93 @@ struct file_system_type nilfs_fs_type = { .fs_flags = FS_REQUIRES_DEV, }; -static int __init init_nilfs_fs(void) +static void nilfs_inode_init_once(void *obj) { - int err; - - err = nilfs_init_inode_cache(); - if (err) - goto failed; + struct nilfs_inode_info *ii = obj; - err = nilfs_init_transaction_cache(); - if (err) - goto failed_inode_cache; + INIT_LIST_HEAD(&ii->i_dirty); +#ifdef CONFIG_NILFS_XATTR + init_rwsem(&ii->xattr_sem); +#endif + nilfs_btnode_cache_init_once(&ii->i_btnode_cache); + ii->i_bmap = (struct nilfs_bmap *)&ii->i_bmap_union; + inode_init_once(&ii->vfs_inode); +} - err = nilfs_init_segbuf_cache(); - if (err) - goto failed_transaction_cache; +static void nilfs_segbuf_init_once(void *obj) +{ + memset(obj, 0, sizeof(struct nilfs_segment_buffer)); +} - err = nilfs_btree_path_cache_init(); - if (err) - goto failed_segbuf_cache; +static void nilfs_destroy_cachep(void) +{ + if (nilfs_inode_cachep) + kmem_cache_destroy(nilfs_inode_cachep); + if (nilfs_transaction_cachep) + kmem_cache_destroy(nilfs_transaction_cachep); + if (nilfs_segbuf_cachep) + kmem_cache_destroy(nilfs_segbuf_cachep); + if (nilfs_btree_path_cache) + kmem_cache_destroy(nilfs_btree_path_cache); +} - err = register_filesystem(&nilfs_fs_type); - if (err) - goto failed_btree_path_cache; +static int __init nilfs_init_cachep(void) +{ + nilfs_inode_cachep = kmem_cache_create("nilfs2_inode_cache", + sizeof(struct nilfs_inode_info), 0, + SLAB_RECLAIM_ACCOUNT, nilfs_inode_init_once); + if (!nilfs_inode_cachep) + goto fail; + + nilfs_transaction_cachep = kmem_cache_create("nilfs2_transaction_cache", + sizeof(struct nilfs_transaction_info), 0, + SLAB_RECLAIM_ACCOUNT, NULL); + if (!nilfs_transaction_cachep) + goto fail; + + nilfs_segbuf_cachep = kmem_cache_create("nilfs2_segbuf_cache", + sizeof(struct nilfs_segment_buffer), 0, + SLAB_RECLAIM_ACCOUNT, nilfs_segbuf_init_once); + if (!nilfs_segbuf_cachep) + goto fail; + + nilfs_btree_path_cache = kmem_cache_create("nilfs2_btree_path_cache", + sizeof(struct nilfs_btree_path) * NILFS_BTREE_LEVEL_MAX, + 0, 0, NULL); + if (!nilfs_btree_path_cache) + goto fail; return 0; - failed_btree_path_cache: - nilfs_btree_path_cache_destroy(); +fail: + nilfs_destroy_cachep(); + return -ENOMEM; +} + +static int __init init_nilfs_fs(void) +{ + int err; - failed_segbuf_cache: - nilfs_destroy_segbuf_cache(); + err = nilfs_init_cachep(); + if (err) + goto fail; - failed_transaction_cache: - nilfs_destroy_transaction_cache(); + err = register_filesystem(&nilfs_fs_type); + if (err) + goto free_cachep; - failed_inode_cache: - nilfs_destroy_inode_cache(); + printk(KERN_INFO "NILFS version 2 loaded\n"); + return 0; - failed: +free_cachep: + nilfs_destroy_cachep(); +fail: return err; } static void __exit exit_nilfs_fs(void) { - nilfs_destroy_segbuf_cache(); - nilfs_destroy_transaction_cache(); - nilfs_destroy_inode_cache(); - nilfs_btree_path_cache_destroy(); + nilfs_destroy_cachep(); unregister_filesystem(&nilfs_fs_type); } diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 33871f7e4f0..a756168a21c 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -486,11 +486,15 @@ static int nilfs_load_super_block(struct the_nilfs *nilfs, printk(KERN_WARNING "NILFS warning: unable to read secondary superblock\n"); + /* + * Compare two super blocks and set 1 in swp if the secondary + * super block is valid and newer. Otherwise, set 0 in swp. + */ valid[0] = nilfs_valid_sb(sbp[0]); valid[1] = nilfs_valid_sb(sbp[1]); - swp = valid[1] && - (!valid[0] || - le64_to_cpu(sbp[1]->s_wtime) > le64_to_cpu(sbp[0]->s_wtime)); + swp = valid[1] && (!valid[0] || + le64_to_cpu(sbp[1]->s_last_cno) > + le64_to_cpu(sbp[0]->s_last_cno)); if (valid[swp] && nilfs_sb2_bad_offset(sbp[swp], sb2off)) { brelse(sbh[1]); diff --git a/fs/ocfs2/Makefile b/fs/ocfs2/Makefile index 791c0886c06..07d9fd85435 100644 --- a/fs/ocfs2/Makefile +++ b/fs/ocfs2/Makefile @@ -29,6 +29,7 @@ ocfs2-objs := \ mmap.o \ namei.o \ refcounttree.o \ + reservations.o \ resize.o \ slot_map.o \ suballoc.o \ diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 9f8bd913c51..215e12ce1d8 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -1006,7 +1006,7 @@ static int ocfs2_create_new_meta_bhs(handle_t *handle, int count, status, i; u16 suballoc_bit_start; u32 num_got; - u64 first_blkno; + u64 suballoc_loc, first_blkno; struct ocfs2_super *osb = OCFS2_SB(ocfs2_metadata_cache_get_super(et->et_ci)); struct ocfs2_extent_block *eb; @@ -1015,10 +1015,10 @@ static int ocfs2_create_new_meta_bhs(handle_t *handle, count = 0; while (count < wanted) { - status = ocfs2_claim_metadata(osb, - handle, + status = ocfs2_claim_metadata(handle, meta_ac, wanted - count, + &suballoc_loc, &suballoc_bit_start, &num_got, &first_blkno); @@ -1052,6 +1052,7 @@ static int ocfs2_create_new_meta_bhs(handle_t *handle, eb->h_fs_generation = cpu_to_le32(osb->fs_generation); eb->h_suballoc_slot = cpu_to_le16(meta_ac->ac_alloc_slot); + eb->h_suballoc_loc = cpu_to_le64(suballoc_loc); eb->h_suballoc_bit = cpu_to_le16(suballoc_bit_start); eb->h_list.l_count = cpu_to_le16(ocfs2_extent_recs_per_eb(osb->sb)); @@ -1061,11 +1062,7 @@ static int ocfs2_create_new_meta_bhs(handle_t *handle, /* We'll also be dirtied by the caller, so * this isn't absolutely necessary. */ - status = ocfs2_journal_dirty(handle, bhs[i]); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, bhs[i]); } count += num_got; @@ -1129,8 +1126,7 @@ static int ocfs2_adjust_rightmost_branch(handle_t *handle, goto out; } - status = ocfs2_extend_trans(handle, path_num_items(path) + - handle->h_buffer_credits); + status = ocfs2_extend_trans(handle, path_num_items(path)); if (status < 0) { mlog_errno(status); goto out; @@ -1270,12 +1266,7 @@ static int ocfs2_add_branch(handle_t *handle, if (!eb_el->l_tree_depth) new_last_eb_blk = le64_to_cpu(eb->h_blkno); - status = ocfs2_journal_dirty(handle, bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } - + ocfs2_journal_dirty(handle, bh); next_blkno = le64_to_cpu(eb->h_blkno); } @@ -1321,17 +1312,10 @@ static int ocfs2_add_branch(handle_t *handle, eb = (struct ocfs2_extent_block *) (*last_eb_bh)->b_data; eb->h_next_leaf_blk = cpu_to_le64(new_last_eb_blk); - status = ocfs2_journal_dirty(handle, *last_eb_bh); - if (status < 0) - mlog_errno(status); - status = ocfs2_journal_dirty(handle, et->et_root_bh); - if (status < 0) - mlog_errno(status); - if (eb_bh) { - status = ocfs2_journal_dirty(handle, eb_bh); - if (status < 0) - mlog_errno(status); - } + ocfs2_journal_dirty(handle, *last_eb_bh); + ocfs2_journal_dirty(handle, et->et_root_bh); + if (eb_bh) + ocfs2_journal_dirty(handle, eb_bh); /* * Some callers want to track the rightmost leaf so pass it @@ -1399,11 +1383,7 @@ static int ocfs2_shift_tree_depth(handle_t *handle, for (i = 0; i < le16_to_cpu(root_el->l_next_free_rec); i++) eb_el->l_recs[i] = root_el->l_recs[i]; - status = ocfs2_journal_dirty(handle, new_eb_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, new_eb_bh); status = ocfs2_et_root_journal_access(handle, et, OCFS2_JOURNAL_ACCESS_WRITE); @@ -1428,11 +1408,7 @@ static int ocfs2_shift_tree_depth(handle_t *handle, if (root_el->l_tree_depth == cpu_to_le16(1)) ocfs2_et_set_last_eb_blk(et, le64_to_cpu(eb->h_blkno)); - status = ocfs2_journal_dirty(handle, et->et_root_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, et->et_root_bh); *ret_new_eb_bh = new_eb_bh; new_eb_bh = NULL; @@ -2064,7 +2040,7 @@ static void ocfs2_complete_edge_insert(handle_t *handle, struct ocfs2_path *right_path, int subtree_index) { - int ret, i, idx; + int i, idx; struct ocfs2_extent_list *el, *left_el, *right_el; struct ocfs2_extent_rec *left_rec, *right_rec; struct buffer_head *root_bh = left_path->p_node[subtree_index].bh; @@ -2102,13 +2078,8 @@ static void ocfs2_complete_edge_insert(handle_t *handle, ocfs2_adjust_adjacent_records(left_rec, left_el, right_rec, right_el); - ret = ocfs2_journal_dirty(handle, left_path->p_node[i].bh); - if (ret) - mlog_errno(ret); - - ret = ocfs2_journal_dirty(handle, right_path->p_node[i].bh); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, left_path->p_node[i].bh); + ocfs2_journal_dirty(handle, right_path->p_node[i].bh); /* * Setup our list pointers now so that the current @@ -2132,9 +2103,7 @@ static void ocfs2_complete_edge_insert(handle_t *handle, root_bh = left_path->p_node[subtree_index].bh; - ret = ocfs2_journal_dirty(handle, root_bh); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, root_bh); } static int ocfs2_rotate_subtree_right(handle_t *handle, @@ -2207,11 +2176,7 @@ static int ocfs2_rotate_subtree_right(handle_t *handle, ocfs2_create_empty_extent(right_el); - ret = ocfs2_journal_dirty(handle, right_leaf_bh); - if (ret) { - mlog_errno(ret); - goto out; - } + ocfs2_journal_dirty(handle, right_leaf_bh); /* Do the copy now. */ i = le16_to_cpu(left_el->l_next_free_rec) - 1; @@ -2230,11 +2195,7 @@ static int ocfs2_rotate_subtree_right(handle_t *handle, memset(&left_el->l_recs[0], 0, sizeof(struct ocfs2_extent_rec)); le16_add_cpu(&left_el->l_next_free_rec, 1); - ret = ocfs2_journal_dirty(handle, left_leaf_bh); - if (ret) { - mlog_errno(ret); - goto out; - } + ocfs2_journal_dirty(handle, left_leaf_bh); ocfs2_complete_edge_insert(handle, left_path, right_path, subtree_index); @@ -2249,8 +2210,8 @@ out: * * Will return zero if the path passed in is already the leftmost path. */ -static int ocfs2_find_cpos_for_left_leaf(struct super_block *sb, - struct ocfs2_path *path, u32 *cpos) +int ocfs2_find_cpos_for_left_leaf(struct super_block *sb, + struct ocfs2_path *path, u32 *cpos) { int i, j, ret = 0; u64 blkno; @@ -2327,20 +2288,14 @@ static int ocfs2_extend_rotate_transaction(handle_t *handle, int subtree_depth, int op_credits, struct ocfs2_path *path) { - int ret; + int ret = 0; int credits = (path->p_tree_depth - subtree_depth) * 2 + 1 + op_credits; - if (handle->h_buffer_credits < credits) { + if (handle->h_buffer_credits < credits) ret = ocfs2_extend_trans(handle, credits - handle->h_buffer_credits); - if (ret) - return ret; - if (unlikely(handle->h_buffer_credits < credits)) - return ocfs2_extend_trans(handle, credits); - } - - return 0; + return ret; } /* @@ -2584,8 +2539,7 @@ static int ocfs2_update_edge_lengths(handle_t *handle, * records for all the bh in the path. * So we have to allocate extra credits and access them. */ - ret = ocfs2_extend_trans(handle, - handle->h_buffer_credits + subtree_index); + ret = ocfs2_extend_trans(handle, subtree_index); if (ret) { mlog_errno(ret); goto out; @@ -2823,12 +2777,8 @@ static int ocfs2_rotate_subtree_left(handle_t *handle, ocfs2_remove_empty_extent(right_leaf_el); } - ret = ocfs2_journal_dirty(handle, path_leaf_bh(left_path)); - if (ret) - mlog_errno(ret); - ret = ocfs2_journal_dirty(handle, path_leaf_bh(right_path)); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, path_leaf_bh(left_path)); + ocfs2_journal_dirty(handle, path_leaf_bh(right_path)); if (del_right_subtree) { ocfs2_unlink_subtree(handle, et, left_path, right_path, @@ -2851,9 +2801,7 @@ static int ocfs2_rotate_subtree_left(handle_t *handle, if (right_has_empty) ocfs2_remove_empty_extent(left_leaf_el); - ret = ocfs2_journal_dirty(handle, et_root_bh); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, et_root_bh); *deleted = 1; } else @@ -2962,10 +2910,7 @@ static int ocfs2_rotate_rightmost_leaf_left(handle_t *handle, } ocfs2_remove_empty_extent(el); - - ret = ocfs2_journal_dirty(handle, bh); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, bh); out: return ret; @@ -3506,15 +3451,9 @@ static int ocfs2_merge_rec_right(struct ocfs2_path *left_path, ocfs2_cleanup_merge(el, index); - ret = ocfs2_journal_dirty(handle, bh); - if (ret) - mlog_errno(ret); - + ocfs2_journal_dirty(handle, bh); if (right_path) { - ret = ocfs2_journal_dirty(handle, path_leaf_bh(right_path)); - if (ret) - mlog_errno(ret); - + ocfs2_journal_dirty(handle, path_leaf_bh(right_path)); ocfs2_complete_edge_insert(handle, left_path, right_path, subtree_index); } @@ -3683,14 +3622,9 @@ static int ocfs2_merge_rec_left(struct ocfs2_path *right_path, ocfs2_cleanup_merge(el, index); - ret = ocfs2_journal_dirty(handle, bh); - if (ret) - mlog_errno(ret); - + ocfs2_journal_dirty(handle, bh); if (left_path) { - ret = ocfs2_journal_dirty(handle, path_leaf_bh(left_path)); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, path_leaf_bh(left_path)); /* * In the situation that the right_rec is empty and the extent @@ -4016,10 +3950,7 @@ static void ocfs2_adjust_rightmost_records(handle_t *handle, le32_add_cpu(&rec->e_int_clusters, -le32_to_cpu(rec->e_cpos)); - ret = ocfs2_journal_dirty(handle, bh); - if (ret) - mlog_errno(ret); - + ocfs2_journal_dirty(handle, bh); } } @@ -4203,17 +4134,13 @@ static int ocfs2_insert_path(handle_t *handle, struct buffer_head *leaf_bh = path_leaf_bh(right_path); if (left_path) { - int credits = handle->h_buffer_credits; - /* * There's a chance that left_path got passed back to * us without being accounted for in the * journal. Extend our transaction here to be sure we * can change those blocks. */ - credits += left_path->p_tree_depth; - - ret = ocfs2_extend_trans(handle, credits); + ret = ocfs2_extend_trans(handle, left_path->p_tree_depth); if (ret < 0) { mlog_errno(ret); goto out; @@ -4251,17 +4178,13 @@ static int ocfs2_insert_path(handle_t *handle, * dirty this for us. */ if (left_path) - ret = ocfs2_journal_dirty(handle, - path_leaf_bh(left_path)); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, + path_leaf_bh(left_path)); } else ocfs2_insert_at_leaf(et, insert_rec, path_leaf_el(right_path), insert); - ret = ocfs2_journal_dirty(handle, leaf_bh); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, leaf_bh); if (left_path) { /* @@ -4384,9 +4307,7 @@ out_update_clusters: ocfs2_et_update_clusters(et, le16_to_cpu(insert_rec->e_leaf_clusters)); - ret = ocfs2_journal_dirty(handle, et->et_root_bh); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, et->et_root_bh); out: ocfs2_free_path(left_path); @@ -4866,7 +4787,7 @@ int ocfs2_add_clusters_in_btree(handle_t *handle, goto leave; } - status = __ocfs2_claim_clusters(osb, handle, data_ac, 1, + status = __ocfs2_claim_clusters(handle, data_ac, 1, clusters_to_add, &bit_off, &num_bits); if (status < 0) { if (status != -ENOSPC) @@ -4895,11 +4816,7 @@ int ocfs2_add_clusters_in_btree(handle_t *handle, goto leave; } - status = ocfs2_journal_dirty(handle, et->et_root_bh); - if (status < 0) { - mlog_errno(status); - goto leave; - } + ocfs2_journal_dirty(handle, et->et_root_bh); clusters_to_add -= num_bits; *logical_offset += num_bits; @@ -5309,7 +5226,7 @@ static int ocfs2_split_tree(handle_t *handle, struct ocfs2_extent_tree *et, int index, u32 new_range, struct ocfs2_alloc_context *meta_ac) { - int ret, depth, credits = handle->h_buffer_credits; + int ret, depth, credits; struct buffer_head *last_eb_bh = NULL; struct ocfs2_extent_block *eb; struct ocfs2_extent_list *rightmost_el, *el; @@ -5340,8 +5257,8 @@ static int ocfs2_split_tree(handle_t *handle, struct ocfs2_extent_tree *et, } else rightmost_el = path_leaf_el(path); - credits += path->p_tree_depth + - ocfs2_extend_meta_needed(et->et_root_el); + credits = path->p_tree_depth + + ocfs2_extend_meta_needed(et->et_root_el); ret = ocfs2_extend_trans(handle, credits); if (ret) { mlog_errno(ret); @@ -5671,19 +5588,97 @@ out: return ret; } +/* + * ocfs2_reserve_blocks_for_rec_trunc() would look basically the + * same as ocfs2_lock_alloctors(), except for it accepts a blocks + * number to reserve some extra blocks, and it only handles meta + * data allocations. + * + * Currently, only ocfs2_remove_btree_range() uses it for truncating + * and punching holes. + */ +static int ocfs2_reserve_blocks_for_rec_trunc(struct inode *inode, + struct ocfs2_extent_tree *et, + u32 extents_to_split, + struct ocfs2_alloc_context **ac, + int extra_blocks) +{ + int ret = 0, num_free_extents; + unsigned int max_recs_needed = 2 * extents_to_split; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + + *ac = NULL; + + num_free_extents = ocfs2_num_free_extents(osb, et); + if (num_free_extents < 0) { + ret = num_free_extents; + mlog_errno(ret); + goto out; + } + + if (!num_free_extents || + (ocfs2_sparse_alloc(osb) && num_free_extents < max_recs_needed)) + extra_blocks += ocfs2_extend_meta_needed(et->et_root_el); + + if (extra_blocks) { + ret = ocfs2_reserve_new_metadata_blocks(osb, extra_blocks, ac); + if (ret < 0) { + if (ret != -ENOSPC) + mlog_errno(ret); + goto out; + } + } + +out: + if (ret) { + if (*ac) { + ocfs2_free_alloc_context(*ac); + *ac = NULL; + } + } + + return ret; +} + int ocfs2_remove_btree_range(struct inode *inode, struct ocfs2_extent_tree *et, - u32 cpos, u32 phys_cpos, u32 len, - struct ocfs2_cached_dealloc_ctxt *dealloc) + u32 cpos, u32 phys_cpos, u32 len, int flags, + struct ocfs2_cached_dealloc_ctxt *dealloc, + u64 refcount_loc) { - int ret; + int ret, credits = 0, extra_blocks = 0; u64 phys_blkno = ocfs2_clusters_to_blocks(inode->i_sb, phys_cpos); struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); struct inode *tl_inode = osb->osb_tl_inode; handle_t *handle; struct ocfs2_alloc_context *meta_ac = NULL; + struct ocfs2_refcount_tree *ref_tree = NULL; + + if ((flags & OCFS2_EXT_REFCOUNTED) && len) { + BUG_ON(!(OCFS2_I(inode)->ip_dyn_features & + OCFS2_HAS_REFCOUNT_FL)); + + ret = ocfs2_lock_refcount_tree(osb, refcount_loc, 1, + &ref_tree, NULL); + if (ret) { + mlog_errno(ret); + goto out; + } - ret = ocfs2_lock_allocators(inode, et, 0, 1, NULL, &meta_ac); + ret = ocfs2_prepare_refcount_change_for_del(inode, + refcount_loc, + phys_blkno, + len, + &credits, + &extra_blocks); + if (ret < 0) { + mlog_errno(ret); + goto out; + } + } + + ret = ocfs2_reserve_blocks_for_rec_trunc(inode, et, 1, &meta_ac, + extra_blocks); if (ret) { mlog_errno(ret); return ret; @@ -5699,7 +5694,8 @@ int ocfs2_remove_btree_range(struct inode *inode, } } - handle = ocfs2_start_trans(osb, ocfs2_remove_extent_credits(osb->sb)); + handle = ocfs2_start_trans(osb, + ocfs2_remove_extent_credits(osb->sb) + credits); if (IS_ERR(handle)) { ret = PTR_ERR(handle); mlog_errno(ret); @@ -5724,15 +5720,22 @@ int ocfs2_remove_btree_range(struct inode *inode, ocfs2_et_update_clusters(et, -len); - ret = ocfs2_journal_dirty(handle, et->et_root_bh); - if (ret) { - mlog_errno(ret); - goto out_commit; - } + ocfs2_journal_dirty(handle, et->et_root_bh); - ret = ocfs2_truncate_log_append(osb, handle, phys_blkno, len); - if (ret) - mlog_errno(ret); + if (phys_blkno) { + if (flags & OCFS2_EXT_REFCOUNTED) + ret = ocfs2_decrease_refcount(inode, handle, + ocfs2_blocks_to_clusters(osb->sb, + phys_blkno), + len, meta_ac, + dealloc, 1); + else + ret = ocfs2_truncate_log_append(osb, handle, + phys_blkno, len); + if (ret) + mlog_errno(ret); + + } out_commit: ocfs2_commit_trans(osb, handle); @@ -5742,6 +5745,9 @@ out: if (meta_ac) ocfs2_free_alloc_context(meta_ac); + if (ref_tree) + ocfs2_unlock_refcount_tree(osb, ref_tree, 1); + return ret; } @@ -5850,11 +5856,7 @@ int ocfs2_truncate_log_append(struct ocfs2_super *osb, } tl->tl_recs[index].t_clusters = cpu_to_le32(num_clusters); - status = ocfs2_journal_dirty(handle, tl_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, tl_bh); bail: mlog_exit(status); @@ -5893,11 +5895,7 @@ static int ocfs2_replay_truncate_records(struct ocfs2_super *osb, tl->tl_used = cpu_to_le16(i); - status = ocfs2_journal_dirty(handle, tl_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, tl_bh); /* TODO: Perhaps we can calculate the bulk of the * credits up front rather than extending like @@ -6298,6 +6296,7 @@ int ocfs2_truncate_log_init(struct ocfs2_super *osb) */ struct ocfs2_cached_block_free { struct ocfs2_cached_block_free *free_next; + u64 free_bg; u64 free_blk; unsigned int free_bit; }; @@ -6344,8 +6343,11 @@ static int ocfs2_free_cached_blocks(struct ocfs2_super *osb, } while (head) { - bg_blkno = ocfs2_which_suballoc_group(head->free_blk, - head->free_bit); + if (head->free_bg) + bg_blkno = head->free_bg; + else + bg_blkno = ocfs2_which_suballoc_group(head->free_blk, + head->free_bit); mlog(0, "Free bit: (bit %u, blkno %llu)\n", head->free_bit, (unsigned long long)head->free_blk); @@ -6393,7 +6395,7 @@ int ocfs2_cache_cluster_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, int ret = 0; struct ocfs2_cached_block_free *item; - item = kmalloc(sizeof(*item), GFP_NOFS); + item = kzalloc(sizeof(*item), GFP_NOFS); if (item == NULL) { ret = -ENOMEM; mlog_errno(ret); @@ -6533,8 +6535,8 @@ ocfs2_find_per_slot_free_list(int type, } int ocfs2_cache_block_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, - int type, int slot, u64 blkno, - unsigned int bit) + int type, int slot, u64 suballoc, + u64 blkno, unsigned int bit) { int ret; struct ocfs2_per_slot_free_list *fl; @@ -6547,7 +6549,7 @@ int ocfs2_cache_block_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, goto out; } - item = kmalloc(sizeof(*item), GFP_NOFS); + item = kzalloc(sizeof(*item), GFP_NOFS); if (item == NULL) { ret = -ENOMEM; mlog_errno(ret); @@ -6557,6 +6559,7 @@ int ocfs2_cache_block_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, mlog(0, "Insert: (type %d, slot %u, bit %u, blk %llu)\n", type, slot, bit, (unsigned long long)blkno); + item->free_bg = suballoc; item->free_blk = blkno; item->free_bit = bit; item->free_next = fl->f_first; @@ -6573,433 +6576,11 @@ static int ocfs2_cache_extent_block_free(struct ocfs2_cached_dealloc_ctxt *ctxt, { return ocfs2_cache_block_dealloc(ctxt, EXTENT_ALLOC_SYSTEM_INODE, le16_to_cpu(eb->h_suballoc_slot), + le64_to_cpu(eb->h_suballoc_loc), le64_to_cpu(eb->h_blkno), le16_to_cpu(eb->h_suballoc_bit)); } -/* This function will figure out whether the currently last extent - * block will be deleted, and if it will, what the new last extent - * block will be so we can update his h_next_leaf_blk field, as well - * as the dinodes i_last_eb_blk */ -static int ocfs2_find_new_last_ext_blk(struct inode *inode, - unsigned int clusters_to_del, - struct ocfs2_path *path, - struct buffer_head **new_last_eb) -{ - int next_free, ret = 0; - u32 cpos; - struct ocfs2_extent_rec *rec; - struct ocfs2_extent_block *eb; - struct ocfs2_extent_list *el; - struct buffer_head *bh = NULL; - - *new_last_eb = NULL; - - /* we have no tree, so of course, no last_eb. */ - if (!path->p_tree_depth) - goto out; - - /* trunc to zero special case - this makes tree_depth = 0 - * regardless of what it is. */ - if (OCFS2_I(inode)->ip_clusters == clusters_to_del) - goto out; - - el = path_leaf_el(path); - BUG_ON(!el->l_next_free_rec); - - /* - * Make sure that this extent list will actually be empty - * after we clear away the data. We can shortcut out if - * there's more than one non-empty extent in the - * list. Otherwise, a check of the remaining extent is - * necessary. - */ - next_free = le16_to_cpu(el->l_next_free_rec); - rec = NULL; - if (ocfs2_is_empty_extent(&el->l_recs[0])) { - if (next_free > 2) - goto out; - - /* We may have a valid extent in index 1, check it. */ - if (next_free == 2) - rec = &el->l_recs[1]; - - /* - * Fall through - no more nonempty extents, so we want - * to delete this leaf. - */ - } else { - if (next_free > 1) - goto out; - - rec = &el->l_recs[0]; - } - - if (rec) { - /* - * Check it we'll only be trimming off the end of this - * cluster. - */ - if (le16_to_cpu(rec->e_leaf_clusters) > clusters_to_del) - goto out; - } - - ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, path, &cpos); - if (ret) { - mlog_errno(ret); - goto out; - } - - ret = ocfs2_find_leaf(INODE_CACHE(inode), path_root_el(path), cpos, &bh); - if (ret) { - mlog_errno(ret); - goto out; - } - - eb = (struct ocfs2_extent_block *) bh->b_data; - el = &eb->h_list; - - /* ocfs2_find_leaf() gets the eb from ocfs2_read_extent_block(). - * Any corruption is a code bug. */ - BUG_ON(!OCFS2_IS_VALID_EXTENT_BLOCK(eb)); - - *new_last_eb = bh; - get_bh(*new_last_eb); - mlog(0, "returning block %llu, (cpos: %u)\n", - (unsigned long long)le64_to_cpu(eb->h_blkno), cpos); -out: - brelse(bh); - - return ret; -} - -/* - * Trim some clusters off the rightmost edge of a tree. Only called - * during truncate. - * - * The caller needs to: - * - start journaling of each path component. - * - compute and fully set up any new last ext block - */ -static int ocfs2_trim_tree(struct inode *inode, struct ocfs2_path *path, - handle_t *handle, struct ocfs2_truncate_context *tc, - u32 clusters_to_del, u64 *delete_start, u8 *flags) -{ - int ret, i, index = path->p_tree_depth; - u32 new_edge = 0; - u64 deleted_eb = 0; - struct buffer_head *bh; - struct ocfs2_extent_list *el; - struct ocfs2_extent_rec *rec; - - *delete_start = 0; - *flags = 0; - - while (index >= 0) { - bh = path->p_node[index].bh; - el = path->p_node[index].el; - - mlog(0, "traveling tree (index = %d, block = %llu)\n", - index, (unsigned long long)bh->b_blocknr); - - BUG_ON(le16_to_cpu(el->l_next_free_rec) == 0); - - if (index != - (path->p_tree_depth - le16_to_cpu(el->l_tree_depth))) { - ocfs2_error(inode->i_sb, - "Inode %lu has invalid ext. block %llu", - inode->i_ino, - (unsigned long long)bh->b_blocknr); - ret = -EROFS; - goto out; - } - -find_tail_record: - i = le16_to_cpu(el->l_next_free_rec) - 1; - rec = &el->l_recs[i]; - - mlog(0, "Extent list before: record %d: (%u, %u, %llu), " - "next = %u\n", i, le32_to_cpu(rec->e_cpos), - ocfs2_rec_clusters(el, rec), - (unsigned long long)le64_to_cpu(rec->e_blkno), - le16_to_cpu(el->l_next_free_rec)); - - BUG_ON(ocfs2_rec_clusters(el, rec) < clusters_to_del); - - if (le16_to_cpu(el->l_tree_depth) == 0) { - /* - * If the leaf block contains a single empty - * extent and no records, we can just remove - * the block. - */ - if (i == 0 && ocfs2_is_empty_extent(rec)) { - memset(rec, 0, - sizeof(struct ocfs2_extent_rec)); - el->l_next_free_rec = cpu_to_le16(0); - - goto delete; - } - - /* - * Remove any empty extents by shifting things - * left. That should make life much easier on - * the code below. This condition is rare - * enough that we shouldn't see a performance - * hit. - */ - if (ocfs2_is_empty_extent(&el->l_recs[0])) { - le16_add_cpu(&el->l_next_free_rec, -1); - - for(i = 0; - i < le16_to_cpu(el->l_next_free_rec); i++) - el->l_recs[i] = el->l_recs[i + 1]; - - memset(&el->l_recs[i], 0, - sizeof(struct ocfs2_extent_rec)); - - /* - * We've modified our extent list. The - * simplest way to handle this change - * is to being the search from the - * start again. - */ - goto find_tail_record; - } - - le16_add_cpu(&rec->e_leaf_clusters, -clusters_to_del); - - /* - * We'll use "new_edge" on our way back up the - * tree to know what our rightmost cpos is. - */ - new_edge = le16_to_cpu(rec->e_leaf_clusters); - new_edge += le32_to_cpu(rec->e_cpos); - - /* - * The caller will use this to delete data blocks. - */ - *delete_start = le64_to_cpu(rec->e_blkno) - + ocfs2_clusters_to_blocks(inode->i_sb, - le16_to_cpu(rec->e_leaf_clusters)); - *flags = rec->e_flags; - - /* - * If it's now empty, remove this record. - */ - if (le16_to_cpu(rec->e_leaf_clusters) == 0) { - memset(rec, 0, - sizeof(struct ocfs2_extent_rec)); - le16_add_cpu(&el->l_next_free_rec, -1); - } - } else { - if (le64_to_cpu(rec->e_blkno) == deleted_eb) { - memset(rec, 0, - sizeof(struct ocfs2_extent_rec)); - le16_add_cpu(&el->l_next_free_rec, -1); - - goto delete; - } - - /* Can this actually happen? */ - if (le16_to_cpu(el->l_next_free_rec) == 0) - goto delete; - - /* - * We never actually deleted any clusters - * because our leaf was empty. There's no - * reason to adjust the rightmost edge then. - */ - if (new_edge == 0) - goto delete; - - rec->e_int_clusters = cpu_to_le32(new_edge); - le32_add_cpu(&rec->e_int_clusters, - -le32_to_cpu(rec->e_cpos)); - - /* - * A deleted child record should have been - * caught above. - */ - BUG_ON(le32_to_cpu(rec->e_int_clusters) == 0); - } - -delete: - ret = ocfs2_journal_dirty(handle, bh); - if (ret) { - mlog_errno(ret); - goto out; - } - - mlog(0, "extent list container %llu, after: record %d: " - "(%u, %u, %llu), next = %u.\n", - (unsigned long long)bh->b_blocknr, i, - le32_to_cpu(rec->e_cpos), ocfs2_rec_clusters(el, rec), - (unsigned long long)le64_to_cpu(rec->e_blkno), - le16_to_cpu(el->l_next_free_rec)); - - /* - * We must be careful to only attempt delete of an - * extent block (and not the root inode block). - */ - if (index > 0 && le16_to_cpu(el->l_next_free_rec) == 0) { - struct ocfs2_extent_block *eb = - (struct ocfs2_extent_block *)bh->b_data; - - /* - * Save this for use when processing the - * parent block. - */ - deleted_eb = le64_to_cpu(eb->h_blkno); - - mlog(0, "deleting this extent block.\n"); - - ocfs2_remove_from_cache(INODE_CACHE(inode), bh); - - BUG_ON(ocfs2_rec_clusters(el, &el->l_recs[0])); - BUG_ON(le32_to_cpu(el->l_recs[0].e_cpos)); - BUG_ON(le64_to_cpu(el->l_recs[0].e_blkno)); - - ret = ocfs2_cache_extent_block_free(&tc->tc_dealloc, eb); - /* An error here is not fatal. */ - if (ret < 0) - mlog_errno(ret); - } else { - deleted_eb = 0; - } - - index--; - } - - ret = 0; -out: - return ret; -} - -static int ocfs2_do_truncate(struct ocfs2_super *osb, - unsigned int clusters_to_del, - struct inode *inode, - struct buffer_head *fe_bh, - handle_t *handle, - struct ocfs2_truncate_context *tc, - struct ocfs2_path *path, - struct ocfs2_alloc_context *meta_ac) -{ - int status; - struct ocfs2_dinode *fe; - struct ocfs2_extent_block *last_eb = NULL; - struct ocfs2_extent_list *el; - struct buffer_head *last_eb_bh = NULL; - u64 delete_blk = 0; - u8 rec_flags; - - fe = (struct ocfs2_dinode *) fe_bh->b_data; - - status = ocfs2_find_new_last_ext_blk(inode, clusters_to_del, - path, &last_eb_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } - - /* - * Each component will be touched, so we might as well journal - * here to avoid having to handle errors later. - */ - status = ocfs2_journal_access_path(INODE_CACHE(inode), handle, path); - if (status < 0) { - mlog_errno(status); - goto bail; - } - - if (last_eb_bh) { - status = ocfs2_journal_access_eb(handle, INODE_CACHE(inode), last_eb_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); - goto bail; - } - - last_eb = (struct ocfs2_extent_block *) last_eb_bh->b_data; - } - - el = &(fe->id2.i_list); - - /* - * Lower levels depend on this never happening, but it's best - * to check it up here before changing the tree. - */ - if (el->l_tree_depth && el->l_recs[0].e_int_clusters == 0) { - ocfs2_error(inode->i_sb, - "Inode %lu has an empty extent record, depth %u\n", - inode->i_ino, le16_to_cpu(el->l_tree_depth)); - status = -EROFS; - goto bail; - } - - dquot_free_space_nodirty(inode, - ocfs2_clusters_to_bytes(osb->sb, clusters_to_del)); - spin_lock(&OCFS2_I(inode)->ip_lock); - OCFS2_I(inode)->ip_clusters = le32_to_cpu(fe->i_clusters) - - clusters_to_del; - spin_unlock(&OCFS2_I(inode)->ip_lock); - le32_add_cpu(&fe->i_clusters, -clusters_to_del); - inode->i_blocks = ocfs2_inode_sector_count(inode); - - status = ocfs2_trim_tree(inode, path, handle, tc, - clusters_to_del, &delete_blk, &rec_flags); - if (status) { - mlog_errno(status); - goto bail; - } - - if (le32_to_cpu(fe->i_clusters) == 0) { - /* trunc to zero is a special case. */ - el->l_tree_depth = 0; - fe->i_last_eb_blk = 0; - } else if (last_eb) - fe->i_last_eb_blk = last_eb->h_blkno; - - status = ocfs2_journal_dirty(handle, fe_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } - - if (last_eb) { - /* If there will be a new last extent block, then by - * definition, there cannot be any leaves to the right of - * him. */ - last_eb->h_next_leaf_blk = 0; - status = ocfs2_journal_dirty(handle, last_eb_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } - } - - if (delete_blk) { - if (rec_flags & OCFS2_EXT_REFCOUNTED) - status = ocfs2_decrease_refcount(inode, handle, - ocfs2_blocks_to_clusters(osb->sb, - delete_blk), - clusters_to_del, meta_ac, - &tc->tc_dealloc, 1); - else - status = ocfs2_truncate_log_append(osb, handle, - delete_blk, - clusters_to_del); - if (status < 0) { - mlog_errno(status); - goto bail; - } - } - status = 0; -bail: - brelse(last_eb_bh); - mlog_exit(status); - return status; -} - static int ocfs2_zero_func(handle_t *handle, struct buffer_head *bh) { set_buffer_uptodate(bh); @@ -7307,7 +6888,9 @@ int ocfs2_convert_inline_data_to_extents(struct inode *inode, goto out_commit; did_quota = 1; - ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, + data_ac->ac_resv = &OCFS2_I(inode)->ip_la_data_resv; + + ret = ocfs2_claim_clusters(handle, data_ac, 1, &bit_off, &num); if (ret) { mlog_errno(ret); @@ -7406,26 +6989,29 @@ out: */ int ocfs2_commit_truncate(struct ocfs2_super *osb, struct inode *inode, - struct buffer_head *fe_bh, - struct ocfs2_truncate_context *tc) + struct buffer_head *di_bh) { - int status, i, credits, tl_sem = 0; - u32 clusters_to_del, new_highest_cpos, range; + int status = 0, i, flags = 0; + u32 new_highest_cpos, range, trunc_cpos, trunc_len, phys_cpos, coff; u64 blkno = 0; struct ocfs2_extent_list *el; - handle_t *handle = NULL; - struct inode *tl_inode = osb->osb_tl_inode; + struct ocfs2_extent_rec *rec; struct ocfs2_path *path = NULL; - struct ocfs2_dinode *di = (struct ocfs2_dinode *)fe_bh->b_data; - struct ocfs2_alloc_context *meta_ac = NULL; - struct ocfs2_refcount_tree *ref_tree = NULL; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + struct ocfs2_extent_list *root_el = &(di->id2.i_list); + u64 refcount_loc = le64_to_cpu(di->i_refcount_loc); + struct ocfs2_extent_tree et; + struct ocfs2_cached_dealloc_ctxt dealloc; mlog_entry_void(); + ocfs2_init_dinode_extent_tree(&et, INODE_CACHE(inode), di_bh); + ocfs2_init_dealloc_ctxt(&dealloc); + new_highest_cpos = ocfs2_clusters_for_bytes(osb->sb, i_size_read(inode)); - path = ocfs2_new_path(fe_bh, &di->id2.i_list, + path = ocfs2_new_path(di_bh, &di->id2.i_list, ocfs2_journal_access_di); if (!path) { status = -ENOMEM; @@ -7444,8 +7030,6 @@ start: goto bail; } - credits = 0; - /* * Truncate always works against the rightmost tree branch. */ @@ -7480,101 +7064,62 @@ start: } i = le16_to_cpu(el->l_next_free_rec) - 1; - range = le32_to_cpu(el->l_recs[i].e_cpos) + - ocfs2_rec_clusters(el, &el->l_recs[i]); - if (i == 0 && ocfs2_is_empty_extent(&el->l_recs[i])) { - clusters_to_del = 0; - } else if (le32_to_cpu(el->l_recs[i].e_cpos) >= new_highest_cpos) { - clusters_to_del = ocfs2_rec_clusters(el, &el->l_recs[i]); - blkno = le64_to_cpu(el->l_recs[i].e_blkno); + rec = &el->l_recs[i]; + flags = rec->e_flags; + range = le32_to_cpu(rec->e_cpos) + ocfs2_rec_clusters(el, rec); + + if (i == 0 && ocfs2_is_empty_extent(rec)) { + /* + * Lower levels depend on this never happening, but it's best + * to check it up here before changing the tree. + */ + if (root_el->l_tree_depth && rec->e_int_clusters == 0) { + ocfs2_error(inode->i_sb, "Inode %lu has an empty " + "extent record, depth %u\n", inode->i_ino, + le16_to_cpu(root_el->l_tree_depth)); + status = -EROFS; + goto bail; + } + trunc_cpos = le32_to_cpu(rec->e_cpos); + trunc_len = 0; + blkno = 0; + } else if (le32_to_cpu(rec->e_cpos) >= new_highest_cpos) { + /* + * Truncate entire record. + */ + trunc_cpos = le32_to_cpu(rec->e_cpos); + trunc_len = ocfs2_rec_clusters(el, rec); + blkno = le64_to_cpu(rec->e_blkno); } else if (range > new_highest_cpos) { - clusters_to_del = (ocfs2_rec_clusters(el, &el->l_recs[i]) + - le32_to_cpu(el->l_recs[i].e_cpos)) - - new_highest_cpos; - blkno = le64_to_cpu(el->l_recs[i].e_blkno) + - ocfs2_clusters_to_blocks(inode->i_sb, - ocfs2_rec_clusters(el, &el->l_recs[i]) - - clusters_to_del); + /* + * Partial truncate. it also should be + * the last truncate we're doing. + */ + trunc_cpos = new_highest_cpos; + trunc_len = range - new_highest_cpos; + coff = new_highest_cpos - le32_to_cpu(rec->e_cpos); + blkno = le64_to_cpu(rec->e_blkno) + + ocfs2_clusters_to_blocks(inode->i_sb, coff); } else { + /* + * Truncate completed, leave happily. + */ status = 0; goto bail; } - mlog(0, "clusters_to_del = %u in this pass, tail blk=%llu\n", - clusters_to_del, (unsigned long long)path_leaf_bh(path)->b_blocknr); - - if (el->l_recs[i].e_flags & OCFS2_EXT_REFCOUNTED && clusters_to_del) { - BUG_ON(!(OCFS2_I(inode)->ip_dyn_features & - OCFS2_HAS_REFCOUNT_FL)); - - status = ocfs2_lock_refcount_tree(osb, - le64_to_cpu(di->i_refcount_loc), - 1, &ref_tree, NULL); - if (status) { - mlog_errno(status); - goto bail; - } - - status = ocfs2_prepare_refcount_change_for_del(inode, fe_bh, - blkno, - clusters_to_del, - &credits, - &meta_ac); - if (status < 0) { - mlog_errno(status); - goto bail; - } - } - - mutex_lock(&tl_inode->i_mutex); - tl_sem = 1; - /* ocfs2_truncate_log_needs_flush guarantees us at least one - * record is free for use. If there isn't any, we flush to get - * an empty truncate log. */ - if (ocfs2_truncate_log_needs_flush(osb)) { - status = __ocfs2_flush_truncate_log(osb); - if (status < 0) { - mlog_errno(status); - goto bail; - } - } + phys_cpos = ocfs2_blocks_to_clusters(inode->i_sb, blkno); - credits += ocfs2_calc_tree_trunc_credits(osb->sb, clusters_to_del, - (struct ocfs2_dinode *)fe_bh->b_data, - el); - handle = ocfs2_start_trans(osb, credits); - if (IS_ERR(handle)) { - status = PTR_ERR(handle); - handle = NULL; - mlog_errno(status); - goto bail; - } - - status = ocfs2_do_truncate(osb, clusters_to_del, inode, fe_bh, handle, - tc, path, meta_ac); + status = ocfs2_remove_btree_range(inode, &et, trunc_cpos, + phys_cpos, trunc_len, flags, &dealloc, + refcount_loc); if (status < 0) { mlog_errno(status); goto bail; } - mutex_unlock(&tl_inode->i_mutex); - tl_sem = 0; - - ocfs2_commit_trans(osb, handle); - handle = NULL; - ocfs2_reinit_path(path, 1); - if (meta_ac) { - ocfs2_free_alloc_context(meta_ac); - meta_ac = NULL; - } - - if (ref_tree) { - ocfs2_unlock_refcount_tree(osb, ref_tree, 1); - ref_tree = NULL; - } - /* * The check above will catch the case where we've truncated * away all allocation. @@ -7585,25 +7130,10 @@ bail: ocfs2_schedule_truncate_log_flush(osb, 1); - if (tl_sem) - mutex_unlock(&tl_inode->i_mutex); - - if (handle) - ocfs2_commit_trans(osb, handle); - - if (meta_ac) - ocfs2_free_alloc_context(meta_ac); - - if (ref_tree) - ocfs2_unlock_refcount_tree(osb, ref_tree, 1); - - ocfs2_run_deallocs(osb, &tc->tc_dealloc); + ocfs2_run_deallocs(osb, &dealloc); ocfs2_free_path(path); - /* This will drop the ext_alloc cluster lock for us */ - ocfs2_free_truncate_context(tc); - mlog_exit(status); return status; } diff --git a/fs/ocfs2/alloc.h b/fs/ocfs2/alloc.h index 1db4359ccb9..55762b554b9 100644 --- a/fs/ocfs2/alloc.h +++ b/fs/ocfs2/alloc.h @@ -140,8 +140,9 @@ int ocfs2_remove_extent(handle_t *handle, struct ocfs2_extent_tree *et, struct ocfs2_cached_dealloc_ctxt *dealloc); int ocfs2_remove_btree_range(struct inode *inode, struct ocfs2_extent_tree *et, - u32 cpos, u32 phys_cpos, u32 len, - struct ocfs2_cached_dealloc_ctxt *dealloc); + u32 cpos, u32 phys_cpos, u32 len, int flags, + struct ocfs2_cached_dealloc_ctxt *dealloc, + u64 refcount_loc); int ocfs2_num_free_extents(struct ocfs2_super *osb, struct ocfs2_extent_tree *et); @@ -209,7 +210,7 @@ static inline void ocfs2_init_dealloc_ctxt(struct ocfs2_cached_dealloc_ctxt *c) int ocfs2_cache_cluster_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, u64 blkno, unsigned int bit); int ocfs2_cache_block_dealloc(struct ocfs2_cached_dealloc_ctxt *ctxt, - int type, int slot, u64 blkno, + int type, int slot, u64 suballoc, u64 blkno, unsigned int bit); static inline int ocfs2_dealloc_has_cluster(struct ocfs2_cached_dealloc_ctxt *c) { @@ -233,8 +234,7 @@ int ocfs2_prepare_truncate(struct ocfs2_super *osb, struct ocfs2_truncate_context **tc); int ocfs2_commit_truncate(struct ocfs2_super *osb, struct inode *inode, - struct buffer_head *fe_bh, - struct ocfs2_truncate_context *tc); + struct buffer_head *di_bh); int ocfs2_truncate_inline(struct inode *inode, struct buffer_head *di_bh, unsigned int start, unsigned int end, int trunc); @@ -319,6 +319,8 @@ int ocfs2_journal_access_path(struct ocfs2_caching_info *ci, struct ocfs2_path *path); int ocfs2_find_cpos_for_right_leaf(struct super_block *sb, struct ocfs2_path *path, u32 *cpos); +int ocfs2_find_cpos_for_left_leaf(struct super_block *sb, + struct ocfs2_path *path, u32 *cpos); int ocfs2_find_subtree_root(struct ocfs2_extent_tree *et, struct ocfs2_path *left, struct ocfs2_path *right); diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 21441ddb550..3623ca20cc1 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -1735,6 +1735,9 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, goto out; } + if (data_ac) + data_ac->ac_resv = &OCFS2_I(inode)->ip_la_data_resv; + credits = ocfs2_calc_extend_credits(inode->i_sb, &di->id2.i_list, clusters_to_alloc); diff --git a/fs/ocfs2/cluster/masklog.c b/fs/ocfs2/cluster/masklog.c index 3bb928a2bf7..c7fba396392 100644 --- a/fs/ocfs2/cluster/masklog.c +++ b/fs/ocfs2/cluster/masklog.c @@ -116,6 +116,7 @@ static struct mlog_attribute mlog_attrs[MLOG_MAX_BITS] = { define_mask(ERROR), define_mask(NOTICE), define_mask(KTHREAD), + define_mask(RESERVATIONS), }; static struct attribute *mlog_attr_ptrs[MLOG_MAX_BITS] = {NULL, }; diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h index 3dfddbec32f..fd96e2a2fa5 100644 --- a/fs/ocfs2/cluster/masklog.h +++ b/fs/ocfs2/cluster/masklog.h @@ -119,6 +119,7 @@ #define ML_ERROR 0x0000000100000000ULL /* sent to KERN_ERR */ #define ML_NOTICE 0x0000000200000000ULL /* setn to KERN_NOTICE */ #define ML_KTHREAD 0x0000000400000000ULL /* kernel thread activity */ +#define ML_RESERVATIONS 0x0000000800000000ULL /* ocfs2 alloc reservations */ #define MLOG_INITIAL_AND_MASK (ML_ERROR|ML_NOTICE) #define MLOG_INITIAL_NOT_MASK (ML_ENTRY|ML_EXIT) diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index 73e743eea2c..aa75ca3f78d 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -583,6 +583,9 @@ static void o2net_state_change(struct sock *sk) o2net_sc_queue_work(sc, &sc->sc_connect_work); break; default: + printk(KERN_INFO "o2net: connection to " SC_NODEF_FMT + " shutdown, state %d\n", + SC_NODEF_ARGS(sc), sk->sk_state); o2net_sc_queue_work(sc, &sc->sc_shutdown_work); break; } diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index efd77d071c8..f04ebcfffc4 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -1194,7 +1194,7 @@ static int __ocfs2_delete_entry(handle_t *handle, struct inode *dir, else de->inode = 0; dir->i_version++; - status = ocfs2_journal_dirty(handle, bh); + ocfs2_journal_dirty(handle, bh); goto bail; } i += le16_to_cpu(de->rec_len); @@ -1752,7 +1752,7 @@ int __ocfs2_add_entry(handle_t *handle, ocfs2_recalc_free_list(dir, handle, lookup); dir->i_version++; - status = ocfs2_journal_dirty(handle, insert_bh); + ocfs2_journal_dirty(handle, insert_bh); retval = 0; goto bail; } @@ -2297,12 +2297,7 @@ static int ocfs2_fill_new_dir_id(struct ocfs2_super *osb, } ocfs2_fill_initial_dirents(inode, parent, data->id_data, size); - ocfs2_journal_dirty(handle, di_bh); - if (ret) { - mlog_errno(ret); - goto out; - } i_size_write(inode, size); inode->i_nlink = 2; @@ -2366,11 +2361,7 @@ static int ocfs2_fill_new_dir_el(struct ocfs2_super *osb, ocfs2_init_dir_trailer(inode, new_bh, size); } - status = ocfs2_journal_dirty(handle, new_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, new_bh); i_size_write(inode, inode->i_sb->s_blocksize); inode->i_nlink = 2; @@ -2404,15 +2395,15 @@ static int ocfs2_dx_dir_attach_index(struct ocfs2_super *osb, int ret; struct ocfs2_dinode *di = (struct ocfs2_dinode *) di_bh->b_data; u16 dr_suballoc_bit; - u64 dr_blkno; + u64 suballoc_loc, dr_blkno; unsigned int num_bits; struct buffer_head *dx_root_bh = NULL; struct ocfs2_dx_root_block *dx_root; struct ocfs2_dir_block_trailer *trailer = ocfs2_trailer_from_bh(dirdata_bh, dir->i_sb); - ret = ocfs2_claim_metadata(osb, handle, meta_ac, 1, &dr_suballoc_bit, - &num_bits, &dr_blkno); + ret = ocfs2_claim_metadata(handle, meta_ac, 1, &suballoc_loc, + &dr_suballoc_bit, &num_bits, &dr_blkno); if (ret) { mlog_errno(ret); goto out; @@ -2440,6 +2431,7 @@ static int ocfs2_dx_dir_attach_index(struct ocfs2_super *osb, memset(dx_root, 0, osb->sb->s_blocksize); strcpy(dx_root->dr_signature, OCFS2_DX_ROOT_SIGNATURE); dx_root->dr_suballoc_slot = cpu_to_le16(meta_ac->ac_alloc_slot); + dx_root->dr_suballoc_loc = cpu_to_le64(suballoc_loc); dx_root->dr_suballoc_bit = cpu_to_le16(dr_suballoc_bit); dx_root->dr_fs_generation = cpu_to_le32(osb->fs_generation); dx_root->dr_blkno = cpu_to_le64(dr_blkno); @@ -2458,10 +2450,7 @@ static int ocfs2_dx_dir_attach_index(struct ocfs2_super *osb, dx_root->dr_list.l_count = cpu_to_le16(ocfs2_extent_recs_per_dx_root(osb->sb)); } - - ret = ocfs2_journal_dirty(handle, dx_root_bh); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, dx_root_bh); ret = ocfs2_journal_access_di(handle, INODE_CACHE(dir), di_bh, OCFS2_JOURNAL_ACCESS_CREATE); @@ -2475,9 +2464,7 @@ static int ocfs2_dx_dir_attach_index(struct ocfs2_super *osb, OCFS2_I(dir)->ip_dyn_features |= OCFS2_INDEXED_DIR_FL; di->i_dyn_features = cpu_to_le16(OCFS2_I(dir)->ip_dyn_features); - ret = ocfs2_journal_dirty(handle, di_bh); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, di_bh); *ret_dx_root_bh = dx_root_bh; dx_root_bh = NULL; @@ -2558,7 +2545,7 @@ static int __ocfs2_dx_dir_new_cluster(struct inode *dir, * chance of contiguousness as the directory grows in number * of entries. */ - ret = __ocfs2_claim_clusters(osb, handle, data_ac, 1, 1, &phys, &num); + ret = __ocfs2_claim_clusters(handle, data_ac, 1, 1, &phys, &num); if (ret) { mlog_errno(ret); goto out; @@ -2991,7 +2978,9 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, * if we only get one now, that's enough to continue. The rest * will be claimed after the conversion to extents. */ - ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, &len); + if (ocfs2_dir_resv_allowed(osb)) + data_ac->ac_resv = &oi->ip_la_data_resv; + ret = ocfs2_claim_clusters(handle, data_ac, 1, &bit_off, &len); if (ret) { mlog_errno(ret); goto out_commit; @@ -3034,11 +3023,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, ocfs2_init_dir_trailer(dir, dirdata_bh, i); } - ret = ocfs2_journal_dirty(handle, dirdata_bh); - if (ret) { - mlog_errno(ret); - goto out_commit; - } + ocfs2_journal_dirty(handle, dirdata_bh); if (ocfs2_supports_indexed_dirs(osb) && !dx_inline) { /* @@ -3104,11 +3089,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, */ dir->i_blocks = ocfs2_inode_sector_count(dir); - ret = ocfs2_journal_dirty(handle, di_bh); - if (ret) { - mlog_errno(ret); - goto out_commit; - } + ocfs2_journal_dirty(handle, di_bh); if (ocfs2_supports_indexed_dirs(osb)) { ret = ocfs2_dx_dir_attach_index(osb, handle, dir, di_bh, @@ -3138,7 +3119,7 @@ static int ocfs2_expand_inline_dir(struct inode *dir, struct buffer_head *di_bh, * pass. Claim the 2nd cluster as a separate extent. */ if (alloc > len) { - ret = ocfs2_claim_clusters(osb, handle, data_ac, 1, &bit_off, + ret = ocfs2_claim_clusters(handle, data_ac, 1, &bit_off, &len); if (ret) { mlog_errno(ret); @@ -3369,6 +3350,9 @@ static int ocfs2_extend_dir(struct ocfs2_super *osb, goto bail; } + if (ocfs2_dir_resv_allowed(osb)) + data_ac->ac_resv = &OCFS2_I(dir)->ip_la_data_resv; + credits = ocfs2_calc_extend_credits(sb, el, 1); } else { spin_unlock(&OCFS2_I(dir)->ip_lock); @@ -3423,11 +3407,7 @@ do_extend: } else { de->rec_len = cpu_to_le16(sb->s_blocksize); } - status = ocfs2_journal_dirty(handle, new_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, new_bh); dir_i_size += dir->i_sb->s_blocksize; i_size_write(dir, dir_i_size); @@ -3906,11 +3886,7 @@ static int ocfs2_dx_dir_rebalance(struct ocfs2_super *osb, struct inode *dir, sizeof(struct ocfs2_dx_entry), dx_leaf_sort_cmp, dx_leaf_sort_swap); - ret = ocfs2_journal_dirty(handle, dx_leaf_bh); - if (ret) { - mlog_errno(ret); - goto out_commit; - } + ocfs2_journal_dirty(handle, dx_leaf_bh); ret = ocfs2_dx_dir_find_leaf_split(dx_leaf, leaf_cpos, insert_hash, &split_hash); @@ -4490,7 +4466,10 @@ static int ocfs2_dx_dir_remove_index(struct inode *dir, blk = le64_to_cpu(dx_root->dr_blkno); bit = le16_to_cpu(dx_root->dr_suballoc_bit); - bg_blkno = ocfs2_which_suballoc_group(blk, bit); + if (dx_root->dr_suballoc_loc) + bg_blkno = le64_to_cpu(dx_root->dr_suballoc_loc); + else + bg_blkno = ocfs2_which_suballoc_group(blk, bit); ret = ocfs2_free_suballoc_bits(handle, dx_alloc_inode, dx_alloc_bh, bit, bg_blkno, 1); if (ret) @@ -4551,8 +4530,8 @@ int ocfs2_dx_dir_truncate(struct inode *dir, struct buffer_head *di_bh) p_cpos = ocfs2_blocks_to_clusters(dir->i_sb, blkno); - ret = ocfs2_remove_btree_range(dir, &et, cpos, p_cpos, clen, - &dealloc); + ret = ocfs2_remove_btree_range(dir, &et, cpos, p_cpos, clen, 0, + &dealloc, 0); if (ret) { mlog_errno(ret); goto out; diff --git a/fs/ocfs2/dlm/dlmast.c b/fs/ocfs2/dlm/dlmast.c index 12d5eb78a11..f4499915683 100644 --- a/fs/ocfs2/dlm/dlmast.c +++ b/fs/ocfs2/dlm/dlmast.c @@ -88,7 +88,7 @@ static int dlm_should_cancel_bast(struct dlm_ctxt *dlm, struct dlm_lock *lock) return 0; } -static void __dlm_queue_ast(struct dlm_ctxt *dlm, struct dlm_lock *lock) +void __dlm_queue_ast(struct dlm_ctxt *dlm, struct dlm_lock *lock) { mlog_entry_void(); @@ -145,7 +145,7 @@ void dlm_queue_ast(struct dlm_ctxt *dlm, struct dlm_lock *lock) } -static void __dlm_queue_bast(struct dlm_ctxt *dlm, struct dlm_lock *lock) +void __dlm_queue_bast(struct dlm_ctxt *dlm, struct dlm_lock *lock) { mlog_entry_void(); @@ -451,7 +451,9 @@ int dlm_send_proxy_ast_msg(struct dlm_ctxt *dlm, struct dlm_lock_resource *res, ret = o2net_send_message_vec(DLM_PROXY_AST_MSG, dlm->key, vec, veclen, lock->ml.node, &status); if (ret < 0) - mlog_errno(ret); + mlog(ML_ERROR, "Error %d when sending message %u (key 0x%x) to " + "node %u\n", ret, DLM_PROXY_AST_MSG, dlm->key, + lock->ml.node); else { if (status == DLM_RECOVERING) { mlog(ML_ERROR, "sent AST to node %u, it thinks this " diff --git a/fs/ocfs2/dlm/dlmcommon.h b/fs/ocfs2/dlm/dlmcommon.h index 0102be35980..4b6ae2c13b4 100644 --- a/fs/ocfs2/dlm/dlmcommon.h +++ b/fs/ocfs2/dlm/dlmcommon.h @@ -37,7 +37,7 @@ #define DLM_THREAD_SHUFFLE_INTERVAL 5 // flush everything every 5 passes #define DLM_THREAD_MS 200 // flush at least every 200 ms -#define DLM_HASH_SIZE_DEFAULT (1 << 14) +#define DLM_HASH_SIZE_DEFAULT (1 << 17) #if DLM_HASH_SIZE_DEFAULT < PAGE_SIZE # define DLM_HASH_PAGES 1 #else @@ -904,6 +904,8 @@ void __dlm_lockres_grab_inflight_ref(struct dlm_ctxt *dlm, void dlm_queue_ast(struct dlm_ctxt *dlm, struct dlm_lock *lock); void dlm_queue_bast(struct dlm_ctxt *dlm, struct dlm_lock *lock); +void __dlm_queue_ast(struct dlm_ctxt *dlm, struct dlm_lock *lock); +void __dlm_queue_bast(struct dlm_ctxt *dlm, struct dlm_lock *lock); void dlm_do_local_ast(struct dlm_ctxt *dlm, struct dlm_lock_resource *res, struct dlm_lock *lock); diff --git a/fs/ocfs2/dlm/dlmconvert.c b/fs/ocfs2/dlm/dlmconvert.c index 90803b47cd8..9f30491e5e8 100644 --- a/fs/ocfs2/dlm/dlmconvert.c +++ b/fs/ocfs2/dlm/dlmconvert.c @@ -390,7 +390,9 @@ static enum dlm_status dlm_send_remote_convert_request(struct dlm_ctxt *dlm, } else if (ret != DLM_NORMAL && ret != DLM_NOTQUEUED) dlm_error(ret); } else { - mlog_errno(tmpret); + mlog(ML_ERROR, "Error %d when sending message %u (key 0x%x) to " + "node %u\n", tmpret, DLM_CONVERT_LOCK_MSG, dlm->key, + res->owner); if (dlm_is_host_down(tmpret)) { /* instead of logging the same network error over * and over, sleep here and wait for the heartbeat diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 988c9055fd4..6b5a492e174 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -511,7 +511,7 @@ static void __dlm_print_nodes(struct dlm_ctxt *dlm) assert_spin_locked(&dlm->spinlock); - printk(KERN_INFO "ocfs2_dlm: Nodes in domain (\"%s\"): ", dlm->name); + printk(KERN_NOTICE "o2dlm: Nodes in domain %s: ", dlm->name); while ((node = find_next_bit(dlm->domain_map, O2NM_MAX_NODES, node + 1)) < O2NM_MAX_NODES) { @@ -534,7 +534,7 @@ static int dlm_exit_domain_handler(struct o2net_msg *msg, u32 len, void *data, node = exit_msg->node_idx; - printk(KERN_INFO "ocfs2_dlm: Node %u leaves domain %s\n", node, dlm->name); + printk(KERN_NOTICE "o2dlm: Node %u leaves domain %s\n", node, dlm->name); spin_lock(&dlm->spinlock); clear_bit(node, dlm->domain_map); @@ -565,7 +565,9 @@ static int dlm_send_one_domain_exit(struct dlm_ctxt *dlm, status = o2net_send_message(DLM_EXIT_DOMAIN_MSG, dlm->key, &leave_msg, sizeof(leave_msg), node, NULL); - + if (status < 0) + mlog(ML_ERROR, "Error %d when sending message %u (key 0x%x) to " + "node %u\n", status, DLM_EXIT_DOMAIN_MSG, dlm->key, node); mlog(0, "status return %d from o2net_send_message\n", status); return status; @@ -904,7 +906,7 @@ static int dlm_assert_joined_handler(struct o2net_msg *msg, u32 len, void *data, set_bit(assert->node_idx, dlm->domain_map); __dlm_set_joining_node(dlm, DLM_LOCK_RES_OWNER_UNKNOWN); - printk(KERN_INFO "ocfs2_dlm: Node %u joins domain %s\n", + printk(KERN_NOTICE "o2dlm: Node %u joins domain %s\n", assert->node_idx, dlm->name); __dlm_print_nodes(dlm); @@ -962,7 +964,9 @@ static int dlm_send_one_join_cancel(struct dlm_ctxt *dlm, &cancel_msg, sizeof(cancel_msg), node, NULL); if (status < 0) { - mlog_errno(status); + mlog(ML_ERROR, "Error %d when sending message %u (key 0x%x) to " + "node %u\n", status, DLM_CANCEL_JOIN_MSG, DLM_MOD_KEY, + node); goto bail; } @@ -1029,10 +1033,11 @@ static int dlm_request_join(struct dlm_ctxt *dlm, byte_copymap(join_msg.node_map, dlm->live_nodes_map, O2NM_MAX_NODES); status = o2net_send_message(DLM_QUERY_JOIN_MSG, DLM_MOD_KEY, &join_msg, - sizeof(join_msg), node, - &join_resp); + sizeof(join_msg), node, &join_resp); if (status < 0 && status != -ENOPROTOOPT) { - mlog_errno(status); + mlog(ML_ERROR, "Error %d when sending message %u (key 0x%x) to " + "node %u\n", status, DLM_QUERY_JOIN_MSG, DLM_MOD_KEY, + node); goto bail; } dlm_query_join_wire_to_packet(join_resp, &packet); @@ -1103,7 +1108,9 @@ static int dlm_send_one_join_assert(struct dlm_ctxt *dlm, &assert_msg, sizeof(assert_msg), node, NULL); if (status < 0) - mlog_errno(status); + mlog(ML_ERROR, "Error %d when sending message %u (key 0x%x) to " + "node %u\n", status, DLM_ASSERT_JOINED_MSG, DLM_MOD_KEY, + node); return status; } @@ -1516,7 +1523,7 @@ static struct dlm_ctxt *dlm_alloc_ctxt(const char *domain, goto leave; } - dlm->name = kmalloc(strlen(domain) + 1, GFP_KERNEL); + dlm->name = kstrdup(domain, GFP_KERNEL); if (dlm->name == NULL) { mlog_errno(-ENOMEM); kfree(dlm); @@ -1550,7 +1557,6 @@ static struct dlm_ctxt *dlm_alloc_ctxt(const char *domain, for (i = 0; i < DLM_HASH_BUCKETS; i++) INIT_HLIST_HEAD(dlm_master_hash(dlm, i)); - strcpy(dlm->name, domain); dlm->key = key; dlm->node_num = o2nm_this_node(); diff --git a/fs/ocfs2/dlm/dlmlock.c b/fs/ocfs2/dlm/dlmlock.c index 73333777267..69cf369961c 100644 --- a/fs/ocfs2/dlm/dlmlock.c +++ b/fs/ocfs2/dlm/dlmlock.c @@ -329,7 +329,9 @@ static enum dlm_status dlm_send_remote_lock_request(struct dlm_ctxt *dlm, BUG(); } } else { - mlog_errno(tmpret); + mlog(ML_ERROR, "Error %d when sending message %u (key 0x%x) to " + "node %u\n", tmpret, DLM_CREATE_LOCK_MSG, dlm->key, + res->owner); if (dlm_is_host_down(tmpret)) { ret = DLM_RECOVERING; mlog(0, "node %u died so returning DLM_RECOVERING " @@ -429,7 +431,7 @@ struct dlm_lock * dlm_new_lock(int type, u8 node, u64 cookie, struct dlm_lock *lock; int kernel_allocated = 0; - lock = (struct dlm_lock *) kmem_cache_zalloc(dlm_lock_cache, GFP_NOFS); + lock = kmem_cache_zalloc(dlm_lock_cache, GFP_NOFS); if (!lock) return NULL; diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c index 9289b4357d2..4a7506a4e31 100644 --- a/fs/ocfs2/dlm/dlmmaster.c +++ b/fs/ocfs2/dlm/dlmmaster.c @@ -617,13 +617,11 @@ struct dlm_lock_resource *dlm_new_lockres(struct dlm_ctxt *dlm, { struct dlm_lock_resource *res = NULL; - res = (struct dlm_lock_resource *) - kmem_cache_zalloc(dlm_lockres_cache, GFP_NOFS); + res = kmem_cache_zalloc(dlm_lockres_cache, GFP_NOFS); if (!res) goto error; - res->lockname.name = (char *) - kmem_cache_zalloc(dlm_lockname_cache, GFP_NOFS); + res->lockname.name = kmem_cache_zalloc(dlm_lockname_cache, GFP_NOFS); if (!res->lockname.name) goto error; @@ -757,8 +755,7 @@ lookup: spin_unlock(&dlm->spinlock); mlog(0, "allocating a new resource\n"); /* nothing found and we need to allocate one. */ - alloc_mle = (struct dlm_master_list_entry *) - kmem_cache_alloc(dlm_mle_cache, GFP_NOFS); + alloc_mle = kmem_cache_alloc(dlm_mle_cache, GFP_NOFS); if (!alloc_mle) goto leave; res = dlm_new_lockres(dlm, lockid, namelen); @@ -1542,8 +1539,7 @@ way_up_top: spin_unlock(&dlm->master_lock); spin_unlock(&dlm->spinlock); - mle = (struct dlm_master_list_entry *) - kmem_cache_alloc(dlm_mle_cache, GFP_NOFS); + mle = kmem_cache_alloc(dlm_mle_cache, GFP_NOFS); if (!mle) { response = DLM_MASTER_RESP_ERROR; mlog_errno(-ENOMEM); @@ -1666,7 +1662,9 @@ again: tmpret = o2net_send_message(DLM_ASSERT_MASTER_MSG, dlm->key, &assert, sizeof(assert), to, &r); if (tmpret < 0) { - mlog(0, "assert_master returned %d!\n", tmpret); + mlog(ML_ERROR, "Error %d when sending message %u (key " + "0x%x) to node %u\n", tmpret, + DLM_ASSERT_MASTER_MSG, dlm->key, to); if (!dlm_is_host_down(tmpret)) { mlog(ML_ERROR, "unhandled error=%d!\n", tmpret); BUG(); @@ -2205,7 +2203,9 @@ int dlm_drop_lockres_ref(struct dlm_ctxt *dlm, struct dlm_lock_resource *res) ret = o2net_send_message(DLM_DEREF_LOCKRES_MSG, dlm->key, &deref, sizeof(deref), res->owner, &r); if (ret < 0) - mlog_errno(ret); + mlog(ML_ERROR, "Error %d when sending message %u (key 0x%x) to " + "node %u\n", ret, DLM_DEREF_LOCKRES_MSG, dlm->key, + res->owner); else if (r < 0) { /* BAD. other node says I did not have a ref. */ mlog(ML_ERROR,"while dropping ref on %s:%.*s " @@ -2452,8 +2452,7 @@ static int dlm_migrate_lockres(struct dlm_ctxt *dlm, goto leave; } - mle = (struct dlm_master_list_entry *) kmem_cache_alloc(dlm_mle_cache, - GFP_NOFS); + mle = kmem_cache_alloc(dlm_mle_cache, GFP_NOFS); if (!mle) { mlog_errno(ret); goto leave; @@ -2975,7 +2974,9 @@ static int dlm_do_migrate_request(struct dlm_ctxt *dlm, &migrate, sizeof(migrate), nodenum, &status); if (ret < 0) { - mlog(0, "migrate_request returned %d!\n", ret); + mlog(ML_ERROR, "Error %d when sending message %u (key " + "0x%x) to node %u\n", ret, DLM_MIGRATE_REQUEST_MSG, + dlm->key, nodenum); if (!dlm_is_host_down(ret)) { mlog(ML_ERROR, "unhandled error=%d!\n", ret); BUG(); @@ -3033,8 +3034,7 @@ int dlm_migrate_request_handler(struct o2net_msg *msg, u32 len, void *data, hash = dlm_lockid_hash(name, namelen); /* preallocate.. if this fails, abort */ - mle = (struct dlm_master_list_entry *) kmem_cache_alloc(dlm_mle_cache, - GFP_NOFS); + mle = kmem_cache_alloc(dlm_mle_cache, GFP_NOFS); if (!mle) { ret = -ENOMEM; diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index b4f99de2caf..f8b75ce4be7 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -803,7 +803,9 @@ static int dlm_request_all_locks(struct dlm_ctxt *dlm, u8 request_from, /* negative status is handled by caller */ if (ret < 0) - mlog_errno(ret); + mlog(ML_ERROR, "Error %d when sending message %u (key " + "0x%x) to node %u\n", ret, DLM_LOCK_REQUEST_MSG, + dlm->key, request_from); // return from here, then // sleep until all received or error @@ -955,10 +957,10 @@ static int dlm_send_all_done_msg(struct dlm_ctxt *dlm, u8 dead_node, u8 send_to) ret = o2net_send_message(DLM_RECO_DATA_DONE_MSG, dlm->key, &done_msg, sizeof(done_msg), send_to, &tmpret); if (ret < 0) { + mlog(ML_ERROR, "Error %d when sending message %u (key " + "0x%x) to node %u\n", ret, DLM_RECO_DATA_DONE_MSG, + dlm->key, send_to); if (!dlm_is_host_down(ret)) { - mlog_errno(ret); - mlog(ML_ERROR, "%s: unknown error sending data-done " - "to %u\n", dlm->name, send_to); BUG(); } } else @@ -1126,7 +1128,9 @@ static int dlm_send_mig_lockres_msg(struct dlm_ctxt *dlm, if (ret < 0) { /* XXX: negative status is not handled. * this will end up killing this node. */ - mlog_errno(ret); + mlog(ML_ERROR, "Error %d when sending message %u (key " + "0x%x) to node %u\n", ret, DLM_MIG_LOCKRES_MSG, + dlm->key, send_to); } else { /* might get an -ENOMEM back here */ ret = status; @@ -1642,7 +1646,9 @@ int dlm_do_master_requery(struct dlm_ctxt *dlm, struct dlm_lock_resource *res, &req, sizeof(req), nodenum, &status); /* XXX: negative status not handled properly here. */ if (ret < 0) - mlog_errno(ret); + mlog(ML_ERROR, "Error %d when sending message %u (key " + "0x%x) to node %u\n", ret, DLM_MASTER_REQUERY_MSG, + dlm->key, nodenum); else { BUG_ON(status < 0); BUG_ON(status > DLM_LOCK_RES_OWNER_UNKNOWN); @@ -2640,7 +2646,7 @@ retry: if (dlm_is_host_down(ret)) { /* node is down. not involved in recovery * so just keep going */ - mlog(0, "%s: node %u was down when sending " + mlog(ML_NOTICE, "%s: node %u was down when sending " "begin reco msg (%d)\n", dlm->name, nodenum, ret); ret = 0; } @@ -2660,11 +2666,12 @@ retry: } if (ret < 0) { struct dlm_lock_resource *res; + /* this is now a serious problem, possibly ENOMEM * in the network stack. must retry */ mlog_errno(ret); mlog(ML_ERROR, "begin reco of dlm %s to node %u " - " returned %d\n", dlm->name, nodenum, ret); + "returned %d\n", dlm->name, nodenum, ret); res = dlm_lookup_lockres(dlm, DLM_RECOVERY_LOCK_NAME, DLM_RECOVERY_LOCK_NAME_LEN); if (res) { @@ -2789,7 +2796,9 @@ stage2: if (ret >= 0) ret = status; if (ret < 0) { - mlog_errno(ret); + mlog(ML_ERROR, "Error %d when sending message %u (key " + "0x%x) to node %u\n", ret, DLM_FINALIZE_RECO_MSG, + dlm->key, nodenum); if (dlm_is_host_down(ret)) { /* this has no effect on this recovery * session, so set the status to zero to diff --git a/fs/ocfs2/dlm/dlmthread.c b/fs/ocfs2/dlm/dlmthread.c index 11a6d1fd1d3..d4f73ca68fe 100644 --- a/fs/ocfs2/dlm/dlmthread.c +++ b/fs/ocfs2/dlm/dlmthread.c @@ -309,6 +309,7 @@ static void dlm_shuffle_lists(struct dlm_ctxt *dlm, * spinlock, and because we know that it is not migrating/ * recovering/in-progress, it is fine to reserve asts and * basts right before queueing them all throughout */ + assert_spin_locked(&dlm->ast_lock); assert_spin_locked(&res->spinlock); BUG_ON((res->state & (DLM_LOCK_RES_MIGRATING| DLM_LOCK_RES_RECOVERING| @@ -337,7 +338,7 @@ converting: /* queue the BAST if not already */ if (lock->ml.highest_blocked == LKM_IVMODE) { __dlm_lockres_reserve_ast(res); - dlm_queue_bast(dlm, lock); + __dlm_queue_bast(dlm, lock); } /* update the highest_blocked if needed */ if (lock->ml.highest_blocked < target->ml.convert_type) @@ -355,7 +356,7 @@ converting: can_grant = 0; if (lock->ml.highest_blocked == LKM_IVMODE) { __dlm_lockres_reserve_ast(res); - dlm_queue_bast(dlm, lock); + __dlm_queue_bast(dlm, lock); } if (lock->ml.highest_blocked < target->ml.convert_type) lock->ml.highest_blocked = @@ -383,7 +384,7 @@ converting: spin_unlock(&target->spinlock); __dlm_lockres_reserve_ast(res); - dlm_queue_ast(dlm, target); + __dlm_queue_ast(dlm, target); /* go back and check for more */ goto converting; } @@ -402,7 +403,7 @@ blocked: can_grant = 0; if (lock->ml.highest_blocked == LKM_IVMODE) { __dlm_lockres_reserve_ast(res); - dlm_queue_bast(dlm, lock); + __dlm_queue_bast(dlm, lock); } if (lock->ml.highest_blocked < target->ml.type) lock->ml.highest_blocked = target->ml.type; @@ -418,7 +419,7 @@ blocked: can_grant = 0; if (lock->ml.highest_blocked == LKM_IVMODE) { __dlm_lockres_reserve_ast(res); - dlm_queue_bast(dlm, lock); + __dlm_queue_bast(dlm, lock); } if (lock->ml.highest_blocked < target->ml.type) lock->ml.highest_blocked = target->ml.type; @@ -444,7 +445,7 @@ blocked: spin_unlock(&target->spinlock); __dlm_lockres_reserve_ast(res); - dlm_queue_ast(dlm, target); + __dlm_queue_ast(dlm, target); /* go back and check for more */ goto converting; } @@ -674,6 +675,7 @@ static int dlm_thread(void *data) /* lockres can be re-dirtied/re-added to the * dirty_list in this gap, but that is ok */ + spin_lock(&dlm->ast_lock); spin_lock(&res->spinlock); if (res->owner != dlm->node_num) { __dlm_print_one_lock_resource(res); @@ -694,6 +696,7 @@ static int dlm_thread(void *data) /* move it to the tail and keep going */ res->state &= ~DLM_LOCK_RES_DIRTY; spin_unlock(&res->spinlock); + spin_unlock(&dlm->ast_lock); mlog(0, "delaying list shuffling for in-" "progress lockres %.*s, state=%d\n", res->lockname.len, res->lockname.name, @@ -715,6 +718,7 @@ static int dlm_thread(void *data) dlm_shuffle_lists(dlm, res); res->state &= ~DLM_LOCK_RES_DIRTY; spin_unlock(&res->spinlock); + spin_unlock(&dlm->ast_lock); dlm_lockres_calc_usage(dlm, res); diff --git a/fs/ocfs2/dlm/dlmunlock.c b/fs/ocfs2/dlm/dlmunlock.c index b47c1b92b82..817287c6a6d 100644 --- a/fs/ocfs2/dlm/dlmunlock.c +++ b/fs/ocfs2/dlm/dlmunlock.c @@ -354,7 +354,8 @@ static enum dlm_status dlm_send_remote_unlock_request(struct dlm_ctxt *dlm, mlog(0, "master was in-progress. retry\n"); ret = status; } else { - mlog_errno(tmpret); + mlog(ML_ERROR, "Error %d when sending message %u (key 0x%x) to " + "node %u\n", tmpret, DLM_UNLOCK_LOCK_MSG, dlm->key, owner); if (dlm_is_host_down(tmpret)) { /* NOTE: this seems strange, but it is what we want. * when the master goes down during a cancel or diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index a5fbd9cea96..f74f1400ecc 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -278,10 +278,7 @@ int ocfs2_update_inode_atime(struct inode *inode, inode->i_atime = CURRENT_TIME; di->i_atime = cpu_to_le64(inode->i_atime.tv_sec); di->i_atime_nsec = cpu_to_le32(inode->i_atime.tv_nsec); - - ret = ocfs2_journal_dirty(handle, bh); - if (ret < 0) - mlog_errno(ret); + ocfs2_journal_dirty(handle, bh); out_commit: ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); @@ -430,9 +427,7 @@ static int ocfs2_orphan_for_truncate(struct ocfs2_super *osb, di->i_ctime = di->i_mtime = cpu_to_le64(inode->i_ctime.tv_sec); di->i_ctime_nsec = di->i_mtime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); - status = ocfs2_journal_dirty(handle, fe_bh); - if (status < 0) - mlog_errno(status); + ocfs2_journal_dirty(handle, fe_bh); out_commit: ocfs2_commit_trans(osb, handle); @@ -449,7 +444,6 @@ static int ocfs2_truncate_file(struct inode *inode, int status = 0; struct ocfs2_dinode *fe = NULL; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - struct ocfs2_truncate_context *tc = NULL; mlog_entry("(inode = %llu, new_i_size = %llu\n", (unsigned long long)OCFS2_I(inode)->ip_blkno, @@ -488,6 +482,9 @@ static int ocfs2_truncate_file(struct inode *inode, down_write(&OCFS2_I(inode)->ip_alloc_sem); + ocfs2_resv_discard(&osb->osb_la_resmap, + &OCFS2_I(inode)->ip_la_data_resv); + /* * The inode lock forced other nodes to sync and drop their * pages, which (correctly) happens even if we have a truncate @@ -517,13 +514,7 @@ static int ocfs2_truncate_file(struct inode *inode, goto bail_unlock_sem; } - status = ocfs2_prepare_truncate(osb, inode, di_bh, &tc); - if (status < 0) { - mlog_errno(status); - goto bail_unlock_sem; - } - - status = ocfs2_commit_truncate(osb, inode, di_bh, tc); + status = ocfs2_commit_truncate(osb, inode, di_bh); if (status < 0) { mlog_errno(status); goto bail_unlock_sem; @@ -666,11 +657,7 @@ restarted_transaction: goto leave; } - status = ocfs2_journal_dirty(handle, bh); - if (status < 0) { - mlog_errno(status); - goto leave; - } + ocfs2_journal_dirty(handle, bh); spin_lock(&OCFS2_I(inode)->ip_lock); clusters_to_add -= (OCFS2_I(inode)->ip_clusters - prev_clusters); @@ -1195,9 +1182,7 @@ static int __ocfs2_write_remove_suid(struct inode *inode, di = (struct ocfs2_dinode *) bh->b_data; di->i_mode = cpu_to_le16(inode->i_mode); - ret = ocfs2_journal_dirty(handle, bh); - if (ret < 0) - mlog_errno(ret); + ocfs2_journal_dirty(handle, bh); out_trans: ocfs2_commit_trans(osb, handle); @@ -1434,16 +1419,90 @@ out: return ret; } +static int ocfs2_find_rec(struct ocfs2_extent_list *el, u32 pos) +{ + int i; + struct ocfs2_extent_rec *rec = NULL; + + for (i = le16_to_cpu(el->l_next_free_rec) - 1; i >= 0; i--) { + + rec = &el->l_recs[i]; + + if (le32_to_cpu(rec->e_cpos) < pos) + break; + } + + return i; +} + +/* + * Helper to calculate the punching pos and length in one run, we handle the + * following three cases in order: + * + * - remove the entire record + * - remove a partial record + * - no record needs to be removed (hole-punching completed) +*/ +static void ocfs2_calc_trunc_pos(struct inode *inode, + struct ocfs2_extent_list *el, + struct ocfs2_extent_rec *rec, + u32 trunc_start, u32 *trunc_cpos, + u32 *trunc_len, u32 *trunc_end, + u64 *blkno, int *done) +{ + int ret = 0; + u32 coff, range; + + range = le32_to_cpu(rec->e_cpos) + ocfs2_rec_clusters(el, rec); + + if (le32_to_cpu(rec->e_cpos) >= trunc_start) { + *trunc_cpos = le32_to_cpu(rec->e_cpos); + /* + * Skip holes if any. + */ + if (range < *trunc_end) + *trunc_end = range; + *trunc_len = *trunc_end - le32_to_cpu(rec->e_cpos); + *blkno = le64_to_cpu(rec->e_blkno); + *trunc_end = le32_to_cpu(rec->e_cpos); + } else if (range > trunc_start) { + *trunc_cpos = trunc_start; + *trunc_len = *trunc_end - trunc_start; + coff = trunc_start - le32_to_cpu(rec->e_cpos); + *blkno = le64_to_cpu(rec->e_blkno) + + ocfs2_clusters_to_blocks(inode->i_sb, coff); + *trunc_end = trunc_start; + } else { + /* + * It may have two following possibilities: + * + * - last record has been removed + * - trunc_start was within a hole + * + * both two cases mean the completion of hole punching. + */ + ret = 1; + } + + *done = ret; +} + static int ocfs2_remove_inode_range(struct inode *inode, struct buffer_head *di_bh, u64 byte_start, u64 byte_len) { - int ret = 0; - u32 trunc_start, trunc_len, cpos, phys_cpos, alloc_size; + int ret = 0, flags = 0, done = 0, i; + u32 trunc_start, trunc_len, trunc_end, trunc_cpos, phys_cpos; + u32 cluster_in_el; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); struct ocfs2_cached_dealloc_ctxt dealloc; struct address_space *mapping = inode->i_mapping; struct ocfs2_extent_tree et; + struct ocfs2_path *path = NULL; + struct ocfs2_extent_list *el = NULL; + struct ocfs2_extent_rec *rec = NULL; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + u64 blkno, refcount_loc = le64_to_cpu(di->i_refcount_loc); ocfs2_init_dinode_extent_tree(&et, INODE_CACHE(inode), di_bh); ocfs2_init_dealloc_ctxt(&dealloc); @@ -1469,17 +1528,35 @@ static int ocfs2_remove_inode_range(struct inode *inode, goto out; } + /* + * For reflinks, we may need to CoW 2 clusters which might be + * partially zero'd later, if hole's start and end offset were + * within one cluster(means is not exactly aligned to clustersize). + */ + + if (OCFS2_I(inode)->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL) { + + ret = ocfs2_cow_file_pos(inode, di_bh, byte_start); + if (ret) { + mlog_errno(ret); + goto out; + } + + ret = ocfs2_cow_file_pos(inode, di_bh, byte_start + byte_len); + if (ret) { + mlog_errno(ret); + goto out; + } + } + trunc_start = ocfs2_clusters_for_bytes(osb->sb, byte_start); - trunc_len = (byte_start + byte_len) >> osb->s_clustersize_bits; - if (trunc_len >= trunc_start) - trunc_len -= trunc_start; - else - trunc_len = 0; + trunc_end = (byte_start + byte_len) >> osb->s_clustersize_bits; + cluster_in_el = trunc_end; - mlog(0, "Inode: %llu, start: %llu, len: %llu, cstart: %u, clen: %u\n", + mlog(0, "Inode: %llu, start: %llu, len: %llu, cstart: %u, cend: %u\n", (unsigned long long)OCFS2_I(inode)->ip_blkno, (unsigned long long)byte_start, - (unsigned long long)byte_len, trunc_start, trunc_len); + (unsigned long long)byte_len, trunc_start, trunc_end); ret = ocfs2_zero_partial_clusters(inode, byte_start, byte_len); if (ret) { @@ -1487,31 +1564,79 @@ static int ocfs2_remove_inode_range(struct inode *inode, goto out; } - cpos = trunc_start; - while (trunc_len) { - ret = ocfs2_get_clusters(inode, cpos, &phys_cpos, - &alloc_size, NULL); + path = ocfs2_new_path_from_et(&et); + if (!path) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + while (trunc_end > trunc_start) { + + ret = ocfs2_find_path(INODE_CACHE(inode), path, + cluster_in_el); if (ret) { mlog_errno(ret); goto out; } - if (alloc_size > trunc_len) - alloc_size = trunc_len; + el = path_leaf_el(path); - /* Only do work for non-holes */ - if (phys_cpos != 0) { - ret = ocfs2_remove_btree_range(inode, &et, cpos, - phys_cpos, alloc_size, - &dealloc); + i = ocfs2_find_rec(el, trunc_end); + /* + * Need to go to previous extent block. + */ + if (i < 0) { + if (path->p_tree_depth == 0) + break; + + ret = ocfs2_find_cpos_for_left_leaf(inode->i_sb, + path, + &cluster_in_el); if (ret) { mlog_errno(ret); goto out; } + + /* + * We've reached the leftmost extent block, + * it's safe to leave. + */ + if (cluster_in_el == 0) + break; + + /* + * The 'pos' searched for previous extent block is + * always one cluster less than actual trunc_end. + */ + trunc_end = cluster_in_el + 1; + + ocfs2_reinit_path(path, 1); + + continue; + + } else + rec = &el->l_recs[i]; + + ocfs2_calc_trunc_pos(inode, el, rec, trunc_start, &trunc_cpos, + &trunc_len, &trunc_end, &blkno, &done); + if (done) + break; + + flags = rec->e_flags; + phys_cpos = ocfs2_blocks_to_clusters(inode->i_sb, blkno); + + ret = ocfs2_remove_btree_range(inode, &et, trunc_cpos, + phys_cpos, trunc_len, flags, + &dealloc, refcount_loc); + if (ret < 0) { + mlog_errno(ret); + goto out; } - cpos += alloc_size; - trunc_len -= alloc_size; + cluster_in_el = trunc_end; + + ocfs2_reinit_path(path, 1); } ocfs2_truncate_cluster_pages(inode, byte_start, byte_len); diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index af189887201..abb0a95cc71 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -376,6 +376,10 @@ void ocfs2_populate_inode(struct inode *inode, struct ocfs2_dinode *fe, OCFS2_I(inode)->ip_last_used_slot = 0; OCFS2_I(inode)->ip_last_used_group = 0; + + if (S_ISDIR(inode->i_mode)) + ocfs2_resv_set_type(&OCFS2_I(inode)->ip_la_data_resv, + OCFS2_RESV_FLAG_DIR); mlog_exit_void(); } @@ -539,7 +543,6 @@ static int ocfs2_truncate_for_delete(struct ocfs2_super *osb, struct buffer_head *fe_bh) { int status = 0; - struct ocfs2_truncate_context *tc = NULL; struct ocfs2_dinode *fe; handle_t *handle = NULL; @@ -582,13 +585,7 @@ static int ocfs2_truncate_for_delete(struct ocfs2_super *osb, ocfs2_commit_trans(osb, handle); handle = NULL; - status = ocfs2_prepare_truncate(osb, inode, fe_bh, &tc); - if (status < 0) { - mlog_errno(status); - goto out; - } - - status = ocfs2_commit_truncate(osb, inode, fe_bh, tc); + status = ocfs2_commit_truncate(osb, inode, fe_bh); if (status < 0) { mlog_errno(status); goto out; @@ -659,12 +656,7 @@ static int ocfs2_remove_inode(struct inode *inode, di->i_dtime = cpu_to_le64(CURRENT_TIME.tv_sec); di->i_flags &= cpu_to_le32(~(OCFS2_VALID_FL | OCFS2_ORPHANED_FL)); - - status = ocfs2_journal_dirty(handle, di_bh); - if (status < 0) { - mlog_errno(status); - goto bail_commit; - } + ocfs2_journal_dirty(handle, di_bh); ocfs2_remove_from_cache(INODE_CACHE(inode), di_bh); dquot_free_inode(inode); @@ -980,7 +972,7 @@ static void ocfs2_cleanup_delete_inode(struct inode *inode, void ocfs2_delete_inode(struct inode *inode) { int wipe, status; - sigset_t blocked, oldset; + sigset_t oldset; struct buffer_head *di_bh = NULL; mlog_entry("(inode->i_ino = %lu)\n", inode->i_ino); @@ -1007,13 +999,7 @@ void ocfs2_delete_inode(struct inode *inode) * messaging paths may return us -ERESTARTSYS. Which would * cause us to exit early, resulting in inodes being orphaned * forever. */ - sigfillset(&blocked); - status = sigprocmask(SIG_BLOCK, &blocked, &oldset); - if (status < 0) { - mlog_errno(status); - ocfs2_cleanup_delete_inode(inode, 1); - goto bail; - } + ocfs2_block_signals(&oldset); /* * Synchronize us against ocfs2_get_dentry. We take this in @@ -1087,9 +1073,7 @@ bail_unlock_nfs_sync: ocfs2_nfs_sync_unlock(OCFS2_SB(inode->i_sb), 0); bail_unblock: - status = sigprocmask(SIG_SETMASK, &oldset, NULL); - if (status < 0) - mlog_errno(status); + ocfs2_unblock_signals(&oldset); bail: clear_inode(inode); mlog_exit_void(); @@ -1123,6 +1107,10 @@ void ocfs2_clear_inode(struct inode *inode) ocfs2_mark_lockres_freeing(&oi->ip_inode_lockres); ocfs2_mark_lockres_freeing(&oi->ip_open_lockres); + ocfs2_resv_discard(&OCFS2_SB(inode->i_sb)->osb_la_resmap, + &oi->ip_la_data_resv); + ocfs2_resv_init_once(&oi->ip_la_data_resv); + /* We very well may get a clear_inode before all an inodes * metadata has hit disk. Of course, we can't drop any cluster * locks until the journal has finished with it. The only @@ -1298,13 +1286,8 @@ int ocfs2_mark_inode_dirty(handle_t *handle, fe->i_mtime = cpu_to_le64(inode->i_mtime.tv_sec); fe->i_mtime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); - status = ocfs2_journal_dirty(handle, bh); - if (status < 0) - mlog_errno(status); - - status = 0; + ocfs2_journal_dirty(handle, bh); leave: - mlog_exit(status); return status; } diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 0b28e1921a3..9f5f5fcadc4 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -70,6 +70,8 @@ struct ocfs2_inode_info /* Only valid if the inode is the dir. */ u32 ip_last_used_slot; u64 ip_last_used_group; + + struct ocfs2_alloc_reservation ip_la_data_resv; }; /* diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 9336c60e3a3..47878cf1641 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -402,9 +402,7 @@ int ocfs2_commit_trans(struct ocfs2_super *osb, } /* - * 'nblocks' is what you want to add to the current - * transaction. extend_trans will either extend the current handle by - * nblocks, or commit it and start a new one with nblocks credits. + * 'nblocks' is what you want to add to the current transaction. * * This might call jbd2_journal_restart() which will commit dirty buffers * and then restart the transaction. Before calling @@ -422,11 +420,15 @@ int ocfs2_commit_trans(struct ocfs2_super *osb, */ int ocfs2_extend_trans(handle_t *handle, int nblocks) { - int status; + int status, old_nblocks; BUG_ON(!handle); - BUG_ON(!nblocks); + BUG_ON(nblocks < 0); + + if (!nblocks) + return 0; + old_nblocks = handle->h_buffer_credits; mlog_entry_void(); mlog(0, "Trying to extend transaction by %d blocks\n", nblocks); @@ -445,7 +447,8 @@ int ocfs2_extend_trans(handle_t *handle, int nblocks) mlog(0, "jbd2_journal_extend failed, trying " "jbd2_journal_restart\n"); - status = jbd2_journal_restart(handle, nblocks); + status = jbd2_journal_restart(handle, + old_nblocks + nblocks); if (status < 0) { mlog_errno(status); goto bail; @@ -734,8 +737,7 @@ int ocfs2_journal_access(handle_t *handle, struct ocfs2_caching_info *ci, return __ocfs2_journal_access(handle, ci, bh, NULL, type); } -int ocfs2_journal_dirty(handle_t *handle, - struct buffer_head *bh) +void ocfs2_journal_dirty(handle_t *handle, struct buffer_head *bh) { int status; @@ -743,13 +745,9 @@ int ocfs2_journal_dirty(handle_t *handle, (unsigned long long)bh->b_blocknr); status = jbd2_journal_dirty_metadata(handle, bh); - if (status < 0) - mlog(ML_ERROR, "Could not dirty metadata buffer. " - "(bh->b_blocknr=%llu)\n", - (unsigned long long)bh->b_blocknr); + BUG_ON(status); - mlog_exit(status); - return status; + mlog_exit_void(); } #define OCFS2_DEFAULT_COMMIT_INTERVAL (HZ * JBD2_DEFAULT_MAX_COMMIT_AGE) diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 3f74e09b0d8..b5baaa8e710 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -325,8 +325,7 @@ int ocfs2_journal_access(handle_t *handle, struct ocfs2_caching_info *ci, * <modify the bh> * ocfs2_journal_dirty(handle, bh); */ -int ocfs2_journal_dirty(handle_t *handle, - struct buffer_head *bh); +void ocfs2_journal_dirty(handle_t *handle, struct buffer_head *bh); /* * Credit Macros: @@ -562,6 +561,18 @@ static inline int ocfs2_calc_group_alloc_credits(struct super_block *sb, return blocks; } +/* + * Allocating a discontiguous block group requires the credits from + * ocfs2_calc_group_alloc_credits() as well as enough credits to fill + * the group descriptor's extent list. The caller already has started + * the transaction with ocfs2_calc_group_alloc_credits(). They extend + * it with these credits. + */ +static inline int ocfs2_calc_bg_discontig_credits(struct super_block *sb) +{ + return ocfs2_extent_recs_per_gd(sb); +} + static inline int ocfs2_calc_tree_trunc_credits(struct super_block *sb, unsigned int clusters_to_del, struct ocfs2_dinode *fe, diff --git a/fs/ocfs2/localalloc.c b/fs/ocfs2/localalloc.c index c983715d8d8..3d7419682dc 100644 --- a/fs/ocfs2/localalloc.c +++ b/fs/ocfs2/localalloc.c @@ -52,7 +52,8 @@ static u32 ocfs2_local_alloc_count_bits(struct ocfs2_dinode *alloc); static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb, struct ocfs2_dinode *alloc, - u32 numbits); + u32 *numbits, + struct ocfs2_alloc_reservation *resv); static void ocfs2_clear_local_alloc(struct ocfs2_dinode *alloc); @@ -74,6 +75,144 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb, static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb, struct inode *local_alloc_inode); +/* + * ocfs2_la_default_mb() - determine a default size, in megabytes of + * the local alloc. + * + * Generally, we'd like to pick as large a local alloc as + * possible. Performance on large workloads tends to scale + * proportionally to la size. In addition to that, the reservations + * code functions more efficiently as it can reserve more windows for + * write. + * + * Some things work against us when trying to choose a large local alloc: + * + * - We need to ensure our sizing is picked to leave enough space in + * group descriptors for other allocations (such as block groups, + * etc). Picking default sizes which are a multiple of 4 could help + * - block groups are allocated in 2mb and 4mb chunks. + * + * - Likewise, we don't want to starve other nodes of bits on small + * file systems. This can easily be taken care of by limiting our + * default to a reasonable size (256M) on larger cluster sizes. + * + * - Some file systems can't support very large sizes - 4k and 8k in + * particular are limited to less than 128 and 256 megabytes respectively. + * + * The following reference table shows group descriptor and local + * alloc maximums at various cluster sizes (4k blocksize) + * + * csize: 4K group: 126M la: 121M + * csize: 8K group: 252M la: 243M + * csize: 16K group: 504M la: 486M + * csize: 32K group: 1008M la: 972M + * csize: 64K group: 2016M la: 1944M + * csize: 128K group: 4032M la: 3888M + * csize: 256K group: 8064M la: 7776M + * csize: 512K group: 16128M la: 15552M + * csize: 1024K group: 32256M la: 31104M + */ +#define OCFS2_LA_MAX_DEFAULT_MB 256 +#define OCFS2_LA_OLD_DEFAULT 8 +unsigned int ocfs2_la_default_mb(struct ocfs2_super *osb) +{ + unsigned int la_mb; + unsigned int gd_mb; + unsigned int megs_per_slot; + struct super_block *sb = osb->sb; + + gd_mb = ocfs2_clusters_to_megabytes(osb->sb, + 8 * ocfs2_group_bitmap_size(sb, 0, osb->s_feature_incompat)); + + /* + * This takes care of files systems with very small group + * descriptors - 512 byte blocksize at cluster sizes lower + * than 16K and also 1k blocksize with 4k cluster size. + */ + if ((sb->s_blocksize == 512 && osb->s_clustersize <= 8192) + || (sb->s_blocksize == 1024 && osb->s_clustersize == 4096)) + return OCFS2_LA_OLD_DEFAULT; + + /* + * Leave enough room for some block groups and make the final + * value we work from a multiple of 4. + */ + gd_mb -= 16; + gd_mb &= 0xFFFFFFFB; + + la_mb = gd_mb; + + /* + * Keep window sizes down to a reasonable default + */ + if (la_mb > OCFS2_LA_MAX_DEFAULT_MB) { + /* + * Some clustersize / blocksize combinations will have + * given us a larger than OCFS2_LA_MAX_DEFAULT_MB + * default size, but get poor distribution when + * limited to exactly 256 megabytes. + * + * As an example, 16K clustersize at 4K blocksize + * gives us a cluster group size of 504M. Paring the + * local alloc size down to 256 however, would give us + * only one window and around 200MB left in the + * cluster group. Instead, find the first size below + * 256 which would give us an even distribution. + * + * Larger cluster group sizes actually work out pretty + * well when pared to 256, so we don't have to do this + * for any group that fits more than two + * OCFS2_LA_MAX_DEFAULT_MB windows. + */ + if (gd_mb > (2 * OCFS2_LA_MAX_DEFAULT_MB)) + la_mb = 256; + else { + unsigned int gd_mult = gd_mb; + + while (gd_mult > 256) + gd_mult = gd_mult >> 1; + + la_mb = gd_mult; + } + } + + megs_per_slot = osb->osb_clusters_at_boot / osb->max_slots; + megs_per_slot = ocfs2_clusters_to_megabytes(osb->sb, megs_per_slot); + /* Too many nodes, too few disk clusters. */ + if (megs_per_slot < la_mb) + la_mb = megs_per_slot; + + return la_mb; +} + +void ocfs2_la_set_sizes(struct ocfs2_super *osb, int requested_mb) +{ + struct super_block *sb = osb->sb; + unsigned int la_default_mb = ocfs2_la_default_mb(osb); + unsigned int la_max_mb; + + la_max_mb = ocfs2_clusters_to_megabytes(sb, + ocfs2_local_alloc_size(sb) * 8); + + mlog(0, "requested: %dM, max: %uM, default: %uM\n", + requested_mb, la_max_mb, la_default_mb); + + if (requested_mb == -1) { + /* No user request - use defaults */ + osb->local_alloc_default_bits = + ocfs2_megabytes_to_clusters(sb, la_default_mb); + } else if (requested_mb > la_max_mb) { + /* Request is too big, we give the maximum available */ + osb->local_alloc_default_bits = + ocfs2_megabytes_to_clusters(sb, la_max_mb); + } else { + osb->local_alloc_default_bits = + ocfs2_megabytes_to_clusters(sb, requested_mb); + } + + osb->local_alloc_bits = osb->local_alloc_default_bits; +} + static inline int ocfs2_la_state_enabled(struct ocfs2_super *osb) { return (osb->local_alloc_state == OCFS2_LA_THROTTLED || @@ -156,7 +295,7 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb) osb->local_alloc_bits, (osb->bitmap_cpg - 1)); osb->local_alloc_bits = ocfs2_megabytes_to_clusters(osb->sb, - OCFS2_DEFAULT_LOCAL_ALLOC_SIZE); + ocfs2_la_default_mb(osb)); } /* read the alloc off disk */ @@ -262,6 +401,8 @@ void ocfs2_shutdown_local_alloc(struct ocfs2_super *osb) osb->local_alloc_state = OCFS2_LA_DISABLED; + ocfs2_resmap_uninit(&osb->osb_la_resmap); + main_bm_inode = ocfs2_get_system_file_inode(osb, GLOBAL_BITMAP_SYSTEM_INODE, OCFS2_INVALID_SLOT); @@ -305,12 +446,7 @@ void ocfs2_shutdown_local_alloc(struct ocfs2_super *osb) } ocfs2_clear_local_alloc(alloc); - - status = ocfs2_journal_dirty(handle, bh); - if (status < 0) { - mlog_errno(status); - goto out_commit; - } + ocfs2_journal_dirty(handle, bh); brelse(bh); osb->local_alloc_bh = NULL; @@ -481,46 +617,6 @@ out: return status; } -/* Check to see if the local alloc window is within ac->ac_max_block */ -static int ocfs2_local_alloc_in_range(struct inode *inode, - struct ocfs2_alloc_context *ac, - u32 bits_wanted) -{ - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - struct ocfs2_dinode *alloc; - struct ocfs2_local_alloc *la; - int start; - u64 block_off; - - if (!ac->ac_max_block) - return 1; - - alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data; - la = OCFS2_LOCAL_ALLOC(alloc); - - start = ocfs2_local_alloc_find_clear_bits(osb, alloc, bits_wanted); - if (start == -1) { - mlog_errno(-ENOSPC); - return 0; - } - - /* - * Converting (bm_off + start + bits_wanted) to blocks gives us - * the blkno just past our actual allocation. This is perfect - * to compare with ac_max_block. - */ - block_off = ocfs2_clusters_to_blocks(inode->i_sb, - le32_to_cpu(la->la_bm_off) + - start + bits_wanted); - mlog(0, "Checking %llu against %llu\n", - (unsigned long long)block_off, - (unsigned long long)ac->ac_max_block); - if (block_off > ac->ac_max_block) - return 0; - - return 1; -} - /* * make sure we've got at least bits_wanted contiguous bits in the * local alloc. You lose them when you drop i_mutex. @@ -613,17 +709,6 @@ int ocfs2_reserve_local_alloc_bits(struct ocfs2_super *osb, mlog(0, "Calling in_range for max block %llu\n", (unsigned long long)ac->ac_max_block); - if (!ocfs2_local_alloc_in_range(local_alloc_inode, ac, - bits_wanted)) { - /* - * The window is outside ac->ac_max_block. - * This errno tells the caller to keep localalloc enabled - * but to get the allocation from the main bitmap. - */ - status = -EFBIG; - goto bail; - } - ac->ac_inode = local_alloc_inode; /* We should never use localalloc from another slot */ ac->ac_alloc_slot = osb->slot_num; @@ -664,7 +749,8 @@ int ocfs2_claim_local_alloc_bits(struct ocfs2_super *osb, alloc = (struct ocfs2_dinode *) osb->local_alloc_bh->b_data; la = OCFS2_LOCAL_ALLOC(alloc); - start = ocfs2_local_alloc_find_clear_bits(osb, alloc, bits_wanted); + start = ocfs2_local_alloc_find_clear_bits(osb, alloc, &bits_wanted, + ac->ac_resv); if (start == -1) { /* TODO: Shouldn't we just BUG here? */ status = -ENOSPC; @@ -674,8 +760,6 @@ int ocfs2_claim_local_alloc_bits(struct ocfs2_super *osb, bitmap = la->la_bitmap; *bit_off = le32_to_cpu(la->la_bm_off) + start; - /* local alloc is always contiguous by nature -- we never - * delete bits from it! */ *num_bits = bits_wanted; status = ocfs2_journal_access_di(handle, @@ -687,18 +771,15 @@ int ocfs2_claim_local_alloc_bits(struct ocfs2_super *osb, goto bail; } + ocfs2_resmap_claimed_bits(&osb->osb_la_resmap, ac->ac_resv, start, + bits_wanted); + while(bits_wanted--) ocfs2_set_bit(start++, bitmap); le32_add_cpu(&alloc->id1.bitmap1.i_used, *num_bits); + ocfs2_journal_dirty(handle, osb->local_alloc_bh); - status = ocfs2_journal_dirty(handle, osb->local_alloc_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } - - status = 0; bail: mlog_exit(status); return status; @@ -722,13 +803,17 @@ static u32 ocfs2_local_alloc_count_bits(struct ocfs2_dinode *alloc) } static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb, - struct ocfs2_dinode *alloc, - u32 numbits) + struct ocfs2_dinode *alloc, + u32 *numbits, + struct ocfs2_alloc_reservation *resv) { int numfound, bitoff, left, startoff, lastzero; + int local_resv = 0; + struct ocfs2_alloc_reservation r; void *bitmap = NULL; + struct ocfs2_reservation_map *resmap = &osb->osb_la_resmap; - mlog_entry("(numbits wanted = %u)\n", numbits); + mlog_entry("(numbits wanted = %u)\n", *numbits); if (!alloc->id1.bitmap1.i_total) { mlog(0, "No bits in my window!\n"); @@ -736,6 +821,30 @@ static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb, goto bail; } + if (!resv) { + local_resv = 1; + ocfs2_resv_init_once(&r); + ocfs2_resv_set_type(&r, OCFS2_RESV_FLAG_TMP); + resv = &r; + } + + numfound = *numbits; + if (ocfs2_resmap_resv_bits(resmap, resv, &bitoff, &numfound) == 0) { + if (numfound < *numbits) + *numbits = numfound; + goto bail; + } + + /* + * Code error. While reservations are enabled, local + * allocation should _always_ go through them. + */ + BUG_ON(osb->osb_resv_level != 0); + + /* + * Reservations are disabled. Handle this the old way. + */ + bitmap = OCFS2_LOCAL_ALLOC(alloc)->la_bitmap; numfound = bitoff = startoff = 0; @@ -761,7 +870,7 @@ static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb, startoff = bitoff+1; } /* we got everything we needed */ - if (numfound == numbits) { + if (numfound == *numbits) { /* mlog(0, "Found it all!\n"); */ break; } @@ -770,12 +879,15 @@ static int ocfs2_local_alloc_find_clear_bits(struct ocfs2_super *osb, mlog(0, "Exiting loop, bitoff = %d, numfound = %d\n", bitoff, numfound); - if (numfound == numbits) + if (numfound == *numbits) bitoff = startoff - numfound; else bitoff = -1; bail: + if (local_resv) + ocfs2_resv_discard(resmap, resv); + mlog_exit(bitoff); return bitoff; } @@ -1049,7 +1161,7 @@ static int ocfs2_local_alloc_new_window(struct ocfs2_super *osb, /* we used the generic suballoc reserve function, but we set * everything up nicely, so there's no reason why we can't use * the more specific cluster api to claim bits. */ - status = ocfs2_claim_clusters(osb, handle, ac, osb->local_alloc_bits, + status = ocfs2_claim_clusters(handle, ac, osb->local_alloc_bits, &cluster_off, &cluster_count); if (status == -ENOSPC) { retry_enospc: @@ -1063,7 +1175,7 @@ retry_enospc: goto bail; ac->ac_bits_wanted = osb->local_alloc_default_bits; - status = ocfs2_claim_clusters(osb, handle, ac, + status = ocfs2_claim_clusters(handle, ac, osb->local_alloc_bits, &cluster_off, &cluster_count); @@ -1098,6 +1210,9 @@ retry_enospc: memset(OCFS2_LOCAL_ALLOC(alloc)->la_bitmap, 0, le16_to_cpu(la->la_size)); + ocfs2_resmap_restart(&osb->osb_la_resmap, cluster_count, + OCFS2_LOCAL_ALLOC(alloc)->la_bitmap); + mlog(0, "New window allocated:\n"); mlog(0, "window la_bm_off = %u\n", OCFS2_LOCAL_ALLOC(alloc)->la_bm_off); @@ -1169,12 +1284,7 @@ static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb, } ocfs2_clear_local_alloc(alloc); - - status = ocfs2_journal_dirty(handle, osb->local_alloc_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, osb->local_alloc_bh); status = ocfs2_sync_local_to_main(osb, handle, alloc_copy, main_bm_inode, main_bm_bh); @@ -1192,7 +1302,6 @@ static int ocfs2_local_alloc_slide_window(struct ocfs2_super *osb, atomic_inc(&osb->alloc_stats.moves); - status = 0; bail: if (handle) ocfs2_commit_trans(osb, handle); diff --git a/fs/ocfs2/localalloc.h b/fs/ocfs2/localalloc.h index ac5ea9f8665..1be9b586446 100644 --- a/fs/ocfs2/localalloc.h +++ b/fs/ocfs2/localalloc.h @@ -30,6 +30,9 @@ int ocfs2_load_local_alloc(struct ocfs2_super *osb); void ocfs2_shutdown_local_alloc(struct ocfs2_super *osb); +void ocfs2_la_set_sizes(struct ocfs2_super *osb, int requested_mb); +unsigned int ocfs2_la_default_mb(struct ocfs2_super *osb); + int ocfs2_begin_local_alloc_recovery(struct ocfs2_super *osb, int node_num, struct ocfs2_dinode **alloc_copy); diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c index 7898bd3a99f..af2b8fe1f13 100644 --- a/fs/ocfs2/mmap.c +++ b/fs/ocfs2/mmap.c @@ -41,44 +41,20 @@ #include "file.h" #include "inode.h" #include "mmap.h" +#include "super.h" -static inline int ocfs2_vm_op_block_sigs(sigset_t *blocked, sigset_t *oldset) -{ - /* The best way to deal with signals in the vm path is - * to block them upfront, rather than allowing the - * locking paths to return -ERESTARTSYS. */ - sigfillset(blocked); - - /* We should technically never get a bad return value - * from sigprocmask */ - return sigprocmask(SIG_BLOCK, blocked, oldset); -} - -static inline int ocfs2_vm_op_unblock_sigs(sigset_t *oldset) -{ - return sigprocmask(SIG_SETMASK, oldset, NULL); -} static int ocfs2_fault(struct vm_area_struct *area, struct vm_fault *vmf) { - sigset_t blocked, oldset; - int error, ret; + sigset_t oldset; + int ret; mlog_entry("(area=%p, page offset=%lu)\n", area, vmf->pgoff); - error = ocfs2_vm_op_block_sigs(&blocked, &oldset); - if (error < 0) { - mlog_errno(error); - ret = VM_FAULT_SIGBUS; - goto out; - } - + ocfs2_block_signals(&oldset); ret = filemap_fault(area, vmf); + ocfs2_unblock_signals(&oldset); - error = ocfs2_vm_op_unblock_sigs(&oldset); - if (error < 0) - mlog_errno(error); -out: mlog_exit_ptr(vmf->page); return ret; } @@ -158,14 +134,10 @@ static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) struct page *page = vmf->page; struct inode *inode = vma->vm_file->f_path.dentry->d_inode; struct buffer_head *di_bh = NULL; - sigset_t blocked, oldset; - int ret, ret2; + sigset_t oldset; + int ret; - ret = ocfs2_vm_op_block_sigs(&blocked, &oldset); - if (ret < 0) { - mlog_errno(ret); - return ret; - } + ocfs2_block_signals(&oldset); /* * The cluster locks taken will block a truncate from another @@ -193,9 +165,7 @@ static int ocfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) ocfs2_inode_unlock(inode, 1); out: - ret2 = ocfs2_vm_op_unblock_sigs(&oldset); - if (ret2 < 0) - mlog_errno(ret2); + ocfs2_unblock_signals(&oldset); if (ret) ret = VM_FAULT_SIGBUS; return ret; diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 4cbb18f26c5..db5dd3ed4df 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -239,6 +239,8 @@ static int ocfs2_mknod(struct inode *dir, }; int did_quota_inode = 0; struct ocfs2_dir_lookup_result lookup = { NULL, }; + sigset_t oldset; + int did_block_signals = 0; mlog_entry("(0x%p, 0x%p, %d, %lu, '%.*s')\n", dir, dentry, mode, (unsigned long)dev, dentry->d_name.len, @@ -350,6 +352,10 @@ static int ocfs2_mknod(struct inode *dir, goto leave; } + /* Starting to change things, restart is no longer possible. */ + ocfs2_block_signals(&oldset); + did_block_signals = 1; + status = dquot_alloc_inode(inode); if (status) goto leave; @@ -384,11 +390,7 @@ static int ocfs2_mknod(struct inode *dir, goto leave; } ocfs2_add_links_count(dirfe, 1); - status = ocfs2_journal_dirty(handle, parent_fe_bh); - if (status < 0) { - mlog_errno(status); - goto leave; - } + ocfs2_journal_dirty(handle, parent_fe_bh); inc_nlink(dir); } @@ -439,6 +441,8 @@ leave: ocfs2_commit_trans(osb, handle); ocfs2_inode_unlock(dir, 1); + if (did_block_signals) + ocfs2_unblock_signals(&oldset); if (status == -ENOSPC) mlog(0, "Disk is full\n"); @@ -487,14 +491,15 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb, int status = 0; struct ocfs2_dinode *fe = NULL; struct ocfs2_extent_list *fel; - u64 fe_blkno = 0; + u64 suballoc_loc, fe_blkno = 0; u16 suballoc_bit; u16 feat; *new_fe_bh = NULL; - status = ocfs2_claim_new_inode(osb, handle, dir, parent_fe_bh, - inode_ac, &suballoc_bit, &fe_blkno); + status = ocfs2_claim_new_inode(handle, dir, parent_fe_bh, + inode_ac, &suballoc_loc, + &suballoc_bit, &fe_blkno); if (status < 0) { mlog_errno(status); goto leave; @@ -531,6 +536,7 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb, fe->i_generation = cpu_to_le32(inode->i_generation); fe->i_fs_generation = cpu_to_le32(osb->fs_generation); fe->i_blkno = cpu_to_le64(fe_blkno); + fe->i_suballoc_loc = cpu_to_le64(suballoc_loc); fe->i_suballoc_bit = cpu_to_le16(suballoc_bit); fe->i_suballoc_slot = cpu_to_le16(inode_ac->ac_alloc_slot); fe->i_uid = cpu_to_le32(inode->i_uid); @@ -567,11 +573,7 @@ static int ocfs2_mknod_locked(struct ocfs2_super *osb, fel->l_count = cpu_to_le16(ocfs2_extent_recs_per_inode(osb->sb)); } - status = ocfs2_journal_dirty(handle, *new_fe_bh); - if (status < 0) { - mlog_errno(status); - goto leave; - } + ocfs2_journal_dirty(handle, *new_fe_bh); ocfs2_populate_inode(inode, fe, 1); ocfs2_ci_set_new(osb, INODE_CACHE(inode)); @@ -637,6 +639,7 @@ static int ocfs2_link(struct dentry *old_dentry, struct ocfs2_dinode *fe = NULL; struct ocfs2_super *osb = OCFS2_SB(dir->i_sb); struct ocfs2_dir_lookup_result lookup = { NULL, }; + sigset_t oldset; mlog_entry("(inode=%lu, old='%.*s' new='%.*s')\n", inode->i_ino, old_dentry->d_name.len, old_dentry->d_name.name, @@ -693,6 +696,9 @@ static int ocfs2_link(struct dentry *old_dentry, goto out_unlock_inode; } + /* Starting to change things, restart is no longer possible. */ + ocfs2_block_signals(&oldset); + err = ocfs2_journal_access_di(handle, INODE_CACHE(inode), fe_bh, OCFS2_JOURNAL_ACCESS_WRITE); if (err < 0) { @@ -705,14 +711,7 @@ static int ocfs2_link(struct dentry *old_dentry, ocfs2_set_links_count(fe, inode->i_nlink); fe->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); fe->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); - - err = ocfs2_journal_dirty(handle, fe_bh); - if (err < 0) { - ocfs2_add_links_count(fe, -1); - drop_nlink(inode); - mlog_errno(err); - goto out_commit; - } + ocfs2_journal_dirty(handle, fe_bh); err = ocfs2_add_entry(handle, dentry, inode, OCFS2_I(inode)->ip_blkno, @@ -736,6 +735,7 @@ static int ocfs2_link(struct dentry *old_dentry, out_commit: ocfs2_commit_trans(osb, handle); + ocfs2_unblock_signals(&oldset); out_unlock_inode: ocfs2_inode_unlock(inode, 1); @@ -909,12 +909,7 @@ static int ocfs2_unlink(struct inode *dir, drop_nlink(inode); drop_nlink(inode); ocfs2_set_links_count(fe, inode->i_nlink); - - status = ocfs2_journal_dirty(handle, fe_bh); - if (status < 0) { - mlog_errno(status); - goto leave; - } + ocfs2_journal_dirty(handle, fe_bh); dir->i_ctime = dir->i_mtime = CURRENT_TIME; if (S_ISDIR(inode->i_mode)) @@ -1332,12 +1327,7 @@ static int ocfs2_rename(struct inode *old_dir, ocfs2_set_links_count(newfe, 0); else ocfs2_add_links_count(newfe, -1); - - status = ocfs2_journal_dirty(handle, newfe_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, newfe_bh); } else { /* if the name was not found in new_dir, add it now */ status = ocfs2_add_entry(handle, new_dentry, old_inode, @@ -1356,10 +1346,7 @@ static int ocfs2_rename(struct inode *old_dir, old_di->i_ctime = cpu_to_le64(old_inode->i_ctime.tv_sec); old_di->i_ctime_nsec = cpu_to_le32(old_inode->i_ctime.tv_nsec); - - status = ocfs2_journal_dirty(handle, old_inode_bh); - if (status < 0) - mlog_errno(status); + ocfs2_journal_dirty(handle, old_inode_bh); } else mlog_errno(status); @@ -1431,7 +1418,7 @@ static int ocfs2_rename(struct inode *old_dir, OCFS2_JOURNAL_ACCESS_WRITE); fe = (struct ocfs2_dinode *) old_dir_bh->b_data; ocfs2_set_links_count(fe, old_dir->i_nlink); - status = ocfs2_journal_dirty(handle, old_dir_bh); + ocfs2_journal_dirty(handle, old_dir_bh); } } ocfs2_dentry_move(old_dentry, new_dentry, old_dir, new_dir); @@ -1563,11 +1550,7 @@ static int ocfs2_create_symlink_data(struct ocfs2_super *osb, (bytes_left > sb->s_blocksize) ? sb->s_blocksize : bytes_left); - status = ocfs2_journal_dirty(handle, bhs[virtual]); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, bhs[virtual]); virtual++; p_blkno++; @@ -1611,6 +1594,8 @@ static int ocfs2_symlink(struct inode *dir, }; int did_quota = 0, did_quota_inode = 0; struct ocfs2_dir_lookup_result lookup = { NULL, }; + sigset_t oldset; + int did_block_signals = 0; mlog_entry("(0x%p, 0x%p, symname='%s' actual='%.*s')\n", dir, dentry, symname, dentry->d_name.len, dentry->d_name.name); @@ -1706,6 +1691,10 @@ static int ocfs2_symlink(struct inode *dir, goto bail; } + /* Starting to change things, restart is no longer possible. */ + ocfs2_block_signals(&oldset); + did_block_signals = 1; + status = dquot_alloc_inode(inode); if (status) goto bail; @@ -1814,6 +1803,8 @@ bail: ocfs2_commit_trans(osb, handle); ocfs2_inode_unlock(dir, 1); + if (did_block_signals) + ocfs2_unblock_signals(&oldset); brelse(new_fe_bh); brelse(parent_fe_bh); @@ -1961,12 +1952,7 @@ static int ocfs2_orphan_add(struct ocfs2_super *osb, if (S_ISDIR(inode->i_mode)) ocfs2_add_links_count(orphan_fe, 1); orphan_dir_inode->i_nlink = ocfs2_read_links_count(orphan_fe); - - status = ocfs2_journal_dirty(handle, orphan_dir_bh); - if (status < 0) { - mlog_errno(status); - goto leave; - } + ocfs2_journal_dirty(handle, orphan_dir_bh); status = __ocfs2_add_entry(handle, orphan_dir_inode, name, OCFS2_ORPHAN_NAMELEN, inode, @@ -2065,12 +2051,7 @@ int ocfs2_orphan_del(struct ocfs2_super *osb, if (S_ISDIR(inode->i_mode)) ocfs2_add_links_count(orphan_fe, -1); orphan_dir_inode->i_nlink = ocfs2_read_links_count(orphan_fe); - - status = ocfs2_journal_dirty(handle, orphan_dir_bh); - if (status < 0) { - mlog_errno(status); - goto leave; - } + ocfs2_journal_dirty(handle, orphan_dir_bh); leave: ocfs2_free_dir_lookup_result(&lookup); diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index adf5e2ebc2c..c67003b6b5a 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -47,6 +47,7 @@ /* For struct ocfs2_blockcheck_stats */ #include "blockcheck.h" +#include "reservations.h" /* Caching of metadata buffers */ @@ -341,6 +342,9 @@ struct ocfs2_super */ unsigned int local_alloc_bits; unsigned int local_alloc_default_bits; + /* osb_clusters_at_boot can become stale! Do not trust it to + * be up to date. */ + unsigned int osb_clusters_at_boot; enum ocfs2_local_alloc_state local_alloc_state; /* protected * by osb_lock */ @@ -349,6 +353,11 @@ struct ocfs2_super u64 la_last_gd; + struct ocfs2_reservation_map osb_la_resmap; + + unsigned int osb_resv_level; + unsigned int osb_dir_resv_level; + /* Next three fields are for local node slot recovery during * mount. */ int dirty; @@ -482,6 +491,13 @@ static inline int ocfs2_supports_indexed_dirs(struct ocfs2_super *osb) return 0; } +static inline int ocfs2_supports_discontig_bg(struct ocfs2_super *osb) +{ + if (osb->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG) + return 1; + return 0; +} + static inline unsigned int ocfs2_link_max(struct ocfs2_super *osb) { if (ocfs2_supports_indexed_dirs(osb)) @@ -763,6 +779,12 @@ static inline unsigned int ocfs2_megabytes_to_clusters(struct super_block *sb, return megs << (20 - OCFS2_SB(sb)->s_clustersize_bits); } +static inline unsigned int ocfs2_clusters_to_megabytes(struct super_block *sb, + unsigned int clusters) +{ + return clusters >> (20 - OCFS2_SB(sb)->s_clustersize_bits); +} + static inline void _ocfs2_set_bit(unsigned int bit, unsigned long *bitmap) { ext2_set_bit(bit, bitmap); diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index bb37218a797..33f1c9a8258 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h @@ -100,7 +100,8 @@ | OCFS2_FEATURE_INCOMPAT_XATTR \ | OCFS2_FEATURE_INCOMPAT_META_ECC \ | OCFS2_FEATURE_INCOMPAT_INDEXED_DIRS \ - | OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE) + | OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE \ + | OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG) #define OCFS2_FEATURE_RO_COMPAT_SUPP (OCFS2_FEATURE_RO_COMPAT_UNWRITTEN \ | OCFS2_FEATURE_RO_COMPAT_USRQUOTA \ | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA) @@ -165,6 +166,9 @@ /* Refcount tree support */ #define OCFS2_FEATURE_INCOMPAT_REFCOUNT_TREE 0x1000 +/* Discontigous block groups */ +#define OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG 0x2000 + /* * backup superblock flag is used to indicate that this volume * has backup superblocks. @@ -283,14 +287,6 @@ #define OCFS2_MIN_JOURNAL_SIZE (4 * 1024 * 1024) /* - * Default local alloc size (in megabytes) - * - * The value chosen should be such that most allocations, including new - * block groups, use local alloc. - */ -#define OCFS2_DEFAULT_LOCAL_ALLOC_SIZE 8 - -/* * Inline extended attribute size (in bytes) * The value chosen should be aligned to 16 byte boundaries. */ @@ -512,7 +508,10 @@ struct ocfs2_extent_block block group */ __le32 h_fs_generation; /* Must match super block */ __le64 h_blkno; /* Offset on disk, in blocks */ -/*20*/ __le64 h_reserved3; +/*20*/ __le64 h_suballoc_loc; /* Suballocator block group this + eb belongs to. Only valid + if allocated from a + discontiguous block group */ __le64 h_next_leaf_blk; /* Offset on disk, in blocks, of next leaf header pointing to data */ @@ -679,7 +678,11 @@ struct ocfs2_dinode { /*80*/ struct ocfs2_block_check i_check; /* Error checking */ /*88*/ __le64 i_dx_root; /* Pointer to dir index root block */ /*90*/ __le64 i_refcount_loc; - __le64 i_reserved2[4]; + __le64 i_suballoc_loc; /* Suballocator block group this + inode belongs to. Only valid + if allocated from a + discontiguous block group */ +/*A0*/ __le64 i_reserved2[3]; /*B8*/ union { __le64 i_pad1; /* Generic way to refer to this 64bit union */ @@ -814,7 +817,12 @@ struct ocfs2_dx_root_block { __le32 dr_reserved2; __le64 dr_free_blk; /* Pointer to head of free * unindexed block list. */ - __le64 dr_reserved3[15]; + __le64 dr_suballoc_loc; /* Suballocator block group + this root belongs to. + Only valid if allocated + from a discontiguous + block group */ + __le64 dr_reserved3[14]; union { struct ocfs2_extent_list dr_list; /* Keep this aligned to 128 * bits for maximum space @@ -840,6 +848,13 @@ struct ocfs2_dx_leaf { }; /* + * Largest bitmap for a block (suballocator) group in bytes. This limit + * does not affect cluster groups (global allocator). Cluster group + * bitmaps run to the end of the block. + */ +#define OCFS2_MAX_BG_BITMAP_SIZE 256 + +/* * On disk allocator group structure for OCFS2 */ struct ocfs2_group_desc @@ -860,7 +875,29 @@ struct ocfs2_group_desc __le64 bg_blkno; /* Offset on disk, in blocks */ /*30*/ struct ocfs2_block_check bg_check; /* Error checking */ __le64 bg_reserved2; -/*40*/ __u8 bg_bitmap[0]; +/*40*/ union { + __u8 bg_bitmap[0]; + struct { + /* + * Block groups may be discontiguous when + * OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG is set. + * The extents of a discontigous block group are + * stored in bg_list. It is a flat list. + * l_tree_depth must always be zero. A + * discontiguous group is signified by a non-zero + * bg_list->l_next_free_rec. Only block groups + * can be discontiguous; Cluster groups cannot. + * We've never made a block group with more than + * 2048 blocks (256 bytes of bg_bitmap). This + * codifies that limit so that we can fit bg_list. + * bg_size of a discontiguous block group will + * be 256 to match bg_bitmap_filler. + */ + __u8 bg_bitmap_filler[OCFS2_MAX_BG_BITMAP_SIZE]; +/*140*/ struct ocfs2_extent_list bg_list; + }; + }; +/* Actual on-disk size is one block */ }; struct ocfs2_refcount_rec { @@ -905,7 +942,11 @@ struct ocfs2_refcount_block { /*40*/ __le32 rf_generation; /* generation number. all be the same * for the same refcount tree. */ __le32 rf_reserved0; - __le64 rf_reserved1[7]; + __le64 rf_suballoc_loc; /* Suballocator block group this + refcount block belongs to. Only + valid if allocated from a + discontiguous block group */ +/*50*/ __le64 rf_reserved1[6]; /*80*/ union { struct ocfs2_refcount_list rf_records; /* List of refcount records */ @@ -1017,7 +1058,10 @@ struct ocfs2_xattr_block { real xattr or a xattr tree. */ __le16 xb_reserved0; __le32 xb_reserved1; - __le64 xb_reserved2; + __le64 xb_suballoc_loc; /* Suballocator block group this + xattr block belongs to. Only + valid if allocated from a + discontiguous block group */ /*30*/ union { struct ocfs2_xattr_header xb_header; /* xattr header if this block contains xattr */ @@ -1254,6 +1298,16 @@ static inline u16 ocfs2_extent_recs_per_eb(struct super_block *sb) return size / sizeof(struct ocfs2_extent_rec); } +static inline u16 ocfs2_extent_recs_per_gd(struct super_block *sb) +{ + int size; + + size = sb->s_blocksize - + offsetof(struct ocfs2_group_desc, bg_list.l_recs); + + return size / sizeof(struct ocfs2_extent_rec); +} + static inline int ocfs2_dx_entries_per_leaf(struct super_block *sb) { int size; @@ -1284,13 +1338,23 @@ static inline u16 ocfs2_local_alloc_size(struct super_block *sb) return size; } -static inline int ocfs2_group_bitmap_size(struct super_block *sb) +static inline int ocfs2_group_bitmap_size(struct super_block *sb, + int suballocator, + u32 feature_incompat) { - int size; - - size = sb->s_blocksize - + int size = sb->s_blocksize - offsetof(struct ocfs2_group_desc, bg_bitmap); + /* + * The cluster allocator uses the entire block. Suballocators have + * never used more than OCFS2_MAX_BG_BITMAP_SIZE. Unfortunately, older + * code expects bg_size set to the maximum. Thus we must keep + * bg_size as-is unless discontig_bg is enabled. + */ + if (suballocator && + (feature_incompat & OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG)) + size = OCFS2_MAX_BG_BITMAP_SIZE; + return size; } @@ -1402,23 +1466,43 @@ static inline int ocfs2_extent_recs_per_eb(int blocksize) return size / sizeof(struct ocfs2_extent_rec); } -static inline int ocfs2_local_alloc_size(int blocksize) +static inline int ocfs2_extent_recs_per_gd(int blocksize) { int size; size = blocksize - - offsetof(struct ocfs2_dinode, id2.i_lab.la_bitmap); + offsetof(struct ocfs2_group_desc, bg_list.l_recs); - return size; + return size / sizeof(struct ocfs2_extent_rec); } -static inline int ocfs2_group_bitmap_size(int blocksize) +static inline int ocfs2_local_alloc_size(int blocksize) { int size; size = blocksize - + offsetof(struct ocfs2_dinode, id2.i_lab.la_bitmap); + + return size; +} + +static inline int ocfs2_group_bitmap_size(int blocksize, + int suballocator, + uint32_t feature_incompat) +{ + int size = sb->s_blocksize - offsetof(struct ocfs2_group_desc, bg_bitmap); + /* + * The cluster allocator uses the entire block. Suballocators have + * never used more than OCFS2_MAX_BG_BITMAP_SIZE. Unfortunately, older + * code expects bg_size set to the maximum. Thus we must keep + * bg_size as-is unless discontig_bg is enabled. + */ + if (suballocator && + (feature_incompat & OCFS2_FEATURE_INCOMPAT_DISCONTIG_BG)) + size = OCFS2_MAX_BG_BITMAP_SIZE; + return size; } @@ -1491,5 +1575,19 @@ static inline void ocfs2_set_de_type(struct ocfs2_dir_entry *de, de->file_type = ocfs2_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; } +static inline int ocfs2_gd_is_discontig(struct ocfs2_group_desc *gd) +{ + if ((offsetof(struct ocfs2_group_desc, bg_bitmap) + + le16_to_cpu(gd->bg_size)) != + offsetof(struct ocfs2_group_desc, bg_list)) + return 0; + /* + * Only valid to check l_next_free_rec if + * bg_bitmap + bg_size == bg_list. + */ + if (!gd->bg_list.l_next_free_rec) + return 0; + return 1; +} #endif /* _OCFS2_FS_H */ diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c index ab42a74c753..04ae76d8c6a 100644 --- a/fs/ocfs2/quota_global.c +++ b/fs/ocfs2/quota_global.c @@ -261,10 +261,8 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type, brelse(bh); goto out; } - err = ocfs2_journal_dirty(handle, bh); + ocfs2_journal_dirty(handle, bh); brelse(bh); - if (err < 0) - goto out; out: if (err) { mutex_unlock(&gqinode->i_mutex); diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index 9ad49305f45..884b641f199 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -119,12 +119,8 @@ static int ocfs2_modify_bh(struct inode *inode, struct buffer_head *bh, lock_buffer(bh); modify(bh, private); unlock_buffer(bh); - status = ocfs2_journal_dirty(handle, bh); - if (status < 0) { - mlog_errno(status); - ocfs2_commit_trans(OCFS2_SB(sb), handle); - return status; - } + ocfs2_journal_dirty(handle, bh); + status = ocfs2_commit_trans(OCFS2_SB(sb), handle); if (status < 0) { mlog_errno(status); @@ -523,9 +519,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode, ocfs2_clear_bit(bit, dchunk->dqc_bitmap); le32_add_cpu(&dchunk->dqc_free, 1); unlock_buffer(qbh); - status = ocfs2_journal_dirty(handle, qbh); - if (status < 0) - mlog_errno(status); + ocfs2_journal_dirty(handle, qbh); out_commit: mutex_unlock(&sb_dqopt(sb)->dqio_mutex); ocfs2_commit_trans(OCFS2_SB(sb), handle); @@ -631,9 +625,7 @@ int ocfs2_finish_quota_recovery(struct ocfs2_super *osb, lock_buffer(bh); ldinfo->dqi_flags = cpu_to_le32(flags | OLQF_CLEAN); unlock_buffer(bh); - status = ocfs2_journal_dirty(handle, bh); - if (status < 0) - mlog_errno(status); + ocfs2_journal_dirty(handle, bh); out_trans: ocfs2_commit_trans(osb, handle); out_bh: @@ -1009,11 +1001,7 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk( sb->s_blocksize - sizeof(struct ocfs2_local_disk_chunk) - OCFS2_QBLK_RESERVED_SPACE); unlock_buffer(bh); - status = ocfs2_journal_dirty(handle, bh); - if (status < 0) { - mlog_errno(status); - goto out_trans; - } + ocfs2_journal_dirty(handle, bh); /* Initialize new block with structures */ down_read(&OCFS2_I(lqinode)->ip_alloc_sem); @@ -1040,11 +1028,7 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk( lock_buffer(dbh); memset(dbh->b_data, 0, sb->s_blocksize - OCFS2_QBLK_RESERVED_SPACE); unlock_buffer(dbh); - status = ocfs2_journal_dirty(handle, dbh); - if (status < 0) { - mlog_errno(status); - goto out_trans; - } + ocfs2_journal_dirty(handle, dbh); /* Update local quotafile info */ oinfo->dqi_blocks += 2; @@ -1155,11 +1139,8 @@ static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file( lock_buffer(bh); memset(bh->b_data, 0, sb->s_blocksize); unlock_buffer(bh); - status = ocfs2_journal_dirty(handle, bh); - if (status < 0) { - mlog_errno(status); - goto out_trans; - } + ocfs2_journal_dirty(handle, bh); + /* Update chunk header */ status = ocfs2_journal_access_dq(handle, INODE_CACHE(lqinode), chunk->qc_headerbh, @@ -1173,11 +1154,8 @@ static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file( lock_buffer(chunk->qc_headerbh); le32_add_cpu(&dchunk->dqc_free, ol_quota_entries_per_block(sb)); unlock_buffer(chunk->qc_headerbh); - status = ocfs2_journal_dirty(handle, chunk->qc_headerbh); - if (status < 0) { - mlog_errno(status); - goto out_trans; - } + ocfs2_journal_dirty(handle, chunk->qc_headerbh); + /* Update file header */ oinfo->dqi_blocks++; status = ocfs2_local_write_info(sb, type); @@ -1312,12 +1290,8 @@ static int ocfs2_local_release_dquot(struct dquot *dquot) ocfs2_clear_bit(offset, dchunk->dqc_bitmap); le32_add_cpu(&dchunk->dqc_free, 1); unlock_buffer(od->dq_chunk->qc_headerbh); - status = ocfs2_journal_dirty(handle, od->dq_chunk->qc_headerbh); - if (status < 0) { - mlog_errno(status); - goto out; - } - status = 0; + ocfs2_journal_dirty(handle, od->dq_chunk->qc_headerbh); + out: /* Clear the read bit so that next time someone uses this * dquot he reads fresh info from disk and allocates local diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 5cbcd0f008f..4793f36f651 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -570,7 +570,7 @@ static int ocfs2_create_refcount_tree(struct inode *inode, struct ocfs2_refcount_tree *new_tree = NULL, *tree = NULL; u16 suballoc_bit_start; u32 num_got; - u64 first_blkno; + u64 suballoc_loc, first_blkno; BUG_ON(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL); @@ -596,7 +596,7 @@ static int ocfs2_create_refcount_tree(struct inode *inode, goto out_commit; } - ret = ocfs2_claim_metadata(osb, handle, meta_ac, 1, + ret = ocfs2_claim_metadata(handle, meta_ac, 1, &suballoc_loc, &suballoc_bit_start, &num_got, &first_blkno); if (ret) { @@ -626,6 +626,7 @@ static int ocfs2_create_refcount_tree(struct inode *inode, memset(rb, 0, inode->i_sb->s_blocksize); strcpy((void *)rb, OCFS2_REFCOUNT_BLOCK_SIGNATURE); rb->rf_suballoc_slot = cpu_to_le16(meta_ac->ac_alloc_slot); + rb->rf_suballoc_loc = cpu_to_le64(suballoc_loc); rb->rf_suballoc_bit = cpu_to_le16(suballoc_bit_start); rb->rf_fs_generation = cpu_to_le32(osb->fs_generation); rb->rf_blkno = cpu_to_le64(first_blkno); @@ -790,7 +791,10 @@ int ocfs2_remove_refcount_tree(struct inode *inode, struct buffer_head *di_bh) if (le32_to_cpu(rb->rf_count) == 1) { blk = le64_to_cpu(rb->rf_blkno); bit = le16_to_cpu(rb->rf_suballoc_bit); - bg_blkno = ocfs2_which_suballoc_group(blk, bit); + if (rb->rf_suballoc_loc) + bg_blkno = le64_to_cpu(rb->rf_suballoc_loc); + else + bg_blkno = ocfs2_which_suballoc_group(blk, bit); alloc_inode = ocfs2_get_system_file_inode(osb, EXTENT_ALLOC_SYSTEM_INODE, @@ -1268,9 +1272,7 @@ static int ocfs2_change_refcount_rec(handle_t *handle, } else if (merge) ocfs2_refcount_rec_merge(rb, index); - ret = ocfs2_journal_dirty(handle, ref_leaf_bh); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, ref_leaf_bh); out: return ret; } @@ -1284,7 +1286,7 @@ static int ocfs2_expand_inline_ref_root(handle_t *handle, int ret; u16 suballoc_bit_start; u32 num_got; - u64 blkno; + u64 suballoc_loc, blkno; struct super_block *sb = ocfs2_metadata_cache_get_super(ci); struct buffer_head *new_bh = NULL; struct ocfs2_refcount_block *new_rb; @@ -1298,7 +1300,7 @@ static int ocfs2_expand_inline_ref_root(handle_t *handle, goto out; } - ret = ocfs2_claim_metadata(OCFS2_SB(sb), handle, meta_ac, 1, + ret = ocfs2_claim_metadata(handle, meta_ac, 1, &suballoc_loc, &suballoc_bit_start, &num_got, &blkno); if (ret) { @@ -1330,6 +1332,7 @@ static int ocfs2_expand_inline_ref_root(handle_t *handle, new_rb = (struct ocfs2_refcount_block *)new_bh->b_data; new_rb->rf_suballoc_slot = cpu_to_le16(meta_ac->ac_alloc_slot); + new_rb->rf_suballoc_loc = cpu_to_le64(suballoc_loc); new_rb->rf_suballoc_bit = cpu_to_le16(suballoc_bit_start); new_rb->rf_blkno = cpu_to_le64(blkno); new_rb->rf_cpos = cpu_to_le32(0); @@ -1524,7 +1527,7 @@ static int ocfs2_new_leaf_refcount_block(handle_t *handle, int ret; u16 suballoc_bit_start; u32 num_got, new_cpos; - u64 blkno; + u64 suballoc_loc, blkno; struct super_block *sb = ocfs2_metadata_cache_get_super(ci); struct ocfs2_refcount_block *root_rb = (struct ocfs2_refcount_block *)ref_root_bh->b_data; @@ -1548,7 +1551,7 @@ static int ocfs2_new_leaf_refcount_block(handle_t *handle, goto out; } - ret = ocfs2_claim_metadata(OCFS2_SB(sb), handle, meta_ac, 1, + ret = ocfs2_claim_metadata(handle, meta_ac, 1, &suballoc_loc, &suballoc_bit_start, &num_got, &blkno); if (ret) { @@ -1576,6 +1579,7 @@ static int ocfs2_new_leaf_refcount_block(handle_t *handle, memset(new_rb, 0, sb->s_blocksize); strcpy((void *)new_rb, OCFS2_REFCOUNT_BLOCK_SIGNATURE); new_rb->rf_suballoc_slot = cpu_to_le16(meta_ac->ac_alloc_slot); + new_rb->rf_suballoc_loc = cpu_to_le64(suballoc_loc); new_rb->rf_suballoc_bit = cpu_to_le16(suballoc_bit_start); new_rb->rf_fs_generation = cpu_to_le32(OCFS2_SB(sb)->fs_generation); new_rb->rf_blkno = cpu_to_le64(blkno); @@ -1694,7 +1698,7 @@ static int ocfs2_adjust_refcount_rec(handle_t *handle, * 2 more credits, one for the leaf refcount block, one for * the extent block contains the extent rec. */ - ret = ocfs2_extend_trans(handle, handle->h_buffer_credits + 2); + ret = ocfs2_extend_trans(handle, 2); if (ret < 0) { mlog_errno(ret); goto out; @@ -1802,11 +1806,7 @@ static int ocfs2_insert_refcount_rec(handle_t *handle, if (merge) ocfs2_refcount_rec_merge(rb, index); - ret = ocfs2_journal_dirty(handle, ref_leaf_bh); - if (ret) { - mlog_errno(ret); - goto out; - } + ocfs2_journal_dirty(handle, ref_leaf_bh); if (index == 0) { ret = ocfs2_adjust_refcount_rec(handle, ci, @@ -1977,9 +1977,7 @@ static int ocfs2_split_refcount_rec(handle_t *handle, ocfs2_refcount_rec_merge(rb, index); } - ret = ocfs2_journal_dirty(handle, ref_leaf_bh); - if (ret) - mlog_errno(ret); + ocfs2_journal_dirty(handle, ref_leaf_bh); out: brelse(new_bh); @@ -2112,6 +2110,7 @@ static int ocfs2_remove_refcount_extent(handle_t *handle, */ ret = ocfs2_cache_block_dealloc(dealloc, EXTENT_ALLOC_SYSTEM_INODE, le16_to_cpu(rb->rf_suballoc_slot), + le64_to_cpu(rb->rf_suballoc_loc), le64_to_cpu(rb->rf_blkno), le16_to_cpu(rb->rf_suballoc_bit)); if (ret) { @@ -2516,20 +2515,19 @@ out: * * Normally the refcount blocks store these refcount should be * contiguous also, so that we can get the number easily. - * As for meta_ac, we will at most add split 2 refcount record and - * 2 more refcount block, so just check it in a rough way. + * We will at most add split 2 refcount records and 2 more + * refcount blocks, so just check it in a rough way. * * Caller must hold refcount tree lock. */ int ocfs2_prepare_refcount_change_for_del(struct inode *inode, - struct buffer_head *di_bh, + u64 refcount_loc, u64 phys_blkno, u32 clusters, int *credits, - struct ocfs2_alloc_context **meta_ac) + int *ref_blocks) { - int ret, ref_blocks = 0; - struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data; + int ret; struct ocfs2_inode_info *oi = OCFS2_I(inode); struct buffer_head *ref_root_bh = NULL; struct ocfs2_refcount_tree *tree; @@ -2546,14 +2544,13 @@ int ocfs2_prepare_refcount_change_for_del(struct inode *inode, BUG_ON(!(oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL)); ret = ocfs2_get_refcount_tree(OCFS2_SB(inode->i_sb), - le64_to_cpu(di->i_refcount_loc), &tree); + refcount_loc, &tree); if (ret) { mlog_errno(ret); goto out; } - ret = ocfs2_read_refcount_block(&tree->rf_ci, - le64_to_cpu(di->i_refcount_loc), + ret = ocfs2_read_refcount_block(&tree->rf_ci, refcount_loc, &ref_root_bh); if (ret) { mlog_errno(ret); @@ -2564,21 +2561,14 @@ int ocfs2_prepare_refcount_change_for_del(struct inode *inode, &tree->rf_ci, ref_root_bh, start_cpos, clusters, - &ref_blocks, credits); + ref_blocks, credits); if (ret) { mlog_errno(ret); goto out; } - mlog(0, "reserve new metadata %d, credits = %d\n", - ref_blocks, *credits); - - if (ref_blocks) { - ret = ocfs2_reserve_new_metadata_blocks(OCFS2_SB(inode->i_sb), - ref_blocks, meta_ac); - if (ret) - mlog_errno(ret); - } + mlog(0, "reserve new metadata %d blocks, credits = %d\n", + *ref_blocks, *credits); out: brelse(ref_root_bh); @@ -3040,11 +3030,7 @@ static int ocfs2_duplicate_clusters_by_jbd(handle_t *handle, } memcpy(new_bh->b_data, old_bh->b_data, sb->s_blocksize); - ret = ocfs2_journal_dirty(handle, new_bh); - if (ret) { - mlog_errno(ret); - break; - } + ocfs2_journal_dirty(handle, new_bh); brelse(new_bh); brelse(old_bh); @@ -3282,7 +3268,7 @@ static int ocfs2_make_clusters_writable(struct super_block *sb, } else { delete = 1; - ret = __ocfs2_claim_clusters(osb, handle, + ret = __ocfs2_claim_clusters(handle, context->data_ac, 1, set_len, &new_bit, &new_len); diff --git a/fs/ocfs2/refcounttree.h b/fs/ocfs2/refcounttree.h index c1d19b1d3ec..9983ba1570e 100644 --- a/fs/ocfs2/refcounttree.h +++ b/fs/ocfs2/refcounttree.h @@ -47,11 +47,11 @@ int ocfs2_decrease_refcount(struct inode *inode, struct ocfs2_cached_dealloc_ctxt *dealloc, int delete); int ocfs2_prepare_refcount_change_for_del(struct inode *inode, - struct buffer_head *di_bh, + u64 refcount_loc, u64 phys_blkno, u32 clusters, int *credits, - struct ocfs2_alloc_context **meta_ac); + int *ref_blocks); int ocfs2_refcount_cow(struct inode *inode, struct buffer_head *di_bh, u32 cpos, u32 write_len, u32 max_cpos); diff --git a/fs/ocfs2/reservations.c b/fs/ocfs2/reservations.c new file mode 100644 index 00000000000..40650021fc2 --- /dev/null +++ b/fs/ocfs2/reservations.c @@ -0,0 +1,847 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * reservations.c + * + * Allocation reservations implementation + * + * Some code borrowed from fs/ext3/balloc.c and is: + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * The rest is copyright (C) 2010 Novell. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <linux/fs.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/highmem.h> +#include <linux/bitops.h> +#include <linux/list.h> + +#define MLOG_MASK_PREFIX ML_RESERVATIONS +#include <cluster/masklog.h> + +#include "ocfs2.h" + +#ifdef CONFIG_OCFS2_DEBUG_FS +#define OCFS2_CHECK_RESERVATIONS +#endif + +DEFINE_SPINLOCK(resv_lock); + +#define OCFS2_MIN_RESV_WINDOW_BITS 8 +#define OCFS2_MAX_RESV_WINDOW_BITS 1024 + +int ocfs2_dir_resv_allowed(struct ocfs2_super *osb) +{ + return (osb->osb_resv_level && osb->osb_dir_resv_level); +} + +static unsigned int ocfs2_resv_window_bits(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv) +{ + struct ocfs2_super *osb = resmap->m_osb; + unsigned int bits; + + if (!(resv->r_flags & OCFS2_RESV_FLAG_DIR)) { + /* 8, 16, 32, 64, 128, 256, 512, 1024 */ + bits = 4 << osb->osb_resv_level; + } else { + bits = 4 << osb->osb_dir_resv_level; + } + return bits; +} + +static inline unsigned int ocfs2_resv_end(struct ocfs2_alloc_reservation *resv) +{ + if (resv->r_len) + return resv->r_start + resv->r_len - 1; + return resv->r_start; +} + +static inline int ocfs2_resv_empty(struct ocfs2_alloc_reservation *resv) +{ + return !!(resv->r_len == 0); +} + +static inline int ocfs2_resmap_disabled(struct ocfs2_reservation_map *resmap) +{ + if (resmap->m_osb->osb_resv_level == 0) + return 1; + return 0; +} + +static void ocfs2_dump_resv(struct ocfs2_reservation_map *resmap) +{ + struct ocfs2_super *osb = resmap->m_osb; + struct rb_node *node; + struct ocfs2_alloc_reservation *resv; + int i = 0; + + mlog(ML_NOTICE, "Dumping resmap for device %s. Bitmap length: %u\n", + osb->dev_str, resmap->m_bitmap_len); + + node = rb_first(&resmap->m_reservations); + while (node) { + resv = rb_entry(node, struct ocfs2_alloc_reservation, r_node); + + mlog(ML_NOTICE, "start: %u\tend: %u\tlen: %u\tlast_start: %u" + "\tlast_len: %u\n", resv->r_start, + ocfs2_resv_end(resv), resv->r_len, resv->r_last_start, + resv->r_last_len); + + node = rb_next(node); + i++; + } + + mlog(ML_NOTICE, "%d reservations found. LRU follows\n", i); + + i = 0; + list_for_each_entry(resv, &resmap->m_lru, r_lru) { + mlog(ML_NOTICE, "LRU(%d) start: %u\tend: %u\tlen: %u\t" + "last_start: %u\tlast_len: %u\n", i, resv->r_start, + ocfs2_resv_end(resv), resv->r_len, resv->r_last_start, + resv->r_last_len); + + i++; + } +} + +#ifdef OCFS2_CHECK_RESERVATIONS +static int ocfs2_validate_resmap_bits(struct ocfs2_reservation_map *resmap, + int i, + struct ocfs2_alloc_reservation *resv) +{ + char *disk_bitmap = resmap->m_disk_bitmap; + unsigned int start = resv->r_start; + unsigned int end = ocfs2_resv_end(resv); + + while (start <= end) { + if (ocfs2_test_bit(start, disk_bitmap)) { + mlog(ML_ERROR, + "reservation %d covers an allocated area " + "starting at bit %u!\n", i, start); + return 1; + } + + start++; + } + return 0; +} + +static void ocfs2_check_resmap(struct ocfs2_reservation_map *resmap) +{ + unsigned int off = 0; + int i = 0; + struct rb_node *node; + struct ocfs2_alloc_reservation *resv; + + node = rb_first(&resmap->m_reservations); + while (node) { + resv = rb_entry(node, struct ocfs2_alloc_reservation, r_node); + + if (i > 0 && resv->r_start <= off) { + mlog(ML_ERROR, "reservation %d has bad start off!\n", + i); + goto bad; + } + + if (resv->r_len == 0) { + mlog(ML_ERROR, "reservation %d has no length!\n", + i); + goto bad; + } + + if (resv->r_start > ocfs2_resv_end(resv)) { + mlog(ML_ERROR, "reservation %d has invalid range!\n", + i); + goto bad; + } + + if (ocfs2_resv_end(resv) >= resmap->m_bitmap_len) { + mlog(ML_ERROR, "reservation %d extends past bitmap!\n", + i); + goto bad; + } + + if (ocfs2_validate_resmap_bits(resmap, i, resv)) + goto bad; + + off = ocfs2_resv_end(resv); + node = rb_next(node); + + i++; + } + return; + +bad: + ocfs2_dump_resv(resmap); + BUG(); +} +#else +static inline void ocfs2_check_resmap(struct ocfs2_reservation_map *resmap) +{ + +} +#endif + +void ocfs2_resv_init_once(struct ocfs2_alloc_reservation *resv) +{ + memset(resv, 0, sizeof(*resv)); + INIT_LIST_HEAD(&resv->r_lru); +} + +void ocfs2_resv_set_type(struct ocfs2_alloc_reservation *resv, + unsigned int flags) +{ + BUG_ON(flags & ~OCFS2_RESV_TYPES); + + resv->r_flags |= flags; +} + +int ocfs2_resmap_init(struct ocfs2_super *osb, + struct ocfs2_reservation_map *resmap) +{ + memset(resmap, 0, sizeof(*resmap)); + + resmap->m_osb = osb; + resmap->m_reservations = RB_ROOT; + /* m_bitmap_len is initialized to zero by the above memset. */ + INIT_LIST_HEAD(&resmap->m_lru); + + return 0; +} + +static void ocfs2_resv_mark_lru(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv) +{ + assert_spin_locked(&resv_lock); + + if (!list_empty(&resv->r_lru)) + list_del_init(&resv->r_lru); + + list_add_tail(&resv->r_lru, &resmap->m_lru); +} + +static void __ocfs2_resv_trunc(struct ocfs2_alloc_reservation *resv) +{ + resv->r_len = 0; + resv->r_start = 0; +} + +static void ocfs2_resv_remove(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv) +{ + if (resv->r_flags & OCFS2_RESV_FLAG_INUSE) { + list_del_init(&resv->r_lru); + rb_erase(&resv->r_node, &resmap->m_reservations); + resv->r_flags &= ~OCFS2_RESV_FLAG_INUSE; + } +} + +static void __ocfs2_resv_discard(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv) +{ + assert_spin_locked(&resv_lock); + + __ocfs2_resv_trunc(resv); + /* + * last_len and last_start no longer make sense if + * we're changing the range of our allocations. + */ + resv->r_last_len = resv->r_last_start = 0; + + ocfs2_resv_remove(resmap, resv); +} + +/* does nothing if 'resv' is null */ +void ocfs2_resv_discard(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv) +{ + if (resv) { + spin_lock(&resv_lock); + __ocfs2_resv_discard(resmap, resv); + spin_unlock(&resv_lock); + } +} + +static void ocfs2_resmap_clear_all_resv(struct ocfs2_reservation_map *resmap) +{ + struct rb_node *node; + struct ocfs2_alloc_reservation *resv; + + assert_spin_locked(&resv_lock); + + while ((node = rb_last(&resmap->m_reservations)) != NULL) { + resv = rb_entry(node, struct ocfs2_alloc_reservation, r_node); + + __ocfs2_resv_discard(resmap, resv); + } +} + +void ocfs2_resmap_restart(struct ocfs2_reservation_map *resmap, + unsigned int clen, char *disk_bitmap) +{ + if (ocfs2_resmap_disabled(resmap)) + return; + + spin_lock(&resv_lock); + + ocfs2_resmap_clear_all_resv(resmap); + resmap->m_bitmap_len = clen; + resmap->m_disk_bitmap = disk_bitmap; + + spin_unlock(&resv_lock); +} + +void ocfs2_resmap_uninit(struct ocfs2_reservation_map *resmap) +{ + /* Does nothing for now. Keep this around for API symmetry */ +} + +static void ocfs2_resv_insert(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *new) +{ + struct rb_root *root = &resmap->m_reservations; + struct rb_node *parent = NULL; + struct rb_node **p = &root->rb_node; + struct ocfs2_alloc_reservation *tmp; + + assert_spin_locked(&resv_lock); + + mlog(0, "Insert reservation start: %u len: %u\n", new->r_start, + new->r_len); + + while (*p) { + parent = *p; + + tmp = rb_entry(parent, struct ocfs2_alloc_reservation, r_node); + + if (new->r_start < tmp->r_start) { + p = &(*p)->rb_left; + + /* + * This is a good place to check for + * overlapping reservations. + */ + BUG_ON(ocfs2_resv_end(new) >= tmp->r_start); + } else if (new->r_start > ocfs2_resv_end(tmp)) { + p = &(*p)->rb_right; + } else { + /* This should never happen! */ + mlog(ML_ERROR, "Duplicate reservation window!\n"); + BUG(); + } + } + + rb_link_node(&new->r_node, parent, p); + rb_insert_color(&new->r_node, root); + new->r_flags |= OCFS2_RESV_FLAG_INUSE; + + ocfs2_resv_mark_lru(resmap, new); + + ocfs2_check_resmap(resmap); +} + +/** + * ocfs2_find_resv_lhs() - find the window which contains goal + * @resmap: reservation map to search + * @goal: which bit to search for + * + * If a window containing that goal is not found, we return the window + * which comes before goal. Returns NULL on empty rbtree or no window + * before goal. + */ +static struct ocfs2_alloc_reservation * +ocfs2_find_resv_lhs(struct ocfs2_reservation_map *resmap, unsigned int goal) +{ + struct ocfs2_alloc_reservation *resv = NULL; + struct ocfs2_alloc_reservation *prev_resv = NULL; + struct rb_node *node = resmap->m_reservations.rb_node; + + assert_spin_locked(&resv_lock); + + if (!node) + return NULL; + + node = rb_first(&resmap->m_reservations); + while (node) { + resv = rb_entry(node, struct ocfs2_alloc_reservation, r_node); + + if (resv->r_start <= goal && ocfs2_resv_end(resv) >= goal) + break; + + /* Check if we overshot the reservation just before goal? */ + if (resv->r_start > goal) { + resv = prev_resv; + break; + } + + prev_resv = resv; + node = rb_next(node); + } + + return resv; +} + +/* + * We are given a range within the bitmap, which corresponds to a gap + * inside the reservations tree (search_start, search_len). The range + * can be anything from the whole bitmap, to a gap between + * reservations. + * + * The start value of *rstart is insignificant. + * + * This function searches the bitmap range starting at search_start + * with length search_len for a set of contiguous free bits. We try + * to find up to 'wanted' bits, but can sometimes return less. + * + * Returns the length of allocation, 0 if no free bits are found. + * + * *cstart and *clen will also be populated with the result. + */ +static int ocfs2_resmap_find_free_bits(struct ocfs2_reservation_map *resmap, + unsigned int wanted, + unsigned int search_start, + unsigned int search_len, + unsigned int *rstart, + unsigned int *rlen) +{ + void *bitmap = resmap->m_disk_bitmap; + unsigned int best_start, best_len = 0; + int offset, start, found; + + mlog(0, "Find %u bits within range (%u, len %u) resmap len: %u\n", + wanted, search_start, search_len, resmap->m_bitmap_len); + + found = best_start = best_len = 0; + + start = search_start; + while ((offset = ocfs2_find_next_zero_bit(bitmap, resmap->m_bitmap_len, + start)) != -1) { + /* Search reached end of the region */ + if (offset >= (search_start + search_len)) + break; + + if (offset == start) { + /* we found a zero */ + found++; + /* move start to the next bit to test */ + start++; + } else { + /* got a zero after some ones */ + found = 1; + start = offset + 1; + } + if (found > best_len) { + best_len = found; + best_start = start - found; + } + + if (found >= wanted) + break; + } + + if (best_len == 0) + return 0; + + if (best_len >= wanted) + best_len = wanted; + + *rlen = best_len; + *rstart = best_start; + + mlog(0, "Found start: %u len: %u\n", best_start, best_len); + + return *rlen; +} + +static void __ocfs2_resv_find_window(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv, + unsigned int goal, unsigned int wanted) +{ + struct rb_root *root = &resmap->m_reservations; + unsigned int gap_start, gap_end, gap_len; + struct ocfs2_alloc_reservation *prev_resv, *next_resv; + struct rb_node *prev, *next; + unsigned int cstart, clen; + unsigned int best_start = 0, best_len = 0; + + /* + * Nasty cases to consider: + * + * - rbtree is empty + * - our window should be first in all reservations + * - our window should be last in all reservations + * - need to make sure we don't go past end of bitmap + */ + + mlog(0, "resv start: %u resv end: %u goal: %u wanted: %u\n", + resv->r_start, ocfs2_resv_end(resv), goal, wanted); + + assert_spin_locked(&resv_lock); + + if (RB_EMPTY_ROOT(root)) { + /* + * Easiest case - empty tree. We can just take + * whatever window of free bits we want. + */ + + mlog(0, "Empty root\n"); + + clen = ocfs2_resmap_find_free_bits(resmap, wanted, goal, + resmap->m_bitmap_len - goal, + &cstart, &clen); + + /* + * This should never happen - the local alloc window + * will always have free bits when we're called. + */ + BUG_ON(goal == 0 && clen == 0); + + if (clen == 0) + return; + + resv->r_start = cstart; + resv->r_len = clen; + + ocfs2_resv_insert(resmap, resv); + return; + } + + prev_resv = ocfs2_find_resv_lhs(resmap, goal); + + if (prev_resv == NULL) { + mlog(0, "Goal on LHS of leftmost window\n"); + + /* + * A NULL here means that the search code couldn't + * find a window that starts before goal. + * + * However, we can take the first window after goal, + * which is also by definition, the leftmost window in + * the entire tree. If we can find free bits in the + * gap between goal and the LHS window, then the + * reservation can safely be placed there. + * + * Otherwise we fall back to a linear search, checking + * the gaps in between windows for a place to + * allocate. + */ + + next = rb_first(root); + next_resv = rb_entry(next, struct ocfs2_alloc_reservation, + r_node); + + /* + * The search should never return such a window. (see + * comment above + */ + if (next_resv->r_start <= goal) { + mlog(ML_ERROR, "goal: %u next_resv: start %u len %u\n", + goal, next_resv->r_start, next_resv->r_len); + ocfs2_dump_resv(resmap); + BUG(); + } + + clen = ocfs2_resmap_find_free_bits(resmap, wanted, goal, + next_resv->r_start - goal, + &cstart, &clen); + if (clen) { + best_len = clen; + best_start = cstart; + if (best_len == wanted) + goto out_insert; + } + + prev_resv = next_resv; + next_resv = NULL; + } + + prev = &prev_resv->r_node; + + /* Now we do a linear search for a window, starting at 'prev_rsv' */ + while (1) { + next = rb_next(prev); + if (next) { + mlog(0, "One more resv found in linear search\n"); + next_resv = rb_entry(next, + struct ocfs2_alloc_reservation, + r_node); + + gap_start = ocfs2_resv_end(prev_resv) + 1; + gap_end = next_resv->r_start - 1; + gap_len = gap_end - gap_start + 1; + } else { + mlog(0, "No next node\n"); + /* + * We're at the rightmost edge of the + * tree. See if a reservation between this + * window and the end of the bitmap will work. + */ + gap_start = ocfs2_resv_end(prev_resv) + 1; + gap_len = resmap->m_bitmap_len - gap_start; + gap_end = resmap->m_bitmap_len - 1; + } + + /* + * No need to check this gap if we have already found + * a larger region of free bits. + */ + if (gap_len <= best_len) + goto next_resv; + + clen = ocfs2_resmap_find_free_bits(resmap, wanted, gap_start, + gap_len, &cstart, &clen); + if (clen == wanted) { + best_len = clen; + best_start = cstart; + goto out_insert; + } else if (clen > best_len) { + best_len = clen; + best_start = cstart; + } + +next_resv: + if (!next) + break; + + prev = next; + prev_resv = rb_entry(prev, struct ocfs2_alloc_reservation, + r_node); + } + +out_insert: + if (best_len) { + resv->r_start = best_start; + resv->r_len = best_len; + ocfs2_resv_insert(resmap, resv); + } +} + +static void ocfs2_cannibalize_resv(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv, + unsigned int wanted) +{ + struct ocfs2_alloc_reservation *lru_resv; + int tmpwindow = !!(resv->r_flags & OCFS2_RESV_FLAG_TMP); + unsigned int min_bits; + + if (!tmpwindow) + min_bits = ocfs2_resv_window_bits(resmap, resv) >> 1; + else + min_bits = wanted; /* We at know the temp window will use all + * of these bits */ + + /* + * Take the first reservation off the LRU as our 'target'. We + * don't try to be smart about it. There might be a case for + * searching based on size but I don't have enough data to be + * sure. --Mark (3/16/2010) + */ + lru_resv = list_first_entry(&resmap->m_lru, + struct ocfs2_alloc_reservation, r_lru); + + mlog(0, "lru resv: start: %u len: %u end: %u\n", lru_resv->r_start, + lru_resv->r_len, ocfs2_resv_end(lru_resv)); + + /* + * Cannibalize (some or all) of the target reservation and + * feed it to the current window. + */ + if (lru_resv->r_len <= min_bits) { + /* + * Discard completely if size is less than or equal to a + * reasonable threshold - 50% of window bits for non temporary + * windows. + */ + resv->r_start = lru_resv->r_start; + resv->r_len = lru_resv->r_len; + + __ocfs2_resv_discard(resmap, lru_resv); + } else { + unsigned int shrink; + if (tmpwindow) + shrink = min_bits; + else + shrink = lru_resv->r_len / 2; + + lru_resv->r_len -= shrink; + + resv->r_start = ocfs2_resv_end(lru_resv) + 1; + resv->r_len = shrink; + } + + mlog(0, "Reservation now looks like: r_start: %u r_end: %u " + "r_len: %u r_last_start: %u r_last_len: %u\n", + resv->r_start, ocfs2_resv_end(resv), resv->r_len, + resv->r_last_start, resv->r_last_len); + + ocfs2_resv_insert(resmap, resv); +} + +static void ocfs2_resv_find_window(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv, + unsigned int wanted) +{ + unsigned int goal = 0; + + BUG_ON(!ocfs2_resv_empty(resv)); + + /* + * Begin by trying to get a window as close to the previous + * one as possible. Using the most recent allocation as a + * start goal makes sense. + */ + if (resv->r_last_len) { + goal = resv->r_last_start + resv->r_last_len; + if (goal >= resmap->m_bitmap_len) + goal = 0; + } + + __ocfs2_resv_find_window(resmap, resv, goal, wanted); + + /* Search from last alloc didn't work, try once more from beginning. */ + if (ocfs2_resv_empty(resv) && goal != 0) + __ocfs2_resv_find_window(resmap, resv, 0, wanted); + + if (ocfs2_resv_empty(resv)) { + /* + * Still empty? Pull oldest one off the LRU, remove it from + * tree, put this one in it's place. + */ + ocfs2_cannibalize_resv(resmap, resv, wanted); + } + + BUG_ON(ocfs2_resv_empty(resv)); +} + +int ocfs2_resmap_resv_bits(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv, + int *cstart, int *clen) +{ + unsigned int wanted = *clen; + + if (resv == NULL || ocfs2_resmap_disabled(resmap)) + return -ENOSPC; + + spin_lock(&resv_lock); + + /* + * We don't want to over-allocate for temporary + * windows. Otherwise, we run the risk of fragmenting the + * allocation space. + */ + wanted = ocfs2_resv_window_bits(resmap, resv); + if ((resv->r_flags & OCFS2_RESV_FLAG_TMP) || wanted < *clen) + wanted = *clen; + + if (ocfs2_resv_empty(resv)) { + mlog(0, "empty reservation, find new window\n"); + + /* + * Try to get a window here. If it works, we must fall + * through and test the bitmap . This avoids some + * ping-ponging of windows due to non-reserved space + * being allocation before we initialize a window for + * that inode. + */ + ocfs2_resv_find_window(resmap, resv, wanted); + } + + BUG_ON(ocfs2_resv_empty(resv)); + + *cstart = resv->r_start; + *clen = resv->r_len; + + spin_unlock(&resv_lock); + return 0; +} + +static void + ocfs2_adjust_resv_from_alloc(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv, + unsigned int start, unsigned int end) +{ + unsigned int rhs = 0; + unsigned int old_end = ocfs2_resv_end(resv); + + BUG_ON(start != resv->r_start || old_end < end); + + /* + * Completely used? We can remove it then. + */ + if (old_end == end) { + __ocfs2_resv_discard(resmap, resv); + return; + } + + rhs = old_end - end; + + /* + * This should have been trapped above. + */ + BUG_ON(rhs == 0); + + resv->r_start = end + 1; + resv->r_len = old_end - resv->r_start + 1; +} + +void ocfs2_resmap_claimed_bits(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv, + u32 cstart, u32 clen) +{ + unsigned int cend = cstart + clen - 1; + + if (resmap == NULL || ocfs2_resmap_disabled(resmap)) + return; + + if (resv == NULL) + return; + + BUG_ON(cstart != resv->r_start); + + spin_lock(&resv_lock); + + mlog(0, "claim bits: cstart: %u cend: %u clen: %u r_start: %u " + "r_end: %u r_len: %u, r_last_start: %u r_last_len: %u\n", + cstart, cend, clen, resv->r_start, ocfs2_resv_end(resv), + resv->r_len, resv->r_last_start, resv->r_last_len); + + BUG_ON(cstart < resv->r_start); + BUG_ON(cstart > ocfs2_resv_end(resv)); + BUG_ON(cend > ocfs2_resv_end(resv)); + + ocfs2_adjust_resv_from_alloc(resmap, resv, cstart, cend); + resv->r_last_start = cstart; + resv->r_last_len = clen; + + /* + * May have been discarded above from + * ocfs2_adjust_resv_from_alloc(). + */ + if (!ocfs2_resv_empty(resv)) + ocfs2_resv_mark_lru(resmap, resv); + + mlog(0, "Reservation now looks like: r_start: %u r_end: %u " + "r_len: %u r_last_start: %u r_last_len: %u\n", + resv->r_start, ocfs2_resv_end(resv), resv->r_len, + resv->r_last_start, resv->r_last_len); + + ocfs2_check_resmap(resmap); + + spin_unlock(&resv_lock); +} diff --git a/fs/ocfs2/reservations.h b/fs/ocfs2/reservations.h new file mode 100644 index 00000000000..1e49cc29d06 --- /dev/null +++ b/fs/ocfs2/reservations.h @@ -0,0 +1,159 @@ +/* -*- mode: c; c-basic-offset: 8; -*- + * vim: noexpandtab sw=8 ts=8 sts=0: + * + * reservations.h + * + * Allocation reservations function prototypes and structures. + * + * Copyright (C) 2010 Novell. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef OCFS2_RESERVATIONS_H +#define OCFS2_RESERVATIONS_H + +#include <linux/rbtree.h> + +#define OCFS2_DEFAULT_RESV_LEVEL 2 +#define OCFS2_MAX_RESV_LEVEL 9 +#define OCFS2_MIN_RESV_LEVEL 0 + +struct ocfs2_alloc_reservation { + struct rb_node r_node; + + unsigned int r_start; /* Begining of current window */ + unsigned int r_len; /* Length of the window */ + + unsigned int r_last_len; /* Length of most recent alloc */ + unsigned int r_last_start; /* Start of most recent alloc */ + struct list_head r_lru; /* LRU list head */ + + unsigned int r_flags; +}; + +#define OCFS2_RESV_FLAG_INUSE 0x01 /* Set when r_node is part of a btree */ +#define OCFS2_RESV_FLAG_TMP 0x02 /* Temporary reservation, will be + * destroyed immedately after use */ +#define OCFS2_RESV_FLAG_DIR 0x04 /* Reservation is for an unindexed + * directory btree */ + +struct ocfs2_reservation_map { + struct rb_root m_reservations; + char *m_disk_bitmap; + + struct ocfs2_super *m_osb; + + /* The following are not initialized to meaningful values until a disk + * bitmap is provided. */ + u32 m_bitmap_len; /* Number of valid + * bits available */ + + struct list_head m_lru; /* LRU of reservations + * structures. */ + +}; + +void ocfs2_resv_init_once(struct ocfs2_alloc_reservation *resv); + +#define OCFS2_RESV_TYPES (OCFS2_RESV_FLAG_TMP|OCFS2_RESV_FLAG_DIR) +void ocfs2_resv_set_type(struct ocfs2_alloc_reservation *resv, + unsigned int flags); + +int ocfs2_dir_resv_allowed(struct ocfs2_super *osb); + +/** + * ocfs2_resv_discard() - truncate a reservation + * @resmap: + * @resv: the reservation to truncate. + * + * After this function is called, the reservation will be empty, and + * unlinked from the rbtree. + */ +void ocfs2_resv_discard(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv); + + +/** + * ocfs2_resmap_init() - Initialize fields of a reservations bitmap + * @resmap: struct ocfs2_reservation_map to initialize + * @obj: unused for now + * @ops: unused for now + * @max_bitmap_bytes: Maximum size of the bitmap (typically blocksize) + * + * Only possible return value other than '0' is -ENOMEM for failure to + * allocation mirror bitmap. + */ +int ocfs2_resmap_init(struct ocfs2_super *osb, + struct ocfs2_reservation_map *resmap); + +/** + * ocfs2_resmap_restart() - "restart" a reservation bitmap + * @resmap: reservations bitmap + * @clen: Number of valid bits in the bitmap + * @disk_bitmap: the disk bitmap this resmap should refer to. + * + * Re-initialize the parameters of a reservation bitmap. This is + * useful for local alloc window slides. + * + * This function will call ocfs2_trunc_resv against all existing + * reservations. A future version will recalculate existing + * reservations based on the new bitmap. + */ +void ocfs2_resmap_restart(struct ocfs2_reservation_map *resmap, + unsigned int clen, char *disk_bitmap); + +/** + * ocfs2_resmap_uninit() - uninitialize a reservation bitmap structure + * @resmap: the struct ocfs2_reservation_map to uninitialize + */ +void ocfs2_resmap_uninit(struct ocfs2_reservation_map *resmap); + +/** + * ocfs2_resmap_resv_bits() - Return still-valid reservation bits + * @resmap: reservations bitmap + * @resv: reservation to base search from + * @cstart: start of proposed allocation + * @clen: length (in clusters) of proposed allocation + * + * Using the reservation data from resv, this function will compare + * resmap and resmap->m_disk_bitmap to determine what part (if any) of + * the reservation window is still clear to use. If resv is empty, + * this function will try to allocate a window for it. + * + * On success, zero is returned and the valid allocation area is set in cstart + * and clen. + * + * Returns -ENOSPC if reservations are disabled. + */ +int ocfs2_resmap_resv_bits(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv, + int *cstart, int *clen); + +/** + * ocfs2_resmap_claimed_bits() - Tell the reservation code that bits were used. + * @resmap: reservations bitmap + * @resv: optional reservation to recalulate based on new bitmap + * @cstart: start of allocation in clusters + * @clen: end of allocation in clusters. + * + * Tell the reservation code that bits were used to fulfill allocation in + * resmap. The bits don't have to have been part of any existing + * reservation. But we must always call this function when bits are claimed. + * Internally, the reservations code will use this information to mark the + * reservations bitmap. If resv is passed, it's next allocation window will be + * calculated. It also expects that 'cstart' is the same as we passed back + * from ocfs2_resmap_resv_bits(). + */ +void ocfs2_resmap_claimed_bits(struct ocfs2_reservation_map *resmap, + struct ocfs2_alloc_reservation *resv, + u32 cstart, u32 clen); + +#endif /* OCFS2_RESERVATIONS_H */ diff --git a/fs/ocfs2/resize.c b/fs/ocfs2/resize.c index 3c3d673a4d2..dacd553d861 100644 --- a/fs/ocfs2/resize.c +++ b/fs/ocfs2/resize.c @@ -134,11 +134,7 @@ static int ocfs2_update_last_group_and_inode(handle_t *handle, le16_add_cpu(&group->bg_free_bits_count, -1 * backups); } - ret = ocfs2_journal_dirty(handle, group_bh); - if (ret < 0) { - mlog_errno(ret); - goto out_rollback; - } + ocfs2_journal_dirty(handle, group_bh); /* update the inode accordingly. */ ret = ocfs2_journal_access_di(handle, INODE_CACHE(bm_inode), bm_bh, @@ -319,7 +315,8 @@ int ocfs2_group_extend(struct inode * inode, int new_clusters) BUG_ON(!OCFS2_IS_VALID_DINODE(fe)); if (le16_to_cpu(fe->id2.i_chain.cl_cpg) != - ocfs2_group_bitmap_size(osb->sb) * 8) { + ocfs2_group_bitmap_size(osb->sb, 0, + osb->s_feature_incompat) * 8) { mlog(ML_ERROR, "The disk is too old and small. " "Force to do offline resize."); ret = -EINVAL; @@ -500,7 +497,8 @@ int ocfs2_group_add(struct inode *inode, struct ocfs2_new_group_input *input) fe = (struct ocfs2_dinode *)main_bm_bh->b_data; if (le16_to_cpu(fe->id2.i_chain.cl_cpg) != - ocfs2_group_bitmap_size(osb->sb) * 8) { + ocfs2_group_bitmap_size(osb->sb, 0, + osb->s_feature_incompat) * 8) { mlog(ML_ERROR, "The disk is too old and small." " Force to do offline resize."); ret = -EINVAL; @@ -545,12 +543,7 @@ int ocfs2_group_add(struct inode *inode, struct ocfs2_new_group_input *input) group = (struct ocfs2_group_desc *)group_bh->b_data; group->bg_next_group = cr->c_blkno; - - ret = ocfs2_journal_dirty(handle, group_bh); - if (ret < 0) { - mlog_errno(ret); - goto out_commit; - } + ocfs2_journal_dirty(handle, group_bh); ret = ocfs2_journal_access_di(handle, INODE_CACHE(main_bm_inode), main_bm_bh, OCFS2_JOURNAL_ACCESS_WRITE); diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index 19ba00f2854..f4c2a9eb8c4 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -53,6 +53,15 @@ #define OCFS2_MAX_TO_STEAL 1024 +struct ocfs2_suballoc_result { + u64 sr_bg_blkno; /* The bg we allocated from. Set + to 0 when a block group is + contiguous. */ + u64 sr_blkno; /* The first allocated block */ + unsigned int sr_bit_offset; /* The bit in the bg */ + unsigned int sr_bits; /* How many bits we claimed */ +}; + static inline void ocfs2_debug_bg(struct ocfs2_group_desc *bg); static inline void ocfs2_debug_suballoc_inode(struct ocfs2_dinode *fe); static inline u16 ocfs2_find_victim_chain(struct ocfs2_chain_list *cl); @@ -60,6 +69,7 @@ static int ocfs2_block_group_fill(handle_t *handle, struct inode *alloc_inode, struct buffer_head *bg_bh, u64 group_blkno, + unsigned int group_clusters, u16 my_chain, struct ocfs2_chain_list *cl); static int ocfs2_block_group_alloc(struct ocfs2_super *osb, @@ -73,20 +83,17 @@ static int ocfs2_cluster_group_search(struct inode *inode, struct buffer_head *group_bh, u32 bits_wanted, u32 min_bits, u64 max_block, - u16 *bit_off, u16 *bits_found); + struct ocfs2_suballoc_result *res); static int ocfs2_block_group_search(struct inode *inode, struct buffer_head *group_bh, u32 bits_wanted, u32 min_bits, u64 max_block, - u16 *bit_off, u16 *bits_found); -static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb, - struct ocfs2_alloc_context *ac, + struct ocfs2_suballoc_result *res); +static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac, handle_t *handle, u32 bits_wanted, u32 min_bits, - u16 *bit_off, - unsigned int *num_bits, - u64 *bg_blkno); + struct ocfs2_suballoc_result *res); static int ocfs2_test_bg_bit_allocatable(struct buffer_head *bg_bh, int nr); static inline int ocfs2_block_group_set_bits(handle_t *handle, @@ -130,6 +137,7 @@ void ocfs2_free_ac_resource(struct ocfs2_alloc_context *ac) } brelse(ac->ac_bh); ac->ac_bh = NULL; + ac->ac_resv = NULL; } void ocfs2_free_alloc_context(struct ocfs2_alloc_context *ac) @@ -325,14 +333,38 @@ out: return rc; } +static void ocfs2_bg_discontig_add_extent(struct ocfs2_super *osb, + struct ocfs2_group_desc *bg, + struct ocfs2_chain_list *cl, + u64 p_blkno, u32 clusters) +{ + struct ocfs2_extent_list *el = &bg->bg_list; + struct ocfs2_extent_rec *rec; + + BUG_ON(!ocfs2_supports_discontig_bg(osb)); + if (!el->l_next_free_rec) + el->l_count = cpu_to_le16(ocfs2_extent_recs_per_gd(osb->sb)); + rec = &el->l_recs[le16_to_cpu(el->l_next_free_rec)]; + rec->e_blkno = cpu_to_le64(p_blkno); + rec->e_cpos = cpu_to_le32(le16_to_cpu(bg->bg_bits) / + le16_to_cpu(cl->cl_bpc)); + rec->e_leaf_clusters = cpu_to_le32(clusters); + le16_add_cpu(&bg->bg_bits, clusters * le16_to_cpu(cl->cl_bpc)); + le16_add_cpu(&bg->bg_free_bits_count, + clusters * le16_to_cpu(cl->cl_bpc)); + le16_add_cpu(&el->l_next_free_rec, 1); +} + static int ocfs2_block_group_fill(handle_t *handle, struct inode *alloc_inode, struct buffer_head *bg_bh, u64 group_blkno, + unsigned int group_clusters, u16 my_chain, struct ocfs2_chain_list *cl) { int status = 0; + struct ocfs2_super *osb = OCFS2_SB(alloc_inode->i_sb); struct ocfs2_group_desc *bg = (struct ocfs2_group_desc *) bg_bh->b_data; struct super_block * sb = alloc_inode->i_sb; @@ -359,19 +391,23 @@ static int ocfs2_block_group_fill(handle_t *handle, memset(bg, 0, sb->s_blocksize); strcpy(bg->bg_signature, OCFS2_GROUP_DESC_SIGNATURE); bg->bg_generation = cpu_to_le32(OCFS2_SB(sb)->fs_generation); - bg->bg_size = cpu_to_le16(ocfs2_group_bitmap_size(sb)); - bg->bg_bits = cpu_to_le16(ocfs2_bits_per_group(cl)); + bg->bg_size = cpu_to_le16(ocfs2_group_bitmap_size(sb, 1, + osb->s_feature_incompat)); bg->bg_chain = cpu_to_le16(my_chain); bg->bg_next_group = cl->cl_recs[my_chain].c_blkno; bg->bg_parent_dinode = cpu_to_le64(OCFS2_I(alloc_inode)->ip_blkno); bg->bg_blkno = cpu_to_le64(group_blkno); + if (group_clusters == le16_to_cpu(cl->cl_cpg)) + bg->bg_bits = cpu_to_le16(ocfs2_bits_per_group(cl)); + else + ocfs2_bg_discontig_add_extent(osb, bg, cl, group_blkno, + group_clusters); + /* set the 1st bit in the bitmap to account for the descriptor block */ ocfs2_set_bit(0, (unsigned long *)bg->bg_bitmap); bg->bg_free_bits_count = cpu_to_le16(le16_to_cpu(bg->bg_bits) - 1); - status = ocfs2_journal_dirty(handle, bg_bh); - if (status < 0) - mlog_errno(status); + ocfs2_journal_dirty(handle, bg_bh); /* There is no need to zero out or otherwise initialize the * other blocks in a group - All valid FS metadata in a block @@ -397,6 +433,238 @@ static inline u16 ocfs2_find_smallest_chain(struct ocfs2_chain_list *cl) return best; } +static struct buffer_head * +ocfs2_block_group_alloc_contig(struct ocfs2_super *osb, handle_t *handle, + struct inode *alloc_inode, + struct ocfs2_alloc_context *ac, + struct ocfs2_chain_list *cl) +{ + int status; + u32 bit_off, num_bits; + u64 bg_blkno; + struct buffer_head *bg_bh; + unsigned int alloc_rec = ocfs2_find_smallest_chain(cl); + + status = ocfs2_claim_clusters(handle, ac, + le16_to_cpu(cl->cl_cpg), &bit_off, + &num_bits); + if (status < 0) { + if (status != -ENOSPC) + mlog_errno(status); + goto bail; + } + + /* setup the group */ + bg_blkno = ocfs2_clusters_to_blocks(osb->sb, bit_off); + mlog(0, "new descriptor, record %u, at block %llu\n", + alloc_rec, (unsigned long long)bg_blkno); + + bg_bh = sb_getblk(osb->sb, bg_blkno); + if (!bg_bh) { + status = -EIO; + mlog_errno(status); + goto bail; + } + ocfs2_set_new_buffer_uptodate(INODE_CACHE(alloc_inode), bg_bh); + + status = ocfs2_block_group_fill(handle, alloc_inode, bg_bh, + bg_blkno, num_bits, alloc_rec, cl); + if (status < 0) { + brelse(bg_bh); + mlog_errno(status); + } + +bail: + return status ? ERR_PTR(status) : bg_bh; +} + +static int ocfs2_block_group_claim_bits(struct ocfs2_super *osb, + handle_t *handle, + struct ocfs2_alloc_context *ac, + unsigned int min_bits, + u32 *bit_off, u32 *num_bits) +{ + int status = 0; + + while (min_bits) { + status = ocfs2_claim_clusters(handle, ac, min_bits, + bit_off, num_bits); + if (status != -ENOSPC) + break; + + min_bits >>= 1; + } + + return status; +} + +static int ocfs2_block_group_grow_discontig(handle_t *handle, + struct inode *alloc_inode, + struct buffer_head *bg_bh, + struct ocfs2_alloc_context *ac, + struct ocfs2_chain_list *cl, + unsigned int min_bits) +{ + int status; + struct ocfs2_super *osb = OCFS2_SB(alloc_inode->i_sb); + struct ocfs2_group_desc *bg = + (struct ocfs2_group_desc *)bg_bh->b_data; + unsigned int needed = le16_to_cpu(cl->cl_cpg) - + le16_to_cpu(bg->bg_bits) / le16_to_cpu(cl->cl_bpc); + u32 p_cpos, clusters; + u64 p_blkno; + struct ocfs2_extent_list *el = &bg->bg_list; + + status = ocfs2_journal_access_gd(handle, + INODE_CACHE(alloc_inode), + bg_bh, + OCFS2_JOURNAL_ACCESS_CREATE); + if (status < 0) { + mlog_errno(status); + goto bail; + } + + while ((needed > 0) && (le16_to_cpu(el->l_next_free_rec) < + le16_to_cpu(el->l_count))) { + if (min_bits > needed) + min_bits = needed; + status = ocfs2_block_group_claim_bits(osb, handle, ac, + min_bits, &p_cpos, + &clusters); + if (status < 0) { + if (status != -ENOSPC) + mlog_errno(status); + goto bail; + } + p_blkno = ocfs2_clusters_to_blocks(osb->sb, p_cpos); + ocfs2_bg_discontig_add_extent(osb, bg, cl, p_blkno, + clusters); + + min_bits = clusters; + needed = le16_to_cpu(cl->cl_cpg) - + le16_to_cpu(bg->bg_bits) / le16_to_cpu(cl->cl_bpc); + } + + if (needed > 0) { + /* + * We have used up all the extent rec but can't fill up + * the cpg. So bail out. + */ + status = -ENOSPC; + goto bail; + } + + ocfs2_journal_dirty(handle, bg_bh); + +bail: + return status; +} + +static void ocfs2_bg_alloc_cleanup(handle_t *handle, + struct ocfs2_alloc_context *cluster_ac, + struct inode *alloc_inode, + struct buffer_head *bg_bh) +{ + int i, ret; + struct ocfs2_group_desc *bg; + struct ocfs2_extent_list *el; + struct ocfs2_extent_rec *rec; + + if (!bg_bh) + return; + + bg = (struct ocfs2_group_desc *)bg_bh->b_data; + el = &bg->bg_list; + for (i = 0; i < le16_to_cpu(el->l_next_free_rec); i++) { + rec = &el->l_recs[i]; + ret = ocfs2_free_clusters(handle, cluster_ac->ac_inode, + cluster_ac->ac_bh, + le64_to_cpu(rec->e_blkno), + le32_to_cpu(rec->e_leaf_clusters)); + if (ret) + mlog_errno(ret); + /* Try all the clusters to free */ + } + + ocfs2_remove_from_cache(INODE_CACHE(alloc_inode), bg_bh); + brelse(bg_bh); +} + +static struct buffer_head * +ocfs2_block_group_alloc_discontig(handle_t *handle, + struct inode *alloc_inode, + struct ocfs2_alloc_context *ac, + struct ocfs2_chain_list *cl) +{ + int status; + u32 bit_off, num_bits; + u64 bg_blkno; + unsigned int min_bits = le16_to_cpu(cl->cl_cpg) >> 1; + struct buffer_head *bg_bh = NULL; + unsigned int alloc_rec = ocfs2_find_smallest_chain(cl); + struct ocfs2_super *osb = OCFS2_SB(alloc_inode->i_sb); + + if (!ocfs2_supports_discontig_bg(osb)) { + status = -ENOSPC; + goto bail; + } + + status = ocfs2_extend_trans(handle, + ocfs2_calc_bg_discontig_credits(osb->sb)); + if (status) { + mlog_errno(status); + goto bail; + } + + /* + * We're going to be grabbing from multiple cluster groups. + * We don't have enough credits to relink them all, and the + * cluster groups will be staying in cache for the duration of + * this operation. + */ + ac->ac_allow_chain_relink = 0; + + /* Claim the first region */ + status = ocfs2_block_group_claim_bits(osb, handle, ac, min_bits, + &bit_off, &num_bits); + if (status < 0) { + if (status != -ENOSPC) + mlog_errno(status); + goto bail; + } + min_bits = num_bits; + + /* setup the group */ + bg_blkno = ocfs2_clusters_to_blocks(osb->sb, bit_off); + mlog(0, "new descriptor, record %u, at block %llu\n", + alloc_rec, (unsigned long long)bg_blkno); + + bg_bh = sb_getblk(osb->sb, bg_blkno); + if (!bg_bh) { + status = -EIO; + mlog_errno(status); + goto bail; + } + ocfs2_set_new_buffer_uptodate(INODE_CACHE(alloc_inode), bg_bh); + + status = ocfs2_block_group_fill(handle, alloc_inode, bg_bh, + bg_blkno, num_bits, alloc_rec, cl); + if (status < 0) { + mlog_errno(status); + goto bail; + } + + status = ocfs2_block_group_grow_discontig(handle, alloc_inode, + bg_bh, ac, cl, min_bits); + if (status) + mlog_errno(status); + +bail: + if (status) + ocfs2_bg_alloc_cleanup(handle, ac, alloc_inode, bg_bh); + return status ? ERR_PTR(status) : bg_bh; +} + /* * We expect the block group allocator to already be locked. */ @@ -412,9 +680,7 @@ static int ocfs2_block_group_alloc(struct ocfs2_super *osb, struct ocfs2_chain_list *cl; struct ocfs2_alloc_context *ac = NULL; handle_t *handle = NULL; - u32 bit_off, num_bits; u16 alloc_rec; - u64 bg_blkno; struct buffer_head *bg_bh = NULL; struct ocfs2_group_desc *bg; @@ -447,44 +713,20 @@ static int ocfs2_block_group_alloc(struct ocfs2_super *osb, (unsigned long long)*last_alloc_group); ac->ac_last_group = *last_alloc_group; } - status = ocfs2_claim_clusters(osb, - handle, - ac, - le16_to_cpu(cl->cl_cpg), - &bit_off, - &num_bits); - if (status < 0) { + + bg_bh = ocfs2_block_group_alloc_contig(osb, handle, alloc_inode, + ac, cl); + if (IS_ERR(bg_bh) && (PTR_ERR(bg_bh) == -ENOSPC)) + bg_bh = ocfs2_block_group_alloc_discontig(handle, + alloc_inode, + ac, cl); + if (IS_ERR(bg_bh)) { + status = PTR_ERR(bg_bh); + bg_bh = NULL; if (status != -ENOSPC) mlog_errno(status); goto bail; } - - alloc_rec = ocfs2_find_smallest_chain(cl); - - /* setup the group */ - bg_blkno = ocfs2_clusters_to_blocks(osb->sb, bit_off); - mlog(0, "new descriptor, record %u, at block %llu\n", - alloc_rec, (unsigned long long)bg_blkno); - - bg_bh = sb_getblk(osb->sb, bg_blkno); - if (!bg_bh) { - status = -EIO; - mlog_errno(status); - goto bail; - } - ocfs2_set_new_buffer_uptodate(INODE_CACHE(alloc_inode), bg_bh); - - status = ocfs2_block_group_fill(handle, - alloc_inode, - bg_bh, - bg_blkno, - alloc_rec, - cl); - if (status < 0) { - mlog_errno(status); - goto bail; - } - bg = (struct ocfs2_group_desc *) bg_bh->b_data; status = ocfs2_journal_access_di(handle, INODE_CACHE(alloc_inode), @@ -494,10 +736,12 @@ static int ocfs2_block_group_alloc(struct ocfs2_super *osb, goto bail; } + alloc_rec = le16_to_cpu(bg->bg_chain); le32_add_cpu(&cl->cl_recs[alloc_rec].c_free, le16_to_cpu(bg->bg_free_bits_count)); - le32_add_cpu(&cl->cl_recs[alloc_rec].c_total, le16_to_cpu(bg->bg_bits)); - cl->cl_recs[alloc_rec].c_blkno = cpu_to_le64(bg_blkno); + le32_add_cpu(&cl->cl_recs[alloc_rec].c_total, + le16_to_cpu(bg->bg_bits)); + cl->cl_recs[alloc_rec].c_blkno = cpu_to_le64(bg->bg_blkno); if (le16_to_cpu(cl->cl_next_free_rec) < le16_to_cpu(cl->cl_count)) le16_add_cpu(&cl->cl_next_free_rec, 1); @@ -506,11 +750,7 @@ static int ocfs2_block_group_alloc(struct ocfs2_super *osb, le32_add_cpu(&fe->id1.bitmap1.i_total, le16_to_cpu(bg->bg_bits)); le32_add_cpu(&fe->i_clusters, le16_to_cpu(cl->cl_cpg)); - status = ocfs2_journal_dirty(handle, bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, bh); spin_lock(&OCFS2_I(alloc_inode)->ip_lock); OCFS2_I(alloc_inode)->ip_clusters = le32_to_cpu(fe->i_clusters); @@ -760,7 +1000,7 @@ int ocfs2_reserve_new_metadata_blocks(struct ocfs2_super *osb, status = ocfs2_reserve_suballoc_bits(osb, (*ac), EXTENT_ALLOC_SYSTEM_INODE, (u32)osb->slot_num, NULL, - ALLOC_NEW_GROUP); + ALLOC_GROUPS_FROM_GLOBAL|ALLOC_NEW_GROUP); if (status >= 0) { @@ -946,11 +1186,7 @@ static int ocfs2_reserve_clusters_with_limit(struct ocfs2_super *osb, status = ocfs2_reserve_local_alloc_bits(osb, bits_wanted, *ac); - if (status == -EFBIG) { - /* The local alloc window is outside ac_max_block. - * use the main bitmap. */ - status = -ENOSPC; - } else if ((status < 0) && (status != -ENOSPC)) { + if ((status < 0) && (status != -ENOSPC)) { mlog_errno(status); goto bail; } @@ -1033,8 +1269,7 @@ static int ocfs2_block_group_find_clear_bits(struct ocfs2_super *osb, struct buffer_head *bg_bh, unsigned int bits_wanted, unsigned int total_bits, - u16 *bit_off, - u16 *bits_found) + struct ocfs2_suballoc_result *res) { void *bitmap; u16 best_offset, best_size; @@ -1078,14 +1313,9 @@ static int ocfs2_block_group_find_clear_bits(struct ocfs2_super *osb, } } - /* XXX: I think the first clause is equivalent to the second - * - jlbec */ - if (found == bits_wanted) { - *bit_off = start - found; - *bits_found = found; - } else if (best_size) { - *bit_off = best_offset; - *bits_found = best_size; + if (best_size) { + res->sr_bit_offset = best_offset; + res->sr_bits = best_size; } else { status = -ENOSPC; /* No error log here -- see the comment above @@ -1129,16 +1359,10 @@ static inline int ocfs2_block_group_set_bits(handle_t *handle, } le16_add_cpu(&bg->bg_free_bits_count, -num_bits); - while(num_bits--) ocfs2_set_bit(bit_off++, bitmap); - status = ocfs2_journal_dirty(handle, - group_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, group_bh); bail: mlog_exit(status); @@ -1202,12 +1426,7 @@ static int ocfs2_relink_block_group(handle_t *handle, } prev_bg->bg_next_group = bg->bg_next_group; - - status = ocfs2_journal_dirty(handle, prev_bg_bh); - if (status < 0) { - mlog_errno(status); - goto out_rollback; - } + ocfs2_journal_dirty(handle, prev_bg_bh); status = ocfs2_journal_access_gd(handle, INODE_CACHE(alloc_inode), bg_bh, OCFS2_JOURNAL_ACCESS_WRITE); @@ -1217,12 +1436,7 @@ static int ocfs2_relink_block_group(handle_t *handle, } bg->bg_next_group = fe->id2.i_chain.cl_recs[chain].c_blkno; - - status = ocfs2_journal_dirty(handle, bg_bh); - if (status < 0) { - mlog_errno(status); - goto out_rollback; - } + ocfs2_journal_dirty(handle, bg_bh); status = ocfs2_journal_access_di(handle, INODE_CACHE(alloc_inode), fe_bh, OCFS2_JOURNAL_ACCESS_WRITE); @@ -1232,14 +1446,8 @@ static int ocfs2_relink_block_group(handle_t *handle, } fe->id2.i_chain.cl_recs[chain].c_blkno = bg->bg_blkno; + ocfs2_journal_dirty(handle, fe_bh); - status = ocfs2_journal_dirty(handle, fe_bh); - if (status < 0) { - mlog_errno(status); - goto out_rollback; - } - - status = 0; out_rollback: if (status < 0) { fe->id2.i_chain.cl_recs[chain].c_blkno = cpu_to_le64(fe_ptr); @@ -1263,14 +1471,13 @@ static int ocfs2_cluster_group_search(struct inode *inode, struct buffer_head *group_bh, u32 bits_wanted, u32 min_bits, u64 max_block, - u16 *bit_off, u16 *bits_found) + struct ocfs2_suballoc_result *res) { int search = -ENOSPC; int ret; u64 blkoff; struct ocfs2_group_desc *gd = (struct ocfs2_group_desc *) group_bh->b_data; struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - u16 tmp_off, tmp_found; unsigned int max_bits, gd_cluster_off; BUG_ON(!ocfs2_is_cluster_bitmap(inode)); @@ -1297,15 +1504,15 @@ static int ocfs2_cluster_group_search(struct inode *inode, ret = ocfs2_block_group_find_clear_bits(OCFS2_SB(inode->i_sb), group_bh, bits_wanted, - max_bits, - &tmp_off, &tmp_found); + max_bits, res); if (ret) return ret; if (max_block) { blkoff = ocfs2_clusters_to_blocks(inode->i_sb, gd_cluster_off + - tmp_off + tmp_found); + res->sr_bit_offset + + res->sr_bits); mlog(0, "Checking %llu against %llu\n", (unsigned long long)blkoff, (unsigned long long)max_block); @@ -1317,16 +1524,14 @@ static int ocfs2_cluster_group_search(struct inode *inode, * return success, but we still want to return * -ENOSPC unless it found the minimum number * of bits. */ - if (min_bits <= tmp_found) { - *bit_off = tmp_off; - *bits_found = tmp_found; + if (min_bits <= res->sr_bits) search = 0; /* success */ - } else if (tmp_found) { + else if (res->sr_bits) { /* * Don't show bits which we'll be returning * for allocation to the local alloc bitmap. */ - ocfs2_local_alloc_seen_free_bits(osb, tmp_found); + ocfs2_local_alloc_seen_free_bits(osb, res->sr_bits); } } @@ -1337,7 +1542,7 @@ static int ocfs2_block_group_search(struct inode *inode, struct buffer_head *group_bh, u32 bits_wanted, u32 min_bits, u64 max_block, - u16 *bit_off, u16 *bits_found) + struct ocfs2_suballoc_result *res) { int ret = -ENOSPC; u64 blkoff; @@ -1350,10 +1555,10 @@ static int ocfs2_block_group_search(struct inode *inode, ret = ocfs2_block_group_find_clear_bits(OCFS2_SB(inode->i_sb), group_bh, bits_wanted, le16_to_cpu(bg->bg_bits), - bit_off, bits_found); + res); if (!ret && max_block) { - blkoff = le64_to_cpu(bg->bg_blkno) + *bit_off + - *bits_found; + blkoff = le64_to_cpu(bg->bg_blkno) + + res->sr_bit_offset + res->sr_bits; mlog(0, "Checking %llu against %llu\n", (unsigned long long)blkoff, (unsigned long long)max_block); @@ -1386,33 +1591,76 @@ static int ocfs2_alloc_dinode_update_counts(struct inode *inode, tmp_used = le32_to_cpu(di->id1.bitmap1.i_used); di->id1.bitmap1.i_used = cpu_to_le32(num_bits + tmp_used); le32_add_cpu(&cl->cl_recs[chain].c_free, -num_bits); - - ret = ocfs2_journal_dirty(handle, di_bh); - if (ret < 0) - mlog_errno(ret); + ocfs2_journal_dirty(handle, di_bh); out: return ret; } +static int ocfs2_bg_discontig_fix_by_rec(struct ocfs2_suballoc_result *res, + struct ocfs2_extent_rec *rec, + struct ocfs2_chain_list *cl) +{ + unsigned int bpc = le16_to_cpu(cl->cl_bpc); + unsigned int bitoff = le32_to_cpu(rec->e_cpos) * bpc; + unsigned int bitcount = le32_to_cpu(rec->e_leaf_clusters) * bpc; + + if (res->sr_bit_offset < bitoff) + return 0; + if (res->sr_bit_offset >= (bitoff + bitcount)) + return 0; + res->sr_blkno = le64_to_cpu(rec->e_blkno) + + (res->sr_bit_offset - bitoff); + if ((res->sr_bit_offset + res->sr_bits) > (bitoff + bitcount)) + res->sr_bits = (bitoff + bitcount) - res->sr_bit_offset; + return 1; +} + +static void ocfs2_bg_discontig_fix_result(struct ocfs2_alloc_context *ac, + struct ocfs2_group_desc *bg, + struct ocfs2_suballoc_result *res) +{ + int i; + u64 bg_blkno = res->sr_bg_blkno; /* Save off */ + struct ocfs2_extent_rec *rec; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)ac->ac_bh->b_data; + struct ocfs2_chain_list *cl = &di->id2.i_chain; + + if (ocfs2_is_cluster_bitmap(ac->ac_inode)) { + res->sr_blkno = 0; + return; + } + + res->sr_blkno = res->sr_bg_blkno + res->sr_bit_offset; + res->sr_bg_blkno = 0; /* Clear it for contig block groups */ + if (!ocfs2_supports_discontig_bg(OCFS2_SB(ac->ac_inode->i_sb)) || + !bg->bg_list.l_next_free_rec) + return; + + for (i = 0; i < le16_to_cpu(bg->bg_list.l_next_free_rec); i++) { + rec = &bg->bg_list.l_recs[i]; + if (ocfs2_bg_discontig_fix_by_rec(res, rec, cl)) { + res->sr_bg_blkno = bg_blkno; /* Restore */ + break; + } + } +} + static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac, handle_t *handle, u32 bits_wanted, u32 min_bits, - u16 *bit_off, - unsigned int *num_bits, - u64 gd_blkno, + struct ocfs2_suballoc_result *res, u16 *bits_left) { int ret; - u16 found; struct buffer_head *group_bh = NULL; struct ocfs2_group_desc *gd; struct ocfs2_dinode *di = (struct ocfs2_dinode *)ac->ac_bh->b_data; struct inode *alloc_inode = ac->ac_inode; - ret = ocfs2_read_group_descriptor(alloc_inode, di, gd_blkno, - &group_bh); + ret = ocfs2_read_group_descriptor(alloc_inode, di, + res->sr_bg_blkno, &group_bh); if (ret < 0) { mlog_errno(ret); return ret; @@ -1420,17 +1668,18 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac, gd = (struct ocfs2_group_desc *) group_bh->b_data; ret = ac->ac_group_search(alloc_inode, group_bh, bits_wanted, min_bits, - ac->ac_max_block, bit_off, &found); + ac->ac_max_block, res); if (ret < 0) { if (ret != -ENOSPC) mlog_errno(ret); goto out; } - *num_bits = found; + if (!ret) + ocfs2_bg_discontig_fix_result(ac, gd, res); ret = ocfs2_alloc_dinode_update_counts(alloc_inode, handle, ac->ac_bh, - *num_bits, + res->sr_bits, le16_to_cpu(gd->bg_chain)); if (ret < 0) { mlog_errno(ret); @@ -1438,7 +1687,7 @@ static int ocfs2_search_one_group(struct ocfs2_alloc_context *ac, } ret = ocfs2_block_group_set_bits(handle, alloc_inode, gd, group_bh, - *bit_off, *num_bits); + res->sr_bit_offset, res->sr_bits); if (ret < 0) mlog_errno(ret); @@ -1454,13 +1703,11 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac, handle_t *handle, u32 bits_wanted, u32 min_bits, - u16 *bit_off, - unsigned int *num_bits, - u64 *bg_blkno, + struct ocfs2_suballoc_result *res, u16 *bits_left) { int status; - u16 chain, tmp_bits; + u16 chain; u32 tmp_used; u64 next_group; struct inode *alloc_inode = ac->ac_inode; @@ -1489,8 +1736,8 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac, * the 1st group with any empty bits. */ while ((status = ac->ac_group_search(alloc_inode, group_bh, bits_wanted, min_bits, - ac->ac_max_block, bit_off, - &tmp_bits)) == -ENOSPC) { + ac->ac_max_block, + res)) == -ENOSPC) { if (!bg->bg_next_group) break; @@ -1515,11 +1762,14 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac, } mlog(0, "alloc succeeds: we give %u bits from block group %llu\n", - tmp_bits, (unsigned long long)le64_to_cpu(bg->bg_blkno)); + res->sr_bits, (unsigned long long)le64_to_cpu(bg->bg_blkno)); - *num_bits = tmp_bits; + res->sr_bg_blkno = le64_to_cpu(bg->bg_blkno); + + BUG_ON(res->sr_bits == 0); + if (!status) + ocfs2_bg_discontig_fix_result(ac, bg, res); - BUG_ON(*num_bits == 0); /* * Keep track of previous block descriptor read. When @@ -1536,7 +1786,7 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac, */ if (ac->ac_allow_chain_relink && (prev_group_bh) && - (ocfs2_block_group_reasonably_empty(bg, *num_bits))) { + (ocfs2_block_group_reasonably_empty(bg, res->sr_bits))) { status = ocfs2_relink_block_group(handle, alloc_inode, ac->ac_bh, group_bh, prev_group_bh, chain); @@ -1558,31 +1808,24 @@ static int ocfs2_search_chain(struct ocfs2_alloc_context *ac, } tmp_used = le32_to_cpu(fe->id1.bitmap1.i_used); - fe->id1.bitmap1.i_used = cpu_to_le32(*num_bits + tmp_used); - le32_add_cpu(&cl->cl_recs[chain].c_free, -(*num_bits)); - - status = ocfs2_journal_dirty(handle, - ac->ac_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + fe->id1.bitmap1.i_used = cpu_to_le32(res->sr_bits + tmp_used); + le32_add_cpu(&cl->cl_recs[chain].c_free, -res->sr_bits); + ocfs2_journal_dirty(handle, ac->ac_bh); status = ocfs2_block_group_set_bits(handle, alloc_inode, bg, group_bh, - *bit_off, - *num_bits); + res->sr_bit_offset, + res->sr_bits); if (status < 0) { mlog_errno(status); goto bail; } - mlog(0, "Allocated %u bits from suballocator %llu\n", *num_bits, + mlog(0, "Allocated %u bits from suballocator %llu\n", res->sr_bits, (unsigned long long)le64_to_cpu(fe->i_blkno)); - *bg_blkno = le64_to_cpu(bg->bg_blkno); *bits_left = le16_to_cpu(bg->bg_free_bits_count); bail: brelse(group_bh); @@ -1593,19 +1836,15 @@ bail: } /* will give out up to bits_wanted contiguous bits. */ -static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb, - struct ocfs2_alloc_context *ac, +static int ocfs2_claim_suballoc_bits(struct ocfs2_alloc_context *ac, handle_t *handle, u32 bits_wanted, u32 min_bits, - u16 *bit_off, - unsigned int *num_bits, - u64 *bg_blkno) + struct ocfs2_suballoc_result *res) { int status; u16 victim, i; u16 bits_left = 0; - u64 hint_blkno = ac->ac_last_group; struct ocfs2_chain_list *cl; struct ocfs2_dinode *fe; @@ -1623,7 +1862,8 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb, if (le32_to_cpu(fe->id1.bitmap1.i_used) >= le32_to_cpu(fe->id1.bitmap1.i_total)) { - ocfs2_error(osb->sb, "Chain allocator dinode %llu has %u used " + ocfs2_error(ac->ac_inode->i_sb, + "Chain allocator dinode %llu has %u used " "bits but only %u total.", (unsigned long long)le64_to_cpu(fe->i_blkno), le32_to_cpu(fe->id1.bitmap1.i_used), @@ -1632,22 +1872,16 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb, goto bail; } - if (hint_blkno) { + res->sr_bg_blkno = ac->ac_last_group; + if (res->sr_bg_blkno) { /* Attempt to short-circuit the usual search mechanism * by jumping straight to the most recently used * allocation group. This helps us mantain some * contiguousness across allocations. */ status = ocfs2_search_one_group(ac, handle, bits_wanted, - min_bits, bit_off, num_bits, - hint_blkno, &bits_left); - if (!status) { - /* Be careful to update *bg_blkno here as the - * caller is expecting it to be filled in, and - * ocfs2_search_one_group() won't do that for - * us. */ - *bg_blkno = hint_blkno; + min_bits, res, &bits_left); + if (!status) goto set_hint; - } if (status < 0 && status != -ENOSPC) { mlog_errno(status); goto bail; @@ -1660,8 +1894,8 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb, ac->ac_chain = victim; ac->ac_allow_chain_relink = 1; - status = ocfs2_search_chain(ac, handle, bits_wanted, min_bits, bit_off, - num_bits, bg_blkno, &bits_left); + status = ocfs2_search_chain(ac, handle, bits_wanted, min_bits, + res, &bits_left); if (!status) goto set_hint; if (status < 0 && status != -ENOSPC) { @@ -1685,8 +1919,7 @@ static int ocfs2_claim_suballoc_bits(struct ocfs2_super *osb, ac->ac_chain = i; status = ocfs2_search_chain(ac, handle, bits_wanted, min_bits, - bit_off, num_bits, bg_blkno, - &bits_left); + res, &bits_left); if (!status) break; if (status < 0 && status != -ENOSPC) { @@ -1703,7 +1936,7 @@ set_hint: if (bits_left < min_bits) ac->ac_last_group = 0; else - ac->ac_last_group = *bg_blkno; + ac->ac_last_group = res->sr_bg_blkno; } bail: @@ -1711,37 +1944,37 @@ bail: return status; } -int ocfs2_claim_metadata(struct ocfs2_super *osb, - handle_t *handle, +int ocfs2_claim_metadata(handle_t *handle, struct ocfs2_alloc_context *ac, u32 bits_wanted, + u64 *suballoc_loc, u16 *suballoc_bit_start, unsigned int *num_bits, u64 *blkno_start) { int status; - u64 bg_blkno; + struct ocfs2_suballoc_result res = { .sr_blkno = 0, }; BUG_ON(!ac); BUG_ON(ac->ac_bits_wanted < (ac->ac_bits_given + bits_wanted)); BUG_ON(ac->ac_which != OCFS2_AC_USE_META); - status = ocfs2_claim_suballoc_bits(osb, - ac, + status = ocfs2_claim_suballoc_bits(ac, handle, bits_wanted, 1, - suballoc_bit_start, - num_bits, - &bg_blkno); + &res); if (status < 0) { mlog_errno(status); goto bail; } - atomic_inc(&osb->alloc_stats.bg_allocs); + atomic_inc(&OCFS2_SB(ac->ac_inode->i_sb)->alloc_stats.bg_allocs); - *blkno_start = bg_blkno + (u64) *suballoc_bit_start; - ac->ac_bits_given += (*num_bits); + *suballoc_loc = res.sr_bg_blkno; + *suballoc_bit_start = res.sr_bit_offset; + *blkno_start = res.sr_blkno; + ac->ac_bits_given += res.sr_bits; + *num_bits = res.sr_bits; status = 0; bail: mlog_exit(status); @@ -1749,10 +1982,10 @@ bail: } static void ocfs2_init_inode_ac_group(struct inode *dir, - struct buffer_head *parent_fe_bh, + struct buffer_head *parent_di_bh, struct ocfs2_alloc_context *ac) { - struct ocfs2_dinode *fe = (struct ocfs2_dinode *)parent_fe_bh->b_data; + struct ocfs2_dinode *di = (struct ocfs2_dinode *)parent_di_bh->b_data; /* * Try to allocate inodes from some specific group. * @@ -1766,10 +1999,14 @@ static void ocfs2_init_inode_ac_group(struct inode *dir, if (OCFS2_I(dir)->ip_last_used_group && OCFS2_I(dir)->ip_last_used_slot == ac->ac_alloc_slot) ac->ac_last_group = OCFS2_I(dir)->ip_last_used_group; - else if (le16_to_cpu(fe->i_suballoc_slot) == ac->ac_alloc_slot) - ac->ac_last_group = ocfs2_which_suballoc_group( - le64_to_cpu(fe->i_blkno), - le16_to_cpu(fe->i_suballoc_bit)); + else if (le16_to_cpu(di->i_suballoc_slot) == ac->ac_alloc_slot) { + if (di->i_suballoc_loc) + ac->ac_last_group = le64_to_cpu(di->i_suballoc_loc); + else + ac->ac_last_group = ocfs2_which_suballoc_group( + le64_to_cpu(di->i_blkno), + le16_to_cpu(di->i_suballoc_bit)); + } } static inline void ocfs2_save_inode_ac_group(struct inode *dir, @@ -1779,17 +2016,16 @@ static inline void ocfs2_save_inode_ac_group(struct inode *dir, OCFS2_I(dir)->ip_last_used_slot = ac->ac_alloc_slot; } -int ocfs2_claim_new_inode(struct ocfs2_super *osb, - handle_t *handle, +int ocfs2_claim_new_inode(handle_t *handle, struct inode *dir, struct buffer_head *parent_fe_bh, struct ocfs2_alloc_context *ac, + u64 *suballoc_loc, u16 *suballoc_bit, u64 *fe_blkno) { int status; - unsigned int num_bits; - u64 bg_blkno; + struct ocfs2_suballoc_result res; mlog_entry_void(); @@ -1800,23 +2036,22 @@ int ocfs2_claim_new_inode(struct ocfs2_super *osb, ocfs2_init_inode_ac_group(dir, parent_fe_bh, ac); - status = ocfs2_claim_suballoc_bits(osb, - ac, + status = ocfs2_claim_suballoc_bits(ac, handle, 1, 1, - suballoc_bit, - &num_bits, - &bg_blkno); + &res); if (status < 0) { mlog_errno(status); goto bail; } - atomic_inc(&osb->alloc_stats.bg_allocs); + atomic_inc(&OCFS2_SB(ac->ac_inode->i_sb)->alloc_stats.bg_allocs); - BUG_ON(num_bits != 1); + BUG_ON(res.sr_bits != 1); - *fe_blkno = bg_blkno + (u64) (*suballoc_bit); + *suballoc_loc = res.sr_bg_blkno; + *suballoc_bit = res.sr_bit_offset; + *fe_blkno = res.sr_blkno; ac->ac_bits_given++; ocfs2_save_inode_ac_group(dir, ac); status = 0; @@ -1886,8 +2121,7 @@ static inline void ocfs2_block_to_cluster_group(struct inode *inode, * contig. allocation, set to '1' to indicate we can deal with extents * of any size. */ -int __ocfs2_claim_clusters(struct ocfs2_super *osb, - handle_t *handle, +int __ocfs2_claim_clusters(handle_t *handle, struct ocfs2_alloc_context *ac, u32 min_clusters, u32 max_clusters, @@ -1896,8 +2130,8 @@ int __ocfs2_claim_clusters(struct ocfs2_super *osb, { int status; unsigned int bits_wanted = max_clusters; - u64 bg_blkno = 0; - u16 bg_bit_off; + struct ocfs2_suballoc_result res = { .sr_blkno = 0, }; + struct ocfs2_super *osb = OCFS2_SB(ac->ac_inode->i_sb); mlog_entry_void(); @@ -1907,6 +2141,8 @@ int __ocfs2_claim_clusters(struct ocfs2_super *osb, && ac->ac_which != OCFS2_AC_USE_MAIN); if (ac->ac_which == OCFS2_AC_USE_LOCAL) { + WARN_ON(min_clusters > 1); + status = ocfs2_claim_local_alloc_bits(osb, handle, ac, @@ -1929,20 +2165,19 @@ int __ocfs2_claim_clusters(struct ocfs2_super *osb, if (bits_wanted > (osb->bitmap_cpg - 1)) bits_wanted = osb->bitmap_cpg - 1; - status = ocfs2_claim_suballoc_bits(osb, - ac, + status = ocfs2_claim_suballoc_bits(ac, handle, bits_wanted, min_clusters, - &bg_bit_off, - num_clusters, - &bg_blkno); + &res); if (!status) { + BUG_ON(res.sr_blkno); /* cluster alloc can't set */ *cluster_start = ocfs2_desc_bitmap_to_cluster_off(ac->ac_inode, - bg_blkno, - bg_bit_off); + res.sr_bg_blkno, + res.sr_bit_offset); atomic_inc(&osb->alloc_stats.bitmap_data); + *num_clusters = res.sr_bits; } } if (status < 0) { @@ -1958,8 +2193,7 @@ bail: return status; } -int ocfs2_claim_clusters(struct ocfs2_super *osb, - handle_t *handle, +int ocfs2_claim_clusters(handle_t *handle, struct ocfs2_alloc_context *ac, u32 min_clusters, u32 *cluster_start, @@ -1967,7 +2201,7 @@ int ocfs2_claim_clusters(struct ocfs2_super *osb, { unsigned int bits_wanted = ac->ac_bits_wanted - ac->ac_bits_given; - return __ocfs2_claim_clusters(osb, handle, ac, min_clusters, + return __ocfs2_claim_clusters(handle, ac, min_clusters, bits_wanted, cluster_start, num_clusters); } @@ -2023,9 +2257,7 @@ static int ocfs2_block_group_clear_bits(handle_t *handle, if (undo_fn) jbd_unlock_bh_state(group_bh); - status = ocfs2_journal_dirty(handle, group_bh); - if (status < 0) - mlog_errno(status); + ocfs2_journal_dirty(handle, group_bh); bail: return status; } @@ -2092,12 +2324,7 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle, count); tmp_used = le32_to_cpu(fe->id1.bitmap1.i_used); fe->id1.bitmap1.i_used = cpu_to_le32(tmp_used - count); - - status = ocfs2_journal_dirty(handle, alloc_bh); - if (status < 0) { - mlog_errno(status); - goto bail; - } + ocfs2_journal_dirty(handle, alloc_bh); bail: brelse(group_bh); @@ -2126,6 +2353,8 @@ int ocfs2_free_dinode(handle_t *handle, u16 bit = le16_to_cpu(di->i_suballoc_bit); u64 bg_blkno = ocfs2_which_suballoc_group(blk, bit); + if (di->i_suballoc_loc) + bg_blkno = le64_to_cpu(di->i_suballoc_loc); return ocfs2_free_suballoc_bits(handle, inode_alloc_inode, inode_alloc_bh, bit, bg_blkno, 1); } @@ -2395,7 +2624,7 @@ static int ocfs2_test_suballoc_bit(struct ocfs2_super *osb, struct buffer_head *alloc_bh, u64 blkno, u16 bit, int *res) { - struct ocfs2_dinode *alloc_fe; + struct ocfs2_dinode *alloc_di; struct ocfs2_group_desc *group; struct buffer_head *group_bh = NULL; u64 bg_blkno; @@ -2404,17 +2633,20 @@ static int ocfs2_test_suballoc_bit(struct ocfs2_super *osb, mlog_entry("blkno: %llu bit: %u\n", (unsigned long long)blkno, (unsigned int)bit); - alloc_fe = (struct ocfs2_dinode *)alloc_bh->b_data; - if ((bit + 1) > ocfs2_bits_per_group(&alloc_fe->id2.i_chain)) { + alloc_di = (struct ocfs2_dinode *)alloc_bh->b_data; + if ((bit + 1) > ocfs2_bits_per_group(&alloc_di->id2.i_chain)) { mlog(ML_ERROR, "suballoc bit %u out of range of %u\n", (unsigned int)bit, - ocfs2_bits_per_group(&alloc_fe->id2.i_chain)); + ocfs2_bits_per_group(&alloc_di->id2.i_chain)); status = -EINVAL; goto bail; } - bg_blkno = ocfs2_which_suballoc_group(blkno, bit); - status = ocfs2_read_group_descriptor(suballoc, alloc_fe, bg_blkno, + if (alloc_di->i_suballoc_loc) + bg_blkno = le64_to_cpu(alloc_di->i_suballoc_loc); + else + bg_blkno = ocfs2_which_suballoc_group(blkno, bit); + status = ocfs2_read_group_descriptor(suballoc, alloc_di, bg_blkno, &group_bh); if (status < 0) { mlog(ML_ERROR, "read group %llu failed %d\n", diff --git a/fs/ocfs2/suballoc.h b/fs/ocfs2/suballoc.h index e0f46df357e..a017dd3ee7d 100644 --- a/fs/ocfs2/suballoc.h +++ b/fs/ocfs2/suballoc.h @@ -26,13 +26,14 @@ #ifndef _CHAINALLOC_H_ #define _CHAINALLOC_H_ +struct ocfs2_suballoc_result; typedef int (group_search_t)(struct inode *, struct buffer_head *, u32, /* bits_wanted */ u32, /* min_bits */ u64, /* max_block */ - u16 *, /* *bit_off */ - u16 *); /* *bits_found */ + struct ocfs2_suballoc_result *); + /* found bits */ struct ocfs2_alloc_context { struct inode *ac_inode; /* which bitmap are we allocating from? */ @@ -54,6 +55,8 @@ struct ocfs2_alloc_context { u64 ac_last_group; u64 ac_max_block; /* Highest block number to allocate. 0 is is the same as ~0 - unlimited */ + + struct ocfs2_alloc_reservation *ac_resv; }; void ocfs2_init_steal_slots(struct ocfs2_super *osb); @@ -80,22 +83,21 @@ int ocfs2_reserve_clusters(struct ocfs2_super *osb, u32 bits_wanted, struct ocfs2_alloc_context **ac); -int ocfs2_claim_metadata(struct ocfs2_super *osb, - handle_t *handle, +int ocfs2_claim_metadata(handle_t *handle, struct ocfs2_alloc_context *ac, u32 bits_wanted, + u64 *suballoc_loc, u16 *suballoc_bit_start, u32 *num_bits, u64 *blkno_start); -int ocfs2_claim_new_inode(struct ocfs2_super *osb, - handle_t *handle, +int ocfs2_claim_new_inode(handle_t *handle, struct inode *dir, struct buffer_head *parent_fe_bh, struct ocfs2_alloc_context *ac, + u64 *suballoc_loc, u16 *suballoc_bit, u64 *fe_blkno); -int ocfs2_claim_clusters(struct ocfs2_super *osb, - handle_t *handle, +int ocfs2_claim_clusters(handle_t *handle, struct ocfs2_alloc_context *ac, u32 min_clusters, u32 *cluster_start, @@ -104,8 +106,7 @@ int ocfs2_claim_clusters(struct ocfs2_super *osb, * Use this variant of ocfs2_claim_clusters to specify a maxiumum * number of clusters smaller than the allocation reserved. */ -int __ocfs2_claim_clusters(struct ocfs2_super *osb, - handle_t *handle, +int __ocfs2_claim_clusters(handle_t *handle, struct ocfs2_alloc_context *ac, u32 min_clusters, u32 max_clusters, diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index dee03197a49..1c2c39f6f0b 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -94,7 +94,9 @@ struct mount_options unsigned long mount_opt; unsigned int atime_quantum; signed short slot; - unsigned int localalloc_opt; + int localalloc_opt; + unsigned int resv_level; + int dir_resv_level; char cluster_stack[OCFS2_STACK_LABEL_LEN + 1]; }; @@ -176,6 +178,8 @@ enum { Opt_noacl, Opt_usrquota, Opt_grpquota, + Opt_resv_level, + Opt_dir_resv_level, Opt_err, }; @@ -202,6 +206,8 @@ static const match_table_t tokens = { {Opt_noacl, "noacl"}, {Opt_usrquota, "usrquota"}, {Opt_grpquota, "grpquota"}, + {Opt_resv_level, "resv_level=%u"}, + {Opt_dir_resv_level, "dir_resv_level=%u"}, {Opt_err, NULL} }; @@ -1028,8 +1034,14 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) osb->s_atime_quantum = parsed_options.atime_quantum; osb->preferred_slot = parsed_options.slot; osb->osb_commit_interval = parsed_options.commit_interval; - osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt); - osb->local_alloc_bits = osb->local_alloc_default_bits; + + ocfs2_la_set_sizes(osb, parsed_options.localalloc_opt); + osb->osb_resv_level = parsed_options.resv_level; + osb->osb_dir_resv_level = parsed_options.resv_level; + if (parsed_options.dir_resv_level == -1) + osb->osb_dir_resv_level = parsed_options.resv_level; + else + osb->osb_dir_resv_level = parsed_options.dir_resv_level; status = ocfs2_verify_userspace_stack(osb, &parsed_options); if (status) @@ -1285,11 +1297,13 @@ static int ocfs2_parse_options(struct super_block *sb, options ? options : "(none)"); mopt->commit_interval = 0; - mopt->mount_opt = 0; + mopt->mount_opt = OCFS2_MOUNT_NOINTR; mopt->atime_quantum = OCFS2_DEFAULT_ATIME_QUANTUM; mopt->slot = OCFS2_INVALID_SLOT; - mopt->localalloc_opt = OCFS2_DEFAULT_LOCAL_ALLOC_SIZE; + mopt->localalloc_opt = -1; mopt->cluster_stack[0] = '\0'; + mopt->resv_level = OCFS2_DEFAULT_RESV_LEVEL; + mopt->dir_resv_level = -1; if (!options) { status = 1; @@ -1380,7 +1394,7 @@ static int ocfs2_parse_options(struct super_block *sb, status = 0; goto bail; } - if (option >= 0 && (option <= ocfs2_local_alloc_size(sb) * 8)) + if (option >= 0) mopt->localalloc_opt = option; break; case Opt_localflocks: @@ -1433,6 +1447,28 @@ static int ocfs2_parse_options(struct super_block *sb, mopt->mount_opt |= OCFS2_MOUNT_NO_POSIX_ACL; mopt->mount_opt &= ~OCFS2_MOUNT_POSIX_ACL; break; + case Opt_resv_level: + if (is_remount) + break; + if (match_int(&args[0], &option)) { + status = 0; + goto bail; + } + if (option >= OCFS2_MIN_RESV_LEVEL && + option < OCFS2_MAX_RESV_LEVEL) + mopt->resv_level = option; + break; + case Opt_dir_resv_level: + if (is_remount) + break; + if (match_int(&args[0], &option)) { + status = 0; + goto bail; + } + if (option >= OCFS2_MIN_RESV_LEVEL && + option < OCFS2_MAX_RESV_LEVEL) + mopt->dir_resv_level = option; + break; default: mlog(ML_ERROR, "Unrecognized mount option \"%s\" " @@ -1487,7 +1523,7 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) (unsigned) (osb->osb_commit_interval / HZ)); local_alloc_megs = osb->local_alloc_bits >> (20 - osb->s_clustersize_bits); - if (local_alloc_megs != OCFS2_DEFAULT_LOCAL_ALLOC_SIZE) + if (local_alloc_megs != ocfs2_la_default_mb(osb)) seq_printf(s, ",localalloc=%d", local_alloc_megs); if (opts & OCFS2_MOUNT_LOCALFLOCKS) @@ -1514,6 +1550,12 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) else seq_printf(s, ",noacl"); + if (osb->osb_resv_level != OCFS2_DEFAULT_RESV_LEVEL) + seq_printf(s, ",resv_level=%d", osb->osb_resv_level); + + if (osb->osb_dir_resv_level != osb->osb_resv_level) + seq_printf(s, ",dir_resv_level=%d", osb->osb_resv_level); + return 0; } @@ -1688,6 +1730,8 @@ static void ocfs2_inode_init_once(void *data) oi->ip_blkno = 0ULL; oi->ip_clusters = 0; + ocfs2_resv_init_once(&oi->ip_la_data_resv); + ocfs2_lock_res_init_once(&oi->ip_rw_lockres); ocfs2_lock_res_init_once(&oi->ip_inode_lockres); ocfs2_lock_res_init_once(&oi->ip_open_lockres); @@ -2042,6 +2086,12 @@ static int ocfs2_initialize_super(struct super_block *sb, init_waitqueue_head(&osb->osb_mount_event); + status = ocfs2_resmap_init(osb, &osb->osb_la_resmap); + if (status) { + mlog_errno(status); + goto bail; + } + osb->vol_label = kmalloc(OCFS2_MAX_VOL_LABEL_LEN, GFP_KERNEL); if (!osb->vol_label) { mlog(ML_ERROR, "unable to alloc vol label\n"); @@ -2224,9 +2274,11 @@ static int ocfs2_initialize_super(struct super_block *sb, } osb->bitmap_blkno = OCFS2_I(inode)->ip_blkno; + osb->osb_clusters_at_boot = OCFS2_I(inode)->ip_clusters; iput(inode); - osb->bitmap_cpg = ocfs2_group_bitmap_size(sb) * 8; + osb->bitmap_cpg = ocfs2_group_bitmap_size(sb, 0, + osb->s_feature_incompat) * 8; status = ocfs2_init_slot_info(osb); if (status < 0) { @@ -2509,5 +2561,25 @@ void __ocfs2_abort(struct super_block* sb, ocfs2_handle_error(sb); } +/* + * Void signal blockers, because in-kernel sigprocmask() only fails + * when SIG_* is wrong. + */ +void ocfs2_block_signals(sigset_t *oldset) +{ + int rc; + sigset_t blocked; + + sigfillset(&blocked); + rc = sigprocmask(SIG_BLOCK, &blocked, oldset); + BUG_ON(rc); +} + +void ocfs2_unblock_signals(sigset_t *oldset) +{ + int rc = sigprocmask(SIG_SETMASK, oldset, NULL); + BUG_ON(rc); +} + module_init(ocfs2_init); module_exit(ocfs2_exit); diff --git a/fs/ocfs2/super.h b/fs/ocfs2/super.h index 783f5270f2a..40c7de084c1 100644 --- a/fs/ocfs2/super.h +++ b/fs/ocfs2/super.h @@ -45,4 +45,11 @@ void __ocfs2_abort(struct super_block *sb, #define ocfs2_abort(sb, fmt, args...) __ocfs2_abort(sb, __PRETTY_FUNCTION__, fmt, ##args) +/* + * Void signal blockers, because in-kernel sigprocmask() only fails + * when SIG_* is wrong. + */ +void ocfs2_block_signals(sigset_t *oldset); +void ocfs2_unblock_signals(sigset_t *oldset); + #endif /* OCFS2_SUPER_H */ diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 3e7773089b9..98ee6c44102 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -79,6 +79,7 @@ struct ocfs2_xattr_set_ctxt { struct ocfs2_alloc_context *meta_ac; struct ocfs2_alloc_context *data_ac; struct ocfs2_cached_dealloc_ctxt dealloc; + int set_abort; }; #define OCFS2_XATTR_ROOT_SIZE (sizeof(struct ocfs2_xattr_def_value_root)) @@ -739,11 +740,7 @@ static int ocfs2_xattr_extend_allocation(struct inode *inode, goto leave; } - status = ocfs2_journal_dirty(handle, vb->vb_bh); - if (status < 0) { - mlog_errno(status); - goto leave; - } + ocfs2_journal_dirty(handle, vb->vb_bh); clusters_to_add -= le32_to_cpu(vb->vb_xv->xr_clusters) - prev_clusters; @@ -786,12 +783,7 @@ static int __ocfs2_remove_xattr_range(struct inode *inode, } le32_add_cpu(&vb->vb_xv->xr_clusters, -len); - - ret = ocfs2_journal_dirty(handle, vb->vb_bh); - if (ret) { - mlog_errno(ret); - goto out; - } + ocfs2_journal_dirty(handle, vb->vb_bh); if (ext_flags & OCFS2_EXT_REFCOUNTED) ret = ocfs2_decrease_refcount(inode, handle, @@ -1374,11 +1366,7 @@ static int __ocfs2_xattr_set_value_outside(struct inode *inode, memset(bh->b_data + cp_len, 0, blocksize - cp_len); - ret = ocfs2_journal_dirty(handle, bh); - if (ret < 0) { - mlog_errno(ret); - goto out; - } + ocfs2_journal_dirty(handle, bh); brelse(bh); bh = NULL; @@ -2148,15 +2136,19 @@ alloc_value: orig_clusters = ocfs2_xa_value_clusters(loc); rc = ocfs2_xa_value_truncate(loc, xi->xi_value_len, ctxt); if (rc < 0) { - /* - * If we tried to grow an existing external value, - * ocfs2_xa_cleanuP-value_truncate() is going to - * let it stand. We have to restore its original - * value size. - */ - loc->xl_entry->xe_value_size = orig_value_size; + ctxt->set_abort = 1; ocfs2_xa_cleanup_value_truncate(loc, "growing", orig_clusters); + /* + * If we were growing an existing value, + * ocfs2_xa_cleanup_value_truncate() won't remove + * the entry. We need to restore the original value + * size. + */ + if (loc->xl_entry) { + BUG_ON(!orig_value_size); + loc->xl_entry->xe_value_size = orig_value_size; + } mlog_errno(rc); } } @@ -2479,7 +2471,10 @@ static int ocfs2_xattr_free_block(struct inode *inode, xb = (struct ocfs2_xattr_block *)blk_bh->b_data; blk = le64_to_cpu(xb->xb_blkno); bit = le16_to_cpu(xb->xb_suballoc_bit); - bg_blkno = ocfs2_which_suballoc_group(blk, bit); + if (xb->xb_suballoc_loc) + bg_blkno = le64_to_cpu(xb->xb_suballoc_loc); + else + bg_blkno = ocfs2_which_suballoc_group(blk, bit); xb_alloc_inode = ocfs2_get_system_file_inode(osb, EXTENT_ALLOC_SYSTEM_INODE, @@ -2594,9 +2589,7 @@ int ocfs2_xattr_remove(struct inode *inode, struct buffer_head *di_bh) di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); spin_unlock(&oi->ip_lock); - ret = ocfs2_journal_dirty(handle, di_bh); - if (ret < 0) - mlog_errno(ret); + ocfs2_journal_dirty(handle, di_bh); out_commit: ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); out: @@ -2724,9 +2717,7 @@ static int ocfs2_xattr_ibody_init(struct inode *inode, di->i_dyn_features = cpu_to_le16(oi->ip_dyn_features); spin_unlock(&oi->ip_lock); - ret = ocfs2_journal_dirty(ctxt->handle, di_bh); - if (ret < 0) - mlog_errno(ret); + ocfs2_journal_dirty(ctxt->handle, di_bh); out: return ret; @@ -2846,9 +2837,8 @@ static int ocfs2_create_xattr_block(struct inode *inode, int ret; u16 suballoc_bit_start; u32 num_got; - u64 first_blkno; + u64 suballoc_loc, first_blkno; struct ocfs2_dinode *di = (struct ocfs2_dinode *)inode_bh->b_data; - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); struct buffer_head *new_bh = NULL; struct ocfs2_xattr_block *xblk; @@ -2859,9 +2849,9 @@ static int ocfs2_create_xattr_block(struct inode *inode, goto end; } - ret = ocfs2_claim_metadata(osb, ctxt->handle, ctxt->meta_ac, 1, - &suballoc_bit_start, &num_got, - &first_blkno); + ret = ocfs2_claim_metadata(ctxt->handle, ctxt->meta_ac, 1, + &suballoc_loc, &suballoc_bit_start, + &num_got, &first_blkno); if (ret < 0) { mlog_errno(ret); goto end; @@ -2883,8 +2873,10 @@ static int ocfs2_create_xattr_block(struct inode *inode, memset(xblk, 0, inode->i_sb->s_blocksize); strcpy((void *)xblk, OCFS2_XATTR_BLOCK_SIGNATURE); xblk->xb_suballoc_slot = cpu_to_le16(ctxt->meta_ac->ac_alloc_slot); + xblk->xb_suballoc_loc = cpu_to_le64(suballoc_loc); xblk->xb_suballoc_bit = cpu_to_le16(suballoc_bit_start); - xblk->xb_fs_generation = cpu_to_le32(osb->fs_generation); + xblk->xb_fs_generation = + cpu_to_le32(OCFS2_SB(inode->i_sb)->fs_generation); xblk->xb_blkno = cpu_to_le64(first_blkno); if (indexed) { struct ocfs2_xattr_tree_root *xr = &xblk->xb_attrs.xb_root; @@ -2956,7 +2948,7 @@ static int ocfs2_xattr_block_set(struct inode *inode, ret = ocfs2_xa_set(&loc, xi, ctxt); if (!ret) xs->here = loc.xl_entry; - else if (ret != -ENOSPC) + else if ((ret != -ENOSPC) || ctxt->set_abort) goto end; else { ret = ocfs2_xattr_create_index_block(inode, xs, ctxt); @@ -3312,14 +3304,13 @@ static int __ocfs2_xattr_set_handle(struct inode *inode, goto out; } - ret = ocfs2_extend_trans(ctxt->handle, credits + - ctxt->handle->h_buffer_credits); + ret = ocfs2_extend_trans(ctxt->handle, credits); if (ret) { mlog_errno(ret); goto out; } ret = ocfs2_xattr_block_set(inode, xi, xbs, ctxt); - } else if (ret == -ENOSPC) { + } else if ((ret == -ENOSPC) && !ctxt->set_abort) { if (di->i_xattr_loc && !xbs->xattr_bh) { ret = ocfs2_xattr_block_find(inode, xi->xi_name_index, @@ -3343,8 +3334,7 @@ static int __ocfs2_xattr_set_handle(struct inode *inode, goto out; } - ret = ocfs2_extend_trans(ctxt->handle, credits + - ctxt->handle->h_buffer_credits); + ret = ocfs2_extend_trans(ctxt->handle, credits); if (ret) { mlog_errno(ret); goto out; @@ -3378,8 +3368,7 @@ static int __ocfs2_xattr_set_handle(struct inode *inode, goto out; } - ret = ocfs2_extend_trans(ctxt->handle, credits + - ctxt->handle->h_buffer_credits); + ret = ocfs2_extend_trans(ctxt->handle, credits); if (ret) { mlog_errno(ret); goto out; @@ -4249,7 +4238,6 @@ static int ocfs2_xattr_create_index_block(struct inode *inode, u32 bit_off, len; u64 blkno; handle_t *handle = ctxt->handle; - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); struct ocfs2_inode_info *oi = OCFS2_I(inode); struct buffer_head *xb_bh = xs->xattr_bh; struct ocfs2_xattr_block *xb = @@ -4277,7 +4265,7 @@ static int ocfs2_xattr_create_index_block(struct inode *inode, goto out; } - ret = __ocfs2_claim_clusters(osb, handle, ctxt->data_ac, + ret = __ocfs2_claim_clusters(handle, ctxt->data_ac, 1, 1, &bit_off, &len); if (ret) { mlog_errno(ret); @@ -4887,8 +4875,7 @@ static int ocfs2_mv_xattr_buckets(struct inode *inode, handle_t *handle, * We need to update the first bucket of the old extent and all * the buckets going to the new extent. */ - credits = ((num_buckets + 1) * blks_per_bucket) + - handle->h_buffer_credits; + credits = ((num_buckets + 1) * blks_per_bucket); ret = ocfs2_extend_trans(handle, credits); if (ret) { mlog_errno(ret); @@ -4958,7 +4945,7 @@ static int ocfs2_divide_xattr_cluster(struct inode *inode, u32 *first_hash) { u16 blk_per_bucket = ocfs2_blocks_per_xattr_bucket(inode->i_sb); - int ret, credits = 2 * blk_per_bucket + handle->h_buffer_credits; + int ret, credits = 2 * blk_per_bucket; BUG_ON(OCFS2_XATTR_BUCKET_SIZE < OCFS2_SB(inode->i_sb)->s_clustersize); @@ -5099,7 +5086,7 @@ static int ocfs2_add_new_xattr_cluster(struct inode *inode, goto leave; } - ret = __ocfs2_claim_clusters(osb, handle, ctxt->data_ac, 1, + ret = __ocfs2_claim_clusters(handle, ctxt->data_ac, 1, clusters_to_add, &bit_off, &num_bits); if (ret < 0) { if (ret != -ENOSPC) @@ -5153,9 +5140,7 @@ static int ocfs2_add_new_xattr_cluster(struct inode *inode, goto leave; } - ret = ocfs2_journal_dirty(handle, root_bh); - if (ret < 0) - mlog_errno(ret); + ocfs2_journal_dirty(handle, root_bh); leave: return ret; @@ -5200,8 +5185,7 @@ static int ocfs2_extend_xattr_bucket(struct inode *inode, * existing bucket. Then we add the last existing bucket, the * new bucket, and the first bucket (3 * blk_per_bucket). */ - credits = (end_blk - target_blk) + (3 * blk_per_bucket) + - handle->h_buffer_credits; + credits = (end_blk - target_blk) + (3 * blk_per_bucket); ret = ocfs2_extend_trans(handle, credits); if (ret) { mlog_errno(ret); @@ -5477,12 +5461,7 @@ static int ocfs2_rm_xattr_cluster(struct inode *inode, } le32_add_cpu(&xb->xb_attrs.xb_root.xt_clusters, -len); - - ret = ocfs2_journal_dirty(handle, root_bh); - if (ret) { - mlog_errno(ret); - goto out_commit; - } + ocfs2_journal_dirty(handle, root_bh); ret = ocfs2_truncate_log_append(osb, handle, blkno, len); if (ret) @@ -6935,7 +6914,7 @@ static int ocfs2_reflink_xattr_rec(struct inode *inode, goto out; } - ret = ocfs2_claim_clusters(osb, handle, data_ac, + ret = ocfs2_claim_clusters(handle, data_ac, len, &p_cluster, &num_clusters); if (ret) { mlog_errno(ret); diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index c94853473ca..f47cd212dee 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -214,7 +214,7 @@ static int ramfs_parse_options(char *data, struct ramfs_mount_opts *opts) return 0; } -static int ramfs_fill_super(struct super_block * sb, void * data, int silent) +int ramfs_fill_super(struct super_block *sb, void *data, int silent) { struct ramfs_fs_info *fsi; struct inode *inode = NULL; diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index e9d293593e5..4e321f7353f 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -46,9 +46,9 @@ struct bin_buffer { }; static int -fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) +fill_read(struct file *file, char *buffer, loff_t off, size_t count) { - struct sysfs_dirent *attr_sd = dentry->d_fsdata; + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; int rc; @@ -59,7 +59,7 @@ fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) rc = -EIO; if (attr->read) - rc = attr->read(kobj, attr, buffer, off, count); + rc = attr->read(file, kobj, attr, buffer, off, count); sysfs_put_active(attr_sd); @@ -70,8 +70,7 @@ static ssize_t read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) { struct bin_buffer *bb = file->private_data; - struct dentry *dentry = file->f_path.dentry; - int size = dentry->d_inode->i_size; + int size = file->f_path.dentry->d_inode->i_size; loff_t offs = *off; int count = min_t(size_t, bytes, PAGE_SIZE); char *temp; @@ -92,7 +91,7 @@ read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) mutex_lock(&bb->mutex); - count = fill_read(dentry, bb->buffer, offs, count); + count = fill_read(file, bb->buffer, offs, count); if (count < 0) { mutex_unlock(&bb->mutex); goto out_free; @@ -117,9 +116,9 @@ read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) } static int -flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) +flush_write(struct file *file, char *buffer, loff_t offset, size_t count) { - struct sysfs_dirent *attr_sd = dentry->d_fsdata; + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; int rc; @@ -130,7 +129,7 @@ flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) rc = -EIO; if (attr->write) - rc = attr->write(kobj, attr, buffer, offset, count); + rc = attr->write(file, kobj, attr, buffer, offset, count); sysfs_put_active(attr_sd); @@ -141,8 +140,7 @@ static ssize_t write(struct file *file, const char __user *userbuf, size_t bytes, loff_t *off) { struct bin_buffer *bb = file->private_data; - struct dentry *dentry = file->f_path.dentry; - int size = dentry->d_inode->i_size; + int size = file->f_path.dentry->d_inode->i_size; loff_t offs = *off; int count = min_t(size_t, bytes, PAGE_SIZE); char *temp; @@ -165,7 +163,7 @@ static ssize_t write(struct file *file, const char __user *userbuf, memcpy(bb->buffer, temp, count); - count = flush_write(dentry, bb->buffer, offs, count); + count = flush_write(file, bb->buffer, offs, count); mutex_unlock(&bb->mutex); if (count > 0) @@ -363,7 +361,7 @@ static int mmap(struct file *file, struct vm_area_struct *vma) if (!attr->mmap) goto out_put; - rc = attr->mmap(kobj, attr, vma); + rc = attr->mmap(file, kobj, attr, vma); if (rc) goto out_put; @@ -501,7 +499,7 @@ int sysfs_create_bin_file(struct kobject *kobj, void sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr) { - sysfs_hash_and_remove(kobj->sd, attr->attr.name); + sysfs_hash_and_remove(kobj->sd, NULL, attr->attr.name); } EXPORT_SYMBOL_GPL(sysfs_create_bin_file); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 590717861c7..7e54bac8c4b 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -380,7 +380,7 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) { struct sysfs_inode_attrs *ps_iattr; - if (sysfs_find_dirent(acxt->parent_sd, sd->s_name)) + if (sysfs_find_dirent(acxt->parent_sd, sd->s_ns, sd->s_name)) return -EEXIST; sd->s_parent = sysfs_get(acxt->parent_sd); @@ -533,13 +533,17 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) * Pointer to sysfs_dirent if found, NULL if not. */ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, + const void *ns, const unsigned char *name) { struct sysfs_dirent *sd; - for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) + for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) { + if (ns && sd->s_ns && (sd->s_ns != ns)) + continue; if (!strcmp(sd->s_name, name)) return sd; + } return NULL; } @@ -558,12 +562,13 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, * Pointer to sysfs_dirent if found, NULL if not. */ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, + const void *ns, const unsigned char *name) { struct sysfs_dirent *sd; mutex_lock(&sysfs_mutex); - sd = sysfs_find_dirent(parent_sd, name); + sd = sysfs_find_dirent(parent_sd, ns, name); sysfs_get(sd); mutex_unlock(&sysfs_mutex); @@ -572,7 +577,8 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, EXPORT_SYMBOL_GPL(sysfs_get_dirent); static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, - const char *name, struct sysfs_dirent **p_sd) + enum kobj_ns_type type, const void *ns, const char *name, + struct sysfs_dirent **p_sd) { umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; struct sysfs_addrm_cxt acxt; @@ -583,6 +589,9 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, sd = sysfs_new_dirent(name, mode, SYSFS_DIR); if (!sd) return -ENOMEM; + + sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT); + sd->s_ns = ns; sd->s_dir.kobj = kobj; /* link in */ @@ -601,7 +610,33 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, int sysfs_create_subdir(struct kobject *kobj, const char *name, struct sysfs_dirent **p_sd) { - return create_dir(kobj, kobj->sd, name, p_sd); + return create_dir(kobj, kobj->sd, + KOBJ_NS_TYPE_NONE, NULL, name, p_sd); +} + +/** + * sysfs_read_ns_type: return associated ns_type + * @kobj: the kobject being queried + * + * Each kobject can be tagged with exactly one namespace type + * (i.e. network or user). Return the ns_type associated with + * this object if any + */ +static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj) +{ + const struct kobj_ns_type_operations *ops; + enum kobj_ns_type type; + + ops = kobj_child_ns_ops(kobj); + if (!ops) + return KOBJ_NS_TYPE_NONE; + + type = ops->type; + BUG_ON(type <= KOBJ_NS_TYPE_NONE); + BUG_ON(type >= KOBJ_NS_TYPES); + BUG_ON(!kobj_ns_type_registered(type)); + + return type; } /** @@ -610,7 +645,9 @@ int sysfs_create_subdir(struct kobject *kobj, const char *name, */ int sysfs_create_dir(struct kobject * kobj) { + enum kobj_ns_type type; struct sysfs_dirent *parent_sd, *sd; + const void *ns = NULL; int error = 0; BUG_ON(!kobj); @@ -620,7 +657,11 @@ int sysfs_create_dir(struct kobject * kobj) else parent_sd = &sysfs_root; - error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd); + if (sysfs_ns_type(parent_sd)) + ns = kobj->ktype->namespace(kobj); + type = sysfs_read_ns_type(kobj); + + error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd); if (!error) kobj->sd = sd; return error; @@ -630,13 +671,19 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct dentry *ret = NULL; - struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata; + struct dentry *parent = dentry->d_parent; + struct sysfs_dirent *parent_sd = parent->d_fsdata; struct sysfs_dirent *sd; struct inode *inode; + enum kobj_ns_type type; + const void *ns; mutex_lock(&sysfs_mutex); - sd = sysfs_find_dirent(parent_sd, dentry->d_name.name); + type = sysfs_ns_type(parent_sd); + ns = sysfs_info(dir->i_sb)->ns[type]; + + sd = sysfs_find_dirent(parent_sd, ns, dentry->d_name.name); /* no such entry */ if (!sd) { @@ -735,7 +782,8 @@ void sysfs_remove_dir(struct kobject * kobj) } int sysfs_rename(struct sysfs_dirent *sd, - struct sysfs_dirent *new_parent_sd, const char *new_name) + struct sysfs_dirent *new_parent_sd, const void *new_ns, + const char *new_name) { const char *dup_name = NULL; int error; @@ -743,12 +791,12 @@ int sysfs_rename(struct sysfs_dirent *sd, mutex_lock(&sysfs_mutex); error = 0; - if ((sd->s_parent == new_parent_sd) && + if ((sd->s_parent == new_parent_sd) && (sd->s_ns == new_ns) && (strcmp(sd->s_name, new_name) == 0)) goto out; /* nothing to rename */ error = -EEXIST; - if (sysfs_find_dirent(new_parent_sd, new_name)) + if (sysfs_find_dirent(new_parent_sd, new_ns, new_name)) goto out; /* rename sysfs_dirent */ @@ -770,6 +818,7 @@ int sysfs_rename(struct sysfs_dirent *sd, sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); } + sd->s_ns = new_ns; error = 0; out: @@ -780,19 +829,28 @@ int sysfs_rename(struct sysfs_dirent *sd, int sysfs_rename_dir(struct kobject *kobj, const char *new_name) { - return sysfs_rename(kobj->sd, kobj->sd->s_parent, new_name); + struct sysfs_dirent *parent_sd = kobj->sd->s_parent; + const void *new_ns = NULL; + + if (sysfs_ns_type(parent_sd)) + new_ns = kobj->ktype->namespace(kobj); + + return sysfs_rename(kobj->sd, parent_sd, new_ns, new_name); } int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj) { struct sysfs_dirent *sd = kobj->sd; struct sysfs_dirent *new_parent_sd; + const void *new_ns = NULL; BUG_ON(!sd->s_parent); + if (sysfs_ns_type(sd->s_parent)) + new_ns = kobj->ktype->namespace(kobj); new_parent_sd = new_parent_kobj && new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root; - return sysfs_rename(sd, new_parent_sd, sd->s_name); + return sysfs_rename(sd, new_parent_sd, new_ns, sd->s_name); } /* Relationship between s_mode and the DT_xxx types */ @@ -807,32 +865,35 @@ static int sysfs_dir_release(struct inode *inode, struct file *filp) return 0; } -static struct sysfs_dirent *sysfs_dir_pos(struct sysfs_dirent *parent_sd, - ino_t ino, struct sysfs_dirent *pos) +static struct sysfs_dirent *sysfs_dir_pos(const void *ns, + struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) { if (pos) { int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && pos->s_parent == parent_sd && ino == pos->s_ino; sysfs_put(pos); - if (valid) - return pos; + if (!valid) + pos = NULL; } - pos = NULL; - if ((ino > 1) && (ino < INT_MAX)) { + if (!pos && (ino > 1) && (ino < INT_MAX)) { pos = parent_sd->s_dir.children; while (pos && (ino > pos->s_ino)) pos = pos->s_sibling; } + while (pos && pos->s_ns && pos->s_ns != ns) + pos = pos->s_sibling; return pos; } -static struct sysfs_dirent *sysfs_dir_next_pos(struct sysfs_dirent *parent_sd, - ino_t ino, struct sysfs_dirent *pos) +static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns, + struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) { - pos = sysfs_dir_pos(parent_sd, ino, pos); + pos = sysfs_dir_pos(ns, parent_sd, ino, pos); if (pos) pos = pos->s_sibling; + while (pos && pos->s_ns && pos->s_ns != ns) + pos = pos->s_sibling; return pos; } @@ -841,8 +902,13 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) struct dentry *dentry = filp->f_path.dentry; struct sysfs_dirent * parent_sd = dentry->d_fsdata; struct sysfs_dirent *pos = filp->private_data; + enum kobj_ns_type type; + const void *ns; ino_t ino; + type = sysfs_ns_type(parent_sd); + ns = sysfs_info(dentry->d_sb)->ns[type]; + if (filp->f_pos == 0) { ino = parent_sd->s_ino; if (filldir(dirent, ".", 1, filp->f_pos, ino, DT_DIR) == 0) @@ -857,9 +923,9 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) filp->f_pos++; } mutex_lock(&sysfs_mutex); - for (pos = sysfs_dir_pos(parent_sd, filp->f_pos, pos); + for (pos = sysfs_dir_pos(ns, parent_sd, filp->f_pos, pos); pos; - pos = sysfs_dir_next_pos(parent_sd, filp->f_pos, pos)) { + pos = sysfs_dir_next_pos(ns, parent_sd, filp->f_pos, pos)) { const char * name; unsigned int type; int len, ret; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index e222b258274..1beaa739d0a 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -478,9 +478,12 @@ void sysfs_notify(struct kobject *k, const char *dir, const char *attr) mutex_lock(&sysfs_mutex); if (sd && dir) - sd = sysfs_find_dirent(sd, dir); + /* Only directories are tagged, so no need to pass + * a tag explicitly. + */ + sd = sysfs_find_dirent(sd, NULL, dir); if (sd && attr) - sd = sysfs_find_dirent(sd, attr); + sd = sysfs_find_dirent(sd, NULL, attr); if (sd) sysfs_notify_dirent(sd); @@ -569,7 +572,7 @@ int sysfs_add_file_to_group(struct kobject *kobj, int error; if (group) - dir_sd = sysfs_get_dirent(kobj->sd, group); + dir_sd = sysfs_get_dirent(kobj->sd, NULL, group); else dir_sd = sysfs_get(kobj->sd); @@ -599,7 +602,7 @@ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) mutex_lock(&sysfs_mutex); rc = -ENOENT; - sd = sysfs_find_dirent(kobj->sd, attr->name); + sd = sysfs_find_dirent(kobj->sd, NULL, attr->name); if (!sd) goto out; @@ -624,7 +627,7 @@ EXPORT_SYMBOL_GPL(sysfs_chmod_file); void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) { - sysfs_hash_and_remove(kobj->sd, attr->name); + sysfs_hash_and_remove(kobj->sd, NULL, attr->name); } void sysfs_remove_files(struct kobject * kobj, const struct attribute **ptr) @@ -646,11 +649,11 @@ void sysfs_remove_file_from_group(struct kobject *kobj, struct sysfs_dirent *dir_sd; if (group) - dir_sd = sysfs_get_dirent(kobj->sd, group); + dir_sd = sysfs_get_dirent(kobj->sd, NULL, group); else dir_sd = sysfs_get(kobj->sd); if (dir_sd) { - sysfs_hash_and_remove(dir_sd, attr->name); + sysfs_hash_and_remove(dir_sd, NULL, attr->name); sysfs_put(dir_sd); } } diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index fe611949a7f..23c1e598792 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -23,7 +23,7 @@ static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, int i; for (i = 0, attr = grp->attrs; *attr; i++, attr++) - sysfs_hash_and_remove(dir_sd, (*attr)->name); + sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); } static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, @@ -39,7 +39,7 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, * visibility. Do this by first removing then * re-adding (if required) the file */ if (update) - sysfs_hash_and_remove(dir_sd, (*attr)->name); + sysfs_hash_and_remove(dir_sd, NULL, (*attr)->name); if (grp->is_visible) { mode = grp->is_visible(kobj, *attr, i); if (!mode) @@ -132,7 +132,7 @@ void sysfs_remove_group(struct kobject * kobj, struct sysfs_dirent *sd; if (grp->name) { - sd = sysfs_get_dirent(dir_sd, grp->name); + sd = sysfs_get_dirent(dir_sd, NULL, grp->name); if (!sd) { WARN(!sd, KERN_WARNING "sysfs group %p not found for " "kobject '%s'\n", grp, kobject_name(kobj)); diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index a4a0a941971..bbd77e95cf7 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -324,7 +324,7 @@ void sysfs_delete_inode(struct inode *inode) sysfs_put(sd); } -int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) +int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const char *name) { struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; @@ -334,7 +334,9 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) sysfs_addrm_start(&acxt, dir_sd); - sd = sysfs_find_dirent(dir_sd, name); + sd = sysfs_find_dirent(dir_sd, ns, name); + if (sd && (sd->s_ns != ns)) + sd = NULL; if (sd) sysfs_remove_one(&acxt, sd); diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 776137828dc..281c0c9bc39 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -35,7 +35,7 @@ static const struct super_operations sysfs_ops = { struct sysfs_dirent sysfs_root = { .s_name = "", .s_count = ATOMIC_INIT(1), - .s_flags = SYSFS_DIR, + .s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT), .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, .s_ino = 1, }; @@ -72,18 +72,107 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent) return 0; } +static int sysfs_test_super(struct super_block *sb, void *data) +{ + struct sysfs_super_info *sb_info = sysfs_info(sb); + struct sysfs_super_info *info = data; + enum kobj_ns_type type; + int found = 1; + + for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { + if (sb_info->ns[type] != info->ns[type]) + found = 0; + } + return found; +} + +static int sysfs_set_super(struct super_block *sb, void *data) +{ + int error; + error = set_anon_super(sb, data); + if (!error) + sb->s_fs_info = data; + return error; +} + static int sysfs_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt); + struct sysfs_super_info *info; + enum kobj_ns_type type; + struct super_block *sb; + int error; + + error = -ENOMEM; + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + goto out; + + for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) + info->ns[type] = kobj_ns_current(type); + + sb = sget(fs_type, sysfs_test_super, sysfs_set_super, info); + if (IS_ERR(sb) || sb->s_fs_info != info) + kfree(info); + if (IS_ERR(sb)) { + error = PTR_ERR(sb); + goto out; + } + if (!sb->s_root) { + sb->s_flags = flags; + error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); + if (error) { + deactivate_locked_super(sb); + goto out; + } + sb->s_flags |= MS_ACTIVE; + } + + simple_set_mnt(mnt, sb); + error = 0; +out: + return error; +} + +static void sysfs_kill_sb(struct super_block *sb) +{ + struct sysfs_super_info *info = sysfs_info(sb); + + /* Remove the superblock from fs_supers/s_instances + * so we can't find it, before freeing sysfs_super_info. + */ + kill_anon_super(sb); + kfree(info); } static struct file_system_type sysfs_fs_type = { .name = "sysfs", .get_sb = sysfs_get_sb, - .kill_sb = kill_anon_super, + .kill_sb = sysfs_kill_sb, }; +void sysfs_exit_ns(enum kobj_ns_type type, const void *ns) +{ + struct super_block *sb; + + mutex_lock(&sysfs_mutex); + spin_lock(&sb_lock); + list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) { + struct sysfs_super_info *info = sysfs_info(sb); + /* + * If we see a superblock on the fs_supers/s_instances + * list the unmount has not completed and sb->s_fs_info + * points to a valid struct sysfs_super_info. + */ + /* Ignore superblocks with the wrong ns */ + if (info->ns[type] != ns) + continue; + info->ns[type] = NULL; + } + spin_unlock(&sb_lock); + mutex_unlock(&sysfs_mutex); +} + int __init sysfs_init(void) { int err = -ENOMEM; diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 942f239a213..f71246bebfe 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -58,6 +58,8 @@ static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, if (!sd) goto out_put; + if (sysfs_ns_type(parent_sd)) + sd->s_ns = target->ktype->namespace(target); sd->s_symlink.target_sd = target_sd; target_sd = NULL; /* reference is now owned by the symlink */ @@ -107,6 +109,26 @@ int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target, } /** + * sysfs_delete_link - remove symlink in object's directory. + * @kobj: object we're acting for. + * @targ: object we're pointing to. + * @name: name of the symlink to remove. + * + * Unlike sysfs_remove_link sysfs_delete_link has enough information + * to successfully delete symlinks in tagged directories. + */ +void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, + const char *name) +{ + const void *ns = NULL; + spin_lock(&sysfs_assoc_lock); + if (targ->sd) + ns = targ->sd->s_ns; + spin_unlock(&sysfs_assoc_lock); + sysfs_hash_and_remove(kobj->sd, ns, name); +} + +/** * sysfs_remove_link - remove symlink in object's directory. * @kobj: object we're acting for. * @name: name of the symlink to remove. @@ -121,7 +143,7 @@ void sysfs_remove_link(struct kobject * kobj, const char * name) else parent_sd = kobj->sd; - sysfs_hash_and_remove(parent_sd, name); + sysfs_hash_and_remove(parent_sd, NULL, name); } /** @@ -137,6 +159,7 @@ int sysfs_rename_link(struct kobject *kobj, struct kobject *targ, const char *old, const char *new) { struct sysfs_dirent *parent_sd, *sd = NULL; + const void *old_ns = NULL, *new_ns = NULL; int result; if (!kobj) @@ -144,8 +167,11 @@ int sysfs_rename_link(struct kobject *kobj, struct kobject *targ, else parent_sd = kobj->sd; + if (targ->sd) + old_ns = targ->sd->s_ns; + result = -ENOENT; - sd = sysfs_get_dirent(parent_sd, old); + sd = sysfs_get_dirent(parent_sd, old_ns, old); if (!sd) goto out; @@ -155,7 +181,10 @@ int sysfs_rename_link(struct kobject *kobj, struct kobject *targ, if (sd->s_symlink.target_sd->s_dir.kobj != targ) goto out; - result = sysfs_rename(sd, parent_sd, new); + if (sysfs_ns_type(parent_sd)) + new_ns = targ->ktype->namespace(targ); + + result = sysfs_rename(sd, parent_sd, new_ns, new); out: sysfs_put(sd); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 30f5a44fb5d..6a13105b559 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -58,6 +58,7 @@ struct sysfs_dirent { struct sysfs_dirent *s_sibling; const char *s_name; + const void *s_ns; /* namespace tag */ union { struct sysfs_elem_dir s_dir; struct sysfs_elem_symlink s_symlink; @@ -81,14 +82,27 @@ struct sysfs_dirent { #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) #define SYSFS_ACTIVE_REF (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR) -#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK -#define SYSFS_FLAG_REMOVED 0x0200 +/* identify any namespace tag on sysfs_dirents */ +#define SYSFS_NS_TYPE_MASK 0xff00 +#define SYSFS_NS_TYPE_SHIFT 8 + +#define SYSFS_FLAG_MASK ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK) +#define SYSFS_FLAG_REMOVED 0x020000 static inline unsigned int sysfs_type(struct sysfs_dirent *sd) { return sd->s_flags & SYSFS_TYPE_MASK; } +/* + * Return any namespace tags on this dirent. + * enum kobj_ns_type is defined in linux/kobject.h + */ +static inline enum kobj_ns_type sysfs_ns_type(struct sysfs_dirent *sd) +{ + return (sd->s_flags & SYSFS_NS_TYPE_MASK) >> SYSFS_NS_TYPE_SHIFT; +} + #ifdef CONFIG_DEBUG_LOCK_ALLOC #define sysfs_dirent_init_lockdep(sd) \ do { \ @@ -114,6 +128,16 @@ struct sysfs_addrm_cxt { /* * mount.c */ + +/* + * Each sb is associated with a set of namespace tags (i.e. + * the network namespace of the task which mounted this sysfs + * instance). + */ +struct sysfs_super_info { + const void *ns[KOBJ_NS_TYPES]; +}; +#define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) extern struct sysfs_dirent sysfs_root; extern struct kmem_cache *sysfs_dir_cachep; @@ -137,8 +161,10 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd); void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, + const void *ns, const unsigned char *name); struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, + const void *ns, const unsigned char *name); struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); @@ -149,7 +175,7 @@ int sysfs_create_subdir(struct kobject *kobj, const char *name, void sysfs_remove_subdir(struct sysfs_dirent *sd); int sysfs_rename(struct sysfs_dirent *sd, - struct sysfs_dirent *new_parent_sd, const char *new_name); + struct sysfs_dirent *new_parent_sd, const void *ns, const char *new_name); static inline struct sysfs_dirent *__sysfs_get(struct sysfs_dirent *sd) { @@ -179,7 +205,7 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); -int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name); +int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const void *ns, const char *name); int sysfs_inode_init(void); /* diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c index 77d5cf4a754..bcf5a16f30b 100644 --- a/fs/ubifs/io.c +++ b/fs/ubifs/io.c @@ -64,6 +64,7 @@ void ubifs_ro_mode(struct ubifs_info *c, int err) if (!c->ro_media) { c->ro_media = 1; c->no_chk_data_crc = 0; + c->vfs_sb->s_flags |= MS_RDONLY; ubifs_warn("switched to read-only mode, error %d", err); dbg_dump_stack(); } diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 0f8b9968a80..089eaca860b 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -45,6 +45,15 @@ #include <linux/pagevec.h> #include <linux/writeback.h> +/* + * Types of I/O for bmap clustering and I/O completion tracking. + */ +enum { + IO_READ, /* mapping for a read */ + IO_DELAY, /* mapping covers delalloc region */ + IO_UNWRITTEN, /* mapping covers allocated but uninitialized data */ + IO_NEW /* just allocated */ +}; /* * Prime number of hash buckets since address is used as the key. @@ -103,8 +112,9 @@ xfs_count_page_state( STATIC struct block_device * xfs_find_bdev_for_inode( - struct xfs_inode *ip) + struct inode *inode) { + struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; if (XFS_IS_REALTIME_INODE(ip)) @@ -183,7 +193,7 @@ xfs_setfilesize( xfs_fsize_t isize; ASSERT((ip->i_d.di_mode & S_IFMT) == S_IFREG); - ASSERT(ioend->io_type != IOMAP_READ); + ASSERT(ioend->io_type != IO_READ); if (unlikely(ioend->io_error)) return 0; @@ -214,7 +224,7 @@ xfs_finish_ioend( if (atomic_dec_and_test(&ioend->io_remaining)) { struct workqueue_struct *wq; - wq = (ioend->io_type == IOMAP_UNWRITTEN) ? + wq = (ioend->io_type == IO_UNWRITTEN) ? xfsconvertd_workqueue : xfsdatad_workqueue; queue_work(wq, &ioend->io_work); if (wait) @@ -237,7 +247,7 @@ xfs_end_io( * For unwritten extents we need to issue transactions to convert a * range to normal written extens after the data I/O has finished. */ - if (ioend->io_type == IOMAP_UNWRITTEN && + if (ioend->io_type == IO_UNWRITTEN && likely(!ioend->io_error && !XFS_FORCED_SHUTDOWN(ip->i_mount))) { error = xfs_iomap_write_unwritten(ip, ioend->io_offset, @@ -250,7 +260,7 @@ xfs_end_io( * We might have to update the on-disk file size after extending * writes. */ - if (ioend->io_type != IOMAP_READ) { + if (ioend->io_type != IO_READ) { error = xfs_setfilesize(ioend); ASSERT(!error || error == EAGAIN); } @@ -309,21 +319,25 @@ xfs_map_blocks( struct inode *inode, loff_t offset, ssize_t count, - xfs_iomap_t *mapp, + struct xfs_bmbt_irec *imap, int flags) { int nmaps = 1; + int new = 0; - return -xfs_iomap(XFS_I(inode), offset, count, flags, mapp, &nmaps); + return -xfs_iomap(XFS_I(inode), offset, count, flags, imap, &nmaps, &new); } STATIC int -xfs_iomap_valid( - xfs_iomap_t *iomapp, - loff_t offset) +xfs_imap_valid( + struct inode *inode, + struct xfs_bmbt_irec *imap, + xfs_off_t offset) { - return offset >= iomapp->iomap_offset && - offset < iomapp->iomap_offset + iomapp->iomap_bsize; + offset >>= inode->i_blkbits; + + return offset >= imap->br_startoff && + offset < imap->br_startoff + imap->br_blockcount; } /* @@ -554,19 +568,23 @@ xfs_add_to_ioend( STATIC void xfs_map_buffer( + struct inode *inode, struct buffer_head *bh, - xfs_iomap_t *mp, - xfs_off_t offset, - uint block_bits) + struct xfs_bmbt_irec *imap, + xfs_off_t offset) { sector_t bn; + struct xfs_mount *m = XFS_I(inode)->i_mount; + xfs_off_t iomap_offset = XFS_FSB_TO_B(m, imap->br_startoff); + xfs_daddr_t iomap_bn = xfs_fsb_to_db(XFS_I(inode), imap->br_startblock); - ASSERT(mp->iomap_bn != IOMAP_DADDR_NULL); + ASSERT(imap->br_startblock != HOLESTARTBLOCK); + ASSERT(imap->br_startblock != DELAYSTARTBLOCK); - bn = (mp->iomap_bn >> (block_bits - BBSHIFT)) + - ((offset - mp->iomap_offset) >> block_bits); + bn = (iomap_bn >> (inode->i_blkbits - BBSHIFT)) + + ((offset - iomap_offset) >> inode->i_blkbits); - ASSERT(bn || (mp->iomap_flags & IOMAP_REALTIME)); + ASSERT(bn || XFS_IS_REALTIME_INODE(XFS_I(inode))); bh->b_blocknr = bn; set_buffer_mapped(bh); @@ -574,17 +592,17 @@ xfs_map_buffer( STATIC void xfs_map_at_offset( + struct inode *inode, struct buffer_head *bh, - loff_t offset, - int block_bits, - xfs_iomap_t *iomapp) + struct xfs_bmbt_irec *imap, + xfs_off_t offset) { - ASSERT(!(iomapp->iomap_flags & IOMAP_HOLE)); - ASSERT(!(iomapp->iomap_flags & IOMAP_DELAY)); + ASSERT(imap->br_startblock != HOLESTARTBLOCK); + ASSERT(imap->br_startblock != DELAYSTARTBLOCK); lock_buffer(bh); - xfs_map_buffer(bh, iomapp, offset, block_bits); - bh->b_bdev = iomapp->iomap_target->bt_bdev; + xfs_map_buffer(inode, bh, imap, offset); + bh->b_bdev = xfs_find_bdev_for_inode(inode); set_buffer_mapped(bh); clear_buffer_delay(bh); clear_buffer_unwritten(bh); @@ -713,11 +731,11 @@ xfs_is_delayed_page( bh = head = page_buffers(page); do { if (buffer_unwritten(bh)) - acceptable = (type == IOMAP_UNWRITTEN); + acceptable = (type == IO_UNWRITTEN); else if (buffer_delay(bh)) - acceptable = (type == IOMAP_DELAY); + acceptable = (type == IO_DELAY); else if (buffer_dirty(bh) && buffer_mapped(bh)) - acceptable = (type == IOMAP_NEW); + acceptable = (type == IO_NEW); else break; } while ((bh = bh->b_this_page) != head); @@ -740,7 +758,7 @@ xfs_convert_page( struct inode *inode, struct page *page, loff_t tindex, - xfs_iomap_t *mp, + struct xfs_bmbt_irec *imap, xfs_ioend_t **ioendp, struct writeback_control *wbc, int startio, @@ -750,7 +768,6 @@ xfs_convert_page( xfs_off_t end_offset; unsigned long p_offset; unsigned int type; - int bbits = inode->i_blkbits; int len, page_dirty; int count = 0, done = 0, uptodate = 1; xfs_off_t offset = page_offset(page); @@ -802,19 +819,19 @@ xfs_convert_page( if (buffer_unwritten(bh) || buffer_delay(bh)) { if (buffer_unwritten(bh)) - type = IOMAP_UNWRITTEN; + type = IO_UNWRITTEN; else - type = IOMAP_DELAY; + type = IO_DELAY; - if (!xfs_iomap_valid(mp, offset)) { + if (!xfs_imap_valid(inode, imap, offset)) { done = 1; continue; } - ASSERT(!(mp->iomap_flags & IOMAP_HOLE)); - ASSERT(!(mp->iomap_flags & IOMAP_DELAY)); + ASSERT(imap->br_startblock != HOLESTARTBLOCK); + ASSERT(imap->br_startblock != DELAYSTARTBLOCK); - xfs_map_at_offset(bh, offset, bbits, mp); + xfs_map_at_offset(inode, bh, imap, offset); if (startio) { xfs_add_to_ioend(inode, bh, offset, type, ioendp, done); @@ -826,7 +843,7 @@ xfs_convert_page( page_dirty--; count++; } else { - type = IOMAP_NEW; + type = IO_NEW; if (buffer_mapped(bh) && all_bh && startio) { lock_buffer(bh); xfs_add_to_ioend(inode, bh, offset, @@ -866,7 +883,7 @@ STATIC void xfs_cluster_write( struct inode *inode, pgoff_t tindex, - xfs_iomap_t *iomapp, + struct xfs_bmbt_irec *imap, xfs_ioend_t **ioendp, struct writeback_control *wbc, int startio, @@ -885,7 +902,7 @@ xfs_cluster_write( for (i = 0; i < pagevec_count(&pvec); i++) { done = xfs_convert_page(inode, pvec.pages[i], tindex++, - iomapp, ioendp, wbc, startio, all_bh); + imap, ioendp, wbc, startio, all_bh); if (done) break; } @@ -930,7 +947,7 @@ xfs_aops_discard_page( loff_t offset = page_offset(page); ssize_t len = 1 << inode->i_blkbits; - if (!xfs_is_delayed_page(page, IOMAP_DELAY)) + if (!xfs_is_delayed_page(page, IO_DELAY)) goto out_invalidate; if (XFS_FORCED_SHUTDOWN(ip->i_mount)) @@ -1042,15 +1059,15 @@ xfs_page_state_convert( int unmapped) /* also implies page uptodate */ { struct buffer_head *bh, *head; - xfs_iomap_t iomap; + struct xfs_bmbt_irec imap; xfs_ioend_t *ioend = NULL, *iohead = NULL; loff_t offset; unsigned long p_offset = 0; unsigned int type; __uint64_t end_offset; - pgoff_t end_index, last_index, tlast; + pgoff_t end_index, last_index; ssize_t size, len; - int flags, err, iomap_valid = 0, uptodate = 1; + int flags, err, imap_valid = 0, uptodate = 1; int page_dirty, count = 0; int trylock = 0; int all_bh = unmapped; @@ -1097,7 +1114,7 @@ xfs_page_state_convert( bh = head = page_buffers(page); offset = page_offset(page); flags = BMAPI_READ; - type = IOMAP_NEW; + type = IO_NEW; /* TODO: cleanup count and page_dirty */ @@ -1111,12 +1128,12 @@ xfs_page_state_convert( * the iomap is actually still valid, but the ioend * isn't. shouldn't happen too often. */ - iomap_valid = 0; + imap_valid = 0; continue; } - if (iomap_valid) - iomap_valid = xfs_iomap_valid(&iomap, offset); + if (imap_valid) + imap_valid = xfs_imap_valid(inode, &imap, offset); /* * First case, map an unwritten extent and prepare for @@ -1137,20 +1154,20 @@ xfs_page_state_convert( * Make sure we don't use a read-only iomap */ if (flags == BMAPI_READ) - iomap_valid = 0; + imap_valid = 0; if (buffer_unwritten(bh)) { - type = IOMAP_UNWRITTEN; + type = IO_UNWRITTEN; flags = BMAPI_WRITE | BMAPI_IGNSTATE; } else if (buffer_delay(bh)) { - type = IOMAP_DELAY; + type = IO_DELAY; flags = BMAPI_ALLOCATE | trylock; } else { - type = IOMAP_NEW; + type = IO_NEW; flags = BMAPI_WRITE | BMAPI_MMAP; } - if (!iomap_valid) { + if (!imap_valid) { /* * if we didn't have a valid mapping then we * need to ensure that we put the new mapping @@ -1160,7 +1177,7 @@ xfs_page_state_convert( * for unwritten extent conversion. */ new_ioend = 1; - if (type == IOMAP_NEW) { + if (type == IO_NEW) { size = xfs_probe_cluster(inode, page, bh, head, 0); } else { @@ -1168,14 +1185,14 @@ xfs_page_state_convert( } err = xfs_map_blocks(inode, offset, size, - &iomap, flags); + &imap, flags); if (err) goto error; - iomap_valid = xfs_iomap_valid(&iomap, offset); + imap_valid = xfs_imap_valid(inode, &imap, + offset); } - if (iomap_valid) { - xfs_map_at_offset(bh, offset, - inode->i_blkbits, &iomap); + if (imap_valid) { + xfs_map_at_offset(inode, bh, &imap, offset); if (startio) { xfs_add_to_ioend(inode, bh, offset, type, &ioend, @@ -1194,40 +1211,41 @@ xfs_page_state_convert( * That means it must already have extents allocated * underneath it. Map the extent by reading it. */ - if (!iomap_valid || flags != BMAPI_READ) { + if (!imap_valid || flags != BMAPI_READ) { flags = BMAPI_READ; size = xfs_probe_cluster(inode, page, bh, head, 1); err = xfs_map_blocks(inode, offset, size, - &iomap, flags); + &imap, flags); if (err) goto error; - iomap_valid = xfs_iomap_valid(&iomap, offset); + imap_valid = xfs_imap_valid(inode, &imap, + offset); } /* - * We set the type to IOMAP_NEW in case we are doing a + * We set the type to IO_NEW in case we are doing a * small write at EOF that is extending the file but * without needing an allocation. We need to update the * file size on I/O completion in this case so it is * the same case as having just allocated a new extent * that we are writing into for the first time. */ - type = IOMAP_NEW; + type = IO_NEW; if (trylock_buffer(bh)) { ASSERT(buffer_mapped(bh)); - if (iomap_valid) + if (imap_valid) all_bh = 1; xfs_add_to_ioend(inode, bh, offset, type, - &ioend, !iomap_valid); + &ioend, !imap_valid); page_dirty--; count++; } else { - iomap_valid = 0; + imap_valid = 0; } } else if ((buffer_uptodate(bh) || PageUptodate(page)) && (unmapped || startio)) { - iomap_valid = 0; + imap_valid = 0; } if (!iohead) @@ -1241,12 +1259,23 @@ xfs_page_state_convert( if (startio) xfs_start_page_writeback(page, 1, count); - if (ioend && iomap_valid) { - offset = (iomap.iomap_offset + iomap.iomap_bsize - 1) >> - PAGE_CACHE_SHIFT; - tlast = min_t(pgoff_t, offset, last_index); - xfs_cluster_write(inode, page->index + 1, &iomap, &ioend, - wbc, startio, all_bh, tlast); + if (ioend && imap_valid) { + xfs_off_t end_index; + + end_index = imap.br_startoff + imap.br_blockcount; + + /* to bytes */ + end_index <<= inode->i_blkbits; + + /* to pages */ + end_index = (end_index - 1) >> PAGE_CACHE_SHIFT; + + /* check against file size */ + if (end_index > last_index) + end_index = last_index; + + xfs_cluster_write(inode, page->index + 1, &imap, &ioend, + wbc, startio, all_bh, end_index); } if (iohead) @@ -1448,10 +1477,11 @@ __xfs_get_blocks( int direct, bmapi_flags_t flags) { - xfs_iomap_t iomap; + struct xfs_bmbt_irec imap; xfs_off_t offset; ssize_t size; - int niomap = 1; + int nimap = 1; + int new = 0; int error; offset = (xfs_off_t)iblock << inode->i_blkbits; @@ -1462,22 +1492,21 @@ __xfs_get_blocks( return 0; error = xfs_iomap(XFS_I(inode), offset, size, - create ? flags : BMAPI_READ, &iomap, &niomap); + create ? flags : BMAPI_READ, &imap, &nimap, &new); if (error) return -error; - if (niomap == 0) + if (nimap == 0) return 0; - if (iomap.iomap_bn != IOMAP_DADDR_NULL) { + if (imap.br_startblock != HOLESTARTBLOCK && + imap.br_startblock != DELAYSTARTBLOCK) { /* * For unwritten extents do not report a disk address on * the read case (treat as if we're reading into a hole). */ - if (create || !(iomap.iomap_flags & IOMAP_UNWRITTEN)) { - xfs_map_buffer(bh_result, &iomap, offset, - inode->i_blkbits); - } - if (create && (iomap.iomap_flags & IOMAP_UNWRITTEN)) { + if (create || !ISUNWRITTEN(&imap)) + xfs_map_buffer(inode, bh_result, &imap, offset); + if (create && ISUNWRITTEN(&imap)) { if (direct) bh_result->b_private = inode; set_buffer_unwritten(bh_result); @@ -1488,7 +1517,7 @@ __xfs_get_blocks( * If this is a realtime file, data may be on a different device. * to that pointed to from the buffer_head b_bdev currently. */ - bh_result->b_bdev = iomap.iomap_target->bt_bdev; + bh_result->b_bdev = xfs_find_bdev_for_inode(inode); /* * If we previously allocated a block out beyond eof and we are now @@ -1502,10 +1531,10 @@ __xfs_get_blocks( if (create && ((!buffer_mapped(bh_result) && !buffer_uptodate(bh_result)) || (offset >= i_size_read(inode)) || - (iomap.iomap_flags & (IOMAP_NEW|IOMAP_UNWRITTEN)))) + (new || ISUNWRITTEN(&imap)))) set_buffer_new(bh_result); - if (iomap.iomap_flags & IOMAP_DELAY) { + if (imap.br_startblock == DELAYSTARTBLOCK) { BUG_ON(direct); if (create) { set_buffer_uptodate(bh_result); @@ -1514,11 +1543,23 @@ __xfs_get_blocks( } } + /* + * If this is O_DIRECT or the mpage code calling tell them how large + * the mapping is, so that we can avoid repeated get_blocks calls. + */ if (direct || size > (1 << inode->i_blkbits)) { - ASSERT(iomap.iomap_bsize - iomap.iomap_delta > 0); - offset = min_t(xfs_off_t, - iomap.iomap_bsize - iomap.iomap_delta, size); - bh_result->b_size = (ssize_t)min_t(xfs_off_t, LONG_MAX, offset); + xfs_off_t mapping_size; + + mapping_size = imap.br_startoff + imap.br_blockcount - iblock; + mapping_size <<= inode->i_blkbits; + + ASSERT(mapping_size > 0); + if (mapping_size > size) + mapping_size = size; + if (mapping_size > LONG_MAX) + mapping_size = LONG_MAX; + + bh_result->b_size = mapping_size; } return 0; @@ -1576,7 +1617,7 @@ xfs_end_io_direct( */ ioend->io_offset = offset; ioend->io_size = size; - if (ioend->io_type == IOMAP_READ) { + if (ioend->io_type == IO_READ) { xfs_finish_ioend(ioend, 0); } else if (private && size > 0) { xfs_finish_ioend(ioend, is_sync_kiocb(iocb)); @@ -1587,7 +1628,7 @@ xfs_end_io_direct( * didn't map an unwritten extent so switch it's completion * handler. */ - ioend->io_type = IOMAP_NEW; + ioend->io_type = IO_NEW; xfs_finish_ioend(ioend, 0); } @@ -1612,10 +1653,10 @@ xfs_vm_direct_IO( struct block_device *bdev; ssize_t ret; - bdev = xfs_find_bdev_for_inode(XFS_I(inode)); + bdev = xfs_find_bdev_for_inode(inode); iocb->private = xfs_alloc_ioend(inode, rw == WRITE ? - IOMAP_UNWRITTEN : IOMAP_READ); + IO_UNWRITTEN : IO_READ); ret = blockdev_direct_IO_no_locking(rw, iocb, inode, bdev, iov, offset, nr_segs, diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 44c2b0ef9a4..f01de3c55c4 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -1007,25 +1007,20 @@ xfs_bwrite( struct xfs_mount *mp, struct xfs_buf *bp) { - int iowait = (bp->b_flags & XBF_ASYNC) == 0; - int error = 0; + int error; bp->b_strat = xfs_bdstrat_cb; bp->b_mount = mp; bp->b_flags |= XBF_WRITE; - if (!iowait) - bp->b_flags |= _XBF_RUN_QUEUES; + bp->b_flags &= ~(XBF_ASYNC | XBF_READ); xfs_buf_delwri_dequeue(bp); xfs_buf_iostrategy(bp); - if (iowait) { - error = xfs_buf_iowait(bp); - if (error) - xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); - xfs_buf_relse(bp); - } - + error = xfs_buf_iowait(bp); + if (error) + xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); + xfs_buf_relse(bp); return error; } @@ -1614,7 +1609,8 @@ xfs_mapping_buftarg( STATIC int xfs_alloc_delwrite_queue( - xfs_buftarg_t *btp) + xfs_buftarg_t *btp, + const char *fsname) { int error = 0; @@ -1622,7 +1618,7 @@ xfs_alloc_delwrite_queue( INIT_LIST_HEAD(&btp->bt_delwrite_queue); spin_lock_init(&btp->bt_delwrite_lock); btp->bt_flags = 0; - btp->bt_task = kthread_run(xfsbufd, btp, "xfsbufd"); + btp->bt_task = kthread_run(xfsbufd, btp, "xfsbufd/%s", fsname); if (IS_ERR(btp->bt_task)) { error = PTR_ERR(btp->bt_task); goto out_error; @@ -1635,7 +1631,8 @@ out_error: xfs_buftarg_t * xfs_alloc_buftarg( struct block_device *bdev, - int external) + int external, + const char *fsname) { xfs_buftarg_t *btp; @@ -1647,7 +1644,7 @@ xfs_alloc_buftarg( goto error; if (xfs_mapping_buftarg(btp, bdev)) goto error; - if (xfs_alloc_delwrite_queue(btp)) + if (xfs_alloc_delwrite_queue(btp, fsname)) goto error; xfs_alloc_bufhash(btp, external); return btp; diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index 386e7361e50..5fbecefa5df 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -390,7 +390,7 @@ static inline void xfs_buf_relse(xfs_buf_t *bp) /* * Handling of buftargs. */ -extern xfs_buftarg_t *xfs_alloc_buftarg(struct block_device *, int); +extern xfs_buftarg_t *xfs_alloc_buftarg(struct block_device *, int, const char *); extern void xfs_free_buftarg(struct xfs_mount *, struct xfs_buftarg *); extern void xfs_wait_buftarg(xfs_buftarg_t *); extern int xfs_setsize_buftarg(xfs_buftarg_t *, unsigned int, unsigned int); diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 42dd3bcfba6..d8fb1b5d6cb 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -115,6 +115,8 @@ xfs_file_fsync( xfs_iflags_clear(ip, XFS_ITRUNCATED); + xfs_ioend_wait(ip); + /* * We always need to make sure that the required inode state is safe on * disk. The inode might be clean but we still might need to force the diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index 7b26cc2fd28..699b60cbab9 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -527,6 +527,10 @@ xfs_attrmulti_by_handle( if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t))) return -XFS_ERROR(EFAULT); + /* overflow check */ + if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t)) + return -E2BIG; + dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq); if (IS_ERR(dentry)) return PTR_ERR(dentry); diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index 593c05b4df8..9287135e9bf 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c @@ -420,6 +420,10 @@ xfs_compat_attrmulti_by_handle( sizeof(compat_xfs_fsop_attrmulti_handlereq_t))) return -XFS_ERROR(EFAULT); + /* overflow check */ + if (am_hreq.opcount >= INT_MAX / sizeof(compat_xfs_attr_multiop_t)) + return -E2BIG; + dentry = xfs_compat_handlereq_to_dentry(parfilp, &am_hreq.hreq); if (IS_ERR(dentry)) return PTR_ERR(dentry); diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index e65a7937f3a..9c8019c78c9 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -673,7 +673,10 @@ xfs_vn_fiemap( bm.bmv_length = BTOBB(length); /* We add one because in getbmap world count includes the header */ - bm.bmv_count = fieinfo->fi_extents_max + 1; + bm.bmv_count = !fieinfo->fi_extents_max ? MAXEXTNUM : + fieinfo->fi_extents_max + 1; + bm.bmv_count = min_t(__s32, bm.bmv_count, + (PAGE_SIZE * 16 / sizeof(struct getbmapx))); bm.bmv_iflags = BMV_IF_PREALLOC; if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) bm.bmv_iflags |= BMV_IF_ATTRFORK; diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 29f1edca76d..e9002513e08 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -789,18 +789,18 @@ xfs_open_devices( * Setup xfs_mount buffer target pointers */ error = ENOMEM; - mp->m_ddev_targp = xfs_alloc_buftarg(ddev, 0); + mp->m_ddev_targp = xfs_alloc_buftarg(ddev, 0, mp->m_fsname); if (!mp->m_ddev_targp) goto out_close_rtdev; if (rtdev) { - mp->m_rtdev_targp = xfs_alloc_buftarg(rtdev, 1); + mp->m_rtdev_targp = xfs_alloc_buftarg(rtdev, 1, mp->m_fsname); if (!mp->m_rtdev_targp) goto out_free_ddev_targ; } if (logdev && logdev != ddev) { - mp->m_logdev_targp = xfs_alloc_buftarg(logdev, 1); + mp->m_logdev_targp = xfs_alloc_buftarg(logdev, 1, mp->m_fsname); if (!mp->m_logdev_targp) goto out_free_rtdev_targ; } else { @@ -902,7 +902,8 @@ xfsaild_start( struct xfs_ail *ailp) { ailp->xa_target = 0; - ailp->xa_task = kthread_run(xfsaild, ailp, "xfsaild"); + ailp->xa_task = kthread_run(xfsaild, ailp, "xfsaild/%s", + ailp->xa_mount->m_fsname); if (IS_ERR(ailp->xa_task)) return -PTR_ERR(ailp->xa_task); return 0; @@ -1092,6 +1093,7 @@ xfs_fs_write_inode( * the code will only flush the inode if it isn't already * being flushed. */ + xfs_ioend_wait(ip); xfs_ilock(ip, XFS_ILOCK_SHARED); if (ip->i_update_core) { error = xfs_log_inode(ip); diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index a427c638d90..3884e20bc14 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -356,68 +356,23 @@ xfs_commit_dummy_trans( STATIC int xfs_sync_fsdata( - struct xfs_mount *mp, - int flags) + struct xfs_mount *mp) { struct xfs_buf *bp; - struct xfs_buf_log_item *bip; - int error = 0; /* - * If this is xfssyncd() then only sync the superblock if we can - * lock it without sleeping and it is not pinned. + * If the buffer is pinned then push on the log so we won't get stuck + * waiting in the write for someone, maybe ourselves, to flush the log. + * + * Even though we just pushed the log above, we did not have the + * superblock buffer locked at that point so it can become pinned in + * between there and here. */ - if (flags & SYNC_TRYLOCK) { - ASSERT(!(flags & SYNC_WAIT)); - - bp = xfs_getsb(mp, XBF_TRYLOCK); - if (!bp) - goto out; - - bip = XFS_BUF_FSPRIVATE(bp, struct xfs_buf_log_item *); - if (!bip || !xfs_buf_item_dirty(bip) || XFS_BUF_ISPINNED(bp)) - goto out_brelse; - } else { - bp = xfs_getsb(mp, 0); - - /* - * If the buffer is pinned then push on the log so we won't - * get stuck waiting in the write for someone, maybe - * ourselves, to flush the log. - * - * Even though we just pushed the log above, we did not have - * the superblock buffer locked at that point so it can - * become pinned in between there and here. - */ - if (XFS_BUF_ISPINNED(bp)) - xfs_log_force(mp, 0); - } - - - if (flags & SYNC_WAIT) - XFS_BUF_UNASYNC(bp); - else - XFS_BUF_ASYNC(bp); - - error = xfs_bwrite(mp, bp); - if (error) - return error; - - /* - * If this is a data integrity sync make sure all pending buffers - * are flushed out for the log coverage check below. - */ - if (flags & SYNC_WAIT) - xfs_flush_buftarg(mp->m_ddev_targp, 1); - - if (xfs_log_need_covered(mp)) - error = xfs_commit_dummy_trans(mp, flags); - return error; + bp = xfs_getsb(mp, 0); + if (XFS_BUF_ISPINNED(bp)) + xfs_log_force(mp, 0); - out_brelse: - xfs_buf_relse(bp); - out: - return error; + return xfs_bwrite(mp, bp); } /* @@ -441,7 +396,7 @@ int xfs_quiesce_data( struct xfs_mount *mp) { - int error; + int error, error2 = 0; /* push non-blocking */ xfs_sync_data(mp, 0); @@ -452,13 +407,20 @@ xfs_quiesce_data( xfs_qm_sync(mp, SYNC_WAIT); /* write superblock and hoover up shutdown errors */ - error = xfs_sync_fsdata(mp, SYNC_WAIT); + error = xfs_sync_fsdata(mp); + + /* make sure all delwri buffers are written out */ + xfs_flush_buftarg(mp->m_ddev_targp, 1); + + /* mark the log as covered if needed */ + if (xfs_log_need_covered(mp)) + error2 = xfs_commit_dummy_trans(mp, SYNC_WAIT); /* flush data-only devices */ if (mp->m_rtdev_targp) XFS_bflush(mp->m_rtdev_targp); - return error; + return error ? error : error2; } STATIC void @@ -581,9 +543,9 @@ xfs_flush_inodes( } /* - * Every sync period we need to unpin all items, reclaim inodes, sync - * quota and write out the superblock. We might need to cover the log - * to indicate it is idle. + * Every sync period we need to unpin all items, reclaim inodes and sync + * disk quotas. We might need to cover the log to indicate that the + * filesystem is idle. */ STATIC void xfs_sync_worker( @@ -597,7 +559,8 @@ xfs_sync_worker( xfs_reclaim_inodes(mp, 0); /* dgc: errors ignored here */ error = xfs_qm_sync(mp, SYNC_TRYLOCK); - error = xfs_sync_fsdata(mp, SYNC_TRYLOCK); + if (xfs_log_need_covered(mp)) + error = xfs_commit_dummy_trans(mp, 0); } mp->m_sync_seq++; wake_up(&mp->m_wait_single_sync_task); @@ -660,7 +623,7 @@ xfs_syncd_init( mp->m_sync_work.w_syncer = xfs_sync_worker; mp->m_sync_work.w_mount = mp; mp->m_sync_work.w_completion = NULL; - mp->m_sync_task = kthread_run(xfssyncd, mp, "xfssyncd"); + mp->m_sync_task = kthread_run(xfssyncd, mp, "xfssyncd/%s", mp->m_fsname); if (IS_ERR(mp->m_sync_task)) return -PTR_ERR(mp->m_sync_task); return 0; diff --git a/fs/xfs/linux-2.6/xfs_trace.c b/fs/xfs/linux-2.6/xfs_trace.c index 5a107601e96..207fa77f63a 100644 --- a/fs/xfs/linux-2.6/xfs_trace.c +++ b/fs/xfs/linux-2.6/xfs_trace.c @@ -41,7 +41,6 @@ #include "xfs_alloc.h" #include "xfs_bmap.h" #include "xfs_attr.h" -#include "xfs_attr_sf.h" #include "xfs_attr_leaf.h" #include "xfs_log_priv.h" #include "xfs_buf_item.h" @@ -50,6 +49,9 @@ #include "xfs_aops.h" #include "quota/xfs_dquot_item.h" #include "quota/xfs_dquot.h" +#include "xfs_log_recover.h" +#include "xfs_buf_item.h" +#include "xfs_inode_item.h" /* * We include this last to have the helpers above available for the trace diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index fcaa62f0799..8a319cfd290 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -32,6 +32,10 @@ struct xfs_da_node_entry; struct xfs_dquot; struct xlog_ticket; struct log; +struct xlog_recover; +struct xlog_recover_item; +struct xfs_buf_log_format; +struct xfs_inode_log_format; DECLARE_EVENT_CLASS(xfs_attr_list_class, TP_PROTO(struct xfs_attr_list_context *ctx), @@ -562,18 +566,21 @@ DECLARE_EVENT_CLASS(xfs_inode_class, __field(dev_t, dev) __field(xfs_ino_t, ino) __field(int, count) + __field(int, pincount) __field(unsigned long, caller_ip) ), TP_fast_assign( __entry->dev = VFS_I(ip)->i_sb->s_dev; __entry->ino = ip->i_ino; __entry->count = atomic_read(&VFS_I(ip)->i_count); + __entry->pincount = atomic_read(&ip->i_pincount); __entry->caller_ip = caller_ip; ), - TP_printk("dev %d:%d ino 0x%llx count %d caller %pf", + TP_printk("dev %d:%d ino 0x%llx count %d pincount %d caller %pf", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->count, + __entry->pincount, (char *)__entry->caller_ip) ) @@ -583,6 +590,10 @@ DEFINE_EVENT(xfs_inode_class, name, \ TP_ARGS(ip, caller_ip)) DEFINE_INODE_EVENT(xfs_ihold); DEFINE_INODE_EVENT(xfs_irele); +DEFINE_INODE_EVENT(xfs_inode_pin); +DEFINE_INODE_EVENT(xfs_inode_unpin); +DEFINE_INODE_EVENT(xfs_inode_unpin_nowait); + /* the old xfs_itrace_entry tracer - to be replaced by s.th. in the VFS */ DEFINE_INODE_EVENT(xfs_inode); #define xfs_itrace_entry(ip) \ @@ -642,8 +653,6 @@ DEFINE_EVENT(xfs_dquot_class, name, \ TP_PROTO(struct xfs_dquot *dqp), \ TP_ARGS(dqp)) DEFINE_DQUOT_EVENT(xfs_dqadjust); -DEFINE_DQUOT_EVENT(xfs_dqshake_dirty); -DEFINE_DQUOT_EVENT(xfs_dqshake_unlink); DEFINE_DQUOT_EVENT(xfs_dqreclaim_want); DEFINE_DQUOT_EVENT(xfs_dqreclaim_dirty); DEFINE_DQUOT_EVENT(xfs_dqreclaim_unlink); @@ -658,7 +667,6 @@ DEFINE_DQUOT_EVENT(xfs_dqread_fail); DEFINE_DQUOT_EVENT(xfs_dqlookup_found); DEFINE_DQUOT_EVENT(xfs_dqlookup_want); DEFINE_DQUOT_EVENT(xfs_dqlookup_freelist); -DEFINE_DQUOT_EVENT(xfs_dqlookup_move); DEFINE_DQUOT_EVENT(xfs_dqlookup_done); DEFINE_DQUOT_EVENT(xfs_dqget_hit); DEFINE_DQUOT_EVENT(xfs_dqget_miss); @@ -1495,6 +1503,140 @@ DEFINE_EVENT(xfs_swap_extent_class, name, \ DEFINE_SWAPEXT_EVENT(xfs_swap_extent_before); DEFINE_SWAPEXT_EVENT(xfs_swap_extent_after); +DECLARE_EVENT_CLASS(xfs_log_recover_item_class, + TP_PROTO(struct log *log, struct xlog_recover *trans, + struct xlog_recover_item *item, int pass), + TP_ARGS(log, trans, item, pass), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(unsigned long, item) + __field(xlog_tid_t, tid) + __field(int, type) + __field(int, pass) + __field(int, count) + __field(int, total) + ), + TP_fast_assign( + __entry->dev = log->l_mp->m_super->s_dev; + __entry->item = (unsigned long)item; + __entry->tid = trans->r_log_tid; + __entry->type = ITEM_TYPE(item); + __entry->pass = pass; + __entry->count = item->ri_cnt; + __entry->total = item->ri_total; + ), + TP_printk("dev %d:%d trans 0x%x, pass %d, item 0x%p, item type %s " + "item region count/total %d/%d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->tid, + __entry->pass, + (void *)__entry->item, + __print_symbolic(__entry->type, XFS_LI_TYPE_DESC), + __entry->count, + __entry->total) +) + +#define DEFINE_LOG_RECOVER_ITEM(name) \ +DEFINE_EVENT(xfs_log_recover_item_class, name, \ + TP_PROTO(struct log *log, struct xlog_recover *trans, \ + struct xlog_recover_item *item, int pass), \ + TP_ARGS(log, trans, item, pass)) + +DEFINE_LOG_RECOVER_ITEM(xfs_log_recover_item_add); +DEFINE_LOG_RECOVER_ITEM(xfs_log_recover_item_add_cont); +DEFINE_LOG_RECOVER_ITEM(xfs_log_recover_item_reorder_head); +DEFINE_LOG_RECOVER_ITEM(xfs_log_recover_item_reorder_tail); +DEFINE_LOG_RECOVER_ITEM(xfs_log_recover_item_recover); + +DECLARE_EVENT_CLASS(xfs_log_recover_buf_item_class, + TP_PROTO(struct log *log, struct xfs_buf_log_format *buf_f), + TP_ARGS(log, buf_f), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(__int64_t, blkno) + __field(unsigned short, len) + __field(unsigned short, flags) + __field(unsigned short, size) + __field(unsigned int, map_size) + ), + TP_fast_assign( + __entry->dev = log->l_mp->m_super->s_dev; + __entry->blkno = buf_f->blf_blkno; + __entry->len = buf_f->blf_len; + __entry->flags = buf_f->blf_flags; + __entry->size = buf_f->blf_size; + __entry->map_size = buf_f->blf_map_size; + ), + TP_printk("dev %d:%d blkno 0x%llx, len %u, flags 0x%x, size %d, " + "map_size %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->blkno, + __entry->len, + __entry->flags, + __entry->size, + __entry->map_size) +) + +#define DEFINE_LOG_RECOVER_BUF_ITEM(name) \ +DEFINE_EVENT(xfs_log_recover_buf_item_class, name, \ + TP_PROTO(struct log *log, struct xfs_buf_log_format *buf_f), \ + TP_ARGS(log, buf_f)) + +DEFINE_LOG_RECOVER_BUF_ITEM(xfs_log_recover_buf_not_cancel); +DEFINE_LOG_RECOVER_BUF_ITEM(xfs_log_recover_buf_cancel); +DEFINE_LOG_RECOVER_BUF_ITEM(xfs_log_recover_buf_cancel_add); +DEFINE_LOG_RECOVER_BUF_ITEM(xfs_log_recover_buf_cancel_ref_inc); +DEFINE_LOG_RECOVER_BUF_ITEM(xfs_log_recover_buf_recover); +DEFINE_LOG_RECOVER_BUF_ITEM(xfs_log_recover_buf_inode_buf); +DEFINE_LOG_RECOVER_BUF_ITEM(xfs_log_recover_buf_reg_buf); +DEFINE_LOG_RECOVER_BUF_ITEM(xfs_log_recover_buf_dquot_buf); + +DECLARE_EVENT_CLASS(xfs_log_recover_ino_item_class, + TP_PROTO(struct log *log, struct xfs_inode_log_format *in_f), + TP_ARGS(log, in_f), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, ino) + __field(unsigned short, size) + __field(int, fields) + __field(unsigned short, asize) + __field(unsigned short, dsize) + __field(__int64_t, blkno) + __field(int, len) + __field(int, boffset) + ), + TP_fast_assign( + __entry->dev = log->l_mp->m_super->s_dev; + __entry->ino = in_f->ilf_ino; + __entry->size = in_f->ilf_size; + __entry->fields = in_f->ilf_fields; + __entry->asize = in_f->ilf_asize; + __entry->dsize = in_f->ilf_dsize; + __entry->blkno = in_f->ilf_blkno; + __entry->len = in_f->ilf_len; + __entry->boffset = in_f->ilf_boffset; + ), + TP_printk("dev %d:%d ino 0x%llx, size %u, fields 0x%x, asize %d, " + "dsize %d, blkno 0x%llx, len %d, boffset %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, + __entry->size, + __entry->fields, + __entry->asize, + __entry->dsize, + __entry->blkno, + __entry->len, + __entry->boffset) +) +#define DEFINE_LOG_RECOVER_INO_ITEM(name) \ +DEFINE_EVENT(xfs_log_recover_ino_item_class, name, \ + TP_PROTO(struct log *log, struct xfs_inode_log_format *in_f), \ + TP_ARGS(log, in_f)) + +DEFINE_LOG_RECOVER_INO_ITEM(xfs_log_recover_inode_recover); +DEFINE_LOG_RECOVER_INO_ITEM(xfs_log_recover_inode_cancel); +DEFINE_LOG_RECOVER_INO_ITEM(xfs_log_recover_inode_skip); + #endif /* _TRACE_XFS_H */ #undef TRACE_INCLUDE_PATH diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c index 5f79dd78626..b89ec5df012 100644 --- a/fs/xfs/quota/xfs_dquot.c +++ b/fs/xfs/quota/xfs_dquot.c @@ -101,7 +101,7 @@ xfs_qm_dqinit( * No need to re-initialize these if this is a reclaimed dquot. */ if (brandnewdquot) { - dqp->dq_flnext = dqp->dq_flprev = dqp; + INIT_LIST_HEAD(&dqp->q_freelist); mutex_init(&dqp->q_qlock); init_waitqueue_head(&dqp->q_pinwait); @@ -119,20 +119,20 @@ xfs_qm_dqinit( * Only the q_core portion was zeroed in dqreclaim_one(). * So, we need to reset others. */ - dqp->q_nrefs = 0; - dqp->q_blkno = 0; - dqp->MPL_NEXT = dqp->HL_NEXT = NULL; - dqp->HL_PREVP = dqp->MPL_PREVP = NULL; - dqp->q_bufoffset = 0; - dqp->q_fileoffset = 0; - dqp->q_transp = NULL; - dqp->q_gdquot = NULL; - dqp->q_res_bcount = 0; - dqp->q_res_icount = 0; - dqp->q_res_rtbcount = 0; - atomic_set(&dqp->q_pincount, 0); - dqp->q_hash = NULL; - ASSERT(dqp->dq_flnext == dqp->dq_flprev); + dqp->q_nrefs = 0; + dqp->q_blkno = 0; + INIT_LIST_HEAD(&dqp->q_mplist); + INIT_LIST_HEAD(&dqp->q_hashlist); + dqp->q_bufoffset = 0; + dqp->q_fileoffset = 0; + dqp->q_transp = NULL; + dqp->q_gdquot = NULL; + dqp->q_res_bcount = 0; + dqp->q_res_icount = 0; + dqp->q_res_rtbcount = 0; + atomic_set(&dqp->q_pincount, 0); + dqp->q_hash = NULL; + ASSERT(list_empty(&dqp->q_freelist)); trace_xfs_dqreuse(dqp); } @@ -158,7 +158,7 @@ void xfs_qm_dqdestroy( xfs_dquot_t *dqp) { - ASSERT(! XFS_DQ_IS_ON_FREELIST(dqp)); + ASSERT(list_empty(&dqp->q_freelist)); mutex_destroy(&dqp->q_qlock); sv_destroy(&dqp->q_pinwait); @@ -252,7 +252,7 @@ xfs_qm_adjust_dqtimers( (be64_to_cpu(d->d_bcount) >= be64_to_cpu(d->d_blk_hardlimit)))) { d->d_btimer = cpu_to_be32(get_seconds() + - XFS_QI_BTIMELIMIT(mp)); + mp->m_quotainfo->qi_btimelimit); } else { d->d_bwarns = 0; } @@ -275,7 +275,7 @@ xfs_qm_adjust_dqtimers( (be64_to_cpu(d->d_icount) >= be64_to_cpu(d->d_ino_hardlimit)))) { d->d_itimer = cpu_to_be32(get_seconds() + - XFS_QI_ITIMELIMIT(mp)); + mp->m_quotainfo->qi_itimelimit); } else { d->d_iwarns = 0; } @@ -298,7 +298,7 @@ xfs_qm_adjust_dqtimers( (be64_to_cpu(d->d_rtbcount) >= be64_to_cpu(d->d_rtb_hardlimit)))) { d->d_rtbtimer = cpu_to_be32(get_seconds() + - XFS_QI_RTBTIMELIMIT(mp)); + mp->m_quotainfo->qi_rtbtimelimit); } else { d->d_rtbwarns = 0; } @@ -325,6 +325,7 @@ xfs_qm_init_dquot_blk( uint type, xfs_buf_t *bp) { + struct xfs_quotainfo *q = mp->m_quotainfo; xfs_dqblk_t *d; int curid, i; @@ -337,16 +338,16 @@ xfs_qm_init_dquot_blk( /* * ID of the first dquot in the block - id's are zero based. */ - curid = id - (id % XFS_QM_DQPERBLK(mp)); + curid = id - (id % q->qi_dqperchunk); ASSERT(curid >= 0); - memset(d, 0, BBTOB(XFS_QI_DQCHUNKLEN(mp))); - for (i = 0; i < XFS_QM_DQPERBLK(mp); i++, d++, curid++) + memset(d, 0, BBTOB(q->qi_dqchunklen)); + for (i = 0; i < q->qi_dqperchunk; i++, d++, curid++) xfs_qm_dqinit_core(curid, type, d); xfs_trans_dquot_buf(tp, bp, (type & XFS_DQ_USER ? XFS_BLI_UDQUOT_BUF : ((type & XFS_DQ_PROJ) ? XFS_BLI_PDQUOT_BUF : XFS_BLI_GDQUOT_BUF))); - xfs_trans_log_buf(tp, bp, 0, BBTOB(XFS_QI_DQCHUNKLEN(mp)) - 1); + xfs_trans_log_buf(tp, bp, 0, BBTOB(q->qi_dqchunklen) - 1); } @@ -419,7 +420,7 @@ xfs_qm_dqalloc( /* now we can just get the buffer (there's nothing to read yet) */ bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, dqp->q_blkno, - XFS_QI_DQCHUNKLEN(mp), + mp->m_quotainfo->qi_dqchunklen, 0); if (!bp || (error = XFS_BUF_GETERROR(bp))) goto error1; @@ -500,7 +501,8 @@ xfs_qm_dqtobp( */ if (dqp->q_blkno == (xfs_daddr_t) 0) { /* We use the id as an index */ - dqp->q_fileoffset = (xfs_fileoff_t)id / XFS_QM_DQPERBLK(mp); + dqp->q_fileoffset = (xfs_fileoff_t)id / + mp->m_quotainfo->qi_dqperchunk; nmaps = 1; quotip = XFS_DQ_TO_QIP(dqp); xfs_ilock(quotip, XFS_ILOCK_SHARED); @@ -529,7 +531,7 @@ xfs_qm_dqtobp( /* * offset of dquot in the (fixed sized) dquot chunk. */ - dqp->q_bufoffset = (id % XFS_QM_DQPERBLK(mp)) * + dqp->q_bufoffset = (id % mp->m_quotainfo->qi_dqperchunk) * sizeof(xfs_dqblk_t); if (map.br_startblock == HOLESTARTBLOCK) { /* @@ -559,15 +561,13 @@ xfs_qm_dqtobp( * Read in the buffer, unless we've just done the allocation * (in which case we already have the buf). */ - if (! newdquot) { + if (!newdquot) { trace_xfs_dqtobp_read(dqp); - if ((error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, - dqp->q_blkno, - XFS_QI_DQCHUNKLEN(mp), - 0, &bp))) { - return (error); - } + error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, + dqp->q_blkno, + mp->m_quotainfo->qi_dqchunklen, + 0, &bp); if (error || !bp) return XFS_ERROR(error); } @@ -689,14 +689,14 @@ xfs_qm_idtodq( tp = NULL; if (flags & XFS_QMOPT_DQALLOC) { tp = xfs_trans_alloc(mp, XFS_TRANS_QM_DQALLOC); - if ((error = xfs_trans_reserve(tp, - XFS_QM_DQALLOC_SPACE_RES(mp), - XFS_WRITE_LOG_RES(mp) + - BBTOB(XFS_QI_DQCHUNKLEN(mp)) - 1 + - 128, - 0, - XFS_TRANS_PERM_LOG_RES, - XFS_WRITE_LOG_COUNT))) { + error = xfs_trans_reserve(tp, XFS_QM_DQALLOC_SPACE_RES(mp), + XFS_WRITE_LOG_RES(mp) + + BBTOB(mp->m_quotainfo->qi_dqchunklen) - 1 + + 128, + 0, + XFS_TRANS_PERM_LOG_RES, + XFS_WRITE_LOG_COUNT); + if (error) { cancelflags = 0; goto error0; } @@ -751,7 +751,6 @@ xfs_qm_dqlookup( { xfs_dquot_t *dqp; uint flist_locked; - xfs_dquot_t *d; ASSERT(mutex_is_locked(&qh->qh_lock)); @@ -760,7 +759,7 @@ xfs_qm_dqlookup( /* * Traverse the hashchain looking for a match */ - for (dqp = qh->qh_next; dqp != NULL; dqp = dqp->HL_NEXT) { + list_for_each_entry(dqp, &qh->qh_list, q_hashlist) { /* * We already have the hashlock. We don't need the * dqlock to look at the id field of the dquot, since the @@ -772,12 +771,12 @@ xfs_qm_dqlookup( /* * All in core dquots must be on the dqlist of mp */ - ASSERT(dqp->MPL_PREVP != NULL); + ASSERT(!list_empty(&dqp->q_mplist)); xfs_dqlock(dqp); if (dqp->q_nrefs == 0) { - ASSERT (XFS_DQ_IS_ON_FREELIST(dqp)); - if (! xfs_qm_freelist_lock_nowait(xfs_Gqm)) { + ASSERT(!list_empty(&dqp->q_freelist)); + if (!mutex_trylock(&xfs_Gqm->qm_dqfrlist_lock)) { trace_xfs_dqlookup_want(dqp); /* @@ -787,7 +786,7 @@ xfs_qm_dqlookup( */ dqp->dq_flags |= XFS_DQ_WANT; xfs_dqunlock(dqp); - xfs_qm_freelist_lock(xfs_Gqm); + mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); xfs_dqlock(dqp); dqp->dq_flags &= ~(XFS_DQ_WANT); } @@ -802,46 +801,28 @@ xfs_qm_dqlookup( if (flist_locked) { if (dqp->q_nrefs != 0) { - xfs_qm_freelist_unlock(xfs_Gqm); + mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); flist_locked = B_FALSE; } else { - /* - * take it off the freelist - */ + /* take it off the freelist */ trace_xfs_dqlookup_freelist(dqp); - XQM_FREELIST_REMOVE(dqp); - /* xfs_qm_freelist_print(&(xfs_Gqm-> - qm_dqfreelist), - "after removal"); */ + list_del_init(&dqp->q_freelist); + xfs_Gqm->qm_dqfrlist_cnt--; } } - /* - * grab a reference - */ XFS_DQHOLD(dqp); if (flist_locked) - xfs_qm_freelist_unlock(xfs_Gqm); + mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); /* * move the dquot to the front of the hashchain */ ASSERT(mutex_is_locked(&qh->qh_lock)); - if (dqp->HL_PREVP != &qh->qh_next) { - trace_xfs_dqlookup_move(dqp); - if ((d = dqp->HL_NEXT)) - d->HL_PREVP = dqp->HL_PREVP; - *(dqp->HL_PREVP) = d; - d = qh->qh_next; - d->HL_PREVP = &dqp->HL_NEXT; - dqp->HL_NEXT = d; - dqp->HL_PREVP = &qh->qh_next; - qh->qh_next = dqp; - } + list_move(&dqp->q_hashlist, &qh->qh_list); trace_xfs_dqlookup_done(dqp); *O_dqpp = dqp; - ASSERT(mutex_is_locked(&qh->qh_lock)); - return (0); + return 0; } } @@ -975,16 +956,17 @@ xfs_qm_dqget( */ if (ip) { xfs_ilock(ip, XFS_ILOCK_EXCL); - if (! XFS_IS_DQTYPE_ON(mp, type)) { - /* inode stays locked on return */ - xfs_qm_dqdestroy(dqp); - return XFS_ERROR(ESRCH); - } + /* * A dquot could be attached to this inode by now, since * we had dropped the ilock. */ if (type == XFS_DQ_USER) { + if (!XFS_IS_UQUOTA_ON(mp)) { + /* inode stays locked on return */ + xfs_qm_dqdestroy(dqp); + return XFS_ERROR(ESRCH); + } if (ip->i_udquot) { xfs_qm_dqdestroy(dqp); dqp = ip->i_udquot; @@ -992,6 +974,11 @@ xfs_qm_dqget( goto dqret; } } else { + if (!XFS_IS_OQUOTA_ON(mp)) { + /* inode stays locked on return */ + xfs_qm_dqdestroy(dqp); + return XFS_ERROR(ESRCH); + } if (ip->i_gdquot) { xfs_qm_dqdestroy(dqp); dqp = ip->i_gdquot; @@ -1033,13 +1020,14 @@ xfs_qm_dqget( */ ASSERT(mutex_is_locked(&h->qh_lock)); dqp->q_hash = h; - XQM_HASHLIST_INSERT(h, dqp); + list_add(&dqp->q_hashlist, &h->qh_list); + h->qh_version++; /* * Attach this dquot to this filesystem's list of all dquots, * kept inside the mount structure in m_quotainfo field */ - xfs_qm_mplist_lock(mp); + mutex_lock(&mp->m_quotainfo->qi_dqlist_lock); /* * We return a locked dquot to the caller, with a reference taken @@ -1047,9 +1035,9 @@ xfs_qm_dqget( xfs_dqlock(dqp); dqp->q_nrefs = 1; - XQM_MPLIST_INSERT(&(XFS_QI_MPL_LIST(mp)), dqp); - - xfs_qm_mplist_unlock(mp); + list_add(&dqp->q_mplist, &mp->m_quotainfo->qi_dqlist); + mp->m_quotainfo->qi_dquots++; + mutex_unlock(&mp->m_quotainfo->qi_dqlist_lock); mutex_unlock(&h->qh_lock); dqret: ASSERT((ip == NULL) || xfs_isilocked(ip, XFS_ILOCK_EXCL)); @@ -1086,10 +1074,10 @@ xfs_qm_dqput( * drop the dqlock and acquire the freelist and dqlock * in the right order; but try to get it out-of-order first */ - if (! xfs_qm_freelist_lock_nowait(xfs_Gqm)) { + if (!mutex_trylock(&xfs_Gqm->qm_dqfrlist_lock)) { trace_xfs_dqput_wait(dqp); xfs_dqunlock(dqp); - xfs_qm_freelist_lock(xfs_Gqm); + mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); xfs_dqlock(dqp); } @@ -1100,10 +1088,8 @@ xfs_qm_dqput( if (--dqp->q_nrefs == 0) { trace_xfs_dqput_free(dqp); - /* - * insert at end of the freelist. - */ - XQM_FREELIST_INSERT(&(xfs_Gqm->qm_dqfreelist), dqp); + list_add_tail(&dqp->q_freelist, &xfs_Gqm->qm_dqfrlist); + xfs_Gqm->qm_dqfrlist_cnt++; /* * If we just added a udquot to the freelist, then @@ -1118,10 +1104,6 @@ xfs_qm_dqput( xfs_dqlock(gdqp); dqp->q_gdquot = NULL; } - - /* xfs_qm_freelist_print(&(xfs_Gqm->qm_dqfreelist), - "@@@@@++ Free list (after append) @@@@@+"); - */ } xfs_dqunlock(dqp); @@ -1133,7 +1115,7 @@ xfs_qm_dqput( break; dqp = gdqp; } - xfs_qm_freelist_unlock(xfs_Gqm); + mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); } /* @@ -1386,10 +1368,10 @@ int xfs_qm_dqpurge( xfs_dquot_t *dqp) { - xfs_dqhash_t *thishash; + xfs_dqhash_t *qh = dqp->q_hash; xfs_mount_t *mp = dqp->q_mount; - ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp)); + ASSERT(mutex_is_locked(&mp->m_quotainfo->qi_dqlist_lock)); ASSERT(mutex_is_locked(&dqp->q_hash->qh_lock)); xfs_dqlock(dqp); @@ -1407,7 +1389,7 @@ xfs_qm_dqpurge( return (1); } - ASSERT(XFS_DQ_IS_ON_FREELIST(dqp)); + ASSERT(!list_empty(&dqp->q_freelist)); /* * If we're turning off quotas, we have to make sure that, for @@ -1452,14 +1434,16 @@ xfs_qm_dqpurge( ASSERT(XFS_FORCED_SHUTDOWN(mp) || !(dqp->q_logitem.qli_item.li_flags & XFS_LI_IN_AIL)); - thishash = dqp->q_hash; - XQM_HASHLIST_REMOVE(thishash, dqp); - XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(mp)), dqp); + list_del_init(&dqp->q_hashlist); + qh->qh_version++; + list_del_init(&dqp->q_mplist); + mp->m_quotainfo->qi_dqreclaims++; + mp->m_quotainfo->qi_dquots--; /* * XXX Move this to the front of the freelist, if we can get the * freelist lock. */ - ASSERT(XFS_DQ_IS_ON_FREELIST(dqp)); + ASSERT(!list_empty(&dqp->q_freelist)); dqp->q_mount = NULL; dqp->q_hash = NULL; @@ -1467,7 +1451,7 @@ xfs_qm_dqpurge( memset(&dqp->q_core, 0, sizeof(dqp->q_core)); xfs_dqfunlock(dqp); xfs_dqunlock(dqp); - mutex_unlock(&thishash->qh_lock); + mutex_unlock(&qh->qh_lock); return (0); } @@ -1517,6 +1501,7 @@ void xfs_qm_dqflock_pushbuf_wait( xfs_dquot_t *dqp) { + xfs_mount_t *mp = dqp->q_mount; xfs_buf_t *bp; /* @@ -1525,14 +1510,14 @@ xfs_qm_dqflock_pushbuf_wait( * out immediately. We'll be able to acquire * the flush lock when the I/O completes. */ - bp = xfs_incore(dqp->q_mount->m_ddev_targp, dqp->q_blkno, - XFS_QI_DQCHUNKLEN(dqp->q_mount), XBF_TRYLOCK); + bp = xfs_incore(mp->m_ddev_targp, dqp->q_blkno, + mp->m_quotainfo->qi_dqchunklen, XBF_TRYLOCK); if (!bp) goto out_lock; if (XFS_BUF_ISDELAYWRITE(bp)) { if (XFS_BUF_ISPINNED(bp)) - xfs_log_force(dqp->q_mount, 0); + xfs_log_force(mp, 0); xfs_buf_delwri_promote(bp); wake_up_process(bp->b_target->bt_task); } diff --git a/fs/xfs/quota/xfs_dquot.h b/fs/xfs/quota/xfs_dquot.h index a0f7da586d1..5da3a23b820 100644 --- a/fs/xfs/quota/xfs_dquot.h +++ b/fs/xfs/quota/xfs_dquot.h @@ -33,40 +33,23 @@ * The hash chain headers (hash buckets) */ typedef struct xfs_dqhash { - struct xfs_dquot *qh_next; + struct list_head qh_list; struct mutex qh_lock; uint qh_version; /* ever increasing version */ uint qh_nelems; /* number of dquots on the list */ } xfs_dqhash_t; -typedef struct xfs_dqlink { - struct xfs_dquot *ql_next; /* forward link */ - struct xfs_dquot **ql_prevp; /* pointer to prev ql_next */ -} xfs_dqlink_t; - struct xfs_mount; struct xfs_trans; /* - * This is the marker which is designed to occupy the first few - * bytes of the xfs_dquot_t structure. Even inside this, the freelist pointers - * must come first. - * This serves as the marker ("sentinel") when we have to restart list - * iterations because of locking considerations. - */ -typedef struct xfs_dqmarker { - struct xfs_dquot*dqm_flnext; /* link to freelist: must be first */ - struct xfs_dquot*dqm_flprev; - xfs_dqlink_t dqm_mplist; /* link to mount's list of dquots */ - xfs_dqlink_t dqm_hashlist; /* link to the hash chain */ - uint dqm_flags; /* various flags (XFS_DQ_*) */ -} xfs_dqmarker_t; - -/* * The incore dquot structure */ typedef struct xfs_dquot { - xfs_dqmarker_t q_lists; /* list ptrs, q_flags (marker) */ + uint dq_flags; /* various flags (XFS_DQ_*) */ + struct list_head q_freelist; /* global free list of dquots */ + struct list_head q_mplist; /* mount's list of dquots */ + struct list_head q_hashlist; /* gloabl hash list of dquots */ xfs_dqhash_t *q_hash; /* the hashchain header */ struct xfs_mount*q_mount; /* filesystem this relates to */ struct xfs_trans*q_transp; /* trans this belongs to currently */ @@ -87,13 +70,6 @@ typedef struct xfs_dquot { wait_queue_head_t q_pinwait; /* dquot pinning wait queue */ } xfs_dquot_t; - -#define dq_flnext q_lists.dqm_flnext -#define dq_flprev q_lists.dqm_flprev -#define dq_mplist q_lists.dqm_mplist -#define dq_hashlist q_lists.dqm_hashlist -#define dq_flags q_lists.dqm_flags - /* * Lock hierarchy for q_qlock: * XFS_QLOCK_NORMAL is the implicit default, @@ -127,7 +103,6 @@ static inline void xfs_dqfunlock(xfs_dquot_t *dqp) } #define XFS_DQ_IS_LOCKED(dqp) (mutex_is_locked(&((dqp)->q_qlock))) -#define XFS_DQ_IS_ON_FREELIST(dqp) ((dqp)->dq_flnext != (dqp)) #define XFS_DQ_IS_DIRTY(dqp) ((dqp)->dq_flags & XFS_DQ_DIRTY) #define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER) #define XFS_QM_ISPDQ(dqp) ((dqp)->dq_flags & XFS_DQ_PROJ) diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c index 4e4ee9a5719..8d89a24ae32 100644 --- a/fs/xfs/quota/xfs_dquot_item.c +++ b/fs/xfs/quota/xfs_dquot_item.c @@ -107,8 +107,7 @@ xfs_qm_dquot_logitem_pin( /* ARGSUSED */ STATIC void xfs_qm_dquot_logitem_unpin( - xfs_dq_logitem_t *logitem, - int stale) + xfs_dq_logitem_t *logitem) { xfs_dquot_t *dqp = logitem->qli_dquot; @@ -123,7 +122,7 @@ xfs_qm_dquot_logitem_unpin_remove( xfs_dq_logitem_t *logitem, xfs_trans_t *tp) { - xfs_qm_dquot_logitem_unpin(logitem, 0); + xfs_qm_dquot_logitem_unpin(logitem); } /* @@ -228,7 +227,7 @@ xfs_qm_dquot_logitem_pushbuf( } mp = dqp->q_mount; bp = xfs_incore(mp->m_ddev_targp, qip->qli_format.qlf_blkno, - XFS_QI_DQCHUNKLEN(mp), XBF_TRYLOCK); + mp->m_quotainfo->qi_dqchunklen, XBF_TRYLOCK); xfs_dqunlock(dqp); if (!bp) return; @@ -329,8 +328,7 @@ static struct xfs_item_ops xfs_dquot_item_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_qm_dquot_logitem_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int)) - xfs_qm_dquot_logitem_unpin, + .iop_unpin = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_unpin, .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*)) xfs_qm_dquot_logitem_unpin_remove, .iop_trylock = (uint(*)(xfs_log_item_t*)) @@ -357,9 +355,8 @@ xfs_qm_dquot_logitem_init( xfs_dq_logitem_t *lp; lp = &dqp->q_logitem; - lp->qli_item.li_type = XFS_LI_DQUOT; - lp->qli_item.li_ops = &xfs_dquot_item_ops; - lp->qli_item.li_mountp = dqp->q_mount; + xfs_log_item_init(dqp->q_mount, &lp->qli_item, XFS_LI_DQUOT, + &xfs_dquot_item_ops); lp->qli_dquot = dqp; lp->qli_format.qlf_type = XFS_LI_DQUOT; lp->qli_format.qlf_id = be32_to_cpu(dqp->q_core.d_id); @@ -426,7 +423,7 @@ xfs_qm_qoff_logitem_pin(xfs_qoff_logitem_t *qf) */ /*ARGSUSED*/ STATIC void -xfs_qm_qoff_logitem_unpin(xfs_qoff_logitem_t *qf, int stale) +xfs_qm_qoff_logitem_unpin(xfs_qoff_logitem_t *qf) { return; } @@ -537,8 +534,7 @@ static struct xfs_item_ops xfs_qm_qoffend_logitem_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_qm_qoff_logitem_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin, - .iop_unpin = (void(*)(xfs_log_item_t* ,int)) - xfs_qm_qoff_logitem_unpin, + .iop_unpin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unpin, .iop_unpin_remove = (void(*)(xfs_log_item_t*,xfs_trans_t*)) xfs_qm_qoff_logitem_unpin_remove, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock, @@ -559,8 +555,7 @@ static struct xfs_item_ops xfs_qm_qoff_logitem_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_qm_qoff_logitem_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int)) - xfs_qm_qoff_logitem_unpin, + .iop_unpin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unpin, .iop_unpin_remove = (void(*)(xfs_log_item_t*,xfs_trans_t*)) xfs_qm_qoff_logitem_unpin_remove, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock, @@ -586,11 +581,8 @@ xfs_qm_qoff_logitem_init( qf = (xfs_qoff_logitem_t*) kmem_zalloc(sizeof(xfs_qoff_logitem_t), KM_SLEEP); - qf->qql_item.li_type = XFS_LI_QUOTAOFF; - if (start) - qf->qql_item.li_ops = &xfs_qm_qoffend_logitem_ops; - else - qf->qql_item.li_ops = &xfs_qm_qoff_logitem_ops; + xfs_log_item_init(mp, &qf->qql_item, XFS_LI_QUOTAOFF, start ? + &xfs_qm_qoffend_logitem_ops : &xfs_qm_qoff_logitem_ops); qf->qql_item.li_mountp = mp; qf->qql_format.qf_type = XFS_LI_QUOTAOFF; qf->qql_format.qf_flags = flags; diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 417e61e3d9d..38e76414664 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -67,9 +67,6 @@ static cred_t xfs_zerocr; STATIC void xfs_qm_list_init(xfs_dqlist_t *, char *, int); STATIC void xfs_qm_list_destroy(xfs_dqlist_t *); -STATIC void xfs_qm_freelist_init(xfs_frlist_t *); -STATIC void xfs_qm_freelist_destroy(xfs_frlist_t *); - STATIC int xfs_qm_init_quotainos(xfs_mount_t *); STATIC int xfs_qm_init_quotainfo(xfs_mount_t *); STATIC int xfs_qm_shake(int, gfp_t); @@ -84,21 +81,25 @@ extern struct mutex qcheck_lock; #endif #ifdef QUOTADEBUG -#define XQM_LIST_PRINT(l, NXT, title) \ -{ \ - xfs_dquot_t *dqp; int i = 0; \ - cmn_err(CE_DEBUG, "%s (#%d)", title, (int) (l)->qh_nelems); \ - for (dqp = (l)->qh_next; dqp != NULL; dqp = dqp->NXT) { \ - cmn_err(CE_DEBUG, " %d. \"%d (%s)\" " \ - "bcnt = %d, icnt = %d, refs = %d", \ - ++i, (int) be32_to_cpu(dqp->q_core.d_id), \ - DQFLAGTO_TYPESTR(dqp), \ - (int) be64_to_cpu(dqp->q_core.d_bcount), \ - (int) be64_to_cpu(dqp->q_core.d_icount), \ - (int) dqp->q_nrefs); } \ +static void +xfs_qm_dquot_list_print( + struct xfs_mount *mp) +{ + xfs_dquot_t *dqp; + int i = 0; + + list_for_each_entry(dqp, &mp->m_quotainfo->qi_dqlist_lock, qi_mplist) { + cmn_err(CE_DEBUG, " %d. \"%d (%s)\" " + "bcnt = %lld, icnt = %lld, refs = %d", + i++, be32_to_cpu(dqp->q_core.d_id), + DQFLAGTO_TYPESTR(dqp), + (long long)be64_to_cpu(dqp->q_core.d_bcount), + (long long)be64_to_cpu(dqp->q_core.d_icount), + dqp->q_nrefs); + } } #else -#define XQM_LIST_PRINT(l, NXT, title) do { } while (0) +static void xfs_qm_dquot_list_print(struct xfs_mount *mp) { } #endif /* @@ -144,7 +145,9 @@ xfs_Gqm_init(void) /* * Freelist of all dquots of all file systems */ - xfs_qm_freelist_init(&(xqm->qm_dqfreelist)); + INIT_LIST_HEAD(&xqm->qm_dqfrlist); + xqm->qm_dqfrlist_cnt = 0; + mutex_init(&xqm->qm_dqfrlist_lock); /* * dquot zone. we register our own low-memory callback. @@ -189,6 +192,7 @@ STATIC void xfs_qm_destroy( struct xfs_qm *xqm) { + struct xfs_dquot *dqp, *n; int hsize, i; ASSERT(xqm != NULL); @@ -204,7 +208,21 @@ xfs_qm_destroy( xqm->qm_usr_dqhtable = NULL; xqm->qm_grp_dqhtable = NULL; xqm->qm_dqhashmask = 0; - xfs_qm_freelist_destroy(&(xqm->qm_dqfreelist)); + + /* frlist cleanup */ + mutex_lock(&xqm->qm_dqfrlist_lock); + list_for_each_entry_safe(dqp, n, &xqm->qm_dqfrlist, q_freelist) { + xfs_dqlock(dqp); +#ifdef QUOTADEBUG + cmn_err(CE_DEBUG, "FREELIST destroy 0x%p", dqp); +#endif + list_del_init(&dqp->q_freelist); + xfs_Gqm->qm_dqfrlist_cnt--; + xfs_dqunlock(dqp); + xfs_qm_dqdestroy(dqp); + } + mutex_unlock(&xqm->qm_dqfrlist_lock); + mutex_destroy(&xqm->qm_dqfrlist_lock); #ifdef DEBUG mutex_destroy(&qcheck_lock); #endif @@ -256,7 +274,7 @@ STATIC void xfs_qm_rele_quotafs_ref( struct xfs_mount *mp) { - xfs_dquot_t *dqp, *nextdqp; + xfs_dquot_t *dqp, *n; ASSERT(xfs_Gqm); ASSERT(xfs_Gqm->qm_nrefs > 0); @@ -264,26 +282,24 @@ xfs_qm_rele_quotafs_ref( /* * Go thru the freelist and destroy all inactive dquots. */ - xfs_qm_freelist_lock(xfs_Gqm); + mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); - for (dqp = xfs_Gqm->qm_dqfreelist.qh_next; - dqp != (xfs_dquot_t *)&(xfs_Gqm->qm_dqfreelist); ) { + list_for_each_entry_safe(dqp, n, &xfs_Gqm->qm_dqfrlist, q_freelist) { xfs_dqlock(dqp); - nextdqp = dqp->dq_flnext; if (dqp->dq_flags & XFS_DQ_INACTIVE) { ASSERT(dqp->q_mount == NULL); ASSERT(! XFS_DQ_IS_DIRTY(dqp)); - ASSERT(dqp->HL_PREVP == NULL); - ASSERT(dqp->MPL_PREVP == NULL); - XQM_FREELIST_REMOVE(dqp); + ASSERT(list_empty(&dqp->q_hashlist)); + ASSERT(list_empty(&dqp->q_mplist)); + list_del_init(&dqp->q_freelist); + xfs_Gqm->qm_dqfrlist_cnt--; xfs_dqunlock(dqp); xfs_qm_dqdestroy(dqp); } else { xfs_dqunlock(dqp); } - dqp = nextdqp; } - xfs_qm_freelist_unlock(xfs_Gqm); + mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); /* * Destroy the entire XQM. If somebody mounts with quotaon, this'll @@ -305,7 +321,7 @@ xfs_qm_unmount( struct xfs_mount *mp) { if (mp->m_quotainfo) { - xfs_qm_dqpurge_all(mp, XFS_QMOPT_QUOTALL | XFS_QMOPT_UMOUNTING); + xfs_qm_dqpurge_all(mp, XFS_QMOPT_QUOTALL); xfs_qm_destroy_quotainfo(mp); } } @@ -449,20 +465,21 @@ xfs_qm_unmount_quotas( */ STATIC int xfs_qm_dqflush_all( - xfs_mount_t *mp, - int sync_mode) + struct xfs_mount *mp, + int sync_mode) { - int recl; - xfs_dquot_t *dqp; - int niters; - int error; + struct xfs_quotainfo *q = mp->m_quotainfo; + int recl; + struct xfs_dquot *dqp; + int niters; + int error; - if (mp->m_quotainfo == NULL) + if (!q) return 0; niters = 0; again: - xfs_qm_mplist_lock(mp); - FOREACH_DQUOT_IN_MP(dqp, mp) { + mutex_lock(&q->qi_dqlist_lock); + list_for_each_entry(dqp, &q->qi_dqlist, q_mplist) { xfs_dqlock(dqp); if (! XFS_DQ_IS_DIRTY(dqp)) { xfs_dqunlock(dqp); @@ -470,7 +487,7 @@ again: } /* XXX a sentinel would be better */ - recl = XFS_QI_MPLRECLAIMS(mp); + recl = q->qi_dqreclaims; if (!xfs_dqflock_nowait(dqp)) { /* * If we can't grab the flush lock then check @@ -485,21 +502,21 @@ again: * Let go of the mplist lock. We don't want to hold it * across a disk write. */ - xfs_qm_mplist_unlock(mp); + mutex_unlock(&q->qi_dqlist_lock); error = xfs_qm_dqflush(dqp, sync_mode); xfs_dqunlock(dqp); if (error) return error; - xfs_qm_mplist_lock(mp); - if (recl != XFS_QI_MPLRECLAIMS(mp)) { - xfs_qm_mplist_unlock(mp); + mutex_lock(&q->qi_dqlist_lock); + if (recl != q->qi_dqreclaims) { + mutex_unlock(&q->qi_dqlist_lock); /* XXX restart limit */ goto again; } } - xfs_qm_mplist_unlock(mp); + mutex_unlock(&q->qi_dqlist_lock); /* return ! busy */ return 0; } @@ -509,15 +526,15 @@ again: */ STATIC void xfs_qm_detach_gdquots( - xfs_mount_t *mp) + struct xfs_mount *mp) { - xfs_dquot_t *dqp, *gdqp; - int nrecl; + struct xfs_quotainfo *q = mp->m_quotainfo; + struct xfs_dquot *dqp, *gdqp; + int nrecl; again: - ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp)); - dqp = XFS_QI_MPLNEXT(mp); - while (dqp) { + ASSERT(mutex_is_locked(&q->qi_dqlist_lock)); + list_for_each_entry(dqp, &q->qi_dqlist, q_mplist) { xfs_dqlock(dqp); if ((gdqp = dqp->q_gdquot)) { xfs_dqlock(gdqp); @@ -530,15 +547,14 @@ xfs_qm_detach_gdquots( * Can't hold the mplist lock across a dqput. * XXXmust convert to marker based iterations here. */ - nrecl = XFS_QI_MPLRECLAIMS(mp); - xfs_qm_mplist_unlock(mp); + nrecl = q->qi_dqreclaims; + mutex_unlock(&q->qi_dqlist_lock); xfs_qm_dqput(gdqp); - xfs_qm_mplist_lock(mp); - if (nrecl != XFS_QI_MPLRECLAIMS(mp)) + mutex_lock(&q->qi_dqlist_lock); + if (nrecl != q->qi_dqreclaims) goto again; } - dqp = dqp->MPL_NEXT; } } @@ -550,23 +566,23 @@ xfs_qm_detach_gdquots( */ STATIC int xfs_qm_dqpurge_int( - xfs_mount_t *mp, - uint flags) /* QUOTAOFF/UMOUNTING/UQUOTA/PQUOTA/GQUOTA */ + struct xfs_mount *mp, + uint flags) { - xfs_dquot_t *dqp; - uint dqtype; - int nrecl; - xfs_dquot_t *nextdqp; - int nmisses; + struct xfs_quotainfo *q = mp->m_quotainfo; + struct xfs_dquot *dqp, *n; + uint dqtype; + int nrecl; + int nmisses; - if (mp->m_quotainfo == NULL) + if (!q) return 0; dqtype = (flags & XFS_QMOPT_UQUOTA) ? XFS_DQ_USER : 0; dqtype |= (flags & XFS_QMOPT_PQUOTA) ? XFS_DQ_PROJ : 0; dqtype |= (flags & XFS_QMOPT_GQUOTA) ? XFS_DQ_GROUP : 0; - xfs_qm_mplist_lock(mp); + mutex_lock(&q->qi_dqlist_lock); /* * In the first pass through all incore dquots of this filesystem, @@ -578,28 +594,25 @@ xfs_qm_dqpurge_int( again: nmisses = 0; - ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp)); + ASSERT(mutex_is_locked(&q->qi_dqlist_lock)); /* * Try to get rid of all of the unwanted dquots. The idea is to * get them off mplist and hashlist, but leave them on freelist. */ - dqp = XFS_QI_MPLNEXT(mp); - while (dqp) { + list_for_each_entry_safe(dqp, n, &q->qi_dqlist, q_mplist) { /* * It's OK to look at the type without taking dqlock here. * We're holding the mplist lock here, and that's needed for * a dqreclaim. */ - if ((dqp->dq_flags & dqtype) == 0) { - dqp = dqp->MPL_NEXT; + if ((dqp->dq_flags & dqtype) == 0) continue; - } if (!mutex_trylock(&dqp->q_hash->qh_lock)) { - nrecl = XFS_QI_MPLRECLAIMS(mp); - xfs_qm_mplist_unlock(mp); + nrecl = q->qi_dqreclaims; + mutex_unlock(&q->qi_dqlist_lock); mutex_lock(&dqp->q_hash->qh_lock); - xfs_qm_mplist_lock(mp); + mutex_lock(&q->qi_dqlist_lock); /* * XXXTheoretically, we can get into a very long @@ -607,7 +620,7 @@ xfs_qm_dqpurge_int( * No one can be adding dquots to the mplist at * this point, but somebody might be taking things off. */ - if (nrecl != XFS_QI_MPLRECLAIMS(mp)) { + if (nrecl != q->qi_dqreclaims) { mutex_unlock(&dqp->q_hash->qh_lock); goto again; } @@ -617,11 +630,9 @@ xfs_qm_dqpurge_int( * Take the dquot off the mplist and hashlist. It may remain on * freelist in INACTIVE state. */ - nextdqp = dqp->MPL_NEXT; nmisses += xfs_qm_dqpurge(dqp); - dqp = nextdqp; } - xfs_qm_mplist_unlock(mp); + mutex_unlock(&q->qi_dqlist_lock); return nmisses; } @@ -921,12 +932,13 @@ xfs_qm_dqdetach( int xfs_qm_sync( - xfs_mount_t *mp, - int flags) + struct xfs_mount *mp, + int flags) { - int recl, restarts; - xfs_dquot_t *dqp; - int error; + struct xfs_quotainfo *q = mp->m_quotainfo; + int recl, restarts; + struct xfs_dquot *dqp; + int error; if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp)) return 0; @@ -934,18 +946,19 @@ xfs_qm_sync( restarts = 0; again: - xfs_qm_mplist_lock(mp); + mutex_lock(&q->qi_dqlist_lock); /* * dqpurge_all() also takes the mplist lock and iterate thru all dquots * in quotaoff. However, if the QUOTA_ACTIVE bits are not cleared * when we have the mplist lock, we know that dquots will be consistent * as long as we have it locked. */ - if (! XFS_IS_QUOTA_ON(mp)) { - xfs_qm_mplist_unlock(mp); + if (!XFS_IS_QUOTA_ON(mp)) { + mutex_unlock(&q->qi_dqlist_lock); return 0; } - FOREACH_DQUOT_IN_MP(dqp, mp) { + ASSERT(mutex_is_locked(&q->qi_dqlist_lock)); + list_for_each_entry(dqp, &q->qi_dqlist, q_mplist) { /* * If this is vfs_sync calling, then skip the dquots that * don't 'seem' to be dirty. ie. don't acquire dqlock. @@ -969,7 +982,7 @@ xfs_qm_sync( } /* XXX a sentinel would be better */ - recl = XFS_QI_MPLRECLAIMS(mp); + recl = q->qi_dqreclaims; if (!xfs_dqflock_nowait(dqp)) { if (flags & SYNC_TRYLOCK) { xfs_dqunlock(dqp); @@ -989,7 +1002,7 @@ xfs_qm_sync( * Let go of the mplist lock. We don't want to hold it * across a disk write */ - xfs_qm_mplist_unlock(mp); + mutex_unlock(&q->qi_dqlist_lock); error = xfs_qm_dqflush(dqp, flags); xfs_dqunlock(dqp); if (error && XFS_FORCED_SHUTDOWN(mp)) @@ -997,17 +1010,17 @@ xfs_qm_sync( else if (error) return error; - xfs_qm_mplist_lock(mp); - if (recl != XFS_QI_MPLRECLAIMS(mp)) { + mutex_lock(&q->qi_dqlist_lock); + if (recl != q->qi_dqreclaims) { if (++restarts >= XFS_QM_SYNC_MAX_RESTARTS) break; - xfs_qm_mplist_unlock(mp); + mutex_unlock(&q->qi_dqlist_lock); goto again; } } - xfs_qm_mplist_unlock(mp); + mutex_unlock(&q->qi_dqlist_lock); return 0; } @@ -1052,8 +1065,9 @@ xfs_qm_init_quotainfo( return error; } - xfs_qm_list_init(&qinf->qi_dqlist, "mpdqlist", 0); - lockdep_set_class(&qinf->qi_dqlist.qh_lock, &xfs_quota_mplist_class); + INIT_LIST_HEAD(&qinf->qi_dqlist); + mutex_init(&qinf->qi_dqlist_lock); + lockdep_set_class(&qinf->qi_dqlist_lock, &xfs_quota_mplist_class); qinf->qi_dqreclaims = 0; @@ -1150,7 +1164,8 @@ xfs_qm_destroy_quotainfo( */ xfs_qm_rele_quotafs_ref(mp); - xfs_qm_list_destroy(&qi->qi_dqlist); + ASSERT(list_empty(&qi->qi_dqlist)); + mutex_destroy(&qi->qi_dqlist_lock); if (qi->qi_uquotaip) { IRELE(qi->qi_uquotaip); @@ -1177,7 +1192,7 @@ xfs_qm_list_init( int n) { mutex_init(&list->qh_lock); - list->qh_next = NULL; + INIT_LIST_HEAD(&list->qh_list); list->qh_version = 0; list->qh_nelems = 0; } @@ -1316,9 +1331,6 @@ xfs_qm_qino_alloc( */ spin_lock(&mp->m_sb_lock); if (flags & XFS_QMOPT_SBVERSION) { -#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) - unsigned oldv = mp->m_sb.sb_versionnum; -#endif ASSERT(!xfs_sb_version_hasquota(&mp->m_sb)); ASSERT((sbfields & (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | XFS_SB_QFLAGS)) == @@ -1331,11 +1343,6 @@ xfs_qm_qino_alloc( /* qflags will get updated _after_ quotacheck */ mp->m_sb.sb_qflags = 0; -#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) - cmn_err(CE_NOTE, - "Old superblock version %x, converting to %x.", - oldv, mp->m_sb.sb_versionnum); -#endif } if (flags & XFS_QMOPT_UQUOTA) mp->m_sb.sb_uquotino = (*ip)->i_ino; @@ -1371,10 +1378,10 @@ xfs_qm_reset_dqcounts( #ifdef DEBUG j = XFS_FSB_TO_B(mp, XFS_DQUOT_CLUSTER_SIZE_FSB); do_div(j, sizeof(xfs_dqblk_t)); - ASSERT(XFS_QM_DQPERBLK(mp) == j); + ASSERT(mp->m_quotainfo->qi_dqperchunk == j); #endif ddq = (xfs_disk_dquot_t *)XFS_BUF_PTR(bp); - for (j = 0; j < XFS_QM_DQPERBLK(mp); j++) { + for (j = 0; j < mp->m_quotainfo->qi_dqperchunk; j++) { /* * Do a sanity check, and if needed, repair the dqblk. Don't * output any warnings because it's perfectly possible to @@ -1429,7 +1436,7 @@ xfs_qm_dqiter_bufs( while (blkcnt--) { error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, XFS_FSB_TO_DADDR(mp, bno), - (int)XFS_QI_DQCHUNKLEN(mp), 0, &bp); + mp->m_quotainfo->qi_dqchunklen, 0, &bp); if (error) break; @@ -1439,7 +1446,7 @@ xfs_qm_dqiter_bufs( * goto the next block. */ bno++; - firstid += XFS_QM_DQPERBLK(mp); + firstid += mp->m_quotainfo->qi_dqperchunk; } return error; } @@ -1505,7 +1512,7 @@ xfs_qm_dqiterate( continue; firstid = (xfs_dqid_t) map[i].br_startoff * - XFS_QM_DQPERBLK(mp); + mp->m_quotainfo->qi_dqperchunk; /* * Do a read-ahead on the next extent. */ @@ -1516,7 +1523,7 @@ xfs_qm_dqiterate( while (rablkcnt--) { xfs_baread(mp->m_ddev_targp, XFS_FSB_TO_DADDR(mp, rablkno), - (int)XFS_QI_DQCHUNKLEN(mp)); + mp->m_quotainfo->qi_dqchunklen); rablkno++; } } @@ -1576,8 +1583,10 @@ xfs_qm_quotacheck_dqadjust( /* * Set default limits, adjust timers (since we changed usages) + * + * There are no timers for the default values set in the root dquot. */ - if (! XFS_IS_SUSER_DQUOT(dqp)) { + if (dqp->q_core.d_id) { xfs_qm_adjust_dqlimits(dqp->q_mount, &dqp->q_core); xfs_qm_adjust_dqtimers(dqp->q_mount, &dqp->q_core); } @@ -1747,14 +1756,14 @@ xfs_qm_quotacheck( lastino = 0; flags = 0; - ASSERT(XFS_QI_UQIP(mp) || XFS_QI_GQIP(mp)); + ASSERT(mp->m_quotainfo->qi_uquotaip || mp->m_quotainfo->qi_gquotaip); ASSERT(XFS_IS_QUOTA_RUNNING(mp)); /* * There should be no cached dquots. The (simplistic) quotacheck * algorithm doesn't like that. */ - ASSERT(XFS_QI_MPLNDQUOTS(mp) == 0); + ASSERT(list_empty(&mp->m_quotainfo->qi_dqlist)); cmn_err(CE_NOTE, "XFS quotacheck %s: Please wait.", mp->m_fsname); @@ -1763,15 +1772,19 @@ xfs_qm_quotacheck( * their counters to zero. We need a clean slate. * We don't log our changes till later. */ - if ((uip = XFS_QI_UQIP(mp))) { - if ((error = xfs_qm_dqiterate(mp, uip, XFS_QMOPT_UQUOTA))) + uip = mp->m_quotainfo->qi_uquotaip; + if (uip) { + error = xfs_qm_dqiterate(mp, uip, XFS_QMOPT_UQUOTA); + if (error) goto error_return; flags |= XFS_UQUOTA_CHKD; } - if ((gip = XFS_QI_GQIP(mp))) { - if ((error = xfs_qm_dqiterate(mp, gip, XFS_IS_GQUOTA_ON(mp) ? - XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA))) + gip = mp->m_quotainfo->qi_gquotaip; + if (gip) { + error = xfs_qm_dqiterate(mp, gip, XFS_IS_GQUOTA_ON(mp) ? + XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA); + if (error) goto error_return; flags |= XFS_OQUOTA_CHKD; } @@ -1804,7 +1817,7 @@ xfs_qm_quotacheck( * at this point (because we intentionally didn't in dqget_noattach). */ if (error) { - xfs_qm_dqpurge_all(mp, XFS_QMOPT_QUOTALL | XFS_QMOPT_QUOTAOFF); + xfs_qm_dqpurge_all(mp, XFS_QMOPT_QUOTALL); goto error_return; } @@ -1825,7 +1838,7 @@ xfs_qm_quotacheck( mp->m_qflags &= ~(XFS_OQUOTA_CHKD | XFS_UQUOTA_CHKD); mp->m_qflags |= flags; - XQM_LIST_PRINT(&(XFS_QI_MPL_LIST(mp)), MPL_NEXT, "++++ Mp list +++"); + xfs_qm_dquot_list_print(mp); error_return: if (error) { @@ -1920,59 +1933,53 @@ xfs_qm_init_quotainos( } } - XFS_QI_UQIP(mp) = uip; - XFS_QI_GQIP(mp) = gip; + mp->m_quotainfo->qi_uquotaip = uip; + mp->m_quotainfo->qi_gquotaip = gip; return 0; } + /* - * Traverse the freelist of dquots and attempt to reclaim a maximum of - * 'howmany' dquots. This operation races with dqlookup(), and attempts to - * favor the lookup function ... - * XXXsup merge this with qm_reclaim_one(). + * Just pop the least recently used dquot off the freelist and + * recycle it. The returned dquot is locked. */ -STATIC int -xfs_qm_shake_freelist( - int howmany) +STATIC xfs_dquot_t * +xfs_qm_dqreclaim_one(void) { - int nreclaimed; - xfs_dqhash_t *hash; - xfs_dquot_t *dqp, *nextdqp; + xfs_dquot_t *dqpout; + xfs_dquot_t *dqp; int restarts; - int nflushes; - - if (howmany <= 0) - return 0; - nreclaimed = 0; restarts = 0; - nflushes = 0; + dqpout = NULL; -#ifdef QUOTADEBUG - cmn_err(CE_DEBUG, "Shake free 0x%x", howmany); -#endif - /* lock order is : hashchainlock, freelistlock, mplistlock */ - tryagain: - xfs_qm_freelist_lock(xfs_Gqm); + /* lockorder: hashchainlock, freelistlock, mplistlock, dqlock, dqflock */ +startagain: + mutex_lock(&xfs_Gqm->qm_dqfrlist_lock); - for (dqp = xfs_Gqm->qm_dqfreelist.qh_next; - ((dqp != (xfs_dquot_t *) &xfs_Gqm->qm_dqfreelist) && - nreclaimed < howmany); ) { + list_for_each_entry(dqp, &xfs_Gqm->qm_dqfrlist, q_freelist) { + struct xfs_mount *mp = dqp->q_mount; xfs_dqlock(dqp); /* * We are racing with dqlookup here. Naturally we don't - * want to reclaim a dquot that lookup wants. + * want to reclaim a dquot that lookup wants. We release the + * freelist lock and start over, so that lookup will grab + * both the dquot and the freelistlock. */ if (dqp->dq_flags & XFS_DQ_WANT) { + ASSERT(! (dqp->dq_flags & XFS_DQ_INACTIVE)); + + trace_xfs_dqreclaim_want(dqp); + xfs_dqunlock(dqp); - xfs_qm_freelist_unlock(xfs_Gqm); + mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) - return nreclaimed; + return NULL; XQM_STATS_INC(xqmstats.xs_qm_dqwants); - goto tryagain; + goto startagain; } /* @@ -1981,23 +1988,27 @@ xfs_qm_shake_freelist( * life easier. */ if (dqp->dq_flags & XFS_DQ_INACTIVE) { - ASSERT(dqp->q_mount == NULL); + ASSERT(mp == NULL); ASSERT(! XFS_DQ_IS_DIRTY(dqp)); - ASSERT(dqp->HL_PREVP == NULL); - ASSERT(dqp->MPL_PREVP == NULL); + ASSERT(list_empty(&dqp->q_hashlist)); + ASSERT(list_empty(&dqp->q_mplist)); + list_del_init(&dqp->q_freelist); + xfs_Gqm->qm_dqfrlist_cnt--; + xfs_dqunlock(dqp); + dqpout = dqp; XQM_STATS_INC(xqmstats.xs_qm_dqinact_reclaims); - nextdqp = dqp->dq_flnext; - goto off_freelist; + break; } - ASSERT(dqp->MPL_PREVP); + ASSERT(dqp->q_hash); + ASSERT(!list_empty(&dqp->q_mplist)); + /* * Try to grab the flush lock. If this dquot is in the process of * getting flushed to disk, we don't want to reclaim it. */ if (!xfs_dqflock_nowait(dqp)) { xfs_dqunlock(dqp); - dqp = dqp->dq_flnext; continue; } @@ -2010,21 +2021,21 @@ xfs_qm_shake_freelist( if (XFS_DQ_IS_DIRTY(dqp)) { int error; - trace_xfs_dqshake_dirty(dqp); + trace_xfs_dqreclaim_dirty(dqp); /* * We flush it delayed write, so don't bother - * releasing the mplock. + * releasing the freelist lock. */ error = xfs_qm_dqflush(dqp, 0); if (error) { - xfs_fs_cmn_err(CE_WARN, dqp->q_mount, - "xfs_qm_dqflush_all: dquot %p flush failed", dqp); + xfs_fs_cmn_err(CE_WARN, mp, + "xfs_qm_dqreclaim: dquot %p flush failed", dqp); } xfs_dqunlock(dqp); /* dqflush unlocks dqflock */ - dqp = dqp->dq_flnext; continue; } + /* * We're trying to get the hashlock out of order. This races * with dqlookup; so, we giveup and goto the next dquot if @@ -2033,56 +2044,74 @@ xfs_qm_shake_freelist( * waiting for the freelist lock. */ if (!mutex_trylock(&dqp->q_hash->qh_lock)) { - xfs_dqfunlock(dqp); - xfs_dqunlock(dqp); - dqp = dqp->dq_flnext; - continue; + restarts++; + goto dqfunlock; } + /* * This races with dquot allocation code as well as dqflush_all * and reclaim code. So, if we failed to grab the mplist lock, * giveup everything and start over. */ - hash = dqp->q_hash; - ASSERT(hash); - if (! xfs_qm_mplist_nowait(dqp->q_mount)) { - /* XXX put a sentinel so that we can come back here */ + if (!mutex_trylock(&mp->m_quotainfo->qi_dqlist_lock)) { + restarts++; + mutex_unlock(&dqp->q_hash->qh_lock); xfs_dqfunlock(dqp); xfs_dqunlock(dqp); - mutex_unlock(&hash->qh_lock); - xfs_qm_freelist_unlock(xfs_Gqm); - if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) - return nreclaimed; - goto tryagain; + mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); + if (restarts++ >= XFS_QM_RECLAIM_MAX_RESTARTS) + return NULL; + goto startagain; } - trace_xfs_dqshake_unlink(dqp); - -#ifdef QUOTADEBUG - cmn_err(CE_DEBUG, "Shake 0x%p, ID 0x%x\n", - dqp, be32_to_cpu(dqp->q_core.d_id)); -#endif ASSERT(dqp->q_nrefs == 0); - nextdqp = dqp->dq_flnext; - XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(dqp->q_mount)), dqp); - XQM_HASHLIST_REMOVE(hash, dqp); + list_del_init(&dqp->q_mplist); + mp->m_quotainfo->qi_dquots--; + mp->m_quotainfo->qi_dqreclaims++; + list_del_init(&dqp->q_hashlist); + dqp->q_hash->qh_version++; + list_del_init(&dqp->q_freelist); + xfs_Gqm->qm_dqfrlist_cnt--; + dqpout = dqp; + mutex_unlock(&mp->m_quotainfo->qi_dqlist_lock); + mutex_unlock(&dqp->q_hash->qh_lock); +dqfunlock: xfs_dqfunlock(dqp); - xfs_qm_mplist_unlock(dqp->q_mount); - mutex_unlock(&hash->qh_lock); - - off_freelist: - XQM_FREELIST_REMOVE(dqp); xfs_dqunlock(dqp); - nreclaimed++; - XQM_STATS_INC(xqmstats.xs_qm_dqshake_reclaims); + if (dqpout) + break; + if (restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) + return NULL; + } + mutex_unlock(&xfs_Gqm->qm_dqfrlist_lock); + return dqpout; +} + +/* + * Traverse the freelist of dquots and attempt to reclaim a maximum of + * 'howmany' dquots. This operation races with dqlookup(), and attempts to + * favor the lookup function ... + */ +STATIC int +xfs_qm_shake_freelist( + int howmany) +{ + int nreclaimed = 0; + xfs_dquot_t *dqp; + + if (howmany <= 0) + return 0; + + while (nreclaimed < howmany) { + dqp = xfs_qm_dqreclaim_one(); + if (!dqp) + return nreclaimed; xfs_qm_dqdestroy(dqp); - dqp = nextdqp; + nreclaimed++; } - xfs_qm_freelist_unlock(xfs_Gqm); return nreclaimed; } - /* * The kmem_shake interface is invoked when memory is running low. */ @@ -2097,7 +2126,7 @@ xfs_qm_shake(int nr_to_scan, gfp_t gfp_mask) if (!xfs_Gqm) return 0; - nfree = xfs_Gqm->qm_dqfreelist.qh_nelems; /* free dquots */ + nfree = xfs_Gqm->qm_dqfrlist_cnt; /* free dquots */ /* incore dquots in all f/s's */ ndqused = atomic_read(&xfs_Gqm->qm_totaldquots) - nfree; @@ -2113,131 +2142,6 @@ xfs_qm_shake(int nr_to_scan, gfp_t gfp_mask) } -/* - * Just pop the least recently used dquot off the freelist and - * recycle it. The returned dquot is locked. - */ -STATIC xfs_dquot_t * -xfs_qm_dqreclaim_one(void) -{ - xfs_dquot_t *dqpout; - xfs_dquot_t *dqp; - int restarts; - int nflushes; - - restarts = 0; - dqpout = NULL; - nflushes = 0; - - /* lockorder: hashchainlock, freelistlock, mplistlock, dqlock, dqflock */ - startagain: - xfs_qm_freelist_lock(xfs_Gqm); - - FOREACH_DQUOT_IN_FREELIST(dqp, &(xfs_Gqm->qm_dqfreelist)) { - xfs_dqlock(dqp); - - /* - * We are racing with dqlookup here. Naturally we don't - * want to reclaim a dquot that lookup wants. We release the - * freelist lock and start over, so that lookup will grab - * both the dquot and the freelistlock. - */ - if (dqp->dq_flags & XFS_DQ_WANT) { - ASSERT(! (dqp->dq_flags & XFS_DQ_INACTIVE)); - - trace_xfs_dqreclaim_want(dqp); - - xfs_dqunlock(dqp); - xfs_qm_freelist_unlock(xfs_Gqm); - if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) - return NULL; - XQM_STATS_INC(xqmstats.xs_qm_dqwants); - goto startagain; - } - - /* - * If the dquot is inactive, we are assured that it is - * not on the mplist or the hashlist, and that makes our - * life easier. - */ - if (dqp->dq_flags & XFS_DQ_INACTIVE) { - ASSERT(dqp->q_mount == NULL); - ASSERT(! XFS_DQ_IS_DIRTY(dqp)); - ASSERT(dqp->HL_PREVP == NULL); - ASSERT(dqp->MPL_PREVP == NULL); - XQM_FREELIST_REMOVE(dqp); - xfs_dqunlock(dqp); - dqpout = dqp; - XQM_STATS_INC(xqmstats.xs_qm_dqinact_reclaims); - break; - } - - ASSERT(dqp->q_hash); - ASSERT(dqp->MPL_PREVP); - - /* - * Try to grab the flush lock. If this dquot is in the process of - * getting flushed to disk, we don't want to reclaim it. - */ - if (!xfs_dqflock_nowait(dqp)) { - xfs_dqunlock(dqp); - continue; - } - - /* - * We have the flush lock so we know that this is not in the - * process of being flushed. So, if this is dirty, flush it - * DELWRI so that we don't get a freelist infested with - * dirty dquots. - */ - if (XFS_DQ_IS_DIRTY(dqp)) { - int error; - - trace_xfs_dqreclaim_dirty(dqp); - - /* - * We flush it delayed write, so don't bother - * releasing the freelist lock. - */ - error = xfs_qm_dqflush(dqp, 0); - if (error) { - xfs_fs_cmn_err(CE_WARN, dqp->q_mount, - "xfs_qm_dqreclaim: dquot %p flush failed", dqp); - } - xfs_dqunlock(dqp); /* dqflush unlocks dqflock */ - continue; - } - - if (! xfs_qm_mplist_nowait(dqp->q_mount)) { - xfs_dqfunlock(dqp); - xfs_dqunlock(dqp); - continue; - } - - if (!mutex_trylock(&dqp->q_hash->qh_lock)) - goto mplistunlock; - - trace_xfs_dqreclaim_unlink(dqp); - - ASSERT(dqp->q_nrefs == 0); - XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(dqp->q_mount)), dqp); - XQM_HASHLIST_REMOVE(dqp->q_hash, dqp); - XQM_FREELIST_REMOVE(dqp); - dqpout = dqp; - mutex_unlock(&dqp->q_hash->qh_lock); - mplistunlock: - xfs_qm_mplist_unlock(dqp->q_mount); - xfs_dqfunlock(dqp); - xfs_dqunlock(dqp); - if (dqpout) - break; - } - - xfs_qm_freelist_unlock(xfs_Gqm); - return dqpout; -} - - /*------------------------------------------------------------------*/ /* @@ -2662,66 +2566,3 @@ xfs_qm_vop_create_dqattach( } } -/* ------------- list stuff -----------------*/ -STATIC void -xfs_qm_freelist_init(xfs_frlist_t *ql) -{ - ql->qh_next = ql->qh_prev = (xfs_dquot_t *) ql; - mutex_init(&ql->qh_lock); - ql->qh_version = 0; - ql->qh_nelems = 0; -} - -STATIC void -xfs_qm_freelist_destroy(xfs_frlist_t *ql) -{ - xfs_dquot_t *dqp, *nextdqp; - - mutex_lock(&ql->qh_lock); - for (dqp = ql->qh_next; - dqp != (xfs_dquot_t *)ql; ) { - xfs_dqlock(dqp); - nextdqp = dqp->dq_flnext; -#ifdef QUOTADEBUG - cmn_err(CE_DEBUG, "FREELIST destroy 0x%p", dqp); -#endif - XQM_FREELIST_REMOVE(dqp); - xfs_dqunlock(dqp); - xfs_qm_dqdestroy(dqp); - dqp = nextdqp; - } - mutex_unlock(&ql->qh_lock); - mutex_destroy(&ql->qh_lock); - - ASSERT(ql->qh_nelems == 0); -} - -STATIC void -xfs_qm_freelist_insert(xfs_frlist_t *ql, xfs_dquot_t *dq) -{ - dq->dq_flnext = ql->qh_next; - dq->dq_flprev = (xfs_dquot_t *)ql; - ql->qh_next = dq; - dq->dq_flnext->dq_flprev = dq; - xfs_Gqm->qm_dqfreelist.qh_nelems++; - xfs_Gqm->qm_dqfreelist.qh_version++; -} - -void -xfs_qm_freelist_unlink(xfs_dquot_t *dq) -{ - xfs_dquot_t *next = dq->dq_flnext; - xfs_dquot_t *prev = dq->dq_flprev; - - next->dq_flprev = prev; - prev->dq_flnext = next; - dq->dq_flnext = dq->dq_flprev = dq; - xfs_Gqm->qm_dqfreelist.qh_nelems--; - xfs_Gqm->qm_dqfreelist.qh_version++; -} - -void -xfs_qm_freelist_append(xfs_frlist_t *ql, xfs_dquot_t *dq) -{ - xfs_qm_freelist_insert((xfs_frlist_t *)ql->qh_prev, dq); -} diff --git a/fs/xfs/quota/xfs_qm.h b/fs/xfs/quota/xfs_qm.h index 495564b8af3..c9446f1c726 100644 --- a/fs/xfs/quota/xfs_qm.h +++ b/fs/xfs/quota/xfs_qm.h @@ -72,17 +72,6 @@ extern kmem_zone_t *qm_dqtrxzone; #define XFS_QM_MAX_DQCLUSTER_LOGSZ 3 typedef xfs_dqhash_t xfs_dqlist_t; -/* - * The freelist head. The first two fields match the first two in the - * xfs_dquot_t structure (in xfs_dqmarker_t) - */ -typedef struct xfs_frlist { - struct xfs_dquot *qh_next; - struct xfs_dquot *qh_prev; - struct mutex qh_lock; - uint qh_version; - uint qh_nelems; -} xfs_frlist_t; /* * Quota Manager (global) structure. Lives only in core. @@ -91,7 +80,9 @@ typedef struct xfs_qm { xfs_dqlist_t *qm_usr_dqhtable;/* udquot hash table */ xfs_dqlist_t *qm_grp_dqhtable;/* gdquot hash table */ uint qm_dqhashmask; /* # buckets in dq hashtab - 1 */ - xfs_frlist_t qm_dqfreelist; /* freelist of dquots */ + struct list_head qm_dqfrlist; /* freelist of dquots */ + struct mutex qm_dqfrlist_lock; + int qm_dqfrlist_cnt; atomic_t qm_totaldquots; /* total incore dquots */ uint qm_nrefs; /* file systems with quota on */ int qm_dqfree_ratio;/* ratio of free to inuse dquots */ @@ -106,7 +97,9 @@ typedef struct xfs_qm { typedef struct xfs_quotainfo { xfs_inode_t *qi_uquotaip; /* user quota inode */ xfs_inode_t *qi_gquotaip; /* group quota inode */ - xfs_dqlist_t qi_dqlist; /* all dquots in filesys */ + struct list_head qi_dqlist; /* all dquots in filesys */ + struct mutex qi_dqlist_lock; + int qi_dquots; int qi_dqreclaims; /* a change here indicates a removal in the dqlist */ time_t qi_btimelimit; /* limit for blks timer */ @@ -175,10 +168,6 @@ extern int xfs_qm_scall_getqstat(xfs_mount_t *, fs_quota_stat_t *); extern int xfs_qm_scall_quotaon(xfs_mount_t *, uint); extern int xfs_qm_scall_quotaoff(xfs_mount_t *, uint); -/* list stuff */ -extern void xfs_qm_freelist_append(xfs_frlist_t *, xfs_dquot_t *); -extern void xfs_qm_freelist_unlink(xfs_dquot_t *); - #ifdef DEBUG extern int xfs_qm_internalqcheck(xfs_mount_t *); #else diff --git a/fs/xfs/quota/xfs_qm_stats.c b/fs/xfs/quota/xfs_qm_stats.c index 83e7ea3e25f..3d1fc79532e 100644 --- a/fs/xfs/quota/xfs_qm_stats.c +++ b/fs/xfs/quota/xfs_qm_stats.c @@ -55,7 +55,7 @@ static int xqm_proc_show(struct seq_file *m, void *v) ndquot, xfs_Gqm? atomic_read(&xfs_Gqm->qm_totaldquots) : 0, xfs_Gqm? xfs_Gqm->qm_dqfree_ratio : 0, - xfs_Gqm? xfs_Gqm->qm_dqfreelist.qh_nelems : 0); + xfs_Gqm? xfs_Gqm->qm_dqfrlist_cnt : 0); return 0; } diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index 50bee07d6b0..26fa43140f2 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -79,6 +79,7 @@ xfs_qm_scall_quotaoff( xfs_mount_t *mp, uint flags) { + struct xfs_quotainfo *q = mp->m_quotainfo; uint dqtype; int error; uint inactivate_flags; @@ -102,11 +103,8 @@ xfs_qm_scall_quotaoff( * critical thing. * If quotaoff, then we must be dealing with the root filesystem. */ - ASSERT(mp->m_quotainfo); - if (mp->m_quotainfo) - mutex_lock(&(XFS_QI_QOFFLOCK(mp))); - - ASSERT(mp->m_quotainfo); + ASSERT(q); + mutex_lock(&q->qi_quotaofflock); /* * If we're just turning off quota enforcement, change mp and go. @@ -117,7 +115,7 @@ xfs_qm_scall_quotaoff( spin_lock(&mp->m_sb_lock); mp->m_sb.sb_qflags = mp->m_qflags; spin_unlock(&mp->m_sb_lock); - mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); + mutex_unlock(&q->qi_quotaofflock); /* XXX what to do if error ? Revert back to old vals incore ? */ error = xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS); @@ -150,10 +148,8 @@ xfs_qm_scall_quotaoff( * Nothing to do? Don't complain. This happens when we're just * turning off quota enforcement. */ - if ((mp->m_qflags & flags) == 0) { - mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); - return (0); - } + if ((mp->m_qflags & flags) == 0) + goto out_unlock; /* * Write the LI_QUOTAOFF log record, and do SB changes atomically, @@ -162,7 +158,7 @@ xfs_qm_scall_quotaoff( */ error = xfs_qm_log_quotaoff(mp, &qoffstart, flags); if (error) - goto out_error; + goto out_unlock; /* * Next we clear the XFS_MOUNT_*DQ_ACTIVE bit(s) in the mount struct @@ -204,7 +200,7 @@ xfs_qm_scall_quotaoff( * So, if we couldn't purge all the dquots from the filesystem, * we can't get rid of the incore data structures. */ - while ((nculprits = xfs_qm_dqpurge_all(mp, dqtype|XFS_QMOPT_QUOTAOFF))) + while ((nculprits = xfs_qm_dqpurge_all(mp, dqtype))) delay(10 * nculprits); /* @@ -222,7 +218,7 @@ xfs_qm_scall_quotaoff( if (error) { /* We're screwed now. Shutdown is the only option. */ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); - goto out_error; + goto out_unlock; } /* @@ -230,27 +226,26 @@ xfs_qm_scall_quotaoff( */ if (((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET1) || ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET2)) { - mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); + mutex_unlock(&q->qi_quotaofflock); xfs_qm_destroy_quotainfo(mp); return (0); } /* - * Release our quotainode references, and vn_purge them, - * if we don't need them anymore. + * Release our quotainode references if we don't need them anymore. */ - if ((dqtype & XFS_QMOPT_UQUOTA) && XFS_QI_UQIP(mp)) { - IRELE(XFS_QI_UQIP(mp)); - XFS_QI_UQIP(mp) = NULL; + if ((dqtype & XFS_QMOPT_UQUOTA) && q->qi_uquotaip) { + IRELE(q->qi_uquotaip); + q->qi_uquotaip = NULL; } - if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && XFS_QI_GQIP(mp)) { - IRELE(XFS_QI_GQIP(mp)); - XFS_QI_GQIP(mp) = NULL; + if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && q->qi_gquotaip) { + IRELE(q->qi_gquotaip); + q->qi_gquotaip = NULL; } -out_error: - mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); - return (error); +out_unlock: + mutex_unlock(&q->qi_quotaofflock); + return error; } int @@ -379,9 +374,9 @@ xfs_qm_scall_quotaon( /* * Switch on quota enforcement in core. */ - mutex_lock(&(XFS_QI_QOFFLOCK(mp))); + mutex_lock(&mp->m_quotainfo->qi_quotaofflock); mp->m_qflags |= (flags & XFS_ALL_QUOTA_ENFD); - mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); + mutex_unlock(&mp->m_quotainfo->qi_quotaofflock); return (0); } @@ -392,11 +387,12 @@ xfs_qm_scall_quotaon( */ int xfs_qm_scall_getqstat( - xfs_mount_t *mp, - fs_quota_stat_t *out) + struct xfs_mount *mp, + struct fs_quota_stat *out) { - xfs_inode_t *uip, *gip; - boolean_t tempuqip, tempgqip; + struct xfs_quotainfo *q = mp->m_quotainfo; + struct xfs_inode *uip, *gip; + boolean_t tempuqip, tempgqip; uip = gip = NULL; tempuqip = tempgqip = B_FALSE; @@ -415,9 +411,9 @@ xfs_qm_scall_getqstat( out->qs_uquota.qfs_ino = mp->m_sb.sb_uquotino; out->qs_gquota.qfs_ino = mp->m_sb.sb_gquotino; - if (mp->m_quotainfo) { - uip = mp->m_quotainfo->qi_uquotaip; - gip = mp->m_quotainfo->qi_gquotaip; + if (q) { + uip = q->qi_uquotaip; + gip = q->qi_gquotaip; } if (!uip && mp->m_sb.sb_uquotino != NULLFSINO) { if (xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, @@ -441,15 +437,15 @@ xfs_qm_scall_getqstat( if (tempgqip) IRELE(gip); } - if (mp->m_quotainfo) { - out->qs_incoredqs = XFS_QI_MPLNDQUOTS(mp); - out->qs_btimelimit = XFS_QI_BTIMELIMIT(mp); - out->qs_itimelimit = XFS_QI_ITIMELIMIT(mp); - out->qs_rtbtimelimit = XFS_QI_RTBTIMELIMIT(mp); - out->qs_bwarnlimit = XFS_QI_BWARNLIMIT(mp); - out->qs_iwarnlimit = XFS_QI_IWARNLIMIT(mp); + if (q) { + out->qs_incoredqs = q->qi_dquots; + out->qs_btimelimit = q->qi_btimelimit; + out->qs_itimelimit = q->qi_itimelimit; + out->qs_rtbtimelimit = q->qi_rtbtimelimit; + out->qs_bwarnlimit = q->qi_bwarnlimit; + out->qs_iwarnlimit = q->qi_iwarnlimit; } - return (0); + return 0; } /* @@ -462,6 +458,7 @@ xfs_qm_scall_setqlim( uint type, fs_disk_quota_t *newlim) { + struct xfs_quotainfo *q = mp->m_quotainfo; xfs_disk_dquot_t *ddq; xfs_dquot_t *dqp; xfs_trans_t *tp; @@ -485,7 +482,7 @@ xfs_qm_scall_setqlim( * a quotaoff from happening). (XXXThis doesn't currently happen * because we take the vfslock before calling xfs_qm_sysent). */ - mutex_lock(&(XFS_QI_QOFFLOCK(mp))); + mutex_lock(&q->qi_quotaofflock); /* * Get the dquot (locked), and join it to the transaction. @@ -493,9 +490,8 @@ xfs_qm_scall_setqlim( */ if ((error = xfs_qm_dqget(mp, NULL, id, type, XFS_QMOPT_DQALLOC, &dqp))) { xfs_trans_cancel(tp, XFS_TRANS_ABORT); - mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); ASSERT(error != ENOENT); - return (error); + goto out_unlock; } xfs_trans_dqjoin(tp, dqp); ddq = &dqp->q_core; @@ -513,8 +509,8 @@ xfs_qm_scall_setqlim( ddq->d_blk_hardlimit = cpu_to_be64(hard); ddq->d_blk_softlimit = cpu_to_be64(soft); if (id == 0) { - mp->m_quotainfo->qi_bhardlimit = hard; - mp->m_quotainfo->qi_bsoftlimit = soft; + q->qi_bhardlimit = hard; + q->qi_bsoftlimit = soft; } } else { qdprintk("blkhard %Ld < blksoft %Ld\n", hard, soft); @@ -529,8 +525,8 @@ xfs_qm_scall_setqlim( ddq->d_rtb_hardlimit = cpu_to_be64(hard); ddq->d_rtb_softlimit = cpu_to_be64(soft); if (id == 0) { - mp->m_quotainfo->qi_rtbhardlimit = hard; - mp->m_quotainfo->qi_rtbsoftlimit = soft; + q->qi_rtbhardlimit = hard; + q->qi_rtbsoftlimit = soft; } } else { qdprintk("rtbhard %Ld < rtbsoft %Ld\n", hard, soft); @@ -546,8 +542,8 @@ xfs_qm_scall_setqlim( ddq->d_ino_hardlimit = cpu_to_be64(hard); ddq->d_ino_softlimit = cpu_to_be64(soft); if (id == 0) { - mp->m_quotainfo->qi_ihardlimit = hard; - mp->m_quotainfo->qi_isoftlimit = soft; + q->qi_ihardlimit = hard; + q->qi_isoftlimit = soft; } } else { qdprintk("ihard %Ld < isoft %Ld\n", hard, soft); @@ -572,23 +568,23 @@ xfs_qm_scall_setqlim( * for warnings. */ if (newlim->d_fieldmask & FS_DQ_BTIMER) { - mp->m_quotainfo->qi_btimelimit = newlim->d_btimer; + q->qi_btimelimit = newlim->d_btimer; ddq->d_btimer = cpu_to_be32(newlim->d_btimer); } if (newlim->d_fieldmask & FS_DQ_ITIMER) { - mp->m_quotainfo->qi_itimelimit = newlim->d_itimer; + q->qi_itimelimit = newlim->d_itimer; ddq->d_itimer = cpu_to_be32(newlim->d_itimer); } if (newlim->d_fieldmask & FS_DQ_RTBTIMER) { - mp->m_quotainfo->qi_rtbtimelimit = newlim->d_rtbtimer; + q->qi_rtbtimelimit = newlim->d_rtbtimer; ddq->d_rtbtimer = cpu_to_be32(newlim->d_rtbtimer); } if (newlim->d_fieldmask & FS_DQ_BWARNS) - mp->m_quotainfo->qi_bwarnlimit = newlim->d_bwarns; + q->qi_bwarnlimit = newlim->d_bwarns; if (newlim->d_fieldmask & FS_DQ_IWARNS) - mp->m_quotainfo->qi_iwarnlimit = newlim->d_iwarns; + q->qi_iwarnlimit = newlim->d_iwarns; if (newlim->d_fieldmask & FS_DQ_RTBWARNS) - mp->m_quotainfo->qi_rtbwarnlimit = newlim->d_rtbwarns; + q->qi_rtbwarnlimit = newlim->d_rtbwarns; } else { /* * If the user is now over quota, start the timelimit. @@ -605,8 +601,9 @@ xfs_qm_scall_setqlim( error = xfs_trans_commit(tp, 0); xfs_qm_dqprint(dqp); xfs_qm_dqrele(dqp); - mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); + out_unlock: + mutex_unlock(&q->qi_quotaofflock); return error; } @@ -853,7 +850,8 @@ xfs_dqrele_inode( int error; /* skip quota inodes */ - if (ip == XFS_QI_UQIP(ip->i_mount) || ip == XFS_QI_GQIP(ip->i_mount)) { + if (ip == ip->i_mount->m_quotainfo->qi_uquotaip || + ip == ip->i_mount->m_quotainfo->qi_gquotaip) { ASSERT(ip->i_udquot == NULL); ASSERT(ip->i_gdquot == NULL); read_unlock(&pag->pag_ici_lock); @@ -931,7 +929,8 @@ struct mutex qcheck_lock; } typedef struct dqtest { - xfs_dqmarker_t q_lists; + uint dq_flags; /* various flags (XFS_DQ_*) */ + struct list_head q_hashlist; xfs_dqhash_t *q_hash; /* the hashchain header */ xfs_mount_t *q_mount; /* filesystem this relates to */ xfs_dqid_t d_id; /* user id or group id */ @@ -942,14 +941,9 @@ typedef struct dqtest { STATIC void xfs_qm_hashinsert(xfs_dqhash_t *h, xfs_dqtest_t *dqp) { - xfs_dquot_t *d; - if (((d) = (h)->qh_next)) - (d)->HL_PREVP = &((dqp)->HL_NEXT); - (dqp)->HL_NEXT = d; - (dqp)->HL_PREVP = &((h)->qh_next); - (h)->qh_next = (xfs_dquot_t *)dqp; - (h)->qh_version++; - (h)->qh_nelems++; + list_add(&dqp->q_hashlist, &h->qh_list); + h->qh_version++; + h->qh_nelems++; } STATIC void xfs_qm_dqtest_print( @@ -1061,9 +1055,7 @@ xfs_qm_internalqcheck_dqget( xfs_dqhash_t *h; h = DQTEST_HASH(mp, id, type); - for (d = (xfs_dqtest_t *) h->qh_next; d != NULL; - d = (xfs_dqtest_t *) d->HL_NEXT) { - /* DQTEST_LIST_PRINT(h, HL_NEXT, "@@@@@ dqtestlist @@@@@"); */ + list_for_each_entry(d, &h->qh_list, q_hashlist) { if (d->d_id == id && mp == d->q_mount) { *O_dq = d; return (0); @@ -1074,6 +1066,7 @@ xfs_qm_internalqcheck_dqget( d->d_id = id; d->q_mount = mp; d->q_hash = h; + INIT_LIST_HEAD(&d->q_hashlist); xfs_qm_hashinsert(h, d); *O_dq = d; return (0); @@ -1180,8 +1173,6 @@ xfs_qm_internalqcheck( xfs_ino_t lastino; int done, count; int i; - xfs_dqtest_t *d, *e; - xfs_dqhash_t *h1; int error; lastino = 0; @@ -1221,19 +1212,18 @@ xfs_qm_internalqcheck( } cmn_err(CE_DEBUG, "Checking results against system dquots"); for (i = 0; i < qmtest_hashmask; i++) { - h1 = &qmtest_udqtab[i]; - for (d = (xfs_dqtest_t *) h1->qh_next; d != NULL; ) { + xfs_dqtest_t *d, *n; + xfs_dqhash_t *h; + + h = &qmtest_udqtab[i]; + list_for_each_entry_safe(d, n, &h->qh_list, q_hashlist) { xfs_dqtest_cmp(d); - e = (xfs_dqtest_t *) d->HL_NEXT; kmem_free(d); - d = e; } - h1 = &qmtest_gdqtab[i]; - for (d = (xfs_dqtest_t *) h1->qh_next; d != NULL; ) { + h = &qmtest_gdqtab[i]; + list_for_each_entry_safe(d, n, &h->qh_list, q_hashlist) { xfs_dqtest_cmp(d); - e = (xfs_dqtest_t *) d->HL_NEXT; kmem_free(d); - d = e; } } diff --git a/fs/xfs/quota/xfs_quota_priv.h b/fs/xfs/quota/xfs_quota_priv.h index 8286b2842b6..94a3d927d71 100644 --- a/fs/xfs/quota/xfs_quota_priv.h +++ b/fs/xfs/quota/xfs_quota_priv.h @@ -24,43 +24,6 @@ */ #define XFS_DQITER_MAP_SIZE 10 -/* Number of dquots that fit in to a dquot block */ -#define XFS_QM_DQPERBLK(mp) ((mp)->m_quotainfo->qi_dqperchunk) - -#define XFS_DQ_IS_ADDEDTO_TRX(t, d) ((d)->q_transp == (t)) - -#define XFS_QI_MPLRECLAIMS(mp) ((mp)->m_quotainfo->qi_dqreclaims) -#define XFS_QI_UQIP(mp) ((mp)->m_quotainfo->qi_uquotaip) -#define XFS_QI_GQIP(mp) ((mp)->m_quotainfo->qi_gquotaip) -#define XFS_QI_DQCHUNKLEN(mp) ((mp)->m_quotainfo->qi_dqchunklen) -#define XFS_QI_BTIMELIMIT(mp) ((mp)->m_quotainfo->qi_btimelimit) -#define XFS_QI_RTBTIMELIMIT(mp) ((mp)->m_quotainfo->qi_rtbtimelimit) -#define XFS_QI_ITIMELIMIT(mp) ((mp)->m_quotainfo->qi_itimelimit) -#define XFS_QI_BWARNLIMIT(mp) ((mp)->m_quotainfo->qi_bwarnlimit) -#define XFS_QI_RTBWARNLIMIT(mp) ((mp)->m_quotainfo->qi_rtbwarnlimit) -#define XFS_QI_IWARNLIMIT(mp) ((mp)->m_quotainfo->qi_iwarnlimit) -#define XFS_QI_QOFFLOCK(mp) ((mp)->m_quotainfo->qi_quotaofflock) - -#define XFS_QI_MPL_LIST(mp) ((mp)->m_quotainfo->qi_dqlist) -#define XFS_QI_MPLNEXT(mp) ((mp)->m_quotainfo->qi_dqlist.qh_next) -#define XFS_QI_MPLNDQUOTS(mp) ((mp)->m_quotainfo->qi_dqlist.qh_nelems) - -#define xfs_qm_mplist_lock(mp) \ - mutex_lock(&(XFS_QI_MPL_LIST(mp).qh_lock)) -#define xfs_qm_mplist_nowait(mp) \ - mutex_trylock(&(XFS_QI_MPL_LIST(mp).qh_lock)) -#define xfs_qm_mplist_unlock(mp) \ - mutex_unlock(&(XFS_QI_MPL_LIST(mp).qh_lock)) -#define XFS_QM_IS_MPLIST_LOCKED(mp) \ - mutex_is_locked(&(XFS_QI_MPL_LIST(mp).qh_lock)) - -#define xfs_qm_freelist_lock(qm) \ - mutex_lock(&((qm)->qm_dqfreelist.qh_lock)) -#define xfs_qm_freelist_lock_nowait(qm) \ - mutex_trylock(&((qm)->qm_dqfreelist.qh_lock)) -#define xfs_qm_freelist_unlock(qm) \ - mutex_unlock(&((qm)->qm_dqfreelist.qh_lock)) - /* * Hash into a bucket in the dquot hash table, based on <mp, id>. */ @@ -72,9 +35,6 @@ XFS_DQ_HASHVAL(mp, id)) : \ (xfs_Gqm->qm_grp_dqhtable + \ XFS_DQ_HASHVAL(mp, id))) -#define XFS_IS_DQTYPE_ON(mp, type) (type == XFS_DQ_USER ? \ - XFS_IS_UQUOTA_ON(mp) : \ - XFS_IS_OQUOTA_ON(mp)) #define XFS_IS_DQUOT_UNINITIALIZED(dqp) ( \ !dqp->q_core.d_blk_hardlimit && \ !dqp->q_core.d_blk_softlimit && \ @@ -86,68 +46,6 @@ !dqp->q_core.d_rtbcount && \ !dqp->q_core.d_icount) -#define HL_PREVP dq_hashlist.ql_prevp -#define HL_NEXT dq_hashlist.ql_next -#define MPL_PREVP dq_mplist.ql_prevp -#define MPL_NEXT dq_mplist.ql_next - - -#define _LIST_REMOVE(h, dqp, PVP, NXT) \ - { \ - xfs_dquot_t *d; \ - if (((d) = (dqp)->NXT)) \ - (d)->PVP = (dqp)->PVP; \ - *((dqp)->PVP) = d; \ - (dqp)->NXT = NULL; \ - (dqp)->PVP = NULL; \ - (h)->qh_version++; \ - (h)->qh_nelems--; \ - } - -#define _LIST_INSERT(h, dqp, PVP, NXT) \ - { \ - xfs_dquot_t *d; \ - if (((d) = (h)->qh_next)) \ - (d)->PVP = &((dqp)->NXT); \ - (dqp)->NXT = d; \ - (dqp)->PVP = &((h)->qh_next); \ - (h)->qh_next = dqp; \ - (h)->qh_version++; \ - (h)->qh_nelems++; \ - } - -#define FOREACH_DQUOT_IN_MP(dqp, mp) \ - for ((dqp) = XFS_QI_MPLNEXT(mp); (dqp) != NULL; (dqp) = (dqp)->MPL_NEXT) - -#define FOREACH_DQUOT_IN_FREELIST(dqp, qlist) \ -for ((dqp) = (qlist)->qh_next; (dqp) != (xfs_dquot_t *)(qlist); \ - (dqp) = (dqp)->dq_flnext) - -#define XQM_HASHLIST_INSERT(h, dqp) \ - _LIST_INSERT(h, dqp, HL_PREVP, HL_NEXT) - -#define XQM_FREELIST_INSERT(h, dqp) \ - xfs_qm_freelist_append(h, dqp) - -#define XQM_MPLIST_INSERT(h, dqp) \ - _LIST_INSERT(h, dqp, MPL_PREVP, MPL_NEXT) - -#define XQM_HASHLIST_REMOVE(h, dqp) \ - _LIST_REMOVE(h, dqp, HL_PREVP, HL_NEXT) -#define XQM_FREELIST_REMOVE(dqp) \ - xfs_qm_freelist_unlink(dqp) -#define XQM_MPLIST_REMOVE(h, dqp) \ - { _LIST_REMOVE(h, dqp, MPL_PREVP, MPL_NEXT); \ - XFS_QI_MPLRECLAIMS((dqp)->q_mount)++; } - -#define XFS_DQ_IS_LOGITEM_INITD(dqp) ((dqp)->q_logitem.qli_dquot == (dqp)) - -#define XFS_QM_DQP_TO_DQACCT(tp, dqp) (XFS_QM_ISUDQ(dqp) ? \ - (tp)->t_dqinfo->dqa_usrdquots : \ - (tp)->t_dqinfo->dqa_grpdquots) -#define XFS_IS_SUSER_DQUOT(dqp) \ - (!((dqp)->q_core.d_id)) - #define DQFLAGTO_TYPESTR(d) (((d)->dq_flags & XFS_DQ_USER) ? "USR" : \ (((d)->dq_flags & XFS_DQ_GROUP) ? "GRP" : \ (((d)->dq_flags & XFS_DQ_PROJ) ? "PRJ":"???"))) diff --git a/fs/xfs/quota/xfs_trans_dquot.c b/fs/xfs/quota/xfs_trans_dquot.c index c3ab75cb1d9..061d827da33 100644 --- a/fs/xfs/quota/xfs_trans_dquot.c +++ b/fs/xfs/quota/xfs_trans_dquot.c @@ -59,12 +59,11 @@ xfs_trans_dqjoin( xfs_trans_t *tp, xfs_dquot_t *dqp) { - xfs_dq_logitem_t *lp; + xfs_dq_logitem_t *lp = &dqp->q_logitem; - ASSERT(! XFS_DQ_IS_ADDEDTO_TRX(tp, dqp)); + ASSERT(dqp->q_transp != tp); ASSERT(XFS_DQ_IS_LOCKED(dqp)); - ASSERT(XFS_DQ_IS_LOGITEM_INITD(dqp)); - lp = &dqp->q_logitem; + ASSERT(lp->qli_dquot == dqp); /* * Get a log_item_desc to point at the new item. @@ -96,7 +95,7 @@ xfs_trans_log_dquot( { xfs_log_item_desc_t *lidp; - ASSERT(XFS_DQ_IS_ADDEDTO_TRX(tp, dqp)); + ASSERT(dqp->q_transp == tp); ASSERT(XFS_DQ_IS_LOCKED(dqp)); lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)(&dqp->q_logitem)); @@ -198,16 +197,16 @@ xfs_trans_get_dqtrx( int i; xfs_dqtrx_t *qa; - for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { - qa = XFS_QM_DQP_TO_DQACCT(tp, dqp); + qa = XFS_QM_ISUDQ(dqp) ? + tp->t_dqinfo->dqa_usrdquots : tp->t_dqinfo->dqa_grpdquots; + for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { if (qa[i].qt_dquot == NULL || - qa[i].qt_dquot == dqp) { - return (&qa[i]); - } + qa[i].qt_dquot == dqp) + return &qa[i]; } - return (NULL); + return NULL; } /* @@ -381,7 +380,7 @@ xfs_trans_apply_dquot_deltas( break; ASSERT(XFS_DQ_IS_LOCKED(dqp)); - ASSERT(XFS_DQ_IS_ADDEDTO_TRX(tp, dqp)); + ASSERT(dqp->q_transp == tp); /* * adjust the actual number of blocks used @@ -639,7 +638,7 @@ xfs_trans_dqresv( softlimit = q->qi_bsoftlimit; timer = be32_to_cpu(dqp->q_core.d_btimer); warns = be16_to_cpu(dqp->q_core.d_bwarns); - warnlimit = XFS_QI_BWARNLIMIT(dqp->q_mount); + warnlimit = dqp->q_mount->m_quotainfo->qi_bwarnlimit; resbcountp = &dqp->q_res_bcount; } else { ASSERT(flags & XFS_TRANS_DQ_RES_RTBLKS); @@ -651,7 +650,7 @@ xfs_trans_dqresv( softlimit = q->qi_rtbsoftlimit; timer = be32_to_cpu(dqp->q_core.d_rtbtimer); warns = be16_to_cpu(dqp->q_core.d_rtbwarns); - warnlimit = XFS_QI_RTBWARNLIMIT(dqp->q_mount); + warnlimit = dqp->q_mount->m_quotainfo->qi_rtbwarnlimit; resbcountp = &dqp->q_res_rtbcount; } @@ -691,7 +690,7 @@ xfs_trans_dqresv( count = be64_to_cpu(dqp->q_core.d_icount); timer = be32_to_cpu(dqp->q_core.d_itimer); warns = be16_to_cpu(dqp->q_core.d_iwarns); - warnlimit = XFS_QI_IWARNLIMIT(dqp->q_mount); + warnlimit = dqp->q_mount->m_quotainfo->qi_iwarnlimit; hardlimit = be64_to_cpu(dqp->q_core.d_ino_hardlimit); if (!hardlimit) hardlimit = q->qi_ihardlimit; diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index 5c11e4d1701..99587ded043 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -3829,7 +3829,7 @@ xfs_bmap_add_attrfork( } if ((error = xfs_bmap_finish(&tp, &flist, &committed))) goto error2; - error = xfs_trans_commit(tp, XFS_TRANS_PERM_LOG_RES); + error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); ASSERT(ip->i_df.if_ext_max == XFS_IFORK_DSIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t)); return error; diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index f3c49e69eab..240340a4727 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -372,12 +372,12 @@ xfs_buf_item_pin( */ STATIC void xfs_buf_item_unpin( - xfs_buf_log_item_t *bip, - int stale) + xfs_buf_log_item_t *bip) { struct xfs_ail *ailp; xfs_buf_t *bp; int freed; + int stale = bip->bli_flags & XFS_BLI_STALE; bp = bip->bli_buf; ASSERT(bp != NULL); @@ -428,40 +428,34 @@ xfs_buf_item_unpin_remove( xfs_buf_log_item_t *bip, xfs_trans_t *tp) { - xfs_buf_t *bp; - xfs_log_item_desc_t *lidp; - int stale = 0; - - bp = bip->bli_buf; - /* - * will xfs_buf_item_unpin() call xfs_buf_item_relse()? - */ + /* will xfs_buf_item_unpin() call xfs_buf_item_relse()? */ if ((atomic_read(&bip->bli_refcount) == 1) && (bip->bli_flags & XFS_BLI_STALE)) { + /* + * yes -- We can safely do some work here and then call + * buf_item_unpin to do the rest because we are + * are holding the buffer locked so no one else will be + * able to bump up the refcount. We have to remove the + * log item from the transaction as we are about to release + * our reference to the buffer. If we don't, the unlock that + * occurs later in the xfs_trans_uncommit() will try to + * reference the buffer which we no longer have a hold on. + */ + struct xfs_log_item_desc *lidp; + ASSERT(XFS_BUF_VALUSEMA(bip->bli_buf) <= 0); trace_xfs_buf_item_unpin_stale(bip); - /* - * yes -- clear the xaction descriptor in-use flag - * and free the chunk if required. We can safely - * do some work here and then call buf_item_unpin - * to do the rest because if the if is true, then - * we are holding the buffer locked so no one else - * will be able to bump up the refcount. - */ - lidp = xfs_trans_find_item(tp, (xfs_log_item_t *) bip); - stale = lidp->lid_flags & XFS_LID_BUF_STALE; + lidp = xfs_trans_find_item(tp, (xfs_log_item_t *)bip); xfs_trans_free_item(tp, lidp); + /* - * Since the transaction no longer refers to the buffer, - * the buffer should no longer refer to the transaction. + * Since the transaction no longer refers to the buffer, the + * buffer should no longer refer to the transaction. */ - XFS_BUF_SET_FSPRIVATE2(bp, NULL); + XFS_BUF_SET_FSPRIVATE2(bip->bli_buf, NULL); } - - xfs_buf_item_unpin(bip, stale); - - return; + xfs_buf_item_unpin(bip); } /* @@ -675,7 +669,7 @@ static struct xfs_item_ops xfs_buf_item_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_buf_item_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_buf_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_buf_item_unpin, + .iop_unpin = (void(*)(xfs_log_item_t*))xfs_buf_item_unpin, .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t *)) xfs_buf_item_unpin_remove, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_buf_item_trylock, @@ -733,10 +727,7 @@ xfs_buf_item_init( bip = (xfs_buf_log_item_t*)kmem_zone_zalloc(xfs_buf_item_zone, KM_SLEEP); - bip->bli_item.li_type = XFS_LI_BUF; - bip->bli_item.li_ops = &xfs_buf_item_ops; - bip->bli_item.li_mountp = mp; - bip->bli_item.li_ailp = mp->m_ail; + xfs_log_item_init(mp, &bip->bli_item, XFS_LI_BUF, &xfs_buf_item_ops); bip->bli_buf = bp; xfs_buf_hold(bp); bip->bli_format.blf_type = XFS_LI_BUF; diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h index 217f34af00c..df4454511f7 100644 --- a/fs/xfs/xfs_buf_item.h +++ b/fs/xfs/xfs_buf_item.h @@ -26,7 +26,7 @@ extern kmem_zone_t *xfs_buf_item_zone; * have been logged. * For 6.2 and beyond, this is XFS_LI_BUF. We use this to log everything. */ -typedef struct xfs_buf_log_format_t { +typedef struct xfs_buf_log_format { unsigned short blf_type; /* buf log item type indicator */ unsigned short blf_size; /* size of this item */ ushort blf_flags; /* misc state */ diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c index 92d5cd5bf4f..ef96175c074 100644 --- a/fs/xfs/xfs_error.c +++ b/fs/xfs/xfs_error.c @@ -186,18 +186,18 @@ xfs_cmn_err(int panic_tag, int level, xfs_mount_t *mp, char *fmt, ...) void xfs_error_report( - char *tag, - int level, - xfs_mount_t *mp, - char *fname, - int linenum, - inst_t *ra) + const char *tag, + int level, + struct xfs_mount *mp, + const char *filename, + int linenum, + inst_t *ra) { if (level <= xfs_error_level) { xfs_cmn_err(XFS_PTAG_ERROR_REPORT, CE_ALERT, mp, "XFS internal error %s at line %d of file %s. Caller 0x%p\n", - tag, linenum, fname, ra); + tag, linenum, filename, ra); xfs_stack_trace(); } @@ -205,15 +205,15 @@ xfs_error_report( void xfs_corruption_error( - char *tag, - int level, - xfs_mount_t *mp, - void *p, - char *fname, - int linenum, - inst_t *ra) + const char *tag, + int level, + struct xfs_mount *mp, + void *p, + const char *filename, + int linenum, + inst_t *ra) { if (level <= xfs_error_level) xfs_hex_dump(p, 16); - xfs_error_report(tag, level, mp, fname, linenum, ra); + xfs_error_report(tag, level, mp, filename, linenum, ra); } diff --git a/fs/xfs/xfs_error.h b/fs/xfs/xfs_error.h index 0c93051c465..c2c1a072bb8 100644 --- a/fs/xfs/xfs_error.h +++ b/fs/xfs/xfs_error.h @@ -29,10 +29,11 @@ extern int xfs_error_trap(int); struct xfs_mount; -extern void xfs_error_report(char *tag, int level, struct xfs_mount *mp, - char *fname, int linenum, inst_t *ra); -extern void xfs_corruption_error(char *tag, int level, struct xfs_mount *mp, - void *p, char *fname, int linenum, inst_t *ra); +extern void xfs_error_report(const char *tag, int level, struct xfs_mount *mp, + const char *filename, int linenum, inst_t *ra); +extern void xfs_corruption_error(const char *tag, int level, + struct xfs_mount *mp, void *p, const char *filename, + int linenum, inst_t *ra); #define XFS_ERROR_REPORT(e, lvl, mp) \ xfs_error_report(e, lvl, mp, __FILE__, __LINE__, __return_address) diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 6f35ed1b39b..409fe81585f 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -106,7 +106,7 @@ xfs_efi_item_pin(xfs_efi_log_item_t *efip) */ /*ARGSUSED*/ STATIC void -xfs_efi_item_unpin(xfs_efi_log_item_t *efip, int stale) +xfs_efi_item_unpin(xfs_efi_log_item_t *efip) { struct xfs_ail *ailp = efip->efi_item.li_ailp; @@ -224,7 +224,7 @@ static struct xfs_item_ops xfs_efi_item_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_efi_item_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_efi_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_efi_item_unpin, + .iop_unpin = (void(*)(xfs_log_item_t*))xfs_efi_item_unpin, .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t *)) xfs_efi_item_unpin_remove, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_efi_item_trylock, @@ -259,10 +259,7 @@ xfs_efi_init(xfs_mount_t *mp, KM_SLEEP); } - efip->efi_item.li_type = XFS_LI_EFI; - efip->efi_item.li_ops = &xfs_efi_item_ops; - efip->efi_item.li_mountp = mp; - efip->efi_item.li_ailp = mp->m_ail; + xfs_log_item_init(mp, &efip->efi_item, XFS_LI_EFI, &xfs_efi_item_ops); efip->efi_format.efi_nextents = nextents; efip->efi_format.efi_id = (__psint_t)(void*)efip; @@ -428,7 +425,7 @@ xfs_efd_item_pin(xfs_efd_log_item_t *efdp) */ /*ARGSUSED*/ STATIC void -xfs_efd_item_unpin(xfs_efd_log_item_t *efdp, int stale) +xfs_efd_item_unpin(xfs_efd_log_item_t *efdp) { return; } @@ -518,7 +515,7 @@ static struct xfs_item_ops xfs_efd_item_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_efd_item_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_efd_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_efd_item_unpin, + .iop_unpin = (void(*)(xfs_log_item_t*))xfs_efd_item_unpin, .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*)) xfs_efd_item_unpin_remove, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_efd_item_trylock, @@ -554,10 +551,7 @@ xfs_efd_init(xfs_mount_t *mp, KM_SLEEP); } - efdp->efd_item.li_type = XFS_LI_EFD; - efdp->efd_item.li_ops = &xfs_efd_item_ops; - efdp->efd_item.li_mountp = mp; - efdp->efd_item.li_ailp = mp->m_ail; + xfs_log_item_init(mp, &efdp->efd_item, XFS_LI_EFD, &xfs_efd_item_ops); efdp->efd_efip = efip; efdp->efd_format.efd_nextents = nextents; efdp->efd_format.efd_efi_id = efip->efi_format.efi_id; diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 0ffd5644704..8cd6e8d8fe9 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2449,6 +2449,8 @@ xfs_iunpin_nowait( { ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)); + trace_xfs_inode_unpin_nowait(ip, _RET_IP_); + /* Give the log a push to start the unpinning I/O */ xfs_log_force_lsn(ip->i_mount, ip->i_itemp->ili_last_lsn, 0); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 7bfea854015..cf8249a6000 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -543,6 +543,7 @@ xfs_inode_item_pin( { ASSERT(xfs_isilocked(iip->ili_inode, XFS_ILOCK_EXCL)); + trace_xfs_inode_pin(iip->ili_inode, _RET_IP_); atomic_inc(&iip->ili_inode->i_pincount); } @@ -556,11 +557,11 @@ xfs_inode_item_pin( /* ARGSUSED */ STATIC void xfs_inode_item_unpin( - xfs_inode_log_item_t *iip, - int stale) + xfs_inode_log_item_t *iip) { struct xfs_inode *ip = iip->ili_inode; + trace_xfs_inode_unpin(ip, _RET_IP_); ASSERT(atomic_read(&ip->i_pincount) > 0); if (atomic_dec_and_test(&ip->i_pincount)) wake_up(&ip->i_ipin_wait); @@ -572,7 +573,7 @@ xfs_inode_item_unpin_remove( xfs_inode_log_item_t *iip, xfs_trans_t *tp) { - xfs_inode_item_unpin(iip, 0); + xfs_inode_item_unpin(iip); } /* @@ -838,7 +839,7 @@ static struct xfs_item_ops xfs_inode_item_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_inode_item_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_inode_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_inode_item_unpin, + .iop_unpin = (void(*)(xfs_log_item_t*))xfs_inode_item_unpin, .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*)) xfs_inode_item_unpin_remove, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_inode_item_trylock, @@ -865,17 +866,9 @@ xfs_inode_item_init( ASSERT(ip->i_itemp == NULL); iip = ip->i_itemp = kmem_zone_zalloc(xfs_ili_zone, KM_SLEEP); - iip->ili_item.li_type = XFS_LI_INODE; - iip->ili_item.li_ops = &xfs_inode_item_ops; - iip->ili_item.li_mountp = mp; - iip->ili_item.li_ailp = mp->m_ail; iip->ili_inode = ip; - - /* - We have zeroed memory. No need ... - iip->ili_extents_buf = NULL; - */ - + xfs_log_item_init(mp, &iip->ili_item, XFS_LI_INODE, + &xfs_inode_item_ops); iip->ili_format.ilf_type = XFS_LI_INODE; iip->ili_format.ilf_ino = ip->i_ino; iip->ili_format.ilf_blkno = ip->i_imap.im_blkno; diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 0b65039951a..ef14943829d 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -55,71 +55,33 @@ #define XFS_STRAT_WRITE_IMAPS 2 #define XFS_WRITE_IMAPS XFS_BMAP_MAX_NMAP -STATIC int -xfs_imap_to_bmap( - xfs_inode_t *ip, - xfs_off_t offset, - xfs_bmbt_irec_t *imap, - xfs_iomap_t *iomapp, - int imaps, /* Number of imap entries */ - int iomaps, /* Number of iomap entries */ - int flags) -{ - xfs_mount_t *mp = ip->i_mount; - int pbm; - xfs_fsblock_t start_block; - - - for (pbm = 0; imaps && pbm < iomaps; imaps--, iomapp++, imap++, pbm++) { - iomapp->iomap_offset = XFS_FSB_TO_B(mp, imap->br_startoff); - iomapp->iomap_delta = offset - iomapp->iomap_offset; - iomapp->iomap_bsize = XFS_FSB_TO_B(mp, imap->br_blockcount); - iomapp->iomap_flags = flags; - - if (XFS_IS_REALTIME_INODE(ip)) { - iomapp->iomap_flags |= IOMAP_REALTIME; - iomapp->iomap_target = mp->m_rtdev_targp; - } else { - iomapp->iomap_target = mp->m_ddev_targp; - } - start_block = imap->br_startblock; - if (start_block == HOLESTARTBLOCK) { - iomapp->iomap_bn = IOMAP_DADDR_NULL; - iomapp->iomap_flags |= IOMAP_HOLE; - } else if (start_block == DELAYSTARTBLOCK) { - iomapp->iomap_bn = IOMAP_DADDR_NULL; - iomapp->iomap_flags |= IOMAP_DELAY; - } else { - iomapp->iomap_bn = xfs_fsb_to_db(ip, start_block); - if (ISUNWRITTEN(imap)) - iomapp->iomap_flags |= IOMAP_UNWRITTEN; - } - - offset += iomapp->iomap_bsize - iomapp->iomap_delta; - } - return pbm; /* Return the number filled */ -} +STATIC int xfs_iomap_write_direct(struct xfs_inode *, xfs_off_t, size_t, + int, struct xfs_bmbt_irec *, int *); +STATIC int xfs_iomap_write_delay(struct xfs_inode *, xfs_off_t, size_t, int, + struct xfs_bmbt_irec *, int *); +STATIC int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t, size_t, + struct xfs_bmbt_irec *, int *); int xfs_iomap( - xfs_inode_t *ip, - xfs_off_t offset, - ssize_t count, - int flags, - xfs_iomap_t *iomapp, - int *niomaps) + struct xfs_inode *ip, + xfs_off_t offset, + ssize_t count, + int flags, + struct xfs_bmbt_irec *imap, + int *nimaps, + int *new) { - xfs_mount_t *mp = ip->i_mount; - xfs_fileoff_t offset_fsb, end_fsb; - int error = 0; - int lockmode = 0; - xfs_bmbt_irec_t imap; - int nimaps = 1; - int bmapi_flags = 0; - int iomap_flags = 0; + struct xfs_mount *mp = ip->i_mount; + xfs_fileoff_t offset_fsb, end_fsb; + int error = 0; + int lockmode = 0; + int bmapi_flags = 0; ASSERT((ip->i_d.di_mode & S_IFMT) == S_IFREG); + *new = 0; + if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); @@ -160,8 +122,8 @@ xfs_iomap( error = xfs_bmapi(NULL, ip, offset_fsb, (xfs_filblks_t)(end_fsb - offset_fsb), - bmapi_flags, NULL, 0, &imap, - &nimaps, NULL, NULL); + bmapi_flags, NULL, 0, imap, + nimaps, NULL, NULL); if (error) goto out; @@ -169,46 +131,41 @@ xfs_iomap( switch (flags & (BMAPI_WRITE|BMAPI_ALLOCATE)) { case BMAPI_WRITE: /* If we found an extent, return it */ - if (nimaps && - (imap.br_startblock != HOLESTARTBLOCK) && - (imap.br_startblock != DELAYSTARTBLOCK)) { - trace_xfs_iomap_found(ip, offset, count, flags, &imap); + if (*nimaps && + (imap->br_startblock != HOLESTARTBLOCK) && + (imap->br_startblock != DELAYSTARTBLOCK)) { + trace_xfs_iomap_found(ip, offset, count, flags, imap); break; } if (flags & (BMAPI_DIRECT|BMAPI_MMAP)) { error = xfs_iomap_write_direct(ip, offset, count, flags, - &imap, &nimaps, nimaps); + imap, nimaps); } else { error = xfs_iomap_write_delay(ip, offset, count, flags, - &imap, &nimaps); + imap, nimaps); } if (!error) { - trace_xfs_iomap_alloc(ip, offset, count, flags, &imap); + trace_xfs_iomap_alloc(ip, offset, count, flags, imap); } - iomap_flags = IOMAP_NEW; + *new = 1; break; case BMAPI_ALLOCATE: /* If we found an extent, return it */ xfs_iunlock(ip, lockmode); lockmode = 0; - if (nimaps && !isnullstartblock(imap.br_startblock)) { - trace_xfs_iomap_found(ip, offset, count, flags, &imap); + if (*nimaps && !isnullstartblock(imap->br_startblock)) { + trace_xfs_iomap_found(ip, offset, count, flags, imap); break; } error = xfs_iomap_write_allocate(ip, offset, count, - &imap, &nimaps); + imap, nimaps); break; } - if (nimaps) { - *niomaps = xfs_imap_to_bmap(ip, offset, &imap, - iomapp, nimaps, *niomaps, iomap_flags); - } else if (niomaps) { - *niomaps = 0; - } + ASSERT(*nimaps <= 1); out: if (lockmode) @@ -216,7 +173,6 @@ out: return XFS_ERROR(error); } - STATIC int xfs_iomap_eof_align_last_fsb( xfs_mount_t *mp, @@ -285,15 +241,14 @@ xfs_cmn_err_fsblock_zero( return EFSCORRUPTED; } -int +STATIC int xfs_iomap_write_direct( xfs_inode_t *ip, xfs_off_t offset, size_t count, int flags, xfs_bmbt_irec_t *ret_imap, - int *nmaps, - int found) + int *nmaps) { xfs_mount_t *mp = ip->i_mount; xfs_fileoff_t offset_fsb; @@ -330,7 +285,7 @@ xfs_iomap_write_direct( if (error) goto error_out; } else { - if (found && (ret_imap->br_startblock == HOLESTARTBLOCK)) + if (*nmaps && (ret_imap->br_startblock == HOLESTARTBLOCK)) last_fsb = MIN(last_fsb, (xfs_fileoff_t) ret_imap->br_blockcount + ret_imap->br_startoff); @@ -485,7 +440,7 @@ xfs_iomap_eof_want_preallocate( return 0; } -int +STATIC int xfs_iomap_write_delay( xfs_inode_t *ip, xfs_off_t offset, @@ -588,7 +543,7 @@ retry: * We no longer bother to look at the incoming map - all we have to * guarantee is that whatever we allocate fills the required range. */ -int +STATIC int xfs_iomap_write_allocate( xfs_inode_t *ip, xfs_off_t offset, diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h index 174f2999099..81ac4afd45b 100644 --- a/fs/xfs/xfs_iomap.h +++ b/fs/xfs/xfs_iomap.h @@ -18,19 +18,6 @@ #ifndef __XFS_IOMAP_H__ #define __XFS_IOMAP_H__ -#define IOMAP_DADDR_NULL ((xfs_daddr_t) (-1LL)) - - -typedef enum { /* iomap_flags values */ - IOMAP_READ = 0, /* mapping for a read */ - IOMAP_HOLE = 0x02, /* mapping covers a hole */ - IOMAP_DELAY = 0x04, /* mapping covers delalloc region */ - IOMAP_REALTIME = 0x10, /* mapping on the realtime device */ - IOMAP_UNWRITTEN = 0x20, /* mapping covers allocated */ - /* but uninitialized file data */ - IOMAP_NEW = 0x40 /* just allocate */ -} iomap_flags_t; - typedef enum { /* base extent manipulation calls */ BMAPI_READ = (1 << 0), /* read extents */ @@ -52,43 +39,11 @@ typedef enum { { BMAPI_MMAP, "MMAP" }, \ { BMAPI_TRYLOCK, "TRYLOCK" } -/* - * xfs_iomap_t: File system I/O map - * - * The iomap_bn field is expressed in 512-byte blocks, and is where the - * mapping starts on disk. - * - * The iomap_offset, iomap_bsize and iomap_delta fields are in bytes. - * iomap_offset is the offset of the mapping in the file itself. - * iomap_bsize is the size of the mapping, iomap_delta is the - * desired data's offset into the mapping, given the offset supplied - * to the file I/O map routine. - * - * When a request is made to read beyond the logical end of the object, - * iomap_size may be set to 0, but iomap_offset and iomap_length should be set - * to the actual amount of underlying storage that has been allocated, if any. - */ - -typedef struct xfs_iomap { - xfs_daddr_t iomap_bn; /* first 512B blk of mapping */ - xfs_buftarg_t *iomap_target; - xfs_off_t iomap_offset; /* offset of mapping, bytes */ - xfs_off_t iomap_bsize; /* size of mapping, bytes */ - xfs_off_t iomap_delta; /* offset into mapping, bytes */ - iomap_flags_t iomap_flags; -} xfs_iomap_t; - struct xfs_inode; struct xfs_bmbt_irec; extern int xfs_iomap(struct xfs_inode *, xfs_off_t, ssize_t, int, - struct xfs_iomap *, int *); -extern int xfs_iomap_write_direct(struct xfs_inode *, xfs_off_t, size_t, - int, struct xfs_bmbt_irec *, int *, int); -extern int xfs_iomap_write_delay(struct xfs_inode *, xfs_off_t, size_t, int, - struct xfs_bmbt_irec *, int *); -extern int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t, size_t, - struct xfs_bmbt_irec *, int *); + struct xfs_bmbt_irec *, int *, int *); extern int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, size_t); #endif /* __XFS_IOMAP_H__*/ diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 2be01913628..3038dd52c72 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -44,13 +44,8 @@ kmem_zone_t *xfs_log_ticket_zone; -#define xlog_write_adv_cnt(ptr, len, off, bytes) \ - { (ptr) += (bytes); \ - (len) -= (bytes); \ - (off) += (bytes);} - /* Local miscellaneous function prototypes */ -STATIC int xlog_commit_record(xfs_mount_t *mp, xlog_ticket_t *ticket, +STATIC int xlog_commit_record(struct log *log, struct xlog_ticket *ticket, xlog_in_core_t **, xfs_lsn_t *); STATIC xlog_t * xlog_alloc_log(xfs_mount_t *mp, xfs_buftarg_t *log_target, @@ -59,11 +54,9 @@ STATIC xlog_t * xlog_alloc_log(xfs_mount_t *mp, STATIC int xlog_space_left(xlog_t *log, int cycle, int bytes); STATIC int xlog_sync(xlog_t *log, xlog_in_core_t *iclog); STATIC void xlog_dealloc_log(xlog_t *log); -STATIC int xlog_write(xfs_mount_t *mp, xfs_log_iovec_t region[], - int nentries, struct xlog_ticket *tic, - xfs_lsn_t *start_lsn, - xlog_in_core_t **commit_iclog, - uint flags); +STATIC int xlog_write(struct log *log, struct xfs_log_vec *log_vector, + struct xlog_ticket *tic, xfs_lsn_t *start_lsn, + xlog_in_core_t **commit_iclog, uint flags); /* local state machine functions */ STATIC void xlog_state_done_syncing(xlog_in_core_t *iclog, int); @@ -102,7 +95,7 @@ STATIC xlog_ticket_t *xlog_ticket_alloc(xlog_t *log, uint flags); #if defined(DEBUG) -STATIC void xlog_verify_dest_ptr(xlog_t *log, __psint_t ptr); +STATIC void xlog_verify_dest_ptr(xlog_t *log, char *ptr); STATIC void xlog_verify_grant_head(xlog_t *log, int equals); STATIC void xlog_verify_iclog(xlog_t *log, xlog_in_core_t *iclog, int count, boolean_t syncing); @@ -258,7 +251,7 @@ xfs_log_done( * If we get an error, just continue and give back the log ticket. */ (((ticket->t_flags & XLOG_TIC_INITED) == 0) && - (xlog_commit_record(mp, ticket, iclog, &lsn)))) { + (xlog_commit_record(log, ticket, iclog, &lsn)))) { lsn = (xfs_lsn_t) -1; if (ticket->t_flags & XLOG_TIC_PERM_RESERV) { flags |= XFS_LOG_REL_PERM_RESERV; @@ -516,18 +509,10 @@ xfs_log_unmount_write(xfs_mount_t *mp) #ifdef DEBUG xlog_in_core_t *first_iclog; #endif - xfs_log_iovec_t reg[1]; xlog_ticket_t *tic = NULL; xfs_lsn_t lsn; int error; - /* the data section must be 32 bit size aligned */ - struct { - __uint16_t magic; - __uint16_t pad1; - __uint32_t pad2; /* may as well make it 64 bits */ - } magic = { XLOG_UNMOUNT_TYPE, 0, 0 }; - /* * Don't write out unmount record on read-only mounts. * Or, if we are doing a forced umount (typically because of IO errors). @@ -549,16 +534,30 @@ xfs_log_unmount_write(xfs_mount_t *mp) } while (iclog != first_iclog); #endif if (! (XLOG_FORCED_SHUTDOWN(log))) { - reg[0].i_addr = (void*)&magic; - reg[0].i_len = sizeof(magic); - reg[0].i_type = XLOG_REG_TYPE_UNMOUNT; - error = xfs_log_reserve(mp, 600, 1, &tic, XFS_LOG, 0, XLOG_UNMOUNT_REC_TYPE); if (!error) { + /* the data section must be 32 bit size aligned */ + struct { + __uint16_t magic; + __uint16_t pad1; + __uint32_t pad2; /* may as well make it 64 bits */ + } magic = { + .magic = XLOG_UNMOUNT_TYPE, + }; + struct xfs_log_iovec reg = { + .i_addr = (void *)&magic, + .i_len = sizeof(magic), + .i_type = XLOG_REG_TYPE_UNMOUNT, + }; + struct xfs_log_vec vec = { + .lv_niovecs = 1, + .lv_iovecp = ®, + }; + /* remove inited flag */ - ((xlog_ticket_t *)tic)->t_flags = 0; - error = xlog_write(mp, reg, 1, tic, &lsn, + tic->t_flags = 0; + error = xlog_write(log, &vec, tic, &lsn, NULL, XLOG_UNMOUNT_TRANS); /* * At this point, we're umounting anyway, @@ -648,10 +647,26 @@ xfs_log_unmount(xfs_mount_t *mp) xlog_dealloc_log(mp->m_log); } +void +xfs_log_item_init( + struct xfs_mount *mp, + struct xfs_log_item *item, + int type, + struct xfs_item_ops *ops) +{ + item->li_mountp = mp; + item->li_ailp = mp->m_ail; + item->li_type = type; + item->li_ops = ops; +} + /* * Write region vectors to log. The write happens using the space reservation * of the ticket (tic). It is not a requirement that all writes for a given - * transaction occur with one call to xfs_log_write(). + * transaction occur with one call to xfs_log_write(). However, it is important + * to note that the transaction reservation code makes an assumption about the + * number of log headers a transaction requires that may be violated if you + * don't pass all the transaction vectors in one call.... */ int xfs_log_write( @@ -663,11 +678,15 @@ xfs_log_write( { struct log *log = mp->m_log; int error; + struct xfs_log_vec vec = { + .lv_niovecs = nentries, + .lv_iovecp = reg, + }; if (XLOG_FORCED_SHUTDOWN(log)) return XFS_ERROR(EIO); - error = xlog_write(mp, reg, nentries, tic, start_lsn, NULL, 0); + error = xlog_write(log, &vec, tic, start_lsn, NULL, 0); if (error) xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR); return error; @@ -1020,6 +1039,7 @@ xlog_alloc_log(xfs_mount_t *mp, int i; int iclogsize; int error = ENOMEM; + uint log2_size = 0; log = kmem_zalloc(sizeof(xlog_t), KM_MAYFAIL); if (!log) { @@ -1045,29 +1065,30 @@ xlog_alloc_log(xfs_mount_t *mp, error = EFSCORRUPTED; if (xfs_sb_version_hassector(&mp->m_sb)) { - log->l_sectbb_log = mp->m_sb.sb_logsectlog - BBSHIFT; - if (log->l_sectbb_log < 0 || - log->l_sectbb_log > mp->m_sectbb_log) { - xlog_warn("XFS: Log sector size (0x%x) out of range.", - log->l_sectbb_log); + log2_size = mp->m_sb.sb_logsectlog; + if (log2_size < BBSHIFT) { + xlog_warn("XFS: Log sector size too small " + "(0x%x < 0x%x)", log2_size, BBSHIFT); goto out_free_log; } - /* for larger sector sizes, must have v2 or external log */ - if (log->l_sectbb_log != 0 && - (log->l_logBBstart != 0 && - !xfs_sb_version_haslogv2(&mp->m_sb))) { - xlog_warn("XFS: log sector size (0x%x) invalid " - "for configuration.", log->l_sectbb_log); + log2_size -= BBSHIFT; + if (log2_size > mp->m_sectbb_log) { + xlog_warn("XFS: Log sector size too large " + "(0x%x > 0x%x)", log2_size, mp->m_sectbb_log); goto out_free_log; } - if (mp->m_sb.sb_logsectlog < BBSHIFT) { - xlog_warn("XFS: Log sector log (0x%x) too small.", - mp->m_sb.sb_logsectlog); + + /* for larger sector sizes, must have v2 or external log */ + if (log2_size && log->l_logBBstart > 0 && + !xfs_sb_version_haslogv2(&mp->m_sb)) { + + xlog_warn("XFS: log sector size (0x%x) invalid " + "for configuration.", log2_size); goto out_free_log; } } - log->l_sectbb_mask = (1 << log->l_sectbb_log) - 1; + log->l_sectBBsize = 1 << log2_size; xlog_get_iclog_buffer_size(mp, log); @@ -1174,26 +1195,31 @@ out: * ticket. Return the lsn of the commit record. */ STATIC int -xlog_commit_record(xfs_mount_t *mp, - xlog_ticket_t *ticket, - xlog_in_core_t **iclog, - xfs_lsn_t *commitlsnp) +xlog_commit_record( + struct log *log, + struct xlog_ticket *ticket, + struct xlog_in_core **iclog, + xfs_lsn_t *commitlsnp) { - int error; - xfs_log_iovec_t reg[1]; - - reg[0].i_addr = NULL; - reg[0].i_len = 0; - reg[0].i_type = XLOG_REG_TYPE_COMMIT; + struct xfs_mount *mp = log->l_mp; + int error; + struct xfs_log_iovec reg = { + .i_addr = NULL, + .i_len = 0, + .i_type = XLOG_REG_TYPE_COMMIT, + }; + struct xfs_log_vec vec = { + .lv_niovecs = 1, + .lv_iovecp = ®, + }; ASSERT_ALWAYS(iclog); - if ((error = xlog_write(mp, reg, 1, ticket, commitlsnp, - iclog, XLOG_COMMIT_TRANS))) { + error = xlog_write(log, &vec, ticket, commitlsnp, iclog, + XLOG_COMMIT_TRANS); + if (error) xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR); - } return error; -} /* xlog_commit_record */ - +} /* * Push on the buffer cache code if we ever use more than 75% of the on-disk @@ -1614,6 +1640,192 @@ xlog_print_tic_res(xfs_mount_t *mp, xlog_ticket_t *ticket) } /* + * Calculate the potential space needed by the log vector. Each region gets + * its own xlog_op_header_t and may need to be double word aligned. + */ +static int +xlog_write_calc_vec_length( + struct xlog_ticket *ticket, + struct xfs_log_vec *log_vector) +{ + struct xfs_log_vec *lv; + int headers = 0; + int len = 0; + int i; + + /* acct for start rec of xact */ + if (ticket->t_flags & XLOG_TIC_INITED) + headers++; + + for (lv = log_vector; lv; lv = lv->lv_next) { + headers += lv->lv_niovecs; + + for (i = 0; i < lv->lv_niovecs; i++) { + struct xfs_log_iovec *vecp = &lv->lv_iovecp[i]; + + len += vecp->i_len; + xlog_tic_add_region(ticket, vecp->i_len, vecp->i_type); + } + } + + ticket->t_res_num_ophdrs += headers; + len += headers * sizeof(struct xlog_op_header); + + return len; +} + +/* + * If first write for transaction, insert start record We can't be trying to + * commit if we are inited. We can't have any "partial_copy" if we are inited. + */ +static int +xlog_write_start_rec( + struct xlog_op_header *ophdr, + struct xlog_ticket *ticket) +{ + if (!(ticket->t_flags & XLOG_TIC_INITED)) + return 0; + + ophdr->oh_tid = cpu_to_be32(ticket->t_tid); + ophdr->oh_clientid = ticket->t_clientid; + ophdr->oh_len = 0; + ophdr->oh_flags = XLOG_START_TRANS; + ophdr->oh_res2 = 0; + + ticket->t_flags &= ~XLOG_TIC_INITED; + + return sizeof(struct xlog_op_header); +} + +static xlog_op_header_t * +xlog_write_setup_ophdr( + struct log *log, + struct xlog_op_header *ophdr, + struct xlog_ticket *ticket, + uint flags) +{ + ophdr->oh_tid = cpu_to_be32(ticket->t_tid); + ophdr->oh_clientid = ticket->t_clientid; + ophdr->oh_res2 = 0; + + /* are we copying a commit or unmount record? */ + ophdr->oh_flags = flags; + + /* + * We've seen logs corrupted with bad transaction client ids. This + * makes sure that XFS doesn't generate them on. Turn this into an EIO + * and shut down the filesystem. + */ + switch (ophdr->oh_clientid) { + case XFS_TRANSACTION: + case XFS_VOLUME: + case XFS_LOG: + break; + default: + xfs_fs_cmn_err(CE_WARN, log->l_mp, + "Bad XFS transaction clientid 0x%x in ticket 0x%p", + ophdr->oh_clientid, ticket); + return NULL; + } + + return ophdr; +} + +/* + * Set up the parameters of the region copy into the log. This has + * to handle region write split across multiple log buffers - this + * state is kept external to this function so that this code can + * can be written in an obvious, self documenting manner. + */ +static int +xlog_write_setup_copy( + struct xlog_ticket *ticket, + struct xlog_op_header *ophdr, + int space_available, + int space_required, + int *copy_off, + int *copy_len, + int *last_was_partial_copy, + int *bytes_consumed) +{ + int still_to_copy; + + still_to_copy = space_required - *bytes_consumed; + *copy_off = *bytes_consumed; + + if (still_to_copy <= space_available) { + /* write of region completes here */ + *copy_len = still_to_copy; + ophdr->oh_len = cpu_to_be32(*copy_len); + if (*last_was_partial_copy) + ophdr->oh_flags |= (XLOG_END_TRANS|XLOG_WAS_CONT_TRANS); + *last_was_partial_copy = 0; + *bytes_consumed = 0; + return 0; + } + + /* partial write of region, needs extra log op header reservation */ + *copy_len = space_available; + ophdr->oh_len = cpu_to_be32(*copy_len); + ophdr->oh_flags |= XLOG_CONTINUE_TRANS; + if (*last_was_partial_copy) + ophdr->oh_flags |= XLOG_WAS_CONT_TRANS; + *bytes_consumed += *copy_len; + (*last_was_partial_copy)++; + + /* account for new log op header */ + ticket->t_curr_res -= sizeof(struct xlog_op_header); + ticket->t_res_num_ophdrs++; + + return sizeof(struct xlog_op_header); +} + +static int +xlog_write_copy_finish( + struct log *log, + struct xlog_in_core *iclog, + uint flags, + int *record_cnt, + int *data_cnt, + int *partial_copy, + int *partial_copy_len, + int log_offset, + struct xlog_in_core **commit_iclog) +{ + if (*partial_copy) { + /* + * This iclog has already been marked WANT_SYNC by + * xlog_state_get_iclog_space. + */ + xlog_state_finish_copy(log, iclog, *record_cnt, *data_cnt); + *record_cnt = 0; + *data_cnt = 0; + return xlog_state_release_iclog(log, iclog); + } + + *partial_copy = 0; + *partial_copy_len = 0; + + if (iclog->ic_size - log_offset <= sizeof(xlog_op_header_t)) { + /* no more space in this iclog - push it. */ + xlog_state_finish_copy(log, iclog, *record_cnt, *data_cnt); + *record_cnt = 0; + *data_cnt = 0; + + spin_lock(&log->l_icloglock); + xlog_state_want_sync(log, iclog); + spin_unlock(&log->l_icloglock); + + if (!commit_iclog) + return xlog_state_release_iclog(log, iclog); + ASSERT(flags & XLOG_COMMIT_TRANS); + *commit_iclog = iclog; + } + + return 0; +} + +/* * Write some region out to in-core log * * This will be called when writing externally provided regions or when @@ -1655,209 +1867,157 @@ xlog_print_tic_res(xfs_mount_t *mp, xlog_ticket_t *ticket) */ STATIC int xlog_write( - struct xfs_mount *mp, - struct xfs_log_iovec reg[], - int nentries, + struct log *log, + struct xfs_log_vec *log_vector, struct xlog_ticket *ticket, xfs_lsn_t *start_lsn, struct xlog_in_core **commit_iclog, uint flags) { - xlog_t *log = mp->m_log; - xlog_in_core_t *iclog = NULL; /* ptr to current in-core log */ - xlog_op_header_t *logop_head; /* ptr to log operation header */ - __psint_t ptr; /* copy address into data region */ - int len; /* # xlog_write() bytes 2 still copy */ - int index; /* region index currently copying */ - int log_offset; /* offset (from 0) into data region */ - int start_rec_copy; /* # bytes to copy for start record */ - int partial_copy; /* did we split a region? */ - int partial_copy_len;/* # bytes copied if split region */ - int need_copy; /* # bytes need to memcpy this region */ - int copy_len; /* # bytes actually memcpy'ing */ - int copy_off; /* # bytes from entry start */ - int contwr; /* continued write of in-core log? */ - int error; - int record_cnt = 0, data_cnt = 0; - - partial_copy_len = partial_copy = 0; - - /* Calculate potential maximum space. Each region gets its own - * xlog_op_header_t and may need to be double word aligned. - */ - len = 0; - if (ticket->t_flags & XLOG_TIC_INITED) { /* acct for start rec of xact */ - len += sizeof(xlog_op_header_t); - ticket->t_res_num_ophdrs++; - } + struct xlog_in_core *iclog = NULL; + struct xfs_log_iovec *vecp; + struct xfs_log_vec *lv; + int len; + int index; + int partial_copy = 0; + int partial_copy_len = 0; + int contwr = 0; + int record_cnt = 0; + int data_cnt = 0; + int error; - for (index = 0; index < nentries; index++) { - len += sizeof(xlog_op_header_t); /* each region gets >= 1 */ - ticket->t_res_num_ophdrs++; - len += reg[index].i_len; - xlog_tic_add_region(ticket, reg[index].i_len, reg[index].i_type); - } - contwr = *start_lsn = 0; + *start_lsn = 0; - if (ticket->t_curr_res < len) { - xlog_print_tic_res(mp, ticket); + len = xlog_write_calc_vec_length(ticket, log_vector); + if (ticket->t_curr_res < len) { + xlog_print_tic_res(log->l_mp, ticket); #ifdef DEBUG - xlog_panic( - "xfs_log_write: reservation ran out. Need to up reservation"); + xlog_panic( + "xfs_log_write: reservation ran out. Need to up reservation"); #else - /* Customer configurable panic */ - xfs_cmn_err(XFS_PTAG_LOGRES, CE_ALERT, mp, - "xfs_log_write: reservation ran out. Need to up reservation"); - /* If we did not panic, shutdown the filesystem */ - xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); + /* Customer configurable panic */ + xfs_cmn_err(XFS_PTAG_LOGRES, CE_ALERT, log->l_mp, + "xfs_log_write: reservation ran out. Need to up reservation"); + + /* If we did not panic, shutdown the filesystem */ + xfs_force_shutdown(log->l_mp, SHUTDOWN_CORRUPT_INCORE); #endif - } else + } + ticket->t_curr_res -= len; - for (index = 0; index < nentries; ) { - if ((error = xlog_state_get_iclog_space(log, len, &iclog, ticket, - &contwr, &log_offset))) - return error; + index = 0; + lv = log_vector; + vecp = lv->lv_iovecp; + while (lv && index < lv->lv_niovecs) { + void *ptr; + int log_offset; - ASSERT(log_offset <= iclog->ic_size - 1); - ptr = (__psint_t) ((char *)iclog->ic_datap+log_offset); + error = xlog_state_get_iclog_space(log, len, &iclog, ticket, + &contwr, &log_offset); + if (error) + return error; - /* start_lsn is the first lsn written to. That's all we need. */ - if (! *start_lsn) - *start_lsn = be64_to_cpu(iclog->ic_header.h_lsn); + ASSERT(log_offset <= iclog->ic_size - 1); + ptr = iclog->ic_datap + log_offset; - /* This loop writes out as many regions as can fit in the amount - * of space which was allocated by xlog_state_get_iclog_space(). - */ - while (index < nentries) { - ASSERT(reg[index].i_len % sizeof(__int32_t) == 0); - ASSERT((__psint_t)ptr % sizeof(__int32_t) == 0); - start_rec_copy = 0; - - /* If first write for transaction, insert start record. - * We can't be trying to commit if we are inited. We can't - * have any "partial_copy" if we are inited. - */ - if (ticket->t_flags & XLOG_TIC_INITED) { - logop_head = (xlog_op_header_t *)ptr; - logop_head->oh_tid = cpu_to_be32(ticket->t_tid); - logop_head->oh_clientid = ticket->t_clientid; - logop_head->oh_len = 0; - logop_head->oh_flags = XLOG_START_TRANS; - logop_head->oh_res2 = 0; - ticket->t_flags &= ~XLOG_TIC_INITED; /* clear bit */ - record_cnt++; - - start_rec_copy = sizeof(xlog_op_header_t); - xlog_write_adv_cnt(ptr, len, log_offset, start_rec_copy); - } + /* start_lsn is the first lsn written to. That's all we need. */ + if (!*start_lsn) + *start_lsn = be64_to_cpu(iclog->ic_header.h_lsn); - /* Copy log operation header directly into data section */ - logop_head = (xlog_op_header_t *)ptr; - logop_head->oh_tid = cpu_to_be32(ticket->t_tid); - logop_head->oh_clientid = ticket->t_clientid; - logop_head->oh_res2 = 0; + /* + * This loop writes out as many regions as can fit in the amount + * of space which was allocated by xlog_state_get_iclog_space(). + */ + while (lv && index < lv->lv_niovecs) { + struct xfs_log_iovec *reg = &vecp[index]; + struct xlog_op_header *ophdr; + int start_rec_copy; + int copy_len; + int copy_off; + + ASSERT(reg->i_len % sizeof(__int32_t) == 0); + ASSERT((unsigned long)ptr % sizeof(__int32_t) == 0); + + start_rec_copy = xlog_write_start_rec(ptr, ticket); + if (start_rec_copy) { + record_cnt++; + xlog_write_adv_cnt(&ptr, &len, &log_offset, + start_rec_copy); + } - /* header copied directly */ - xlog_write_adv_cnt(ptr, len, log_offset, sizeof(xlog_op_header_t)); + ophdr = xlog_write_setup_ophdr(log, ptr, ticket, flags); + if (!ophdr) + return XFS_ERROR(EIO); - /* are we copying a commit or unmount record? */ - logop_head->oh_flags = flags; + xlog_write_adv_cnt(&ptr, &len, &log_offset, + sizeof(struct xlog_op_header)); + + len += xlog_write_setup_copy(ticket, ophdr, + iclog->ic_size-log_offset, + reg->i_len, + ©_off, ©_len, + &partial_copy, + &partial_copy_len); + xlog_verify_dest_ptr(log, ptr); + + /* copy region */ + ASSERT(copy_len >= 0); + memcpy(ptr, reg->i_addr + copy_off, copy_len); + xlog_write_adv_cnt(&ptr, &len, &log_offset, copy_len); + + copy_len += start_rec_copy + sizeof(xlog_op_header_t); + record_cnt++; + data_cnt += contwr ? copy_len : 0; + + error = xlog_write_copy_finish(log, iclog, flags, + &record_cnt, &data_cnt, + &partial_copy, + &partial_copy_len, + log_offset, + commit_iclog); + if (error) + return error; - /* - * We've seen logs corrupted with bad transaction client - * ids. This makes sure that XFS doesn't generate them on. - * Turn this into an EIO and shut down the filesystem. - */ - switch (logop_head->oh_clientid) { - case XFS_TRANSACTION: - case XFS_VOLUME: - case XFS_LOG: - break; - default: - xfs_fs_cmn_err(CE_WARN, mp, - "Bad XFS transaction clientid 0x%x in ticket 0x%p", - logop_head->oh_clientid, ticket); - return XFS_ERROR(EIO); - } + /* + * if we had a partial copy, we need to get more iclog + * space but we don't want to increment the region + * index because there is still more is this region to + * write. + * + * If we completed writing this region, and we flushed + * the iclog (indicated by resetting of the record + * count), then we also need to get more log space. If + * this was the last record, though, we are done and + * can just return. + */ + if (partial_copy) + break; - /* Partial write last time? => (partial_copy != 0) - * need_copy is the amount we'd like to copy if everything could - * fit in the current memcpy. - */ - need_copy = reg[index].i_len - partial_copy_len; - - copy_off = partial_copy_len; - if (need_copy <= iclog->ic_size - log_offset) { /*complete write */ - copy_len = need_copy; - logop_head->oh_len = cpu_to_be32(copy_len); - if (partial_copy) - logop_head->oh_flags|= (XLOG_END_TRANS|XLOG_WAS_CONT_TRANS); - partial_copy_len = partial_copy = 0; - } else { /* partial write */ - copy_len = iclog->ic_size - log_offset; - logop_head->oh_len = cpu_to_be32(copy_len); - logop_head->oh_flags |= XLOG_CONTINUE_TRANS; - if (partial_copy) - logop_head->oh_flags |= XLOG_WAS_CONT_TRANS; - partial_copy_len += copy_len; - partial_copy++; - len += sizeof(xlog_op_header_t); /* from splitting of region */ - /* account for new log op header */ - ticket->t_curr_res -= sizeof(xlog_op_header_t); - ticket->t_res_num_ophdrs++; - } - xlog_verify_dest_ptr(log, ptr); - - /* copy region */ - ASSERT(copy_len >= 0); - memcpy((xfs_caddr_t)ptr, reg[index].i_addr + copy_off, copy_len); - xlog_write_adv_cnt(ptr, len, log_offset, copy_len); - - /* make copy_len total bytes copied, including headers */ - copy_len += start_rec_copy + sizeof(xlog_op_header_t); - record_cnt++; - data_cnt += contwr ? copy_len : 0; - if (partial_copy) { /* copied partial region */ - /* already marked WANT_SYNC by xlog_state_get_iclog_space */ - xlog_state_finish_copy(log, iclog, record_cnt, data_cnt); - record_cnt = data_cnt = 0; - if ((error = xlog_state_release_iclog(log, iclog))) - return error; - break; /* don't increment index */ - } else { /* copied entire region */ - index++; - partial_copy_len = partial_copy = 0; - - if (iclog->ic_size - log_offset <= sizeof(xlog_op_header_t)) { - xlog_state_finish_copy(log, iclog, record_cnt, data_cnt); - record_cnt = data_cnt = 0; - spin_lock(&log->l_icloglock); - xlog_state_want_sync(log, iclog); - spin_unlock(&log->l_icloglock); - if (commit_iclog) { - ASSERT(flags & XLOG_COMMIT_TRANS); - *commit_iclog = iclog; - } else if ((error = xlog_state_release_iclog(log, iclog))) - return error; - if (index == nentries) - return 0; /* we are done */ - else - break; + if (++index == lv->lv_niovecs) { + lv = lv->lv_next; + index = 0; + if (lv) + vecp = lv->lv_iovecp; + } + if (record_cnt == 0) { + if (!lv) + return 0; + break; + } } - } /* if (partial_copy) */ - } /* while (index < nentries) */ - } /* for (index = 0; index < nentries; ) */ - ASSERT(len == 0); + } + + ASSERT(len == 0); + + xlog_state_finish_copy(log, iclog, record_cnt, data_cnt); + if (!commit_iclog) + return xlog_state_release_iclog(log, iclog); - xlog_state_finish_copy(log, iclog, record_cnt, data_cnt); - if (commit_iclog) { ASSERT(flags & XLOG_COMMIT_TRANS); *commit_iclog = iclog; return 0; - } - return xlog_state_release_iclog(log, iclog); -} /* xlog_write */ +} /***************************************************************************** @@ -3157,14 +3317,16 @@ xfs_log_ticket_get( * Allocate and initialise a new log ticket. */ STATIC xlog_ticket_t * -xlog_ticket_alloc(xlog_t *log, - int unit_bytes, - int cnt, - char client, - uint xflags) +xlog_ticket_alloc( + struct log *log, + int unit_bytes, + int cnt, + char client, + uint xflags) { - xlog_ticket_t *tic; + struct xlog_ticket *tic; uint num_headers; + int iclog_space; tic = kmem_zone_zalloc(xfs_log_ticket_zone, KM_SLEEP|KM_MAYFAIL); if (!tic) @@ -3208,16 +3370,40 @@ xlog_ticket_alloc(xlog_t *log, /* for start-rec */ unit_bytes += sizeof(xlog_op_header_t); - /* for LR headers */ - num_headers = ((unit_bytes + log->l_iclog_size-1) >> log->l_iclog_size_log); + /* + * for LR headers - the space for data in an iclog is the size minus + * the space used for the headers. If we use the iclog size, then we + * undercalculate the number of headers required. + * + * Furthermore - the addition of op headers for split-recs might + * increase the space required enough to require more log and op + * headers, so take that into account too. + * + * IMPORTANT: This reservation makes the assumption that if this + * transaction is the first in an iclog and hence has the LR headers + * accounted to it, then the remaining space in the iclog is + * exclusively for this transaction. i.e. if the transaction is larger + * than the iclog, it will be the only thing in that iclog. + * Fundamentally, this means we must pass the entire log vector to + * xlog_write to guarantee this. + */ + iclog_space = log->l_iclog_size - log->l_iclog_hsize; + num_headers = howmany(unit_bytes, iclog_space); + + /* for split-recs - ophdrs added when data split over LRs */ + unit_bytes += sizeof(xlog_op_header_t) * num_headers; + + /* add extra header reservations if we overrun */ + while (!num_headers || + howmany(unit_bytes, iclog_space) > num_headers) { + unit_bytes += sizeof(xlog_op_header_t); + num_headers++; + } unit_bytes += log->l_iclog_hsize * num_headers; /* for commit-rec LR header - note: padding will subsume the ophdr */ unit_bytes += log->l_iclog_hsize; - /* for split-recs - ophdrs added when data split over LRs */ - unit_bytes += sizeof(xlog_op_header_t) * num_headers; - /* for roundoff padding for transaction data and one for commit record */ if (xfs_sb_version_haslogv2(&log->l_mp->m_sb) && log->l_mp->m_sb.sb_logsunit > 1) { @@ -3233,13 +3419,13 @@ xlog_ticket_alloc(xlog_t *log, tic->t_curr_res = unit_bytes; tic->t_cnt = cnt; tic->t_ocnt = cnt; - tic->t_tid = (xlog_tid_t)((__psint_t)tic & 0xffffffff); + tic->t_tid = random32(); tic->t_clientid = client; tic->t_flags = XLOG_TIC_INITED; tic->t_trans_type = 0; if (xflags & XFS_LOG_PERM_RESERV) tic->t_flags |= XLOG_TIC_PERM_RESERV; - sv_init(&(tic->t_wait), SV_DEFAULT, "logtick"); + sv_init(&tic->t_wait, SV_DEFAULT, "logtick"); xlog_tic_reset_res(tic); @@ -3260,20 +3446,22 @@ xlog_ticket_alloc(xlog_t *log, * part of the log in case we trash the log structure. */ void -xlog_verify_dest_ptr(xlog_t *log, - __psint_t ptr) +xlog_verify_dest_ptr( + struct log *log, + char *ptr) { int i; int good_ptr = 0; - for (i=0; i < log->l_iclog_bufs; i++) { - if (ptr >= (__psint_t)log->l_iclog_bak[i] && - ptr <= (__psint_t)log->l_iclog_bak[i]+log->l_iclog_size) + for (i = 0; i < log->l_iclog_bufs; i++) { + if (ptr >= log->l_iclog_bak[i] && + ptr <= log->l_iclog_bak[i] + log->l_iclog_size) good_ptr++; } - if (! good_ptr) + + if (!good_ptr) xlog_panic("xlog_verify_dest_ptr: invalid ptr"); -} /* xlog_verify_dest_ptr */ +} STATIC void xlog_verify_grant_head(xlog_t *log, int equals) diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index 97a24c7795a..229d1f36ba9 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -110,6 +110,12 @@ typedef struct xfs_log_iovec { uint i_type; /* type of region */ } xfs_log_iovec_t; +struct xfs_log_vec { + struct xfs_log_vec *lv_next; /* next lv in build list */ + int lv_niovecs; /* number of iovecs in lv */ + struct xfs_log_iovec *lv_iovecp; /* iovec array */ +}; + /* * Structure used to pass callback function and the function's argument * to the log manager. @@ -126,6 +132,13 @@ typedef struct xfs_log_callback { struct xfs_mount; struct xlog_in_core; struct xlog_ticket; +struct xfs_log_item; +struct xfs_item_ops; + +void xfs_log_item_init(struct xfs_mount *mp, + struct xfs_log_item *item, + int type, + struct xfs_item_ops *ops); xfs_lsn_t xfs_log_done(struct xfs_mount *mp, struct xlog_ticket *ticket, diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h index fd02a18facd..9cf69515445 100644 --- a/fs/xfs/xfs_log_priv.h +++ b/fs/xfs/xfs_log_priv.h @@ -396,9 +396,7 @@ typedef struct log { struct xfs_buf_cancel **l_buf_cancel_table; int l_iclog_hsize; /* size of iclog header */ int l_iclog_heads; /* # of iclog header sectors */ - uint l_sectbb_log; /* log2 of sector size in BBs */ - uint l_sectbb_mask; /* sector size (in BBs) - * alignment mask */ + uint l_sectBBsize; /* sector size in BBs (2^n) */ int l_iclog_size; /* size of log in bytes */ int l_iclog_size_log; /* log power size of log */ int l_iclog_bufs; /* number of iclog buffers */ @@ -449,6 +447,14 @@ extern void xlog_pack_data(xlog_t *log, xlog_in_core_t *iclog, int); extern kmem_zone_t *xfs_log_ticket_zone; +static inline void +xlog_write_adv_cnt(void **ptr, int *len, int *off, size_t bytes) +{ + *ptr += bytes; + *len -= bytes; + *off += bytes; +} + /* * Unmount record type is used as a pseudo transaction type for the ticket. * It's value must be outside the range of XFS_TRANS_* values. diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 22e6efdc17e..0de08e36631 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -56,33 +56,61 @@ STATIC void xlog_recover_check_summary(xlog_t *); #define xlog_recover_check_summary(log) #endif - /* * Sector aligned buffer routines for buffer create/read/write/access */ -#define XLOG_SECTOR_ROUNDUP_BBCOUNT(log, bbs) \ - ( ((log)->l_sectbb_mask && (bbs & (log)->l_sectbb_mask)) ? \ - ((bbs + (log)->l_sectbb_mask + 1) & ~(log)->l_sectbb_mask) : (bbs) ) -#define XLOG_SECTOR_ROUNDDOWN_BLKNO(log, bno) ((bno) & ~(log)->l_sectbb_mask) +/* + * Verify the given count of basic blocks is valid number of blocks + * to specify for an operation involving the given XFS log buffer. + * Returns nonzero if the count is valid, 0 otherwise. + */ +static inline int +xlog_buf_bbcount_valid( + xlog_t *log, + int bbcount) +{ + return bbcount > 0 && bbcount <= log->l_logBBsize; +} + +/* + * Allocate a buffer to hold log data. The buffer needs to be able + * to map to a range of nbblks basic blocks at any valid (basic + * block) offset within the log. + */ STATIC xfs_buf_t * xlog_get_bp( xlog_t *log, int nbblks) { - if (nbblks <= 0 || nbblks > log->l_logBBsize) { - xlog_warn("XFS: Invalid block length (0x%x) given for buffer", nbblks); - XFS_ERROR_REPORT("xlog_get_bp(1)", - XFS_ERRLEVEL_HIGH, log->l_mp); + if (!xlog_buf_bbcount_valid(log, nbblks)) { + xlog_warn("XFS: Invalid block length (0x%x) given for buffer", + nbblks); + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_HIGH, log->l_mp); return NULL; } - if (log->l_sectbb_log) { - if (nbblks > 1) - nbblks += XLOG_SECTOR_ROUNDUP_BBCOUNT(log, 1); - nbblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, nbblks); - } + /* + * We do log I/O in units of log sectors (a power-of-2 + * multiple of the basic block size), so we round up the + * requested size to acommodate the basic blocks required + * for complete log sectors. + * + * In addition, the buffer may be used for a non-sector- + * aligned block offset, in which case an I/O of the + * requested size could extend beyond the end of the + * buffer. If the requested size is only 1 basic block it + * will never straddle a sector boundary, so this won't be + * an issue. Nor will this be a problem if the log I/O is + * done in basic blocks (sector size 1). But otherwise we + * extend the buffer by one extra log sector to ensure + * there's space to accomodate this possiblility. + */ + if (nbblks > 1 && log->l_sectBBsize > 1) + nbblks += log->l_sectBBsize; + nbblks = round_up(nbblks, log->l_sectBBsize); + return xfs_buf_get_noaddr(BBTOB(nbblks), log->l_mp->m_logdev_targp); } @@ -93,6 +121,10 @@ xlog_put_bp( xfs_buf_free(bp); } +/* + * Return the address of the start of the given block number's data + * in a log buffer. The buffer covers a log sector-aligned region. + */ STATIC xfs_caddr_t xlog_align( xlog_t *log, @@ -100,14 +132,14 @@ xlog_align( int nbblks, xfs_buf_t *bp) { + xfs_daddr_t offset; xfs_caddr_t ptr; - if (!log->l_sectbb_log) - return XFS_BUF_PTR(bp); + offset = blk_no & ((xfs_daddr_t) log->l_sectBBsize - 1); + ptr = XFS_BUF_PTR(bp) + BBTOB(offset); + + ASSERT(ptr + BBTOB(nbblks) <= XFS_BUF_PTR(bp) + XFS_BUF_SIZE(bp)); - ptr = XFS_BUF_PTR(bp) + BBTOB((int)blk_no & log->l_sectbb_mask); - ASSERT(XFS_BUF_SIZE(bp) >= - BBTOB(nbblks + (blk_no & log->l_sectbb_mask))); return ptr; } @@ -124,21 +156,18 @@ xlog_bread_noalign( { int error; - if (nbblks <= 0 || nbblks > log->l_logBBsize) { - xlog_warn("XFS: Invalid block length (0x%x) given for buffer", nbblks); - XFS_ERROR_REPORT("xlog_bread(1)", - XFS_ERRLEVEL_HIGH, log->l_mp); + if (!xlog_buf_bbcount_valid(log, nbblks)) { + xlog_warn("XFS: Invalid block length (0x%x) given for buffer", + nbblks); + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_HIGH, log->l_mp); return EFSCORRUPTED; } - if (log->l_sectbb_log) { - blk_no = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, blk_no); - nbblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, nbblks); - } + blk_no = round_down(blk_no, log->l_sectBBsize); + nbblks = round_up(nbblks, log->l_sectBBsize); ASSERT(nbblks > 0); ASSERT(BBTOB(nbblks) <= XFS_BUF_SIZE(bp)); - ASSERT(bp); XFS_BUF_SET_ADDR(bp, log->l_logBBstart + blk_no); XFS_BUF_READ(bp); @@ -186,17 +215,15 @@ xlog_bwrite( { int error; - if (nbblks <= 0 || nbblks > log->l_logBBsize) { - xlog_warn("XFS: Invalid block length (0x%x) given for buffer", nbblks); - XFS_ERROR_REPORT("xlog_bwrite(1)", - XFS_ERRLEVEL_HIGH, log->l_mp); + if (!xlog_buf_bbcount_valid(log, nbblks)) { + xlog_warn("XFS: Invalid block length (0x%x) given for buffer", + nbblks); + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_HIGH, log->l_mp); return EFSCORRUPTED; } - if (log->l_sectbb_log) { - blk_no = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, blk_no); - nbblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, nbblks); - } + blk_no = round_down(blk_no, log->l_sectBBsize); + nbblks = round_up(nbblks, log->l_sectBBsize); ASSERT(nbblks > 0); ASSERT(BBTOB(nbblks) <= XFS_BUF_SIZE(bp)); @@ -327,39 +354,38 @@ xlog_find_cycle_start( { xfs_caddr_t offset; xfs_daddr_t mid_blk; + xfs_daddr_t end_blk; uint mid_cycle; int error; - mid_blk = BLK_AVG(first_blk, *last_blk); - while (mid_blk != first_blk && mid_blk != *last_blk) { + end_blk = *last_blk; + mid_blk = BLK_AVG(first_blk, end_blk); + while (mid_blk != first_blk && mid_blk != end_blk) { error = xlog_bread(log, mid_blk, 1, bp, &offset); if (error) return error; mid_cycle = xlog_get_cycle(offset); - if (mid_cycle == cycle) { - *last_blk = mid_blk; - /* last_half_cycle == mid_cycle */ - } else { - first_blk = mid_blk; - /* first_half_cycle == mid_cycle */ - } - mid_blk = BLK_AVG(first_blk, *last_blk); + if (mid_cycle == cycle) + end_blk = mid_blk; /* last_half_cycle == mid_cycle */ + else + first_blk = mid_blk; /* first_half_cycle == mid_cycle */ + mid_blk = BLK_AVG(first_blk, end_blk); } - ASSERT((mid_blk == first_blk && mid_blk+1 == *last_blk) || - (mid_blk == *last_blk && mid_blk-1 == first_blk)); + ASSERT((mid_blk == first_blk && mid_blk+1 == end_blk) || + (mid_blk == end_blk && mid_blk-1 == first_blk)); + + *last_blk = end_blk; return 0; } /* - * Check that the range of blocks does not contain the cycle number - * given. The scan needs to occur from front to back and the ptr into the - * region must be updated since a later routine will need to perform another - * test. If the region is completely good, we end up returning the same - * last block number. - * - * Set blkno to -1 if we encounter no errors. This is an invalid block number - * since we don't ever expect logs to get this large. + * Check that a range of blocks does not contain stop_on_cycle_no. + * Fill in *new_blk with the block offset where such a block is + * found, or with -1 (an invalid block number) if there is no such + * block in the range. The scan needs to occur from front to back + * and the pointer into the region must be updated since a later + * routine will need to perform another test. */ STATIC int xlog_find_verify_cycle( @@ -376,12 +402,16 @@ xlog_find_verify_cycle( xfs_caddr_t buf = NULL; int error = 0; + /* + * Greedily allocate a buffer big enough to handle the full + * range of basic blocks we'll be examining. If that fails, + * try a smaller size. We need to be able to read at least + * a log sector, or we're out of luck. + */ bufblks = 1 << ffs(nbblks); - while (!(bp = xlog_get_bp(log, bufblks))) { - /* can't get enough memory to do everything in one big buffer */ bufblks >>= 1; - if (bufblks <= log->l_sectbb_log) + if (bufblks < log->l_sectBBsize) return ENOMEM; } @@ -629,7 +659,7 @@ xlog_find_head( * In this case we want to find the first block with cycle * number matching last_half_cycle. We expect the log to be * some variation on - * x + 1 ... | x ... + * x + 1 ... | x ... | x * The first block with cycle number x (last_half_cycle) will * be where the new head belongs. First we do a binary search * for the first occurrence of last_half_cycle. The binary @@ -639,11 +669,13 @@ xlog_find_head( * the log, then we look for occurrences of last_half_cycle - 1 * at the end of the log. The cases we're looking for look * like - * x + 1 ... | x | x + 1 | x ... - * ^ binary search stopped here + * v binary search stopped here + * x + 1 ... | x | x + 1 | x ... | x + * ^ but we want to locate this spot * or - * x + 1 ... | x ... | x - 1 | x * <---------> less than scan distance + * x + 1 ... | x ... | x - 1 | x + * ^ we want to locate this spot */ stop_on_cycle = last_half_cycle; if ((error = xlog_find_cycle_start(log, bp, first_blk, @@ -699,16 +731,16 @@ xlog_find_head( * certainly not the head of the log. By searching for * last_half_cycle-1 we accomplish that. */ - start_blk = log_bbnum - num_scan_bblks + head_blk; ASSERT(head_blk <= INT_MAX && - (xfs_daddr_t) num_scan_bblks - head_blk >= 0); + (xfs_daddr_t) num_scan_bblks >= head_blk); + start_blk = log_bbnum - (num_scan_bblks - head_blk); if ((error = xlog_find_verify_cycle(log, start_blk, num_scan_bblks - (int)head_blk, (stop_on_cycle - 1), &new_blk))) goto bp_err; if (new_blk != -1) { head_blk = new_blk; - goto bad_blk; + goto validate_head; } /* @@ -726,7 +758,7 @@ xlog_find_head( head_blk = new_blk; } - bad_blk: +validate_head: /* * Now we need to make sure head_blk is not pointing to a block in * the middle of a log record. @@ -748,7 +780,7 @@ xlog_find_head( if ((error = xlog_find_verify_log_record(log, start_blk, &head_blk, 0)) == -1) { /* We hit the beginning of the log during our search */ - start_blk = log_bbnum - num_scan_bblks + head_blk; + start_blk = log_bbnum - (num_scan_bblks - head_blk); new_blk = log_bbnum; ASSERT(start_blk <= INT_MAX && (xfs_daddr_t) log_bbnum-start_blk >= 0); @@ -833,12 +865,12 @@ xlog_find_tail( if (*head_blk == 0) { /* special case */ error = xlog_bread(log, 0, 1, bp, &offset); if (error) - goto bread_err; + goto done; if (xlog_get_cycle(offset) == 0) { *tail_blk = 0; /* leave all other log inited values alone */ - goto exit; + goto done; } } @@ -849,7 +881,7 @@ xlog_find_tail( for (i = (int)(*head_blk) - 1; i >= 0; i--) { error = xlog_bread(log, i, 1, bp, &offset); if (error) - goto bread_err; + goto done; if (XLOG_HEADER_MAGIC_NUM == be32_to_cpu(*(__be32 *)offset)) { found = 1; @@ -866,7 +898,7 @@ xlog_find_tail( for (i = log->l_logBBsize - 1; i >= (int)(*head_blk); i--) { error = xlog_bread(log, i, 1, bp, &offset); if (error) - goto bread_err; + goto done; if (XLOG_HEADER_MAGIC_NUM == be32_to_cpu(*(__be32 *)offset)) { @@ -941,7 +973,7 @@ xlog_find_tail( umount_data_blk = (i + hblks) % log->l_logBBsize; error = xlog_bread(log, umount_data_blk, 1, bp, &offset); if (error) - goto bread_err; + goto done; op_head = (xlog_op_header_t *)offset; if (op_head->oh_flags & XLOG_UNMOUNT_TRANS) { @@ -987,12 +1019,10 @@ xlog_find_tail( * But... if the -device- itself is readonly, just skip this. * We can't recover this device anyway, so it won't matter. */ - if (!xfs_readonly_buftarg(log->l_mp->m_logdev_targp)) { + if (!xfs_readonly_buftarg(log->l_mp->m_logdev_targp)) error = xlog_clear_stale_blocks(log, tail_lsn); - } -bread_err: -exit: +done: xlog_put_bp(bp); if (error) @@ -1152,16 +1182,22 @@ xlog_write_log_records( xfs_caddr_t offset; xfs_buf_t *bp; int balign, ealign; - int sectbb = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, 1); + int sectbb = log->l_sectBBsize; int end_block = start_block + blocks; int bufblks; int error = 0; int i, j = 0; + /* + * Greedily allocate a buffer big enough to handle the full + * range of basic blocks to be written. If that fails, try + * a smaller size. We need to be able to write at least a + * log sector, or we're out of luck. + */ bufblks = 1 << ffs(blocks); while (!(bp = xlog_get_bp(log, bufblks))) { bufblks >>= 1; - if (bufblks <= log->l_sectbb_log) + if (bufblks < sectbb) return ENOMEM; } @@ -1169,7 +1205,7 @@ xlog_write_log_records( * the buffer in the starting sector not covered by the first * write below. */ - balign = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, start_block); + balign = round_down(start_block, sectbb); if (balign != start_block) { error = xlog_bread_noalign(log, start_block, 1, bp); if (error) @@ -1188,7 +1224,7 @@ xlog_write_log_records( * the buffer in the final sector not covered by the write. * If this is the same sector as the above read, skip it. */ - ealign = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, end_block); + ealign = round_down(end_block, sectbb); if (j == 0 && (start_block + endcount > ealign)) { offset = XFS_BUF_PTR(bp); balign = BBTOB(ealign - start_block); @@ -1408,6 +1444,7 @@ xlog_recover_add_item( STATIC int xlog_recover_add_to_cont_trans( + struct log *log, xlog_recover_t *trans, xfs_caddr_t dp, int len) @@ -1434,6 +1471,7 @@ xlog_recover_add_to_cont_trans( memcpy(&ptr[old_len], dp, len); /* d, s, l */ item->ri_buf[item->ri_cnt-1].i_len += len; item->ri_buf[item->ri_cnt-1].i_addr = ptr; + trace_xfs_log_recover_item_add_cont(log, trans, item, 0); return 0; } @@ -1452,6 +1490,7 @@ xlog_recover_add_to_cont_trans( */ STATIC int xlog_recover_add_to_trans( + struct log *log, xlog_recover_t *trans, xfs_caddr_t dp, int len) @@ -1510,6 +1549,7 @@ xlog_recover_add_to_trans( item->ri_buf[item->ri_cnt].i_addr = ptr; item->ri_buf[item->ri_cnt].i_len = len; item->ri_cnt++; + trace_xfs_log_recover_item_add(log, trans, item, 0); return 0; } @@ -1521,7 +1561,9 @@ xlog_recover_add_to_trans( */ STATIC int xlog_recover_reorder_trans( - xlog_recover_t *trans) + struct log *log, + xlog_recover_t *trans, + int pass) { xlog_recover_item_t *item, *n; LIST_HEAD(sort_list); @@ -1535,6 +1577,8 @@ xlog_recover_reorder_trans( switch (ITEM_TYPE(item)) { case XFS_LI_BUF: if (!(buf_f->blf_flags & XFS_BLI_CANCEL)) { + trace_xfs_log_recover_item_reorder_head(log, + trans, item, pass); list_move(&item->ri_list, &trans->r_itemq); break; } @@ -1543,6 +1587,8 @@ xlog_recover_reorder_trans( case XFS_LI_QUOTAOFF: case XFS_LI_EFD: case XFS_LI_EFI: + trace_xfs_log_recover_item_reorder_tail(log, + trans, item, pass); list_move_tail(&item->ri_list, &trans->r_itemq); break; default: @@ -1592,8 +1638,10 @@ xlog_recover_do_buffer_pass1( /* * If this isn't a cancel buffer item, then just return. */ - if (!(flags & XFS_BLI_CANCEL)) + if (!(flags & XFS_BLI_CANCEL)) { + trace_xfs_log_recover_buf_not_cancel(log, buf_f); return; + } /* * Insert an xfs_buf_cancel record into the hash table of @@ -1627,6 +1675,7 @@ xlog_recover_do_buffer_pass1( while (nextp != NULL) { if (nextp->bc_blkno == blkno && nextp->bc_len == len) { nextp->bc_refcount++; + trace_xfs_log_recover_buf_cancel_ref_inc(log, buf_f); return; } prevp = nextp; @@ -1640,6 +1689,7 @@ xlog_recover_do_buffer_pass1( bcp->bc_refcount = 1; bcp->bc_next = NULL; prevp->bc_next = bcp; + trace_xfs_log_recover_buf_cancel_add(log, buf_f); } /* @@ -1779,6 +1829,8 @@ xlog_recover_do_inode_buffer( unsigned int *data_map = NULL; unsigned int map_size = 0; + trace_xfs_log_recover_buf_inode_buf(mp->m_log, buf_f); + switch (buf_f->blf_type) { case XFS_LI_BUF: data_map = buf_f->blf_data_map; @@ -1874,6 +1926,7 @@ xlog_recover_do_inode_buffer( /*ARGSUSED*/ STATIC void xlog_recover_do_reg_buffer( + struct xfs_mount *mp, xlog_recover_item_t *item, xfs_buf_t *bp, xfs_buf_log_format_t *buf_f) @@ -1885,6 +1938,8 @@ xlog_recover_do_reg_buffer( unsigned int map_size = 0; int error; + trace_xfs_log_recover_buf_reg_buf(mp->m_log, buf_f); + switch (buf_f->blf_type) { case XFS_LI_BUF: data_map = buf_f->blf_data_map; @@ -2083,6 +2138,8 @@ xlog_recover_do_dquot_buffer( { uint type; + trace_xfs_log_recover_buf_dquot_buf(log, buf_f); + /* * Filesystems are required to send in quota flags at mount time. */ @@ -2103,7 +2160,7 @@ xlog_recover_do_dquot_buffer( if (log->l_quotaoffs_flag & type) return; - xlog_recover_do_reg_buffer(item, bp, buf_f); + xlog_recover_do_reg_buffer(mp, item, bp, buf_f); } /* @@ -2164,9 +2221,11 @@ xlog_recover_do_buffer_trans( */ cancel = xlog_recover_do_buffer_pass2(log, buf_f); if (cancel) { + trace_xfs_log_recover_buf_cancel(log, buf_f); return 0; } } + trace_xfs_log_recover_buf_recover(log, buf_f); switch (buf_f->blf_type) { case XFS_LI_BUF: blkno = buf_f->blf_blkno; @@ -2204,7 +2263,7 @@ xlog_recover_do_buffer_trans( (XFS_BLI_UDQUOT_BUF|XFS_BLI_PDQUOT_BUF|XFS_BLI_GDQUOT_BUF)) { xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f); } else { - xlog_recover_do_reg_buffer(item, bp, buf_f); + xlog_recover_do_reg_buffer(mp, item, bp, buf_f); } if (error) return XFS_ERROR(error); @@ -2284,8 +2343,10 @@ xlog_recover_do_inode_trans( if (xlog_check_buffer_cancelled(log, in_f->ilf_blkno, in_f->ilf_len, 0)) { error = 0; + trace_xfs_log_recover_inode_cancel(log, in_f); goto error; } + trace_xfs_log_recover_inode_recover(log, in_f); bp = xfs_buf_read(mp->m_ddev_targp, in_f->ilf_blkno, in_f->ilf_len, XBF_LOCK); @@ -2337,6 +2398,7 @@ xlog_recover_do_inode_trans( /* do nothing */ } else { xfs_buf_relse(bp); + trace_xfs_log_recover_inode_skip(log, in_f); error = 0; goto error; } @@ -2758,11 +2820,12 @@ xlog_recover_do_trans( int error = 0; xlog_recover_item_t *item; - error = xlog_recover_reorder_trans(trans); + error = xlog_recover_reorder_trans(log, trans, pass); if (error) return error; list_for_each_entry(item, &trans->r_itemq, ri_list) { + trace_xfs_log_recover_item_recover(log, trans, item, pass); switch (ITEM_TYPE(item)) { case XFS_LI_BUF: error = xlog_recover_do_buffer_trans(log, item, pass); @@ -2919,8 +2982,9 @@ xlog_recover_process_data( error = xlog_recover_unmount_trans(trans); break; case XLOG_WAS_CONT_TRANS: - error = xlog_recover_add_to_cont_trans(trans, - dp, be32_to_cpu(ohead->oh_len)); + error = xlog_recover_add_to_cont_trans(log, + trans, dp, + be32_to_cpu(ohead->oh_len)); break; case XLOG_START_TRANS: xlog_warn( @@ -2930,7 +2994,7 @@ xlog_recover_process_data( break; case 0: case XLOG_CONTINUE_TRANS: - error = xlog_recover_add_to_trans(trans, + error = xlog_recover_add_to_trans(log, trans, dp, be32_to_cpu(ohead->oh_len)); break; default: @@ -3331,42 +3395,6 @@ xlog_pack_data( } } -#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) -STATIC void -xlog_unpack_data_checksum( - xlog_rec_header_t *rhead, - xfs_caddr_t dp, - xlog_t *log) -{ - __be32 *up = (__be32 *)dp; - uint chksum = 0; - int i; - - /* divide length by 4 to get # words */ - for (i=0; i < be32_to_cpu(rhead->h_len) >> 2; i++) { - chksum ^= be32_to_cpu(*up); - up++; - } - if (chksum != be32_to_cpu(rhead->h_chksum)) { - if (rhead->h_chksum || - ((log->l_flags & XLOG_CHKSUM_MISMATCH) == 0)) { - cmn_err(CE_DEBUG, - "XFS: LogR chksum mismatch: was (0x%x) is (0x%x)\n", - be32_to_cpu(rhead->h_chksum), chksum); - cmn_err(CE_DEBUG, -"XFS: Disregard message if filesystem was created with non-DEBUG kernel"); - if (xfs_sb_version_haslogv2(&log->l_mp->m_sb)) { - cmn_err(CE_DEBUG, - "XFS: LogR this is a LogV2 filesystem\n"); - } - log->l_flags |= XLOG_CHKSUM_MISMATCH; - } - } -} -#else -#define xlog_unpack_data_checksum(rhead, dp, log) -#endif - STATIC void xlog_unpack_data( xlog_rec_header_t *rhead, @@ -3390,8 +3418,6 @@ xlog_unpack_data( dp += BBSIZE; } } - - xlog_unpack_data_checksum(rhead, dp, log); } STATIC int @@ -3490,7 +3516,7 @@ xlog_do_recovery_pass( hblks = 1; } } else { - ASSERT(log->l_sectbb_log == 0); + ASSERT(log->l_sectBBsize == 1); hblks = 1; hbp = xlog_get_bp(log, 1); h_size = XLOG_BIG_RECORD_BSIZE; @@ -3946,10 +3972,6 @@ xlog_recover_check_summary( xfs_agf_t *agfp; xfs_buf_t *agfbp; xfs_buf_t *agibp; - xfs_buf_t *sbbp; -#ifdef XFS_LOUD_RECOVERY - xfs_sb_t *sbp; -#endif xfs_agnumber_t agno; __uint64_t freeblks; __uint64_t itotal; @@ -3984,30 +4006,5 @@ xlog_recover_check_summary( xfs_buf_relse(agibp); } } - - sbbp = xfs_getsb(mp, 0); -#ifdef XFS_LOUD_RECOVERY - sbp = &mp->m_sb; - xfs_sb_from_disk(sbp, XFS_BUF_TO_SBP(sbbp)); - cmn_err(CE_NOTE, - "xlog_recover_check_summary: sb_icount %Lu itotal %Lu", - sbp->sb_icount, itotal); - cmn_err(CE_NOTE, - "xlog_recover_check_summary: sb_ifree %Lu itotal %Lu", - sbp->sb_ifree, ifree); - cmn_err(CE_NOTE, - "xlog_recover_check_summary: sb_fdblocks %Lu freeblks %Lu", - sbp->sb_fdblocks, freeblks); -#if 0 - /* - * This is turned off until I account for the allocation - * btree blocks which live in free space. - */ - ASSERT(sbp->sb_icount == itotal); - ASSERT(sbp->sb_ifree == ifree); - ASSERT(sbp->sb_fdblocks == freeblks); -#endif -#endif - xfs_buf_relse(sbbp); } #endif /* DEBUG */ diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index e79b56b4bca..d7bf38c8cd1 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1405,13 +1405,6 @@ xfs_mountfs( xfs_qm_mount_quotas(mp); } -#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) - if (XFS_IS_QUOTA_ON(mp)) - xfs_fs_cmn_err(CE_NOTE, mp, "Disk quotas turned on"); - else - xfs_fs_cmn_err(CE_NOTE, mp, "Disk quotas not turned on"); -#endif - /* * Now we are mounted, reserve a small amount of unused space for * privileged transactions. This is needed so that transaction diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h index fdcab3f81dd..e0e64b113bd 100644 --- a/fs/xfs/xfs_quota.h +++ b/fs/xfs/xfs_quota.h @@ -201,9 +201,6 @@ typedef struct xfs_qoff_logformat { #define XFS_QMOPT_FORCE_RES 0x0000010 /* ignore quota limits */ #define XFS_QMOPT_DQSUSER 0x0000020 /* don't cache super users dquot */ #define XFS_QMOPT_SBVERSION 0x0000040 /* change superblock version num */ -#define XFS_QMOPT_QUOTAOFF 0x0000080 /* quotas are being turned off */ -#define XFS_QMOPT_UMOUNTING 0x0000100 /* filesys is being unmounted */ -#define XFS_QMOPT_DOLOG 0x0000200 /* log buf changes (in quotacheck) */ #define XFS_QMOPT_DOWARN 0x0000400 /* increase warning cnt if needed */ #define XFS_QMOPT_DQREPAIR 0x0001000 /* repair dquot if damaged */ #define XFS_QMOPT_GQUOTA 0x0002000 /* group dquot requested */ diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index f73e358bae8..be578ecb4af 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -45,23 +45,12 @@ #include "xfs_trans_space.h" #include "xfs_inode_item.h" - -STATIC void xfs_trans_apply_sb_deltas(xfs_trans_t *); -STATIC uint xfs_trans_count_vecs(xfs_trans_t *); -STATIC void xfs_trans_fill_vecs(xfs_trans_t *, xfs_log_iovec_t *); -STATIC void xfs_trans_uncommit(xfs_trans_t *, uint); -STATIC void xfs_trans_committed(xfs_trans_t *, int); -STATIC void xfs_trans_chunk_committed(xfs_log_item_chunk_t *, xfs_lsn_t, int); -STATIC void xfs_trans_free(xfs_trans_t *); - kmem_zone_t *xfs_trans_zone; - /* * Reservation functions here avoid a huge stack in xfs_trans_init * due to register overflow from temporaries in the calculations. */ - STATIC uint xfs_calc_write_reservation(xfs_mount_t *mp) { @@ -261,6 +250,19 @@ _xfs_trans_alloc( } /* + * Free the transaction structure. If there is more clean up + * to do when the structure is freed, add it here. + */ +STATIC void +xfs_trans_free( + xfs_trans_t *tp) +{ + atomic_dec(&tp->t_mountp->m_active_trans); + xfs_trans_free_dqinfo(tp); + kmem_zone_free(xfs_trans_zone, tp); +} + +/* * This is called to create a new transaction which will share the * permanent log reservation of the given transaction. The remaining * unused block and rt extent reservations are also inherited. This @@ -764,94 +766,278 @@ xfs_trans_unreserve_and_mod_sb( } } +/* + * Total up the number of log iovecs needed to commit this + * transaction. The transaction itself needs one for the + * transaction header. Ask each dirty item in turn how many + * it needs to get the total. + */ +static uint +xfs_trans_count_vecs( + struct xfs_trans *tp) +{ + int nvecs; + xfs_log_item_desc_t *lidp; + + nvecs = 1; + lidp = xfs_trans_first_item(tp); + ASSERT(lidp != NULL); + + /* In the non-debug case we need to start bailing out if we + * didn't find a log_item here, return zero and let trans_commit + * deal with it. + */ + if (lidp == NULL) + return 0; + + while (lidp != NULL) { + /* + * Skip items which aren't dirty in this transaction. + */ + if (!(lidp->lid_flags & XFS_LID_DIRTY)) { + lidp = xfs_trans_next_item(tp, lidp); + continue; + } + lidp->lid_size = IOP_SIZE(lidp->lid_item); + nvecs += lidp->lid_size; + lidp = xfs_trans_next_item(tp, lidp); + } + + return nvecs; +} /* - * xfs_trans_commit + * Fill in the vector with pointers to data to be logged + * by this transaction. The transaction header takes + * the first vector, and then each dirty item takes the + * number of vectors it indicated it needed in xfs_trans_count_vecs(). * - * Commit the given transaction to the log a/synchronously. + * As each item fills in the entries it needs, also pin the item + * so that it cannot be flushed out until the log write completes. + */ +static void +xfs_trans_fill_vecs( + struct xfs_trans *tp, + struct xfs_log_iovec *log_vector) +{ + xfs_log_item_desc_t *lidp; + struct xfs_log_iovec *vecp; + uint nitems; + + /* + * Skip over the entry for the transaction header, we'll + * fill that in at the end. + */ + vecp = log_vector + 1; + + nitems = 0; + lidp = xfs_trans_first_item(tp); + ASSERT(lidp); + while (lidp) { + /* Skip items which aren't dirty in this transaction. */ + if (!(lidp->lid_flags & XFS_LID_DIRTY)) { + lidp = xfs_trans_next_item(tp, lidp); + continue; + } + + /* + * The item may be marked dirty but not log anything. This can + * be used to get called when a transaction is committed. + */ + if (lidp->lid_size) + nitems++; + IOP_FORMAT(lidp->lid_item, vecp); + vecp += lidp->lid_size; + IOP_PIN(lidp->lid_item); + lidp = xfs_trans_next_item(tp, lidp); + } + + /* + * Now that we've counted the number of items in this transaction, fill + * in the transaction header. Note that the transaction header does not + * have a log item. + */ + tp->t_header.th_magic = XFS_TRANS_HEADER_MAGIC; + tp->t_header.th_type = tp->t_type; + tp->t_header.th_num_items = nitems; + log_vector->i_addr = (xfs_caddr_t)&tp->t_header; + log_vector->i_len = sizeof(xfs_trans_header_t); + log_vector->i_type = XLOG_REG_TYPE_TRANSHDR; +} + +/* + * The committed item processing consists of calling the committed routine of + * each logged item, updating the item's position in the AIL if necessary, and + * unpinning each item. If the committed routine returns -1, then do nothing + * further with the item because it may have been freed. * - * XFS disk error handling mechanism is not based on a typical - * transaction abort mechanism. Logically after the filesystem - * gets marked 'SHUTDOWN', we can't let any new transactions - * be durable - ie. committed to disk - because some metadata might - * be inconsistent. In such cases, this returns an error, and the - * caller may assume that all locked objects joined to the transaction - * have already been unlocked as if the commit had succeeded. - * Do not reference the transaction structure after this call. + * Since items are unlocked when they are copied to the incore log, it is + * possible for two transactions to be completing and manipulating the same + * item simultaneously. The AIL lock will protect the lsn field of each item. + * The value of this field can never go backwards. + * + * We unpin the items after repositioning them in the AIL, because otherwise + * they could be immediately flushed and we'd have to race with the flusher + * trying to pull the item from the AIL as we add it. */ - /*ARGSUSED*/ -int -_xfs_trans_commit( - xfs_trans_t *tp, - uint flags, - int *log_flushed) +static void +xfs_trans_item_committed( + struct xfs_log_item *lip, + xfs_lsn_t commit_lsn, + int aborted) { - xfs_log_iovec_t *log_vector; - int nvec; - xfs_mount_t *mp; - xfs_lsn_t commit_lsn; - /* REFERENCED */ - int error; - int log_flags; - int sync; -#define XFS_TRANS_LOGVEC_COUNT 16 - xfs_log_iovec_t log_vector_fast[XFS_TRANS_LOGVEC_COUNT]; - struct xlog_in_core *commit_iclog; - int shutdown; + xfs_lsn_t item_lsn; + struct xfs_ail *ailp; - commit_lsn = -1; + if (aborted) + lip->li_flags |= XFS_LI_ABORTED; + item_lsn = IOP_COMMITTED(lip, commit_lsn); + + /* If the committed routine returns -1, item has been freed. */ + if (XFS_LSN_CMP(item_lsn, (xfs_lsn_t)-1) == 0) + return; /* - * Determine whether this commit is releasing a permanent - * log reservation or not. + * If the returned lsn is greater than what it contained before, update + * the location of the item in the AIL. If it is not, then do nothing. + * Items can never move backwards in the AIL. + * + * While the new lsn should usually be greater, it is possible that a + * later transaction completing simultaneously with an earlier one + * using the same item could complete first with a higher lsn. This + * would cause the earlier transaction to fail the test below. */ - if (flags & XFS_TRANS_RELEASE_LOG_RES) { - ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES); - log_flags = XFS_LOG_REL_PERM_RESERV; + ailp = lip->li_ailp; + spin_lock(&ailp->xa_lock); + if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0) { + /* + * This will set the item's lsn to item_lsn and update the + * position of the item in the AIL. + * + * xfs_trans_ail_update() drops the AIL lock. + */ + xfs_trans_ail_update(ailp, lip, item_lsn); } else { - log_flags = 0; + spin_unlock(&ailp->xa_lock); } - mp = tp->t_mountp; /* - * If there is nothing to be logged by the transaction, - * then unlock all of the items associated with the - * transaction and free the transaction structure. - * Also make sure to return any reserved blocks to - * the free pool. + * Now that we've repositioned the item in the AIL, unpin it so it can + * be flushed. Pass information about buffer stale state down from the + * log item flags, if anyone else stales the buffer we do not want to + * pay any attention to it. */ -shut_us_down: - shutdown = XFS_FORCED_SHUTDOWN(mp) ? EIO : 0; - if (!(tp->t_flags & XFS_TRANS_DIRTY) || shutdown) { - xfs_trans_unreserve_and_mod_sb(tp); + IOP_UNPIN(lip); +} + +/* Clear all the per-AG busy list items listed in this transaction */ +static void +xfs_trans_clear_busy_extents( + struct xfs_trans *tp) +{ + xfs_log_busy_chunk_t *lbcp; + xfs_log_busy_slot_t *lbsp; + int i; + + for (lbcp = &tp->t_busy; lbcp != NULL; lbcp = lbcp->lbc_next) { + i = 0; + for (lbsp = lbcp->lbc_busy; i < lbcp->lbc_unused; i++, lbsp++) { + if (XFS_LBC_ISFREE(lbcp, i)) + continue; + xfs_alloc_clear_busy(tp, lbsp->lbc_ag, lbsp->lbc_idx); + } + } + xfs_trans_free_busy(tp); +} + +/* + * This is typically called by the LM when a transaction has been fully + * committed to disk. It needs to unpin the items which have + * been logged by the transaction and update their positions + * in the AIL if necessary. + * + * This also gets called when the transactions didn't get written out + * because of an I/O error. Abortflag & XFS_LI_ABORTED is set then. + */ +STATIC void +xfs_trans_committed( + struct xfs_trans *tp, + int abortflag) +{ + xfs_log_item_desc_t *lidp; + xfs_log_item_chunk_t *licp; + xfs_log_item_chunk_t *next_licp; + + /* Call the transaction's completion callback if there is one. */ + if (tp->t_callback != NULL) + tp->t_callback(tp, tp->t_callarg); + + for (lidp = xfs_trans_first_item(tp); + lidp != NULL; + lidp = xfs_trans_next_item(tp, lidp)) { + xfs_trans_item_committed(lidp->lid_item, tp->t_lsn, abortflag); + } + + /* free the item chunks, ignoring the embedded chunk */ + for (licp = tp->t_items.lic_next; licp != NULL; licp = next_licp) { + next_licp = licp->lic_next; + kmem_free(licp); + } + + xfs_trans_clear_busy_extents(tp); + xfs_trans_free(tp); +} + +/* + * Called from the trans_commit code when we notice that + * the filesystem is in the middle of a forced shutdown. + */ +STATIC void +xfs_trans_uncommit( + struct xfs_trans *tp, + uint flags) +{ + xfs_log_item_desc_t *lidp; + + for (lidp = xfs_trans_first_item(tp); + lidp != NULL; + lidp = xfs_trans_next_item(tp, lidp)) { /* - * It is indeed possible for the transaction to be - * not dirty but the dqinfo portion to be. All that - * means is that we have some (non-persistent) quota - * reservations that need to be unreserved. + * Unpin all but those that aren't dirty. */ - xfs_trans_unreserve_and_mod_dquots(tp); - if (tp->t_ticket) { - commit_lsn = xfs_log_done(mp, tp->t_ticket, - NULL, log_flags); - if (commit_lsn == -1 && !shutdown) - shutdown = XFS_ERROR(EIO); - } - current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); - xfs_trans_free_items(tp, shutdown? XFS_TRANS_ABORT : 0); - xfs_trans_free_busy(tp); - xfs_trans_free(tp); - XFS_STATS_INC(xs_trans_empty); - return (shutdown); + if (lidp->lid_flags & XFS_LID_DIRTY) + IOP_UNPIN_REMOVE(lidp->lid_item, tp); } - ASSERT(tp->t_ticket != NULL); - /* - * If we need to update the superblock, then do it now. - */ - if (tp->t_flags & XFS_TRANS_SB_DIRTY) - xfs_trans_apply_sb_deltas(tp); - xfs_trans_apply_dquot_deltas(tp); + xfs_trans_unreserve_and_mod_sb(tp); + xfs_trans_unreserve_and_mod_dquots(tp); + + xfs_trans_free_items(tp, flags); + xfs_trans_free_busy(tp); + xfs_trans_free(tp); +} + +/* + * Format the transaction direct to the iclog. This isolates the physical + * transaction commit operation from the logical operation and hence allows + * other methods to be introduced without affecting the existing commit path. + */ +static int +xfs_trans_commit_iclog( + struct xfs_mount *mp, + struct xfs_trans *tp, + xfs_lsn_t *commit_lsn, + int flags) +{ + int shutdown; + int error; + int log_flags = 0; + struct xlog_in_core *commit_iclog; +#define XFS_TRANS_LOGVEC_COUNT 16 + struct xfs_log_iovec log_vector_fast[XFS_TRANS_LOGVEC_COUNT]; + struct xfs_log_iovec *log_vector; + uint nvec; + /* * Ask each log item how many log_vector entries it will @@ -861,8 +1047,7 @@ shut_us_down: */ nvec = xfs_trans_count_vecs(tp); if (nvec == 0) { - xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR); - goto shut_us_down; + return ENOMEM; /* triggers a shutdown! */ } else if (nvec <= XFS_TRANS_LOGVEC_COUNT) { log_vector = log_vector_fast; } else { @@ -877,6 +1062,9 @@ shut_us_down: */ xfs_trans_fill_vecs(tp, log_vector); + if (flags & XFS_TRANS_RELEASE_LOG_RES) + log_flags = XFS_LOG_REL_PERM_RESERV; + error = xfs_log_write(mp, log_vector, nvec, tp->t_ticket, &(tp->t_lsn)); /* @@ -884,18 +1072,17 @@ shut_us_down: * at any time after this call. However, all the items associated * with the transaction are still locked and pinned in memory. */ - commit_lsn = xfs_log_done(mp, tp->t_ticket, &commit_iclog, log_flags); + *commit_lsn = xfs_log_done(mp, tp->t_ticket, &commit_iclog, log_flags); - tp->t_commit_lsn = commit_lsn; - if (nvec > XFS_TRANS_LOGVEC_COUNT) { + tp->t_commit_lsn = *commit_lsn; + if (nvec > XFS_TRANS_LOGVEC_COUNT) kmem_free(log_vector); - } /* * If we got a log write error. Unpin the logitems that we * had pinned, clean up, free trans structure, and return error. */ - if (error || commit_lsn == -1) { + if (error || *commit_lsn == -1) { current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); xfs_trans_uncommit(tp, flags|XFS_TRANS_ABORT); return XFS_ERROR(EIO); @@ -909,8 +1096,6 @@ shut_us_down: */ xfs_trans_unreserve_and_mod_sb(tp); - sync = tp->t_flags & XFS_TRANS_SYNC; - /* * Tell the LM to call the transaction completion routine * when the log write with LSN commit_lsn completes (e.g. @@ -953,7 +1138,7 @@ shut_us_down: * the commit lsn of this transaction for dependency tracking * purposes. */ - xfs_trans_unlock_items(tp, commit_lsn); + xfs_trans_unlock_items(tp, *commit_lsn); /* * If we detected a log error earlier, finish committing @@ -973,156 +1158,114 @@ shut_us_down: * and the items are released we can finally allow the iclog to * go to disk. */ - error = xfs_log_release_iclog(mp, commit_iclog); - - /* - * If the transaction needs to be synchronous, then force the - * log out now and wait for it. - */ - if (sync) { - if (!error) { - error = _xfs_log_force_lsn(mp, commit_lsn, - XFS_LOG_SYNC, log_flushed); - } - XFS_STATS_INC(xs_trans_sync); - } else { - XFS_STATS_INC(xs_trans_async); - } - - return (error); + return xfs_log_release_iclog(mp, commit_iclog); } /* - * Total up the number of log iovecs needed to commit this - * transaction. The transaction itself needs one for the - * transaction header. Ask each dirty item in turn how many - * it needs to get the total. + * xfs_trans_commit + * + * Commit the given transaction to the log a/synchronously. + * + * XFS disk error handling mechanism is not based on a typical + * transaction abort mechanism. Logically after the filesystem + * gets marked 'SHUTDOWN', we can't let any new transactions + * be durable - ie. committed to disk - because some metadata might + * be inconsistent. In such cases, this returns an error, and the + * caller may assume that all locked objects joined to the transaction + * have already been unlocked as if the commit had succeeded. + * Do not reference the transaction structure after this call. */ -STATIC uint -xfs_trans_count_vecs( - xfs_trans_t *tp) +int +_xfs_trans_commit( + struct xfs_trans *tp, + uint flags, + int *log_flushed) { - int nvecs; - xfs_log_item_desc_t *lidp; + struct xfs_mount *mp = tp->t_mountp; + xfs_lsn_t commit_lsn = -1; + int error = 0; + int log_flags = 0; + int sync = tp->t_flags & XFS_TRANS_SYNC; - nvecs = 1; - lidp = xfs_trans_first_item(tp); - ASSERT(lidp != NULL); - - /* In the non-debug case we need to start bailing out if we - * didn't find a log_item here, return zero and let trans_commit - * deal with it. + /* + * Determine whether this commit is releasing a permanent + * log reservation or not. */ - if (lidp == NULL) - return 0; - - while (lidp != NULL) { - /* - * Skip items which aren't dirty in this transaction. - */ - if (!(lidp->lid_flags & XFS_LID_DIRTY)) { - lidp = xfs_trans_next_item(tp, lidp); - continue; - } - lidp->lid_size = IOP_SIZE(lidp->lid_item); - nvecs += lidp->lid_size; - lidp = xfs_trans_next_item(tp, lidp); + if (flags & XFS_TRANS_RELEASE_LOG_RES) { + ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES); + log_flags = XFS_LOG_REL_PERM_RESERV; } - return nvecs; -} - -/* - * Called from the trans_commit code when we notice that - * the filesystem is in the middle of a forced shutdown. - */ -STATIC void -xfs_trans_uncommit( - xfs_trans_t *tp, - uint flags) -{ - xfs_log_item_desc_t *lidp; + /* + * If there is nothing to be logged by the transaction, + * then unlock all of the items associated with the + * transaction and free the transaction structure. + * Also make sure to return any reserved blocks to + * the free pool. + */ + if (!(tp->t_flags & XFS_TRANS_DIRTY)) + goto out_unreserve; - for (lidp = xfs_trans_first_item(tp); - lidp != NULL; - lidp = xfs_trans_next_item(tp, lidp)) { - /* - * Unpin all but those that aren't dirty. - */ - if (lidp->lid_flags & XFS_LID_DIRTY) - IOP_UNPIN_REMOVE(lidp->lid_item, tp); + if (XFS_FORCED_SHUTDOWN(mp)) { + error = XFS_ERROR(EIO); + goto out_unreserve; } - xfs_trans_unreserve_and_mod_sb(tp); - xfs_trans_unreserve_and_mod_dquots(tp); + ASSERT(tp->t_ticket != NULL); - xfs_trans_free_items(tp, flags); - xfs_trans_free_busy(tp); - xfs_trans_free(tp); -} + /* + * If we need to update the superblock, then do it now. + */ + if (tp->t_flags & XFS_TRANS_SB_DIRTY) + xfs_trans_apply_sb_deltas(tp); + xfs_trans_apply_dquot_deltas(tp); -/* - * Fill in the vector with pointers to data to be logged - * by this transaction. The transaction header takes - * the first vector, and then each dirty item takes the - * number of vectors it indicated it needed in xfs_trans_count_vecs(). - * - * As each item fills in the entries it needs, also pin the item - * so that it cannot be flushed out until the log write completes. - */ -STATIC void -xfs_trans_fill_vecs( - xfs_trans_t *tp, - xfs_log_iovec_t *log_vector) -{ - xfs_log_item_desc_t *lidp; - xfs_log_iovec_t *vecp; - uint nitems; + error = xfs_trans_commit_iclog(mp, tp, &commit_lsn, flags); + if (error == ENOMEM) { + xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR); + error = XFS_ERROR(EIO); + goto out_unreserve; + } /* - * Skip over the entry for the transaction header, we'll - * fill that in at the end. + * If the transaction needs to be synchronous, then force the + * log out now and wait for it. */ - vecp = log_vector + 1; /* pointer arithmetic */ - - nitems = 0; - lidp = xfs_trans_first_item(tp); - ASSERT(lidp != NULL); - while (lidp != NULL) { - /* - * Skip items which aren't dirty in this transaction. - */ - if (!(lidp->lid_flags & XFS_LID_DIRTY)) { - lidp = xfs_trans_next_item(tp, lidp); - continue; - } - /* - * The item may be marked dirty but not log anything. - * This can be used to get called when a transaction - * is committed. - */ - if (lidp->lid_size) { - nitems++; + if (sync) { + if (!error) { + error = _xfs_log_force_lsn(mp, commit_lsn, + XFS_LOG_SYNC, log_flushed); } - IOP_FORMAT(lidp->lid_item, vecp); - vecp += lidp->lid_size; /* pointer arithmetic */ - IOP_PIN(lidp->lid_item); - lidp = xfs_trans_next_item(tp, lidp); + XFS_STATS_INC(xs_trans_sync); + } else { + XFS_STATS_INC(xs_trans_async); } + return error; + +out_unreserve: + xfs_trans_unreserve_and_mod_sb(tp); + /* - * Now that we've counted the number of items in this - * transaction, fill in the transaction header. + * It is indeed possible for the transaction to be not dirty but + * the dqinfo portion to be. All that means is that we have some + * (non-persistent) quota reservations that need to be unreserved. */ - tp->t_header.th_magic = XFS_TRANS_HEADER_MAGIC; - tp->t_header.th_type = tp->t_type; - tp->t_header.th_num_items = nitems; - log_vector->i_addr = (xfs_caddr_t)&tp->t_header; - log_vector->i_len = sizeof(xfs_trans_header_t); - log_vector->i_type = XLOG_REG_TYPE_TRANSHDR; -} + xfs_trans_unreserve_and_mod_dquots(tp); + if (tp->t_ticket) { + commit_lsn = xfs_log_done(mp, tp->t_ticket, NULL, log_flags); + if (commit_lsn == -1 && !error) + error = XFS_ERROR(EIO); + } + current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); + xfs_trans_free_items(tp, error ? XFS_TRANS_ABORT : 0); + xfs_trans_free_busy(tp); + xfs_trans_free(tp); + XFS_STATS_INC(xs_trans_empty); + return error; +} /* * Unlock all of the transaction's items and free the transaction. @@ -1200,20 +1343,6 @@ xfs_trans_cancel( xfs_trans_free(tp); } - -/* - * Free the transaction structure. If there is more clean up - * to do when the structure is freed, add it here. - */ -STATIC void -xfs_trans_free( - xfs_trans_t *tp) -{ - atomic_dec(&tp->t_mountp->m_active_trans); - xfs_trans_free_dqinfo(tp); - kmem_zone_free(xfs_trans_zone, tp); -} - /* * Roll from one trans in the sequence of PERMANENT transactions to * the next: permanent transactions are only flushed out when @@ -1283,174 +1412,3 @@ xfs_trans_roll( xfs_trans_ihold(trans, dp); return 0; } - -/* - * THIS SHOULD BE REWRITTEN TO USE xfs_trans_next_item(). - * - * This is typically called by the LM when a transaction has been fully - * committed to disk. It needs to unpin the items which have - * been logged by the transaction and update their positions - * in the AIL if necessary. - * This also gets called when the transactions didn't get written out - * because of an I/O error. Abortflag & XFS_LI_ABORTED is set then. - * - * Call xfs_trans_chunk_committed() to process the items in - * each chunk. - */ -STATIC void -xfs_trans_committed( - xfs_trans_t *tp, - int abortflag) -{ - xfs_log_item_chunk_t *licp; - xfs_log_item_chunk_t *next_licp; - xfs_log_busy_chunk_t *lbcp; - xfs_log_busy_slot_t *lbsp; - int i; - - /* - * Call the transaction's completion callback if there - * is one. - */ - if (tp->t_callback != NULL) { - tp->t_callback(tp, tp->t_callarg); - } - - /* - * Special case the chunk embedded in the transaction. - */ - licp = &(tp->t_items); - if (!(xfs_lic_are_all_free(licp))) { - xfs_trans_chunk_committed(licp, tp->t_lsn, abortflag); - } - - /* - * Process the items in each chunk in turn. - */ - licp = licp->lic_next; - while (licp != NULL) { - ASSERT(!xfs_lic_are_all_free(licp)); - xfs_trans_chunk_committed(licp, tp->t_lsn, abortflag); - next_licp = licp->lic_next; - kmem_free(licp); - licp = next_licp; - } - - /* - * Clear all the per-AG busy list items listed in this transaction - */ - lbcp = &tp->t_busy; - while (lbcp != NULL) { - for (i = 0, lbsp = lbcp->lbc_busy; i < lbcp->lbc_unused; i++, lbsp++) { - if (!XFS_LBC_ISFREE(lbcp, i)) { - xfs_alloc_clear_busy(tp, lbsp->lbc_ag, - lbsp->lbc_idx); - } - } - lbcp = lbcp->lbc_next; - } - xfs_trans_free_busy(tp); - - /* - * That's it for the transaction structure. Free it. - */ - xfs_trans_free(tp); -} - -/* - * This is called to perform the commit processing for each - * item described by the given chunk. - * - * The commit processing consists of unlocking items which were - * held locked with the SYNC_UNLOCK attribute, calling the committed - * routine of each logged item, updating the item's position in the AIL - * if necessary, and unpinning each item. If the committed routine - * returns -1, then do nothing further with the item because it - * may have been freed. - * - * Since items are unlocked when they are copied to the incore - * log, it is possible for two transactions to be completing - * and manipulating the same item simultaneously. The AIL lock - * will protect the lsn field of each item. The value of this - * field can never go backwards. - * - * We unpin the items after repositioning them in the AIL, because - * otherwise they could be immediately flushed and we'd have to race - * with the flusher trying to pull the item from the AIL as we add it. - */ -STATIC void -xfs_trans_chunk_committed( - xfs_log_item_chunk_t *licp, - xfs_lsn_t lsn, - int aborted) -{ - xfs_log_item_desc_t *lidp; - xfs_log_item_t *lip; - xfs_lsn_t item_lsn; - int i; - - lidp = licp->lic_descs; - for (i = 0; i < licp->lic_unused; i++, lidp++) { - struct xfs_ail *ailp; - - if (xfs_lic_isfree(licp, i)) { - continue; - } - - lip = lidp->lid_item; - if (aborted) - lip->li_flags |= XFS_LI_ABORTED; - - /* - * Send in the ABORTED flag to the COMMITTED routine - * so that it knows whether the transaction was aborted - * or not. - */ - item_lsn = IOP_COMMITTED(lip, lsn); - - /* - * If the committed routine returns -1, make - * no more references to the item. - */ - if (XFS_LSN_CMP(item_lsn, (xfs_lsn_t)-1) == 0) { - continue; - } - - /* - * If the returned lsn is greater than what it - * contained before, update the location of the - * item in the AIL. If it is not, then do nothing. - * Items can never move backwards in the AIL. - * - * While the new lsn should usually be greater, it - * is possible that a later transaction completing - * simultaneously with an earlier one using the - * same item could complete first with a higher lsn. - * This would cause the earlier transaction to fail - * the test below. - */ - ailp = lip->li_ailp; - spin_lock(&ailp->xa_lock); - if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0) { - /* - * This will set the item's lsn to item_lsn - * and update the position of the item in - * the AIL. - * - * xfs_trans_ail_update() drops the AIL lock. - */ - xfs_trans_ail_update(ailp, lip, item_lsn); - } else { - spin_unlock(&ailp->xa_lock); - } - - /* - * Now that we've repositioned the item in the AIL, - * unpin it so it can be flushed. Pass information - * about buffer stale state down from the log item - * flags, if anyone else stales the buffer we do not - * want to pay any attention to it. - */ - IOP_UNPIN(lip, lidp->lid_flags & XFS_LID_BUF_STALE); - } -} diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 79c8bab9dff..c62beee0921 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -49,6 +49,15 @@ typedef struct xfs_trans_header { #define XFS_LI_DQUOT 0x123d #define XFS_LI_QUOTAOFF 0x123e +#define XFS_LI_TYPE_DESC \ + { XFS_LI_EFI, "XFS_LI_EFI" }, \ + { XFS_LI_EFD, "XFS_LI_EFD" }, \ + { XFS_LI_IUNLINK, "XFS_LI_IUNLINK" }, \ + { XFS_LI_INODE, "XFS_LI_INODE" }, \ + { XFS_LI_BUF, "XFS_LI_BUF" }, \ + { XFS_LI_DQUOT, "XFS_LI_DQUOT" }, \ + { XFS_LI_QUOTAOFF, "XFS_LI_QUOTAOFF" } + /* * Transaction types. Used to distinguish types of buffers. */ @@ -159,7 +168,6 @@ typedef struct xfs_log_item_desc { #define XFS_LID_DIRTY 0x1 #define XFS_LID_PINNED 0x2 -#define XFS_LID_BUF_STALE 0x8 /* * This structure is used to maintain a chunk list of log_item_desc @@ -833,7 +841,7 @@ typedef struct xfs_item_ops { uint (*iop_size)(xfs_log_item_t *); void (*iop_format)(xfs_log_item_t *, struct xfs_log_iovec *); void (*iop_pin)(xfs_log_item_t *); - void (*iop_unpin)(xfs_log_item_t *, int); + void (*iop_unpin)(xfs_log_item_t *); void (*iop_unpin_remove)(xfs_log_item_t *, struct xfs_trans *); uint (*iop_trylock)(xfs_log_item_t *); void (*iop_unlock)(xfs_log_item_t *); @@ -846,7 +854,7 @@ typedef struct xfs_item_ops { #define IOP_SIZE(ip) (*(ip)->li_ops->iop_size)(ip) #define IOP_FORMAT(ip,vp) (*(ip)->li_ops->iop_format)(ip, vp) #define IOP_PIN(ip) (*(ip)->li_ops->iop_pin)(ip) -#define IOP_UNPIN(ip, flags) (*(ip)->li_ops->iop_unpin)(ip, flags) +#define IOP_UNPIN(ip) (*(ip)->li_ops->iop_unpin)(ip) #define IOP_UNPIN_REMOVE(ip,tp) (*(ip)->li_ops->iop_unpin_remove)(ip, tp) #define IOP_TRYLOCK(ip) (*(ip)->li_ops->iop_trylock)(ip) #define IOP_UNLOCK(ip) (*(ip)->li_ops->iop_unlock)(ip) diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index fb586360d1c..9cd809025f3 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -40,11 +40,51 @@ #include "xfs_rw.h" #include "xfs_trace.h" +/* + * Check to see if a buffer matching the given parameters is already + * a part of the given transaction. + */ +STATIC struct xfs_buf * +xfs_trans_buf_item_match( + struct xfs_trans *tp, + struct xfs_buftarg *target, + xfs_daddr_t blkno, + int len) +{ + xfs_log_item_chunk_t *licp; + xfs_log_item_desc_t *lidp; + xfs_buf_log_item_t *blip; + int i; -STATIC xfs_buf_t *xfs_trans_buf_item_match(xfs_trans_t *, xfs_buftarg_t *, - xfs_daddr_t, int); -STATIC xfs_buf_t *xfs_trans_buf_item_match_all(xfs_trans_t *, xfs_buftarg_t *, - xfs_daddr_t, int); + len = BBTOB(len); + for (licp = &tp->t_items; licp != NULL; licp = licp->lic_next) { + if (xfs_lic_are_all_free(licp)) { + ASSERT(licp == &tp->t_items); + ASSERT(licp->lic_next == NULL); + return NULL; + } + + for (i = 0; i < licp->lic_unused; i++) { + /* + * Skip unoccupied slots. + */ + if (xfs_lic_isfree(licp, i)) + continue; + + lidp = xfs_lic_slot(licp, i); + blip = (xfs_buf_log_item_t *)lidp->lid_item; + if (blip->bli_item.li_type != XFS_LI_BUF) + continue; + + if (XFS_BUF_TARGET(blip->bli_buf) == target && + XFS_BUF_ADDR(blip->bli_buf) == blkno && + XFS_BUF_COUNT(blip->bli_buf) == len) + return blip->bli_buf; + } + } + + return NULL; +} /* * Add the locked buffer to the transaction. @@ -112,14 +152,6 @@ xfs_trans_bjoin( * within the transaction, just increment its lock recursion count * and return a pointer to it. * - * Use the fast path function xfs_trans_buf_item_match() or the buffer - * cache routine incore_match() to find the buffer - * if it is already owned by this transaction. - * - * If we don't already own the buffer, use get_buf() to get it. - * If it doesn't yet have an associated xfs_buf_log_item structure, - * then allocate one and add the item to this transaction. - * * If the transaction pointer is NULL, make this just a normal * get_buf() call. */ @@ -149,11 +181,7 @@ xfs_trans_get_buf(xfs_trans_t *tp, * have it locked. In this case we just increment the lock * recursion count and return the buffer to the caller. */ - if (tp->t_items.lic_next == NULL) { - bp = xfs_trans_buf_item_match(tp, target_dev, blkno, len); - } else { - bp = xfs_trans_buf_item_match_all(tp, target_dev, blkno, len); - } + bp = xfs_trans_buf_item_match(tp, target_dev, blkno, len); if (bp != NULL) { ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); if (XFS_FORCED_SHUTDOWN(tp->t_mountp)) @@ -259,14 +287,6 @@ int xfs_error_mod = 33; * within the transaction and already read in, just increment its * lock recursion count and return a pointer to it. * - * Use the fast path function xfs_trans_buf_item_match() or the buffer - * cache routine incore_match() to find the buffer - * if it is already owned by this transaction. - * - * If we don't already own the buffer, use read_buf() to get it. - * If it doesn't yet have an associated xfs_buf_log_item structure, - * then allocate one and add the item to this transaction. - * * If the transaction pointer is NULL, make this just a normal * read_buf() call. */ @@ -328,11 +348,7 @@ xfs_trans_read_buf( * If the buffer is not yet read in, then we read it in, increment * the lock recursion count, and return it to the caller. */ - if (tp->t_items.lic_next == NULL) { - bp = xfs_trans_buf_item_match(tp, target, blkno, len); - } else { - bp = xfs_trans_buf_item_match_all(tp, target, blkno, len); - } + bp = xfs_trans_buf_item_match(tp, target, blkno, len); if (bp != NULL) { ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp); @@ -696,7 +712,6 @@ xfs_trans_log_buf(xfs_trans_t *tp, tp->t_flags |= XFS_TRANS_DIRTY; lidp->lid_flags |= XFS_LID_DIRTY; - lidp->lid_flags &= ~XFS_LID_BUF_STALE; bip->bli_flags |= XFS_BLI_LOGGED; xfs_buf_item_log(bip, first, last); } @@ -782,7 +797,7 @@ xfs_trans_binval( bip->bli_format.blf_flags |= XFS_BLI_CANCEL; memset((char *)(bip->bli_format.blf_data_map), 0, (bip->bli_format.blf_map_size * sizeof(uint))); - lidp->lid_flags |= XFS_LID_DIRTY|XFS_LID_BUF_STALE; + lidp->lid_flags |= XFS_LID_DIRTY; tp->t_flags |= XFS_TRANS_DIRTY; } @@ -902,111 +917,3 @@ xfs_trans_dquot_buf( bip->bli_format.blf_flags |= type; } - -/* - * Check to see if a buffer matching the given parameters is already - * a part of the given transaction. Only check the first, embedded - * chunk, since we don't want to spend all day scanning large transactions. - */ -STATIC xfs_buf_t * -xfs_trans_buf_item_match( - xfs_trans_t *tp, - xfs_buftarg_t *target, - xfs_daddr_t blkno, - int len) -{ - xfs_log_item_chunk_t *licp; - xfs_log_item_desc_t *lidp; - xfs_buf_log_item_t *blip; - xfs_buf_t *bp; - int i; - - bp = NULL; - len = BBTOB(len); - licp = &tp->t_items; - if (!xfs_lic_are_all_free(licp)) { - for (i = 0; i < licp->lic_unused; i++) { - /* - * Skip unoccupied slots. - */ - if (xfs_lic_isfree(licp, i)) { - continue; - } - - lidp = xfs_lic_slot(licp, i); - blip = (xfs_buf_log_item_t *)lidp->lid_item; - if (blip->bli_item.li_type != XFS_LI_BUF) { - continue; - } - - bp = blip->bli_buf; - if ((XFS_BUF_TARGET(bp) == target) && - (XFS_BUF_ADDR(bp) == blkno) && - (XFS_BUF_COUNT(bp) == len)) { - /* - * We found it. Break out and - * return the pointer to the buffer. - */ - break; - } else { - bp = NULL; - } - } - } - return bp; -} - -/* - * Check to see if a buffer matching the given parameters is already - * a part of the given transaction. Check all the chunks, we - * want to be thorough. - */ -STATIC xfs_buf_t * -xfs_trans_buf_item_match_all( - xfs_trans_t *tp, - xfs_buftarg_t *target, - xfs_daddr_t blkno, - int len) -{ - xfs_log_item_chunk_t *licp; - xfs_log_item_desc_t *lidp; - xfs_buf_log_item_t *blip; - xfs_buf_t *bp; - int i; - - bp = NULL; - len = BBTOB(len); - for (licp = &tp->t_items; licp != NULL; licp = licp->lic_next) { - if (xfs_lic_are_all_free(licp)) { - ASSERT(licp == &tp->t_items); - ASSERT(licp->lic_next == NULL); - return NULL; - } - for (i = 0; i < licp->lic_unused; i++) { - /* - * Skip unoccupied slots. - */ - if (xfs_lic_isfree(licp, i)) { - continue; - } - - lidp = xfs_lic_slot(licp, i); - blip = (xfs_buf_log_item_t *)lidp->lid_item; - if (blip->bli_item.li_type != XFS_LI_BUF) { - continue; - } - - bp = blip->bli_buf; - if ((XFS_BUF_TARGET(bp) == target) && - (XFS_BUF_ADDR(bp) == blkno) && - (XFS_BUF_COUNT(bp) == len)) { - /* - * We found it. Break out and - * return the pointer to the buffer. - */ - return bp; - } - } - } - return NULL; -} diff --git a/include/linux/altera_jtaguart.h b/include/linux/altera_jtaguart.h new file mode 100644 index 00000000000..953b178a165 --- /dev/null +++ b/include/linux/altera_jtaguart.h @@ -0,0 +1,16 @@ +/* + * altera_jtaguart.h -- Altera JTAG UART driver defines. + */ + +#ifndef __ALTJUART_H +#define __ALTJUART_H + +#define ALTERA_JTAGUART_MAJOR 204 +#define ALTERA_JTAGUART_MINOR 186 + +struct altera_jtaguart_platform_uart { + unsigned long mapbase; /* Physical address base */ + unsigned int irq; /* Interrupt vector */ +}; + +#endif /* __ALTJUART_H */ diff --git a/include/linux/altera_uart.h b/include/linux/altera_uart.h new file mode 100644 index 00000000000..8d441064a30 --- /dev/null +++ b/include/linux/altera_uart.h @@ -0,0 +1,14 @@ +/* + * altera_uart.h -- Altera UART driver defines. + */ + +#ifndef __ALTUART_H +#define __ALTUART_H + +struct altera_uart_platform_uart { + unsigned long mapbase; /* Physical address base */ + unsigned int irq; /* Interrupt vector */ + unsigned int uartclk; /* UART clock rate */ +}; + +#endif /* __ALTUART_H */ diff --git a/include/linux/device.h b/include/linux/device.h index 241b96bcd7a..7bb9f426f3e 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -22,7 +22,6 @@ #include <linux/types.h> #include <linux/module.h> #include <linux/pm.h> -#include <linux/semaphore.h> #include <asm/atomic.h> #include <asm/device.h> @@ -203,6 +202,9 @@ struct class { int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); + const struct kobj_ns_type_operations *ns_type; + const void *(*namespace)(struct device *dev); + const struct dev_pm_ops *pm; struct class_private *p; @@ -404,7 +406,7 @@ struct device { const char *init_name; /* initial name of the device */ struct device_type *type; - struct semaphore sem; /* semaphore to synchronize calls to + struct mutex mutex; /* mutex to synchronize calls to * its driver. */ @@ -514,17 +516,17 @@ static inline bool device_async_suspend_enabled(struct device *dev) static inline void device_lock(struct device *dev) { - down(&dev->sem); + mutex_lock(&dev->mutex); } static inline int device_trylock(struct device *dev) { - return down_trylock(&dev->sem); + return mutex_trylock(&dev->mutex); } static inline void device_unlock(struct device *dev) { - up(&dev->sem); + mutex_unlock(&dev->mutex); } void driver_init(void); diff --git a/include/linux/firmware.h b/include/linux/firmware.h index 043811f0d27..53d1e6c4f84 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -12,6 +12,7 @@ struct firmware { size_t size; const u8 *data; + struct page **pages; }; struct device; diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 39e71b0a3bf..c082f223e2f 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h @@ -25,6 +25,9 @@ const char *ftrace_print_flags_seq(struct trace_seq *p, const char *delim, const char *ftrace_print_symbols_seq(struct trace_seq *p, unsigned long val, const struct trace_print_flags *symbol_array); +const char *ftrace_print_hex_seq(struct trace_seq *p, + const unsigned char *buf, int len); + /* * The trace entry - the most basic unit of tracing. This is what * is printed in the end as a single line in the trace output, such as: diff --git a/include/linux/gsmmux.h b/include/linux/gsmmux.h new file mode 100644 index 00000000000..378de4195ca --- /dev/null +++ b/include/linux/gsmmux.h @@ -0,0 +1,25 @@ +#ifndef _LINUX_GSMMUX_H +#define _LINUX_GSMMUX_H + +struct gsm_config +{ + unsigned int adaption; + unsigned int encapsulation; + unsigned int initiator; + unsigned int t1; + unsigned int t2; + unsigned int t3; + unsigned int n2; + unsigned int mru; + unsigned int mtu; + unsigned int k; + unsigned int i; + unsigned int unused[8]; /* Padding for expansion without + breaking stuff */ +}; + +#define GSMIOC_GETCONF _IOR('G', 0, struct gsm_config) +#define GSMIOC_SETCONF _IOW('G', 1, struct gsm_config) + + +#endif diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 5137db3317f..c2331138ca1 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -78,7 +78,7 @@ enum { IRQTF_AFFINITY, }; -/** +/* * These values can be returned by request_any_context_irq() and * describe the context the interrupt will be run in. * diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 2b32d638147..0874ab59ffe 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -215,8 +215,8 @@ union jffs2_node_union /* Data payload for device nodes. */ union jffs2_device_node { - jint16_t old; - jint32_t new; + jint16_t old_id; + jint32_t new_id; }; #endif /* __LINUX_JFFS2_H__ */ diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 3950d3c2850..cf343a85253 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -108,6 +108,8 @@ struct kobj_type { void (*release)(struct kobject *kobj); const struct sysfs_ops *sysfs_ops; struct attribute **default_attrs; + const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); + const void *(*namespace)(struct kobject *kobj); }; struct kobj_uevent_env { @@ -134,6 +136,42 @@ struct kobj_attribute { extern const struct sysfs_ops kobj_sysfs_ops; +/* + * Namespace types which are used to tag kobjects and sysfs entries. + * Network namespace will likely be the first. + */ +enum kobj_ns_type { + KOBJ_NS_TYPE_NONE = 0, + KOBJ_NS_TYPE_NET, + KOBJ_NS_TYPES +}; + +struct sock; + +/* + * Callbacks so sysfs can determine namespaces + * @current_ns: return calling task's namespace + * @netlink_ns: return namespace to which a sock belongs (right?) + * @initial_ns: return the initial namespace (i.e. init_net_ns) + */ +struct kobj_ns_type_operations { + enum kobj_ns_type type; + const void *(*current_ns)(void); + const void *(*netlink_ns)(struct sock *sk); + const void *(*initial_ns)(void); +}; + +int kobj_ns_type_register(const struct kobj_ns_type_operations *ops); +int kobj_ns_type_registered(enum kobj_ns_type type); +const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent); +const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj); + +const void *kobj_ns_current(enum kobj_ns_type type); +const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk); +const void *kobj_ns_initial(enum kobj_ns_type type); +void kobj_ns_exit(enum kobj_ns_type type, const void *ns); + + /** * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. * diff --git a/include/linux/kref.h b/include/linux/kref.h index baf4b9e4b19..6cc38fc07ab 100644 --- a/include/linux/kref.h +++ b/include/linux/kref.h @@ -21,7 +21,6 @@ struct kref { atomic_t refcount; }; -void kref_set(struct kref *kref, int num); void kref_init(struct kref *kref); void kref_get(struct kref *kref); int kref_put(struct kref *kref, void (*release) (struct kref *kref)); diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index a03977a96d7..06aed8305bf 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -44,6 +44,8 @@ struct lock_class_key { struct lockdep_subclass_key subkeys[MAX_LOCKDEP_SUBCLASSES]; }; +extern struct lock_class_key __lockdep_no_validate__; + #define LOCKSTAT_POINTS 4 /* @@ -270,6 +272,9 @@ extern void lockdep_init_map(struct lockdep_map *lock, const char *name, #define lockdep_set_subclass(lock, sub) \ lockdep_init_map(&(lock)->dep_map, #lock, \ (lock)->dep_map.key, sub) + +#define lockdep_set_novalidate_class(lock) \ + lockdep_set_class(lock, &__lockdep_no_validate__) /* * Compare locking classes */ @@ -354,6 +359,9 @@ static inline void lockdep_on(void) #define lockdep_set_class_and_subclass(lock, key, sub) \ do { (void)(key); } while (0) #define lockdep_set_subclass(lock, sub) do { } while (0) + +#define lockdep_set_novalidate_class(lock) do { } while (0) + /* * We don't define lockdep_match_class() and lockdep_match_key() for !LOCKDEP * case since the result is not well defined and the caller should rather diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h index 8b4aa0523db..b481ccd7ff3 100644 --- a/include/linux/mtd/blktrans.h +++ b/include/linux/mtd/blktrans.h @@ -9,6 +9,8 @@ #define __MTD_TRANS_H__ #include <linux/mutex.h> +#include <linux/kref.h> +#include <linux/sysfs.h> struct hd_geometry; struct mtd_info; @@ -24,11 +26,16 @@ struct mtd_blktrans_dev { int devnum; unsigned long size; int readonly; - void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */ + int open; + struct kref ref; + struct gendisk *disk; + struct attribute_group *disk_attributes; + struct task_struct *thread; + struct request_queue *rq; + spinlock_t queue_lock; + void *priv; }; -struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */ - struct mtd_blktrans_ops { char *name; int major; @@ -60,8 +67,6 @@ struct mtd_blktrans_ops { struct list_head devs; struct list_head list; struct module *owner; - - struct mtd_blkcore_priv *blkcore_priv; }; extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr); diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index df89f427523..574d9ee066f 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -253,6 +253,7 @@ struct cfi_bri_query { #define P_ID_MITSUBISHI_STD 0x0100 #define P_ID_MITSUBISHI_EXT 0x0101 #define P_ID_SST_PAGE 0x0102 +#define P_ID_SST_OLD 0x0701 #define P_ID_INTEL_PERFORMANCE 0x0200 #define P_ID_INTEL_DATA 0x0210 #define P_ID_RESERVED 0xffff @@ -297,7 +298,7 @@ static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, * and 32bit devices on 16 bit busses * set the low bit of the alternating bit sequence of the address. */ - if (((type * interleave) > bankwidth) && ((uint8_t)cmd_ofs == 0xaa)) + if (((type * interleave) > bankwidth) && ((cmd_ofs & 0xff) == 0xaa)) addr |= (type >> 1)*interleave; return addr; @@ -515,14 +516,25 @@ struct cfi_fixup { void* param; }; -#define CFI_MFR_ANY 0xffff -#define CFI_ID_ANY 0xffff - -#define CFI_MFR_AMD 0x0001 -#define CFI_MFR_INTEL 0x0089 -#define CFI_MFR_ATMEL 0x001F -#define CFI_MFR_SAMSUNG 0x00EC -#define CFI_MFR_ST 0x0020 /* STMicroelectronics */ +#define CFI_MFR_ANY 0xFFFF +#define CFI_ID_ANY 0xFFFF +#define CFI_MFR_CONTINUATION 0x007F + +#define CFI_MFR_AMD 0x0001 +#define CFI_MFR_ATMEL 0x001F +#define CFI_MFR_EON 0x001C +#define CFI_MFR_FUJITSU 0x0004 +#define CFI_MFR_HYUNDAI 0x00AD +#define CFI_MFR_INTEL 0x0089 +#define CFI_MFR_MACRONIX 0x00C2 +#define CFI_MFR_NEC 0x0010 +#define CFI_MFR_PMC 0x009D +#define CFI_MFR_SAMSUNG 0x00EC +#define CFI_MFR_SHARP 0x00B0 +#define CFI_MFR_SST 0x00BF +#define CFI_MFR_ST 0x0020 /* STMicroelectronics */ +#define CFI_MFR_TOSHIBA 0x0098 +#define CFI_MFR_WINBOND 0x00DA void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups); diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index d0bf422ae37..f43e9b49b75 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h @@ -15,6 +15,7 @@ * has asm/spinlock.h, or 2.4, which has linux/spinlock.h */ #include <linux/sched.h> +#include <linux/mutex.h> typedef enum { FL_READY, @@ -74,8 +75,7 @@ struct flchip { unsigned int erase_suspended:1; unsigned long in_progress_block_addr; - spinlock_t *mutex; - spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */ + struct mutex mutex; wait_queue_head_t wq; /* Wait on here when we're waiting for the chip to be ready */ int word_write_time; diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index b981b877221..de89eca864c 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -7,6 +7,7 @@ #include <linux/types.h> #include <linux/list.h> #include <linux/string.h> +#include <linux/bug.h> #include <linux/mtd/compatmac.h> @@ -386,6 +387,8 @@ static inline map_word inline_map_read(struct map_info *map, unsigned long ofs) #endif else if (map_bankwidth_is_large(map)) memcpy_fromio(r.x, map->virt+ofs, map->bankwidth); + else + BUG(); return r; } diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 0f32a9b6ff5..5326435a757 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -20,7 +20,6 @@ #define MTD_CHAR_MAJOR 90 #define MTD_BLOCK_MAJOR 31 -#define MAX_MTD_DEVICES 32 #define MTD_ERASE_PENDING 0x01 #define MTD_ERASING 0x02 @@ -61,9 +60,7 @@ struct mtd_erase_region_info { * MTD_OOB_PLACE: oob data are placed at the given offset * MTD_OOB_AUTO: oob data are automatically placed at the free areas * which are defined by the ecclayout - * MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data - * is inserted into the data. Thats a raw image of the - * flash contents. + * MTD_OOB_RAW: mode to read oob and data without doing ECC checking */ typedef enum { MTD_OOB_PLACE, @@ -290,8 +287,9 @@ extern int add_mtd_device(struct mtd_info *mtd); extern int del_mtd_device (struct mtd_info *mtd); extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); +extern int __get_mtd_device(struct mtd_info *mtd); +extern void __put_mtd_device(struct mtd_info *mtd); extern struct mtd_info *get_mtd_device_nm(const char *name); - extern void put_mtd_device(struct mtd_info *mtd); diff --git a/include/linux/mtd/mtdram.h b/include/linux/mtd/mtdram.h index 04fdc07b735..68891313875 100644 --- a/include/linux/mtd/mtdram.h +++ b/include/linux/mtd/mtdram.h @@ -3,6 +3,6 @@ #include <linux/mtd/mtd.h> int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, - unsigned long size, char *name); + unsigned long size, char *name); #endif /* __MTD_MTDRAM_H__ */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ccab9dfc521..a81b185e23a 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -25,11 +25,13 @@ #include <linux/mtd/bbm.h> struct mtd_info; +struct nand_flash_dev; /* Scan and identify a NAND device */ extern int nand_scan (struct mtd_info *mtd, int max_chips); /* Separate phases of nand_scan(), allowing board driver to intervene * and override command or ECC setup according to flash type */ -extern int nand_scan_ident(struct mtd_info *mtd, int max_chips); +extern int nand_scan_ident(struct mtd_info *mtd, int max_chips, + struct nand_flash_dev *table); extern int nand_scan_tail(struct mtd_info *mtd); /* Free resources held by the NAND device */ @@ -38,6 +40,12 @@ extern void nand_release (struct mtd_info *mtd); /* Internal helper for board drivers which need to override command function */ extern void nand_wait_ready(struct mtd_info *mtd); +/* locks all blockes present in the device */ +extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); + +/* unlocks specified locked blockes */ +extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); + /* The maximum number of NAND chips in an array */ #define NAND_MAX_CHIPS 8 @@ -45,7 +53,7 @@ extern void nand_wait_ready(struct mtd_info *mtd); * is supported now. If you add a chip with bigger oobsize/page * adjust this accordingly. */ -#define NAND_MAX_OOBSIZE 128 +#define NAND_MAX_OOBSIZE 256 #define NAND_MAX_PAGESIZE 4096 /* @@ -82,6 +90,10 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff +#define NAND_CMD_LOCK 0x2a +#define NAND_CMD_UNLOCK1 0x23 +#define NAND_CMD_UNLOCK2 0x24 + /* Extended commands for large page devices */ #define NAND_CMD_READSTART 0x30 #define NAND_CMD_RNDOUTSTART 0xE0 @@ -169,6 +181,14 @@ typedef enum { #define NAND_NO_READRDY 0x00000100 /* Chip does not allow subpage writes */ #define NAND_NO_SUBPAGE_WRITE 0x00000200 +/* Chip stores bad block marker on the last page of the eraseblock */ +#define NAND_BB_LAST_PAGE 0x00000400 + +/* Device is one of 'new' xD cards that expose fake nand command set */ +#define NAND_BROKEN_XD 0x00000400 + +/* Device behaves just like nand, but is readonly */ +#define NAND_ROM 0x00000800 /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS \ @@ -391,6 +411,7 @@ struct nand_chip { int subpagesize; uint8_t cellinfo; int badblockpos; + int badblockbits; flstate_t state; diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 5509eb06b32..c26ff86ad08 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -125,6 +125,9 @@ struct onenand_chip { flstate_t state; unsigned char *page_buf; unsigned char *oob_buf; +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + unsigned char *verify_buf; +#endif int subpagesize; struct nand_ecclayout *ecclayout; @@ -175,10 +178,14 @@ struct onenand_chip { #define ONENAND_HAS_CONT_LOCK (0x0001) #define ONENAND_HAS_UNLOCK_ALL (0x0002) #define ONENAND_HAS_2PLANE (0x0004) +#define ONENAND_HAS_4KB_PAGE (0x0008) #define ONENAND_SKIP_UNLOCK_CHECK (0x0100) #define ONENAND_PAGEBUF_ALLOC (0x1000) #define ONENAND_OOBBUF_ALLOC (0x2000) +#define ONENAND_IS_4KB_PAGE(this) \ + (this->options & ONENAND_HAS_4KB_PAGE) + /* * OneNAND Flash Manufacturer ID Codes */ @@ -205,6 +212,8 @@ struct mtd_partition; struct onenand_platform_data { void (*mmcontrol)(struct mtd_info *mtd, int sync_read); + int (*read_bufferram)(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, size_t count); struct mtd_partition *parts; unsigned int nr_parts; }; diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h index ab77609ec33..9cf4c4c7955 100644 --- a/include/linux/mtd/sh_flctl.h +++ b/include/linux/mtd/sh_flctl.h @@ -93,8 +93,6 @@ #define INIT_FL4ECCRESULT_VAL 0x03FF03FF #define LOOP_TIMEOUT_MAX 0x00010000 -#define mtd_to_flctl(mtd) container_of(mtd, struct sh_flctl, mtd) - struct sh_flctl { struct mtd_info mtd; struct nand_chip chip; @@ -125,4 +123,9 @@ struct sh_flctl_platform_data { unsigned has_hwecc:1; }; +static inline struct sh_flctl *mtd_to_flctl(struct mtd_info *mtdinfo) +{ + return container_of(mtdinfo, struct sh_flctl, mtd); +} + #endif /* __SH_FLCTL_H__ */ diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 6eaca5e1e8c..59d066936ab 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -188,6 +188,10 @@ extern int netlink_has_listeners(struct sock *sk, unsigned int group); extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock); extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid, __u32 group, gfp_t allocation); +extern int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, + __u32 pid, __u32 group, gfp_t allocation, + int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data), + void *filter_data); extern int netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code); extern int netlink_register_notifier(struct notifier_block *nb); extern int netlink_unregister_notifier(struct notifier_block *nb); diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 640702e9745..8c2c6116e78 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -199,16 +199,15 @@ struct nilfs_super_block { __le32 s_creator_os; /* OS */ __le16 s_def_resuid; /* Default uid for reserved blocks */ __le16 s_def_resgid; /* Default gid for reserved blocks */ - __le32 s_first_ino; /* First non-reserved inode */ + __le32 s_first_ino; /* First non-reserved inode */ - __le16 s_inode_size; /* Size of an inode */ + __le16 s_inode_size; /* Size of an inode */ __le16 s_dat_entry_size; /* Size of a dat entry */ __le16 s_checkpoint_size; /* Size of a checkpoint */ __le16 s_segment_usage_size; /* Size of a segment usage */ __u8 s_uuid[16]; /* 128-bit uuid for volume */ - char s_volume_name[16]; /* volume name */ - char s_last_mounted[64]; /* directory where last mounted */ + char s_volume_name[80]; /* volume name */ __le32 s_c_interval; /* Commit interval of segment */ __le32 s_c_block_max; /* Threshold of data amount for @@ -377,6 +376,7 @@ union nilfs_binfo { * @ss_nfinfo: number of finfo structures * @ss_sumbytes: total size of segment summary in bytes * @ss_pad: padding + * @ss_cno: checkpoint number */ struct nilfs_segment_summary { __le32 ss_datasum; @@ -391,6 +391,7 @@ struct nilfs_segment_summary { __le32 ss_nfinfo; __le32 ss_sumbytes; __le32 ss_pad; + __le64 ss_cno; /* array of finfo structures */ }; @@ -437,10 +438,10 @@ struct nilfs_palloc_group_desc { /** * struct nilfs_dat_entry - disk address translation entry - * @dt_blocknr: block number - * @dt_start: start checkpoint number - * @dt_end: end checkpoint number - * @dt_rsv: reserved for future use + * @de_blocknr: block number + * @de_start: start checkpoint number + * @de_end: end checkpoint number + * @de_rsv: reserved for future use */ struct nilfs_dat_entry { __le64 de_blocknr; diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h index 4e768dda87b..8600508c77a 100644 --- a/include/linux/ramfs.h +++ b/include/linux/ramfs.h @@ -20,4 +20,6 @@ extern const struct file_operations ramfs_file_operations; extern const struct vm_operations_struct generic_file_vm_ops; extern int __init init_rootfs(void); +int ramfs_fill_super(struct super_block *sb, void *data, int silent); + #endif diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 78dd1e7120a..09d0d2d5a08 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -182,6 +182,10 @@ /* Aeroflex Gaisler GRLIB APBUART */ #define PORT_APBUART 90 +/* Altera UARTs */ +#define PORT_ALTERA_JTAGUART 91 +#define PORT_ALTERA_UART 92 + #ifdef __KERNEL__ #include <linux/compiler.h> diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index f0496b3d181..f2694eb4dd3 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -20,6 +20,7 @@ struct kobject; struct module; +enum kobj_ns_type; /* FIXME * The *owner field is no longer used. @@ -86,17 +87,18 @@ struct attribute_group { #define attr_name(_attr) (_attr).attr.name +struct file; struct vm_area_struct; struct bin_attribute { struct attribute attr; size_t size; void *private; - ssize_t (*read)(struct kobject *, struct bin_attribute *, + ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *, char *, loff_t, size_t); - ssize_t (*write)(struct kobject *, struct bin_attribute *, + ssize_t (*write)(struct file *,struct kobject *, struct bin_attribute *, char *, loff_t, size_t); - int (*mmap)(struct kobject *, struct bin_attribute *attr, + int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr, struct vm_area_struct *vma); }; @@ -154,6 +156,9 @@ void sysfs_remove_link(struct kobject *kobj, const char *name); int sysfs_rename_link(struct kobject *kobj, struct kobject *target, const char *old_name, const char *new_name); +void sysfs_delete_link(struct kobject *dir, struct kobject *targ, + const char *name); + int __must_check sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); int sysfs_update_group(struct kobject *kobj, @@ -168,10 +173,15 @@ void sysfs_remove_file_from_group(struct kobject *kobj, void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr); void sysfs_notify_dirent(struct sysfs_dirent *sd); struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, + const void *ns, const unsigned char *name); struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd); void sysfs_put(struct sysfs_dirent *sd); void sysfs_printk_last_file(void); + +/* Called to clear a ns tag when it is no longer valid */ +void sysfs_exit_ns(enum kobj_ns_type type, const void *tag); + int __must_check sysfs_init(void); #else /* CONFIG_SYSFS */ @@ -264,6 +274,11 @@ static inline int sysfs_rename_link(struct kobject *k, struct kobject *t, return 0; } +static inline void sysfs_delete_link(struct kobject *k, struct kobject *t, + const char *name) +{ +} + static inline int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) { @@ -301,6 +316,7 @@ static inline void sysfs_notify_dirent(struct sysfs_dirent *sd) } static inline struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, + const void *ns, const unsigned char *name) { return NULL; @@ -313,6 +329,10 @@ static inline void sysfs_put(struct sysfs_dirent *sd) { } +static inline void sysfs_exit_ns(int type, const void *tag) +{ +} + static inline int __must_check sysfs_init(void) { return 0; diff --git a/include/linux/tty.h b/include/linux/tty.h index bb44fa9ae13..931078b7322 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -23,7 +23,7 @@ */ #define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */ #define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */ -#define NR_LDISCS 21 +#define NR_LDISCS 30 /* line disciplines */ #define N_TTY 0 @@ -48,6 +48,7 @@ #define N_PPS 18 /* Pulse per Second */ #define N_V253 19 /* Codec control over voice modem */ #define N_CAIF 20 /* CAIF protocol for talking to modems */ +#define N_GSM0710 21 /* GSM 0710 Mux */ /* * This character is the same as _POSIX_VDISABLE: it cannot be used as diff --git a/include/scsi/Kbuild b/include/scsi/Kbuild index b3a0ee6b2f1..f2b94918994 100644 --- a/include/scsi/Kbuild +++ b/include/scsi/Kbuild @@ -1,4 +1,3 @@ -header-y += scsi.h header-y += scsi_netlink.h header-y += scsi_netlink_fc.h header-y += scsi_bsg_fc.h diff --git a/include/scsi/fc/fc_fcp.h b/include/scsi/fc/fc_fcp.h index 747e2c7d88d..8e9b222251c 100644 --- a/include/scsi/fc/fc_fcp.h +++ b/include/scsi/fc/fc_fcp.h @@ -76,6 +76,7 @@ struct fcp_cmnd32 { #define FCP_PTA_HEADQ 1 /* head of queue task attribute */ #define FCP_PTA_ORDERED 2 /* ordered task attribute */ #define FCP_PTA_ACA 4 /* auto. contigent allegiance */ +#define FCP_PTA_MASK 7 /* mask for task attribute field */ #define FCP_PRI_SHIFT 3 /* priority field starts in bit 3 */ #define FCP_PRI_RESVD_MASK 0x80 /* reserved bits in priority field */ diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h index 8eb0a0fc0a7..9b4867c9c2d 100644 --- a/include/scsi/fc_encode.h +++ b/include/scsi/fc_encode.h @@ -74,7 +74,7 @@ static inline void fc_adisc_fill(struct fc_lport *lport, struct fc_frame *fp) adisc->adisc_cmd = ELS_ADISC; put_unaligned_be64(lport->wwpn, &adisc->adisc_wwpn); put_unaligned_be64(lport->wwnn, &adisc->adisc_wwnn); - hton24(adisc->adisc_port_id, fc_host_port_id(lport->host)); + hton24(adisc->adisc_port_id, lport->port_id); } /** @@ -127,15 +127,13 @@ static inline int fc_ct_fill(struct fc_lport *lport, case FC_NS_RFT_ID: ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rft)); - hton24(ct->payload.rft.fid.fp_fid, - fc_host_port_id(lport->host)); + hton24(ct->payload.rft.fid.fp_fid, lport->port_id); ct->payload.rft.fts = lport->fcts; break; case FC_NS_RFF_ID: ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rff_id)); - hton24(ct->payload.rff.fr_fid.fp_fid, - fc_host_port_id(lport->host)); + hton24(ct->payload.rff.fr_fid.fp_fid, lport->port_id); ct->payload.rff.fr_type = FC_TYPE_FCP; if (lport->service_params & FCP_SPPF_INIT_FCN) ct->payload.rff.fr_feat = FCP_FEAT_INIT; @@ -145,16 +143,14 @@ static inline int fc_ct_fill(struct fc_lport *lport, case FC_NS_RNN_ID: ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rn_id)); - hton24(ct->payload.rn.fr_fid.fp_fid, - fc_host_port_id(lport->host)); + hton24(ct->payload.rn.fr_fid.fp_fid, lport->port_id); put_unaligned_be64(lport->wwnn, &ct->payload.rn.fr_wwn); break; case FC_NS_RSPN_ID: len = strnlen(fc_host_symbolic_name(lport->host), 255); ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rspn) + len); - hton24(ct->payload.spn.fr_fid.fp_fid, - fc_host_port_id(lport->host)); + hton24(ct->payload.spn.fr_fid.fp_fid, lport->port_id); strncpy(ct->payload.spn.fr_name, fc_host_symbolic_name(lport->host), len); ct->payload.spn.fr_name_len = len; @@ -268,7 +264,7 @@ static inline void fc_logo_fill(struct fc_lport *lport, struct fc_frame *fp) logo = fc_frame_payload_get(fp, sizeof(*logo)); memset(logo, 0, sizeof(*logo)); logo->fl_cmd = ELS_LOGO; - hton24(logo->fl_n_port_id, fc_host_port_id(lport->host)); + hton24(logo->fl_n_port_id, lport->port_id); logo->fl_n_port_wwn = htonll(lport->wwpn); } @@ -295,7 +291,7 @@ static inline void fc_rec_fill(struct fc_lport *lport, struct fc_frame *fp) rec = fc_frame_payload_get(fp, sizeof(*rec)); memset(rec, 0, sizeof(*rec)); rec->rec_cmd = ELS_REC; - hton24(rec->rec_s_id, fc_host_port_id(lport->host)); + hton24(rec->rec_s_id, lport->port_id); rec->rec_ox_id = htons(ep->oxid); rec->rec_rx_id = htons(ep->rxid); } diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 4b912eee33e..7495c0ba67e 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -47,13 +47,18 @@ #define ntohll(x) be64_to_cpu(x) #define htonll(x) cpu_to_be64(x) -#define ntoh24(p) (((p)[0] << 16) | ((p)[1] << 8) | ((p)[2])) -#define hton24(p, v) do { \ - p[0] = (((v) >> 16) & 0xFF); \ - p[1] = (((v) >> 8) & 0xFF); \ - p[2] = ((v) & 0xFF); \ - } while (0) +static inline u32 ntoh24(const u8 *p) +{ + return (p[0] << 16) | (p[1] << 8) | p[2]; +} + +static inline void hton24(u8 *p, u32 v) +{ + p[0] = (v >> 16) & 0xff; + p[1] = (v >> 8) & 0xff; + p[2] = v & 0xff; +} /** * enum fc_lport_state - Local port states @@ -775,6 +780,7 @@ struct fc_disc { * @dev_stats: FCoE device stats (TODO: libfc should not be * FCoE aware) * @retry_count: Number of retries in the current state + * @port_id: FC Port ID * @wwpn: World Wide Port Name * @wwnn: World Wide Node Name * @service_params: Common service parameters @@ -821,6 +827,7 @@ struct fc_lport { u8 retry_count; /* Fabric information */ + u32 port_id; u64 wwpn; u64 wwnn; unsigned int service_params; @@ -918,15 +925,6 @@ static inline void fc_lport_free_stats(struct fc_lport *lport) } /** - * fc_lport_get_stats() - Get a local port's statistics - * @lport: The local port whose statistics are to be retreived - */ -static inline struct fcoe_dev_stats *fc_lport_get_stats(struct fc_lport *lport) -{ - return per_cpu_ptr(lport->dev_stats, smp_processor_id()); -} - -/** * lport_priv() - Return the private data from a local port * @lport: The local port whose private data is to be retreived */ @@ -1053,7 +1051,6 @@ void fc_exch_mgr_reset(struct fc_lport *, u32 s_id, u32 d_id); * Functions for fc_functions_template */ void fc_get_host_speed(struct Scsi_Host *); -void fc_get_host_port_type(struct Scsi_Host *); void fc_get_host_port_state(struct Scsi_Host *); void fc_set_rport_loss_tmo(struct fc_rport *, u32 timeout); struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *); diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h index c603f4a7e7f..ec13f51531f 100644 --- a/include/scsi/libfcoe.h +++ b/include/scsi/libfcoe.h @@ -29,6 +29,8 @@ #include <scsi/fc/fc_fcoe.h> #include <scsi/libfc.h> +#define FCOE_MAX_CMD_LEN 16 /* Supported CDB length */ + /* * FIP tunable parameters. */ @@ -65,14 +67,12 @@ enum fip_state { * @port_ka_time: time of next port keep-alive. * @ctlr_ka_time: time of next controller keep-alive. * @timer: timer struct used for all delayed events. - * @link_work: &work_struct for doing FCF selection. + * @timer_work: &work_struct for doing keep-alives and resets. * @recv_work: &work_struct for receiving FIP frames. * @fip_recv_list: list of received FIP frames. * @user_mfs: configured maximum FC frame size, including FC header. * @flogi_oxid: exchange ID of most recent fabric login. * @flogi_count: number of FLOGI attempts in AUTO mode. - * @link: current link status for libfc. - * @last_link: last link state reported to libfc. * @map_dest: use the FC_MAP mode for destination MAC addresses. * @spma: supports SPMA server-provided MACs mode * @send_ctlr_ka: need to send controller keep alive @@ -100,14 +100,12 @@ struct fcoe_ctlr { unsigned long port_ka_time; unsigned long ctlr_ka_time; struct timer_list timer; - struct work_struct link_work; + struct work_struct timer_work; struct work_struct recv_work; struct sk_buff_head fip_recv_list; u16 user_mfs; u16 flogi_oxid; u8 flogi_count; - u8 link; - u8 last_link; u8 reset_req; u8 map_dest; u8 spma; diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 8b4deca996a..9ae5c613131 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -114,6 +114,7 @@ struct scsi_cmnd; #define READ_12 0xa8 #define WRITE_12 0xaa #define WRITE_VERIFY_12 0xae +#define VERIFY_12 0xaf #define SEARCH_HIGH_12 0xb0 #define SEARCH_EQUAL_12 0xb1 #define SEARCH_LOW_12 0xb2 @@ -134,6 +135,7 @@ struct scsi_cmnd; #define MO_SET_TARGET_PGS 0x0a /* values for variable length command */ #define READ_32 0x09 +#define VERIFY_32 0x0a #define WRITE_32 0x0b #define WRITE_SAME_32 0x0d @@ -423,6 +425,7 @@ static inline int scsi_is_wlun(unsigned int lun) #define ADD_TO_MLQUEUE 0x2006 #define TIMEOUT_ERROR 0x2007 #define SCSI_RETURN_NOT_HANDLED 0x2008 +#define FAST_IO_FAIL 0x2009 /* * Midlevel queue return values. diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 8e86a94faf0..87d81b3ce56 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -807,6 +807,6 @@ void fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number, struct fc_vport *fc_vport_create(struct Scsi_Host *shost, int channel, struct fc_vport_identifiers *); int fc_vport_terminate(struct fc_vport *vport); -void fc_block_scsi_eh(struct scsi_cmnd *cmnd); +int fc_block_scsi_eh(struct scsi_cmnd *cmnd); #endif /* SCSI_TRANSPORT_FC_H */ diff --git a/include/trace/events/scsi.h b/include/trace/events/scsi.h new file mode 100644 index 00000000000..25fbefdf2f2 --- /dev/null +++ b/include/trace/events/scsi.h @@ -0,0 +1,345 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM scsi + +#if !defined(_TRACE_SCSI_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SCSI_H + +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_host.h> +#include <linux/tracepoint.h> +#include <linux/trace_seq.h> + +#define scsi_opcode_name(opcode) { opcode, #opcode } +#define show_opcode_name(val) \ + __print_symbolic(val, \ + scsi_opcode_name(TEST_UNIT_READY), \ + scsi_opcode_name(REZERO_UNIT), \ + scsi_opcode_name(REQUEST_SENSE), \ + scsi_opcode_name(FORMAT_UNIT), \ + scsi_opcode_name(READ_BLOCK_LIMITS), \ + scsi_opcode_name(REASSIGN_BLOCKS), \ + scsi_opcode_name(INITIALIZE_ELEMENT_STATUS), \ + scsi_opcode_name(READ_6), \ + scsi_opcode_name(WRITE_6), \ + scsi_opcode_name(SEEK_6), \ + scsi_opcode_name(READ_REVERSE), \ + scsi_opcode_name(WRITE_FILEMARKS), \ + scsi_opcode_name(SPACE), \ + scsi_opcode_name(INQUIRY), \ + scsi_opcode_name(RECOVER_BUFFERED_DATA), \ + scsi_opcode_name(MODE_SELECT), \ + scsi_opcode_name(RESERVE), \ + scsi_opcode_name(RELEASE), \ + scsi_opcode_name(COPY), \ + scsi_opcode_name(ERASE), \ + scsi_opcode_name(MODE_SENSE), \ + scsi_opcode_name(START_STOP), \ + scsi_opcode_name(RECEIVE_DIAGNOSTIC), \ + scsi_opcode_name(SEND_DIAGNOSTIC), \ + scsi_opcode_name(ALLOW_MEDIUM_REMOVAL), \ + scsi_opcode_name(SET_WINDOW), \ + scsi_opcode_name(READ_CAPACITY), \ + scsi_opcode_name(READ_10), \ + scsi_opcode_name(WRITE_10), \ + scsi_opcode_name(SEEK_10), \ + scsi_opcode_name(POSITION_TO_ELEMENT), \ + scsi_opcode_name(WRITE_VERIFY), \ + scsi_opcode_name(VERIFY), \ + scsi_opcode_name(SEARCH_HIGH), \ + scsi_opcode_name(SEARCH_EQUAL), \ + scsi_opcode_name(SEARCH_LOW), \ + scsi_opcode_name(SET_LIMITS), \ + scsi_opcode_name(PRE_FETCH), \ + scsi_opcode_name(READ_POSITION), \ + scsi_opcode_name(SYNCHRONIZE_CACHE), \ + scsi_opcode_name(LOCK_UNLOCK_CACHE), \ + scsi_opcode_name(READ_DEFECT_DATA), \ + scsi_opcode_name(MEDIUM_SCAN), \ + scsi_opcode_name(COMPARE), \ + scsi_opcode_name(COPY_VERIFY), \ + scsi_opcode_name(WRITE_BUFFER), \ + scsi_opcode_name(READ_BUFFER), \ + scsi_opcode_name(UPDATE_BLOCK), \ + scsi_opcode_name(READ_LONG), \ + scsi_opcode_name(WRITE_LONG), \ + scsi_opcode_name(CHANGE_DEFINITION), \ + scsi_opcode_name(WRITE_SAME), \ + scsi_opcode_name(UNMAP), \ + scsi_opcode_name(READ_TOC), \ + scsi_opcode_name(LOG_SELECT), \ + scsi_opcode_name(LOG_SENSE), \ + scsi_opcode_name(XDWRITEREAD_10), \ + scsi_opcode_name(MODE_SELECT_10), \ + scsi_opcode_name(RESERVE_10), \ + scsi_opcode_name(RELEASE_10), \ + scsi_opcode_name(MODE_SENSE_10), \ + scsi_opcode_name(PERSISTENT_RESERVE_IN), \ + scsi_opcode_name(PERSISTENT_RESERVE_OUT), \ + scsi_opcode_name(VARIABLE_LENGTH_CMD), \ + scsi_opcode_name(REPORT_LUNS), \ + scsi_opcode_name(MAINTENANCE_IN), \ + scsi_opcode_name(MAINTENANCE_OUT), \ + scsi_opcode_name(MOVE_MEDIUM), \ + scsi_opcode_name(EXCHANGE_MEDIUM), \ + scsi_opcode_name(READ_12), \ + scsi_opcode_name(WRITE_12), \ + scsi_opcode_name(WRITE_VERIFY_12), \ + scsi_opcode_name(SEARCH_HIGH_12), \ + scsi_opcode_name(SEARCH_EQUAL_12), \ + scsi_opcode_name(SEARCH_LOW_12), \ + scsi_opcode_name(READ_ELEMENT_STATUS), \ + scsi_opcode_name(SEND_VOLUME_TAG), \ + scsi_opcode_name(WRITE_LONG_2), \ + scsi_opcode_name(READ_16), \ + scsi_opcode_name(WRITE_16), \ + scsi_opcode_name(VERIFY_16), \ + scsi_opcode_name(WRITE_SAME_16), \ + scsi_opcode_name(SERVICE_ACTION_IN), \ + scsi_opcode_name(SAI_READ_CAPACITY_16), \ + scsi_opcode_name(SAI_GET_LBA_STATUS), \ + scsi_opcode_name(MI_REPORT_TARGET_PGS), \ + scsi_opcode_name(MO_SET_TARGET_PGS), \ + scsi_opcode_name(READ_32), \ + scsi_opcode_name(WRITE_32), \ + scsi_opcode_name(WRITE_SAME_32), \ + scsi_opcode_name(ATA_16), \ + scsi_opcode_name(ATA_12)) + +#define scsi_hostbyte_name(result) { result, #result } +#define show_hostbyte_name(val) \ + __print_symbolic(val, \ + scsi_hostbyte_name(DID_OK), \ + scsi_hostbyte_name(DID_NO_CONNECT), \ + scsi_hostbyte_name(DID_BUS_BUSY), \ + scsi_hostbyte_name(DID_TIME_OUT), \ + scsi_hostbyte_name(DID_BAD_TARGET), \ + scsi_hostbyte_name(DID_ABORT), \ + scsi_hostbyte_name(DID_PARITY), \ + scsi_hostbyte_name(DID_ERROR), \ + scsi_hostbyte_name(DID_RESET), \ + scsi_hostbyte_name(DID_BAD_INTR), \ + scsi_hostbyte_name(DID_PASSTHROUGH), \ + scsi_hostbyte_name(DID_SOFT_ERROR), \ + scsi_hostbyte_name(DID_IMM_RETRY), \ + scsi_hostbyte_name(DID_REQUEUE), \ + scsi_hostbyte_name(DID_TRANSPORT_DISRUPTED), \ + scsi_hostbyte_name(DID_TRANSPORT_FAILFAST)) + +#define scsi_driverbyte_name(result) { result, #result } +#define show_driverbyte_name(val) \ + __print_symbolic(val, \ + scsi_driverbyte_name(DRIVER_OK), \ + scsi_driverbyte_name(DRIVER_BUSY), \ + scsi_driverbyte_name(DRIVER_SOFT), \ + scsi_driverbyte_name(DRIVER_MEDIA), \ + scsi_driverbyte_name(DRIVER_ERROR), \ + scsi_driverbyte_name(DRIVER_INVALID), \ + scsi_driverbyte_name(DRIVER_TIMEOUT), \ + scsi_driverbyte_name(DRIVER_HARD), \ + scsi_driverbyte_name(DRIVER_SENSE)) + +#define scsi_msgbyte_name(result) { result, #result } +#define show_msgbyte_name(val) \ + __print_symbolic(val, \ + scsi_msgbyte_name(COMMAND_COMPLETE), \ + scsi_msgbyte_name(EXTENDED_MESSAGE), \ + scsi_msgbyte_name(SAVE_POINTERS), \ + scsi_msgbyte_name(RESTORE_POINTERS), \ + scsi_msgbyte_name(DISCONNECT), \ + scsi_msgbyte_name(INITIATOR_ERROR), \ + scsi_msgbyte_name(ABORT_TASK_SET), \ + scsi_msgbyte_name(MESSAGE_REJECT), \ + scsi_msgbyte_name(NOP), \ + scsi_msgbyte_name(MSG_PARITY_ERROR), \ + scsi_msgbyte_name(LINKED_CMD_COMPLETE), \ + scsi_msgbyte_name(LINKED_FLG_CMD_COMPLETE), \ + scsi_msgbyte_name(TARGET_RESET), \ + scsi_msgbyte_name(ABORT_TASK), \ + scsi_msgbyte_name(CLEAR_TASK_SET), \ + scsi_msgbyte_name(INITIATE_RECOVERY), \ + scsi_msgbyte_name(RELEASE_RECOVERY), \ + scsi_msgbyte_name(CLEAR_ACA), \ + scsi_msgbyte_name(LOGICAL_UNIT_RESET), \ + scsi_msgbyte_name(SIMPLE_QUEUE_TAG), \ + scsi_msgbyte_name(HEAD_OF_QUEUE_TAG), \ + scsi_msgbyte_name(ORDERED_QUEUE_TAG), \ + scsi_msgbyte_name(IGNORE_WIDE_RESIDUE), \ + scsi_msgbyte_name(ACA), \ + scsi_msgbyte_name(QAS_REQUEST), \ + scsi_msgbyte_name(BUS_DEVICE_RESET), \ + scsi_msgbyte_name(ABORT)) + +#define scsi_statusbyte_name(result) { result, #result } +#define show_statusbyte_name(val) \ + __print_symbolic(val, \ + scsi_statusbyte_name(SAM_STAT_GOOD), \ + scsi_statusbyte_name(SAM_STAT_CHECK_CONDITION), \ + scsi_statusbyte_name(SAM_STAT_CONDITION_MET), \ + scsi_statusbyte_name(SAM_STAT_BUSY), \ + scsi_statusbyte_name(SAM_STAT_INTERMEDIATE), \ + scsi_statusbyte_name(SAM_STAT_INTERMEDIATE_CONDITION_MET), \ + scsi_statusbyte_name(SAM_STAT_RESERVATION_CONFLICT), \ + scsi_statusbyte_name(SAM_STAT_COMMAND_TERMINATED), \ + scsi_statusbyte_name(SAM_STAT_TASK_SET_FULL), \ + scsi_statusbyte_name(SAM_STAT_ACA_ACTIVE), \ + scsi_statusbyte_name(SAM_STAT_TASK_ABORTED)) + +const char *scsi_trace_parse_cdb(struct trace_seq*, unsigned char*, int); +#define __parse_cdb(cdb, len) scsi_trace_parse_cdb(p, cdb, len) + +TRACE_EVENT(scsi_dispatch_cmd_start, + + TP_PROTO(struct scsi_cmnd *cmd), + + TP_ARGS(cmd), + + TP_STRUCT__entry( + __field( unsigned int, host_no ) + __field( unsigned int, channel ) + __field( unsigned int, id ) + __field( unsigned int, lun ) + __field( unsigned int, opcode ) + __field( unsigned int, cmd_len ) + __field( unsigned int, data_sglen ) + __field( unsigned int, prot_sglen ) + __dynamic_array(unsigned char, cmnd, cmd->cmd_len) + ), + + TP_fast_assign( + __entry->host_no = cmd->device->host->host_no; + __entry->channel = cmd->device->channel; + __entry->id = cmd->device->id; + __entry->lun = cmd->device->lun; + __entry->opcode = cmd->cmnd[0]; + __entry->cmd_len = cmd->cmd_len; + __entry->data_sglen = scsi_sg_count(cmd); + __entry->prot_sglen = scsi_prot_sg_count(cmd); + memcpy(__get_dynamic_array(cmnd), cmd->cmnd, cmd->cmd_len); + ), + + TP_printk("host_no=%u channel=%u id=%u lun=%u data_sgl=%u prot_sgl=%u" \ + " cmnd=(%s %s raw=%s)", + __entry->host_no, __entry->channel, __entry->id, + __entry->lun, __entry->data_sglen, __entry->prot_sglen, + show_opcode_name(__entry->opcode), + __parse_cdb(__get_dynamic_array(cmnd), __entry->cmd_len), + __print_hex(__get_dynamic_array(cmnd), __entry->cmd_len)) +); + +TRACE_EVENT(scsi_dispatch_cmd_error, + + TP_PROTO(struct scsi_cmnd *cmd, int rtn), + + TP_ARGS(cmd, rtn), + + TP_STRUCT__entry( + __field( unsigned int, host_no ) + __field( unsigned int, channel ) + __field( unsigned int, id ) + __field( unsigned int, lun ) + __field( int, rtn ) + __field( unsigned int, opcode ) + __field( unsigned int, cmd_len ) + __field( unsigned int, data_sglen ) + __field( unsigned int, prot_sglen ) + __dynamic_array(unsigned char, cmnd, cmd->cmd_len) + ), + + TP_fast_assign( + __entry->host_no = cmd->device->host->host_no; + __entry->channel = cmd->device->channel; + __entry->id = cmd->device->id; + __entry->lun = cmd->device->lun; + __entry->rtn = rtn; + __entry->opcode = cmd->cmnd[0]; + __entry->cmd_len = cmd->cmd_len; + __entry->data_sglen = scsi_sg_count(cmd); + __entry->prot_sglen = scsi_prot_sg_count(cmd); + memcpy(__get_dynamic_array(cmnd), cmd->cmnd, cmd->cmd_len); + ), + + TP_printk("host_no=%u channel=%u id=%u lun=%u data_sgl=%u prot_sgl=%u" \ + " cmnd=(%s %s raw=%s) rtn=%d", + __entry->host_no, __entry->channel, __entry->id, + __entry->lun, __entry->data_sglen, __entry->prot_sglen, + show_opcode_name(__entry->opcode), + __parse_cdb(__get_dynamic_array(cmnd), __entry->cmd_len), + __print_hex(__get_dynamic_array(cmnd), __entry->cmd_len), + __entry->rtn) +); + +DECLARE_EVENT_CLASS(scsi_cmd_done_timeout_template, + + TP_PROTO(struct scsi_cmnd *cmd), + + TP_ARGS(cmd), + + TP_STRUCT__entry( + __field( unsigned int, host_no ) + __field( unsigned int, channel ) + __field( unsigned int, id ) + __field( unsigned int, lun ) + __field( int, result ) + __field( unsigned int, opcode ) + __field( unsigned int, cmd_len ) + __field( unsigned int, data_sglen ) + __field( unsigned int, prot_sglen ) + __dynamic_array(unsigned char, cmnd, cmd->cmd_len) + ), + + TP_fast_assign( + __entry->host_no = cmd->device->host->host_no; + __entry->channel = cmd->device->channel; + __entry->id = cmd->device->id; + __entry->lun = cmd->device->lun; + __entry->result = cmd->result; + __entry->opcode = cmd->cmnd[0]; + __entry->cmd_len = cmd->cmd_len; + __entry->data_sglen = scsi_sg_count(cmd); + __entry->prot_sglen = scsi_prot_sg_count(cmd); + memcpy(__get_dynamic_array(cmnd), cmd->cmnd, cmd->cmd_len); + ), + + TP_printk("host_no=%u channel=%u id=%u lun=%u data_sgl=%u " \ + "prot_sgl=%u cmnd=(%s %s raw=%s) result=(driver=%s host=%s " \ + "message=%s status=%s)", + __entry->host_no, __entry->channel, __entry->id, + __entry->lun, __entry->data_sglen, __entry->prot_sglen, + show_opcode_name(__entry->opcode), + __parse_cdb(__get_dynamic_array(cmnd), __entry->cmd_len), + __print_hex(__get_dynamic_array(cmnd), __entry->cmd_len), + show_driverbyte_name(((__entry->result) >> 24) & 0xff), + show_hostbyte_name(((__entry->result) >> 16) & 0xff), + show_msgbyte_name(((__entry->result) >> 8) & 0xff), + show_statusbyte_name(__entry->result & 0xff)) +); + +DEFINE_EVENT(scsi_cmd_done_timeout_template, scsi_dispatch_cmd_done, + TP_PROTO(struct scsi_cmnd *cmd), + TP_ARGS(cmd)); + +DEFINE_EVENT(scsi_cmd_done_timeout_template, scsi_dispatch_cmd_timeout, + TP_PROTO(struct scsi_cmnd *cmd), + TP_ARGS(cmd)); + +TRACE_EVENT(scsi_eh_wakeup, + + TP_PROTO(struct Scsi_Host *shost), + + TP_ARGS(shost), + + TP_STRUCT__entry( + __field( unsigned int, host_no ) + ), + + TP_fast_assign( + __entry->host_no = shost->host_no; + ), + + TP_printk("host_no=%u", __entry->host_no) +); + +#endif /* _TRACE_SCSI_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 16253db38d7..88c59c13ea7 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -200,6 +200,9 @@ ftrace_print_symbols_seq(p, value, symbols); \ }) +#undef __print_hex +#define __print_hex(buf, buf_len) ftrace_print_hex_seq(p, buf, buf_len) + #undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ static notrace enum print_line_t \ diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index 21fe3c42694..0b624e79180 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -138,7 +138,8 @@ extern const void __start_notes __attribute__((weak)); extern const void __stop_notes __attribute__((weak)); #define notes_size (&__stop_notes - &__start_notes) -static ssize_t notes_read(struct kobject *kobj, struct bin_attribute *bin_attr, +static ssize_t notes_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { memcpy(buf, &__start_notes + off, count); diff --git a/kernel/lockdep.c b/kernel/lockdep.c index ec21304856d..54286798c37 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -2711,6 +2711,8 @@ void lockdep_init_map(struct lockdep_map *lock, const char *name, } EXPORT_SYMBOL_GPL(lockdep_init_map); +struct lock_class_key __lockdep_no_validate__; + /* * This gets called for every mutex_lock*()/spin_lock*() operation. * We maintain the dependency maps and validate the locking attempt: @@ -2745,6 +2747,9 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, return 0; } + if (lock->key == &__lockdep_no_validate__) + check = 1; + if (!subclass) class = lock->class_cache; /* diff --git a/kernel/module.c b/kernel/module.c index e2564580f3f..5e14483768b 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1182,7 +1182,7 @@ struct module_notes_attrs { struct bin_attribute attrs[0]; }; -static ssize_t module_notes_read(struct kobject *kobj, +static ssize_t module_notes_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 2404c129a8c..ab13d700806 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -209,6 +209,7 @@ int trace_seq_putc(struct trace_seq *s, unsigned char c) return 1; } +EXPORT_SYMBOL(trace_seq_putc); int trace_seq_putmem(struct trace_seq *s, const void *mem, size_t len) { @@ -355,6 +356,21 @@ ftrace_print_symbols_seq(struct trace_seq *p, unsigned long val, } EXPORT_SYMBOL(ftrace_print_symbols_seq); +const char * +ftrace_print_hex_seq(struct trace_seq *p, const unsigned char *buf, int buf_len) +{ + int i; + const char *ret = p->buffer + p->len; + + for (i = 0; i < buf_len; i++) + trace_seq_printf(p, "%s%2.2x", i == 0 ? "" : " ", buf[i]); + + trace_seq_putc(p, 0); + + return ret; +} +EXPORT_SYMBOL(ftrace_print_hex_seq); + #ifdef CONFIG_KRETPROBES static inline const char *kretprobed(const char *name) { diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 076c7c8215b..b2d70d38dff 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -54,8 +54,8 @@ int create_user_ns(struct cred *new) #endif /* tgcred will be cleared in our caller bc CLONE_THREAD won't be set */ - /* alloc_uid() incremented the userns refcount. Just set it to 1 */ - kref_set(&ns->kref, 1); + /* root_user holds a reference to ns, our reference can be dropped */ + put_user_ns(ns); return 0; } diff --git a/lib/idr.c b/lib/idr.c index 2eb1dca0368..422a9d5069c 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -623,7 +623,7 @@ void *idr_get_next(struct idr *idp, int *nextidp) } return NULL; } - +EXPORT_SYMBOL(idr_get_next); /** diff --git a/lib/kobject.c b/lib/kobject.c index 8115eb1bbf4..f07c57252e8 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -850,6 +850,121 @@ struct kset *kset_create_and_add(const char *name, } EXPORT_SYMBOL_GPL(kset_create_and_add); + +static DEFINE_SPINLOCK(kobj_ns_type_lock); +static const struct kobj_ns_type_operations *kobj_ns_ops_tbl[KOBJ_NS_TYPES]; + +int kobj_ns_type_register(const struct kobj_ns_type_operations *ops) +{ + enum kobj_ns_type type = ops->type; + int error; + + spin_lock(&kobj_ns_type_lock); + + error = -EINVAL; + if (type >= KOBJ_NS_TYPES) + goto out; + + error = -EINVAL; + if (type <= KOBJ_NS_TYPE_NONE) + goto out; + + error = -EBUSY; + if (kobj_ns_ops_tbl[type]) + goto out; + + error = 0; + kobj_ns_ops_tbl[type] = ops; + +out: + spin_unlock(&kobj_ns_type_lock); + return error; +} + +int kobj_ns_type_registered(enum kobj_ns_type type) +{ + int registered = 0; + + spin_lock(&kobj_ns_type_lock); + if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES)) + registered = kobj_ns_ops_tbl[type] != NULL; + spin_unlock(&kobj_ns_type_lock); + + return registered; +} + +const struct kobj_ns_type_operations *kobj_child_ns_ops(struct kobject *parent) +{ + const struct kobj_ns_type_operations *ops = NULL; + + if (parent && parent->ktype->child_ns_type) + ops = parent->ktype->child_ns_type(parent); + + return ops; +} + +const struct kobj_ns_type_operations *kobj_ns_ops(struct kobject *kobj) +{ + return kobj_child_ns_ops(kobj->parent); +} + + +const void *kobj_ns_current(enum kobj_ns_type type) +{ + const void *ns = NULL; + + spin_lock(&kobj_ns_type_lock); + if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && + kobj_ns_ops_tbl[type]) + ns = kobj_ns_ops_tbl[type]->current_ns(); + spin_unlock(&kobj_ns_type_lock); + + return ns; +} + +const void *kobj_ns_netlink(enum kobj_ns_type type, struct sock *sk) +{ + const void *ns = NULL; + + spin_lock(&kobj_ns_type_lock); + if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && + kobj_ns_ops_tbl[type]) + ns = kobj_ns_ops_tbl[type]->netlink_ns(sk); + spin_unlock(&kobj_ns_type_lock); + + return ns; +} + +const void *kobj_ns_initial(enum kobj_ns_type type) +{ + const void *ns = NULL; + + spin_lock(&kobj_ns_type_lock); + if ((type > KOBJ_NS_TYPE_NONE) && (type < KOBJ_NS_TYPES) && + kobj_ns_ops_tbl[type]) + ns = kobj_ns_ops_tbl[type]->initial_ns(); + spin_unlock(&kobj_ns_type_lock); + + return ns; +} + +/* + * kobj_ns_exit - invalidate a namespace tag + * + * @type: the namespace type (i.e. KOBJ_NS_TYPE_NET) + * @ns: the actual namespace being invalidated + * + * This is called when a tag is no longer valid. For instance, + * when a network namespace exits, it uses this helper to + * make sure no sb's sysfs_info points to the now-invalidated + * netns. + */ +void kobj_ns_exit(enum kobj_ns_type type, const void *ns) +{ + sysfs_exit_ns(type, ns); +} + + EXPORT_SYMBOL(kobject_get); EXPORT_SYMBOL(kobject_put); EXPORT_SYMBOL(kobject_del); diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 7b48d44ced6..59c15511d58 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -19,18 +19,24 @@ #include <linux/kobject.h> #include <linux/module.h> #include <linux/slab.h> - +#include <linux/user_namespace.h> #include <linux/socket.h> #include <linux/skbuff.h> #include <linux/netlink.h> #include <net/sock.h> +#include <net/net_namespace.h> u64 uevent_seqnum; char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; static DEFINE_SPINLOCK(sequence_lock); -#if defined(CONFIG_NET) -static struct sock *uevent_sock; +#ifdef CONFIG_NET +struct uevent_sock { + struct list_head list; + struct sock *sk; +}; +static LIST_HEAD(uevent_sock_list); +static DEFINE_MUTEX(uevent_sock_mutex); #endif /* the strings here must match the enum in include/linux/kobject.h */ @@ -77,6 +83,37 @@ out: return ret; } +static int kobj_bcast_filter(struct sock *dsk, struct sk_buff *skb, void *data) +{ + struct kobject *kobj = data; + const struct kobj_ns_type_operations *ops; + + ops = kobj_ns_ops(kobj); + if (ops) { + const void *sock_ns, *ns; + ns = kobj->ktype->namespace(kobj); + sock_ns = ops->netlink_ns(dsk); + return sock_ns != ns; + } + + return 0; +} + +static int kobj_usermode_filter(struct kobject *kobj) +{ + const struct kobj_ns_type_operations *ops; + + ops = kobj_ns_ops(kobj); + if (ops) { + const void *init_ns, *ns; + ns = kobj->ktype->namespace(kobj); + init_ns = ops->initial_ns(); + return ns != init_ns; + } + + return 0; +} + /** * kobject_uevent_env - send an uevent with environmental data * @@ -100,6 +137,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, u64 seq; int i = 0; int retval = 0; +#ifdef CONFIG_NET + struct uevent_sock *ue_sk; +#endif pr_debug("kobject: '%s' (%p): %s\n", kobject_name(kobj), kobj, __func__); @@ -211,7 +251,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, #if defined(CONFIG_NET) /* send netlink message */ - if (uevent_sock) { + mutex_lock(&uevent_sock_mutex); + list_for_each_entry(ue_sk, &uevent_sock_list, list) { + struct sock *uevent_sock = ue_sk->sk; struct sk_buff *skb; size_t len; @@ -233,18 +275,21 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, } NETLINK_CB(skb).dst_group = 1; - retval = netlink_broadcast(uevent_sock, skb, 0, 1, - GFP_KERNEL); + retval = netlink_broadcast_filtered(uevent_sock, skb, + 0, 1, GFP_KERNEL, + kobj_bcast_filter, + kobj); /* ENOBUFS should be handled in userspace */ if (retval == -ENOBUFS) retval = 0; } else retval = -ENOMEM; } + mutex_unlock(&uevent_sock_mutex); #endif /* call uevent_helper, usually only enabled during early boot */ - if (uevent_helper[0]) { + if (uevent_helper[0] && !kobj_usermode_filter(kobj)) { char *argv [3]; argv [0] = uevent_helper; @@ -320,18 +365,58 @@ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...) EXPORT_SYMBOL_GPL(add_uevent_var); #if defined(CONFIG_NET) -static int __init kobject_uevent_init(void) +static int uevent_net_init(struct net *net) { - uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT, - 1, NULL, NULL, THIS_MODULE); - if (!uevent_sock) { + struct uevent_sock *ue_sk; + + ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL); + if (!ue_sk) + return -ENOMEM; + + ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT, + 1, NULL, NULL, THIS_MODULE); + if (!ue_sk->sk) { printk(KERN_ERR "kobject_uevent: unable to create netlink socket!\n"); return -ENODEV; } - netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV); + mutex_lock(&uevent_sock_mutex); + list_add_tail(&ue_sk->list, &uevent_sock_list); + mutex_unlock(&uevent_sock_mutex); return 0; } +static void uevent_net_exit(struct net *net) +{ + struct uevent_sock *ue_sk; + + mutex_lock(&uevent_sock_mutex); + list_for_each_entry(ue_sk, &uevent_sock_list, list) { + if (sock_net(ue_sk->sk) == net) + goto found; + } + mutex_unlock(&uevent_sock_mutex); + return; + +found: + list_del(&ue_sk->list); + mutex_unlock(&uevent_sock_mutex); + + netlink_kernel_release(ue_sk->sk); + kfree(ue_sk); +} + +static struct pernet_operations uevent_net_ops = { + .init = uevent_net_init, + .exit = uevent_net_exit, +}; + +static int __init kobject_uevent_init(void) +{ + netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV); + return register_pernet_subsys(&uevent_net_ops); +} + + postcore_initcall(kobject_uevent_init); #endif diff --git a/lib/kref.c b/lib/kref.c index 6d19f690380..d3d227a08a4 100644 --- a/lib/kref.c +++ b/lib/kref.c @@ -16,23 +16,13 @@ #include <linux/slab.h> /** - * kref_set - initialize object and set refcount to requested number. - * @kref: object in question. - * @num: initial reference counter - */ -void kref_set(struct kref *kref, int num) -{ - atomic_set(&kref->refcount, num); - smp_mb(); -} - -/** * kref_init - initialize object. * @kref: object in question. */ void kref_init(struct kref *kref) { - kref_set(kref, 1); + atomic_set(&kref->refcount, 1); + smp_mb(); } /** @@ -72,7 +62,6 @@ int kref_put(struct kref *kref, void (*release)(struct kref *kref)) return 0; } -EXPORT_SYMBOL(kref_set); EXPORT_SYMBOL(kref_init); EXPORT_SYMBOL(kref_get); EXPORT_SYMBOL(kref_put); diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index dd321e39e62..486b8f3861d 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -659,7 +659,7 @@ static struct attribute_group bridge_group = { * * Returns the number of bytes read. */ -static ssize_t brforward_read(struct kobject *kobj, +static ssize_t brforward_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { diff --git a/net/core/dev.c b/net/core/dev.c index 6c820650b80..d273e4e3ecd 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1002,15 +1002,10 @@ int dev_change_name(struct net_device *dev, const char *newname) return err; rollback: - /* For now only devices in the initial network namespace - * are in sysfs. - */ - if (net_eq(net, &init_net)) { - ret = device_rename(&dev->dev, dev->name); - if (ret) { - memcpy(dev->name, oldname, IFNAMSIZ); - return ret; - } + ret = device_rename(&dev->dev, dev->name); + if (ret) { + memcpy(dev->name, oldname, IFNAMSIZ); + return ret; } write_lock_bh(&dev_base_lock); @@ -4994,8 +4989,6 @@ int register_netdevice(struct net_device *dev) if (dev->features & NETIF_F_SG) dev->features |= NETIF_F_GSO; - netdev_initialize_kobject(dev); - ret = call_netdevice_notifiers(NETDEV_POST_INIT, dev); ret = notifier_to_errno(ret); if (ret) @@ -5547,15 +5540,6 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char if (dev->features & NETIF_F_NETNS_LOCAL) goto out; -#ifdef CONFIG_SYSFS - /* Don't allow real devices to be moved when sysfs - * is enabled. - */ - err = -EINVAL; - if (dev->dev.parent) - goto out; -#endif - /* Ensure the device has been registrered */ err = -EINVAL; if (dev->reg_state != NETREG_REGISTERED) @@ -5606,8 +5590,6 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char dev_uc_flush(dev); dev_mc_flush(dev); - netdev_unregister_kobject(dev); - /* Actually switch the network namespace */ dev_net_set(dev, net); @@ -5620,7 +5602,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char } /* Fixup kobjects */ - err = netdev_register_kobject(dev); + err = device_rename(&dev->dev, dev->name); WARN_ON(err); /* Add the device back in the hashes */ diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index c57c4b228bb..99e7052d732 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -14,7 +14,9 @@ #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/slab.h> +#include <linux/nsproxy.h> #include <net/sock.h> +#include <net/net_namespace.h> #include <linux/rtnetlink.h> #include <linux/wireless.h> #include <linux/vmalloc.h> @@ -467,6 +469,7 @@ static struct attribute_group wireless_group = { .attrs = wireless_attrs, }; #endif +#endif /* CONFIG_SYSFS */ #ifdef CONFIG_RPS /* @@ -766,7 +769,38 @@ static void rx_queue_remove_kobjects(struct net_device *net) kset_unregister(net->queues_kset); } #endif /* CONFIG_RPS */ -#endif /* CONFIG_SYSFS */ + +static const void *net_current_ns(void) +{ + return current->nsproxy->net_ns; +} + +static const void *net_initial_ns(void) +{ + return &init_net; +} + +static const void *net_netlink_ns(struct sock *sk) +{ + return sock_net(sk); +} + +static struct kobj_ns_type_operations net_ns_type_operations = { + .type = KOBJ_NS_TYPE_NET, + .current_ns = net_current_ns, + .netlink_ns = net_netlink_ns, + .initial_ns = net_initial_ns, +}; + +static void net_kobj_ns_exit(struct net *net) +{ + kobj_ns_exit(KOBJ_NS_TYPE_NET, net); +} + +static struct pernet_operations kobj_net_ops = { + .exit = net_kobj_ns_exit, +}; + #ifdef CONFIG_HOTPLUG static int netdev_uevent(struct device *d, struct kobj_uevent_env *env) @@ -774,9 +808,6 @@ static int netdev_uevent(struct device *d, struct kobj_uevent_env *env) struct net_device *dev = to_net_dev(d); int retval; - if (!net_eq(dev_net(dev), &init_net)) - return 0; - /* pass interface to uevent. */ retval = add_uevent_var(env, "INTERFACE=%s", dev->name); if (retval) @@ -806,6 +837,13 @@ static void netdev_release(struct device *d) kfree((char *)dev - dev->padded); } +static const void *net_namespace(struct device *d) +{ + struct net_device *dev; + dev = container_of(d, struct net_device, dev); + return dev_net(dev); +} + static struct class net_class = { .name = "net", .dev_release = netdev_release, @@ -815,6 +853,8 @@ static struct class net_class = { #ifdef CONFIG_HOTPLUG .dev_uevent = netdev_uevent, #endif + .ns_type = &net_ns_type_operations, + .namespace = net_namespace, }; /* Delete sysfs entries but hold kobject reference until after all @@ -826,9 +866,6 @@ void netdev_unregister_kobject(struct net_device * net) kobject_get(&dev->kobj); - if (!net_eq(dev_net(net), &init_net)) - return; - #ifdef CONFIG_RPS rx_queue_remove_kobjects(net); #endif @@ -843,6 +880,7 @@ int netdev_register_kobject(struct net_device *net) const struct attribute_group **groups = net->sysfs_groups; int error = 0; + device_initialize(dev); dev->class = &net_class; dev->platform_data = net; dev->groups = groups; @@ -865,9 +903,6 @@ int netdev_register_kobject(struct net_device *net) #endif #endif /* CONFIG_SYSFS */ - if (!net_eq(dev_net(net), &init_net)) - return 0; - error = device_add(dev); if (error) return error; @@ -896,13 +931,9 @@ void netdev_class_remove_file(struct class_attribute *class_attr) EXPORT_SYMBOL(netdev_class_create_file); EXPORT_SYMBOL(netdev_class_remove_file); -void netdev_initialize_kobject(struct net_device *net) -{ - struct device *device = &(net->dev); - device_initialize(device); -} - int netdev_kobject_init(void) { + kobj_ns_type_register(&net_ns_type_operations); + register_pernet_subsys(&kobj_net_ops); return class_register(&net_class); } diff --git a/net/core/net-sysfs.h b/net/core/net-sysfs.h index 14e7524260b..805555e8b18 100644 --- a/net/core/net-sysfs.h +++ b/net/core/net-sysfs.h @@ -4,5 +4,4 @@ int netdev_kobject_init(void); int netdev_register_kobject(struct net_device *); void netdev_unregister_kobject(struct net_device *); -void netdev_initialize_kobject(struct net_device *); #endif diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6464a1972a6..a2eb965207d 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -978,6 +978,8 @@ struct netlink_broadcast_data { int delivered; gfp_t allocation; struct sk_buff *skb, *skb2; + int (*tx_filter)(struct sock *dsk, struct sk_buff *skb, void *data); + void *tx_data; }; static inline int do_one_broadcast(struct sock *sk, @@ -1020,6 +1022,9 @@ static inline int do_one_broadcast(struct sock *sk, p->failure = 1; if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR) p->delivery_failure = 1; + } else if (p->tx_filter && p->tx_filter(sk, p->skb2, p->tx_data)) { + kfree_skb(p->skb2); + p->skb2 = NULL; } else if (sk_filter(sk, p->skb2)) { kfree_skb(p->skb2); p->skb2 = NULL; @@ -1038,8 +1043,10 @@ out: return 0; } -int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, - u32 group, gfp_t allocation) +int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, u32 pid, + u32 group, gfp_t allocation, + int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data), + void *filter_data) { struct net *net = sock_net(ssk); struct netlink_broadcast_data info; @@ -1059,6 +1066,8 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, info.allocation = allocation; info.skb = skb; info.skb2 = NULL; + info.tx_filter = filter; + info.tx_data = filter_data; /* While we sleep in clone, do not allow to change socket list */ @@ -1083,6 +1092,14 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, } return -ESRCH; } +EXPORT_SYMBOL(netlink_broadcast_filtered); + +int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, + u32 group, gfp_t allocation) +{ + return netlink_broadcast_filtered(ssk, skb, pid, group, allocation, + NULL, NULL); +} EXPORT_SYMBOL(netlink_broadcast); struct netlink_set_err_data { diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index a4d74344d80..f2bbea90070 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2656,6 +2656,7 @@ sub process { # check for semaphores used as mutexes if ($line =~ /^.\s*init_MUTEX_LOCKED\s*\(/) { WARN("consider using a completion\n" . $herecurr); + } # recommend strict_strto* over simple_strto* if ($line =~ /\bsimple_(strto.*?)\s*\(/) { @@ -2740,6 +2741,16 @@ sub process { WARN("use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); } } + +# check for lockdep_set_novalidate_class + if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || + $line =~ /__lockdep_no_validate__\s*\)/ ) { + if ($realfile !~ m@^kernel/lockdep@ && + $realfile !~ m@^include/linux/lockdep@ && + $realfile !~ m@^drivers/base/core@) { + ERROR("lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); + } + } } # If we have no input at all, then there is nothing to report on diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c index 2dc2d659414..7625b85c227 100644 --- a/security/integrity/ima/ima_iint.c +++ b/security/integrity/ima/ima_iint.c @@ -94,7 +94,7 @@ void iint_free(struct kref *kref) iint->opencount); iint->opencount = 0; } - kref_set(&iint->refcount, 1); + kref_init(&iint->refcount); kmem_cache_free(iint_cache, iint); } @@ -133,7 +133,7 @@ static void init_once(void *foo) iint->readcount = 0; iint->writecount = 0; iint->opencount = 0; - kref_set(&iint->refcount, 1); + kref_init(&iint->refcount); } static int __init ima_iintcache_init(void) |