diff options
Diffstat (limited to 'drivers')
172 files changed, 9911 insertions, 1643 deletions
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 8524939..f911a2f 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -32,6 +32,7 @@ #include <linux/pm_runtime.h> #include <linux/pci.h> #include <linux/pci-acpi.h> +#include <linux/pci-aspm.h> #include <linux/acpi.h> #include <linux/slab.h> #include <acpi/acpi_bus.h> @@ -564,7 +565,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) /* Indicate support for various _OSC capabilities. */ if (pci_ext_cfg_avail(root->bus->self)) flags |= OSC_EXT_PCI_CONFIG_SUPPORT; - if (pcie_aspm_enabled()) + if (pcie_aspm_support_enabled()) flags |= OSC_ACTIVE_STATE_PWR_SUPPORT | OSC_CLOCK_PWR_CAPABILITY_SUPPORT; if (pci_msi_enabled()) @@ -591,12 +592,16 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) status = acpi_pci_osc_control_set(device->handle, &flags, OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); - if (ACPI_SUCCESS(status)) + if (ACPI_SUCCESS(status)) { dev_info(root->bus->bridge, "ACPI _OSC control (0x%02x) granted\n", flags); - else + } else { dev_dbg(root->bus->bridge, "ACPI _OSC request failed (code %d)\n", status); + printk(KERN_INFO "Unable to assume _OSC PCIe control. " + "Disabling ASPM\n"); + pcie_no_aspm(); + } } pci_acpi_add_bus_pm_notifier(device, root->bus); diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index d57e8d0..e9e5238 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -168,4 +168,11 @@ config SYS_HYPERVISOR bool default n +config ARCH_NO_SYSDEV_OPS + bool + ---help--- + To be selected by architectures that don't use sysdev class or + sysdev driver power management (suspend/resume) and shutdown + operations. + endmenu diff --git a/drivers/base/sys.c b/drivers/base/sys.c index f6fb547..fbe72da 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -329,7 +329,7 @@ void sysdev_unregister(struct sys_device *sysdev) } - +#ifndef CONFIG_ARCH_NO_SYSDEV_OPS /** * sysdev_shutdown - Shut down all system devices. * @@ -524,6 +524,7 @@ int sysdev_resume(void) return 0; } EXPORT_SYMBOL_GPL(sysdev_resume); +#endif /* CONFIG_ARCH_NO_SYSDEV_OPS */ int __init system_bus_init(void) { diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 0f17ad8..b03771d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -28,6 +28,7 @@ #include <linux/cpu.h> #include <linux/completion.h> #include <linux/mutex.h> +#include <linux/syscore_ops.h> #include <trace/events/power.h> @@ -1340,35 +1341,31 @@ out: } EXPORT_SYMBOL(cpufreq_get); +static struct sysdev_driver cpufreq_sysdev_driver = { + .add = cpufreq_add_dev, + .remove = cpufreq_remove_dev, +}; + /** - * cpufreq_suspend - let the low level driver prepare for suspend + * cpufreq_bp_suspend - Prepare the boot CPU for system suspend. + * + * This function is only executed for the boot processor. The other CPUs + * have been put offline by means of CPU hotplug. */ - -static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg) +static int cpufreq_bp_suspend(void) { int ret = 0; - int cpu = sysdev->id; + int cpu = smp_processor_id(); struct cpufreq_policy *cpu_policy; dprintk("suspending cpu %u\n", cpu); - if (!cpu_online(cpu)) - return 0; - - /* we may be lax here as interrupts are off. Nonetheless - * we need to grab the correct cpu policy, as to check - * whether we really run on this CPU. - */ - + /* If there's no policy for the boot CPU, we have nothing to do. */ cpu_policy = cpufreq_cpu_get(cpu); if (!cpu_policy) - return -EINVAL; - - /* only handle each CPU group once */ - if (unlikely(cpu_policy->cpu != cpu)) - goto out; + return 0; if (cpufreq_driver->suspend) { ret = cpufreq_driver->suspend(cpu_policy); @@ -1377,13 +1374,12 @@ static int cpufreq_suspend(struct sys_device *sysdev, pm_message_t pmsg) "step on CPU %u\n", cpu_policy->cpu); } -out: cpufreq_cpu_put(cpu_policy); return ret; } /** - * cpufreq_resume - restore proper CPU frequency handling after resume + * cpufreq_bp_resume - Restore proper frequency handling of the boot CPU. * * 1.) resume CPUfreq hardware support (cpufreq_driver->resume()) * 2.) schedule call cpufreq_update_policy() ASAP as interrupts are @@ -1391,31 +1387,23 @@ out: * what we believe it to be. This is a bit later than when it * should be, but nonethteless it's better than calling * cpufreq_driver->get() here which might re-enable interrupts... + * + * This function is only executed for the boot CPU. The other CPUs have not + * been turned on yet. */ -static int cpufreq_resume(struct sys_device *sysdev) +static void cpufreq_bp_resume(void) { int ret = 0; - int cpu = sysdev->id; + int cpu = smp_processor_id(); struct cpufreq_policy *cpu_policy; dprintk("resuming cpu %u\n", cpu); - if (!cpu_online(cpu)) - return 0; - - /* we may be lax here as interrupts are off. Nonetheless - * we need to grab the correct cpu policy, as to check - * whether we really run on this CPU. - */ - + /* If there's no policy for the boot CPU, we have nothing to do. */ cpu_policy = cpufreq_cpu_get(cpu); if (!cpu_policy) - return -EINVAL; - - /* only handle each CPU group once */ - if (unlikely(cpu_policy->cpu != cpu)) - goto fail; + return; if (cpufreq_driver->resume) { ret = cpufreq_driver->resume(cpu_policy); @@ -1430,14 +1418,11 @@ static int cpufreq_resume(struct sys_device *sysdev) fail: cpufreq_cpu_put(cpu_policy); - return ret; } -static struct sysdev_driver cpufreq_sysdev_driver = { - .add = cpufreq_add_dev, - .remove = cpufreq_remove_dev, - .suspend = cpufreq_suspend, - .resume = cpufreq_resume, +static struct syscore_ops cpufreq_syscore_ops = { + .suspend = cpufreq_bp_suspend, + .resume = cpufreq_bp_resume, }; @@ -2002,6 +1987,7 @@ static int __init cpufreq_core_init(void) cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_sysdev_class.kset.kobj); BUG_ON(!cpufreq_global_kobject); + register_syscore_ops(&cpufreq_syscore_ops); return 0; } diff --git a/drivers/gpio/adp5588-gpio.c b/drivers/gpio/adp5588-gpio.c index 33fc685..3525ad9 100644 --- a/drivers/gpio/adp5588-gpio.c +++ b/drivers/gpio/adp5588-gpio.c @@ -289,10 +289,10 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev) for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) { int irq = gpio + dev->irq_base; - set_irq_chip_data(irq, dev); - set_irq_chip_and_handler(irq, &adp5588_irq_chip, + irq_set_chip_data(irq, dev); + irq_set_chip_and_handler(irq, &adp5588_irq_chip, handle_level_irq); - set_irq_nested_thread(irq, 1); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM /* * ARM needs us to explicitly flag the IRQ as VALID, @@ -300,7 +300,7 @@ static int adp5588_irq_setup(struct adp5588_gpio *dev) */ set_irq_flags(irq, IRQF_VALID); #else - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 649550e..36a2974 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1656,51 +1656,6 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) chip->get ? (chip->get(chip, i) ? "hi" : "lo") : "? "); - - if (!is_out) { - int irq = gpio_to_irq(gpio); - struct irq_desc *desc = irq_to_desc(irq); - - /* This races with request_irq(), set_irq_type(), - * and set_irq_wake() ... but those are "rare". - * - * More significantly, trigger type flags aren't - * currently maintained by genirq. - */ - if (irq >= 0 && desc->action) { - char *trigger; - - switch (desc->status & IRQ_TYPE_SENSE_MASK) { - case IRQ_TYPE_NONE: - trigger = "(default)"; - break; - case IRQ_TYPE_EDGE_FALLING: - trigger = "edge-falling"; - break; - case IRQ_TYPE_EDGE_RISING: - trigger = "edge-rising"; - break; - case IRQ_TYPE_EDGE_BOTH: - trigger = "edge-both"; - break; - case IRQ_TYPE_LEVEL_HIGH: - trigger = "level-high"; - break; - case IRQ_TYPE_LEVEL_LOW: - trigger = "level-low"; - break; - default: - trigger = "?trigger?"; - break; - } - - seq_printf(s, " irq-%d %s%s", - irq, trigger, - (desc->status & IRQ_WAKEUP) - ? " wakeup" : ""); - } - } - seq_printf(s, "\n"); } } diff --git a/drivers/gpio/max732x.c b/drivers/gpio/max732x.c index 9e1d01f..ad6951e 100644 --- a/drivers/gpio/max732x.c +++ b/drivers/gpio/max732x.c @@ -470,14 +470,14 @@ static int max732x_irq_setup(struct max732x_chip *chip, if (!(chip->dir_input & (1 << lvl))) continue; - set_irq_chip_data(irq, chip); - set_irq_chip_and_handler(irq, &max732x_irq_chip, + irq_set_chip_data(irq, chip); + irq_set_chip_and_handler(irq, &max732x_irq_chip, handle_edge_irq); - set_irq_nested_thread(irq, 1); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index 2fc25de..583e925 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -395,13 +395,13 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) { int irq = lvl + chip->irq_base; - set_irq_chip_data(irq, chip); - set_irq_chip_and_handler(irq, &pca953x_irq_chip, + irq_set_chip_data(irq, chip); + irq_set_chip_and_handler(irq, &pca953x_irq_chip, handle_edge_irq); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } diff --git a/drivers/gpio/pl061.c b/drivers/gpio/pl061.c index 838ddbd..6fcb28c 100644 --- a/drivers/gpio/pl061.c +++ b/drivers/gpio/pl061.c @@ -210,7 +210,7 @@ static struct irq_chip pl061_irqchip = { static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) { - struct list_head *chip_list = get_irq_data(irq); + struct list_head *chip_list = irq_get_handler_data(irq); struct list_head *ptr; struct pl061_gpio *chip; @@ -294,7 +294,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) ret = -ENODEV; goto iounmap; } - set_irq_chained_handler(irq, pl061_irq_handler); + irq_set_chained_handler(irq, pl061_irq_handler); if (!test_and_set_bit(irq, init_irq)) { /* list initialized? */ chip_list = kmalloc(sizeof(*chip_list), GFP_KERNEL); if (chip_list == NULL) { @@ -303,9 +303,9 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) goto iounmap; } INIT_LIST_HEAD(chip_list); - set_irq_data(irq, chip_list); + irq_set_handler_data(irq, chip_list); } else - chip_list = get_irq_data(irq); + chip_list = irq_get_handler_data(irq); list_add(&chip->list, chip_list); for (i = 0; i < PL061_GPIO_NR; i++) { @@ -315,10 +315,10 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) else pl061_direction_input(&chip->gc, i); - set_irq_chip(i+chip->irq_base, &pl061_irqchip); - set_irq_handler(i+chip->irq_base, handle_simple_irq); + irq_set_chip_and_handler(i + chip->irq_base, &pl061_irqchip, + handle_simple_irq); set_irq_flags(i+chip->irq_base, IRQF_VALID); - set_irq_chip_data(i+chip->irq_base, chip); + irq_set_chip_data(i + chip->irq_base, chip); } return 0; diff --git a/drivers/gpio/stmpe-gpio.c b/drivers/gpio/stmpe-gpio.c index eb2901f..4c980b5 100644 --- a/drivers/gpio/stmpe-gpio.c +++ b/drivers/gpio/stmpe-gpio.c @@ -254,14 +254,14 @@ static int __devinit stmpe_gpio_irq_init(struct stmpe_gpio *stmpe_gpio) int irq; for (irq = base; irq < base + stmpe_gpio->chip.ngpio; irq++) { - set_irq_chip_data(irq, stmpe_gpio); - set_irq_chip_and_handler(irq, &stmpe_gpio_irq_chip, + irq_set_chip_data(irq, stmpe_gpio); + irq_set_chip_and_handler(irq, &stmpe_gpio_irq_chip, handle_simple_irq); - set_irq_nested_thread(irq, 1); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } @@ -277,8 +277,8 @@ static void stmpe_gpio_irq_remove(struct stmpe_gpio *stmpe_gpio) #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif - set_irq_chip_and_handler(irq, NULL, NULL); - set_irq_chip_data(irq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); } } diff --git a/drivers/gpio/sx150x.c b/drivers/gpio/sx150x.c index d2f874c..a4f7353 100644 --- a/drivers/gpio/sx150x.c +++ b/drivers/gpio/sx150x.c @@ -551,12 +551,12 @@ static int sx150x_install_irq_chip(struct sx150x_chip *chip, for (n = 0; n < chip->dev_cfg->ngpios; ++n) { irq = irq_base + n; - set_irq_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq); - set_irq_nested_thread(irq, 1); + irq_set_chip_and_handler(irq, &chip->irq_chip, handle_edge_irq); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } @@ -583,8 +583,7 @@ static void sx150x_remove_irq_chip(struct sx150x_chip *chip) for (n = 0; n < chip->dev_cfg->ngpios; ++n) { irq = chip->irq_base + n; - set_irq_handler(irq, NULL); - set_irq_chip(irq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); } } diff --git a/drivers/gpio/tc3589x-gpio.c b/drivers/gpio/tc3589x-gpio.c index 27200af..2a82e89 100644 --- a/drivers/gpio/tc3589x-gpio.c +++ b/drivers/gpio/tc3589x-gpio.c @@ -239,14 +239,14 @@ static int tc3589x_gpio_irq_init(struct tc3589x_gpio *tc3589x_gpio) int irq; for (irq = base; irq < base + tc3589x_gpio->chip.ngpio; irq++) { - set_irq_chip_data(irq, tc3589x_gpio); - set_irq_chip_and_handler(irq, &tc3589x_gpio_irq_chip, + irq_set_chip_data(irq, tc3589x_gpio); + irq_set_chip_and_handler(irq, &tc3589x_gpio_irq_chip, handle_simple_irq); - set_irq_nested_thread(irq, 1); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } @@ -262,8 +262,8 @@ static void tc3589x_gpio_irq_remove(struct tc3589x_gpio *tc3589x_gpio) #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif - set_irq_chip_and_handler(irq, NULL, NULL); - set_irq_chip_data(irq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); } } diff --git a/drivers/gpio/timbgpio.c b/drivers/gpio/timbgpio.c index ffcd815..edbe1ea 100644 --- a/drivers/gpio/timbgpio.c +++ b/drivers/gpio/timbgpio.c @@ -196,7 +196,7 @@ out: static void timbgpio_irq(unsigned int irq, struct irq_desc *desc) { - struct timbgpio *tgpio = get_irq_data(irq); + struct timbgpio *tgpio = irq_get_handler_data(irq); unsigned long ipr; int offset; @@ -292,16 +292,16 @@ static int __devinit timbgpio_probe(struct platform_device *pdev) return 0; for (i = 0; i < pdata->nr_pins; i++) { - set_irq_chip_and_handler_name(tgpio->irq_base + i, + irq_set_chip_and_handler_name(tgpio->irq_base + i, &timbgpio_irqchip, handle_simple_irq, "mux"); - set_irq_chip_data(tgpio->irq_base + i, tgpio); + irq_set_chip_data(tgpio->irq_base + i, tgpio); #ifdef CONFIG_ARM set_irq_flags(tgpio->irq_base + i, IRQF_VALID | IRQF_PROBE); #endif } - set_irq_data(irq, tgpio); - set_irq_chained_handler(irq, timbgpio_irq); + irq_set_handler_data(irq, tgpio); + irq_set_chained_handler(irq, timbgpio_irq); return 0; @@ -327,12 +327,12 @@ static int __devexit timbgpio_remove(struct platform_device *pdev) if (irq >= 0 && tgpio->irq_base > 0) { int i; for (i = 0; i < tgpio->gpio.ngpio; i++) { - set_irq_chip(tgpio->irq_base + i, NULL); - set_irq_chip_data(tgpio->irq_base + i, NULL); + irq_set_chip(tgpio->irq_base + i, NULL); + irq_set_chip_data(tgpio->irq_base + i, NULL); } - set_irq_handler(irq, NULL); - set_irq_data(irq, NULL); + irq_set_handler(irq, NULL); + irq_set_handler_data(irq, NULL); } err = gpiochip_remove(&tgpio->gpio); diff --git a/drivers/gpio/vr41xx_giu.c b/drivers/gpio/vr41xx_giu.c index cffa3bd..a365be0 100644 --- a/drivers/gpio/vr41xx_giu.c +++ b/drivers/gpio/vr41xx_giu.c @@ -238,13 +238,13 @@ void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, break; } } - set_irq_chip_and_handler(GIU_IRQ(pin), + irq_set_chip_and_handler(GIU_IRQ(pin), &giuint_low_irq_chip, handle_edge_irq); } else { giu_clear(GIUINTTYPL, mask); giu_clear(GIUINTHTSELL, mask); - set_irq_chip_and_handler(GIU_IRQ(pin), + irq_set_chip_and_handler(GIU_IRQ(pin), &giuint_low_irq_chip, handle_level_irq); } @@ -273,13 +273,13 @@ void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, break; } } - set_irq_chip_and_handler(GIU_IRQ(pin), + irq_set_chip_and_handler(GIU_IRQ(pin), &giuint_high_irq_chip, handle_edge_irq); } else { giu_clear(GIUINTTYPH, mask); giu_clear(GIUINTHTSELH, mask); - set_irq_chip_and_handler(GIU_IRQ(pin), + irq_set_chip_and_handler(GIU_IRQ(pin), &giuint_high_irq_chip, handle_level_irq); } @@ -539,9 +539,9 @@ static int __devinit giu_probe(struct platform_device *pdev) chip = &giuint_high_irq_chip; if (trigger & (1 << pin)) - set_irq_chip_and_handler(i, chip, handle_edge_irq); + irq_set_chip_and_handler(i, chip, handle_edge_irq); else - set_irq_chip_and_handler(i, chip, handle_level_irq); + irq_set_chip_and_handler(i, chip, handle_level_irq); } diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index c7a6213..fbe1973 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -625,7 +625,7 @@ static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, !!(mqp->flags & MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK), - MLX4_PROTOCOL_IB); + MLX4_PROT_IB_IPV6); if (err) return err; @@ -636,7 +636,7 @@ static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) return 0; err_add: - mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, MLX4_PROTOCOL_IB); + mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, MLX4_PROT_IB_IPV6); return err; } @@ -666,7 +666,7 @@ static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) struct mlx4_ib_gid_entry *ge; err = mlx4_multicast_detach(mdev->dev, - &mqp->mqp, gid->raw, MLX4_PROTOCOL_IB); + &mqp->mqp, gid->raw, MLX4_PROT_IB_IPV6); if (err) return err; @@ -721,7 +721,6 @@ static int init_node_data(struct mlx4_ib_dev *dev) if (err) goto out; - dev->dev->rev_id = be32_to_cpup((__be32 *) (out_mad->data + 32)); memcpy(&dev->ib_dev.node_guid, out_mad->data + 12, 8); out: @@ -954,7 +953,7 @@ static int mlx4_ib_netdev_event(struct notifier_block *this, unsigned long event mlx4_foreach_ib_transport_port(port, ibdev->dev) { oldnd = iboe->netdevs[port - 1]; iboe->netdevs[port - 1] = - mlx4_get_protocol_dev(ibdev->dev, MLX4_PROTOCOL_EN, port); + mlx4_get_protocol_dev(ibdev->dev, MLX4_PROT_ETH, port); if (oldnd != iboe->netdevs[port - 1]) { if (iboe->netdevs[port - 1]) netdev_added(ibdev, port); @@ -1207,7 +1206,7 @@ static struct mlx4_interface mlx4_ib_interface = { .add = mlx4_ib_add, .remove = mlx4_ib_remove, .event = mlx4_ib_event, - .protocol = MLX4_PROTOCOL_IB + .protocol = MLX4_PROT_IB_IPV6 }; static int __init mlx4_ib_init(void) diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index ef32915..cfa3a2b 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -1116,7 +1116,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi return rc; } - if (netif_is_bond_slave(netdev)) + if (netif_is_bond_slave(nesvnic->netdev)) netdev = nesvnic->netdev->master; else netdev = nesvnic->netdev; diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 112ec55..434fd80 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -641,7 +641,7 @@ config TOUCHSCREEN_TOUCHIT213 config TOUCHSCREEN_TSC2005 tristate "TSC2005 based touchscreens" - depends on SPI_MASTER + depends on SPI_MASTER && GENERIC_HARDIRQS help Say Y here if you have a TSC2005 based touchscreen. diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 8742061..cbf0ff3 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -358,7 +358,7 @@ static void __tsc2005_enable(struct tsc2005 *ts) if (ts->esd_timeout && ts->set_reset) { ts->last_valid_interrupt = jiffies; schedule_delayed_work(&ts->esd_work, - round_jiffies(jiffies + + round_jiffies_relative( msecs_to_jiffies(ts->esd_timeout))); } @@ -477,7 +477,14 @@ static void tsc2005_esd_work(struct work_struct *work) int error; u16 r; - mutex_lock(&ts->mutex); + if (!mutex_trylock(&ts->mutex)) { + /* + * If the mutex is taken, it means that disable or enable is in + * progress. In that case just reschedule the work. If the work + * is not needed, it will be canceled by disable. + */ + goto reschedule; + } if (time_is_after_jiffies(ts->last_valid_interrupt + msecs_to_jiffies(ts->esd_timeout))) @@ -510,11 +517,12 @@ static void tsc2005_esd_work(struct work_struct *work) tsc2005_start_scan(ts); out: + mutex_unlock(&ts->mutex); +reschedule: /* re-arm the watchdog */ schedule_delayed_work(&ts->esd_work, - round_jiffies(jiffies + + round_jiffies_relative( msecs_to_jiffies(ts->esd_timeout))); - mutex_unlock(&ts->mutex); } static int tsc2005_open(struct input_dev *input) @@ -663,7 +671,7 @@ static int __devinit tsc2005_probe(struct spi_device *spi) goto err_remove_sysfs; } - set_irq_wake(spi->irq, 1); + irq_set_irq_wake(spi->irq, 1); return 0; err_remove_sysfs: diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index c41eb61..4bebae7 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -231,6 +231,26 @@ void led_trigger_event(struct led_trigger *trigger, } EXPORT_SYMBOL_GPL(led_trigger_event); +void led_trigger_blink(struct led_trigger *trigger, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct list_head *entry; + + if (!trigger) + return; + + read_lock(&trigger->leddev_list_lock); + list_for_each(entry, &trigger->led_cdevs) { + struct led_classdev *led_cdev; + + led_cdev = list_entry(entry, struct led_classdev, trig_list); + led_blink_set(led_cdev, delay_on, delay_off); + } + read_unlock(&trigger->leddev_list_lock); +} +EXPORT_SYMBOL_GPL(led_trigger_blink); + void led_trigger_register_simple(const char *name, struct led_trigger **tp) { struct led_trigger *trigger; diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 98d9ec8..8420129 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -327,4 +327,10 @@ config DM_UEVENT ---help--- Generate udev events for DM events. +config DM_FLAKEY + tristate "Flakey target (EXPERIMENTAL)" + depends on BLK_DEV_DM && EXPERIMENTAL + ---help--- + A target that intermittently fails I/O for debugging purposes. + endif # MD diff --git a/drivers/md/Makefile b/drivers/md/Makefile index d013860..448838b 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_BLK_DEV_MD) += md-mod.o obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o obj-$(CONFIG_DM_CRYPT) += dm-crypt.o obj-$(CONFIG_DM_DELAY) += dm-delay.o +obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 2c62c11..c8827ff 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -1324,20 +1324,29 @@ static int crypt_setkey_allcpus(struct crypt_config *cc) static int crypt_set_key(struct crypt_config *cc, char *key) { + int r = -EINVAL; + int key_string_len = strlen(key); + /* The key size may not be changed. */ - if (cc->key_size != (strlen(key) >> 1)) - return -EINVAL; + if (cc->key_size != (key_string_len >> 1)) + goto out; /* Hyphen (which gives a key_size of zero) means there is no key. */ if (!cc->key_size && strcmp(key, "-")) - return -EINVAL; + goto out; if (cc->key_size && crypt_decode_key(cc->key, key, cc->key_size) < 0) - return -EINVAL; + goto out; set_bit(DM_CRYPT_KEY_VALID, &cc->flags); - return crypt_setkey_allcpus(cc); + r = crypt_setkey_allcpus(cc); + +out: + /* Hex key string not needed after here, so wipe it. */ + memset(key, '0', key_string_len); + + return r; } static int crypt_wipe_key(struct crypt_config *cc) diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c new file mode 100644 index 0000000..ea79062 --- /dev/null +++ b/drivers/md/dm-flakey.c @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2003 Sistina Software (UK) Limited. + * Copyright (C) 2004, 2010 Red Hat, Inc. All rights reserved. + * + * This file is released under the GPL. + */ + +#include <linux/device-mapper.h> + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/blkdev.h> +#include <linux/bio.h> +#include <linux/slab.h> + +#define DM_MSG_PREFIX "flakey" + +/* + * Flakey: Used for testing only, simulates intermittent, + * catastrophic device failure. + */ +struct flakey_c { + struct dm_dev *dev; + unsigned long start_time; + sector_t start; + unsigned up_interval; + unsigned down_interval; +}; + +/* + * Construct a flakey mapping: <dev_path> <offset> <up interval> <down interval> + */ +static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) +{ + struct flakey_c *fc; + unsigned long long tmp; + + if (argc != 4) { + ti->error = "dm-flakey: Invalid argument count"; + return -EINVAL; + } + + fc = kmalloc(sizeof(*fc), GFP_KERNEL); + if (!fc) { + ti->error = "dm-flakey: Cannot allocate linear context"; + return -ENOMEM; + } + fc->start_time = jiffies; + + if (sscanf(argv[1], "%llu", &tmp) != 1) { + ti->error = "dm-flakey: Invalid device sector"; + goto bad; + } + fc->start = tmp; + + if (sscanf(argv[2], "%u", &fc->up_interval) != 1) { + ti->error = "dm-flakey: Invalid up interval"; + goto bad; + } + + if (sscanf(argv[3], "%u", &fc->down_interval) != 1) { + ti->error = "dm-flakey: Invalid down interval"; + goto bad; + } + + if (!(fc->up_interval + fc->down_interval)) { + ti->error = "dm-flakey: Total (up + down) interval is zero"; + goto bad; + } + + if (fc->up_interval + fc->down_interval < fc->up_interval) { + ti->error = "dm-flakey: Interval overflow"; + goto bad; + } + + if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &fc->dev)) { + ti->error = "dm-flakey: Device lookup failed"; + goto bad; + } + + ti->num_flush_requests = 1; + ti->private = fc; + return 0; + +bad: + kfree(fc); + return -EINVAL; +} + +static void flakey_dtr(struct dm_target *ti) +{ + struct flakey_c *fc = ti->private; + + dm_put_device(ti, fc->dev); + kfree(fc); +} + +static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector) +{ + struct flakey_c *fc = ti->private; + + return fc->start + (bi_sector - ti->begin); +} + +static void flakey_map_bio(struct dm_target *ti, struct bio *bio) +{ + struct flakey_c *fc = ti->private; + + bio->bi_bdev = fc->dev->bdev; + if (bio_sectors(bio)) + bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); +} + +static int flakey_map(struct dm_target *ti, struct bio *bio, + union map_info *map_context) +{ + struct flakey_c *fc = ti->private; + unsigned elapsed; + + /* Are we alive ? */ + elapsed = (jiffies - fc->start_time) / HZ; + if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) + return -EIO; + + flakey_map_bio(ti, bio); + + return DM_MAPIO_REMAPPED; +} + +static int flakey_status(struct dm_target *ti, status_type_t type, + char *result, unsigned int maxlen) +{ + struct flakey_c *fc = ti->private; + + switch (type) { + case STATUSTYPE_INFO: + result[0] = '\0'; + break; + + case STATUSTYPE_TABLE: + snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name, + (unsigned long long)fc->start, fc->up_interval, + fc->down_interval); + break; + } + return 0; +} + +static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) +{ + struct flakey_c *fc = ti->private; + + return __blkdev_driver_ioctl(fc->dev->bdev, fc->dev->mode, cmd, arg); +} + +static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size) +{ + struct flakey_c *fc = ti->private; + struct request_queue *q = bdev_get_queue(fc->dev->bdev); + + if (!q->merge_bvec_fn) + return max_size; + + bvm->bi_bdev = fc->dev->bdev; + bvm->bi_sector = flakey_map_sector(ti, bvm->bi_sector); + + return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); +} + +static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) +{ + struct flakey_c *fc = ti->private; + + return fn(ti, fc->dev, fc->start, ti->len, data); +} + +static struct target_type flakey_target = { + .name = "flakey", + .version = {1, 1, 0}, + .module = THIS_MODULE, + .ctr = flakey_ctr, + .dtr = flakey_dtr, + .map = flakey_map, + .status = flakey_status, + .ioctl = flakey_ioctl, + .merge = flakey_merge, + .iterate_devices = flakey_iterate_devices, +}; + +static int __init dm_flakey_init(void) +{ + int r = dm_register_target(&flakey_target); + + if (r < 0) + DMERR("register failed %d", r); + + return r; +} + +static void __exit dm_flakey_exit(void) +{ + dm_unregister_target(&flakey_target); +} + +/* Module hooks */ +module_init(dm_flakey_init); +module_exit(dm_flakey_exit); + +MODULE_DESCRIPTION(DM_NAME " flakey target"); +MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 6d12775..4cacdad 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1501,14 +1501,10 @@ static int check_version(unsigned int cmd, struct dm_ioctl __user *user) return r; } -static void free_params(struct dm_ioctl *param) -{ - vfree(param); -} - static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param) { struct dm_ioctl tmp, *dmi; + int secure_data; if (copy_from_user(&tmp, user, sizeof(tmp) - sizeof(tmp.data))) return -EFAULT; @@ -1516,17 +1512,30 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl **param) if (tmp.data_size < (sizeof(tmp) - sizeof(tmp.data))) return -EINVAL; + secure_data = tmp.flags & DM_SECURE_DATA_FLAG; + dmi = vmalloc(tmp.data_size); - if (!dmi) + if (!dmi) { + if (secure_data && clear_user(user, tmp.data_size)) + return -EFAULT; return -ENOMEM; - - if (copy_from_user(dmi, user, tmp.data_size)) { - vfree(dmi); - return -EFAULT; } + if (copy_from_user(dmi, user, tmp.data_size)) + goto bad; + + /* Wipe the user buffer so we do not return it to userspace */ + if (secure_data && clear_user(user, tmp.data_size)) + goto bad; + *param = dmi; return 0; + +bad: + if (secure_data) + memset(dmi, 0, tmp.data_size); + vfree(dmi); + return -EFAULT; } static int validate_params(uint cmd, struct dm_ioctl *param) @@ -1534,6 +1543,7 @@ static int validate_params(uint cmd, struct dm_ioctl *param) /* Always clear this flag */ param->flags &= ~DM_BUFFER_FULL_FLAG; param->flags &= ~DM_UEVENT_GENERATED_FLAG; + param->flags &= ~DM_SECURE_DATA_FLAG; /* Ignores parameters */ if (cmd == DM_REMOVE_ALL_CMD || @@ -1561,10 +1571,11 @@ static int validate_params(uint cmd, struct dm_ioctl *param) static int ctl_ioctl(uint command, struct dm_ioctl __user *user) { int r = 0; + int wipe_buffer; unsigned int cmd; struct dm_ioctl *uninitialized_var(param); ioctl_fn fn = NULL; - size_t param_size; + size_t input_param_size; /* only root can play with this */ if (!capable(CAP_SYS_ADMIN)) @@ -1611,13 +1622,15 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user) if (r) return r; + input_param_size = param->data_size; + wipe_buffer = param->flags & DM_SECURE_DATA_FLAG; + r = validate_params(cmd, param); if (r) goto out; - param_size = param->data_size; param->data_size = sizeof(*param); - r = fn(param, param_size); + r = fn(param, input_param_size); /* * Copy the results back to userland. @@ -1625,8 +1638,11 @@ static int ctl_ioctl(uint command, struct dm_ioctl __user *user) if (!r && copy_to_user(user, param, param->data_size)) r = -EFAULT; - out: - free_params(param); +out: + if (wipe_buffer) + memset(param, 0, input_param_size); + + vfree(param); return r; } diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 57968eb..a1f3218 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -543,7 +543,7 @@ static int disk_ctr(struct dm_dirty_log *log, struct dm_target *ti, return -EINVAL; } - r = dm_get_device(ti, argv[0], FMODE_READ | FMODE_WRITE, &dev); + r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &dev); if (r) return r; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 4b0b63c..a550a05 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -844,8 +844,8 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc, { /* target parameters */ static struct param _params[] = { - {1, 1024, "invalid number of priority groups"}, - {1, 1024, "invalid initial priority group number"}, + {0, 1024, "invalid number of priority groups"}, + {0, 1024, "invalid initial priority group number"}, }; int r; @@ -879,6 +879,13 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc, if (r) goto bad; + if ((!m->nr_priority_groups && next_pg_num) || + (m->nr_priority_groups && !next_pg_num)) { + ti->error = "invalid initial priority group"; + r = -EINVAL; + goto bad; + } + /* parse the priority groups */ while (as.argc) { struct priority_group *pg; @@ -1065,7 +1072,7 @@ out: static int action_dev(struct multipath *m, struct dm_dev *dev, action_fn action) { - int r = 0; + int r = -EINVAL; struct pgpath *pgpath; struct priority_group *pg; @@ -1415,7 +1422,7 @@ static int multipath_status(struct dm_target *ti, status_type_t type, else if (m->current_pg) pg_num = m->current_pg->pg_num; else - pg_num = 1; + pg_num = (m->nr_priority_groups ? 1 : 0); DMEMIT("%u ", pg_num); @@ -1669,7 +1676,7 @@ out: *---------------------------------------------------------------*/ static struct target_type multipath_target = { .name = "multipath", - .version = {1, 2, 0}, + .version = {1, 3, 0}, .module = THIS_MODULE, .ctr = multipath_ctr, .dtr = multipath_dtr, diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index fdde53c..a2d3309 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c @@ -1080,7 +1080,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv) argv++; argc--; - r = dm_get_device(ti, cow_path, FMODE_READ | FMODE_WRITE, &s->cow); + r = dm_get_device(ti, cow_path, dm_table_get_mode(ti->table), &s->cow); if (r) { ti->error = "Cannot get COW device"; goto bad_cow; diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index dddfa14..3d80cf0 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -396,9 +396,29 @@ static void stripe_io_hints(struct dm_target *ti, blk_limits_io_opt(limits, chunk_size * sc->stripes); } +static int stripe_merge(struct dm_target *ti, struct bvec_merge_data *bvm, + struct bio_vec *biovec, int max_size) +{ + struct stripe_c *sc = ti->private; + sector_t bvm_sector = bvm->bi_sector; + uint32_t stripe; + struct request_queue *q; + + stripe_map_sector(sc, bvm_sector, &stripe, &bvm_sector); + + q = bdev_get_queue(sc->stripe[stripe].dev->bdev); + if (!q->merge_bvec_fn) + return max_size; + + bvm->bi_bdev = sc->stripe[stripe].dev->bdev; + bvm->bi_sector = sc->stripe[stripe].physical_start + bvm_sector; + + return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); +} + static struct target_type stripe_target = { .name = "striped", - .version = {1, 3, 1}, + .version = {1, 4, 0}, .module = THIS_MODULE, .ctr = stripe_ctr, .dtr = stripe_dtr, @@ -407,6 +427,7 @@ static struct target_type stripe_target = { .status = stripe_status, .iterate_devices = stripe_iterate_devices, .io_hints = stripe_io_hints, + .merge = stripe_merge, }; int __init dm_stripe_init(void) diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig index 4ce5c8d..cc0997a 100644 --- a/drivers/memstick/host/Kconfig +++ b/drivers/memstick/host/Kconfig @@ -30,3 +30,15 @@ config MEMSTICK_JMICRON_38X To compile this driver as a module, choose M here: the module will be called jmb38x_ms. + +config MEMSTICK_R592 + tristate "Ricoh R5C592 MemoryStick interface support (EXPERIMENTAL)" + depends on EXPERIMENTAL && PCI + + help + Say Y here if you want to be able to access MemoryStick cards with + the Ricoh R5C592 MemoryStick card reader (which is part of 5 in one + multifunction reader) + + To compile this driver as a module, choose M here: the module will + be called r592. diff --git a/drivers/memstick/host/Makefile b/drivers/memstick/host/Makefile index a1815e9..31ba8d3 100644 --- a/drivers/memstick/host/Makefile +++ b/drivers/memstick/host/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o +obj-$(CONFIG_MEMSTICK_R592) += r592.o diff --git a/drivers/memstick/host/r592.c b/drivers/memstick/host/r592.c new file mode 100644 index 0000000..767406c --- /dev/null +++ b/drivers/memstick/host/r592.c @@ -0,0 +1,908 @@ +/* + * Copyright (C) 2010 - Maxim Levitsky + * driver for Ricoh memstick 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/freezer.h> +#include <linux/jiffies.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/kthread.h> +#include <linux/sched.h> +#include <linux/highmem.h> +#include <asm/byteorder.h> +#include <linux/swab.h> +#include "r592.h" + +static int enable_dma = 1; +static int debug; + +static const char *tpc_names[] = { + "MS_TPC_READ_MG_STATUS", + "MS_TPC_READ_LONG_DATA", + "MS_TPC_READ_SHORT_DATA", + "MS_TPC_READ_REG", + "MS_TPC_READ_QUAD_DATA", + "INVALID", + "MS_TPC_GET_INT", + "MS_TPC_SET_RW_REG_ADRS", + "MS_TPC_EX_SET_CMD", + "MS_TPC_WRITE_QUAD_DATA", + "MS_TPC_WRITE_REG", + "MS_TPC_WRITE_SHORT_DATA", + "MS_TPC_WRITE_LONG_DATA", + "MS_TPC_SET_CMD", +}; + +/** + * memstick_debug_get_tpc_name - debug helper that returns string for + * a TPC number + */ +const char *memstick_debug_get_tpc_name(int tpc) +{ + return tpc_names[tpc-1]; +} +EXPORT_SYMBOL(memstick_debug_get_tpc_name); + + +/* Read a register*/ +static inline u32 r592_read_reg(struct r592_device *dev, int address) +{ + u32 value = readl(dev->mmio + address); + dbg_reg("reg #%02d == 0x%08x", address, value); + return value; +} + +/* Write a register */ +static inline void r592_write_reg(struct r592_device *dev, + int address, u32 value) +{ + dbg_reg("reg #%02d <- 0x%08x", address, value); + writel(value, dev->mmio + address); +} + +/* Reads a big endian DWORD register */ +static inline u32 r592_read_reg_raw_be(struct r592_device *dev, int address) +{ + u32 value = __raw_readl(dev->mmio + address); + dbg_reg("reg #%02d == 0x%08x", address, value); + return be32_to_cpu(value); +} + +/* Writes a big endian DWORD register */ +static inline void r592_write_reg_raw_be(struct r592_device *dev, + int address, u32 value) +{ + dbg_reg("reg #%02d <- 0x%08x", address, value); + __raw_writel(cpu_to_be32(value), dev->mmio + address); +} + +/* Set specific bits in a register (little endian) */ +static inline void r592_set_reg_mask(struct r592_device *dev, + int address, u32 mask) +{ + u32 reg = readl(dev->mmio + address); + dbg_reg("reg #%02d |= 0x%08x (old =0x%08x)", address, mask, reg); + writel(reg | mask , dev->mmio + address); +} + +/* Clear specific bits in a register (little endian) */ +static inline void r592_clear_reg_mask(struct r592_device *dev, + int address, u32 mask) +{ + u32 reg = readl(dev->mmio + address); + dbg_reg("reg #%02d &= 0x%08x (old = 0x%08x, mask = 0x%08x)", + address, ~mask, reg, mask); + writel(reg & ~mask, dev->mmio + address); +} + + +/* Wait for status bits while checking for errors */ +static int r592_wait_status(struct r592_device *dev, u32 mask, u32 wanted_mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + u32 reg = r592_read_reg(dev, R592_STATUS); + + if ((reg & mask) == wanted_mask) + return 0; + + while (time_before(jiffies, timeout)) { + + reg = r592_read_reg(dev, R592_STATUS); + + if ((reg & mask) == wanted_mask) + return 0; + + if (reg & (R592_STATUS_SEND_ERR | R592_STATUS_RECV_ERR)) + return -EIO; + + cpu_relax(); + } + return -ETIME; +} + + +/* Enable/disable device */ +static int r592_enable_device(struct r592_device *dev, bool enable) +{ + dbg("%sabling the device", enable ? "en" : "dis"); + + if (enable) { + + /* Power up the card */ + r592_write_reg(dev, R592_POWER, R592_POWER_0 | R592_POWER_1); + + /* Perform a reset */ + r592_set_reg_mask(dev, R592_IO, R592_IO_RESET); + + msleep(100); + } else + /* Power down the card */ + r592_write_reg(dev, R592_POWER, 0); + + return 0; +} + +/* Set serial/parallel mode */ +static int r592_set_mode(struct r592_device *dev, bool parallel_mode) +{ + if (!parallel_mode) { + dbg("switching to serial mode"); + + /* Set serial mode */ + r592_write_reg(dev, R592_IO_MODE, R592_IO_MODE_SERIAL); + + r592_clear_reg_mask(dev, R592_POWER, R592_POWER_20); + + } else { + dbg("switching to parallel mode"); + + /* This setting should be set _before_ switch TPC */ + r592_set_reg_mask(dev, R592_POWER, R592_POWER_20); + + r592_clear_reg_mask(dev, R592_IO, + R592_IO_SERIAL1 | R592_IO_SERIAL2); + + /* Set the parallel mode now */ + r592_write_reg(dev, R592_IO_MODE, R592_IO_MODE_PARALLEL); + } + + dev->parallel_mode = parallel_mode; + return 0; +} + +/* Perform a controller reset without powering down the card */ +static void r592_host_reset(struct r592_device *dev) +{ + r592_set_reg_mask(dev, R592_IO, R592_IO_RESET); + msleep(100); + r592_set_mode(dev, dev->parallel_mode); +} + +/* Disable all hardware interrupts */ +static void r592_clear_interrupts(struct r592_device *dev) +{ + /* Disable & ACK all interrupts */ + r592_clear_reg_mask(dev, R592_REG_MSC, IRQ_ALL_ACK_MASK); + r592_clear_reg_mask(dev, R592_REG_MSC, IRQ_ALL_EN_MASK); +} + +/* Tests if there is an CRC error */ +static int r592_test_io_error(struct r592_device *dev) +{ + if (!(r592_read_reg(dev, R592_STATUS) & + (R592_STATUS_SEND_ERR | R592_STATUS_RECV_ERR))) + return 0; + + return -EIO; +} + +/* Ensure that FIFO is ready for use */ +static int r592_test_fifo_empty(struct r592_device *dev) +{ + if (r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_FIFO_EMPTY) + return 0; + + dbg("FIFO not ready, trying to reset the device"); + r592_host_reset(dev); + + if (r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_FIFO_EMPTY) + return 0; + + message("FIFO still not ready, giving up"); + return -EIO; +} + +/* Activates the DMA transfer from to FIFO */ +static void r592_start_dma(struct r592_device *dev, bool is_write) +{ + unsigned long flags; + u32 reg; + spin_lock_irqsave(&dev->irq_lock, flags); + + /* Ack interrupts (just in case) + enable them */ + r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_ACK_MASK); + r592_set_reg_mask(dev, R592_REG_MSC, DMA_IRQ_EN_MASK); + + /* Set DMA address */ + r592_write_reg(dev, R592_FIFO_DMA, sg_dma_address(&dev->req->sg)); + + /* Enable the DMA */ + reg = r592_read_reg(dev, R592_FIFO_DMA_SETTINGS); + reg |= R592_FIFO_DMA_SETTINGS_EN; + + if (!is_write) + reg |= R592_FIFO_DMA_SETTINGS_DIR; + else + reg &= ~R592_FIFO_DMA_SETTINGS_DIR; + r592_write_reg(dev, R592_FIFO_DMA_SETTINGS, reg); + + spin_unlock_irqrestore(&dev->irq_lock, flags); +} + +/* Cleanups DMA related settings */ +static void r592_stop_dma(struct r592_device *dev, int error) +{ + r592_clear_reg_mask(dev, R592_FIFO_DMA_SETTINGS, + R592_FIFO_DMA_SETTINGS_EN); + + /* This is only a precation */ + r592_write_reg(dev, R592_FIFO_DMA, + dev->dummy_dma_page_physical_address); + + r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_EN_MASK); + r592_clear_reg_mask(dev, R592_REG_MSC, DMA_IRQ_ACK_MASK); + dev->dma_error = error; +} + +/* Test if hardware supports DMA */ +static void r592_check_dma(struct r592_device *dev) +{ + dev->dma_capable = enable_dma && + (r592_read_reg(dev, R592_FIFO_DMA_SETTINGS) & + R592_FIFO_DMA_SETTINGS_CAP); +} + +/* Transfers fifo contents in/out using DMA */ +static int r592_transfer_fifo_dma(struct r592_device *dev) +{ + int len, sg_count; + bool is_write; + + if (!dev->dma_capable || !dev->req->long_data) + return -EINVAL; + + len = dev->req->sg.length; + is_write = dev->req->data_dir == WRITE; + + if (len != R592_LFIFO_SIZE) + return -EINVAL; + + dbg_verbose("doing dma transfer"); + + dev->dma_error = 0; + INIT_COMPLETION(dev->dma_done); + + /* TODO: hidden assumption about nenth beeing always 1 */ + sg_count = dma_map_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ? + PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + if (sg_count != 1 || + (sg_dma_len(&dev->req->sg) < dev->req->sg.length)) { + message("problem in dma_map_sg"); + return -EIO; + } + + r592_start_dma(dev, is_write); + + /* Wait for DMA completion */ + if (!wait_for_completion_timeout( + &dev->dma_done, msecs_to_jiffies(1000))) { + message("DMA timeout"); + r592_stop_dma(dev, -ETIMEDOUT); + } + + dma_unmap_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ? + PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + + return dev->dma_error; +} + +/* + * Writes the FIFO in 4 byte chunks. + * If length isn't 4 byte aligned, rest of the data if put to a fifo + * to be written later + * Use r592_flush_fifo_write to flush that fifo when writing for the + * last time + */ +static void r592_write_fifo_pio(struct r592_device *dev, + unsigned char *buffer, int len) +{ + /* flush spill from former write */ + if (!kfifo_is_empty(&dev->pio_fifo)) { + + u8 tmp[4] = {0}; + int copy_len = kfifo_in(&dev->pio_fifo, buffer, len); + + if (!kfifo_is_full(&dev->pio_fifo)) + return; + len -= copy_len; + buffer += copy_len; + + copy_len = kfifo_out(&dev->pio_fifo, tmp, 4); + WARN_ON(copy_len != 4); + r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)tmp); + } + + WARN_ON(!kfifo_is_empty(&dev->pio_fifo)); + + /* write full dwords */ + while (len >= 4) { + r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)buffer); + buffer += 4; + len -= 4; + } + + /* put remaining bytes to the spill */ + if (len) + kfifo_in(&dev->pio_fifo, buffer, len); +} + +/* Flushes the temporary FIFO used to make aligned DWORD writes */ +static void r592_flush_fifo_write(struct r592_device *dev) +{ + u8 buffer[4] = { 0 }; + int len; + + if (kfifo_is_empty(&dev->pio_fifo)) + return; + + len = kfifo_out(&dev->pio_fifo, buffer, 4); + r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)buffer); +} + +/* + * Read a fifo in 4 bytes chunks. + * If input doesn't fit the buffer, it places bytes of last dword in spill + * buffer, so that they don't get lost on last read, just throw these away. + */ +static void r592_read_fifo_pio(struct r592_device *dev, + unsigned char *buffer, int len) +{ + u8 tmp[4]; + + /* Read from last spill */ + if (!kfifo_is_empty(&dev->pio_fifo)) { + int bytes_copied = + kfifo_out(&dev->pio_fifo, buffer, min(4, len)); + buffer += bytes_copied; + len -= bytes_copied; + + if (!kfifo_is_empty(&dev->pio_fifo)) + return; + } + + /* Reads dwords from FIFO */ + while (len >= 4) { + *(u32 *)buffer = r592_read_reg_raw_be(dev, R592_FIFO_PIO); + buffer += 4; + len -= 4; + } + + if (len) { + *(u32 *)tmp = r592_read_reg_raw_be(dev, R592_FIFO_PIO); + kfifo_in(&dev->pio_fifo, tmp, 4); + len -= kfifo_out(&dev->pio_fifo, buffer, len); + } + + WARN_ON(len); + return; +} + +/* Transfers actual data using PIO. */ +static int r592_transfer_fifo_pio(struct r592_device *dev) +{ + unsigned long flags; + + bool is_write = dev->req->tpc >= MS_TPC_SET_RW_REG_ADRS; + struct sg_mapping_iter miter; + + kfifo_reset(&dev->pio_fifo); + + if (!dev->req->long_data) { + if (is_write) { + r592_write_fifo_pio(dev, dev->req->data, + dev->req->data_len); + r592_flush_fifo_write(dev); + } else + r592_read_fifo_pio(dev, dev->req->data, + dev->req->data_len); + return 0; + } + + local_irq_save(flags); + sg_miter_start(&miter, &dev->req->sg, 1, SG_MITER_ATOMIC | + (is_write ? SG_MITER_FROM_SG : SG_MITER_TO_SG)); + + /* Do the transfer fifo<->memory*/ + while (sg_miter_next(&miter)) + if (is_write) + r592_write_fifo_pio(dev, miter.addr, miter.length); + else + r592_read_fifo_pio(dev, miter.addr, miter.length); + + + /* Write last few non aligned bytes*/ + if (is_write) + r592_flush_fifo_write(dev); + + sg_miter_stop(&miter); + local_irq_restore(flags); + return 0; +} + +/* Executes one TPC (data is read/written from small or large fifo) */ +static void r592_execute_tpc(struct r592_device *dev) +{ + bool is_write = dev->req->tpc >= MS_TPC_SET_RW_REG_ADRS; + int len, error; + u32 status, reg; + + if (!dev->req) { + message("BUG: tpc execution without request!"); + return; + } + + len = dev->req->long_data ? + dev->req->sg.length : dev->req->data_len; + + /* Ensure that FIFO can hold the input data */ + if (len > R592_LFIFO_SIZE) { + message("IO: hardware doesn't support TPCs longer that 512"); + error = -ENOSYS; + goto out; + } + + if (!(r592_read_reg(dev, R592_REG_MSC) & R592_REG_MSC_PRSNT)) { + dbg("IO: refusing to send TPC because card is absent"); + error = -ENODEV; + goto out; + } + + dbg("IO: executing %s LEN=%d", + memstick_debug_get_tpc_name(dev->req->tpc), len); + + /* Set IO direction */ + if (is_write) + r592_set_reg_mask(dev, R592_IO, R592_IO_DIRECTION); + else + r592_clear_reg_mask(dev, R592_IO, R592_IO_DIRECTION); + + + error = r592_test_fifo_empty(dev); + if (error) + goto out; + + /* Transfer write data */ + if (is_write) { + error = r592_transfer_fifo_dma(dev); + if (error == -EINVAL) + error = r592_transfer_fifo_pio(dev); + } + + if (error) + goto out; + + /* Trigger the TPC */ + reg = (len << R592_TPC_EXEC_LEN_SHIFT) | + (dev->req->tpc << R592_TPC_EXEC_TPC_SHIFT) | + R592_TPC_EXEC_BIG_FIFO; + + r592_write_reg(dev, R592_TPC_EXEC, reg); + + /* Wait for TPC completion */ + status = R592_STATUS_RDY; + if (dev->req->need_card_int) + status |= R592_STATUS_CED; + + error = r592_wait_status(dev, status, status); + if (error) { + message("card didn't respond"); + goto out; + } + + /* Test IO errors */ + error = r592_test_io_error(dev); + if (error) { + dbg("IO error"); + goto out; + } + + /* Read data from FIFO */ + if (!is_write) { + error = r592_transfer_fifo_dma(dev); + if (error == -EINVAL) + error = r592_transfer_fifo_pio(dev); + } + + /* read INT reg. This can be shortened with shifts, but that way + its more readable */ + if (dev->parallel_mode && dev->req->need_card_int) { + + dev->req->int_reg = 0; + status = r592_read_reg(dev, R592_STATUS); + + if (status & R592_STATUS_P_CMDNACK) + dev->req->int_reg |= MEMSTICK_INT_CMDNAK; + if (status & R592_STATUS_P_BREQ) + dev->req->int_reg |= MEMSTICK_INT_BREQ; + if (status & R592_STATUS_P_INTERR) + dev->req->int_reg |= MEMSTICK_INT_ERR; + if (status & R592_STATUS_P_CED) + dev->req->int_reg |= MEMSTICK_INT_CED; + } + + if (error) + dbg("FIFO read error"); +out: + dev->req->error = error; + r592_clear_reg_mask(dev, R592_REG_MSC, R592_REG_MSC_LED); + return; +} + +/* Main request processing thread */ +static int r592_process_thread(void *data) +{ + int error; + struct r592_device *dev = (struct r592_device *)data; + unsigned long flags; + + while (!kthread_should_stop()) { + spin_lock_irqsave(&dev->io_thread_lock, flags); + set_current_state(TASK_INTERRUPTIBLE); + error = memstick_next_req(dev->host, &dev->req); + spin_unlock_irqrestore(&dev->io_thread_lock, flags); + + if (error) { + if (error == -ENXIO || error == -EAGAIN) { + dbg_verbose("IO: done IO, sleeping"); + } else { + dbg("IO: unknown error from " + "memstick_next_req %d", error); + } + + if (kthread_should_stop()) + set_current_state(TASK_RUNNING); + + schedule(); + } else { + set_current_state(TASK_RUNNING); + r592_execute_tpc(dev); + } + } + return 0; +} + +/* Reprogram chip to detect change in card state */ +/* eg, if card is detected, arm it to detect removal, and vice versa */ +static void r592_update_card_detect(struct r592_device *dev) +{ + u32 reg = r592_read_reg(dev, R592_REG_MSC); + bool card_detected = reg & R592_REG_MSC_PRSNT; + + dbg("update card detect. card state: %s", card_detected ? + "present" : "absent"); + + reg &= ~((R592_REG_MSC_IRQ_REMOVE | R592_REG_MSC_IRQ_INSERT) << 16); + + if (card_detected) + reg |= (R592_REG_MSC_IRQ_REMOVE << 16); + else + reg |= (R592_REG_MSC_IRQ_INSERT << 16); + + r592_write_reg(dev, R592_REG_MSC, reg); +} + +/* Timer routine that fires 1 second after last card detection event, */ +static void r592_detect_timer(long unsigned int data) +{ + struct r592_device *dev = (struct r592_device *)data; + r592_update_card_detect(dev); + memstick_detect_change(dev->host); +} + +/* Interrupt handler */ +static irqreturn_t r592_irq(int irq, void *data) +{ + struct r592_device *dev = (struct r592_device *)data; + irqreturn_t ret = IRQ_NONE; + u32 reg; + u16 irq_enable, irq_status; + unsigned long flags; + int error; + + spin_lock_irqsave(&dev->irq_lock, flags); + + reg = r592_read_reg(dev, R592_REG_MSC); + irq_enable = reg >> 16; + irq_status = reg & 0xFFFF; + + /* Ack the interrupts */ + reg &= ~irq_status; + r592_write_reg(dev, R592_REG_MSC, reg); + + /* Get the IRQ status minus bits that aren't enabled */ + irq_status &= (irq_enable); + + /* Due to limitation of memstick core, we don't look at bits that + indicate that card was removed/inserted and/or present */ + if (irq_status & (R592_REG_MSC_IRQ_INSERT | R592_REG_MSC_IRQ_REMOVE)) { + + bool card_was_added = irq_status & R592_REG_MSC_IRQ_INSERT; + ret = IRQ_HANDLED; + + message("IRQ: card %s", card_was_added ? "added" : "removed"); + + mod_timer(&dev->detect_timer, + jiffies + msecs_to_jiffies(card_was_added ? 500 : 50)); + } + + if (irq_status & + (R592_REG_MSC_FIFO_DMA_DONE | R592_REG_MSC_FIFO_DMA_ERR)) { + ret = IRQ_HANDLED; + + if (irq_status & R592_REG_MSC_FIFO_DMA_ERR) { + message("IRQ: DMA error"); + error = -EIO; + } else { + dbg_verbose("IRQ: dma done"); + error = 0; + } + + r592_stop_dma(dev, error); + complete(&dev->dma_done); + } + + spin_unlock_irqrestore(&dev->irq_lock, flags); + return ret; +} + +/* External inteface: set settings */ +static int r592_set_param(struct memstick_host *host, + enum memstick_param param, int value) +{ + struct r592_device *dev = memstick_priv(host); + + switch (param) { + case MEMSTICK_POWER: + switch (value) { + case MEMSTICK_POWER_ON: + return r592_enable_device(dev, true); + case MEMSTICK_POWER_OFF: + return r592_enable_device(dev, false); + default: + return -EINVAL; + } + case MEMSTICK_INTERFACE: + switch (value) { + case MEMSTICK_SERIAL: + return r592_set_mode(dev, 0); + case MEMSTICK_PAR4: + return r592_set_mode(dev, 1); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +/* External interface: submit requests */ +static void r592_submit_req(struct memstick_host *host) +{ + struct r592_device *dev = memstick_priv(host); + unsigned long flags; + + if (dev->req) + return; + + spin_lock_irqsave(&dev->io_thread_lock, flags); + if (wake_up_process(dev->io_thread)) + dbg_verbose("IO thread woken to process requests"); + spin_unlock_irqrestore(&dev->io_thread_lock, flags); +} + +static const struct pci_device_id r592_pci_id_tbl[] = { + + { PCI_VDEVICE(RICOH, 0x0592), }, + { }, +}; + +/* Main entry */ +static int r592_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int error = -ENOMEM; + struct memstick_host *host; + struct r592_device *dev; + + /* Allocate memory */ + host = memstick_alloc_host(sizeof(struct r592_device), &pdev->dev); + if (!host) + goto error1; + + dev = memstick_priv(host); + dev->host = host; + dev->pci_dev = pdev; + pci_set_drvdata(pdev, dev); + + /* pci initialization */ + error = pci_enable_device(pdev); + if (error) + goto error2; + + pci_set_master(pdev); + error = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (error) + goto error3; + + error = pci_request_regions(pdev, DRV_NAME); + if (error) + goto error3; + + dev->mmio = pci_ioremap_bar(pdev, 0); + if (!dev->mmio) + goto error4; + + dev->irq = pdev->irq; + spin_lock_init(&dev->irq_lock); + spin_lock_init(&dev->io_thread_lock); + init_completion(&dev->dma_done); + INIT_KFIFO(dev->pio_fifo); + setup_timer(&dev->detect_timer, + r592_detect_timer, (long unsigned int)dev); + + /* Host initialization */ + host->caps = MEMSTICK_CAP_PAR4; + host->request = r592_submit_req; + host->set_param = r592_set_param; + r592_check_dma(dev); + + dev->io_thread = kthread_run(r592_process_thread, dev, "r592_io"); + if (IS_ERR(dev->io_thread)) { + error = PTR_ERR(dev->io_thread); + goto error5; + } + + /* This is just a precation, so don't fail */ + dev->dummy_dma_page = pci_alloc_consistent(pdev, PAGE_SIZE, + &dev->dummy_dma_page_physical_address); + r592_stop_dma(dev , 0); + + if (request_irq(dev->irq, &r592_irq, IRQF_SHARED, + DRV_NAME, dev)) + goto error6; + + r592_update_card_detect(dev); + if (memstick_add_host(host)) + goto error7; + + message("driver succesfully loaded"); + return 0; +error7: + free_irq(dev->irq, dev); +error6: + if (dev->dummy_dma_page) + pci_free_consistent(pdev, PAGE_SIZE, dev->dummy_dma_page, + dev->dummy_dma_page_physical_address); + + kthread_stop(dev->io_thread); +error5: + iounmap(dev->mmio); +error4: + pci_release_regions(pdev); +error3: + pci_disable_device(pdev); +error2: + memstick_free_host(host); +error1: + return error; +} + +static void r592_remove(struct pci_dev *pdev) +{ + int error = 0; + struct r592_device *dev = pci_get_drvdata(pdev); + + /* Stop the processing thread. + That ensures that we won't take any more requests */ + kthread_stop(dev->io_thread); + + r592_enable_device(dev, false); + + while (!error && dev->req) { + dev->req->error = -ETIME; + error = memstick_next_req(dev->host, &dev->req); + } + memstick_remove_host(dev->host); + + free_irq(dev->irq, dev); + iounmap(dev->mmio); + pci_release_regions(pdev); + pci_disable_device(pdev); + memstick_free_host(dev->host); + + if (dev->dummy_dma_page) + pci_free_consistent(pdev, PAGE_SIZE, dev->dummy_dma_page, + dev->dummy_dma_page_physical_address); +} + +#ifdef CONFIG_PM +static int r592_suspend(struct device *core_dev) +{ + struct pci_dev *pdev = to_pci_dev(core_dev); + struct r592_device *dev = pci_get_drvdata(pdev); + + r592_clear_interrupts(dev); + memstick_suspend_host(dev->host); + del_timer_sync(&dev->detect_timer); + return 0; +} + +static int r592_resume(struct device *core_dev) +{ + struct pci_dev *pdev = to_pci_dev(core_dev); + struct r592_device *dev = pci_get_drvdata(pdev); + + r592_clear_interrupts(dev); + r592_enable_device(dev, false); + memstick_resume_host(dev->host); + r592_update_card_detect(dev); + return 0; +} + +SIMPLE_DEV_PM_OPS(r592_pm_ops, r592_suspend, r592_resume); +#endif + +MODULE_DEVICE_TABLE(pci, r592_pci_id_tbl); + +static struct pci_driver r852_pci_driver = { + .name = DRV_NAME, + .id_table = r592_pci_id_tbl, + .probe = r592_probe, + .remove = r592_remove, +#ifdef CONFIG_PM + .driver.pm = &r592_pm_ops, +#endif +}; + +static __init int r592_module_init(void) +{ + return pci_register_driver(&r852_pci_driver); +} + +static void __exit r592_module_exit(void) +{ + pci_unregister_driver(&r852_pci_driver); +} + +module_init(r592_module_init); +module_exit(r592_module_exit); + +module_param(enable_dma, bool, S_IRUGO); +MODULE_PARM_DESC(enable_dma, "Enable usage of the DMA (default)"); +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0-3)"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); +MODULE_DESCRIPTION("Ricoh R5C592 Memstick/Memstick PRO card reader driver"); diff --git a/drivers/memstick/host/r592.h b/drivers/memstick/host/r592.h new file mode 100644 index 0000000..eee264e --- /dev/null +++ b/drivers/memstick/host/r592.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2010 - Maxim Levitsky + * driver for Ricoh memstick 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. + */ + +#ifndef R592_H + +#include <linux/memstick.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/kfifo.h> +#include <linux/ctype.h> + +/* write to this reg (number,len) triggers TPC execution */ +#define R592_TPC_EXEC 0x00 +#define R592_TPC_EXEC_LEN_SHIFT 16 /* Bits 16..25 are TPC len */ +#define R592_TPC_EXEC_BIG_FIFO (1 << 26) /* If bit 26 is set, large fifo is used (reg 48) */ +#define R592_TPC_EXEC_TPC_SHIFT 28 /* Bits 28..31 are the TPC number */ + + +/* Window for small TPC fifo (big endian)*/ +/* reads and writes always are done in 8 byte chunks */ +/* Not used in driver, because large fifo does better job */ +#define R592_SFIFO 0x08 + + +/* Status register (ms int, small fifo, IO)*/ +#define R592_STATUS 0x10 + /* Parallel INT bits */ +#define R592_STATUS_P_CMDNACK (1 << 16) /* INT reg: NACK (parallel mode) */ +#define R592_STATUS_P_BREQ (1 << 17) /* INT reg: card ready (parallel mode)*/ +#define R592_STATUS_P_INTERR (1 << 18) /* INT reg: int error (parallel mode)*/ +#define R592_STATUS_P_CED (1 << 19) /* INT reg: command done (parallel mode) */ + + /* Fifo status */ +#define R592_STATUS_SFIFO_FULL (1 << 20) /* Small Fifo almost full (last chunk is written) */ +#define R592_STATUS_SFIFO_EMPTY (1 << 21) /* Small Fifo empty */ + + /* Error detection via CRC */ +#define R592_STATUS_SEND_ERR (1 << 24) /* Send failed */ +#define R592_STATUS_RECV_ERR (1 << 25) /* Recieve failed */ + + /* Card state */ +#define R592_STATUS_RDY (1 << 28) /* RDY signal recieved */ +#define R592_STATUS_CED (1 << 29) /* INT: Command done (serial mode)*/ +#define R592_STATUS_SFIFO_INPUT (1 << 30) /* Small fifo recieved data*/ + +#define R592_SFIFO_SIZE 32 /* total size of small fifo is 32 bytes */ +#define R592_SFIFO_PACKET 8 /* packet size of small fifo */ + +/* IO control */ +#define R592_IO 0x18 +#define R592_IO_16 (1 << 16) /* Set by default, can be cleared */ +#define R592_IO_18 (1 << 18) /* Set by default, can be cleared */ +#define R592_IO_SERIAL1 (1 << 20) /* Set by default, can be cleared, (cleared on parallel) */ +#define R592_IO_22 (1 << 22) /* Set by default, can be cleared */ +#define R592_IO_DIRECTION (1 << 24) /* TPC direction (1 write 0 read) */ +#define R592_IO_26 (1 << 26) /* Set by default, can be cleared */ +#define R592_IO_SERIAL2 (1 << 30) /* Set by default, can be cleared (cleared on parallel), serial doesn't work if unset */ +#define R592_IO_RESET (1 << 31) /* Reset, sets defaults*/ + + +/* Turns hardware on/off */ +#define R592_POWER 0x20 /* bits 0-7 writeable */ +#define R592_POWER_0 (1 << 0) /* set on start, cleared on stop - must be set*/ +#define R592_POWER_1 (1 << 1) /* set on start, cleared on stop - must be set*/ +#define R592_POWER_3 (1 << 3) /* must be clear */ +#define R592_POWER_20 (1 << 5) /* set before switch to parallel */ + +/* IO mode*/ +#define R592_IO_MODE 0x24 +#define R592_IO_MODE_SERIAL 1 +#define R592_IO_MODE_PARALLEL 3 + + +/* IRQ,card detection,large fifo (first word irq status, second enable) */ +/* IRQs are ACKed by clearing the bits */ +#define R592_REG_MSC 0x28 +#define R592_REG_MSC_PRSNT (1 << 1) /* card present (only status)*/ +#define R592_REG_MSC_IRQ_INSERT (1 << 8) /* detect insert / card insered */ +#define R592_REG_MSC_IRQ_REMOVE (1 << 9) /* detect removal / card removed */ +#define R592_REG_MSC_FIFO_EMPTY (1 << 10) /* fifo is empty */ +#define R592_REG_MSC_FIFO_DMA_DONE (1 << 11) /* dma enable / dma done */ + +#define R592_REG_MSC_FIFO_USER_ORN (1 << 12) /* set if software reads empty fifo (if R592_REG_MSC_FIFO_EMPTY is set) */ +#define R592_REG_MSC_FIFO_MISMATH (1 << 13) /* set if amount of data in fifo doesn't match amount in TPC */ +#define R592_REG_MSC_FIFO_DMA_ERR (1 << 14) /* IO failure */ +#define R592_REG_MSC_LED (1 << 15) /* clear to turn led off (only status)*/ + +#define DMA_IRQ_ACK_MASK \ + (R592_REG_MSC_FIFO_DMA_DONE | R592_REG_MSC_FIFO_DMA_ERR) + +#define DMA_IRQ_EN_MASK (DMA_IRQ_ACK_MASK << 16) + +#define IRQ_ALL_ACK_MASK 0x00007F00 +#define IRQ_ALL_EN_MASK (IRQ_ALL_ACK_MASK << 16) + +/* DMA address for large FIFO read/writes*/ +#define R592_FIFO_DMA 0x2C + +/* PIO access to large FIFO (512 bytes) (big endian)*/ +#define R592_FIFO_PIO 0x30 +#define R592_LFIFO_SIZE 512 /* large fifo size */ + + +/* large FIFO DMA settings */ +#define R592_FIFO_DMA_SETTINGS 0x34 +#define R592_FIFO_DMA_SETTINGS_EN (1 << 0) /* DMA enabled */ +#define R592_FIFO_DMA_SETTINGS_DIR (1 << 1) /* Dma direction (1 read, 0 write) */ +#define R592_FIFO_DMA_SETTINGS_CAP (1 << 24) /* Dma is aviable */ + +/* Maybe just an delay */ +/* Bits 17..19 are just number */ +/* bit 16 is set, then bit 20 is waited */ +/* time to wait is about 50 spins * 2 ^ (bits 17..19) */ +/* seems to be possible just to ignore */ +/* Probably debug register */ +#define R592_REG38 0x38 +#define R592_REG38_CHANGE (1 << 16) /* Start bit */ +#define R592_REG38_DONE (1 << 20) /* HW set this after the delay */ +#define R592_REG38_SHIFT 17 + +/* Debug register, written (0xABCDEF00) when error happens - not used*/ +#define R592_REG_3C 0x3C + +struct r592_device { + struct pci_dev *pci_dev; + struct memstick_host *host; /* host backpointer */ + struct memstick_request *req; /* current request */ + + /* Registers, IRQ */ + void __iomem *mmio; + int irq; + spinlock_t irq_lock; + spinlock_t io_thread_lock; + struct timer_list detect_timer; + + struct task_struct *io_thread; + bool parallel_mode; + + DECLARE_KFIFO(pio_fifo, u8, sizeof(u32)); + + /* DMA area */ + int dma_capable; + int dma_error; + struct completion dma_done; + void *dummy_dma_page; + dma_addr_t dummy_dma_page_physical_address; + +}; + +#define DRV_NAME "r592" + + +#define message(format, ...) \ + printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__) + +#define __dbg(level, format, ...) \ + do { \ + if (debug >= level) \ + printk(KERN_DEBUG DRV_NAME \ + ": " format "\n", ## __VA_ARGS__); \ + } while (0) + + +#define dbg(format, ...) __dbg(1, format, ## __VA_ARGS__) +#define dbg_verbose(format, ...) __dbg(2, format, ## __VA_ARGS__) +#define dbg_reg(format, ...) __dbg(3, format, ## __VA_ARGS__) + +#endif diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 59c118c..27dc463 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -988,7 +988,7 @@ static void kgdbts_run_tests(void) static int kgdbts_option_setup(char *opt) { - if (strlen(opt) > MAX_CONFIG_LEN) { + if (strlen(opt) >= MAX_CONFIG_LEN) { printk(KERN_ERR "kgdbts: config string too long\n"); return -ENOSPC; } diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c index f142cc2..deaa8bc 100644 --- a/drivers/net/a2065.c +++ b/drivers/net/a2065.c @@ -711,14 +711,14 @@ static int __devinit a2065_init_one(struct zorro_dev *z, return -EBUSY; r2 = request_mem_region(mem_start, A2065_RAM_SIZE, "RAM"); if (!r2) { - release_resource(r1); + release_mem_region(base_addr, sizeof(struct lance_regs)); return -EBUSY; } dev = alloc_etherdev(sizeof(struct lance_private)); if (dev == NULL) { - release_resource(r1); - release_resource(r2); + release_mem_region(base_addr, sizeof(struct lance_regs)); + release_mem_region(mem_start, A2065_RAM_SIZE); return -ENOMEM; } @@ -764,8 +764,8 @@ static int __devinit a2065_init_one(struct zorro_dev *z, err = register_netdev(dev); if (err) { - release_resource(r1); - release_resource(r2); + release_mem_region(base_addr, sizeof(struct lance_regs)); + release_mem_region(mem_start, A2065_RAM_SIZE); free_netdev(dev); return err; } diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c index 7ca0ede..b7f45cd 100644 --- a/drivers/net/ariadne.c +++ b/drivers/net/ariadne.c @@ -182,14 +182,14 @@ static int __devinit ariadne_init_one(struct zorro_dev *z, return -EBUSY; r2 = request_mem_region(mem_start, ARIADNE_RAM_SIZE, "RAM"); if (!r2) { - release_resource(r1); + release_mem_region(base_addr, sizeof(struct Am79C960)); return -EBUSY; } dev = alloc_etherdev(sizeof(struct ariadne_private)); if (dev == NULL) { - release_resource(r1); - release_resource(r2); + release_mem_region(base_addr, sizeof(struct Am79C960)); + release_mem_region(mem_start, ARIADNE_RAM_SIZE); return -ENOMEM; } @@ -213,8 +213,8 @@ static int __devinit ariadne_init_one(struct zorro_dev *z, err = register_netdev(dev); if (err) { - release_resource(r1); - release_resource(r2); + release_mem_region(base_addr, sizeof(struct Am79C960)); + release_mem_region(mem_start, ARIADNE_RAM_SIZE); free_netdev(dev); return err; } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 338bea1..16d6fe9 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1482,21 +1482,16 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) { struct sk_buff *skb = *pskb; struct slave *slave; - struct net_device *bond_dev; struct bonding *bond; - slave = bond_slave_get_rcu(skb->dev); - bond_dev = ACCESS_ONCE(slave->dev->master); - if (unlikely(!bond_dev)) - return RX_HANDLER_PASS; - skb = skb_share_check(skb, GFP_ATOMIC); if (unlikely(!skb)) return RX_HANDLER_CONSUMED; *pskb = skb; - bond = netdev_priv(bond_dev); + slave = bond_slave_get_rcu(skb->dev); + bond = slave->bond; if (bond->params.arp_interval) slave->dev->last_rx = jiffies; @@ -1505,10 +1500,10 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) return RX_HANDLER_EXACT; } - skb->dev = bond_dev; + skb->dev = bond->dev; if (bond->params.mode == BOND_MODE_ALB && - bond_dev->priv_flags & IFF_BRIDGE_PORT && + bond->dev->priv_flags & IFF_BRIDGE_PORT && skb->pkt_type == PACKET_HOST) { if (unlikely(skb_cow_head(skb, @@ -1516,7 +1511,7 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) kfree_skb(skb); return RX_HANDLER_CONSUMED; } - memcpy(eth_hdr(skb)->h_dest, bond_dev->dev_addr, ETH_ALEN); + memcpy(eth_hdr(skb)->h_dest, bond->dev->dev_addr, ETH_ALEN); } return RX_HANDLER_ANOTHER; @@ -1698,20 +1693,15 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) pr_debug("Error %d calling netdev_set_bond_master\n", res); goto err_restore_mac; } - res = netdev_rx_handler_register(slave_dev, bond_handle_frame, - new_slave); - if (res) { - pr_debug("Error %d calling netdev_rx_handler_register\n", res); - goto err_unset_master; - } /* open the slave since the application closed it */ res = dev_open(slave_dev); if (res) { pr_debug("Opening slave %s failed\n", slave_dev->name); - goto err_unreg_rxhandler; + goto err_unset_master; } + new_slave->bond = bond; new_slave->dev = slave_dev; slave_dev->priv_flags |= IFF_BONDING; @@ -1907,6 +1897,13 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) if (res) goto err_close; + res = netdev_rx_handler_register(slave_dev, bond_handle_frame, + new_slave); + if (res) { + pr_debug("Error %d calling netdev_rx_handler_register\n", res); + goto err_dest_symlinks; + } + pr_info("%s: enslaving %s as a%s interface with a%s link.\n", bond_dev->name, slave_dev->name, bond_is_active_slave(new_slave) ? "n active" : " backup", @@ -1916,13 +1913,12 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) return 0; /* Undo stages on error */ +err_dest_symlinks: + bond_destroy_slave_symlinks(bond_dev, slave_dev); + err_close: dev_close(slave_dev); -err_unreg_rxhandler: - netdev_rx_handler_unregister(slave_dev); - synchronize_net(); - err_unset_master: netdev_set_bond_master(slave_dev, NULL); @@ -1988,6 +1984,14 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) return -EINVAL; } + /* unregister rx_handler early so bond_handle_frame wouldn't be called + * for this slave anymore. + */ + netdev_rx_handler_unregister(slave_dev); + write_unlock_bh(&bond->lock); + synchronize_net(); + write_lock_bh(&bond->lock); + if (!bond->params.fail_over_mac) { if (!compare_ether_addr(bond_dev->dev_addr, slave->perm_hwaddr) && bond->slave_cnt > 1) @@ -2104,8 +2108,6 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) netif_addr_unlock_bh(bond_dev); } - netdev_rx_handler_unregister(slave_dev); - synchronize_net(); netdev_set_bond_master(slave_dev, NULL); slave_disable_netpoll(slave); @@ -2186,6 +2188,12 @@ static int bond_release_all(struct net_device *bond_dev) */ write_unlock_bh(&bond->lock); + /* unregister rx_handler early so bond_handle_frame wouldn't + * be called for this slave anymore. + */ + netdev_rx_handler_unregister(slave_dev); + synchronize_net(); + if (bond_is_lb(bond)) { /* must be called only after the slave * has been detached from the list @@ -2217,8 +2225,6 @@ static int bond_release_all(struct net_device *bond_dev) netif_addr_unlock_bh(bond_dev); } - netdev_rx_handler_unregister(slave_dev); - synchronize_net(); netdev_set_bond_master(slave_dev, NULL); slave_disable_netpoll(slave); diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 6b26962..90736cb 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -187,6 +187,7 @@ struct slave { struct net_device *dev; /* first - useful for panic debug */ struct slave *next; struct slave *prev; + struct bonding *bond; /* our master */ int delay; unsigned long jiffies; unsigned long last_arp_rx; diff --git a/drivers/net/davinci_cpdma.c b/drivers/net/davinci_cpdma.c index e92b2b6..ae47f23 100644 --- a/drivers/net/davinci_cpdma.c +++ b/drivers/net/davinci_cpdma.c @@ -76,6 +76,7 @@ struct cpdma_desc { struct cpdma_desc_pool { u32 phys; + u32 hw_addr; void __iomem *iomap; /* ioremap map */ void *cpumap; /* dma_alloc map */ int desc_size, mem_size; @@ -137,7 +138,8 @@ struct cpdma_chan { * abstract out these details */ static struct cpdma_desc_pool * -cpdma_desc_pool_create(struct device *dev, u32 phys, int size, int align) +cpdma_desc_pool_create(struct device *dev, u32 phys, u32 hw_addr, + int size, int align) { int bitmap_size; struct cpdma_desc_pool *pool; @@ -161,10 +163,12 @@ cpdma_desc_pool_create(struct device *dev, u32 phys, int size, int align) if (phys) { pool->phys = phys; pool->iomap = ioremap(phys, size); + pool->hw_addr = hw_addr; } else { pool->cpumap = dma_alloc_coherent(dev, size, &pool->phys, GFP_KERNEL); pool->iomap = (void __force __iomem *)pool->cpumap; + pool->hw_addr = pool->phys; } if (pool->iomap) @@ -201,14 +205,14 @@ static inline dma_addr_t desc_phys(struct cpdma_desc_pool *pool, { if (!desc) return 0; - return pool->phys + (__force dma_addr_t)desc - + return pool->hw_addr + (__force dma_addr_t)desc - (__force dma_addr_t)pool->iomap; } static inline struct cpdma_desc __iomem * desc_from_phys(struct cpdma_desc_pool *pool, dma_addr_t dma) { - return dma ? pool->iomap + dma - pool->phys : NULL; + return dma ? pool->iomap + dma - pool->hw_addr : NULL; } static struct cpdma_desc __iomem * @@ -260,6 +264,7 @@ struct cpdma_ctlr *cpdma_ctlr_create(struct cpdma_params *params) ctlr->pool = cpdma_desc_pool_create(ctlr->dev, ctlr->params.desc_mem_phys, + ctlr->params.desc_hw_addr, ctlr->params.desc_mem_size, ctlr->params.desc_align); if (!ctlr->pool) { diff --git a/drivers/net/davinci_cpdma.h b/drivers/net/davinci_cpdma.h index 868e50e..afa19a0 100644 --- a/drivers/net/davinci_cpdma.h +++ b/drivers/net/davinci_cpdma.h @@ -33,6 +33,7 @@ struct cpdma_params { bool has_soft_reset; int min_packet_size; u32 desc_mem_phys; + u32 desc_hw_addr; int desc_mem_size; int desc_align; diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 082d6ea..baca6bf 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -1854,10 +1854,13 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev) dma_params.rxcp = priv->emac_base + 0x660; dma_params.num_chan = EMAC_MAX_TXRX_CHANNELS; dma_params.min_packet_size = EMAC_DEF_MIN_ETHPKTSIZE; - dma_params.desc_mem_phys = hw_ram_addr; + dma_params.desc_hw_addr = hw_ram_addr; dma_params.desc_mem_size = pdata->ctrl_ram_size; dma_params.desc_align = 16; + dma_params.desc_mem_phys = pdata->no_bd_ram ? 0 : + (u32 __force)res->start + pdata->ctrl_ram_offset; + priv->dma = cpdma_ctlr_create(&dma_params); if (!priv->dma) { dev_err(emac_dev, "DaVinci EMAC: Error initializing DMA\n"); diff --git a/drivers/net/mlx4/alloc.c b/drivers/net/mlx4/alloc.c index 3a4277f..116cae3 100644 --- a/drivers/net/mlx4/alloc.c +++ b/drivers/net/mlx4/alloc.c @@ -62,6 +62,9 @@ u32 mlx4_bitmap_alloc(struct mlx4_bitmap *bitmap) } else obj = -1; + if (obj != -1) + --bitmap->avail; + spin_unlock(&bitmap->lock); return obj; @@ -101,11 +104,19 @@ u32 mlx4_bitmap_alloc_range(struct mlx4_bitmap *bitmap, int cnt, int align) } else obj = -1; + if (obj != -1) + bitmap->avail -= cnt; + spin_unlock(&bitmap->lock); return obj; } +u32 mlx4_bitmap_avail(struct mlx4_bitmap *bitmap) +{ + return bitmap->avail; +} + void mlx4_bitmap_free_range(struct mlx4_bitmap *bitmap, u32 obj, int cnt) { obj &= bitmap->max + bitmap->reserved_top - 1; @@ -115,6 +126,7 @@ void mlx4_bitmap_free_range(struct mlx4_bitmap *bitmap, u32 obj, int cnt) bitmap->last = min(bitmap->last, obj); bitmap->top = (bitmap->top + bitmap->max + bitmap->reserved_top) & bitmap->mask; + bitmap->avail += cnt; spin_unlock(&bitmap->lock); } @@ -130,6 +142,7 @@ int mlx4_bitmap_init(struct mlx4_bitmap *bitmap, u32 num, u32 mask, bitmap->max = num - reserved_top; bitmap->mask = mask; bitmap->reserved_top = reserved_top; + bitmap->avail = num - reserved_top - reserved_bot; spin_lock_init(&bitmap->lock); bitmap->table = kzalloc(BITS_TO_LONGS(bitmap->max) * sizeof (long), GFP_KERNEL); diff --git a/drivers/net/mlx4/cq.c b/drivers/net/mlx4/cq.c index 7cd34e9..bd8ef9f 100644 --- a/drivers/net/mlx4/cq.c +++ b/drivers/net/mlx4/cq.c @@ -198,7 +198,7 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, struct mlx4_mtt *mtt, u64 mtt_addr; int err; - if (vector >= dev->caps.num_comp_vectors) + if (vector > dev->caps.num_comp_vectors + dev->caps.comp_pool) return -EINVAL; cq->vector = vector; diff --git a/drivers/net/mlx4/en_cq.c b/drivers/net/mlx4/en_cq.c index 21786ad..ec4b6d0 100644 --- a/drivers/net/mlx4/en_cq.c +++ b/drivers/net/mlx4/en_cq.c @@ -51,13 +51,10 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv, int err; cq->size = entries; - if (mode == RX) { + if (mode == RX) cq->buf_size = cq->size * sizeof(struct mlx4_cqe); - cq->vector = ring % mdev->dev->caps.num_comp_vectors; - } else { + else cq->buf_size = sizeof(struct mlx4_cqe); - cq->vector = 0; - } cq->ring = ring; cq->is_tx = mode; @@ -80,7 +77,8 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv, int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) { struct mlx4_en_dev *mdev = priv->mdev; - int err; + int err = 0; + char name[25]; cq->dev = mdev->pndev[priv->port]; cq->mcq.set_ci_db = cq->wqres.db.db; @@ -89,6 +87,29 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) *cq->mcq.arm_db = 0; memset(cq->buf, 0, cq->buf_size); + if (cq->is_tx == RX) { + if (mdev->dev->caps.comp_pool) { + if (!cq->vector) { + sprintf(name , "%s-rx-%d", priv->dev->name, cq->ring); + if (mlx4_assign_eq(mdev->dev, name, &cq->vector)) { + cq->vector = (cq->ring + 1 + priv->port) % + mdev->dev->caps.num_comp_vectors; + mlx4_warn(mdev, "Failed Assigning an EQ to " + "%s_rx-%d ,Falling back to legacy EQ's\n", + priv->dev->name, cq->ring); + } + } + } else { + cq->vector = (cq->ring + 1 + priv->port) % + mdev->dev->caps.num_comp_vectors; + } + } else { + if (!cq->vector || !mdev->dev->caps.comp_pool) { + /*Fallback to legacy pool in case of error*/ + cq->vector = 0; + } + } + if (!cq->is_tx) cq->size = priv->rx_ring[cq->ring].actual_size; @@ -112,12 +133,15 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) return 0; } -void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) +void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, + bool reserve_vectors) { struct mlx4_en_dev *mdev = priv->mdev; mlx4_en_unmap_buffer(&cq->wqres.buf); mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size); + if (priv->mdev->dev->caps.comp_pool && cq->vector && !reserve_vectors) + mlx4_release_eq(priv->mdev->dev, cq->vector); cq->buf_size = 0; cq->buf = NULL; } diff --git a/drivers/net/mlx4/en_ethtool.c b/drivers/net/mlx4/en_ethtool.c index 056152b..d54b7ab 100644 --- a/drivers/net/mlx4/en_ethtool.c +++ b/drivers/net/mlx4/en_ethtool.c @@ -45,7 +45,7 @@ mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; - sprintf(drvinfo->driver, DRV_NAME " (%s)", mdev->dev->board_id); + strncpy(drvinfo->driver, DRV_NAME, 32); strncpy(drvinfo->version, DRV_VERSION " (" DRV_RELDATE ")", 32); sprintf(drvinfo->fw_version, "%d.%d.%d", (u16) (mdev->dev->caps.fw_ver >> 32), @@ -131,8 +131,65 @@ static void mlx4_en_set_msglevel(struct net_device *dev, u32 val) static void mlx4_en_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { - wol->supported = 0; - wol->wolopts = 0; + struct mlx4_en_priv *priv = netdev_priv(netdev); + int err = 0; + u64 config = 0; + + if (!priv->mdev->dev->caps.wol) { + wol->supported = 0; + wol->wolopts = 0; + return; + } + + err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); + if (err) { + en_err(priv, "Failed to get WoL information\n"); + return; + } + + if (config & MLX4_EN_WOL_MAGIC) + wol->supported = WAKE_MAGIC; + else + wol->supported = 0; + + if (config & MLX4_EN_WOL_ENABLED) + wol->wolopts = WAKE_MAGIC; + else + wol->wolopts = 0; +} + +static int mlx4_en_set_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct mlx4_en_priv *priv = netdev_priv(netdev); + u64 config = 0; + int err = 0; + + if (!priv->mdev->dev->caps.wol) + return -EOPNOTSUPP; + + if (wol->supported & ~WAKE_MAGIC) + return -EINVAL; + + err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); + if (err) { + en_err(priv, "Failed to get WoL info, unable to modify\n"); + return err; + } + + if (wol->wolopts & WAKE_MAGIC) { + config |= MLX4_EN_WOL_DO_MODIFY | MLX4_EN_WOL_ENABLED | + MLX4_EN_WOL_MAGIC; + } else { + config &= ~(MLX4_EN_WOL_ENABLED | MLX4_EN_WOL_MAGIC); + config |= MLX4_EN_WOL_DO_MODIFY; + } + + err = mlx4_wol_write(priv->mdev->dev, config, priv->port); + if (err) + en_err(priv, "Failed to set WoL information\n"); + + return err; } static int mlx4_en_get_sset_count(struct net_device *dev, int sset) @@ -388,7 +445,7 @@ static int mlx4_en_set_ringparam(struct net_device *dev, mlx4_en_stop_port(dev); } - mlx4_en_free_resources(priv); + mlx4_en_free_resources(priv, true); priv->prof->tx_ring_size = tx_size; priv->prof->rx_ring_size = rx_size; @@ -442,6 +499,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = { .get_ethtool_stats = mlx4_en_get_ethtool_stats, .self_test = mlx4_en_self_test, .get_wol = mlx4_en_get_wol, + .set_wol = mlx4_en_set_wol, .get_msglevel = mlx4_en_get_msglevel, .set_msglevel = mlx4_en_set_msglevel, .get_coalesce = mlx4_en_get_coalesce, diff --git a/drivers/net/mlx4/en_main.c b/drivers/net/mlx4/en_main.c index 1ff6ca6..9317b61 100644 --- a/drivers/net/mlx4/en_main.c +++ b/drivers/net/mlx4/en_main.c @@ -241,16 +241,18 @@ static void *mlx4_en_add(struct mlx4_dev *dev) mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) mdev->port_cnt++; - /* If we did not receive an explicit number of Rx rings, default to - * the number of completion vectors populated by the mlx4_core */ mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) { - mlx4_info(mdev, "Using %d tx rings for port:%d\n", - mdev->profile.prof[i].tx_ring_num, i); - mdev->profile.prof[i].rx_ring_num = min_t(int, - roundup_pow_of_two(dev->caps.num_comp_vectors), - MAX_RX_RINGS); - mlx4_info(mdev, "Defaulting to %d rx rings for port:%d\n", - mdev->profile.prof[i].rx_ring_num, i); + if (!dev->caps.comp_pool) { + mdev->profile.prof[i].rx_ring_num = + rounddown_pow_of_two(max_t(int, MIN_RX_RINGS, + min_t(int, + dev->caps.num_comp_vectors, + MAX_RX_RINGS))); + } else { + mdev->profile.prof[i].rx_ring_num = rounddown_pow_of_two( + min_t(int, dev->caps.comp_pool/ + dev->caps.num_ports - 1 , MAX_MSIX_P_PORT - 1)); + } } /* Create our own workqueue for reset/multicast tasks @@ -294,7 +296,7 @@ static struct mlx4_interface mlx4_en_interface = { .remove = mlx4_en_remove, .event = mlx4_en_event, .get_dev = mlx4_en_get_netdev, - .protocol = MLX4_PROTOCOL_EN, + .protocol = MLX4_PROT_ETH, }; static int __init mlx4_en_init(void) diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c index 897f576..5762ebd 100644 --- a/drivers/net/mlx4/en_netdev.c +++ b/drivers/net/mlx4/en_netdev.c @@ -156,9 +156,8 @@ static void mlx4_en_do_set_mac(struct work_struct *work) mutex_lock(&mdev->state_lock); if (priv->port_up) { /* Remove old MAC and insert the new one */ - mlx4_unregister_mac(mdev->dev, priv->port, priv->mac_index); - err = mlx4_register_mac(mdev->dev, priv->port, - priv->mac, &priv->mac_index); + err = mlx4_replace_mac(mdev->dev, priv->port, + priv->base_qpn, priv->mac, 0); if (err) en_err(priv, "Failed changing HW MAC address\n"); } else @@ -214,6 +213,7 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) struct mlx4_en_dev *mdev = priv->mdev; struct net_device *dev = priv->dev; u64 mcast_addr = 0; + u8 mc_list[16] = {0}; int err; mutex_lock(&mdev->state_lock); @@ -239,8 +239,12 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) priv->flags |= MLX4_EN_FLAG_PROMISC; /* Enable promiscouos mode */ - err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port, - priv->base_qpn, 1); + if (!mdev->dev->caps.vep_uc_steering) + err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port, + priv->base_qpn, 1); + else + err = mlx4_unicast_promisc_add(mdev->dev, priv->base_qpn, + priv->port); if (err) en_err(priv, "Failed enabling " "promiscous mode\n"); @@ -252,10 +256,21 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) en_err(priv, "Failed disabling " "multicast filter\n"); - /* Disable port VLAN filter */ - err = mlx4_SET_VLAN_FLTR(mdev->dev, priv->port, NULL); - if (err) - en_err(priv, "Failed disabling VLAN filter\n"); + /* Add the default qp number as multicast promisc */ + if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { + err = mlx4_multicast_promisc_add(mdev->dev, priv->base_qpn, + priv->port); + if (err) + en_err(priv, "Failed entering multicast promisc mode\n"); + priv->flags |= MLX4_EN_FLAG_MC_PROMISC; + } + + if (priv->vlgrp) { + /* Disable port VLAN filter */ + err = mlx4_SET_VLAN_FLTR(mdev->dev, priv->port, NULL); + if (err) + en_err(priv, "Failed disabling VLAN filter\n"); + } } goto out; } @@ -270,11 +285,24 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) priv->flags &= ~MLX4_EN_FLAG_PROMISC; /* Disable promiscouos mode */ - err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port, - priv->base_qpn, 0); + if (!mdev->dev->caps.vep_uc_steering) + err = mlx4_SET_PORT_qpn_calc(mdev->dev, priv->port, + priv->base_qpn, 0); + else + err = mlx4_unicast_promisc_remove(mdev->dev, priv->base_qpn, + priv->port); if (err) en_err(priv, "Failed disabling promiscous mode\n"); + /* Disable Multicast promisc */ + if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { + err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, + priv->port); + if (err) + en_err(priv, "Failed disabling multicast promiscous mode\n"); + priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; + } + /* Enable port VLAN filter */ err = mlx4_SET_VLAN_FLTR(mdev->dev, priv->port, priv->vlgrp); if (err) @@ -287,14 +315,38 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) 0, MLX4_MCAST_DISABLE); if (err) en_err(priv, "Failed disabling multicast filter\n"); + + /* Add the default qp number as multicast promisc */ + if (!(priv->flags & MLX4_EN_FLAG_MC_PROMISC)) { + err = mlx4_multicast_promisc_add(mdev->dev, priv->base_qpn, + priv->port); + if (err) + en_err(priv, "Failed entering multicast promisc mode\n"); + priv->flags |= MLX4_EN_FLAG_MC_PROMISC; + } } else { int i; + /* Disable Multicast promisc */ + if (priv->flags & MLX4_EN_FLAG_MC_PROMISC) { + err = mlx4_multicast_promisc_remove(mdev->dev, priv->base_qpn, + priv->port); + if (err) + en_err(priv, "Failed disabling multicast promiscous mode\n"); + priv->flags &= ~MLX4_EN_FLAG_MC_PROMISC; + } err = mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, 0, MLX4_MCAST_DISABLE); if (err) en_err(priv, "Failed disabling multicast filter\n"); + /* Detach our qp from all the multicast addresses */ + for (i = 0; i < priv->mc_addrs_cnt; i++) { + memcpy(&mc_list[10], priv->mc_addrs + i * ETH_ALEN, ETH_ALEN); + mc_list[5] = priv->port; + mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, + mc_list, MLX4_PROT_ETH); + } /* Flush mcast filter and init it with broadcast address */ mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, ETH_BCAST, 1, MLX4_MCAST_CONFIG); @@ -307,6 +359,10 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) for (i = 0; i < priv->mc_addrs_cnt; i++) { mcast_addr = mlx4_en_mac_to_u64(priv->mc_addrs + i * ETH_ALEN); + memcpy(&mc_list[10], priv->mc_addrs + i * ETH_ALEN, ETH_ALEN); + mc_list[5] = priv->port; + mlx4_multicast_attach(mdev->dev, &priv->rss_map.indir_qp, + mc_list, 0, MLX4_PROT_ETH); mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, mcast_addr, 0, MLX4_MCAST_CONFIG); } @@ -314,8 +370,6 @@ static void mlx4_en_do_set_multicast(struct work_struct *work) 0, MLX4_MCAST_ENABLE); if (err) en_err(priv, "Failed enabling multicast filter\n"); - - mlx4_en_clear_list(dev); } out: mutex_unlock(&mdev->state_lock); @@ -417,7 +471,6 @@ static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv) unsigned long avg_pkt_size; unsigned long rx_packets; unsigned long rx_bytes; - unsigned long rx_byte_diff; unsigned long tx_packets; unsigned long tx_pkt_diff; unsigned long rx_pkt_diff; @@ -441,25 +494,20 @@ static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv) rx_pkt_diff = ((unsigned long) (rx_packets - priv->last_moder_packets)); packets = max(tx_pkt_diff, rx_pkt_diff); - rx_byte_diff = rx_bytes - priv->last_moder_bytes; - rx_byte_diff = rx_byte_diff ? rx_byte_diff : 1; rate = packets * HZ / period; avg_pkt_size = packets ? ((unsigned long) (rx_bytes - priv->last_moder_bytes)) / packets : 0; /* Apply auto-moderation only when packet rate exceeds a rate that * it matters */ - if (rate > MLX4_EN_RX_RATE_THRESH) { + if (rate > MLX4_EN_RX_RATE_THRESH && avg_pkt_size > MLX4_EN_AVG_PKT_SMALL) { /* If tx and rx packet rates are not balanced, assume that * traffic is mainly BW bound and apply maximum moderation. * Otherwise, moderate according to packet rate */ - if (2 * tx_pkt_diff > 3 * rx_pkt_diff && - rx_pkt_diff / rx_byte_diff < - MLX4_EN_SMALL_PKT_SIZE) - moder_time = priv->rx_usecs_low; - else if (2 * rx_pkt_diff > 3 * tx_pkt_diff) + if (2 * tx_pkt_diff > 3 * rx_pkt_diff || + 2 * rx_pkt_diff > 3 * tx_pkt_diff) { moder_time = priv->rx_usecs_high; - else { + } else { if (rate < priv->pkt_rate_low) moder_time = priv->rx_usecs_low; else if (rate > priv->pkt_rate_high) @@ -471,9 +519,7 @@ static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv) priv->rx_usecs_low; } } else { - /* When packet rate is low, use default moderation rather than - * 0 to prevent interrupt storms if traffic suddenly increases */ - moder_time = priv->rx_usecs; + moder_time = priv->rx_usecs_low; } en_dbg(INTR, priv, "tx rate:%lu rx_rate:%lu\n", @@ -565,6 +611,8 @@ int mlx4_en_start_port(struct net_device *dev) int err = 0; int i; int j; + u8 mc_list[16] = {0}; + char name[32]; if (priv->port_up) { en_dbg(DRV, priv, "start port called while port already up\n"); @@ -603,16 +651,35 @@ int mlx4_en_start_port(struct net_device *dev) ++rx_index; } + /* Set port mac number */ + en_dbg(DRV, priv, "Setting mac for port %d\n", priv->port); + err = mlx4_register_mac(mdev->dev, priv->port, + priv->mac, &priv->base_qpn, 0); + if (err) { + en_err(priv, "Failed setting port mac\n"); + goto cq_err; + } + mdev->mac_removed[priv->port] = 0; + err = mlx4_en_config_rss_steer(priv); if (err) { en_err(priv, "Failed configuring rss steering\n"); - goto cq_err; + goto mac_err; } + if (mdev->dev->caps.comp_pool && !priv->tx_vector) { + sprintf(name , "%s-tx", priv->dev->name); + if (mlx4_assign_eq(mdev->dev , name, &priv->tx_vector)) { + mlx4_warn(mdev, "Failed Assigning an EQ to " + "%s_tx ,Falling back to legacy " + "EQ's\n", priv->dev->name); + } + } /* Configure tx cq's and rings */ for (i = 0; i < priv->tx_ring_num; i++) { /* Configure cq */ cq = &priv->tx_cq[i]; + cq->vector = priv->tx_vector; err = mlx4_en_activate_cq(priv, cq); if (err) { en_err(priv, "Failed allocating Tx CQ\n"); @@ -659,24 +726,22 @@ int mlx4_en_start_port(struct net_device *dev) en_err(priv, "Failed setting default qp numbers\n"); goto tx_err; } - /* Set port mac number */ - en_dbg(DRV, priv, "Setting mac for port %d\n", priv->port); - err = mlx4_register_mac(mdev->dev, priv->port, - priv->mac, &priv->mac_index); - if (err) { - en_err(priv, "Failed setting port mac\n"); - goto tx_err; - } - mdev->mac_removed[priv->port] = 0; /* Init port */ en_dbg(HW, priv, "Initializing port\n"); err = mlx4_INIT_PORT(mdev->dev, priv->port); if (err) { en_err(priv, "Failed Initializing port\n"); - goto mac_err; + goto tx_err; } + /* Attach rx QP to bradcast address */ + memset(&mc_list[10], 0xff, ETH_ALEN); + mc_list[5] = priv->port; + if (mlx4_multicast_attach(mdev->dev, &priv->rss_map.indir_qp, mc_list, + 0, MLX4_PROT_ETH)) + mlx4_warn(mdev, "Failed Attaching Broadcast\n"); + /* Schedule multicast task to populate multicast list */ queue_work(mdev->workqueue, &priv->mcast_task); @@ -684,8 +749,6 @@ int mlx4_en_start_port(struct net_device *dev) netif_tx_start_all_queues(dev); return 0; -mac_err: - mlx4_unregister_mac(mdev->dev, priv->port, priv->mac_index); tx_err: while (tx_index--) { mlx4_en_deactivate_tx_ring(priv, &priv->tx_ring[tx_index]); @@ -693,6 +756,8 @@ tx_err: } mlx4_en_release_rss_steer(priv); +mac_err: + mlx4_unregister_mac(mdev->dev, priv->port, priv->base_qpn); cq_err: while (rx_index--) mlx4_en_deactivate_cq(priv, &priv->rx_cq[rx_index]); @@ -708,6 +773,7 @@ void mlx4_en_stop_port(struct net_device *dev) struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; int i; + u8 mc_list[16] = {0}; if (!priv->port_up) { en_dbg(DRV, priv, "stop port called while port already down\n"); @@ -722,8 +788,23 @@ void mlx4_en_stop_port(struct net_device *dev) /* Set port as not active */ priv->port_up = false; + /* Detach All multicasts */ + memset(&mc_list[10], 0xff, ETH_ALEN); + mc_list[5] = priv->port; + mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, mc_list, + MLX4_PROT_ETH); + for (i = 0; i < priv->mc_addrs_cnt; i++) { + memcpy(&mc_list[10], priv->mc_addrs + i * ETH_ALEN, ETH_ALEN); + mc_list[5] = priv->port; + mlx4_multicast_detach(mdev->dev, &priv->rss_map.indir_qp, + mc_list, MLX4_PROT_ETH); + } + mlx4_en_clear_list(dev); + /* Flush multicast filter */ + mlx4_SET_MCAST_FLTR(mdev->dev, priv->port, 0, 1, MLX4_MCAST_CONFIG); + /* Unregister Mac address for the port */ - mlx4_unregister_mac(mdev->dev, priv->port, priv->mac_index); + mlx4_unregister_mac(mdev->dev, priv->port, priv->base_qpn); mdev->mac_removed[priv->port] = 1; /* Free TX Rings */ @@ -801,7 +882,6 @@ static int mlx4_en_open(struct net_device *dev) priv->rx_ring[i].packets = 0; } - mlx4_en_set_default_moderation(priv); err = mlx4_en_start_port(dev); if (err) en_err(priv, "Failed starting port:%d\n", priv->port); @@ -828,7 +908,7 @@ static int mlx4_en_close(struct net_device *dev) return 0; } -void mlx4_en_free_resources(struct mlx4_en_priv *priv) +void mlx4_en_free_resources(struct mlx4_en_priv *priv, bool reserve_vectors) { int i; @@ -836,14 +916,14 @@ void mlx4_en_free_resources(struct mlx4_en_priv *priv) if (priv->tx_ring[i].tx_info) mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]); if (priv->tx_cq[i].buf) - mlx4_en_destroy_cq(priv, &priv->tx_cq[i]); + mlx4_en_destroy_cq(priv, &priv->tx_cq[i], reserve_vectors); } for (i = 0; i < priv->rx_ring_num; i++) { if (priv->rx_ring[i].rx_info) mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i]); if (priv->rx_cq[i].buf) - mlx4_en_destroy_cq(priv, &priv->rx_cq[i]); + mlx4_en_destroy_cq(priv, &priv->rx_cq[i], reserve_vectors); } } @@ -851,6 +931,13 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv) { struct mlx4_en_port_profile *prof = priv->prof; int i; + int base_tx_qpn, err; + + err = mlx4_qp_reserve_range(priv->mdev->dev, priv->tx_ring_num, 256, &base_tx_qpn); + if (err) { + en_err(priv, "failed reserving range for TX rings\n"); + return err; + } /* Create tx Rings */ for (i = 0; i < priv->tx_ring_num; i++) { @@ -858,7 +945,7 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv) prof->tx_ring_size, i, TX)) goto err; - if (mlx4_en_create_tx_ring(priv, &priv->tx_ring[i], + if (mlx4_en_create_tx_ring(priv, &priv->tx_ring[i], base_tx_qpn + i, prof->tx_ring_size, TXBB_SIZE)) goto err; } @@ -878,6 +965,7 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv) err: en_err(priv, "Failed to allocate NIC resources\n"); + mlx4_qp_release_range(priv->mdev->dev, base_tx_qpn, priv->tx_ring_num); return -ENOMEM; } @@ -905,7 +993,7 @@ void mlx4_en_destroy_netdev(struct net_device *dev) mdev->pndev[priv->port] = NULL; mutex_unlock(&mdev->state_lock); - mlx4_en_free_resources(priv); + mlx4_en_free_resources(priv, false); free_netdev(dev); } @@ -932,7 +1020,6 @@ static int mlx4_en_change_mtu(struct net_device *dev, int new_mtu) en_dbg(DRV, priv, "Change MTU called with card down!?\n"); } else { mlx4_en_stop_port(dev); - mlx4_en_set_default_moderation(priv); err = mlx4_en_start_port(dev); if (err) { en_err(priv, "Failed restarting port:%d\n", @@ -1079,7 +1166,25 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, en_warn(priv, "Using %d TX rings\n", prof->tx_ring_num); en_warn(priv, "Using %d RX rings\n", prof->rx_ring_num); + /* Configure port */ + err = mlx4_SET_PORT_general(mdev->dev, priv->port, + MLX4_EN_MIN_MTU, + 0, 0, 0, 0); + if (err) { + en_err(priv, "Failed setting port general configurations " + "for port %d, with error %d\n", priv->port, err); + goto out; + } + + /* Init port */ + en_warn(priv, "Initializing port\n"); + err = mlx4_INIT_PORT(mdev->dev, priv->port); + if (err) { + en_err(priv, "Failed Initializing port\n"); + goto out; + } priv->registered = 1; + mlx4_en_set_default_moderation(priv); queue_delayed_work(mdev->workqueue, &priv->stats_task, STATS_DELAY); return 0; diff --git a/drivers/net/mlx4/en_port.c b/drivers/net/mlx4/en_port.c index 7f5a322..f2a4f5d 100644 --- a/drivers/net/mlx4/en_port.c +++ b/drivers/net/mlx4/en_port.c @@ -119,6 +119,10 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, struct mlx4_set_port_rqp_calc_context *context; int err; u32 in_mod; + u32 m_promisc = (dev->caps.vep_mc_steering) ? MCAST_DIRECT : MCAST_DEFAULT; + + if (dev->caps.vep_mc_steering && dev->caps.vep_uc_steering) + return 0; mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) @@ -127,8 +131,11 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, memset(context, 0, sizeof *context); context->base_qpn = cpu_to_be32(base_qpn); - context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_EN_SHIFT | base_qpn); - context->mcast = cpu_to_be32(1 << SET_PORT_PROMISC_MODE_SHIFT | base_qpn); + context->n_mac = 0x7; + context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_SHIFT | + base_qpn); + context->mcast = cpu_to_be32(m_promisc << SET_PORT_MC_PROMISC_SHIFT | + base_qpn); context->intra_no_vlan = 0; context->no_vlan = MLX4_NO_VLAN_IDX; context->intra_vlan_miss = 0; @@ -206,7 +213,7 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) } stats->tx_packets = 0; stats->tx_bytes = 0; - for (i = 0; i <= priv->tx_ring_num; i++) { + for (i = 0; i < priv->tx_ring_num; i++) { stats->tx_packets += priv->tx_ring[i].packets; stats->tx_bytes += priv->tx_ring[i].bytes; } diff --git a/drivers/net/mlx4/en_port.h b/drivers/net/mlx4/en_port.h index 092e814..e3d73e4 100644 --- a/drivers/net/mlx4/en_port.h +++ b/drivers/net/mlx4/en_port.h @@ -36,8 +36,8 @@ #define SET_PORT_GEN_ALL_VALID 0x7 -#define SET_PORT_PROMISC_EN_SHIFT 31 -#define SET_PORT_PROMISC_MODE_SHIFT 30 +#define SET_PORT_PROMISC_SHIFT 31 +#define SET_PORT_MC_PROMISC_SHIFT 30 enum { MLX4_CMD_SET_VLAN_FLTR = 0x47, @@ -45,6 +45,12 @@ enum { MLX4_CMD_DUMP_ETH_STATS = 0x49, }; +enum { + MCAST_DIRECT_ONLY = 0, + MCAST_DIRECT = 1, + MCAST_DEFAULT = 2 +}; + struct mlx4_set_port_general_context { u8 reserved[3]; u8 flags; @@ -60,14 +66,17 @@ struct mlx4_set_port_general_context { struct mlx4_set_port_rqp_calc_context { __be32 base_qpn; - __be32 flags; - u8 reserved[3]; + u8 rererved; + u8 n_mac; + u8 n_vlan; + u8 n_prio; + u8 reserved2[3]; u8 mac_miss; u8 intra_no_vlan; u8 no_vlan; u8 intra_vlan_miss; u8 vlan_miss; - u8 reserved2[3]; + u8 reserved3[3]; u8 no_vlan_prio; __be32 promisc; __be32 mcast; diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c index 570f250..05998ee 100644 --- a/drivers/net/mlx4/en_rx.c +++ b/drivers/net/mlx4/en_rx.c @@ -845,16 +845,10 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) } /* Configure RSS indirection qp */ - err = mlx4_qp_reserve_range(mdev->dev, 1, 1, &priv->base_qpn); - if (err) { - en_err(priv, "Failed to reserve range for RSS " - "indirection qp\n"); - goto rss_err; - } err = mlx4_qp_alloc(mdev->dev, priv->base_qpn, &rss_map->indir_qp); if (err) { en_err(priv, "Failed to allocate RSS indirection QP\n"); - goto reserve_err; + goto rss_err; } rss_map->indir_qp.event = mlx4_en_sqp_event; mlx4_en_fill_qp_context(priv, 0, 0, 0, 1, priv->base_qpn, @@ -881,8 +875,6 @@ indir_err: MLX4_QP_STATE_RST, NULL, 0, 0, &rss_map->indir_qp); mlx4_qp_remove(mdev->dev, &rss_map->indir_qp); mlx4_qp_free(mdev->dev, &rss_map->indir_qp); -reserve_err: - mlx4_qp_release_range(mdev->dev, priv->base_qpn, 1); rss_err: for (i = 0; i < good_qps; i++) { mlx4_qp_modify(mdev->dev, NULL, rss_map->state[i], @@ -904,7 +896,6 @@ void mlx4_en_release_rss_steer(struct mlx4_en_priv *priv) MLX4_QP_STATE_RST, NULL, 0, 0, &rss_map->indir_qp); mlx4_qp_remove(mdev->dev, &rss_map->indir_qp); mlx4_qp_free(mdev->dev, &rss_map->indir_qp); - mlx4_qp_release_range(mdev->dev, priv->base_qpn, 1); for (i = 0; i < priv->rx_ring_num; i++) { mlx4_qp_modify(mdev->dev, NULL, rss_map->state[i], diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c index a680cd4..01feb8f 100644 --- a/drivers/net/mlx4/en_tx.c +++ b/drivers/net/mlx4/en_tx.c @@ -44,6 +44,7 @@ enum { MAX_INLINE = 104, /* 128 - 16 - 4 - 4 */ + MAX_BF = 256, }; static int inline_thold __read_mostly = MAX_INLINE; @@ -52,7 +53,7 @@ module_param_named(inline_thold, inline_thold, int, 0444); MODULE_PARM_DESC(inline_thold, "threshold for using inline data"); int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_tx_ring *ring, u32 size, + struct mlx4_en_tx_ring *ring, int qpn, u32 size, u16 stride) { struct mlx4_en_dev *mdev = priv->mdev; @@ -103,23 +104,25 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, "buf_size:%d dma:%llx\n", ring, ring->buf, ring->size, ring->buf_size, (unsigned long long) ring->wqres.buf.direct.map); - err = mlx4_qp_reserve_range(mdev->dev, 1, 1, &ring->qpn); - if (err) { - en_err(priv, "Failed reserving qp for tx ring.\n"); - goto err_map; - } - + ring->qpn = qpn; err = mlx4_qp_alloc(mdev->dev, ring->qpn, &ring->qp); if (err) { en_err(priv, "Failed allocating qp %d\n", ring->qpn); - goto err_reserve; + goto err_map; } ring->qp.event = mlx4_en_sqp_event; + err = mlx4_bf_alloc(mdev->dev, &ring->bf); + if (err) { + en_dbg(DRV, priv, "working without blueflame (%d)", err); + ring->bf.uar = &mdev->priv_uar; + ring->bf.uar->map = mdev->uar_map; + ring->bf_enabled = false; + } else + ring->bf_enabled = true; + return 0; -err_reserve: - mlx4_qp_release_range(mdev->dev, ring->qpn, 1); err_map: mlx4_en_unmap_buffer(&ring->wqres.buf); err_hwq_res: @@ -139,6 +142,8 @@ void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_dev *mdev = priv->mdev; en_dbg(DRV, priv, "Destroying tx ring, qpn: %d\n", ring->qpn); + if (ring->bf_enabled) + mlx4_bf_free(mdev->dev, &ring->bf); mlx4_qp_remove(mdev->dev, &ring->qp); mlx4_qp_free(mdev->dev, &ring->qp); mlx4_qp_release_range(mdev->dev, ring->qpn, 1); @@ -171,6 +176,8 @@ int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, mlx4_en_fill_qp_context(priv, ring->size, ring->stride, 1, 0, ring->qpn, ring->cqn, &ring->context); + if (ring->bf_enabled) + ring->context.usr_page = cpu_to_be32(ring->bf.uar->index); err = mlx4_qp_to_ready(mdev->dev, &ring->wqres.mtt, &ring->context, &ring->qp, &ring->qp_state); @@ -591,6 +598,11 @@ u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb) return skb_tx_hash(dev, skb); } +static void mlx4_bf_copy(unsigned long *dst, unsigned long *src, unsigned bytecnt) +{ + __iowrite64_copy(dst, src, bytecnt / 8); +} + netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) { struct mlx4_en_priv *priv = netdev_priv(dev); @@ -609,12 +621,13 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) int desc_size; int real_size; dma_addr_t dma; - u32 index; + u32 index, bf_index; __be32 op_own; u16 vlan_tag = 0; int i; int lso_header_size; void *fragptr; + bool bounce = false; if (!priv->port_up) goto tx_drop; @@ -657,13 +670,16 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) /* Packet is good - grab an index and transmit it */ index = ring->prod & ring->size_mask; + bf_index = ring->prod; /* See if we have enough space for whole descriptor TXBB for setting * SW ownership on next descriptor; if not, use a bounce buffer. */ if (likely(index + nr_txbb <= ring->size)) tx_desc = ring->buf + index * TXBB_SIZE; - else + else { tx_desc = (struct mlx4_en_tx_desc *) ring->bounce_buf; + bounce = true; + } /* Save skb in tx_info ring */ tx_info = &ring->tx_info[index]; @@ -768,21 +784,37 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) ring->prod += nr_txbb; /* If we used a bounce buffer then copy descriptor back into place */ - if (tx_desc == (struct mlx4_en_tx_desc *) ring->bounce_buf) + if (bounce) tx_desc = mlx4_en_bounce_to_desc(priv, ring, index, desc_size); /* Run destructor before passing skb to HW */ if (likely(!skb_shared(skb))) skb_orphan(skb); - /* Ensure new descirptor hits memory - * before setting ownership of this descriptor to HW */ - wmb(); - tx_desc->ctrl.owner_opcode = op_own; + if (ring->bf_enabled && desc_size <= MAX_BF && !bounce && !vlan_tag) { + *(u32 *) (&tx_desc->ctrl.vlan_tag) |= ring->doorbell_qpn; + op_own |= htonl((bf_index & 0xffff) << 8); + /* Ensure new descirptor hits memory + * before setting ownership of this descriptor to HW */ + wmb(); + tx_desc->ctrl.owner_opcode = op_own; - /* Ring doorbell! */ - wmb(); - writel(ring->doorbell_qpn, mdev->uar_map + MLX4_SEND_DOORBELL); + wmb(); + + mlx4_bf_copy(ring->bf.reg + ring->bf.offset, (unsigned long *) &tx_desc->ctrl, + desc_size); + + wmb(); + + ring->bf.offset ^= ring->bf.buf_size; + } else { + /* Ensure new descirptor hits memory + * before setting ownership of this descriptor to HW */ + wmb(); + tx_desc->ctrl.owner_opcode = op_own; + wmb(); + writel(ring->doorbell_qpn, ring->bf.uar->map + MLX4_SEND_DOORBELL); + } /* Poll CQ here */ mlx4_en_xmit_poll(priv, tx_ind); diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c index 552d0fc..506cfd0 100644 --- a/drivers/net/mlx4/eq.c +++ b/drivers/net/mlx4/eq.c @@ -42,7 +42,7 @@ #include "fw.h" enum { - MLX4_IRQNAME_SIZE = 64 + MLX4_IRQNAME_SIZE = 32 }; enum { @@ -317,8 +317,8 @@ static int mlx4_num_eq_uar(struct mlx4_dev *dev) * we need to map, take the difference of highest index and * the lowest index we'll use and add 1. */ - return (dev->caps.num_comp_vectors + 1 + dev->caps.reserved_eqs) / 4 - - dev->caps.reserved_eqs / 4 + 1; + return (dev->caps.num_comp_vectors + 1 + dev->caps.reserved_eqs + + dev->caps.comp_pool)/4 - dev->caps.reserved_eqs/4 + 1; } static void __iomem *mlx4_get_eq_uar(struct mlx4_dev *dev, struct mlx4_eq *eq) @@ -496,16 +496,32 @@ static void mlx4_free_eq(struct mlx4_dev *dev, static void mlx4_free_irqs(struct mlx4_dev *dev) { struct mlx4_eq_table *eq_table = &mlx4_priv(dev)->eq_table; - int i; + struct mlx4_priv *priv = mlx4_priv(dev); + int i, vec; if (eq_table->have_irq) free_irq(dev->pdev->irq, dev); + for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i) if (eq_table->eq[i].have_irq) { free_irq(eq_table->eq[i].irq, eq_table->eq + i); eq_table->eq[i].have_irq = 0; } + for (i = 0; i < dev->caps.comp_pool; i++) { + /* + * Freeing the assigned irq's + * all bits should be 0, but we need to validate + */ + if (priv->msix_ctl.pool_bm & 1ULL << i) { + /* NO need protecting*/ + vec = dev->caps.num_comp_vectors + 1 + i; + free_irq(priv->eq_table.eq[vec].irq, + &priv->eq_table.eq[vec]); + } + } + + kfree(eq_table->irq_names); } @@ -578,7 +594,8 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) (priv->eq_table.inta_pin < 32 ? 4 : 0); priv->eq_table.irq_names = - kmalloc(MLX4_IRQNAME_SIZE * (dev->caps.num_comp_vectors + 1), + kmalloc(MLX4_IRQNAME_SIZE * (dev->caps.num_comp_vectors + 1 + + dev->caps.comp_pool), GFP_KERNEL); if (!priv->eq_table.irq_names) { err = -ENOMEM; @@ -601,6 +618,22 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) if (err) goto err_out_comp; + /*if additional completion vectors poolsize is 0 this loop will not run*/ + for (i = dev->caps.num_comp_vectors + 1; + i < dev->caps.num_comp_vectors + dev->caps.comp_pool + 1; ++i) { + + err = mlx4_create_eq(dev, dev->caps.num_cqs - + dev->caps.reserved_cqs + + MLX4_NUM_SPARE_EQE, + (dev->flags & MLX4_FLAG_MSI_X) ? i : 0, + &priv->eq_table.eq[i]); + if (err) { + --i; + goto err_out_unmap; + } + } + + if (dev->flags & MLX4_FLAG_MSI_X) { const char *eq_name; @@ -686,7 +719,7 @@ void mlx4_cleanup_eq_table(struct mlx4_dev *dev) mlx4_free_irqs(dev); - for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i) + for (i = 0; i < dev->caps.num_comp_vectors + dev->caps.comp_pool + 1; ++i) mlx4_free_eq(dev, &priv->eq_table.eq[i]); mlx4_unmap_clr_int(dev); @@ -743,3 +776,65 @@ int mlx4_test_interrupts(struct mlx4_dev *dev) return err; } EXPORT_SYMBOL(mlx4_test_interrupts); + +int mlx4_assign_eq(struct mlx4_dev *dev, char* name, int * vector) +{ + + struct mlx4_priv *priv = mlx4_priv(dev); + int vec = 0, err = 0, i; + + spin_lock(&priv->msix_ctl.pool_lock); + for (i = 0; !vec && i < dev->caps.comp_pool; i++) { + if (~priv->msix_ctl.pool_bm & 1ULL << i) { + priv->msix_ctl.pool_bm |= 1ULL << i; + vec = dev->caps.num_comp_vectors + 1 + i; + snprintf(priv->eq_table.irq_names + + vec * MLX4_IRQNAME_SIZE, + MLX4_IRQNAME_SIZE, "%s", name); + err = request_irq(priv->eq_table.eq[vec].irq, + mlx4_msi_x_interrupt, 0, + &priv->eq_table.irq_names[vec<<5], + priv->eq_table.eq + vec); + if (err) { + /*zero out bit by fliping it*/ + priv->msix_ctl.pool_bm ^= 1 << i; + vec = 0; + continue; + /*we dont want to break here*/ + } + eq_set_ci(&priv->eq_table.eq[vec], 1); + } + } + spin_unlock(&priv->msix_ctl.pool_lock); + + if (vec) { + *vector = vec; + } else { + *vector = 0; + err = (i == dev->caps.comp_pool) ? -ENOSPC : err; + } + return err; +} +EXPORT_SYMBOL(mlx4_assign_eq); + +void mlx4_release_eq(struct mlx4_dev *dev, int vec) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + /*bm index*/ + int i = vec - dev->caps.num_comp_vectors - 1; + + if (likely(i >= 0)) { + /*sanity check , making sure were not trying to free irq's + Belonging to a legacy EQ*/ + spin_lock(&priv->msix_ctl.pool_lock); + if (priv->msix_ctl.pool_bm & 1ULL << i) { + free_irq(priv->eq_table.eq[vec].irq, + &priv->eq_table.eq[vec]); + priv->msix_ctl.pool_bm &= ~(1ULL << i); + } + spin_unlock(&priv->msix_ctl.pool_lock); + } + +} +EXPORT_SYMBOL(mlx4_release_eq); + diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c index 5de1db8..67a209ba 100644 --- a/drivers/net/mlx4/fw.c +++ b/drivers/net/mlx4/fw.c @@ -274,8 +274,11 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev_cap->stat_rate_support = stat_rate; MLX4_GET(field, outbox, QUERY_DEV_CAP_UDP_RSS_OFFSET); dev_cap->udp_rss = field & 0x1; + dev_cap->vep_uc_steering = field & 0x2; + dev_cap->vep_mc_steering = field & 0x4; MLX4_GET(field, outbox, QUERY_DEV_CAP_ETH_UC_LOOPBACK_OFFSET); dev_cap->loopback_support = field & 0x1; + dev_cap->wol = field & 0x40; MLX4_GET(dev_cap->flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET); MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET); dev_cap->reserved_uars = field >> 4; @@ -737,6 +740,7 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) #define INIT_HCA_MC_BASE_OFFSET (INIT_HCA_MCAST_OFFSET + 0x00) #define INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x12) #define INIT_HCA_LOG_MC_HASH_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x16) +#define INIT_HCA_UC_STEERING_OFFSET (INIT_HCA_MCAST_OFFSET + 0x18) #define INIT_HCA_LOG_MC_TABLE_SZ_OFFSET (INIT_HCA_MCAST_OFFSET + 0x1b) #define INIT_HCA_TPT_OFFSET 0x0f0 #define INIT_HCA_DMPT_BASE_OFFSET (INIT_HCA_TPT_OFFSET + 0x00) @@ -797,6 +801,8 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) MLX4_PUT(inbox, param->mc_base, INIT_HCA_MC_BASE_OFFSET); MLX4_PUT(inbox, param->log_mc_entry_sz, INIT_HCA_LOG_MC_ENTRY_SZ_OFFSET); MLX4_PUT(inbox, param->log_mc_hash_sz, INIT_HCA_LOG_MC_HASH_SZ_OFFSET); + if (dev->caps.vep_mc_steering) + MLX4_PUT(inbox, (u8) (1 << 3), INIT_HCA_UC_STEERING_OFFSET); MLX4_PUT(inbox, param->log_mc_table_sz, INIT_HCA_LOG_MC_TABLE_SZ_OFFSET); /* TPT attributes */ @@ -908,3 +914,22 @@ int mlx4_NOP(struct mlx4_dev *dev) /* Input modifier of 0x1f means "finish as soon as possible." */ return mlx4_cmd(dev, 0, 0x1f, 0, MLX4_CMD_NOP, 100); } + +#define MLX4_WOL_SETUP_MODE (5 << 28) +int mlx4_wol_read(struct mlx4_dev *dev, u64 *config, int port) +{ + u32 in_mod = MLX4_WOL_SETUP_MODE | port << 8; + + return mlx4_cmd_imm(dev, 0, config, in_mod, 0x3, + MLX4_CMD_MOD_STAT_CFG, MLX4_CMD_TIME_CLASS_A); +} +EXPORT_SYMBOL_GPL(mlx4_wol_read); + +int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port) +{ + u32 in_mod = MLX4_WOL_SETUP_MODE | port << 8; + + return mlx4_cmd(dev, config, in_mod, 0x1, MLX4_CMD_MOD_STAT_CFG, + MLX4_CMD_TIME_CLASS_A); +} +EXPORT_SYMBOL_GPL(mlx4_wol_write); diff --git a/drivers/net/mlx4/fw.h b/drivers/net/mlx4/fw.h index 65cc72e..88003eb 100644 --- a/drivers/net/mlx4/fw.h +++ b/drivers/net/mlx4/fw.h @@ -80,6 +80,9 @@ struct mlx4_dev_cap { u16 stat_rate_support; int udp_rss; int loopback_support; + int vep_uc_steering; + int vep_mc_steering; + int wol; u32 flags; int reserved_uars; int uar_size; diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index c835011..62fa7ee 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -39,6 +39,7 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/slab.h> +#include <linux/io-mapping.h> #include <linux/mlx4/device.h> #include <linux/mlx4/doorbell.h> @@ -227,6 +228,9 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.stat_rate_support = dev_cap->stat_rate_support; dev->caps.udp_rss = dev_cap->udp_rss; dev->caps.loopback_support = dev_cap->loopback_support; + dev->caps.vep_uc_steering = dev_cap->vep_uc_steering; + dev->caps.vep_mc_steering = dev_cap->vep_mc_steering; + dev->caps.wol = dev_cap->wol; dev->caps.max_gso_sz = dev_cap->max_gso_sz; dev->caps.log_num_macs = log_num_mac; @@ -718,8 +722,31 @@ static void mlx4_free_icms(struct mlx4_dev *dev) mlx4_free_icm(dev, priv->fw.aux_icm, 0); } +static int map_bf_area(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + resource_size_t bf_start; + resource_size_t bf_len; + int err = 0; + + bf_start = pci_resource_start(dev->pdev, 2) + (dev->caps.num_uars << PAGE_SHIFT); + bf_len = pci_resource_len(dev->pdev, 2) - (dev->caps.num_uars << PAGE_SHIFT); + priv->bf_mapping = io_mapping_create_wc(bf_start, bf_len); + if (!priv->bf_mapping) + err = -ENOMEM; + + return err; +} + +static void unmap_bf_area(struct mlx4_dev *dev) +{ + if (mlx4_priv(dev)->bf_mapping) + io_mapping_free(mlx4_priv(dev)->bf_mapping); +} + static void mlx4_close_hca(struct mlx4_dev *dev) { + unmap_bf_area(dev); mlx4_CLOSE_HCA(dev, 0); mlx4_free_icms(dev); mlx4_UNMAP_FA(dev); @@ -772,6 +799,9 @@ static int mlx4_init_hca(struct mlx4_dev *dev) goto err_stop_fw; } + if (map_bf_area(dev)) + mlx4_dbg(dev, "Failed to map blue flame area\n"); + init_hca.log_uar_sz = ilog2(dev->caps.num_uars); err = mlx4_init_icm(dev, &dev_cap, &init_hca, icm_size); @@ -802,6 +832,7 @@ err_free_icm: mlx4_free_icms(dev); err_stop_fw: + unmap_bf_area(dev); mlx4_UNMAP_FA(dev); mlx4_free_icm(dev, priv->fw.fw_icm, 0); @@ -969,13 +1000,15 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); struct msix_entry *entries; - int nreq; + int nreq = min_t(int, dev->caps.num_ports * + min_t(int, num_online_cpus() + 1, MAX_MSIX_P_PORT) + + MSIX_LEGACY_SZ, MAX_MSIX); int err; int i; if (msi_x) { nreq = min_t(int, dev->caps.num_eqs - dev->caps.reserved_eqs, - num_possible_cpus() + 1); + nreq); entries = kcalloc(nreq, sizeof *entries, GFP_KERNEL); if (!entries) goto no_msi; @@ -998,7 +1031,15 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) goto no_msi; } - dev->caps.num_comp_vectors = nreq - 1; + if (nreq < + MSIX_LEGACY_SZ + dev->caps.num_ports * MIN_MSIX_P_PORT) { + /*Working in legacy mode , all EQ's shared*/ + dev->caps.comp_pool = 0; + dev->caps.num_comp_vectors = nreq - 1; + } else { + dev->caps.comp_pool = nreq - MSIX_LEGACY_SZ; + dev->caps.num_comp_vectors = MSIX_LEGACY_SZ - 1; + } for (i = 0; i < nreq; ++i) priv->eq_table.eq[i].irq = entries[i].vector; @@ -1010,6 +1051,7 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) no_msi: dev->caps.num_comp_vectors = 1; + dev->caps.comp_pool = 0; for (i = 0; i < 2; ++i) priv->eq_table.eq[i].irq = dev->pdev->irq; @@ -1049,6 +1091,59 @@ static void mlx4_cleanup_port_info(struct mlx4_port_info *info) device_remove_file(&info->dev->pdev->dev, &info->port_attr); } +static int mlx4_init_steering(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int num_entries = dev->caps.num_ports; + int i, j; + + priv->steer = kzalloc(sizeof(struct mlx4_steer) * num_entries, GFP_KERNEL); + if (!priv->steer) + return -ENOMEM; + + for (i = 0; i < num_entries; i++) { + for (j = 0; j < MLX4_NUM_STEERS; j++) { + INIT_LIST_HEAD(&priv->steer[i].promisc_qps[j]); + INIT_LIST_HEAD(&priv->steer[i].steer_entries[j]); + } + INIT_LIST_HEAD(&priv->steer[i].high_prios); + } + return 0; +} + +static void mlx4_clear_steering(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_steer_index *entry, *tmp_entry; + struct mlx4_promisc_qp *pqp, *tmp_pqp; + int num_entries = dev->caps.num_ports; + int i, j; + + for (i = 0; i < num_entries; i++) { + for (j = 0; j < MLX4_NUM_STEERS; j++) { + list_for_each_entry_safe(pqp, tmp_pqp, + &priv->steer[i].promisc_qps[j], + list) { + list_del(&pqp->list); + kfree(pqp); + } + list_for_each_entry_safe(entry, tmp_entry, + &priv->steer[i].steer_entries[j], + list) { + list_del(&entry->list); + list_for_each_entry_safe(pqp, tmp_pqp, + &entry->duplicates, + list) { + list_del(&pqp->list); + kfree(pqp); + } + kfree(entry); + } + } + } + kfree(priv->steer); +} + static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { struct mlx4_priv *priv; @@ -1130,6 +1225,11 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&priv->pgdir_list); mutex_init(&priv->pgdir_mutex); + pci_read_config_byte(pdev, PCI_REVISION_ID, &dev->rev_id); + + INIT_LIST_HEAD(&priv->bf_list); + mutex_init(&priv->bf_mutex); + /* * Now reset the HCA before we touch the PCI capabilities or * attempt a firmware command, since a boot ROM may have left @@ -1154,8 +1254,15 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id) if (err) goto err_close; + priv->msix_ctl.pool_bm = 0; + spin_lock_init(&priv->msix_ctl.pool_lock); + mlx4_enable_msi_x(dev); + err = mlx4_init_steering(dev); + if (err) + goto err_free_eq; + err = mlx4_setup_hca(dev); if (err == -EBUSY && (dev->flags & MLX4_FLAG_MSI_X)) { dev->flags &= ~MLX4_FLAG_MSI_X; @@ -1164,7 +1271,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id) } if (err) - goto err_free_eq; + goto err_steer; for (port = 1; port <= dev->caps.num_ports; port++) { err = mlx4_init_port_info(dev, port); @@ -1197,6 +1304,9 @@ err_port: mlx4_cleanup_pd_table(dev); mlx4_cleanup_uar_table(dev); +err_steer: + mlx4_clear_steering(dev); + err_free_eq: mlx4_free_eq_table(dev); @@ -1256,6 +1366,7 @@ static void mlx4_remove_one(struct pci_dev *pdev) iounmap(priv->kar); mlx4_uar_free(dev, &priv->driver_uar); mlx4_cleanup_uar_table(dev); + mlx4_clear_steering(dev); mlx4_free_eq_table(dev); mlx4_close_hca(dev); mlx4_cmd_cleanup(dev); diff --git a/drivers/net/mlx4/mcg.c b/drivers/net/mlx4/mcg.c index 79cf42d..e71372a 100644 --- a/drivers/net/mlx4/mcg.c +++ b/drivers/net/mlx4/mcg.c @@ -32,6 +32,7 @@ */ #include <linux/string.h> +#include <linux/etherdevice.h> #include <linux/mlx4/cmd.h> @@ -40,38 +41,40 @@ #define MGM_QPN_MASK 0x00FFFFFF #define MGM_BLCK_LB_BIT 30 -struct mlx4_mgm { - __be32 next_gid_index; - __be32 members_count; - u32 reserved[2]; - u8 gid[16]; - __be32 qp[MLX4_QP_PER_MGM]; -}; - static const u8 zero_gid[16]; /* automatically initialized to 0 */ -static int mlx4_READ_MCG(struct mlx4_dev *dev, int index, - struct mlx4_cmd_mailbox *mailbox) +static int mlx4_READ_ENTRY(struct mlx4_dev *dev, int index, + struct mlx4_cmd_mailbox *mailbox) { return mlx4_cmd_box(dev, 0, mailbox->dma, index, 0, MLX4_CMD_READ_MCG, MLX4_CMD_TIME_CLASS_A); } -static int mlx4_WRITE_MCG(struct mlx4_dev *dev, int index, - struct mlx4_cmd_mailbox *mailbox) +static int mlx4_WRITE_ENTRY(struct mlx4_dev *dev, int index, + struct mlx4_cmd_mailbox *mailbox) { return mlx4_cmd(dev, mailbox->dma, index, 0, MLX4_CMD_WRITE_MCG, MLX4_CMD_TIME_CLASS_A); } -static int mlx4_MGID_HASH(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, - u16 *hash) +static int mlx4_WRITE_PROMISC(struct mlx4_dev *dev, u8 vep_num, u8 port, u8 steer, + struct mlx4_cmd_mailbox *mailbox) +{ + u32 in_mod; + + in_mod = (u32) vep_num << 24 | (u32) port << 16 | steer << 1; + return mlx4_cmd(dev, mailbox->dma, in_mod, 0x1, + MLX4_CMD_WRITE_MCG, MLX4_CMD_TIME_CLASS_A); +} + +static int mlx4_GID_HASH(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, + u16 *hash, u8 op_mod) { u64 imm; int err; - err = mlx4_cmd_imm(dev, mailbox->dma, &imm, 0, 0, MLX4_CMD_MGID_HASH, - MLX4_CMD_TIME_CLASS_A); + err = mlx4_cmd_imm(dev, mailbox->dma, &imm, 0, op_mod, + MLX4_CMD_MGID_HASH, MLX4_CMD_TIME_CLASS_A); if (!err) *hash = imm; @@ -79,6 +82,457 @@ static int mlx4_MGID_HASH(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox return err; } +static struct mlx4_promisc_qp *get_promisc_qp(struct mlx4_dev *dev, u8 pf_num, + enum mlx4_steer_type steer, + u32 qpn) +{ + struct mlx4_steer *s_steer = &mlx4_priv(dev)->steer[pf_num]; + struct mlx4_promisc_qp *pqp; + + list_for_each_entry(pqp, &s_steer->promisc_qps[steer], list) { + if (pqp->qpn == qpn) + return pqp; + } + /* not found */ + return NULL; +} + +/* + * Add new entry to steering data structure. + * All promisc QPs should be added as well + */ +static int new_steering_entry(struct mlx4_dev *dev, u8 vep_num, u8 port, + enum mlx4_steer_type steer, + unsigned int index, u32 qpn) +{ + struct mlx4_steer *s_steer; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_mgm *mgm; + u32 members_count; + struct mlx4_steer_index *new_entry; + struct mlx4_promisc_qp *pqp; + struct mlx4_promisc_qp *dqp; + u32 prot; + int err; + u8 pf_num; + + pf_num = (dev->caps.num_ports == 1) ? vep_num : (vep_num << 1) | (port - 1); + s_steer = &mlx4_priv(dev)->steer[pf_num]; + new_entry = kzalloc(sizeof *new_entry, GFP_KERNEL); + if (!new_entry) + return -ENOMEM; + + INIT_LIST_HEAD(&new_entry->duplicates); + new_entry->index = index; + list_add_tail(&new_entry->list, &s_steer->steer_entries[steer]); + + /* If the given qpn is also a promisc qp, + * it should be inserted to duplicates list + */ + pqp = get_promisc_qp(dev, pf_num, steer, qpn); + if (pqp) { + dqp = kmalloc(sizeof *dqp, GFP_KERNEL); + if (!dqp) { + err = -ENOMEM; + goto out_alloc; + } + dqp->qpn = qpn; + list_add_tail(&dqp->list, &new_entry->duplicates); + } + + /* if no promisc qps for this vep, we are done */ + if (list_empty(&s_steer->promisc_qps[steer])) + return 0; + + /* now need to add all the promisc qps to the new + * steering entry, as they should also receive the packets + * destined to this address */ + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) { + err = -ENOMEM; + goto out_alloc; + } + mgm = mailbox->buf; + + err = mlx4_READ_ENTRY(dev, index, mailbox); + if (err) + goto out_mailbox; + + members_count = be32_to_cpu(mgm->members_count) & 0xffffff; + prot = be32_to_cpu(mgm->members_count) >> 30; + list_for_each_entry(pqp, &s_steer->promisc_qps[steer], list) { + /* don't add already existing qpn */ + if (pqp->qpn == qpn) + continue; + if (members_count == MLX4_QP_PER_MGM) { + /* out of space */ + err = -ENOMEM; + goto out_mailbox; + } + + /* add the qpn */ + mgm->qp[members_count++] = cpu_to_be32(pqp->qpn & MGM_QPN_MASK); + } + /* update the qps count and update the entry with all the promisc qps*/ + mgm->members_count = cpu_to_be32(members_count | (prot << 30)); + err = mlx4_WRITE_ENTRY(dev, index, mailbox); + +out_mailbox: + mlx4_free_cmd_mailbox(dev, mailbox); + if (!err) + return 0; +out_alloc: + if (dqp) { + list_del(&dqp->list); + kfree(&dqp); + } + list_del(&new_entry->list); + kfree(new_entry); + return err; +} + +/* update the data structures with existing steering entry */ +static int existing_steering_entry(struct mlx4_dev *dev, u8 vep_num, u8 port, + enum mlx4_steer_type steer, + unsigned int index, u32 qpn) +{ + struct mlx4_steer *s_steer; + struct mlx4_steer_index *tmp_entry, *entry = NULL; + struct mlx4_promisc_qp *pqp; + struct mlx4_promisc_qp *dqp; + u8 pf_num; + + pf_num = (dev->caps.num_ports == 1) ? vep_num : (vep_num << 1) | (port - 1); + s_steer = &mlx4_priv(dev)->steer[pf_num]; + + pqp = get_promisc_qp(dev, pf_num, steer, qpn); + if (!pqp) + return 0; /* nothing to do */ + + list_for_each_entry(tmp_entry, &s_steer->steer_entries[steer], list) { + if (tmp_entry->index == index) { + entry = tmp_entry; + break; + } + } + if (unlikely(!entry)) { + mlx4_warn(dev, "Steering entry at index %x is not registered\n", index); + return -EINVAL; + } + + /* the given qpn is listed as a promisc qpn + * we need to add it as a duplicate to this entry + * for future refernce */ + list_for_each_entry(dqp, &entry->duplicates, list) { + if (qpn == dqp->qpn) + return 0; /* qp is already duplicated */ + } + + /* add the qp as a duplicate on this index */ + dqp = kmalloc(sizeof *dqp, GFP_KERNEL); + if (!dqp) + return -ENOMEM; + dqp->qpn = qpn; + list_add_tail(&dqp->list, &entry->duplicates); + + return 0; +} + +/* Check whether a qpn is a duplicate on steering entry + * If so, it should not be removed from mgm */ +static bool check_duplicate_entry(struct mlx4_dev *dev, u8 vep_num, u8 port, + enum mlx4_steer_type steer, + unsigned int index, u32 qpn) +{ + struct mlx4_steer *s_steer; + struct mlx4_steer_index *tmp_entry, *entry = NULL; + struct mlx4_promisc_qp *dqp, *tmp_dqp; + u8 pf_num; + + pf_num = (dev->caps.num_ports == 1) ? vep_num : (vep_num << 1) | (port - 1); + s_steer = &mlx4_priv(dev)->steer[pf_num]; + + /* if qp is not promisc, it cannot be duplicated */ + if (!get_promisc_qp(dev, pf_num, steer, qpn)) + return false; + + /* The qp is promisc qp so it is a duplicate on this index + * Find the index entry, and remove the duplicate */ + list_for_each_entry(tmp_entry, &s_steer->steer_entries[steer], list) { + if (tmp_entry->index == index) { + entry = tmp_entry; + break; + } + } + if (unlikely(!entry)) { + mlx4_warn(dev, "Steering entry for index %x is not registered\n", index); + return false; + } + list_for_each_entry_safe(dqp, tmp_dqp, &entry->duplicates, list) { + if (dqp->qpn == qpn) { + list_del(&dqp->list); + kfree(dqp); + } + } + return true; +} + +/* I a steering entry contains only promisc QPs, it can be removed. */ +static bool can_remove_steering_entry(struct mlx4_dev *dev, u8 vep_num, u8 port, + enum mlx4_steer_type steer, + unsigned int index, u32 tqpn) +{ + struct mlx4_steer *s_steer; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_mgm *mgm; + struct mlx4_steer_index *entry = NULL, *tmp_entry; + u32 qpn; + u32 members_count; + bool ret = false; + int i; + u8 pf_num; + + pf_num = (dev->caps.num_ports == 1) ? vep_num : (vep_num << 1) | (port - 1); + s_steer = &mlx4_priv(dev)->steer[pf_num]; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return false; + mgm = mailbox->buf; + + if (mlx4_READ_ENTRY(dev, index, mailbox)) + goto out; + members_count = be32_to_cpu(mgm->members_count) & 0xffffff; + for (i = 0; i < members_count; i++) { + qpn = be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK; + if (!get_promisc_qp(dev, pf_num, steer, qpn) && qpn != tqpn) { + /* the qp is not promisc, the entry can't be removed */ + goto out; + } + } + /* All the qps currently registered for this entry are promiscuous, + * Checking for duplicates */ + ret = true; + list_for_each_entry_safe(entry, tmp_entry, &s_steer->steer_entries[steer], list) { + if (entry->index == index) { + if (list_empty(&entry->duplicates)) { + list_del(&entry->list); + kfree(entry); + } else { + /* This entry contains duplicates so it shouldn't be removed */ + ret = false; + goto out; + } + } + } + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + return ret; +} + +static int add_promisc_qp(struct mlx4_dev *dev, u8 vep_num, u8 port, + enum mlx4_steer_type steer, u32 qpn) +{ + struct mlx4_steer *s_steer; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_mgm *mgm; + struct mlx4_steer_index *entry; + struct mlx4_promisc_qp *pqp; + struct mlx4_promisc_qp *dqp; + u32 members_count; + u32 prot; + int i; + bool found; + int last_index; + int err; + u8 pf_num; + struct mlx4_priv *priv = mlx4_priv(dev); + pf_num = (dev->caps.num_ports == 1) ? vep_num : (vep_num << 1) | (port - 1); + s_steer = &mlx4_priv(dev)->steer[pf_num]; + + mutex_lock(&priv->mcg_table.mutex); + + if (get_promisc_qp(dev, pf_num, steer, qpn)) { + err = 0; /* Noting to do, already exists */ + goto out_mutex; + } + + pqp = kmalloc(sizeof *pqp, GFP_KERNEL); + if (!pqp) { + err = -ENOMEM; + goto out_mutex; + } + pqp->qpn = qpn; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) { + err = -ENOMEM; + goto out_alloc; + } + mgm = mailbox->buf; + + /* the promisc qp needs to be added for each one of the steering + * entries, if it already exists, needs to be added as a duplicate + * for this entry */ + list_for_each_entry(entry, &s_steer->steer_entries[steer], list) { + err = mlx4_READ_ENTRY(dev, entry->index, mailbox); + if (err) + goto out_mailbox; + + members_count = be32_to_cpu(mgm->members_count) & 0xffffff; + prot = be32_to_cpu(mgm->members_count) >> 30; + found = false; + for (i = 0; i < members_count; i++) { + if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qpn) { + /* Entry already exists, add to duplicates */ + dqp = kmalloc(sizeof *dqp, GFP_KERNEL); + if (!dqp) + goto out_mailbox; + dqp->qpn = qpn; + list_add_tail(&dqp->list, &entry->duplicates); + found = true; + } + } + if (!found) { + /* Need to add the qpn to mgm */ + if (members_count == MLX4_QP_PER_MGM) { + /* entry is full */ + err = -ENOMEM; + goto out_mailbox; + } + mgm->qp[members_count++] = cpu_to_be32(qpn & MGM_QPN_MASK); + mgm->members_count = cpu_to_be32(members_count | (prot << 30)); + err = mlx4_WRITE_ENTRY(dev, entry->index, mailbox); + if (err) + goto out_mailbox; + } + last_index = entry->index; + } + + /* add the new qpn to list of promisc qps */ + list_add_tail(&pqp->list, &s_steer->promisc_qps[steer]); + /* now need to add all the promisc qps to default entry */ + memset(mgm, 0, sizeof *mgm); + members_count = 0; + list_for_each_entry(dqp, &s_steer->promisc_qps[steer], list) + mgm->qp[members_count++] = cpu_to_be32(dqp->qpn & MGM_QPN_MASK); + mgm->members_count = cpu_to_be32(members_count | MLX4_PROT_ETH << 30); + + err = mlx4_WRITE_PROMISC(dev, vep_num, port, steer, mailbox); + if (err) + goto out_list; + + mlx4_free_cmd_mailbox(dev, mailbox); + mutex_unlock(&priv->mcg_table.mutex); + return 0; + +out_list: + list_del(&pqp->list); +out_mailbox: + mlx4_free_cmd_mailbox(dev, mailbox); +out_alloc: + kfree(pqp); +out_mutex: + mutex_unlock(&priv->mcg_table.mutex); + return err; +} + +static int remove_promisc_qp(struct mlx4_dev *dev, u8 vep_num, u8 port, + enum mlx4_steer_type steer, u32 qpn) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_steer *s_steer; + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_mgm *mgm; + struct mlx4_steer_index *entry; + struct mlx4_promisc_qp *pqp; + struct mlx4_promisc_qp *dqp; + u32 members_count; + bool found; + bool back_to_list = false; + int loc, i; + int err; + u8 pf_num; + + pf_num = (dev->caps.num_ports == 1) ? vep_num : (vep_num << 1) | (port - 1); + s_steer = &mlx4_priv(dev)->steer[pf_num]; + mutex_lock(&priv->mcg_table.mutex); + + pqp = get_promisc_qp(dev, pf_num, steer, qpn); + if (unlikely(!pqp)) { + mlx4_warn(dev, "QP %x is not promiscuous QP\n", qpn); + /* nothing to do */ + err = 0; + goto out_mutex; + } + + /*remove from list of promisc qps */ + list_del(&pqp->list); + kfree(pqp); + + /* set the default entry not to include the removed one */ + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) { + err = -ENOMEM; + back_to_list = true; + goto out_list; + } + mgm = mailbox->buf; + members_count = 0; + list_for_each_entry(dqp, &s_steer->promisc_qps[steer], list) + mgm->qp[members_count++] = cpu_to_be32(dqp->qpn & MGM_QPN_MASK); + mgm->members_count = cpu_to_be32(members_count | MLX4_PROT_ETH << 30); + + err = mlx4_WRITE_PROMISC(dev, vep_num, port, steer, mailbox); + if (err) + goto out_mailbox; + + /* remove the qp from all the steering entries*/ + list_for_each_entry(entry, &s_steer->steer_entries[steer], list) { + found = false; + list_for_each_entry(dqp, &entry->duplicates, list) { + if (dqp->qpn == qpn) { + found = true; + break; + } + } + if (found) { + /* a duplicate, no need to change the mgm, + * only update the duplicates list */ + list_del(&dqp->list); + kfree(dqp); + } else { + err = mlx4_READ_ENTRY(dev, entry->index, mailbox); + if (err) + goto out_mailbox; + members_count = be32_to_cpu(mgm->members_count) & 0xffffff; + for (loc = -1, i = 0; i < members_count; ++i) + if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qpn) + loc = i; + + mgm->members_count = cpu_to_be32(--members_count | + (MLX4_PROT_ETH << 30)); + mgm->qp[loc] = mgm->qp[i - 1]; + mgm->qp[i - 1] = 0; + + err = mlx4_WRITE_ENTRY(dev, entry->index, mailbox); + if (err) + goto out_mailbox; + } + + } + +out_mailbox: + mlx4_free_cmd_mailbox(dev, mailbox); +out_list: + if (back_to_list) + list_add_tail(&pqp->list, &s_steer->promisc_qps[steer]); +out_mutex: + mutex_unlock(&priv->mcg_table.mutex); + return err; +} + /* * Caller must hold MCG table semaphore. gid and mgm parameters must * be properly aligned for command interface. @@ -94,15 +548,17 @@ static int mlx4_MGID_HASH(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox * If no AMGM exists for given gid, *index = -1, *prev = index of last * entry in hash chain and *mgm holds end of hash chain. */ -static int find_mgm(struct mlx4_dev *dev, - u8 *gid, enum mlx4_protocol protocol, - struct mlx4_cmd_mailbox *mgm_mailbox, - u16 *hash, int *prev, int *index) +static int find_entry(struct mlx4_dev *dev, u8 port, + u8 *gid, enum mlx4_protocol prot, + enum mlx4_steer_type steer, + struct mlx4_cmd_mailbox *mgm_mailbox, + u16 *hash, int *prev, int *index) { struct mlx4_cmd_mailbox *mailbox; struct mlx4_mgm *mgm = mgm_mailbox->buf; u8 *mgid; int err; + u8 op_mod = (prot == MLX4_PROT_ETH) ? !!(dev->caps.vep_mc_steering) : 0; mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) @@ -111,7 +567,7 @@ static int find_mgm(struct mlx4_dev *dev, memcpy(mgid, gid, 16); - err = mlx4_MGID_HASH(dev, mailbox, hash); + err = mlx4_GID_HASH(dev, mailbox, hash, op_mod); mlx4_free_cmd_mailbox(dev, mailbox); if (err) return err; @@ -123,11 +579,11 @@ static int find_mgm(struct mlx4_dev *dev, *prev = -1; do { - err = mlx4_READ_MCG(dev, *index, mgm_mailbox); + err = mlx4_READ_ENTRY(dev, *index, mgm_mailbox); if (err) return err; - if (!memcmp(mgm->gid, zero_gid, 16)) { + if (!(be32_to_cpu(mgm->members_count) & 0xffffff)) { if (*index != *hash) { mlx4_err(dev, "Found zero MGID in AMGM.\n"); err = -EINVAL; @@ -136,7 +592,7 @@ static int find_mgm(struct mlx4_dev *dev, } if (!memcmp(mgm->gid, gid, 16) && - be32_to_cpu(mgm->members_count) >> 30 == protocol) + be32_to_cpu(mgm->members_count) >> 30 == prot) return err; *prev = *index; @@ -147,8 +603,9 @@ static int find_mgm(struct mlx4_dev *dev, return err; } -int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], - int block_mcast_loopback, enum mlx4_protocol protocol) +int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], + int block_mcast_loopback, enum mlx4_protocol prot, + enum mlx4_steer_type steer) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_cmd_mailbox *mailbox; @@ -159,6 +616,8 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int link = 0; int i; int err; + u8 port = gid[5]; + u8 new_entry = 0; mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) @@ -166,14 +625,16 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], mgm = mailbox->buf; mutex_lock(&priv->mcg_table.mutex); - - err = find_mgm(dev, gid, protocol, mailbox, &hash, &prev, &index); + err = find_entry(dev, port, gid, prot, steer, + mailbox, &hash, &prev, &index); if (err) goto out; if (index != -1) { - if (!memcmp(mgm->gid, zero_gid, 16)) + if (!(be32_to_cpu(mgm->members_count) & 0xffffff)) { + new_entry = 1; memcpy(mgm->gid, gid, 16); + } } else { link = 1; @@ -209,26 +670,34 @@ int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], else mgm->qp[members_count++] = cpu_to_be32(qp->qpn & MGM_QPN_MASK); - mgm->members_count = cpu_to_be32(members_count | (u32) protocol << 30); + mgm->members_count = cpu_to_be32(members_count | (u32) prot << 30); - err = mlx4_WRITE_MCG(dev, index, mailbox); + err = mlx4_WRITE_ENTRY(dev, index, mailbox); if (err) goto out; if (!link) goto out; - err = mlx4_READ_MCG(dev, prev, mailbox); + err = mlx4_READ_ENTRY(dev, prev, mailbox); if (err) goto out; mgm->next_gid_index = cpu_to_be32(index << 6); - err = mlx4_WRITE_MCG(dev, prev, mailbox); + err = mlx4_WRITE_ENTRY(dev, prev, mailbox); if (err) goto out; out: + if (prot == MLX4_PROT_ETH) { + /* manage the steering entry for promisc mode */ + if (new_entry) + new_steering_entry(dev, 0, port, steer, index, qp->qpn); + else + existing_steering_entry(dev, 0, port, steer, + index, qp->qpn); + } if (err && link && index != -1) { if (index < dev->caps.num_mgms) mlx4_warn(dev, "Got AMGM index %d < %d", @@ -242,10 +711,9 @@ out: mlx4_free_cmd_mailbox(dev, mailbox); return err; } -EXPORT_SYMBOL_GPL(mlx4_multicast_attach); -int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], - enum mlx4_protocol protocol) +int mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], + enum mlx4_protocol prot, enum mlx4_steer_type steer) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_cmd_mailbox *mailbox; @@ -255,6 +723,8 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], int prev, index; int i, loc; int err; + u8 port = gid[5]; + bool removed_entry = false; mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) @@ -263,7 +733,8 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], mutex_lock(&priv->mcg_table.mutex); - err = find_mgm(dev, gid, protocol, mailbox, &hash, &prev, &index); + err = find_entry(dev, port, gid, prot, steer, + mailbox, &hash, &prev, &index); if (err) goto out; @@ -273,6 +744,11 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], goto out; } + /* if this pq is also a promisc qp, it shouldn't be removed */ + if (prot == MLX4_PROT_ETH && + check_duplicate_entry(dev, 0, port, steer, index, qp->qpn)) + goto out; + members_count = be32_to_cpu(mgm->members_count) & 0xffffff; for (loc = -1, i = 0; i < members_count; ++i) if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qp->qpn) @@ -285,26 +761,31 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], } - mgm->members_count = cpu_to_be32(--members_count | (u32) protocol << 30); + mgm->members_count = cpu_to_be32(--members_count | (u32) prot << 30); mgm->qp[loc] = mgm->qp[i - 1]; mgm->qp[i - 1] = 0; - if (i != 1) { - err = mlx4_WRITE_MCG(dev, index, mailbox); + if (prot == MLX4_PROT_ETH) + removed_entry = can_remove_steering_entry(dev, 0, port, steer, index, qp->qpn); + if (i != 1 && (prot != MLX4_PROT_ETH || !removed_entry)) { + err = mlx4_WRITE_ENTRY(dev, index, mailbox); goto out; } + /* We are going to delete the entry, members count should be 0 */ + mgm->members_count = cpu_to_be32((u32) prot << 30); + if (prev == -1) { /* Remove entry from MGM */ int amgm_index = be32_to_cpu(mgm->next_gid_index) >> 6; if (amgm_index) { - err = mlx4_READ_MCG(dev, amgm_index, mailbox); + err = mlx4_READ_ENTRY(dev, amgm_index, mailbox); if (err) goto out; } else memset(mgm->gid, 0, 16); - err = mlx4_WRITE_MCG(dev, index, mailbox); + err = mlx4_WRITE_ENTRY(dev, index, mailbox); if (err) goto out; @@ -319,13 +800,13 @@ int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], } else { /* Remove entry from AMGM */ int cur_next_index = be32_to_cpu(mgm->next_gid_index) >> 6; - err = mlx4_READ_MCG(dev, prev, mailbox); + err = mlx4_READ_ENTRY(dev, prev, mailbox); if (err) goto out; mgm->next_gid_index = cpu_to_be32(cur_next_index << 6); - err = mlx4_WRITE_MCG(dev, prev, mailbox); + err = mlx4_WRITE_ENTRY(dev, prev, mailbox); if (err) goto out; @@ -343,8 +824,85 @@ out: mlx4_free_cmd_mailbox(dev, mailbox); return err; } + + +int mlx4_multicast_attach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], + int block_mcast_loopback, enum mlx4_protocol prot) +{ + enum mlx4_steer_type steer; + + steer = (is_valid_ether_addr(&gid[10])) ? MLX4_UC_STEER : MLX4_MC_STEER; + + if (prot == MLX4_PROT_ETH && !dev->caps.vep_mc_steering) + return 0; + + if (prot == MLX4_PROT_ETH) + gid[7] |= (steer << 1); + + return mlx4_qp_attach_common(dev, qp, gid, + block_mcast_loopback, prot, + steer); +} +EXPORT_SYMBOL_GPL(mlx4_multicast_attach); + +int mlx4_multicast_detach(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], + enum mlx4_protocol prot) +{ + enum mlx4_steer_type steer; + + steer = (is_valid_ether_addr(&gid[10])) ? MLX4_UC_STEER : MLX4_MC_STEER; + + if (prot == MLX4_PROT_ETH && !dev->caps.vep_mc_steering) + return 0; + + if (prot == MLX4_PROT_ETH) { + gid[7] |= (steer << 1); + } + + return mlx4_qp_detach_common(dev, qp, gid, prot, steer); +} EXPORT_SYMBOL_GPL(mlx4_multicast_detach); + +int mlx4_multicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port) +{ + if (!dev->caps.vep_mc_steering) + return 0; + + + return add_promisc_qp(dev, 0, port, MLX4_MC_STEER, qpn); +} +EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_add); + +int mlx4_multicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port) +{ + if (!dev->caps.vep_mc_steering) + return 0; + + + return remove_promisc_qp(dev, 0, port, MLX4_MC_STEER, qpn); +} +EXPORT_SYMBOL_GPL(mlx4_multicast_promisc_remove); + +int mlx4_unicast_promisc_add(struct mlx4_dev *dev, u32 qpn, u8 port) +{ + if (!dev->caps.vep_mc_steering) + return 0; + + + return add_promisc_qp(dev, 0, port, MLX4_UC_STEER, qpn); +} +EXPORT_SYMBOL_GPL(mlx4_unicast_promisc_add); + +int mlx4_unicast_promisc_remove(struct mlx4_dev *dev, u32 qpn, u8 port) +{ + if (!dev->caps.vep_mc_steering) + return 0; + + return remove_promisc_qp(dev, 0, port, MLX4_UC_STEER, qpn); +} +EXPORT_SYMBOL_GPL(mlx4_unicast_promisc_remove); + int mlx4_init_mcg_table(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h index 0da5bb72..c1e0e5f 100644 --- a/drivers/net/mlx4/mlx4.h +++ b/drivers/net/mlx4/mlx4.h @@ -105,6 +105,7 @@ struct mlx4_bitmap { u32 max; u32 reserved_top; u32 mask; + u32 avail; spinlock_t lock; unsigned long *table; }; @@ -162,6 +163,27 @@ struct mlx4_fw { u8 catas_bar; }; +#define MGM_QPN_MASK 0x00FFFFFF +#define MGM_BLCK_LB_BIT 30 + +struct mlx4_promisc_qp { + struct list_head list; + u32 qpn; +}; + +struct mlx4_steer_index { + struct list_head list; + unsigned int index; + struct list_head duplicates; +}; + +struct mlx4_mgm { + __be32 next_gid_index; + __be32 members_count; + u32 reserved[2]; + u8 gid[16]; + __be32 qp[MLX4_QP_PER_MGM]; +}; struct mlx4_cmd { struct pci_pool *pool; void __iomem *hcr; @@ -265,6 +287,10 @@ struct mlx4_vlan_table { int max; }; +struct mlx4_mac_entry { + u64 mac; +}; + struct mlx4_port_info { struct mlx4_dev *dev; int port; @@ -272,7 +298,9 @@ struct mlx4_port_info { struct device_attribute port_attr; enum mlx4_port_type tmp_type; struct mlx4_mac_table mac_table; + struct radix_tree_root mac_tree; struct mlx4_vlan_table vlan_table; + int base_qpn; }; struct mlx4_sense { @@ -282,6 +310,17 @@ struct mlx4_sense { struct delayed_work sense_poll; }; +struct mlx4_msix_ctl { + u64 pool_bm; + spinlock_t pool_lock; +}; + +struct mlx4_steer { + struct list_head promisc_qps[MLX4_NUM_STEERS]; + struct list_head steer_entries[MLX4_NUM_STEERS]; + struct list_head high_prios; +}; + struct mlx4_priv { struct mlx4_dev dev; @@ -313,6 +352,11 @@ struct mlx4_priv { struct mlx4_port_info port[MLX4_MAX_PORTS + 1]; struct mlx4_sense sense; struct mutex port_mutex; + struct mlx4_msix_ctl msix_ctl; + struct mlx4_steer *steer; + struct list_head bf_list; + struct mutex bf_mutex; + struct io_mapping *bf_mapping; }; static inline struct mlx4_priv *mlx4_priv(struct mlx4_dev *dev) @@ -328,6 +372,7 @@ u32 mlx4_bitmap_alloc(struct mlx4_bitmap *bitmap); void mlx4_bitmap_free(struct mlx4_bitmap *bitmap, u32 obj); u32 mlx4_bitmap_alloc_range(struct mlx4_bitmap *bitmap, int cnt, int align); void mlx4_bitmap_free_range(struct mlx4_bitmap *bitmap, u32 obj, int cnt); +u32 mlx4_bitmap_avail(struct mlx4_bitmap *bitmap); int mlx4_bitmap_init(struct mlx4_bitmap *bitmap, u32 num, u32 mask, u32 reserved_bot, u32 resetrved_top); void mlx4_bitmap_cleanup(struct mlx4_bitmap *bitmap); @@ -403,4 +448,9 @@ void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table); int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port); int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps); +int mlx4_qp_detach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], + enum mlx4_protocol prot, enum mlx4_steer_type steer); +int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], + int block_mcast_loopback, enum mlx4_protocol prot, + enum mlx4_steer_type steer); #endif /* MLX4_H */ diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h index dfed6a0..e30f609 100644 --- a/drivers/net/mlx4/mlx4_en.h +++ b/drivers/net/mlx4/mlx4_en.h @@ -49,8 +49,8 @@ #include "en_port.h" #define DRV_NAME "mlx4_en" -#define DRV_VERSION "1.5.1.6" -#define DRV_RELDATE "August 2010" +#define DRV_VERSION "1.5.4.1" +#define DRV_RELDATE "March 2011" #define MLX4_EN_MSG_LEVEL (NETIF_MSG_LINK | NETIF_MSG_IFDOWN) @@ -62,6 +62,7 @@ #define MLX4_EN_PAGE_SHIFT 12 #define MLX4_EN_PAGE_SIZE (1 << MLX4_EN_PAGE_SHIFT) #define MAX_RX_RINGS 16 +#define MIN_RX_RINGS 4 #define TXBB_SIZE 64 #define HEADROOM (2048 / TXBB_SIZE + 1) #define STAMP_STRIDE 64 @@ -124,6 +125,7 @@ enum { #define MLX4_EN_RX_SIZE_THRESH 1024 #define MLX4_EN_RX_RATE_THRESH (1000000 / MLX4_EN_RX_COAL_TIME_HIGH) #define MLX4_EN_SAMPLE_INTERVAL 0 +#define MLX4_EN_AVG_PKT_SMALL 256 #define MLX4_EN_AUTO_CONF 0xffff @@ -214,6 +216,9 @@ struct mlx4_en_tx_desc { #define MLX4_EN_USE_SRQ 0x01000000 +#define MLX4_EN_CX3_LOW_ID 0x1000 +#define MLX4_EN_CX3_HIGH_ID 0x1005 + struct mlx4_en_rx_alloc { struct page *page; u16 offset; @@ -243,6 +248,8 @@ struct mlx4_en_tx_ring { unsigned long bytes; unsigned long packets; spinlock_t comp_lock; + struct mlx4_bf bf; + bool bf_enabled; }; struct mlx4_en_rx_desc { @@ -453,6 +460,7 @@ struct mlx4_en_priv { struct mlx4_en_rss_map rss_map; u32 flags; #define MLX4_EN_FLAG_PROMISC 0x1 +#define MLX4_EN_FLAG_MC_PROMISC 0x2 u32 tx_ring_num; u32 rx_ring_num; u32 rx_skb_size; @@ -461,6 +469,7 @@ struct mlx4_en_priv { u16 log_rx_info; struct mlx4_en_tx_ring tx_ring[MAX_TX_RINGS]; + int tx_vector; struct mlx4_en_rx_ring rx_ring[MAX_RX_RINGS]; struct mlx4_en_cq tx_cq[MAX_TX_RINGS]; struct mlx4_en_cq rx_cq[MAX_RX_RINGS]; @@ -476,6 +485,13 @@ struct mlx4_en_priv { int mc_addrs_cnt; struct mlx4_en_stat_out_mbox hw_stats; int vids[128]; + bool wol; +}; + +enum mlx4_en_wol { + MLX4_EN_WOL_MAGIC = (1ULL << 61), + MLX4_EN_WOL_ENABLED = (1ULL << 62), + MLX4_EN_WOL_DO_MODIFY = (1ULL << 63), }; @@ -486,12 +502,13 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, int mlx4_en_start_port(struct net_device *dev); void mlx4_en_stop_port(struct net_device *dev); -void mlx4_en_free_resources(struct mlx4_en_priv *priv); +void mlx4_en_free_resources(struct mlx4_en_priv *priv, bool reserve_vectors); int mlx4_en_alloc_resources(struct mlx4_en_priv *priv); int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, int entries, int ring, enum cq_type mode); -void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); +void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, + bool reserve_vectors); int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); int mlx4_en_set_cq_moder(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); @@ -503,7 +520,7 @@ u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb); netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev); int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, - u32 size, u16 stride); + int qpn, u32 size, u16 stride); void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring); int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, diff --git a/drivers/net/mlx4/pd.c b/drivers/net/mlx4/pd.c index c4988d6..1286b88 100644 --- a/drivers/net/mlx4/pd.c +++ b/drivers/net/mlx4/pd.c @@ -32,12 +32,17 @@ */ #include <linux/errno.h> +#include <linux/io-mapping.h> #include <asm/page.h> #include "mlx4.h" #include "icm.h" +enum { + MLX4_NUM_RESERVED_UARS = 8 +}; + int mlx4_pd_alloc(struct mlx4_dev *dev, u32 *pdn) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -77,6 +82,7 @@ int mlx4_uar_alloc(struct mlx4_dev *dev, struct mlx4_uar *uar) return -ENOMEM; uar->pfn = (pci_resource_start(dev->pdev, 2) >> PAGE_SHIFT) + uar->index; + uar->map = NULL; return 0; } @@ -88,6 +94,102 @@ void mlx4_uar_free(struct mlx4_dev *dev, struct mlx4_uar *uar) } EXPORT_SYMBOL_GPL(mlx4_uar_free); +int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_uar *uar; + int err = 0; + int idx; + + if (!priv->bf_mapping) + return -ENOMEM; + + mutex_lock(&priv->bf_mutex); + if (!list_empty(&priv->bf_list)) + uar = list_entry(priv->bf_list.next, struct mlx4_uar, bf_list); + else { + if (mlx4_bitmap_avail(&priv->uar_table.bitmap) < MLX4_NUM_RESERVED_UARS) { + err = -ENOMEM; + goto out; + } + uar = kmalloc(sizeof *uar, GFP_KERNEL); + if (!uar) { + err = -ENOMEM; + goto out; + } + err = mlx4_uar_alloc(dev, uar); + if (err) + goto free_kmalloc; + + uar->map = ioremap(uar->pfn << PAGE_SHIFT, PAGE_SIZE); + if (!uar->map) { + err = -ENOMEM; + goto free_uar; + } + + uar->bf_map = io_mapping_map_wc(priv->bf_mapping, uar->index << PAGE_SHIFT); + if (!uar->bf_map) { + err = -ENOMEM; + goto unamp_uar; + } + uar->free_bf_bmap = 0; + list_add(&uar->bf_list, &priv->bf_list); + } + + bf->uar = uar; + idx = ffz(uar->free_bf_bmap); + uar->free_bf_bmap |= 1 << idx; + bf->uar = uar; + bf->offset = 0; + bf->buf_size = dev->caps.bf_reg_size / 2; + bf->reg = uar->bf_map + idx * dev->caps.bf_reg_size; + if (uar->free_bf_bmap == (1 << dev->caps.bf_regs_per_page) - 1) + list_del_init(&uar->bf_list); + + goto out; + +unamp_uar: + bf->uar = NULL; + iounmap(uar->map); + +free_uar: + mlx4_uar_free(dev, uar); + +free_kmalloc: + kfree(uar); + +out: + mutex_unlock(&priv->bf_mutex); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_bf_alloc); + +void mlx4_bf_free(struct mlx4_dev *dev, struct mlx4_bf *bf) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int idx; + + if (!bf->uar || !bf->uar->bf_map) + return; + + mutex_lock(&priv->bf_mutex); + idx = (bf->reg - bf->uar->bf_map) / dev->caps.bf_reg_size; + bf->uar->free_bf_bmap &= ~(1 << idx); + if (!bf->uar->free_bf_bmap) { + if (!list_empty(&bf->uar->bf_list)) + list_del(&bf->uar->bf_list); + + io_mapping_unmap(bf->uar->bf_map); + iounmap(bf->uar->map); + mlx4_uar_free(dev, bf->uar); + kfree(bf->uar); + } else if (list_empty(&bf->uar->bf_list)) + list_add(&bf->uar->bf_list, &priv->bf_list); + + mutex_unlock(&priv->bf_mutex); +} +EXPORT_SYMBOL_GPL(mlx4_bf_free); + int mlx4_init_uar_table(struct mlx4_dev *dev) { if (dev->caps.num_uars <= 128) { diff --git a/drivers/net/mlx4/port.c b/drivers/net/mlx4/port.c index 4513395..eca7d85 100644 --- a/drivers/net/mlx4/port.c +++ b/drivers/net/mlx4/port.c @@ -90,12 +90,79 @@ static int mlx4_set_port_mac_table(struct mlx4_dev *dev, u8 port, return err; } -int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *index) +static int mlx4_uc_steer_add(struct mlx4_dev *dev, u8 port, + u64 mac, int *qpn, u8 reserve) { - struct mlx4_mac_table *table = &mlx4_priv(dev)->port[port].mac_table; + struct mlx4_qp qp; + u8 gid[16] = {0}; + int err; + + if (reserve) { + err = mlx4_qp_reserve_range(dev, 1, 1, qpn); + if (err) { + mlx4_err(dev, "Failed to reserve qp for mac registration\n"); + return err; + } + } + qp.qpn = *qpn; + + mac &= 0xffffffffffffULL; + mac = cpu_to_be64(mac << 16); + memcpy(&gid[10], &mac, ETH_ALEN); + gid[5] = port; + gid[7] = MLX4_UC_STEER << 1; + + err = mlx4_qp_attach_common(dev, &qp, gid, 0, + MLX4_PROT_ETH, MLX4_UC_STEER); + if (err && reserve) + mlx4_qp_release_range(dev, *qpn, 1); + + return err; +} + +static void mlx4_uc_steer_release(struct mlx4_dev *dev, u8 port, + u64 mac, int qpn, u8 free) +{ + struct mlx4_qp qp; + u8 gid[16] = {0}; + + qp.qpn = qpn; + mac &= 0xffffffffffffULL; + mac = cpu_to_be64(mac << 16); + memcpy(&gid[10], &mac, ETH_ALEN); + gid[5] = port; + gid[7] = MLX4_UC_STEER << 1; + + mlx4_qp_detach_common(dev, &qp, gid, MLX4_PROT_ETH, MLX4_UC_STEER); + if (free) + mlx4_qp_release_range(dev, qpn, 1); +} + +int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *qpn, u8 wrap) +{ + struct mlx4_port_info *info = &mlx4_priv(dev)->port[port]; + struct mlx4_mac_table *table = &info->mac_table; + struct mlx4_mac_entry *entry; int i, err = 0; int free = -1; + if (dev->caps.vep_uc_steering) { + err = mlx4_uc_steer_add(dev, port, mac, qpn, 1); + if (!err) { + entry = kmalloc(sizeof *entry, GFP_KERNEL); + if (!entry) { + mlx4_uc_steer_release(dev, port, mac, *qpn, 1); + return -ENOMEM; + } + entry->mac = mac; + err = radix_tree_insert(&info->mac_tree, *qpn, entry); + if (err) { + mlx4_uc_steer_release(dev, port, mac, *qpn, 1); + return err; + } + } else + return err; + } mlx4_dbg(dev, "Registering MAC: 0x%llx\n", (unsigned long long) mac); mutex_lock(&table->mutex); for (i = 0; i < MLX4_MAX_MAC_NUM - 1; i++) { @@ -106,7 +173,6 @@ int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *index) if (mac == (MLX4_MAC_MASK & be64_to_cpu(table->entries[i]))) { /* MAC already registered, increase refernce count */ - *index = i; ++table->refs[i]; goto out; } @@ -137,7 +203,8 @@ int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac, int *index) goto out; } - *index = free; + if (!dev->caps.vep_uc_steering) + *qpn = info->base_qpn + free; ++table->total; out: mutex_unlock(&table->mutex); @@ -145,20 +212,52 @@ out: } EXPORT_SYMBOL_GPL(mlx4_register_mac); -void mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, int index) +static int validate_index(struct mlx4_dev *dev, + struct mlx4_mac_table *table, int index) { - struct mlx4_mac_table *table = &mlx4_priv(dev)->port[port].mac_table; + int err = 0; - mutex_lock(&table->mutex); - if (!table->refs[index]) { - mlx4_warn(dev, "No MAC entry for index %d\n", index); - goto out; + if (index < 0 || index >= table->max || !table->entries[index]) { + mlx4_warn(dev, "No valid Mac entry for the given index\n"); + err = -EINVAL; } - if (--table->refs[index]) { - mlx4_warn(dev, "Have more references for index %d," - "no need to modify MAC table\n", index); - goto out; + return err; +} + +static int find_index(struct mlx4_dev *dev, + struct mlx4_mac_table *table, u64 mac) +{ + int i; + for (i = 0; i < MLX4_MAX_MAC_NUM; i++) { + if (mac == (MLX4_MAC_MASK & be64_to_cpu(table->entries[i]))) + return i; } + /* Mac not found */ + return -EINVAL; +} + +void mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, int qpn) +{ + struct mlx4_port_info *info = &mlx4_priv(dev)->port[port]; + struct mlx4_mac_table *table = &info->mac_table; + int index = qpn - info->base_qpn; + struct mlx4_mac_entry *entry; + + if (dev->caps.vep_uc_steering) { + entry = radix_tree_lookup(&info->mac_tree, qpn); + if (entry) { + mlx4_uc_steer_release(dev, port, entry->mac, qpn, 1); + radix_tree_delete(&info->mac_tree, qpn); + index = find_index(dev, table, entry->mac); + kfree(entry); + } + } + + mutex_lock(&table->mutex); + + if (validate_index(dev, table, index)) + goto out; + table->entries[index] = 0; mlx4_set_port_mac_table(dev, port, table->entries); --table->total; @@ -167,6 +266,44 @@ out: } EXPORT_SYMBOL_GPL(mlx4_unregister_mac); +int mlx4_replace_mac(struct mlx4_dev *dev, u8 port, int qpn, u64 new_mac, u8 wrap) +{ + struct mlx4_port_info *info = &mlx4_priv(dev)->port[port]; + struct mlx4_mac_table *table = &info->mac_table; + int index = qpn - info->base_qpn; + struct mlx4_mac_entry *entry; + int err; + + if (dev->caps.vep_uc_steering) { + entry = radix_tree_lookup(&info->mac_tree, qpn); + if (!entry) + return -EINVAL; + index = find_index(dev, table, entry->mac); + mlx4_uc_steer_release(dev, port, entry->mac, qpn, 0); + entry->mac = new_mac; + err = mlx4_uc_steer_add(dev, port, entry->mac, &qpn, 0); + if (err || index < 0) + return err; + } + + mutex_lock(&table->mutex); + + err = validate_index(dev, table, index); + if (err) + goto out; + + table->entries[index] = cpu_to_be64(new_mac | MLX4_MAC_VALID); + + err = mlx4_set_port_mac_table(dev, port, table->entries); + if (unlikely(err)) { + mlx4_err(dev, "Failed adding MAC: 0x%llx\n", (unsigned long long) new_mac); + table->entries[index] = 0; + } +out: + mutex_unlock(&table->mutex); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_replace_mac); static int mlx4_set_port_vlan_table(struct mlx4_dev *dev, u8 port, __be32 *entries) { diff --git a/drivers/net/mlx4/profile.c b/drivers/net/mlx4/profile.c index e749f82..b967647 100644 --- a/drivers/net/mlx4/profile.c +++ b/drivers/net/mlx4/profile.c @@ -107,9 +107,7 @@ u64 mlx4_make_profile(struct mlx4_dev *dev, profile[MLX4_RES_AUXC].num = request->num_qp; profile[MLX4_RES_SRQ].num = request->num_srq; profile[MLX4_RES_CQ].num = request->num_cq; - profile[MLX4_RES_EQ].num = min_t(unsigned, dev_cap->max_eqs, - dev_cap->reserved_eqs + - num_possible_cpus() + 1); + profile[MLX4_RES_EQ].num = min_t(unsigned, dev_cap->max_eqs, MAX_MSIX); profile[MLX4_RES_DMPT].num = request->num_mpt; profile[MLX4_RES_CMPT].num = MLX4_NUM_CMPTS; profile[MLX4_RES_MTT].num = request->num_mtt; diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index a7f2eed..1f4e868 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -3645,6 +3645,7 @@ static void myri10ge_free_slices(struct myri10ge_priv *mgp) dma_free_coherent(&pdev->dev, bytes, ss->fw_stats, ss->fw_stats_bus); ss->fw_stats = NULL; + netif_napi_del(&ss->napi); } } kfree(mgp->ss); diff --git a/drivers/net/pch_gbe/pch_gbe_main.c b/drivers/net/pch_gbe/pch_gbe_main.c index 8c66e22..5098684 100644 --- a/drivers/net/pch_gbe/pch_gbe_main.c +++ b/drivers/net/pch_gbe/pch_gbe_main.c @@ -2441,7 +2441,7 @@ static struct pci_error_handlers pch_gbe_err_handler = { .resume = pch_gbe_io_resume }; -static struct pci_driver pch_gbe_pcidev = { +static struct pci_driver pch_gbe_driver = { .name = KBUILD_MODNAME, .id_table = pch_gbe_pcidev_id, .probe = pch_gbe_probe, @@ -2458,7 +2458,7 @@ static int __init pch_gbe_init_module(void) { int ret; - ret = pci_register_driver(&pch_gbe_pcidev); + ret = pci_register_driver(&pch_gbe_driver); if (copybreak != PCH_GBE_COPYBREAK_DEFAULT) { if (copybreak == 0) { pr_info("copybreak disabled\n"); @@ -2472,7 +2472,7 @@ static int __init pch_gbe_init_module(void) static void __exit pch_gbe_exit_module(void) { - pci_unregister_driver(&pch_gbe_pcidev); + pci_unregister_driver(&pch_gbe_driver); } module_init(pch_gbe_init_module); diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index b8bd936..d890679 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -1054,6 +1054,7 @@ static int efx_init_io(struct efx_nic *efx) { struct pci_dev *pci_dev = efx->pci_dev; dma_addr_t dma_mask = efx->type->max_dma_mask; + bool use_wc; int rc; netif_dbg(efx, probe, efx->net_dev, "initialising I/O\n"); @@ -1104,8 +1105,21 @@ static int efx_init_io(struct efx_nic *efx) rc = -EIO; goto fail3; } - efx->membase = ioremap_wc(efx->membase_phys, - efx->type->mem_map_size); + + /* bug22643: If SR-IOV is enabled then tx push over a write combined + * mapping is unsafe. We need to disable write combining in this case. + * MSI is unsupported when SR-IOV is enabled, and the firmware will + * have removed the MSI capability. So write combining is safe if + * there is an MSI capability. + */ + use_wc = (!EFX_WORKAROUND_22643(efx) || + pci_find_capability(pci_dev, PCI_CAP_ID_MSI)); + if (use_wc) + efx->membase = ioremap_wc(efx->membase_phys, + efx->type->mem_map_size); + else + efx->membase = ioremap_nocache(efx->membase_phys, + efx->type->mem_map_size); if (!efx->membase) { netif_err(efx, probe, efx->net_dev, "could not map memory BAR at %llx+%x\n", diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h index e4dd3a7..99ff114 100644 --- a/drivers/net/sfc/workarounds.h +++ b/drivers/net/sfc/workarounds.h @@ -38,6 +38,8 @@ #define EFX_WORKAROUND_15783 EFX_WORKAROUND_ALWAYS /* Legacy interrupt storm when interrupt fifo fills */ #define EFX_WORKAROUND_17213 EFX_WORKAROUND_SIENA +/* Write combining and sriov=enabled are incompatible */ +#define EFX_WORKAROUND_22643 EFX_WORKAROUND_SIENA /* Spurious parity errors in TSORT buffers */ #define EFX_WORKAROUND_5129 EFX_WORKAROUND_FALCON_A diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index bc86f4b..727874d 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -49,6 +49,8 @@ struct smsc95xx_priv { u32 mac_cr; + u32 hash_hi; + u32 hash_lo; spinlock_t mac_cr_lock; bool use_tx_csum; bool use_rx_csum; @@ -370,10 +372,11 @@ static void smsc95xx_set_multicast(struct net_device *netdev) { struct usbnet *dev = netdev_priv(netdev); struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); - u32 hash_hi = 0; - u32 hash_lo = 0; unsigned long flags; + pdata->hash_hi = 0; + pdata->hash_lo = 0; + spin_lock_irqsave(&pdata->mac_cr_lock, flags); if (dev->net->flags & IFF_PROMISC) { @@ -394,13 +397,13 @@ static void smsc95xx_set_multicast(struct net_device *netdev) u32 bitnum = smsc95xx_hash(ha->addr); u32 mask = 0x01 << (bitnum & 0x1F); if (bitnum & 0x20) - hash_hi |= mask; + pdata->hash_hi |= mask; else - hash_lo |= mask; + pdata->hash_lo |= mask; } netif_dbg(dev, drv, dev->net, "HASHH=0x%08X, HASHL=0x%08X\n", - hash_hi, hash_lo); + pdata->hash_hi, pdata->hash_lo); } else { netif_dbg(dev, drv, dev->net, "receive own packets only\n"); pdata->mac_cr &= @@ -410,8 +413,8 @@ static void smsc95xx_set_multicast(struct net_device *netdev) spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); /* Initiate async writes, as we can't wait for completion here */ - smsc95xx_write_reg_async(dev, HASHH, &hash_hi); - smsc95xx_write_reg_async(dev, HASHL, &hash_lo); + smsc95xx_write_reg_async(dev, HASHH, &pdata->hash_hi); + smsc95xx_write_reg_async(dev, HASHL, &pdata->hash_lo); smsc95xx_write_reg_async(dev, MAC_CR, &pdata->mac_cr); } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 115f162..5248257 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2160,6 +2160,8 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop) if (!ath_drain_all_txq(sc, false)) ath_reset(sc, false); + ieee80211_wake_queues(hw); + out: ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0); mutex_unlock(&sc->mutex); diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 960d717..a3241cd 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1328,7 +1328,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, hdr = (struct ieee80211_hdr *)skb->data; fc = hdr->frame_control; - for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + for (i = 0; i < sc->hw->max_rates; i++) { struct ieee80211_tx_rate *rate = &tx_info->status.rates[i]; if (!rate->count) break; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index ef22096..26734e5 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1725,8 +1725,8 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf, u8 tidno; spin_lock_bh(&txctl->txq->axq_lock); - - if (ieee80211_is_data_qos(hdr->frame_control) && txctl->an) { + if ((sc->sc_flags & SC_OP_TXAGGR) && txctl->an && + ieee80211_is_data_qos(hdr->frame_control)) { tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; tid = ATH_AN_2_TID(txctl->an, tidno); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 2003c1d..08ccb94 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -2265,7 +2265,7 @@ signed long iwlagn_wait_notification(struct iwl_priv *priv, int ret; ret = wait_event_timeout(priv->_agn.notif_waitq, - &wait_entry->triggered, + wait_entry->triggered, timeout); spin_lock_bh(&priv->_agn.notif_wait_lock); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 581dc9f..321b18b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3009,14 +3009,17 @@ static int iwl_mac_offchannel_tx_cancel_wait(struct ieee80211_hw *hw) mutex_lock(&priv->mutex); - if (!priv->_agn.offchan_tx_skb) - return -EINVAL; + if (!priv->_agn.offchan_tx_skb) { + ret = -EINVAL; + goto unlock; + } priv->_agn.offchan_tx_skb = NULL; ret = iwl_scan_cancel_timeout(priv, 200); if (ret) ret = -EIO; +unlock: mutex_unlock(&priv->mutex); return ret; diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c index 09fae2f..736bbb9 100644 --- a/drivers/net/wireless/orinoco/cfg.c +++ b/drivers/net/wireless/orinoco/cfg.c @@ -153,6 +153,9 @@ static int orinoco_scan(struct wiphy *wiphy, struct net_device *dev, priv->scan_request = request; err = orinoco_hw_trigger_scan(priv, request->ssids); + /* On error the we aren't processing the request */ + if (err) + priv->scan_request = NULL; return err; } diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c index f3d396e..62c6b2b 100644 --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c @@ -1376,13 +1376,13 @@ static void orinoco_process_scan_results(struct work_struct *work) spin_lock_irqsave(&priv->scan_lock, flags); list_for_each_entry_safe(sd, temp, &priv->scan_list, list) { - spin_unlock_irqrestore(&priv->scan_lock, flags); buf = sd->buf; len = sd->len; type = sd->type; list_del(&sd->list); + spin_unlock_irqrestore(&priv->scan_lock, flags); kfree(sd); if (len > 0) { diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index f1a9214..4e36865 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -719,6 +719,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0b05, 0x1732), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0b05, 0x1742), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0b05, 0x1784), USB_DEVICE_DATA(&rt2800usb_ops) }, + { USB_DEVICE(0x1761, 0x0b05), USB_DEVICE_DATA(&rt2800usb_ops) }, /* AzureWave */ { USB_DEVICE(0x13d3, 0x3247), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x13d3, 0x3273), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -913,7 +914,6 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0b05, 0x1760), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0b05, 0x1761), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x0b05, 0x1790), USB_DEVICE_DATA(&rt2800usb_ops) }, - { USB_DEVICE(0x1761, 0x0b05), USB_DEVICE_DATA(&rt2800usb_ops) }, /* AzureWave */ { USB_DEVICE(0x13d3, 0x3262), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x13d3, 0x3284), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -937,6 +937,8 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x07d1, 0x3c13), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07d1, 0x3c15), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x07d1, 0x3c17), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Edimax */ + { USB_DEVICE(0x7392, 0x4085), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Encore */ { USB_DEVICE(0x203d, 0x14a1), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Gemtek */ @@ -961,6 +963,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x1d4d, 0x0010), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x1d4d, 0x0011), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Planex */ + { USB_DEVICE(0x2019, 0x5201), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x2019, 0xab24), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Qcom */ { USB_DEVICE(0x18e8, 0x6259), USB_DEVICE_DATA(&rt2800usb_ops) }, @@ -972,6 +975,8 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Sweex */ { USB_DEVICE(0x177f, 0x0153), USB_DEVICE_DATA(&rt2800usb_ops) }, { USB_DEVICE(0x177f, 0x0313), USB_DEVICE_DATA(&rt2800usb_ops) }, + /* Toshiba */ + { USB_DEVICE(0x0930, 0x0a07), USB_DEVICE_DATA(&rt2800usb_ops) }, /* Zyxel */ { USB_DEVICE(0x0586, 0x341a), USB_DEVICE_DATA(&rt2800usb_ops) }, #endif diff --git a/drivers/net/wireless/rtlwifi/efuse.c b/drivers/net/wireless/rtlwifi/efuse.c index 4f92cba..f74a870 100644 --- a/drivers/net/wireless/rtlwifi/efuse.c +++ b/drivers/net/wireless/rtlwifi/efuse.c @@ -410,8 +410,8 @@ bool efuse_shadow_update(struct ieee80211_hw *hw) if (!efuse_shadow_update_chk(hw)) { efuse_read_all_map(hw, &rtlefuse->efuse_map[EFUSE_INIT_MAP][0]); - memcpy((void *)&rtlefuse->efuse_map[EFUSE_MODIFY_MAP][0], - (void *)&rtlefuse->efuse_map[EFUSE_INIT_MAP][0], + memcpy(&rtlefuse->efuse_map[EFUSE_MODIFY_MAP][0], + &rtlefuse->efuse_map[EFUSE_INIT_MAP][0], rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE]); RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, @@ -446,9 +446,9 @@ bool efuse_shadow_update(struct ieee80211_hw *hw) if (word_en != 0x0F) { u8 tmpdata[8]; - memcpy((void *)tmpdata, - (void *)(&rtlefuse-> - efuse_map[EFUSE_MODIFY_MAP][base]), 8); + memcpy(tmpdata, + &rtlefuse->efuse_map[EFUSE_MODIFY_MAP][base], + 8); RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_LOUD, ("U-efuse\n"), tmpdata, 8); @@ -465,8 +465,8 @@ bool efuse_shadow_update(struct ieee80211_hw *hw) efuse_power_switch(hw, true, false); efuse_read_all_map(hw, &rtlefuse->efuse_map[EFUSE_INIT_MAP][0]); - memcpy((void *)&rtlefuse->efuse_map[EFUSE_MODIFY_MAP][0], - (void *)&rtlefuse->efuse_map[EFUSE_INIT_MAP][0], + memcpy(&rtlefuse->efuse_map[EFUSE_MODIFY_MAP][0], + &rtlefuse->efuse_map[EFUSE_INIT_MAP][0], rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE]); RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, ("<---\n")); @@ -479,13 +479,12 @@ void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw) struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); if (rtlefuse->autoload_failflag == true) { - memset((void *)(&rtlefuse->efuse_map[EFUSE_INIT_MAP][0]), 128, - 0xFF); + memset(&rtlefuse->efuse_map[EFUSE_INIT_MAP][0], 0xFF, 128); } else efuse_read_all_map(hw, &rtlefuse->efuse_map[EFUSE_INIT_MAP][0]); - memcpy((void *)&rtlefuse->efuse_map[EFUSE_MODIFY_MAP][0], - (void *)&rtlefuse->efuse_map[EFUSE_INIT_MAP][0], + memcpy(&rtlefuse->efuse_map[EFUSE_MODIFY_MAP][0], + &rtlefuse->efuse_map[EFUSE_INIT_MAP][0], rtlpriv->cfg->maps[EFUSE_HWSET_MAX_SIZE]); } @@ -694,8 +693,8 @@ static int efuse_pg_packet_read(struct ieee80211_hw *hw, u8 offset, u8 *data) if (offset > 15) return false; - memset((void *)data, PGPKT_DATA_SIZE * sizeof(u8), 0xff); - memset((void *)tmpdata, PGPKT_DATA_SIZE * sizeof(u8), 0xff); + memset(data, 0xff, PGPKT_DATA_SIZE * sizeof(u8)); + memset(tmpdata, 0xff, PGPKT_DATA_SIZE * sizeof(u8)); while (bcontinual && (efuse_addr < EFUSE_MAX_SIZE)) { if (readstate & PG_STATE_HEADER) { @@ -862,7 +861,7 @@ static void efuse_write_data_case2(struct ieee80211_hw *hw, u16 *efuse_addr, tmp_word_cnts = efuse_calculate_word_cnts(tmp_pkt.word_en); - memset((void *)originaldata, 8 * sizeof(u8), 0xff); + memset(originaldata, 0xff, 8 * sizeof(u8)); if (efuse_pg_packet_read(hw, tmp_pkt.offset, originaldata)) { badworden = efuse_word_enable_data_write(hw, @@ -917,7 +916,7 @@ static int efuse_pg_packet_write(struct ieee80211_hw *hw, target_pkt.offset = offset; target_pkt.word_en = word_en; - memset((void *)target_pkt.data, 8 * sizeof(u8), 0xFF); + memset(target_pkt.data, 0xFF, 8 * sizeof(u8)); efuse_word_enable_data_read(word_en, data, target_pkt.data); target_word_cnts = efuse_calculate_word_cnts(target_pkt.word_en); @@ -1022,7 +1021,7 @@ static u8 efuse_word_enable_data_write(struct ieee80211_hw *hw, u8 badworden = 0x0F; u8 tmpdata[8]; - memset((void *)tmpdata, PGPKT_DATA_SIZE, 0xff); + memset(tmpdata, 0xff, PGPKT_DATA_SIZE); RT_TRACE(rtlpriv, COMP_EFUSE, DBG_LOUD, ("word_en = %x efuse_addr=%x\n", word_en, efuse_addr)); diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 81e8048..58236e6 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -60,6 +60,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x157e, 0x300a), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x3207), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, /* ZD1211B */ diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 4789f8e..a4115f1 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -36,7 +36,7 @@ #include <linux/iova.h> #include <linux/iommu.h> #include <linux/intel-iommu.h> -#include <linux/sysdev.h> +#include <linux/syscore_ops.h> #include <linux/tboot.h> #include <linux/dmi.h> #include <asm/cacheflush.h> @@ -3135,7 +3135,7 @@ static void iommu_flush_all(void) } } -static int iommu_suspend(struct sys_device *dev, pm_message_t state) +static int iommu_suspend(void) { struct dmar_drhd_unit *drhd; struct intel_iommu *iommu = NULL; @@ -3175,7 +3175,7 @@ nomem: return -ENOMEM; } -static int iommu_resume(struct sys_device *dev) +static void iommu_resume(void) { struct dmar_drhd_unit *drhd; struct intel_iommu *iommu = NULL; @@ -3183,7 +3183,7 @@ static int iommu_resume(struct sys_device *dev) if (init_iommu_hw()) { WARN(1, "IOMMU setup failed, DMAR can not resume!\n"); - return -EIO; + return; } for_each_active_iommu(iommu, drhd) { @@ -3204,40 +3204,20 @@ static int iommu_resume(struct sys_device *dev) for_each_active_iommu(iommu, drhd) kfree(iommu->iommu_state); - - return 0; } -static struct sysdev_class iommu_sysclass = { - .name = "iommu", +static struct syscore_ops iommu_syscore_ops = { .resume = iommu_resume, .suspend = iommu_suspend, }; -static struct sys_device device_iommu = { - .cls = &iommu_sysclass, -}; - -static int __init init_iommu_sysfs(void) +static void __init init_iommu_pm_ops(void) { - int error; - - error = sysdev_class_register(&iommu_sysclass); - if (error) - return error; - - error = sysdev_register(&device_iommu); - if (error) - sysdev_class_unregister(&iommu_sysclass); - - return error; + register_syscore_ops(&iommu_syscore_ops); } #else -static int __init init_iommu_sysfs(void) -{ - return 0; -} +static inline int init_iommu_pm_ops(void) { } #endif /* CONFIG_PM */ /* @@ -3320,7 +3300,7 @@ int __init intel_iommu_init(void) #endif dma_ops = &intel_dma_ops; - init_iommu_sysfs(); + init_iommu_pm_ops(); register_iommu(&intel_iommu_ops); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b714d78..2472e71 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -740,6 +740,12 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (!__pci_complete_power_transition(dev, state)) error = 0; + /* + * When aspm_policy is "powersave" this call ensures + * that ASPM is configured. + */ + if (!error && dev->bus->self) + pcie_aspm_powersave_config_link(dev->bus->self); return error; } diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 3188cd9..eee09f7 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -69,6 +69,7 @@ struct pcie_link_state { }; static int aspm_disabled, aspm_force, aspm_clear_state; +static bool aspm_support_enabled = true; static DEFINE_MUTEX(aspm_lock); static LIST_HEAD(link_list); @@ -707,6 +708,28 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev) up_read(&pci_bus_sem); } +void pcie_aspm_powersave_config_link(struct pci_dev *pdev) +{ + struct pcie_link_state *link = pdev->link_state; + + if (aspm_disabled || !pci_is_pcie(pdev) || !link) + return; + + if (aspm_policy != POLICY_POWERSAVE) + return; + + if ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && + (pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)) + return; + + down_read(&pci_bus_sem); + mutex_lock(&aspm_lock); + pcie_config_aspm_path(link); + pcie_set_clkpm(link, policy_to_clkpm_state(link)); + mutex_unlock(&aspm_lock); + up_read(&pci_bus_sem); +} + /* * pci_disable_link_state - disable pci device's link state, so the link will * never enter specific states @@ -747,6 +770,8 @@ static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) int i; struct pcie_link_state *link; + if (aspm_disabled) + return -EPERM; for (i = 0; i < ARRAY_SIZE(policy_str); i++) if (!strncmp(val, policy_str[i], strlen(policy_str[i]))) break; @@ -801,6 +826,8 @@ static ssize_t link_state_store(struct device *dev, struct pcie_link_state *link, *root = pdev->link_state->root; u32 val = buf[0] - '0', state = 0; + if (aspm_disabled) + return -EPERM; if (n < 1 || val > 3) return -EINVAL; @@ -896,6 +923,7 @@ static int __init pcie_aspm_disable(char *str) { if (!strcmp(str, "off")) { aspm_disabled = 1; + aspm_support_enabled = false; printk(KERN_INFO "PCIe ASPM is disabled\n"); } else if (!strcmp(str, "force")) { aspm_force = 1; @@ -930,3 +958,8 @@ int pcie_aspm_enabled(void) } EXPORT_SYMBOL(pcie_aspm_enabled); +bool pcie_aspm_support_enabled(void) +{ + return aspm_support_enabled; +} +EXPORT_SYMBOL(pcie_aspm_support_enabled); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 5130d0d..595654a 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -15,7 +15,6 @@ #include <linux/slab.h> #include <linux/pcieport_if.h> #include <linux/aer.h> -#include <linux/pci-aspm.h> #include "../pci.h" #include "portdrv.h" @@ -356,10 +355,8 @@ int pcie_port_device_register(struct pci_dev *dev) /* Get and check PCI Express port services */ capabilities = get_port_device_capability(dev); - if (!capabilities) { - pcie_no_aspm(); + if (!capabilities) return 0; - } pci_set_master(dev); /* diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 61bf5d7..52a462f 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -117,10 +117,24 @@ config BATTERY_BQ20Z75 config BATTERY_BQ27x00 tristate "BQ27x00 battery driver" + help + Say Y here to enable support for batteries with BQ27x00 (I2C/HDQ) chips. + +config BATTERY_BQ27X00_I2C + bool "BQ27200/BQ27500 support" + depends on BATTERY_BQ27x00 depends on I2C + default y help Say Y here to enable support for batteries with BQ27x00 (I2C) chips. +config BATTERY_BQ27X00_PLATFORM + bool "BQ27000 support" + depends on BATTERY_BQ27x00 + default y + help + Say Y here to enable support for batteries with BQ27000 (HDQ) chips. + config BATTERY_DA9030 tristate "DA9030 battery driver" depends on PMIC_DA903X diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c index 492da27..506585e 100644 --- a/drivers/power/bq20z75.c +++ b/drivers/power/bq20z75.c @@ -25,6 +25,10 @@ #include <linux/power_supply.h> #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> + +#include <linux/power/bq20z75.h> enum { REG_MANUFACTURER_DATA, @@ -38,11 +42,22 @@ enum { REG_CYCLE_COUNT, REG_SERIAL_NUMBER, REG_REMAINING_CAPACITY, + REG_REMAINING_CAPACITY_CHARGE, REG_FULL_CHARGE_CAPACITY, + REG_FULL_CHARGE_CAPACITY_CHARGE, REG_DESIGN_CAPACITY, + REG_DESIGN_CAPACITY_CHARGE, REG_DESIGN_VOLTAGE, }; +/* Battery Mode defines */ +#define BATTERY_MODE_OFFSET 0x03 +#define BATTERY_MODE_MASK 0x8000 +enum bq20z75_battery_mode { + BATTERY_MODE_AMPS, + BATTERY_MODE_WATTS +}; + /* manufacturer access defines */ #define MANUFACTURER_ACCESS_STATUS 0x0006 #define MANUFACTURER_ACCESS_SLEEP 0x0011 @@ -78,8 +93,12 @@ static const struct bq20z75_device_data { BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100), [REG_REMAINING_CAPACITY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535), + [REG_REMAINING_CAPACITY_CHARGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535), [REG_FULL_CHARGE_CAPACITY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535), + [REG_FULL_CHARGE_CAPACITY_CHARGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535), [REG_TIME_TO_EMPTY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535), @@ -93,6 +112,9 @@ static const struct bq20z75_device_data { [REG_DESIGN_CAPACITY] = BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, 65535), + [REG_DESIGN_CAPACITY_CHARGE] = + BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, + 65535), [REG_DESIGN_VOLTAGE] = BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535), @@ -117,39 +139,72 @@ static enum power_supply_property bq20z75_properties[] = { POWER_SUPPLY_PROP_ENERGY_NOW, POWER_SUPPLY_PROP_ENERGY_FULL, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, }; struct bq20z75_info { - struct i2c_client *client; - struct power_supply power_supply; + struct i2c_client *client; + struct power_supply power_supply; + struct bq20z75_platform_data *pdata; + bool is_present; + bool gpio_detect; + bool enable_detection; + int irq; }; static int bq20z75_read_word_data(struct i2c_client *client, u8 address) { - s32 ret; + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + s32 ret = 0; + int retries = 1; + + if (bq20z75_device->pdata) + retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1); + + while (retries > 0) { + ret = i2c_smbus_read_word_data(client, address); + if (ret >= 0) + break; + retries--; + } - ret = i2c_smbus_read_word_data(client, address); if (ret < 0) { - dev_err(&client->dev, + dev_dbg(&client->dev, "%s: i2c read at address 0x%x failed\n", __func__, address); return ret; } + return le16_to_cpu(ret); } static int bq20z75_write_word_data(struct i2c_client *client, u8 address, u16 value) { - s32 ret; + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + s32 ret = 0; + int retries = 1; + + if (bq20z75_device->pdata) + retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1); + + while (retries > 0) { + ret = i2c_smbus_write_word_data(client, address, + le16_to_cpu(value)); + if (ret >= 0) + break; + retries--; + } - ret = i2c_smbus_write_word_data(client, address, le16_to_cpu(value)); if (ret < 0) { - dev_err(&client->dev, + dev_dbg(&client->dev, "%s: i2c write to address 0x%x failed\n", __func__, address); return ret; } + return 0; } @@ -158,6 +213,19 @@ static int bq20z75_get_battery_presence_and_health( union power_supply_propval *val) { s32 ret; + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + + if (psp == POWER_SUPPLY_PROP_PRESENT && + bq20z75_device->gpio_detect) { + ret = gpio_get_value( + bq20z75_device->pdata->battery_detect); + if (ret == bq20z75_device->pdata->battery_detect_present) + val->intval = 1; + else + val->intval = 0; + bq20z75_device->is_present = val->intval; + return ret; + } /* Write to ManufacturerAccess with * ManufacturerAccess command and then @@ -165,9 +233,11 @@ static int bq20z75_get_battery_presence_and_health( ret = bq20z75_write_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr, MANUFACTURER_ACCESS_STATUS); - if (ret < 0) + if (ret < 0) { + if (psp == POWER_SUPPLY_PROP_PRESENT) + val->intval = 0; /* battery removed */ return ret; - + } ret = bq20z75_read_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr); @@ -248,30 +318,39 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, { #define BASE_UNIT_CONVERSION 1000 #define BATTERY_MODE_CAP_MULT_WATT (10 * BASE_UNIT_CONVERSION) -#define TIME_UNIT_CONVERSION 600 -#define TEMP_KELVIN_TO_CELCIUS 2731 +#define TIME_UNIT_CONVERSION 60 +#define TEMP_KELVIN_TO_CELSIUS 2731 switch (psp) { case POWER_SUPPLY_PROP_ENERGY_NOW: case POWER_SUPPLY_PROP_ENERGY_FULL: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + /* bq20z75 provides energy in units of 10mWh. + * Convert to µWh + */ val->intval *= BATTERY_MODE_CAP_MULT_WATT; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: val->intval *= BASE_UNIT_CONVERSION; break; case POWER_SUPPLY_PROP_TEMP: - /* bq20z75 provides battery tempreture in 0.1°K - * so convert it to 0.1°C */ - val->intval -= TEMP_KELVIN_TO_CELCIUS; - val->intval *= 10; + /* bq20z75 provides battery temperature in 0.1K + * so convert it to 0.1°C + */ + val->intval -= TEMP_KELVIN_TO_CELSIUS; break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + /* bq20z75 provides time to empty and time to full in minutes. + * Convert to seconds + */ val->intval *= TIME_UNIT_CONVERSION; break; @@ -281,11 +360,44 @@ static void bq20z75_unit_adjustment(struct i2c_client *client, } } +static enum bq20z75_battery_mode +bq20z75_set_battery_mode(struct i2c_client *client, + enum bq20z75_battery_mode mode) +{ + int ret, original_val; + + original_val = bq20z75_read_word_data(client, BATTERY_MODE_OFFSET); + if (original_val < 0) + return original_val; + + if ((original_val & BATTERY_MODE_MASK) == mode) + return mode; + + if (mode == BATTERY_MODE_AMPS) + ret = original_val & ~BATTERY_MODE_MASK; + else + ret = original_val | BATTERY_MODE_MASK; + + ret = bq20z75_write_word_data(client, BATTERY_MODE_OFFSET, ret); + if (ret < 0) + return ret; + + return original_val & BATTERY_MODE_MASK; +} + static int bq20z75_get_battery_capacity(struct i2c_client *client, int reg_offset, enum power_supply_property psp, union power_supply_propval *val) { s32 ret; + enum bq20z75_battery_mode mode = BATTERY_MODE_WATTS; + + if (power_supply_is_amp_property(psp)) + mode = BATTERY_MODE_AMPS; + + mode = bq20z75_set_battery_mode(client, mode); + if (mode < 0) + return mode; ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr); if (ret < 0) @@ -298,6 +410,10 @@ static int bq20z75_get_battery_capacity(struct i2c_client *client, } else val->intval = ret; + ret = bq20z75_set_battery_mode(client, mode); + if (ret < 0) + return ret; + return 0; } @@ -318,12 +434,25 @@ static int bq20z75_get_battery_serial_number(struct i2c_client *client, return 0; } +static int bq20z75_get_property_index(struct i2c_client *client, + enum power_supply_property psp) +{ + int count; + for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) + if (psp == bq20z75_data[count].psp) + return count; + + dev_warn(&client->dev, + "%s: Invalid Property - %d\n", __func__, psp); + + return -EINVAL; +} + static int bq20z75_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - int count; - int ret; + int ret = 0; struct bq20z75_info *bq20z75_device = container_of(psy, struct bq20z75_info, power_supply); struct i2c_client *client = bq20z75_device->client; @@ -332,8 +461,8 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_HEALTH: ret = bq20z75_get_battery_presence_and_health(client, psp, val); - if (ret) - return ret; + if (psp == POWER_SUPPLY_PROP_PRESENT) + return 0; break; case POWER_SUPPLY_PROP_TECHNOLOGY: @@ -343,22 +472,19 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_ENERGY_NOW: case POWER_SUPPLY_PROP_ENERGY_FULL: case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_NOW: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: case POWER_SUPPLY_PROP_CAPACITY: - for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { - if (psp == bq20z75_data[count].psp) - break; - } - - ret = bq20z75_get_battery_capacity(client, count, psp, val); - if (ret) - return ret; + ret = bq20z75_get_property_index(client, psp); + if (ret < 0) + break; + ret = bq20z75_get_battery_capacity(client, ret, psp, val); break; case POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = bq20z75_get_battery_serial_number(client, val); - if (ret) - return ret; break; case POWER_SUPPLY_PROP_STATUS: @@ -369,15 +495,11 @@ static int bq20z75_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++) { - if (psp == bq20z75_data[count].psp) - break; - } - - ret = bq20z75_get_battery_property(client, count, psp, val); - if (ret) - return ret; + ret = bq20z75_get_property_index(client, psp); + if (ret < 0) + break; + ret = bq20z75_get_battery_property(client, ret, psp, val); break; default: @@ -386,26 +508,58 @@ static int bq20z75_get_property(struct power_supply *psy, return -EINVAL; } - /* Convert units to match requirements for power supply class */ - bq20z75_unit_adjustment(client, psp, val); + if (!bq20z75_device->enable_detection) + goto done; + + if (!bq20z75_device->gpio_detect && + bq20z75_device->is_present != (ret >= 0)) { + bq20z75_device->is_present = (ret >= 0); + power_supply_changed(&bq20z75_device->power_supply); + } + +done: + if (!ret) { + /* Convert units to match requirements for power supply class */ + bq20z75_unit_adjustment(client, psp, val); + } dev_dbg(&client->dev, - "%s: property = %d, value = %d\n", __func__, psp, val->intval); + "%s: property = %d, value = %x\n", __func__, psp, val->intval); + + if (ret && bq20z75_device->is_present) + return ret; + + /* battery not present, so return NODATA for properties */ + if (ret) + return -ENODATA; return 0; } -static int bq20z75_probe(struct i2c_client *client, +static irqreturn_t bq20z75_irq(int irq, void *devid) +{ + struct power_supply *battery = devid; + + power_supply_changed(battery); + + return IRQ_HANDLED; +} + +static int __devinit bq20z75_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bq20z75_info *bq20z75_device; + struct bq20z75_platform_data *pdata = client->dev.platform_data; int rc; + int irq; bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); if (!bq20z75_device) return -ENOMEM; bq20z75_device->client = client; + bq20z75_device->enable_detection = false; + bq20z75_device->gpio_detect = false; bq20z75_device->power_supply.name = "battery"; bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; bq20z75_device->power_supply.properties = bq20z75_properties; @@ -413,26 +567,86 @@ static int bq20z75_probe(struct i2c_client *client, ARRAY_SIZE(bq20z75_properties); bq20z75_device->power_supply.get_property = bq20z75_get_property; + if (pdata) { + bq20z75_device->gpio_detect = + gpio_is_valid(pdata->battery_detect); + bq20z75_device->pdata = pdata; + } + i2c_set_clientdata(client, bq20z75_device); + if (!bq20z75_device->gpio_detect) + goto skip_gpio; + + rc = gpio_request(pdata->battery_detect, dev_name(&client->dev)); + if (rc) { + dev_warn(&client->dev, "Failed to request gpio: %d\n", rc); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + rc = gpio_direction_input(pdata->battery_detect); + if (rc) { + dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc); + gpio_free(pdata->battery_detect); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + irq = gpio_to_irq(pdata->battery_detect); + if (irq <= 0) { + dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq); + gpio_free(pdata->battery_detect); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + rc = request_irq(irq, bq20z75_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + dev_name(&client->dev), &bq20z75_device->power_supply); + if (rc) { + dev_warn(&client->dev, "Failed to request irq: %d\n", rc); + gpio_free(pdata->battery_detect); + bq20z75_device->gpio_detect = false; + goto skip_gpio; + } + + bq20z75_device->irq = irq; + +skip_gpio: + rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); if (rc) { dev_err(&client->dev, "%s: Failed to register power supply\n", __func__); - kfree(bq20z75_device); - return rc; + goto exit_psupply; } dev_info(&client->dev, "%s: battery gas gauge device registered\n", client->name); return 0; + +exit_psupply: + if (bq20z75_device->irq) + free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); + if (bq20z75_device->gpio_detect) + gpio_free(pdata->battery_detect); + + kfree(bq20z75_device); + + return rc; } -static int bq20z75_remove(struct i2c_client *client) +static int __devexit bq20z75_remove(struct i2c_client *client) { struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); + if (bq20z75_device->irq) + free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); + if (bq20z75_device->gpio_detect) + gpio_free(bq20z75_device->pdata->battery_detect); + power_supply_unregister(&bq20z75_device->power_supply); kfree(bq20z75_device); bq20z75_device = NULL; @@ -444,13 +658,14 @@ static int bq20z75_remove(struct i2c_client *client) static int bq20z75_suspend(struct i2c_client *client, pm_message_t state) { + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); s32 ret; /* write to manufacturer access with sleep command */ ret = bq20z75_write_word_data(client, bq20z75_data[REG_MANUFACTURER_DATA].addr, MANUFACTURER_ACCESS_SLEEP); - if (ret < 0) + if (bq20z75_device->is_present && ret < 0) return ret; return 0; @@ -465,10 +680,11 @@ static const struct i2c_device_id bq20z75_id[] = { { "bq20z75", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, bq20z75_id); static struct i2c_driver bq20z75_battery_driver = { .probe = bq20z75_probe, - .remove = bq20z75_remove, + .remove = __devexit_p(bq20z75_remove), .suspend = bq20z75_suspend, .resume = bq20z75_resume, .id_table = bq20z75_id, diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index eff0273..59e68db 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -3,6 +3,7 @@ * * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it> + * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de> * * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. * @@ -15,6 +16,13 @@ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * */ + +/* + * Datasheets: + * http://focus.ti.com/docs/prod/folders/print/bq27000.html + * http://focus.ti.com/docs/prod/folders/print/bq27500.html + */ + #include <linux/module.h> #include <linux/param.h> #include <linux/jiffies.h> @@ -27,7 +35,9 @@ #include <linux/slab.h> #include <asm/unaligned.h> -#define DRIVER_VERSION "1.1.0" +#include <linux/power/bq27x00_battery.h> + +#define DRIVER_VERSION "1.2.0" #define BQ27x00_REG_TEMP 0x06 #define BQ27x00_REG_VOLT 0x08 @@ -36,36 +46,59 @@ #define BQ27x00_REG_TTE 0x16 #define BQ27x00_REG_TTF 0x18 #define BQ27x00_REG_TTECP 0x26 +#define BQ27x00_REG_NAC 0x0C /* Nominal available capaciy */ +#define BQ27x00_REG_LMD 0x12 /* Last measured discharge */ +#define BQ27x00_REG_CYCT 0x2A /* Cycle count total */ +#define BQ27x00_REG_AE 0x22 /* Available enery */ #define BQ27000_REG_RSOC 0x0B /* Relative State-of-Charge */ +#define BQ27000_REG_ILMD 0x76 /* Initial last measured discharge */ #define BQ27000_FLAG_CHGS BIT(7) +#define BQ27000_FLAG_FC BIT(5) -#define BQ27500_REG_SOC 0x2c +#define BQ27500_REG_SOC 0x2C +#define BQ27500_REG_DCAP 0x3C /* Design capacity */ #define BQ27500_FLAG_DSC BIT(0) #define BQ27500_FLAG_FC BIT(9) -/* If the system has several batteries we need a different name for each - * of them... - */ -static DEFINE_IDR(battery_id); -static DEFINE_MUTEX(battery_mutex); +#define BQ27000_RS 20 /* Resistor sense */ struct bq27x00_device_info; struct bq27x00_access_methods { - int (*read)(u8 reg, int *rt_value, int b_single, - struct bq27x00_device_info *di); + int (*read)(struct bq27x00_device_info *di, u8 reg, bool single); }; enum bq27x00_chip { BQ27000, BQ27500 }; +struct bq27x00_reg_cache { + int temperature; + int time_to_empty; + int time_to_empty_avg; + int time_to_full; + int charge_full; + int charge_counter; + int capacity; + int flags; + + int current_now; +}; + struct bq27x00_device_info { struct device *dev; int id; - struct bq27x00_access_methods *bus; - struct power_supply bat; enum bq27x00_chip chip; - struct i2c_client *client; + struct bq27x00_reg_cache cache; + int charge_design_full; + + unsigned long last_update; + struct delayed_work work; + + struct power_supply bat; + + struct bq27x00_access_methods bus; + + struct mutex lock; }; static enum power_supply_property bq27x00_battery_props[] = { @@ -78,164 +111,328 @@ static enum power_supply_property bq27x00_battery_props[] = { POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_ENERGY_NOW, }; +static unsigned int poll_interval = 360; +module_param(poll_interval, uint, 0644); +MODULE_PARM_DESC(poll_interval, "battery poll interval in seconds - " \ + "0 disables polling"); + /* * Common code for BQ27x00 devices */ -static int bq27x00_read(u8 reg, int *rt_value, int b_single, - struct bq27x00_device_info *di) +static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg, + bool single) { - return di->bus->read(reg, rt_value, b_single, di); + return di->bus.read(di, reg, single); } /* - * Return the battery temperature in tenths of degree Celsius + * Return the battery Relative State-of-Charge * Or < 0 if something fails. */ -static int bq27x00_battery_temperature(struct bq27x00_device_info *di) +static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di) { - int ret; - int temp = 0; + int rsoc; - ret = bq27x00_read(BQ27x00_REG_TEMP, &temp, 0, di); - if (ret) { - dev_err(di->dev, "error reading temperature\n"); - return ret; + if (di->chip == BQ27500) + rsoc = bq27x00_read(di, BQ27500_REG_SOC, false); + else + rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true); + + if (rsoc < 0) + dev_err(di->dev, "error reading relative State-of-Charge\n"); + + return rsoc; +} + +/* + * Return a battery charge value in µAh + * Or < 0 if something fails. + */ +static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg) +{ + int charge; + + charge = bq27x00_read(di, reg, false); + if (charge < 0) { + dev_err(di->dev, "error reading nominal available capacity\n"); + return charge; } if (di->chip == BQ27500) - return temp - 2731; + charge *= 1000; else - return ((temp >> 2) - 273) * 10; + charge = charge * 3570 / BQ27000_RS; + + return charge; } /* - * Return the battery Voltage in milivolts + * Return the battery Nominal available capaciy in µAh * Or < 0 if something fails. */ -static int bq27x00_battery_voltage(struct bq27x00_device_info *di) +static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di) { - int ret; - int volt = 0; + return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC); +} - ret = bq27x00_read(BQ27x00_REG_VOLT, &volt, 0, di); - if (ret) { - dev_err(di->dev, "error reading voltage\n"); - return ret; +/* + * Return the battery Last measured discharge in µAh + * Or < 0 if something fails. + */ +static inline int bq27x00_battery_read_lmd(struct bq27x00_device_info *di) +{ + return bq27x00_battery_read_charge(di, BQ27x00_REG_LMD); +} + +/* + * Return the battery Initial last measured discharge in µAh + * Or < 0 if something fails. + */ +static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) +{ + int ilmd; + + if (di->chip == BQ27500) + ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); + else + ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); + + if (ilmd < 0) { + dev_err(di->dev, "error reading initial last measured discharge\n"); + return ilmd; } - return volt * 1000; + if (di->chip == BQ27500) + ilmd *= 1000; + else + ilmd = ilmd * 256 * 3570 / BQ27000_RS; + + return ilmd; } /* - * Return the battery average current - * Note that current can be negative signed as well - * Or 0 if something fails. + * Return the battery Cycle count total + * Or < 0 if something fails. */ -static int bq27x00_battery_current(struct bq27x00_device_info *di) +static int bq27x00_battery_read_cyct(struct bq27x00_device_info *di) { - int ret; - int curr = 0; - int flags = 0; + int cyct; - ret = bq27x00_read(BQ27x00_REG_AI, &curr, 0, di); - if (ret) { - dev_err(di->dev, "error reading current\n"); - return 0; + cyct = bq27x00_read(di, BQ27x00_REG_CYCT, false); + if (cyct < 0) + dev_err(di->dev, "error reading cycle count total\n"); + + return cyct; +} + +/* + * Read a time register. + * Return < 0 if something fails. + */ +static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg) +{ + int tval; + + tval = bq27x00_read(di, reg, false); + if (tval < 0) { + dev_err(di->dev, "error reading register %02x: %d\n", reg, tval); + return tval; } - if (di->chip == BQ27500) { - /* bq27500 returns signed value */ - curr = (int)(s16)curr; - } else { - ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di); - if (ret < 0) { - dev_err(di->dev, "error reading flags\n"); - return 0; - } - if (flags & BQ27000_FLAG_CHGS) { - dev_dbg(di->dev, "negative current!\n"); - curr = -curr; - } + if (tval == 65535) + return -ENODATA; + + return tval * 60; +} + +static void bq27x00_update(struct bq27x00_device_info *di) +{ + struct bq27x00_reg_cache cache = {0, }; + bool is_bq27500 = di->chip == BQ27500; + + cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, is_bq27500); + if (cache.flags >= 0) { + cache.capacity = bq27x00_battery_read_rsoc(di); + cache.temperature = bq27x00_read(di, BQ27x00_REG_TEMP, false); + cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE); + cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP); + cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF); + cache.charge_full = bq27x00_battery_read_lmd(di); + cache.charge_counter = bq27x00_battery_read_cyct(di); + + if (!is_bq27500) + cache.current_now = bq27x00_read(di, BQ27x00_REG_AI, false); + + /* We only have to read charge design full once */ + if (di->charge_design_full <= 0) + di->charge_design_full = bq27x00_battery_read_ilmd(di); + } + + /* Ignore current_now which is a snapshot of the current battery state + * and is likely to be different even between two consecutive reads */ + if (memcmp(&di->cache, &cache, sizeof(cache) - sizeof(int)) != 0) { + di->cache = cache; + power_supply_changed(&di->bat); } - return curr * 1000; + di->last_update = jiffies; +} + +static void bq27x00_battery_poll(struct work_struct *work) +{ + struct bq27x00_device_info *di = + container_of(work, struct bq27x00_device_info, work.work); + + bq27x00_update(di); + + if (poll_interval > 0) { + /* The timer does not have to be accurate. */ + set_timer_slack(&di->work.timer, poll_interval * HZ / 4); + schedule_delayed_work(&di->work, poll_interval * HZ); + } } + /* - * Return the battery Relative State-of-Charge + * Return the battery temperature in tenths of degree Celsius * Or < 0 if something fails. */ -static int bq27x00_battery_rsoc(struct bq27x00_device_info *di) +static int bq27x00_battery_temperature(struct bq27x00_device_info *di, + union power_supply_propval *val) { - int ret; - int rsoc = 0; + if (di->cache.temperature < 0) + return di->cache.temperature; if (di->chip == BQ27500) - ret = bq27x00_read(BQ27500_REG_SOC, &rsoc, 0, di); + val->intval = di->cache.temperature - 2731; else - ret = bq27x00_read(BQ27000_REG_RSOC, &rsoc, 1, di); - if (ret) { - dev_err(di->dev, "error reading relative State-of-Charge\n"); - return ret; + val->intval = ((di->cache.temperature * 5) - 5463) / 2; + + return 0; +} + +/* + * Return the battery average current in µA + * Note that current can be negative signed as well + * Or 0 if something fails. + */ +static int bq27x00_battery_current(struct bq27x00_device_info *di, + union power_supply_propval *val) +{ + int curr; + + if (di->chip == BQ27500) + curr = bq27x00_read(di, BQ27x00_REG_AI, false); + else + curr = di->cache.current_now; + + if (curr < 0) + return curr; + + if (di->chip == BQ27500) { + /* bq27500 returns signed value */ + val->intval = (int)((s16)curr) * 1000; + } else { + if (di->cache.flags & BQ27000_FLAG_CHGS) { + dev_dbg(di->dev, "negative current!\n"); + curr = -curr; + } + + val->intval = curr * 3570 / BQ27000_RS; } - return rsoc; + return 0; } static int bq27x00_battery_status(struct bq27x00_device_info *di, - union power_supply_propval *val) + union power_supply_propval *val) { - int flags = 0; int status; - int ret; - - ret = bq27x00_read(BQ27x00_REG_FLAGS, &flags, 0, di); - if (ret < 0) { - dev_err(di->dev, "error reading flags\n"); - return ret; - } if (di->chip == BQ27500) { - if (flags & BQ27500_FLAG_FC) + if (di->cache.flags & BQ27500_FLAG_FC) status = POWER_SUPPLY_STATUS_FULL; - else if (flags & BQ27500_FLAG_DSC) + else if (di->cache.flags & BQ27500_FLAG_DSC) status = POWER_SUPPLY_STATUS_DISCHARGING; else status = POWER_SUPPLY_STATUS_CHARGING; } else { - if (flags & BQ27000_FLAG_CHGS) + if (di->cache.flags & BQ27000_FLAG_FC) + status = POWER_SUPPLY_STATUS_FULL; + else if (di->cache.flags & BQ27000_FLAG_CHGS) status = POWER_SUPPLY_STATUS_CHARGING; + else if (power_supply_am_i_supplied(&di->bat)) + status = POWER_SUPPLY_STATUS_NOT_CHARGING; else status = POWER_SUPPLY_STATUS_DISCHARGING; } val->intval = status; + return 0; } /* - * Read a time register. - * Return < 0 if something fails. + * Return the battery Voltage in milivolts + * Or < 0 if something fails. */ -static int bq27x00_battery_time(struct bq27x00_device_info *di, int reg, - union power_supply_propval *val) +static int bq27x00_battery_voltage(struct bq27x00_device_info *di, + union power_supply_propval *val) { - int tval = 0; - int ret; + int volt; - ret = bq27x00_read(reg, &tval, 0, di); - if (ret) { - dev_err(di->dev, "error reading register %02x\n", reg); - return ret; + volt = bq27x00_read(di, BQ27x00_REG_VOLT, false); + if (volt < 0) + return volt; + + val->intval = volt * 1000; + + return 0; +} + +/* + * Return the battery Available energy in µWh + * Or < 0 if something fails. + */ +static int bq27x00_battery_energy(struct bq27x00_device_info *di, + union power_supply_propval *val) +{ + int ae; + + ae = bq27x00_read(di, BQ27x00_REG_AE, false); + if (ae < 0) { + dev_err(di->dev, "error reading available energy\n"); + return ae; } - if (tval == 65535) - return -ENODATA; + if (di->chip == BQ27500) + ae *= 1000; + else + ae = ae * 29200 / BQ27000_RS; + + val->intval = ae; + + return 0; +} + + +static int bq27x00_simple_value(int value, + union power_supply_propval *val) +{ + if (value < 0) + return value; + + val->intval = value; - val->intval = tval * 60; return 0; } @@ -249,33 +446,61 @@ static int bq27x00_battery_get_property(struct power_supply *psy, int ret = 0; struct bq27x00_device_info *di = to_bq27x00_device_info(psy); + mutex_lock(&di->lock); + if (time_is_before_jiffies(di->last_update + 5 * HZ)) { + cancel_delayed_work_sync(&di->work); + bq27x00_battery_poll(&di->work.work); + } + mutex_unlock(&di->lock); + + if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0) + return -ENODEV; + switch (psp) { case POWER_SUPPLY_PROP_STATUS: ret = bq27x00_battery_status(di, val); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq27x00_battery_voltage(di, val); + break; case POWER_SUPPLY_PROP_PRESENT: - val->intval = bq27x00_battery_voltage(di); - if (psp == POWER_SUPPLY_PROP_PRESENT) - val->intval = val->intval <= 0 ? 0 : 1; + val->intval = di->cache.flags < 0 ? 0 : 1; break; case POWER_SUPPLY_PROP_CURRENT_NOW: - val->intval = bq27x00_battery_current(di); + ret = bq27x00_battery_current(di, val); break; case POWER_SUPPLY_PROP_CAPACITY: - val->intval = bq27x00_battery_rsoc(di); + ret = bq27x00_simple_value(di->cache.capacity, val); break; case POWER_SUPPLY_PROP_TEMP: - val->intval = bq27x00_battery_temperature(di); + ret = bq27x00_battery_temperature(di, val); break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: - ret = bq27x00_battery_time(di, BQ27x00_REG_TTE, val); + ret = bq27x00_simple_value(di->cache.time_to_empty, val); break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: - ret = bq27x00_battery_time(di, BQ27x00_REG_TTECP, val); + ret = bq27x00_simple_value(di->cache.time_to_empty_avg, val); break; case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: - ret = bq27x00_battery_time(di, BQ27x00_REG_TTF, val); + ret = bq27x00_simple_value(di->cache.time_to_full, val); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = bq27x00_simple_value(bq27x00_battery_read_nac(di), val); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = bq27x00_simple_value(di->cache.charge_full, val); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = bq27x00_simple_value(di->charge_design_full, val); + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = bq27x00_simple_value(di->cache.charge_counter, val); + break; + case POWER_SUPPLY_PROP_ENERGY_NOW: + ret = bq27x00_battery_energy(di, val); break; default: return -EINVAL; @@ -284,56 +509,91 @@ static int bq27x00_battery_get_property(struct power_supply *psy, return ret; } -static void bq27x00_powersupply_init(struct bq27x00_device_info *di) +static void bq27x00_external_power_changed(struct power_supply *psy) { + struct bq27x00_device_info *di = to_bq27x00_device_info(psy); + + cancel_delayed_work_sync(&di->work); + schedule_delayed_work(&di->work, 0); +} + +static int bq27x00_powersupply_init(struct bq27x00_device_info *di) +{ + int ret; + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; di->bat.properties = bq27x00_battery_props; di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props); di->bat.get_property = bq27x00_battery_get_property; - di->bat.external_power_changed = NULL; + di->bat.external_power_changed = bq27x00_external_power_changed; + + INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll); + mutex_init(&di->lock); + + ret = power_supply_register(di->dev, &di->bat); + if (ret) { + dev_err(di->dev, "failed to register battery: %d\n", ret); + return ret; + } + + dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION); + + bq27x00_update(di); + + return 0; } -/* - * i2c specific code +static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di) +{ + cancel_delayed_work_sync(&di->work); + + power_supply_unregister(&di->bat); + + mutex_destroy(&di->lock); +} + + +/* i2c specific code */ +#ifdef CONFIG_BATTERY_BQ27X00_I2C + +/* If the system has several batteries we need a different name for each + * of them... */ +static DEFINE_IDR(battery_id); +static DEFINE_MUTEX(battery_mutex); -static int bq27x00_read_i2c(u8 reg, int *rt_value, int b_single, - struct bq27x00_device_info *di) +static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single) { - struct i2c_client *client = di->client; - struct i2c_msg msg[1]; + struct i2c_client *client = to_i2c_client(di->dev); + struct i2c_msg msg[2]; unsigned char data[2]; - int err; + int ret; if (!client->adapter) return -ENODEV; - msg->addr = client->addr; - msg->flags = 0; - msg->len = 1; - msg->buf = data; - - data[0] = reg; - err = i2c_transfer(client->adapter, msg, 1); + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].buf = ® + msg[0].len = sizeof(reg); + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = data; + if (single) + msg[1].len = 1; + else + msg[1].len = 2; - if (err >= 0) { - if (!b_single) - msg->len = 2; - else - msg->len = 1; + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) + return ret; - msg->flags = I2C_M_RD; - err = i2c_transfer(client->adapter, msg, 1); - if (err >= 0) { - if (!b_single) - *rt_value = get_unaligned_le16(data); - else - *rt_value = data[0]; + if (!single) + ret = get_unaligned_le16(data); + else + ret = data[0]; - return 0; - } - } - return err; + return ret; } static int bq27x00_battery_probe(struct i2c_client *client, @@ -341,7 +601,6 @@ static int bq27x00_battery_probe(struct i2c_client *client, { char *name; struct bq27x00_device_info *di; - struct bq27x00_access_methods *bus; int num; int retval = 0; @@ -368,38 +627,20 @@ static int bq27x00_battery_probe(struct i2c_client *client, retval = -ENOMEM; goto batt_failed_2; } + di->id = num; + di->dev = &client->dev; di->chip = id->driver_data; + di->bat.name = name; + di->bus.read = &bq27x00_read_i2c; - bus = kzalloc(sizeof(*bus), GFP_KERNEL); - if (!bus) { - dev_err(&client->dev, "failed to allocate access method " - "data\n"); - retval = -ENOMEM; + if (bq27x00_powersupply_init(di)) goto batt_failed_3; - } i2c_set_clientdata(client, di); - di->dev = &client->dev; - di->bat.name = name; - bus->read = &bq27x00_read_i2c; - di->bus = bus; - di->client = client; - - bq27x00_powersupply_init(di); - - retval = power_supply_register(&client->dev, &di->bat); - if (retval) { - dev_err(&client->dev, "failed to register battery\n"); - goto batt_failed_4; - } - - dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION); return 0; -batt_failed_4: - kfree(bus); batt_failed_3: kfree(di); batt_failed_2: @@ -416,9 +657,8 @@ static int bq27x00_battery_remove(struct i2c_client *client) { struct bq27x00_device_info *di = i2c_get_clientdata(client); - power_supply_unregister(&di->bat); + bq27x00_powersupply_unregister(di); - kfree(di->bus); kfree(di->bat.name); mutex_lock(&battery_mutex); @@ -430,15 +670,12 @@ static int bq27x00_battery_remove(struct i2c_client *client) return 0; } -/* - * Module stuff - */ - static const struct i2c_device_id bq27x00_id[] = { { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */ { "bq27500", BQ27500 }, {}, }; +MODULE_DEVICE_TABLE(i2c, bq27x00_id); static struct i2c_driver bq27x00_battery_driver = { .driver = { @@ -449,13 +686,164 @@ static struct i2c_driver bq27x00_battery_driver = { .id_table = bq27x00_id, }; +static inline int bq27x00_battery_i2c_init(void) +{ + int ret = i2c_add_driver(&bq27x00_battery_driver); + if (ret) + printk(KERN_ERR "Unable to register BQ27x00 i2c driver\n"); + + return ret; +} + +static inline void bq27x00_battery_i2c_exit(void) +{ + i2c_del_driver(&bq27x00_battery_driver); +} + +#else + +static inline int bq27x00_battery_i2c_init(void) { return 0; } +static inline void bq27x00_battery_i2c_exit(void) {}; + +#endif + +/* platform specific code */ +#ifdef CONFIG_BATTERY_BQ27X00_PLATFORM + +static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg, + bool single) +{ + struct device *dev = di->dev; + struct bq27000_platform_data *pdata = dev->platform_data; + unsigned int timeout = 3; + int upper, lower; + int temp; + + if (!single) { + /* Make sure the value has not changed in between reading the + * lower and the upper part */ + upper = pdata->read(dev, reg + 1); + do { + temp = upper; + if (upper < 0) + return upper; + + lower = pdata->read(dev, reg); + if (lower < 0) + return lower; + + upper = pdata->read(dev, reg + 1); + } while (temp != upper && --timeout); + + if (timeout == 0) + return -EIO; + + return (upper << 8) | lower; + } + + return pdata->read(dev, reg); +} + +static int __devinit bq27000_battery_probe(struct platform_device *pdev) +{ + struct bq27x00_device_info *di; + struct bq27000_platform_data *pdata = pdev->dev.platform_data; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "no platform_data supplied\n"); + return -EINVAL; + } + + if (!pdata->read) { + dev_err(&pdev->dev, "no hdq read callback supplied\n"); + return -EINVAL; + } + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) { + dev_err(&pdev->dev, "failed to allocate device info data\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, di); + + di->dev = &pdev->dev; + di->chip = BQ27000; + + di->bat.name = pdata->name ?: dev_name(&pdev->dev); + di->bus.read = &bq27000_read_platform; + + ret = bq27x00_powersupply_init(di); + if (ret) + goto err_free; + + return 0; + +err_free: + platform_set_drvdata(pdev, NULL); + kfree(di); + + return ret; +} + +static int __devexit bq27000_battery_remove(struct platform_device *pdev) +{ + struct bq27x00_device_info *di = platform_get_drvdata(pdev); + + bq27x00_powersupply_unregister(di); + + platform_set_drvdata(pdev, NULL); + kfree(di); + + return 0; +} + +static struct platform_driver bq27000_battery_driver = { + .probe = bq27000_battery_probe, + .remove = __devexit_p(bq27000_battery_remove), + .driver = { + .name = "bq27000-battery", + .owner = THIS_MODULE, + }, +}; + +static inline int bq27x00_battery_platform_init(void) +{ + int ret = platform_driver_register(&bq27000_battery_driver); + if (ret) + printk(KERN_ERR "Unable to register BQ27000 platform driver\n"); + + return ret; +} + +static inline void bq27x00_battery_platform_exit(void) +{ + platform_driver_unregister(&bq27000_battery_driver); +} + +#else + +static inline int bq27x00_battery_platform_init(void) { return 0; } +static inline void bq27x00_battery_platform_exit(void) {}; + +#endif + +/* + * Module stuff + */ + static int __init bq27x00_battery_init(void) { int ret; - ret = i2c_add_driver(&bq27x00_battery_driver); + ret = bq27x00_battery_i2c_init(); if (ret) - printk(KERN_ERR "Unable to register BQ27x00 driver\n"); + return ret; + + ret = bq27x00_battery_platform_init(); + if (ret) + bq27x00_battery_i2c_exit(); return ret; } @@ -463,7 +851,8 @@ module_init(bq27x00_battery_init); static void __exit bq27x00_battery_exit(void) { - i2c_del_driver(&bq27x00_battery_driver); + bq27x00_battery_platform_exit(); + bq27x00_battery_i2c_exit(); } module_exit(bq27x00_battery_exit); diff --git a/drivers/power/ds2782_battery.c b/drivers/power/ds2782_battery.c index 6957e8a..4d2dc4f 100644 --- a/drivers/power/ds2782_battery.c +++ b/drivers/power/ds2782_battery.c @@ -393,6 +393,7 @@ static const struct i2c_device_id ds278x_id[] = { {"ds2786", DS2786}, {}, }; +MODULE_DEVICE_TABLE(i2c, ds278x_id); static struct i2c_driver ds278x_battery_driver = { .driver = { diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 970f733..329b46b 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -171,6 +171,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy) dev_set_drvdata(dev, psy); psy->dev = dev; + INIT_WORK(&psy->changed_work, power_supply_changed_work); + rc = kobject_set_name(&dev->kobj, "%s", psy->name); if (rc) goto kobject_set_name_failed; @@ -179,8 +181,6 @@ int power_supply_register(struct device *parent, struct power_supply *psy) if (rc) goto device_add_failed; - INIT_WORK(&psy->changed_work, power_supply_changed_work); - rc = power_supply_create_triggers(psy); if (rc) goto create_triggers_failed; diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c index 031a554..da25eb9 100644 --- a/drivers/power/power_supply_leds.c +++ b/drivers/power/power_supply_leds.c @@ -21,6 +21,8 @@ static void power_supply_update_bat_leds(struct power_supply *psy) { union power_supply_propval status; + unsigned long delay_on = 0; + unsigned long delay_off = 0; if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) return; @@ -32,16 +34,22 @@ static void power_supply_update_bat_leds(struct power_supply *psy) led_trigger_event(psy->charging_full_trig, LED_FULL); led_trigger_event(psy->charging_trig, LED_OFF); led_trigger_event(psy->full_trig, LED_FULL); + led_trigger_event(psy->charging_blink_full_solid_trig, + LED_FULL); break; case POWER_SUPPLY_STATUS_CHARGING: led_trigger_event(psy->charging_full_trig, LED_FULL); led_trigger_event(psy->charging_trig, LED_FULL); led_trigger_event(psy->full_trig, LED_OFF); + led_trigger_blink(psy->charging_blink_full_solid_trig, + &delay_on, &delay_off); break; default: led_trigger_event(psy->charging_full_trig, LED_OFF); led_trigger_event(psy->charging_trig, LED_OFF); led_trigger_event(psy->full_trig, LED_OFF); + led_trigger_event(psy->charging_blink_full_solid_trig, + LED_OFF); break; } } @@ -64,15 +72,24 @@ static int power_supply_create_bat_triggers(struct power_supply *psy) if (!psy->full_trig_name) goto full_failed; + psy->charging_blink_full_solid_trig_name = kasprintf(GFP_KERNEL, + "%s-charging-blink-full-solid", psy->name); + if (!psy->charging_blink_full_solid_trig_name) + goto charging_blink_full_solid_failed; + led_trigger_register_simple(psy->charging_full_trig_name, &psy->charging_full_trig); led_trigger_register_simple(psy->charging_trig_name, &psy->charging_trig); led_trigger_register_simple(psy->full_trig_name, &psy->full_trig); + led_trigger_register_simple(psy->charging_blink_full_solid_trig_name, + &psy->charging_blink_full_solid_trig); goto success; +charging_blink_full_solid_failed: + kfree(psy->full_trig_name); full_failed: kfree(psy->charging_trig_name); charging_failed: @@ -88,6 +105,8 @@ static void power_supply_remove_bat_triggers(struct power_supply *psy) led_trigger_unregister_simple(psy->charging_full_trig); led_trigger_unregister_simple(psy->charging_trig); led_trigger_unregister_simple(psy->full_trig); + led_trigger_unregister_simple(psy->charging_blink_full_solid_trig); + kfree(psy->charging_blink_full_solid_trig_name); kfree(psy->full_trig_name); kfree(psy->charging_trig_name); kfree(psy->charging_full_trig_name); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index cd1f907..605514a 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -270,7 +270,7 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env) attr = &power_supply_attrs[psy->properties[j]]; ret = power_supply_show_property(dev, attr, prop_buf); - if (ret == -ENODEV) { + if (ret == -ENODEV || ret == -ENODATA) { /* When a battery is absent, we expect -ENODEV. Don't abort; send the uevent with at least the the PRESENT=0 property */ ret = 0; diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c index 4255f23..d36c289 100644 --- a/drivers/power/s3c_adc_battery.c +++ b/drivers/power/s3c_adc_battery.c @@ -406,8 +406,8 @@ static int s3c_adc_bat_resume(struct platform_device *pdev) return 0; } #else -#define s3c_adc_battery_suspend NULL -#define s3c_adc_battery_resume NULL +#define s3c_adc_bat_suspend NULL +#define s3c_adc_bat_resume NULL #endif static struct platform_driver s3c_adc_bat_driver = { diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index ff1f423..92c16e1 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -71,8 +71,11 @@ struct twl4030_bci { struct power_supply usb; struct otg_transceiver *transceiver; struct notifier_block otg_nb; + struct work_struct work; int irq_chg; int irq_bci; + + unsigned long event; }; /* @@ -258,14 +261,11 @@ static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) return IRQ_HANDLED; } -static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, - void *priv) +static void twl4030_bci_usb_work(struct work_struct *data) { - struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb); + struct twl4030_bci *bci = container_of(data, struct twl4030_bci, work); - dev_dbg(bci->dev, "OTG notify %lu\n", val); - - switch (val) { + switch (bci->event) { case USB_EVENT_VBUS: case USB_EVENT_CHARGER: twl4030_charger_enable_usb(bci, true); @@ -274,6 +274,17 @@ static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, twl4030_charger_enable_usb(bci, false); break; } +} + +static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, + void *priv) +{ + struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb); + + dev_dbg(bci->dev, "OTG notify %lu\n", val); + + bci->event = val; + schedule_work(&bci->work); return NOTIFY_OK; } @@ -466,6 +477,8 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) goto fail_bci_irq; } + INIT_WORK(&bci->work, twl4030_bci_usb_work); + bci->transceiver = otg_get_transceiver(); if (bci->transceiver != NULL) { bci->otg_nb.notifier_call = twl4030_bci_usb_ncb; diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c index e5ed52d..2a9ab89 100644 --- a/drivers/power/z2_battery.c +++ b/drivers/power/z2_battery.c @@ -134,6 +134,8 @@ static int z2_batt_ps_init(struct z2_charger *charger, int props) enum power_supply_property *prop; struct z2_battery_info *info = charger->info; + if (info->charge_gpio >= 0) + props++; /* POWER_SUPPLY_PROP_STATUS */ if (info->batt_tech >= 0) props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ if (info->batt_I2C_reg >= 0) @@ -293,6 +295,7 @@ static const struct i2c_device_id z2_batt_id[] = { { "aer915", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, z2_batt_id); static struct i2c_driver z2_batt_driver = { .driver = { diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 379d859..459f2cb 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -3982,8 +3982,10 @@ out_err: } static struct ccw_driver dasd_eckd_driver = { - .name = "dasd-eckd", - .owner = THIS_MODULE, + .driver = { + .name = "dasd-eckd", + .owner = THIS_MODULE, + }, .ids = dasd_eckd_ids, .probe = dasd_eckd_probe, .remove = dasd_generic_remove, diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index be89b3a..4b71b11 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -65,8 +65,10 @@ dasd_fba_set_online(struct ccw_device *cdev) } static struct ccw_driver dasd_fba_driver = { - .name = "dasd-fba", - .owner = THIS_MODULE, + .driver = { + .name = "dasd-fba", + .owner = THIS_MODULE, + }, .ids = dasd_fba_ids, .probe = dasd_fba_probe, .remove = dasd_generic_remove, diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 3fb4335..694464c 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -764,8 +764,10 @@ static struct ccw_device_id raw3215_id[] = { }; static struct ccw_driver raw3215_ccw_driver = { - .name = "3215", - .owner = THIS_MODULE, + .driver = { + .name = "3215", + .owner = THIS_MODULE, + }, .ids = raw3215_id, .probe = &raw3215_probe, .remove = &raw3215_remove, diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 96ba2fd..4c02376 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -1388,8 +1388,10 @@ static struct ccw_device_id raw3270_id[] = { }; static struct ccw_driver raw3270_ccw_driver = { - .name = "3270", - .owner = THIS_MODULE, + .driver = { + .name = "3270", + .owner = THIS_MODULE, + }, .ids = raw3270_id, .probe = &raw3270_probe, .remove = &raw3270_remove, diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c index c265111..9eff2df 100644 --- a/drivers/s390/char/tape_34xx.c +++ b/drivers/s390/char/tape_34xx.c @@ -1320,8 +1320,10 @@ tape_34xx_online(struct ccw_device *cdev) } static struct ccw_driver tape_34xx_driver = { - .name = "tape_34xx", - .owner = THIS_MODULE, + .driver = { + .name = "tape_34xx", + .owner = THIS_MODULE, + }, .ids = tape_34xx_ids, .probe = tape_generic_probe, .remove = tape_generic_remove, diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index de2e99e..b98dcbd 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -1761,8 +1761,10 @@ tape_3590_online(struct ccw_device *cdev) } static struct ccw_driver tape_3590_driver = { - .name = "tape_3590", - .owner = THIS_MODULE, + .driver = { + .name = "tape_3590", + .owner = THIS_MODULE, + }, .ids = tape_3590_ids, .probe = tape_generic_probe, .remove = tape_generic_remove, diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index caef175..f6b00c3 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -64,8 +64,10 @@ static int ur_set_offline(struct ccw_device *cdev); static int ur_pm_suspend(struct ccw_device *cdev); static struct ccw_driver ur_driver = { - .name = "vmur", - .owner = THIS_MODULE, + .driver = { + .name = "vmur", + .owner = THIS_MODULE, + }, .ids = ur_ids, .probe = ur_probe, .remove = ur_remove, diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 2864581..5c56741 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -428,7 +428,7 @@ ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const gdev = to_ccwgroupdev(dev); gdrv = to_ccwgroupdrv(dev->driver); - if (!try_module_get(gdrv->owner)) + if (!try_module_get(gdrv->driver.owner)) return -EINVAL; ret = strict_strtoul(buf, 0, &value); @@ -442,7 +442,7 @@ ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const else ret = -EINVAL; out: - module_put(gdrv->owner); + module_put(gdrv->driver.owner); return (ret == 0) ? count : ret; } @@ -616,8 +616,6 @@ int ccwgroup_driver_register(struct ccwgroup_driver *cdriver) { /* register our new driver with the core */ cdriver->driver.bus = &ccwgroup_bus_type; - cdriver->driver.name = cdriver->name; - cdriver->driver.owner = cdriver->owner; return driver_register(&cdriver->driver); } diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e50b121..df14c51 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -127,7 +127,7 @@ static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env) return ret; } -struct bus_type ccw_bus_type; +static struct bus_type ccw_bus_type; static void io_subchannel_irq(struct subchannel *); static int io_subchannel_probe(struct subchannel *); @@ -547,7 +547,7 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) return -EAGAIN; - if (cdev->drv && !try_module_get(cdev->drv->owner)) { + if (cdev->drv && !try_module_get(cdev->drv->driver.owner)) { atomic_set(&cdev->private->onoff, 0); return -EINVAL; } @@ -573,7 +573,7 @@ static ssize_t online_store (struct device *dev, struct device_attribute *attr, } out: if (cdev->drv) - module_put(cdev->drv->owner); + module_put(cdev->drv->driver.owner); atomic_set(&cdev->private->onoff, 0); return (ret < 0) ? ret : count; } @@ -1970,7 +1970,7 @@ static const struct dev_pm_ops ccw_pm_ops = { .restore = ccw_device_pm_restore, }; -struct bus_type ccw_bus_type = { +static struct bus_type ccw_bus_type = { .name = "ccw", .match = ccw_bus_match, .uevent = ccw_uevent, @@ -1993,8 +1993,6 @@ int ccw_driver_register(struct ccw_driver *cdriver) struct device_driver *drv = &cdriver->driver; drv->bus = &ccw_bus_type; - drv->name = cdriver->name; - drv->owner = cdriver->owner; return driver_register(drv); } @@ -2112,5 +2110,4 @@ EXPORT_SYMBOL(ccw_device_set_offline); EXPORT_SYMBOL(ccw_driver_register); EXPORT_SYMBOL(ccw_driver_unregister); EXPORT_SYMBOL(get_ccwdev_by_busid); -EXPORT_SYMBOL(ccw_bus_type); EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id); diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h index 379de2d..7e297c7 100644 --- a/drivers/s390/cio/device.h +++ b/drivers/s390/cio/device.h @@ -133,7 +133,6 @@ void ccw_device_set_notoper(struct ccw_device *cdev); /* qdio needs this. */ void ccw_device_set_timeout(struct ccw_device *, int); extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *); -extern struct bus_type ccw_bus_type; /* Channel measurement facility related */ void retry_set_schib(struct ccw_device *cdev); diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 5640c89..479c665 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -1508,7 +1508,8 @@ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, if (irq_ptr->state != QDIO_IRQ_STATE_ACTIVE) return -EBUSY; - + if (!count) + return 0; if (callflags & QDIO_FLAG_SYNC_INPUT) return handle_inbound(irq_ptr->input_qs[q_nr], callflags, bufnr, count); diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index ce3a5c1..9feb62f 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -264,8 +264,10 @@ static struct device *claw_root_dev; /* ccwgroup table */ static struct ccwgroup_driver claw_group_driver = { - .owner = THIS_MODULE, - .name = "claw", + .driver = { + .owner = THIS_MODULE, + .name = "claw", + }, .max_slaves = 2, .driver_id = 0xC3D3C1E6, .probe = claw_probe, @@ -282,8 +284,10 @@ static struct ccw_device_id claw_ids[] = { MODULE_DEVICE_TABLE(ccw, claw_ids); static struct ccw_driver claw_ccw_driver = { - .owner = THIS_MODULE, - .name = "claw", + .driver = { + .owner = THIS_MODULE, + .name = "claw", + }, .ids = claw_ids, .probe = ccwgroup_probe_ccwdev, .remove = ccwgroup_remove_ccwdev, diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 4c28459..c189296 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -1764,16 +1764,20 @@ static struct ccw_device_id ctcm_ids[] = { MODULE_DEVICE_TABLE(ccw, ctcm_ids); static struct ccw_driver ctcm_ccw_driver = { - .owner = THIS_MODULE, - .name = "ctcm", + .driver = { + .owner = THIS_MODULE, + .name = "ctcm", + }, .ids = ctcm_ids, .probe = ccwgroup_probe_ccwdev, .remove = ccwgroup_remove_ccwdev, }; static struct ccwgroup_driver ctcm_group_driver = { - .owner = THIS_MODULE, - .name = CTC_DRIVER_NAME, + .driver = { + .owner = THIS_MODULE, + .name = CTC_DRIVER_NAME, + }, .max_slaves = 2, .driver_id = 0xC3E3C3D4, /* CTCM */ .probe = ctcm_probe_device, diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 30b2a82..7fbc4ad 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -2396,8 +2396,10 @@ static struct ccw_device_id lcs_ids[] = { MODULE_DEVICE_TABLE(ccw, lcs_ids); static struct ccw_driver lcs_ccw_driver = { - .owner = THIS_MODULE, - .name = "lcs", + .driver = { + .owner = THIS_MODULE, + .name = "lcs", + }, .ids = lcs_ids, .probe = ccwgroup_probe_ccwdev, .remove = ccwgroup_remove_ccwdev, @@ -2407,8 +2409,10 @@ static struct ccw_driver lcs_ccw_driver = { * LCS ccwgroup driver registration */ static struct ccwgroup_driver lcs_group_driver = { - .owner = THIS_MODULE, - .name = "lcs", + .driver = { + .owner = THIS_MODULE, + .name = "lcs", + }, .max_slaves = 2, .driver_id = 0xD3C3E2, .probe = lcs_probe_device, diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 25eef304..10a3a3b 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3902,7 +3902,9 @@ static struct ccw_device_id qeth_ids[] = { MODULE_DEVICE_TABLE(ccw, qeth_ids); static struct ccw_driver qeth_ccw_driver = { - .name = "qeth", + .driver = { + .name = "qeth", + }, .ids = qeth_ids, .probe = ccwgroup_probe_ccwdev, .remove = ccwgroup_remove_ccwdev, @@ -4428,8 +4430,10 @@ static int qeth_core_restore(struct ccwgroup_device *gdev) } static struct ccwgroup_driver qeth_core_ccwgroup_driver = { - .owner = THIS_MODULE, - .name = "qeth", + .driver = { + .owner = THIS_MODULE, + .name = "qeth", + }, .driver_id = 0xD8C5E3C8, .probe = qeth_core_probe_device, .remove = qeth_core_remove_device, diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index 4f7852d..e8b7cee 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -251,8 +251,10 @@ static void zfcp_ccw_shutdown(struct ccw_device *cdev) } struct ccw_driver zfcp_ccw_driver = { - .owner = THIS_MODULE, - .name = "zfcp", + .driver = { + .owner = THIS_MODULE, + .name = "zfcp", + }, .ids = zfcp_ccw_device_id, .probe = zfcp_ccw_probe, .remove = zfcp_ccw_remove, diff --git a/drivers/scsi/aacraid/Makefile b/drivers/scsi/aacraid/Makefile index 92df4d6..1bd9fd1 100644 --- a/drivers/scsi/aacraid/Makefile +++ b/drivers/scsi/aacraid/Makefile @@ -3,6 +3,6 @@ obj-$(CONFIG_SCSI_AACRAID) := aacraid.o aacraid-objs := linit.o aachba.o commctrl.o comminit.o commsup.o \ - dpcsup.o rx.o sa.o rkt.o nark.o + dpcsup.o rx.o sa.o rkt.o nark.o src.o ccflags-y := -Idrivers/scsi diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 7df2dd1..118ce83 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -5,7 +5,8 @@ * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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 @@ -1486,7 +1487,9 @@ int aac_get_adapter_info(struct aac_dev* dev) dev->a_ops.adapter_write = aac_write_block; } dev->scsi_host_ptr->max_sectors = AAC_MAX_32BIT_SGBCOUNT; - if(!(dev->adapter_info.options & AAC_OPT_NEW_COMM)) { + if (dev->adapter_info.options & AAC_OPT_NEW_COMM_TYPE1) + dev->adapter_info.options |= AAC_OPT_NEW_COMM; + if (!(dev->adapter_info.options & AAC_OPT_NEW_COMM)) { /* * Worst case size that could cause sg overflow when * we break up SG elements that are larger than 64KB. diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 4dbcc05..29ab000 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 26400 +# define AAC_DRIVER_BUILD 28000 # define AAC_DRIVER_BRANCH "-ms" #endif #define MAXIMUM_NUM_CONTAINERS 32 @@ -277,6 +277,16 @@ enum aac_queue_types { #define FsaNormal 1 +/* transport FIB header (PMC) */ +struct aac_fib_xporthdr { + u64 HostAddress; /* FIB host address w/o xport header */ + u32 Size; /* FIB size excluding xport header */ + u32 Handle; /* driver handle to reference the FIB */ + u64 Reserved[2]; +}; + +#define ALIGN32 32 + /* * Define the FIB. The FIB is the where all the requested data and * command information are put to the application on the FSA adapter. @@ -394,7 +404,9 @@ enum fib_xfer_state { AdapterMicroFib = (1<<17), BIOSFibPath = (1<<18), FastResponseCapable = (1<<19), - ApiFib = (1<<20) // Its an API Fib. + ApiFib = (1<<20), /* Its an API Fib */ + /* PMC NEW COMM: There is no more AIF data pending */ + NoMoreAifDataAvailable = (1<<21) }; /* @@ -404,6 +416,7 @@ enum fib_xfer_state { #define ADAPTER_INIT_STRUCT_REVISION 3 #define ADAPTER_INIT_STRUCT_REVISION_4 4 // rocket science +#define ADAPTER_INIT_STRUCT_REVISION_6 6 /* PMC src */ struct aac_init { @@ -428,9 +441,15 @@ struct aac_init #define INITFLAGS_NEW_COMM_SUPPORTED 0x00000001 #define INITFLAGS_DRIVER_USES_UTC_TIME 0x00000010 #define INITFLAGS_DRIVER_SUPPORTS_PM 0x00000020 +#define INITFLAGS_NEW_COMM_TYPE1_SUPPORTED 0x00000041 __le32 MaxIoCommands; /* max outstanding commands */ __le32 MaxIoSize; /* largest I/O command */ __le32 MaxFibSize; /* largest FIB to adapter */ + /* ADAPTER_INIT_STRUCT_REVISION_5 begins here */ + __le32 MaxNumAif; /* max number of aif */ + /* ADAPTER_INIT_STRUCT_REVISION_6 begins here */ + __le32 HostRRQ_AddrLow; + __le32 HostRRQ_AddrHigh; /* Host RRQ (response queue) for SRC */ }; enum aac_log_level { @@ -685,7 +704,7 @@ struct rx_inbound { #define OutboundDoorbellReg MUnit.ODR struct rx_registers { - struct rx_mu_registers MUnit; /* 1300h - 1344h */ + struct rx_mu_registers MUnit; /* 1300h - 1347h */ __le32 reserved1[2]; /* 1348h - 134ch */ struct rx_inbound IndexRegs; }; @@ -703,7 +722,7 @@ struct rx_registers { #define rkt_inbound rx_inbound struct rkt_registers { - struct rkt_mu_registers MUnit; /* 1300h - 1344h */ + struct rkt_mu_registers MUnit; /* 1300h - 1347h */ __le32 reserved1[1006]; /* 1348h - 22fch */ struct rkt_inbound IndexRegs; /* 2300h - */ }; @@ -713,6 +732,44 @@ struct rkt_registers { #define rkt_writeb(AEP, CSR, value) writeb(value, &((AEP)->regs.rkt->CSR)) #define rkt_writel(AEP, CSR, value) writel(value, &((AEP)->regs.rkt->CSR)) +/* + * PMC SRC message unit registers + */ + +#define src_inbound rx_inbound + +struct src_mu_registers { + /* PCI*| Name */ + __le32 reserved0[8]; /* 00h | Reserved */ + __le32 IDR; /* 20h | Inbound Doorbell Register */ + __le32 IISR; /* 24h | Inbound Int. Status Register */ + __le32 reserved1[3]; /* 28h | Reserved */ + __le32 OIMR; /* 34h | Outbound Int. Mask Register */ + __le32 reserved2[25]; /* 38h | Reserved */ + __le32 ODR_R; /* 9ch | Outbound Doorbell Read */ + __le32 ODR_C; /* a0h | Outbound Doorbell Clear */ + __le32 reserved3[6]; /* a4h | Reserved */ + __le32 OMR; /* bch | Outbound Message Register */ + __le32 IQ_L; /* c0h | Inbound Queue (Low address) */ + __le32 IQ_H; /* c4h | Inbound Queue (High address) */ +}; + +struct src_registers { + struct src_mu_registers MUnit; /* 00h - c7h */ + __le32 reserved1[130790]; /* c8h - 7fc5fh */ + struct src_inbound IndexRegs; /* 7fc60h */ +}; + +#define src_readb(AEP, CSR) readb(&((AEP)->regs.src.bar0->CSR)) +#define src_readl(AEP, CSR) readl(&((AEP)->regs.src.bar0->CSR)) +#define src_writeb(AEP, CSR, value) writeb(value, \ + &((AEP)->regs.src.bar0->CSR)) +#define src_writel(AEP, CSR, value) writel(value, \ + &((AEP)->regs.src.bar0->CSR)) + +#define SRC_ODR_SHIFT 12 +#define SRC_IDR_SHIFT 9 + typedef void (*fib_callback)(void *ctxt, struct fib *fibctx); struct aac_fib_context { @@ -879,6 +936,7 @@ struct aac_supplement_adapter_info #define AAC_OPTION_MU_RESET cpu_to_le32(0x00000001) #define AAC_OPTION_IGNORE_RESET cpu_to_le32(0x00000002) #define AAC_OPTION_POWER_MANAGEMENT cpu_to_le32(0x00000004) +#define AAC_OPTION_DOORBELL_RESET cpu_to_le32(0x00004000) #define AAC_SIS_VERSION_V3 3 #define AAC_SIS_SLOT_UNKNOWN 0xFF @@ -940,6 +998,7 @@ struct aac_bus_info_response { #define AAC_OPT_SUPPLEMENT_ADAPTER_INFO cpu_to_le32(1<<16) #define AAC_OPT_NEW_COMM cpu_to_le32(1<<17) #define AAC_OPT_NEW_COMM_64 cpu_to_le32(1<<18) +#define AAC_OPT_NEW_COMM_TYPE1 cpu_to_le32(1<<28) struct aac_dev { @@ -952,6 +1011,7 @@ struct aac_dev */ unsigned max_fib_size; unsigned sg_tablesize; + unsigned max_num_aif; /* * Map for 128 fib objects (64k) @@ -980,10 +1040,21 @@ struct aac_dev struct adapter_ops a_ops; unsigned long fsrev; /* Main driver's revision number */ - unsigned base_size; /* Size of mapped in region */ + unsigned long dbg_base; /* address of UART + * debug buffer */ + + unsigned base_size, dbg_size; /* Size of + * mapped in region */ + struct aac_init *init; /* Holds initialization info to communicate with adapter */ dma_addr_t init_pa; /* Holds physical address of the init struct */ + u32 *host_rrq; /* response queue + * if AAC_COMM_MESSAGE_TYPE1 */ + + dma_addr_t host_rrq_pa; /* phys. address */ + u32 host_rrq_idx; /* index into rrq buffer */ + struct pci_dev *pdev; /* Our PCI interface */ void * printfbuf; /* pointer to buffer used for printf's from the adapter */ void * comm_addr; /* Base address of Comm area */ @@ -1003,14 +1074,20 @@ struct aac_dev */ #ifndef AAC_MIN_FOOTPRINT_SIZE # define AAC_MIN_FOOTPRINT_SIZE 8192 +# define AAC_MIN_SRC_BAR0_SIZE 0x400000 +# define AAC_MIN_SRC_BAR1_SIZE 0x800 #endif union { struct sa_registers __iomem *sa; struct rx_registers __iomem *rx; struct rkt_registers __iomem *rkt; + struct { + struct src_registers __iomem *bar0; + char __iomem *bar1; + } src; } regs; - volatile void __iomem *base; + volatile void __iomem *base, *dbg_base_mapped; volatile struct rx_inbound __iomem *IndexRegs; u32 OIMR; /* Mask Register Cache */ /* @@ -1031,9 +1108,8 @@ struct aac_dev u8 comm_interface; # define AAC_COMM_PRODUCER 0 # define AAC_COMM_MESSAGE 1 - /* macro side-effects BEWARE */ -# define raw_io_interface \ - init->InitStructRevision==cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_4) +# define AAC_COMM_MESSAGE_TYPE1 3 + u8 raw_io_interface; u8 raw_io_64; u8 printf_enabled; u8 in_reset; @@ -1789,6 +1865,10 @@ extern struct aac_common aac_config; #define DoorBellAdapterNormCmdNotFull (1<<3) /* Adapter -> Host */ #define DoorBellAdapterNormRespNotFull (1<<4) /* Adapter -> Host */ #define DoorBellPrintfReady (1<<5) /* Adapter -> Host */ +#define DoorBellAifPending (1<<6) /* Adapter -> Host */ + +/* PMC specific outbound doorbell bits */ +#define PmDoorBellResponseSent (1<<1) /* Adapter -> Host */ /* * For FIB communication, we need all of the following things @@ -1831,6 +1911,9 @@ extern struct aac_common aac_config; #define AifReqAPIJobUpdate 109 /* Update a job report from the API */ #define AifReqAPIJobFinish 110 /* Finish a job from the API */ +/* PMC NEW COMM: Request the event data */ +#define AifReqEvent 200 + /* * Adapter Initiated FIB command structures. Start with the adapter * initiated FIBs that really come from the adapter, and get responded @@ -1886,10 +1969,13 @@ int aac_rx_init(struct aac_dev *dev); int aac_rkt_init(struct aac_dev *dev); int aac_nark_init(struct aac_dev *dev); int aac_sa_init(struct aac_dev *dev); +int aac_src_init(struct aac_dev *dev); int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw_fib, int wait, struct fib * fibptr, unsigned long *nonotify); unsigned int aac_response_normal(struct aac_queue * q); unsigned int aac_command_normal(struct aac_queue * q); -unsigned int aac_intr_normal(struct aac_dev * dev, u32 Index); +unsigned int aac_intr_normal(struct aac_dev *dev, u32 Index, + int isAif, int isFastResponse, + struct hw_fib *aif_fib); int aac_reset_adapter(struct aac_dev * dev, int forced); int aac_check_health(struct aac_dev * dev); int aac_command_thread(void *data); diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c index 645ddd9..8a0b330 100644 --- a/drivers/scsi/aacraid/commctrl.c +++ b/drivers/scsi/aacraid/commctrl.c @@ -5,7 +5,8 @@ * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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 diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c index a726148..7ac8fdb 100644 --- a/drivers/scsi/aacraid/comminit.c +++ b/drivers/scsi/aacraid/comminit.c @@ -5,7 +5,8 @@ * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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 @@ -52,12 +53,16 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co unsigned long size, align; const unsigned long fibsize = 4096; const unsigned long printfbufsiz = 256; + unsigned long host_rrq_size = 0; struct aac_init *init; dma_addr_t phys; unsigned long aac_max_hostphysmempages; - size = fibsize + sizeof(struct aac_init) + commsize + commalign + printfbufsiz; - + if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) + host_rrq_size = (dev->scsi_host_ptr->can_queue + + AAC_NUM_MGT_FIB) * sizeof(u32); + size = fibsize + sizeof(struct aac_init) + commsize + + commalign + printfbufsiz + host_rrq_size; base = pci_alloc_consistent(dev->pdev, size, &phys); @@ -70,8 +75,14 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co dev->comm_phys = phys; dev->comm_size = size; - dev->init = (struct aac_init *)(base + fibsize); - dev->init_pa = phys + fibsize; + if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) { + dev->host_rrq = (u32 *)(base + fibsize); + dev->host_rrq_pa = phys + fibsize; + memset(dev->host_rrq, 0, host_rrq_size); + } + + dev->init = (struct aac_init *)(base + fibsize + host_rrq_size); + dev->init_pa = phys + fibsize + host_rrq_size; init = dev->init; @@ -106,8 +117,13 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co init->InitFlags = 0; if (dev->comm_interface == AAC_COMM_MESSAGE) { - init->InitFlags = cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED); + init->InitFlags |= cpu_to_le32(INITFLAGS_NEW_COMM_SUPPORTED); dprintk((KERN_WARNING"aacraid: New Comm Interface enabled\n")); + } else if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) { + init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_6); + init->InitFlags |= cpu_to_le32(INITFLAGS_NEW_COMM_TYPE1_SUPPORTED); + dprintk((KERN_WARNING + "aacraid: New Comm Interface type1 enabled\n")); } init->InitFlags |= cpu_to_le32(INITFLAGS_DRIVER_USES_UTC_TIME | INITFLAGS_DRIVER_SUPPORTS_PM); @@ -115,11 +131,18 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co init->MaxIoSize = cpu_to_le32(dev->scsi_host_ptr->max_sectors << 9); init->MaxFibSize = cpu_to_le32(dev->max_fib_size); + init->MaxNumAif = cpu_to_le32(dev->max_num_aif); + init->HostRRQ_AddrHigh = (u32)((u64)dev->host_rrq_pa >> 32); + init->HostRRQ_AddrLow = (u32)(dev->host_rrq_pa & 0xffffffff); + + /* * Increment the base address by the amount already used */ - base = base + fibsize + sizeof(struct aac_init); - phys = (dma_addr_t)((ulong)phys + fibsize + sizeof(struct aac_init)); + base = base + fibsize + host_rrq_size + sizeof(struct aac_init); + phys = (dma_addr_t)((ulong)phys + fibsize + host_rrq_size + + sizeof(struct aac_init)); + /* * Align the beginning of Headers to commalign */ @@ -314,15 +337,22 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) - sizeof(struct aac_write) + sizeof(struct sgentry)) / sizeof(struct sgentry); dev->comm_interface = AAC_COMM_PRODUCER; - dev->raw_io_64 = 0; + dev->raw_io_interface = dev->raw_io_64 = 0; + if ((!aac_adapter_sync_cmd(dev, GET_ADAPTER_PROPERTIES, 0, 0, 0, 0, 0, 0, status+0, status+1, status+2, NULL, NULL)) && (status[0] == 0x00000001)) { if (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM_64)) dev->raw_io_64 = 1; - if (dev->a_ops.adapter_comm && - (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM))) - dev->comm_interface = AAC_COMM_MESSAGE; + if (dev->a_ops.adapter_comm) { + if (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM_TYPE1)) { + dev->comm_interface = AAC_COMM_MESSAGE_TYPE1; + dev->raw_io_interface = 1; + } else if (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM)) { + dev->comm_interface = AAC_COMM_MESSAGE; + dev->raw_io_interface = 1; + } + } if ((dev->comm_interface == AAC_COMM_MESSAGE) && (status[2] > dev->base_size)) { aac_adapter_ioremap(dev, 0); @@ -350,10 +380,12 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) * status[3] & 0xFFFF maximum number FIBs outstanding */ host->max_sectors = (status[1] >> 16) << 1; - dev->max_fib_size = status[1] & 0xFFFF; + /* Multiple of 32 for PMC */ + dev->max_fib_size = status[1] & 0xFFE0; host->sg_tablesize = status[2] >> 16; dev->sg_tablesize = status[2] & 0xFFFF; host->can_queue = (status[3] & 0xFFFF) - AAC_NUM_MGT_FIB; + dev->max_num_aif = status[4] & 0xFFFF; /* * NOTE: * All these overrides are based on a fixed internal diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 060ac4b..dd7ad3b 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -5,7 +5,8 @@ * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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 @@ -63,9 +64,11 @@ static int fib_map_alloc(struct aac_dev *dev) "allocate hardware fibs pci_alloc_consistent(%p, %d * (%d + %d), %p)\n", dev->pdev, dev->max_fib_size, dev->scsi_host_ptr->can_queue, AAC_NUM_MGT_FIB, &dev->hw_fib_pa)); - if((dev->hw_fib_va = pci_alloc_consistent(dev->pdev, dev->max_fib_size - * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB), - &dev->hw_fib_pa))==NULL) + dev->hw_fib_va = pci_alloc_consistent(dev->pdev, + (dev->max_fib_size + sizeof(struct aac_fib_xporthdr)) + * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB) + (ALIGN32 - 1), + &dev->hw_fib_pa); + if (dev->hw_fib_va == NULL) return -ENOMEM; return 0; } @@ -110,9 +113,22 @@ int aac_fib_setup(struct aac_dev * dev) if (i<0) return -ENOMEM; + /* 32 byte alignment for PMC */ + hw_fib_pa = (dev->hw_fib_pa + (ALIGN32 - 1)) & ~(ALIGN32 - 1); + dev->hw_fib_va = (struct hw_fib *)((unsigned char *)dev->hw_fib_va + + (hw_fib_pa - dev->hw_fib_pa)); + dev->hw_fib_pa = hw_fib_pa; + memset(dev->hw_fib_va, 0, + (dev->max_fib_size + sizeof(struct aac_fib_xporthdr)) * + (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)); + + /* add Xport header */ + dev->hw_fib_va = (struct hw_fib *)((unsigned char *)dev->hw_fib_va + + sizeof(struct aac_fib_xporthdr)); + dev->hw_fib_pa += sizeof(struct aac_fib_xporthdr); + hw_fib = dev->hw_fib_va; hw_fib_pa = dev->hw_fib_pa; - memset(hw_fib, 0, dev->max_fib_size * (dev->scsi_host_ptr->can_queue + AAC_NUM_MGT_FIB)); /* * Initialise the fibs */ @@ -129,8 +145,10 @@ int aac_fib_setup(struct aac_dev * dev) hw_fib->header.XferState = cpu_to_le32(0xffffffff); hw_fib->header.SenderSize = cpu_to_le16(dev->max_fib_size); fibptr->hw_fib_pa = hw_fib_pa; - hw_fib = (struct hw_fib *)((unsigned char *)hw_fib + dev->max_fib_size); - hw_fib_pa = hw_fib_pa + dev->max_fib_size; + hw_fib = (struct hw_fib *)((unsigned char *)hw_fib + + dev->max_fib_size + sizeof(struct aac_fib_xporthdr)); + hw_fib_pa = hw_fib_pa + + dev->max_fib_size + sizeof(struct aac_fib_xporthdr); } /* * Add the fib chain to the free list @@ -664,9 +682,14 @@ int aac_fib_adapter_complete(struct fib *fibptr, unsigned short size) unsigned long nointr = 0; unsigned long qflags; + if (dev->comm_interface == AAC_COMM_MESSAGE_TYPE1) { + kfree(hw_fib); + return 0; + } + if (hw_fib->header.XferState == 0) { if (dev->comm_interface == AAC_COMM_MESSAGE) - kfree (hw_fib); + kfree(hw_fib); return 0; } /* @@ -674,7 +697,7 @@ int aac_fib_adapter_complete(struct fib *fibptr, unsigned short size) */ if (hw_fib->header.StructType != FIB_MAGIC) { if (dev->comm_interface == AAC_COMM_MESSAGE) - kfree (hw_fib); + kfree(hw_fib); return -EINVAL; } /* diff --git a/drivers/scsi/aacraid/dpcsup.c b/drivers/scsi/aacraid/dpcsup.c index 9c7408fe..f0c66a8 100644 --- a/drivers/scsi/aacraid/dpcsup.c +++ b/drivers/scsi/aacraid/dpcsup.c @@ -5,7 +5,8 @@ * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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 @@ -228,6 +229,48 @@ unsigned int aac_command_normal(struct aac_queue *q) return 0; } +/* + * + * aac_aif_callback + * @context: the context set in the fib - here it is scsi cmd + * @fibptr: pointer to the fib + * + * Handles the AIFs - new method (SRC) + * + */ + +static void aac_aif_callback(void *context, struct fib * fibptr) +{ + struct fib *fibctx; + struct aac_dev *dev; + struct aac_aifcmd *cmd; + int status; + + fibctx = (struct fib *)context; + BUG_ON(fibptr == NULL); + dev = fibptr->dev; + + if (fibptr->hw_fib_va->header.XferState & + cpu_to_le32(NoMoreAifDataAvailable)) { + aac_fib_complete(fibptr); + aac_fib_free(fibptr); + return; + } + + aac_intr_normal(dev, 0, 1, 0, fibptr->hw_fib_va); + + aac_fib_init(fibctx); + cmd = (struct aac_aifcmd *) fib_data(fibctx); + cmd->command = cpu_to_le32(AifReqEvent); + + status = aac_fib_send(AifRequest, + fibctx, + sizeof(struct hw_fib)-sizeof(struct aac_fibhdr), + FsaNormal, + 0, 1, + (fib_callback)aac_aif_callback, fibctx); +} + /** * aac_intr_normal - Handle command replies @@ -238,19 +281,17 @@ unsigned int aac_command_normal(struct aac_queue *q) * know there is a response on our normal priority queue. We will pull off * all QE there are and wake up all the waiters before exiting. */ - -unsigned int aac_intr_normal(struct aac_dev * dev, u32 index) +unsigned int aac_intr_normal(struct aac_dev *dev, u32 index, + int isAif, int isFastResponse, struct hw_fib *aif_fib) { unsigned long mflags; dprintk((KERN_INFO "aac_intr_normal(%p,%x)\n", dev, index)); - if ((index & 0x00000002L)) { + if (isAif == 1) { /* AIF - common */ struct hw_fib * hw_fib; struct fib * fib; struct aac_queue *q = &dev->queues->queue[HostNormCmdQueue]; unsigned long flags; - if (index == 0xFFFFFFFEL) /* Special Case */ - return 0; /* Do nothing */ /* * Allocate a FIB. For non queued stuff we can just use * the stack so we are happy. We need a fib object in order to @@ -263,8 +304,13 @@ unsigned int aac_intr_normal(struct aac_dev * dev, u32 index) kfree (fib); return 1; } - memcpy(hw_fib, (struct hw_fib *)(((uintptr_t)(dev->regs.sa)) + - (index & ~0x00000002L)), sizeof(struct hw_fib)); + if (aif_fib != NULL) { + memcpy(hw_fib, aif_fib, sizeof(struct hw_fib)); + } else { + memcpy(hw_fib, + (struct hw_fib *)(((uintptr_t)(dev->regs.sa)) + + index), sizeof(struct hw_fib)); + } INIT_LIST_HEAD(&fib->fiblink); fib->type = FSAFS_NTC_FIB_CONTEXT; fib->size = sizeof(struct fib); @@ -277,9 +323,26 @@ unsigned int aac_intr_normal(struct aac_dev * dev, u32 index) wake_up_interruptible(&q->cmdready); spin_unlock_irqrestore(q->lock, flags); return 1; + } else if (isAif == 2) { /* AIF - new (SRC) */ + struct fib *fibctx; + struct aac_aifcmd *cmd; + + fibctx = aac_fib_alloc(dev); + if (!fibctx) + return 1; + aac_fib_init(fibctx); + + cmd = (struct aac_aifcmd *) fib_data(fibctx); + cmd->command = cpu_to_le32(AifReqEvent); + + return aac_fib_send(AifRequest, + fibctx, + sizeof(struct hw_fib)-sizeof(struct aac_fibhdr), + FsaNormal, + 0, 1, + (fib_callback)aac_aif_callback, fibctx); } else { - int fast = index & 0x01; - struct fib * fib = &dev->fibs[index >> 2]; + struct fib *fib = &dev->fibs[index]; struct hw_fib * hwfib = fib->hw_fib_va; /* @@ -298,7 +361,7 @@ unsigned int aac_intr_normal(struct aac_dev * dev, u32 index) return 0; } - if (fast) { + if (isFastResponse) { /* * Doctor the fib */ diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 2c93d94..4ff2652 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -5,7 +5,8 @@ * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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 @@ -54,7 +55,7 @@ #include "aacraid.h" -#define AAC_DRIVER_VERSION "1.1-5" +#define AAC_DRIVER_VERSION "1.1-7" #ifndef AAC_DRIVER_BRANCH #define AAC_DRIVER_BRANCH "" #endif @@ -161,6 +162,7 @@ static const struct pci_device_id aac_pci_tbl[] __devinitdata = { { 0x9005, 0x0285, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 59 }, /* Adaptec Catch All */ { 0x9005, 0x0286, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 60 }, /* Adaptec Rocket Catch All */ { 0x9005, 0x0288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 61 }, /* Adaptec NEMER/ARK Catch All */ + { 0x9005, 0x028b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 62 }, /* Adaptec PMC Catch All */ { 0,} }; MODULE_DEVICE_TABLE(pci, aac_pci_tbl); @@ -235,7 +237,8 @@ static struct aac_driver_ident aac_drivers[] = { { aac_rx_init, "aacraid", "Legend ", "RAID ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG | AAC_QUIRK_SCSI_32 }, /* Legend Catchall */ { aac_rx_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec Catch All */ { aac_rkt_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec Rocket Catch All */ - { aac_nark_init, "aacraid", "ADAPTEC ", "RAID ", 2 } /* Adaptec NEMER/ARK Catch All */ + { aac_nark_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec NEMER/ARK Catch All */ + { aac_src_init, "aacraid", "ADAPTEC ", "RAID ", 2 } /* Adaptec PMC Catch All */ }; /** @@ -653,8 +656,10 @@ static int aac_eh_reset(struct scsi_cmnd* cmd) * This adapter needs a blind reset, only do so for Adapters that * support a register, instead of a commanded, reset. */ - if ((aac->supplement_adapter_info.SupportedOptions2 & - AAC_OPTION_MU_RESET) && + if (((aac->supplement_adapter_info.SupportedOptions2 & + AAC_OPTION_MU_RESET) || + (aac->supplement_adapter_info.SupportedOptions2 & + AAC_OPTION_DOORBELL_RESET)) && aac_check_reset && ((aac_check_reset != 1) || !(aac->supplement_adapter_info.SupportedOptions2 & diff --git a/drivers/scsi/aacraid/nark.c b/drivers/scsi/aacraid/nark.c index c55f7c8..f397d21 100644 --- a/drivers/scsi/aacraid/nark.c +++ b/drivers/scsi/aacraid/nark.c @@ -4,7 +4,8 @@ * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2006-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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 diff --git a/drivers/scsi/aacraid/rkt.c b/drivers/scsi/aacraid/rkt.c index 16d8db5..be44de9 100644 --- a/drivers/scsi/aacraid/rkt.c +++ b/drivers/scsi/aacraid/rkt.c @@ -5,7 +5,8 @@ * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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 diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c index 84d77fd..ce530f1 100644 --- a/drivers/scsi/aacraid/rx.c +++ b/drivers/scsi/aacraid/rx.c @@ -5,7 +5,8 @@ * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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 @@ -84,15 +85,35 @@ static irqreturn_t aac_rx_intr_producer(int irq, void *dev_id) static irqreturn_t aac_rx_intr_message(int irq, void *dev_id) { + int isAif, isFastResponse, isSpecial; struct aac_dev *dev = dev_id; u32 Index = rx_readl(dev, MUnit.OutboundQueue); if (unlikely(Index == 0xFFFFFFFFL)) Index = rx_readl(dev, MUnit.OutboundQueue); if (likely(Index != 0xFFFFFFFFL)) { do { - if (unlikely(aac_intr_normal(dev, Index))) { - rx_writel(dev, MUnit.OutboundQueue, Index); - rx_writel(dev, MUnit.ODR, DoorBellAdapterNormRespReady); + isAif = isFastResponse = isSpecial = 0; + if (Index & 0x00000002L) { + isAif = 1; + if (Index == 0xFFFFFFFEL) + isSpecial = 1; + Index &= ~0x00000002L; + } else { + if (Index & 0x00000001L) + isFastResponse = 1; + Index >>= 2; + } + if (!isSpecial) { + if (unlikely(aac_intr_normal(dev, + Index, isAif, + isFastResponse, NULL))) { + rx_writel(dev, + MUnit.OutboundQueue, + Index); + rx_writel(dev, + MUnit.ODR, + DoorBellAdapterNormRespReady); + } } Index = rx_readl(dev, MUnit.OutboundQueue); } while (Index != 0xFFFFFFFFL); @@ -631,6 +652,10 @@ int _aac_rx_init(struct aac_dev *dev) name, instance); goto error_iounmap; } + dev->dbg_base = dev->scsi_host_ptr->base; + dev->dbg_base_mapped = dev->base; + dev->dbg_size = dev->base_size; + aac_adapter_enable_int(dev); /* * Tell the adapter that all is configured, and it can diff --git a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c index 622c21c..e5d4457 100644 --- a/drivers/scsi/aacraid/sa.c +++ b/drivers/scsi/aacraid/sa.c @@ -5,7 +5,8 @@ * based on the old aacraid driver that is.. * Adaptec aacraid device driver for Linux. * - * Copyright (c) 2000-2007 Adaptec, Inc. (aacraid@adaptec.com) + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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 @@ -391,6 +392,10 @@ int aac_sa_init(struct aac_dev *dev) name, instance); goto error_iounmap; } + dev->dbg_base = dev->scsi_host_ptr->base; + dev->dbg_base_mapped = dev->base; + dev->dbg_size = dev->base_size; + aac_adapter_enable_int(dev); /* diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c new file mode 100644 index 0000000..c204946 --- /dev/null +++ b/drivers/scsi/aacraid/src.c @@ -0,0 +1,594 @@ +/* + * Adaptec AAC series RAID controller driver + * (c) Copyright 2001 Red Hat Inc. + * + * based on the old aacraid driver that is.. + * Adaptec aacraid device driver for Linux. + * + * Copyright (c) 2000-2010 Adaptec, Inc. + * 2010 PMC-Sierra, Inc. (aacraid@pmc-sierra.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, 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Module Name: + * src.c + * + * Abstract: Hardware Device Interface for PMC SRC based controllers + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/version.h> +#include <linux/completion.h> +#include <linux/time.h> +#include <linux/interrupt.h> +#include <scsi/scsi_host.h> + +#include "aacraid.h" + +static irqreturn_t aac_src_intr_message(int irq, void *dev_id) +{ + struct aac_dev *dev = dev_id; + unsigned long bellbits, bellbits_shifted; + int our_interrupt = 0; + int isFastResponse; + u32 index, handle; + + bellbits = src_readl(dev, MUnit.ODR_R); + if (bellbits & PmDoorBellResponseSent) { + bellbits = PmDoorBellResponseSent; + /* handle async. status */ + our_interrupt = 1; + index = dev->host_rrq_idx; + if (dev->host_rrq[index] == 0) { + u32 old_index = index; + /* adjust index */ + do { + index++; + if (index == dev->scsi_host_ptr->can_queue + + AAC_NUM_MGT_FIB) + index = 0; + if (dev->host_rrq[index] != 0) + break; + } while (index != old_index); + dev->host_rrq_idx = index; + } + for (;;) { + isFastResponse = 0; + /* remove toggle bit (31) */ + handle = (dev->host_rrq[index] & 0x7fffffff); + /* check fast response bit (30) */ + if (handle & 0x40000000) + isFastResponse = 1; + handle &= 0x0000ffff; + if (handle == 0) + break; + + aac_intr_normal(dev, handle-1, 0, isFastResponse, NULL); + + dev->host_rrq[index++] = 0; + if (index == dev->scsi_host_ptr->can_queue + + AAC_NUM_MGT_FIB) + index = 0; + dev->host_rrq_idx = index; + } + } else { + bellbits_shifted = (bellbits >> SRC_ODR_SHIFT); + if (bellbits_shifted & DoorBellAifPending) { + our_interrupt = 1; + /* handle AIF */ + aac_intr_normal(dev, 0, 2, 0, NULL); + } + } + + if (our_interrupt) { + src_writel(dev, MUnit.ODR_C, bellbits); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/** + * aac_src_disable_interrupt - Disable interrupts + * @dev: Adapter + */ + +static void aac_src_disable_interrupt(struct aac_dev *dev) +{ + src_writel(dev, MUnit.OIMR, dev->OIMR = 0xffffffff); +} + +/** + * aac_src_enable_interrupt_message - Enable interrupts + * @dev: Adapter + */ + +static void aac_src_enable_interrupt_message(struct aac_dev *dev) +{ + src_writel(dev, MUnit.OIMR, dev->OIMR = 0xfffffff8); +} + +/** + * src_sync_cmd - send a command and wait + * @dev: Adapter + * @command: Command to execute + * @p1: first parameter + * @ret: adapter status + * + * This routine will send a synchronous command to the adapter and wait + * for its completion. + */ + +static int src_sync_cmd(struct aac_dev *dev, u32 command, + u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, + u32 *status, u32 * r1, u32 * r2, u32 * r3, u32 * r4) +{ + unsigned long start; + int ok; + + /* + * Write the command into Mailbox 0 + */ + writel(command, &dev->IndexRegs->Mailbox[0]); + /* + * Write the parameters into Mailboxes 1 - 6 + */ + writel(p1, &dev->IndexRegs->Mailbox[1]); + writel(p2, &dev->IndexRegs->Mailbox[2]); + writel(p3, &dev->IndexRegs->Mailbox[3]); + writel(p4, &dev->IndexRegs->Mailbox[4]); + + /* + * Clear the synch command doorbell to start on a clean slate. + */ + src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT); + + /* + * Disable doorbell interrupts + */ + src_writel(dev, MUnit.OIMR, dev->OIMR = 0xffffffff); + + /* + * Force the completion of the mask register write before issuing + * the interrupt. + */ + src_readl(dev, MUnit.OIMR); + + /* + * Signal that there is a new synch command + */ + src_writel(dev, MUnit.IDR, INBOUNDDOORBELL_0 << SRC_IDR_SHIFT); + + ok = 0; + start = jiffies; + + /* + * Wait up to 30 seconds + */ + while (time_before(jiffies, start+30*HZ)) { + /* Delay 5 microseconds to let Mon960 get info. */ + udelay(5); + + /* Mon960 will set doorbell0 bit + * when it has completed the command + */ + if ((src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT) & OUTBOUNDDOORBELL_0) { + /* Clear the doorbell */ + src_writel(dev, + MUnit.ODR_C, + OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT); + ok = 1; + break; + } + + /* Yield the processor in case we are slow */ + msleep(1); + } + if (unlikely(ok != 1)) { + /* Restore interrupt mask even though we timed out */ + aac_adapter_enable_int(dev); + return -ETIMEDOUT; + } + + /* Pull the synch status from Mailbox 0 */ + if (status) + *status = readl(&dev->IndexRegs->Mailbox[0]); + if (r1) + *r1 = readl(&dev->IndexRegs->Mailbox[1]); + if (r2) + *r2 = readl(&dev->IndexRegs->Mailbox[2]); + if (r3) + *r3 = readl(&dev->IndexRegs->Mailbox[3]); + if (r4) + *r4 = readl(&dev->IndexRegs->Mailbox[4]); + + /* Clear the synch command doorbell */ + src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT); + + /* Restore interrupt mask */ + aac_adapter_enable_int(dev); + return 0; + +} + +/** + * aac_src_interrupt_adapter - interrupt adapter + * @dev: Adapter + * + * Send an interrupt to the i960 and breakpoint it. + */ + +static void aac_src_interrupt_adapter(struct aac_dev *dev) +{ + src_sync_cmd(dev, BREAKPOINT_REQUEST, + 0, 0, 0, 0, 0, 0, + NULL, NULL, NULL, NULL, NULL); +} + +/** + * aac_src_notify_adapter - send an event to the adapter + * @dev: Adapter + * @event: Event to send + * + * Notify the i960 that something it probably cares about has + * happened. + */ + +static void aac_src_notify_adapter(struct aac_dev *dev, u32 event) +{ + switch (event) { + + case AdapNormCmdQue: + src_writel(dev, MUnit.ODR_C, + INBOUNDDOORBELL_1 << SRC_ODR_SHIFT); + break; + case HostNormRespNotFull: + src_writel(dev, MUnit.ODR_C, + INBOUNDDOORBELL_4 << SRC_ODR_SHIFT); + break; + case AdapNormRespQue: + src_writel(dev, MUnit.ODR_C, + INBOUNDDOORBELL_2 << SRC_ODR_SHIFT); + break; + case HostNormCmdNotFull: + src_writel(dev, MUnit.ODR_C, + INBOUNDDOORBELL_3 << SRC_ODR_SHIFT); + break; + case FastIo: + src_writel(dev, MUnit.ODR_C, + INBOUNDDOORBELL_6 << SRC_ODR_SHIFT); + break; + case AdapPrintfDone: + src_writel(dev, MUnit.ODR_C, + INBOUNDDOORBELL_5 << SRC_ODR_SHIFT); + break; + default: + BUG(); + break; + } +} + +/** + * aac_src_start_adapter - activate adapter + * @dev: Adapter + * + * Start up processing on an i960 based AAC adapter + */ + +static void aac_src_start_adapter(struct aac_dev *dev) +{ + struct aac_init *init; + + init = dev->init; + init->HostElapsedSeconds = cpu_to_le32(get_seconds()); + + /* We can only use a 32 bit address here */ + src_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa, + 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL); +} + +/** + * aac_src_check_health + * @dev: device to check if healthy + * + * Will attempt to determine if the specified adapter is alive and + * capable of handling requests, returning 0 if alive. + */ +static int aac_src_check_health(struct aac_dev *dev) +{ + u32 status = src_readl(dev, MUnit.OMR); + + /* + * Check to see if the board failed any self tests. + */ + if (unlikely(status & SELF_TEST_FAILED)) + return -1; + + /* + * Check to see if the board panic'd. + */ + if (unlikely(status & KERNEL_PANIC)) + return (status >> 16) & 0xFF; + /* + * Wait for the adapter to be up and running. + */ + if (unlikely(!(status & KERNEL_UP_AND_RUNNING))) + return -3; + /* + * Everything is OK + */ + return 0; +} + +/** + * aac_src_deliver_message + * @fib: fib to issue + * + * Will send a fib, returning 0 if successful. + */ +static int aac_src_deliver_message(struct fib *fib) +{ + struct aac_dev *dev = fib->dev; + struct aac_queue *q = &dev->queues->queue[AdapNormCmdQueue]; + unsigned long qflags; + u32 fibsize; + u64 address; + struct aac_fib_xporthdr *pFibX; + + spin_lock_irqsave(q->lock, qflags); + q->numpending++; + spin_unlock_irqrestore(q->lock, qflags); + + /* Calculate the amount to the fibsize bits */ + fibsize = (sizeof(struct aac_fib_xporthdr) + + fib->hw_fib_va->header.Size + 127) / 128 - 1; + if (fibsize > (ALIGN32 - 1)) + fibsize = ALIGN32 - 1; + + /* Fill XPORT header */ + pFibX = (struct aac_fib_xporthdr *) + ((unsigned char *)fib->hw_fib_va - + sizeof(struct aac_fib_xporthdr)); + pFibX->Handle = fib->hw_fib_va->header.SenderData + 1; + pFibX->HostAddress = fib->hw_fib_pa; + pFibX->Size = fib->hw_fib_va->header.Size; + address = fib->hw_fib_pa - (u64)sizeof(struct aac_fib_xporthdr); + + src_writel(dev, MUnit.IQ_H, (u32)(address >> 32)); + src_writel(dev, MUnit.IQ_L, (u32)(address & 0xffffffff) + fibsize); + return 0; +} + +/** + * aac_src_ioremap + * @size: mapping resize request + * + */ +static int aac_src_ioremap(struct aac_dev *dev, u32 size) +{ + if (!size) { + iounmap(dev->regs.src.bar0); + dev->regs.src.bar0 = NULL; + iounmap(dev->base); + dev->base = NULL; + return 0; + } + dev->regs.src.bar1 = ioremap(pci_resource_start(dev->pdev, 2), + AAC_MIN_SRC_BAR1_SIZE); + dev->base = NULL; + if (dev->regs.src.bar1 == NULL) + return -1; + dev->base = dev->regs.src.bar0 = ioremap(dev->scsi_host_ptr->base, + size); + if (dev->base == NULL) { + iounmap(dev->regs.src.bar1); + dev->regs.src.bar1 = NULL; + return -1; + } + dev->IndexRegs = &((struct src_registers __iomem *) + dev->base)->IndexRegs; + return 0; +} + +static int aac_src_restart_adapter(struct aac_dev *dev, int bled) +{ + u32 var, reset_mask; + + if (bled >= 0) { + if (bled) + printk(KERN_ERR "%s%d: adapter kernel panic'd %x.\n", + dev->name, dev->id, bled); + bled = aac_adapter_sync_cmd(dev, IOP_RESET_ALWAYS, + 0, 0, 0, 0, 0, 0, &var, &reset_mask, NULL, NULL, NULL); + if (bled || (var != 0x00000001)) + bled = -EINVAL; + if (dev->supplement_adapter_info.SupportedOptions2 & + AAC_OPTION_DOORBELL_RESET) { + src_writel(dev, MUnit.IDR, reset_mask); + msleep(5000); /* Delay 5 seconds */ + } + } + + if (src_readl(dev, MUnit.OMR) & KERNEL_PANIC) + return -ENODEV; + + if (startup_timeout < 300) + startup_timeout = 300; + + return 0; +} + +/** + * aac_src_select_comm - Select communications method + * @dev: Adapter + * @comm: communications method + */ +int aac_src_select_comm(struct aac_dev *dev, int comm) +{ + switch (comm) { + case AAC_COMM_MESSAGE: + dev->a_ops.adapter_enable_int = aac_src_enable_interrupt_message; + dev->a_ops.adapter_intr = aac_src_intr_message; + dev->a_ops.adapter_deliver = aac_src_deliver_message; + break; + default: + return 1; + } + return 0; +} + +/** + * aac_src_init - initialize an Cardinal Frey Bar card + * @dev: device to configure + * + */ + +int aac_src_init(struct aac_dev *dev) +{ + unsigned long start; + unsigned long status; + int restart = 0; + int instance = dev->id; + const char *name = dev->name; + + dev->a_ops.adapter_ioremap = aac_src_ioremap; + dev->a_ops.adapter_comm = aac_src_select_comm; + + dev->base_size = AAC_MIN_SRC_BAR0_SIZE; + if (aac_adapter_ioremap(dev, dev->base_size)) { + printk(KERN_WARNING "%s: unable to map adapter.\n", name); + goto error_iounmap; + } + + /* Failure to reset here is an option ... */ + dev->a_ops.adapter_sync_cmd = src_sync_cmd; + dev->a_ops.adapter_enable_int = aac_src_disable_interrupt; + if ((aac_reset_devices || reset_devices) && + !aac_src_restart_adapter(dev, 0)) + ++restart; + /* + * Check to see if the board panic'd while booting. + */ + status = src_readl(dev, MUnit.OMR); + if (status & KERNEL_PANIC) { + if (aac_src_restart_adapter(dev, aac_src_check_health(dev))) + goto error_iounmap; + ++restart; + } + /* + * Check to see if the board failed any self tests. + */ + status = src_readl(dev, MUnit.OMR); + if (status & SELF_TEST_FAILED) { + printk(KERN_ERR "%s%d: adapter self-test failed.\n", + dev->name, instance); + goto error_iounmap; + } + /* + * Check to see if the monitor panic'd while booting. + */ + if (status & MONITOR_PANIC) { + printk(KERN_ERR "%s%d: adapter monitor panic.\n", + dev->name, instance); + goto error_iounmap; + } + start = jiffies; + /* + * Wait for the adapter to be up and running. Wait up to 3 minutes + */ + while (!((status = src_readl(dev, MUnit.OMR)) & + KERNEL_UP_AND_RUNNING)) { + if ((restart && + (status & (KERNEL_PANIC|SELF_TEST_FAILED|MONITOR_PANIC))) || + time_after(jiffies, start+HZ*startup_timeout)) { + printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %lx.\n", + dev->name, instance, status); + goto error_iounmap; + } + if (!restart && + ((status & (KERNEL_PANIC|SELF_TEST_FAILED|MONITOR_PANIC)) || + time_after(jiffies, start + HZ * + ((startup_timeout > 60) + ? (startup_timeout - 60) + : (startup_timeout / 2))))) { + if (likely(!aac_src_restart_adapter(dev, + aac_src_check_health(dev)))) + start = jiffies; + ++restart; + } + msleep(1); + } + if (restart && aac_commit) + aac_commit = 1; + /* + * Fill in the common function dispatch table. + */ + dev->a_ops.adapter_interrupt = aac_src_interrupt_adapter; + dev->a_ops.adapter_disable_int = aac_src_disable_interrupt; + dev->a_ops.adapter_notify = aac_src_notify_adapter; + dev->a_ops.adapter_sync_cmd = src_sync_cmd; + dev->a_ops.adapter_check_health = aac_src_check_health; + dev->a_ops.adapter_restart = aac_src_restart_adapter; + + /* + * First clear out all interrupts. Then enable the one's that we + * can handle. + */ + aac_adapter_comm(dev, AAC_COMM_MESSAGE); + aac_adapter_disable_int(dev); + src_writel(dev, MUnit.ODR_C, 0xffffffff); + aac_adapter_enable_int(dev); + + if (aac_init_adapter(dev) == NULL) + goto error_iounmap; + if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE1) + goto error_iounmap; + + dev->msi = aac_msi && !pci_enable_msi(dev->pdev); + + if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr, + IRQF_SHARED|IRQF_DISABLED, "aacraid", dev) < 0) { + + if (dev->msi) + pci_disable_msi(dev->pdev); + + printk(KERN_ERR "%s%d: Interrupt unavailable.\n", + name, instance); + goto error_iounmap; + } + dev->dbg_base = pci_resource_start(dev->pdev, 2); + dev->dbg_base_mapped = dev->regs.src.bar1; + dev->dbg_size = AAC_MIN_SRC_BAR1_SIZE; + + aac_adapter_enable_int(dev); + /* + * Tell the adapter that all is configured, and it can + * start accepting requests + */ + aac_src_start_adapter(dev); + + return 0; + +error_iounmap: + + return -1; +} diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h index df2fc09..b6d350a 100644 --- a/drivers/scsi/bnx2fc/bnx2fc.h +++ b/drivers/scsi/bnx2fc/bnx2fc.h @@ -62,7 +62,7 @@ #include "bnx2fc_constants.h" #define BNX2FC_NAME "bnx2fc" -#define BNX2FC_VERSION "1.0.0" +#define BNX2FC_VERSION "1.0.1" #define PFX "bnx2fc: " @@ -84,9 +84,15 @@ #define BNX2FC_NUM_MAX_SESS 128 #define BNX2FC_NUM_MAX_SESS_LOG (ilog2(BNX2FC_NUM_MAX_SESS)) -#define BNX2FC_MAX_OUTSTANDING_CMNDS 4096 +#define BNX2FC_MAX_OUTSTANDING_CMNDS 2048 +#define BNX2FC_CAN_QUEUE BNX2FC_MAX_OUTSTANDING_CMNDS +#define BNX2FC_ELSTM_XIDS BNX2FC_CAN_QUEUE #define BNX2FC_MIN_PAYLOAD 256 #define BNX2FC_MAX_PAYLOAD 2048 +#define BNX2FC_MFS \ + (BNX2FC_MAX_PAYLOAD + sizeof(struct fc_frame_header)) +#define BNX2FC_MINI_JUMBO_MTU 2500 + #define BNX2FC_RQ_BUF_SZ 256 #define BNX2FC_RQ_BUF_LOG_SZ (ilog2(BNX2FC_RQ_BUF_SZ)) @@ -98,7 +104,8 @@ #define BNX2FC_CONFQ_WQE_SIZE (sizeof(struct fcoe_confqe)) #define BNX2FC_5771X_DB_PAGE_SIZE 128 -#define BNX2FC_MAX_TASKS BNX2FC_MAX_OUTSTANDING_CMNDS +#define BNX2FC_MAX_TASKS \ + (BNX2FC_MAX_OUTSTANDING_CMNDS + BNX2FC_ELSTM_XIDS) #define BNX2FC_TASK_SIZE 128 #define BNX2FC_TASKS_PER_PAGE (PAGE_SIZE/BNX2FC_TASK_SIZE) #define BNX2FC_TASK_CTX_ARR_SZ (BNX2FC_MAX_TASKS/BNX2FC_TASKS_PER_PAGE) @@ -112,10 +119,10 @@ #define BNX2FC_WRITE (1 << 0) #define BNX2FC_MIN_XID 0 -#define BNX2FC_MAX_XID (BNX2FC_MAX_OUTSTANDING_CMNDS - 1) -#define FCOE_MIN_XID (BNX2FC_MAX_OUTSTANDING_CMNDS) -#define FCOE_MAX_XID \ - (BNX2FC_MAX_OUTSTANDING_CMNDS + (nr_cpu_ids * 256)) +#define BNX2FC_MAX_XID \ + (BNX2FC_MAX_OUTSTANDING_CMNDS + BNX2FC_ELSTM_XIDS - 1) +#define FCOE_MIN_XID (BNX2FC_MAX_XID + 1) +#define FCOE_MAX_XID (FCOE_MIN_XID + 4095) #define BNX2FC_MAX_LUN 0xFFFF #define BNX2FC_MAX_FCP_TGT 256 #define BNX2FC_MAX_CMD_LEN 16 @@ -125,7 +132,6 @@ #define BNX2FC_WAIT_CNT 120 #define BNX2FC_FW_TIMEOUT (3 * HZ) - #define PORT_MAX 2 #define CMD_SCSI_STATUS(Cmnd) ((Cmnd)->SCp.Status) diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index e476e87..e2e6475 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -21,7 +21,7 @@ DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu); #define DRV_MODULE_NAME "bnx2fc" #define DRV_MODULE_VERSION BNX2FC_VERSION -#define DRV_MODULE_RELDATE "Jan 25, 2011" +#define DRV_MODULE_RELDATE "Mar 17, 2011" static char version[] __devinitdata = @@ -437,17 +437,16 @@ static int bnx2fc_l2_rcv_thread(void *arg) set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { schedule(); - set_current_state(TASK_RUNNING); spin_lock_bh(&bg->fcoe_rx_list.lock); while ((skb = __skb_dequeue(&bg->fcoe_rx_list)) != NULL) { spin_unlock_bh(&bg->fcoe_rx_list.lock); bnx2fc_recv_frame(skb); spin_lock_bh(&bg->fcoe_rx_list.lock); } + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_bh(&bg->fcoe_rx_list.lock); - set_current_state(TASK_INTERRUPTIBLE); } - set_current_state(TASK_RUNNING); + __set_current_state(TASK_RUNNING); return 0; } @@ -569,7 +568,6 @@ int bnx2fc_percpu_io_thread(void *arg) set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { schedule(); - set_current_state(TASK_RUNNING); spin_lock_bh(&p->fp_work_lock); while (!list_empty(&p->work_list)) { list_splice_init(&p->work_list, &work_list); @@ -583,10 +581,10 @@ int bnx2fc_percpu_io_thread(void *arg) spin_lock_bh(&p->fp_work_lock); } + __set_current_state(TASK_INTERRUPTIBLE); spin_unlock_bh(&p->fp_work_lock); - set_current_state(TASK_INTERRUPTIBLE); } - set_current_state(TASK_RUNNING); + __set_current_state(TASK_RUNNING); return 0; } @@ -661,31 +659,6 @@ static int bnx2fc_shost_config(struct fc_lport *lport, struct device *dev) return 0; } -static int bnx2fc_mfs_update(struct fc_lport *lport) -{ - struct fcoe_port *port = lport_priv(lport); - struct bnx2fc_hba *hba = port->priv; - struct net_device *netdev = hba->netdev; - u32 mfs; - u32 max_mfs; - - mfs = netdev->mtu - (sizeof(struct fcoe_hdr) + - sizeof(struct fcoe_crc_eof)); - max_mfs = BNX2FC_MAX_PAYLOAD + sizeof(struct fc_frame_header); - BNX2FC_HBA_DBG(lport, "mfs = %d, max_mfs = %d\n", mfs, max_mfs); - if (mfs > max_mfs) - mfs = max_mfs; - - /* Adjust mfs to be a multiple of 256 bytes */ - mfs = (((mfs - sizeof(struct fc_frame_header)) / BNX2FC_MIN_PAYLOAD) * - BNX2FC_MIN_PAYLOAD); - mfs = mfs + sizeof(struct fc_frame_header); - - BNX2FC_HBA_DBG(lport, "Set MFS = %d\n", mfs); - if (fc_set_mfs(lport, mfs)) - return -EINVAL; - return 0; -} static void bnx2fc_link_speed_update(struct fc_lport *lport) { struct fcoe_port *port = lport_priv(lport); @@ -754,7 +727,7 @@ static int bnx2fc_net_config(struct fc_lport *lport) !hba->phys_dev->ethtool_ops->get_pauseparam) return -EOPNOTSUPP; - if (bnx2fc_mfs_update(lport)) + if (fc_set_mfs(lport, BNX2FC_MFS)) return -EINVAL; skb_queue_head_init(&port->fcoe_pending_queue); @@ -825,14 +798,6 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event) if (!test_bit(ADAPTER_STATE_UP, &hba->adapter_state)) printk(KERN_ERR "indicate_netevent: "\ "adapter is not UP!!\n"); - /* fall thru to update mfs if MTU has changed */ - case NETDEV_CHANGEMTU: - BNX2FC_HBA_DBG(lport, "NETDEV_CHANGEMTU event\n"); - bnx2fc_mfs_update(lport); - mutex_lock(&lport->lp_mutex); - list_for_each_entry(vport, &lport->vports, list) - bnx2fc_mfs_update(vport); - mutex_unlock(&lport->lp_mutex); break; case NETDEV_DOWN: @@ -1095,13 +1060,6 @@ static int bnx2fc_netdev_setup(struct bnx2fc_hba *hba) struct netdev_hw_addr *ha; int sel_san_mac = 0; - /* Do not support for bonding device */ - if ((netdev->priv_flags & IFF_MASTER_ALB) || - (netdev->priv_flags & IFF_SLAVE_INACTIVE) || - (netdev->priv_flags & IFF_MASTER_8023AD)) { - return -EOPNOTSUPP; - } - /* setup Source MAC Address */ rcu_read_lock(); for_each_dev_addr(physdev, ha) { @@ -1432,16 +1390,9 @@ static int bnx2fc_destroy(struct net_device *netdev) struct net_device *phys_dev; int rc = 0; - if (!rtnl_trylock()) - return restart_syscall(); + rtnl_lock(); mutex_lock(&bnx2fc_dev_lock); -#ifdef CONFIG_SCSI_BNX2X_FCOE_MODULE - if (THIS_MODULE->state != MODULE_STATE_LIVE) { - rc = -ENODEV; - goto netdev_err; - } -#endif /* obtain physical netdev */ if (netdev->priv_flags & IFF_802_1Q_VLAN) phys_dev = vlan_dev_real_dev(netdev); @@ -1805,18 +1756,10 @@ static int bnx2fc_disable(struct net_device *netdev) struct ethtool_drvinfo drvinfo; int rc = 0; - if (!rtnl_trylock()) { - printk(KERN_ERR PFX "retrying for rtnl_lock\n"); - return -EIO; - } + rtnl_lock(); mutex_lock(&bnx2fc_dev_lock); - if (THIS_MODULE->state != MODULE_STATE_LIVE) { - rc = -ENODEV; - goto nodev; - } - /* obtain physical netdev */ if (netdev->priv_flags & IFF_802_1Q_VLAN) phys_dev = vlan_dev_real_dev(netdev); @@ -1867,19 +1810,11 @@ static int bnx2fc_enable(struct net_device *netdev) struct ethtool_drvinfo drvinfo; int rc = 0; - if (!rtnl_trylock()) { - printk(KERN_ERR PFX "retrying for rtnl_lock\n"); - return -EIO; - } + rtnl_lock(); BNX2FC_MISC_DBG("Entered %s\n", __func__); mutex_lock(&bnx2fc_dev_lock); - if (THIS_MODULE->state != MODULE_STATE_LIVE) { - rc = -ENODEV; - goto nodev; - } - /* obtain physical netdev */ if (netdev->priv_flags & IFF_802_1Q_VLAN) phys_dev = vlan_dev_real_dev(netdev); @@ -1942,18 +1877,9 @@ static int bnx2fc_create(struct net_device *netdev, enum fip_state fip_mode) return -EIO; } - if (!rtnl_trylock()) { - printk(KERN_ERR "trying for rtnl_lock\n"); - return -EIO; - } - mutex_lock(&bnx2fc_dev_lock); + rtnl_lock(); -#ifdef CONFIG_SCSI_BNX2X_FCOE_MODULE - if (THIS_MODULE->state != MODULE_STATE_LIVE) { - rc = -ENODEV; - goto mod_err; - } -#endif + mutex_lock(&bnx2fc_dev_lock); if (!try_module_get(THIS_MODULE)) { rc = -EINVAL; @@ -2506,7 +2432,7 @@ static struct scsi_host_template bnx2fc_shost_template = { .change_queue_type = fc_change_queue_type, .this_id = -1, .cmd_per_lun = 3, - .can_queue = (BNX2FC_MAX_OUTSTANDING_CMNDS/2), + .can_queue = BNX2FC_CAN_QUEUE, .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = BNX2FC_MAX_BDS_PER_CMD, .max_sectors = 512, diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c index 4f40968..1b680e2 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c +++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c @@ -87,7 +87,7 @@ int bnx2fc_send_fw_fcoe_init_msg(struct bnx2fc_hba *hba) fcoe_init1.task_list_pbl_addr_lo = (u32) hba->task_ctx_bd_dma; fcoe_init1.task_list_pbl_addr_hi = (u32) ((u64) hba->task_ctx_bd_dma >> 32); - fcoe_init1.mtu = hba->netdev->mtu; + fcoe_init1.mtu = BNX2FC_MINI_JUMBO_MTU; fcoe_init1.flags = (PAGE_SHIFT << FCOE_KWQE_INIT1_LOG_PAGE_SIZE_SHIFT); @@ -590,7 +590,10 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) num_rq = (frame_len + BNX2FC_RQ_BUF_SZ - 1) / BNX2FC_RQ_BUF_SZ; + spin_lock_bh(&tgt->tgt_lock); rq_data = (unsigned char *)bnx2fc_get_next_rqe(tgt, num_rq); + spin_unlock_bh(&tgt->tgt_lock); + if (rq_data) { buf = rq_data; } else { @@ -603,8 +606,10 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) } for (i = 0; i < num_rq; i++) { + spin_lock_bh(&tgt->tgt_lock); rq_data = (unsigned char *) bnx2fc_get_next_rqe(tgt, 1); + spin_unlock_bh(&tgt->tgt_lock); len = BNX2FC_RQ_BUF_SZ; memcpy(buf1, rq_data, len); buf1 += len; @@ -615,13 +620,15 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) if (buf != rq_data) kfree(buf); + spin_lock_bh(&tgt->tgt_lock); bnx2fc_return_rqe(tgt, num_rq); + spin_unlock_bh(&tgt->tgt_lock); break; case FCOE_ERROR_DETECTION_CQE_TYPE: /* - *In case of error reporting CQE a single RQ entry - * is consumes. + * In case of error reporting CQE a single RQ entry + * is consumed. */ spin_lock_bh(&tgt->tgt_lock); num_rq = 1; @@ -705,6 +712,7 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) *In case of warning reporting CQE a single RQ entry * is consumes. */ + spin_lock_bh(&tgt->tgt_lock); num_rq = 1; err_entry = (struct fcoe_err_report_entry *) bnx2fc_get_next_rqe(tgt, 1); @@ -717,6 +725,7 @@ static void bnx2fc_process_unsol_compl(struct bnx2fc_rport *tgt, u16 wqe) err_entry->tx_buf_off, err_entry->rx_buf_off); bnx2fc_return_rqe(tgt, 1); + spin_unlock_bh(&tgt->tgt_lock); break; default: diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c index 0f1dd23..d3fc302 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_io.c +++ b/drivers/scsi/bnx2fc/bnx2fc_io.c @@ -11,6 +11,9 @@ */ #include "bnx2fc.h" + +#define RESERVE_FREE_LIST_INDEX num_possible_cpus() + static int bnx2fc_split_bd(struct bnx2fc_cmd *io_req, u64 addr, int sg_len, int bd_index); static int bnx2fc_map_sg(struct bnx2fc_cmd *io_req); @@ -242,8 +245,9 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba, u32 mem_size; u16 xid; int i; - int num_ios; + int num_ios, num_pri_ios; size_t bd_tbl_sz; + int arr_sz = num_possible_cpus() + 1; if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN) { printk(KERN_ERR PFX "cmd_mgr_alloc: Invalid min_xid 0x%x \ @@ -263,14 +267,14 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba, } cmgr->free_list = kzalloc(sizeof(*cmgr->free_list) * - num_possible_cpus(), GFP_KERNEL); + arr_sz, GFP_KERNEL); if (!cmgr->free_list) { printk(KERN_ERR PFX "failed to alloc free_list\n"); goto mem_err; } cmgr->free_list_lock = kzalloc(sizeof(*cmgr->free_list_lock) * - num_possible_cpus(), GFP_KERNEL); + arr_sz, GFP_KERNEL); if (!cmgr->free_list_lock) { printk(KERN_ERR PFX "failed to alloc free_list_lock\n"); goto mem_err; @@ -279,13 +283,18 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba, cmgr->hba = hba; cmgr->cmds = (struct bnx2fc_cmd **)(cmgr + 1); - for (i = 0; i < num_possible_cpus(); i++) { + for (i = 0; i < arr_sz; i++) { INIT_LIST_HEAD(&cmgr->free_list[i]); spin_lock_init(&cmgr->free_list_lock[i]); } - /* Pre-allocated pool of bnx2fc_cmds */ + /* + * Pre-allocated pool of bnx2fc_cmds. + * Last entry in the free list array is the free list + * of slow path requests. + */ xid = BNX2FC_MIN_XID; + num_pri_ios = num_ios - BNX2FC_ELSTM_XIDS; for (i = 0; i < num_ios; i++) { io_req = kzalloc(sizeof(*io_req), GFP_KERNEL); @@ -298,11 +307,13 @@ struct bnx2fc_cmd_mgr *bnx2fc_cmd_mgr_alloc(struct bnx2fc_hba *hba, INIT_DELAYED_WORK(&io_req->timeout_work, bnx2fc_cmd_timeout); io_req->xid = xid++; - if (io_req->xid >= BNX2FC_MAX_OUTSTANDING_CMNDS) - printk(KERN_ERR PFX "ERROR allocating xids - 0x%x\n", - io_req->xid); - list_add_tail(&io_req->link, - &cmgr->free_list[io_req->xid % num_possible_cpus()]); + if (i < num_pri_ios) + list_add_tail(&io_req->link, + &cmgr->free_list[io_req->xid % + num_possible_cpus()]); + else + list_add_tail(&io_req->link, + &cmgr->free_list[num_possible_cpus()]); io_req++; } @@ -389,7 +400,7 @@ free_cmd_pool: if (!cmgr->free_list) goto free_cmgr; - for (i = 0; i < num_possible_cpus(); i++) { + for (i = 0; i < num_possible_cpus() + 1; i++) { struct list_head *list; struct list_head *tmp; @@ -413,6 +424,7 @@ struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type) struct bnx2fc_cmd *io_req; struct list_head *listp; struct io_bdt *bd_tbl; + int index = RESERVE_FREE_LIST_INDEX; u32 max_sqes; u16 xid; @@ -432,26 +444,26 @@ struct bnx2fc_cmd *bnx2fc_elstm_alloc(struct bnx2fc_rport *tgt, int type) * NOTE: Free list insertions and deletions are protected with * cmgr lock */ - spin_lock_bh(&cmd_mgr->free_list_lock[smp_processor_id()]); - if ((list_empty(&(cmd_mgr->free_list[smp_processor_id()]))) || + spin_lock_bh(&cmd_mgr->free_list_lock[index]); + if ((list_empty(&(cmd_mgr->free_list[index]))) || (tgt->num_active_ios.counter >= max_sqes)) { BNX2FC_TGT_DBG(tgt, "No free els_tm cmds available " "ios(%d):sqes(%d)\n", tgt->num_active_ios.counter, tgt->max_sqes); - if (list_empty(&(cmd_mgr->free_list[smp_processor_id()]))) + if (list_empty(&(cmd_mgr->free_list[index]))) printk(KERN_ERR PFX "elstm_alloc: list_empty\n"); - spin_unlock_bh(&cmd_mgr->free_list_lock[smp_processor_id()]); + spin_unlock_bh(&cmd_mgr->free_list_lock[index]); return NULL; } listp = (struct list_head *) - cmd_mgr->free_list[smp_processor_id()].next; + cmd_mgr->free_list[index].next; list_del_init(listp); io_req = (struct bnx2fc_cmd *) listp; xid = io_req->xid; cmd_mgr->cmds[xid] = io_req; atomic_inc(&tgt->num_active_ios); - spin_unlock_bh(&cmd_mgr->free_list_lock[smp_processor_id()]); + spin_unlock_bh(&cmd_mgr->free_list_lock[index]); INIT_LIST_HEAD(&io_req->link); @@ -479,27 +491,30 @@ static struct bnx2fc_cmd *bnx2fc_cmd_alloc(struct bnx2fc_rport *tgt) struct io_bdt *bd_tbl; u32 max_sqes; u16 xid; + int index = get_cpu(); max_sqes = BNX2FC_SCSI_MAX_SQES; /* * NOTE: Free list insertions and deletions are protected with * cmgr lock */ - spin_lock_bh(&cmd_mgr->free_list_lock[smp_processor_id()]); - if ((list_empty(&cmd_mgr->free_list[smp_processor_id()])) || + spin_lock_bh(&cmd_mgr->free_list_lock[index]); + if ((list_empty(&cmd_mgr->free_list[index])) || (tgt->num_active_ios.counter >= max_sqes)) { - spin_unlock_bh(&cmd_mgr->free_list_lock[smp_processor_id()]); + spin_unlock_bh(&cmd_mgr->free_list_lock[index]); + put_cpu(); return NULL; } listp = (struct list_head *) - cmd_mgr->free_list[smp_processor_id()].next; + cmd_mgr->free_list[index].next; list_del_init(listp); io_req = (struct bnx2fc_cmd *) listp; xid = io_req->xid; cmd_mgr->cmds[xid] = io_req; atomic_inc(&tgt->num_active_ios); - spin_unlock_bh(&cmd_mgr->free_list_lock[smp_processor_id()]); + spin_unlock_bh(&cmd_mgr->free_list_lock[index]); + put_cpu(); INIT_LIST_HEAD(&io_req->link); @@ -522,8 +537,15 @@ void bnx2fc_cmd_release(struct kref *ref) struct bnx2fc_cmd *io_req = container_of(ref, struct bnx2fc_cmd, refcount); struct bnx2fc_cmd_mgr *cmd_mgr = io_req->cmd_mgr; + int index; + + if (io_req->cmd_type == BNX2FC_SCSI_CMD) + index = io_req->xid % num_possible_cpus(); + else + index = RESERVE_FREE_LIST_INDEX; - spin_lock_bh(&cmd_mgr->free_list_lock[smp_processor_id()]); + + spin_lock_bh(&cmd_mgr->free_list_lock[index]); if (io_req->cmd_type != BNX2FC_SCSI_CMD) bnx2fc_free_mp_resc(io_req); cmd_mgr->cmds[io_req->xid] = NULL; @@ -531,9 +553,10 @@ void bnx2fc_cmd_release(struct kref *ref) list_del_init(&io_req->link); /* Add it to the free list */ list_add(&io_req->link, - &cmd_mgr->free_list[smp_processor_id()]); + &cmd_mgr->free_list[index]); atomic_dec(&io_req->tgt->num_active_ios); - spin_unlock_bh(&cmd_mgr->free_list_lock[smp_processor_id()]); + spin_unlock_bh(&cmd_mgr->free_list_lock[index]); + } static void bnx2fc_free_mp_resc(struct bnx2fc_cmd *io_req) diff --git a/drivers/scsi/bnx2fc/bnx2fc_tgt.c b/drivers/scsi/bnx2fc/bnx2fc_tgt.c index 7ea93af..7cc05e4 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_tgt.c +++ b/drivers/scsi/bnx2fc/bnx2fc_tgt.c @@ -304,10 +304,8 @@ static void bnx2fc_upload_session(struct fcoe_port *port, " not sent to FW\n"); /* Free session resources */ - spin_lock_bh(&tgt->cq_lock); bnx2fc_free_session_resc(hba, tgt); bnx2fc_free_conn_id(hba, tgt->fcoe_conn_id); - spin_unlock_bh(&tgt->cq_lock); } static int bnx2fc_init_tgt(struct bnx2fc_rport *tgt, @@ -830,11 +828,13 @@ static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba, tgt->rq = NULL; } /* Free CQ */ + spin_lock_bh(&tgt->cq_lock); if (tgt->cq) { dma_free_coherent(&hba->pcidev->dev, tgt->cq_mem_size, tgt->cq, tgt->cq_dma); tgt->cq = NULL; } + spin_unlock_bh(&tgt->cq_lock); /* Free SQ */ if (tgt->sq) { dma_free_coherent(&hba->pcidev->dev, tgt->sq_mem_size, diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index 8eeb39f..e98ae33 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -132,14 +132,25 @@ static void iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv) if (page_count(sg_page(sg)) >= 1 && !recv) return; - segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0); + if (recv) { + segment->atomic_mapped = true; + segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0); + } else { + segment->atomic_mapped = false; + /* the xmit path can sleep with the page mapped so use kmap */ + segment->sg_mapped = kmap(sg_page(sg)); + } + segment->data = segment->sg_mapped + sg->offset + segment->sg_offset; } void iscsi_tcp_segment_unmap(struct iscsi_segment *segment) { if (segment->sg_mapped) { - kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0); + if (segment->atomic_mapped) + kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0); + else + kunmap(sg_page(segment->sg)); segment->sg_mapped = NULL; segment->data = NULL; } diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile index 14de249..88928f0 100644 --- a/drivers/scsi/lpfc/Makefile +++ b/drivers/scsi/lpfc/Makefile @@ -1,7 +1,7 @@ #/******************************************************************* # * This file is part of the Emulex Linux Device Driver for * # * Fibre Channel Host Bus Adapters. * -# * Copyright (C) 2004-2006 Emulex. All rights reserved. * +# * Copyright (C) 2004-2011 Emulex. All rights reserved. * # * EMULEX and SLI are trademarks of Emulex. * # * www.emulex.com * # * * diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index b64c6da..60e98a62 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -539,6 +539,8 @@ struct lpfc_hba { (struct lpfc_hba *, uint32_t); int (*lpfc_hba_down_link) (struct lpfc_hba *, uint32_t); + int (*lpfc_selective_reset) + (struct lpfc_hba *); /* SLI4 specific HBA data structure */ struct lpfc_sli4_hba sli4_hba; @@ -895,7 +897,18 @@ lpfc_worker_wake_up(struct lpfc_hba *phba) return; } -static inline void +static inline int +lpfc_readl(void __iomem *addr, uint32_t *data) +{ + uint32_t temp; + temp = readl(addr); + if (temp == 0xffffffff) + return -EIO; + *data = temp; + return 0; +} + +static inline int lpfc_sli_read_hs(struct lpfc_hba *phba) { /* @@ -904,15 +917,17 @@ lpfc_sli_read_hs(struct lpfc_hba *phba) */ phba->sli.slistat.err_attn_event++; - /* Save status info */ - phba->work_hs = readl(phba->HSregaddr); - phba->work_status[0] = readl(phba->MBslimaddr + 0xa8); - phba->work_status[1] = readl(phba->MBslimaddr + 0xac); + /* Save status info and check for unplug error */ + if (lpfc_readl(phba->HSregaddr, &phba->work_hs) || + lpfc_readl(phba->MBslimaddr + 0xa8, &phba->work_status[0]) || + lpfc_readl(phba->MBslimaddr + 0xac, &phba->work_status[1])) { + return -EIO; + } /* Clear chip Host Attention error bit */ writel(HA_ERATT, phba->HAregaddr); readl(phba->HAregaddr); /* flush */ phba->pport->stopped = 1; - return; + return 0; } diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index e7c020d..4e0faa0 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -685,7 +685,7 @@ lpfc_do_offline(struct lpfc_hba *phba, uint32_t type) * -EIO reset not configured or error posting the event * zero for success **/ -static int +int lpfc_selective_reset(struct lpfc_hba *phba) { struct completion online_compl; @@ -746,7 +746,7 @@ lpfc_issue_reset(struct device *dev, struct device_attribute *attr, int status = -EINVAL; if (strncmp(buf, "selective", sizeof("selective") - 1) == 0) - status = lpfc_selective_reset(phba); + status = phba->lpfc_selective_reset(phba); if (status == 0) return strlen(buf); @@ -1224,7 +1224,10 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr, if (val & ENABLE_FCP_RING_POLLING) { if ((val & DISABLE_FCP_RING_INT) && !(old_val & DISABLE_FCP_RING_INT)) { - creg_val = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &creg_val)) { + spin_unlock_irq(&phba->hbalock); + return -EINVAL; + } creg_val &= ~(HC_R0INT_ENA << LPFC_FCP_RING); writel(creg_val, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ @@ -1242,7 +1245,10 @@ lpfc_poll_store(struct device *dev, struct device_attribute *attr, spin_unlock_irq(&phba->hbalock); del_timer(&phba->fcp_poll_timer); spin_lock_irq(&phba->hbalock); - creg_val = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &creg_val)) { + spin_unlock_irq(&phba->hbalock); + return -EINVAL; + } creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); writel(creg_val, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 0dd43bb..793b9f1 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2009-2010 Emulex. All rights reserved. * + * Copyright (C) 2009-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -348,7 +348,10 @@ lpfc_bsg_send_mgmt_cmd(struct fc_bsg_job *job) dd_data->context_un.iocb.bmp = bmp; if (phba->cfg_poll & DISABLE_FCP_RING_INT) { - creg_val = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &creg_val)) { + rc = -EIO ; + goto free_cmdiocbq; + } creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); writel(creg_val, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ @@ -599,7 +602,10 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) dd_data->context_un.iocb.ndlp = ndlp; if (phba->cfg_poll & DISABLE_FCP_RING_INT) { - creg_val = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &creg_val)) { + rc = -EIO; + goto linkdown_err; + } creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); writel(creg_val, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ @@ -613,6 +619,7 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job) else rc = -EIO; +linkdown_err: pci_unmap_sg(phba->pcidev, job->request_payload.sg_list, job->request_payload.sg_cnt, DMA_TO_DEVICE); pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list, @@ -1357,7 +1364,10 @@ lpfc_issue_ct_rsp(struct lpfc_hba *phba, struct fc_bsg_job *job, uint32_t tag, dd_data->context_un.iocb.ndlp = ndlp; if (phba->cfg_poll & DISABLE_FCP_RING_INT) { - creg_val = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &creg_val)) { + rc = -IOCB_ERROR; + goto issue_ct_rsp_exit; + } creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); writel(creg_val, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ @@ -2479,16 +2489,18 @@ lpfc_bsg_wake_mbox_wait(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) from = (uint8_t *)dd_data->context_un.mbox.mb; job = dd_data->context_un.mbox.set_job; - 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, - from, size); - job->reply->result = 0; + if (job) { + 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, + from, size); + job->reply->result = 0; + job->dd_data = NULL; + job->job_done(job); + } 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 diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index 3d40023..f0b332f 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2010 Emulex. All rights reserved. * + * Copyright (C) 2004-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * @@ -254,8 +254,8 @@ uint16_t lpfc_sli_next_iotag(struct lpfc_hba *, struct lpfc_iocbq *); void lpfc_sli_cancel_iocbs(struct lpfc_hba *, struct list_head *, uint32_t, uint32_t); void lpfc_sli_wake_mbox_wait(struct lpfc_hba *, LPFC_MBOXQ_t *); - -void lpfc_reset_barrier(struct lpfc_hba * phba); +int lpfc_selective_reset(struct lpfc_hba *); +void lpfc_reset_barrier(struct lpfc_hba *); int lpfc_sli_brdready(struct lpfc_hba *, uint32_t); int lpfc_sli_brdkill(struct lpfc_hba *); int lpfc_sli_brdreset(struct lpfc_hba *); diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 8e28edf..735028f 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -89,7 +89,8 @@ lpfc_els_chk_latt(struct lpfc_vport *vport) return 0; /* Read the HBA Host Attention Register */ - ha_copy = readl(phba->HAregaddr); + if (lpfc_readl(phba->HAregaddr, &ha_copy)) + return 1; if (!(ha_copy & HA_LATT)) return 0; diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 94ae37c..95f11ed 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1344,7 +1344,7 @@ typedef struct { /* FireFly BIU registers */ #define HS_FFER1 0x80000000 /* Bit 31 */ #define HS_CRIT_TEMP 0x00000100 /* Bit 8 */ #define HS_FFERM 0xFF000100 /* Mask for error bits 31:24 and 8 */ - +#define UNPLUG_ERR 0x00000001 /* Indicate pci hot unplug */ /* Host Control Register */ #define HC_REG_OFFSET 12 /* Byte offset from register base address */ @@ -1713,6 +1713,17 @@ struct lpfc_pde6 { #define pde6_apptagval_WORD word2 }; +struct lpfc_pde7 { + uint32_t word0; +#define pde7_type_SHIFT 24 +#define pde7_type_MASK 0x000000ff +#define pde7_type_WORD word0 +#define pde7_rsvd0_SHIFT 0 +#define pde7_rsvd0_MASK 0x00ffffff +#define pde7_rsvd0_WORD word0 + uint32_t addrHigh; + uint32_t addrLow; +}; /* Structure for MB Command LOAD_SM and DOWN_LOAD */ @@ -3621,7 +3632,7 @@ typedef struct _IOCB { /* IOCB structure */ ASYNCSTAT_FIELDS asyncstat; /* async_status iocb */ QUE_XRI64_CX_FIELDS quexri64cx; /* que_xri64_cx fields */ struct rcv_seq64 rcvseq64; /* RCV_SEQ64 and RCV_CONT64 */ - struct sli4_bls_acc bls_acc; /* UNSOL ABTS BLS_ACC params */ + struct sli4_bls_rsp bls_rsp; /* UNSOL ABTS BLS_RSP params */ uint32_t ulpWord[IOCB_WORD_SZ - 2]; /* generic 6 'words' */ } un; union { diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index c7178d6..8433ac0 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -215,7 +215,7 @@ struct lpfc_sli4_flags { #define lpfc_fip_flag_WORD word0 }; -struct sli4_bls_acc { +struct sli4_bls_rsp { uint32_t word0_rsvd; /* Word0 must be reserved */ uint32_t word1; #define lpfc_abts_orig_SHIFT 0 @@ -231,6 +231,16 @@ struct sli4_bls_acc { #define lpfc_abts_oxid_MASK 0x0000FFFF #define lpfc_abts_oxid_WORD word2 uint32_t word3; +#define lpfc_vndr_code_SHIFT 0 +#define lpfc_vndr_code_MASK 0x000000FF +#define lpfc_vndr_code_WORD word3 +#define lpfc_rsn_expln_SHIFT 8 +#define lpfc_rsn_expln_MASK 0x000000FF +#define lpfc_rsn_expln_WORD word3 +#define lpfc_rsn_code_SHIFT 16 +#define lpfc_rsn_code_MASK 0x000000FF +#define lpfc_rsn_code_WORD word3 + uint32_t word4; uint32_t word5_rsvd; /* Word5 must be reserved */ }; @@ -711,21 +721,27 @@ struct lpfc_sli4_cfg_mhdr { union lpfc_sli4_cfg_shdr { struct { uint32_t word6; -#define lpfc_mbox_hdr_opcode_SHIFT 0 -#define lpfc_mbox_hdr_opcode_MASK 0x000000FF -#define lpfc_mbox_hdr_opcode_WORD word6 -#define lpfc_mbox_hdr_subsystem_SHIFT 8 -#define lpfc_mbox_hdr_subsystem_MASK 0x000000FF -#define lpfc_mbox_hdr_subsystem_WORD word6 -#define lpfc_mbox_hdr_port_number_SHIFT 16 -#define lpfc_mbox_hdr_port_number_MASK 0x000000FF -#define lpfc_mbox_hdr_port_number_WORD word6 -#define lpfc_mbox_hdr_domain_SHIFT 24 -#define lpfc_mbox_hdr_domain_MASK 0x000000FF -#define lpfc_mbox_hdr_domain_WORD word6 +#define lpfc_mbox_hdr_opcode_SHIFT 0 +#define lpfc_mbox_hdr_opcode_MASK 0x000000FF +#define lpfc_mbox_hdr_opcode_WORD word6 +#define lpfc_mbox_hdr_subsystem_SHIFT 8 +#define lpfc_mbox_hdr_subsystem_MASK 0x000000FF +#define lpfc_mbox_hdr_subsystem_WORD word6 +#define lpfc_mbox_hdr_port_number_SHIFT 16 +#define lpfc_mbox_hdr_port_number_MASK 0x000000FF +#define lpfc_mbox_hdr_port_number_WORD word6 +#define lpfc_mbox_hdr_domain_SHIFT 24 +#define lpfc_mbox_hdr_domain_MASK 0x000000FF +#define lpfc_mbox_hdr_domain_WORD word6 uint32_t timeout; uint32_t request_length; - uint32_t reserved9; + uint32_t word9; +#define lpfc_mbox_hdr_version_SHIFT 0 +#define lpfc_mbox_hdr_version_MASK 0x000000FF +#define lpfc_mbox_hdr_version_WORD word9 +#define LPFC_Q_CREATE_VERSION_2 2 +#define LPFC_Q_CREATE_VERSION_1 1 +#define LPFC_Q_CREATE_VERSION_0 0 } request; struct { uint32_t word6; @@ -917,9 +933,12 @@ struct cq_context { #define LPFC_CQ_CNT_512 0x1 #define LPFC_CQ_CNT_1024 0x2 uint32_t word1; -#define lpfc_cq_eq_id_SHIFT 22 +#define lpfc_cq_eq_id_SHIFT 22 /* Version 0 Only */ #define lpfc_cq_eq_id_MASK 0x000000FF #define lpfc_cq_eq_id_WORD word1 +#define lpfc_cq_eq_id_2_SHIFT 0 /* Version 2 Only */ +#define lpfc_cq_eq_id_2_MASK 0x0000FFFF +#define lpfc_cq_eq_id_2_WORD word1 uint32_t reserved0; uint32_t reserved1; }; @@ -929,6 +948,9 @@ struct lpfc_mbx_cq_create { union { struct { uint32_t word0; +#define lpfc_mbx_cq_create_page_size_SHIFT 16 /* Version 2 Only */ +#define lpfc_mbx_cq_create_page_size_MASK 0x000000FF +#define lpfc_mbx_cq_create_page_size_WORD word0 #define lpfc_mbx_cq_create_num_pages_SHIFT 0 #define lpfc_mbx_cq_create_num_pages_MASK 0x0000FFFF #define lpfc_mbx_cq_create_num_pages_WORD word0 @@ -969,7 +991,7 @@ struct wq_context { struct lpfc_mbx_wq_create { struct mbox_header header; union { - struct { + struct { /* Version 0 Request */ uint32_t word0; #define lpfc_mbx_wq_create_num_pages_SHIFT 0 #define lpfc_mbx_wq_create_num_pages_MASK 0x0000FFFF @@ -979,6 +1001,23 @@ struct lpfc_mbx_wq_create { #define lpfc_mbx_wq_create_cq_id_WORD word0 struct dma_address page[LPFC_MAX_WQ_PAGE]; } request; + struct { /* Version 1 Request */ + uint32_t word0; /* Word 0 is the same as in v0 */ + uint32_t word1; +#define lpfc_mbx_wq_create_page_size_SHIFT 0 +#define lpfc_mbx_wq_create_page_size_MASK 0x000000FF +#define lpfc_mbx_wq_create_page_size_WORD word1 +#define lpfc_mbx_wq_create_wqe_size_SHIFT 8 +#define lpfc_mbx_wq_create_wqe_size_MASK 0x0000000F +#define lpfc_mbx_wq_create_wqe_size_WORD word1 +#define LPFC_WQ_WQE_SIZE_64 0x5 +#define LPFC_WQ_WQE_SIZE_128 0x6 +#define lpfc_mbx_wq_create_wqe_count_SHIFT 16 +#define lpfc_mbx_wq_create_wqe_count_MASK 0x0000FFFF +#define lpfc_mbx_wq_create_wqe_count_WORD word1 + uint32_t word2; + struct dma_address page[LPFC_MAX_WQ_PAGE-1]; + } request_1; struct { uint32_t word0; #define lpfc_mbx_wq_create_q_id_SHIFT 0 @@ -1007,13 +1046,22 @@ struct lpfc_mbx_wq_destroy { #define LPFC_DATA_BUF_SIZE 2048 struct rq_context { uint32_t word0; -#define lpfc_rq_context_rq_size_SHIFT 16 -#define lpfc_rq_context_rq_size_MASK 0x0000000F -#define lpfc_rq_context_rq_size_WORD word0 +#define lpfc_rq_context_rqe_count_SHIFT 16 /* Version 0 Only */ +#define lpfc_rq_context_rqe_count_MASK 0x0000000F +#define lpfc_rq_context_rqe_count_WORD word0 #define LPFC_RQ_RING_SIZE_512 9 /* 512 entries */ #define LPFC_RQ_RING_SIZE_1024 10 /* 1024 entries */ #define LPFC_RQ_RING_SIZE_2048 11 /* 2048 entries */ #define LPFC_RQ_RING_SIZE_4096 12 /* 4096 entries */ +#define lpfc_rq_context_rqe_count_1_SHIFT 16 /* Version 1 Only */ +#define lpfc_rq_context_rqe_count_1_MASK 0x0000FFFF +#define lpfc_rq_context_rqe_count_1_WORD word0 +#define lpfc_rq_context_rqe_size_SHIFT 8 /* Version 1 Only */ +#define lpfc_rq_context_rqe_size_MASK 0x0000000F +#define lpfc_rq_context_rqe_size_WORD word0 +#define lpfc_rq_context_page_size_SHIFT 0 /* Version 1 Only */ +#define lpfc_rq_context_page_size_MASK 0x000000FF +#define lpfc_rq_context_page_size_WORD word0 uint32_t reserved1; uint32_t word2; #define lpfc_rq_context_cq_id_SHIFT 16 @@ -1022,7 +1070,7 @@ struct rq_context { #define lpfc_rq_context_buf_size_SHIFT 0 #define lpfc_rq_context_buf_size_MASK 0x0000FFFF #define lpfc_rq_context_buf_size_WORD word2 - uint32_t reserved3; + uint32_t buffer_size; /* Version 1 Only */ }; struct lpfc_mbx_rq_create { @@ -1062,16 +1110,16 @@ struct lpfc_mbx_rq_destroy { struct mq_context { uint32_t word0; -#define lpfc_mq_context_cq_id_SHIFT 22 +#define lpfc_mq_context_cq_id_SHIFT 22 /* Version 0 Only */ #define lpfc_mq_context_cq_id_MASK 0x000003FF #define lpfc_mq_context_cq_id_WORD word0 -#define lpfc_mq_context_count_SHIFT 16 -#define lpfc_mq_context_count_MASK 0x0000000F -#define lpfc_mq_context_count_WORD word0 -#define LPFC_MQ_CNT_16 0x5 -#define LPFC_MQ_CNT_32 0x6 -#define LPFC_MQ_CNT_64 0x7 -#define LPFC_MQ_CNT_128 0x8 +#define lpfc_mq_context_ring_size_SHIFT 16 +#define lpfc_mq_context_ring_size_MASK 0x0000000F +#define lpfc_mq_context_ring_size_WORD word0 +#define LPFC_MQ_RING_SIZE_16 0x5 +#define LPFC_MQ_RING_SIZE_32 0x6 +#define LPFC_MQ_RING_SIZE_64 0x7 +#define LPFC_MQ_RING_SIZE_128 0x8 uint32_t word1; #define lpfc_mq_context_valid_SHIFT 31 #define lpfc_mq_context_valid_MASK 0x00000001 @@ -1105,9 +1153,12 @@ struct lpfc_mbx_mq_create_ext { 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 +#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 +#define lpfc_mbx_mq_create_ext_cq_id_SHIFT 16 /* Version 1 Only */ +#define lpfc_mbx_mq_create_ext_cq_id_MASK 0x0000FFFF +#define lpfc_mbx_mq_create_ext_cq_id_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 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 35665cfb..e6ebe51 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2010 Emulex. All rights reserved. * + * Copyright (C) 2004-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -507,7 +507,10 @@ lpfc_config_port_post(struct lpfc_hba *phba) phba->hba_flag &= ~HBA_ERATT_HANDLED; /* Enable appropriate host interrupts */ - status = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &status)) { + spin_unlock_irq(&phba->hbalock); + return -EIO; + } status |= HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA; if (psli->num_rings > 0) status |= HC_R0INT_ENA; @@ -1222,7 +1225,10 @@ lpfc_handle_deferred_eratt(struct lpfc_hba *phba) /* Wait for the ER1 bit to clear.*/ while (phba->work_hs & HS_FFER1) { msleep(100); - phba->work_hs = readl(phba->HSregaddr); + if (lpfc_readl(phba->HSregaddr, &phba->work_hs)) { + phba->work_hs = UNPLUG_ERR ; + break; + } /* If driver is unloading let the worker thread continue */ if (phba->pport->load_flag & FC_UNLOADING) { phba->work_hs = 0; @@ -4474,6 +4480,7 @@ lpfc_init_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp) { phba->lpfc_hba_init_link = lpfc_hba_init_link; phba->lpfc_hba_down_link = lpfc_hba_down_link; + phba->lpfc_selective_reset = lpfc_selective_reset; switch (dev_grp) { case LPFC_PCI_DEV_LP: phba->lpfc_hba_down_post = lpfc_hba_down_post_s3; @@ -5385,13 +5392,16 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba) int i, port_error = 0; uint32_t if_type; + memset(&portsmphr_reg, 0, sizeof(portsmphr_reg)); + memset(®_data, 0, sizeof(reg_data)); if (!phba->sli4_hba.PSMPHRregaddr) return -ENODEV; /* Wait up to 30 seconds for the SLI Port POST done and ready */ for (i = 0; i < 3000; i++) { - portsmphr_reg.word0 = readl(phba->sli4_hba.PSMPHRregaddr); - if (bf_get(lpfc_port_smphr_perr, &portsmphr_reg)) { + if (lpfc_readl(phba->sli4_hba.PSMPHRregaddr, + &portsmphr_reg.word0) || + (bf_get(lpfc_port_smphr_perr, &portsmphr_reg))) { /* Port has a fatal POST error, break out */ port_error = -ENODEV; break; @@ -5472,9 +5482,9 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba) break; case LPFC_SLI_INTF_IF_TYPE_2: /* Final checks. The port status should be clean. */ - reg_data.word0 = - readl(phba->sli4_hba.u.if_type2.STATUSregaddr); - if (bf_get(lpfc_sliport_status_err, ®_data)) { + if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr, + ®_data.word0) || + bf_get(lpfc_sliport_status_err, ®_data)) { phba->work_status[0] = readl(phba->sli4_hba.u.if_type2. ERR1regaddr); @@ -6760,9 +6770,11 @@ lpfc_pci_function_reset(struct lpfc_hba *phba) * the loop again. */ for (rdy_chk = 0; rdy_chk < 1000; rdy_chk++) { - reg_data.word0 = - readl(phba->sli4_hba.u.if_type2. - STATUSregaddr); + if (lpfc_readl(phba->sli4_hba.u.if_type2. + STATUSregaddr, ®_data.word0)) { + rc = -ENODEV; + break; + } if (bf_get(lpfc_sliport_status_rdy, ®_data)) break; if (bf_get(lpfc_sliport_status_rn, ®_data)) { @@ -6783,8 +6795,11 @@ lpfc_pci_function_reset(struct lpfc_hba *phba) } /* Detect any port errors. */ - reg_data.word0 = readl(phba->sli4_hba.u.if_type2. - STATUSregaddr); + if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr, + ®_data.word0)) { + rc = -ENODEV; + break; + } if ((bf_get(lpfc_sliport_status_err, ®_data)) || (rdy_chk >= 1000)) { phba->work_status[0] = readl( diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index bf34178..2b962b0 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2009 Emulex. All rights reserved. * + * Copyright (C) 2004-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -1514,10 +1514,11 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, struct scatterlist *sgpe = NULL; /* s/g prot entry */ struct lpfc_pde5 *pde5 = NULL; struct lpfc_pde6 *pde6 = NULL; - struct ulp_bde64 *prot_bde = NULL; + struct lpfc_pde7 *pde7 = NULL; dma_addr_t dataphysaddr, protphysaddr; unsigned short curr_data = 0, curr_prot = 0; - unsigned int split_offset, protgroup_len; + unsigned int split_offset; + unsigned int protgroup_len, protgroup_offset = 0, protgroup_remainder; unsigned int protgrp_blks, protgrp_bytes; unsigned int remainder, subtotal; int status; @@ -1585,23 +1586,33 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, bpl++; /* setup the first BDE that points to protection buffer */ - prot_bde = (struct ulp_bde64 *) bpl; - protphysaddr = sg_dma_address(sgpe); - prot_bde->addrHigh = le32_to_cpu(putPaddrLow(protphysaddr)); - prot_bde->addrLow = le32_to_cpu(putPaddrHigh(protphysaddr)); - protgroup_len = sg_dma_len(sgpe); + protphysaddr = sg_dma_address(sgpe) + protgroup_offset; + protgroup_len = sg_dma_len(sgpe) - protgroup_offset; /* must be integer multiple of the DIF block length */ BUG_ON(protgroup_len % 8); + pde7 = (struct lpfc_pde7 *) bpl; + memset(pde7, 0, sizeof(struct lpfc_pde7)); + bf_set(pde7_type, pde7, LPFC_PDE7_DESCRIPTOR); + + pde7->addrHigh = le32_to_cpu(putPaddrLow(protphysaddr)); + pde7->addrLow = le32_to_cpu(putPaddrHigh(protphysaddr)); + protgrp_blks = protgroup_len / 8; protgrp_bytes = protgrp_blks * blksize; - prot_bde->tus.f.bdeSize = protgroup_len; - prot_bde->tus.f.bdeFlags = LPFC_PDE7_DESCRIPTOR; - prot_bde->tus.w = le32_to_cpu(bpl->tus.w); + /* check if this pde is crossing the 4K boundary; if so split */ + if ((pde7->addrLow & 0xfff) + protgroup_len > 0x1000) { + protgroup_remainder = 0x1000 - (pde7->addrLow & 0xfff); + protgroup_offset += protgroup_remainder; + protgrp_blks = protgroup_remainder / 8; + protgrp_bytes = protgroup_remainder * blksize; + } else { + protgroup_offset = 0; + curr_prot++; + } - curr_prot++; num_bde++; /* setup BDE's for data blocks associated with DIF data */ @@ -1653,6 +1664,13 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, } + if (protgroup_offset) { + /* update the reference tag */ + reftag += protgrp_blks; + bpl++; + continue; + } + /* are we done ? */ if (curr_prot == protcnt) { alldone = 1; @@ -1675,6 +1693,7 @@ out: return num_bde; } + /* * Given a SCSI command that supports DIF, determine composition of protection * groups involved in setting up buffer lists diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 2ee0374..4746dcd 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2009 Emulex. All rights reserved. * + * Copyright (C) 2004-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -3477,7 +3477,8 @@ lpfc_sli_brdready_s3(struct lpfc_hba *phba, uint32_t mask) int retval = 0; /* Read the HBA Host Status Register */ - status = readl(phba->HSregaddr); + if (lpfc_readl(phba->HSregaddr, &status)) + return 1; /* * Check status register every 100ms for 5 retries, then every @@ -3502,7 +3503,10 @@ lpfc_sli_brdready_s3(struct lpfc_hba *phba, uint32_t mask) lpfc_sli_brdrestart(phba); } /* Read the HBA Host Status Register */ - status = readl(phba->HSregaddr); + if (lpfc_readl(phba->HSregaddr, &status)) { + retval = 1; + break; + } } /* Check to see if any errors occurred during init */ @@ -3584,7 +3588,7 @@ void lpfc_reset_barrier(struct lpfc_hba *phba) uint32_t __iomem *resp_buf; uint32_t __iomem *mbox_buf; volatile uint32_t mbox; - uint32_t hc_copy; + uint32_t hc_copy, ha_copy, resp_data; int i; uint8_t hdrtype; @@ -3601,12 +3605,15 @@ void lpfc_reset_barrier(struct lpfc_hba *phba) resp_buf = phba->MBslimaddr; /* Disable the error attention */ - hc_copy = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &hc_copy)) + return; writel((hc_copy & ~HC_ERINT_ENA), phba->HCregaddr); readl(phba->HCregaddr); /* flush */ phba->link_flag |= LS_IGNORE_ERATT; - if (readl(phba->HAregaddr) & HA_ERATT) { + if (lpfc_readl(phba->HAregaddr, &ha_copy)) + return; + if (ha_copy & HA_ERATT) { /* Clear Chip error bit */ writel(HA_ERATT, phba->HAregaddr); phba->pport->stopped = 1; @@ -3620,11 +3627,18 @@ void lpfc_reset_barrier(struct lpfc_hba *phba) mbox_buf = phba->MBslimaddr; writel(mbox, mbox_buf); - for (i = 0; - readl(resp_buf + 1) != ~(BARRIER_TEST_PATTERN) && i < 50; i++) - mdelay(1); - - if (readl(resp_buf + 1) != ~(BARRIER_TEST_PATTERN)) { + for (i = 0; i < 50; i++) { + if (lpfc_readl((resp_buf + 1), &resp_data)) + return; + if (resp_data != ~(BARRIER_TEST_PATTERN)) + mdelay(1); + else + break; + } + resp_data = 0; + if (lpfc_readl((resp_buf + 1), &resp_data)) + return; + if (resp_data != ~(BARRIER_TEST_PATTERN)) { if (phba->sli.sli_flag & LPFC_SLI_ACTIVE || phba->pport->stopped) goto restore_hc; @@ -3633,13 +3647,26 @@ void lpfc_reset_barrier(struct lpfc_hba *phba) } ((MAILBOX_t *)&mbox)->mbxOwner = OWN_HOST; - for (i = 0; readl(resp_buf) != mbox && i < 500; i++) - mdelay(1); + resp_data = 0; + for (i = 0; i < 500; i++) { + if (lpfc_readl(resp_buf, &resp_data)) + return; + if (resp_data != mbox) + mdelay(1); + else + break; + } clear_errat: - while (!(readl(phba->HAregaddr) & HA_ERATT) && ++i < 500) - mdelay(1); + while (++i < 500) { + if (lpfc_readl(phba->HAregaddr, &ha_copy)) + return; + if (!(ha_copy & HA_ERATT)) + mdelay(1); + else + break; + } if (readl(phba->HAregaddr) & HA_ERATT) { writel(HA_ERATT, phba->HAregaddr); @@ -3686,7 +3713,11 @@ lpfc_sli_brdkill(struct lpfc_hba *phba) /* Disable the error attention */ spin_lock_irq(&phba->hbalock); - status = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &status)) { + spin_unlock_irq(&phba->hbalock); + mempool_free(pmb, phba->mbox_mem_pool); + return 1; + } status &= ~HC_ERINT_ENA; writel(status, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ @@ -3720,11 +3751,12 @@ lpfc_sli_brdkill(struct lpfc_hba *phba) * 3 seconds we still set HBA_ERROR state because the status of the * board is now undefined. */ - ha_copy = readl(phba->HAregaddr); - + if (lpfc_readl(phba->HAregaddr, &ha_copy)) + return 1; while ((i++ < 30) && !(ha_copy & HA_ERATT)) { mdelay(100); - ha_copy = readl(phba->HAregaddr); + if (lpfc_readl(phba->HAregaddr, &ha_copy)) + return 1; } del_timer_sync(&psli->mbox_tmo); @@ -4018,7 +4050,8 @@ lpfc_sli_chipset_init(struct lpfc_hba *phba) uint32_t status, i = 0; /* Read the HBA Host Status Register */ - status = readl(phba->HSregaddr); + if (lpfc_readl(phba->HSregaddr, &status)) + return -EIO; /* Check status register to see what current state is */ i = 0; @@ -4073,7 +4106,8 @@ lpfc_sli_chipset_init(struct lpfc_hba *phba) lpfc_sli_brdrestart(phba); } /* Read the HBA Host Status Register */ - status = readl(phba->HSregaddr); + if (lpfc_readl(phba->HSregaddr, &status)) + return -EIO; } /* Check to see if any errors occurred during init */ @@ -5136,7 +5170,7 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, MAILBOX_t *mb; struct lpfc_sli *psli = &phba->sli; uint32_t status, evtctr; - uint32_t ha_copy; + uint32_t ha_copy, hc_copy; int i; unsigned long timeout; unsigned long drvr_flag = 0; @@ -5202,15 +5236,17 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, goto out_not_finished; } - if (mb->mbxCommand != MBX_KILL_BOARD && flag & MBX_NOWAIT && - !(readl(phba->HCregaddr) & HC_MBINT_ENA)) { - spin_unlock_irqrestore(&phba->hbalock, drvr_flag); - lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, + if (mb->mbxCommand != MBX_KILL_BOARD && flag & MBX_NOWAIT) { + if (lpfc_readl(phba->HCregaddr, &hc_copy) || + !(hc_copy & HC_MBINT_ENA)) { + spin_unlock_irqrestore(&phba->hbalock, drvr_flag); + lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI, "(%d):2528 Mailbox command x%x cannot " "issue Data: x%x x%x\n", pmbox->vport ? pmbox->vport->vpi : 0, pmbox->u.mb.mbxCommand, psli->sli_flag, flag); - goto out_not_finished; + goto out_not_finished; + } } if (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE) { @@ -5408,11 +5444,19 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, word0 = le32_to_cpu(word0); } else { /* First read mbox status word */ - word0 = readl(phba->MBslimaddr); + if (lpfc_readl(phba->MBslimaddr, &word0)) { + spin_unlock_irqrestore(&phba->hbalock, + drvr_flag); + goto out_not_finished; + } } /* Read the HBA Host Attention Register */ - ha_copy = readl(phba->HAregaddr); + if (lpfc_readl(phba->HAregaddr, &ha_copy)) { + spin_unlock_irqrestore(&phba->hbalock, + drvr_flag); + goto out_not_finished; + } timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, mb->mbxCommand) * 1000) + jiffies; @@ -5463,7 +5507,11 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox, word0 = readl(phba->MBslimaddr); } /* Read the HBA Host Attention Register */ - ha_copy = readl(phba->HAregaddr); + if (lpfc_readl(phba->HAregaddr, &ha_copy)) { + spin_unlock_irqrestore(&phba->hbalock, + drvr_flag); + goto out_not_finished; + } } if (psli->sli_flag & LPFC_SLI_ACTIVE) { @@ -6263,7 +6311,6 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq, bf_set(lpfc_sli4_sge_last, sgl, 1); else bf_set(lpfc_sli4_sge_last, sgl, 0); - sgl->word2 = cpu_to_le32(sgl->word2); /* swap the size field back to the cpu so we * can assign it to the sgl. */ @@ -6283,6 +6330,7 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq, bf_set(lpfc_sli4_sge_offset, sgl, offset); offset += bde.tus.f.bdeSize; } + sgl->word2 = cpu_to_le32(sgl->word2); bpl++; sgl++; } @@ -6528,9 +6576,9 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, numBdes = iocbq->iocb.un.genreq64.bdl.bdeSize / sizeof(struct ulp_bde64); for (i = 0; i < numBdes; i++) { - if (bpl[i].tus.f.bdeFlags != BUFF_TYPE_BDE_64) - break; bde.tus.w = le32_to_cpu(bpl[i].tus.w); + if (bde.tus.f.bdeFlags != BUFF_TYPE_BDE_64) + break; xmit_len += bde.tus.f.bdeSize; } /* word3 iocb=IO_TAG wqe=request_payload_len */ @@ -6620,15 +6668,15 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, xritag = 0; break; case CMD_XMIT_BLS_RSP64_CX: - /* As BLS ABTS-ACC WQE is very different from other WQEs, + /* As BLS ABTS RSP WQE is very different from other WQEs, * we re-construct this WQE here based on information in * iocbq from scratch. */ memset(wqe, 0, sizeof(union lpfc_wqe)); /* OX_ID is invariable to who sent ABTS to CT exchange */ bf_set(xmit_bls_rsp64_oxid, &wqe->xmit_bls_rsp, - bf_get(lpfc_abts_oxid, &iocbq->iocb.un.bls_acc)); - if (bf_get(lpfc_abts_orig, &iocbq->iocb.un.bls_acc) == + bf_get(lpfc_abts_oxid, &iocbq->iocb.un.bls_rsp)); + if (bf_get(lpfc_abts_orig, &iocbq->iocb.un.bls_rsp) == LPFC_ABTS_UNSOL_INT) { /* ABTS sent by initiator to CT exchange, the * RX_ID field will be filled with the newly @@ -6642,7 +6690,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, * RX_ID from ABTS. */ bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp, - bf_get(lpfc_abts_rxid, &iocbq->iocb.un.bls_acc)); + bf_get(lpfc_abts_rxid, &iocbq->iocb.un.bls_rsp)); } bf_set(xmit_bls_rsp64_seqcnthi, &wqe->xmit_bls_rsp, 0xffff); bf_set(wqe_xmit_bls_pt, &wqe->xmit_bls_rsp.wqe_dest, 0x1); @@ -6653,6 +6701,15 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, LPFC_WQE_LENLOC_NONE); /* Overwrite the pre-set comnd type with OTHER_COMMAND */ command_type = OTHER_COMMAND; + if (iocbq->iocb.un.xseq64.w5.hcsw.Rctl == FC_RCTL_BA_RJT) { + bf_set(xmit_bls_rsp64_rjt_vspec, &wqe->xmit_bls_rsp, + bf_get(lpfc_vndr_code, &iocbq->iocb.un.bls_rsp)); + bf_set(xmit_bls_rsp64_rjt_expc, &wqe->xmit_bls_rsp, + bf_get(lpfc_rsn_expln, &iocbq->iocb.un.bls_rsp)); + bf_set(xmit_bls_rsp64_rjt_rsnc, &wqe->xmit_bls_rsp, + bf_get(lpfc_rsn_code, &iocbq->iocb.un.bls_rsp)); + } + break; case CMD_XRI_ABORTED_CX: case CMD_CREATE_XRI_CR: /* Do we expect to use this? */ @@ -6701,7 +6758,8 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number, if (piocb->sli4_xritag == NO_XRI) { if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN || - piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN) + piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN || + piocb->iocb.ulpCommand == CMD_XMIT_BLS_RSP64_CX) sglq = NULL; else { if (pring->txq_cnt) { @@ -8194,7 +8252,8 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba, piocb->iocb_flag &= ~LPFC_IO_WAKE; if (phba->cfg_poll & DISABLE_FCP_RING_INT) { - creg_val = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &creg_val)) + return IOCB_ERROR; creg_val |= (HC_R0INT_ENA << LPFC_FCP_RING); writel(creg_val, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ @@ -8236,7 +8295,8 @@ lpfc_sli_issue_iocb_wait(struct lpfc_hba *phba, } if (phba->cfg_poll & DISABLE_FCP_RING_INT) { - creg_val = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &creg_val)) + return IOCB_ERROR; creg_val &= ~(HC_R0INT_ENA << LPFC_FCP_RING); writel(creg_val, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ @@ -8387,10 +8447,13 @@ lpfc_sli_eratt_read(struct lpfc_hba *phba) uint32_t ha_copy; /* Read chip Host Attention (HA) register */ - ha_copy = readl(phba->HAregaddr); + if (lpfc_readl(phba->HAregaddr, &ha_copy)) + goto unplug_err; + if (ha_copy & HA_ERATT) { /* Read host status register to retrieve error event */ - lpfc_sli_read_hs(phba); + if (lpfc_sli_read_hs(phba)) + goto unplug_err; /* Check if there is a deferred error condition is active */ if ((HS_FFER1 & phba->work_hs) && @@ -8409,6 +8472,15 @@ lpfc_sli_eratt_read(struct lpfc_hba *phba) return 1; } return 0; + +unplug_err: + /* Set the driver HS work bitmap */ + phba->work_hs |= UNPLUG_ERR; + /* Set the driver HA work bitmap */ + phba->work_ha |= HA_ERATT; + /* Indicate polling handles this ERATT */ + phba->hba_flag |= HBA_ERATT_HANDLED; + return 1; } /** @@ -8436,8 +8508,15 @@ lpfc_sli4_eratt_read(struct lpfc_hba *phba) if_type = bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf); switch (if_type) { case LPFC_SLI_INTF_IF_TYPE_0: - uerr_sta_lo = readl(phba->sli4_hba.u.if_type0.UERRLOregaddr); - uerr_sta_hi = readl(phba->sli4_hba.u.if_type0.UERRHIregaddr); + if (lpfc_readl(phba->sli4_hba.u.if_type0.UERRLOregaddr, + &uerr_sta_lo) || + lpfc_readl(phba->sli4_hba.u.if_type0.UERRHIregaddr, + &uerr_sta_hi)) { + phba->work_hs |= UNPLUG_ERR; + phba->work_ha |= HA_ERATT; + phba->hba_flag |= HBA_ERATT_HANDLED; + return 1; + } if ((~phba->sli4_hba.ue_mask_lo & uerr_sta_lo) || (~phba->sli4_hba.ue_mask_hi & uerr_sta_hi)) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -8456,9 +8535,15 @@ lpfc_sli4_eratt_read(struct lpfc_hba *phba) } break; case LPFC_SLI_INTF_IF_TYPE_2: - portstat_reg.word0 = - readl(phba->sli4_hba.u.if_type2.STATUSregaddr); - portsmphr = readl(phba->sli4_hba.PSMPHRregaddr); + if (lpfc_readl(phba->sli4_hba.u.if_type2.STATUSregaddr, + &portstat_reg.word0) || + lpfc_readl(phba->sli4_hba.PSMPHRregaddr, + &portsmphr)){ + phba->work_hs |= UNPLUG_ERR; + phba->work_ha |= HA_ERATT; + phba->hba_flag |= HBA_ERATT_HANDLED; + return 1; + } if (bf_get(lpfc_sliport_status_err, &portstat_reg)) { phba->work_status[0] = readl(phba->sli4_hba.u.if_type2.ERR1regaddr); @@ -8639,7 +8724,8 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id) return IRQ_NONE; /* Need to read HA REG for slow-path events */ spin_lock_irqsave(&phba->hbalock, iflag); - ha_copy = readl(phba->HAregaddr); + if (lpfc_readl(phba->HAregaddr, &ha_copy)) + goto unplug_error; /* If somebody is waiting to handle an eratt don't process it * here. The brdkill function will do this. */ @@ -8665,7 +8751,9 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id) } /* Clear up only attention source related to slow-path */ - hc_copy = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &hc_copy)) + goto unplug_error; + writel(hc_copy & ~(HC_MBINT_ENA | HC_R2INT_ENA | HC_LAINT_ENA | HC_ERINT_ENA), phba->HCregaddr); @@ -8688,7 +8776,8 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id) */ spin_lock_irqsave(&phba->hbalock, iflag); phba->sli.sli_flag &= ~LPFC_PROCESS_LA; - control = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &control)) + goto unplug_error; control &= ~HC_LAINT_ENA; writel(control, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ @@ -8708,7 +8797,8 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id) status >>= (4*LPFC_ELS_RING); if (status & HA_RXMASK) { spin_lock_irqsave(&phba->hbalock, iflag); - control = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &control)) + goto unplug_error; lpfc_debugfs_slow_ring_trc(phba, "ISR slow ring: ctl:x%x stat:x%x isrcnt:x%x", @@ -8741,7 +8831,8 @@ lpfc_sli_sp_intr_handler(int irq, void *dev_id) } spin_lock_irqsave(&phba->hbalock, iflag); if (work_ha_copy & HA_ERATT) { - lpfc_sli_read_hs(phba); + if (lpfc_sli_read_hs(phba)) + goto unplug_error; /* * Check if there is a deferred error condition * is active @@ -8872,6 +8963,9 @@ send_current_mbox: lpfc_worker_wake_up(phba); } return IRQ_HANDLED; +unplug_error: + spin_unlock_irqrestore(&phba->hbalock, iflag); + return IRQ_HANDLED; } /* lpfc_sli_sp_intr_handler */ @@ -8919,7 +9013,8 @@ lpfc_sli_fp_intr_handler(int irq, void *dev_id) if (lpfc_intr_state_check(phba)) return IRQ_NONE; /* Need to read HA REG for FCP ring and other ring events */ - ha_copy = readl(phba->HAregaddr); + if (lpfc_readl(phba->HAregaddr, &ha_copy)) + return IRQ_HANDLED; /* Clear up only attention source related to fast-path */ spin_lock_irqsave(&phba->hbalock, iflag); /* @@ -9004,7 +9099,11 @@ lpfc_sli_intr_handler(int irq, void *dev_id) return IRQ_NONE; spin_lock(&phba->hbalock); - phba->ha_copy = readl(phba->HAregaddr); + if (lpfc_readl(phba->HAregaddr, &phba->ha_copy)) { + spin_unlock(&phba->hbalock); + return IRQ_HANDLED; + } + if (unlikely(!phba->ha_copy)) { spin_unlock(&phba->hbalock); return IRQ_NONE; @@ -9026,7 +9125,10 @@ lpfc_sli_intr_handler(int irq, void *dev_id) } /* Clear attention sources except link and error attentions */ - hc_copy = readl(phba->HCregaddr); + if (lpfc_readl(phba->HCregaddr, &hc_copy)) { + spin_unlock(&phba->hbalock); + return IRQ_HANDLED; + } writel(hc_copy & ~(HC_MBINT_ENA | HC_R0INT_ENA | HC_R1INT_ENA | HC_R2INT_ENA | HC_LAINT_ENA | HC_ERINT_ENA), phba->HCregaddr); @@ -10403,7 +10505,6 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq, 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; @@ -10413,11 +10514,22 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq, LPFC_MBOX_OPCODE_CQ_CREATE, length, LPFC_SLI4_MBX_EMBED); cq_create = &mbox->u.mqe.un.cq_create; + shdr = (union lpfc_sli4_cfg_shdr *) &cq_create->header.cfg_shdr; bf_set(lpfc_mbx_cq_create_num_pages, &cq_create->u.request, cq->page_count); bf_set(lpfc_cq_context_event, &cq_create->u.request.context, 1); bf_set(lpfc_cq_context_valid, &cq_create->u.request.context, 1); - bf_set(lpfc_cq_eq_id, &cq_create->u.request.context, eq->queue_id); + bf_set(lpfc_mbox_hdr_version, &shdr->request, + phba->sli4_hba.pc_sli4_params.cqv); + if (phba->sli4_hba.pc_sli4_params.cqv == LPFC_Q_CREATE_VERSION_2) { + bf_set(lpfc_mbx_cq_create_page_size, &cq_create->u.request, + (PAGE_SIZE/SLI4_PAGE_SIZE)); + bf_set(lpfc_cq_eq_id_2, &cq_create->u.request.context, + eq->queue_id); + } else { + bf_set(lpfc_cq_eq_id, &cq_create->u.request.context, + eq->queue_id); + } switch (cq->entry_count) { default: lpfc_printf_log(phba, KERN_ERR, LOG_SLI, @@ -10449,7 +10561,6 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq, rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); /* The IOCTL status is embedded in the mailbox subheader. */ - shdr = (union lpfc_sli4_cfg_shdr *) &cq_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) { @@ -10515,20 +10626,20 @@ lpfc_mq_create_fb_init(struct lpfc_hba *phba, struct lpfc_queue *mq, 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); + bf_set(lpfc_mq_context_ring_size, &mq_create->u.request.context, + LPFC_MQ_RING_SIZE_16); break; case 32: - bf_set(lpfc_mq_context_count, &mq_create->u.request.context, - LPFC_MQ_CNT_32); + bf_set(lpfc_mq_context_ring_size, &mq_create->u.request.context, + LPFC_MQ_RING_SIZE_32); break; case 64: - bf_set(lpfc_mq_context_count, &mq_create->u.request.context, - LPFC_MQ_CNT_64); + bf_set(lpfc_mq_context_ring_size, &mq_create->u.request.context, + LPFC_MQ_RING_SIZE_64); break; case 128: - bf_set(lpfc_mq_context_count, &mq_create->u.request.context, - LPFC_MQ_CNT_128); + bf_set(lpfc_mq_context_ring_size, &mq_create->u.request.context, + LPFC_MQ_RING_SIZE_128); break; } list_for_each_entry(dmabuf, &mq->page_list, list) { @@ -10586,6 +10697,7 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq, length, LPFC_SLI4_MBX_EMBED); mq_create_ext = &mbox->u.mqe.un.mq_create_ext; + shdr = (union lpfc_sli4_cfg_shdr *) &mq_create_ext->header.cfg_shdr; bf_set(lpfc_mbx_mq_create_ext_num_pages, &mq_create_ext->u.request, mq->page_count); bf_set(lpfc_mbx_mq_create_ext_async_evt_link, @@ -10598,9 +10710,15 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq, &mq_create_ext->u.request, 1); bf_set(lpfc_mbx_mq_create_ext_async_evt_sli, &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); + bf_set(lpfc_mbox_hdr_version, &shdr->request, + phba->sli4_hba.pc_sli4_params.mqv); + if (phba->sli4_hba.pc_sli4_params.mqv == LPFC_Q_CREATE_VERSION_1) + bf_set(lpfc_mbx_mq_create_ext_cq_id, &mq_create_ext->u.request, + cq->queue_id); + else + bf_set(lpfc_mq_context_cq_id, &mq_create_ext->u.request.context, + cq->queue_id); switch (mq->entry_count) { default: lpfc_printf_log(phba, KERN_ERR, LOG_SLI, @@ -10610,20 +10728,24 @@ 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_ext->u.request.context, - LPFC_MQ_CNT_16); + bf_set(lpfc_mq_context_ring_size, + &mq_create_ext->u.request.context, + LPFC_MQ_RING_SIZE_16); break; case 32: - bf_set(lpfc_mq_context_count, &mq_create_ext->u.request.context, - LPFC_MQ_CNT_32); + bf_set(lpfc_mq_context_ring_size, + &mq_create_ext->u.request.context, + LPFC_MQ_RING_SIZE_32); break; case 64: - bf_set(lpfc_mq_context_count, &mq_create_ext->u.request.context, - LPFC_MQ_CNT_64); + bf_set(lpfc_mq_context_ring_size, + &mq_create_ext->u.request.context, + LPFC_MQ_RING_SIZE_64); break; case 128: - bf_set(lpfc_mq_context_count, &mq_create_ext->u.request.context, - LPFC_MQ_CNT_128); + bf_set(lpfc_mq_context_ring_size, + &mq_create_ext->u.request.context, + LPFC_MQ_RING_SIZE_128); break; } list_for_each_entry(dmabuf, &mq->page_list, list) { @@ -10634,7 +10756,6 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq, 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) { @@ -10711,6 +10832,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, 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; + struct dma_address *page; if (!phba->sli4_hba.pc_sli4_params.supported) hw_page_size = SLI4_PAGE_SIZE; @@ -10724,20 +10846,42 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq, LPFC_MBOX_OPCODE_FCOE_WQ_CREATE, length, LPFC_SLI4_MBX_EMBED); wq_create = &mbox->u.mqe.un.wq_create; + shdr = (union lpfc_sli4_cfg_shdr *) &wq_create->header.cfg_shdr; bf_set(lpfc_mbx_wq_create_num_pages, &wq_create->u.request, wq->page_count); bf_set(lpfc_mbx_wq_create_cq_id, &wq_create->u.request, cq->queue_id); + bf_set(lpfc_mbox_hdr_version, &shdr->request, + phba->sli4_hba.pc_sli4_params.wqv); + if (phba->sli4_hba.pc_sli4_params.wqv == LPFC_Q_CREATE_VERSION_1) { + bf_set(lpfc_mbx_wq_create_wqe_count, &wq_create->u.request_1, + wq->entry_count); + switch (wq->entry_size) { + default: + case 64: + bf_set(lpfc_mbx_wq_create_wqe_size, + &wq_create->u.request_1, + LPFC_WQ_WQE_SIZE_64); + break; + case 128: + bf_set(lpfc_mbx_wq_create_wqe_size, + &wq_create->u.request_1, + LPFC_WQ_WQE_SIZE_128); + break; + } + bf_set(lpfc_mbx_wq_create_page_size, &wq_create->u.request_1, + (PAGE_SIZE/SLI4_PAGE_SIZE)); + page = wq_create->u.request_1.page; + } else { + page = wq_create->u.request.page; + } 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 = - putPaddrHigh(dmabuf->phys); + page[dmabuf->buffer_tag].addr_lo = putPaddrLow(dmabuf->phys); + page[dmabuf->buffer_tag].addr_hi = putPaddrHigh(dmabuf->phys); } rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); /* The IOCTL status is embedded in the mailbox subheader. */ - shdr = (union lpfc_sli4_cfg_shdr *) &wq_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) { @@ -10815,37 +10959,51 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq, LPFC_MBOX_OPCODE_FCOE_RQ_CREATE, length, LPFC_SLI4_MBX_EMBED); rq_create = &mbox->u.mqe.un.rq_create; - switch (hrq->entry_count) { - default: - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "2535 Unsupported RQ count. (%d)\n", - hrq->entry_count); - if (hrq->entry_count < 512) - return -EINVAL; - /* otherwise default to smallest count (drop through) */ - case 512: - bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context, - LPFC_RQ_RING_SIZE_512); - break; - case 1024: - bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context, - LPFC_RQ_RING_SIZE_1024); - break; - case 2048: - bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context, - LPFC_RQ_RING_SIZE_2048); - break; - case 4096: - bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context, - LPFC_RQ_RING_SIZE_4096); - break; + shdr = (union lpfc_sli4_cfg_shdr *) &rq_create->header.cfg_shdr; + bf_set(lpfc_mbox_hdr_version, &shdr->request, + phba->sli4_hba.pc_sli4_params.rqv); + if (phba->sli4_hba.pc_sli4_params.rqv == LPFC_Q_CREATE_VERSION_1) { + bf_set(lpfc_rq_context_rqe_count_1, + &rq_create->u.request.context, + hrq->entry_count); + rq_create->u.request.context.buffer_size = LPFC_HDR_BUF_SIZE; + } else { + switch (hrq->entry_count) { + default: + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2535 Unsupported RQ count. (%d)\n", + hrq->entry_count); + if (hrq->entry_count < 512) + return -EINVAL; + /* otherwise default to smallest count (drop through) */ + case 512: + bf_set(lpfc_rq_context_rqe_count, + &rq_create->u.request.context, + LPFC_RQ_RING_SIZE_512); + break; + case 1024: + bf_set(lpfc_rq_context_rqe_count, + &rq_create->u.request.context, + LPFC_RQ_RING_SIZE_1024); + break; + case 2048: + bf_set(lpfc_rq_context_rqe_count, + &rq_create->u.request.context, + LPFC_RQ_RING_SIZE_2048); + break; + case 4096: + bf_set(lpfc_rq_context_rqe_count, + &rq_create->u.request.context, + LPFC_RQ_RING_SIZE_4096); + break; + } + bf_set(lpfc_rq_context_buf_size, &rq_create->u.request.context, + LPFC_HDR_BUF_SIZE); } bf_set(lpfc_rq_context_cq_id, &rq_create->u.request.context, cq->queue_id); bf_set(lpfc_mbx_rq_create_num_pages, &rq_create->u.request, hrq->page_count); - 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 = @@ -10855,7 +11013,6 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq, } rc = lpfc_sli_issue_mbox(phba, mbox, MBX_POLL); /* The IOCTL status is embedded in the mailbox subheader. */ - shdr = (union lpfc_sli4_cfg_shdr *) &rq_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) { @@ -10881,37 +11038,50 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq, lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE, LPFC_MBOX_OPCODE_FCOE_RQ_CREATE, length, LPFC_SLI4_MBX_EMBED); - switch (drq->entry_count) { - default: - lpfc_printf_log(phba, KERN_ERR, LOG_SLI, - "2536 Unsupported RQ count. (%d)\n", - drq->entry_count); - if (drq->entry_count < 512) - return -EINVAL; - /* otherwise default to smallest count (drop through) */ - case 512: - bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context, - LPFC_RQ_RING_SIZE_512); - break; - case 1024: - bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context, - LPFC_RQ_RING_SIZE_1024); - break; - case 2048: - bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context, - LPFC_RQ_RING_SIZE_2048); - break; - case 4096: - bf_set(lpfc_rq_context_rq_size, &rq_create->u.request.context, - LPFC_RQ_RING_SIZE_4096); - break; + bf_set(lpfc_mbox_hdr_version, &shdr->request, + phba->sli4_hba.pc_sli4_params.rqv); + if (phba->sli4_hba.pc_sli4_params.rqv == LPFC_Q_CREATE_VERSION_1) { + bf_set(lpfc_rq_context_rqe_count_1, + &rq_create->u.request.context, + hrq->entry_count); + rq_create->u.request.context.buffer_size = LPFC_DATA_BUF_SIZE; + } else { + switch (drq->entry_count) { + default: + lpfc_printf_log(phba, KERN_ERR, LOG_SLI, + "2536 Unsupported RQ count. (%d)\n", + drq->entry_count); + if (drq->entry_count < 512) + return -EINVAL; + /* otherwise default to smallest count (drop through) */ + case 512: + bf_set(lpfc_rq_context_rqe_count, + &rq_create->u.request.context, + LPFC_RQ_RING_SIZE_512); + break; + case 1024: + bf_set(lpfc_rq_context_rqe_count, + &rq_create->u.request.context, + LPFC_RQ_RING_SIZE_1024); + break; + case 2048: + bf_set(lpfc_rq_context_rqe_count, + &rq_create->u.request.context, + LPFC_RQ_RING_SIZE_2048); + break; + case 4096: + bf_set(lpfc_rq_context_rqe_count, + &rq_create->u.request.context, + LPFC_RQ_RING_SIZE_4096); + break; + } + bf_set(lpfc_rq_context_buf_size, &rq_create->u.request.context, + LPFC_DATA_BUF_SIZE); } bf_set(lpfc_rq_context_cq_id, &rq_create->u.request.context, cq->queue_id); bf_set(lpfc_mbx_rq_create_num_pages, &rq_create->u.request, drq->page_count); - bf_set(lpfc_rq_context_buf_size, &rq_create->u.request.context, - LPFC_DATA_BUF_SIZE); list_for_each_entry(dmabuf, &drq->page_list, list) { rq_create->u.request.page[dmabuf->buffer_tag].addr_lo = putPaddrLow(dmabuf->phys); @@ -11580,6 +11750,7 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr) static char *rctl_names[] = FC_RCTL_NAMES_INIT; char *type_names[] = FC_TYPE_NAMES_INIT; struct fc_vft_header *fc_vft_hdr; + uint32_t *header = (uint32_t *) fc_hdr; switch (fc_hdr->fh_r_ctl) { case FC_RCTL_DD_UNCAT: /* uncategorized information */ @@ -11628,10 +11799,15 @@ lpfc_fc_frame_check(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr) default: goto drop; } + lpfc_printf_log(phba, KERN_INFO, LOG_ELS, - "2538 Received frame rctl:%s type:%s\n", + "2538 Received frame rctl:%s type:%s " + "Frame Data:%08x %08x %08x %08x %08x %08x\n", rctl_names[fc_hdr->fh_r_ctl], - type_names[fc_hdr->fh_type]); + type_names[fc_hdr->fh_type], + be32_to_cpu(header[0]), be32_to_cpu(header[1]), + be32_to_cpu(header[2]), be32_to_cpu(header[3]), + be32_to_cpu(header[4]), be32_to_cpu(header[5])); return 0; drop: lpfc_printf_log(phba, KERN_WARNING, LOG_ELS, @@ -11928,17 +12104,17 @@ lpfc_sli4_abort_partial_seq(struct lpfc_vport *vport, } /** - * lpfc_sli4_seq_abort_acc_cmpl - Accept seq abort iocb complete handler + * lpfc_sli4_seq_abort_rsp_cmpl - BLS ABORT RSP seq abort iocb complete handler * @phba: Pointer to HBA context object. * @cmd_iocbq: pointer to the command iocbq structure. * @rsp_iocbq: pointer to the response iocbq structure. * - * This function handles the sequence abort accept iocb command complete + * This function handles the sequence abort response iocb command complete * event. It properly releases the memory allocated to the sequence abort * accept iocb. **/ static void -lpfc_sli4_seq_abort_acc_cmpl(struct lpfc_hba *phba, +lpfc_sli4_seq_abort_rsp_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmd_iocbq, struct lpfc_iocbq *rsp_iocbq) { @@ -11947,15 +12123,15 @@ lpfc_sli4_seq_abort_acc_cmpl(struct lpfc_hba *phba, } /** - * lpfc_sli4_seq_abort_acc - Accept sequence abort + * lpfc_sli4_seq_abort_rsp - bls rsp to sequence abort * @phba: Pointer to HBA context object. * @fc_hdr: pointer to a FC frame header. * - * This function sends a basic accept to a previous unsol sequence abort + * This function sends a basic response to a previous unsol sequence abort * event after aborting the sequence handling. **/ static void -lpfc_sli4_seq_abort_acc(struct lpfc_hba *phba, +lpfc_sli4_seq_abort_rsp(struct lpfc_hba *phba, struct fc_frame_header *fc_hdr) { struct lpfc_iocbq *ctiocb = NULL; @@ -11963,6 +12139,7 @@ lpfc_sli4_seq_abort_acc(struct lpfc_hba *phba, uint16_t oxid, rxid; uint32_t sid, fctl; IOCB_t *icmd; + int rc; if (!lpfc_is_link_up(phba)) return; @@ -11983,7 +12160,7 @@ lpfc_sli4_seq_abort_acc(struct lpfc_hba *phba, + phba->sli4_hba.max_cfg_param.xri_base)) lpfc_set_rrq_active(phba, ndlp, rxid, oxid, 0); - /* Allocate buffer for acc iocb */ + /* Allocate buffer for rsp iocb */ ctiocb = lpfc_sli_get_iocbq(phba); if (!ctiocb) return; @@ -12008,32 +12185,54 @@ lpfc_sli4_seq_abort_acc(struct lpfc_hba *phba, ctiocb->iocb_cmpl = NULL; ctiocb->vport = phba->pport; - ctiocb->iocb_cmpl = lpfc_sli4_seq_abort_acc_cmpl; + ctiocb->iocb_cmpl = lpfc_sli4_seq_abort_rsp_cmpl; + ctiocb->sli4_xritag = NO_XRI; + + /* If the oxid maps to the FCP XRI range or if it is out of range, + * send a BLS_RJT. The driver no longer has that exchange. + * Override the IOCB for a BA_RJT. + */ + if (oxid > (phba->sli4_hba.max_cfg_param.max_xri + + phba->sli4_hba.max_cfg_param.xri_base) || + oxid > (lpfc_sli4_get_els_iocb_cnt(phba) + + phba->sli4_hba.max_cfg_param.xri_base)) { + icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_RJT; + bf_set(lpfc_vndr_code, &icmd->un.bls_rsp, 0); + bf_set(lpfc_rsn_expln, &icmd->un.bls_rsp, FC_BA_RJT_INV_XID); + bf_set(lpfc_rsn_code, &icmd->un.bls_rsp, FC_BA_RJT_UNABLE); + } if (fctl & FC_FC_EX_CTX) { /* ABTS sent by responder to CT exchange, construction * of BA_ACC will use OX_ID from ABTS for the XRI_TAG * field and RX_ID from ABTS for RX_ID field. */ - bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_RSP); - bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, rxid); - ctiocb->sli4_xritag = oxid; + bf_set(lpfc_abts_orig, &icmd->un.bls_rsp, LPFC_ABTS_UNSOL_RSP); + bf_set(lpfc_abts_rxid, &icmd->un.bls_rsp, rxid); } else { /* ABTS sent by initiator to CT exchange, construction * of BA_ACC will need to allocate a new XRI as for the * XRI_TAG and RX_ID fields. */ - bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_INT); - bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, NO_XRI); - ctiocb->sli4_xritag = NO_XRI; + bf_set(lpfc_abts_orig, &icmd->un.bls_rsp, LPFC_ABTS_UNSOL_INT); + bf_set(lpfc_abts_rxid, &icmd->un.bls_rsp, NO_XRI); } - bf_set(lpfc_abts_oxid, &icmd->un.bls_acc, oxid); + bf_set(lpfc_abts_oxid, &icmd->un.bls_rsp, oxid); - /* Xmit CT abts accept on exchange <xid> */ + /* Xmit CT abts response on exchange <xid> */ lpfc_printf_log(phba, KERN_INFO, LOG_ELS, - "1200 Xmit CT ABTS ACC on exchange x%x Data: x%x\n", - CMD_XMIT_BLS_RSP64_CX, phba->link_state); - lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0); + "1200 Send BLS cmd x%x on oxid x%x Data: x%x\n", + icmd->un.xseq64.w5.hcsw.Rctl, oxid, phba->link_state); + + rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, ctiocb, 0); + if (rc == IOCB_ERROR) { + lpfc_printf_log(phba, KERN_ERR, LOG_ELS, + "2925 Failed to issue CT ABTS RSP x%x on " + "xri x%x, Data x%x\n", + icmd->un.xseq64.w5.hcsw.Rctl, oxid, + phba->link_state); + lpfc_sli_release_iocbq(phba, ctiocb); + } } /** @@ -12081,7 +12280,7 @@ lpfc_sli4_handle_unsol_abort(struct lpfc_vport *vport, lpfc_in_buf_free(phba, &dmabuf->dbuf); } /* Send basic accept (BA_ACC) to the abort requester */ - lpfc_sli4_seq_abort_acc(phba, &fc_hdr); + lpfc_sli4_seq_abort_rsp(phba, &fc_hdr); } /** diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 595056b..1a3cbf8 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2009 Emulex. All rights reserved. * + * Copyright (C) 2009-2011 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * * diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 0a4d376..2404d1d 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.21" +#define LPFC_DRIVER_VERSION "8.3.22" #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/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index e8a6f1c..5e001ff 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -1748,6 +1748,54 @@ _base_display_intel_branding(struct MPT2SAS_ADAPTER *ioc) } /** + * _base_display_hp_branding - Display branding string + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_base_display_hp_branding(struct MPT2SAS_ADAPTER *ioc) +{ + if (ioc->pdev->subsystem_vendor != MPT2SAS_HP_3PAR_SSVID) + return; + + switch (ioc->pdev->device) { + case MPI2_MFGPAGE_DEVID_SAS2004: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING); + break; + default: + break; + } + case MPI2_MFGPAGE_DEVID_SAS2308_2: + switch (ioc->pdev->subsystem_device) { + case MPT2SAS_HP_2_4_INTERNAL_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_HP_2_4_INTERNAL_BRANDING); + break; + case MPT2SAS_HP_2_4_EXTERNAL_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_HP_2_4_EXTERNAL_BRANDING); + break; + case MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING); + break; + case MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID: + printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, + MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING); + break; + default: + break; + } + default: + break; + } +} + +/** * _base_display_ioc_capabilities - Disply IOC's capabilities. * @ioc: per adapter object * @@ -1778,6 +1826,7 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc) _base_display_dell_branding(ioc); _base_display_intel_branding(ioc); + _base_display_hp_branding(ioc); printk(MPT2SAS_INFO_FMT "Protocol=(", ioc->name); diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index a3f8aa9..5003282 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -168,6 +168,26 @@ #define MPT2SAS_INTEL_RMS2LL080_SSDID 0x350E #define MPT2SAS_INTEL_RMS2LL040_SSDID 0x350F + +/* + * HP HBA branding + */ +#define MPT2SAS_HP_3PAR_SSVID 0x1590 +#define MPT2SAS_HP_2_4_INTERNAL_BRANDING "HP H220 Host Bus Adapter" +#define MPT2SAS_HP_2_4_EXTERNAL_BRANDING "HP H221 Host Bus Adapter" +#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_BRANDING "HP H222 Host Bus Adapter" +#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_BRANDING "HP H220i Host Bus Adapter" +#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_BRANDING "HP H210i Host Bus Adapter" + +/* + * HO HBA SSDIDs + */ +#define MPT2SAS_HP_2_4_INTERNAL_SSDID 0x0041 +#define MPT2SAS_HP_2_4_EXTERNAL_SSDID 0x0042 +#define MPT2SAS_HP_1_4_INTERNAL_1_4_EXTERNAL_SSDID 0x0043 +#define MPT2SAS_HP_EMBEDDED_2_4_INTERNAL_SSDID 0x0044 +#define MPT2SAS_HP_DAUGHTER_2_4_INTERNAL_SSDID 0x0046 + /* * per target private data */ diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 19ad34f..938d045 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -663,6 +663,13 @@ static struct pci_device_id __devinitdata mvs_pci_table[] = { { PCI_VDEVICE(ARECA, PCI_DEVICE_ID_ARECA_1300), chip_1300 }, { PCI_VDEVICE(ARECA, PCI_DEVICE_ID_ARECA_1320), chip_1320 }, { PCI_VDEVICE(ADAPTEC2, 0x0450), chip_6440 }, + { PCI_VDEVICE(TTI, 0x2710), chip_9480 }, + { PCI_VDEVICE(TTI, 0x2720), chip_9480 }, + { PCI_VDEVICE(TTI, 0x2721), chip_9480 }, + { PCI_VDEVICE(TTI, 0x2722), chip_9480 }, + { PCI_VDEVICE(TTI, 0x2740), chip_9480 }, + { PCI_VDEVICE(TTI, 0x2744), chip_9480 }, + { PCI_VDEVICE(TTI, 0x2760), chip_9480 }, { } /* terminate list */ }; diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index 2fc0045..c1f8d1b 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -53,6 +53,9 @@ #define PCI_DEVICE_ID_QLOGIC_ISP8022 0x8022 #endif +#define ISP4XXX_PCI_FN_1 0x1 +#define ISP4XXX_PCI_FN_2 0x3 + #define QLA_SUCCESS 0 #define QLA_ERROR 1 @@ -233,9 +236,6 @@ struct ddb_entry { unsigned long flags; /* DDB Flags */ - unsigned long dev_scan_wait_to_start_relogin; - unsigned long dev_scan_wait_to_complete_relogin; - uint16_t fw_ddb_index; /* DDB firmware index */ uint16_t options; uint32_t fw_ddb_device_state; /* F/W Device State -- see ql4_fw.h */ @@ -289,8 +289,6 @@ struct ddb_entry { * DDB flags. */ #define DF_RELOGIN 0 /* Relogin to device */ -#define DF_NO_RELOGIN 1 /* Do not relogin if IOCTL - * logged it out */ #define DF_ISNS_DISCOVERED 2 /* Device was discovered via iSNS */ #define DF_FO_MASKED 3 @@ -376,7 +374,7 @@ struct scsi_qla_host { #define AF_LINK_UP 8 /* 0x00000100 */ #define AF_IRQ_ATTACHED 10 /* 0x00000400 */ #define AF_DISABLE_ACB_COMPLETE 11 /* 0x00000800 */ -#define AF_HBA_GOING_AWAY 12 /* 0x00001000 */ +#define AF_HA_REMOVAL 12 /* 0x00001000 */ #define AF_INTx_ENABLED 15 /* 0x00008000 */ #define AF_MSI_ENABLED 16 /* 0x00010000 */ #define AF_MSIX_ENABLED 17 /* 0x00020000 */ @@ -479,7 +477,6 @@ struct scsi_qla_host { uint32_t timer_active; /* Recovery Timers */ - uint32_t discovery_wait; atomic_t check_relogin_timeouts; uint32_t retry_reset_ha_cnt; uint32_t isp_reset_timer; /* reset test timer */ @@ -765,6 +762,5 @@ static inline void ql4xxx_unlock_drvr(struct scsi_qla_host *a) /* Defines for process_aen() */ #define PROCESS_ALL_AENS 0 #define FLUSH_DDB_CHANGED_AENS 1 -#define RELOGIN_DDB_CHANGED_AENS 2 #endif /*_QLA4XXX_H */ diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index c198579..31e2bf9 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -455,6 +455,7 @@ struct addr_ctrl_blk { uint8_t res0; /* 07 */ uint16_t eth_mtu_size; /* 08-09 */ uint16_t add_fw_options; /* 0A-0B */ +#define SERIALIZE_TASK_MGMT 0x0400 uint8_t hb_interval; /* 0C */ uint8_t inst_num; /* 0D */ diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index 8fad99b..cc53e3f 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -136,7 +136,6 @@ void qla4_8xxx_clear_drv_active(struct scsi_qla_host *ha); void qla4_8xxx_set_drv_active(struct scsi_qla_host *ha); extern int ql4xextended_error_logging; -extern int ql4xdiscoverywait; extern int ql4xdontresethba; extern int ql4xenablemsix; diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 1629c48..bbb2e90 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -723,13 +723,38 @@ int qla4_is_relogin_allowed(struct scsi_qla_host *ha, uint32_t conn_err) return relogin; } +static void qla4xxx_flush_AENS(struct scsi_qla_host *ha) +{ + unsigned long wtime; + + /* Flush the 0x8014 AEN from the firmware as a result of + * Auto connect. We are basically doing get_firmware_ddb() + * to determine whether we need to log back in or not. + * Trying to do a set ddb before we have processed 0x8014 + * will result in another set_ddb() for the same ddb. In other + * words there will be stale entries in the aen_q. + */ + wtime = jiffies + (2 * HZ); + do { + if (qla4xxx_get_firmware_state(ha) == QLA_SUCCESS) + if (ha->firmware_state & (BIT_2 | BIT_0)) + return; + + if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags)) + qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS); + + msleep(1000); + } while (!time_after_eq(jiffies, wtime)); +} + /** - * qla4xxx_configure_ddbs - builds driver ddb list + * qla4xxx_build_ddb_list - builds driver ddb list * @ha: Pointer to host adapter structure. * * This routine searches for all valid firmware ddb entries and builds * an internal ddb list. Ddbs that are considered valid are those with * a device state of SESSION_ACTIVE. + * A relogin (set_ddb) is issued for DDBs that are not online. **/ static int qla4xxx_build_ddb_list(struct scsi_qla_host *ha) { @@ -744,6 +769,8 @@ static int qla4xxx_build_ddb_list(struct scsi_qla_host *ha) uint32_t ipv6_device; uint32_t new_tgt; + qla4xxx_flush_AENS(ha); + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), &fw_ddb_entry_dma, GFP_KERNEL); if (fw_ddb_entry == NULL) { @@ -847,144 +874,6 @@ exit_build_ddb_list_no_free: return status; } -struct qla4_relog_scan { - int halt_wait; - uint32_t conn_err; - uint32_t fw_ddb_index; - uint32_t next_fw_ddb_index; - uint32_t fw_ddb_device_state; -}; - -static int qla4_test_rdy(struct scsi_qla_host *ha, struct qla4_relog_scan *rs) -{ - struct ddb_entry *ddb_entry; - - 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 - * relogged in */ - rs->halt_wait = 0; - - ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, - rs->fw_ddb_index); - if (ddb_entry == NULL) - return QLA_ERROR; - - if (ddb_entry->dev_scan_wait_to_start_relogin != 0 - && time_after_eq(jiffies, - ddb_entry-> - dev_scan_wait_to_start_relogin)) - { - ddb_entry->dev_scan_wait_to_start_relogin = 0; - qla4xxx_set_ddb_entry(ha, rs->fw_ddb_index, 0); - } - } - return QLA_SUCCESS; -} - -static int qla4_scan_for_relogin(struct scsi_qla_host *ha, - struct qla4_relog_scan *rs) -{ - int error; - - /* scan for relogins - * ----------------- */ - for (rs->fw_ddb_index = 0; rs->fw_ddb_index < MAX_DDB_ENTRIES; - rs->fw_ddb_index = rs->next_fw_ddb_index) { - if (qla4xxx_get_fwddb_entry(ha, rs->fw_ddb_index, NULL, 0, - NULL, &rs->next_fw_ddb_index, - &rs->fw_ddb_device_state, - &rs->conn_err, NULL, NULL) - == QLA_ERROR) - return QLA_ERROR; - - if (rs->fw_ddb_device_state == DDB_DS_LOGIN_IN_PROCESS) - rs->halt_wait = 0; - - if (rs->fw_ddb_device_state == DDB_DS_SESSION_FAILED || - rs->fw_ddb_device_state == DDB_DS_NO_CONNECTION_ACTIVE) { - error = qla4_test_rdy(ha, rs); - if (error) - return error; - } - - /* We know we've reached the last device when - * next_fw_ddb_index is 0 */ - if (rs->next_fw_ddb_index == 0) - break; - } - return QLA_SUCCESS; -} - -/** - * qla4xxx_devices_ready - wait for target devices to be logged in - * @ha: pointer to adapter structure - * - * This routine waits up to ql4xdiscoverywait seconds - * F/W database during driver load time. - **/ -static int qla4xxx_devices_ready(struct scsi_qla_host *ha) -{ - int error; - unsigned long discovery_wtime; - struct qla4_relog_scan rs; - - discovery_wtime = jiffies + (ql4xdiscoverywait * HZ); - - DEBUG(printk("Waiting (%d) for devices ...\n", ql4xdiscoverywait)); - do { - /* poll for AEN. */ - qla4xxx_get_firmware_state(ha); - if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags)) { - /* Set time-between-relogin timer */ - qla4xxx_process_aen(ha, RELOGIN_DDB_CHANGED_AENS); - } - - /* if no relogins active or needed, halt discvery wait */ - rs.halt_wait = 1; - - error = qla4_scan_for_relogin(ha, &rs); - - if (rs.halt_wait) { - DEBUG2(printk("scsi%ld: %s: Delay halted. Devices " - "Ready.\n", ha->host_no, __func__)); - return QLA_SUCCESS; - } - - msleep(2000); - } while (!time_after_eq(jiffies, discovery_wtime)); - - DEBUG3(qla4xxx_get_conn_event_log(ha)); - - return QLA_SUCCESS; -} - -static void qla4xxx_flush_AENS(struct scsi_qla_host *ha) -{ - unsigned long wtime; - - /* Flush the 0x8014 AEN from the firmware as a result of - * Auto connect. We are basically doing get_firmware_ddb() - * to determine whether we need to log back in or not. - * Trying to do a set ddb before we have processed 0x8014 - * will result in another set_ddb() for the same ddb. In other - * words there will be stale entries in the aen_q. - */ - wtime = jiffies + (2 * HZ); - do { - if (qla4xxx_get_firmware_state(ha) == QLA_SUCCESS) - if (ha->firmware_state & (BIT_2 | BIT_0)) - return; - - if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags)) - qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS); - - msleep(1000); - } while (!time_after_eq(jiffies, wtime)); - -} - static int qla4xxx_initialize_ddb_list(struct scsi_qla_host *ha) { uint16_t fw_ddb_index; @@ -996,29 +885,12 @@ static int qla4xxx_initialize_ddb_list(struct scsi_qla_host *ha) for (fw_ddb_index = 0; fw_ddb_index < MAX_DDB_ENTRIES; fw_ddb_index++) ha->fw_ddb_index_map[fw_ddb_index] = - (struct ddb_entry *)INVALID_ENTRY; + (struct ddb_entry *)INVALID_ENTRY; ha->tot_ddbs = 0; - qla4xxx_flush_AENS(ha); - - /* Wait for an AEN */ - qla4xxx_devices_ready(ha); - - /* - * First perform device discovery for active - * fw ddb indexes and build - * ddb list. - */ - if ((status = qla4xxx_build_ddb_list(ha)) == QLA_ERROR) - return status; - - /* - * Targets can come online after the inital discovery, so processing - * the aens here will catch them. - */ - if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags)) - qla4xxx_process_aen(ha, PROCESS_ALL_AENS); + /* Perform device discovery and build ddb list. */ + status = qla4xxx_build_ddb_list(ha); return status; } @@ -1537,7 +1409,6 @@ 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; /* check for out of range index */ if (fw_ddb_index >= MAX_DDB_ENTRIES) @@ -1553,27 +1424,18 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, } /* Device already exists in our database. */ - old_fw_ddb_device_state = ddb_entry->fw_ddb_device_state; DEBUG2(printk("scsi%ld: %s DDB - old state= 0x%x, new state=0x%x for " "index [%d]\n", ha->host_no, __func__, ddb_entry->fw_ddb_device_state, state, fw_ddb_index)); - if (old_fw_ddb_device_state == state && - state == DDB_DS_SESSION_ACTIVE) { - if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { - atomic_set(&ddb_entry->state, DDB_STATE_ONLINE); - iscsi_unblock_session(ddb_entry->sess); - } - return QLA_SUCCESS; - } ddb_entry->fw_ddb_device_state = state; /* Device is back online. */ - if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) { + if ((ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) && + (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE)) { atomic_set(&ddb_entry->state, DDB_STATE_ONLINE); atomic_set(&ddb_entry->relogin_retry_count, 0); atomic_set(&ddb_entry->relogin_timer, 0); clear_bit(DF_RELOGIN, &ddb_entry->flags); - clear_bit(DF_NO_RELOGIN, &ddb_entry->flags); iscsi_unblock_session(ddb_entry->sess); iscsi_session_event(ddb_entry->sess, ISCSI_KEVENT_CREATE_SESSION); @@ -1581,7 +1443,7 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, * Change the lun state to READY in case the lun TIMEOUT before * the device came back. */ - } else { + } else if (ddb_entry->fw_ddb_device_state != DDB_DS_SESSION_ACTIVE) { /* Device went away, mark device missing */ if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) { DEBUG2(ql4_printk(KERN_INFO, ha, "%s mark missing " @@ -1598,7 +1460,6 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, uint32_t fw_ddb_index, */ 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) && qla4_is_relogin_allowed(ha, conn_err)) { /* * This triggers a relogin. After the relogin_timer diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index 03e028e..2f40ac7 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -801,7 +801,7 @@ irqreturn_t qla4xxx_intr_handler(int irq, void *dev_id) &ha->reg->ctrl_status); readl(&ha->reg->ctrl_status); - if (!test_bit(AF_HBA_GOING_AWAY, &ha->flags)) + if (!test_bit(AF_HA_REMOVAL, &ha->flags)) set_bit(DPC_RESET_HA_INTR, &ha->dpc_flags); break; @@ -1008,34 +1008,9 @@ void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen) mbox_sts[0], mbox_sts[2], mbox_sts[3])); break; - } else if (process_aen == RELOGIN_DDB_CHANGED_AENS) { - /* for use during init time, we only want to - * relogin non-active ddbs */ - struct ddb_entry *ddb_entry; - - ddb_entry = - /* FIXME: name length? */ - qla4xxx_lookup_ddb_by_fw_index(ha, - mbox_sts[2]); - if (!ddb_entry) - break; - - ddb_entry->dev_scan_wait_to_complete_relogin = - 0; - ddb_entry->dev_scan_wait_to_start_relogin = - jiffies + - ((ddb_entry->default_time2wait + - 4) * HZ); - - DEBUG2(printk("scsi%ld: ddb [%d] initiate" - " RELOGIN after %d seconds\n", - ha->host_no, - ddb_entry->fw_ddb_index, - ddb_entry->default_time2wait + - 4)); - break; } - + case PROCESS_ALL_AENS: + default: if (mbox_sts[1] == 0) { /* Global DB change. */ qla4xxx_reinitialize_ddb_list(ha); } else if (mbox_sts[1] == 1) { /* Specific device. */ diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c index f65626a..f9d81c8 100644 --- a/drivers/scsi/qla4xxx/ql4_mbx.c +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -32,6 +32,7 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, u_long wait_count; uint32_t intr_status; unsigned long flags = 0; + uint32_t dev_state; /* Make sure that pointers are valid */ if (!mbx_cmd || !mbx_sts) { @@ -40,12 +41,23 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, return status; } - if (is_qla8022(ha) && - test_bit(AF_FW_RECOVERY, &ha->flags)) { - DEBUG2(ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: prematurely " - "completing mbx cmd as firmware recovery detected\n", - ha->host_no, __func__)); - return status; + if (is_qla8022(ha)) { + if (test_bit(AF_FW_RECOVERY, &ha->flags)) { + DEBUG2(ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: " + "prematurely completing mbx cmd as firmware " + "recovery detected\n", ha->host_no, __func__)); + return status; + } + /* Do not send any mbx cmd if h/w is in failed state*/ + qla4_8xxx_idc_lock(ha); + dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + qla4_8xxx_idc_unlock(ha); + if (dev_state == QLA82XX_DEV_FAILED) { + ql4_printk(KERN_WARNING, ha, "scsi%ld: %s: H/W is in " + "failed state, do not send any mailbox commands\n", + ha->host_no, __func__); + return status; + } } if ((is_aer_supported(ha)) && @@ -139,7 +151,7 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, if (test_bit(AF_IRQ_ATTACHED, &ha->flags) && test_bit(AF_INTERRUPTS_ON, &ha->flags) && test_bit(AF_ONLINE, &ha->flags) && - !test_bit(AF_HBA_GOING_AWAY, &ha->flags)) { + !test_bit(AF_HA_REMOVAL, &ha->flags)) { /* Do not poll for completion. Use completion queue */ set_bit(AF_MBOX_COMMAND_NOPOLL, &ha->flags); wait_for_completion_timeout(&ha->mbx_intr_comp, MBOX_TOV * HZ); @@ -395,9 +407,6 @@ qla4xxx_update_local_ifcb(struct scsi_qla_host *ha, /*memcpy(ha->alias, init_fw_cb->Alias, min(sizeof(ha->alias), sizeof(init_fw_cb->Alias)));*/ - /* Save Command Line Paramater info */ - 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; @@ -467,6 +476,11 @@ int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha) init_fw_cb->fw_options &= __constant_cpu_to_le16(~FWOPT_TARGET_MODE); + /* Set bit for "serialize task mgmt" all other bits need to be zero */ + init_fw_cb->add_fw_options = 0; + init_fw_cb->add_fw_options |= + __constant_cpu_to_le16(SERIALIZE_TASK_MGMT); + if (qla4xxx_set_ifcb(ha, &mbox_cmd[0], &mbox_sts[0], init_fw_cb_dma) != QLA_SUCCESS) { DEBUG2(printk(KERN_WARNING diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c index 3d5ef2d..35381cb 100644 --- a/drivers/scsi/qla4xxx/ql4_nx.c +++ b/drivers/scsi/qla4xxx/ql4_nx.c @@ -2304,14 +2304,13 @@ qla4_8xxx_enable_intrs(struct scsi_qla_host *ha) void qla4_8xxx_disable_intrs(struct scsi_qla_host *ha) { - if (test_bit(AF_INTERRUPTS_ON, &ha->flags)) + if (test_and_clear_bit(AF_INTERRUPTS_ON, &ha->flags)) qla4_8xxx_mbx_intr_disable(ha); spin_lock_irq(&ha->hardware_lock); /* BIT 10 - set */ qla4_8xxx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0x0400); spin_unlock_irq(&ha->hardware_lock); - clear_bit(AF_INTERRUPTS_ON, &ha->flags); } struct ql4_init_msix_entry { diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 967836e..a4acb0d 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -29,10 +29,6 @@ static struct kmem_cache *srb_cachep; /* * Module parameter information and variables */ -int ql4xdiscoverywait = 60; -module_param(ql4xdiscoverywait, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(ql4xdiscoverywait, "Discovery wait time"); - int ql4xdontresethba = 0; module_param(ql4xdontresethba, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ql4xdontresethba, @@ -55,6 +51,17 @@ MODULE_PARM_DESC(ql4xenablemsix, " 2 = enable MSI interrupt mechanism."); #define QL4_DEF_QDEPTH 32 +static int ql4xmaxqdepth = QL4_DEF_QDEPTH; +module_param(ql4xmaxqdepth, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ql4xmaxqdepth, + "Maximum queue depth to report for target devices.\n" + " Default: 32."); + +static int ql4xsess_recovery_tmo = QL4_SESS_RECOVERY_TMO; +module_param(ql4xsess_recovery_tmo, int, S_IRUGO); +MODULE_PARM_DESC(ql4xsess_recovery_tmo, + "Target Session Recovery Timeout.\n" + " Default: 30 sec."); /* * SCSI host template entry points @@ -165,7 +172,7 @@ static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session) DEBUG2(printk("scsi%ld: %s: ddb [%d] session recovery timeout " "of (%d) secs exhausted, marking device DEAD.\n", ha->host_no, __func__, ddb_entry->fw_ddb_index, - QL4_SESS_RECOVERY_TMO)); + ddb_entry->sess->recovery_tmo)); } } @@ -295,7 +302,7 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry) { int err; - ddb_entry->sess->recovery_tmo = QL4_SESS_RECOVERY_TMO; + ddb_entry->sess->recovery_tmo = ql4xsess_recovery_tmo; err = iscsi_add_session(ddb_entry->sess, ddb_entry->fw_ddb_index); if (err) { @@ -753,12 +760,6 @@ static void qla4xxx_timer(struct scsi_qla_host *ha) if (!pci_channel_offline(ha->pdev)) pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w); - if (test_bit(AF_HBA_GOING_AWAY, &ha->flags)) { - DEBUG2(ql4_printk(KERN_INFO, ha, "%s exited. HBA GOING AWAY\n", - __func__)); - return; - } - if (is_qla8022(ha)) { qla4_8xxx_watchdog(ha); } @@ -1067,7 +1068,6 @@ void qla4xxx_dead_adapter_cleanup(struct scsi_qla_host *ha) /* Disable the board */ ql4_printk(KERN_INFO, ha, "Disabling the board\n"); - set_bit(AF_HBA_GOING_AWAY, &ha->flags); qla4xxx_abort_active_cmds(ha, DID_NO_CONNECT << 16); qla4xxx_mark_all_devices_missing(ha); @@ -1218,6 +1218,27 @@ recover_ha_init_adapter: return status; } +static void qla4xxx_relogin_all_devices(struct scsi_qla_host *ha) +{ + struct ddb_entry *ddb_entry, *dtemp; + + 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); + ql4_printk(KERN_INFO, ha, "scsi%ld: %s: ddb[%d]" + " marked ONLINE\n", ha->host_no, __func__, + ddb_entry->fw_ddb_index); + + iscsi_unblock_session(ddb_entry->sess); + } else + qla4xxx_relogin_device(ha, ddb_entry); + } + } +} + void qla4xxx_wake_dpc(struct scsi_qla_host *ha) { if (ha->dpc_thread && @@ -1259,11 +1280,6 @@ static void qla4xxx_do_dpc(struct work_struct *work) goto do_dpc_exit; } - /* HBA is in the process of being permanently disabled. - * Don't process anything */ - if (test_bit(AF_HBA_GOING_AWAY, &ha->flags)) - return; - if (is_qla8022(ha)) { if (test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags)) { qla4_8xxx_idc_lock(ha); @@ -1331,13 +1347,7 @@ dpc_post_reset_ha: 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); - } + qla4xxx_mark_all_devices_missing(ha); } else { /* ---- link up? --- * * F/W will auto login to all devices ONLY ONCE after @@ -1346,30 +1356,7 @@ dpc_post_reset_ha: * 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); - ql4_printk(KERN_INFO, ha, - "scsi%ld: %s: ddb[%d]" - " marked ONLINE\n", - ha->host_no, __func__, - ddb_entry->fw_ddb_index); - - iscsi_unblock_session( - ddb_entry->sess); - } else - qla4xxx_relogin_device( - ha, ddb_entry); - } - - } + qla4xxx_relogin_all_devices(ha); } } @@ -1630,6 +1617,7 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, uint8_t init_retry_count = 0; char buf[34]; struct qla4_8xxx_legacy_intr_set *nx_legacy_intr; + uint32_t dev_state; if (pci_enable_device(pdev)) return -1; @@ -1713,6 +1701,18 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST); while ((!test_bit(AF_ONLINE, &ha->flags)) && init_retry_count++ < MAX_INIT_RETRIES) { + + if (is_qla8022(ha)) { + qla4_8xxx_idc_lock(ha); + dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + qla4_8xxx_idc_unlock(ha); + if (dev_state == QLA82XX_DEV_FAILED) { + ql4_printk(KERN_WARNING, ha, "%s: don't retry " + "initialize adapter. H/W is in failed state\n", + __func__); + break; + } + } DEBUG2(printk("scsi: %s: retrying adapter initialization " "(%d)\n", __func__, init_retry_count)); @@ -1815,6 +1815,44 @@ probe_disable_device: } /** + * qla4xxx_prevent_other_port_reinit - prevent other port from re-initialize + * @ha: pointer to adapter structure + * + * Mark the other ISP-4xxx port to indicate that the driver is being removed, + * so that the other port will not re-initialize while in the process of + * removing the ha due to driver unload or hba hotplug. + **/ +static void qla4xxx_prevent_other_port_reinit(struct scsi_qla_host *ha) +{ + struct scsi_qla_host *other_ha = NULL; + struct pci_dev *other_pdev = NULL; + int fn = ISP4XXX_PCI_FN_2; + + /*iscsi function numbers for ISP4xxx is 1 and 3*/ + if (PCI_FUNC(ha->pdev->devfn) & BIT_1) + fn = ISP4XXX_PCI_FN_1; + + other_pdev = + pci_get_domain_bus_and_slot(pci_domain_nr(ha->pdev->bus), + ha->pdev->bus->number, PCI_DEVFN(PCI_SLOT(ha->pdev->devfn), + fn)); + + /* Get other_ha if other_pdev is valid and state is enable*/ + if (other_pdev) { + if (atomic_read(&other_pdev->enable_cnt)) { + other_ha = pci_get_drvdata(other_pdev); + if (other_ha) { + set_bit(AF_HA_REMOVAL, &other_ha->flags); + DEBUG2(ql4_printk(KERN_INFO, ha, "%s: " + "Prevent %s reinit\n", __func__, + dev_name(&other_ha->pdev->dev))); + } + } + pci_dev_put(other_pdev); + } +} + +/** * qla4xxx_remove_adapter - calback function to remove adapter. * @pci_dev: PCI device pointer **/ @@ -1824,7 +1862,8 @@ static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev) ha = pci_get_drvdata(pdev); - set_bit(AF_HBA_GOING_AWAY, &ha->flags); + if (!is_qla8022(ha)) + qla4xxx_prevent_other_port_reinit(ha); /* remove devs from iscsi_sessions to scsi_devices */ qla4xxx_free_ddb_list(ha); @@ -1868,10 +1907,15 @@ static int qla4xxx_slave_alloc(struct scsi_device *sdev) { struct iscsi_cls_session *sess = starget_to_session(sdev->sdev_target); struct ddb_entry *ddb = sess->dd_data; + int queue_depth = QL4_DEF_QDEPTH; sdev->hostdata = ddb; sdev->tagged_supported = 1; - scsi_activate_tcq(sdev, QL4_DEF_QDEPTH); + + if (ql4xmaxqdepth != 0 && ql4xmaxqdepth <= 0xffffU) + queue_depth = ql4xmaxqdepth; + + scsi_activate_tcq(sdev, queue_depth); return 0; } diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h index 8475b30..6031557 100644 --- a/drivers/scsi/qla4xxx/ql4_version.h +++ b/drivers/scsi/qla4xxx/ql4_version.h @@ -5,4 +5,4 @@ * See LICENSE.qla4xxx for copyright and licensing details. */ -#define QLA4XXX_DRIVER_VERSION "5.02.00-k5" +#define QLA4XXX_DRIVER_VERSION "5.02.00-k6" diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index b421839..3fd16d7 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1917,7 +1917,7 @@ store_priv_session_##field(struct device *dev, \ #define iscsi_priv_session_rw_attr(field, format) \ iscsi_priv_session_attr_show(field, format) \ iscsi_priv_session_attr_store(field) \ -static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUGO, \ +static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUSR, \ show_priv_session_##field, \ store_priv_session_##field) iscsi_priv_session_rw_attr(recovery_tmo, "%d"); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 7ff61d7..b61ebec 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2027,14 +2027,10 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) int old_rcd = sdkp->RCD; int old_dpofua = sdkp->DPOFUA; - if (sdp->skip_ms_page_8) { - if (sdp->type == TYPE_RBC) - goto defaults; - else { - modepage = 0x3F; - dbd = 0; - } - } else if (sdp->type == TYPE_RBC) { + if (sdp->skip_ms_page_8) + goto defaults; + + if (sdp->type == TYPE_RBC) { modepage = 6; dbd = 8; } else { @@ -2062,11 +2058,13 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) */ if (len < 3) goto bad_sense; - else if (len > SD_BUF_SIZE) { - sd_printk(KERN_NOTICE, sdkp, "Truncating mode parameter " - "data from %d to %d bytes\n", len, SD_BUF_SIZE); - len = SD_BUF_SIZE; - } + if (len > 20) + len = 20; + + /* Take headers and block descriptors into account */ + len += data.header_length + data.block_descriptor_length; + if (len > SD_BUF_SIZE) + goto bad_sense; /* Get the data */ res = sd_do_mode_sense(sdp, dbd, modepage, buffer, len, &data, &sshdr); @@ -2074,45 +2072,16 @@ sd_read_cache_type(struct scsi_disk *sdkp, unsigned char *buffer) if (scsi_status_is_good(res)) { int offset = data.header_length + data.block_descriptor_length; - while (offset < len) { - u8 page_code = buffer[offset] & 0x3F; - u8 spf = buffer[offset] & 0x40; - - if (page_code == 8 || page_code == 6) { - /* We're interested only in the first 3 bytes. - */ - if (len - offset <= 2) { - sd_printk(KERN_ERR, sdkp, "Incomplete " - "mode parameter data\n"); - goto defaults; - } else { - modepage = page_code; - goto Page_found; - } - } else { - /* Go to the next page */ - if (spf && len - offset > 3) - offset += 4 + (buffer[offset+2] << 8) + - buffer[offset+3]; - else if (!spf && len - offset > 1) - offset += 2 + buffer[offset+1]; - else { - sd_printk(KERN_ERR, sdkp, "Incomplete " - "mode parameter data\n"); - goto defaults; - } - } + if (offset >= SD_BUF_SIZE - 2) { + sd_printk(KERN_ERR, sdkp, "Malformed MODE SENSE response\n"); + goto defaults; } - if (modepage == 0x3F) { - sd_printk(KERN_ERR, sdkp, "No Caching mode page " - "present\n"); - goto defaults; - } else if ((buffer[offset] & 0x3f) != modepage) { + if ((buffer[offset] & 0x3f) != modepage) { sd_printk(KERN_ERR, sdkp, "Got wrong page\n"); goto defaults; } - Page_found: + if (modepage == 8) { sdkp->WCE = ((buffer[offset + 2] & 0x04) != 0); sdkp->RCD = ((buffer[offset + 2] & 0x01) != 0); diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index 7f5a6a8..eb7a3e8 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -35,9 +35,11 @@ struct ses_device { unsigned char *page1; + unsigned char *page1_types; unsigned char *page2; unsigned char *page10; short page1_len; + short page1_num_types; short page2_len; short page10_len; }; @@ -110,12 +112,12 @@ static int ses_set_page2_descriptor(struct enclosure_device *edev, int i, j, count = 0, descriptor = ecomp->number; struct scsi_device *sdev = to_scsi_device(edev->edev.parent); struct ses_device *ses_dev = edev->scratch; - unsigned char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; + unsigned char *type_ptr = ses_dev->page1_types; unsigned char *desc_ptr = ses_dev->page2 + 8; /* Clear everything */ memset(desc_ptr, 0, ses_dev->page2_len - 8); - for (i = 0; i < ses_dev->page1[10]; i++, type_ptr += 4) { + for (i = 0; i < ses_dev->page1_num_types; i++, type_ptr += 4) { for (j = 0; j < type_ptr[1]; j++) { desc_ptr += 4; if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && @@ -140,12 +142,12 @@ static unsigned char *ses_get_page2_descriptor(struct enclosure_device *edev, int i, j, count = 0, descriptor = ecomp->number; struct scsi_device *sdev = to_scsi_device(edev->edev.parent); struct ses_device *ses_dev = edev->scratch; - unsigned char *type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; + unsigned char *type_ptr = ses_dev->page1_types; unsigned char *desc_ptr = ses_dev->page2 + 8; ses_recv_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len); - for (i = 0; i < ses_dev->page1[10]; i++, type_ptr += 4) { + for (i = 0; i < ses_dev->page1_num_types; i++, type_ptr += 4) { for (j = 0; j < type_ptr[1]; j++) { desc_ptr += 4; if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE && @@ -358,7 +360,7 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, unsigned char *buf = NULL, *type_ptr, *desc_ptr, *addl_desc_ptr = NULL; int i, j, page7_len, len, components; struct ses_device *ses_dev = edev->scratch; - int types = ses_dev->page1[10]; + int types = ses_dev->page1_num_types; unsigned char *hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL); if (!hdr_buf) @@ -390,10 +392,10 @@ static void ses_enclosure_data_process(struct enclosure_device *edev, len = (desc_ptr[2] << 8) + desc_ptr[3]; /* skip past overall descriptor */ desc_ptr += len + 4; - if (ses_dev->page10) - addl_desc_ptr = ses_dev->page10 + 8; } - type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11]; + if (ses_dev->page10) + addl_desc_ptr = ses_dev->page10 + 8; + type_ptr = ses_dev->page1_types; components = 0; for (i = 0; i < types; i++, type_ptr += 4) { for (j = 0; j < type_ptr[1]; j++) { @@ -503,6 +505,7 @@ static int ses_intf_add(struct device *cdev, u32 result; int i, types, len, components = 0; int err = -ENOMEM; + int num_enclosures; struct enclosure_device *edev; struct ses_component *scomp = NULL; @@ -530,16 +533,6 @@ static int ses_intf_add(struct device *cdev, if (result) goto recv_failed; - if (hdr_buf[1] != 0) { - /* FIXME: need subenclosure support; I've just never - * seen a device with subenclosures and it makes the - * traversal routines more complex */ - sdev_printk(KERN_ERR, sdev, - "FIXME driver has no support for subenclosures (%d)\n", - hdr_buf[1]); - goto err_free; - } - len = (hdr_buf[2] << 8) + hdr_buf[3] + 4; buf = kzalloc(len, GFP_KERNEL); if (!buf) @@ -549,11 +542,24 @@ static int ses_intf_add(struct device *cdev, if (result) goto recv_failed; - types = buf[10]; + types = 0; - type_ptr = buf + 12 + buf[11]; + /* we always have one main enclosure and the rest are referred + * to as secondary subenclosures */ + num_enclosures = buf[1] + 1; - for (i = 0; i < types; i++, type_ptr += 4) { + /* begin at the enclosure descriptor */ + type_ptr = buf + 8; + /* skip all the enclosure descriptors */ + for (i = 0; i < num_enclosures && type_ptr < buf + len; i++) { + types += type_ptr[2]; + type_ptr += type_ptr[3] + 4; + } + + ses_dev->page1_types = type_ptr; + ses_dev->page1_num_types = types; + + for (i = 0; i < types && type_ptr < buf + len; i++, type_ptr += 4) { if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE || type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) components += type_ptr[1]; diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig index 2fac3be..9ef2dbb 100644 --- a/drivers/target/Kconfig +++ b/drivers/target/Kconfig @@ -29,4 +29,6 @@ config TCM_PSCSI Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered passthrough access to Linux/SCSI device +source "drivers/target/loopback/Kconfig" + endif diff --git a/drivers/target/Makefile b/drivers/target/Makefile index 973bb19..1178bbf 100644 --- a/drivers/target/Makefile +++ b/drivers/target/Makefile @@ -1,4 +1,3 @@ -EXTRA_CFLAGS += -I$(srctree)/drivers/target/ -I$(srctree)/drivers/scsi/ target_core_mod-y := target_core_configfs.o \ target_core_device.o \ @@ -13,7 +12,8 @@ target_core_mod-y := target_core_configfs.o \ target_core_transport.o \ target_core_cdb.o \ target_core_ua.o \ - target_core_rd.o + target_core_rd.o \ + target_core_stat.o obj-$(CONFIG_TARGET_CORE) += target_core_mod.o @@ -21,3 +21,6 @@ obj-$(CONFIG_TARGET_CORE) += target_core_mod.o obj-$(CONFIG_TCM_IBLOCK) += target_core_iblock.o obj-$(CONFIG_TCM_FILEIO) += target_core_file.o obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o + +# Fabric modules +obj-$(CONFIG_LOOPBACK_TARGET) += loopback/ diff --git a/drivers/target/loopback/Kconfig b/drivers/target/loopback/Kconfig new file mode 100644 index 0000000..57dcbc2 --- /dev/null +++ b/drivers/target/loopback/Kconfig @@ -0,0 +1,11 @@ +config LOOPBACK_TARGET + tristate "TCM Virtual SAS target and Linux/SCSI LDD fabric loopback module" + help + Say Y here to enable the TCM Virtual SAS target and Linux/SCSI LLD + fabric loopback module. + +config LOOPBACK_TARGET_CDB_DEBUG + bool "TCM loopback fabric module CDB debug code" + depends on LOOPBACK_TARGET + help + Say Y here to enable the TCM loopback fabric module CDB debug code diff --git a/drivers/target/loopback/Makefile b/drivers/target/loopback/Makefile new file mode 100644 index 0000000..6abebdf --- /dev/null +++ b/drivers/target/loopback/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_LOOPBACK_TARGET) += tcm_loop.o diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c new file mode 100644 index 0000000..aed4e46 --- /dev/null +++ b/drivers/target/loopback/tcm_loop.c @@ -0,0 +1,1579 @@ +/******************************************************************************* + * + * This file contains the Linux/SCSI LLD virtual SCSI initiator driver + * for emulated SAS initiator ports + * + * © Copyright 2011 RisingTide Systems LLC. + * + * Licensed to the Linux Foundation under the General Public License (GPL) version 2. + * + * Author: Nicholas A. Bellinger <nab@risingtidesystems.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. + ****************************************************************************/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/configfs.h> +#include <scsi/scsi.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/libsas.h> /* For TASK_ATTR_* */ + +#include <target/target_core_base.h> +#include <target/target_core_transport.h> +#include <target/target_core_fabric_ops.h> +#include <target/target_core_fabric_configfs.h> +#include <target/target_core_fabric_lib.h> +#include <target/target_core_configfs.h> +#include <target/target_core_device.h> +#include <target/target_core_tpg.h> +#include <target/target_core_tmr.h> + +#include "tcm_loop.h" + +#define to_tcm_loop_hba(hba) container_of(hba, struct tcm_loop_hba, dev) + +/* Local pointer to allocated TCM configfs fabric module */ +static struct target_fabric_configfs *tcm_loop_fabric_configfs; + +static struct kmem_cache *tcm_loop_cmd_cache; + +static int tcm_loop_hba_no_cnt; + +/* + * Allocate a tcm_loop cmd descriptor from target_core_mod code + * + * Can be called from interrupt context in tcm_loop_queuecommand() below + */ +static struct se_cmd *tcm_loop_allocate_core_cmd( + struct tcm_loop_hba *tl_hba, + struct se_portal_group *se_tpg, + struct scsi_cmnd *sc) +{ + struct se_cmd *se_cmd; + struct se_session *se_sess; + struct tcm_loop_nexus *tl_nexus = tl_hba->tl_nexus; + struct tcm_loop_cmd *tl_cmd; + int sam_task_attr; + + if (!tl_nexus) { + scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus" + " does not exist\n"); + set_host_byte(sc, DID_ERROR); + return NULL; + } + se_sess = tl_nexus->se_sess; + + tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_ATOMIC); + if (!tl_cmd) { + printk(KERN_ERR "Unable to allocate struct tcm_loop_cmd\n"); + set_host_byte(sc, DID_ERROR); + return NULL; + } + se_cmd = &tl_cmd->tl_se_cmd; + /* + * Save the pointer to struct scsi_cmnd *sc + */ + tl_cmd->sc = sc; + /* + * Locate the SAM Task Attr from struct scsi_cmnd * + */ + if (sc->device->tagged_supported) { + switch (sc->tag) { + case HEAD_OF_QUEUE_TAG: + sam_task_attr = TASK_ATTR_HOQ; + break; + case ORDERED_QUEUE_TAG: + sam_task_attr = TASK_ATTR_ORDERED; + break; + default: + sam_task_attr = TASK_ATTR_SIMPLE; + break; + } + } else + sam_task_attr = TASK_ATTR_SIMPLE; + + /* + * Initialize struct se_cmd descriptor from target_core_mod infrastructure + */ + transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, + scsi_bufflen(sc), sc->sc_data_direction, sam_task_attr, + &tl_cmd->tl_sense_buf[0]); + + /* + * Signal BIDI usage with T_TASK(cmd)->t_tasks_bidi + */ + if (scsi_bidi_cmnd(sc)) + T_TASK(se_cmd)->t_tasks_bidi = 1; + /* + * Locate the struct se_lun pointer and attach it to struct se_cmd + */ + if (transport_get_lun_for_cmd(se_cmd, NULL, tl_cmd->sc->device->lun) < 0) { + kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); + set_host_byte(sc, DID_NO_CONNECT); + return NULL; + } + + transport_device_setup_cmd(se_cmd); + return se_cmd; +} + +/* + * Called by struct target_core_fabric_ops->new_cmd_map() + * + * Always called in process context. A non zero return value + * here will signal to handle an exception based on the return code. + */ +static int tcm_loop_new_cmd_map(struct se_cmd *se_cmd) +{ + struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, + struct tcm_loop_cmd, tl_se_cmd); + struct scsi_cmnd *sc = tl_cmd->sc; + void *mem_ptr, *mem_bidi_ptr = NULL; + u32 sg_no_bidi = 0; + int ret; + /* + * Allocate the necessary tasks to complete the received CDB+data + */ + ret = transport_generic_allocate_tasks(se_cmd, tl_cmd->sc->cmnd); + if (ret == -1) { + /* Out of Resources */ + return PYX_TRANSPORT_LU_COMM_FAILURE; + } else if (ret == -2) { + /* + * Handle case for SAM_STAT_RESERVATION_CONFLICT + */ + if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT) + return PYX_TRANSPORT_RESERVATION_CONFLICT; + /* + * Otherwise, return SAM_STAT_CHECK_CONDITION and return + * sense data. + */ + return PYX_TRANSPORT_USE_SENSE_REASON; + } + /* + * Setup the struct scatterlist memory from the received + * struct scsi_cmnd. + */ + if (scsi_sg_count(sc)) { + se_cmd->se_cmd_flags |= SCF_PASSTHROUGH_SG_TO_MEM; + mem_ptr = (void *)scsi_sglist(sc); + /* + * For BIDI commands, pass in the extra READ buffer + * to transport_generic_map_mem_to_cmd() below.. + */ + if (T_TASK(se_cmd)->t_tasks_bidi) { + struct scsi_data_buffer *sdb = scsi_in(sc); + + mem_bidi_ptr = (void *)sdb->table.sgl; + sg_no_bidi = sdb->table.nents; + } + } else { + /* + * Used for DMA_NONE + */ + mem_ptr = NULL; + } + /* + * Map the SG memory into struct se_mem->page linked list using the same + * physical memory at sg->page_link. + */ + ret = transport_generic_map_mem_to_cmd(se_cmd, mem_ptr, + scsi_sg_count(sc), mem_bidi_ptr, sg_no_bidi); + if (ret < 0) + return PYX_TRANSPORT_LU_COMM_FAILURE; + + return 0; +} + +/* + * Called from struct target_core_fabric_ops->check_stop_free() + */ +static void tcm_loop_check_stop_free(struct se_cmd *se_cmd) +{ + /* + * Do not release struct se_cmd's containing a valid TMR + * pointer. These will be released directly in tcm_loop_device_reset() + * with transport_generic_free_cmd(). + */ + if (se_cmd->se_tmr_req) + return; + /* + * Release the struct se_cmd, which will make a callback to release + * struct tcm_loop_cmd * in tcm_loop_deallocate_core_cmd() + */ + transport_generic_free_cmd(se_cmd, 0, 1, 0); +} + +/* + * Called from struct target_core_fabric_ops->release_cmd_to_pool() + */ +static void tcm_loop_deallocate_core_cmd(struct se_cmd *se_cmd) +{ + struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, + struct tcm_loop_cmd, tl_se_cmd); + + kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); +} + +static int tcm_loop_proc_info(struct Scsi_Host *host, char *buffer, + char **start, off_t offset, + int length, int inout) +{ + return sprintf(buffer, "tcm_loop_proc_info()\n"); +} + +static int tcm_loop_driver_probe(struct device *); +static int tcm_loop_driver_remove(struct device *); + +static int pseudo_lld_bus_match(struct device *dev, + struct device_driver *dev_driver) +{ + return 1; +} + +static struct bus_type tcm_loop_lld_bus = { + .name = "tcm_loop_bus", + .match = pseudo_lld_bus_match, + .probe = tcm_loop_driver_probe, + .remove = tcm_loop_driver_remove, +}; + +static struct device_driver tcm_loop_driverfs = { + .name = "tcm_loop", + .bus = &tcm_loop_lld_bus, +}; +/* + * Used with root_device_register() in tcm_loop_alloc_core_bus() below + */ +struct device *tcm_loop_primary; + +/* + * Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and + * drivers/scsi/libiscsi.c:iscsi_change_queue_depth() + */ +static int tcm_loop_change_queue_depth( + struct scsi_device *sdev, + int depth, + int reason) +{ + switch (reason) { + case SCSI_QDEPTH_DEFAULT: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + case SCSI_QDEPTH_QFULL: + scsi_track_queue_full(sdev, depth); + break; + case SCSI_QDEPTH_RAMP_UP: + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); + break; + default: + return -EOPNOTSUPP; + } + return sdev->queue_depth; +} + +/* + * Main entry point from struct scsi_host_template for incoming SCSI CDB+Data + * from Linux/SCSI subsystem for SCSI low level device drivers (LLDs) + */ +static int tcm_loop_queuecommand( + struct Scsi_Host *sh, + struct scsi_cmnd *sc) +{ + struct se_cmd *se_cmd; + struct se_portal_group *se_tpg; + struct tcm_loop_hba *tl_hba; + struct tcm_loop_tpg *tl_tpg; + + TL_CDB_DEBUG("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x" + " scsi_buf_len: %u\n", sc->device->host->host_no, + sc->device->id, sc->device->channel, sc->device->lun, + sc->cmnd[0], scsi_bufflen(sc)); + /* + * Locate the tcm_loop_hba_t pointer + */ + tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); + tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; + se_tpg = &tl_tpg->tl_se_tpg; + /* + * Determine the SAM Task Attribute and allocate tl_cmd and + * tl_cmd->tl_se_cmd from TCM infrastructure + */ + se_cmd = tcm_loop_allocate_core_cmd(tl_hba, se_tpg, sc); + if (!se_cmd) { + sc->scsi_done(sc); + return 0; + } + /* + * Queue up the newly allocated to be processed in TCM thread context. + */ + transport_generic_handle_cdb_map(se_cmd); + return 0; +} + +/* + * Called from SCSI EH process context to issue a LUN_RESET TMR + * to struct scsi_device + */ +static int tcm_loop_device_reset(struct scsi_cmnd *sc) +{ + struct se_cmd *se_cmd = NULL; + struct se_portal_group *se_tpg; + struct se_session *se_sess; + struct tcm_loop_cmd *tl_cmd = NULL; + struct tcm_loop_hba *tl_hba; + struct tcm_loop_nexus *tl_nexus; + struct tcm_loop_tmr *tl_tmr = NULL; + struct tcm_loop_tpg *tl_tpg; + int ret = FAILED; + /* + * Locate the tcm_loop_hba_t pointer + */ + tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); + /* + * Locate the tl_nexus and se_sess pointers + */ + tl_nexus = tl_hba->tl_nexus; + if (!tl_nexus) { + printk(KERN_ERR "Unable to perform device reset without" + " active I_T Nexus\n"); + return FAILED; + } + se_sess = tl_nexus->se_sess; + /* + * Locate the tl_tpg and se_tpg pointers from TargetID in sc->device->id + */ + tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; + se_tpg = &tl_tpg->tl_se_tpg; + + tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL); + if (!tl_cmd) { + printk(KERN_ERR "Unable to allocate memory for tl_cmd\n"); + return FAILED; + } + + tl_tmr = kzalloc(sizeof(struct tcm_loop_tmr), GFP_KERNEL); + if (!tl_tmr) { + printk(KERN_ERR "Unable to allocate memory for tl_tmr\n"); + goto release; + } + init_waitqueue_head(&tl_tmr->tl_tmr_wait); + + se_cmd = &tl_cmd->tl_se_cmd; + /* + * Initialize struct se_cmd descriptor from target_core_mod infrastructure + */ + transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, 0, + DMA_NONE, TASK_ATTR_SIMPLE, + &tl_cmd->tl_sense_buf[0]); + /* + * Allocate the LUN_RESET TMR + */ + se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd, (void *)tl_tmr, + TMR_LUN_RESET); + if (!se_cmd->se_tmr_req) + goto release; + /* + * Locate the underlying TCM struct se_lun from sc->device->lun + */ + if (transport_get_lun_for_tmr(se_cmd, sc->device->lun) < 0) + goto release; + /* + * Queue the TMR to TCM Core and sleep waiting for tcm_loop_queue_tm_rsp() + * to wake us up. + */ + transport_generic_handle_tmr(se_cmd); + wait_event(tl_tmr->tl_tmr_wait, atomic_read(&tl_tmr->tmr_complete)); + /* + * The TMR LUN_RESET has completed, check the response status and + * then release allocations. + */ + ret = (se_cmd->se_tmr_req->response == TMR_FUNCTION_COMPLETE) ? + SUCCESS : FAILED; +release: + if (se_cmd) + transport_generic_free_cmd(se_cmd, 1, 1, 0); + else + kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); + kfree(tl_tmr); + return ret; +} + +static int tcm_loop_slave_alloc(struct scsi_device *sd) +{ + set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags); + return 0; +} + +static int tcm_loop_slave_configure(struct scsi_device *sd) +{ + return 0; +} + +static struct scsi_host_template tcm_loop_driver_template = { + .proc_info = tcm_loop_proc_info, + .proc_name = "tcm_loopback", + .name = "TCM_Loopback", + .queuecommand = tcm_loop_queuecommand, + .change_queue_depth = tcm_loop_change_queue_depth, + .eh_device_reset_handler = tcm_loop_device_reset, + .can_queue = TL_SCSI_CAN_QUEUE, + .this_id = -1, + .sg_tablesize = TL_SCSI_SG_TABLESIZE, + .cmd_per_lun = TL_SCSI_CMD_PER_LUN, + .max_sectors = TL_SCSI_MAX_SECTORS, + .use_clustering = DISABLE_CLUSTERING, + .slave_alloc = tcm_loop_slave_alloc, + .slave_configure = tcm_loop_slave_configure, + .module = THIS_MODULE, +}; + +static int tcm_loop_driver_probe(struct device *dev) +{ + struct tcm_loop_hba *tl_hba; + struct Scsi_Host *sh; + int error; + + tl_hba = to_tcm_loop_hba(dev); + + sh = scsi_host_alloc(&tcm_loop_driver_template, + sizeof(struct tcm_loop_hba)); + if (!sh) { + printk(KERN_ERR "Unable to allocate struct scsi_host\n"); + return -ENODEV; + } + tl_hba->sh = sh; + + /* + * Assign the struct tcm_loop_hba pointer to struct Scsi_Host->hostdata + */ + *((struct tcm_loop_hba **)sh->hostdata) = tl_hba; + /* + * Setup single ID, Channel and LUN for now.. + */ + sh->max_id = 2; + sh->max_lun = 0; + sh->max_channel = 0; + sh->max_cmd_len = TL_SCSI_MAX_CMD_LEN; + + error = scsi_add_host(sh, &tl_hba->dev); + if (error) { + printk(KERN_ERR "%s: scsi_add_host failed\n", __func__); + scsi_host_put(sh); + return -ENODEV; + } + return 0; +} + +static int tcm_loop_driver_remove(struct device *dev) +{ + struct tcm_loop_hba *tl_hba; + struct Scsi_Host *sh; + + tl_hba = to_tcm_loop_hba(dev); + sh = tl_hba->sh; + + scsi_remove_host(sh); + scsi_host_put(sh); + return 0; +} + +static void tcm_loop_release_adapter(struct device *dev) +{ + struct tcm_loop_hba *tl_hba = to_tcm_loop_hba(dev); + + kfree(tl_hba); +} + +/* + * Called from tcm_loop_make_scsi_hba() in tcm_loop_configfs.c + */ +static int tcm_loop_setup_hba_bus(struct tcm_loop_hba *tl_hba, int tcm_loop_host_id) +{ + int ret; + + tl_hba->dev.bus = &tcm_loop_lld_bus; + tl_hba->dev.parent = tcm_loop_primary; + tl_hba->dev.release = &tcm_loop_release_adapter; + dev_set_name(&tl_hba->dev, "tcm_loop_adapter_%d", tcm_loop_host_id); + + ret = device_register(&tl_hba->dev); + if (ret) { + printk(KERN_ERR "device_register() failed for" + " tl_hba->dev: %d\n", ret); + return -ENODEV; + } + + return 0; +} + +/* + * Called from tcm_loop_fabric_init() in tcl_loop_fabric.c to load the emulated + * tcm_loop SCSI bus. + */ +static int tcm_loop_alloc_core_bus(void) +{ + int ret; + + tcm_loop_primary = root_device_register("tcm_loop_0"); + if (IS_ERR(tcm_loop_primary)) { + printk(KERN_ERR "Unable to allocate tcm_loop_primary\n"); + return PTR_ERR(tcm_loop_primary); + } + + ret = bus_register(&tcm_loop_lld_bus); + if (ret) { + printk(KERN_ERR "bus_register() failed for tcm_loop_lld_bus\n"); + goto dev_unreg; + } + + ret = driver_register(&tcm_loop_driverfs); + if (ret) { + printk(KERN_ERR "driver_register() failed for" + "tcm_loop_driverfs\n"); + goto bus_unreg; + } + + printk(KERN_INFO "Initialized TCM Loop Core Bus\n"); + return ret; + +bus_unreg: + bus_unregister(&tcm_loop_lld_bus); +dev_unreg: + root_device_unregister(tcm_loop_primary); + return ret; +} + +static void tcm_loop_release_core_bus(void) +{ + driver_unregister(&tcm_loop_driverfs); + bus_unregister(&tcm_loop_lld_bus); + root_device_unregister(tcm_loop_primary); + + printk(KERN_INFO "Releasing TCM Loop Core BUS\n"); +} + +static char *tcm_loop_get_fabric_name(void) +{ + return "loopback"; +} + +static u8 tcm_loop_get_fabric_proto_ident(struct se_portal_group *se_tpg) +{ + struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + /* + * tl_proto_id is set at tcm_loop_configfs.c:tcm_loop_make_scsi_hba() + * time based on the protocol dependent prefix of the passed configfs group. + * + * Based upon tl_proto_id, TCM_Loop emulates the requested fabric + * ProtocolID using target_core_fabric_lib.c symbols. + */ + switch (tl_hba->tl_proto_id) { + case SCSI_PROTOCOL_SAS: + return sas_get_fabric_proto_ident(se_tpg); + case SCSI_PROTOCOL_FCP: + return fc_get_fabric_proto_ident(se_tpg); + case SCSI_PROTOCOL_ISCSI: + return iscsi_get_fabric_proto_ident(se_tpg); + default: + printk(KERN_ERR "Unknown tl_proto_id: 0x%02x, using" + " SAS emulation\n", tl_hba->tl_proto_id); + break; + } + + return sas_get_fabric_proto_ident(se_tpg); +} + +static char *tcm_loop_get_endpoint_wwn(struct se_portal_group *se_tpg) +{ + struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + /* + * Return the passed NAA identifier for the SAS Target Port + */ + return &tl_tpg->tl_hba->tl_wwn_address[0]; +} + +static u16 tcm_loop_get_tag(struct se_portal_group *se_tpg) +{ + struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + /* + * This Tag is used when forming SCSI Name identifier in EVPD=1 0x83 + * to represent the SCSI Target Port. + */ + return tl_tpg->tl_tpgt; +} + +static u32 tcm_loop_get_default_depth(struct se_portal_group *se_tpg) +{ + return 1; +} + +static u32 tcm_loop_get_pr_transport_id( + struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl, + struct t10_pr_registration *pr_reg, + int *format_code, + unsigned char *buf) +{ + struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + + switch (tl_hba->tl_proto_id) { + case SCSI_PROTOCOL_SAS: + return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg, + format_code, buf); + case SCSI_PROTOCOL_FCP: + return fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg, + format_code, buf); + case SCSI_PROTOCOL_ISCSI: + return iscsi_get_pr_transport_id(se_tpg, se_nacl, pr_reg, + format_code, buf); + default: + printk(KERN_ERR "Unknown tl_proto_id: 0x%02x, using" + " SAS emulation\n", tl_hba->tl_proto_id); + break; + } + + return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg, + format_code, buf); +} + +static u32 tcm_loop_get_pr_transport_id_len( + struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl, + struct t10_pr_registration *pr_reg, + int *format_code) +{ + struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + + switch (tl_hba->tl_proto_id) { + case SCSI_PROTOCOL_SAS: + return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, + format_code); + case SCSI_PROTOCOL_FCP: + return fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, + format_code); + case SCSI_PROTOCOL_ISCSI: + return iscsi_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, + format_code); + default: + printk(KERN_ERR "Unknown tl_proto_id: 0x%02x, using" + " SAS emulation\n", tl_hba->tl_proto_id); + break; + } + + return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, + format_code); +} + +/* + * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above + * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations. + */ +static char *tcm_loop_parse_pr_out_transport_id( + struct se_portal_group *se_tpg, + const char *buf, + u32 *out_tid_len, + char **port_nexus_ptr) +{ + struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + + switch (tl_hba->tl_proto_id) { + case SCSI_PROTOCOL_SAS: + return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, + port_nexus_ptr); + case SCSI_PROTOCOL_FCP: + return fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, + port_nexus_ptr); + case SCSI_PROTOCOL_ISCSI: + return iscsi_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, + port_nexus_ptr); + default: + printk(KERN_ERR "Unknown tl_proto_id: 0x%02x, using" + " SAS emulation\n", tl_hba->tl_proto_id); + break; + } + + return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, + port_nexus_ptr); +} + +/* + * Returning (1) here allows for target_core_mod struct se_node_acl to be generated + * based upon the incoming fabric dependent SCSI Initiator Port + */ +static int tcm_loop_check_demo_mode(struct se_portal_group *se_tpg) +{ + return 1; +} + +static int tcm_loop_check_demo_mode_cache(struct se_portal_group *se_tpg) +{ + return 0; +} + +/* + * Allow I_T Nexus full READ-WRITE access without explict Initiator Node ACLs for + * local virtual Linux/SCSI LLD passthrough into VM hypervisor guest + */ +static int tcm_loop_check_demo_mode_write_protect(struct se_portal_group *se_tpg) +{ + return 0; +} + +/* + * Because TCM_Loop does not use explict ACLs and MappedLUNs, this will + * never be called for TCM_Loop by target_core_fabric_configfs.c code. + * It has been added here as a nop for target_fabric_tf_ops_check() + */ +static int tcm_loop_check_prod_mode_write_protect(struct se_portal_group *se_tpg) +{ + return 0; +} + +static struct se_node_acl *tcm_loop_tpg_alloc_fabric_acl( + struct se_portal_group *se_tpg) +{ + struct tcm_loop_nacl *tl_nacl; + + tl_nacl = kzalloc(sizeof(struct tcm_loop_nacl), GFP_KERNEL); + if (!tl_nacl) { + printk(KERN_ERR "Unable to allocate struct tcm_loop_nacl\n"); + return NULL; + } + + return &tl_nacl->se_node_acl; +} + +static void tcm_loop_tpg_release_fabric_acl( + struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl) +{ + struct tcm_loop_nacl *tl_nacl = container_of(se_nacl, + struct tcm_loop_nacl, se_node_acl); + + kfree(tl_nacl); +} + +static u32 tcm_loop_get_inst_index(struct se_portal_group *se_tpg) +{ + return 1; +} + +static void tcm_loop_new_cmd_failure(struct se_cmd *se_cmd) +{ + /* + * Since TCM_loop is already passing struct scatterlist data from + * struct scsi_cmnd, no more Linux/SCSI failure dependent state need + * to be handled here. + */ + return; +} + +static int tcm_loop_is_state_remove(struct se_cmd *se_cmd) +{ + /* + * Assume struct scsi_cmnd is not in remove state.. + */ + return 0; +} + +static int tcm_loop_sess_logged_in(struct se_session *se_sess) +{ + /* + * Assume that TL Nexus is always active + */ + return 1; +} + +static u32 tcm_loop_sess_get_index(struct se_session *se_sess) +{ + return 1; +} + +static void tcm_loop_set_default_node_attributes(struct se_node_acl *se_acl) +{ + return; +} + +static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd) +{ + return 1; +} + +static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd) +{ + struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, + struct tcm_loop_cmd, tl_se_cmd); + + return tl_cmd->sc_cmd_state; +} + +static int tcm_loop_shutdown_session(struct se_session *se_sess) +{ + return 0; +} + +static void tcm_loop_close_session(struct se_session *se_sess) +{ + return; +}; + +static void tcm_loop_stop_session( + struct se_session *se_sess, + int sess_sleep, + int conn_sleep) +{ + return; +} + +static void tcm_loop_fall_back_to_erl0(struct se_session *se_sess) +{ + return; +} + +static int tcm_loop_write_pending(struct se_cmd *se_cmd) +{ + /* + * Since Linux/SCSI has already sent down a struct scsi_cmnd + * sc->sc_data_direction of DMA_TO_DEVICE with struct scatterlist array + * memory, and memory has already been mapped to struct se_cmd->t_mem_list + * format with transport_generic_map_mem_to_cmd(). + * + * We now tell TCM to add this WRITE CDB directly into the TCM storage + * object execution queue. + */ + transport_generic_process_write(se_cmd); + return 0; +} + +static int tcm_loop_write_pending_status(struct se_cmd *se_cmd) +{ + return 0; +} + +static int tcm_loop_queue_data_in(struct se_cmd *se_cmd) +{ + struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, + struct tcm_loop_cmd, tl_se_cmd); + struct scsi_cmnd *sc = tl_cmd->sc; + + TL_CDB_DEBUG("tcm_loop_queue_data_in() called for scsi_cmnd: %p" + " cdb: 0x%02x\n", sc, sc->cmnd[0]); + + sc->result = SAM_STAT_GOOD; + set_host_byte(sc, DID_OK); + sc->scsi_done(sc); + return 0; +} + +static int tcm_loop_queue_status(struct se_cmd *se_cmd) +{ + struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, + struct tcm_loop_cmd, tl_se_cmd); + struct scsi_cmnd *sc = tl_cmd->sc; + + TL_CDB_DEBUG("tcm_loop_queue_status() called for scsi_cmnd: %p" + " cdb: 0x%02x\n", sc, sc->cmnd[0]); + + if (se_cmd->sense_buffer && + ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) || + (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE))) { + + memcpy((void *)sc->sense_buffer, (void *)se_cmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE); + sc->result = SAM_STAT_CHECK_CONDITION; + set_driver_byte(sc, DRIVER_SENSE); + } else + sc->result = se_cmd->scsi_status; + + set_host_byte(sc, DID_OK); + sc->scsi_done(sc); + return 0; +} + +static int tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd) +{ + struct se_tmr_req *se_tmr = se_cmd->se_tmr_req; + struct tcm_loop_tmr *tl_tmr = se_tmr->fabric_tmr_ptr; + /* + * The SCSI EH thread will be sleeping on se_tmr->tl_tmr_wait, go ahead + * and wake up the wait_queue_head_t in tcm_loop_device_reset() + */ + atomic_set(&tl_tmr->tmr_complete, 1); + wake_up(&tl_tmr->tl_tmr_wait); + return 0; +} + +static u16 tcm_loop_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length) +{ + return 0; +} + +static u16 tcm_loop_get_fabric_sense_len(void) +{ + return 0; +} + +static u64 tcm_loop_pack_lun(unsigned int lun) +{ + u64 result; + + /* LSB of lun into byte 1 big-endian */ + result = ((lun & 0xff) << 8); + /* use flat space addressing method */ + result |= 0x40 | ((lun >> 8) & 0x3f); + + return cpu_to_le64(result); +} + +static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba) +{ + switch (tl_hba->tl_proto_id) { + case SCSI_PROTOCOL_SAS: + return "SAS"; + case SCSI_PROTOCOL_FCP: + return "FCP"; + case SCSI_PROTOCOL_ISCSI: + return "iSCSI"; + default: + break; + } + + return "Unknown"; +} + +/* Start items for tcm_loop_port_cit */ + +static int tcm_loop_port_link( + struct se_portal_group *se_tpg, + struct se_lun *lun) +{ + struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, + struct tcm_loop_tpg, tl_se_tpg); + struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + + atomic_inc(&tl_tpg->tl_tpg_port_count); + smp_mb__after_atomic_inc(); + /* + * Add Linux/SCSI struct scsi_device by HCTL + */ + scsi_add_device(tl_hba->sh, 0, tl_tpg->tl_tpgt, lun->unpacked_lun); + + printk(KERN_INFO "TCM_Loop_ConfigFS: Port Link Successful\n"); + return 0; +} + +static void tcm_loop_port_unlink( + struct se_portal_group *se_tpg, + struct se_lun *se_lun) +{ + struct scsi_device *sd; + struct tcm_loop_hba *tl_hba; + struct tcm_loop_tpg *tl_tpg; + + tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, tl_se_tpg); + tl_hba = tl_tpg->tl_hba; + + sd = scsi_device_lookup(tl_hba->sh, 0, tl_tpg->tl_tpgt, + se_lun->unpacked_lun); + if (!sd) { + printk(KERN_ERR "Unable to locate struct scsi_device for %d:%d:" + "%d\n", 0, tl_tpg->tl_tpgt, se_lun->unpacked_lun); + return; + } + /* + * Remove Linux/SCSI struct scsi_device by HCTL + */ + scsi_remove_device(sd); + scsi_device_put(sd); + + atomic_dec(&tl_tpg->tl_tpg_port_count); + smp_mb__after_atomic_dec(); + + printk(KERN_INFO "TCM_Loop_ConfigFS: Port Unlink Successful\n"); +} + +/* End items for tcm_loop_port_cit */ + +/* Start items for tcm_loop_nexus_cit */ + +static int tcm_loop_make_nexus( + struct tcm_loop_tpg *tl_tpg, + const char *name) +{ + struct se_portal_group *se_tpg; + struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + struct tcm_loop_nexus *tl_nexus; + + if (tl_tpg->tl_hba->tl_nexus) { + printk(KERN_INFO "tl_tpg->tl_hba->tl_nexus already exists\n"); + return -EEXIST; + } + se_tpg = &tl_tpg->tl_se_tpg; + + tl_nexus = kzalloc(sizeof(struct tcm_loop_nexus), GFP_KERNEL); + if (!tl_nexus) { + printk(KERN_ERR "Unable to allocate struct tcm_loop_nexus\n"); + return -ENOMEM; + } + /* + * Initialize the struct se_session pointer + */ + tl_nexus->se_sess = transport_init_session(); + if (!tl_nexus->se_sess) + goto out; + /* + * Since we are running in 'demo mode' this call with generate a + * struct se_node_acl for the tcm_loop struct se_portal_group with the SCSI + * Initiator port name of the passed configfs group 'name'. + */ + tl_nexus->se_sess->se_node_acl = core_tpg_check_initiator_node_acl( + se_tpg, (unsigned char *)name); + if (!tl_nexus->se_sess->se_node_acl) { + transport_free_session(tl_nexus->se_sess); + goto out; + } + /* + * Now, register the SAS I_T Nexus as active with the call to + * transport_register_session() + */ + __transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl, + tl_nexus->se_sess, (void *)tl_nexus); + tl_tpg->tl_hba->tl_nexus = tl_nexus; + printk(KERN_INFO "TCM_Loop_ConfigFS: Established I_T Nexus to emulated" + " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba), + name); + return 0; + +out: + kfree(tl_nexus); + return -ENOMEM; +} + +static int tcm_loop_drop_nexus( + struct tcm_loop_tpg *tpg) +{ + struct se_session *se_sess; + struct tcm_loop_nexus *tl_nexus; + struct tcm_loop_hba *tl_hba = tpg->tl_hba; + + tl_nexus = tpg->tl_hba->tl_nexus; + if (!tl_nexus) + return -ENODEV; + + se_sess = tl_nexus->se_sess; + if (!se_sess) + return -ENODEV; + + if (atomic_read(&tpg->tl_tpg_port_count)) { + printk(KERN_ERR "Unable to remove TCM_Loop I_T Nexus with" + " active TPG port count: %d\n", + atomic_read(&tpg->tl_tpg_port_count)); + return -EPERM; + } + + printk(KERN_INFO "TCM_Loop_ConfigFS: Removing I_T Nexus to emulated" + " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba), + tl_nexus->se_sess->se_node_acl->initiatorname); + /* + * Release the SCSI I_T Nexus to the emulated SAS Target Port + */ + transport_deregister_session(tl_nexus->se_sess); + tpg->tl_hba->tl_nexus = NULL; + kfree(tl_nexus); + return 0; +} + +/* End items for tcm_loop_nexus_cit */ + +static ssize_t tcm_loop_tpg_show_nexus( + struct se_portal_group *se_tpg, + char *page) +{ + struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, + struct tcm_loop_tpg, tl_se_tpg); + struct tcm_loop_nexus *tl_nexus; + ssize_t ret; + + tl_nexus = tl_tpg->tl_hba->tl_nexus; + if (!tl_nexus) + return -ENODEV; + + ret = snprintf(page, PAGE_SIZE, "%s\n", + tl_nexus->se_sess->se_node_acl->initiatorname); + + return ret; +} + +static ssize_t tcm_loop_tpg_store_nexus( + struct se_portal_group *se_tpg, + const char *page, + size_t count) +{ + struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, + struct tcm_loop_tpg, tl_se_tpg); + struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + unsigned char i_port[TL_WWN_ADDR_LEN], *ptr, *port_ptr; + int ret; + /* + * Shutdown the active I_T nexus if 'NULL' is passed.. + */ + if (!strncmp(page, "NULL", 4)) { + ret = tcm_loop_drop_nexus(tl_tpg); + return (!ret) ? count : ret; + } + /* + * Otherwise make sure the passed virtual Initiator port WWN matches + * the fabric protocol_id set in tcm_loop_make_scsi_hba(), and call + * tcm_loop_make_nexus() + */ + if (strlen(page) > TL_WWN_ADDR_LEN) { + printk(KERN_ERR "Emulated NAA Sas Address: %s, exceeds" + " max: %d\n", page, TL_WWN_ADDR_LEN); + return -EINVAL; + } + snprintf(&i_port[0], TL_WWN_ADDR_LEN, "%s", page); + + ptr = strstr(i_port, "naa."); + if (ptr) { + if (tl_hba->tl_proto_id != SCSI_PROTOCOL_SAS) { + printk(KERN_ERR "Passed SAS Initiator Port %s does not" + " match target port protoid: %s\n", i_port, + tcm_loop_dump_proto_id(tl_hba)); + return -EINVAL; + } + port_ptr = &i_port[0]; + goto check_newline; + } + ptr = strstr(i_port, "fc."); + if (ptr) { + if (tl_hba->tl_proto_id != SCSI_PROTOCOL_FCP) { + printk(KERN_ERR "Passed FCP Initiator Port %s does not" + " match target port protoid: %s\n", i_port, + tcm_loop_dump_proto_id(tl_hba)); + return -EINVAL; + } + port_ptr = &i_port[3]; /* Skip over "fc." */ + goto check_newline; + } + ptr = strstr(i_port, "iqn."); + if (ptr) { + if (tl_hba->tl_proto_id != SCSI_PROTOCOL_ISCSI) { + printk(KERN_ERR "Passed iSCSI Initiator Port %s does not" + " match target port protoid: %s\n", i_port, + tcm_loop_dump_proto_id(tl_hba)); + return -EINVAL; + } + port_ptr = &i_port[0]; + goto check_newline; + } + printk(KERN_ERR "Unable to locate prefix for emulated Initiator Port:" + " %s\n", i_port); + return -EINVAL; + /* + * Clear any trailing newline for the NAA WWN + */ +check_newline: + if (i_port[strlen(i_port)-1] == '\n') + i_port[strlen(i_port)-1] = '\0'; + + ret = tcm_loop_make_nexus(tl_tpg, port_ptr); + if (ret < 0) + return ret; + + return count; +} + +TF_TPG_BASE_ATTR(tcm_loop, nexus, S_IRUGO | S_IWUSR); + +static struct configfs_attribute *tcm_loop_tpg_attrs[] = { + &tcm_loop_tpg_nexus.attr, + NULL, +}; + +/* Start items for tcm_loop_naa_cit */ + +struct se_portal_group *tcm_loop_make_naa_tpg( + struct se_wwn *wwn, + struct config_group *group, + const char *name) +{ + struct tcm_loop_hba *tl_hba = container_of(wwn, + struct tcm_loop_hba, tl_hba_wwn); + struct tcm_loop_tpg *tl_tpg; + char *tpgt_str, *end_ptr; + int ret; + unsigned short int tpgt; + + tpgt_str = strstr(name, "tpgt_"); + if (!tpgt_str) { + printk(KERN_ERR "Unable to locate \"tpgt_#\" directory" + " group\n"); + return ERR_PTR(-EINVAL); + } + tpgt_str += 5; /* Skip ahead of "tpgt_" */ + tpgt = (unsigned short int) simple_strtoul(tpgt_str, &end_ptr, 0); + + if (tpgt > TL_TPGS_PER_HBA) { + printk(KERN_ERR "Passed tpgt: %hu exceeds TL_TPGS_PER_HBA:" + " %u\n", tpgt, TL_TPGS_PER_HBA); + return ERR_PTR(-EINVAL); + } + tl_tpg = &tl_hba->tl_hba_tpgs[tpgt]; + tl_tpg->tl_hba = tl_hba; + tl_tpg->tl_tpgt = tpgt; + /* + * Register the tl_tpg as a emulated SAS TCM Target Endpoint + */ + ret = core_tpg_register(&tcm_loop_fabric_configfs->tf_ops, + wwn, &tl_tpg->tl_se_tpg, (void *)tl_tpg, + TRANSPORT_TPG_TYPE_NORMAL); + if (ret < 0) + return ERR_PTR(-ENOMEM); + + printk(KERN_INFO "TCM_Loop_ConfigFS: Allocated Emulated %s" + " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba), + config_item_name(&wwn->wwn_group.cg_item), tpgt); + + return &tl_tpg->tl_se_tpg; +} + +void tcm_loop_drop_naa_tpg( + struct se_portal_group *se_tpg) +{ + struct se_wwn *wwn = se_tpg->se_tpg_wwn; + struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, + struct tcm_loop_tpg, tl_se_tpg); + struct tcm_loop_hba *tl_hba; + unsigned short tpgt; + + tl_hba = tl_tpg->tl_hba; + tpgt = tl_tpg->tl_tpgt; + /* + * Release the I_T Nexus for the Virtual SAS link if present + */ + tcm_loop_drop_nexus(tl_tpg); + /* + * Deregister the tl_tpg as a emulated SAS TCM Target Endpoint + */ + core_tpg_deregister(se_tpg); + + printk(KERN_INFO "TCM_Loop_ConfigFS: Deallocated Emulated %s" + " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba), + config_item_name(&wwn->wwn_group.cg_item), tpgt); +} + +/* End items for tcm_loop_naa_cit */ + +/* Start items for tcm_loop_cit */ + +struct se_wwn *tcm_loop_make_scsi_hba( + struct target_fabric_configfs *tf, + struct config_group *group, + const char *name) +{ + struct tcm_loop_hba *tl_hba; + struct Scsi_Host *sh; + char *ptr; + int ret, off = 0; + + tl_hba = kzalloc(sizeof(struct tcm_loop_hba), GFP_KERNEL); + if (!tl_hba) { + printk(KERN_ERR "Unable to allocate struct tcm_loop_hba\n"); + return ERR_PTR(-ENOMEM); + } + /* + * Determine the emulated Protocol Identifier and Target Port Name + * based on the incoming configfs directory name. + */ + ptr = strstr(name, "naa."); + if (ptr) { + tl_hba->tl_proto_id = SCSI_PROTOCOL_SAS; + goto check_len; + } + ptr = strstr(name, "fc."); + if (ptr) { + tl_hba->tl_proto_id = SCSI_PROTOCOL_FCP; + off = 3; /* Skip over "fc." */ + goto check_len; + } + ptr = strstr(name, "iqn."); + if (ptr) { + tl_hba->tl_proto_id = SCSI_PROTOCOL_ISCSI; + goto check_len; + } + + printk(KERN_ERR "Unable to locate prefix for emulated Target Port:" + " %s\n", name); + return ERR_PTR(-EINVAL); + +check_len: + if (strlen(name) > TL_WWN_ADDR_LEN) { + printk(KERN_ERR "Emulated NAA %s Address: %s, exceeds" + " max: %d\n", name, tcm_loop_dump_proto_id(tl_hba), + TL_WWN_ADDR_LEN); + kfree(tl_hba); + return ERR_PTR(-EINVAL); + } + snprintf(&tl_hba->tl_wwn_address[0], TL_WWN_ADDR_LEN, "%s", &name[off]); + + /* + * Call device_register(tl_hba->dev) to register the emulated + * Linux/SCSI LLD of type struct Scsi_Host at tl_hba->sh after + * device_register() callbacks in tcm_loop_driver_probe() + */ + ret = tcm_loop_setup_hba_bus(tl_hba, tcm_loop_hba_no_cnt); + if (ret) + goto out; + + sh = tl_hba->sh; + tcm_loop_hba_no_cnt++; + printk(KERN_INFO "TCM_Loop_ConfigFS: Allocated emulated Target" + " %s Address: %s at Linux/SCSI Host ID: %d\n", + tcm_loop_dump_proto_id(tl_hba), name, sh->host_no); + + return &tl_hba->tl_hba_wwn; +out: + kfree(tl_hba); + return ERR_PTR(ret); +} + +void tcm_loop_drop_scsi_hba( + struct se_wwn *wwn) +{ + struct tcm_loop_hba *tl_hba = container_of(wwn, + struct tcm_loop_hba, tl_hba_wwn); + int host_no = tl_hba->sh->host_no; + /* + * Call device_unregister() on the original tl_hba->dev. + * tcm_loop_fabric_scsi.c:tcm_loop_release_adapter() will + * release *tl_hba; + */ + device_unregister(&tl_hba->dev); + + printk(KERN_INFO "TCM_Loop_ConfigFS: Deallocated emulated Target" + " SAS Address: %s at Linux/SCSI Host ID: %d\n", + config_item_name(&wwn->wwn_group.cg_item), host_no); +} + +/* Start items for tcm_loop_cit */ +static ssize_t tcm_loop_wwn_show_attr_version( + struct target_fabric_configfs *tf, + char *page) +{ + return sprintf(page, "TCM Loopback Fabric module %s\n", TCM_LOOP_VERSION); +} + +TF_WWN_ATTR_RO(tcm_loop, version); + +static struct configfs_attribute *tcm_loop_wwn_attrs[] = { + &tcm_loop_wwn_version.attr, + NULL, +}; + +/* End items for tcm_loop_cit */ + +static int tcm_loop_register_configfs(void) +{ + struct target_fabric_configfs *fabric; + struct config_group *tf_cg; + int ret; + /* + * Set the TCM Loop HBA counter to zero + */ + tcm_loop_hba_no_cnt = 0; + /* + * Register the top level struct config_item_type with TCM core + */ + fabric = target_fabric_configfs_init(THIS_MODULE, "loopback"); + if (!fabric) { + printk(KERN_ERR "tcm_loop_register_configfs() failed!\n"); + return -1; + } + /* + * Setup the fabric API of function pointers used by target_core_mod + */ + fabric->tf_ops.get_fabric_name = &tcm_loop_get_fabric_name; + fabric->tf_ops.get_fabric_proto_ident = &tcm_loop_get_fabric_proto_ident; + fabric->tf_ops.tpg_get_wwn = &tcm_loop_get_endpoint_wwn; + fabric->tf_ops.tpg_get_tag = &tcm_loop_get_tag; + fabric->tf_ops.tpg_get_default_depth = &tcm_loop_get_default_depth; + fabric->tf_ops.tpg_get_pr_transport_id = &tcm_loop_get_pr_transport_id; + fabric->tf_ops.tpg_get_pr_transport_id_len = + &tcm_loop_get_pr_transport_id_len; + fabric->tf_ops.tpg_parse_pr_out_transport_id = + &tcm_loop_parse_pr_out_transport_id; + fabric->tf_ops.tpg_check_demo_mode = &tcm_loop_check_demo_mode; + fabric->tf_ops.tpg_check_demo_mode_cache = + &tcm_loop_check_demo_mode_cache; + fabric->tf_ops.tpg_check_demo_mode_write_protect = + &tcm_loop_check_demo_mode_write_protect; + fabric->tf_ops.tpg_check_prod_mode_write_protect = + &tcm_loop_check_prod_mode_write_protect; + /* + * The TCM loopback fabric module runs in demo-mode to a local + * virtual SCSI device, so fabric dependent initator ACLs are + * not required. + */ + fabric->tf_ops.tpg_alloc_fabric_acl = &tcm_loop_tpg_alloc_fabric_acl; + fabric->tf_ops.tpg_release_fabric_acl = + &tcm_loop_tpg_release_fabric_acl; + fabric->tf_ops.tpg_get_inst_index = &tcm_loop_get_inst_index; + /* + * Since tcm_loop is mapping physical memory from Linux/SCSI + * struct scatterlist arrays for each struct scsi_cmnd I/O, + * we do not need TCM to allocate a iovec array for + * virtual memory address mappings + */ + fabric->tf_ops.alloc_cmd_iovecs = NULL; + /* + * Used for setting up remaining TCM resources in process context + */ + fabric->tf_ops.new_cmd_map = &tcm_loop_new_cmd_map; + fabric->tf_ops.check_stop_free = &tcm_loop_check_stop_free; + fabric->tf_ops.release_cmd_to_pool = &tcm_loop_deallocate_core_cmd; + fabric->tf_ops.release_cmd_direct = &tcm_loop_deallocate_core_cmd; + fabric->tf_ops.shutdown_session = &tcm_loop_shutdown_session; + fabric->tf_ops.close_session = &tcm_loop_close_session; + fabric->tf_ops.stop_session = &tcm_loop_stop_session; + fabric->tf_ops.fall_back_to_erl0 = &tcm_loop_fall_back_to_erl0; + fabric->tf_ops.sess_logged_in = &tcm_loop_sess_logged_in; + fabric->tf_ops.sess_get_index = &tcm_loop_sess_get_index; + fabric->tf_ops.sess_get_initiator_sid = NULL; + fabric->tf_ops.write_pending = &tcm_loop_write_pending; + fabric->tf_ops.write_pending_status = &tcm_loop_write_pending_status; + /* + * Not used for TCM loopback + */ + fabric->tf_ops.set_default_node_attributes = + &tcm_loop_set_default_node_attributes; + fabric->tf_ops.get_task_tag = &tcm_loop_get_task_tag; + fabric->tf_ops.get_cmd_state = &tcm_loop_get_cmd_state; + fabric->tf_ops.new_cmd_failure = &tcm_loop_new_cmd_failure; + fabric->tf_ops.queue_data_in = &tcm_loop_queue_data_in; + fabric->tf_ops.queue_status = &tcm_loop_queue_status; + fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp; + fabric->tf_ops.set_fabric_sense_len = &tcm_loop_set_fabric_sense_len; + fabric->tf_ops.get_fabric_sense_len = &tcm_loop_get_fabric_sense_len; + fabric->tf_ops.is_state_remove = &tcm_loop_is_state_remove; + fabric->tf_ops.pack_lun = &tcm_loop_pack_lun; + + tf_cg = &fabric->tf_group; + /* + * Setup function pointers for generic logic in target_core_fabric_configfs.c + */ + fabric->tf_ops.fabric_make_wwn = &tcm_loop_make_scsi_hba; + fabric->tf_ops.fabric_drop_wwn = &tcm_loop_drop_scsi_hba; + fabric->tf_ops.fabric_make_tpg = &tcm_loop_make_naa_tpg; + fabric->tf_ops.fabric_drop_tpg = &tcm_loop_drop_naa_tpg; + /* + * fabric_post_link() and fabric_pre_unlink() are used for + * registration and release of TCM Loop Virtual SCSI LUNs. + */ + fabric->tf_ops.fabric_post_link = &tcm_loop_port_link; + fabric->tf_ops.fabric_pre_unlink = &tcm_loop_port_unlink; + fabric->tf_ops.fabric_make_np = NULL; + fabric->tf_ops.fabric_drop_np = NULL; + /* + * Setup default attribute lists for various fabric->tf_cit_tmpl + */ + TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs; + TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs; + TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL; + TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL; + TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL; + /* + * Once fabric->tf_ops has been setup, now register the fabric for + * use within TCM + */ + ret = target_fabric_configfs_register(fabric); + if (ret < 0) { + printk(KERN_ERR "target_fabric_configfs_register() for" + " TCM_Loop failed!\n"); + target_fabric_configfs_free(fabric); + return -1; + } + /* + * Setup our local pointer to *fabric. + */ + tcm_loop_fabric_configfs = fabric; + printk(KERN_INFO "TCM_LOOP[0] - Set fabric ->" + " tcm_loop_fabric_configfs\n"); + return 0; +} + +static void tcm_loop_deregister_configfs(void) +{ + if (!tcm_loop_fabric_configfs) + return; + + target_fabric_configfs_deregister(tcm_loop_fabric_configfs); + tcm_loop_fabric_configfs = NULL; + printk(KERN_INFO "TCM_LOOP[0] - Cleared" + " tcm_loop_fabric_configfs\n"); +} + +static int __init tcm_loop_fabric_init(void) +{ + int ret; + + tcm_loop_cmd_cache = kmem_cache_create("tcm_loop_cmd_cache", + sizeof(struct tcm_loop_cmd), + __alignof__(struct tcm_loop_cmd), + 0, NULL); + if (!tcm_loop_cmd_cache) { + printk(KERN_ERR "kmem_cache_create() for" + " tcm_loop_cmd_cache failed\n"); + return -ENOMEM; + } + + ret = tcm_loop_alloc_core_bus(); + if (ret) + return ret; + + ret = tcm_loop_register_configfs(); + if (ret) { + tcm_loop_release_core_bus(); + return ret; + } + + return 0; +} + +static void __exit tcm_loop_fabric_exit(void) +{ + tcm_loop_deregister_configfs(); + tcm_loop_release_core_bus(); + kmem_cache_destroy(tcm_loop_cmd_cache); +} + +MODULE_DESCRIPTION("TCM loopback virtual Linux/SCSI fabric module"); +MODULE_AUTHOR("Nicholas A. Bellinger <nab@risingtidesystems.com>"); +MODULE_LICENSE("GPL"); +module_init(tcm_loop_fabric_init); +module_exit(tcm_loop_fabric_exit); diff --git a/drivers/target/loopback/tcm_loop.h b/drivers/target/loopback/tcm_loop.h new file mode 100644 index 0000000..7e9f7ab --- /dev/null +++ b/drivers/target/loopback/tcm_loop.h @@ -0,0 +1,77 @@ +#define TCM_LOOP_VERSION "v2.1-rc1" +#define TL_WWN_ADDR_LEN 256 +#define TL_TPGS_PER_HBA 32 +/* + * Defaults for struct scsi_host_template tcm_loop_driver_template + * + * We use large can_queue and cmd_per_lun here and let TCM enforce + * the underlying se_device_t->queue_depth. + */ +#define TL_SCSI_CAN_QUEUE 1024 +#define TL_SCSI_CMD_PER_LUN 1024 +#define TL_SCSI_MAX_SECTORS 1024 +#define TL_SCSI_SG_TABLESIZE 256 +/* + * Used in tcm_loop_driver_probe() for struct Scsi_Host->max_cmd_len + */ +#define TL_SCSI_MAX_CMD_LEN 32 + +#ifdef CONFIG_LOOPBACK_TARGET_CDB_DEBUG +# define TL_CDB_DEBUG(x...) printk(KERN_INFO x) +#else +# define TL_CDB_DEBUG(x...) +#endif + +struct tcm_loop_cmd { + /* State of Linux/SCSI CDB+Data descriptor */ + u32 sc_cmd_state; + /* Pointer to the CDB+Data descriptor from Linux/SCSI subsystem */ + struct scsi_cmnd *sc; + struct list_head *tl_cmd_list; + /* The TCM I/O descriptor that is accessed via container_of() */ + struct se_cmd tl_se_cmd; + /* Sense buffer that will be mapped into outgoing status */ + unsigned char tl_sense_buf[TRANSPORT_SENSE_BUFFER]; +}; + +struct tcm_loop_tmr { + atomic_t tmr_complete; + wait_queue_head_t tl_tmr_wait; +}; + +struct tcm_loop_nexus { + int it_nexus_active; + /* + * Pointer to Linux/SCSI HBA from linux/include/scsi_host.h + */ + struct scsi_host *sh; + /* + * Pointer to TCM session for I_T Nexus + */ + struct se_session *se_sess; +}; + +struct tcm_loop_nacl { + struct se_node_acl se_node_acl; +}; + +struct tcm_loop_tpg { + unsigned short tl_tpgt; + atomic_t tl_tpg_port_count; + struct se_portal_group tl_se_tpg; + struct tcm_loop_hba *tl_hba; +}; + +struct tcm_loop_hba { + u8 tl_proto_id; + unsigned char tl_wwn_address[TL_WWN_ADDR_LEN]; + struct se_hba_s *se_hba; + struct se_lun *tl_hba_lun; + struct se_port *tl_hba_lun_sep; + struct se_device_s *se_dev_hba_ptr; + struct tcm_loop_nexus *tl_nexus; + struct device dev; + struct Scsi_Host *sh; + struct tcm_loop_tpg tl_hba_tpgs[TL_TPGS_PER_HBA]; + struct se_wwn tl_hba_wwn; +}; diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index caf8dc1..a5f44a6 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -3,8 +3,8 @@ * * This file contains ConfigFS logic for the Generic Target Engine project. * - * Copyright (c) 2008-2010 Rising Tide Systems - * Copyright (c) 2008-2010 Linux-iSCSI.org + * Copyright (c) 2008-2011 Rising Tide Systems + * Copyright (c) 2008-2011 Linux-iSCSI.org * * Nicholas A. Bellinger <nab@kernel.org> * @@ -50,6 +50,7 @@ #include "target_core_hba.h" #include "target_core_pr.h" #include "target_core_rd.h" +#include "target_core_stat.h" static struct list_head g_tf_list; static struct mutex g_tf_lock; @@ -1451,8 +1452,8 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( size_t count) { struct se_device *dev; - unsigned char *i_fabric, *t_fabric, *i_port = NULL, *t_port = NULL; - unsigned char *isid = NULL; + unsigned char *i_fabric = NULL, *i_port = NULL, *isid = NULL; + unsigned char *t_fabric = NULL, *t_port = NULL; char *orig, *ptr, *arg_p, *opts; substring_t args[MAX_OPT_ARGS]; unsigned long long tmp_ll; @@ -1488,9 +1489,17 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( switch (token) { case Opt_initiator_fabric: i_fabric = match_strdup(&args[0]); + if (!i_fabric) { + ret = -ENOMEM; + goto out; + } break; case Opt_initiator_node: i_port = match_strdup(&args[0]); + if (!i_port) { + ret = -ENOMEM; + goto out; + } if (strlen(i_port) > PR_APTPL_MAX_IPORT_LEN) { printk(KERN_ERR "APTPL metadata initiator_node=" " exceeds PR_APTPL_MAX_IPORT_LEN: %d\n", @@ -1501,6 +1510,10 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( break; case Opt_initiator_sid: isid = match_strdup(&args[0]); + if (!isid) { + ret = -ENOMEM; + goto out; + } if (strlen(isid) > PR_REG_ISID_LEN) { printk(KERN_ERR "APTPL metadata initiator_isid" "= exceeds PR_REG_ISID_LEN: %d\n", @@ -1511,6 +1524,10 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( break; case Opt_sa_res_key: arg_p = match_strdup(&args[0]); + if (!arg_p) { + ret = -ENOMEM; + goto out; + } ret = strict_strtoull(arg_p, 0, &tmp_ll); if (ret < 0) { printk(KERN_ERR "strict_strtoull() failed for" @@ -1547,9 +1564,17 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( */ case Opt_target_fabric: t_fabric = match_strdup(&args[0]); + if (!t_fabric) { + ret = -ENOMEM; + goto out; + } break; case Opt_target_node: t_port = match_strdup(&args[0]); + if (!t_port) { + ret = -ENOMEM; + goto out; + } if (strlen(t_port) > PR_APTPL_MAX_TPORT_LEN) { printk(KERN_ERR "APTPL metadata target_node=" " exceeds PR_APTPL_MAX_TPORT_LEN: %d\n", @@ -1592,6 +1617,11 @@ static ssize_t target_core_dev_pr_store_attr_res_aptpl_metadata( i_port, isid, mapped_lun, t_port, tpgt, target_lun, res_holder, all_tg_pt, type); out: + kfree(i_fabric); + kfree(i_port); + kfree(isid); + kfree(t_fabric); + kfree(t_port); kfree(orig); return (ret == 0) ? count : ret; } @@ -1798,7 +1828,9 @@ static ssize_t target_core_store_dev_enable( return -EINVAL; dev = t->create_virtdevice(hba, se_dev, se_dev->se_dev_su_ptr); - if (!(dev) || IS_ERR(dev)) + if (IS_ERR(dev)) + return PTR_ERR(dev); + else if (!dev) return -EINVAL; se_dev->se_dev_ptr = dev; @@ -2678,6 +2710,34 @@ static struct config_item_type target_core_alua_cit = { /* End functions for struct config_item_type target_core_alua_cit */ +/* Start functions for struct config_item_type target_core_stat_cit */ + +static struct config_group *target_core_stat_mkdir( + struct config_group *group, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static void target_core_stat_rmdir( + struct config_group *group, + struct config_item *item) +{ + return; +} + +static struct configfs_group_operations target_core_stat_group_ops = { + .make_group = &target_core_stat_mkdir, + .drop_item = &target_core_stat_rmdir, +}; + +static struct config_item_type target_core_stat_cit = { + .ct_group_ops = &target_core_stat_group_ops, + .ct_owner = THIS_MODULE, +}; + +/* End functions for struct config_item_type target_core_stat_cit */ + /* Start functions for struct config_item_type target_core_hba_cit */ static struct config_group *target_core_make_subdev( @@ -2690,10 +2750,12 @@ static struct config_group *target_core_make_subdev( struct config_item *hba_ci = &group->cg_item; struct se_hba *hba = item_to_hba(hba_ci); struct config_group *dev_cg = NULL, *tg_pt_gp_cg = NULL; + struct config_group *dev_stat_grp = NULL; + int errno = -ENOMEM, ret; - if (mutex_lock_interruptible(&hba->hba_access_mutex)) - return NULL; - + ret = mutex_lock_interruptible(&hba->hba_access_mutex); + if (ret) + return ERR_PTR(ret); /* * Locate the struct se_subsystem_api from parent's struct se_hba. */ @@ -2723,7 +2785,7 @@ static struct config_group *target_core_make_subdev( se_dev->se_dev_hba = hba; dev_cg = &se_dev->se_dev_group; - dev_cg->default_groups = kzalloc(sizeof(struct config_group) * 6, + dev_cg->default_groups = kzalloc(sizeof(struct config_group) * 7, GFP_KERNEL); if (!(dev_cg->default_groups)) goto out; @@ -2755,13 +2817,17 @@ static struct config_group *target_core_make_subdev( &target_core_dev_wwn_cit); config_group_init_type_name(&se_dev->t10_alua.alua_tg_pt_gps_group, "alua", &target_core_alua_tg_pt_gps_cit); + config_group_init_type_name(&se_dev->dev_stat_grps.stat_group, + "statistics", &target_core_stat_cit); + dev_cg->default_groups[0] = &se_dev->se_dev_attrib.da_group; dev_cg->default_groups[1] = &se_dev->se_dev_pr_group; dev_cg->default_groups[2] = &se_dev->t10_wwn.t10_wwn_group; dev_cg->default_groups[3] = &se_dev->t10_alua.alua_tg_pt_gps_group; - dev_cg->default_groups[4] = NULL; + dev_cg->default_groups[4] = &se_dev->dev_stat_grps.stat_group; + dev_cg->default_groups[5] = NULL; /* - * Add core/$HBA/$DEV/alua/tg_pt_gps/default_tg_pt_gp + * Add core/$HBA/$DEV/alua/default_tg_pt_gp */ tg_pt_gp = core_alua_allocate_tg_pt_gp(se_dev, "default_tg_pt_gp", 1); if (!(tg_pt_gp)) @@ -2781,6 +2847,17 @@ static struct config_group *target_core_make_subdev( tg_pt_gp_cg->default_groups[0] = &tg_pt_gp->tg_pt_gp_group; tg_pt_gp_cg->default_groups[1] = NULL; T10_ALUA(se_dev)->default_tg_pt_gp = tg_pt_gp; + /* + * Add core/$HBA/$DEV/statistics/ default groups + */ + dev_stat_grp = &DEV_STAT_GRP(se_dev)->stat_group; + dev_stat_grp->default_groups = kzalloc(sizeof(struct config_group) * 4, + GFP_KERNEL); + if (!dev_stat_grp->default_groups) { + printk(KERN_ERR "Unable to allocate dev_stat_grp->default_groups\n"); + goto out; + } + target_stat_setup_dev_default_groups(se_dev); printk(KERN_INFO "Target_Core_ConfigFS: Allocated struct se_subsystem_dev:" " %p se_dev_su_ptr: %p\n", se_dev, se_dev->se_dev_su_ptr); @@ -2792,6 +2869,8 @@ out: core_alua_free_tg_pt_gp(T10_ALUA(se_dev)->default_tg_pt_gp); T10_ALUA(se_dev)->default_tg_pt_gp = NULL; } + if (dev_stat_grp) + kfree(dev_stat_grp->default_groups); if (tg_pt_gp_cg) kfree(tg_pt_gp_cg->default_groups); if (dev_cg) @@ -2801,7 +2880,7 @@ out: kfree(se_dev); unlock: mutex_unlock(&hba->hba_access_mutex); - return NULL; + return ERR_PTR(errno); } static void target_core_drop_subdev( @@ -2813,7 +2892,7 @@ static void target_core_drop_subdev( struct se_hba *hba; struct se_subsystem_api *t; struct config_item *df_item; - struct config_group *dev_cg, *tg_pt_gp_cg; + struct config_group *dev_cg, *tg_pt_gp_cg, *dev_stat_grp; int i; hba = item_to_hba(&se_dev->se_dev_hba->hba_group.cg_item); @@ -2825,6 +2904,14 @@ static void target_core_drop_subdev( list_del(&se_dev->g_se_dev_list); spin_unlock(&se_global->g_device_lock); + dev_stat_grp = &DEV_STAT_GRP(se_dev)->stat_group; + for (i = 0; dev_stat_grp->default_groups[i]; i++) { + df_item = &dev_stat_grp->default_groups[i]->cg_item; + dev_stat_grp->default_groups[i] = NULL; + config_item_put(df_item); + } + kfree(dev_stat_grp->default_groups); + tg_pt_gp_cg = &T10_ALUA(se_dev)->alua_tg_pt_gps_group; for (i = 0; tg_pt_gp_cg->default_groups[i]; i++) { df_item = &tg_pt_gp_cg->default_groups[i]->cg_item; @@ -3044,7 +3131,7 @@ static struct config_item_type target_core_cit = { /* Stop functions for struct config_item_type target_core_hba_cit */ -static int target_core_init_configfs(void) +static int __init target_core_init_configfs(void) { struct config_group *target_cg, *hba_cg = NULL, *alua_cg = NULL; struct config_group *lu_gp_cg = NULL; @@ -3176,7 +3263,7 @@ out_global: return -1; } -static void target_core_exit_configfs(void) +static void __exit target_core_exit_configfs(void) { struct configfs_subsystem *subsys; struct config_group *hba_cg, *alua_cg, *lu_gp_cg; diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 350ed40..3fb8e32 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -589,6 +589,7 @@ static void core_export_port( * Called with struct se_device->se_port_lock spinlock held. */ static void core_release_port(struct se_device *dev, struct se_port *port) + __releases(&dev->se_port_lock) __acquires(&dev->se_port_lock) { /* * Wait for any port reference for PR ALL_TG_PT=1 operation @@ -779,49 +780,14 @@ void se_release_vpd_for_dev(struct se_device *dev) return; } -/* - * Called with struct se_hba->device_lock held. - */ -void se_clear_dev_ports(struct se_device *dev) -{ - struct se_hba *hba = dev->se_hba; - struct se_lun *lun; - struct se_portal_group *tpg; - struct se_port *sep, *sep_tmp; - - spin_lock(&dev->se_port_lock); - list_for_each_entry_safe(sep, sep_tmp, &dev->dev_sep_list, sep_list) { - spin_unlock(&dev->se_port_lock); - spin_unlock(&hba->device_lock); - - lun = sep->sep_lun; - tpg = sep->sep_tpg; - spin_lock(&lun->lun_sep_lock); - if (lun->lun_se_dev == NULL) { - spin_unlock(&lun->lun_sep_lock); - continue; - } - spin_unlock(&lun->lun_sep_lock); - - core_dev_del_lun(tpg, lun->unpacked_lun); - - spin_lock(&hba->device_lock); - spin_lock(&dev->se_port_lock); - } - spin_unlock(&dev->se_port_lock); - - return; -} - /* se_free_virtual_device(): * * Used for IBLOCK, RAMDISK, and FILEIO Transport Drivers. */ int se_free_virtual_device(struct se_device *dev, struct se_hba *hba) { - spin_lock(&hba->device_lock); - se_clear_dev_ports(dev); - spin_unlock(&hba->device_lock); + if (!list_empty(&dev->dev_sep_list)) + dump_stack(); core_alua_free_lu_gp_mem(dev); se_release_device_for_hba(dev); diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index b65d1c8..07ab5a3 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -4,10 +4,10 @@ * This file contains generic fabric module configfs infrastructure for * TCM v4.x code * - * Copyright (c) 2010 Rising Tide Systems - * Copyright (c) 2010 Linux-iSCSI.org + * Copyright (c) 2010,2011 Rising Tide Systems + * Copyright (c) 2010,2011 Linux-iSCSI.org * - * Copyright (c) 2010 Nicholas A. Bellinger <nab@linux-iscsi.org> + * Copyright (c) Nicholas A. Bellinger <nab@linux-iscsi.org> * * 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 @@ -48,6 +48,7 @@ #include "target_core_alua.h" #include "target_core_hba.h" #include "target_core_pr.h" +#include "target_core_stat.h" #define TF_CIT_SETUP(_name, _item_ops, _group_ops, _attrs) \ static void target_fabric_setup_##_name##_cit(struct target_fabric_configfs *tf) \ @@ -241,6 +242,32 @@ TF_CIT_SETUP(tpg_mappedlun, &target_fabric_mappedlun_item_ops, NULL, /* End of tfc_tpg_mappedlun_cit */ +/* Start of tfc_tpg_mappedlun_port_cit */ + +static struct config_group *target_core_mappedlun_stat_mkdir( + struct config_group *group, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static void target_core_mappedlun_stat_rmdir( + struct config_group *group, + struct config_item *item) +{ + return; +} + +static struct configfs_group_operations target_fabric_mappedlun_stat_group_ops = { + .make_group = target_core_mappedlun_stat_mkdir, + .drop_item = target_core_mappedlun_stat_rmdir, +}; + +TF_CIT_SETUP(tpg_mappedlun_stat, NULL, &target_fabric_mappedlun_stat_group_ops, + NULL); + +/* End of tfc_tpg_mappedlun_port_cit */ + /* Start of tfc_tpg_nacl_attrib_cit */ CONFIGFS_EATTR_OPS(target_fabric_nacl_attrib, se_node_acl, acl_attrib_group); @@ -294,6 +321,7 @@ static struct config_group *target_fabric_make_mappedlun( struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf; struct se_lun_acl *lacl; struct config_item *acl_ci; + struct config_group *lacl_cg = NULL, *ml_stat_grp = NULL; char *buf; unsigned long mapped_lun; int ret = 0; @@ -330,15 +358,42 @@ static struct config_group *target_fabric_make_mappedlun( lacl = core_dev_init_initiator_node_lun_acl(se_tpg, mapped_lun, config_item_name(acl_ci), &ret); - if (!(lacl)) + if (!(lacl)) { + ret = -EINVAL; goto out; + } + + lacl_cg = &lacl->se_lun_group; + lacl_cg->default_groups = kzalloc(sizeof(struct config_group) * 2, + GFP_KERNEL); + if (!lacl_cg->default_groups) { + printk(KERN_ERR "Unable to allocate lacl_cg->default_groups\n"); + ret = -ENOMEM; + goto out; + } config_group_init_type_name(&lacl->se_lun_group, name, &TF_CIT_TMPL(tf)->tfc_tpg_mappedlun_cit); + config_group_init_type_name(&lacl->ml_stat_grps.stat_group, + "statistics", &TF_CIT_TMPL(tf)->tfc_tpg_mappedlun_stat_cit); + lacl_cg->default_groups[0] = &lacl->ml_stat_grps.stat_group; + lacl_cg->default_groups[1] = NULL; + + ml_stat_grp = &ML_STAT_GRPS(lacl)->stat_group; + ml_stat_grp->default_groups = kzalloc(sizeof(struct config_group) * 3, + GFP_KERNEL); + if (!ml_stat_grp->default_groups) { + printk(KERN_ERR "Unable to allocate ml_stat_grp->default_groups\n"); + ret = -ENOMEM; + goto out; + } + target_stat_setup_mappedlun_default_groups(lacl); kfree(buf); return &lacl->se_lun_group; out: + if (lacl_cg) + kfree(lacl_cg->default_groups); kfree(buf); return ERR_PTR(ret); } @@ -347,6 +402,28 @@ static void target_fabric_drop_mappedlun( struct config_group *group, struct config_item *item) { + struct se_lun_acl *lacl = container_of(to_config_group(item), + struct se_lun_acl, se_lun_group); + struct config_item *df_item; + struct config_group *lacl_cg = NULL, *ml_stat_grp = NULL; + int i; + + ml_stat_grp = &ML_STAT_GRPS(lacl)->stat_group; + for (i = 0; ml_stat_grp->default_groups[i]; i++) { + df_item = &ml_stat_grp->default_groups[i]->cg_item; + ml_stat_grp->default_groups[i] = NULL; + config_item_put(df_item); + } + kfree(ml_stat_grp->default_groups); + + lacl_cg = &lacl->se_lun_group; + for (i = 0; lacl_cg->default_groups[i]; i++) { + df_item = &lacl_cg->default_groups[i]->cg_item; + lacl_cg->default_groups[i] = NULL; + config_item_put(df_item); + } + kfree(lacl_cg->default_groups); + config_item_put(item); } @@ -376,6 +453,15 @@ TF_CIT_SETUP(tpg_nacl_base, &target_fabric_nacl_base_item_ops, /* End of tfc_tpg_nacl_base_cit */ +/* Start of tfc_node_fabric_stats_cit */ +/* + * This is used as a placeholder for struct se_node_acl->acl_fabric_stat_group + * to allow fabrics access to ->acl_fabric_stat_group->default_groups[] + */ +TF_CIT_SETUP(tpg_nacl_stat, NULL, NULL, NULL); + +/* End of tfc_wwn_fabric_stats_cit */ + /* Start of tfc_tpg_nacl_cit */ static struct config_group *target_fabric_make_nodeacl( @@ -402,7 +488,8 @@ static struct config_group *target_fabric_make_nodeacl( nacl_cg->default_groups[0] = &se_nacl->acl_attrib_group; nacl_cg->default_groups[1] = &se_nacl->acl_auth_group; nacl_cg->default_groups[2] = &se_nacl->acl_param_group; - nacl_cg->default_groups[3] = NULL; + nacl_cg->default_groups[3] = &se_nacl->acl_fabric_stat_group; + nacl_cg->default_groups[4] = NULL; config_group_init_type_name(&se_nacl->acl_group, name, &TF_CIT_TMPL(tf)->tfc_tpg_nacl_base_cit); @@ -412,6 +499,9 @@ static struct config_group *target_fabric_make_nodeacl( &TF_CIT_TMPL(tf)->tfc_tpg_nacl_auth_cit); config_group_init_type_name(&se_nacl->acl_param_group, "param", &TF_CIT_TMPL(tf)->tfc_tpg_nacl_param_cit); + config_group_init_type_name(&se_nacl->acl_fabric_stat_group, + "fabric_statistics", + &TF_CIT_TMPL(tf)->tfc_tpg_nacl_stat_cit); return &se_nacl->acl_group; } @@ -758,6 +848,31 @@ TF_CIT_SETUP(tpg_port, &target_fabric_port_item_ops, NULL, target_fabric_port_at /* End of tfc_tpg_port_cit */ +/* Start of tfc_tpg_port_stat_cit */ + +static struct config_group *target_core_port_stat_mkdir( + struct config_group *group, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static void target_core_port_stat_rmdir( + struct config_group *group, + struct config_item *item) +{ + return; +} + +static struct configfs_group_operations target_fabric_port_stat_group_ops = { + .make_group = target_core_port_stat_mkdir, + .drop_item = target_core_port_stat_rmdir, +}; + +TF_CIT_SETUP(tpg_port_stat, NULL, &target_fabric_port_stat_group_ops, NULL); + +/* End of tfc_tpg_port_stat_cit */ + /* Start of tfc_tpg_lun_cit */ static struct config_group *target_fabric_make_lun( @@ -768,7 +883,9 @@ static struct config_group *target_fabric_make_lun( struct se_portal_group *se_tpg = container_of(group, struct se_portal_group, tpg_lun_group); struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf; + struct config_group *lun_cg = NULL, *port_stat_grp = NULL; unsigned long unpacked_lun; + int errno; if (strstr(name, "lun_") != name) { printk(KERN_ERR "Unable to locate \'_\" in" @@ -782,16 +899,64 @@ static struct config_group *target_fabric_make_lun( if (!(lun)) return ERR_PTR(-EINVAL); + lun_cg = &lun->lun_group; + lun_cg->default_groups = kzalloc(sizeof(struct config_group) * 2, + GFP_KERNEL); + if (!lun_cg->default_groups) { + printk(KERN_ERR "Unable to allocate lun_cg->default_groups\n"); + return ERR_PTR(-ENOMEM); + } + config_group_init_type_name(&lun->lun_group, name, &TF_CIT_TMPL(tf)->tfc_tpg_port_cit); + config_group_init_type_name(&lun->port_stat_grps.stat_group, + "statistics", &TF_CIT_TMPL(tf)->tfc_tpg_port_stat_cit); + lun_cg->default_groups[0] = &lun->port_stat_grps.stat_group; + lun_cg->default_groups[1] = NULL; + + port_stat_grp = &PORT_STAT_GRP(lun)->stat_group; + port_stat_grp->default_groups = kzalloc(sizeof(struct config_group) * 3, + GFP_KERNEL); + if (!port_stat_grp->default_groups) { + printk(KERN_ERR "Unable to allocate port_stat_grp->default_groups\n"); + errno = -ENOMEM; + goto out; + } + target_stat_setup_port_default_groups(lun); return &lun->lun_group; +out: + if (lun_cg) + kfree(lun_cg->default_groups); + return ERR_PTR(errno); } static void target_fabric_drop_lun( struct config_group *group, struct config_item *item) { + struct se_lun *lun = container_of(to_config_group(item), + struct se_lun, lun_group); + struct config_item *df_item; + struct config_group *lun_cg, *port_stat_grp; + int i; + + port_stat_grp = &PORT_STAT_GRP(lun)->stat_group; + for (i = 0; port_stat_grp->default_groups[i]; i++) { + df_item = &port_stat_grp->default_groups[i]->cg_item; + port_stat_grp->default_groups[i] = NULL; + config_item_put(df_item); + } + kfree(port_stat_grp->default_groups); + + lun_cg = &lun->lun_group; + for (i = 0; lun_cg->default_groups[i]; i++) { + df_item = &lun_cg->default_groups[i]->cg_item; + lun_cg->default_groups[i] = NULL; + config_item_put(df_item); + } + kfree(lun_cg->default_groups); + config_item_put(item); } @@ -946,6 +1111,15 @@ TF_CIT_SETUP(tpg, &target_fabric_tpg_item_ops, &target_fabric_tpg_group_ops, /* End of tfc_tpg_cit */ +/* Start of tfc_wwn_fabric_stats_cit */ +/* + * This is used as a placeholder for struct se_wwn->fabric_stat_group + * to allow fabrics access to ->fabric_stat_group->default_groups[] + */ +TF_CIT_SETUP(wwn_fabric_stats, NULL, NULL, NULL); + +/* End of tfc_wwn_fabric_stats_cit */ + /* Start of tfc_wwn_cit */ static struct config_group *target_fabric_make_wwn( @@ -966,8 +1140,17 @@ static struct config_group *target_fabric_make_wwn( return ERR_PTR(-EINVAL); wwn->wwn_tf = tf; + /* + * Setup default groups from pre-allocated wwn->wwn_default_groups + */ + wwn->wwn_group.default_groups = wwn->wwn_default_groups; + wwn->wwn_group.default_groups[0] = &wwn->fabric_stat_group; + wwn->wwn_group.default_groups[1] = NULL; + config_group_init_type_name(&wwn->wwn_group, name, &TF_CIT_TMPL(tf)->tfc_tpg_cit); + config_group_init_type_name(&wwn->fabric_stat_group, "fabric_statistics", + &TF_CIT_TMPL(tf)->tfc_wwn_fabric_stats_cit); return &wwn->wwn_group; } @@ -976,6 +1159,18 @@ static void target_fabric_drop_wwn( struct config_group *group, struct config_item *item) { + struct se_wwn *wwn = container_of(to_config_group(item), + struct se_wwn, wwn_group); + struct config_item *df_item; + struct config_group *cg = &wwn->wwn_group; + int i; + + for (i = 0; cg->default_groups[i]; i++) { + df_item = &cg->default_groups[i]->cg_item; + cg->default_groups[i] = NULL; + config_item_put(df_item); + } + config_item_put(item); } @@ -1015,9 +1210,11 @@ int target_fabric_setup_cits(struct target_fabric_configfs *tf) { target_fabric_setup_discovery_cit(tf); target_fabric_setup_wwn_cit(tf); + target_fabric_setup_wwn_fabric_stats_cit(tf); target_fabric_setup_tpg_cit(tf); target_fabric_setup_tpg_base_cit(tf); target_fabric_setup_tpg_port_cit(tf); + target_fabric_setup_tpg_port_stat_cit(tf); target_fabric_setup_tpg_lun_cit(tf); target_fabric_setup_tpg_np_cit(tf); target_fabric_setup_tpg_np_base_cit(tf); @@ -1028,7 +1225,9 @@ int target_fabric_setup_cits(struct target_fabric_configfs *tf) target_fabric_setup_tpg_nacl_attrib_cit(tf); target_fabric_setup_tpg_nacl_auth_cit(tf); target_fabric_setup_tpg_nacl_param_cit(tf); + target_fabric_setup_tpg_nacl_stat_cit(tf); target_fabric_setup_tpg_mappedlun_cit(tf); + target_fabric_setup_tpg_mappedlun_stat_cit(tf); return 0; } diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index a3c695a..d57ad67 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -34,6 +34,7 @@ #include <target/target_core_base.h> #include <target/target_core_device.h> #include <target/target_core_transport.h> +#include <target/target_core_fabric_lib.h> #include <target/target_core_fabric_ops.h> #include <target/target_core_configfs.h> diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 190ca8a..02f553a 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -134,7 +134,7 @@ static struct se_device *fd_create_virtdevice( mm_segment_t old_fs; struct file *file; struct inode *inode = NULL; - int dev_flags = 0, flags; + int dev_flags = 0, flags, ret = -EINVAL; memset(&dev_limits, 0, sizeof(struct se_dev_limits)); @@ -146,6 +146,7 @@ static struct se_device *fd_create_virtdevice( if (IS_ERR(dev_p)) { printk(KERN_ERR "getname(%s) failed: %lu\n", fd_dev->fd_dev_name, IS_ERR(dev_p)); + ret = PTR_ERR(dev_p); goto fail; } #if 0 @@ -165,8 +166,12 @@ static struct se_device *fd_create_virtdevice( flags |= O_SYNC; file = filp_open(dev_p, flags, 0600); - - if (IS_ERR(file) || !file || !file->f_dentry) { + if (IS_ERR(file)) { + printk(KERN_ERR "filp_open(%s) failed\n", dev_p); + ret = PTR_ERR(file); + goto fail; + } + if (!file || !file->f_dentry) { printk(KERN_ERR "filp_open(%s) failed\n", dev_p); goto fail; } @@ -241,7 +246,7 @@ fail: fd_dev->fd_file = NULL; } putname(dev_p); - return NULL; + return ERR_PTR(ret); } /* fd_free_device(): (Part of se_subsystem_api_t template) @@ -509,7 +514,7 @@ enum { static match_table_t tokens = { {Opt_fd_dev_name, "fd_dev_name=%s"}, {Opt_fd_dev_size, "fd_dev_size=%s"}, - {Opt_fd_buffered_io, "fd_buffered_id=%d"}, + {Opt_fd_buffered_io, "fd_buffered_io=%d"}, {Opt_err, NULL} }; @@ -536,15 +541,26 @@ static ssize_t fd_set_configfs_dev_params( token = match_token(ptr, tokens, args); switch (token) { case Opt_fd_dev_name: + arg_p = match_strdup(&args[0]); + if (!arg_p) { + ret = -ENOMEM; + break; + } snprintf(fd_dev->fd_dev_name, FD_MAX_DEV_NAME, - "%s", match_strdup(&args[0])); + "%s", arg_p); + kfree(arg_p); printk(KERN_INFO "FILEIO: Referencing Path: %s\n", fd_dev->fd_dev_name); fd_dev->fbd_flags |= FBDF_HAS_PATH; break; case Opt_fd_dev_size: arg_p = match_strdup(&args[0]); + if (!arg_p) { + ret = -ENOMEM; + break; + } ret = strict_strtoull(arg_p, 0, &fd_dev->fd_dev_size); + kfree(arg_p); if (ret < 0) { printk(KERN_ERR "strict_strtoull() failed for" " fd_dev_size=\n"); diff --git a/drivers/target/target_core_hba.c b/drivers/target/target_core_hba.c index 6ec51cb..0b8f8da 100644 --- a/drivers/target/target_core_hba.c +++ b/drivers/target/target_core_hba.c @@ -151,19 +151,8 @@ out_free_hba: int core_delete_hba(struct se_hba *hba) { - struct se_device *dev, *dev_tmp; - - spin_lock(&hba->device_lock); - list_for_each_entry_safe(dev, dev_tmp, &hba->hba_dev_list, dev_list) { - - se_clear_dev_ports(dev); - spin_unlock(&hba->device_lock); - - se_release_device_for_hba(dev); - - spin_lock(&hba->device_lock); - } - spin_unlock(&hba->device_lock); + if (!list_empty(&hba->hba_dev_list)) + dump_stack(); hba->transport->detach_hba(hba); diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index eb0afec..8663900 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -129,10 +129,11 @@ static struct se_device *iblock_create_virtdevice( struct request_queue *q; struct queue_limits *limits; u32 dev_flags = 0; + int ret = -EINVAL; if (!(ib_dev)) { printk(KERN_ERR "Unable to locate struct iblock_dev parameter\n"); - return 0; + return ERR_PTR(ret); } memset(&dev_limits, 0, sizeof(struct se_dev_limits)); /* @@ -141,7 +142,7 @@ static struct se_device *iblock_create_virtdevice( ib_dev->ibd_bio_set = bioset_create(32, 64); if (!(ib_dev->ibd_bio_set)) { printk(KERN_ERR "IBLOCK: Unable to create bioset()\n"); - return 0; + return ERR_PTR(-ENOMEM); } printk(KERN_INFO "IBLOCK: Created bio_set()\n"); /* @@ -153,8 +154,10 @@ static struct se_device *iblock_create_virtdevice( bd = blkdev_get_by_path(ib_dev->ibd_udev_path, FMODE_WRITE|FMODE_READ|FMODE_EXCL, ib_dev); - if (IS_ERR(bd)) + if (IS_ERR(bd)) { + ret = PTR_ERR(bd); goto failed; + } /* * Setup the local scope queue_limits from struct request_queue->limits * to pass into transport_add_device_to_core_hba() as struct se_dev_limits. @@ -184,9 +187,7 @@ static struct se_device *iblock_create_virtdevice( * the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM * in ATA and we need to set TPE=1 */ - if (blk_queue_discard(bdev_get_queue(bd))) { - struct request_queue *q = bdev_get_queue(bd); - + if (blk_queue_discard(q)) { DEV_ATTRIB(dev)->max_unmap_lba_count = q->limits.max_discard_sectors; /* @@ -212,7 +213,7 @@ failed: ib_dev->ibd_bd = NULL; ib_dev->ibd_major = 0; ib_dev->ibd_minor = 0; - return NULL; + return ERR_PTR(ret); } static void iblock_free_device(void *p) @@ -467,7 +468,7 @@ static ssize_t iblock_set_configfs_dev_params(struct se_hba *hba, const char *page, ssize_t count) { struct iblock_dev *ib_dev = se_dev->se_dev_su_ptr; - char *orig, *ptr, *opts; + char *orig, *ptr, *arg_p, *opts; substring_t args[MAX_OPT_ARGS]; int ret = 0, arg, token; @@ -490,9 +491,14 @@ static ssize_t iblock_set_configfs_dev_params(struct se_hba *hba, ret = -EEXIST; goto out; } - - ret = snprintf(ib_dev->ibd_udev_path, SE_UDEV_PATH_LEN, - "%s", match_strdup(&args[0])); + arg_p = match_strdup(&args[0]); + if (!arg_p) { + ret = -ENOMEM; + break; + } + snprintf(ib_dev->ibd_udev_path, SE_UDEV_PATH_LEN, + "%s", arg_p); + kfree(arg_p); printk(KERN_INFO "IBLOCK: Referencing UDEV path: %s\n", ib_dev->ibd_udev_path); ib_dev->ibd_flags |= IBDF_HAS_UDEV_PATH; diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 5a9d2ba..7ff6a35 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -441,6 +441,7 @@ static struct se_device *pscsi_create_type_disk( struct pscsi_dev_virt *pdv, struct se_subsystem_dev *se_dev, struct se_hba *hba) + __releases(sh->host_lock) { struct se_device *dev; struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr; @@ -488,6 +489,7 @@ static struct se_device *pscsi_create_type_rom( struct pscsi_dev_virt *pdv, struct se_subsystem_dev *se_dev, struct se_hba *hba) + __releases(sh->host_lock) { struct se_device *dev; struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr; @@ -522,6 +524,7 @@ static struct se_device *pscsi_create_type_other( struct pscsi_dev_virt *pdv, struct se_subsystem_dev *se_dev, struct se_hba *hba) + __releases(sh->host_lock) { struct se_device *dev; struct pscsi_hba_virt *phv = (struct pscsi_hba_virt *)pdv->pdv_se_hba->hba_ptr; @@ -555,7 +558,7 @@ static struct se_device *pscsi_create_virtdevice( if (!(pdv)) { printk(KERN_ERR "Unable to locate struct pscsi_dev_virt" " parameter\n"); - return NULL; + return ERR_PTR(-EINVAL); } /* * If not running in PHV_LLD_SCSI_HOST_NO mode, locate the @@ -565,7 +568,7 @@ static struct se_device *pscsi_create_virtdevice( if (phv->phv_mode == PHV_LLD_SCSI_HOST_NO) { printk(KERN_ERR "pSCSI: Unable to locate struct" " Scsi_Host for PHV_LLD_SCSI_HOST_NO\n"); - return NULL; + return ERR_PTR(-ENODEV); } /* * For the newer PHV_VIRUTAL_HOST_ID struct scsi_device @@ -574,7 +577,7 @@ static struct se_device *pscsi_create_virtdevice( if (!(se_dev->su_dev_flags & SDF_USING_UDEV_PATH)) { printk(KERN_ERR "pSCSI: udev_path attribute has not" " been set before ENABLE=1\n"); - return NULL; + return ERR_PTR(-EINVAL); } /* * If no scsi_host_id= was passed for PHV_VIRUTAL_HOST_ID, @@ -587,12 +590,12 @@ static struct se_device *pscsi_create_virtdevice( printk(KERN_ERR "pSCSI: Unable to set hba_mode" " with active devices\n"); spin_unlock(&hba->device_lock); - return NULL; + return ERR_PTR(-EEXIST); } spin_unlock(&hba->device_lock); if (pscsi_pmode_enable_hba(hba, 1) != 1) - return NULL; + return ERR_PTR(-ENODEV); legacy_mode_enable = 1; hba->hba_flags |= HBA_FLAGS_PSCSI_MODE; @@ -602,14 +605,14 @@ static struct se_device *pscsi_create_virtdevice( if (!(sh)) { printk(KERN_ERR "pSCSI: Unable to locate" " pdv_host_id: %d\n", pdv->pdv_host_id); - return NULL; + return ERR_PTR(-ENODEV); } } } else { if (phv->phv_mode == PHV_VIRUTAL_HOST_ID) { printk(KERN_ERR "pSCSI: PHV_VIRUTAL_HOST_ID set while" " struct Scsi_Host exists\n"); - return NULL; + return ERR_PTR(-EEXIST); } } @@ -644,7 +647,7 @@ static struct se_device *pscsi_create_virtdevice( hba->hba_flags &= ~HBA_FLAGS_PSCSI_MODE; } pdv->pdv_sd = NULL; - return NULL; + return ERR_PTR(-ENODEV); } return dev; } @@ -660,7 +663,7 @@ static struct se_device *pscsi_create_virtdevice( hba->hba_flags &= ~HBA_FLAGS_PSCSI_MODE; } - return NULL; + return ERR_PTR(-ENODEV); } /* pscsi_free_device(): (Part of se_subsystem_api_t template) @@ -816,6 +819,7 @@ pscsi_alloc_task(struct se_cmd *cmd) if (!(pt->pscsi_cdb)) { printk(KERN_ERR "pSCSI: Unable to allocate extended" " pt->pscsi_cdb\n"); + kfree(pt); return NULL; } } else diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index 8dc6d74..7837dd3 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -150,7 +150,7 @@ static int rd_build_device_space(struct rd_dev *rd_dev) if (rd_dev->rd_page_count <= 0) { printk(KERN_ERR "Illegal page count: %u for Ramdisk device\n", rd_dev->rd_page_count); - return -1; + return -EINVAL; } total_sg_needed = rd_dev->rd_page_count; @@ -160,7 +160,7 @@ static int rd_build_device_space(struct rd_dev *rd_dev) if (!(sg_table)) { printk(KERN_ERR "Unable to allocate memory for Ramdisk" " scatterlist tables\n"); - return -1; + return -ENOMEM; } rd_dev->sg_table_array = sg_table; @@ -175,7 +175,7 @@ static int rd_build_device_space(struct rd_dev *rd_dev) if (!(sg)) { printk(KERN_ERR "Unable to allocate scatterlist array" " for struct rd_dev\n"); - return -1; + return -ENOMEM; } sg_init_table((struct scatterlist *)&sg[0], sg_per_table); @@ -191,7 +191,7 @@ static int rd_build_device_space(struct rd_dev *rd_dev) if (!(pg)) { printk(KERN_ERR "Unable to allocate scatterlist" " pages for struct rd_dev_sg_table\n"); - return -1; + return -ENOMEM; } sg_assign_page(&sg[j], pg); sg[j].length = PAGE_SIZE; @@ -253,12 +253,13 @@ static struct se_device *rd_create_virtdevice( struct se_dev_limits dev_limits; struct rd_dev *rd_dev = p; struct rd_host *rd_host = hba->hba_ptr; - int dev_flags = 0; + int dev_flags = 0, ret; char prod[16], rev[4]; memset(&dev_limits, 0, sizeof(struct se_dev_limits)); - if (rd_build_device_space(rd_dev) < 0) + ret = rd_build_device_space(rd_dev); + if (ret < 0) goto fail; snprintf(prod, 16, "RAMDISK-%s", (rd_dev->rd_direct) ? "DR" : "MCP"); @@ -292,7 +293,7 @@ static struct se_device *rd_create_virtdevice( fail: rd_release_device_space(rd_dev); - return NULL; + return ERR_PTR(ret); } static struct se_device *rd_DIRECT_create_virtdevice( diff --git a/drivers/target/target_core_rd.h b/drivers/target/target_core_rd.h index 13badfb..3ea19e2 100644 --- a/drivers/target/target_core_rd.h +++ b/drivers/target/target_core_rd.h @@ -14,8 +14,6 @@ #define RD_BLOCKSIZE 512 #define RD_MAX_SECTORS 1024 -extern struct kmem_cache *se_mem_cache; - /* Used in target_core_init_configfs() for virtual LUN 0 access */ int __init rd_module_init(void); void rd_module_exit(void); diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c new file mode 100644 index 0000000..5e3a067 --- /dev/null +++ b/drivers/target/target_core_stat.c @@ -0,0 +1,1810 @@ +/******************************************************************************* + * Filename: target_core_stat.c + * + * Copyright (c) 2011 Rising Tide Systems + * Copyright (c) 2011 Linux-iSCSI.org + * + * Modern ConfigFS group context specific statistics based on original + * target_core_mib.c code + * + * Copyright (c) 2006-2007 SBE, Inc. All Rights Reserved. + * + * Nicholas A. Bellinger <nab@linux-iscsi.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ******************************************************************************/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <linux/version.h> +#include <generated/utsrelease.h> +#include <linux/utsname.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/blkdev.h> +#include <linux/configfs.h> +#include <scsi/scsi.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> + +#include <target/target_core_base.h> +#include <target/target_core_transport.h> +#include <target/target_core_fabric_ops.h> +#include <target/target_core_configfs.h> +#include <target/configfs_macros.h> + +#include "target_core_hba.h" + +#ifndef INITIAL_JIFFIES +#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ)) +#endif + +#define NONE "None" +#define ISPRINT(a) ((a >= ' ') && (a <= '~')) + +#define SCSI_LU_INDEX 1 +#define LU_COUNT 1 + +/* + * SCSI Device Table + */ + +CONFIGFS_EATTR_STRUCT(target_stat_scsi_dev, se_dev_stat_grps); +#define DEV_STAT_SCSI_DEV_ATTR(_name, _mode) \ +static struct target_stat_scsi_dev_attribute \ + target_stat_scsi_dev_##_name = \ + __CONFIGFS_EATTR(_name, _mode, \ + target_stat_scsi_dev_show_attr_##_name, \ + target_stat_scsi_dev_store_attr_##_name); + +#define DEV_STAT_SCSI_DEV_ATTR_RO(_name) \ +static struct target_stat_scsi_dev_attribute \ + target_stat_scsi_dev_##_name = \ + __CONFIGFS_EATTR_RO(_name, \ + target_stat_scsi_dev_show_attr_##_name); + +static ssize_t target_stat_scsi_dev_show_attr_inst( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_hba *hba = se_subdev->se_dev_hba; + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index); +} +DEV_STAT_SCSI_DEV_ATTR_RO(inst); + +static ssize_t target_stat_scsi_dev_show_attr_indx( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index); +} +DEV_STAT_SCSI_DEV_ATTR_RO(indx); + +static ssize_t target_stat_scsi_dev_show_attr_role( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "Target\n"); +} +DEV_STAT_SCSI_DEV_ATTR_RO(role); + +static ssize_t target_stat_scsi_dev_show_attr_ports( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_port_count); +} +DEV_STAT_SCSI_DEV_ATTR_RO(ports); + +CONFIGFS_EATTR_OPS(target_stat_scsi_dev, se_dev_stat_grps, scsi_dev_group); + +static struct configfs_attribute *target_stat_scsi_dev_attrs[] = { + &target_stat_scsi_dev_inst.attr, + &target_stat_scsi_dev_indx.attr, + &target_stat_scsi_dev_role.attr, + &target_stat_scsi_dev_ports.attr, + NULL, +}; + +static struct configfs_item_operations target_stat_scsi_dev_attrib_ops = { + .show_attribute = target_stat_scsi_dev_attr_show, + .store_attribute = target_stat_scsi_dev_attr_store, +}; + +static struct config_item_type target_stat_scsi_dev_cit = { + .ct_item_ops = &target_stat_scsi_dev_attrib_ops, + .ct_attrs = target_stat_scsi_dev_attrs, + .ct_owner = THIS_MODULE, +}; + +/* + * SCSI Target Device Table + */ + +CONFIGFS_EATTR_STRUCT(target_stat_scsi_tgt_dev, se_dev_stat_grps); +#define DEV_STAT_SCSI_TGT_DEV_ATTR(_name, _mode) \ +static struct target_stat_scsi_tgt_dev_attribute \ + target_stat_scsi_tgt_dev_##_name = \ + __CONFIGFS_EATTR(_name, _mode, \ + target_stat_scsi_tgt_dev_show_attr_##_name, \ + target_stat_scsi_tgt_dev_store_attr_##_name); + +#define DEV_STAT_SCSI_TGT_DEV_ATTR_RO(_name) \ +static struct target_stat_scsi_tgt_dev_attribute \ + target_stat_scsi_tgt_dev_##_name = \ + __CONFIGFS_EATTR_RO(_name, \ + target_stat_scsi_tgt_dev_show_attr_##_name); + +static ssize_t target_stat_scsi_tgt_dev_show_attr_inst( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_hba *hba = se_subdev->se_dev_hba; + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index); +} +DEV_STAT_SCSI_TGT_DEV_ATTR_RO(inst); + +static ssize_t target_stat_scsi_tgt_dev_show_attr_indx( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index); +} +DEV_STAT_SCSI_TGT_DEV_ATTR_RO(indx); + +static ssize_t target_stat_scsi_tgt_dev_show_attr_num_lus( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "%u\n", LU_COUNT); +} +DEV_STAT_SCSI_TGT_DEV_ATTR_RO(num_lus); + +static ssize_t target_stat_scsi_tgt_dev_show_attr_status( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + char status[16]; + + if (!dev) + return -ENODEV; + + switch (dev->dev_status) { + case TRANSPORT_DEVICE_ACTIVATED: + strcpy(status, "activated"); + break; + case TRANSPORT_DEVICE_DEACTIVATED: + strcpy(status, "deactivated"); + break; + case TRANSPORT_DEVICE_SHUTDOWN: + strcpy(status, "shutdown"); + break; + case TRANSPORT_DEVICE_OFFLINE_ACTIVATED: + case TRANSPORT_DEVICE_OFFLINE_DEACTIVATED: + strcpy(status, "offline"); + break; + default: + sprintf(status, "unknown(%d)", dev->dev_status); + break; + } + + return snprintf(page, PAGE_SIZE, "%s\n", status); +} +DEV_STAT_SCSI_TGT_DEV_ATTR_RO(status); + +static ssize_t target_stat_scsi_tgt_dev_show_attr_non_access_lus( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + int non_accessible_lus; + + if (!dev) + return -ENODEV; + + switch (dev->dev_status) { + case TRANSPORT_DEVICE_ACTIVATED: + non_accessible_lus = 0; + break; + case TRANSPORT_DEVICE_DEACTIVATED: + case TRANSPORT_DEVICE_SHUTDOWN: + case TRANSPORT_DEVICE_OFFLINE_ACTIVATED: + case TRANSPORT_DEVICE_OFFLINE_DEACTIVATED: + default: + non_accessible_lus = 1; + break; + } + + return snprintf(page, PAGE_SIZE, "%u\n", non_accessible_lus); +} +DEV_STAT_SCSI_TGT_DEV_ATTR_RO(non_access_lus); + +static ssize_t target_stat_scsi_tgt_dev_show_attr_resets( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "%u\n", dev->num_resets); +} +DEV_STAT_SCSI_TGT_DEV_ATTR_RO(resets); + + +CONFIGFS_EATTR_OPS(target_stat_scsi_tgt_dev, se_dev_stat_grps, scsi_tgt_dev_group); + +static struct configfs_attribute *target_stat_scsi_tgt_dev_attrs[] = { + &target_stat_scsi_tgt_dev_inst.attr, + &target_stat_scsi_tgt_dev_indx.attr, + &target_stat_scsi_tgt_dev_num_lus.attr, + &target_stat_scsi_tgt_dev_status.attr, + &target_stat_scsi_tgt_dev_non_access_lus.attr, + &target_stat_scsi_tgt_dev_resets.attr, + NULL, +}; + +static struct configfs_item_operations target_stat_scsi_tgt_dev_attrib_ops = { + .show_attribute = target_stat_scsi_tgt_dev_attr_show, + .store_attribute = target_stat_scsi_tgt_dev_attr_store, +}; + +static struct config_item_type target_stat_scsi_tgt_dev_cit = { + .ct_item_ops = &target_stat_scsi_tgt_dev_attrib_ops, + .ct_attrs = target_stat_scsi_tgt_dev_attrs, + .ct_owner = THIS_MODULE, +}; + +/* + * SCSI Logical Unit Table + */ + +CONFIGFS_EATTR_STRUCT(target_stat_scsi_lu, se_dev_stat_grps); +#define DEV_STAT_SCSI_LU_ATTR(_name, _mode) \ +static struct target_stat_scsi_lu_attribute target_stat_scsi_lu_##_name = \ + __CONFIGFS_EATTR(_name, _mode, \ + target_stat_scsi_lu_show_attr_##_name, \ + target_stat_scsi_lu_store_attr_##_name); + +#define DEV_STAT_SCSI_LU_ATTR_RO(_name) \ +static struct target_stat_scsi_lu_attribute target_stat_scsi_lu_##_name = \ + __CONFIGFS_EATTR_RO(_name, \ + target_stat_scsi_lu_show_attr_##_name); + +static ssize_t target_stat_scsi_lu_show_attr_inst( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_hba *hba = se_subdev->se_dev_hba; + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index); +} +DEV_STAT_SCSI_LU_ATTR_RO(inst); + +static ssize_t target_stat_scsi_lu_show_attr_dev( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index); +} +DEV_STAT_SCSI_LU_ATTR_RO(dev); + +static ssize_t target_stat_scsi_lu_show_attr_indx( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + return snprintf(page, PAGE_SIZE, "%u\n", SCSI_LU_INDEX); +} +DEV_STAT_SCSI_LU_ATTR_RO(indx); + +static ssize_t target_stat_scsi_lu_show_attr_lun( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + /* FIXME: scsiLuDefaultLun */ + return snprintf(page, PAGE_SIZE, "%llu\n", (unsigned long long)0); +} +DEV_STAT_SCSI_LU_ATTR_RO(lun); + +static ssize_t target_stat_scsi_lu_show_attr_lu_name( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + /* scsiLuWwnName */ + return snprintf(page, PAGE_SIZE, "%s\n", + (strlen(DEV_T10_WWN(dev)->unit_serial)) ? + (char *)&DEV_T10_WWN(dev)->unit_serial[0] : "None"); +} +DEV_STAT_SCSI_LU_ATTR_RO(lu_name); + +static ssize_t target_stat_scsi_lu_show_attr_vend( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + int j; + char str[28]; + + if (!dev) + return -ENODEV; + /* scsiLuVendorId */ + memcpy(&str[0], (void *)DEV_T10_WWN(dev), 28); + for (j = 0; j < 8; j++) + str[j] = ISPRINT(DEV_T10_WWN(dev)->vendor[j]) ? + DEV_T10_WWN(dev)->vendor[j] : 0x20; + str[8] = 0; + return snprintf(page, PAGE_SIZE, "%s\n", str); +} +DEV_STAT_SCSI_LU_ATTR_RO(vend); + +static ssize_t target_stat_scsi_lu_show_attr_prod( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + int j; + char str[28]; + + if (!dev) + return -ENODEV; + + /* scsiLuProductId */ + memcpy(&str[0], (void *)DEV_T10_WWN(dev), 28); + for (j = 0; j < 16; j++) + str[j] = ISPRINT(DEV_T10_WWN(dev)->model[j]) ? + DEV_T10_WWN(dev)->model[j] : 0x20; + str[16] = 0; + return snprintf(page, PAGE_SIZE, "%s\n", str); +} +DEV_STAT_SCSI_LU_ATTR_RO(prod); + +static ssize_t target_stat_scsi_lu_show_attr_rev( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + int j; + char str[28]; + + if (!dev) + return -ENODEV; + + /* scsiLuRevisionId */ + memcpy(&str[0], (void *)DEV_T10_WWN(dev), 28); + for (j = 0; j < 4; j++) + str[j] = ISPRINT(DEV_T10_WWN(dev)->revision[j]) ? + DEV_T10_WWN(dev)->revision[j] : 0x20; + str[4] = 0; + return snprintf(page, PAGE_SIZE, "%s\n", str); +} +DEV_STAT_SCSI_LU_ATTR_RO(rev); + +static ssize_t target_stat_scsi_lu_show_attr_dev_type( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + /* scsiLuPeripheralType */ + return snprintf(page, PAGE_SIZE, "%u\n", + TRANSPORT(dev)->get_device_type(dev)); +} +DEV_STAT_SCSI_LU_ATTR_RO(dev_type); + +static ssize_t target_stat_scsi_lu_show_attr_status( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + /* scsiLuStatus */ + return snprintf(page, PAGE_SIZE, "%s\n", + (dev->dev_status == TRANSPORT_DEVICE_ACTIVATED) ? + "available" : "notavailable"); +} +DEV_STAT_SCSI_LU_ATTR_RO(status); + +static ssize_t target_stat_scsi_lu_show_attr_state_bit( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + /* scsiLuState */ + return snprintf(page, PAGE_SIZE, "exposed\n"); +} +DEV_STAT_SCSI_LU_ATTR_RO(state_bit); + +static ssize_t target_stat_scsi_lu_show_attr_num_cmds( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + /* scsiLuNumCommands */ + return snprintf(page, PAGE_SIZE, "%llu\n", + (unsigned long long)dev->num_cmds); +} +DEV_STAT_SCSI_LU_ATTR_RO(num_cmds); + +static ssize_t target_stat_scsi_lu_show_attr_read_mbytes( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + /* scsiLuReadMegaBytes */ + return snprintf(page, PAGE_SIZE, "%u\n", (u32)(dev->read_bytes >> 20)); +} +DEV_STAT_SCSI_LU_ATTR_RO(read_mbytes); + +static ssize_t target_stat_scsi_lu_show_attr_write_mbytes( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + /* scsiLuWrittenMegaBytes */ + return snprintf(page, PAGE_SIZE, "%u\n", (u32)(dev->write_bytes >> 20)); +} +DEV_STAT_SCSI_LU_ATTR_RO(write_mbytes); + +static ssize_t target_stat_scsi_lu_show_attr_resets( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + /* scsiLuInResets */ + return snprintf(page, PAGE_SIZE, "%u\n", dev->num_resets); +} +DEV_STAT_SCSI_LU_ATTR_RO(resets); + +static ssize_t target_stat_scsi_lu_show_attr_full_stat( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + /* FIXME: scsiLuOutTaskSetFullStatus */ + return snprintf(page, PAGE_SIZE, "%u\n", 0); +} +DEV_STAT_SCSI_LU_ATTR_RO(full_stat); + +static ssize_t target_stat_scsi_lu_show_attr_hs_num_cmds( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + /* FIXME: scsiLuHSInCommands */ + return snprintf(page, PAGE_SIZE, "%u\n", 0); +} +DEV_STAT_SCSI_LU_ATTR_RO(hs_num_cmds); + +static ssize_t target_stat_scsi_lu_show_attr_creation_time( + struct se_dev_stat_grps *sgrps, char *page) +{ + struct se_subsystem_dev *se_subdev = container_of(sgrps, + struct se_subsystem_dev, dev_stat_grps); + struct se_device *dev = se_subdev->se_dev_ptr; + + if (!dev) + return -ENODEV; + + /* scsiLuCreationTime */ + return snprintf(page, PAGE_SIZE, "%u\n", (u32)(((u32)dev->creation_time - + INITIAL_JIFFIES) * 100 / HZ)); +} +DEV_STAT_SCSI_LU_ATTR_RO(creation_time); + +CONFIGFS_EATTR_OPS(target_stat_scsi_lu, se_dev_stat_grps, scsi_lu_group); + +static struct configfs_attribute *target_stat_scsi_lu_attrs[] = { + &target_stat_scsi_lu_inst.attr, + &target_stat_scsi_lu_dev.attr, + &target_stat_scsi_lu_indx.attr, + &target_stat_scsi_lu_lun.attr, + &target_stat_scsi_lu_lu_name.attr, + &target_stat_scsi_lu_vend.attr, + &target_stat_scsi_lu_prod.attr, + &target_stat_scsi_lu_rev.attr, + &target_stat_scsi_lu_dev_type.attr, + &target_stat_scsi_lu_status.attr, + &target_stat_scsi_lu_state_bit.attr, + &target_stat_scsi_lu_num_cmds.attr, + &target_stat_scsi_lu_read_mbytes.attr, + &target_stat_scsi_lu_write_mbytes.attr, + &target_stat_scsi_lu_resets.attr, + &target_stat_scsi_lu_full_stat.attr, + &target_stat_scsi_lu_hs_num_cmds.attr, + &target_stat_scsi_lu_creation_time.attr, + NULL, +}; + +static struct configfs_item_operations target_stat_scsi_lu_attrib_ops = { + .show_attribute = target_stat_scsi_lu_attr_show, + .store_attribute = target_stat_scsi_lu_attr_store, +}; + +static struct config_item_type target_stat_scsi_lu_cit = { + .ct_item_ops = &target_stat_scsi_lu_attrib_ops, + .ct_attrs = target_stat_scsi_lu_attrs, + .ct_owner = THIS_MODULE, +}; + +/* + * Called from target_core_configfs.c:target_core_make_subdev() to setup + * the target statistics groups + configfs CITs located in target_core_stat.c + */ +void target_stat_setup_dev_default_groups(struct se_subsystem_dev *se_subdev) +{ + struct config_group *dev_stat_grp = &DEV_STAT_GRP(se_subdev)->stat_group; + + config_group_init_type_name(&DEV_STAT_GRP(se_subdev)->scsi_dev_group, + "scsi_dev", &target_stat_scsi_dev_cit); + config_group_init_type_name(&DEV_STAT_GRP(se_subdev)->scsi_tgt_dev_group, + "scsi_tgt_dev", &target_stat_scsi_tgt_dev_cit); + config_group_init_type_name(&DEV_STAT_GRP(se_subdev)->scsi_lu_group, + "scsi_lu", &target_stat_scsi_lu_cit); + + dev_stat_grp->default_groups[0] = &DEV_STAT_GRP(se_subdev)->scsi_dev_group; + dev_stat_grp->default_groups[1] = &DEV_STAT_GRP(se_subdev)->scsi_tgt_dev_group; + dev_stat_grp->default_groups[2] = &DEV_STAT_GRP(se_subdev)->scsi_lu_group; + dev_stat_grp->default_groups[3] = NULL; +} + +/* + * SCSI Port Table + */ + +CONFIGFS_EATTR_STRUCT(target_stat_scsi_port, se_port_stat_grps); +#define DEV_STAT_SCSI_PORT_ATTR(_name, _mode) \ +static struct target_stat_scsi_port_attribute \ + target_stat_scsi_port_##_name = \ + __CONFIGFS_EATTR(_name, _mode, \ + target_stat_scsi_port_show_attr_##_name, \ + target_stat_scsi_port_store_attr_##_name); + +#define DEV_STAT_SCSI_PORT_ATTR_RO(_name) \ +static struct target_stat_scsi_port_attribute \ + target_stat_scsi_port_##_name = \ + __CONFIGFS_EATTR_RO(_name, \ + target_stat_scsi_port_show_attr_##_name); + +static ssize_t target_stat_scsi_port_show_attr_inst( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + struct se_device *dev = lun->lun_se_dev; + struct se_hba *hba; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + hba = dev->se_hba; + ret = snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_PORT_ATTR_RO(inst); + +static ssize_t target_stat_scsi_port_show_attr_dev( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + struct se_device *dev = lun->lun_se_dev; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + ret = snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_PORT_ATTR_RO(dev); + +static ssize_t target_stat_scsi_port_show_attr_indx( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + ret = snprintf(page, PAGE_SIZE, "%u\n", sep->sep_index); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_PORT_ATTR_RO(indx); + +static ssize_t target_stat_scsi_port_show_attr_role( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_device *dev = lun->lun_se_dev; + struct se_port *sep; + ssize_t ret; + + if (!dev) + return -ENODEV; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + ret = snprintf(page, PAGE_SIZE, "%s%u\n", "Device", dev->dev_index); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_PORT_ATTR_RO(role); + +static ssize_t target_stat_scsi_port_show_attr_busy_count( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + /* FIXME: scsiPortBusyStatuses */ + ret = snprintf(page, PAGE_SIZE, "%u\n", 0); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_PORT_ATTR_RO(busy_count); + +CONFIGFS_EATTR_OPS(target_stat_scsi_port, se_port_stat_grps, scsi_port_group); + +static struct configfs_attribute *target_stat_scsi_port_attrs[] = { + &target_stat_scsi_port_inst.attr, + &target_stat_scsi_port_dev.attr, + &target_stat_scsi_port_indx.attr, + &target_stat_scsi_port_role.attr, + &target_stat_scsi_port_busy_count.attr, + NULL, +}; + +static struct configfs_item_operations target_stat_scsi_port_attrib_ops = { + .show_attribute = target_stat_scsi_port_attr_show, + .store_attribute = target_stat_scsi_port_attr_store, +}; + +static struct config_item_type target_stat_scsi_port_cit = { + .ct_item_ops = &target_stat_scsi_port_attrib_ops, + .ct_attrs = target_stat_scsi_port_attrs, + .ct_owner = THIS_MODULE, +}; + +/* + * SCSI Target Port Table + */ +CONFIGFS_EATTR_STRUCT(target_stat_scsi_tgt_port, se_port_stat_grps); +#define DEV_STAT_SCSI_TGT_PORT_ATTR(_name, _mode) \ +static struct target_stat_scsi_tgt_port_attribute \ + target_stat_scsi_tgt_port_##_name = \ + __CONFIGFS_EATTR(_name, _mode, \ + target_stat_scsi_tgt_port_show_attr_##_name, \ + target_stat_scsi_tgt_port_store_attr_##_name); + +#define DEV_STAT_SCSI_TGT_PORT_ATTR_RO(_name) \ +static struct target_stat_scsi_tgt_port_attribute \ + target_stat_scsi_tgt_port_##_name = \ + __CONFIGFS_EATTR_RO(_name, \ + target_stat_scsi_tgt_port_show_attr_##_name); + +static ssize_t target_stat_scsi_tgt_port_show_attr_inst( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_device *dev = lun->lun_se_dev; + struct se_port *sep; + struct se_hba *hba; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + hba = dev->se_hba; + ret = snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TGT_PORT_ATTR_RO(inst); + +static ssize_t target_stat_scsi_tgt_port_show_attr_dev( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_device *dev = lun->lun_se_dev; + struct se_port *sep; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + ret = snprintf(page, PAGE_SIZE, "%u\n", dev->dev_index); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TGT_PORT_ATTR_RO(dev); + +static ssize_t target_stat_scsi_tgt_port_show_attr_indx( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + ret = snprintf(page, PAGE_SIZE, "%u\n", sep->sep_index); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TGT_PORT_ATTR_RO(indx); + +static ssize_t target_stat_scsi_tgt_port_show_attr_name( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + tpg = sep->sep_tpg; + + ret = snprintf(page, PAGE_SIZE, "%sPort#%u\n", + TPG_TFO(tpg)->get_fabric_name(), sep->sep_index); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TGT_PORT_ATTR_RO(name); + +static ssize_t target_stat_scsi_tgt_port_show_attr_port_index( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + tpg = sep->sep_tpg; + + ret = snprintf(page, PAGE_SIZE, "%s%s%d\n", + TPG_TFO(tpg)->tpg_get_wwn(tpg), "+t+", + TPG_TFO(tpg)->tpg_get_tag(tpg)); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TGT_PORT_ATTR_RO(port_index); + +static ssize_t target_stat_scsi_tgt_port_show_attr_in_cmds( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + tpg = sep->sep_tpg; + + ret = snprintf(page, PAGE_SIZE, "%llu\n", sep->sep_stats.cmd_pdus); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TGT_PORT_ATTR_RO(in_cmds); + +static ssize_t target_stat_scsi_tgt_port_show_attr_write_mbytes( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + tpg = sep->sep_tpg; + + ret = snprintf(page, PAGE_SIZE, "%u\n", + (u32)(sep->sep_stats.rx_data_octets >> 20)); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TGT_PORT_ATTR_RO(write_mbytes); + +static ssize_t target_stat_scsi_tgt_port_show_attr_read_mbytes( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + tpg = sep->sep_tpg; + + ret = snprintf(page, PAGE_SIZE, "%u\n", + (u32)(sep->sep_stats.tx_data_octets >> 20)); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TGT_PORT_ATTR_RO(read_mbytes); + +static ssize_t target_stat_scsi_tgt_port_show_attr_hs_in_cmds( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + tpg = sep->sep_tpg; + + /* FIXME: scsiTgtPortHsInCommands */ + ret = snprintf(page, PAGE_SIZE, "%u\n", 0); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TGT_PORT_ATTR_RO(hs_in_cmds); + +CONFIGFS_EATTR_OPS(target_stat_scsi_tgt_port, se_port_stat_grps, + scsi_tgt_port_group); + +static struct configfs_attribute *target_stat_scsi_tgt_port_attrs[] = { + &target_stat_scsi_tgt_port_inst.attr, + &target_stat_scsi_tgt_port_dev.attr, + &target_stat_scsi_tgt_port_indx.attr, + &target_stat_scsi_tgt_port_name.attr, + &target_stat_scsi_tgt_port_port_index.attr, + &target_stat_scsi_tgt_port_in_cmds.attr, + &target_stat_scsi_tgt_port_write_mbytes.attr, + &target_stat_scsi_tgt_port_read_mbytes.attr, + &target_stat_scsi_tgt_port_hs_in_cmds.attr, + NULL, +}; + +static struct configfs_item_operations target_stat_scsi_tgt_port_attrib_ops = { + .show_attribute = target_stat_scsi_tgt_port_attr_show, + .store_attribute = target_stat_scsi_tgt_port_attr_store, +}; + +static struct config_item_type target_stat_scsi_tgt_port_cit = { + .ct_item_ops = &target_stat_scsi_tgt_port_attrib_ops, + .ct_attrs = target_stat_scsi_tgt_port_attrs, + .ct_owner = THIS_MODULE, +}; + +/* + * SCSI Transport Table +o */ + +CONFIGFS_EATTR_STRUCT(target_stat_scsi_transport, se_port_stat_grps); +#define DEV_STAT_SCSI_TRANSPORT_ATTR(_name, _mode) \ +static struct target_stat_scsi_transport_attribute \ + target_stat_scsi_transport_##_name = \ + __CONFIGFS_EATTR(_name, _mode, \ + target_stat_scsi_transport_show_attr_##_name, \ + target_stat_scsi_transport_store_attr_##_name); + +#define DEV_STAT_SCSI_TRANSPORT_ATTR_RO(_name) \ +static struct target_stat_scsi_transport_attribute \ + target_stat_scsi_transport_##_name = \ + __CONFIGFS_EATTR_RO(_name, \ + target_stat_scsi_transport_show_attr_##_name); + +static ssize_t target_stat_scsi_transport_show_attr_inst( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_device *dev = lun->lun_se_dev; + struct se_port *sep; + struct se_hba *hba; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + + hba = dev->se_hba; + ret = snprintf(page, PAGE_SIZE, "%u\n", hba->hba_index); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TRANSPORT_ATTR_RO(inst); + +static ssize_t target_stat_scsi_transport_show_attr_device( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + tpg = sep->sep_tpg; + /* scsiTransportType */ + ret = snprintf(page, PAGE_SIZE, "scsiTransport%s\n", + TPG_TFO(tpg)->get_fabric_name()); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TRANSPORT_ATTR_RO(device); + +static ssize_t target_stat_scsi_transport_show_attr_indx( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_port *sep; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + tpg = sep->sep_tpg; + ret = snprintf(page, PAGE_SIZE, "%u\n", + TPG_TFO(tpg)->tpg_get_inst_index(tpg)); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TRANSPORT_ATTR_RO(indx); + +static ssize_t target_stat_scsi_transport_show_attr_dev_name( + struct se_port_stat_grps *pgrps, char *page) +{ + struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps); + struct se_device *dev = lun->lun_se_dev; + struct se_port *sep; + struct se_portal_group *tpg; + struct t10_wwn *wwn; + ssize_t ret; + + spin_lock(&lun->lun_sep_lock); + sep = lun->lun_sep; + if (!sep) { + spin_unlock(&lun->lun_sep_lock); + return -ENODEV; + } + tpg = sep->sep_tpg; + wwn = DEV_T10_WWN(dev); + /* scsiTransportDevName */ + ret = snprintf(page, PAGE_SIZE, "%s+%s\n", + TPG_TFO(tpg)->tpg_get_wwn(tpg), + (strlen(wwn->unit_serial)) ? wwn->unit_serial : + wwn->vendor); + spin_unlock(&lun->lun_sep_lock); + return ret; +} +DEV_STAT_SCSI_TRANSPORT_ATTR_RO(dev_name); + +CONFIGFS_EATTR_OPS(target_stat_scsi_transport, se_port_stat_grps, + scsi_transport_group); + +static struct configfs_attribute *target_stat_scsi_transport_attrs[] = { + &target_stat_scsi_transport_inst.attr, + &target_stat_scsi_transport_device.attr, + &target_stat_scsi_transport_indx.attr, + &target_stat_scsi_transport_dev_name.attr, + NULL, +}; + +static struct configfs_item_operations target_stat_scsi_transport_attrib_ops = { + .show_attribute = target_stat_scsi_transport_attr_show, + .store_attribute = target_stat_scsi_transport_attr_store, +}; + +static struct config_item_type target_stat_scsi_transport_cit = { + .ct_item_ops = &target_stat_scsi_transport_attrib_ops, + .ct_attrs = target_stat_scsi_transport_attrs, + .ct_owner = THIS_MODULE, +}; + +/* + * Called from target_core_fabric_configfs.c:target_fabric_make_lun() to setup + * the target port statistics groups + configfs CITs located in target_core_stat.c + */ +void target_stat_setup_port_default_groups(struct se_lun *lun) +{ + struct config_group *port_stat_grp = &PORT_STAT_GRP(lun)->stat_group; + + config_group_init_type_name(&PORT_STAT_GRP(lun)->scsi_port_group, + "scsi_port", &target_stat_scsi_port_cit); + config_group_init_type_name(&PORT_STAT_GRP(lun)->scsi_tgt_port_group, + "scsi_tgt_port", &target_stat_scsi_tgt_port_cit); + config_group_init_type_name(&PORT_STAT_GRP(lun)->scsi_transport_group, + "scsi_transport", &target_stat_scsi_transport_cit); + + port_stat_grp->default_groups[0] = &PORT_STAT_GRP(lun)->scsi_port_group; + port_stat_grp->default_groups[1] = &PORT_STAT_GRP(lun)->scsi_tgt_port_group; + port_stat_grp->default_groups[2] = &PORT_STAT_GRP(lun)->scsi_transport_group; + port_stat_grp->default_groups[3] = NULL; +} + +/* + * SCSI Authorized Initiator Table + */ + +CONFIGFS_EATTR_STRUCT(target_stat_scsi_auth_intr, se_ml_stat_grps); +#define DEV_STAT_SCSI_AUTH_INTR_ATTR(_name, _mode) \ +static struct target_stat_scsi_auth_intr_attribute \ + target_stat_scsi_auth_intr_##_name = \ + __CONFIGFS_EATTR(_name, _mode, \ + target_stat_scsi_auth_intr_show_attr_##_name, \ + target_stat_scsi_auth_intr_store_attr_##_name); + +#define DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(_name) \ +static struct target_stat_scsi_auth_intr_attribute \ + target_stat_scsi_auth_intr_##_name = \ + __CONFIGFS_EATTR_RO(_name, \ + target_stat_scsi_auth_intr_show_attr_##_name); + +static ssize_t target_stat_scsi_auth_intr_show_attr_inst( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + tpg = nacl->se_tpg; + /* scsiInstIndex */ + ret = snprintf(page, PAGE_SIZE, "%u\n", + TPG_TFO(tpg)->tpg_get_inst_index(tpg)); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(inst); + +static ssize_t target_stat_scsi_auth_intr_show_attr_dev( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + struct se_lun *lun; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + tpg = nacl->se_tpg; + lun = deve->se_lun; + /* scsiDeviceIndex */ + ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_se_dev->dev_index); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(dev); + +static ssize_t target_stat_scsi_auth_intr_show_attr_port( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + tpg = nacl->se_tpg; + /* scsiAuthIntrTgtPortIndex */ + ret = snprintf(page, PAGE_SIZE, "%u\n", TPG_TFO(tpg)->tpg_get_tag(tpg)); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(port); + +static ssize_t target_stat_scsi_auth_intr_show_attr_indx( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* scsiAuthIntrIndex */ + ret = snprintf(page, PAGE_SIZE, "%u\n", nacl->acl_index); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(indx); + +static ssize_t target_stat_scsi_auth_intr_show_attr_dev_or_port( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* scsiAuthIntrDevOrPort */ + ret = snprintf(page, PAGE_SIZE, "%u\n", 1); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(dev_or_port); + +static ssize_t target_stat_scsi_auth_intr_show_attr_intr_name( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* scsiAuthIntrName */ + ret = snprintf(page, PAGE_SIZE, "%s\n", nacl->initiatorname); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(intr_name); + +static ssize_t target_stat_scsi_auth_intr_show_attr_map_indx( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* FIXME: scsiAuthIntrLunMapIndex */ + ret = snprintf(page, PAGE_SIZE, "%u\n", 0); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(map_indx); + +static ssize_t target_stat_scsi_auth_intr_show_attr_att_count( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* scsiAuthIntrAttachedTimes */ + ret = snprintf(page, PAGE_SIZE, "%u\n", deve->attach_count); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(att_count); + +static ssize_t target_stat_scsi_auth_intr_show_attr_num_cmds( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* scsiAuthIntrOutCommands */ + ret = snprintf(page, PAGE_SIZE, "%u\n", deve->total_cmds); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(num_cmds); + +static ssize_t target_stat_scsi_auth_intr_show_attr_read_mbytes( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* scsiAuthIntrReadMegaBytes */ + ret = snprintf(page, PAGE_SIZE, "%u\n", (u32)(deve->read_bytes >> 20)); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(read_mbytes); + +static ssize_t target_stat_scsi_auth_intr_show_attr_write_mbytes( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* scsiAuthIntrWrittenMegaBytes */ + ret = snprintf(page, PAGE_SIZE, "%u\n", (u32)(deve->write_bytes >> 20)); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(write_mbytes); + +static ssize_t target_stat_scsi_auth_intr_show_attr_hs_num_cmds( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* FIXME: scsiAuthIntrHSOutCommands */ + ret = snprintf(page, PAGE_SIZE, "%u\n", 0); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(hs_num_cmds); + +static ssize_t target_stat_scsi_auth_intr_show_attr_creation_time( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* scsiAuthIntrLastCreation */ + ret = snprintf(page, PAGE_SIZE, "%u\n", (u32)(((u32)deve->creation_time - + INITIAL_JIFFIES) * 100 / HZ)); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(creation_time); + +static ssize_t target_stat_scsi_auth_intr_show_attr_row_status( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* FIXME: scsiAuthIntrRowStatus */ + ret = snprintf(page, PAGE_SIZE, "Ready\n"); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_AUTH_INTR_ATTR_RO(row_status); + +CONFIGFS_EATTR_OPS(target_stat_scsi_auth_intr, se_ml_stat_grps, + scsi_auth_intr_group); + +static struct configfs_attribute *target_stat_scsi_auth_intr_attrs[] = { + &target_stat_scsi_auth_intr_inst.attr, + &target_stat_scsi_auth_intr_dev.attr, + &target_stat_scsi_auth_intr_port.attr, + &target_stat_scsi_auth_intr_indx.attr, + &target_stat_scsi_auth_intr_dev_or_port.attr, + &target_stat_scsi_auth_intr_intr_name.attr, + &target_stat_scsi_auth_intr_map_indx.attr, + &target_stat_scsi_auth_intr_att_count.attr, + &target_stat_scsi_auth_intr_num_cmds.attr, + &target_stat_scsi_auth_intr_read_mbytes.attr, + &target_stat_scsi_auth_intr_write_mbytes.attr, + &target_stat_scsi_auth_intr_hs_num_cmds.attr, + &target_stat_scsi_auth_intr_creation_time.attr, + &target_stat_scsi_auth_intr_row_status.attr, + NULL, +}; + +static struct configfs_item_operations target_stat_scsi_auth_intr_attrib_ops = { + .show_attribute = target_stat_scsi_auth_intr_attr_show, + .store_attribute = target_stat_scsi_auth_intr_attr_store, +}; + +static struct config_item_type target_stat_scsi_auth_intr_cit = { + .ct_item_ops = &target_stat_scsi_auth_intr_attrib_ops, + .ct_attrs = target_stat_scsi_auth_intr_attrs, + .ct_owner = THIS_MODULE, +}; + +/* + * SCSI Attached Initiator Port Table + */ + +CONFIGFS_EATTR_STRUCT(target_stat_scsi_att_intr_port, se_ml_stat_grps); +#define DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR(_name, _mode) \ +static struct target_stat_scsi_att_intr_port_attribute \ + target_stat_scsi_att_intr_port_##_name = \ + __CONFIGFS_EATTR(_name, _mode, \ + target_stat_scsi_att_intr_port_show_attr_##_name, \ + target_stat_scsi_att_intr_port_store_attr_##_name); + +#define DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(_name) \ +static struct target_stat_scsi_att_intr_port_attribute \ + target_stat_scsi_att_intr_port_##_name = \ + __CONFIGFS_EATTR_RO(_name, \ + target_stat_scsi_att_intr_port_show_attr_##_name); + +static ssize_t target_stat_scsi_att_intr_port_show_attr_inst( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + tpg = nacl->se_tpg; + /* scsiInstIndex */ + ret = snprintf(page, PAGE_SIZE, "%u\n", + TPG_TFO(tpg)->tpg_get_inst_index(tpg)); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(inst); + +static ssize_t target_stat_scsi_att_intr_port_show_attr_dev( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + struct se_lun *lun; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + tpg = nacl->se_tpg; + lun = deve->se_lun; + /* scsiDeviceIndex */ + ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_se_dev->dev_index); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(dev); + +static ssize_t target_stat_scsi_att_intr_port_show_attr_port( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + tpg = nacl->se_tpg; + /* scsiPortIndex */ + ret = snprintf(page, PAGE_SIZE, "%u\n", TPG_TFO(tpg)->tpg_get_tag(tpg)); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(port); + +static ssize_t target_stat_scsi_att_intr_port_show_attr_indx( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_session *se_sess; + struct se_portal_group *tpg; + ssize_t ret; + + spin_lock_irq(&nacl->nacl_sess_lock); + se_sess = nacl->nacl_sess; + if (!se_sess) { + spin_unlock_irq(&nacl->nacl_sess_lock); + return -ENODEV; + } + + tpg = nacl->se_tpg; + /* scsiAttIntrPortIndex */ + ret = snprintf(page, PAGE_SIZE, "%u\n", + TPG_TFO(tpg)->sess_get_index(se_sess)); + spin_unlock_irq(&nacl->nacl_sess_lock); + return ret; +} +DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(indx); + +static ssize_t target_stat_scsi_att_intr_port_show_attr_port_auth_indx( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_dev_entry *deve; + ssize_t ret; + + spin_lock_irq(&nacl->device_list_lock); + deve = &nacl->device_list[lacl->mapped_lun]; + if (!deve->se_lun || !deve->se_lun_acl) { + spin_unlock_irq(&nacl->device_list_lock); + return -ENODEV; + } + /* scsiAttIntrPortAuthIntrIdx */ + ret = snprintf(page, PAGE_SIZE, "%u\n", nacl->acl_index); + spin_unlock_irq(&nacl->device_list_lock); + return ret; +} +DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(port_auth_indx); + +static ssize_t target_stat_scsi_att_intr_port_show_attr_port_ident( + struct se_ml_stat_grps *lgrps, char *page) +{ + struct se_lun_acl *lacl = container_of(lgrps, + struct se_lun_acl, ml_stat_grps); + struct se_node_acl *nacl = lacl->se_lun_nacl; + struct se_session *se_sess; + struct se_portal_group *tpg; + ssize_t ret; + unsigned char buf[64]; + + spin_lock_irq(&nacl->nacl_sess_lock); + se_sess = nacl->nacl_sess; + if (!se_sess) { + spin_unlock_irq(&nacl->nacl_sess_lock); + return -ENODEV; + } + + tpg = nacl->se_tpg; + /* scsiAttIntrPortName+scsiAttIntrPortIdentifier */ + memset(buf, 0, 64); + if (TPG_TFO(tpg)->sess_get_initiator_sid != NULL) + TPG_TFO(tpg)->sess_get_initiator_sid(se_sess, + (unsigned char *)&buf[0], 64); + + ret = snprintf(page, PAGE_SIZE, "%s+i+%s\n", nacl->initiatorname, buf); + spin_unlock_irq(&nacl->nacl_sess_lock); + return ret; +} +DEV_STAT_SCSI_ATTR_INTR_PORT_ATTR_RO(port_ident); + +CONFIGFS_EATTR_OPS(target_stat_scsi_att_intr_port, se_ml_stat_grps, + scsi_att_intr_port_group); + +static struct configfs_attribute *target_stat_scsi_ath_intr_port_attrs[] = { + &target_stat_scsi_att_intr_port_inst.attr, + &target_stat_scsi_att_intr_port_dev.attr, + &target_stat_scsi_att_intr_port_port.attr, + &target_stat_scsi_att_intr_port_indx.attr, + &target_stat_scsi_att_intr_port_port_auth_indx.attr, + &target_stat_scsi_att_intr_port_port_ident.attr, + NULL, +}; + +static struct configfs_item_operations target_stat_scsi_att_intr_port_attrib_ops = { + .show_attribute = target_stat_scsi_att_intr_port_attr_show, + .store_attribute = target_stat_scsi_att_intr_port_attr_store, +}; + +static struct config_item_type target_stat_scsi_att_intr_port_cit = { + .ct_item_ops = &target_stat_scsi_att_intr_port_attrib_ops, + .ct_attrs = target_stat_scsi_ath_intr_port_attrs, + .ct_owner = THIS_MODULE, +}; + +/* + * Called from target_core_fabric_configfs.c:target_fabric_make_mappedlun() to setup + * the target MappedLUN statistics groups + configfs CITs located in target_core_stat.c + */ +void target_stat_setup_mappedlun_default_groups(struct se_lun_acl *lacl) +{ + struct config_group *ml_stat_grp = &ML_STAT_GRPS(lacl)->stat_group; + + config_group_init_type_name(&ML_STAT_GRPS(lacl)->scsi_auth_intr_group, + "scsi_auth_intr", &target_stat_scsi_auth_intr_cit); + config_group_init_type_name(&ML_STAT_GRPS(lacl)->scsi_att_intr_port_group, + "scsi_att_intr_port", &target_stat_scsi_att_intr_port_cit); + + ml_stat_grp->default_groups[0] = &ML_STAT_GRPS(lacl)->scsi_auth_intr_group; + ml_stat_grp->default_groups[1] = &ML_STAT_GRPS(lacl)->scsi_att_intr_port_group; + ml_stat_grp->default_groups[2] = NULL; +} diff --git a/drivers/target/target_core_stat.h b/drivers/target/target_core_stat.h new file mode 100644 index 0000000..86c252f --- /dev/null +++ b/drivers/target/target_core_stat.h @@ -0,0 +1,8 @@ +#ifndef TARGET_CORE_STAT_H +#define TARGET_CORE_STAT_H + +extern void target_stat_setup_dev_default_groups(struct se_subsystem_dev *); +extern void target_stat_setup_port_default_groups(struct se_lun *); +extern void target_stat_setup_mappedlun_default_groups(struct se_lun_acl *); + +#endif /*** TARGET_CORE_STAT_H ***/ diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index ff9ace0..bf6aa8a 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -227,8 +227,6 @@ static void transport_remove_cmd_from_queue(struct se_cmd *cmd, static int transport_set_sense_codes(struct se_cmd *cmd, u8 asc, u8 ascq); static void transport_stop_all_task_timers(struct se_cmd *cmd); -int transport_emulate_control_cdb(struct se_task *task); - int init_se_global(void) { struct se_global *global; @@ -1622,7 +1620,7 @@ struct se_device *transport_add_device_to_core_hba( const char *inquiry_prod, const char *inquiry_rev) { - int ret = 0, force_pt; + int force_pt; struct se_device *dev; dev = kzalloc(sizeof(struct se_device), GFP_KERNEL); @@ -1739,9 +1737,8 @@ struct se_device *transport_add_device_to_core_hba( } scsi_dump_inquiry(dev); + return dev; out: - if (!ret) - return dev; kthread_stop(dev->process_thread); spin_lock(&hba->device_lock); @@ -4359,11 +4356,9 @@ transport_generic_get_mem(struct se_cmd *cmd, u32 length, u32 dma_size) printk(KERN_ERR "Unable to allocate struct se_mem\n"); goto out; } - INIT_LIST_HEAD(&se_mem->se_list); - se_mem->se_len = (length > dma_size) ? dma_size : length; /* #warning FIXME Allocate contigous pages for struct se_mem elements */ - se_mem->se_page = (struct page *) alloc_pages(GFP_KERNEL, 0); + se_mem->se_page = alloc_pages(GFP_KERNEL, 0); if (!(se_mem->se_page)) { printk(KERN_ERR "alloc_pages() failed\n"); goto out; @@ -4374,6 +4369,8 @@ transport_generic_get_mem(struct se_cmd *cmd, u32 length, u32 dma_size) printk(KERN_ERR "kmap_atomic() failed\n"); goto out; } + INIT_LIST_HEAD(&se_mem->se_list); + se_mem->se_len = (length > dma_size) ? dma_size : length; memset(buf, 0, se_mem->se_len); kunmap_atomic(buf, KM_IRQ0); @@ -4392,10 +4389,13 @@ transport_generic_get_mem(struct se_cmd *cmd, u32 length, u32 dma_size) return 0; out: + if (se_mem) + __free_pages(se_mem->se_page, 0); + kmem_cache_free(se_mem_cache, se_mem); return -1; } -extern u32 transport_calc_sg_num( +u32 transport_calc_sg_num( struct se_task *task, struct se_mem *in_se_mem, u32 task_offset) @@ -5834,31 +5834,26 @@ int transport_generic_do_tmr(struct se_cmd *cmd) int ret; switch (tmr->function) { - case ABORT_TASK: + case TMR_ABORT_TASK: ref_cmd = tmr->ref_cmd; tmr->response = TMR_FUNCTION_REJECTED; break; - case ABORT_TASK_SET: - case CLEAR_ACA: - case CLEAR_TASK_SET: + case TMR_ABORT_TASK_SET: + case TMR_CLEAR_ACA: + case TMR_CLEAR_TASK_SET: tmr->response = TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED; break; - case LUN_RESET: + case TMR_LUN_RESET: ret = core_tmr_lun_reset(dev, tmr, NULL, NULL); tmr->response = (!ret) ? TMR_FUNCTION_COMPLETE : TMR_FUNCTION_REJECTED; break; -#if 0 - case TARGET_WARM_RESET: - transport_generic_host_reset(dev->se_hba); + case TMR_TARGET_WARM_RESET: tmr->response = TMR_FUNCTION_REJECTED; break; - case TARGET_COLD_RESET: - transport_generic_host_reset(dev->se_hba); - transport_generic_cold_reset(dev->se_hba); + case TMR_TARGET_COLD_RESET: tmr->response = TMR_FUNCTION_REJECTED; break; -#endif default: printk(KERN_ERR "Uknown TMR function: 0x%02x.\n", tmr->function); diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index 25a8bc5..87e7e6c 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -131,7 +131,7 @@ static void kgdboc_unregister_kbd(void) static int kgdboc_option_setup(char *opt) { - if (strlen(opt) > MAX_CONFIG_LEN) { + if (strlen(opt) >= MAX_CONFIG_LEN) { printk(KERN_ERR "kgdboc: config string too long\n"); return -ENOSPC; } |