Merge branch 'writeback-plugging'
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 12 Sep 2015 18:19:01 +0000 (11:19 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 12 Sep 2015 18:19:01 +0000 (11:19 -0700)
Fix up the writeback plugging introduced in commit d353d7587d02
("writeback: plug writeback at a high level") that then caused problems
due to the unplug happening with a spinlock held.

* writeback-plugging:
  writeback: plug writeback in wb_writeback() and writeback_inodes_wb()
  Revert "writeback: plug writeback at a high level"

249 files changed:
Documentation/devicetree/bindings/input/touchscreen/colibri-vf50-ts.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/atmel-sama5d4-wdt.txt [new file with mode: 0644]
Documentation/devicetree/bindings/watchdog/lpc18xx-wdt.txt [new file with mode: 0644]
Documentation/thermal/sysfs-api.txt
Documentation/watchdog/src/watchdog-test.c
MAINTAINERS
arch/arc/plat-axs10x/axs10x.c
arch/x86/entry/syscalls/syscall_32.tbl
arch/x86/entry/syscalls/syscall_64.tbl
drivers/acpi/thermal.c
drivers/base/power/domain.c
drivers/base/power/opp.c
drivers/cpufreq/Kconfig.arm
drivers/cpufreq/cpufreq-dt.c
drivers/cpufreq/cpufreq.c
drivers/cpufreq/intel_pstate.c
drivers/cpuidle/coupled.c
drivers/cpuidle/cpuidle.h
drivers/cpuidle/driver.c
drivers/edac/sb_edac.c
drivers/gpu/drm/exynos/Kconfig
drivers/gpu/drm/exynos/exynos_drm_g2d.c
drivers/gpu/drm/exynos/exynos_drm_gem.c
drivers/hwmon/lm75.c
drivers/hwmon/ntc_thermistor.c
drivers/hwmon/tmp102.c
drivers/infiniband/ulp/isert/ib_isert.c
drivers/input/evdev.c
drivers/input/keyboard/imx_keypad.c
drivers/input/misc/ab8500-ponkey.c
drivers/input/misc/pwm-beeper.c
drivers/input/misc/regulator-haptic.c
drivers/input/misc/sparcspkr.c
drivers/input/mouse/elan_i2c_core.c
drivers/input/serio/i8042.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/colibri-vf50-ts.c [new file with mode: 0644]
drivers/input/touchscreen/cyttsp4_i2c.c
drivers/input/touchscreen/cyttsp_i2c.c
drivers/input/touchscreen/elants_i2c.c
drivers/input/touchscreen/imx6ul_tsc.c [new file with mode: 0644]
drivers/input/touchscreen/sun4i-ts.c
drivers/iommu/omap-iommu-debug.c
drivers/md/Kconfig
drivers/md/dm-mpath.c
drivers/media/platform/omap/Kconfig
drivers/media/platform/omap/omap_vout.c
drivers/media/v4l2-core/Kconfig
drivers/media/v4l2-core/videobuf2-core.c
drivers/media/v4l2-core/videobuf2-dma-contig.c
drivers/media/v4l2-core/videobuf2-dma-sg.c
drivers/media/v4l2-core/videobuf2-memops.c
drivers/media/v4l2-core/videobuf2-vmalloc.c
drivers/misc/mei/wd.c
drivers/net/ntb_netdev.c
drivers/ntb/hw/intel/ntb_hw_intel.c
drivers/ntb/hw/intel/ntb_hw_intel.h
drivers/ntb/ntb_transport.c
drivers/platform/x86/acerhdf.c
drivers/platform/x86/intel_mid_thermal.c
drivers/power/charger-manager.c
drivers/power/power_supply_core.c
drivers/scsi/Makefile
drivers/scsi/aic94xx/aic94xx_sds.c
drivers/scsi/bfa/bfa_ioc.c
drivers/scsi/device_handler/Kconfig
drivers/scsi/device_handler/Makefile
drivers/scsi/device_handler/scsi_dh.c [deleted file]
drivers/scsi/device_handler/scsi_dh_alua.c
drivers/scsi/device_handler/scsi_dh_emc.c
drivers/scsi/device_handler/scsi_dh_hp_sw.c
drivers/scsi/device_handler/scsi_dh_rdac.c
drivers/scsi/fcoe/fcoe.c
drivers/scsi/ipr.c
drivers/scsi/libiscsi.c
drivers/scsi/lpfc/lpfc_mbox.c
drivers/scsi/mpt2sas/mpt2sas_base.c
drivers/scsi/mpt2sas/mpt2sas_base.h
drivers/scsi/mpt2sas/mpt2sas_ctl.c
drivers/scsi/mpt2sas/mpt2sas_scsih.c
drivers/scsi/mpt2sas/mpt2sas_transport.c
drivers/scsi/mpt3sas/mpi/mpi2.h
drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h
drivers/scsi/mpt3sas/mpi/mpi2_ioc.h
drivers/scsi/mpt3sas/mpi/mpi2_tool.h
drivers/scsi/mpt3sas/mpt3sas_base.c
drivers/scsi/mpt3sas/mpt3sas_base.h
drivers/scsi/mpt3sas/mpt3sas_scsih.c
drivers/scsi/mpt3sas/mpt3sas_transport.c
drivers/scsi/pm8001/pm8001_hwi.c
drivers/scsi/pm8001/pm80xx_hwi.c
drivers/scsi/qla2xxx/Kconfig
drivers/scsi/qla2xxx/tcm_qla2xxx.c
drivers/scsi/scsi_common.c
drivers/scsi/scsi_debug.c
drivers/scsi/scsi_dh.c [new file with mode: 0644]
drivers/scsi/scsi_error.c
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_priv.h
drivers/scsi/scsi_sysfs.c
drivers/scsi/scsi_transport_sas.c
drivers/staging/board/armadillo800eva.c
drivers/staging/board/board.c
drivers/target/iscsi/iscsi_target.c
drivers/target/iscsi/iscsi_target.h
drivers/target/iscsi/iscsi_target_configfs.c
drivers/target/iscsi/iscsi_target_device.c
drivers/target/iscsi/iscsi_target_login.c
drivers/target/iscsi/iscsi_target_login.h
drivers/target/iscsi/iscsi_target_nego.c
drivers/target/iscsi/iscsi_target_stat.c
drivers/target/iscsi/iscsi_target_tmr.c
drivers/target/iscsi/iscsi_target_tpg.c
drivers/target/iscsi/iscsi_target_tpg.h
drivers/target/iscsi/iscsi_target_util.c
drivers/target/loopback/tcm_loop.c
drivers/target/target_core_device.c
drivers/target/target_core_fabric_configfs.c
drivers/target/target_core_hba.c
drivers/target/target_core_sbc.c
drivers/target/target_core_spc.c
drivers/target/target_core_tpg.c
drivers/target/target_core_transport.c
drivers/target/target_core_user.c
drivers/target/target_core_xcopy.c
drivers/target/tcm_fc/tfc_cmd.c
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/armada_thermal.c
drivers/thermal/db8500_thermal.c
drivers/thermal/dove_thermal.c
drivers/thermal/fair_share.c
drivers/thermal/gov_bang_bang.c
drivers/thermal/hisi_thermal.c
drivers/thermal/imx_thermal.c
drivers/thermal/int340x_thermal/int3400_thermal.c
drivers/thermal/int340x_thermal/int340x_thermal_zone.c
drivers/thermal/int340x_thermal/int340x_thermal_zone.h
drivers/thermal/int340x_thermal/processor_thermal_device.c
drivers/thermal/intel_pch_thermal.c [new file with mode: 0644]
drivers/thermal/intel_powerclamp.c
drivers/thermal/intel_quark_dts_thermal.c
drivers/thermal/intel_soc_dts_iosf.c
drivers/thermal/kirkwood_thermal.c
drivers/thermal/of-thermal.c
drivers/thermal/power_allocator.c
drivers/thermal/qcom-spmi-temp-alarm.c
drivers/thermal/rcar_thermal.c
drivers/thermal/rockchip_thermal.c
drivers/thermal/samsung/exynos_tmu.c
drivers/thermal/spear_thermal.c
drivers/thermal/st/st_thermal.c
drivers/thermal/step_wise.c
drivers/thermal/tegra_soctherm.c
drivers/thermal/thermal_core.c
drivers/thermal/thermal_hwmon.c
drivers/thermal/ti-soc-thermal/ti-thermal-common.c
drivers/thermal/x86_pkg_temp_thermal.c
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/at91rm9200_wdt.c
drivers/watchdog/at91sam9_wdt.c
drivers/watchdog/at91sam9_wdt.h
drivers/watchdog/bcm2835_wdt.c
drivers/watchdog/bcm47xx_wdt.c
drivers/watchdog/bcm_kona_wdt.c
drivers/watchdog/booke_wdt.c
drivers/watchdog/coh901327_wdt.c
drivers/watchdog/da9052_wdt.c
drivers/watchdog/da9055_wdt.c
drivers/watchdog/da9062_wdt.c
drivers/watchdog/da9063_wdt.c
drivers/watchdog/davinci_wdt.c
drivers/watchdog/digicolor_wdt.c
drivers/watchdog/ep93xx_wdt.c
drivers/watchdog/gpio_wdt.c
drivers/watchdog/ie6xx_wdt.c
drivers/watchdog/imgpdc_wdt.c
drivers/watchdog/intel-mid_wdt.c
drivers/watchdog/jz4740_wdt.c
drivers/watchdog/lpc18xx_wdt.c [new file with mode: 0644]
drivers/watchdog/mena21_wdt.c
drivers/watchdog/menf21bmc_wdt.c
drivers/watchdog/mpc8xxx_wdt.c
drivers/watchdog/mtk_wdt.c
drivers/watchdog/nv_tco.c
drivers/watchdog/omap_wdt.c
drivers/watchdog/orion_wdt.c
drivers/watchdog/pnx4008_wdt.c
drivers/watchdog/qcom-wdt.c
drivers/watchdog/retu_wdt.c
drivers/watchdog/rt2880_wdt.c
drivers/watchdog/s3c2410_wdt.c
drivers/watchdog/sama5d4_wdt.c [new file with mode: 0644]
drivers/watchdog/shwdt.c
drivers/watchdog/sirfsoc_wdt.c
drivers/watchdog/sp805_wdt.c
drivers/watchdog/st_lpc_wdt.c
drivers/watchdog/stmp3xxx_rtc_wdt.c
drivers/watchdog/sunxi_wdt.c
drivers/watchdog/tegra_wdt.c
drivers/watchdog/twl4030_wdt.c
drivers/watchdog/txx9wdt.c
drivers/watchdog/ux500_wdt.c
drivers/watchdog/via_wdt.c
drivers/watchdog/wm831x_wdt.c
drivers/watchdog/wm8350_wdt.c
fs/nsfs.c
fs/ocfs2/dlm/dlmrecovery.c
fs/seq_file.c
include/linux/mm.h
include/linux/ntb.h
include/linux/ntb_transport.h
include/linux/pm_opp.h
include/linux/seq_file.h
include/linux/syscalls.h
include/linux/thermal.h
include/media/videobuf2-memops.h
include/scsi/scsi_common.h
include/scsi/scsi_device.h
include/scsi/scsi_dh.h
include/scsi/scsi_eh.h
include/target/iscsi/iscsi_target_core.h
include/target/iscsi/iscsi_target_stat.h
include/target/iscsi/iscsi_transport.h
include/target/target_core_backend.h
include/target/target_core_base.h
include/target/target_core_fabric.h
include/trace/events/thermal_power_allocator.h
include/uapi/asm-generic/unistd.h
include/uapi/linux/Kbuild
include/uapi/linux/membarrier.h [new file with mode: 0644]
include/uapi/linux/target_core_user.h
init/Kconfig
kernel/Makefile
kernel/cpu_pm.c
kernel/membarrier.c [new file with mode: 0644]
kernel/sys_ni.c
mm/Kconfig
mm/Makefile
mm/early_ioremap.c
mm/frame_vector.c [new file with mode: 0644]
scripts/extract-cert.c
tools/testing/selftests/Makefile
tools/testing/selftests/membarrier/.gitignore [new file with mode: 0644]
tools/testing/selftests/membarrier/Makefile [new file with mode: 0644]
tools/testing/selftests/membarrier/membarrier_test.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/input/touchscreen/colibri-vf50-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/colibri-vf50-ts.txt
new file mode 100644 (file)
index 0000000..9d9e930
--- /dev/null
@@ -0,0 +1,36 @@
+* Toradex Colibri VF50 Touchscreen driver
+
+Required Properties:
+- compatible must be toradex,vf50-touchscreen
+- io-channels: adc channels being used by the Colibri VF50 module
+- xp-gpios: FET gate driver for input of X+
+- xm-gpios: FET gate driver for input of X-
+- yp-gpios: FET gate driver for input of Y+
+- ym-gpios: FET gate driver for input of Y-
+- interrupt-parent: phandle for the interrupt controller
+- interrupts: pen irq interrupt for touch detection
+- pinctrl-names: "idle", "default", "gpios"
+- pinctrl-0: pinctrl node for pen/touch detection state pinmux
+- pinctrl-1: pinctrl node for X/Y and pressure measurement (ADC) state pinmux
+- pinctrl-2: pinctrl node for gpios functioning as FET gate drivers
+- vf50-ts-min-pressure: pressure level at which to stop measuring X/Y values
+
+Example:
+
+       touchctrl: vf50_touchctrl {
+               compatible = "toradex,vf50-touchscreen";
+               io-channels = <&adc1 0>,<&adc0 0>,
+                               <&adc0 1>,<&adc1 2>;
+               xp-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
+               xm-gpios = <&gpio2 29 GPIO_ACTIVE_HIGH>;
+               yp-gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
+               ym-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
+               interrupt-parent = <&gpio0>;
+               interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
+               pinctrl-names = "idle","default","gpios";
+               pinctrl-0 = <&pinctrl_touchctrl_idle>;
+               pinctrl-1 = <&pinctrl_touchctrl_default>;
+               pinctrl-2 = <&pinctrl_touchctrl_gpios>;
+               vf50-ts-min-pressure = <200>;
+               status = "disabled";
+       };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt b/Documentation/devicetree/bindings/input/touchscreen/imx6ul_tsc.txt
new file mode 100644 (file)
index 0000000..853dff9
--- /dev/null
@@ -0,0 +1,36 @@
+* Freescale i.MX6UL Touch Controller
+
+Required properties:
+- compatible: must be "fsl,imx6ul-tsc".
+- reg: this touch controller address and the ADC2 address.
+- interrupts: the interrupt of this touch controller and ADC2.
+- clocks: the root clock of touch controller and ADC2.
+- clock-names; must be "tsc" and "adc".
+- xnur-gpio: the X- gpio this controller connect to.
+  This xnur-gpio returns to low once the finger leave the touch screen (The
+  last touch event the touch controller capture).
+
+Optional properties:
+- measure-delay-time: the value of measure delay time.
+  Before X-axis or Y-axis measurement, the screen need some time before
+  even potential distribution ready.
+  This value depends on the touch screen.
+- pre-charge-time: the touch screen need some time to precharge.
+  This value depends on the touch screen.
+
+Example:
+       tsc: tsc@02040000 {
+               compatible = "fsl,imx6ul-tsc";
+               reg = <0x02040000 0x4000>, <0x0219c000 0x4000>;
+               interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
+                            <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&clks IMX6UL_CLK_IPG>,
+                        <&clks IMX6UL_CLK_ADC2>;
+               clock-names = "tsc", "adc";
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_tsc>;
+               xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
+               measure-delay-time = <0xfff>;
+               pre-charge-time = <0xffff>;
+               status = "okay";
+       };
diff --git a/Documentation/devicetree/bindings/watchdog/atmel-sama5d4-wdt.txt b/Documentation/devicetree/bindings/watchdog/atmel-sama5d4-wdt.txt
new file mode 100644 (file)
index 0000000..f7cc7c0
--- /dev/null
@@ -0,0 +1,35 @@
+* Atmel SAMA5D4 Watchdog Timer (WDT) Controller
+
+Required properties:
+- compatible: "atmel,sama5d4-wdt"
+- reg: base physical address and length of memory mapped region.
+
+Optional properties:
+- timeout-sec: watchdog timeout value (in seconds).
+- interrupts: interrupt number to the CPU.
+- atmel,watchdog-type: should be "hardware" or "software".
+       "hardware": enable watchdog fault reset. A watchdog fault triggers
+                   watchdog reset.
+       "software": enable watchdog fault interrupt. A watchdog fault asserts
+                   watchdog interrupt.
+- atmel,idle-halt: present if you want to stop the watchdog when the CPU is
+                  in idle state.
+       CAUTION: This property should be used with care, it actually makes the
+       watchdog not counting when the CPU is in idle state, therefore the
+       watchdog reset time depends on mean CPU usage and will not reset at all
+       if the CPU stop working while it is in idle state, which is probably
+       not what you want.
+- atmel,dbg-halt: present if you want to stop the watchdog when the CPU is
+                 in debug state.
+
+Example:
+       watchdog@fc068640 {
+               compatible = "atmel,sama5d4-wdt";
+               reg = <0xfc068640 0x10>;
+               interrupts = <4 IRQ_TYPE_LEVEL_HIGH 5>;
+               timeout-sec = <10>;
+               atmel,watchdog-type = "hardware";
+               atmel,dbg-halt;
+               atmel,idle-halt;
+               status = "okay";
+       };
diff --git a/Documentation/devicetree/bindings/watchdog/lpc18xx-wdt.txt b/Documentation/devicetree/bindings/watchdog/lpc18xx-wdt.txt
new file mode 100644 (file)
index 0000000..09f6b24
--- /dev/null
@@ -0,0 +1,19 @@
+* NXP LPC18xx Watchdog Timer (WDT)
+
+Required properties:
+- compatible: Should be "nxp,lpc1850-wwdt"
+- reg: Should contain WDT registers location and length
+- clocks: Must contain an entry for each entry in clock-names.
+- clock-names: Should contain "wdtclk" and "reg"; the watchdog counter
+               clock and register interface clock respectively.
+- interrupts: Should contain WDT interrupt
+
+Examples:
+
+watchdog@40080000 {
+       compatible = "nxp,lpc1850-wwdt";
+       reg = <0x40080000 0x24>;
+       clocks = <&cgu BASE_SAFE_CLK>, <&ccu1 CLK_CPU_WWDT>;
+       clock-names = "wdtclk", "reg";
+       interrupts = <49>;
+};
index c1f6864..10f062e 100644 (file)
@@ -180,6 +180,7 @@ Thermal zone device sys I/F, created once it's registered:
     |---temp:                  Current temperature
     |---mode:                  Working mode of the thermal zone
     |---policy:                        Thermal governor used for this zone
+    |---available_policies:    Available thermal governors for this zone
     |---trip_point_[0-*]_temp: Trip point temperature
     |---trip_point_[0-*]_type: Trip point type
     |---trip_point_[0-*]_hyst: Hysteresis value for this trip point
@@ -256,6 +257,10 @@ policy
        One of the various thermal governors used for a particular zone.
        RW, Required
 
+available_policies
+       Available thermal governors which can be used for a particular zone.
+       RO, Required
+
 trip_point_[0-*]_temp
        The temperature above which trip point will be fired.
        Unit: millidegree Celsius
@@ -417,6 +422,7 @@ method, the sys I/F structure will be built like this:
     |---temp:                  37000
     |---mode:                  enabled
     |---policy:                        step_wise
+    |---available_policies:    step_wise fair_share
     |---trip_point_0_temp:     100000
     |---trip_point_0_type:     critical
     |---trip_point_1_temp:     80000
index 3da8229..fcdde8f 100644 (file)
@@ -41,6 +41,7 @@ static void term(int sig)
 int main(int argc, char *argv[])
 {
     int flags;
+    unsigned int ping_rate = 1;
 
     fd = open("/dev/watchdog", O_WRONLY);
 
@@ -63,22 +64,33 @@ int main(int argc, char *argv[])
            fprintf(stderr, "Watchdog card enabled.\n");
            fflush(stderr);
            goto end;
+       } else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) {
+           flags = atoi(argv[2]);
+           ioctl(fd, WDIOC_SETTIMEOUT, &flags);
+           fprintf(stderr, "Watchdog timeout set to %u seconds.\n", flags);
+           fflush(stderr);
+           goto end;
+       } else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) {
+           ping_rate = strtoul(argv[2], NULL, 0);
+           fprintf(stderr, "Watchdog ping rate set to %u seconds.\n", ping_rate);
+           fflush(stderr);
        } else {
-           fprintf(stderr, "-d to disable, -e to enable.\n");
+           fprintf(stderr, "-d to disable, -e to enable, -t <n> to set " \
+               "the timeout,\n-p <n> to set the ping rate, and \n");
            fprintf(stderr, "run by itself to tick the card.\n");
            fflush(stderr);
            goto end;
        }
-    } else {
-       fprintf(stderr, "Watchdog Ticking Away!\n");
-       fflush(stderr);
     }
 
+    fprintf(stderr, "Watchdog Ticking Away!\n");
+    fflush(stderr);
+
     signal(SIGINT, term);
 
     while(1) {
        keep_alive();
-       sleep(1);
+       sleep(ping_rate);
     }
 end:
     close(fd);
index 310da42..7ba7ab7 100644 (file)
@@ -6789,6 +6789,14 @@ W:       http://www.mellanox.com
 Q:     http://patchwork.ozlabs.org/project/netdev/list/
 F:     drivers/net/ethernet/mellanox/mlxsw/
 
+MEMBARRIER SUPPORT
+M:     Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+M:     "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
+L:     linux-kernel@vger.kernel.org
+S:     Supported
+F:     kernel/membarrier.c
+F:     include/uapi/linux/membarrier.h
+
 MEMORY MANAGEMENT
 L:     linux-mm@kvack.org
 W:     http://www.linux-mm.org
@@ -7396,6 +7404,7 @@ NTB DRIVER CORE
 M:     Jon Mason <jdmason@kudzu.us>
 M:     Dave Jiang <dave.jiang@intel.com>
 M:     Allen Hubbe <Allen.Hubbe@emc.com>
+L:     linux-ntb@googlegroups.com
 S:     Supported
 W:     https://github.com/jonmason/ntb/wiki
 T:     git git://github.com/jonmason/ntb.git
@@ -7407,6 +7416,7 @@ F:        include/linux/ntb_transport.h
 NTB INTEL DRIVER
 M:     Jon Mason <jdmason@kudzu.us>
 M:     Dave Jiang <dave.jiang@intel.com>
+L:     linux-ntb@googlegroups.com
 S:     Supported
 W:     https://github.com/jonmason/ntb/wiki
 T:     git git://github.com/jonmason/ntb.git
index ad9825d..0a77b19 100644 (file)
@@ -402,6 +402,8 @@ static void __init axs103_early_init(void)
        unsigned int num_cores = (read_aux_reg(ARC_REG_MCIP_BCR) >> 16) & 0x3F;
        if (num_cores > 2)
                arc_set_core_freq(50 * 1000000);
+       else if (num_cores == 2)
+               arc_set_core_freq(75 * 1000000);
 #endif
 
        switch (arc_get_core_freq()/1000000) {
index 477bfa6..7663c45 100644 (file)
 372    i386    recvmsg                 sys_recvmsg                     compat_sys_recvmsg
 373    i386    shutdown                sys_shutdown
 374    i386    userfaultfd             sys_userfaultfd
+375    i386    membarrier              sys_membarrier
index 81c4906..278842f 100644 (file)
 321    common  bpf                     sys_bpf
 322    64      execveat                stub_execveat
 323    common  userfaultfd             sys_userfaultfd
+324    common  membarrier              sys_membarrier
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
index fc28b9f..30d8518 100644 (file)
@@ -525,8 +525,7 @@ static void acpi_thermal_check(void *data)
 
 /* sys I/F for generic thermal sysfs support */
 
-static int thermal_get_temp(struct thermal_zone_device *thermal,
-                           unsigned long *temp)
+static int thermal_get_temp(struct thermal_zone_device *thermal, int *temp)
 {
        struct acpi_thermal *tz = thermal->devdata;
        int result;
@@ -633,7 +632,7 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal,
 }
 
 static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
-                                int trip, unsigned long *temp)
+                                int trip, int *temp)
 {
        struct acpi_thermal *tz = thermal->devdata;
        int i;
@@ -686,7 +685,8 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
 }
 
 static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
-                               unsigned long *temperature) {
+                               int *temperature)
+{
        struct acpi_thermal *tz = thermal->devdata;
 
        if (tz->trips.critical.flags.valid) {
@@ -709,8 +709,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal,
                return -EINVAL;
 
        if (type == THERMAL_TRIP_ACTIVE) {
-               unsigned long trip_temp;
-               unsigned long temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
+               int trip_temp;
+               int temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
                                        tz->temperature, tz->kelvin_offset);
                if (thermal_get_trip_temp(thermal, trip, &trip_temp))
                        return -EINVAL;
index 4167201..16550c6 100644 (file)
@@ -212,6 +212,18 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)
        return ret;
 }
 
+/**
+ * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
+ * @genpd: PM domait to power off.
+ *
+ * Queue up the execution of pm_genpd_poweroff() unless it's already been done
+ * before.
+ */
+static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
+{
+       queue_work(pm_wq, &genpd->power_off_work);
+}
+
 /**
  * __pm_genpd_poweron - Restore power to a given PM domain and its masters.
  * @genpd: PM domain to power up.
@@ -259,8 +271,12 @@ static int __pm_genpd_poweron(struct generic_pm_domain *genpd)
        return 0;
 
  err:
-       list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node)
+       list_for_each_entry_continue_reverse(link,
+                                       &genpd->slave_links,
+                                       slave_node) {
                genpd_sd_counter_dec(link->master);
+               genpd_queue_power_off_work(link->master);
+       }
 
        return ret;
 }
@@ -348,18 +364,6 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
        return NOTIFY_DONE;
 }
 
-/**
- * genpd_queue_power_off_work - Queue up the execution of pm_genpd_poweroff().
- * @genpd: PM domait to power off.
- *
- * Queue up the execution of pm_genpd_poweroff() unless it's already been done
- * before.
- */
-static void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
-{
-       queue_work(pm_wq, &genpd->power_off_work);
-}
-
 /**
  * pm_genpd_poweroff - Remove power from a given PM domain.
  * @genpd: PM domain to power down.
@@ -1469,6 +1473,13 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
 
        mutex_lock(&genpd->lock);
 
+       if (!list_empty(&subdomain->slave_links) || subdomain->device_count) {
+               pr_warn("%s: unable to remove subdomain %s\n", genpd->name,
+                       subdomain->name);
+               ret = -EBUSY;
+               goto out;
+       }
+
        list_for_each_entry(link, &genpd->master_links, master_node) {
                if (link->slave != subdomain)
                        continue;
@@ -1487,6 +1498,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
                break;
        }
 
+out:
        mutex_unlock(&genpd->lock);
 
        return ret;
index eb25449..28cd75c 100644 (file)
@@ -340,6 +340,34 @@ unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency);
 
+/**
+ * dev_pm_opp_get_suspend_opp() - Get suspend opp
+ * @dev:       device for which we do this operation
+ *
+ * Return: This function returns pointer to the suspend opp if it is
+ * defined and available, otherwise it returns NULL.
+ *
+ * Locking: This function must be called under rcu_read_lock(). opp is a rcu
+ * protected pointer. The reason for the same is that the opp pointer which is
+ * returned will remain valid for use with opp_get_{voltage, freq} only while
+ * under the locked area. The pointer returned must be used prior to unlocking
+ * with rcu_read_unlock() to maintain the integrity of the pointer.
+ */
+struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev)
+{
+       struct device_opp *dev_opp;
+
+       opp_rcu_lockdep_assert();
+
+       dev_opp = _find_device_opp(dev);
+       if (IS_ERR(dev_opp) || !dev_opp->suspend_opp ||
+           !dev_opp->suspend_opp->available)
+               return NULL;
+
+       return dev_opp->suspend_opp;
+}
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp);
+
 /**
  * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list
  * @dev:       device for which we do this operation
index 5f498d9..cd0391e 100644 (file)
@@ -84,6 +84,7 @@ config ARM_KIRKWOOD_CPUFREQ
 config ARM_MT8173_CPUFREQ
        bool "Mediatek MT8173 CPUFreq support"
        depends on ARCH_MEDIATEK && REGULATOR
+       depends on !CPU_THERMAL || THERMAL=y
        select PM_OPP
        help
          This adds the CPUFreq driver support for Mediatek MT8173 SoC.
index c3583cd..7c0d70e 100644 (file)
@@ -196,6 +196,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
        struct device *cpu_dev;
        struct regulator *cpu_reg;
        struct clk *cpu_clk;
+       struct dev_pm_opp *suspend_opp;
        unsigned long min_uV = ~0, max_uV = 0;
        unsigned int transition_latency;
        bool need_update = false;
@@ -239,6 +240,17 @@ static int cpufreq_init(struct cpufreq_policy *policy)
         */
        of_cpumask_init_opp_table(policy->cpus);
 
+       /*
+        * But we need OPP table to function so if it is not there let's
+        * give platform code chance to provide it for us.
+        */
+       ret = dev_pm_opp_get_opp_count(cpu_dev);
+       if (ret <= 0) {
+               pr_debug("OPP table is not ready, deferring probe\n");
+               ret = -EPROBE_DEFER;
+               goto out_free_opp;
+       }
+
        if (need_update) {
                struct cpufreq_dt_platform_data *pd = cpufreq_get_driver_data();
 
@@ -249,24 +261,16 @@ static int cpufreq_init(struct cpufreq_policy *policy)
                 * OPP tables are initialized only for policy->cpu, do it for
                 * others as well.
                 */
-               set_cpus_sharing_opps(cpu_dev, policy->cpus);
+               ret = set_cpus_sharing_opps(cpu_dev, policy->cpus);
+               if (ret)
+                       dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
+                               __func__, ret);
 
                of_property_read_u32(np, "clock-latency", &transition_latency);
        } else {
                transition_latency = dev_pm_opp_get_max_clock_latency(cpu_dev);
        }
 
-       /*
-        * But we need OPP table to function so if it is not there let's
-        * give platform code chance to provide it for us.
-        */
-       ret = dev_pm_opp_get_opp_count(cpu_dev);
-       if (ret <= 0) {
-               pr_debug("OPP table is not ready, deferring probe\n");
-               ret = -EPROBE_DEFER;
-               goto out_free_opp;
-       }
-
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv) {
                ret = -ENOMEM;
@@ -300,7 +304,8 @@ static int cpufreq_init(struct cpufreq_policy *policy)
                        rcu_read_unlock();
 
                        tol_uV = opp_uV * priv->voltage_tolerance / 100;
-                       if (regulator_is_supported_voltage(cpu_reg, opp_uV,
+                       if (regulator_is_supported_voltage(cpu_reg,
+                                                          opp_uV - tol_uV,
                                                           opp_uV + tol_uV)) {
                                if (opp_uV < min_uV)
                                        min_uV = opp_uV;
@@ -329,6 +334,13 @@ static int cpufreq_init(struct cpufreq_policy *policy)
        policy->driver_data = priv;
 
        policy->clk = cpu_clk;
+
+       rcu_read_lock();
+       suspend_opp = dev_pm_opp_get_suspend_opp(cpu_dev);
+       if (suspend_opp)
+               policy->suspend_freq = dev_pm_opp_get_freq(suspend_opp) / 1000;
+       rcu_read_unlock();
+
        ret = cpufreq_table_validate_and_show(policy, freq_table);
        if (ret) {
                dev_err(cpu_dev, "%s: invalid frequency table: %d\n", __func__,
@@ -419,6 +431,7 @@ static struct cpufreq_driver dt_cpufreq_driver = {
        .ready = cpufreq_ready,
        .name = "cpufreq-dt",
        .attr = cpufreq_dt_attr,
+       .suspend = cpufreq_generic_suspend,
 };
 
 static int dt_cpufreq_probe(struct platform_device *pdev)
index b3d9368..6633b3f 100644 (file)
@@ -239,7 +239,7 @@ int cpufreq_generic_init(struct cpufreq_policy *policy,
 EXPORT_SYMBOL_GPL(cpufreq_generic_init);
 
 /* Only for cpufreq core internal use */
-struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu)
+static struct cpufreq_policy *cpufreq_cpu_get_raw(unsigned int cpu)
 {
        struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu);
 
@@ -1626,8 +1626,8 @@ int cpufreq_generic_suspend(struct cpufreq_policy *policy)
        int ret;
 
        if (!policy->suspend_freq) {
-               pr_err("%s: suspend_freq can't be zero\n", __func__);
-               return -EINVAL;
+               pr_debug("%s: suspend_freq not defined\n", __func__);
+               return 0;
        }
 
        pr_debug("%s: Setting suspend-freq: %u\n", __func__,
@@ -2031,8 +2031,7 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
                if (!try_module_get(policy->governor->owner))
                        return -EINVAL;
 
-       pr_debug("__cpufreq_governor for CPU %u, event %u\n",
-                policy->cpu, event);
+       pr_debug("%s: for CPU %u, event %u\n", __func__, policy->cpu, event);
 
        mutex_lock(&cpufreq_governor_lock);
        if ((policy->governor_enabled && event == CPUFREQ_GOV_START)
index cddc619..3af9dd7 100644 (file)
@@ -260,24 +260,31 @@ static inline void update_turbo_state(void)
                 cpu->pstate.max_pstate == cpu->pstate.turbo_pstate);
 }
 
-#define PCT_TO_HWP(x) (x * 255 / 100)
 static void intel_pstate_hwp_set(void)
 {
-       int min, max, cpu;
-       u64 value, freq;
+       int min, hw_min, max, hw_max, cpu, range, adj_range;
+       u64 value, cap;
+
+       rdmsrl(MSR_HWP_CAPABILITIES, cap);
+       hw_min = HWP_LOWEST_PERF(cap);
+       hw_max = HWP_HIGHEST_PERF(cap);
+       range = hw_max - hw_min;
 
        get_online_cpus();
 
        for_each_online_cpu(cpu) {
                rdmsrl_on_cpu(cpu, MSR_HWP_REQUEST, &value);
-               min = PCT_TO_HWP(limits.min_perf_pct);
+               adj_range = limits.min_perf_pct * range / 100;
+               min = hw_min + adj_range;
                value &= ~HWP_MIN_PERF(~0L);
                value |= HWP_MIN_PERF(min);
 
-               max = PCT_TO_HWP(limits.max_perf_pct);
+               adj_range = limits.max_perf_pct * range / 100;
+               max = hw_min + adj_range;
                if (limits.no_turbo) {
-                       rdmsrl( MSR_HWP_CAPABILITIES, freq);
-                       max = HWP_GUARANTEED_PERF(freq);
+                       hw_max = HWP_GUARANTEED_PERF(cap);
+                       if (hw_max < max)
+                               max = hw_max;
                }
 
                value &= ~HWP_MAX_PERF(~0L);
@@ -423,6 +430,8 @@ static ssize_t store_max_perf_pct(struct kobject *a, struct attribute *b,
 
        limits.max_sysfs_pct = clamp_t(int, input, 0 , 100);
        limits.max_perf_pct = min(limits.max_policy_pct, limits.max_sysfs_pct);
+       limits.max_perf_pct = max(limits.min_policy_pct, limits.max_perf_pct);
+       limits.max_perf_pct = max(limits.min_perf_pct, limits.max_perf_pct);
        limits.max_perf = div_fp(int_tofp(limits.max_perf_pct), int_tofp(100));
 
        if (hwp_active)
@@ -442,6 +451,8 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct attribute *b,
 
        limits.min_sysfs_pct = clamp_t(int, input, 0 , 100);
        limits.min_perf_pct = max(limits.min_policy_pct, limits.min_sysfs_pct);
+       limits.min_perf_pct = min(limits.max_policy_pct, limits.min_perf_pct);
+       limits.min_perf_pct = min(limits.max_perf_pct, limits.min_perf_pct);
        limits.min_perf = div_fp(int_tofp(limits.min_perf_pct), int_tofp(100));
 
        if (hwp_active)
@@ -989,12 +1000,19 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
 
        limits.min_policy_pct = (policy->min * 100) / policy->cpuinfo.max_freq;
        limits.min_policy_pct = clamp_t(int, limits.min_policy_pct, 0 , 100);
-       limits.min_perf_pct = max(limits.min_policy_pct, limits.min_sysfs_pct);
-       limits.min_perf = div_fp(int_tofp(limits.min_perf_pct), int_tofp(100));
-
        limits.max_policy_pct = (policy->max * 100) / policy->cpuinfo.max_freq;
        limits.max_policy_pct = clamp_t(int, limits.max_policy_pct, 0 , 100);
+
+       /* Normalize user input to [min_policy_pct, max_policy_pct] */
+       limits.min_perf_pct = max(limits.min_policy_pct, limits.min_sysfs_pct);
+       limits.min_perf_pct = min(limits.max_policy_pct, limits.min_perf_pct);
        limits.max_perf_pct = min(limits.max_policy_pct, limits.max_sysfs_pct);
+       limits.max_perf_pct = max(limits.min_policy_pct, limits.max_perf_pct);
+
+       /* Make sure min_perf_pct <= max_perf_pct */
+       limits.min_perf_pct = min(limits.max_perf_pct, limits.min_perf_pct);
+
+       limits.min_perf = div_fp(int_tofp(limits.min_perf_pct), int_tofp(100));
        limits.max_perf = div_fp(int_tofp(limits.max_perf_pct), int_tofp(100));
 
        if (hwp_active)
index 1523e2d..344058f 100644 (file)
@@ -186,6 +186,28 @@ bool cpuidle_state_is_coupled(struct cpuidle_driver *drv, int state)
        return drv->states[state].flags & CPUIDLE_FLAG_COUPLED;
 }
 
+/**
+ * cpuidle_coupled_state_verify - check if the coupled states are correctly set.
+ * @drv: struct cpuidle_driver for the platform
+ *
+ * Returns 0 for valid state values, a negative error code otherwise:
+ *  * -EINVAL if any coupled state(safe_state_index) is wrongly set.
+ */
+int cpuidle_coupled_state_verify(struct cpuidle_driver *drv)
+{
+       int i;
+
+       for (i = drv->state_count - 1; i >= 0; i--) {
+               if (cpuidle_state_is_coupled(drv, i) &&
+                   (drv->safe_state_index == i ||
+                    drv->safe_state_index < 0 ||
+                    drv->safe_state_index >= drv->state_count))
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
 /**
  * cpuidle_coupled_set_ready - mark a cpu as ready
  * @coupled: the struct coupled that contains the current cpu
index 178c5ad..f87f399 100644 (file)
@@ -35,6 +35,7 @@ extern void cpuidle_remove_sysfs(struct cpuidle_device *dev);
 
 #ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED
 bool cpuidle_state_is_coupled(struct cpuidle_driver *drv, int state);
+int cpuidle_coupled_state_verify(struct cpuidle_driver *drv);
 int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
                struct cpuidle_driver *drv, int next_state);
 int cpuidle_coupled_register_device(struct cpuidle_device *dev);
@@ -46,6 +47,11 @@ bool cpuidle_state_is_coupled(struct cpuidle_driver *drv, int state)
        return false;
 }
 
+static inline int cpuidle_coupled_state_verify(struct cpuidle_driver *drv)
+{
+       return 0;
+}
+
 static inline int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
                struct cpuidle_driver *drv, int next_state)
 {
index 5db1478..389ade4 100644 (file)
@@ -227,6 +227,10 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv)
        if (!drv || !drv->state_count)
                return -EINVAL;
 
+       ret = cpuidle_coupled_state_verify(drv);
+       if (ret)
+               return ret;
+
        if (cpuidle_disabled())
                return -ENODEV;
 
index ca78311..cf1268d 100644 (file)
@@ -280,6 +280,7 @@ struct sbridge_info {
        u8              max_interleave;
        u8              (*get_node_id)(struct sbridge_pvt *pvt);
        enum mem_type   (*get_memory_type)(struct sbridge_pvt *pvt);
+       enum dev_type   (*get_width)(struct sbridge_pvt *pvt, u32 mtr);
        struct pci_dev  *pci_vtd;
 };
 
@@ -471,6 +472,9 @@ static const struct pci_id_table pci_dev_descr_ibridge_table[] = {
 #define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD2 0x2f6c
 #define PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TAD3 0x2f6d
 #define PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO0 0x2fbd
+#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO1 0x2fbf
+#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO2 0x2fb9
+#define PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO3 0x2fbb
 static const struct pci_id_descr pci_dev_descr_haswell[] = {
        /* first item must be the HA */
        { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0, 0)             },
@@ -488,6 +492,9 @@ static const struct pci_id_descr pci_dev_descr_haswell[] = {
        { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0_TAD3, 1)        },
 
        { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO0, 1)          },
+       { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO1, 1)          },
+       { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO2, 1)          },
+       { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO3, 1)          },
 
        { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_TA, 1)          },
        { PCI_DESCR(PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1_THERMAL, 1)     },
@@ -762,6 +769,49 @@ out:
        return mtype;
 }
 
+static enum dev_type sbridge_get_width(struct sbridge_pvt *pvt, u32 mtr)
+{
+       /* there's no way to figure out */
+       return DEV_UNKNOWN;
+}
+
+static enum dev_type __ibridge_get_width(u32 mtr)
+{
+       enum dev_type type;
+
+       switch (mtr) {
+       case 3:
+               type = DEV_UNKNOWN;
+               break;
+       case 2:
+               type = DEV_X16;
+               break;
+       case 1:
+               type = DEV_X8;
+               break;
+       case 0:
+               type = DEV_X4;
+               break;
+       }
+
+       return type;
+}
+
+static enum dev_type ibridge_get_width(struct sbridge_pvt *pvt, u32 mtr)
+{
+       /*
+        * ddr3_width on the documentation but also valid for DDR4 on
+        * Haswell
+        */
+       return __ibridge_get_width(GET_BITFIELD(mtr, 7, 8));
+}
+
+static enum dev_type broadwell_get_width(struct sbridge_pvt *pvt, u32 mtr)
+{
+       /* ddr3_width on the documentation but also valid for DDR4 */
+       return __ibridge_get_width(GET_BITFIELD(mtr, 8, 9));
+}
+
 static u8 get_node_id(struct sbridge_pvt *pvt)
 {
        u32 reg;
@@ -966,17 +1016,7 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 
                                dimm->nr_pages = npages;
                                dimm->grain = 32;
-                               switch (banks) {
-                               case 16:
-                                       dimm->dtype = DEV_X16;
-                                       break;
-                               case 8:
-                                       dimm->dtype = DEV_X8;
-                                       break;
-                               case 4:
-                                       dimm->dtype = DEV_X4;
-                                       break;
-                               }
+                               dimm->dtype = pvt->info.get_width(pvt, mtr);
                                dimm->mtype = mtype;
                                dimm->edac_mode = mode;
                                snprintf(dimm->label, sizeof(dimm->label),
@@ -1869,7 +1909,11 @@ static int haswell_mci_bind_devs(struct mem_ctl_info *mci,
                }
                        break;
                case PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO0:
-                       pvt->pci_ddrio = pdev;
+               case PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO1:
+               case PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO2:
+               case PCI_DEVICE_ID_INTEL_HASWELL_IMC_DDRIO3:
+                       if (!pvt->pci_ddrio)
+                               pvt->pci_ddrio = pdev;
                        break;
                case PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA1:
                        pvt->pci_ha1 = pdev;
@@ -2361,6 +2405,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
                pvt->info.interleave_list = ibridge_interleave_list;
                pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
                pvt->info.interleave_pkg = ibridge_interleave_pkg;
+               pvt->info.get_width = ibridge_get_width;
                mci->ctl_name = kasprintf(GFP_KERNEL, "Ivy Bridge Socket#%d", mci->mc_idx);
 
                /* Store pci devices at mci for faster access */
@@ -2380,6 +2425,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
                pvt->info.interleave_list = sbridge_interleave_list;
                pvt->info.max_interleave = ARRAY_SIZE(sbridge_interleave_list);
                pvt->info.interleave_pkg = sbridge_interleave_pkg;
+               pvt->info.get_width = sbridge_get_width;
                mci->ctl_name = kasprintf(GFP_KERNEL, "Sandy Bridge Socket#%d", mci->mc_idx);
 
                /* Store pci devices at mci for faster access */
@@ -2399,6 +2445,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
                pvt->info.interleave_list = ibridge_interleave_list;
                pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
                pvt->info.interleave_pkg = ibridge_interleave_pkg;
+               pvt->info.get_width = ibridge_get_width;
                mci->ctl_name = kasprintf(GFP_KERNEL, "Haswell Socket#%d", mci->mc_idx);
 
                /* Store pci devices at mci for faster access */
@@ -2418,6 +2465,7 @@ static int sbridge_register_mci(struct sbridge_dev *sbridge_dev, enum type type)
                pvt->info.interleave_list = ibridge_interleave_list;
                pvt->info.max_interleave = ARRAY_SIZE(ibridge_interleave_list);
                pvt->info.interleave_pkg = ibridge_interleave_pkg;
+               pvt->info.get_width = broadwell_get_width;
                mci->ctl_name = kasprintf(GFP_KERNEL, "Broadwell Socket#%d", mci->mc_idx);
 
                /* Store pci devices at mci for faster access */
index df0b61a..bd1a415 100644 (file)
@@ -77,6 +77,7 @@ config DRM_EXYNOS_VIDI
 config DRM_EXYNOS_G2D
        bool "Exynos DRM G2D"
        depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_G2D
+       select FRAME_VECTOR
        help
          Choose this option if you want to use Exynos G2D for DRM.
 
index 535b4ad..3734c34 100644 (file)
@@ -194,10 +194,8 @@ struct g2d_cmdlist_userptr {
        dma_addr_t              dma_addr;
        unsigned long           userptr;
        unsigned long           size;
-       struct page             **pages;
-       unsigned int            npages;
+       struct frame_vector     *vec;
        struct sg_table         *sgt;
-       struct vm_area_struct   *vma;
        atomic_t                refcount;
        bool                    in_pool;
        bool                    out_of_list;
@@ -367,6 +365,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev,
 {
        struct g2d_cmdlist_userptr *g2d_userptr =
                                        (struct g2d_cmdlist_userptr *)obj;
+       struct page **pages;
 
        if (!obj)
                return;
@@ -386,19 +385,21 @@ out:
        exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt,
                                        DMA_BIDIRECTIONAL);
 
-       exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
-                                       g2d_userptr->npages,
-                                       g2d_userptr->vma);
+       pages = frame_vector_pages(g2d_userptr->vec);
+       if (!IS_ERR(pages)) {
+               int i;
 
-       exynos_gem_put_vma(g2d_userptr->vma);
+               for (i = 0; i < frame_vector_count(g2d_userptr->vec); i++)
+                       set_page_dirty_lock(pages[i]);
+       }
+       put_vaddr_frames(g2d_userptr->vec);
+       frame_vector_destroy(g2d_userptr->vec);
 
        if (!g2d_userptr->out_of_list)
                list_del_init(&g2d_userptr->list);
 
        sg_free_table(g2d_userptr->sgt);
        kfree(g2d_userptr->sgt);
-
-       drm_free_large(g2d_userptr->pages);
        kfree(g2d_userptr);
 }
 
@@ -412,9 +413,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
        struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
        struct g2d_cmdlist_userptr *g2d_userptr;
        struct g2d_data *g2d;
-       struct page **pages;
        struct sg_table *sgt;
-       struct vm_area_struct *vma;
        unsigned long start, end;
        unsigned int npages, offset;
        int ret;
@@ -460,65 +459,40 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
                return ERR_PTR(-ENOMEM);
 
        atomic_set(&g2d_userptr->refcount, 1);
+       g2d_userptr->size = size;
 
        start = userptr & PAGE_MASK;
        offset = userptr & ~PAGE_MASK;
        end = PAGE_ALIGN(userptr + size);
        npages = (end - start) >> PAGE_SHIFT;
-       g2d_userptr->npages = npages;
-
-       pages = drm_calloc_large(npages, sizeof(struct page *));
-       if (!pages) {
-               DRM_ERROR("failed to allocate pages.\n");
+       g2d_userptr->vec = frame_vector_create(npages);
+       if (!g2d_userptr->vec) {
                ret = -ENOMEM;
                goto err_free;
        }
 
-       down_read(&current->mm->mmap_sem);
-       vma = find_vma(current->mm, userptr);
-       if (!vma) {
-               up_read(&current->mm->mmap_sem);
-               DRM_ERROR("failed to get vm region.\n");
+       ret = get_vaddr_frames(start, npages, true, true, g2d_userptr->vec);
+       if (ret != npages) {
+               DRM_ERROR("failed to get user pages from userptr.\n");
+               if (ret < 0)
+                       goto err_destroy_framevec;
                ret = -EFAULT;
-               goto err_free_pages;
+               goto err_put_framevec;
        }
-
-       if (vma->vm_end < userptr + size) {
-               up_read(&current->mm->mmap_sem);
-               DRM_ERROR("vma is too small.\n");
+       if (frame_vector_to_pages(g2d_userptr->vec) < 0) {
                ret = -EFAULT;
-               goto err_free_pages;
-       }
-
-       g2d_userptr->vma = exynos_gem_get_vma(vma);
-       if (!g2d_userptr->vma) {
-               up_read(&current->mm->mmap_sem);
-               DRM_ERROR("failed to copy vma.\n");
-               ret = -ENOMEM;
-               goto err_free_pages;
-       }
-
-       g2d_userptr->size = size;
-
-       ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK,
-                                               npages, pages, vma);
-       if (ret < 0) {
-               up_read(&current->mm->mmap_sem);
-               DRM_ERROR("failed to get user pages from userptr.\n");
-               goto err_put_vma;
+               goto err_put_framevec;
        }
 
-       up_read(&current->mm->mmap_sem);
-       g2d_userptr->pages = pages;
-
        sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
        if (!sgt) {
                ret = -ENOMEM;
-               goto err_free_userptr;
+               goto err_put_framevec;
        }
 
-       ret = sg_alloc_table_from_pages(sgt, pages, npages, offset,
-                                       size, GFP_KERNEL);
+       ret = sg_alloc_table_from_pages(sgt,
+                                       frame_vector_pages(g2d_userptr->vec),
+                                       npages, offset, size, GFP_KERNEL);
        if (ret < 0) {
                DRM_ERROR("failed to get sgt from pages.\n");
                goto err_free_sgt;
@@ -553,16 +527,11 @@ err_sg_free_table:
 err_free_sgt:
        kfree(sgt);
 
-err_free_userptr:
-       exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
-                                       g2d_userptr->npages,
-                                       g2d_userptr->vma);
-
-err_put_vma:
-       exynos_gem_put_vma(g2d_userptr->vma);
+err_put_framevec:
+       put_vaddr_frames(g2d_userptr->vec);
 
-err_free_pages:
-       drm_free_large(pages);
+err_destroy_framevec:
+       frame_vector_destroy(g2d_userptr->vec);
 
 err_free:
        kfree(g2d_userptr);
index 62b9ea1..f12fbc3 100644 (file)
@@ -366,103 +366,6 @@ int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
        return 0;
 }
 
-struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma)
-{
-       struct vm_area_struct *vma_copy;
-
-       vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
-       if (!vma_copy)
-               return NULL;
-
-       if (vma->vm_ops && vma->vm_ops->open)
-               vma->vm_ops->open(vma);
-
-       if (vma->vm_file)
-               get_file(vma->vm_file);
-
-       memcpy(vma_copy, vma, sizeof(*vma));
-
-       vma_copy->vm_mm = NULL;
-       vma_copy->vm_next = NULL;
-       vma_copy->vm_prev = NULL;
-
-       return vma_copy;
-}
-
-void exynos_gem_put_vma(struct vm_area_struct *vma)
-{
-       if (!vma)
-               return;
-
-       if (vma->vm_ops && vma->vm_ops->close)
-               vma->vm_ops->close(vma);
-
-       if (vma->vm_file)
-               fput(vma->vm_file);
-
-       kfree(vma);
-}
-
-int exynos_gem_get_pages_from_userptr(unsigned long start,
-                                               unsigned int npages,
-                                               struct page **pages,
-                                               struct vm_area_struct *vma)
-{
-       int get_npages;
-
-       /* the memory region mmaped with VM_PFNMAP. */
-       if (vma_is_io(vma)) {
-               unsigned int i;
-
-               for (i = 0; i < npages; ++i, start += PAGE_SIZE) {
-                       unsigned long pfn;
-                       int ret = follow_pfn(vma, start, &pfn);
-                       if (ret)
-                               return ret;
-
-                       pages[i] = pfn_to_page(pfn);
-               }
-
-               if (i != npages) {
-                       DRM_ERROR("failed to get user_pages.\n");
-                       return -EINVAL;
-               }
-
-               return 0;
-       }
-
-       get_npages = get_user_pages(current, current->mm, start,
-                                       npages, 1, 1, pages, NULL);
-       get_npages = max(get_npages, 0);
-       if (get_npages != npages) {
-               DRM_ERROR("failed to get user_pages.\n");
-               while (get_npages)
-                       put_page(pages[--get_npages]);
-               return -EFAULT;
-       }
-
-       return 0;
-}
-
-void exynos_gem_put_pages_to_userptr(struct page **pages,
-                                       unsigned int npages,
-                                       struct vm_area_struct *vma)
-{
-       if (!vma_is_io(vma)) {
-               unsigned int i;
-
-               for (i = 0; i < npages; i++) {
-                       set_page_dirty_lock(pages[i]);
-
-                       /*
-                        * undo the reference we took when populating
-                        * the table.
-                        */
-                       put_page(pages[i]);
-               }
-       }
-}
-
 int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,
                                struct sg_table *sgt,
                                enum dma_data_direction dir)
index fe41d5a..e4e57bb 100644 (file)
@@ -104,7 +104,7 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
 
 /* sysfs attributes for hwmon */
 
-static int lm75_read_temp(void *dev, long *temp)
+static int lm75_read_temp(void *dev, int *temp)
 {
        struct lm75_data *data = lm75_update_device(dev);
 
index dc0b76c..feed306 100644 (file)
@@ -477,7 +477,7 @@ static int ntc_thermistor_get_ohm(struct ntc_data *data)
        return -EINVAL;
 }
 
-static int ntc_read_temp(void *dev, long *temp)
+static int ntc_read_temp(void *dev, int *temp)
 {
        struct ntc_data *data = dev_get_drvdata(dev);
        int ohm;
index 9da2735..6548262 100644 (file)
@@ -98,7 +98,7 @@ static struct tmp102 *tmp102_update_device(struct device *dev)
        return tmp102;
 }
 
-static int tmp102_read_temp(void *dev, long *temp)
+static int tmp102_read_temp(void *dev, int *temp)
 {
        struct tmp102 *tmp102 = tmp102_update_device(dev);
 
index dc439a4..403bd29 100644 (file)
@@ -3095,7 +3095,7 @@ out:
 
 static int
 isert_setup_np(struct iscsi_np *np,
-              struct __kernel_sockaddr_storage *ksockaddr)
+              struct sockaddr_storage *ksockaddr)
 {
        struct isert_np *isert_np;
        struct rdma_cm_id *isert_lid;
@@ -3117,7 +3117,7 @@ isert_setup_np(struct iscsi_np *np,
         * in iscsi_target_configfs.c code..
         */
        memcpy(&np->np_sockaddr, ksockaddr,
-              sizeof(struct __kernel_sockaddr_storage));
+              sizeof(struct sockaddr_storage));
 
        isert_lid = isert_setup_id(isert_np);
        if (IS_ERR(isert_lid)) {
@@ -3199,32 +3199,11 @@ isert_set_conn_info(struct iscsi_np *np, struct iscsi_conn *conn,
 {
        struct rdma_cm_id *cm_id = isert_conn->cm_id;
        struct rdma_route *cm_route = &cm_id->route;
-       struct sockaddr_in *sock_in;
-       struct sockaddr_in6 *sock_in6;
 
        conn->login_family = np->np_sockaddr.ss_family;
 
-       if (np->np_sockaddr.ss_family == AF_INET6) {
-               sock_in6 = (struct sockaddr_in6 *)&cm_route->addr.dst_addr;
-               snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI6c",
-                        &sock_in6->sin6_addr.in6_u);
-               conn->login_port = ntohs(sock_in6->sin6_port);
-
-               sock_in6 = (struct sockaddr_in6 *)&cm_route->addr.src_addr;
-               snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI6c",
-                        &sock_in6->sin6_addr.in6_u);
-               conn->local_port = ntohs(sock_in6->sin6_port);
-       } else {
-               sock_in = (struct sockaddr_in *)&cm_route->addr.dst_addr;
-               sprintf(conn->login_ip, "%pI4",
-                       &sock_in->sin_addr.s_addr);
-               conn->login_port = ntohs(sock_in->sin_port);
-
-               sock_in = (struct sockaddr_in *)&cm_route->addr.src_addr;
-               sprintf(conn->local_ip, "%pI4",
-                       &sock_in->sin_addr.s_addr);
-               conn->local_port = ntohs(sock_in->sin_port);
-       }
+       conn->login_sockaddr = cm_route->addr.dst_addr;
+       conn->local_sockaddr = cm_route->addr.src_addr;
 }
 
 static int
index 9d35499..08d4964 100644 (file)
@@ -290,19 +290,14 @@ static int evdev_flush(struct file *file, fl_owner_t id)
 {
        struct evdev_client *client = file->private_data;
        struct evdev *evdev = client->evdev;
-       int retval;
 
-       retval = mutex_lock_interruptible(&evdev->mutex);
-       if (retval)
-               return retval;
+       mutex_lock(&evdev->mutex);
 
-       if (!evdev->exist || client->revoked)
-               retval = -ENODEV;
-       else
-               retval = input_flush_device(&evdev->handle, file);
+       if (evdev->exist && !client->revoked)
+               input_flush_device(&evdev->handle, file);
 
        mutex_unlock(&evdev->mutex);
-       return retval;
+       return 0;
 }
 
 static void evdev_free(struct device *dev)
index d2ea863..2165f3d 100644 (file)
@@ -5,8 +5,6 @@
  * 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.
- *
- * <<Power management needs to be implemented>>.
  */
 
 #include <linux/clk.h>
index 1f7e15c..4f5ef5b 100644 (file)
@@ -118,6 +118,7 @@ static const struct of_device_id ab8500_ponkey_match[] = {
        { .compatible = "stericsson,ab8500-ponkey", },
        {}
 };
+MODULE_DEVICE_TABLE(of, ab8500_ponkey_match);
 #endif
 
 static struct platform_driver ab8500_ponkey_driver = {
index e82edf8..f2261ab 100644 (file)
@@ -173,6 +173,7 @@ static const struct of_device_id pwm_beeper_match[] = {
        { .compatible = "pwm-beeper", },
        { },
 };
+MODULE_DEVICE_TABLE(of, pwm_beeper_match);
 #endif
 
 static struct platform_driver pwm_beeper_driver = {
index 6bf3f10..a804705 100644 (file)
@@ -249,6 +249,7 @@ static const struct of_device_id regulator_haptic_dt_match[] = {
        { .compatible = "regulator-haptic" },
        { /* sentinel */ },
 };
+MODULE_DEVICE_TABLE(of, regulator_haptic_dt_match);
 
 static struct platform_driver regulator_haptic_driver = {
        .probe          = regulator_haptic_probe,
index 54116e5..6f997aa 100644 (file)
@@ -253,6 +253,7 @@ static const struct of_device_id bbc_beep_match[] = {
        },
        {},
 };
+MODULE_DEVICE_TABLE(of, bbc_beep_match);
 
 static struct platform_driver bbc_beep_driver = {
        .driver = {
@@ -332,6 +333,7 @@ static const struct of_device_id grover_beep_match[] = {
        },
        {},
 };
+MODULE_DEVICE_TABLE(of, grover_beep_match);
 
 static struct platform_driver grover_beep_driver = {
        .driver = {
index e2b7420..fa94530 100644 (file)
@@ -1170,6 +1170,7 @@ static const struct acpi_device_id elan_acpi_id[] = {
        { "ELAN0000", 0 },
        { "ELAN0100", 0 },
        { "ELAN0600", 0 },
+       { "ELAN1000", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(acpi, elan_acpi_id);
index c9c98f0..db91de5 100644 (file)
@@ -877,7 +877,7 @@ static int __init i8042_check_aux(void)
 static int i8042_controller_check(void)
 {
        if (i8042_flush()) {
-               pr_err("No controller found\n");
+               pr_info("No controller found\n");
                return -ENODEV;
        }
 
index 059edeb..600dcce 100644 (file)
@@ -479,6 +479,18 @@ config TOUCHSCREEN_MTOUCH
          To compile this driver as a module, choose M here: the
          module will be called mtouch.
 
+config TOUCHSCREEN_IMX6UL_TSC
+       tristate "Freescale i.MX6UL touchscreen controller"
+       depends on (OF && GPIOLIB) || COMPILE_TEST
+       help
+         Say Y here if you have a Freescale i.MX6UL, and want to
+         use the internal touchscreen controller.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called imx6ul_tsc.
+
 config TOUCHSCREEN_INEXIO
        tristate "iNexio serial touchscreens"
        select SERIO
@@ -1040,4 +1052,16 @@ config TOUCHSCREEN_ZFORCE
          To compile this driver as a module, choose M here: the
          module will be called zforce_ts.
 
+config TOUCHSCREEN_COLIBRI_VF50
+       tristate "Toradex Colibri on board touchscreen driver"
+       depends on GPIOLIB && IIO && VF610_ADC
+       help
+         Say Y here if you have a Colibri VF50 and plan to use
+         the on-board provided 4-wire touchscreen driver.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called colibri_vf50_ts.
+
 endif
index c85aae2..1b79cc0 100644 (file)
@@ -38,6 +38,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX)      += egalax_ts.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)      += fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GOODIX)       += goodix.o
 obj-$(CONFIG_TOUCHSCREEN_ILI210X)      += ili210x.o
+obj-$(CONFIG_TOUCHSCREEN_IMX6UL_TSC)   += imx6ul_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)       += inexio.o
 obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)    += intel-mid-touch.o
 obj-$(CONFIG_TOUCHSCREEN_IPROC)                += bcm_iproc_tsc.o
@@ -85,3 +86,4 @@ obj-$(CONFIG_TOUCHSCREEN_W90X900)     += w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_SX8654)       += sx8654.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)     += tps6507x-ts.o
 obj-$(CONFIG_TOUCHSCREEN_ZFORCE)       += zforce_ts.o
+obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o
diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c
new file mode 100644 (file)
index 0000000..5d4903a
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * Toradex Colibri VF50 Touchscreen driver
+ *
+ * Copyright 2015 Toradex AG
+ *
+ * Originally authored by Stefan Agner for 3.0 kernel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define DRIVER_NAME                    "colibri-vf50-ts"
+#define DRV_VERSION                    "1.0"
+
+#define VF_ADC_MAX                     ((1 << 12) - 1)
+
+#define COLI_TOUCH_MIN_DELAY_US                1000
+#define COLI_TOUCH_MAX_DELAY_US                2000
+#define COLI_PULLUP_MIN_DELAY_US       10000
+#define COLI_PULLUP_MAX_DELAY_US       11000
+#define COLI_TOUCH_NO_OF_AVGS          5
+#define COLI_TOUCH_REQ_ADC_CHAN                4
+
+struct vf50_touch_device {
+       struct platform_device *pdev;
+       struct input_dev *ts_input;
+       struct iio_channel *channels;
+       struct gpio_desc *gpio_xp;
+       struct gpio_desc *gpio_xm;
+       struct gpio_desc *gpio_yp;
+       struct gpio_desc *gpio_ym;
+       int pen_irq;
+       int min_pressure;
+       bool stop_touchscreen;
+};
+
+/*
+ * Enables given plates and measures touch parameters using ADC
+ */
+static int adc_ts_measure(struct iio_channel *channel,
+                         struct gpio_desc *plate_p, struct gpio_desc *plate_m)
+{
+       int i, value = 0, val = 0;
+       int error;
+
+       gpiod_set_value(plate_p, 1);
+       gpiod_set_value(plate_m, 1);
+
+       usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US);
+
+       for (i = 0; i < COLI_TOUCH_NO_OF_AVGS; i++) {
+               error = iio_read_channel_raw(channel, &val);
+               if (error < 0) {
+                       value = error;
+                       goto error_iio_read;
+               }
+
+               value += val;
+       }
+
+       value /= COLI_TOUCH_NO_OF_AVGS;
+
+error_iio_read:
+       gpiod_set_value(plate_p, 0);
+       gpiod_set_value(plate_m, 0);
+
+       return value;
+}
+
+/*
+ * Enable touch detection using falling edge detection on XM
+ */
+static void vf50_ts_enable_touch_detection(struct vf50_touch_device *vf50_ts)
+{
+       /* Enable plate YM (needs to be strong GND, high active) */
+       gpiod_set_value(vf50_ts->gpio_ym, 1);
+
+       /*
+        * Let the platform mux to idle state in order to enable
+        * Pull-Up on GPIO
+        */
+       pinctrl_pm_select_idle_state(&vf50_ts->pdev->dev);
+
+       /* Wait for the pull-up to be stable on high */
+       usleep_range(COLI_PULLUP_MIN_DELAY_US, COLI_PULLUP_MAX_DELAY_US);
+}
+
+/*
+ * ADC touch screen sampling bottom half irq handler
+ */
+static irqreturn_t vf50_ts_irq_bh(int irq, void *private)
+{
+       struct vf50_touch_device *vf50_ts = private;
+       struct device *dev = &vf50_ts->pdev->dev;
+       int val_x, val_y, val_z1, val_z2, val_p = 0;
+       bool discard_val_on_start = true;
+
+       /* Disable the touch detection plates */
+       gpiod_set_value(vf50_ts->gpio_ym, 0);
+
+       /* Let the platform mux to default state in order to mux as ADC */
+       pinctrl_pm_select_default_state(dev);
+
+       while (!vf50_ts->stop_touchscreen) {
+               /* X-Direction */
+               val_x = adc_ts_measure(&vf50_ts->channels[0],
+                               vf50_ts->gpio_xp, vf50_ts->gpio_xm);
+               if (val_x < 0)
+                       break;
+
+               /* Y-Direction */
+               val_y = adc_ts_measure(&vf50_ts->channels[1],
+                               vf50_ts->gpio_yp, vf50_ts->gpio_ym);
+               if (val_y < 0)
+                       break;
+
+               /*
+                * Touch pressure
+                * Measure on XP/YM
+                */
+               val_z1 = adc_ts_measure(&vf50_ts->channels[2],
+                               vf50_ts->gpio_yp, vf50_ts->gpio_xm);
+               if (val_z1 < 0)
+                       break;
+               val_z2 = adc_ts_measure(&vf50_ts->channels[3],
+                               vf50_ts->gpio_yp, vf50_ts->gpio_xm);
+               if (val_z2 < 0)
+                       break;
+
+               /* Validate signal (avoid calculation using noise) */
+               if (val_z1 > 64 && val_x > 64) {
+                       /*
+                        * Calculate resistance between the plates
+                        * lower resistance means higher pressure
+                        */
+                       int r_x = (1000 * val_x) / VF_ADC_MAX;
+
+                       val_p = (r_x * val_z2) / val_z1 - r_x;
+
+               } else {
+                       val_p = 2000;
+               }
+
+               val_p = 2000 - val_p;
+               dev_dbg(dev,
+                       "Measured values: x: %d, y: %d, z1: %d, z2: %d, p: %d\n",
+                       val_x, val_y, val_z1, val_z2, val_p);
+
+               /*
+                * If touch pressure is too low, stop measuring and reenable
+                * touch detection
+                */
+               if (val_p < vf50_ts->min_pressure || val_p > 2000)
+                       break;
+
+               /*
+                * The pressure may not be enough for the first x and the
+                * second y measurement, but, the pressure is ok when the
+                * driver is doing the third and fourth measurement. To
+                * take care of this, we drop the first measurement always.
+                */
+               if (discard_val_on_start) {
+                       discard_val_on_start = false;
+               } else {
+                       /*
+                        * Report touch position and sleep for
+                        * the next measurement.
+                        */
+                       input_report_abs(vf50_ts->ts_input,
+                                       ABS_X, VF_ADC_MAX - val_x);
+                       input_report_abs(vf50_ts->ts_input,
+                                       ABS_Y, VF_ADC_MAX - val_y);
+                       input_report_abs(vf50_ts->ts_input,
+                                       ABS_PRESSURE, val_p);
+                       input_report_key(vf50_ts->ts_input, BTN_TOUCH, 1);
+                       input_sync(vf50_ts->ts_input);
+               }
+
+               usleep_range(COLI_PULLUP_MIN_DELAY_US,
+                            COLI_PULLUP_MAX_DELAY_US);
+       }
+
+       /* Report no more touch, re-enable touch detection */
+       input_report_abs(vf50_ts->ts_input, ABS_PRESSURE, 0);
+       input_report_key(vf50_ts->ts_input, BTN_TOUCH, 0);
+       input_sync(vf50_ts->ts_input);
+
+       vf50_ts_enable_touch_detection(vf50_ts);
+
+       return IRQ_HANDLED;
+}
+
+static int vf50_ts_open(struct input_dev *dev_input)
+{
+       struct vf50_touch_device *touchdev = input_get_drvdata(dev_input);
+       struct device *dev = &touchdev->pdev->dev;
+
+       dev_dbg(dev, "Input device %s opened, starting touch detection\n",
+               dev_input->name);
+
+       touchdev->stop_touchscreen = false;
+
+       /* Mux detection before request IRQ, wait for pull-up to settle */
+       vf50_ts_enable_touch_detection(touchdev);
+
+       return 0;
+}
+
+static void vf50_ts_close(struct input_dev *dev_input)
+{
+       struct vf50_touch_device *touchdev = input_get_drvdata(dev_input);
+       struct device *dev = &touchdev->pdev->dev;
+
+       touchdev->stop_touchscreen = true;
+
+       /* Make sure IRQ is not running past close */
+       mb();
+       synchronize_irq(touchdev->pen_irq);
+
+       gpiod_set_value(touchdev->gpio_ym, 0);
+       pinctrl_pm_select_default_state(dev);
+
+       dev_dbg(dev, "Input device %s closed, disable touch detection\n",
+               dev_input->name);
+}
+
+static int vf50_ts_get_gpiod(struct device *dev, struct gpio_desc **gpio_d,
+                            const char *con_id, enum gpiod_flags flags)
+{
+       int error;
+
+       *gpio_d = devm_gpiod_get(dev, con_id, flags);
+       if (IS_ERR(*gpio_d)) {
+               error = PTR_ERR(*gpio_d);
+               dev_err(dev, "Could not get gpio_%s %d\n", con_id, error);
+               return error;
+       }
+
+       return 0;
+}
+
+static void vf50_ts_channel_release(void *data)
+{
+       struct iio_channel *channels = data;
+
+       iio_channel_release_all(channels);
+}
+
+static int vf50_ts_probe(struct platform_device *pdev)
+{
+       struct input_dev *input;
+       struct iio_channel *channels;
+       struct device *dev = &pdev->dev;
+       struct vf50_touch_device *touchdev;
+       int num_adc_channels;
+       int error;
+
+       channels = iio_channel_get_all(dev);
+       if (IS_ERR(channels))
+               return PTR_ERR(channels);
+
+       error = devm_add_action(dev, vf50_ts_channel_release, channels);
+       if (error) {
+               iio_channel_release_all(channels);
+               dev_err(dev, "Failed to register iio channel release action");
+               return error;
+       }
+
+       num_adc_channels = 0;
+       while (channels[num_adc_channels].indio_dev)
+               num_adc_channels++;
+
+       if (num_adc_channels != COLI_TOUCH_REQ_ADC_CHAN) {
+               dev_err(dev, "Inadequate ADC channels specified\n");
+               return -EINVAL;
+       }
+
+       touchdev = devm_kzalloc(dev, sizeof(*touchdev), GFP_KERNEL);
+       if (!touchdev)
+               return -ENOMEM;
+
+       touchdev->pdev = pdev;
+       touchdev->channels = channels;
+
+       error = of_property_read_u32(dev->of_node, "vf50-ts-min-pressure",
+                                &touchdev->min_pressure);
+       if (error)
+               return error;
+
+       input = devm_input_allocate_device(dev);
+       if (!input) {
+               dev_err(dev, "Failed to allocate TS input device\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(pdev, touchdev);
+
+       input->name = DRIVER_NAME;
+       input->id.bustype = BUS_HOST;
+       input->dev.parent = dev;
+       input->open = vf50_ts_open;
+       input->close = vf50_ts_close;
+
+       input_set_capability(input, EV_KEY, BTN_TOUCH);
+       input_set_abs_params(input, ABS_X, 0, VF_ADC_MAX, 0, 0);
+       input_set_abs_params(input, ABS_Y, 0, VF_ADC_MAX, 0, 0);
+       input_set_abs_params(input, ABS_PRESSURE, 0, VF_ADC_MAX, 0, 0);
+
+       touchdev->ts_input = input;
+       input_set_drvdata(input, touchdev);
+
+       error = input_register_device(input);
+       if (error) {
+               dev_err(dev, "Failed to register input device\n");
+               return error;
+       }
+
+       error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xp, "xp", GPIOD_OUT_LOW);
+       if (error)
+               return error;
+
+       error = vf50_ts_get_gpiod(dev, &touchdev->gpio_xm,
+                               "xm", GPIOD_OUT_LOW);
+       if (error)
+               return error;
+
+       error = vf50_ts_get_gpiod(dev, &touchdev->gpio_yp, "yp", GPIOD_OUT_LOW);
+       if (error)
+               return error;
+
+       error = vf50_ts_get_gpiod(dev, &touchdev->gpio_ym, "ym", GPIOD_OUT_LOW);
+       if (error)
+               return error;
+
+       touchdev->pen_irq = platform_get_irq(pdev, 0);
+       if (touchdev->pen_irq < 0)
+               return touchdev->pen_irq;
+
+       error = devm_request_threaded_irq(dev, touchdev->pen_irq,
+                                         NULL, vf50_ts_irq_bh, IRQF_ONESHOT,
+                                         "vf50 touch", touchdev);
+       if (error) {
+               dev_err(dev, "Failed to request IRQ %d: %d\n",
+                       touchdev->pen_irq, error);
+               return error;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id vf50_touch_of_match[] = {
+       { .compatible = "toradex,vf50-touchscreen", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, vf50_touch_of_match);
+
+static struct platform_driver vf50_touch_driver = {
+       .driver = {
+               .name = "toradex,vf50_touchctrl",
+               .of_match_table = vf50_touch_of_match,
+       },
+       .probe = vf50_ts_probe,
+};
+module_platform_driver(vf50_touch_driver);
+
+MODULE_AUTHOR("Sanchayan Maity");
+MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
index 9a323dd..a9f95c7 100644 (file)
@@ -86,4 +86,3 @@ module_i2c_driver(cyttsp4_i2c_driver);
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
 MODULE_AUTHOR("Cypress");
-MODULE_ALIAS("i2c:cyttsp4");
index 519e2de..eee51b3 100644 (file)
@@ -86,4 +86,3 @@ module_i2c_driver(cyttsp_i2c_driver);
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
 MODULE_AUTHOR("Cypress");
-MODULE_ALIAS("i2c:cyttsp");
index ddac134..17cc20e 100644 (file)
 #define ELAN_FW_PAGESIZE       132
 
 /* calibration timeout definition */
-#define ELAN_CALI_TIMEOUT_MSEC 10000
+#define ELAN_CALI_TIMEOUT_MSEC 12000
 
 #define ELAN_POWERON_DELAY_USEC        500
 #define ELAN_RESET_DELAY_MSEC  20
diff --git a/drivers/input/touchscreen/imx6ul_tsc.c b/drivers/input/touchscreen/imx6ul_tsc.c
new file mode 100644 (file)
index 0000000..ff0b758
--- /dev/null
@@ -0,0 +1,523 @@
+/*
+ * Freescale i.MX6UL touchscreen controller driver
+ *
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ *
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+/* ADC configuration registers field define */
+#define ADC_AIEN               (0x1 << 7)
+#define ADC_CONV_DISABLE       0x1F
+#define ADC_CAL                        (0x1 << 7)
+#define ADC_CALF               0x2
+#define ADC_12BIT_MODE         (0x2 << 2)
+#define ADC_IPG_CLK            0x00
+#define ADC_CLK_DIV_8          (0x03 << 5)
+#define ADC_SHORT_SAMPLE_MODE  (0x0 << 4)
+#define ADC_HARDWARE_TRIGGER   (0x1 << 13)
+#define SELECT_CHANNEL_4       0x04
+#define SELECT_CHANNEL_1       0x01
+#define DISABLE_CONVERSION_INT (0x0 << 7)
+
+/* ADC registers */
+#define REG_ADC_HC0            0x00
+#define REG_ADC_HC1            0x04
+#define REG_ADC_HC2            0x08
+#define REG_ADC_HC3            0x0C
+#define REG_ADC_HC4            0x10
+#define REG_ADC_HS             0x14
+#define REG_ADC_R0             0x18
+#define REG_ADC_CFG            0x2C
+#define REG_ADC_GC             0x30
+#define REG_ADC_GS             0x34
+
+#define ADC_TIMEOUT            msecs_to_jiffies(100)
+
+/* TSC registers */
+#define REG_TSC_BASIC_SETING   0x00
+#define REG_TSC_PRE_CHARGE_TIME        0x10
+#define REG_TSC_FLOW_CONTROL   0x20
+#define REG_TSC_MEASURE_VALUE  0x30
+#define REG_TSC_INT_EN         0x40
+#define REG_TSC_INT_SIG_EN     0x50
+#define REG_TSC_INT_STATUS     0x60
+#define REG_TSC_DEBUG_MODE     0x70
+#define REG_TSC_DEBUG_MODE2    0x80
+
+/* TSC configuration registers field define */
+#define DETECT_4_WIRE_MODE     (0x0 << 4)
+#define AUTO_MEASURE           0x1
+#define MEASURE_SIGNAL         0x1
+#define DETECT_SIGNAL          (0x1 << 4)
+#define VALID_SIGNAL           (0x1 << 8)
+#define MEASURE_INT_EN         0x1
+#define MEASURE_SIG_EN         0x1
+#define VALID_SIG_EN           (0x1 << 8)
+#define DE_GLITCH_2            (0x2 << 29)
+#define START_SENSE            (0x1 << 12)
+#define TSC_DISABLE            (0x1 << 16)
+#define DETECT_MODE            0x2
+
+struct imx6ul_tsc {
+       struct device *dev;
+       struct input_dev *input;
+       void __iomem *tsc_regs;
+       void __iomem *adc_regs;
+       struct clk *tsc_clk;
+       struct clk *adc_clk;
+       struct gpio_desc *xnur_gpio;
+
+       int measure_delay_time;
+       int pre_charge_time;
+
+       struct completion completion;
+};
+
+/*
+ * TSC module need ADC to get the measure value. So
+ * before config TSC, we should initialize ADC module.
+ */
+static void imx6ul_adc_init(struct imx6ul_tsc *tsc)
+{
+       int adc_hc = 0;
+       int adc_gc;
+       int adc_gs;
+       int adc_cfg;
+       int timeout;
+
+       reinit_completion(&tsc->completion);
+
+       adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
+       adc_cfg |= ADC_12BIT_MODE | ADC_IPG_CLK;
+       adc_cfg |= ADC_CLK_DIV_8 | ADC_SHORT_SAMPLE_MODE;
+       adc_cfg &= ~ADC_HARDWARE_TRIGGER;
+       writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);
+
+       /* enable calibration interrupt */
+       adc_hc |= ADC_AIEN;
+       adc_hc |= ADC_CONV_DISABLE;
+       writel(adc_hc, tsc->adc_regs + REG_ADC_HC0);
+
+       /* start ADC calibration */
+       adc_gc = readl(tsc->adc_regs + REG_ADC_GC);
+       adc_gc |= ADC_CAL;
+       writel(adc_gc, tsc->adc_regs + REG_ADC_GC);
+
+       timeout = wait_for_completion_timeout
+                       (&tsc->completion, ADC_TIMEOUT);
+       if (timeout == 0)
+               dev_err(tsc->dev, "Timeout for adc calibration\n");
+
+       adc_gs = readl(tsc->adc_regs + REG_ADC_GS);
+       if (adc_gs & ADC_CALF)
+               dev_err(tsc->dev, "ADC calibration failed\n");
+
+       /* TSC need the ADC work in hardware trigger */
+       adc_cfg = readl(tsc->adc_regs + REG_ADC_CFG);
+       adc_cfg |= ADC_HARDWARE_TRIGGER;
+       writel(adc_cfg, tsc->adc_regs + REG_ADC_CFG);
+}
+
+/*
+ * This is a TSC workaround. Currently TSC misconnect two
+ * ADC channels, this function remap channel configure for
+ * hardware trigger.
+ */
+static void imx6ul_tsc_channel_config(struct imx6ul_tsc *tsc)
+{
+       int adc_hc0, adc_hc1, adc_hc2, adc_hc3, adc_hc4;
+
+       adc_hc0 = DISABLE_CONVERSION_INT;
+       writel(adc_hc0, tsc->adc_regs + REG_ADC_HC0);
+
+       adc_hc1 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_4;
+       writel(adc_hc1, tsc->adc_regs + REG_ADC_HC1);
+
+       adc_hc2 = DISABLE_CONVERSION_INT;
+       writel(adc_hc2, tsc->adc_regs + REG_ADC_HC2);
+
+       adc_hc3 = DISABLE_CONVERSION_INT | SELECT_CHANNEL_1;
+       writel(adc_hc3, tsc->adc_regs + REG_ADC_HC3);
+
+       adc_hc4 = DISABLE_CONVERSION_INT;
+       writel(adc_hc4, tsc->adc_regs + REG_ADC_HC4);
+}
+
+/*
+ * TSC setting, confige the pre-charge time and measure delay time.
+ * different touch screen may need different pre-charge time and
+ * measure delay time.
+ */
+static void imx6ul_tsc_set(struct imx6ul_tsc *tsc)
+{
+       int basic_setting = 0;
+       int start;
+
+       basic_setting |= tsc->measure_delay_time << 8;
+       basic_setting |= DETECT_4_WIRE_MODE | AUTO_MEASURE;
+       writel(basic_setting, tsc->tsc_regs + REG_TSC_BASIC_SETING);
+
+       writel(DE_GLITCH_2, tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
+
+       writel(tsc->pre_charge_time, tsc->tsc_regs + REG_TSC_PRE_CHARGE_TIME);
+       writel(MEASURE_INT_EN, tsc->tsc_regs + REG_TSC_INT_EN);
+       writel(MEASURE_SIG_EN | VALID_SIG_EN,
+               tsc->tsc_regs + REG_TSC_INT_SIG_EN);
+
+       /* start sense detection */
+       start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+       start |= START_SENSE;
+       start &= ~TSC_DISABLE;
+       writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+}
+
+static void imx6ul_tsc_init(struct imx6ul_tsc *tsc)
+{
+       imx6ul_adc_init(tsc);
+       imx6ul_tsc_channel_config(tsc);
+       imx6ul_tsc_set(tsc);
+}
+
+static void imx6ul_tsc_disable(struct imx6ul_tsc *tsc)
+{
+       int tsc_flow;
+       int adc_cfg;
+
+       /* TSC controller enters to idle status */
+       tsc_flow = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+       tsc_flow |= TSC_DISABLE;
+       writel(tsc_flow, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+
+       /* ADC controller enters to stop mode */
+       adc_cfg = readl(tsc->adc_regs + REG_ADC_HC0);
+       adc_cfg |= ADC_CONV_DISABLE;
+       writel(adc_cfg, tsc->adc_regs + REG_ADC_HC0);
+}
+
+/* Delay some time (max 2ms), wait the pre-charge done. */
+static bool tsc_wait_detect_mode(struct imx6ul_tsc *tsc)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(2);
+       int state_machine;
+       int debug_mode2;
+
+       do {
+               if (time_after(jiffies, timeout))
+                       return false;
+
+               usleep_range(200, 400);
+               debug_mode2 = readl(tsc->tsc_regs + REG_TSC_DEBUG_MODE2);
+               state_machine = (debug_mode2 >> 20) & 0x7;
+       } while (state_machine != DETECT_MODE);
+
+       usleep_range(200, 400);
+       return true;
+}
+
+static irqreturn_t tsc_irq_fn(int irq, void *dev_id)
+{
+       struct imx6ul_tsc *tsc = dev_id;
+       int status;
+       int value;
+       int x, y;
+       int start;
+
+       status = readl(tsc->tsc_regs + REG_TSC_INT_STATUS);
+
+       /* write 1 to clear the bit measure-signal */
+       writel(MEASURE_SIGNAL | DETECT_SIGNAL,
+               tsc->tsc_regs + REG_TSC_INT_STATUS);
+
+       /* It's a HW self-clean bit. Set this bit and start sense detection */
+       start = readl(tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+       start |= START_SENSE;
+       writel(start, tsc->tsc_regs + REG_TSC_FLOW_CONTROL);
+
+       if (status & MEASURE_SIGNAL) {
+               value = readl(tsc->tsc_regs + REG_TSC_MEASURE_VALUE);
+               x = (value >> 16) & 0x0fff;
+               y = value & 0x0fff;
+
+               /*
+                * In detect mode, we can get the xnur gpio value,
+                * otherwise assume contact is stiull active.
+                */
+               if (!tsc_wait_detect_mode(tsc) ||
+                   gpiod_get_value_cansleep(tsc->xnur_gpio)) {
+                       input_report_key(tsc->input, BTN_TOUCH, 1);
+                       input_report_abs(tsc->input, ABS_X, x);
+                       input_report_abs(tsc->input, ABS_Y, y);
+               } else {
+                       input_report_key(tsc->input, BTN_TOUCH, 0);
+               }
+
+               input_sync(tsc->input);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t adc_irq_fn(int irq, void *dev_id)
+{
+       struct imx6ul_tsc *tsc = dev_id;
+       int coco;
+       int value;
+
+       coco = readl(tsc->adc_regs + REG_ADC_HS);
+       if (coco & 0x01) {
+               value = readl(tsc->adc_regs + REG_ADC_R0);
+               complete(&tsc->completion);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int imx6ul_tsc_open(struct input_dev *input_dev)
+{
+       struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
+       int err;
+
+       err = clk_prepare_enable(tsc->adc_clk);
+       if (err) {
+               dev_err(tsc->dev,
+                       "Could not prepare or enable the adc clock: %d\n",
+                       err);
+               return err;
+       }
+
+       err = clk_prepare_enable(tsc->tsc_clk);
+       if (err) {
+               dev_err(tsc->dev,
+                       "Could not prepare or enable the tsc clock: %d\n",
+                       err);
+               clk_disable_unprepare(tsc->adc_clk);
+               return err;
+       }
+
+       imx6ul_tsc_init(tsc);
+
+       return 0;
+}
+
+static void imx6ul_tsc_close(struct input_dev *input_dev)
+{
+       struct imx6ul_tsc *tsc = input_get_drvdata(input_dev);
+
+       imx6ul_tsc_disable(tsc);
+
+       clk_disable_unprepare(tsc->tsc_clk);
+       clk_disable_unprepare(tsc->adc_clk);
+}
+
+static int imx6ul_tsc_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct imx6ul_tsc *tsc;
+       struct input_dev *input_dev;
+       struct resource *tsc_mem;
+       struct resource *adc_mem;
+       int err;
+       int tsc_irq;
+       int adc_irq;
+
+       tsc = devm_kzalloc(&pdev->dev, sizeof(struct imx6ul_tsc), GFP_KERNEL);
+       if (!tsc)
+               return -ENOMEM;
+
+       input_dev = devm_input_allocate_device(&pdev->dev);
+       if (!input_dev)
+               return -ENOMEM;
+
+       input_dev->name = "iMX6UL TouchScreen Controller";
+       input_dev->id.bustype = BUS_HOST;
+
+       input_dev->open = imx6ul_tsc_open;
+       input_dev->close = imx6ul_tsc_close;
+
+       input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+       input_set_abs_params(input_dev, ABS_X, 0, 0xFFF, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y, 0, 0xFFF, 0, 0);
+
+       input_set_drvdata(input_dev, tsc);
+
+       tsc->dev = &pdev->dev;
+       tsc->input = input_dev;
+       init_completion(&tsc->completion);
+
+       tsc->xnur_gpio = devm_gpiod_get(&pdev->dev, "xnur", GPIOD_IN);
+       if (IS_ERR(tsc->xnur_gpio)) {
+               err = PTR_ERR(tsc->xnur_gpio);
+               dev_err(&pdev->dev,
+                       "failed to request GPIO tsc_X- (xnur): %d\n", err);
+               return err;
+       }
+
+       tsc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       tsc->tsc_regs = devm_ioremap_resource(&pdev->dev, tsc_mem);
+       if (IS_ERR(tsc->tsc_regs)) {
+               err = PTR_ERR(tsc->tsc_regs);
+               dev_err(&pdev->dev, "failed to remap tsc memory: %d\n", err);
+               return err;
+       }
+
+       adc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       tsc->adc_regs = devm_ioremap_resource(&pdev->dev, adc_mem);
+       if (IS_ERR(tsc->adc_regs)) {
+               err = PTR_ERR(tsc->adc_regs);
+               dev_err(&pdev->dev, "failed to remap adc memory: %d\n", err);
+               return err;
+       }
+
+       tsc->tsc_clk = devm_clk_get(&pdev->dev, "tsc");
+       if (IS_ERR(tsc->tsc_clk)) {
+               err = PTR_ERR(tsc->tsc_clk);
+               dev_err(&pdev->dev, "failed getting tsc clock: %d\n", err);
+               return err;
+       }
+
+       tsc->adc_clk = devm_clk_get(&pdev->dev, "adc");
+       if (IS_ERR(tsc->adc_clk)) {
+               err = PTR_ERR(tsc->adc_clk);
+               dev_err(&pdev->dev, "failed getting adc clock: %d\n", err);
+               return err;
+       }
+
+       tsc_irq = platform_get_irq(pdev, 0);
+       if (tsc_irq < 0) {
+               dev_err(&pdev->dev, "no tsc irq resource?\n");
+               return tsc_irq;
+       }
+
+       adc_irq = platform_get_irq(pdev, 1);
+       if (adc_irq <= 0) {
+               dev_err(&pdev->dev, "no adc irq resource?\n");
+               return adc_irq;
+       }
+
+       err = devm_request_threaded_irq(tsc->dev, tsc_irq,
+                                       NULL, tsc_irq_fn, IRQF_ONESHOT,
+                                       dev_name(&pdev->dev), tsc);
+       if (err) {
+               dev_err(&pdev->dev,
+                       "failed requesting tsc irq %d: %d\n",
+                       tsc_irq, err);
+               return err;
+       }
+
+       err = devm_request_irq(tsc->dev, adc_irq, adc_irq_fn, 0,
+                               dev_name(&pdev->dev), tsc);
+       if (err) {
+               dev_err(&pdev->dev,
+                       "failed requesting adc irq %d: %d\n",
+                       adc_irq, err);
+               return err;
+       }
+
+       err = of_property_read_u32(np, "measure-delay-time",
+                                  &tsc->measure_delay_time);
+       if (err)
+               tsc->measure_delay_time = 0xffff;
+
+       err = of_property_read_u32(np, "pre-charge-time",
+                                  &tsc->pre_charge_time);
+       if (err)
+               tsc->pre_charge_time = 0xfff;
+
+       err = input_register_device(tsc->input);
+       if (err) {
+               dev_err(&pdev->dev,
+                       "failed to register input device: %d\n", err);
+               return err;
+       }
+
+       platform_set_drvdata(pdev, tsc);
+       return 0;
+}
+
+static int __maybe_unused imx6ul_tsc_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct imx6ul_tsc *tsc = platform_get_drvdata(pdev);
+       struct input_dev *input_dev = tsc->input;
+
+       mutex_lock(&input_dev->mutex);
+
+       if (input_dev->users) {
+               imx6ul_tsc_disable(tsc);
+
+               clk_disable_unprepare(tsc->tsc_clk);
+               clk_disable_unprepare(tsc->adc_clk);
+       }
+
+       mutex_unlock(&input_dev->mutex);
+
+       return 0;
+}
+
+static int __maybe_unused imx6ul_tsc_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct imx6ul_tsc *tsc = platform_get_drvdata(pdev);
+       struct input_dev *input_dev = tsc->input;
+       int retval = 0;
+
+       mutex_lock(&input_dev->mutex);
+
+       if (input_dev->users) {
+               retval = clk_prepare_enable(tsc->adc_clk);
+               if (retval)
+                       goto out;
+
+               retval = clk_prepare_enable(tsc->tsc_clk);
+               if (retval) {
+                       clk_disable_unprepare(tsc->adc_clk);
+                       goto out;
+               }
+
+               imx6ul_tsc_init(tsc);
+       }
+
+out:
+       mutex_unlock(&input_dev->mutex);
+       return retval;
+}
+
+static SIMPLE_DEV_PM_OPS(imx6ul_tsc_pm_ops,
+                        imx6ul_tsc_suspend, imx6ul_tsc_resume);
+
+static const struct of_device_id imx6ul_tsc_match[] = {
+       { .compatible = "fsl,imx6ul-tsc", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx6ul_tsc_match);
+
+static struct platform_driver imx6ul_tsc_driver = {
+       .driver         = {
+               .name   = "imx6ul-tsc",
+               .of_match_table = imx6ul_tsc_match,
+               .pm     = &imx6ul_tsc_pm_ops,
+       },
+       .probe          = imx6ul_tsc_probe,
+};
+module_platform_driver(imx6ul_tsc_driver);
+
+MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
+MODULE_DESCRIPTION("Freescale i.MX6UL Touchscreen controller driver");
+MODULE_LICENSE("GPL v2");
index c011699..4857943 100644 (file)
@@ -191,7 +191,7 @@ static void sun4i_ts_close(struct input_dev *dev)
        writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC);
 }
 
-static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
+static int sun4i_get_temp(const struct sun4i_ts_data *ts, int *temp)
 {
        /* No temp_data until the first irq */
        if (ts->temp_data == -1)
@@ -202,7 +202,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
        return 0;
 }
 
-static int sun4i_get_tz_temp(void *data, long *temp)
+static int sun4i_get_tz_temp(void *data, int *temp)
 {
        return sun4i_get_temp(data, temp);
 }
@@ -215,14 +215,14 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
                         char *buf)
 {
        struct sun4i_ts_data *ts = dev_get_drvdata(dev);
-       long temp;
+       int temp;
        int error;
 
        error = sun4i_get_temp(ts, &temp);
        if (error)
                return error;
 
-       return sprintf(buf, "%ld\n", temp);
+       return sprintf(buf, "%d\n", temp);
 }
 
 static ssize_t show_temp_label(struct device *dev,
index 0717aa9..9bc20e2 100644 (file)
@@ -135,8 +135,9 @@ __dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num)
 static ssize_t iotlb_dump_cr(struct omap_iommu *obj, struct cr_regs *cr,
                             struct seq_file *s)
 {
-       return seq_printf(s, "%08x %08x %01x\n", cr->cam, cr->ram,
+       seq_printf(s, "%08x %08x %01x\n", cr->cam, cr->ram,
                          (cr->cam & MMU_CAM_P) ? 1 : 0);
+       return 0;
 }
 
 static size_t omap_dump_tlb_entries(struct omap_iommu *obj, struct seq_file *s)
index d5415ee..3e01e6f 100644 (file)
@@ -393,7 +393,7 @@ config DM_MULTIPATH
        # of SCSI_DH if the latter isn't defined but if
        # it is, DM_MULTIPATH must depend on it.  We get a build
        # error if SCSI_DH=m and DM_MULTIPATH=y
-       depends on SCSI_DH || !SCSI_DH
+       depends on !SCSI_DH || SCSI
        ---help---
          Allow volume managers to support multipath hardware.
 
index eff7bdd..5a67671 100644 (file)
@@ -159,12 +159,9 @@ static struct priority_group *alloc_priority_group(void)
 static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
 {
        struct pgpath *pgpath, *tmp;
-       struct multipath *m = ti->private;
 
        list_for_each_entry_safe(pgpath, tmp, pgpaths, list) {
                list_del(&pgpath->list);
-               if (m->hw_handler_name)
-                       scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev));
                dm_put_device(ti, pgpath->path.dev);
                free_pgpath(pgpath);
        }
@@ -580,6 +577,7 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
                q = bdev_get_queue(p->path.dev->bdev);
 
        if (m->retain_attached_hw_handler) {
+retain:
                attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL);
                if (attached_handler_name) {
                        /*
@@ -599,20 +597,14 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
        }
 
        if (m->hw_handler_name) {
-               /*
-                * Increments scsi_dh reference, even when using an
-                * already-attached handler.
-                */
                r = scsi_dh_attach(q, m->hw_handler_name);
                if (r == -EBUSY) {
-                       /*
-                        * Already attached to different hw_handler:
-                        * try to reattach with correct one.
-                        */
-                       scsi_dh_detach(q);
-                       r = scsi_dh_attach(q, m->hw_handler_name);
-               }
+                       char b[BDEVNAME_SIZE];
 
+                       printk(KERN_INFO "dm-mpath: retaining handler on device %s\n",
+                               bdevname(p->path.dev->bdev, b));
+                       goto retain;
+               }
                if (r < 0) {
                        ti->error = "error attaching hardware handler";
                        dm_put_device(ti, p->path.dev);
@@ -624,7 +616,6 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
                        if (r < 0) {
                                ti->error = "unable to set hardware "
                                                        "handler parameters";
-                               scsi_dh_detach(q);
                                dm_put_device(ti, p->path.dev);
                                goto bad;
                        }
@@ -734,12 +725,6 @@ static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m)
                return 0;
 
        m->hw_handler_name = kstrdup(dm_shift_arg(as), GFP_KERNEL);
-       if (!try_then_request_module(scsi_dh_handler_exist(m->hw_handler_name),
-                                    "scsi_dh_%s", m->hw_handler_name)) {
-               ti->error = "unknown hardware handler type";
-               ret = -EINVAL;
-               goto fail;
-       }
 
        if (hw_argc > 1) {
                char *p;
index dc2aaab..217d613 100644 (file)
@@ -10,6 +10,7 @@ config VIDEO_OMAP2_VOUT
        select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS
        select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
        select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB
+       select FRAME_VECTOR
        default n
        ---help---
          V4L2 Display driver support for OMAP2/3 based boards.
index de2474e..70c28d1 100644 (file)
@@ -195,46 +195,34 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix)
 }
 
 /*
- * omap_vout_uservirt_to_phys: This inline function is used to convert user
- * space virtual address to physical address.
+ * omap_vout_get_userptr: Convert user space virtual address to physical
+ * address.
  */
-static unsigned long omap_vout_uservirt_to_phys(unsigned long virtp)
+static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp,
+                                u32 *physp)
 {
-       unsigned long physp = 0;
-       struct vm_area_struct *vma;
-       struct mm_struct *mm = current->mm;
+       struct frame_vector *vec;
+       int ret;
 
        /* For kernel direct-mapped memory, take the easy way */
-       if (virtp >= PAGE_OFFSET)
-               return virt_to_phys((void *) virtp);
-
-       down_read(&current->mm->mmap_sem);
-       vma = find_vma(mm, virtp);
-       if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
-               /* this will catch, kernel-allocated, mmaped-to-usermode
-                  addresses */
-               physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
-               up_read(&current->mm->mmap_sem);
-       } else {
-               /* otherwise, use get_user_pages() for general userland pages */
-               int res, nr_pages = 1;
-               struct page *pages;
+       if (virtp >= PAGE_OFFSET) {
+               *physp = virt_to_phys((void *)virtp);
+               return 0;
+       }
 
-               res = get_user_pages(current, current->mm, virtp, nr_pages, 1,
-                               0, &pages, NULL);
-               up_read(&current->mm->mmap_sem);
+       vec = frame_vector_create(1);
+       if (!vec)
+               return -ENOMEM;
 
-               if (res == nr_pages) {
-                       physp =  __pa(page_address(&pages[0]) +
-                                       (virtp & ~PAGE_MASK));
-               } else {
-                       printk(KERN_WARNING VOUT_NAME
-                                       "get_user_pages failed\n");
-                       return 0;
-               }
+       ret = get_vaddr_frames(virtp, 1, true, false, vec);
+       if (ret != 1) {
+               frame_vector_destroy(vec);
+               return -EINVAL;
        }
+       *physp = __pfn_to_phys(frame_vector_pfns(vec)[0]);
+       vb->priv = vec;
 
-       return physp;
+       return 0;
 }
 
 /*
@@ -784,11 +772,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q,
         * address of the buffer
         */
        if (V4L2_MEMORY_USERPTR == vb->memory) {
+               int ret;
+
                if (0 == vb->baddr)
                        return -EINVAL;
                /* Physical address */
-               vout->queued_buf_addr[vb->i] = (u8 *)
-                       omap_vout_uservirt_to_phys(vb->baddr);
+               ret = omap_vout_get_userptr(vb, vb->baddr,
+                               (u32 *)&vout->queued_buf_addr[vb->i]);
+               if (ret < 0)
+                       return ret;
        } else {
                unsigned long addr, dma_addr;
                unsigned long size;
@@ -834,12 +826,13 @@ static void omap_vout_buffer_queue(struct videobuf_queue *q,
 static void omap_vout_buffer_release(struct videobuf_queue *q,
                            struct videobuf_buffer *vb)
 {
-       struct omap_vout_device *vout = q->priv_data;
-
        vb->state = VIDEOBUF_NEEDS_INIT;
+       if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) {
+               struct frame_vector *vec = vb->priv;
 
-       if (V4L2_MEMORY_MMAP != vout->memory)
-               return;
+               put_vaddr_frames(vec);
+               frame_vector_destroy(vec);
+       }
 }
 
 /*
index b4b0229..82876a6 100644 (file)
@@ -84,6 +84,7 @@ config VIDEOBUF2_CORE
 
 config VIDEOBUF2_MEMOPS
        tristate
+       select FRAME_VECTOR
 
 config VIDEOBUF2_DMA_CONTIG
        tristate
index f1022d8..4f59b7e 100644 (file)
@@ -1691,9 +1691,7 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                ret = __qbuf_mmap(vb, b);
                break;
        case V4L2_MEMORY_USERPTR:
-               down_read(&current->mm->mmap_sem);
                ret = __qbuf_userptr(vb, b);
-               up_read(&current->mm->mmap_sem);
                break;
        case V4L2_MEMORY_DMABUF:
                ret = __qbuf_dmabuf(vb, b);
index 94c1e64..2397ceb 100644 (file)
@@ -32,15 +32,13 @@ struct vb2_dc_buf {
        dma_addr_t                      dma_addr;
        enum dma_data_direction         dma_dir;
        struct sg_table                 *dma_sgt;
+       struct frame_vector             *vec;
 
        /* MMAP related */
        struct vb2_vmarea_handler       handler;
        atomic_t                        refcount;
        struct sg_table                 *sgt_base;
 
-       /* USERPTR related */
-       struct vm_area_struct           *vma;
-
        /* DMABUF related */
        struct dma_buf_attachment       *db_attach;
 };
@@ -49,24 +47,6 @@ struct vb2_dc_buf {
 /*        scatterlist table functions        */
 /*********************************************/
 
-
-static void vb2_dc_sgt_foreach_page(struct sg_table *sgt,
-       void (*cb)(struct page *pg))
-{
-       struct scatterlist *s;
-       unsigned int i;
-
-       for_each_sg(sgt->sgl, s, sgt->orig_nents, i) {
-               struct page *page = sg_page(s);
-               unsigned int n_pages = PAGE_ALIGN(s->offset + s->length)
-                       >> PAGE_SHIFT;
-               unsigned int j;
-
-               for (j = 0; j < n_pages; ++j, ++page)
-                       cb(page);
-       }
-}
-
 static unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt)
 {
        struct scatterlist *s;
@@ -429,92 +409,12 @@ static struct dma_buf *vb2_dc_get_dmabuf(void *buf_priv, unsigned long flags)
 /*       callbacks for USERPTR buffers       */
 /*********************************************/
 
-static inline int vma_is_io(struct vm_area_struct *vma)
-{
-       return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
-}
-
-static int vb2_dc_get_user_pfn(unsigned long start, int n_pages,
-       struct vm_area_struct *vma, unsigned long *res)
-{
-       unsigned long pfn, start_pfn, prev_pfn;
-       unsigned int i;
-       int ret;
-
-       if (!vma_is_io(vma))
-               return -EFAULT;
-
-       ret = follow_pfn(vma, start, &pfn);
-       if (ret)
-               return ret;
-
-       start_pfn = pfn;
-       start += PAGE_SIZE;
-
-       for (i = 1; i < n_pages; ++i, start += PAGE_SIZE) {
-               prev_pfn = pfn;
-               ret = follow_pfn(vma, start, &pfn);
-
-               if (ret) {
-                       pr_err("no page for address %lu\n", start);
-                       return ret;
-               }
-               if (pfn != prev_pfn + 1)
-                       return -EINVAL;
-       }
-
-       *res = start_pfn;
-       return 0;
-}
-
-static int vb2_dc_get_user_pages(unsigned long start, struct page **pages,
-       int n_pages, struct vm_area_struct *vma,
-       enum dma_data_direction dma_dir)
-{
-       if (vma_is_io(vma)) {
-               unsigned int i;
-
-               for (i = 0; i < n_pages; ++i, start += PAGE_SIZE) {
-                       unsigned long pfn;
-                       int ret = follow_pfn(vma, start, &pfn);
-
-                       if (!pfn_valid(pfn))
-                               return -EINVAL;
-
-                       if (ret) {
-                               pr_err("no page for address %lu\n", start);
-                               return ret;
-                       }
-                       pages[i] = pfn_to_page(pfn);
-               }
-       } else {
-               int n;
-
-               n = get_user_pages(current, current->mm, start & PAGE_MASK,
-                       n_pages, dma_dir == DMA_FROM_DEVICE, 1, pages, NULL);
-               /* negative error means that no page was pinned */
-               n = max(n, 0);
-               if (n != n_pages) {
-                       pr_err("got only %d of %d user pages\n", n, n_pages);
-                       while (n)
-                               put_page(pages[--n]);
-                       return -EFAULT;
-               }
-       }
-
-       return 0;
-}
-
-static void vb2_dc_put_dirty_page(struct page *page)
-{
-       set_page_dirty_lock(page);
-       put_page(page);
-}
-
 static void vb2_dc_put_userptr(void *buf_priv)
 {
        struct vb2_dc_buf *buf = buf_priv;
        struct sg_table *sgt = buf->dma_sgt;
+       int i;
+       struct page **pages;
 
        if (sgt) {
                DEFINE_DMA_ATTRS(attrs);
@@ -526,13 +426,15 @@ static void vb2_dc_put_userptr(void *buf_priv)
                 */
                dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
                                   buf->dma_dir, &attrs);
-               if (!vma_is_io(buf->vma))
-                       vb2_dc_sgt_foreach_page(sgt, vb2_dc_put_dirty_page);
-
+               pages = frame_vector_pages(buf->vec);
+               /* sgt should exist only if vector contains pages... */
+               BUG_ON(IS_ERR(pages));
+               for (i = 0; i < frame_vector_count(buf->vec); i++)
+                       set_page_dirty_lock(pages[i]);
                sg_free_table(sgt);
                kfree(sgt);
        }
-       vb2_put_vma(buf->vma);
+       vb2_destroy_framevec(buf->vec);
        kfree(buf);
 }
 
@@ -572,13 +474,10 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
 {
        struct vb2_dc_conf *conf = alloc_ctx;
        struct vb2_dc_buf *buf;
-       unsigned long start;
-       unsigned long end;
+       struct frame_vector *vec;
        unsigned long offset;
-       struct page **pages;
-       int n_pages;
+       int n_pages, i;
        int ret = 0;
-       struct vm_area_struct *vma;
        struct sg_table *sgt;
        unsigned long contig_size;
        unsigned long dma_align = dma_get_cache_alignment();
@@ -604,72 +503,43 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
        buf->dev = conf->dev;
        buf->dma_dir = dma_dir;
 
-       start = vaddr & PAGE_MASK;
        offset = vaddr & ~PAGE_MASK;
-       end = PAGE_ALIGN(vaddr + size);
-       n_pages = (end - start) >> PAGE_SHIFT;
-
-       pages = kmalloc(n_pages * sizeof(pages[0]), GFP_KERNEL);
-       if (!pages) {
-               ret = -ENOMEM;
-               pr_err("failed to allocate pages table\n");
+       vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE);
+       if (IS_ERR(vec)) {
+               ret = PTR_ERR(vec);
                goto fail_buf;
        }
+       buf->vec = vec;
+       n_pages = frame_vector_count(vec);
+       ret = frame_vector_to_pages(vec);
+       if (ret < 0) {
+               unsigned long *nums = frame_vector_pfns(vec);
 
-       /* current->mm->mmap_sem is taken by videobuf2 core */
-       vma = find_vma(current->mm, vaddr);
-       if (!vma) {
-               pr_err("no vma for address %lu\n", vaddr);
-               ret = -EFAULT;
-               goto fail_pages;
-       }
-
-       if (vma->vm_end < vaddr + size) {
-               pr_err("vma at %lu is too small for %lu bytes\n", vaddr, size);
-               ret = -EFAULT;
-               goto fail_pages;
-       }
-
-       buf->vma = vb2_get_vma(vma);
-       if (!buf->vma) {
-               pr_err("failed to copy vma\n");
-               ret = -ENOMEM;
-               goto fail_pages;
-       }
-
-       /* extract page list from userspace mapping */
-       ret = vb2_dc_get_user_pages(start, pages, n_pages, vma, dma_dir);
-       if (ret) {
-               unsigned long pfn;
-               if (vb2_dc_get_user_pfn(start, n_pages, vma, &pfn) == 0) {
-                       buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, pfn);
-                       buf->size = size;
-                       kfree(pages);
-                       return buf;
-               }
-
-               pr_err("failed to get user pages\n");
-               goto fail_vma;
+               /*
+                * Failed to convert to pages... Check the memory is physically
+                * contiguous and use direct mapping
+                */
+               for (i = 1; i < n_pages; i++)
+                       if (nums[i-1] + 1 != nums[i])
+                               goto fail_pfnvec;
+               buf->dma_addr = vb2_dc_pfn_to_dma(buf->dev, nums[0]);
+               goto out;
        }
 
        sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
        if (!sgt) {
                pr_err("failed to allocate sg table\n");
                ret = -ENOMEM;
-               goto fail_get_user_pages;
+               goto fail_pfnvec;
        }
 
-       ret = sg_alloc_table_from_pages(sgt, pages, n_pages,
+       ret = sg_alloc_table_from_pages(sgt, frame_vector_pages(vec), n_pages,
                offset, size, GFP_KERNEL);
        if (ret) {
                pr_err("failed to initialize sg table\n");
                goto fail_sgt;
        }
 
-       /* pages are no longer needed */
-       kfree(pages);
-       pages = NULL;
-
        /*
         * No need to sync to the device, this will happen later when the
         * prepare() memop is called.
@@ -691,8 +561,9 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
        }
 
        buf->dma_addr = sg_dma_address(sgt->sgl);
-       buf->size = size;
        buf->dma_sgt = sgt;
+out:
+       buf->size = size;
 
        return buf;
 
@@ -701,23 +572,13 @@ fail_map_sg:
                           buf->dma_dir, &attrs);
 
 fail_sgt_init:
-       if (!vma_is_io(buf->vma))
-               vb2_dc_sgt_foreach_page(sgt, put_page);
        sg_free_table(sgt);
 
 fail_sgt:
        kfree(sgt);
 
-fail_get_user_pages:
-       if (pages && !vma_is_io(buf->vma))
-               while (n_pages)
-                       put_page(pages[--n_pages]);
-
-fail_vma:
-       vb2_put_vma(buf->vma);
-
-fail_pages:
-       kfree(pages); /* kfree is NULL-proof */
+fail_pfnvec:
+       vb2_destroy_framevec(vec);
 
 fail_buf:
        kfree(buf);
index 7289b81..be7bd65 100644 (file)
@@ -38,6 +38,7 @@ struct vb2_dma_sg_buf {
        struct device                   *dev;
        void                            *vaddr;
        struct page                     **pages;
+       struct frame_vector             *vec;
        int                             offset;
        enum dma_data_direction         dma_dir;
        struct sg_table                 sg_table;
@@ -51,7 +52,6 @@ struct vb2_dma_sg_buf {
        unsigned int                    num_pages;
        atomic_t                        refcount;
        struct vb2_vmarea_handler       handler;
-       struct vm_area_struct           *vma;
 
        struct dma_buf_attachment       *db_attach;
 };
@@ -225,25 +225,17 @@ static void vb2_dma_sg_finish(void *buf_priv)
        dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir);
 }
 
-static inline int vma_is_io(struct vm_area_struct *vma)
-{
-       return !!(vma->vm_flags & (VM_IO | VM_PFNMAP));
-}
-
 static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
                                    unsigned long size,
                                    enum dma_data_direction dma_dir)
 {
        struct vb2_dma_sg_conf *conf = alloc_ctx;
        struct vb2_dma_sg_buf *buf;
-       unsigned long first, last;
-       int num_pages_from_user;
-       struct vm_area_struct *vma;
        struct sg_table *sgt;
        DEFINE_DMA_ATTRS(attrs);
+       struct frame_vector *vec;
 
        dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
-
        buf = kzalloc(sizeof *buf, GFP_KERNEL);
        if (!buf)
                return NULL;
@@ -254,61 +246,19 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
        buf->offset = vaddr & ~PAGE_MASK;
        buf->size = size;
        buf->dma_sgt = &buf->sg_table;
+       vec = vb2_create_framevec(vaddr, size, buf->dma_dir == DMA_FROM_DEVICE);
+       if (IS_ERR(vec))
+               goto userptr_fail_pfnvec;
+       buf->vec = vec;
 
-       first = (vaddr           & PAGE_MASK) >> PAGE_SHIFT;
-       last  = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT;
-       buf->num_pages = last - first + 1;
-
-       buf->pages = kzalloc(buf->num_pages * sizeof(struct page *),
-                            GFP_KERNEL);
-       if (!buf->pages)
-               goto userptr_fail_alloc_pages;
-
-       vma = find_vma(current->mm, vaddr);
-       if (!vma) {
-               dprintk(1, "no vma for address %lu\n", vaddr);
-               goto userptr_fail_find_vma;
-       }
-
-       if (vma->vm_end < vaddr + size) {
-               dprintk(1, "vma at %lu is too small for %lu bytes\n",
-                       vaddr, size);
-               goto userptr_fail_find_vma;
-       }
-
-       buf->vma = vb2_get_vma(vma);
-       if (!buf->vma) {
-               dprintk(1, "failed to copy vma\n");
-               goto userptr_fail_find_vma;
-       }
-
-       if (vma_is_io(buf->vma)) {
-               for (num_pages_from_user = 0;
-                    num_pages_from_user < buf->num_pages;
-                    ++num_pages_from_user, vaddr += PAGE_SIZE) {
-                       unsigned long pfn;
-
-                       if (follow_pfn(vma, vaddr, &pfn)) {
-                               dprintk(1, "no page for address %lu\n", vaddr);
-                               break;
-                       }
-                       buf->pages[num_pages_from_user] = pfn_to_page(pfn);
-               }
-       } else
-               num_pages_from_user = get_user_pages(current, current->mm,
-                                            vaddr & PAGE_MASK,
-                                            buf->num_pages,
-                                            buf->dma_dir == DMA_FROM_DEVICE,
-                                            1, /* force */
-                                            buf->pages,
-                                            NULL);
-
-       if (num_pages_from_user != buf->num_pages)
-               goto userptr_fail_get_user_pages;
+       buf->pages = frame_vector_pages(vec);
+       if (IS_ERR(buf->pages))
+               goto userptr_fail_sgtable;
+       buf->num_pages = frame_vector_count(vec);
 
        if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages,
                        buf->num_pages, buf->offset, size, 0))
-               goto userptr_fail_alloc_table_from_pages;
+               goto userptr_fail_sgtable;
 
        sgt = &buf->sg_table;
        /*
@@ -324,17 +274,9 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
 
 userptr_fail_map:
        sg_free_table(&buf->sg_table);
-userptr_fail_alloc_table_from_pages:
-userptr_fail_get_user_pages:
-       dprintk(1, "get_user_pages requested/got: %d/%d]\n",
-               buf->num_pages, num_pages_from_user);
-       if (!vma_is_io(buf->vma))
-               while (--num_pages_from_user >= 0)
-                       put_page(buf->pages[num_pages_from_user]);
-       vb2_put_vma(buf->vma);
-userptr_fail_find_vma:
-       kfree(buf->pages);
-userptr_fail_alloc_pages:
+userptr_fail_sgtable:
+       vb2_destroy_framevec(vec);
+userptr_fail_pfnvec:
        kfree(buf);
        return NULL;
 }
@@ -362,11 +304,8 @@ static void vb2_dma_sg_put_userptr(void *buf_priv)
        while (--i >= 0) {
                if (buf->dma_dir == DMA_FROM_DEVICE)
                        set_page_dirty_lock(buf->pages[i]);
-               if (!vma_is_io(buf->vma))
-                       put_page(buf->pages[i]);
        }
-       kfree(buf->pages);
-       vb2_put_vma(buf->vma);
+       vb2_destroy_framevec(buf->vec);
        kfree(buf);
 }
 
index 0d49b79..48c6a49 100644 (file)
 #include <media/videobuf2-memops.h>
 
 /**
- * vb2_get_vma() - acquire and lock the virtual memory area
- * @vma:       given virtual memory area
+ * vb2_create_framevec() - map virtual addresses to pfns
+ * @start:     Virtual user address where we start mapping
+ * @length:    Length of a range to map
+ * @write:     Should we map for writing into the area
  *
- * This function attempts to acquire an area mapped in the userspace for
- * the duration of a hardware operation. The area is "locked" by performing
- * the same set of operation that are done when process calls fork() and
- * memory areas are duplicated.
- *
- * Returns a copy of a virtual memory region on success or NULL.
- */
-struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma)
-{
-       struct vm_area_struct *vma_copy;
-
-       vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
-       if (vma_copy == NULL)
-               return NULL;
-
-       if (vma->vm_ops && vma->vm_ops->open)
-               vma->vm_ops->open(vma);
-
-       if (vma->vm_file)
-               get_file(vma->vm_file);
-
-       memcpy(vma_copy, vma, sizeof(*vma));
-
-       vma_copy->vm_mm = NULL;
-       vma_copy->vm_next = NULL;
-       vma_copy->vm_prev = NULL;
-
-       return vma_copy;
-}
-EXPORT_SYMBOL_GPL(vb2_get_vma);
-
-/**
- * vb2_put_userptr() - release a userspace virtual memory area
- * @vma:       virtual memory region associated with the area to be released
- *
- * This function releases the previously acquired memory area after a hardware
- * operation.
+ * This function allocates and fills in a vector with pfns corresponding to
+ * virtual address range passed in arguments. If pfns have corresponding pages,
+ * page references are also grabbed to pin pages in memory. The function
+ * returns pointer to the vector on success and error pointer in case of
+ * failure. Returned vector needs to be freed via vb2_destroy_pfnvec().
  */
-void vb2_put_vma(struct vm_area_struct *vma)
+struct frame_vector *vb2_create_framevec(unsigned long start,
+                                        unsigned long length,
+                                        bool write)
 {
-       if (!vma)
-               return;
-
-       if (vma->vm_ops && vma->vm_ops->close)
-               vma->vm_ops->close(vma);
-
-       if (vma->vm_file)
-               fput(vma->vm_file);
-
-       kfree(vma);
+       int ret;
+       unsigned long first, last;
+       unsigned long nr;
+       struct frame_vector *vec;
+
+       first = start >> PAGE_SHIFT;
+       last = (start + length - 1) >> PAGE_SHIFT;
+       nr = last - first + 1;
+       vec = frame_vector_create(nr);
+       if (!vec)
+               return ERR_PTR(-ENOMEM);
+       ret = get_vaddr_frames(start, nr, write, 1, vec);
+       if (ret < 0)
+               goto out_destroy;
+       /* We accept only complete set of PFNs */
+       if (ret != nr) {
+               ret = -EFAULT;
+               goto out_release;
+       }
+       return vec;
+out_release:
+       put_vaddr_frames(vec);
+out_destroy:
+       frame_vector_destroy(vec);
+       return ERR_PTR(ret);
 }
-EXPORT_SYMBOL_GPL(vb2_put_vma);
+EXPORT_SYMBOL(vb2_create_framevec);
 
 /**
- * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory
- * @vaddr:     starting virtual address of the area to be verified
- * @size:      size of the area
- * @res_paddr: will return physical address for the given vaddr
- * @res_vma:   will return locked copy of struct vm_area for the given area
- *
- * This function will go through memory area of size @size mapped at @vaddr and
- * verify that the underlying physical pages are contiguous. If they are
- * contiguous the virtual memory area is locked and a @res_vma is filled with
- * the copy and @res_pa set to the physical address of the buffer.
+ * vb2_destroy_framevec() - release vector of mapped pfns
+ * @vec:       vector of pfns / pages to release
  *
- * Returns 0 on success.
+ * This releases references to all pages in the vector @vec (if corresponding
+ * pfns are backed by pages) and frees the passed vector.
  */
-int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
-                          struct vm_area_struct **res_vma, dma_addr_t *res_pa)
+void vb2_destroy_framevec(struct frame_vector *vec)
 {
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma;
-       unsigned long offset, start, end;
-       unsigned long this_pfn, prev_pfn;
-       dma_addr_t pa = 0;
-
-       start = vaddr;
-       offset = start & ~PAGE_MASK;
-       end = start + size;
-
-       vma = find_vma(mm, start);
-
-       if (vma == NULL || vma->vm_end < end)
-               return -EFAULT;
-
-       for (prev_pfn = 0; start < end; start += PAGE_SIZE) {
-               int ret = follow_pfn(vma, start, &this_pfn);
-               if (ret)
-                       return ret;
-
-               if (prev_pfn == 0)
-                       pa = this_pfn << PAGE_SHIFT;
-               else if (this_pfn != prev_pfn + 1)
-                       return -EFAULT;
-
-               prev_pfn = this_pfn;
-       }
-
-       /*
-        * Memory is contiguous, lock vma and return to the caller
-        */
-       *res_vma = vb2_get_vma(vma);
-       if (*res_vma == NULL)
-               return -ENOMEM;
-
-       *res_pa = pa + offset;
-       return 0;
+       put_vaddr_frames(vec);
+       frame_vector_destroy(vec);
 }
-EXPORT_SYMBOL_GPL(vb2_get_contig_userptr);
+EXPORT_SYMBOL(vb2_destroy_framevec);
 
 /**
  * vb2_common_vm_open() - increase refcount of the vma
index 2fe4c27..ecb8f0c 100644 (file)
 
 struct vb2_vmalloc_buf {
        void                            *vaddr;
-       struct page                     **pages;
-       struct vm_area_struct           *vma;
+       struct frame_vector             *vec;
        enum dma_data_direction         dma_dir;
        unsigned long                   size;
-       unsigned int                    n_pages;
        atomic_t                        refcount;
        struct vb2_vmarea_handler       handler;
        struct dma_buf                  *dbuf;
@@ -76,10 +74,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
                                     enum dma_data_direction dma_dir)
 {
        struct vb2_vmalloc_buf *buf;
-       unsigned long first, last;
-       int n_pages, offset;
-       struct vm_area_struct *vma;
-       dma_addr_t physp;
+       struct frame_vector *vec;
+       int n_pages, offset, i;
 
        buf = kzalloc(sizeof(*buf), GFP_KERNEL);
        if (!buf)
@@ -88,51 +84,36 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
        buf->dma_dir = dma_dir;
        offset = vaddr & ~PAGE_MASK;
        buf->size = size;
-
-
-       vma = find_vma(current->mm, vaddr);
-       if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
-               if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
-                       goto fail_pages_array_alloc;
-               buf->vma = vma;
-               buf->vaddr = (__force void *)ioremap_nocache(physp, size);
-               if (!buf->vaddr)
-                       goto fail_pages_array_alloc;
+       vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE);
+       if (IS_ERR(vec))
+               goto fail_pfnvec_create;
+       buf->vec = vec;
+       n_pages = frame_vector_count(vec);
+       if (frame_vector_to_pages(vec) < 0) {
+               unsigned long *nums = frame_vector_pfns(vec);
+
+               /*
+                * We cannot get page pointers for these pfns. Check memory is
+                * physically contiguous and use direct mapping.
+                */
+               for (i = 1; i < n_pages; i++)
+                       if (nums[i-1] + 1 != nums[i])
+                               goto fail_map;
+               buf->vaddr = (__force void *)
+                               ioremap_nocache(nums[0] << PAGE_SHIFT, size);
        } else {
-               first = vaddr >> PAGE_SHIFT;
-               last  = (vaddr + size - 1) >> PAGE_SHIFT;
-               buf->n_pages = last - first + 1;
-               buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
-                                    GFP_KERNEL);
-               if (!buf->pages)
-                       goto fail_pages_array_alloc;
-
-               /* current->mm->mmap_sem is taken by videobuf2 core */
-               n_pages = get_user_pages(current, current->mm,
-                                        vaddr & PAGE_MASK, buf->n_pages,
-                                        dma_dir == DMA_FROM_DEVICE,
-                                        1, /* force */
-                                        buf->pages, NULL);
-               if (n_pages != buf->n_pages)
-                       goto fail_get_user_pages;
-
-               buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
+               buf->vaddr = vm_map_ram(frame_vector_pages(vec), n_pages, -1,
                                        PAGE_KERNEL);
-               if (!buf->vaddr)
-                       goto fail_get_user_pages;
        }
 
+       if (!buf->vaddr)
+               goto fail_map;
        buf->vaddr += offset;
        return buf;
 
-fail_get_user_pages:
-       pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
-                buf->n_pages);
-       while (--n_pages >= 0)
-               put_page(buf->pages[n_pages]);
-       kfree(buf->pages);
-
-fail_pages_array_alloc:
+fail_map:
+       vb2_destroy_framevec(vec);
+fail_pfnvec_create:
        kfree(buf);
 
        return NULL;
@@ -143,20 +124,21 @@ static void vb2_vmalloc_put_userptr(void *buf_priv)
        struct vb2_vmalloc_buf *buf = buf_priv;
        unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
        unsigned int i;
+       struct page **pages;
+       unsigned int n_pages;
 
-       if (buf->pages) {
+       if (!buf->vec->is_pfns) {
+               n_pages = frame_vector_count(buf->vec);
+               pages = frame_vector_pages(buf->vec);
                if (vaddr)
-                       vm_unmap_ram((void *)vaddr, buf->n_pages);
-               for (i = 0; i < buf->n_pages; ++i) {
-                       if (buf->dma_dir == DMA_FROM_DEVICE)
-                               set_page_dirty_lock(buf->pages[i]);
-                       put_page(buf->pages[i]);
-               }
-               kfree(buf->pages);
+                       vm_unmap_ram((void *)vaddr, n_pages);
+               if (buf->dma_dir == DMA_FROM_DEVICE)
+                       for (i = 0; i < n_pages; i++)
+                               set_page_dirty_lock(pages[i]);
        } else {
-               vb2_put_vma(buf->vma);
                iounmap((__force void __iomem *)buf->vaddr);
        }
+       vb2_destroy_framevec(buf->vec);
        kfree(buf);
 }
 
index 2bc0f50..b346638 100644 (file)
@@ -364,6 +364,7 @@ int mei_watchdog_register(struct mei_device *dev)
 
        int ret;
 
+       amt_wd_dev.parent = dev->dev;
        /* unlock to perserve correct locking order */
        mutex_unlock(&dev->device_lock);
        ret = watchdog_register_device(&amt_wd_dev);
index d8757bf..a9acf71 100644 (file)
@@ -61,11 +61,21 @@ MODULE_VERSION(NTB_NETDEV_VER);
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("Intel Corporation");
 
+/* Time in usecs for tx resource reaper */
+static unsigned int tx_time = 1;
+
+/* Number of descriptors to free before resuming tx */
+static unsigned int tx_start = 10;
+
+/* Number of descriptors still available before stop upper layer tx */
+static unsigned int tx_stop = 5;
+
 struct ntb_netdev {
        struct list_head list;
        struct pci_dev *pdev;
        struct net_device *ndev;
        struct ntb_transport_qp *qp;
+       struct timer_list tx_timer;
 };
 
 #define        NTB_TX_TIMEOUT_MS       1000
@@ -136,11 +146,42 @@ enqueue_again:
        }
 }
 
+static int __ntb_netdev_maybe_stop_tx(struct net_device *netdev,
+                                     struct ntb_transport_qp *qp, int size)
+{
+       struct ntb_netdev *dev = netdev_priv(netdev);
+
+       netif_stop_queue(netdev);
+       /* Make sure to see the latest value of ntb_transport_tx_free_entry()
+        * since the queue was last started.
+        */
+       smp_mb();
+
+       if (likely(ntb_transport_tx_free_entry(qp) < size)) {
+               mod_timer(&dev->tx_timer, jiffies + usecs_to_jiffies(tx_time));
+               return -EBUSY;
+       }
+
+       netif_start_queue(netdev);
+       return 0;
+}
+
+static int ntb_netdev_maybe_stop_tx(struct net_device *ndev,
+                                   struct ntb_transport_qp *qp, int size)
+{
+       if (netif_queue_stopped(ndev) ||
+           (ntb_transport_tx_free_entry(qp) >= size))
+               return 0;
+
+       return __ntb_netdev_maybe_stop_tx(ndev, qp, size);
+}
+
 static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data,
                                  void *data, int len)
 {
        struct net_device *ndev = qp_data;
        struct sk_buff *skb;
+       struct ntb_netdev *dev = netdev_priv(ndev);
 
        skb = data;
        if (!skb || !ndev)
@@ -155,6 +196,15 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data,
        }
 
        dev_kfree_skb(skb);
+
+       if (ntb_transport_tx_free_entry(dev->qp) >= tx_start) {
+               /* Make sure anybody stopping the queue after this sees the new
+                * value of ntb_transport_tx_free_entry()
+                */
+               smp_mb();
+               if (netif_queue_stopped(ndev))
+                       netif_wake_queue(ndev);
+       }
 }
 
 static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb,
@@ -163,10 +213,15 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb,
        struct ntb_netdev *dev = netdev_priv(ndev);
        int rc;
 
+       ntb_netdev_maybe_stop_tx(ndev, dev->qp, tx_stop);
+
        rc = ntb_transport_tx_enqueue(dev->qp, skb, skb->data, skb->len);
        if (rc)
                goto err;
 
+       /* check for next submit */
+       ntb_netdev_maybe_stop_tx(ndev, dev->qp, tx_stop);
+
        return NETDEV_TX_OK;
 
 err:
@@ -175,6 +230,23 @@ err:
        return NETDEV_TX_BUSY;
 }
 
+static void ntb_netdev_tx_timer(unsigned long data)
+{
+       struct net_device *ndev = (struct net_device *)data;
+       struct ntb_netdev *dev = netdev_priv(ndev);
+
+       if (ntb_transport_tx_free_entry(dev->qp) < tx_stop) {
+               mod_timer(&dev->tx_timer, jiffies + msecs_to_jiffies(tx_time));
+       } else {
+               /* Make sure anybody stopping the queue after this sees the new
+                * value of ntb_transport_tx_free_entry()
+                */
+               smp_mb();
+               if (netif_queue_stopped(ndev))
+                       netif_wake_queue(ndev);
+       }
+}
+
 static int ntb_netdev_open(struct net_device *ndev)
 {
        struct ntb_netdev *dev = netdev_priv(ndev);
@@ -197,8 +269,11 @@ static int ntb_netdev_open(struct net_device *ndev)
                }
        }
 
+       setup_timer(&dev->tx_timer, ntb_netdev_tx_timer, (unsigned long)ndev);
+
        netif_carrier_off(ndev);
        ntb_transport_link_up(dev->qp);
+       netif_start_queue(ndev);
 
        return 0;
 
@@ -219,6 +294,8 @@ static int ntb_netdev_close(struct net_device *ndev)
        while ((skb = ntb_transport_rx_remove(dev->qp, &len)))
                dev_kfree_skb(skb);
 
+       del_timer_sync(&dev->tx_timer);
+
        return 0;
 }
 
index 87751cf..865a3e3 100644 (file)
@@ -190,14 +190,17 @@ static inline int pdev_is_xeon(struct pci_dev *pdev)
        case PCI_DEVICE_ID_INTEL_NTB_SS_SNB:
        case PCI_DEVICE_ID_INTEL_NTB_SS_IVT:
        case PCI_DEVICE_ID_INTEL_NTB_SS_HSX:
+       case PCI_DEVICE_ID_INTEL_NTB_SS_BDX:
        case PCI_DEVICE_ID_INTEL_NTB_PS_JSF:
        case PCI_DEVICE_ID_INTEL_NTB_PS_SNB:
        case PCI_DEVICE_ID_INTEL_NTB_PS_IVT:
        case PCI_DEVICE_ID_INTEL_NTB_PS_HSX:
+       case PCI_DEVICE_ID_INTEL_NTB_PS_BDX:
        case PCI_DEVICE_ID_INTEL_NTB_B2B_JSF:
        case PCI_DEVICE_ID_INTEL_NTB_B2B_SNB:
        case PCI_DEVICE_ID_INTEL_NTB_B2B_IVT:
        case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX:
+       case PCI_DEVICE_ID_INTEL_NTB_B2B_BDX:
                return 1;
        }
        return 0;
@@ -237,7 +240,7 @@ static inline int ndev_ignore_unsafe(struct intel_ntb_dev *ndev,
 
 static int ndev_mw_to_bar(struct intel_ntb_dev *ndev, int idx)
 {
-       if (idx < 0 || idx > ndev->mw_count)
+       if (idx < 0 || idx >= ndev->mw_count)
                return -EINVAL;
        return ndev->reg->mw_bar[idx];
 }
@@ -572,10 +575,13 @@ static ssize_t ndev_debugfs_read(struct file *filp, char __user *ubuf,
                         "Connection Topology -\t%s\n",
                         ntb_topo_string(ndev->ntb.topo));
 
-       off += scnprintf(buf + off, buf_size - off,
-                        "B2B Offset -\t\t%#lx\n", ndev->b2b_off);
-       off += scnprintf(buf + off, buf_size - off,
-                        "B2B MW Idx -\t\t%d\n", ndev->b2b_idx);
+       if (ndev->b2b_idx != UINT_MAX) {
+               off += scnprintf(buf + off, buf_size - off,
+                                "B2B MW Idx -\t\t%u\n", ndev->b2b_idx);
+               off += scnprintf(buf + off, buf_size - off,
+                                "B2B Offset -\t\t%#lx\n", ndev->b2b_off);
+       }
+
        off += scnprintf(buf + off, buf_size - off,
                         "BAR4 Split -\t\t%s\n",
                         ndev->bar4_split ? "yes" : "no");
@@ -1484,7 +1490,7 @@ static int xeon_setup_b2b_mw(struct intel_ntb_dev *ndev,
        pdev = ndev_pdev(ndev);
        mmio = ndev->self_mmio;
 
-       if (ndev->b2b_idx >= ndev->mw_count) {
+       if (ndev->b2b_idx == UINT_MAX) {
                dev_dbg(ndev_dev(ndev), "not using b2b mw\n");
                b2b_bar = 0;
                ndev->b2b_off = 0;
@@ -1776,6 +1782,13 @@ static int xeon_init_ntb(struct intel_ntb_dev *ndev)
                        else
                                ndev->b2b_idx = b2b_mw_idx;
 
+                       if (ndev->b2b_idx >= ndev->mw_count) {
+                               dev_dbg(ndev_dev(ndev),
+                                       "b2b_mw_idx %d invalid for mw_count %u\n",
+                                       b2b_mw_idx, ndev->mw_count);
+                               return -EINVAL;
+                       }
+
                        dev_dbg(ndev_dev(ndev),
                                "setting up b2b mw idx %d means %d\n",
                                b2b_mw_idx, ndev->b2b_idx);
@@ -1843,6 +1856,9 @@ static int xeon_init_dev(struct intel_ntb_dev *ndev)
        case PCI_DEVICE_ID_INTEL_NTB_SS_HSX:
        case PCI_DEVICE_ID_INTEL_NTB_PS_HSX:
        case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX:
+       case PCI_DEVICE_ID_INTEL_NTB_SS_BDX:
+       case PCI_DEVICE_ID_INTEL_NTB_PS_BDX:
+       case PCI_DEVICE_ID_INTEL_NTB_B2B_BDX:
                ndev->hwerr_flags |= NTB_HWERR_SDOORBELL_LOCKUP;
                break;
        }
@@ -1857,6 +1873,9 @@ static int xeon_init_dev(struct intel_ntb_dev *ndev)
        case PCI_DEVICE_ID_INTEL_NTB_SS_HSX:
        case PCI_DEVICE_ID_INTEL_NTB_PS_HSX:
        case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX:
+       case PCI_DEVICE_ID_INTEL_NTB_SS_BDX:
+       case PCI_DEVICE_ID_INTEL_NTB_PS_BDX:
+       case PCI_DEVICE_ID_INTEL_NTB_B2B_BDX:
                ndev->hwerr_flags |= NTB_HWERR_SB01BASE_LOCKUP;
                break;
        }
@@ -1878,6 +1897,9 @@ static int xeon_init_dev(struct intel_ntb_dev *ndev)
        case PCI_DEVICE_ID_INTEL_NTB_SS_HSX:
        case PCI_DEVICE_ID_INTEL_NTB_PS_HSX:
        case PCI_DEVICE_ID_INTEL_NTB_B2B_HSX:
+       case PCI_DEVICE_ID_INTEL_NTB_SS_BDX:
+       case PCI_DEVICE_ID_INTEL_NTB_PS_BDX:
+       case PCI_DEVICE_ID_INTEL_NTB_B2B_BDX:
                ndev->hwerr_flags |= NTB_HWERR_B2BDOORBELL_BIT14;
                break;
        }
@@ -1996,7 +2018,7 @@ static inline void ndev_init_struct(struct intel_ntb_dev *ndev,
        ndev->ntb.ops = &intel_ntb_ops;
 
        ndev->b2b_off = 0;
-       ndev->b2b_idx = INT_MAX;
+       ndev->b2b_idx = UINT_MAX;
 
        ndev->bar4_split = 0;
 
@@ -2234,14 +2256,17 @@ static const struct pci_device_id intel_ntb_pci_tbl[] = {
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_SNB)},
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_IVT)},
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_HSX)},
+       {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_BDX)},
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_PS_JSF)},
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_PS_SNB)},
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_PS_IVT)},
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_PS_HSX)},
+       {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_PS_BDX)},
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_SS_JSF)},
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_SS_SNB)},
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_SS_IVT)},
        {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_SS_HSX)},
+       {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_SS_BDX)},
        {0}
 };
 MODULE_DEVICE_TABLE(pci, intel_ntb_pci_tbl);
index 7ddaf38..ea0612f 100644 (file)
@@ -67,6 +67,9 @@
 #define PCI_DEVICE_ID_INTEL_NTB_PS_HSX 0x2F0E
 #define PCI_DEVICE_ID_INTEL_NTB_SS_HSX 0x2F0F
 #define PCI_DEVICE_ID_INTEL_NTB_B2B_BWD        0x0C4E
+#define PCI_DEVICE_ID_INTEL_NTB_B2B_BDX        0x6F0D
+#define PCI_DEVICE_ID_INTEL_NTB_PS_BDX 0x6F0E
+#define PCI_DEVICE_ID_INTEL_NTB_SS_BDX 0x6F0F
 
 /* Intel Xeon hardware */
 
index 1c6386d..6e3ee90 100644 (file)
@@ -119,7 +119,8 @@ struct ntb_transport_qp {
        struct ntb_transport_ctx *transport;
        struct ntb_dev *ndev;
        void *cb_data;
-       struct dma_chan *dma_chan;
+       struct dma_chan *tx_dma_chan;
+       struct dma_chan *rx_dma_chan;
 
        bool client_ready;
        bool link_is_up;
@@ -297,7 +298,7 @@ static LIST_HEAD(ntb_transport_list);
 
 static int ntb_bus_init(struct ntb_transport_ctx *nt)
 {
-       list_add(&nt->entry, &ntb_transport_list);
+       list_add_tail(&nt->entry, &ntb_transport_list);
        return 0;
 }
 
@@ -452,7 +453,7 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count,
 
        out_offset = 0;
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
-                              "NTB QP stats\n");
+                              "\nNTB QP stats:\n\n");
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
                               "rx_bytes - \t%llu\n", qp->rx_bytes);
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
@@ -470,11 +471,11 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count,
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
                               "rx_err_ver - \t%llu\n", qp->rx_err_ver);
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
-                              "rx_buff - \t%p\n", qp->rx_buff);
+                              "rx_buff - \t0x%p\n", qp->rx_buff);
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
                               "rx_index - \t%u\n", qp->rx_index);
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
-                              "rx_max_entry - \t%u\n", qp->rx_max_entry);
+                              "rx_max_entry - \t%u\n\n", qp->rx_max_entry);
 
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
                               "tx_bytes - \t%llu\n", qp->tx_bytes);
@@ -489,15 +490,32 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count,
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
                               "tx_err_no_buf - %llu\n", qp->tx_err_no_buf);
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
-                              "tx_mw - \t%p\n", qp->tx_mw);
+                              "tx_mw - \t0x%p\n", qp->tx_mw);
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
-                              "tx_index - \t%u\n", qp->tx_index);
+                              "tx_index (H) - \t%u\n", qp->tx_index);
+       out_offset += snprintf(buf + out_offset, out_count - out_offset,
+                              "RRI (T) - \t%u\n",
+                              qp->remote_rx_info->entry);
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
                               "tx_max_entry - \t%u\n", qp->tx_max_entry);
+       out_offset += snprintf(buf + out_offset, out_count - out_offset,
+                              "free tx - \t%u\n",
+                              ntb_transport_tx_free_entry(qp));
 
        out_offset += snprintf(buf + out_offset, out_count - out_offset,
-                              "\nQP Link %s\n",
+                              "\n");
+       out_offset += snprintf(buf + out_offset, out_count - out_offset,
+                              "Using TX DMA - \t%s\n",
+                              qp->tx_dma_chan ? "Yes" : "No");
+       out_offset += snprintf(buf + out_offset, out_count - out_offset,
+                              "Using RX DMA - \t%s\n",
+                              qp->rx_dma_chan ? "Yes" : "No");
+       out_offset += snprintf(buf + out_offset, out_count - out_offset,
+                              "QP Link - \t%s\n",
                               qp->link_is_up ? "Up" : "Down");
+       out_offset += snprintf(buf + out_offset, out_count - out_offset,
+                              "\n");
+
        if (out_offset > out_count)
                out_offset = out_count;
 
@@ -535,6 +553,7 @@ static struct ntb_queue_entry *ntb_list_rm(spinlock_t *lock,
        }
        entry = list_first_entry(list, struct ntb_queue_entry, entry);
        list_del(&entry->entry);
+
 out:
        spin_unlock_irqrestore(lock, flags);
 
@@ -1206,7 +1225,7 @@ static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset)
 {
        struct dma_async_tx_descriptor *txd;
        struct ntb_transport_qp *qp = entry->qp;
-       struct dma_chan *chan = qp->dma_chan;
+       struct dma_chan *chan = qp->rx_dma_chan;
        struct dma_device *device;
        size_t pay_off, buff_off, len;
        struct dmaengine_unmap_data *unmap;
@@ -1219,18 +1238,18 @@ static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset)
                goto err;
 
        if (len < copy_bytes)
-               goto err_wait;
+               goto err;
 
        device = chan->device;
        pay_off = (size_t)offset & ~PAGE_MASK;
        buff_off = (size_t)buf & ~PAGE_MASK;
 
        if (!is_dma_copy_aligned(device, pay_off, buff_off, len))
-               goto err_wait;
+               goto err;
 
        unmap = dmaengine_get_unmap_data(device->dev, 2, GFP_NOWAIT);
        if (!unmap)
-               goto err_wait;
+               goto err;
 
        unmap->len = len;
        unmap->addr[0] = dma_map_page(device->dev, virt_to_page(offset),
@@ -1273,12 +1292,6 @@ err_set_unmap:
        dmaengine_unmap_put(unmap);
 err_get_unmap:
        dmaengine_unmap_put(unmap);
-err_wait:
-       /* If the callbacks come out of order, the writing of the index to the
-        * last completed will be out of order.  This may result in the
-        * receive stalling forever.
-        */
-       dma_sync_wait(chan, qp->last_cookie);
 err:
        ntb_memcpy_rx(entry, offset);
        qp->rx_memcpy++;
@@ -1373,8 +1386,8 @@ static void ntb_transport_rxc_db(unsigned long data)
                        break;
        }
 
-       if (i && qp->dma_chan)
-               dma_async_issue_pending(qp->dma_chan);
+       if (i && qp->rx_dma_chan)
+               dma_async_issue_pending(qp->rx_dma_chan);
 
        if (i == qp->rx_max_entry) {
                /* there is more work to do */
@@ -1441,7 +1454,7 @@ static void ntb_async_tx(struct ntb_transport_qp *qp,
 {
        struct ntb_payload_header __iomem *hdr;
        struct dma_async_tx_descriptor *txd;
-       struct dma_chan *chan = qp->dma_chan;
+       struct dma_chan *chan = qp->tx_dma_chan;
        struct dma_device *device;
        size_t dest_off, buff_off;
        struct dmaengine_unmap_data *unmap;
@@ -1634,14 +1647,27 @@ ntb_transport_create_queue(void *data, struct device *client_dev,
        dma_cap_set(DMA_MEMCPY, dma_mask);
 
        if (use_dma) {
-               qp->dma_chan = dma_request_channel(dma_mask, ntb_dma_filter_fn,
-                                                  (void *)(unsigned long)node);
-               if (!qp->dma_chan)
-                       dev_info(&pdev->dev, "Unable to allocate DMA channel\n");
+               qp->tx_dma_chan =
+                       dma_request_channel(dma_mask, ntb_dma_filter_fn,
+                                           (void *)(unsigned long)node);
+               if (!qp->tx_dma_chan)
+                       dev_info(&pdev->dev, "Unable to allocate TX DMA channel\n");
+
+               qp->rx_dma_chan =
+                       dma_request_channel(dma_mask, ntb_dma_filter_fn,
+                                           (void *)(unsigned long)node);
+               if (!qp->rx_dma_chan)
+                       dev_info(&pdev->dev, "Unable to allocate RX DMA channel\n");
        } else {
-               qp->dma_chan = NULL;
+               qp->tx_dma_chan = NULL;
+               qp->rx_dma_chan = NULL;
        }
-       dev_dbg(&pdev->dev, "Using %s memcpy\n", qp->dma_chan ? "DMA" : "CPU");
+
+       dev_dbg(&pdev->dev, "Using %s memcpy for TX\n",
+               qp->tx_dma_chan ? "DMA" : "CPU");
+
+       dev_dbg(&pdev->dev, "Using %s memcpy for RX\n",
+               qp->rx_dma_chan ? "DMA" : "CPU");
 
        for (i = 0; i < NTB_QP_DEF_NUM_ENTRIES; i++) {
                entry = kzalloc_node(sizeof(*entry), GFP_ATOMIC, node);
@@ -1676,8 +1702,10 @@ err2:
 err1:
        while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q)))
                kfree(entry);
-       if (qp->dma_chan)
-               dma_release_channel(qp->dma_chan);
+       if (qp->tx_dma_chan)
+               dma_release_channel(qp->tx_dma_chan);
+       if (qp->rx_dma_chan)
+               dma_release_channel(qp->rx_dma_chan);
        nt->qp_bitmap_free |= qp_bit;
 err:
        return NULL;
@@ -1701,12 +1729,27 @@ void ntb_transport_free_queue(struct ntb_transport_qp *qp)
 
        pdev = qp->ndev->pdev;
 
-       if (qp->dma_chan) {
-               struct dma_chan *chan = qp->dma_chan;
+       if (qp->tx_dma_chan) {
+               struct dma_chan *chan = qp->tx_dma_chan;
+               /* Putting the dma_chan to NULL will force any new traffic to be
+                * processed by the CPU instead of the DAM engine
+                */
+               qp->tx_dma_chan = NULL;
+
+               /* Try to be nice and wait for any queued DMA engine
+                * transactions to process before smashing it with a rock
+                */
+               dma_sync_wait(chan, qp->last_cookie);
+               dmaengine_terminate_all(chan);
+               dma_release_channel(chan);
+       }
+
+       if (qp->rx_dma_chan) {
+               struct dma_chan *chan = qp->rx_dma_chan;
                /* Putting the dma_chan to NULL will force any new traffic to be
                 * processed by the CPU instead of the DAM engine
                 */
-               qp->dma_chan = NULL;
+               qp->rx_dma_chan = NULL;
 
                /* Try to be nice and wait for any queued DMA engine
                 * transactions to process before smashing it with a rock
@@ -1843,7 +1886,7 @@ int ntb_transport_tx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data,
        entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q);
        if (!entry) {
                qp->tx_err_no_buf++;
-               return -ENOMEM;
+               return -EBUSY;
        }
 
        entry->cb_data = cb;
@@ -1954,21 +1997,34 @@ EXPORT_SYMBOL_GPL(ntb_transport_qp_num);
 unsigned int ntb_transport_max_size(struct ntb_transport_qp *qp)
 {
        unsigned int max;
+       unsigned int copy_align;
 
        if (!qp)
                return 0;
 
-       if (!qp->dma_chan)
+       if (!qp->tx_dma_chan && !qp->rx_dma_chan)
                return qp->tx_max_frame - sizeof(struct ntb_payload_header);
 
+       copy_align = max(qp->tx_dma_chan->device->copy_align,
+                        qp->rx_dma_chan->device->copy_align);
+
        /* If DMA engine usage is possible, try to find the max size for that */
        max = qp->tx_max_frame - sizeof(struct ntb_payload_header);
-       max -= max % (1 << qp->dma_chan->device->copy_align);
+       max -= max % (1 << copy_align);
 
        return max;
 }
 EXPORT_SYMBOL_GPL(ntb_transport_max_size);
 
+unsigned int ntb_transport_tx_free_entry(struct ntb_transport_qp *qp)
+{
+       unsigned int head = qp->tx_index;
+       unsigned int tail = qp->remote_rx_info->entry;
+
+       return tail > head ? tail - head : qp->tx_max_entry + tail - head;
+}
+EXPORT_SYMBOL_GPL(ntb_transport_tx_free_entry);
+
 static void ntb_transport_doorbell_callback(void *data, int vector)
 {
        struct ntb_transport_ctx *nt = data;
index 1ef02da..460fa67 100644 (file)
@@ -346,8 +346,7 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal)
  * as late as the polling interval is since we can't do that in the respective
  * accessors of the module parameters.
  */
-static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal,
-                              unsigned long *t)
+static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, int *t)
 {
        int temp, err = 0;
 
@@ -453,7 +452,7 @@ static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip,
 }
 
 static int acerhdf_get_trip_hyst(struct thermal_zone_device *thermal, int trip,
-                                unsigned long *temp)
+                                int *temp)
 {
        if (trip != 0)
                return -EINVAL;
@@ -464,7 +463,7 @@ static int acerhdf_get_trip_hyst(struct thermal_zone_device *thermal, int trip,
 }
 
 static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
-                                unsigned long *temp)
+                                int *temp)
 {
        if (trip == 0)
                *temp = fanon;
@@ -477,7 +476,7 @@ static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
 }
 
 static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal,
-                                unsigned long *temperature)
+                                int *temperature)
 {
        *temperature = ACERHDF_TEMP_CRIT;
        return 0;
index 0944e83..9f713b8 100644 (file)
@@ -132,7 +132,7 @@ static int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max)
  * to achieve very close approximate temp value with less than
  * 0.5C error
  */
-static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp)
+static int adc_to_temp(int direct, uint16_t adc_val, int *tp)
 {
        int temp;
 
@@ -174,14 +174,13 @@ static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp)
  *
  * Can sleep
  */
-static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp)
+static int mid_read_temp(struct thermal_zone_device *tzd, int *temp)
 {
        struct thermal_device_info *td_info = tzd->devdata;
        uint16_t adc_val, addr;
        uint8_t data = 0;
        int ret;
-       unsigned long curr_temp;
-
+       int curr_temp;
 
        addr = td_info->chnl_addr;
 
@@ -453,7 +452,7 @@ static SIMPLE_DEV_PM_OPS(mid_thermal_pm,
  *
  * Can sleep
  */
-static int read_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp)
+static int read_curr_temp(struct thermal_zone_device *tzd, int *temp)
 {
        WARN_ON(tzd == NULL);
        return mid_read_temp(tzd, temp);
index 1c202cc..907293e 100644 (file)
@@ -619,7 +619,7 @@ static int cm_get_battery_temperature(struct charger_manager *cm,
 
 #ifdef CONFIG_THERMAL
        if (cm->tzd_batt) {
-               ret = thermal_zone_get_temp(cm->tzd_batt, (unsigned long *)temp);
+               ret = thermal_zone_get_temp(cm->tzd_batt, temp);
                if (!ret)
                        /* Calibrate temperature unit */
                        *temp /= 100;
index 869284c..456987c 100644 (file)
@@ -557,7 +557,7 @@ EXPORT_SYMBOL_GPL(power_supply_unreg_notifier);
 
 #ifdef CONFIG_THERMAL
 static int power_supply_read_temp(struct thermal_zone_device *tzd,
-               unsigned long *temp)
+               int *temp)
 {
        struct power_supply *psy;
        union power_supply_propval val;
index 471d087..1a8c9b5 100644 (file)
@@ -172,6 +172,7 @@ scsi_mod-$(CONFIG_SYSCTL)   += scsi_sysctl.o
 scsi_mod-$(CONFIG_SCSI_PROC_FS)        += scsi_proc.o
 scsi_mod-y                     += scsi_trace.o scsi_logging.o
 scsi_mod-$(CONFIG_PM)          += scsi_pm.o
+scsi_mod-$(CONFIG_SCSI_DH)     += scsi_dh.o
 
 hv_storvsc-y                   := storvsc_drv.o
 
index edb43fd..c831e30 100644 (file)
@@ -983,7 +983,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha,
 {
        int err, i;
        u32 offs, size;
-       struct asd_ll_el *el;
+       struct asd_ll_el *el = NULL;
        struct asd_ctrla_phy_settings *ps;
        struct asd_ctrla_phy_settings dflt_ps;
 
@@ -1004,6 +1004,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha,
 
                size = sizeof(struct asd_ctrla_phy_settings);
                ps = &dflt_ps;
+               goto out_process;
        }
 
        if (size == 0)
@@ -1028,7 +1029,7 @@ static int asd_process_ctrl_a_user(struct asd_ha_struct *asd_ha,
                ASD_DPRINTK("couldn't find ctrla phy settings struct\n");
                goto out2;
        }
-
+out_process:
        err = asd_process_ctrla_phy_settings(asd_ha, ps);
        if (err) {
                ASD_DPRINTK("couldn't process ctrla phy settings\n");
index 315d6d6..98f7e8c 100644 (file)
@@ -3665,19 +3665,19 @@ bfa_cb_sfp_state_query(struct bfa_sfp_s *sfp)
                if (sfp->state_query_cbfn)
                        sfp->state_query_cbfn(sfp->state_query_cbarg,
                                        sfp->status);
-                       sfp->media = NULL;
-               }
+               sfp->media = NULL;
+       }
 
-               if (sfp->portspeed) {
-                       sfp->status = bfa_sfp_speed_valid(sfp, sfp->portspeed);
-                       if (sfp->state_query_cbfn)
-                               sfp->state_query_cbfn(sfp->state_query_cbarg,
-                                               sfp->status);
-                               sfp->portspeed = BFA_PORT_SPEED_UNKNOWN;
-               }
+       if (sfp->portspeed) {
+               sfp->status = bfa_sfp_speed_valid(sfp, sfp->portspeed);
+               if (sfp->state_query_cbfn)
+                       sfp->state_query_cbfn(sfp->state_query_cbarg,
+                                       sfp->status);
+               sfp->portspeed = BFA_PORT_SPEED_UNKNOWN;
+       }
 
-               sfp->state_query_lock = 0;
-               sfp->state_query_cbfn = NULL;
+       sfp->state_query_lock = 0;
+       sfp->state_query_cbfn = NULL;
 }
 
 /*
@@ -3878,7 +3878,7 @@ bfa_sfp_show_comp(struct bfa_sfp_s *sfp, struct bfi_mbmsg_s *msg)
                bfa_trc(sfp, sfp->data_valid);
                if (sfp->data_valid) {
                        u32     size = sizeof(struct sfp_mem_s);
-                       u8 *des = (u8 *) &(sfp->sfpmem);
+                       u8 *des = (u8 *)(sfp->sfpmem);
                        memcpy(des, sfp->dbuf_kva, size);
                }
                /*
index 69abd0a..e5647d5 100644 (file)
@@ -3,7 +3,7 @@
 #
 
 menuconfig SCSI_DH
-       tristate "SCSI Device Handlers"
+       bool "SCSI Device Handlers"
        depends on SCSI
        default n
        help
index e1d2ea0..09866c5 100644 (file)
@@ -1,7 +1,6 @@
 #
 # SCSI Device Handler
 #
-obj-$(CONFIG_SCSI_DH)          += scsi_dh.o
 obj-$(CONFIG_SCSI_DH_RDAC)     += scsi_dh_rdac.o
 obj-$(CONFIG_SCSI_DH_HP_SW)    += scsi_dh_hp_sw.o
 obj-$(CONFIG_SCSI_DH_EMC)      += scsi_dh_emc.o
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
deleted file mode 100644 (file)
index 1efebc9..0000000
+++ /dev/null
@@ -1,621 +0,0 @@
-/*
- * SCSI device handler infrastruture.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * Copyright IBM Corporation, 2007
- *      Authors:
- *               Chandra Seetharaman <sekharan@us.ibm.com>
- *               Mike Anderson <andmike@linux.vnet.ibm.com>
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <scsi/scsi_dh.h>
-#include "../scsi_priv.h"
-
-static DEFINE_SPINLOCK(list_lock);
-static LIST_HEAD(scsi_dh_list);
-
-static struct scsi_device_handler *get_device_handler(const char *name)
-{
-       struct scsi_device_handler *tmp, *found = NULL;
-
-       spin_lock(&list_lock);
-       list_for_each_entry(tmp, &scsi_dh_list, list) {
-               if (!strncmp(tmp->name, name, strlen(tmp->name))) {
-                       found = tmp;
-                       break;
-               }
-       }
-       spin_unlock(&list_lock);
-       return found;
-}
-
-/*
- * device_handler_match_function - Match a device handler to a device
- * @sdev - SCSI device to be tested
- *
- * Tests @sdev against the match function of all registered device_handler.
- * Returns the found device handler or NULL if not found.
- */
-static struct scsi_device_handler *
-device_handler_match_function(struct scsi_device *sdev)
-{
-       struct scsi_device_handler *tmp_dh, *found_dh = NULL;
-
-       spin_lock(&list_lock);
-       list_for_each_entry(tmp_dh, &scsi_dh_list, list) {
-               if (tmp_dh->match && tmp_dh->match(sdev)) {
-                       found_dh = tmp_dh;
-                       break;
-               }
-       }
-       spin_unlock(&list_lock);
-       return found_dh;
-}
-
-/*
- * device_handler_match - Attach a device handler to a device
- * @scsi_dh - The device handler to match against or NULL
- * @sdev - SCSI device to be tested against @scsi_dh
- *
- * Tests @sdev against the device handler @scsi_dh or against
- * all registered device_handler if @scsi_dh == NULL.
- * Returns the found device handler or NULL if not found.
- */
-static struct scsi_device_handler *
-device_handler_match(struct scsi_device_handler *scsi_dh,
-                    struct scsi_device *sdev)
-{
-       struct scsi_device_handler *found_dh;
-
-       found_dh = device_handler_match_function(sdev);
-
-       if (scsi_dh && found_dh != scsi_dh)
-               found_dh = NULL;
-
-       return found_dh;
-}
-
-/*
- * scsi_dh_handler_attach - Attach a device handler to a device
- * @sdev - SCSI device the device handler should attach to
- * @scsi_dh - The device handler to attach
- */
-static int scsi_dh_handler_attach(struct scsi_device *sdev,
-                                 struct scsi_device_handler *scsi_dh)
-{
-       struct scsi_dh_data *d;
-
-       if (sdev->scsi_dh_data) {
-               if (sdev->scsi_dh_data->scsi_dh != scsi_dh)
-                       return -EBUSY;
-
-               kref_get(&sdev->scsi_dh_data->kref);
-               return 0;
-       }
-
-       if (!try_module_get(scsi_dh->module))
-               return -EINVAL;
-
-       d = scsi_dh->attach(sdev);
-       if (IS_ERR(d)) {
-               sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%ld)\n",
-                           scsi_dh->name, PTR_ERR(d));
-               module_put(scsi_dh->module);
-               return PTR_ERR(d);
-       }
-
-       d->scsi_dh = scsi_dh;
-       kref_init(&d->kref);
-       d->sdev = sdev;
-
-       spin_lock_irq(sdev->request_queue->queue_lock);
-       sdev->scsi_dh_data = d;
-       spin_unlock_irq(sdev->request_queue->queue_lock);
-       return 0;
-}
-
-static void __detach_handler (struct kref *kref)
-{
-       struct scsi_dh_data *scsi_dh_data =
-               container_of(kref, struct scsi_dh_data, kref);
-       struct scsi_device_handler *scsi_dh = scsi_dh_data->scsi_dh;
-       struct scsi_device *sdev = scsi_dh_data->sdev;
-
-       scsi_dh->detach(sdev);
-
-       spin_lock_irq(sdev->request_queue->queue_lock);
-       sdev->scsi_dh_data = NULL;
-       spin_unlock_irq(sdev->request_queue->queue_lock);
-
-       sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", scsi_dh->name);
-       module_put(scsi_dh->module);
-}
-
-/*
- * scsi_dh_handler_detach - Detach a device handler from a device
- * @sdev - SCSI device the device handler should be detached from
- * @scsi_dh - Device handler to be detached
- *
- * Detach from a device handler. If a device handler is specified,
- * only detach if the currently attached handler matches @scsi_dh.
- */
-static void scsi_dh_handler_detach(struct scsi_device *sdev,
-                                  struct scsi_device_handler *scsi_dh)
-{
-       if (!sdev->scsi_dh_data)
-               return;
-
-       if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh)
-               return;
-
-       if (!scsi_dh)
-               scsi_dh = sdev->scsi_dh_data->scsi_dh;
-
-       if (scsi_dh)
-               kref_put(&sdev->scsi_dh_data->kref, __detach_handler);
-}
-
-/*
- * Functions for sysfs attribute 'dh_state'
- */
-static ssize_t
-store_dh_state(struct device *dev, struct device_attribute *attr,
-              const char *buf, size_t count)
-{
-       struct scsi_device *sdev = to_scsi_device(dev);
-       struct scsi_device_handler *scsi_dh;
-       int err = -EINVAL;
-
-       if (sdev->sdev_state == SDEV_CANCEL ||
-           sdev->sdev_state == SDEV_DEL)
-               return -ENODEV;
-
-       if (!sdev->scsi_dh_data) {
-               /*
-                * Attach to a device handler
-                */
-               if (!(scsi_dh = get_device_handler(buf)))
-                       return err;
-               err = scsi_dh_handler_attach(sdev, scsi_dh);
-       } else {
-               scsi_dh = sdev->scsi_dh_data->scsi_dh;
-               if (!strncmp(buf, "detach", 6)) {
-                       /*
-                        * Detach from a device handler
-                        */
-                       scsi_dh_handler_detach(sdev, scsi_dh);
-                       err = 0;
-               } else if (!strncmp(buf, "activate", 8)) {
-                       /*
-                        * Activate a device handler
-                        */
-                       if (scsi_dh->activate)
-                               err = scsi_dh->activate(sdev, NULL, NULL);
-                       else
-                               err = 0;
-               }
-       }
-
-       return err<0?err:count;
-}
-
-static ssize_t
-show_dh_state(struct device *dev, struct device_attribute *attr, char *buf)
-{
-       struct scsi_device *sdev = to_scsi_device(dev);
-
-       if (!sdev->scsi_dh_data)
-               return snprintf(buf, 20, "detached\n");
-
-       return snprintf(buf, 20, "%s\n", sdev->scsi_dh_data->scsi_dh->name);
-}
-
-static struct device_attribute scsi_dh_state_attr =
-       __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state,
-              store_dh_state);
-
-/*
- * scsi_dh_sysfs_attr_add - Callback for scsi_init_dh
- */
-static int scsi_dh_sysfs_attr_add(struct device *dev, void *data)
-{
-       struct scsi_device *sdev;
-       int err;
-
-       if (!scsi_is_sdev_device(dev))
-               return 0;
-
-       sdev = to_scsi_device(dev);
-
-       err = device_create_file(&sdev->sdev_gendev,
-                                &scsi_dh_state_attr);
-
-       return 0;
-}
-
-/*
- * scsi_dh_sysfs_attr_remove - Callback for scsi_exit_dh
- */
-static int scsi_dh_sysfs_attr_remove(struct device *dev, void *data)
-{
-       struct scsi_device *sdev;
-
-       if (!scsi_is_sdev_device(dev))
-               return 0;
-
-       sdev = to_scsi_device(dev);
-
-       device_remove_file(&sdev->sdev_gendev,
-                          &scsi_dh_state_attr);
-
-       return 0;
-}
-
-/*
- * scsi_dh_notifier - notifier chain callback
- */
-static int scsi_dh_notifier(struct notifier_block *nb,
-                           unsigned long action, void *data)
-{
-       struct device *dev = data;
-       struct scsi_device *sdev;
-       int err = 0;
-       struct scsi_device_handler *devinfo = NULL;
-
-       if (!scsi_is_sdev_device(dev))
-               return 0;
-
-       sdev = to_scsi_device(dev);
-
-       if (action == BUS_NOTIFY_ADD_DEVICE) {
-               err = device_create_file(dev, &scsi_dh_state_attr);
-               /* don't care about err */
-               devinfo = device_handler_match(NULL, sdev);
-               if (devinfo)
-                       err = scsi_dh_handler_attach(sdev, devinfo);
-       } else if (action == BUS_NOTIFY_DEL_DEVICE) {
-               device_remove_file(dev, &scsi_dh_state_attr);
-               scsi_dh_handler_detach(sdev, NULL);
-       }
-       return err;
-}
-
-/*
- * scsi_dh_notifier_add - Callback for scsi_register_device_handler
- */
-static int scsi_dh_notifier_add(struct device *dev, void *data)
-{
-       struct scsi_device_handler *scsi_dh = data;
-       struct scsi_device *sdev;
-
-       if (!scsi_is_sdev_device(dev))
-               return 0;
-
-       if (!get_device(dev))
-               return 0;
-
-       sdev = to_scsi_device(dev);
-
-       if (device_handler_match(scsi_dh, sdev))
-               scsi_dh_handler_attach(sdev, scsi_dh);
-
-       put_device(dev);
-
-       return 0;
-}
-
-/*
- * scsi_dh_notifier_remove - Callback for scsi_unregister_device_handler
- */
-static int scsi_dh_notifier_remove(struct device *dev, void *data)
-{
-       struct scsi_device_handler *scsi_dh = data;
-       struct scsi_device *sdev;
-
-       if (!scsi_is_sdev_device(dev))
-               return 0;
-
-       if (!get_device(dev))
-               return 0;
-
-       sdev = to_scsi_device(dev);
-
-       scsi_dh_handler_detach(sdev, scsi_dh);
-
-       put_device(dev);
-
-       return 0;
-}
-
-/*
- * scsi_register_device_handler - register a device handler personality
- *      module.
- * @scsi_dh - device handler to be registered.
- *
- * Returns 0 on success, -EBUSY if handler already registered.
- */
-int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
-{
-
-       if (get_device_handler(scsi_dh->name))
-               return -EBUSY;
-
-       if (!scsi_dh->attach || !scsi_dh->detach)
-               return -EINVAL;
-
-       spin_lock(&list_lock);
-       list_add(&scsi_dh->list, &scsi_dh_list);
-       spin_unlock(&list_lock);
-
-       bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
-       printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name);
-
-       return SCSI_DH_OK;
-}
-EXPORT_SYMBOL_GPL(scsi_register_device_handler);
-
-/*
- * scsi_unregister_device_handler - register a device handler personality
- *      module.
- * @scsi_dh - device handler to be unregistered.
- *
- * Returns 0 on success, -ENODEV if handler not registered.
- */
-int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
-{
-
-       if (!get_device_handler(scsi_dh->name))
-               return -ENODEV;
-
-       bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
-                        scsi_dh_notifier_remove);
-
-       spin_lock(&list_lock);
-       list_del(&scsi_dh->list);
-       spin_unlock(&list_lock);
-       printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
-
-       return SCSI_DH_OK;
-}
-EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
-
-/*
- * scsi_dh_activate - activate the path associated with the scsi_device
- *      corresponding to the given request queue.
- *     Returns immediately without waiting for activation to be completed.
- * @q    - Request queue that is associated with the scsi_device to be
- *         activated.
- * @fn   - Function to be called upon completion of the activation.
- *         Function fn is called with data (below) and the error code.
- *         Function fn may be called from the same calling context. So,
- *         do not hold the lock in the caller which may be needed in fn.
- * @data - data passed to the function fn upon completion.
- *
- */
-int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
-{
-       int err = 0;
-       unsigned long flags;
-       struct scsi_device *sdev;
-       struct scsi_device_handler *scsi_dh = NULL;
-       struct device *dev = NULL;
-
-       spin_lock_irqsave(q->queue_lock, flags);
-       sdev = q->queuedata;
-       if (!sdev) {
-               spin_unlock_irqrestore(q->queue_lock, flags);
-               err = SCSI_DH_NOSYS;
-               if (fn)
-                       fn(data, err);
-               return err;
-       }
-
-       if (sdev->scsi_dh_data)
-               scsi_dh = sdev->scsi_dh_data->scsi_dh;
-       dev = get_device(&sdev->sdev_gendev);
-       if (!scsi_dh || !dev ||
-           sdev->sdev_state == SDEV_CANCEL ||
-           sdev->sdev_state == SDEV_DEL)
-               err = SCSI_DH_NOSYS;
-       if (sdev->sdev_state == SDEV_OFFLINE)
-               err = SCSI_DH_DEV_OFFLINED;
-       spin_unlock_irqrestore(q->queue_lock, flags);
-
-       if (err) {
-               if (fn)
-                       fn(data, err);
-               goto out;
-       }
-
-       if (scsi_dh->activate)
-               err = scsi_dh->activate(sdev, fn, data);
-out:
-       put_device(dev);
-       return err;
-}
-EXPORT_SYMBOL_GPL(scsi_dh_activate);
-
-/*
- * scsi_dh_set_params - set the parameters for the device as per the
- *      string specified in params.
- * @q - Request queue that is associated with the scsi_device for
- *      which the parameters to be set.
- * @params - parameters in the following format
- *      "no_of_params\0param1\0param2\0param3\0...\0"
- *      for example, string for 2 parameters with value 10 and 21
- *      is specified as "2\010\021\0".
- */
-int scsi_dh_set_params(struct request_queue *q, const char *params)
-{
-       int err = -SCSI_DH_NOSYS;
-       unsigned long flags;
-       struct scsi_device *sdev;
-       struct scsi_device_handler *scsi_dh = NULL;
-
-       spin_lock_irqsave(q->queue_lock, flags);
-       sdev = q->queuedata;
-       if (sdev && sdev->scsi_dh_data)
-               scsi_dh = sdev->scsi_dh_data->scsi_dh;
-       if (scsi_dh && scsi_dh->set_params && get_device(&sdev->sdev_gendev))
-               err = 0;
-       spin_unlock_irqrestore(q->queue_lock, flags);
-
-       if (err)
-               return err;
-       err = scsi_dh->set_params(sdev, params);
-       put_device(&sdev->sdev_gendev);
-       return err;
-}
-EXPORT_SYMBOL_GPL(scsi_dh_set_params);
-
-/*
- * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
- *     the given name. FALSE(0) otherwise.
- * @name - name of the device handler.
- */
-int scsi_dh_handler_exist(const char *name)
-{
-       return (get_device_handler(name) != NULL);
-}
-EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
-
-/*
- * scsi_dh_attach - Attach device handler
- * @q - Request queue that is associated with the scsi_device
- *      the handler should be attached to
- * @name - name of the handler to attach
- */
-int scsi_dh_attach(struct request_queue *q, const char *name)
-{
-       unsigned long flags;
-       struct scsi_device *sdev;
-       struct scsi_device_handler *scsi_dh;
-       int err = 0;
-
-       scsi_dh = get_device_handler(name);
-       if (!scsi_dh)
-               return -EINVAL;
-
-       spin_lock_irqsave(q->queue_lock, flags);
-       sdev = q->queuedata;
-       if (!sdev || !get_device(&sdev->sdev_gendev))
-               err = -ENODEV;
-       spin_unlock_irqrestore(q->queue_lock, flags);
-
-       if (!err) {
-               err = scsi_dh_handler_attach(sdev, scsi_dh);
-               put_device(&sdev->sdev_gendev);
-       }
-       return err;
-}
-EXPORT_SYMBOL_GPL(scsi_dh_attach);
-
-/*
- * scsi_dh_detach - Detach device handler
- * @q - Request queue that is associated with the scsi_device
- *      the handler should be detached from
- *
- * This function will detach the device handler only
- * if the sdev is not part of the internal list, ie
- * if it has been attached manually.
- */
-void scsi_dh_detach(struct request_queue *q)
-{
-       unsigned long flags;
-       struct scsi_device *sdev;
-       struct scsi_device_handler *scsi_dh = NULL;
-
-       spin_lock_irqsave(q->queue_lock, flags);
-       sdev = q->queuedata;
-       if (!sdev || !get_device(&sdev->sdev_gendev))
-               sdev = NULL;
-       spin_unlock_irqrestore(q->queue_lock, flags);
-
-       if (!sdev)
-               return;
-
-       if (sdev->scsi_dh_data) {
-               scsi_dh = sdev->scsi_dh_data->scsi_dh;
-               scsi_dh_handler_detach(sdev, scsi_dh);
-       }
-       put_device(&sdev->sdev_gendev);
-}
-EXPORT_SYMBOL_GPL(scsi_dh_detach);
-
-/*
- * scsi_dh_attached_handler_name - Get attached device handler's name
- * @q - Request queue that is associated with the scsi_device
- *      that may have a device handler attached
- * @gfp - the GFP mask used in the kmalloc() call when allocating memory
- *
- * Returns name of attached handler, NULL if no handler is attached.
- * Caller must take care to free the returned string.
- */
-const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp)
-{
-       unsigned long flags;
-       struct scsi_device *sdev;
-       const char *handler_name = NULL;
-
-       spin_lock_irqsave(q->queue_lock, flags);
-       sdev = q->queuedata;
-       if (!sdev || !get_device(&sdev->sdev_gendev))
-               sdev = NULL;
-       spin_unlock_irqrestore(q->queue_lock, flags);
-
-       if (!sdev)
-               return NULL;
-
-       if (sdev->scsi_dh_data)
-               handler_name = kstrdup(sdev->scsi_dh_data->scsi_dh->name, gfp);
-
-       put_device(&sdev->sdev_gendev);
-       return handler_name;
-}
-EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name);
-
-static struct notifier_block scsi_dh_nb = {
-       .notifier_call = scsi_dh_notifier
-};
-
-static int __init scsi_dh_init(void)
-{
-       int r;
-
-       r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb);
-
-       if (!r)
-               bus_for_each_dev(&scsi_bus_type, NULL, NULL,
-                                scsi_dh_sysfs_attr_add);
-
-       return r;
-}
-
-static void __exit scsi_dh_exit(void)
-{
-       bus_for_each_dev(&scsi_bus_type, NULL, NULL,
-                        scsi_dh_sysfs_attr_remove);
-       bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb);
-}
-
-module_init(scsi_dh_init);
-module_exit(scsi_dh_exit);
-
-MODULE_DESCRIPTION("SCSI device handler");
-MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
-MODULE_LICENSE("GPL");
index 854b568..cc2773b 100644 (file)
@@ -62,7 +62,6 @@
 #define ALUA_OPTIMIZE_STPG             1
 
 struct alua_dh_data {
-       struct scsi_dh_data     dh_data;
        int                     group_id;
        int                     rel_port;
        int                     tpgs;
@@ -86,11 +85,6 @@ struct alua_dh_data {
 static char print_alua_state(int);
 static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *);
 
-static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev)
-{
-       return container_of(sdev->scsi_dh_data, struct alua_dh_data, dh_data);
-}
-
 static int realloc_buffer(struct alua_dh_data *h, unsigned len)
 {
        if (h->buff && h->buff != h->inq)
@@ -708,7 +702,7 @@ out:
  */
 static int alua_set_params(struct scsi_device *sdev, const char *params)
 {
-       struct alua_dh_data *h = get_alua_data(sdev);
+       struct alua_dh_data *h = sdev->handler_data;
        unsigned int optimize = 0, argc;
        const char *p = params;
        int result = SCSI_DH_OK;
@@ -746,7 +740,7 @@ MODULE_PARM_DESC(optimize_stpg, "Allow use of a non-optimized path, rather than
 static int alua_activate(struct scsi_device *sdev,
                        activate_complete fn, void *data)
 {
-       struct alua_dh_data *h = get_alua_data(sdev);
+       struct alua_dh_data *h = sdev->handler_data;
        int err = SCSI_DH_OK;
        int stpg = 0;
 
@@ -804,7 +798,7 @@ out:
  */
 static int alua_prep_fn(struct scsi_device *sdev, struct request *req)
 {
-       struct alua_dh_data *h = get_alua_data(sdev);
+       struct alua_dh_data *h = sdev->handler_data;
        int ret = BLKPREP_OK;
 
        if (h->state == TPGS_STATE_TRANSITIONING)
@@ -819,23 +813,18 @@ static int alua_prep_fn(struct scsi_device *sdev, struct request *req)
 
 }
 
-static bool alua_match(struct scsi_device *sdev)
-{
-       return (scsi_device_tpgs(sdev) != 0);
-}
-
 /*
  * alua_bus_attach - Attach device handler
  * @sdev: device to be attached to
  */
-static struct scsi_dh_data *alua_bus_attach(struct scsi_device *sdev)
+static int alua_bus_attach(struct scsi_device *sdev)
 {
        struct alua_dh_data *h;
        int err;
 
        h = kzalloc(sizeof(*h) , GFP_KERNEL);
        if (!h)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
        h->tpgs = TPGS_MODE_UNINITIALIZED;
        h->state = TPGS_STATE_OPTIMIZED;
        h->group_id = -1;
@@ -848,11 +837,11 @@ static struct scsi_dh_data *alua_bus_attach(struct scsi_device *sdev)
        if (err != SCSI_DH_OK && err != SCSI_DH_DEV_OFFLINED)
                goto failed;
 
-       sdev_printk(KERN_NOTICE, sdev, "%s: Attached\n", ALUA_DH_NAME);
-       return &h->dh_data;
+       sdev->handler_data = h;
+       return 0;
 failed:
        kfree(h);
-       return ERR_PTR(-EINVAL);
+       return -EINVAL;
 }
 
 /*
@@ -861,10 +850,11 @@ failed:
  */
 static void alua_bus_detach(struct scsi_device *sdev)
 {
-       struct alua_dh_data *h = get_alua_data(sdev);
+       struct alua_dh_data *h = sdev->handler_data;
 
        if (h->buff && h->inq != h->buff)
                kfree(h->buff);
+       sdev->handler_data = NULL;
        kfree(h);
 }
 
@@ -877,7 +867,6 @@ static struct scsi_device_handler alua_dh = {
        .check_sense = alua_check_sense,
        .activate = alua_activate,
        .set_params = alua_set_params,
-       .match = alua_match,
 };
 
 static int __init alua_init(void)
index 6ed1caa..e6fb97c 100644 (file)
@@ -72,7 +72,6 @@ static const char * lun_state[] =
 };
 
 struct clariion_dh_data {
-       struct scsi_dh_data dh_data;
        /*
         * Flags:
         *  CLARIION_SHORT_TRESPASS
@@ -114,13 +113,6 @@ struct clariion_dh_data {
        int current_sp;
 };
 
-static inline struct clariion_dh_data
-                       *get_clariion_data(struct scsi_device *sdev)
-{
-       return container_of(sdev->scsi_dh_data, struct clariion_dh_data,
-                       dh_data);
-}
-
 /*
  * Parse MODE_SELECT cmd reply.
  */
@@ -450,7 +442,7 @@ static int clariion_check_sense(struct scsi_device *sdev,
 
 static int clariion_prep_fn(struct scsi_device *sdev, struct request *req)
 {
-       struct clariion_dh_data *h = get_clariion_data(sdev);
+       struct clariion_dh_data *h = sdev->handler_data;
        int ret = BLKPREP_OK;
 
        if (h->lun_state != CLARIION_LUN_OWNED) {
@@ -533,7 +525,7 @@ retry:
 static int clariion_activate(struct scsi_device *sdev,
                                activate_complete fn, void *data)
 {
-       struct clariion_dh_data *csdev = get_clariion_data(sdev);
+       struct clariion_dh_data *csdev = sdev->handler_data;
        int result;
 
        result = clariion_send_inquiry(sdev, csdev);
@@ -574,7 +566,7 @@ done:
  */
 static int clariion_set_params(struct scsi_device *sdev, const char *params)
 {
-       struct clariion_dh_data *csdev = get_clariion_data(sdev);
+       struct clariion_dh_data *csdev = sdev->handler_data;
        unsigned int hr = 0, st = 0, argc;
        const char *p = params;
        int result = SCSI_DH_OK;
@@ -622,42 +614,14 @@ done:
        return result;
 }
 
-static const struct {
-       char *vendor;
-       char *model;
-} clariion_dev_list[] = {
-       {"DGC", "RAID"},
-       {"DGC", "DISK"},
-       {"DGC", "VRAID"},
-       {NULL, NULL},
-};
-
-static bool clariion_match(struct scsi_device *sdev)
-{
-       int i;
-
-       if (scsi_device_tpgs(sdev))
-               return false;
-
-       for (i = 0; clariion_dev_list[i].vendor; i++) {
-               if (!strncmp(sdev->vendor, clariion_dev_list[i].vendor,
-                       strlen(clariion_dev_list[i].vendor)) &&
-                   !strncmp(sdev->model, clariion_dev_list[i].model,
-                       strlen(clariion_dev_list[i].model))) {
-                       return true;
-               }
-       }
-       return false;
-}
-
-static struct scsi_dh_data *clariion_bus_attach(struct scsi_device *sdev)
+static int clariion_bus_attach(struct scsi_device *sdev)
 {
        struct clariion_dh_data *h;
        int err;
 
        h = kzalloc(sizeof(*h) , GFP_KERNEL);
        if (!h)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
        h->lun_state = CLARIION_LUN_UNINITIALIZED;
        h->default_sp = CLARIION_UNBOUND_LU;
        h->current_sp = CLARIION_UNBOUND_LU;
@@ -675,18 +639,19 @@ static struct scsi_dh_data *clariion_bus_attach(struct scsi_device *sdev)
                    CLARIION_NAME, h->current_sp + 'A',
                    h->port, lun_state[h->lun_state],
                    h->default_sp + 'A');
-       return &h->dh_data;
+
+       sdev->handler_data = h;
+       return 0;
 
 failed:
        kfree(h);
-       return ERR_PTR(-EINVAL);
+       return -EINVAL;
 }
 
 static void clariion_bus_detach(struct scsi_device *sdev)
 {
-       struct clariion_dh_data *h = get_clariion_data(sdev);
-
-       kfree(h);
+       kfree(sdev->handler_data);
+       sdev->handler_data = NULL;
 }
 
 static struct scsi_device_handler clariion_dh = {
@@ -698,7 +663,6 @@ static struct scsi_device_handler clariion_dh = {
        .activate       = clariion_activate,
        .prep_fn        = clariion_prep_fn,
        .set_params     = clariion_set_params,
-       .match          = clariion_match,
 };
 
 static int __init clariion_init(void)
index 485d995..9406d5f 100644 (file)
@@ -38,7 +38,6 @@
 #define HP_SW_PATH_PASSIVE             1
 
 struct hp_sw_dh_data {
-       struct scsi_dh_data dh_data;
        unsigned char sense[SCSI_SENSE_BUFFERSIZE];
        int path_state;
        int retries;
@@ -50,11 +49,6 @@ struct hp_sw_dh_data {
 
 static int hp_sw_start_stop(struct hp_sw_dh_data *);
 
-static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
-{
-       return container_of(sdev->scsi_dh_data, struct hp_sw_dh_data, dh_data);
-}
-
 /*
  * tur_done - Handle TEST UNIT READY return status
  * @sdev: sdev the command has been sent to
@@ -267,7 +261,7 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h)
 
 static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
 {
-       struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+       struct hp_sw_dh_data *h = sdev->handler_data;
        int ret = BLKPREP_OK;
 
        if (h->path_state != HP_SW_PATH_ACTIVE) {
@@ -292,7 +286,7 @@ static int hp_sw_activate(struct scsi_device *sdev,
                                activate_complete fn, void *data)
 {
        int ret = SCSI_DH_OK;
-       struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+       struct hp_sw_dh_data *h = sdev->handler_data;
 
        ret = hp_sw_tur(sdev, h);
 
@@ -311,43 +305,14 @@ static int hp_sw_activate(struct scsi_device *sdev,
        return 0;
 }
 
-static const struct {
-       char *vendor;
-       char *model;
-} hp_sw_dh_data_list[] = {
-       {"COMPAQ", "MSA1000 VOLUME"},
-       {"COMPAQ", "HSV110"},
-       {"HP", "HSV100"},
-       {"DEC", "HSG80"},
-       {NULL, NULL},
-};
-
-static bool hp_sw_match(struct scsi_device *sdev)
-{
-       int i;
-
-       if (scsi_device_tpgs(sdev))
-               return false;
-
-       for (i = 0; hp_sw_dh_data_list[i].vendor; i++) {
-               if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor,
-                       strlen(hp_sw_dh_data_list[i].vendor)) &&
-                   !strncmp(sdev->model, hp_sw_dh_data_list[i].model,
-                       strlen(hp_sw_dh_data_list[i].model))) {
-                       return true;
-               }
-       }
-       return false;
-}
-
-static struct scsi_dh_data *hp_sw_bus_attach(struct scsi_device *sdev)
+static int hp_sw_bus_attach(struct scsi_device *sdev)
 {
        struct hp_sw_dh_data *h;
        int ret;
 
        h = kzalloc(sizeof(*h), GFP_KERNEL);
        if (!h)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
        h->path_state = HP_SW_PATH_UNINITIALIZED;
        h->retries = HP_SW_RETRIES;
        h->sdev = sdev;
@@ -359,17 +324,18 @@ static struct scsi_dh_data *hp_sw_bus_attach(struct scsi_device *sdev)
        sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n",
                    HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE?
                    "active":"passive");
-       return &h->dh_data;
+
+       sdev->handler_data = h;
+       return 0;
 failed:
        kfree(h);
-       return ERR_PTR(-EINVAL);
+       return -EINVAL;
 }
 
 static void hp_sw_bus_detach( struct scsi_device *sdev )
 {
-       struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
-
-       kfree(h);
+       kfree(sdev->handler_data);
+       sdev->handler_data = NULL;
 }
 
 static struct scsi_device_handler hp_sw_dh = {
@@ -379,7 +345,6 @@ static struct scsi_device_handler hp_sw_dh = {
        .detach         = hp_sw_bus_detach,
        .activate       = hp_sw_activate,
        .prep_fn        = hp_sw_prep_fn,
-       .match          = hp_sw_match,
 };
 
 static int __init hp_sw_init(void)
index b46ace3..3613581 100644 (file)
@@ -181,7 +181,6 @@ struct c2_inquiry {
 };
 
 struct rdac_dh_data {
-       struct scsi_dh_data     dh_data;
        struct rdac_controller  *ctlr;
 #define UNINITIALIZED_LUN      (1 << 8)
        unsigned                lun;
@@ -260,11 +259,6 @@ do { \
                sdev_printk(KERN_INFO, sdev, RDAC_NAME ": " f "\n", ## arg); \
 } while (0);
 
-static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev)
-{
-       return container_of(sdev->scsi_dh_data, struct rdac_dh_data, dh_data);
-}
-
 static struct request *get_rdac_req(struct scsi_device *sdev,
                        void *buffer, unsigned buflen, int rw)
 {
@@ -544,7 +538,7 @@ static int mode_select_handle_sense(struct scsi_device *sdev,
 {
        struct scsi_sense_hdr sense_hdr;
        int err = SCSI_DH_IO, ret;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
+       struct rdac_dh_data *h = sdev->handler_data;
 
        ret = scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sense_hdr);
        if (!ret)
@@ -589,7 +583,7 @@ static void send_mode_select(struct work_struct *work)
                container_of(work, struct rdac_controller, ms_work);
        struct request *rq;
        struct scsi_device *sdev = ctlr->ms_sdev;
-       struct rdac_dh_data *h = get_rdac_data(sdev);
+       struct rdac_dh_data *h = sdev->handler_data;
        struct request_queue *q = sdev->request_queue;
        int err, retry_cnt = RDAC_RETRY_COUNT;
        struct rdac_queue_data *tmp, *qdata;
@@ -648,7 +642,7 @@ static int queue_mode_select(struct scsi_device *sdev,
        if (!qdata)
                return SCSI_DH_RETRY;
 
-       qdata->h = get_rdac_data(sdev);
+       qdata->h = sdev->handler_data;
        qdata->callback_fn = fn;
        qdata->callback_data = data;
 
@@ -667,7 +661,7 @@ static int queue_mode_select(struct scsi_device *sdev,
 static int rdac_activate(struct scsi_device *sdev,
                        activate_complete fn, void *data)
 {
-       struct rdac_dh_data *h = get_rdac_data(sdev);
+       struct rdac_dh_data *h = sdev->handler_data;
        int err = SCSI_DH_OK;
        int act = 0;
 
@@ -702,7 +696,7 @@ done:
 
 static int rdac_prep_fn(struct scsi_device *sdev, struct request *req)
 {
-       struct rdac_dh_data *h = get_rdac_data(sdev);
+       struct rdac_dh_data *h = sdev->handler_data;
        int ret = BLKPREP_OK;
 
        if (h->state != RDAC_STATE_ACTIVE) {
@@ -716,7 +710,7 @@ static int rdac_prep_fn(struct scsi_device *sdev, struct request *req)
 static int rdac_check_sense(struct scsi_device *sdev,
                                struct scsi_sense_hdr *sense_hdr)
 {
-       struct rdac_dh_data *h = get_rdac_data(sdev);
+       struct rdac_dh_data *h = sdev->handler_data;
 
        RDAC_LOG(RDAC_LOG_SENSE, sdev, "array %s, ctlr %d, "
                        "I/O returned with sense %02x/%02x/%02x",
@@ -778,56 +772,7 @@ static int rdac_check_sense(struct scsi_device *sdev,
        return SCSI_RETURN_NOT_HANDLED;
 }
 
-static const struct {
-       char *vendor;
-       char *model;
-} rdac_dev_list[] = {
-       {"IBM", "1722"},
-       {"IBM", "1724"},
-       {"IBM", "1726"},
-       {"IBM", "1742"},
-       {"IBM", "1745"},
-       {"IBM", "1746"},
-       {"IBM", "1813"},
-       {"IBM", "1814"},
-       {"IBM", "1815"},
-       {"IBM", "1818"},
-       {"IBM", "3526"},
-       {"SGI", "TP9"},
-       {"SGI", "IS"},
-       {"STK", "OPENstorage D280"},
-       {"STK", "FLEXLINE 380"},
-       {"SUN", "CSM"},
-       {"SUN", "LCSM100"},
-       {"SUN", "STK6580_6780"},
-       {"SUN", "SUN_6180"},
-       {"SUN", "ArrayStorage"},
-       {"DELL", "MD3"},
-       {"NETAPP", "INF-01-00"},
-       {"LSI", "INF-01-00"},
-       {"ENGENIO", "INF-01-00"},
-       {NULL, NULL},
-};
-
-static bool rdac_match(struct scsi_device *sdev)
-{
-       int i;
-
-       if (scsi_device_tpgs(sdev))
-               return false;
-
-       for (i = 0; rdac_dev_list[i].vendor; i++) {
-               if (!strncmp(sdev->vendor, rdac_dev_list[i].vendor,
-                       strlen(rdac_dev_list[i].vendor)) &&
-                   !strncmp(sdev->model, rdac_dev_list[i].model,
-                       strlen(rdac_dev_list[i].model))) {
-                       return true;
-               }
-       }
-       return false;
-}
-
-static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev)
+static int rdac_bus_attach(struct scsi_device *sdev)
 {
        struct rdac_dh_data *h;
        int err;
@@ -836,7 +781,7 @@ static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev)
 
        h = kzalloc(sizeof(*h) , GFP_KERNEL);
        if (!h)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
        h->lun = UNINITIALIZED_LUN;
        h->state = RDAC_STATE_ACTIVE;
 
@@ -861,7 +806,8 @@ static struct scsi_dh_data *rdac_bus_attach(struct scsi_device *sdev)
                    RDAC_NAME, h->lun, mode[(int)h->mode],
                    lun_state[(int)h->lun_state]);
 
-       return &h->dh_data;
+       sdev->handler_data = h;
+       return 0;
 
 clean_ctlr:
        spin_lock(&list_lock);
@@ -870,12 +816,12 @@ clean_ctlr:
 
 failed:
        kfree(h);
-       return ERR_PTR(-EINVAL);
+       return -EINVAL;
 }
 
 static void rdac_bus_detach( struct scsi_device *sdev )
 {
-       struct rdac_dh_data *h = get_rdac_data(sdev);
+       struct rdac_dh_data *h = sdev->handler_data;
 
        if (h->ctlr && h->ctlr->ms_queued)
                flush_workqueue(kmpath_rdacd);
@@ -884,6 +830,7 @@ static void rdac_bus_detach( struct scsi_device *sdev )
        if (h->ctlr)
                kref_put(&h->ctlr->kref, release_controller);
        spin_unlock(&list_lock);
+       sdev->handler_data = NULL;
        kfree(h);
 }
 
@@ -895,7 +842,6 @@ static struct scsi_device_handler rdac_dh = {
        .attach = rdac_bus_attach,
        .detach = rdac_bus_detach,
        .activate = rdac_activate,
-       .match = rdac_match,
 };
 
 static int __init rdac_init(void)
index ec193a8..d3eb80c 100644 (file)
@@ -364,7 +364,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
         * on the ethertype for the given device
         */
        fcoe->fcoe_packet_type.func = fcoe_rcv;
-       fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+       fcoe->fcoe_packet_type.type = htons(ETH_P_FCOE);
        fcoe->fcoe_packet_type.dev = netdev;
        dev_add_pack(&fcoe->fcoe_packet_type);
 
index 3411919..b62836d 100644 (file)
@@ -4555,7 +4555,7 @@ static ssize_t ipr_store_raw_mode(struct device *dev,
        spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
        res = (struct ipr_resource_entry *)sdev->hostdata;
        if (res) {
-               if (ioa_cfg->sis64 && ipr_is_af_dasd_device(res)) {
+               if (ipr_is_af_dasd_device(res)) {
                        res->raw_mode = simple_strtoul(buf, NULL, 10);
                        len = strlen(buf);
                        if (res->sdev)
@@ -6383,9 +6383,13 @@ static int ipr_queuecommand(struct Scsi_Host *shost,
            (!ipr_is_gscsi(res) || scsi_cmd->cmnd[0] == IPR_QUERY_RSRC_STATE)) {
                ioarcb->cmd_pkt.request_type = IPR_RQTYPE_IOACMD;
        }
-       if (res->raw_mode && ipr_is_af_dasd_device(res))
+       if (res->raw_mode && ipr_is_af_dasd_device(res)) {
                ioarcb->cmd_pkt.request_type = IPR_RQTYPE_PIPE;
 
+               if (scsi_cmd->underflow == 0)
+                       ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK;
+       }
+
        if (ioa_cfg->sis64)
                rc = ipr_build_ioadl64(ioa_cfg, ipr_cmd);
        else
index 98d9bb6..33c74d3 100644 (file)
@@ -853,12 +853,9 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                                     SAM_STAT_CHECK_CONDITION;
                        scsi_build_sense_buffer(1, sc->sense_buffer,
                                                ILLEGAL_REQUEST, 0x10, ascq);
-                       sc->sense_buffer[7] = 0xc; /* Additional sense length */
-                       sc->sense_buffer[8] = 0;   /* Information desc type */
-                       sc->sense_buffer[9] = 0xa; /* Additional desc length */
-                       sc->sense_buffer[10] = 0x80; /* Validity bit */
-
-                       put_unaligned_be64(sector, &sc->sense_buffer[12]);
+                       scsi_set_sense_information(sc->sense_buffer,
+                                                  SCSI_SENSE_BUFFERSIZE,
+                                                  sector);
                        goto out;
                }
        }
index eb62772..4abb93a 100644 (file)
@@ -2284,7 +2284,7 @@ lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
                        (struct lpfc_rdp_context *)(mbox->context2);
 
        if (bf_get(lpfc_mqe_status, &mbox->u.mqe))
-               goto error;
+               goto error_mbuf_free;
 
        lpfc_sli_bemem_bcopy(mp->virt, &rdp_context->page_a2,
                                DMP_SFF_PAGE_A2_SIZE);
@@ -2299,13 +2299,14 @@ lpfc_mbx_cmpl_rdp_page_a2(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
        mbox->mbox_cmpl = lpfc_mbx_cmpl_rdp_link_stat;
        mbox->context2 = (struct lpfc_rdp_context *) rdp_context;
        if (lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT) == MBX_NOT_FINISHED)
-               goto error;
+               goto error_cmd_free;
 
        return;
 
-error:
+error_mbuf_free:
        lpfc_mbuf_free(phba, mp->virt, mp->phys);
        kfree(mp);
+error_cmd_free:
        lpfc_sli4_mbox_cmd_free(phba, mbox);
        rdp_context->cmpl(phba, rdp_context, FAILURE);
 }
index 6dec7cf..c167911 100644 (file)
@@ -112,9 +112,12 @@ _scsih_set_fwfault_debug(const char *val, struct kernel_param *kp)
        if (ret)
                return ret;
 
+       /* global ioc spinlock to protect controller list on list operations */
        printk(KERN_INFO "setting fwfault_debug(%d)\n", mpt2sas_fwfault_debug);
+       spin_lock(&gioc_lock);
        list_for_each_entry(ioc, &mpt2sas_ioc_list, list)
                ioc->fwfault_debug = mpt2sas_fwfault_debug;
+       spin_unlock(&gioc_lock);
        return 0;
 }
 
@@ -4437,6 +4440,8 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc)
        dexitprintk(ioc, printk(MPT2SAS_INFO_FMT "%s\n", ioc->name,
            __func__));
 
+       /* synchronizing freeing resource with pci_access_mutex lock */
+       mutex_lock(&ioc->pci_access_mutex);
        if (ioc->chip_phys && ioc->chip) {
                _base_mask_interrupts(ioc);
                ioc->shost_recovery = 1;
@@ -4456,6 +4461,7 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc)
                pci_disable_pcie_error_reporting(pdev);
                pci_disable_device(pdev);
        }
+       mutex_unlock(&ioc->pci_access_mutex);
        return;
 }
 
index caff8d1..97ea360 100644 (file)
  * @flags: MPT_TARGET_FLAGS_XXX flags
  * @deleted: target flaged for deletion
  * @tm_busy: target is busy with TM request.
+ * @sdev: The sas_device associated with this target
  */
 struct MPT2SAS_TARGET {
        struct scsi_target *starget;
@@ -248,6 +249,7 @@ struct MPT2SAS_TARGET {
        u32     flags;
        u8      deleted;
        u8      tm_busy;
+       struct _sas_device *sdev;
 };
 
 
@@ -376,8 +378,24 @@ struct _sas_device {
        u8      phy;
        u8      responding;
        u8      pfa_led_on;
+       struct kref refcount;
 };
 
+static inline void sas_device_get(struct _sas_device *s)
+{
+       kref_get(&s->refcount);
+}
+
+static inline void sas_device_free(struct kref *r)
+{
+       kfree(container_of(r, struct _sas_device, refcount));
+}
+
+static inline void sas_device_put(struct _sas_device *s)
+{
+       kref_put(&s->refcount, sas_device_free);
+}
+
 /**
  * struct _raid_device - raid volume link list
  * @list: sas device list
@@ -799,6 +817,12 @@ typedef void (*MPT2SAS_FLUSH_RUNNING_CMDS)(struct MPT2SAS_ADAPTER *ioc);
  * @delayed_tr_list: target reset link list
  * @delayed_tr_volume_list: volume target reset link list
  * @@temp_sensors_count: flag to carry the number of temperature sensors
+ * @pci_access_mutex: Mutex to synchronize ioctl,sysfs show path and
+ * pci resource handling. PCI resource freeing will lead to free
+ * vital hardware/memory resource, which might be in use by cli/sysfs
+ * path functions resulting in Null pointer reference followed by kernel
+ * crash. To avoid the above race condition we use mutex syncrhonization
+ * which ensures the syncrhonization between cli/sysfs_show path
  */
 struct MPT2SAS_ADAPTER {
        struct list_head list;
@@ -1015,6 +1039,7 @@ struct MPT2SAS_ADAPTER {
        u8              mfg_pg10_hide_flag;
        u8              hide_drives;
 
+       struct mutex pci_access_mutex;
 };
 
 typedef u8 (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
@@ -1023,6 +1048,17 @@ typedef u8 (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
 
 /* base shared API */
 extern struct list_head mpt2sas_ioc_list;
+/* spinlock on list operations over IOCs
+ * Case: when multiple warpdrive cards(IOCs) are in use
+ * Each IOC will added to the ioc list stucture on initialization.
+ * Watchdog threads run at regular intervals to check IOC for any
+ * fault conditions which will trigger the dead_ioc thread to
+ * deallocate pci resource, resulting deleting the IOC netry from list,
+ * this deletion need to protected by spinlock to enusre that
+ * ioc removal is syncrhonized, if not synchronized it might lead to
+ * list_del corruption as the ioc list is traversed in cli path
+ */
+extern spinlock_t gioc_lock;
 void mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc);
 void mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc);
 
@@ -1095,11 +1131,12 @@ struct _sas_node *mpt2sas_scsih_expander_find_by_handle(struct MPT2SAS_ADAPTER *
     u16 handle);
 struct _sas_node *mpt2sas_scsih_expander_find_by_sas_address(struct MPT2SAS_ADAPTER
     *ioc, u64 sas_address);
-struct _sas_device *mpt2sas_scsih_sas_device_find_by_sas_address(
+struct _sas_device *mpt2sas_get_sdev_by_addr(
+    struct MPT2SAS_ADAPTER *ioc, u64 sas_address);
+struct _sas_device *__mpt2sas_get_sdev_by_addr(
     struct MPT2SAS_ADAPTER *ioc, u64 sas_address);
 
 void mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc);
-
 void mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase);
 
 /* config shared API */
index 4e50960..3694b63 100644 (file)
@@ -427,13 +427,16 @@ static int
 _ctl_verify_adapter(int ioc_number, struct MPT2SAS_ADAPTER **iocpp)
 {
        struct MPT2SAS_ADAPTER *ioc;
-
+       /* global ioc lock to protect controller on list operations */
+       spin_lock(&gioc_lock);
        list_for_each_entry(ioc, &mpt2sas_ioc_list, list) {
                if (ioc->id != ioc_number)
                        continue;
+               spin_unlock(&gioc_lock);
                *iocpp = ioc;
                return ioc_number;
        }
+       spin_unlock(&gioc_lock);
        *iocpp = NULL;
        return -1;
 }
@@ -522,10 +525,15 @@ _ctl_poll(struct file *filep, poll_table *wait)
 
        poll_wait(filep, &ctl_poll_wait, wait);
 
+       /* global ioc lock to protect controller on list operations */
+       spin_lock(&gioc_lock);
        list_for_each_entry(ioc, &mpt2sas_ioc_list, list) {
-               if (ioc->aen_event_read_flag)
+               if (ioc->aen_event_read_flag) {
+                       spin_unlock(&gioc_lock);
                        return POLLIN | POLLRDNORM;
+               }
        }
+       spin_unlock(&gioc_lock);
        return 0;
 }
 
@@ -2168,16 +2176,23 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg,
 
        if (_ctl_verify_adapter(ioctl_header.ioc_number, &ioc) == -1 || !ioc)
                return -ENODEV;
+       /* pci_access_mutex lock acquired by ioctl path */
+       mutex_lock(&ioc->pci_access_mutex);
        if (ioc->shost_recovery || ioc->pci_error_recovery ||
-           ioc->is_driver_loading)
-               return -EAGAIN;
+               ioc->is_driver_loading || ioc->remove_host) {
+               ret = -EAGAIN;
+               goto out_unlock_pciaccess;
+       }
 
        state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING;
        if (state == NON_BLOCKING) {
-               if (!mutex_trylock(&ioc->ctl_cmds.mutex))
-                       return -EAGAIN;
+               if (!mutex_trylock(&ioc->ctl_cmds.mutex)) {
+                       ret = -EAGAIN;
+                       goto out_unlock_pciaccess;
+               }
        } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) {
-               return -ERESTARTSYS;
+               ret = -ERESTARTSYS;
+               goto out_unlock_pciaccess;
        }
 
        switch (cmd) {
@@ -2258,6 +2273,8 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg,
        }
 
        mutex_unlock(&ioc->ctl_cmds.mutex);
+out_unlock_pciaccess:
+       mutex_unlock(&ioc->pci_access_mutex);
        return ret;
 }
 
@@ -2711,6 +2728,12 @@ _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr,
                    "warpdrive\n", ioc->name, __func__);
                goto out;
        }
+       /* pci_access_mutex lock acquired by sysfs show path */
+       mutex_lock(&ioc->pci_access_mutex);
+       if (ioc->pci_error_recovery || ioc->remove_host) {
+               mutex_unlock(&ioc->pci_access_mutex);
+               return 0;
+       }
 
        /* allocate upto GPIOVal 36 entries */
        sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36);
@@ -2749,6 +2772,7 @@ _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr,
 
  out:
        kfree(io_unit_pg3);
+       mutex_unlock(&ioc->pci_access_mutex);
        return rc;
 }
 static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL);
index 3f26147..0ad09b2 100644 (file)
@@ -79,7 +79,8 @@ static int _scsih_scan_finished(struct Scsi_Host *shost, unsigned long time);
 
 /* global parameters */
 LIST_HEAD(mpt2sas_ioc_list);
-
+/* global ioc lock for list operations */
+DEFINE_SPINLOCK(gioc_lock);
 /* local parameters */
 static u8 scsi_io_cb_idx = -1;
 static u8 tm_cb_idx = -1;
@@ -176,9 +177,37 @@ struct fw_event_work {
        u8                      VP_ID;
        u8                      ignore;
        u16                     event;
+       struct kref             refcount;
        char                    event_data[0] __aligned(4);
 };
 
+static void fw_event_work_free(struct kref *r)
+{
+       kfree(container_of(r, struct fw_event_work, refcount));
+}
+
+static void fw_event_work_get(struct fw_event_work *fw_work)
+{
+       kref_get(&fw_work->refcount);
+}
+
+static void fw_event_work_put(struct fw_event_work *fw_work)
+{
+       kref_put(&fw_work->refcount, fw_event_work_free);
+}
+
+static struct fw_event_work *alloc_fw_event_work(int len)
+{
+       struct fw_event_work *fw_event;
+
+       fw_event = kzalloc(sizeof(*fw_event) + len, GFP_ATOMIC);
+       if (!fw_event)
+               return NULL;
+
+       kref_init(&fw_event->refcount);
+       return fw_event;
+}
+
 /* raid transport support */
 static struct raid_template *mpt2sas_raid_template;
 
@@ -293,8 +322,10 @@ _scsih_set_debug_level(const char *val, struct kernel_param *kp)
                return ret;
 
        printk(KERN_INFO "setting logging_level(0x%08x)\n", logging_level);
+       spin_lock(&gioc_lock);
        list_for_each_entry(ioc, &mpt2sas_ioc_list, list)
                ioc->logging_level = logging_level;
+       spin_unlock(&gioc_lock);
        return 0;
 }
 module_param_call(logging_level, _scsih_set_debug_level, param_get_int,
@@ -526,8 +557,61 @@ _scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc,
        }
 }
 
+static struct _sas_device *
+__mpt2sas_get_sdev_from_target(struct MPT2SAS_ADAPTER *ioc,
+               struct MPT2SAS_TARGET *tgt_priv)
+{
+       struct _sas_device *ret;
+
+       assert_spin_locked(&ioc->sas_device_lock);
+
+       ret = tgt_priv->sdev;
+       if (ret)
+               sas_device_get(ret);
+
+       return ret;
+}
+
+static struct _sas_device *
+mpt2sas_get_sdev_from_target(struct MPT2SAS_ADAPTER *ioc,
+               struct MPT2SAS_TARGET *tgt_priv)
+{
+       struct _sas_device *ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       ret = __mpt2sas_get_sdev_from_target(ioc, tgt_priv);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       return ret;
+}
+
+
+struct _sas_device *
+__mpt2sas_get_sdev_by_addr(struct MPT2SAS_ADAPTER *ioc,
+    u64 sas_address)
+{
+       struct _sas_device *sas_device;
+
+       assert_spin_locked(&ioc->sas_device_lock);
+
+       list_for_each_entry(sas_device, &ioc->sas_device_list, list)
+               if (sas_device->sas_address == sas_address)
+                       goto found_device;
+
+       list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
+               if (sas_device->sas_address == sas_address)
+                       goto found_device;
+
+       return NULL;
+
+found_device:
+       sas_device_get(sas_device);
+       return sas_device;
+}
+
 /**
- * mpt2sas_scsih_sas_device_find_by_sas_address - sas device search
+ * mpt2sas_get_sdev_by_addr - sas device search
  * @ioc: per adapter object
  * @sas_address: sas address
  * Context: Calling function should acquire ioc->sas_device_lock
@@ -536,24 +620,44 @@ _scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc,
  * object.
  */
 struct _sas_device *
-mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
+mpt2sas_get_sdev_by_addr(struct MPT2SAS_ADAPTER *ioc,
     u64 sas_address)
 {
        struct _sas_device *sas_device;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device = __mpt2sas_get_sdev_by_addr(ioc,
+                       sas_address);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       return sas_device;
+}
+
+static struct _sas_device *
+__mpt2sas_get_sdev_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+{
+       struct _sas_device *sas_device;
+
+       assert_spin_locked(&ioc->sas_device_lock);
 
        list_for_each_entry(sas_device, &ioc->sas_device_list, list)
-               if (sas_device->sas_address == sas_address)
-                       return sas_device;
+               if (sas_device->handle == handle)
+                       goto found_device;
 
        list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
-               if (sas_device->sas_address == sas_address)
-                       return sas_device;
+               if (sas_device->handle == handle)
+                       goto found_device;
 
        return NULL;
+
+found_device:
+       sas_device_get(sas_device);
+       return sas_device;
 }
 
 /**
- * _scsih_sas_device_find_by_handle - sas device search
+ * mpt2sas_get_sdev_by_handle - sas device search
  * @ioc: per adapter object
  * @handle: sas device handle (assigned by firmware)
  * Context: Calling function should acquire ioc->sas_device_lock
@@ -562,19 +666,16 @@ mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
  * object.
  */
 static struct _sas_device *
-_scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
+mpt2sas_get_sdev_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 {
        struct _sas_device *sas_device;
+       unsigned long flags;
 
-       list_for_each_entry(sas_device, &ioc->sas_device_list, list)
-               if (sas_device->handle == handle)
-                       return sas_device;
-
-       list_for_each_entry(sas_device, &ioc->sas_device_init_list, list)
-               if (sas_device->handle == handle)
-                       return sas_device;
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle);
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-       return NULL;
+       return sas_device;
 }
 
 /**
@@ -583,7 +684,7 @@ _scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
  * @sas_device: the sas_device object
  * Context: This function will acquire ioc->sas_device_lock.
  *
- * Removing object and freeing associated memory from the ioc->sas_device_list.
+ * If sas_device is on the list, remove it and decrement its reference count.
  */
 static void
 _scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc,
@@ -594,9 +695,15 @@ _scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc,
        if (!sas_device)
                return;
 
+       /*
+        * The lock serializes access to the list, but we still need to verify
+        * that nobody removed the entry while we were waiting on the lock.
+        */
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       list_del(&sas_device->list);
-       kfree(sas_device);
+       if (!list_empty(&sas_device->list)) {
+               list_del_init(&sas_device->list);
+               sas_device_put(sas_device);
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 }
 
@@ -620,6 +727,7 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,
            sas_device->handle, (unsigned long long)sas_device->sas_address));
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device_get(sas_device);
        list_add_tail(&sas_device->list, &ioc->sas_device_list);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
@@ -659,6 +767,7 @@ _scsih_sas_device_init_add(struct MPT2SAS_ADAPTER *ioc,
            sas_device->handle, (unsigned long long)sas_device->sas_address));
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       sas_device_get(sas_device);
        list_add_tail(&sas_device->list, &ioc->sas_device_init_list);
        _scsih_determine_boot_device(ioc, sas_device, 0);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
@@ -1208,12 +1317,15 @@ _scsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
                goto not_sata;
        if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))
                goto not_sata;
+
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
-          sas_device_priv_data->sas_target->sas_address);
-       if (sas_device && sas_device->device_info &
-           MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
-               max_depth = MPT2SAS_SATA_QUEUE_DEPTH;
+       sas_device = __mpt2sas_get_sdev_from_target(ioc, sas_target_priv_data);
+       if (sas_device) {
+               if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SATA_DEVICE)
+                       max_depth = MPT2SAS_SATA_QUEUE_DEPTH;
+
+               sas_device_put(sas_device);
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
  not_sata:
@@ -1271,18 +1383,20 @@ _scsih_target_alloc(struct scsi_target *starget)
        /* sas/sata devices */
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        rphy = dev_to_rphy(starget->dev.parent);
-       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+       sas_device = __mpt2sas_get_sdev_by_addr(ioc,
           rphy->identify.sas_address);
 
        if (sas_device) {
                sas_target_priv_data->handle = sas_device->handle;
                sas_target_priv_data->sas_address = sas_device->sas_address;
+               sas_target_priv_data->sdev = sas_device;
                sas_device->starget = starget;
                sas_device->id = starget->id;
                sas_device->channel = starget->channel;
                if (test_bit(sas_device->handle, ioc->pd_handles))
                        sas_target_priv_data->flags |=
                            MPT_TARGET_FLAGS_RAID_COMPONENT;
+
        }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
@@ -1324,13 +1438,21 @@ _scsih_target_destroy(struct scsi_target *starget)
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        rphy = dev_to_rphy(starget->dev.parent);
-       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
-          rphy->identify.sas_address);
+       sas_device = __mpt2sas_get_sdev_from_target(ioc, sas_target_priv_data);
        if (sas_device && (sas_device->starget == starget) &&
            (sas_device->id == starget->id) &&
            (sas_device->channel == starget->channel))
                sas_device->starget = NULL;
 
+       if (sas_device) {
+               /*
+                * Corresponding get() is in _scsih_target_alloc()
+                */
+               sas_target_priv_data->sdev = NULL;
+               sas_device_put(sas_device);
+
+               sas_device_put(sas_device);
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
  out:
@@ -1386,7 +1508,7 @@ _scsih_slave_alloc(struct scsi_device *sdev)
 
        if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
                spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+               sas_device = __mpt2sas_get_sdev_by_addr(ioc,
                                sas_target_priv_data->sas_address);
                if (sas_device && (sas_device->starget == NULL)) {
                        sdev_printk(KERN_INFO, sdev,
@@ -1394,6 +1516,10 @@ _scsih_slave_alloc(struct scsi_device *sdev)
                             __func__, __LINE__);
                        sas_device->starget = starget;
                }
+
+               if (sas_device)
+                       sas_device_put(sas_device);
+
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
 
@@ -1428,10 +1554,13 @@ _scsih_slave_destroy(struct scsi_device *sdev)
 
        if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) {
                spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
-                  sas_target_priv_data->sas_address);
+               sas_device = __mpt2sas_get_sdev_from_target(ioc,
+                               sas_target_priv_data);
                if (sas_device && !sas_target_priv_data->num_luns)
                        sas_device->starget = NULL;
+
+               if (sas_device)
+                       sas_device_put(sas_device);
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
 
@@ -2078,7 +2207,7 @@ _scsih_slave_configure(struct scsi_device *sdev)
        }
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+       sas_device = __mpt2sas_get_sdev_by_addr(ioc,
           sas_device_priv_data->sas_target->sas_address);
        if (!sas_device) {
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
@@ -2112,17 +2241,18 @@ _scsih_slave_configure(struct scsi_device *sdev)
            (unsigned long long) sas_device->enclosure_logical_id,
            sas_device->slot);
 
+       sas_device_put(sas_device);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        if (!ssp_target)
                _scsih_display_sata_capabilities(ioc, handle, sdev);
 
-
        _scsih_change_queue_depth(sdev, qdepth);
 
        if (ssp_target) {
                sas_read_port_mode_page(sdev);
                _scsih_enable_tlr(ioc, sdev);
        }
+
        return 0;
 }
 
@@ -2509,8 +2639,7 @@ _scsih_tm_display_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
                    device_str, (unsigned long long)priv_target->sas_address);
        } else {
                spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
-                   priv_target->sas_address);
+               sas_device = __mpt2sas_get_sdev_from_target(ioc, priv_target);
                if (sas_device) {
                        if (priv_target->flags &
                            MPT_TARGET_FLAGS_RAID_COMPONENT) {
@@ -2529,6 +2658,8 @@ _scsih_tm_display_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
                            "enclosure_logical_id(0x%016llx), slot(%d)\n",
                           (unsigned long long)sas_device->enclosure_logical_id,
                            sas_device->slot);
+
+                       sas_device_put(sas_device);
                }
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
@@ -2604,12 +2735,12 @@ _scsih_dev_reset(struct scsi_cmnd *scmd)
 {
        struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
        struct MPT2SAS_DEVICE *sas_device_priv_data;
-       struct _sas_device *sas_device;
-       unsigned long flags;
+       struct _sas_device *sas_device = NULL;
        u16     handle;
        int r;
 
        struct scsi_target *starget = scmd->device->sdev_target;
+       struct MPT2SAS_TARGET *target_priv_data = starget->hostdata;
 
        starget_printk(KERN_INFO, starget, "attempting device reset! "
            "scmd(%p)\n", scmd);
@@ -2629,12 +2760,10 @@ _scsih_dev_reset(struct scsi_cmnd *scmd)
        handle = 0;
        if (sas_device_priv_data->sas_target->flags &
            MPT_TARGET_FLAGS_RAID_COMPONENT) {
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = _scsih_sas_device_find_by_handle(ioc,
-                  sas_device_priv_data->sas_target->handle);
+               sas_device = mpt2sas_get_sdev_from_target(ioc,
+                               target_priv_data);
                if (sas_device)
                        handle = sas_device->volume_handle;
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        } else
                handle = sas_device_priv_data->sas_target->handle;
 
@@ -2651,6 +2780,10 @@ _scsih_dev_reset(struct scsi_cmnd *scmd)
  out:
        sdev_printk(KERN_INFO, scmd->device, "device reset: %s scmd(%p)\n",
            ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+       if (sas_device)
+               sas_device_put(sas_device);
+
        return r;
 }
 
@@ -2665,11 +2798,11 @@ _scsih_target_reset(struct scsi_cmnd *scmd)
 {
        struct MPT2SAS_ADAPTER *ioc = shost_priv(scmd->device->host);
        struct MPT2SAS_DEVICE *sas_device_priv_data;
-       struct _sas_device *sas_device;
-       unsigned long flags;
+       struct _sas_device *sas_device = NULL;
        u16     handle;
        int r;
        struct scsi_target *starget = scmd->device->sdev_target;
+       struct MPT2SAS_TARGET *target_priv_data = starget->hostdata;
 
        starget_printk(KERN_INFO, starget, "attempting target reset! "
            "scmd(%p)\n", scmd);
@@ -2689,12 +2822,10 @@ _scsih_target_reset(struct scsi_cmnd *scmd)
        handle = 0;
        if (sas_device_priv_data->sas_target->flags &
            MPT_TARGET_FLAGS_RAID_COMPONENT) {
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = _scsih_sas_device_find_by_handle(ioc,
-                  sas_device_priv_data->sas_target->handle);
+               sas_device = mpt2sas_get_sdev_from_target(ioc,
+                               target_priv_data);
                if (sas_device)
                        handle = sas_device->volume_handle;
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        } else
                handle = sas_device_priv_data->sas_target->handle;
 
@@ -2711,6 +2842,10 @@ _scsih_target_reset(struct scsi_cmnd *scmd)
  out:
        starget_printk(KERN_INFO, starget, "target reset: %s scmd(%p)\n",
            ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd);
+
+       if (sas_device)
+               sas_device_put(sas_device);
+
        return r;
 }
 
@@ -2768,36 +2903,39 @@ _scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event)
                return;
 
        spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       fw_event_work_get(fw_event);
        list_add_tail(&fw_event->list, &ioc->fw_event_list);
        INIT_DELAYED_WORK(&fw_event->delayed_work, _firmware_event_work);
+       fw_event_work_get(fw_event);
        queue_delayed_work(ioc->firmware_event_thread,
            &fw_event->delayed_work, 0);
        spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
 }
 
 /**
- * _scsih_fw_event_free - delete fw_event
+ * _scsih_fw_event_del_from_list - delete fw_event from the list
  * @ioc: per adapter object
  * @fw_event: object describing the event
  * Context: This function will acquire ioc->fw_event_lock.
  *
- * This removes firmware event object from link list, frees associated memory.
+ * If the fw_event is on the fw_event_list, remove it and do a put.
  *
  * Return nothing.
  */
 static void
-_scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work
+_scsih_fw_event_del_from_list(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work
     *fw_event)
 {
        unsigned long flags;
 
        spin_lock_irqsave(&ioc->fw_event_lock, flags);
-       list_del(&fw_event->list);
-       kfree(fw_event);
+       if (!list_empty(&fw_event->list)) {
+               list_del_init(&fw_event->list);
+               fw_event_work_put(fw_event);
+       }
        spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
 }
 
-
 /**
  * _scsih_error_recovery_delete_devices - remove devices not responding
  * @ioc: per adapter object
@@ -2812,13 +2950,14 @@ _scsih_error_recovery_delete_devices(struct MPT2SAS_ADAPTER *ioc)
        if (ioc->is_driver_loading)
                return;
 
-       fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+       fw_event = alloc_fw_event_work(0);
        if (!fw_event)
                return;
 
        fw_event->event = MPT2SAS_REMOVE_UNRESPONDING_DEVICES;
        fw_event->ioc = ioc;
        _scsih_fw_event_add(ioc, fw_event);
+       fw_event_work_put(fw_event);
 }
 
 /**
@@ -2832,12 +2971,29 @@ mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc)
 {
        struct fw_event_work *fw_event;
 
-       fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+       fw_event = alloc_fw_event_work(0);
        if (!fw_event)
                return;
        fw_event->event = MPT2SAS_PORT_ENABLE_COMPLETE;
        fw_event->ioc = ioc;
        _scsih_fw_event_add(ioc, fw_event);
+       fw_event_work_put(fw_event);
+}
+
+static struct fw_event_work *dequeue_next_fw_event(struct MPT2SAS_ADAPTER *ioc)
+{
+       unsigned long flags;
+       struct fw_event_work *fw_event = NULL;
+
+       spin_lock_irqsave(&ioc->fw_event_lock, flags);
+       if (!list_empty(&ioc->fw_event_list)) {
+               fw_event = list_first_entry(&ioc->fw_event_list,
+                               struct fw_event_work, list);
+               list_del_init(&fw_event->list);
+       }
+       spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
+
+       return fw_event;
 }
 
 /**
@@ -2852,17 +3008,25 @@ mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc)
 static void
 _scsih_fw_event_cleanup_queue(struct MPT2SAS_ADAPTER *ioc)
 {
-       struct fw_event_work *fw_event, *next;
+       struct fw_event_work *fw_event;
 
        if (list_empty(&ioc->fw_event_list) ||
             !ioc->firmware_event_thread || in_interrupt())
                return;
 
-       list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) {
-               if (cancel_delayed_work_sync(&fw_event->delayed_work)) {
-                       _scsih_fw_event_free(ioc, fw_event);
-                       continue;
-               }
+       while ((fw_event = dequeue_next_fw_event(ioc))) {
+               /*
+                * Wait on the fw_event to complete. If this returns 1, then
+                * the event was never executed, and we need a put for the
+                * reference the delayed_work had on the fw_event.
+                *
+                * If it did execute, we wait for it to finish, and the put will
+                * happen from _firmware_event_work()
+                */
+               if (cancel_delayed_work_sync(&fw_event->delayed_work))
+                       fw_event_work_put(fw_event);
+
+               fw_event_work_put(fw_event);
        }
 }
 
@@ -3002,15 +3166,15 @@ _scsih_block_io_to_children_attached_to_ex(struct MPT2SAS_ADAPTER *ioc,
 
        list_for_each_entry(mpt2sas_port,
           &sas_expander->sas_port_list, port_list) {
-               if (mpt2sas_port->remote_identify.device_type ==
-                   SAS_END_DEVICE) {
+               if (mpt2sas_port->remote_identify.device_type == SAS_END_DEVICE) {
                        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-                       sas_device =
-                           mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
-                          mpt2sas_port->remote_identify.sas_address);
-                       if (sas_device)
+                       sas_device = __mpt2sas_get_sdev_by_addr(ioc,
+                                       mpt2sas_port->remote_identify.sas_address);
+                       if (sas_device) {
                                set_bit(sas_device->handle,
-                                   ioc->blocking_handles);
+                                               ioc->blocking_handles);
+                               sas_device_put(sas_device);
+                       }
                        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
                }
        }
@@ -3080,7 +3244,7 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 {
        Mpi2SCSITaskManagementRequest_t *mpi_request;
        u16 smid;
-       struct _sas_device *sas_device;
+       struct _sas_device *sas_device = NULL;
        struct MPT2SAS_TARGET *sas_target_priv_data = NULL;
        u64 sas_address = 0;
        unsigned long flags;
@@ -3110,7 +3274,7 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
                return;
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle);
        if (sas_device && sas_device->starget &&
             sas_device->starget->hostdata) {
                sas_target_priv_data = sas_device->starget->hostdata;
@@ -3131,14 +3295,14 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        if (!smid) {
                delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC);
                if (!delayed_tr)
-                       return;
+                       goto out;
                INIT_LIST_HEAD(&delayed_tr->list);
                delayed_tr->handle = handle;
                list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list);
                dewtprintk(ioc, printk(MPT2SAS_INFO_FMT
                    "DELAYED:tr:handle(0x%04x), (open)\n",
                    ioc->name, handle));
-               return;
+               goto out;
        }
 
        dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), "
@@ -3150,6 +3314,9 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        mpi_request->DevHandle = cpu_to_le16(handle);
        mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
        mpt2sas_base_put_smid_hi_priority(ioc, smid);
+out:
+       if (sas_device)
+               sas_device_put(sas_device);
 }
 
 
@@ -4068,7 +4235,6 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
        char *desc_scsi_state = ioc->tmp_string;
        u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo);
        struct _sas_device *sas_device = NULL;
-       unsigned long flags;
        struct scsi_target *starget = scmd->device->sdev_target;
        struct MPT2SAS_TARGET *priv_target = starget->hostdata;
        char *device_str = NULL;
@@ -4200,9 +4366,7 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
                printk(MPT2SAS_WARN_FMT "\t%s wwid(0x%016llx)\n", ioc->name,
                    device_str, (unsigned long long)priv_target->sas_address);
        } else {
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
-                   priv_target->sas_address);
+               sas_device = mpt2sas_get_sdev_from_target(ioc, priv_target);
                if (sas_device) {
                        printk(MPT2SAS_WARN_FMT "\tsas_address(0x%016llx), "
                            "phy(%d)\n", ioc->name, sas_device->sas_address,
@@ -4211,8 +4375,9 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
                            "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
                            ioc->name, sas_device->enclosure_logical_id,
                            sas_device->slot);
+
+                       sas_device_put(sas_device);
                }
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
 
        printk(MPT2SAS_WARN_FMT "\thandle(0x%04x), ioc_status(%s)(0x%04x), "
@@ -4259,7 +4424,7 @@ _scsih_turn_on_pfa_led(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        Mpi2SepRequest_t mpi_request;
        struct _sas_device *sas_device;
 
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       sas_device = mpt2sas_get_sdev_by_handle(ioc, handle);
        if (!sas_device)
                return;
 
@@ -4274,7 +4439,7 @@ _scsih_turn_on_pfa_led(struct MPT2SAS_ADAPTER *ioc, u16 handle)
            &mpi_request)) != 0) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name,
                __FILE__, __LINE__, __func__);
-               return;
+               goto out;
        }
        sas_device->pfa_led_on = 1;
 
@@ -4284,8 +4449,10 @@ _scsih_turn_on_pfa_led(struct MPT2SAS_ADAPTER *ioc, u16 handle)
                 "enclosure_processor: ioc_status (0x%04x), loginfo(0x%08x)\n",
                 ioc->name, le16_to_cpu(mpi_reply.IOCStatus),
                 le32_to_cpu(mpi_reply.IOCLogInfo)));
-               return;
+               goto out;
        }
+out:
+       sas_device_put(sas_device);
 }
 
 /**
@@ -4340,13 +4507,14 @@ _scsih_send_event_to_turn_on_pfa_led(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 {
        struct fw_event_work *fw_event;
 
-       fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
+       fw_event = alloc_fw_event_work(0);
        if (!fw_event)
                return;
        fw_event->event = MPT2SAS_TURN_ON_PFA_LED;
        fw_event->device_handle = handle;
        fw_event->ioc = ioc;
        _scsih_fw_event_add(ioc, fw_event);
+       fw_event_work_put(fw_event);
 }
 
 /**
@@ -4370,19 +4538,17 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
        /* only handle non-raid devices */
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle);
        if (!sas_device) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
+               goto out_unlock;
        }
        starget = sas_device->starget;
        sas_target_priv_data = starget->hostdata;
 
        if ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_RAID_COMPONENT) ||
-          ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME))) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
-       }
+          ((sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)))
+               goto out_unlock;
+
        starget_printk(KERN_WARNING, starget, "predicted fault\n");
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
@@ -4396,7 +4562,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        if (!event_reply) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
-               return;
+               goto out;
        }
 
        event_reply->Function = MPI2_FUNCTION_EVENT_NOTIFICATION;
@@ -4413,6 +4579,14 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)
        event_data->SASAddress = cpu_to_le64(sas_target_priv_data->sas_address);
        mpt2sas_ctl_add_to_event_log(ioc, event_reply);
        kfree(event_reply);
+out:
+       if (sas_device)
+               sas_device_put(sas_device);
+       return;
+
+out_unlock:
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+       goto out;
 }
 
 /**
@@ -5148,14 +5322,13 @@ _scsih_check_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        sas_address = le64_to_cpu(sas_device_pg0.SASAddress);
-       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+       sas_device = __mpt2sas_get_sdev_by_addr(ioc,
            sas_address);
 
        if (!sas_device) {
                printk(MPT2SAS_ERR_FMT "device is not present "
                    "handle(0x%04x), no sas_device!!!\n", ioc->name, handle);
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
+               goto out_unlock;
        }
 
        if (unlikely(sas_device->handle != handle)) {
@@ -5172,19 +5345,24 @@ _scsih_check_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
            MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) {
                printk(MPT2SAS_ERR_FMT "device is not present "
                    "handle(0x%04x), flags!!!\n", ioc->name, handle);
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
+               goto out_unlock;
        }
 
        /* check if there were any issues with discovery */
        if (_scsih_check_access_status(ioc, sas_address, handle,
-           sas_device_pg0.AccessStatus)) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
-       }
+           sas_device_pg0.AccessStatus))
+               goto out_unlock;
+
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        _scsih_ublock_io_device(ioc, sas_address);
+       if (sas_device)
+               sas_device_put(sas_device);
+       return;
 
+out_unlock:
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+       if (sas_device)
+               sas_device_put(sas_device);
 }
 
 /**
@@ -5208,7 +5386,6 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
        u32 ioc_status;
        __le64 sas_address;
        u32 device_info;
-       unsigned long flags;
 
        if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
            MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
@@ -5250,14 +5427,13 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
                return -1;
        }
 
-
-       spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+       sas_device = mpt2sas_get_sdev_by_addr(ioc,
            sas_address);
-       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
-       if (sas_device)
+       if (sas_device) {
+               sas_device_put(sas_device);
                return 0;
+       }
 
        sas_device = kzalloc(sizeof(struct _sas_device),
            GFP_KERNEL);
@@ -5267,6 +5443,7 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
                return -1;
        }
 
+       kref_init(&sas_device->refcount);
        sas_device->handle = handle;
        if (_scsih_get_sas_address(ioc, le16_to_cpu
                (sas_device_pg0.ParentDevHandle),
@@ -5296,6 +5473,7 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)
        else
                _scsih_sas_device_add(ioc, sas_device);
 
+       sas_device_put(sas_device);
        return 0;
 }
 
@@ -5344,7 +5522,6 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc,
            "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__,
            sas_device->handle, (unsigned long long)
            sas_device->sas_address));
-       kfree(sas_device);
 }
 /**
  * _scsih_device_remove_by_handle - removing device object by handle
@@ -5363,12 +5540,17 @@ _scsih_device_remove_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)
                return;
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
-       if (sas_device)
-               list_del(&sas_device->list);
+       sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle);
+       if (sas_device) {
+               list_del_init(&sas_device->list);
+               sas_device_put(sas_device);
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-       if (sas_device)
+
+       if (sas_device) {
                _scsih_remove_device(ioc, sas_device);
+               sas_device_put(sas_device);
+       }
 }
 
 /**
@@ -5389,13 +5571,17 @@ mpt2sas_device_remove_by_sas_address(struct MPT2SAS_ADAPTER *ioc,
                return;
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
-           sas_address);
-       if (sas_device)
-               list_del(&sas_device->list);
+       sas_device = __mpt2sas_get_sdev_by_addr(ioc, sas_address);
+       if (sas_device) {
+               list_del_init(&sas_device->list);
+               sas_device_put(sas_device);
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-       if (sas_device)
+
+       if (sas_device) {
                _scsih_remove_device(ioc, sas_device);
+               sas_device_put(sas_device);
+       }
 }
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
 /**
@@ -5716,26 +5902,28 @@ _scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc,
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        sas_address = le64_to_cpu(event_data->SASAddress);
-       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+       sas_device = __mpt2sas_get_sdev_by_addr(ioc,
            sas_address);
 
-       if (!sas_device || !sas_device->starget) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
-       }
+       if (!sas_device || !sas_device->starget)
+               goto out;
 
        target_priv_data = sas_device->starget->hostdata;
-       if (!target_priv_data) {
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               return;
-       }
+       if (!target_priv_data)
+               goto out;
 
        if (event_data->ReasonCode ==
            MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET)
                target_priv_data->tm_busy = 1;
        else
                target_priv_data->tm_busy = 0;
+
+out:
+       if (sas_device)
+               sas_device_put(sas_device);
+
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
 }
 
 #ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -6123,7 +6311,7 @@ _scsih_sas_pd_expose(struct MPT2SAS_ADAPTER *ioc,
        u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle);
        if (sas_device) {
                sas_device->volume_handle = 0;
                sas_device->volume_wwid = 0;
@@ -6142,6 +6330,8 @@ _scsih_sas_pd_expose(struct MPT2SAS_ADAPTER *ioc,
        /* exposing raid component */
        if (starget)
                starget_for_each_device(starget, NULL, _scsih_reprobe_lun);
+
+       sas_device_put(sas_device);
 }
 
 /**
@@ -6170,7 +6360,7 @@ _scsih_sas_pd_hide(struct MPT2SAS_ADAPTER *ioc,
                    &volume_wwid);
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       sas_device = __mpt2sas_get_sdev_by_handle(ioc, handle);
        if (sas_device) {
                set_bit(handle, ioc->pd_handles);
                if (sas_device->starget && sas_device->starget->hostdata) {
@@ -6189,6 +6379,8 @@ _scsih_sas_pd_hide(struct MPT2SAS_ADAPTER *ioc,
        /* hiding raid component */
        if (starget)
                starget_for_each_device(starget, (void *)1, _scsih_reprobe_lun);
+
+       sas_device_put(sas_device);
 }
 
 /**
@@ -6221,7 +6413,6 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
     Mpi2EventIrConfigElement_t *element)
 {
        struct _sas_device *sas_device;
-       unsigned long flags;
        u16 handle = le16_to_cpu(element->PhysDiskDevHandle);
        Mpi2ConfigReply_t mpi_reply;
        Mpi2SasDevicePage0_t sas_device_pg0;
@@ -6231,11 +6422,11 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
 
        set_bit(handle, ioc->pd_handles);
 
-       spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
-       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-       if (sas_device)
+       sas_device = mpt2sas_get_sdev_by_handle(ioc, handle);
+       if (sas_device) {
+               sas_device_put(sas_device);
                return;
+       }
 
        if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
            MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
@@ -6509,7 +6700,6 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
        u16 handle, parent_handle;
        u32 state;
        struct _sas_device *sas_device;
-       unsigned long flags;
        Mpi2ConfigReply_t mpi_reply;
        Mpi2SasDevicePage0_t sas_device_pg0;
        u32 ioc_status;
@@ -6542,12 +6732,11 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,
                if (!ioc->is_warpdrive)
                        set_bit(handle, ioc->pd_handles);
 
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-
-               if (sas_device)
+               sas_device = mpt2sas_get_sdev_by_handle(ioc, handle);
+               if (sas_device) {
+                       sas_device_put(sas_device);
                        return;
+               }
 
                if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
                    &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
@@ -7015,6 +7204,7 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
        struct _raid_device *raid_device, *raid_device_next;
        struct list_head tmp_list;
        unsigned long flags;
+       LIST_HEAD(head);
 
        printk(MPT2SAS_INFO_FMT "removing unresponding devices: start\n",
            ioc->name);
@@ -7022,14 +7212,29 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)
        /* removing unresponding end devices */
        printk(MPT2SAS_INFO_FMT "removing unresponding devices: end-devices\n",
            ioc->name);
+
+       /*
+        * Iterate, pulling off devices marked as non-responding. We become the
+        * owner for the reference the list had on any object we prune.
+        */
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
        list_for_each_entry_safe(sas_device, sas_device_next,
-           &ioc->sas_device_list, list) {
+                       &ioc->sas_device_list, list) {
                if (!sas_device->responding)
-                       mpt2sas_device_remove_by_sas_address(ioc,
-                               sas_device->sas_address);
+                       list_move_tail(&sas_device->list, &head);
                else
                        sas_device->responding = 0;
        }
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       /*
+        * Now, uninitialize and remove the unresponding devices we pruned.
+        */
+       list_for_each_entry_safe(sas_device, sas_device_next, &head, list) {
+               _scsih_remove_device(ioc, sas_device);
+               list_del_init(&sas_device->list);
+               sas_device_put(sas_device);
+       }
 
        /* removing unresponding volumes */
        if (ioc->ir_firmware) {
@@ -7179,11 +7384,11 @@ _scsih_scan_for_devices_after_reset(struct MPT2SAS_ADAPTER *ioc)
                }
                phys_disk_num = pd_pg0.PhysDiskNum;
                handle = le16_to_cpu(pd_pg0.DevHandle);
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               if (sas_device)
+               sas_device = mpt2sas_get_sdev_by_handle(ioc, handle);
+               if (sas_device) {
+                       sas_device_put(sas_device);
                        continue;
+               }
                if (mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply,
                    &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE,
                    handle) != 0)
@@ -7302,12 +7507,12 @@ _scsih_scan_for_devices_after_reset(struct MPT2SAS_ADAPTER *ioc)
                if (!(_scsih_is_end_device(
                    le32_to_cpu(sas_device_pg0.DeviceInfo))))
                        continue;
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+               sas_device = mpt2sas_get_sdev_by_addr(ioc,
                    le64_to_cpu(sas_device_pg0.SASAddress));
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
-               if (sas_device)
+               if (sas_device) {
+                       sas_device_put(sas_device);
                        continue;
+               }
                parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle);
                if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) {
                        printk(MPT2SAS_INFO_FMT "\tBEFORE adding end device: "
@@ -7410,17 +7615,27 @@ _firmware_event_work(struct work_struct *work)
            struct fw_event_work, delayed_work.work);
        struct MPT2SAS_ADAPTER *ioc = fw_event->ioc;
 
+       _scsih_fw_event_del_from_list(ioc, fw_event);
+
        /* the queue is being flushed so ignore this event */
-       if (ioc->remove_host ||
-           ioc->pci_error_recovery) {
-               _scsih_fw_event_free(ioc, fw_event);
+       if (ioc->remove_host || ioc->pci_error_recovery) {
+               fw_event_work_put(fw_event);
                return;
        }
 
        switch (fw_event->event) {
        case MPT2SAS_REMOVE_UNRESPONDING_DEVICES:
-               while (scsi_host_in_recovery(ioc->shost) || ioc->shost_recovery)
+               while (scsi_host_in_recovery(ioc->shost) ||
+                               ioc->shost_recovery) {
+                       /*
+                        * If we're unloading, bail. Otherwise, this can become
+                        * an infinite loop.
+                        */
+                       if (ioc->remove_host)
+                               goto out;
+
                        ssleep(1);
+               }
                _scsih_remove_unresponding_sas_devices(ioc);
                _scsih_scan_for_devices_after_reset(ioc);
                break;
@@ -7469,7 +7684,8 @@ _firmware_event_work(struct work_struct *work)
                _scsih_sas_ir_operation_status_event(ioc, fw_event);
                break;
        }
-       _scsih_fw_event_free(ioc, fw_event);
+out:
+       fw_event_work_put(fw_event);
 }
 
 /**
@@ -7607,7 +7823,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
        }
 
        sz = le16_to_cpu(mpi_reply->EventDataLength) * 4;
-       fw_event = kzalloc(sizeof(*fw_event) + sz, GFP_ATOMIC);
+       fw_event = alloc_fw_event_work(sz);
        if (!fw_event) {
                printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
@@ -7620,6 +7836,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,
        fw_event->VP_ID = mpi_reply->VP_ID;
        fw_event->event = event;
        _scsih_fw_event_add(ioc, fw_event);
+       fw_event_work_put(fw_event);
        return;
 }
 
@@ -7867,7 +8084,9 @@ _scsih_remove(struct pci_dev *pdev)
        sas_remove_host(shost);
        scsi_remove_host(shost);
        mpt2sas_base_detach(ioc);
+       spin_lock(&gioc_lock);
        list_del(&ioc->list);
+       spin_unlock(&gioc_lock);
        scsi_host_put(shost);
 }
 
@@ -7966,6 +8185,48 @@ _scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc)
        }
 }
 
+static struct _sas_device *get_next_sas_device(struct MPT2SAS_ADAPTER *ioc)
+{
+       struct _sas_device *sas_device = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+       if (!list_empty(&ioc->sas_device_init_list)) {
+               sas_device = list_first_entry(&ioc->sas_device_init_list,
+                               struct _sas_device, list);
+               sas_device_get(sas_device);
+       }
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+       return sas_device;
+}
+
+static void sas_device_make_active(struct MPT2SAS_ADAPTER *ioc,
+               struct _sas_device *sas_device)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&ioc->sas_device_lock, flags);
+
+       /*
+        * Since we dropped the lock during the call to port_add(), we need to
+        * be careful here that somebody else didn't move or delete this item
+        * while we were busy with other things.
+        *
+        * If it was on the list, we need a put() for the reference the list
+        * had. Either way, we need a get() for the destination list.
+        */
+       if (!list_empty(&sas_device->list)) {
+               list_del_init(&sas_device->list);
+               sas_device_put(sas_device);
+       }
+
+       sas_device_get(sas_device);
+       list_add_tail(&sas_device->list, &ioc->sas_device_list);
+
+       spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+}
+
 /**
  * _scsih_probe_sas - reporting sas devices to sas transport
  * @ioc: per adapter object
@@ -7975,34 +8236,30 @@ _scsih_probe_raid(struct MPT2SAS_ADAPTER *ioc)
 static void
 _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)
 {
-       struct _sas_device *sas_device, *next;
-       unsigned long flags;
-
-       /* SAS Device List */
-       list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,
-           list) {
+       struct _sas_device *sas_device;
 
-               if (ioc->hide_drives)
-                       continue;
+       if (ioc->hide_drives)
+               return;
 
+       while ((sas_device = get_next_sas_device(ioc))) {
                if (!mpt2sas_transport_port_add(ioc, sas_device->handle,
-                   sas_device->sas_address_parent)) {
-                       list_del(&sas_device->list);
-                       kfree(sas_device);
+                               sas_device->sas_address_parent)) {
+                       _scsih_sas_device_remove(ioc, sas_device);
+                       sas_device_put(sas_device);
                        continue;
                } else if (!sas_device->starget) {
                        if (!ioc->is_driver_loading) {
                                mpt2sas_transport_port_remove(ioc,
-                                       sas_device->sas_address,
-                                       sas_device->sas_address_parent);
-                               list_del(&sas_device->list);
-                               kfree(sas_device);
+                                               sas_device->sas_address,
+                                               sas_device->sas_address_parent);
+                               _scsih_sas_device_remove(ioc, sas_device);
+                               sas_device_put(sas_device);
                                continue;
                        }
                }
-               spin_lock_irqsave(&ioc->sas_device_lock, flags);
-               list_move_tail(&sas_device->list, &ioc->sas_device_list);
-               spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
+
+               sas_device_make_active(ioc, sas_device);
+               sas_device_put(sas_device);
        }
 }
 
@@ -8142,7 +8399,9 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        ioc = shost_priv(shost);
        memset(ioc, 0, sizeof(struct MPT2SAS_ADAPTER));
        INIT_LIST_HEAD(&ioc->list);
+       spin_lock(&gioc_lock);
        list_add_tail(&ioc->list, &mpt2sas_ioc_list);
+       spin_unlock(&gioc_lock);
        ioc->shost = shost;
        ioc->id = mpt_ids++;
        sprintf(ioc->name, "%s%d", MPT2SAS_DRIVER_NAME, ioc->id);
@@ -8167,6 +8426,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds;
        /* misc semaphores and spin locks */
        mutex_init(&ioc->reset_in_progress_mutex);
+       /* initializing pci_access_mutex lock */
+       mutex_init(&ioc->pci_access_mutex);
        spin_lock_init(&ioc->ioc_reset_in_progress_lock);
        spin_lock_init(&ioc->scsi_lookup_lock);
        spin_lock_init(&ioc->sas_device_lock);
@@ -8269,7 +8530,9 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
  out_attach_fail:
        destroy_workqueue(ioc->firmware_event_thread);
  out_thread_fail:
+       spin_lock(&gioc_lock);
        list_del(&ioc->list);
+       spin_unlock(&gioc_lock);
        scsi_host_put(shost);
        return rv;
 }
index ff2500a..af86800 100644 (file)
@@ -1323,15 +1323,17 @@ _transport_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
        int rc;
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+       sas_device = __mpt2sas_get_sdev_by_addr(ioc,
            rphy->identify.sas_address);
        if (sas_device) {
                *identifier = sas_device->enclosure_logical_id;
                rc = 0;
+               sas_device_put(sas_device);
        } else {
                *identifier = 0;
                rc = -ENXIO;
        }
+
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        return rc;
 }
@@ -1351,12 +1353,14 @@ _transport_get_bay_identifier(struct sas_rphy *rphy)
        int rc;
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
-       sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,
+       sas_device = __mpt2sas_get_sdev_by_addr(ioc,
            rphy->identify.sas_address);
-       if (sas_device)
+       if (sas_device) {
                rc = sas_device->slot;
-       else
+               sas_device_put(sas_device);
+       } else {
                rc = -ENXIO;
+       }
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        return rc;
 }
index c34c115..ec27ad2 100644 (file)
@@ -8,7 +8,7 @@
  *                 scatter/gather formats.
  * Creation Date:  June 21, 2006
  *
- * mpi2.h Version:  02.00.31
+ * mpi2.h Version:  02.00.35
  *
  * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
  *       prefix are for use only on MPI v2.5 products, and must not be used
  *                     Added MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET.
  * 04-09-13  02.00.30  Bumped MPI2_HEADER_VERSION_UNIT.
  * 04-17-13  02.00.31  Bumped MPI2_HEADER_VERSION_UNIT.
+ * 08-19-13  02.00.32  Bumped MPI2_HEADER_VERSION_UNIT.
+ * 12-05-13  02.00.33  Bumped MPI2_HEADER_VERSION_UNIT.
+ * 01-08-14  02.00.34  Bumped MPI2_HEADER_VERSION_UNIT
+ * 06-13-14  02.00.35  Bumped MPI2_HEADER_VERSION_UNIT.
  * --------------------------------------------------------------------------
  */
 
 #define MPI2_VERSION_02_05                  (0x0205)
 
 /*Unit and Dev versioning for this MPI header set */
-#define MPI2_HEADER_VERSION_UNIT            (0x1F)
+#define MPI2_HEADER_VERSION_UNIT            (0x23)
 #define MPI2_HEADER_VERSION_DEV             (0x00)
 #define MPI2_HEADER_VERSION_UNIT_MASK       (0xFF00)
 #define MPI2_HEADER_VERSION_UNIT_SHIFT      (8)
index e261a31..581fdb3 100644 (file)
@@ -6,7 +6,7 @@
  *         Title:  MPI Configuration messages and pages
  * Creation Date:  November 10, 2006
  *
- *   mpi2_cnfg.h Version:  02.00.26
+ *   mpi2_cnfg.h Version:  02.00.29
  *
  * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
  *       prefix are for use only on MPI v2.5 products, and must not be used
  *                     match the specification.
  * 08-19-13  02.00.26  Added reserved words to MPI2_CONFIG_PAGE_IO_UNIT_7 for
  *                     future use.
+ * 12-05-13  02.00.27  Added MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL for
+ *                    MPI2_CONFIG_PAGE_MAN_7.
+ *                    Added EnclosureLevel and ConnectorName fields to
+ *                    MPI2_CONFIG_PAGE_SAS_DEV_0.
+ *                    Added MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID for
+ *                    MPI2_CONFIG_PAGE_SAS_DEV_0.
+ *                    Added EnclosureLevel field to
+ *                    MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0.
+ *                    Added MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID for
+ *                    MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0.
+ * 01-08-14  02.00.28  Added more defines for the BiosOptions field of
+ *                    MPI2_CONFIG_PAGE_BIOS_1.
+ * 06-13-14  02.00.29  Added SSUTimeout field to MPI2_CONFIG_PAGE_BIOS_1, and
+ *                    more defines for the BiosOptions field..
  * --------------------------------------------------------------------------
  */
 
@@ -724,6 +738,7 @@ typedef struct _MPI2_CONFIG_PAGE_MAN_7 {
 #define MPI2_MANUFACTURING7_PAGEVERSION                 (0x01)
 
 /*defines for the Flags field */
+#define MPI2_MANPAGE7_FLAG_BASE_ENCLOSURE_LEVEL         (0x00000008)
 #define MPI2_MANPAGE7_FLAG_EVENTREPLAY_SLOT_ORDER       (0x00000002)
 #define MPI2_MANPAGE7_FLAG_USE_SLOT_INFO                (0x00000001)
 
@@ -1311,7 +1326,9 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_1 {
        MPI2_CONFIG_PAGE_HEADER Header;                     /*0x00 */
        U32                     BiosOptions;                /*0x04 */
        U32                     IOCSettings;                /*0x08 */
-       U32                     Reserved1;                  /*0x0C */
+       U8                      SSUTimeout;                 /*0x0C */
+       U8                      Reserved1;                  /*0x0D */
+       U16                     Reserved2;                  /*0x0E */
        U32                     DeviceSettings;             /*0x10 */
        U16                     NumberOfDevices;            /*0x14 */
        U16                     UEFIVersion;                /*0x16 */
@@ -1323,9 +1340,24 @@ typedef struct _MPI2_CONFIG_PAGE_BIOS_1 {
        *PTR_MPI2_CONFIG_PAGE_BIOS_1,
        Mpi2BiosPage1_t, *pMpi2BiosPage1_t;
 
-#define MPI2_BIOSPAGE1_PAGEVERSION                      (0x05)
+#define MPI2_BIOSPAGE1_PAGEVERSION                      (0x07)
 
 /*values for BIOS Page 1 BiosOptions field */
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_MASK                         (0x00003800)
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_PBDHL                        (0x00000000)
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_ENCSLOSURE                   (0x00000800)
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_LWWID                        (0x00001000)
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_PSENS                        (0x00001800)
+#define MPI2_BIOSPAGE1_OPTIONS_PNS_ESPHY                        (0x00002000)
+
+#define MPI2_BIOSPAGE1_OPTIONS_X86_DISABLE_BIOS                (0x00000400)
+
+#define MPI2_BIOSPAGE1_OPTIONS_MASK_REGISTRATION_UEFI_BSD      (0x00000300)
+#define MPI2_BIOSPAGE1_OPTIONS_USE_BIT0_REGISTRATION_UEFI_BSD  (0x00000000)
+#define MPI2_BIOSPAGE1_OPTIONS_FULL_REGISTRATION_UEFI_BSD      (0x00000100)
+#define MPI2_BIOSPAGE1_OPTIONS_ADAPTER_REGISTRATION_UEFI_BSD   (0x00000200)
+#define MPI2_BIOSPAGE1_OPTIONS_DISABLE_REGISTRATION_UEFI_BSD   (0x00000300)
+
 #define MPI2_BIOSPAGE1_OPTIONS_MASK_OEM_ID                  (0x000000F0)
 #define MPI2_BIOSPAGE1_OPTIONS_LSI_OEM_ID                   (0x00000000)
 
@@ -2633,9 +2665,9 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 {
        U8
                ControlGroup;           /*0x2E */
        U8
-               Reserved1;              /*0x2F */
+               EnclosureLevel;         /*0x2F */
        U32
-               Reserved2;              /*0x30 */
+               ConnectorName[4];       /*0x30 */
        U32
                Reserved3;              /*0x34 */
 } MPI2_CONFIG_PAGE_SAS_DEV_0,
@@ -2643,7 +2675,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 {
        Mpi2SasDevicePage0_t,
        *pMpi2SasDevicePage0_t;
 
-#define MPI2_SASDEVICE0_PAGEVERSION         (0x08)
+#define MPI2_SASDEVICE0_PAGEVERSION         (0x09)
 
 /*values for SAS Device Page 0 AccessStatus field */
 #define MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS                  (0x00)
@@ -2683,6 +2715,7 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_DEV_0 {
 #define MPI2_SAS_DEVICE0_FLAGS_SATA_NCQ_SUPPORTED           (0x0020)
 #define MPI2_SAS_DEVICE0_FLAGS_SATA_FUA_SUPPORTED           (0x0010)
 #define MPI2_SAS_DEVICE0_FLAGS_PORT_SELECTOR_ATTACH         (0x0008)
+#define MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID             (0x0002)
 #define MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT               (0x0001)
 
 
@@ -3019,8 +3052,10 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 {
                NumSlots;                   /*0x18 */
        U16
                StartSlot;                  /*0x1A */
-       U16
+       U8
                Reserved2;                  /*0x1C */
+       U8
+               EnclosureLevel;             /*0x1D */
        U16
                SEPDevHandle;               /*0x1E */
        U32
@@ -3031,9 +3066,10 @@ typedef struct _MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0 {
        *PTR_MPI2_CONFIG_PAGE_SAS_ENCLOSURE_0,
        Mpi2SasEnclosurePage0_t, *pMpi2SasEnclosurePage0_t;
 
-#define MPI2_SASENCLOSURE0_PAGEVERSION      (0x03)
+#define MPI2_SASENCLOSURE0_PAGEVERSION      (0x04)
 
 /*values for SAS Enclosure Page 0 Flags field */
+#define MPI2_SAS_ENCLS0_FLAGS_ENCL_LEVEL_VALID      (0x0010)
 #define MPI2_SAS_ENCLS0_FLAGS_MNG_MASK              (0x000F)
 #define MPI2_SAS_ENCLS0_FLAGS_MNG_UNKNOWN           (0x0000)
 #define MPI2_SAS_ENCLS0_FLAGS_MNG_IOC_SES           (0x0001)
index 4908309..d7598cc 100644 (file)
@@ -6,7 +6,7 @@
  *         Title:  MPI IOC, Port, Event, FW Download, and FW Upload messages
  * Creation Date:  October 11, 2006
  *
- * mpi2_ioc.h Version:  02.00.23
+ * mpi2_ioc.h Version:  02.00.24
  *
  * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25
  *       prefix are for use only on MPI v2.5 products, and must not be used
  *                     Added MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE.
  *                     Added MPI2_FW_DOWNLOAD_ITYPE_PUBLIC_KEY.
  *                     Added Encrypted Hash Extended Image.
+ * 12-05-13  02.00.24  Added MPI25_HASH_IMAGE_TYPE_BIOS.
  * --------------------------------------------------------------------------
  */
 
@@ -1598,6 +1599,7 @@ Mpi25EncryptedHashEntry_t, *pMpi25EncryptedHashEntry_t;
 /* values for HashImageType */
 #define MPI25_HASH_IMAGE_TYPE_UNUSED           (0x00)
 #define MPI25_HASH_IMAGE_TYPE_FIRMWARE         (0x01)
+#define MPI25_HASH_IMAGE_TYPE_BIOS              (0x02)
 
 /* values for HashAlgorithm */
 #define MPI25_HASH_ALGORITHM_UNUSED            (0x00)
index 904910d..1629e5b 100644 (file)
@@ -6,7 +6,7 @@
  *         Title:  MPI diagnostic tool structures and definitions
  * Creation Date:  March 26, 2007
  *
- *   mpi2_tool.h Version:  02.00.11
+ *   mpi2_tool.h Version:  02.00.12
  *
  * Version History
  * ---------------
@@ -33,6 +33,7 @@
  * 07-26-12  02.00.10  Modified MPI2_TOOLBOX_DIAGNOSTIC_CLI_REQUEST so that
  *                     it uses MPI Chain SGE as well as MPI Simple SGE.
  * 08-19-13  02.00.11  Added MPI2_TOOLBOX_TEXT_DISPLAY_TOOL and related info.
+ * 01-08-14  02.00.12  Added MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC.
  * --------------------------------------------------------------------------
  */
 
@@ -100,6 +101,7 @@ typedef struct _MPI2_TOOLBOX_CLEAN_REQUEST {
 #define MPI2_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES      (0x20000000)
 #define MPI2_TOOLBOX_CLEAN_FW_CURRENT               (0x10000000)
 #define MPI2_TOOLBOX_CLEAN_FW_BACKUP                (0x08000000)
+#define MPI2_TOOLBOX_CLEAN_BIT26_PRODUCT_SPECIFIC   (0x04000000)
 #define MPI2_TOOLBOX_CLEAN_MEGARAID                 (0x02000000)
 #define MPI2_TOOLBOX_CLEAN_INITIALIZATION           (0x01000000)
 #define MPI2_TOOLBOX_CLEAN_FLASH                    (0x00000004)
index 43f87e9..d4f1dcd 100644 (file)
@@ -83,10 +83,10 @@ static int msix_disable = -1;
 module_param(msix_disable, int, 0);
 MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)");
 
-static int max_msix_vectors = 8;
+static int max_msix_vectors = -1;
 module_param(max_msix_vectors, int, 0);
 MODULE_PARM_DESC(max_msix_vectors,
-       " max msix vectors - (default=8)");
+       " max msix vectors");
 
 static int mpt3sas_fwfault_debug;
 MODULE_PARM_DESC(mpt3sas_fwfault_debug,
@@ -1009,8 +1009,30 @@ _base_interrupt(int irq, void *bus_id)
        }
 
        wmb();
-       writel(reply_q->reply_post_host_index | (msix_index <<
-           MPI2_RPHI_MSIX_INDEX_SHIFT), &ioc->chip->ReplyPostHostIndex);
+
+       /* Update Reply Post Host Index.
+        * For those HBA's which support combined reply queue feature
+        * 1. Get the correct Supplemental Reply Post Host Index Register.
+        *    i.e. (msix_index / 8)th entry from Supplemental Reply Post Host
+        *    Index Register address bank i.e replyPostRegisterIndex[],
+        * 2. Then update this register with new reply host index value
+        *    in ReplyPostIndex field and the MSIxIndex field with
+        *    msix_index value reduced to a value between 0 and 7,
+        *    using a modulo 8 operation. Since each Supplemental Reply Post
+        *    Host Index Register supports 8 MSI-X vectors.
+        *
+        * For other HBA's just update the Reply Post Host Index register with
+        * new reply host index value in ReplyPostIndex Field and msix_index
+        * value in MSIxIndex field.
+        */
+       if (ioc->msix96_vector)
+               writel(reply_q->reply_post_host_index | ((msix_index  & 7) <<
+                       MPI2_RPHI_MSIX_INDEX_SHIFT),
+                       ioc->replyPostRegisterIndex[msix_index/8]);
+       else
+               writel(reply_q->reply_post_host_index | (msix_index <<
+                       MPI2_RPHI_MSIX_INDEX_SHIFT),
+                       &ioc->chip->ReplyPostHostIndex);
        atomic_dec(&reply_q->busy);
        return IRQ_HANDLED;
 }
@@ -1338,7 +1360,7 @@ _base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc,
 
        sg_scmd = scsi_sglist(scmd);
        sges_left = scsi_dma_map(scmd);
-       if (!sges_left) {
+       if (sges_left < 0) {
                sdev_printk(KERN_ERR, scmd->device,
                        "pci_map_sg failed: request for %d bytes!\n",
                        scsi_bufflen(scmd));
@@ -1407,7 +1429,7 @@ _base_build_sg_scmd_ieee(struct MPT3SAS_ADAPTER *ioc,
  fill_in_last_segment:
 
        /* fill the last segment */
-       while (sges_left) {
+       while (sges_left > 0) {
                if (sges_left == 1)
                        _base_add_sg_single_ieee(sg_local,
                            simple_sgl_flags_last, 0, sg_dma_len(sg_scmd),
@@ -1560,8 +1582,6 @@ _base_check_enable_msix(struct MPT3SAS_ADAPTER *ioc)
 
        pci_read_config_word(ioc->pdev, base + 2, &message_control);
        ioc->msix_vector_count = (message_control & 0x3FF) + 1;
-       if (ioc->msix_vector_count > 8)
-               ioc->msix_vector_count = 8;
        dinitprintk(ioc, pr_info(MPT3SAS_FMT
                "msix is supported, vector_count(%d)\n",
                ioc->name, ioc->msix_vector_count));
@@ -1792,6 +1812,36 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc)
        return r;
 }
 
+/**
+ * mpt3sas_base_unmap_resources - free controller resources
+ * @ioc: per adapter object
+ */
+void
+mpt3sas_base_unmap_resources(struct MPT3SAS_ADAPTER *ioc)
+{
+       struct pci_dev *pdev = ioc->pdev;
+
+       dexitprintk(ioc, printk(MPT3SAS_FMT "%s\n",
+               ioc->name, __func__));
+
+       _base_free_irq(ioc);
+       _base_disable_msix(ioc);
+
+       if (ioc->msix96_vector)
+               kfree(ioc->replyPostRegisterIndex);
+
+       if (ioc->chip_phys) {
+               iounmap(ioc->chip);
+               ioc->chip_phys = 0;
+       }
+
+       if (pci_is_enabled(pdev)) {
+               pci_release_selected_regions(ioc->pdev, ioc->bars);
+               pci_disable_pcie_error_reporting(pdev);
+               pci_disable_device(pdev);
+       }
+}
+
 /**
  * mpt3sas_base_map_resources - map in controller resources (io/irq/memap)
  * @ioc: per adapter object
@@ -1882,6 +1932,36 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc)
        if (r)
                goto out_fail;
 
+       /* Use the Combined reply queue feature only for SAS3 C0 & higher
+        * revision HBAs and also only when reply queue count is greater than 8
+        */
+       if (ioc->msix96_vector && ioc->reply_queue_count > 8) {
+               /* Determine the Supplemental Reply Post Host Index Registers
+                * Addresse. Supplemental Reply Post Host Index Registers
+                * starts at offset MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET and
+                * each register is at offset bytes of
+                * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET from previous one.
+                */
+               ioc->replyPostRegisterIndex = kcalloc(
+                    MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT,
+                    sizeof(resource_size_t *), GFP_KERNEL);
+               if (!ioc->replyPostRegisterIndex) {
+                       dfailprintk(ioc, printk(MPT3SAS_FMT
+                       "allocation for reply Post Register Index failed!!!\n",
+                                                                  ioc->name));
+                       r = -ENOMEM;
+                       goto out_fail;
+               }
+
+               for (i = 0; i < MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT; i++) {
+                       ioc->replyPostRegisterIndex[i] = (resource_size_t *)
+                            ((u8 *)&ioc->chip->Doorbell +
+                            MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET +
+                            (i * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET));
+               }
+       } else
+               ioc->msix96_vector = 0;
+
        list_for_each_entry(reply_q, &ioc->reply_queue_list, list)
                pr_info(MPT3SAS_FMT "%s: IRQ %d\n",
                    reply_q->name,  ((ioc->msix_enable) ? "PCI-MSI-X enabled" :
@@ -1897,12 +1977,7 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc)
        return 0;
 
  out_fail:
-       if (ioc->chip_phys)
-               iounmap(ioc->chip);
-       ioc->chip_phys = 0;
-       pci_release_selected_regions(ioc->pdev, ioc->bars);
-       pci_disable_pcie_error_reporting(pdev);
-       pci_disable_device(pdev);
+       mpt3sas_base_unmap_resources(ioc);
        return r;
 }
 
@@ -2291,6 +2366,99 @@ _base_display_intel_branding(struct MPT3SAS_ADAPTER *ioc)
 
 
 
+/**
+ * _base_display_dell_branding - Display branding string
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_base_display_dell_branding(struct MPT3SAS_ADAPTER *ioc)
+{
+       if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_DELL)
+               return;
+
+       switch (ioc->pdev->device) {
+       case MPI25_MFGPAGE_DEVID_SAS3008:
+               switch (ioc->pdev->subsystem_device) {
+               case MPT3SAS_DELL_12G_HBA_SSDID:
+                       pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+                               MPT3SAS_DELL_12G_HBA_BRANDING);
+                       break;
+               default:
+                       pr_info(MPT3SAS_FMT
+                          "Dell 12Gbps HBA: Subsystem ID: 0x%X\n", ioc->name,
+                          ioc->pdev->subsystem_device);
+                       break;
+               }
+               break;
+       default:
+               pr_info(MPT3SAS_FMT
+                       "Dell 12Gbps HBA: Subsystem ID: 0x%X\n", ioc->name,
+                       ioc->pdev->subsystem_device);
+               break;
+       }
+}
+
+/**
+ * _base_display_cisco_branding - Display branding string
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_base_display_cisco_branding(struct MPT3SAS_ADAPTER *ioc)
+{
+       if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_CISCO)
+               return;
+
+       switch (ioc->pdev->device) {
+       case MPI25_MFGPAGE_DEVID_SAS3008:
+               switch (ioc->pdev->subsystem_device) {
+               case MPT3SAS_CISCO_12G_8E_HBA_SSDID:
+                       pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+                               MPT3SAS_CISCO_12G_8E_HBA_BRANDING);
+                       break;
+               case MPT3SAS_CISCO_12G_8I_HBA_SSDID:
+                       pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+                               MPT3SAS_CISCO_12G_8I_HBA_BRANDING);
+                       break;
+               case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID:
+                       pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+                               MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING);
+                       break;
+               default:
+                       pr_info(MPT3SAS_FMT
+                         "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n",
+                         ioc->name, ioc->pdev->subsystem_device);
+                       break;
+               }
+               break;
+       case MPI25_MFGPAGE_DEVID_SAS3108_1:
+               switch (ioc->pdev->subsystem_device) {
+               case MPT3SAS_CISCO_12G_AVILA_HBA_SSDID:
+                       pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+                       MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING);
+                       break;
+               case MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID:
+                       pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+                       MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING);
+                       break;
+               default:
+                       pr_info(MPT3SAS_FMT
+                        "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n",
+                        ioc->name, ioc->pdev->subsystem_device);
+                       break;
+               }
+               break;
+       default:
+                pr_info(MPT3SAS_FMT
+                       "Cisco 12Gbps SAS HBA: Subsystem ID: 0x%X\n",
+                       ioc->name, ioc->pdev->subsystem_device);
+               break;
+       }
+}
+
 /**
  * _base_display_ioc_capabilities - Disply IOC's capabilities.
  * @ioc: per adapter object
@@ -2321,6 +2489,8 @@ _base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc)
            bios_version & 0x000000FF);
 
        _base_display_intel_branding(ioc);
+       _base_display_dell_branding(ioc);
+       _base_display_cisco_branding(ioc);
 
        pr_info(MPT3SAS_FMT "Protocol=(", ioc->name);
 
@@ -3138,6 +3308,9 @@ _base_wait_on_iocstate(struct MPT3SAS_ADAPTER *ioc, u32 ioc_state, int timeout,
  *
  * Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell.
  */
+static int
+_base_diag_reset(struct MPT3SAS_ADAPTER *ioc, int sleep_flag);
+
 static int
 _base_wait_for_doorbell_int(struct MPT3SAS_ADAPTER *ioc, int timeout,
        int sleep_flag)
@@ -3680,6 +3853,64 @@ _base_get_port_facts(struct MPT3SAS_ADAPTER *ioc, int port, int sleep_flag)
        return 0;
 }
 
+/**
+ * _base_wait_for_iocstate - Wait until the card is in READY or OPERATIONAL
+ * @ioc: per adapter object
+ * @timeout:
+ * @sleep_flag: CAN_SLEEP or NO_SLEEP
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_base_wait_for_iocstate(struct MPT3SAS_ADAPTER *ioc, int timeout,
+       int sleep_flag)
+{
+       u32 ioc_state;
+       int rc;
+
+       dinitprintk(ioc, printk(MPT3SAS_FMT "%s\n", ioc->name,
+           __func__));
+
+       if (ioc->pci_error_recovery) {
+               dfailprintk(ioc, printk(MPT3SAS_FMT
+                   "%s: host in pci error recovery\n", ioc->name, __func__));
+               return -EFAULT;
+       }
+
+       ioc_state = mpt3sas_base_get_iocstate(ioc, 0);
+       dhsprintk(ioc, printk(MPT3SAS_FMT "%s: ioc_state(0x%08x)\n",
+           ioc->name, __func__, ioc_state));
+
+       if (((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_READY) ||
+           (ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_OPERATIONAL)
+               return 0;
+
+       if (ioc_state & MPI2_DOORBELL_USED) {
+               dhsprintk(ioc, printk(MPT3SAS_FMT
+                   "unexpected doorbell active!\n", ioc->name));
+               goto issue_diag_reset;
+       }
+
+       if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {
+               mpt3sas_base_fault_info(ioc, ioc_state &
+                   MPI2_DOORBELL_DATA_MASK);
+               goto issue_diag_reset;
+       }
+
+       ioc_state = _base_wait_on_iocstate(ioc, MPI2_IOC_STATE_READY,
+           timeout, sleep_flag);
+       if (ioc_state) {
+               dfailprintk(ioc, printk(MPT3SAS_FMT
+                   "%s: failed going to ready state (ioc_state=0x%x)\n",
+                   ioc->name, __func__, ioc_state));
+               return -EFAULT;
+       }
+
+ issue_diag_reset:
+       rc = _base_diag_reset(ioc, sleep_flag);
+       return rc;
+}
+
 /**
  * _base_get_ioc_facts - obtain ioc facts reply and save in ioc
  * @ioc: per adapter object
@@ -3698,6 +3929,13 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
        dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
            __func__));
 
+       r = _base_wait_for_iocstate(ioc, 10, sleep_flag);
+       if (r) {
+               dfailprintk(ioc, printk(MPT3SAS_FMT
+                   "%s: failed getting to correct state\n",
+                   ioc->name, __func__));
+               return r;
+       }
        mpi_reply_sz = sizeof(Mpi2IOCFactsReply_t);
        mpi_request_sz = sizeof(Mpi2IOCFactsRequest_t);
        memset(&mpi_request, 0, mpi_request_sz);
@@ -3783,7 +4021,7 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
        mpi_request.WhoInit = MPI2_WHOINIT_HOST_DRIVER;
        mpi_request.VF_ID = 0; /* TODO */
        mpi_request.VP_ID = 0;
-       mpi_request.MsgVersion = cpu_to_le16(MPI2_VERSION);
+       mpi_request.MsgVersion = cpu_to_le16(MPI25_VERSION);
        mpi_request.HeaderVersion = cpu_to_le16(MPI2_HEADER_VERSION);
 
        if (_base_is_controller_msix_enabled(ioc))
@@ -4524,8 +4762,15 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
 
        /* initialize reply post host index */
        list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
-               writel(reply_q->msix_index << MPI2_RPHI_MSIX_INDEX_SHIFT,
-                   &ioc->chip->ReplyPostHostIndex);
+               if (ioc->msix96_vector)
+                       writel((reply_q->msix_index & 7)<<
+                          MPI2_RPHI_MSIX_INDEX_SHIFT,
+                          ioc->replyPostRegisterIndex[reply_q->msix_index/8]);
+               else
+                       writel(reply_q->msix_index <<
+                               MPI2_RPHI_MSIX_INDEX_SHIFT,
+                               &ioc->chip->ReplyPostHostIndex);
+
                if (!_base_is_controller_msix_enabled(ioc))
                        goto skip_init_reply_post_host_index;
        }
@@ -4564,8 +4809,6 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
 void
 mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc)
 {
-       struct pci_dev *pdev = ioc->pdev;
-
        dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
            __func__));
 
@@ -4576,18 +4819,7 @@ mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc)
                ioc->shost_recovery = 0;
        }
 
-       _base_free_irq(ioc);
-       _base_disable_msix(ioc);
-
-       if (ioc->chip_phys && ioc->chip)
-               iounmap(ioc->chip);
-       ioc->chip_phys = 0;
-
-       if (pci_is_enabled(pdev)) {
-               pci_release_selected_regions(ioc->pdev, ioc->bars);
-               pci_disable_pcie_error_reporting(pdev);
-               pci_disable_device(pdev);
-       }
+       mpt3sas_base_unmap_resources(ioc);
        return;
 }
 
@@ -4602,6 +4834,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc)
 {
        int r, i;
        int cpu_id, last_cpu_id = 0;
+       u8 revision;
 
        dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
            __func__));
@@ -4621,6 +4854,20 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc)
                goto out_free_resources;
        }
 
+       /* Check whether the controller revision is C0 or above.
+        * only C0 and above revision controllers support 96 MSI-X vectors.
+        */
+       revision = ioc->pdev->revision;
+
+       if ((ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3004 ||
+            ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3008 ||
+            ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3108_1 ||
+            ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3108_2 ||
+            ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3108_5 ||
+            ioc->pdev->device == MPI25_MFGPAGE_DEVID_SAS3108_6) &&
+            (revision >= 0x02))
+               ioc->msix96_vector = 1;
+
        ioc->rdpq_array_enable_assigned = 0;
        ioc->dma_mask = 0;
        r = mpt3sas_base_map_resources(ioc);
@@ -4643,7 +4890,6 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc)
        ioc->build_sg_scmd = &_base_build_sg_scmd_ieee;
        ioc->build_sg = &_base_build_sg_ieee;
        ioc->build_zero_len_sge = &_base_build_zero_len_sge_ieee;
-       ioc->mpi25 = 1;
        ioc->sge_size_ieee = sizeof(Mpi2IeeeSgeSimple64_t);
 
        /*
index afa8816..f0e462b 100644 (file)
@@ -71,8 +71,8 @@
 #define MPT3SAS_DRIVER_NAME            "mpt3sas"
 #define MPT3SAS_AUTHOR "Avago Technologies <MPT-FusionLinux.pdl@avagotech.com>"
 #define MPT3SAS_DESCRIPTION    "LSI MPT Fusion SAS 3.0 Device Driver"
-#define MPT3SAS_DRIVER_VERSION         "04.100.00.00"
-#define MPT3SAS_MAJOR_VERSION          4
+#define MPT3SAS_DRIVER_VERSION         "09.100.00.00"
+#define MPT3SAS_MAJOR_VERSION          9
 #define MPT3SAS_MINOR_VERSION          100
 #define MPT3SAS_BUILD_VERSION          0
 #define MPT3SAS_RELEASE_VERSION        00
 #define MPT3SAS_INTEL_RS3FC044_SSDID   0x3523
 #define MPT3SAS_INTEL_RS3UC080_SSDID    0x3524
 
+/*
+ * Dell HBA branding
+ */
+#define MPT3SAS_DELL_12G_HBA_BRANDING       \
+       "Dell 12Gbps HBA"
+
+/*
+ * Dell HBA SSDIDs
+ */
+#define MPT3SAS_DELL_12G_HBA_SSDID     0x1F46
+
+/*
+ * Cisco HBA branding
+ */
+#define MPT3SAS_CISCO_12G_8E_HBA_BRANDING              \
+               "Cisco 9300-8E 12G SAS HBA"
+#define MPT3SAS_CISCO_12G_8I_HBA_BRANDING              \
+               "Cisco 9300-8i 12G SAS HBA"
+#define MPT3SAS_CISCO_12G_AVILA_HBA_BRANDING   \
+               "Cisco 12G Modular SAS Pass through Controller"
+#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_BRANDING                \
+               "UCS C3X60 12G SAS Pass through Controller"
+/*
+ * Cisco HBA SSSDIDs
+ */
+#define MPT3SAS_CISCO_12G_8E_HBA_SSDID  0x14C
+#define MPT3SAS_CISCO_12G_8I_HBA_SSDID  0x154
+#define MPT3SAS_CISCO_12G_AVILA_HBA_SSDID  0x155
+#define MPT3SAS_CISCO_12G_COLUSA_MEZZANINE_HBA_SSDID  0x156
+
 /*
  * status bits for ioc->diag_buffer_status
  */
 #define MPT3_DIAG_BUFFER_IS_RELEASED   (0x02)
 #define MPT3_DIAG_BUFFER_IS_DIAG_RESET (0x04)
 
+/*
+ * Combined Reply Queue constants,
+ * There are twelve Supplemental Reply Post Host Index Registers
+ * and each register is at offset 0x10 bytes from the previous one.
+ */
+#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_COUNT 12
+#define MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET (0x10)
 
 /* OEM Identifiers */
 #define MFG10_OEM_ID_INVALID                   (0x00000000)
 #define MFG10_GF0_SSD_DATA_SCRUB_DISABLE       (0x00000008)
 #define MFG10_GF0_SINGLE_DRIVE_R0              (0x00000010)
 
+#define VIRTUAL_IO_FAILED_RETRY                        (0x32010081)
+
 /* OEM Specific Flags will come from OEM specific header files */
 struct Mpi2ManufacturingPage10_t {
        MPI2_CONFIG_PAGE_HEADER Header;         /* 00h */
@@ -294,7 +333,8 @@ struct _internal_cmd {
  * @responding: used in _scsih_sas_device_mark_responding
  * @fast_path: fast path feature enable bit
  * @pfa_led_on: flag for PFA LED status
- *
+ * @pend_sas_rphy_add: flag to check if device is in sas_rphy_add()
+ *     addition routine.
  */
 struct _sas_device {
        struct list_head list;
@@ -315,6 +355,9 @@ struct _sas_device {
        u8      responding;
        u8      fast_path;
        u8      pfa_led_on;
+       u8      pend_sas_rphy_add;
+       u8      enclosure_level;
+       u8      connector_name[4];
 };
 
 /**
@@ -728,7 +771,8 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc);
  *                             is assigned only ones
  * @reply_queue_count: number of reply queue's
  * @reply_queue_list: link list contaning the reply queue info
- * @reply_post_host_index: head index in the pool where FW completes IO
+ * @msix96_vector: 96 MSI-X vector support
+ * @replyPostRegisterIndex: index of next position in Reply Desc Post Queue
  * @delayed_tr_list: target reset link list
  * @delayed_tr_volume_list: volume target reset link list
  * @@temp_sensors_count: flag to carry the number of temperature sensors
@@ -814,7 +858,6 @@ struct MPT3SAS_ADAPTER {
        MPT_BUILD_SG_SCMD build_sg_scmd;
        MPT_BUILD_SG    build_sg;
        MPT_BUILD_ZERO_LEN_SGE build_zero_len_sge;
-       u8              mpi25;
        u16             sge_size_ieee;
 
        /* function ptr for MPI sg elements only */
@@ -937,6 +980,10 @@ struct MPT3SAS_ADAPTER {
        u8              reply_queue_count;
        struct list_head reply_queue_list;
 
+       u8              msix96_vector;
+       /* reply post register index */
+       resource_size_t **replyPostRegisterIndex;
+
        struct list_head delayed_tr_list;
        struct list_head delayed_tr_volume_list;
        u8              temp_sensors_count;
index 5a97e32..8ccef38 100644 (file)
@@ -585,6 +585,22 @@ _scsih_sas_device_remove(struct MPT3SAS_ADAPTER *ioc,
 
        if (!sas_device)
                return;
+       pr_info(MPT3SAS_FMT
+           "removing handle(0x%04x), sas_addr(0x%016llx)\n",
+           ioc->name, sas_device->handle,
+           (unsigned long long) sas_device->sas_address);
+
+       if (sas_device->enclosure_handle != 0)
+               pr_info(MPT3SAS_FMT
+                  "removing enclosure logical id(0x%016llx), slot(%d)\n",
+                  ioc->name, (unsigned long long)
+                  sas_device->enclosure_logical_id, sas_device->slot);
+
+       if (sas_device->connector_name[0] != '\0')
+               pr_info(MPT3SAS_FMT
+                  "removing enclosure level(0x%04x), connector name( %s)\n",
+                  ioc->name, sas_device->enclosure_level,
+                  sas_device->connector_name);
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        list_del(&sas_device->list);
@@ -663,6 +679,18 @@ _scsih_sas_device_add(struct MPT3SAS_ADAPTER *ioc,
                ioc->name, __func__, sas_device->handle,
                (unsigned long long)sas_device->sas_address));
 
+       if (sas_device->enclosure_handle != 0)
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: enclosure logical id(0x%016llx), slot( %d)\n",
+                   ioc->name, __func__, (unsigned long long)
+                   sas_device->enclosure_logical_id, sas_device->slot));
+
+       if (sas_device->connector_name[0] != '\0')
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: enclosure level(0x%04x), connector name( %s)\n",
+                   ioc->name, __func__,
+                   sas_device->enclosure_level, sas_device->connector_name));
+
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        list_add_tail(&sas_device->list, &ioc->sas_device_list);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
@@ -704,6 +732,18 @@ _scsih_sas_device_init_add(struct MPT3SAS_ADAPTER *ioc,
                __func__, sas_device->handle,
                (unsigned long long)sas_device->sas_address));
 
+       if (sas_device->enclosure_handle != 0)
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: enclosure logical id(0x%016llx), slot( %d)\n",
+                   ioc->name, __func__, (unsigned long long)
+                   sas_device->enclosure_logical_id, sas_device->slot));
+
+       if (sas_device->connector_name[0] != '\0')
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: enclosure level(0x%04x), connector name( %s)\n",
+                   ioc->name, __func__, sas_device->enclosure_level,
+                   sas_device->connector_name));
+
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        list_add_tail(&sas_device->list, &ioc->sas_device_init_list);
        _scsih_determine_boot_device(ioc, sas_device, 0);
@@ -1772,10 +1812,16 @@ _scsih_slave_configure(struct scsi_device *sdev)
            "sas_addr(0x%016llx), phy(%d), device_name(0x%016llx)\n",
            ds, handle, (unsigned long long)sas_device->sas_address,
            sas_device->phy, (unsigned long long)sas_device->device_name);
-       sdev_printk(KERN_INFO, sdev,
-               "%s: enclosure_logical_id(0x%016llx), slot(%d)\n",
-               ds, (unsigned long long)
-           sas_device->enclosure_logical_id, sas_device->slot);
+       if (sas_device->enclosure_handle != 0)
+               sdev_printk(KERN_INFO, sdev,
+                    "%s: enclosure_logical_id(0x%016llx), slot(%d)\n",
+                    ds, (unsigned long long)
+                    sas_device->enclosure_logical_id, sas_device->slot);
+       if (sas_device->connector_name[0] != '\0')
+               sdev_printk(KERN_INFO, sdev,
+                    "%s: enclosure level(0x%04x), connector name( %s)\n",
+                    ds, sas_device->enclosure_level,
+                    sas_device->connector_name);
 
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
@@ -2189,10 +2235,17 @@ _scsih_tm_display_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd)
                            sas_device->handle,
                            (unsigned long long)sas_device->sas_address,
                            sas_device->phy);
-                       starget_printk(KERN_INFO, starget,
-                           "enclosure_logical_id(0x%016llx), slot(%d)\n",
-                          (unsigned long long)sas_device->enclosure_logical_id,
-                           sas_device->slot);
+                       if (sas_device->enclosure_handle != 0)
+                               starget_printk(KERN_INFO, starget,
+                                "enclosure_logical_id(0x%016llx), slot(%d)\n",
+                                (unsigned long long)
+                                sas_device->enclosure_logical_id,
+                                sas_device->slot);
+                       if (sas_device->connector_name)
+                               starget_printk(KERN_INFO, starget,
+                               "enclosure level(0x%04x),connector name(%s)\n",
+                                sas_device->enclosure_level,
+                                sas_device->connector_name);
                }
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
@@ -2551,6 +2604,75 @@ _scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc)
        }
 }
 
+/**
+ * _scsih_internal_device_block - block the sdev device
+ * @sdev: per device object
+ * @sas_device_priv_data : per device driver private data
+ *
+ * make sure device is blocked without error, if not
+ * print an error
+ */
+static void
+_scsih_internal_device_block(struct scsi_device *sdev,
+                       struct MPT3SAS_DEVICE *sas_device_priv_data)
+{
+       int r = 0;
+
+       sdev_printk(KERN_INFO, sdev, "device_block, handle(0x%04x)\n",
+           sas_device_priv_data->sas_target->handle);
+       sas_device_priv_data->block = 1;
+
+       r = scsi_internal_device_block(sdev);
+       if (r == -EINVAL)
+               sdev_printk(KERN_WARNING, sdev,
+                   "device_block failed with return(%d) for handle(0x%04x)\n",
+                   sas_device_priv_data->sas_target->handle, r);
+}
+
+/**
+ * _scsih_internal_device_unblock - unblock the sdev device
+ * @sdev: per device object
+ * @sas_device_priv_data : per device driver private data
+ * make sure device is unblocked without error, if not retry
+ * by blocking and then unblocking
+ */
+
+static void
+_scsih_internal_device_unblock(struct scsi_device *sdev,
+                       struct MPT3SAS_DEVICE *sas_device_priv_data)
+{
+       int r = 0;
+
+       sdev_printk(KERN_WARNING, sdev, "device_unblock and setting to running, "
+           "handle(0x%04x)\n", sas_device_priv_data->sas_target->handle);
+       sas_device_priv_data->block = 0;
+       r = scsi_internal_device_unblock(sdev, SDEV_RUNNING);
+       if (r == -EINVAL) {
+               /* The device has been set to SDEV_RUNNING by SD layer during
+                * device addition but the request queue is still stopped by
+                * our earlier block call. We need to perform a block again
+                * to get the device to SDEV_BLOCK and then to SDEV_RUNNING */
+
+               sdev_printk(KERN_WARNING, sdev,
+                   "device_unblock failed with return(%d) for handle(0x%04x) "
+                   "performing a block followed by an unblock\n",
+                   sas_device_priv_data->sas_target->handle, r);
+               sas_device_priv_data->block = 1;
+               r = scsi_internal_device_block(sdev);
+               if (r)
+                       sdev_printk(KERN_WARNING, sdev, "retried device_block "
+                           "failed with return(%d) for handle(0x%04x)\n",
+                           sas_device_priv_data->sas_target->handle, r);
+
+               sas_device_priv_data->block = 0;
+               r = scsi_internal_device_unblock(sdev, SDEV_RUNNING);
+               if (r)
+                       sdev_printk(KERN_WARNING, sdev, "retried device_unblock"
+                           " failed with return(%d) for handle(0x%04x)\n",
+                           sas_device_priv_data->sas_target->handle, r);
+       }
+}
+
 /**
  * _scsih_ublock_io_all_device - unblock every device
  * @ioc: per adapter object
@@ -2570,11 +2692,10 @@ _scsih_ublock_io_all_device(struct MPT3SAS_ADAPTER *ioc)
                if (!sas_device_priv_data->block)
                        continue;
 
-               sas_device_priv_data->block = 0;
                dewtprintk(ioc, sdev_printk(KERN_INFO, sdev,
                        "device_running, handle(0x%04x)\n",
                    sas_device_priv_data->sas_target->handle));
-               scsi_internal_device_unblock(sdev, SDEV_RUNNING);
+               _scsih_internal_device_unblock(sdev, sas_device_priv_data);
        }
 }
 
@@ -2599,10 +2720,9 @@ _scsih_ublock_io_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address)
                if (sas_device_priv_data->sas_target->sas_address
                    != sas_address)
                        continue;
-               if (sas_device_priv_data->block) {
-                       sas_device_priv_data->block = 0;
-                       scsi_internal_device_unblock(sdev, SDEV_RUNNING);
-               }
+               if (sas_device_priv_data->block)
+                       _scsih_internal_device_unblock(sdev,
+                               sas_device_priv_data);
        }
 }
 
@@ -2625,10 +2745,7 @@ _scsih_block_io_all_device(struct MPT3SAS_ADAPTER *ioc)
                        continue;
                if (sas_device_priv_data->block)
                        continue;
-               sas_device_priv_data->block = 1;
-               scsi_internal_device_block(sdev);
-               sdev_printk(KERN_INFO, sdev, "device_blocked, handle(0x%04x)\n",
-                   sas_device_priv_data->sas_target->handle);
+               _scsih_internal_device_block(sdev, sas_device_priv_data);
        }
 }
 
@@ -2644,6 +2761,11 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
 {
        struct MPT3SAS_DEVICE *sas_device_priv_data;
        struct scsi_device *sdev;
+       struct _sas_device *sas_device;
+
+       sas_device = _scsih_sas_device_find_by_handle(ioc, handle);
+       if (!sas_device)
+               return;
 
        shost_for_each_device(sdev, ioc->shost) {
                sas_device_priv_data = sdev->hostdata;
@@ -2653,10 +2775,9 @@ _scsih_block_io_device(struct MPT3SAS_ADAPTER *ioc, u16 handle)
                        continue;
                if (sas_device_priv_data->block)
                        continue;
-               sas_device_priv_data->block = 1;
-               scsi_internal_device_block(sdev);
-               sdev_printk(KERN_INFO, sdev,
-                       "device_blocked, handle(0x%04x)\n", handle);
+               if (sas_device->pend_sas_rphy_add)
+                       continue;
+               _scsih_internal_device_block(sdev, sas_device_priv_data);
        }
 }
 
@@ -2806,6 +2927,18 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle)
                        "setting delete flag: handle(0x%04x), sas_addr(0x%016llx)\n",
                        ioc->name, handle,
                    (unsigned long long)sas_address));
+               if (sas_device->enclosure_handle != 0)
+                       dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                        "setting delete flag:enclosure logical id(0x%016llx),"
+                        " slot(%d)\n", ioc->name, (unsigned long long)
+                         sas_device->enclosure_logical_id,
+                         sas_device->slot));
+               if (sas_device->connector_name)
+                       dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                        "setting delete flag: enclosure level(0x%04x),"
+                        " connector name( %s)\n", ioc->name,
+                         sas_device->enclosure_level,
+                         sas_device->connector_name));
                _scsih_ublock_io_device(ioc, sas_address);
                sas_target_priv_data->handle = MPT3SAS_INVALID_DEVICE_HANDLE;
        }
@@ -3821,10 +3954,19 @@ _scsih_scsi_ioc_info(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
                                "\tsas_address(0x%016llx), phy(%d)\n",
                                ioc->name, (unsigned long long)
                            sas_device->sas_address, sas_device->phy);
-                       pr_warn(MPT3SAS_FMT
-                           "\tenclosure_logical_id(0x%016llx), slot(%d)\n",
-                           ioc->name, (unsigned long long)
-                           sas_device->enclosure_logical_id, sas_device->slot);
+                       if (sas_device->enclosure_handle != 0)
+                               pr_warn(MPT3SAS_FMT
+                                 "\tenclosure_logical_id(0x%016llx),"
+                                 "slot(%d)\n", ioc->name,
+                                 (unsigned long long)
+                                 sas_device->enclosure_logical_id,
+                                 sas_device->slot);
+                       if (sas_device->connector_name[0])
+                               pr_warn(MPT3SAS_FMT
+                                 "\tenclosure level(0x%04x),"
+                                 " connector name( %s)\n", ioc->name,
+                                 sas_device->enclosure_level,
+                                 sas_device->connector_name);
                }
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
        }
@@ -3999,7 +4141,16 @@ _scsih_smart_predicted_fault(struct MPT3SAS_ADAPTER *ioc, u16 handle)
                spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
                return;
        }
-       starget_printk(KERN_WARNING, starget, "predicted fault\n");
+       if (sas_device->enclosure_handle != 0)
+               starget_printk(KERN_INFO, starget, "predicted fault, "
+                       "enclosure logical id(0x%016llx), slot(%d)\n",
+                       (unsigned long long)sas_device->enclosure_logical_id,
+                       sas_device->slot);
+       if (sas_device->connector_name[0] != '\0')
+               starget_printk(KERN_WARNING, starget, "predicted fault, "
+                       "enclosure level(0x%04x), connector name( %s)\n",
+                       sas_device->enclosure_level,
+                       sas_device->connector_name);
        spin_unlock_irqrestore(&ioc->sas_device_lock, flags);
 
        if (ioc->pdev->subsystem_vendor == PCI_VENDOR_ID_IBM)
@@ -4119,8 +4270,15 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
                        _scsih_smart_predicted_fault(ioc,
                            le16_to_cpu(mpi_reply->DevHandle));
                mpt3sas_trigger_scsi(ioc, data.skey, data.asc, data.ascq);
-       }
 
+#ifdef CONFIG_SCSI_MPT3SAS_LOGGING
+               if (!(ioc->logging_level & MPT_DEBUG_REPLY) &&
+                    ((scmd->sense_buffer[2] == UNIT_ATTENTION) ||
+                    (scmd->sense_buffer[2] == MEDIUM_ERROR) ||
+                    (scmd->sense_buffer[2] == HARDWARE_ERROR)))
+                       _scsih_scsi_ioc_info(ioc, scmd, mpi_reply, smid);
+#endif
+       }
        switch (ioc_status) {
        case MPI2_IOCSTATUS_BUSY:
        case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES:
@@ -4146,6 +4304,9 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)
                                scmd->device->expecting_cc_ua = 1;
                        }
                        break;
+               } else if (log_info == VIRTUAL_IO_FAILED_RETRY) {
+                       scmd->result = DID_RESET << 16;
+                       break;
                }
                scmd->result = DID_SOFT_ERROR << 16;
                break;
@@ -4788,6 +4949,16 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc,
                        sas_device->handle, handle);
                sas_target_priv_data->handle = handle;
                sas_device->handle = handle;
+               if (sas_device_pg0.Flags &
+                    MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) {
+                       sas_device->enclosure_level =
+                               le16_to_cpu(sas_device_pg0.EnclosureLevel);
+                       memcpy(&sas_device->connector_name[0],
+                               &sas_device_pg0.ConnectorName[0], 4);
+               } else {
+                       sas_device->enclosure_level = 0;
+                       sas_device->connector_name[0] = '\0';
+               }
        }
 
        /* check if device is present */
@@ -4894,14 +5065,24 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num,
                    ioc->name, __FILE__, __LINE__, __func__);
        sas_device->enclosure_handle =
            le16_to_cpu(sas_device_pg0.EnclosureHandle);
-       sas_device->slot =
-           le16_to_cpu(sas_device_pg0.Slot);
+       if (sas_device->enclosure_handle != 0)
+               sas_device->slot =
+                   le16_to_cpu(sas_device_pg0.Slot);
        sas_device->device_info = device_info;
        sas_device->sas_address = sas_address;
        sas_device->phy = sas_device_pg0.PhyNum;
        sas_device->fast_path = (le16_to_cpu(sas_device_pg0.Flags) &
            MPI25_SAS_DEVICE0_FLAGS_FAST_PATH_CAPABLE) ? 1 : 0;
 
+       if (sas_device_pg0.Flags & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) {
+               sas_device->enclosure_level =
+                       le16_to_cpu(sas_device_pg0.EnclosureLevel);
+               memcpy(&sas_device->connector_name[0],
+                       &sas_device_pg0.ConnectorName[0], 4);
+       } else {
+               sas_device->enclosure_level = 0;
+               sas_device->connector_name[0] = '\0';
+       }
        /* get enclosure_logical_id */
        if (sas_device->enclosure_handle && !(mpt3sas_config_get_enclosure_pg0(
           ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE,
@@ -4943,6 +5124,18 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc,
                ioc->name, __func__,
            sas_device->handle, (unsigned long long)
            sas_device->sas_address));
+       if (sas_device->enclosure_handle != 0)
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: enter: enclosure logical id(0x%016llx), slot(%d)\n",
+                   ioc->name, __func__,
+                   (unsigned long long)sas_device->enclosure_logical_id,
+                   sas_device->slot));
+       if (sas_device->connector_name[0] != '\0')
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                 "%s: enter: enclosure level(0x%04x), connector name( %s)\n",
+                 ioc->name, __func__,
+                 sas_device->enclosure_level,
+                 sas_device->connector_name));
 
        if (sas_device->starget && sas_device->starget->hostdata) {
                sas_target_priv_data = sas_device->starget->hostdata;
@@ -4959,12 +5152,34 @@ _scsih_remove_device(struct MPT3SAS_ADAPTER *ioc,
                "removing handle(0x%04x), sas_addr(0x%016llx)\n",
                ioc->name, sas_device->handle,
            (unsigned long long) sas_device->sas_address);
+       if (sas_device->enclosure_handle != 0)
+               pr_info(MPT3SAS_FMT
+                 "removing : enclosure logical id(0x%016llx), slot(%d)\n",
+                 ioc->name,
+                 (unsigned long long)sas_device->enclosure_logical_id,
+                 sas_device->slot);
+       if (sas_device->connector_name[0] != '\0')
+               pr_info(MPT3SAS_FMT
+                 "removing enclosure level(0x%04x), connector name( %s)\n",
+                 ioc->name, sas_device->enclosure_level,
+                 sas_device->connector_name);
 
        dewtprintk(ioc, pr_info(MPT3SAS_FMT
                "%s: exit: handle(0x%04x), sas_addr(0x%016llx)\n",
                ioc->name, __func__,
-           sas_device->handle, (unsigned long long)
-           sas_device->sas_address));
+               sas_device->handle, (unsigned long long)
+               sas_device->sas_address));
+       if (sas_device->enclosure_handle != 0)
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: exit: enclosure logical id(0x%016llx), slot(%d)\n",
+                   ioc->name, __func__,
+                   (unsigned long long)sas_device->enclosure_logical_id,
+                   sas_device->slot));
+       if (sas_device->connector_name[0] != '\0')
+               dewtprintk(ioc, pr_info(MPT3SAS_FMT
+                   "%s: exit: enclosure level(0x%04x), connector name(%s)\n",
+                   ioc->name, __func__, sas_device->enclosure_level,
+                   sas_device->connector_name));
 
        kfree(sas_device);
 }
@@ -6357,9 +6572,7 @@ _scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc)
 /**
  * _scsih_mark_responding_sas_device - mark a sas_devices as responding
  * @ioc: per adapter object
- * @sas_address: sas address
- * @slot: enclosure slot id
- * @handle: device handle
+ * @sas_device_pg0: SAS Device page 0
  *
  * After host reset, find out whether devices are still responding.
  * Used in _scsih_remove_unresponsive_sas_devices.
@@ -6367,8 +6580,8 @@ _scsih_prep_device_scan(struct MPT3SAS_ADAPTER *ioc)
  * Return nothing.
  */
 static void
-_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
-       u16 slot, u16 handle)
+_scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc,
+Mpi2SasDevicePage0_t *sas_device_pg0)
 {
        struct MPT3SAS_TARGET *sas_target_priv_data = NULL;
        struct scsi_target *starget;
@@ -6377,8 +6590,8 @@ _scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
 
        spin_lock_irqsave(&ioc->sas_device_lock, flags);
        list_for_each_entry(sas_device, &ioc->sas_device_list, list) {
-               if (sas_device->sas_address == sas_address &&
-                   sas_device->slot == slot) {
+               if ((sas_device->sas_address == sas_device_pg0->SASAddress) &&
+                       (sas_device->slot == sas_device_pg0->Slot)) {
                        sas_device->responding = 1;
                        starget = sas_device->starget;
                        if (starget && starget->hostdata) {
@@ -6387,22 +6600,40 @@ _scsih_mark_responding_sas_device(struct MPT3SAS_ADAPTER *ioc, u64 sas_address,
                                sas_target_priv_data->deleted = 0;
                        } else
                                sas_target_priv_data = NULL;
-                       if (starget)
+                       if (starget) {
                                starget_printk(KERN_INFO, starget,
-                                   "handle(0x%04x), sas_addr(0x%016llx), "
-                                   "enclosure logical id(0x%016llx), "
-                                   "slot(%d)\n", handle,
-                                   (unsigned long long)sas_device->sas_address,
+                                   "handle(0x%04x), sas_addr(0x%016llx)\n",
+                                   sas_device_pg0->DevHandle,
                                    (unsigned long long)
-                                   sas_device->enclosure_logical_id,
-                                   sas_device->slot);
-                       if (sas_device->handle == handle)
+                                   sas_device->sas_address);
+
+                               if (sas_device->enclosure_handle != 0)
+                                       starget_printk(KERN_INFO, starget,
+                                        "enclosure logical id(0x%016llx),"
+                                        " slot(%d)\n",
+                                        (unsigned long long)
+                                        sas_device->enclosure_logical_id,
+                                        sas_device->slot);
+                       }
+                       if (sas_device_pg0->Flags &
+                             MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) {
+                               sas_device->enclosure_level =
+                                  le16_to_cpu(sas_device_pg0->EnclosureLevel);
+                               memcpy(&sas_device->connector_name[0],
+                                       &sas_device_pg0->ConnectorName[0], 4);
+                       } else {
+                               sas_device->enclosure_level = 0;
+                               sas_device->connector_name[0] = '\0';
+                       }
+
+                       if (sas_device->handle == sas_device_pg0->DevHandle)
                                goto out;
                        pr_info("\thandle changed from(0x%04x)!!!\n",
                            sas_device->handle);
-                       sas_device->handle = handle;
+                       sas_device->handle = sas_device_pg0->DevHandle;
                        if (sas_target_priv_data)
-                               sas_target_priv_data->handle = handle;
+                               sas_target_priv_data->handle =
+                                       sas_device_pg0->DevHandle;
                        goto out;
                }
        }
@@ -6441,13 +6672,15 @@ _scsih_search_responding_sas_devices(struct MPT3SAS_ADAPTER *ioc)
                    MPI2_IOCSTATUS_MASK;
                if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
                        break;
-               handle = le16_to_cpu(sas_device_pg0.DevHandle);
+               handle = sas_device_pg0.DevHandle =
+                               le16_to_cpu(sas_device_pg0.DevHandle);
                device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);
                if (!(_scsih_is_end_device(device_info)))
                        continue;
-               _scsih_mark_responding_sas_device(ioc,
-                   le64_to_cpu(sas_device_pg0.SASAddress),
-                   le16_to_cpu(sas_device_pg0.Slot), handle);
+               sas_device_pg0.SASAddress =
+                               le64_to_cpu(sas_device_pg0.SASAddress);
+               sas_device_pg0.Slot = le16_to_cpu(sas_device_pg0.Slot);
+               _scsih_mark_responding_sas_device(ioc, &sas_device_pg0);
        }
 
  out:
@@ -7854,8 +8087,8 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        /* event thread */
        snprintf(ioc->firmware_event_name, sizeof(ioc->firmware_event_name),
            "fw_event%d", ioc->id);
-       ioc->firmware_event_thread = create_singlethread_workqueue(
-           ioc->firmware_event_name);
+       ioc->firmware_event_thread = alloc_ordered_workqueue(
+           ioc->firmware_event_name, WQ_MEM_RECLAIM);
        if (!ioc->firmware_event_thread) {
                pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
index efb98af..70fd019 100644 (file)
@@ -649,6 +649,7 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle,
        unsigned long flags;
        struct _sas_node *sas_node;
        struct sas_rphy *rphy;
+       struct _sas_device *sas_device = NULL;
        int i;
        struct sas_port *port;
 
@@ -731,10 +732,27 @@ mpt3sas_transport_port_add(struct MPT3SAS_ADAPTER *ioc, u16 handle,
                    mpt3sas_port->remote_identify.device_type);
 
        rphy->identify = mpt3sas_port->remote_identify;
+
+       if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE) {
+               sas_device = mpt3sas_scsih_sas_device_find_by_sas_address(ioc,
+                                   mpt3sas_port->remote_identify.sas_address);
+               if (!sas_device) {
+                       dfailprintk(ioc, printk(MPT3SAS_FMT
+                               "failure at %s:%d/%s()!\n",
+                               ioc->name, __FILE__, __LINE__, __func__));
+                       goto out_fail;
+               }
+               sas_device->pend_sas_rphy_add = 1;
+       }
+
        if ((sas_rphy_add(rphy))) {
                pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
                    ioc->name, __FILE__, __LINE__, __func__);
        }
+
+       if (mpt3sas_port->remote_identify.device_type == SAS_END_DEVICE)
+               sas_device->pend_sas_rphy_add = 0;
+
        if ((ioc->logging_level & MPT_DEBUG_TRANSPORT))
                dev_printk(KERN_INFO, &rphy->dev,
                        "add: handle(0x%04x), sas_addr(0x%016llx)\n",
@@ -1946,7 +1964,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
        } else {
                dma_addr_out = pci_map_single(ioc->pdev, bio_data(req->bio),
                    blk_rq_bytes(req), PCI_DMA_BIDIRECTIONAL);
-               if (!dma_addr_out) {
+               if (pci_dma_mapping_error(ioc->pdev, dma_addr_out)) {
                        pr_info(MPT3SAS_FMT "%s(): DMA Addr out = NULL\n",
                            ioc->name, __func__);
                        rc = -ENOMEM;
@@ -1968,7 +1986,7 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
        } else {
                dma_addr_in =  pci_map_single(ioc->pdev, bio_data(rsp->bio),
                    blk_rq_bytes(rsp), PCI_DMA_BIDIRECTIONAL);
-               if (!dma_addr_in) {
+               if (pci_dma_mapping_error(ioc->pdev, dma_addr_in)) {
                        pr_info(MPT3SAS_FMT "%s(): DMA Addr in = NULL\n",
                            ioc->name, __func__);
                        rc = -ENOMEM;
index 39306b1..04e67a1 100644 (file)
@@ -2642,6 +2642,7 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
                ts->resp = SAS_TASK_COMPLETE;
                ts->stat = SAS_OPEN_REJECT;
                ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
        default:
                PM8001_IO_DBG(pm8001_ha,
                        pm8001_printk("Unknown status 0x%x\n", status));
index 0e1628f..9a389f1 100644 (file)
@@ -2337,6 +2337,7 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
                ts->resp = SAS_TASK_COMPLETE;
                ts->stat = SAS_OPEN_REJECT;
                ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+               break;
        default:
                PM8001_IO_DBG(pm8001_ha,
                        pm8001_printk("Unknown status 0x%x\n", status));
index 33f60c9..a0f732b 100644 (file)
@@ -32,10 +32,10 @@ config SCSI_QLA_FC
        They are also included in the linux-firmware tree as well.
 
 config TCM_QLA2XXX
-       tristate "TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs"
+       tristate "TCM_QLA2XXX fabric module for QLogic 24xx+ series target mode HBAs"
        depends on SCSI_QLA_FC && TARGET_CORE
        depends on LIBFC
        select BTREE
        default n
        ---help---
-       Say Y here to enable the TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs
+       Say Y here to enable the TCM_QLA2XXX fabric module for QLogic 24xx+ series target mode HBAs
index 7ed7bae..ac65cb7 100644 (file)
@@ -1359,9 +1359,7 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess)
        struct qla_hw_data *ha = tgt->ha;
        scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
        struct se_session *se_sess;
-       struct se_node_acl *se_nacl;
        struct tcm_qla2xxx_lport *lport;
-       struct tcm_qla2xxx_nacl *nacl;
 
        BUG_ON(in_interrupt());
 
@@ -1371,8 +1369,6 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess)
                dump_stack();
                return;
        }
-       se_nacl = se_sess->se_node_acl;
-       nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl);
 
        lport = vha->vha_tgt.target_lport_ptr;
        if (!lport) {
@@ -1680,7 +1676,6 @@ static int tcm_qla2xxx_lport_register_npiv_cb(struct scsi_qla_host *base_vha,
                        (struct tcm_qla2xxx_lport *)target_lport_ptr;
        struct tcm_qla2xxx_lport *base_lport =
                        (struct tcm_qla2xxx_lport *)base_vha->vha_tgt.target_lport_ptr;
-       struct tcm_qla2xxx_tpg *base_tpg;
        struct fc_vport_identifiers vport_id;
 
        if (!qla_tgt_mode_enabled(base_vha)) {
@@ -1693,7 +1688,6 @@ static int tcm_qla2xxx_lport_register_npiv_cb(struct scsi_qla_host *base_vha,
                pr_err("qla2xxx base_lport or tpg_1 not available\n");
                return -EPERM;
        }
-       base_tpg = base_lport->tpg_1;
 
        memset(&vport_id, 0, sizeof(vport_id));
        vport_id.port_name = npiv_wwpn;
@@ -1810,6 +1804,11 @@ static const struct target_core_fabric_ops tcm_qla2xxx_ops = {
        .module                         = THIS_MODULE,
        .name                           = "qla2xxx",
        .node_acl_size                  = sizeof(struct tcm_qla2xxx_nacl),
+       /*
+        * XXX: Limit assumes single page per scatter-gather-list entry.
+        * Current maximum is ~4.9 MB per se_cmd->t_data_sg with PAGE_SIZE=4096
+        */
+       .max_data_sg_nents              = 1200,
        .get_fabric_name                = tcm_qla2xxx_get_fabric_name,
        .tpg_get_wwn                    = tcm_qla2xxx_get_fabric_wwn,
        .tpg_get_tag                    = tcm_qla2xxx_get_tag,
@@ -1958,7 +1957,7 @@ static void __exit tcm_qla2xxx_exit(void)
        tcm_qla2xxx_deregister_configfs();
 }
 
-MODULE_DESCRIPTION("TCM QLA2XXX series NPIV enabled fabric driver");
+MODULE_DESCRIPTION("TCM QLA24XX+ series NPIV enabled fabric driver");
 MODULE_LICENSE("GPL");
 module_init(tcm_qla2xxx_init);
 module_exit(tcm_qla2xxx_exit);
index 2ff0922..c126966 100644 (file)
@@ -5,6 +5,8 @@
 #include <linux/bug.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/unaligned.h>
 #include <scsi/scsi_common.h>
 
 /* NB: These are exposed through /proc/scsi/scsi and form part of the ABI.
@@ -176,3 +178,110 @@ bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
        return true;
 }
 EXPORT_SYMBOL(scsi_normalize_sense);
+
+/**
+ * scsi_sense_desc_find - search for a given descriptor type in        descriptor sense data format.
+ * @sense_buffer:      byte array of descriptor format sense data
+ * @sb_len:            number of valid bytes in sense_buffer
+ * @desc_type:         value of descriptor type to find
+ *                     (e.g. 0 -> information)
+ *
+ * Notes:
+ *     only valid when sense data is in descriptor format
+ *
+ * Return value:
+ *     pointer to start of (first) descriptor if found else NULL
+ */
+const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len,
+                               int desc_type)
+{
+       int add_sen_len, add_len, desc_len, k;
+       const u8 * descp;
+
+       if ((sb_len < 8) || (0 == (add_sen_len = sense_buffer[7])))
+               return NULL;
+       if ((sense_buffer[0] < 0x72) || (sense_buffer[0] > 0x73))
+               return NULL;
+       add_sen_len = (add_sen_len < (sb_len - 8)) ?
+                       add_sen_len : (sb_len - 8);
+       descp = &sense_buffer[8];
+       for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
+               descp += desc_len;
+               add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
+               desc_len = add_len + 2;
+               if (descp[0] == desc_type)
+                       return descp;
+               if (add_len < 0) // short descriptor ??
+                       break;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL(scsi_sense_desc_find);
+
+/**
+ * scsi_build_sense_buffer - build sense data in a buffer
+ * @desc:      Sense format (non zero == descriptor format,
+ *              0 == fixed format)
+ * @buf:       Where to build sense data
+ * @key:       Sense key
+ * @asc:       Additional sense code
+ * @ascq:      Additional sense code qualifier
+ *
+ **/
+void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq)
+{
+       if (desc) {
+               buf[0] = 0x72;  /* descriptor, current */
+               buf[1] = key;
+               buf[2] = asc;
+               buf[3] = ascq;
+               buf[7] = 0;
+       } else {
+               buf[0] = 0x70;  /* fixed, current */
+               buf[2] = key;
+               buf[7] = 0xa;
+               buf[12] = asc;
+               buf[13] = ascq;
+       }
+}
+EXPORT_SYMBOL(scsi_build_sense_buffer);
+
+/**
+ * scsi_set_sense_information - set the information field in a
+ *             formatted sense data buffer
+ * @buf:       Where to build sense data
+ * @buf_len:    buffer length
+ * @info:      64-bit information value to be set
+ *
+ * Return value:
+ *     0 on success or EINVAL for invalid sense buffer length
+ **/
+int scsi_set_sense_information(u8 *buf, int buf_len, u64 info)
+{
+       if ((buf[0] & 0x7f) == 0x72) {
+               u8 *ucp, len;
+
+               len = buf[7];
+               ucp = (char *)scsi_sense_desc_find(buf, len + 8, 0);
+               if (!ucp) {
+                       buf[7] = len + 0xc;
+                       ucp = buf + 8 + len;
+               }
+
+               if (buf_len < len + 0xc)
+                       /* Not enough room for info */
+                       return -EINVAL;
+
+               ucp[0] = 0;
+               ucp[1] = 0xa;
+               ucp[2] = 0x80; /* Valid bit */
+               ucp[3] = 0;
+               put_unaligned_be64(info, &ucp[4]);
+       } else if ((buf[0] & 0x7f) == 0x70) {
+               buf[0] |= 0x80;
+               put_unaligned_be64(info, &buf[3]);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(scsi_set_sense_information);
index 30268bb..dfcc45b 100644 (file)
@@ -25,6 +25,9 @@
  *        module options to "modprobe scsi_debug num_tgts=2" [20021221]
  */
 
+
+#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
+
 #include <linux/module.h>
 
 #include <linux/kernel.h>
@@ -201,7 +204,6 @@ static const char *scsi_debug_version_date = "20141022";
 /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
  * or "peripheral device" addressing (value 0) */
 #define SAM2_LUN_ADDRESS_METHOD 0
-#define SAM2_WLUN_REPORT_LUNS 0xc101
 
 /* SCSI_DEBUG_CANQUEUE is the maximum number of commands that can be queued
  * (for response) at one time. Can be reduced by max_queue option. Command
@@ -698,7 +700,7 @@ static void sdebug_max_tgts_luns(void)
                else
                        hpnt->max_id = scsi_debug_num_tgts;
                /* scsi_debug_max_luns; */
-               hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;
+               hpnt->max_lun = SCSI_W_LUN_REPORT_LUNS + 1;
        }
        spin_unlock(&sdebug_host_list_lock);
 }
@@ -1288,7 +1290,7 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
        arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);
        if (! arr)
                return DID_REQUEUE << 16;
-       have_wlun = (scp->device->lun == SAM2_WLUN_REPORT_LUNS);
+       have_wlun = (scp->device->lun == SCSI_W_LUN_REPORT_LUNS);
        if (have_wlun)
                pq_pdt = 0x1e;  /* present, wlun */
        else if (scsi_debug_no_lun_0 && (0 == devip->lun))
@@ -1427,12 +1429,11 @@ static int resp_requests(struct scsi_cmnd * scp,
        unsigned char * sbuff;
        unsigned char *cmd = scp->cmnd;
        unsigned char arr[SCSI_SENSE_BUFFERSIZE];
-       bool dsense, want_dsense;
+       bool dsense;
        int len = 18;
 
        memset(arr, 0, sizeof(arr));
        dsense = !!(cmd[1] & 1);
-       want_dsense = dsense || scsi_debug_dsense;
        sbuff = scp->sense_buffer;
        if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
                if (dsense) {
@@ -2446,8 +2447,7 @@ static int dif_verify(struct sd_dif_tuple *sdt, const void *data,
        __be16 csum = dif_compute_csum(data, scsi_debug_sector_size);
 
        if (sdt->guard_tag != csum) {
-               pr_err("%s: GUARD check failed on sector %lu rcvd 0x%04x, data 0x%04x\n",
-                       __func__,
+               pr_err("GUARD check failed on sector %lu rcvd 0x%04x, data 0x%04x\n",
                        (unsigned long)sector,
                        be16_to_cpu(sdt->guard_tag),
                        be16_to_cpu(csum));
@@ -2455,14 +2455,14 @@ static int dif_verify(struct sd_dif_tuple *sdt, const void *data,
        }
        if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
            be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) {
-               pr_err("%s: REF check failed on sector %lu\n",
-                       __func__, (unsigned long)sector);
+               pr_err("REF check failed on sector %lu\n",
+                       (unsigned long)sector);
                return 0x03;
        }
        if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
            be32_to_cpu(sdt->ref_tag) != ei_lba) {
-               pr_err("%s: REF check failed on sector %lu\n",
-                       __func__, (unsigned long)sector);
+               pr_err("REF check failed on sector %lu\n",
+                       (unsigned long)sector);
                return 0x03;
        }
        return 0;
@@ -2680,7 +2680,7 @@ resp_read_dt0(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
        return 0;
 }
 
-void dump_sector(unsigned char *buf, int len)
+static void dump_sector(unsigned char *buf, int len)
 {
        int i, j, n;
 
@@ -3365,8 +3365,8 @@ static int resp_report_luns(struct scsi_cmnd * scp,
                one_lun[i].scsi_lun[1] = lun & 0xff;
        }
        if (want_wlun) {
-               one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
-               one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
+               one_lun[i].scsi_lun[0] = (SCSI_W_LUN_REPORT_LUNS >> 8) & 0xff;
+               one_lun[i].scsi_lun[1] = SCSI_W_LUN_REPORT_LUNS & 0xff;
                i++;
        }
        alloc_len = (unsigned char *)(one_lun + i) - arr;
@@ -3449,7 +3449,7 @@ static void sdebug_q_cmd_complete(unsigned long indx)
        atomic_inc(&sdebug_completions);
        qa_indx = indx;
        if ((qa_indx < 0) || (qa_indx >= SCSI_DEBUG_CANQUEUE)) {
-               pr_err("%s: wild qa_indx=%d\n", __func__, qa_indx);
+               pr_err("wild qa_indx=%d\n", qa_indx);
                return;
        }
        spin_lock_irqsave(&queued_arr_lock, iflags);
@@ -3457,21 +3457,21 @@ static void sdebug_q_cmd_complete(unsigned long indx)
        scp = sqcp->a_cmnd;
        if (NULL == scp) {
                spin_unlock_irqrestore(&queued_arr_lock, iflags);
-               pr_err("%s: scp is NULL\n", __func__);
+               pr_err("scp is NULL\n");
                return;
        }
        devip = (struct sdebug_dev_info *)scp->device->hostdata;
        if (devip)
                atomic_dec(&devip->num_in_q);
        else
-               pr_err("%s: devip=NULL\n", __func__);
+               pr_err("devip=NULL\n");
        if (atomic_read(&retired_max_queue) > 0)
                retiring = 1;
 
        sqcp->a_cmnd = NULL;
        if (!test_and_clear_bit(qa_indx, queued_in_use_bm)) {
                spin_unlock_irqrestore(&queued_arr_lock, iflags);
-               pr_err("%s: Unexpected completion\n", __func__);
+               pr_err("Unexpected completion\n");
                return;
        }
 
@@ -3481,7 +3481,7 @@ static void sdebug_q_cmd_complete(unsigned long indx)
                retval = atomic_read(&retired_max_queue);
                if (qa_indx >= retval) {
                        spin_unlock_irqrestore(&queued_arr_lock, iflags);
-                       pr_err("%s: index %d too large\n", __func__, retval);
+                       pr_err("index %d too large\n", retval);
                        return;
                }
                k = find_last_bit(queued_in_use_bm, retval);
@@ -3509,7 +3509,7 @@ sdebug_q_cmd_hrt_complete(struct hrtimer *timer)
        atomic_inc(&sdebug_completions);
        qa_indx = sd_hrtp->qa_indx;
        if ((qa_indx < 0) || (qa_indx >= SCSI_DEBUG_CANQUEUE)) {
-               pr_err("%s: wild qa_indx=%d\n", __func__, qa_indx);
+               pr_err("wild qa_indx=%d\n", qa_indx);
                goto the_end;
        }
        spin_lock_irqsave(&queued_arr_lock, iflags);
@@ -3517,21 +3517,21 @@ sdebug_q_cmd_hrt_complete(struct hrtimer *timer)
        scp = sqcp->a_cmnd;
        if (NULL == scp) {
                spin_unlock_irqrestore(&queued_arr_lock, iflags);
-               pr_err("%s: scp is NULL\n", __func__);
+               pr_err("scp is NULL\n");
                goto the_end;
        }
        devip = (struct sdebug_dev_info *)scp->device->hostdata;
        if (devip)
                atomic_dec(&devip->num_in_q);
        else
-               pr_err("%s: devip=NULL\n", __func__);
+               pr_err("devip=NULL\n");
        if (atomic_read(&retired_max_queue) > 0)
                retiring = 1;
 
        sqcp->a_cmnd = NULL;
        if (!test_and_clear_bit(qa_indx, queued_in_use_bm)) {
                spin_unlock_irqrestore(&queued_arr_lock, iflags);
-               pr_err("%s: Unexpected completion\n", __func__);
+               pr_err("Unexpected completion\n");
                goto the_end;
        }
 
@@ -3541,7 +3541,7 @@ sdebug_q_cmd_hrt_complete(struct hrtimer *timer)
                retval = atomic_read(&retired_max_queue);
                if (qa_indx >= retval) {
                        spin_unlock_irqrestore(&queued_arr_lock, iflags);
-                       pr_err("%s: index %d too large\n", __func__, retval);
+                       pr_err("index %d too large\n", retval);
                        goto the_end;
                }
                k = find_last_bit(queued_in_use_bm, retval);
@@ -3580,7 +3580,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
                return devip;
        sdbg_host = *(struct sdebug_host_info **)shost_priv(sdev->host);
        if (!sdbg_host) {
-               pr_err("%s: Host info NULL\n", __func__);
+               pr_err("Host info NULL\n");
                return NULL;
         }
        list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
@@ -3596,8 +3596,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
        if (!open_devip) { /* try and make a new one */
                open_devip = sdebug_device_create(sdbg_host, GFP_ATOMIC);
                if (!open_devip) {
-                       printk(KERN_ERR "%s: out of memory at line %d\n",
-                               __func__, __LINE__);
+                       pr_err("out of memory at line %d\n", __LINE__);
                        return NULL;
                }
        }
@@ -3615,7 +3614,7 @@ static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
 static int scsi_debug_slave_alloc(struct scsi_device *sdp)
 {
        if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
-               printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %llu>\n",
+               pr_info("slave_alloc <%u %u %u %llu>\n",
                       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
        queue_flag_set_unlocked(QUEUE_FLAG_BIDI, sdp->request_queue);
        return 0;
@@ -3626,7 +3625,7 @@ static int scsi_debug_slave_configure(struct scsi_device *sdp)
        struct sdebug_dev_info *devip;
 
        if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
-               printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %llu>\n",
+               pr_info("slave_configure <%u %u %u %llu>\n",
                       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
        if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
                sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
@@ -3646,7 +3645,7 @@ static void scsi_debug_slave_destroy(struct scsi_device *sdp)
                (struct sdebug_dev_info *)sdp->hostdata;
 
        if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
-               printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %llu>\n",
+               pr_info("slave_destroy <%u %u %u %llu>\n",
                       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
        if (devip) {
                /* make this slot available for re-use */
@@ -3897,8 +3896,7 @@ static void __init sdebug_build_parts(unsigned char *ramp,
                return;
        if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
                scsi_debug_num_parts = SDEBUG_MAX_PARTS;
-               pr_warn("%s: reducing partitions to %d\n", __func__,
-                       SDEBUG_MAX_PARTS);
+               pr_warn("reducing partitions to %d\n", SDEBUG_MAX_PARTS);
        }
        num_sectors = (int)sdebug_store_sectors;
        sectors_per_part = (num_sectors - sdebug_sectors_per)
@@ -3942,14 +3940,20 @@ schedule_resp(struct scsi_cmnd *cmnd, struct sdebug_dev_info *devip,
        unsigned long iflags;
        int k, num_in_q, qdepth, inject;
        struct sdebug_queued_cmd *sqcp = NULL;
-       struct scsi_device *sdp = cmnd->device;
+       struct scsi_device *sdp;
+
+       /* this should never happen */
+       if (WARN_ON(!cmnd))
+               return SCSI_MLQUEUE_HOST_BUSY;
 
-       if (NULL == cmnd || NULL == devip) {
-               pr_warn("%s: called with NULL cmnd or devip pointer\n",
-                       __func__);
+       if (NULL == devip) {
+               pr_warn("called devip == NULL\n");
                /* no particularly good error to report back */
                return SCSI_MLQUEUE_HOST_BUSY;
        }
+
+       sdp = cmnd->device;
+
        if ((scsi_result) && (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
                sdev_printk(KERN_INFO, sdp, "%s: non-zero result=0x%x\n",
                            __func__, scsi_result);
@@ -4383,8 +4387,7 @@ static ssize_t fake_rw_store(struct device_driver *ddp, const char *buf,
 
                                fake_storep = vmalloc(sz);
                                if (NULL == fake_storep) {
-                                       pr_err("%s: out of memory, 9\n",
-                                              __func__);
+                                       pr_err("out of memory, 9\n");
                                        return -ENOMEM;
                                }
                                memset(fake_storep, 0, sz);
@@ -4784,8 +4787,7 @@ static int __init scsi_debug_init(void)
        atomic_set(&retired_max_queue, 0);
 
        if (scsi_debug_ndelay >= 1000000000) {
-               pr_warn("%s: ndelay must be less than 1 second, ignored\n",
-                       __func__);
+               pr_warn("ndelay must be less than 1 second, ignored\n");
                scsi_debug_ndelay = 0;
        } else if (scsi_debug_ndelay > 0)
                scsi_debug_delay = DELAY_OVERRIDDEN;
@@ -4797,8 +4799,7 @@ static int __init scsi_debug_init(void)
        case 4096:
                break;
        default:
-               pr_err("%s: invalid sector_size %d\n", __func__,
-                      scsi_debug_sector_size);
+               pr_err("invalid sector_size %d\n", scsi_debug_sector_size);
                return -EINVAL;
        }
 
@@ -4811,29 +4812,28 @@ static int __init scsi_debug_init(void)
                break;
 
        default:
-               pr_err("%s: dif must be 0, 1, 2 or 3\n", __func__);
+               pr_err("dif must be 0, 1, 2 or 3\n");
                return -EINVAL;
        }
 
        if (scsi_debug_guard > 1) {
-               pr_err("%s: guard must be 0 or 1\n", __func__);
+               pr_err("guard must be 0 or 1\n");
                return -EINVAL;
        }
 
        if (scsi_debug_ato > 1) {
-               pr_err("%s: ato must be 0 or 1\n", __func__);
+               pr_err("ato must be 0 or 1\n");
                return -EINVAL;
        }
 
        if (scsi_debug_physblk_exp > 15) {
-               pr_err("%s: invalid physblk_exp %u\n", __func__,
-                      scsi_debug_physblk_exp);
+               pr_err("invalid physblk_exp %u\n", scsi_debug_physblk_exp);
                return -EINVAL;
        }
 
        if (scsi_debug_lowest_aligned > 0x3fff) {
-               pr_err("%s: lowest_aligned too big: %u\n", __func__,
-                      scsi_debug_lowest_aligned);
+               pr_err("lowest_aligned too big: %u\n",
+                       scsi_debug_lowest_aligned);
                return -EINVAL;
        }
 
@@ -4863,7 +4863,7 @@ static int __init scsi_debug_init(void)
        if (0 == scsi_debug_fake_rw) {
                fake_storep = vmalloc(sz);
                if (NULL == fake_storep) {
-                       pr_err("%s: out of memory, 1\n", __func__);
+                       pr_err("out of memory, 1\n");
                        return -ENOMEM;
                }
                memset(fake_storep, 0, sz);
@@ -4877,11 +4877,10 @@ static int __init scsi_debug_init(void)
                dif_size = sdebug_store_sectors * sizeof(struct sd_dif_tuple);
                dif_storep = vmalloc(dif_size);
 
-               pr_err("%s: dif_storep %u bytes @ %p\n", __func__, dif_size,
-                       dif_storep);
+               pr_err("dif_storep %u bytes @ %p\n", dif_size, dif_storep);
 
                if (dif_storep == NULL) {
-                       pr_err("%s: out of mem. (DIX)\n", __func__);
+                       pr_err("out of mem. (DIX)\n");
                        ret = -ENOMEM;
                        goto free_vm;
                }
@@ -4903,18 +4902,17 @@ static int __init scsi_debug_init(void)
                if (scsi_debug_unmap_alignment &&
                    scsi_debug_unmap_granularity <=
                    scsi_debug_unmap_alignment) {
-                       pr_err("%s: ERR: unmap_granularity <= unmap_alignment\n",
-                              __func__);
+                       pr_err("ERR: unmap_granularity <= unmap_alignment\n");
                        return -EINVAL;
                }
 
                map_size = lba_to_map_index(sdebug_store_sectors - 1) + 1;
                map_storep = vmalloc(BITS_TO_LONGS(map_size) * sizeof(long));
 
-               pr_info("%s: %lu provisioning blocks\n", __func__, map_size);
+               pr_info("%lu provisioning blocks\n", map_size);
 
                if (map_storep == NULL) {
-                       pr_err("%s: out of mem. (MAP)\n", __func__);
+                       pr_err("out of mem. (MAP)\n");
                        ret = -ENOMEM;
                        goto free_vm;
                }
@@ -4928,18 +4926,18 @@ static int __init scsi_debug_init(void)
 
        pseudo_primary = root_device_register("pseudo_0");
        if (IS_ERR(pseudo_primary)) {
-               pr_warn("%s: root_device_register() error\n", __func__);
+               pr_warn("root_device_register() error\n");
                ret = PTR_ERR(pseudo_primary);
                goto free_vm;
        }
        ret = bus_register(&pseudo_lld_bus);
        if (ret < 0) {
-               pr_warn("%s: bus_register error: %d\n", __func__, ret);
+               pr_warn("bus_register error: %d\n", ret);
                goto dev_unreg;
        }
        ret = driver_register(&sdebug_driverfs_driver);
        if (ret < 0) {
-               pr_warn("%s: driver_register error: %d\n", __func__, ret);
+               pr_warn("driver_register error: %d\n", ret);
                goto bus_unreg;
        }
 
@@ -4948,16 +4946,14 @@ static int __init scsi_debug_init(void)
 
         for (k = 0; k < host_to_add; k++) {
                 if (sdebug_add_adapter()) {
-                       pr_err("%s: sdebug_add_adapter failed k=%d\n",
-                               __func__, k);
+                       pr_err("sdebug_add_adapter failed k=%d\n", k);
                         break;
                 }
         }
 
-       if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
-               pr_info("%s: built %d host(s)\n", __func__,
-                       scsi_debug_add_host);
-       }
+       if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
+               pr_info("built %d host(s)\n", scsi_debug_add_host);
+
        return 0;
 
 bus_unreg:
@@ -4965,10 +4961,8 @@ bus_unreg:
 dev_unreg:
        root_device_unregister(pseudo_primary);
 free_vm:
-       if (map_storep)
-               vfree(map_storep);
-       if (dif_storep)
-               vfree(dif_storep);
+       vfree(map_storep);
+       vfree(dif_storep);
        vfree(fake_storep);
 
        return ret;
@@ -4986,9 +4980,7 @@ static void __exit scsi_debug_exit(void)
        bus_unregister(&pseudo_lld_bus);
        root_device_unregister(pseudo_primary);
 
-       if (dif_storep)
-               vfree(dif_storep);
-
+       vfree(dif_storep);
        vfree(fake_storep);
 }
 
@@ -5012,8 +5004,7 @@ static int sdebug_add_adapter(void)
 
         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
         if (NULL == sdbg_host) {
-                printk(KERN_ERR "%s: out of memory at line %d\n",
-                       __func__, __LINE__);
+               pr_err("out of memory at line %d\n", __LINE__);
                 return -ENOMEM;
         }
 
@@ -5023,8 +5014,7 @@ static int sdebug_add_adapter(void)
         for (k = 0; k < devs_per_host; k++) {
                sdbg_devinfo = sdebug_device_create(sdbg_host, GFP_KERNEL);
                if (!sdbg_devinfo) {
-                        printk(KERN_ERR "%s: out of memory at line %d\n",
-                               __func__, __LINE__);
+                       pr_err("out of memory at line %d\n", __LINE__);
                         error = -ENOMEM;
                        goto clean;
                 }
@@ -5178,7 +5168,7 @@ scsi_debug_queuecommand(struct scsi_cmnd *scp)
                }
                sdev_printk(KERN_INFO, sdp, "%s: cmd %s\n", my_name, b);
        }
-       has_wlun_rl = (sdp->lun == SAM2_WLUN_REPORT_LUNS);
+       has_wlun_rl = (sdp->lun == SCSI_W_LUN_REPORT_LUNS);
        if ((sdp->lun >= scsi_debug_max_luns) && !has_wlun_rl)
                return schedule_resp(scp, NULL, errsts_no_connect, 0);
 
@@ -5338,7 +5328,7 @@ static int sdebug_driver_probe(struct device * dev)
                sdebug_driver_template.use_clustering = ENABLE_CLUSTERING;
        hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
        if (NULL == hpnt) {
-               pr_err("%s: scsi_host_alloc failed\n", __func__);
+               pr_err("scsi_host_alloc failed\n");
                error = -ENODEV;
                return error;
        }
@@ -5349,7 +5339,8 @@ static int sdebug_driver_probe(struct device * dev)
                hpnt->max_id = scsi_debug_num_tgts + 1;
        else
                hpnt->max_id = scsi_debug_num_tgts;
-       hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;  /* = scsi_debug_max_luns; */
+       /* = scsi_debug_max_luns; */
+       hpnt->max_lun = SCSI_W_LUN_REPORT_LUNS + 1;
 
        host_prot = 0;
 
@@ -5381,7 +5372,7 @@ static int sdebug_driver_probe(struct device * dev)
 
        scsi_host_set_prot(hpnt, host_prot);
 
-       printk(KERN_INFO "scsi_debug: host protection%s%s%s%s%s%s%s\n",
+       pr_info("host protection%s%s%s%s%s%s%s\n",
               (host_prot & SHOST_DIF_TYPE1_PROTECTION) ? " DIF1" : "",
               (host_prot & SHOST_DIF_TYPE2_PROTECTION) ? " DIF2" : "",
               (host_prot & SHOST_DIF_TYPE3_PROTECTION) ? " DIF3" : "",
@@ -5409,7 +5400,7 @@ static int sdebug_driver_probe(struct device * dev)
 
         error = scsi_add_host(hpnt, &sdbg_host->dev);
         if (error) {
-                printk(KERN_ERR "%s: scsi_add_host failed\n", __func__);
+               pr_err("scsi_add_host failed\n");
                 error = -ENODEV;
                scsi_host_put(hpnt);
         } else
@@ -5426,8 +5417,7 @@ static int sdebug_driver_remove(struct device * dev)
        sdbg_host = to_sdebug_host(dev);
 
        if (!sdbg_host) {
-               printk(KERN_ERR "%s: Unable to locate host info\n",
-                      __func__);
+               pr_err("Unable to locate host info\n");
                return -ENODEV;
        }
 
diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c
new file mode 100644 (file)
index 0000000..edb044a
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * SCSI device handler infrastruture.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright IBM Corporation, 2007
+ *      Authors:
+ *               Chandra Seetharaman <sekharan@us.ibm.com>
+ *               Mike Anderson <andmike@linux.vnet.ibm.com>
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <scsi/scsi_dh.h>
+#include "scsi_priv.h"
+
+static DEFINE_SPINLOCK(list_lock);
+static LIST_HEAD(scsi_dh_list);
+
+struct scsi_dh_blist {
+       const char *vendor;
+       const char *model;
+       const char *driver;
+};
+
+static const struct scsi_dh_blist scsi_dh_blist[] = {
+       {"DGC", "RAID",                 "clariion" },
+       {"DGC", "DISK",                 "clariion" },
+       {"DGC", "VRAID",                "clariion" },
+
+       {"COMPAQ", "MSA1000 VOLUME",    "hp_sw" },
+       {"COMPAQ", "HSV110",            "hp_sw" },
+       {"HP", "HSV100",                "hp_sw"},
+       {"DEC", "HSG80",                "hp_sw"},
+
+       {"IBM", "1722",                 "rdac", },
+       {"IBM", "1724",                 "rdac", },
+       {"IBM", "1726",                 "rdac", },
+       {"IBM", "1742",                 "rdac", },
+       {"IBM", "1745",                 "rdac", },
+       {"IBM", "1746",                 "rdac", },
+       {"IBM", "1813",                 "rdac", },
+       {"IBM", "1814",                 "rdac", },
+       {"IBM", "1815",                 "rdac", },
+       {"IBM", "1818",                 "rdac", },
+       {"IBM", "3526",                 "rdac", },
+       {"SGI", "TP9",                  "rdac", },
+       {"SGI", "IS",                   "rdac", },
+       {"STK", "OPENstorage D280",     "rdac", },
+       {"STK", "FLEXLINE 380",         "rdac", },
+       {"SUN", "CSM",                  "rdac", },
+       {"SUN", "LCSM100",              "rdac", },
+       {"SUN", "STK6580_6780",         "rdac", },
+       {"SUN", "SUN_6180",             "rdac", },
+       {"SUN", "ArrayStorage",         "rdac", },
+       {"DELL", "MD3",                 "rdac", },
+       {"NETAPP", "INF-01-00",         "rdac", },
+       {"LSI", "INF-01-00",            "rdac", },
+       {"ENGENIO", "INF-01-00",        "rdac", },
+       {NULL, NULL,                    NULL },
+};
+
+static const char *
+scsi_dh_find_driver(struct scsi_device *sdev)
+{
+       const struct scsi_dh_blist *b;
+
+       if (scsi_device_tpgs(sdev))
+               return "alua";
+
+       for (b = scsi_dh_blist; b->vendor; b++) {
+               if (!strncmp(sdev->vendor, b->vendor, strlen(b->vendor)) &&
+                   !strncmp(sdev->model, b->model, strlen(b->model))) {
+                       return b->driver;
+               }
+       }
+       return NULL;
+}
+
+
+static struct scsi_device_handler *__scsi_dh_lookup(const char *name)
+{
+       struct scsi_device_handler *tmp, *found = NULL;
+
+       spin_lock(&list_lock);
+       list_for_each_entry(tmp, &scsi_dh_list, list) {
+               if (!strncmp(tmp->name, name, strlen(tmp->name))) {
+                       found = tmp;
+                       break;
+               }
+       }
+       spin_unlock(&list_lock);
+       return found;
+}
+
+static struct scsi_device_handler *scsi_dh_lookup(const char *name)
+{
+       struct scsi_device_handler *dh;
+
+       dh = __scsi_dh_lookup(name);
+       if (!dh) {
+               request_module(name);
+               dh = __scsi_dh_lookup(name);
+       }
+
+       return dh;
+}
+
+/*
+ * scsi_dh_handler_attach - Attach a device handler to a device
+ * @sdev - SCSI device the device handler should attach to
+ * @scsi_dh - The device handler to attach
+ */
+static int scsi_dh_handler_attach(struct scsi_device *sdev,
+                                 struct scsi_device_handler *scsi_dh)
+{
+       int error;
+
+       if (!try_module_get(scsi_dh->module))
+               return -EINVAL;
+
+       error = scsi_dh->attach(sdev);
+       if (error) {
+               sdev_printk(KERN_ERR, sdev, "%s: Attach failed (%d)\n",
+                           scsi_dh->name, error);
+               module_put(scsi_dh->module);
+       } else
+               sdev->handler = scsi_dh;
+
+       return error;
+}
+
+/*
+ * scsi_dh_handler_detach - Detach a device handler from a device
+ * @sdev - SCSI device the device handler should be detached from
+ */
+static void scsi_dh_handler_detach(struct scsi_device *sdev)
+{
+       sdev->handler->detach(sdev);
+       sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", sdev->handler->name);
+       module_put(sdev->handler->module);
+}
+
+/*
+ * Functions for sysfs attribute 'dh_state'
+ */
+static ssize_t
+store_dh_state(struct device *dev, struct device_attribute *attr,
+              const char *buf, size_t count)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+       struct scsi_device_handler *scsi_dh;
+       int err = -EINVAL;
+
+       if (sdev->sdev_state == SDEV_CANCEL ||
+           sdev->sdev_state == SDEV_DEL)
+               return -ENODEV;
+
+       if (!sdev->handler) {
+               /*
+                * Attach to a device handler
+                */
+               scsi_dh = scsi_dh_lookup(buf);
+               if (!scsi_dh)
+                       return err;
+               err = scsi_dh_handler_attach(sdev, scsi_dh);
+       } else {
+               if (!strncmp(buf, "detach", 6)) {
+                       /*
+                        * Detach from a device handler
+                        */
+                       sdev_printk(KERN_WARNING, sdev,
+                                   "can't detach handler %s.\n",
+                                   sdev->handler->name);
+                       err = -EINVAL;
+               } else if (!strncmp(buf, "activate", 8)) {
+                       /*
+                        * Activate a device handler
+                        */
+                       if (sdev->handler->activate)
+                               err = sdev->handler->activate(sdev, NULL, NULL);
+                       else
+                               err = 0;
+               }
+       }
+
+       return err<0?err:count;
+}
+
+static ssize_t
+show_dh_state(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct scsi_device *sdev = to_scsi_device(dev);
+
+       if (!sdev->handler)
+               return snprintf(buf, 20, "detached\n");
+
+       return snprintf(buf, 20, "%s\n", sdev->handler->name);
+}
+
+static struct device_attribute scsi_dh_state_attr =
+       __ATTR(dh_state, S_IRUGO | S_IWUSR, show_dh_state,
+              store_dh_state);
+
+int scsi_dh_add_device(struct scsi_device *sdev)
+{
+       struct scsi_device_handler *devinfo = NULL;
+       const char *drv;
+       int err;
+
+       err = device_create_file(&sdev->sdev_gendev, &scsi_dh_state_attr);
+       if (err)
+               return err;
+
+       drv = scsi_dh_find_driver(sdev);
+       if (drv)
+               devinfo = scsi_dh_lookup(drv);
+       if (devinfo)
+               err = scsi_dh_handler_attach(sdev, devinfo);
+       return err;
+}
+
+void scsi_dh_remove_device(struct scsi_device *sdev)
+{
+       if (sdev->handler)
+               scsi_dh_handler_detach(sdev);
+       device_remove_file(&sdev->sdev_gendev, &scsi_dh_state_attr);
+}
+
+/*
+ * scsi_register_device_handler - register a device handler personality
+ *      module.
+ * @scsi_dh - device handler to be registered.
+ *
+ * Returns 0 on success, -EBUSY if handler already registered.
+ */
+int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
+{
+       if (__scsi_dh_lookup(scsi_dh->name))
+               return -EBUSY;
+
+       if (!scsi_dh->attach || !scsi_dh->detach)
+               return -EINVAL;
+
+       spin_lock(&list_lock);
+       list_add(&scsi_dh->list, &scsi_dh_list);
+       spin_unlock(&list_lock);
+
+       printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name);
+
+       return SCSI_DH_OK;
+}
+EXPORT_SYMBOL_GPL(scsi_register_device_handler);
+
+/*
+ * scsi_unregister_device_handler - register a device handler personality
+ *      module.
+ * @scsi_dh - device handler to be unregistered.
+ *
+ * Returns 0 on success, -ENODEV if handler not registered.
+ */
+int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
+{
+       if (!__scsi_dh_lookup(scsi_dh->name))
+               return -ENODEV;
+
+       spin_lock(&list_lock);
+       list_del(&scsi_dh->list);
+       spin_unlock(&list_lock);
+       printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name);
+
+       return SCSI_DH_OK;
+}
+EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
+
+static struct scsi_device *get_sdev_from_queue(struct request_queue *q)
+{
+       struct scsi_device *sdev;
+       unsigned long flags;
+
+       spin_lock_irqsave(q->queue_lock, flags);
+       sdev = q->queuedata;
+       if (!sdev || !get_device(&sdev->sdev_gendev))
+               sdev = NULL;
+       spin_unlock_irqrestore(q->queue_lock, flags);
+
+       return sdev;
+}
+
+/*
+ * scsi_dh_activate - activate the path associated with the scsi_device
+ *      corresponding to the given request queue.
+ *     Returns immediately without waiting for activation to be completed.
+ * @q    - Request queue that is associated with the scsi_device to be
+ *         activated.
+ * @fn   - Function to be called upon completion of the activation.
+ *         Function fn is called with data (below) and the error code.
+ *         Function fn may be called from the same calling context. So,
+ *         do not hold the lock in the caller which may be needed in fn.
+ * @data - data passed to the function fn upon completion.
+ *
+ */
+int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)
+{
+       struct scsi_device *sdev;
+       int err = SCSI_DH_NOSYS;
+
+       sdev = get_sdev_from_queue(q);
+       if (!sdev) {
+               if (fn)
+                       fn(data, err);
+               return err;
+       }
+
+       if (!sdev->handler)
+               goto out_fn;
+       err = SCSI_DH_NOTCONN;
+       if (sdev->sdev_state == SDEV_CANCEL ||
+           sdev->sdev_state == SDEV_DEL)
+               goto out_fn;
+
+       err = SCSI_DH_DEV_OFFLINED;
+       if (sdev->sdev_state == SDEV_OFFLINE)
+               goto out_fn;
+
+       if (sdev->handler->activate)
+               err = sdev->handler->activate(sdev, fn, data);
+
+out_put_device:
+       put_device(&sdev->sdev_gendev);
+       return err;
+
+out_fn:
+       if (fn)
+               fn(data, err);
+       goto out_put_device;
+}
+EXPORT_SYMBOL_GPL(scsi_dh_activate);
+
+/*
+ * scsi_dh_set_params - set the parameters for the device as per the
+ *      string specified in params.
+ * @q - Request queue that is associated with the scsi_device for
+ *      which the parameters to be set.
+ * @params - parameters in the following format
+ *      "no_of_params\0param1\0param2\0param3\0...\0"
+ *      for example, string for 2 parameters with value 10 and 21
+ *      is specified as "2\010\021\0".
+ */
+int scsi_dh_set_params(struct request_queue *q, const char *params)
+{
+       struct scsi_device *sdev;
+       int err = -SCSI_DH_NOSYS;
+
+       sdev = get_sdev_from_queue(q);
+       if (!sdev)
+               return err;
+
+       if (sdev->handler && sdev->handler->set_params)
+               err = sdev->handler->set_params(sdev, params);
+       put_device(&sdev->sdev_gendev);
+       return err;
+}
+EXPORT_SYMBOL_GPL(scsi_dh_set_params);
+
+/*
+ * scsi_dh_attach - Attach device handler
+ * @q - Request queue that is associated with the scsi_device
+ *      the handler should be attached to
+ * @name - name of the handler to attach
+ */
+int scsi_dh_attach(struct request_queue *q, const char *name)
+{
+       struct scsi_device *sdev;
+       struct scsi_device_handler *scsi_dh;
+       int err = 0;
+
+       sdev = get_sdev_from_queue(q);
+       if (!sdev)
+               return -ENODEV;
+
+       scsi_dh = scsi_dh_lookup(name);
+       if (!scsi_dh) {
+               err = -EINVAL;
+               goto out_put_device;
+       }
+
+       if (sdev->handler) {
+               if (sdev->handler != scsi_dh)
+                       err = -EBUSY;
+               goto out_put_device;
+       }
+
+       err = scsi_dh_handler_attach(sdev, scsi_dh);
+
+out_put_device:
+       put_device(&sdev->sdev_gendev);
+       return err;
+}
+EXPORT_SYMBOL_GPL(scsi_dh_attach);
+
+/*
+ * scsi_dh_attached_handler_name - Get attached device handler's name
+ * @q - Request queue that is associated with the scsi_device
+ *      that may have a device handler attached
+ * @gfp - the GFP mask used in the kmalloc() call when allocating memory
+ *
+ * Returns name of attached handler, NULL if no handler is attached.
+ * Caller must take care to free the returned string.
+ */
+const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp)
+{
+       struct scsi_device *sdev;
+       const char *handler_name = NULL;
+
+       sdev = get_sdev_from_queue(q);
+       if (!sdev)
+               return NULL;
+
+       if (sdev->handler)
+               handler_name = kstrdup(sdev->handler->name, gfp);
+       put_device(&sdev->sdev_gendev);
+       return handler_name;
+}
+EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name);
index afd34a6..66a96cd 100644 (file)
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_driver.h>
 #include <scsi/scsi_eh.h>
+#include <scsi/scsi_common.h>
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_dh.h>
 #include <scsi/sg.h>
 
 #include "scsi_priv.h"
@@ -463,11 +465,10 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
        if (scsi_sense_is_deferred(&sshdr))
                return NEEDS_RETRY;
 
-       if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh &&
-                       sdev->scsi_dh_data->scsi_dh->check_sense) {
+       if (sdev->handler && sdev->handler->check_sense) {
                int rc;
 
-               rc = sdev->scsi_dh_data->scsi_dh->check_sense(sdev, &sshdr);
+               rc = sdev->handler->check_sense(sdev, &sshdr);
                if (rc != SCSI_RETURN_NOT_HANDLED)
                        return rc;
                /* handler does not care. Drop down to default handling */
@@ -2178,8 +2179,17 @@ int scsi_error_handler(void *data)
         * We never actually get interrupted because kthread_run
         * disables signal delivery for the created thread.
         */
-       while (!kthread_should_stop()) {
+       while (true) {
+               /*
+                * The sequence in kthread_stop() sets the stop flag first
+                * then wakes the process.  To avoid missed wakeups, the task
+                * should always be in a non running state before the stop
+                * flag is checked
+                */
                set_current_state(TASK_INTERRUPTIBLE);
+               if (kthread_should_stop())
+                       break;
+
                if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) ||
                    shost->host_failed != atomic_read(&shost->host_busy)) {
                        SCSI_LOG_ERROR_RECOVERY(1,
@@ -2415,45 +2425,6 @@ bool scsi_command_normalize_sense(const struct scsi_cmnd *cmd,
 }
 EXPORT_SYMBOL(scsi_command_normalize_sense);
 
-/**
- * scsi_sense_desc_find - search for a given descriptor type in        descriptor sense data format.
- * @sense_buffer:      byte array of descriptor format sense data
- * @sb_len:            number of valid bytes in sense_buffer
- * @desc_type:         value of descriptor type to find
- *                     (e.g. 0 -> information)
- *
- * Notes:
- *     only valid when sense data is in descriptor format
- *
- * Return value:
- *     pointer to start of (first) descriptor if found else NULL
- */
-const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len,
-                               int desc_type)
-{
-       int add_sen_len, add_len, desc_len, k;
-       const u8 * descp;
-
-       if ((sb_len < 8) || (0 == (add_sen_len = sense_buffer[7])))
-               return NULL;
-       if ((sense_buffer[0] < 0x72) || (sense_buffer[0] > 0x73))
-               return NULL;
-       add_sen_len = (add_sen_len < (sb_len - 8)) ?
-                       add_sen_len : (sb_len - 8);
-       descp = &sense_buffer[8];
-       for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
-               descp += desc_len;
-               add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
-               desc_len = add_len + 2;
-               if (descp[0] == desc_type)
-                       return descp;
-               if (add_len < 0) // short descriptor ??
-                       break;
-       }
-       return NULL;
-}
-EXPORT_SYMBOL(scsi_sense_desc_find);
-
 /**
  * scsi_get_sense_info_fld - get information field from sense data (either fixed or descriptor format)
  * @sense_buffer:      byte array of sense data
@@ -2503,31 +2474,3 @@ int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len,
        }
 }
 EXPORT_SYMBOL(scsi_get_sense_info_fld);
-
-/**
- * scsi_build_sense_buffer - build sense data in a buffer
- * @desc:      Sense format (non zero == descriptor format,
- *             0 == fixed format)
- * @buf:       Where to build sense data
- * @key:       Sense key
- * @asc:       Additional sense code
- * @ascq:      Additional sense code qualifier
- *
- **/
-void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq)
-{
-       if (desc) {
-               buf[0] = 0x72;  /* descriptor, current */
-               buf[1] = key;
-               buf[2] = asc;
-               buf[3] = ascq;
-               buf[7] = 0;
-       } else {
-               buf[0] = 0x70;  /* fixed, current */
-               buf[2] = key;
-               buf[7] = 0xa;
-               buf[12] = asc;
-               buf[13] = ascq;
-       }
-}
-EXPORT_SYMBOL(scsi_build_sense_buffer);
index 882864f..cbfc599 100644 (file)
@@ -31,6 +31,7 @@
 #include <scsi/scsi_driver.h>
 #include <scsi/scsi_eh.h>
 #include <scsi/scsi_host.h>
+#include <scsi/scsi_dh.h>
 
 #include <trace/events/scsi.h>
 
@@ -1248,9 +1249,8 @@ static int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req)
 {
        struct scsi_cmnd *cmd = req->special;
 
-       if (unlikely(sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh
-                        && sdev->scsi_dh_data->scsi_dh->prep_fn)) {
-               int ret = sdev->scsi_dh_data->scsi_dh->prep_fn(sdev, req);
+       if (unlikely(sdev->handler && sdev->handler->prep_fn)) {
+               int ret = sdev->handler->prep_fn(sdev, req);
                if (ret != BLKPREP_OK)
                        return ret;
        }
index e3902fc..644bb73 100644 (file)
@@ -170,6 +170,15 @@ static inline void scsi_autopm_put_host(struct Scsi_Host *h) {}
 extern struct async_domain scsi_sd_pm_domain;
 extern struct async_domain scsi_sd_probe_domain;
 
+/* scsi_dh.c */
+#ifdef CONFIG_SCSI_DH
+int scsi_dh_add_device(struct scsi_device *sdev);
+void scsi_dh_remove_device(struct scsi_device *sdev);
+#else
+static inline int scsi_dh_add_device(struct scsi_device *sdev) { return 0; }
+static inline void scsi_dh_remove_device(struct scsi_device *sdev) { }
+#endif
+
 /* 
  * internal scsi timeout functions: for use by mid-layer and transport
  * classes.
index 9ad4116..b333389 100644 (file)
@@ -1030,11 +1030,20 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
                                "failed to add device: %d\n", error);
                return error;
        }
+
+       error = scsi_dh_add_device(sdev);
+       if (error) {
+               sdev_printk(KERN_INFO, sdev,
+                               "failed to add device handler: %d\n", error);
+               return error;
+       }
+
        device_enable_async_suspend(&sdev->sdev_dev);
        error = device_add(&sdev->sdev_dev);
        if (error) {
                sdev_printk(KERN_INFO, sdev,
                                "failed to add class device: %d\n", error);
+               scsi_dh_remove_device(sdev);
                device_del(&sdev->sdev_gendev);
                return error;
        }
@@ -1074,6 +1083,7 @@ void __scsi_remove_device(struct scsi_device *sdev)
                bsg_unregister_queue(sdev->request_queue);
                device_unregister(&sdev->sdev_dev);
                transport_remove_device(dev);
+               scsi_dh_remove_device(sdev);
                device_del(dev);
        } else
                put_device(&sdev->sdev_dev);
index 9a05819..30d26e3 100644 (file)
@@ -1222,13 +1222,6 @@ show_sas_rphy_enclosure_identifier(struct device *dev,
        u64 identifier;
        int error;
 
-       /*
-        * Only devices behind an expander are supported, because the
-        * enclosure identifier is a SMP feature.
-        */
-       if (scsi_is_sas_phy_local(phy))
-               return -EINVAL;
-
        error = i->f->get_enclosure_identifier(rphy, &identifier);
        if (error)
                return error;
@@ -1248,9 +1241,6 @@ show_sas_rphy_bay_identifier(struct device *dev,
        struct sas_internal *i = to_sas_internal(shost->transportt);
        int val;
 
-       if (scsi_is_sas_phy_local(phy))
-               return -EINVAL;
-
        val = i->f->get_bay_identifier(rphy);
        if (val < 0)
                return val;
index 81df77b..9c41652 100644 (file)
@@ -91,7 +91,7 @@ static const struct board_staging_dev armadillo800eva_devices[] __initconst = {
                .pdev           = &lcdc0_device,
                .clocks         = lcdc0_clocks,
                .nclocks        = ARRAY_SIZE(lcdc0_clocks),
-               .domain         = "a4lc",
+               .domain         = "/system-controller@e6180000/pm-domains/c5/a4lc@1"
        },
 };
 
index 29d456e..3eb5eb8 100644 (file)
@@ -135,6 +135,40 @@ int __init board_staging_register_clock(const struct board_staging_clk *bsc)
        return error;
 }
 
+#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
+static int board_staging_add_dev_domain(struct platform_device *pdev,
+                                       const char *domain)
+{
+       struct of_phandle_args pd_args;
+       struct generic_pm_domain *pd;
+       struct device_node *np;
+
+       np = of_find_node_by_path(domain);
+       if (!np) {
+               pr_err("Cannot find domain node %s\n", domain);
+               return -ENOENT;
+       }
+
+       pd_args.np = np;
+       pd_args.args_count = 0;
+       pd = of_genpd_get_from_provider(&pd_args);
+       if (IS_ERR(pd)) {
+               pr_err("Cannot find genpd %s (%ld)\n", domain, PTR_ERR(pd));
+               return PTR_ERR(pd);
+
+       }
+       pr_debug("Found genpd %s for device %s\n", pd->name, pdev->name);
+
+       return pm_genpd_add_device(pd, &pdev->dev);
+}
+#else
+static inline int board_staging_add_dev_domain(struct platform_device *pdev,
+                                              const char *domain)
+{
+       return 0;
+}
+#endif
+
 int __init board_staging_register_device(const struct board_staging_dev *dev)
 {
        struct platform_device *pdev = dev->pdev;
@@ -161,7 +195,7 @@ int __init board_staging_register_device(const struct board_staging_dev *dev)
        }
 
        if (dev->domain)
-               __pm_genpd_name_add_device(dev->domain, &pdev->dev, NULL);
+               board_staging_add_dev_domain(pdev, dev->domain);
 
        return error;
 }
index fd09290..342a07c 100644 (file)
@@ -269,14 +269,14 @@ int iscsit_deaccess_np(struct iscsi_np *np, struct iscsi_portal_group *tpg,
 }
 
 bool iscsit_check_np_match(
-       struct __kernel_sockaddr_storage *sockaddr,
+       struct sockaddr_storage *sockaddr,
        struct iscsi_np *np,
        int network_transport)
 {
        struct sockaddr_in *sock_in, *sock_in_e;
        struct sockaddr_in6 *sock_in6, *sock_in6_e;
        bool ip_match = false;
-       u16 port;
+       u16 port, port_e;
 
        if (sockaddr->ss_family == AF_INET6) {
                sock_in6 = (struct sockaddr_in6 *)sockaddr;
@@ -288,6 +288,7 @@ bool iscsit_check_np_match(
                        ip_match = true;
 
                port = ntohs(sock_in6->sin6_port);
+               port_e = ntohs(sock_in6_e->sin6_port);
        } else {
                sock_in = (struct sockaddr_in *)sockaddr;
                sock_in_e = (struct sockaddr_in *)&np->np_sockaddr;
@@ -296,9 +297,10 @@ bool iscsit_check_np_match(
                        ip_match = true;
 
                port = ntohs(sock_in->sin_port);
+               port_e = ntohs(sock_in_e->sin_port);
        }
 
-       if (ip_match && (np->np_port == port) &&
+       if (ip_match && (port_e == port) &&
            (np->np_network_transport == network_transport))
                return true;
 
@@ -309,7 +311,7 @@ bool iscsit_check_np_match(
  * Called with mutex np_lock held
  */
 static struct iscsi_np *iscsit_get_np(
-       struct __kernel_sockaddr_storage *sockaddr,
+       struct sockaddr_storage *sockaddr,
        int network_transport)
 {
        struct iscsi_np *np;
@@ -340,12 +342,9 @@ static struct iscsi_np *iscsit_get_np(
 }
 
 struct iscsi_np *iscsit_add_np(
-       struct __kernel_sockaddr_storage *sockaddr,
-       char *ip_str,
+       struct sockaddr_storage *sockaddr,
        int network_transport)
 {
-       struct sockaddr_in *sock_in;
-       struct sockaddr_in6 *sock_in6;
        struct iscsi_np *np;
        int ret;
 
@@ -368,16 +367,6 @@ struct iscsi_np *iscsit_add_np(
        }
 
        np->np_flags |= NPF_IP_NETWORK;
-       if (sockaddr->ss_family == AF_INET6) {
-               sock_in6 = (struct sockaddr_in6 *)sockaddr;
-               snprintf(np->np_ip, IPV6_ADDRESS_SPACE, "%s", ip_str);
-               np->np_port = ntohs(sock_in6->sin6_port);
-       } else {
-               sock_in = (struct sockaddr_in *)sockaddr;
-               sprintf(np->np_ip, "%s", ip_str);
-               np->np_port = ntohs(sock_in->sin_port);
-       }
-
        np->np_network_transport = network_transport;
        spin_lock_init(&np->np_thread_lock);
        init_completion(&np->np_restart_comp);
@@ -411,8 +400,8 @@ struct iscsi_np *iscsit_add_np(
        list_add_tail(&np->np_list, &g_np_list);
        mutex_unlock(&np_lock);
 
-       pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n",
-               np->np_ip, np->np_port, np->np_transport->name);
+       pr_debug("CORE[0] - Added Network Portal: %pISpc on %s\n",
+               &np->np_sockaddr, np->np_transport->name);
 
        return np;
 }
@@ -481,8 +470,8 @@ int iscsit_del_np(struct iscsi_np *np)
        list_del(&np->np_list);
        mutex_unlock(&np_lock);
 
-       pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n",
-               np->np_ip, np->np_port, np->np_transport->name);
+       pr_debug("CORE[0] - Removed Network Portal: %pISpc on %s\n",
+               &np->np_sockaddr, np->np_transport->name);
 
        iscsit_put_transport(np->np_transport);
        kfree(np);
@@ -1209,7 +1198,6 @@ static u32 iscsit_do_crypto_hash_sg(
        u8 *pad_bytes)
 {
        u32 data_crc;
-       u32 i;
        struct scatterlist *sg;
        unsigned int page_off;
 
@@ -1218,15 +1206,15 @@ static u32 iscsit_do_crypto_hash_sg(
        sg = cmd->first_data_sg;
        page_off = cmd->first_data_sg_off;
 
-       i = 0;
        while (data_length) {
-               u32 cur_len = min_t(u32, data_length, (sg[i].length - page_off));
+               u32 cur_len = min_t(u32, data_length, (sg->length - page_off));
 
-               crypto_hash_update(hash, &sg[i], cur_len);
+               crypto_hash_update(hash, sg, cur_len);
 
                data_length -= cur_len;
                page_off = 0;
-               i++;
+               /* iscsit_map_iovec has already checked for invalid sg pointers */
+               sg = sg_next(sg);
        }
 
        if (padding) {
@@ -2556,7 +2544,7 @@ static int iscsit_send_conn_drop_async_message(
        cmd->stat_sn            = conn->stat_sn++;
        hdr->statsn             = cpu_to_be32(cmd->stat_sn);
        hdr->exp_cmdsn          = cpu_to_be32(conn->sess->exp_cmd_sn);
-       hdr->max_cmdsn          = cpu_to_be32(conn->sess->max_cmd_sn);
+       hdr->max_cmdsn          = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
        hdr->async_event        = ISCSI_ASYNC_MSG_DROPPING_CONNECTION;
        hdr->param1             = cpu_to_be16(cmd->logout_cid);
        hdr->param2             = cpu_to_be16(conn->sess->sess_ops->DefaultTime2Wait);
@@ -2628,7 +2616,7 @@ iscsit_build_datain_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
                hdr->statsn             = cpu_to_be32(0xFFFFFFFF);
 
        hdr->exp_cmdsn          = cpu_to_be32(conn->sess->exp_cmd_sn);
-       hdr->max_cmdsn          = cpu_to_be32(conn->sess->max_cmd_sn);
+       hdr->max_cmdsn          = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
        hdr->datasn             = cpu_to_be32(datain->data_sn);
        hdr->offset             = cpu_to_be32(datain->offset);
 
@@ -2839,7 +2827,7 @@ iscsit_build_logout_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
 
        iscsit_increment_maxcmdsn(cmd, conn->sess);
        hdr->exp_cmdsn          = cpu_to_be32(conn->sess->exp_cmd_sn);
-       hdr->max_cmdsn          = cpu_to_be32(conn->sess->max_cmd_sn);
+       hdr->max_cmdsn          = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
 
        pr_debug("Built Logout Response ITT: 0x%08x StatSN:"
                " 0x%08x Response: 0x%02x CID: %hu on CID: %hu\n",
@@ -2902,7 +2890,7 @@ iscsit_build_nopin_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
                iscsit_increment_maxcmdsn(cmd, conn->sess);
 
        hdr->exp_cmdsn          = cpu_to_be32(conn->sess->exp_cmd_sn);
-       hdr->max_cmdsn          = cpu_to_be32(conn->sess->max_cmd_sn);
+       hdr->max_cmdsn          = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
 
        pr_debug("Built NOPIN %s Response ITT: 0x%08x, TTT: 0x%08x,"
                " StatSN: 0x%08x, Length %u\n", (nopout_response) ?
@@ -3049,7 +3037,7 @@ static int iscsit_send_r2t(
        hdr->ttt                = cpu_to_be32(r2t->targ_xfer_tag);
        hdr->statsn             = cpu_to_be32(conn->stat_sn);
        hdr->exp_cmdsn          = cpu_to_be32(conn->sess->exp_cmd_sn);
-       hdr->max_cmdsn          = cpu_to_be32(conn->sess->max_cmd_sn);
+       hdr->max_cmdsn          = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
        hdr->r2tsn              = cpu_to_be32(r2t->r2t_sn);
        hdr->data_offset        = cpu_to_be32(r2t->offset);
        hdr->data_length        = cpu_to_be32(r2t->xfer_len);
@@ -3202,7 +3190,7 @@ void iscsit_build_rsp_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
 
        iscsit_increment_maxcmdsn(cmd, conn->sess);
        hdr->exp_cmdsn          = cpu_to_be32(conn->sess->exp_cmd_sn);
-       hdr->max_cmdsn          = cpu_to_be32(conn->sess->max_cmd_sn);
+       hdr->max_cmdsn          = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
 
        pr_debug("Built SCSI Response, ITT: 0x%08x, StatSN: 0x%08x,"
                " Response: 0x%02x, SAM Status: 0x%02x, CID: %hu\n",
@@ -3321,7 +3309,7 @@ iscsit_build_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
 
        iscsit_increment_maxcmdsn(cmd, conn->sess);
        hdr->exp_cmdsn          = cpu_to_be32(conn->sess->exp_cmd_sn);
-       hdr->max_cmdsn          = cpu_to_be32(conn->sess->max_cmd_sn);
+       hdr->max_cmdsn          = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
 
        pr_debug("Built Task Management Response ITT: 0x%08x,"
                " StatSN: 0x%08x, Response: 0x%02x, CID: %hu\n",
@@ -3399,6 +3387,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
        int target_name_printed;
        unsigned char buf[ISCSI_IQN_LEN+12]; /* iqn + "TargetName=" + \0 */
        unsigned char *text_in = cmd->text_in_ptr, *text_ptr = NULL;
+       bool active;
 
        buffer_len = min(conn->conn_ops->MaxRecvDataSegmentLength,
                         SENDTARGETS_BUF_LIMIT);
@@ -3452,19 +3441,18 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
                        }
 
                        spin_lock(&tpg->tpg_state_lock);
-                       if ((tpg->tpg_state == TPG_STATE_FREE) ||
-                           (tpg->tpg_state == TPG_STATE_INACTIVE)) {
-                               spin_unlock(&tpg->tpg_state_lock);
-                               continue;
-                       }
+                       active = (tpg->tpg_state == TPG_STATE_ACTIVE);
                        spin_unlock(&tpg->tpg_state_lock);
 
+                       if (!active && tpg->tpg_attrib.tpg_enabled_sendtargets)
+                               continue;
+
                        spin_lock(&tpg->tpg_np_lock);
                        list_for_each_entry(tpg_np, &tpg->tpg_gnp_list,
                                                tpg_np_list) {
                                struct iscsi_np *np = tpg_np->tpg_np;
                                bool inaddr_any = iscsit_check_inaddr_any(np);
-                               char *fmt_str;
+                               struct sockaddr_storage *sockaddr;
 
                                if (np->np_network_transport != network_transport)
                                        continue;
@@ -3492,15 +3480,15 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
                                        }
                                }
 
-                               if (np->np_sockaddr.ss_family == AF_INET6)
-                                       fmt_str = "TargetAddress=[%s]:%hu,%hu";
+                               if (inaddr_any)
+                                       sockaddr = &conn->local_sockaddr;
                                else
-                                       fmt_str = "TargetAddress=%s:%hu,%hu";
+                                       sockaddr = &np->np_sockaddr;
 
-                               len = sprintf(buf, fmt_str,
-                                       inaddr_any ? conn->local_ip : np->np_ip,
-                                       np->np_port,
-                                       tpg->tpgt);
+                               len = sprintf(buf, "TargetAddress="
+                                             "%pISpc,%hu",
+                                             sockaddr,
+                                             tpg->tpgt);
                                len += 1;
 
                                if ((len + payload_len) > buffer_len) {
@@ -3576,7 +3564,7 @@ iscsit_build_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
         */
        cmd->maxcmdsn_inc = 0;
        hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
-       hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn);
+       hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
 
        pr_debug("Built Text Response: ITT: 0x%08x, TTT: 0x%08x, StatSN: 0x%08x,"
                " Length: %u, CID: %hu F: %d C: %d\n", cmd->init_task_tag,
@@ -3654,7 +3642,7 @@ iscsit_build_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
        cmd->stat_sn            = conn->stat_sn++;
        hdr->statsn             = cpu_to_be32(cmd->stat_sn);
        hdr->exp_cmdsn          = cpu_to_be32(conn->sess->exp_cmd_sn);
-       hdr->max_cmdsn          = cpu_to_be32(conn->sess->max_cmd_sn);
+       hdr->max_cmdsn          = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
 
 }
 EXPORT_SYMBOL(iscsit_build_reject);
index 7d0f9c0..4cf2c0f 100644 (file)
@@ -10,10 +10,10 @@ extern int iscsit_access_np(struct iscsi_np *, struct iscsi_portal_group *);
 extern void iscsit_login_kref_put(struct kref *);
 extern int iscsit_deaccess_np(struct iscsi_np *, struct iscsi_portal_group *,
                                struct iscsi_tpg_np *);
-extern bool iscsit_check_np_match(struct __kernel_sockaddr_storage *,
+extern bool iscsit_check_np_match(struct sockaddr_storage *,
                                struct iscsi_np *, int);
-extern struct iscsi_np *iscsit_add_np(struct __kernel_sockaddr_storage *,
-                               char *, int);
+extern struct iscsi_np *iscsit_add_np(struct sockaddr_storage *,
+                               int);
 extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *,
                                struct iscsi_portal_group *, bool);
 extern int iscsit_del_np(struct iscsi_np *);
index c1898c8..c7461d7 100644 (file)
@@ -99,7 +99,7 @@ static ssize_t lio_target_np_store_sctp(
                 * Use existing np->np_sockaddr for SCTP network portal reference
                 */
                tpg_np_sctp = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr,
-                                       np->np_ip, tpg_np, ISCSI_SCTP_TCP);
+                                       tpg_np, ISCSI_SCTP_TCP);
                if (!tpg_np_sctp || IS_ERR(tpg_np_sctp))
                        goto out;
        } else {
@@ -177,7 +177,7 @@ static ssize_t lio_target_np_store_iser(
                }
 
                tpg_np_iser = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr,
-                               np->np_ip, tpg_np, ISCSI_INFINIBAND);
+                               tpg_np, ISCSI_INFINIBAND);
                if (IS_ERR(tpg_np_iser)) {
                        rc = PTR_ERR(tpg_np_iser);
                        goto out;
@@ -220,7 +220,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg(
        struct iscsi_portal_group *tpg;
        struct iscsi_tpg_np *tpg_np;
        char *str, *str2, *ip_str, *port_str;
-       struct __kernel_sockaddr_storage sockaddr;
+       struct sockaddr_storage sockaddr;
        struct sockaddr_in *sock_in;
        struct sockaddr_in6 *sock_in6;
        unsigned long port;
@@ -235,7 +235,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg(
        memset(buf, 0, MAX_PORTAL_LEN + 1);
        snprintf(buf, MAX_PORTAL_LEN + 1, "%s", name);
 
-       memset(&sockaddr, 0, sizeof(struct __kernel_sockaddr_storage));
+       memset(&sockaddr, 0, sizeof(struct sockaddr_storage));
 
        str = strstr(buf, "[");
        if (str) {
@@ -248,8 +248,8 @@ static struct se_tpg_np *lio_target_call_addnptotpg(
                        return ERR_PTR(-EINVAL);
                }
                str++; /* Skip over leading "[" */
-               *str2 = '\0'; /* Terminate the IPv6 address */
-               str2++; /* Skip over the "]" */
+               *str2 = '\0'; /* Terminate the unbracketed IPv6 address */
+               str2++; /* Skip over the \0 */
                port_str = strstr(str2, ":");
                if (!port_str) {
                        pr_err("Unable to locate \":port\""
@@ -267,7 +267,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg(
                sock_in6 = (struct sockaddr_in6 *)&sockaddr;
                sock_in6->sin6_family = AF_INET6;
                sock_in6->sin6_port = htons((unsigned short)port);
-               ret = in6_pton(str, IPV6_ADDRESS_SPACE,
+               ret = in6_pton(str, -1,
                                (void *)&sock_in6->sin6_addr.in6_u, -1, &end);
                if (ret <= 0) {
                        pr_err("in6_pton returned: %d\n", ret);
@@ -316,7 +316,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg(
         * sys/kernel/config/iscsi/$IQN/$TPG/np/$IP:$PORT/
         *
         */
-       tpg_np = iscsit_tpg_add_network_portal(tpg, &sockaddr, str, NULL,
+       tpg_np = iscsit_tpg_add_network_portal(tpg, &sockaddr, NULL,
                                ISCSI_TCP);
        if (IS_ERR(tpg_np)) {
                iscsit_put_tpg(tpg);
@@ -344,8 +344,8 @@ static void lio_target_call_delnpfromtpg(
 
        se_tpg = &tpg->tpg_se_tpg;
        pr_debug("LIO_Target_ConfigFS: DEREGISTER -> %s TPGT: %hu"
-               " PORTAL: %s:%hu\n", config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item),
-               tpg->tpgt, tpg_np->tpg_np->np_ip, tpg_np->tpg_np->np_port);
+               " PORTAL: %pISpc\n", config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item),
+               tpg->tpgt, &tpg_np->tpg_np->np_sockaddr);
 
        ret = iscsit_tpg_del_network_portal(tpg, tpg_np);
        if (ret < 0)
@@ -656,6 +656,7 @@ static ssize_t lio_target_nacl_show_info(
        struct iscsi_conn *conn;
        struct se_session *se_sess;
        ssize_t rb = 0;
+       u32 max_cmd_sn;
 
        spin_lock_bh(&se_nacl->nacl_sess_lock);
        se_sess = se_nacl->nacl_sess;
@@ -703,11 +704,12 @@ static ssize_t lio_target_nacl_show_info(
                                " Values]-----------------------\n");
                rb += sprintf(page+rb, "  CmdSN/WR  :  CmdSN/WC  :  ExpCmdSN"
                                "  :  MaxCmdSN  :     ITT    :     TTT\n");
+               max_cmd_sn = (u32) atomic_read(&sess->max_cmd_sn);
                rb += sprintf(page+rb, " 0x%08x   0x%08x   0x%08x   0x%08x"
                                "   0x%08x   0x%08x\n",
                        sess->cmdsn_window,
-                       (sess->max_cmd_sn - sess->exp_cmd_sn) + 1,
-                       sess->exp_cmd_sn, sess->max_cmd_sn,
+                       (max_cmd_sn - sess->exp_cmd_sn) + 1,
+                       sess->exp_cmd_sn, max_cmd_sn,
                        sess->init_task_tag, sess->targ_xfer_tag);
                rb += sprintf(page+rb, "----------------------[iSCSI"
                                " Connections]-------------------------\n");
@@ -751,7 +753,7 @@ static ssize_t lio_target_nacl_show_info(
                                break;
                        }
 
-                       rb += sprintf(page+rb, "   Address %s %s", conn->login_ip,
+                       rb += sprintf(page+rb, "   Address %pISc %s", &conn->login_sockaddr,
                                (conn->network_transport == ISCSI_TCP) ?
                                "TCP" : "SCTP");
                        rb += sprintf(page+rb, "  StatSN: 0x%08x\n",
@@ -1010,6 +1012,11 @@ TPG_ATTR(t10_pi, S_IRUGO | S_IWUSR);
  */
 DEF_TPG_ATTRIB(fabric_prot_type);
 TPG_ATTR(fabric_prot_type, S_IRUGO | S_IWUSR);
+/*
+ * Define iscsi_tpg_attrib_s_tpg_enabled_sendtargets
+ */
+DEF_TPG_ATTRIB(tpg_enabled_sendtargets);
+TPG_ATTR(tpg_enabled_sendtargets, S_IRUGO | S_IWUSR);
 
 static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = {
        &iscsi_tpg_attrib_authentication.attr,
@@ -1024,6 +1031,7 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = {
        &iscsi_tpg_attrib_default_erl.attr,
        &iscsi_tpg_attrib_t10_pi.attr,
        &iscsi_tpg_attrib_fabric_prot_type.attr,
+       &iscsi_tpg_attrib_tpg_enabled_sendtargets.attr,
        NULL,
 };
 
index 5fabcd3..0382fa2 100644 (file)
@@ -47,19 +47,19 @@ void iscsit_determine_maxcmdsn(struct iscsi_session *sess)
         * core_set_queue_depth_for_node().
         */
        sess->cmdsn_window = se_nacl->queue_depth;
-       sess->max_cmd_sn = (sess->max_cmd_sn + se_nacl->queue_depth) - 1;
+       atomic_add(se_nacl->queue_depth - 1, &sess->max_cmd_sn);
 }
 
 void iscsit_increment_maxcmdsn(struct iscsi_cmd *cmd, struct iscsi_session *sess)
 {
+       u32 max_cmd_sn;
+
        if (cmd->immediate_cmd || cmd->maxcmdsn_inc)
                return;
 
        cmd->maxcmdsn_inc = 1;
 
-       mutex_lock(&sess->cmdsn_mutex);
-       sess->max_cmd_sn += 1;
-       pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn);
-       mutex_unlock(&sess->cmdsn_mutex);
+       max_cmd_sn = atomic_inc_return(&sess->max_cmd_sn);
+       pr_debug("Updated MaxCmdSN to 0x%08x\n", max_cmd_sn);
 }
 EXPORT_SYMBOL(iscsit_increment_maxcmdsn);
index 7e8f65e..96e78c8 100644 (file)
@@ -331,7 +331,7 @@ static int iscsi_login_zero_tsih_s1(
         * The FFP CmdSN window values will be allocated from the TPG's
         * Initiator Node's ACL once the login has been successfully completed.
         */
-       sess->max_cmd_sn        = be32_to_cpu(pdu->cmdsn);
+       atomic_set(&sess->max_cmd_sn, be32_to_cpu(pdu->cmdsn));
 
        sess->sess_ops = kzalloc(sizeof(struct iscsi_sess_ops), GFP_KERNEL);
        if (!sess->sess_ops) {
@@ -729,9 +729,9 @@ void iscsi_post_login_handler(
                        stop_timer = 1;
                }
 
-               pr_debug("iSCSI Login successful on CID: %hu from %s to"
-                       " %s:%hu,%hu\n", conn->cid, conn->login_ip,
-                       conn->local_ip, conn->local_port, tpg->tpgt);
+               pr_debug("iSCSI Login successful on CID: %hu from %pISpc to"
+                       " %pISpc,%hu\n", conn->cid, &conn->login_sockaddr,
+                       &conn->local_sockaddr, tpg->tpgt);
 
                list_add_tail(&conn->conn_list, &sess->sess_conn_list);
                atomic_inc(&sess->nconn);
@@ -776,8 +776,8 @@ void iscsi_post_login_handler(
        pr_debug("Moving to TARG_SESS_STATE_LOGGED_IN.\n");
        sess->session_state = TARG_SESS_STATE_LOGGED_IN;
 
-       pr_debug("iSCSI Login successful on CID: %hu from %s to %s:%hu,%hu\n",
-               conn->cid, conn->login_ip, conn->local_ip, conn->local_port,
+       pr_debug("iSCSI Login successful on CID: %hu from %pISpc to %pISpc,%hu\n",
+               conn->cid, &conn->login_sockaddr, &conn->local_sockaddr,
                tpg->tpgt);
 
        spin_lock_bh(&sess->conn_lock);
@@ -823,8 +823,8 @@ static void iscsi_handle_login_thread_timeout(unsigned long data)
        struct iscsi_np *np = (struct iscsi_np *) data;
 
        spin_lock_bh(&np->np_thread_lock);
-       pr_err("iSCSI Login timeout on Network Portal %s:%hu\n",
-                       np->np_ip, np->np_port);
+       pr_err("iSCSI Login timeout on Network Portal %pISpc\n",
+                       &np->np_sockaddr);
 
        if (np->np_login_timer_flags & ISCSI_TF_STOP) {
                spin_unlock_bh(&np->np_thread_lock);
@@ -877,7 +877,7 @@ static void iscsi_stop_login_thread_timer(struct iscsi_np *np)
 
 int iscsit_setup_np(
        struct iscsi_np *np,
-       struct __kernel_sockaddr_storage *sockaddr)
+       struct sockaddr_storage *sockaddr)
 {
        struct socket *sock = NULL;
        int backlog = ISCSIT_TCP_BACKLOG, ret, opt = 0, len;
@@ -916,7 +916,7 @@ int iscsit_setup_np(
         * in iscsi_target_configfs.c code..
         */
        memcpy(&np->np_sockaddr, sockaddr,
-                       sizeof(struct __kernel_sockaddr_storage));
+                       sizeof(struct sockaddr_storage));
 
        if (sockaddr->ss_family == AF_INET6)
                len = sizeof(struct sockaddr_in6);
@@ -975,7 +975,7 @@ fail:
 
 int iscsi_target_setup_login_socket(
        struct iscsi_np *np,
-       struct __kernel_sockaddr_storage *sockaddr)
+       struct sockaddr_storage *sockaddr)
 {
        struct iscsit_transport *t;
        int rc;
@@ -1015,44 +1015,42 @@ int iscsit_accept_np(struct iscsi_np *np, struct iscsi_conn *conn)
                rc = conn->sock->ops->getname(conn->sock,
                                (struct sockaddr *)&sock_in6, &err, 1);
                if (!rc) {
-                       if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr))
-                               snprintf(conn->login_ip, sizeof(conn->login_ip), "[%pI6c]",
-                                       &sock_in6.sin6_addr.in6_u);
-                       else
-                               snprintf(conn->login_ip, sizeof(conn->login_ip), "%pI4",
-                                       &sock_in6.sin6_addr.s6_addr32[3]);
-                       conn->login_port = ntohs(sock_in6.sin6_port);
+                       if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr)) {
+                               memcpy(&conn->login_sockaddr, &sock_in6, sizeof(sock_in6));
+                       } else {
+                               /* Pretend to be an ipv4 socket */
+                               sock_in.sin_family = AF_INET;
+                               sock_in.sin_port = sock_in6.sin6_port;
+                               memcpy(&sock_in.sin_addr, &sock_in6.sin6_addr.s6_addr32[3], 4);
+                               memcpy(&conn->login_sockaddr, &sock_in, sizeof(sock_in));
+                       }
                }
 
                rc = conn->sock->ops->getname(conn->sock,
                                (struct sockaddr *)&sock_in6, &err, 0);
                if (!rc) {
-                       if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr))
-                               snprintf(conn->local_ip, sizeof(conn->local_ip), "[%pI6c]",
-                                       &sock_in6.sin6_addr.in6_u);
-                       else
-                               snprintf(conn->local_ip, sizeof(conn->local_ip), "%pI4",
-                                       &sock_in6.sin6_addr.s6_addr32[3]);
-                       conn->local_port = ntohs(sock_in6.sin6_port);
+                       if (!ipv6_addr_v4mapped(&sock_in6.sin6_addr)) {
+                               memcpy(&conn->local_sockaddr, &sock_in6, sizeof(sock_in6));
+                       } else {
+                               /* Pretend to be an ipv4 socket */
+                               sock_in.sin_family = AF_INET;
+                               sock_in.sin_port = sock_in6.sin6_port;
+                               memcpy(&sock_in.sin_addr, &sock_in6.sin6_addr.s6_addr32[3], 4);
+                               memcpy(&conn->local_sockaddr, &sock_in, sizeof(sock_in));
+                       }
                }
        } else {
                memset(&sock_in, 0, sizeof(struct sockaddr_in));
 
                rc = conn->sock->ops->getname(conn->sock,
                                (struct sockaddr *)&sock_in, &err, 1);
-               if (!rc) {
-                       sprintf(conn->login_ip, "%pI4",
-                                       &sock_in.sin_addr.s_addr);
-                       conn->login_port = ntohs(sock_in.sin_port);
-               }
+               if (!rc)
+                       memcpy(&conn->login_sockaddr, &sock_in, sizeof(sock_in));
 
                rc = conn->sock->ops->getname(conn->sock,
                                (struct sockaddr *)&sock_in, &err, 0);
-               if (!rc) {
-                       sprintf(conn->local_ip, "%pI4",
-                                       &sock_in.sin_addr.s_addr);
-                       conn->local_port = ntohs(sock_in.sin_port);
-               }
+               if (!rc)
+                       memcpy(&conn->local_sockaddr, &sock_in, sizeof(sock_in));
        }
 
        return 0;
@@ -1302,8 +1300,8 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
        spin_lock_bh(&np->np_thread_lock);
        if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
                spin_unlock_bh(&np->np_thread_lock);
-               pr_err("iSCSI Network Portal on %s:%hu currently not"
-                       " active.\n", np->np_ip, np->np_port);
+               pr_err("iSCSI Network Portal on %pISpc currently not"
+                       " active.\n", &np->np_sockaddr);
                iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
                                ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
                goto new_sess_out;
@@ -1312,9 +1310,9 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
 
        conn->network_transport = np->np_network_transport;
 
-       pr_debug("Received iSCSI login request from %s on %s Network"
-               " Portal %s:%hu\n", conn->login_ip, np->np_transport->name,
-               conn->local_ip, conn->local_port);
+       pr_debug("Received iSCSI login request from %pISpc on %s Network"
+               " Portal %pISpc\n", &conn->login_sockaddr, np->np_transport->name,
+               &conn->local_sockaddr);
 
        pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n");
        conn->conn_state        = TARG_CONN_STATE_IN_LOGIN;
index 57aa0d0..b597aa2 100644 (file)
@@ -5,9 +5,9 @@ extern int iscsi_login_setup_crypto(struct iscsi_conn *);
 extern int iscsi_check_for_session_reinstatement(struct iscsi_conn *);
 extern int iscsi_login_post_auth_non_zero_tsih(struct iscsi_conn *, u16, u32);
 extern int iscsit_setup_np(struct iscsi_np *,
-                               struct __kernel_sockaddr_storage *);
+                               struct sockaddr_storage *);
 extern int iscsi_target_setup_login_socket(struct iscsi_np *,
-                               struct __kernel_sockaddr_storage *);
+                               struct sockaddr_storage *);
 extern int iscsit_accept_np(struct iscsi_np *, struct iscsi_conn *);
 extern int iscsit_get_login_rx(struct iscsi_conn *, struct iscsi_login *);
 extern int iscsit_put_login_tx(struct iscsi_conn *, struct iscsi_login *, u32);
index f9cde91..5c964c0 100644 (file)
@@ -341,7 +341,6 @@ static int iscsi_target_check_first_request(
 static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
 {
        u32 padding = 0;
-       struct iscsi_session *sess = conn->sess;
        struct iscsi_login_rsp *login_rsp;
 
        login_rsp = (struct iscsi_login_rsp *) login->rsp;
@@ -353,7 +352,7 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log
        login_rsp->itt                  = login->init_task_tag;
        login_rsp->statsn               = cpu_to_be32(conn->stat_sn++);
        login_rsp->exp_cmdsn            = cpu_to_be32(conn->sess->exp_cmd_sn);
-       login_rsp->max_cmdsn            = cpu_to_be32(conn->sess->max_cmd_sn);
+       login_rsp->max_cmdsn            = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
 
        pr_debug("Sending Login Response, Flags: 0x%02x, ITT: 0x%08x,"
                " ExpCmdSN; 0x%08x, MaxCmdSN: 0x%08x, StatSN: 0x%08x, Length:"
@@ -382,10 +381,6 @@ static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_log
                goto err;
 
        login->rsp_length               = 0;
-       mutex_lock(&sess->cmdsn_mutex);
-       login_rsp->exp_cmdsn            = cpu_to_be32(sess->exp_cmd_sn);
-       login_rsp->max_cmdsn            = cpu_to_be32(sess->max_cmd_sn);
-       mutex_unlock(&sess->cmdsn_mutex);
 
        return 0;
 
index 5e1349a..9dd94ff 100644 (file)
@@ -430,7 +430,7 @@ static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr(
        int ret;
 
        spin_lock(&lstat->lock);
-       ret = snprintf(page, PAGE_SIZE, "%s\n", lstat->last_intr_fail_ip_addr);
+       ret = snprintf(page, PAGE_SIZE, "%pISc\n", &lstat->last_intr_fail_sockaddr);
        spin_unlock(&lstat->lock);
 
        return ret;
index cf59c39..11320df 100644 (file)
@@ -50,7 +50,7 @@ u8 iscsit_tmr_abort_task(
                pr_err("Unable to locate RefTaskTag: 0x%08x on CID:"
                        " %hu.\n", hdr->rtt, conn->cid);
                return (iscsi_sna_gte(be32_to_cpu(hdr->refcmdsn), conn->sess->exp_cmd_sn) &&
-                       iscsi_sna_lte(be32_to_cpu(hdr->refcmdsn), conn->sess->max_cmd_sn)) ?
+                       iscsi_sna_lte(be32_to_cpu(hdr->refcmdsn), (u32) atomic_read(&conn->sess->max_cmd_sn))) ?
                        ISCSI_TMF_RSP_COMPLETE : ISCSI_TMF_RSP_NO_TASK;
        }
        if (ref_cmd->cmd_sn != be32_to_cpu(hdr->refcmdsn)) {
index 968068f..23c95cd 100644 (file)
@@ -226,6 +226,7 @@ static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg)
        a->default_erl = TA_DEFAULT_ERL;
        a->t10_pi = TA_DEFAULT_T10_PI;
        a->fabric_prot_type = TA_DEFAULT_FABRIC_PROT_TYPE;
+       a->tpg_enabled_sendtargets = TA_DEFAULT_TPG_ENABLED_SENDTARGETS;
 }
 
 int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg)
@@ -430,7 +431,7 @@ struct iscsi_tpg_np *iscsit_tpg_locate_child_np(
 
 static bool iscsit_tpg_check_network_portal(
        struct iscsi_tiqn *tiqn,
-       struct __kernel_sockaddr_storage *sockaddr,
+       struct sockaddr_storage *sockaddr,
        int network_transport)
 {
        struct iscsi_portal_group *tpg;
@@ -459,8 +460,7 @@ static bool iscsit_tpg_check_network_portal(
 
 struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
        struct iscsi_portal_group *tpg,
-       struct __kernel_sockaddr_storage *sockaddr,
-       char *ip_str,
+       struct sockaddr_storage *sockaddr,
        struct iscsi_tpg_np *tpg_np_parent,
        int network_transport)
 {
@@ -470,8 +470,8 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
        if (!tpg_np_parent) {
                if (iscsit_tpg_check_network_portal(tpg->tpg_tiqn, sockaddr,
                                network_transport)) {
-                       pr_err("Network Portal: %s already exists on a"
-                               " different TPG on %s\n", ip_str,
+                       pr_err("Network Portal: %pISc already exists on a"
+                               " different TPG on %s\n", sockaddr,
                                tpg->tpg_tiqn->tiqn);
                        return ERR_PTR(-EEXIST);
                }
@@ -484,7 +484,7 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
                return ERR_PTR(-ENOMEM);
        }
 
-       np = iscsit_add_np(sockaddr, ip_str, network_transport);
+       np = iscsit_add_np(sockaddr, network_transport);
        if (IS_ERR(np)) {
                kfree(tpg_np);
                return ERR_CAST(np);
@@ -514,8 +514,8 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal(
                spin_unlock(&tpg_np_parent->tpg_np_parent_lock);
        }
 
-       pr_debug("CORE[%s] - Added Network Portal: %s:%hu,%hu on %s\n",
-               tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt,
+       pr_debug("CORE[%s] - Added Network Portal: %pISpc,%hu on %s\n",
+               tpg->tpg_tiqn->tiqn, &np->np_sockaddr, tpg->tpgt,
                np->np_transport->name);
 
        return tpg_np;
@@ -528,8 +528,8 @@ static int iscsit_tpg_release_np(
 {
        iscsit_clear_tpg_np_login_thread(tpg_np, tpg, true);
 
-       pr_debug("CORE[%s] - Removed Network Portal: %s:%hu,%hu on %s\n",
-               tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt,
+       pr_debug("CORE[%s] - Removed Network Portal: %pISpc,%hu on %s\n",
+               tpg->tpg_tiqn->tiqn, &np->np_sockaddr, tpg->tpgt,
                np->np_transport->name);
 
        tpg_np->tpg_np = NULL;
@@ -892,3 +892,21 @@ int iscsit_ta_fabric_prot_type(
 
        return 0;
 }
+
+int iscsit_ta_tpg_enabled_sendtargets(
+       struct iscsi_portal_group *tpg,
+       u32 flag)
+{
+       struct iscsi_tpg_attrib *a = &tpg->tpg_attrib;
+
+       if ((flag != 0) && (flag != 1)) {
+               pr_err("Illegal value %d\n", flag);
+               return -EINVAL;
+       }
+
+       a->tpg_enabled_sendtargets = flag;
+       pr_debug("iSCSI_TPG[%hu] - TPG enabled bit required for SendTargets:"
+               " %s\n", tpg->tpgt, (a->tpg_enabled_sendtargets) ? "ON" : "OFF");
+
+       return 0;
+}
index 95ff5bd..9db32bd 100644 (file)
@@ -22,7 +22,7 @@ extern struct iscsi_node_attrib *iscsit_tpg_get_node_attrib(struct iscsi_session
 extern void iscsit_tpg_del_external_nps(struct iscsi_tpg_np *);
 extern struct iscsi_tpg_np *iscsit_tpg_locate_child_np(struct iscsi_tpg_np *, int);
 extern struct iscsi_tpg_np *iscsit_tpg_add_network_portal(struct iscsi_portal_group *,
-                       struct __kernel_sockaddr_storage *, char *, struct iscsi_tpg_np *,
+                       struct sockaddr_storage *, struct iscsi_tpg_np *,
                        int);
 extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *,
                        struct iscsi_tpg_np *);
@@ -40,5 +40,6 @@ extern int iscsit_ta_demo_mode_discovery(struct iscsi_portal_group *, u32);
 extern int iscsit_ta_default_erl(struct iscsi_portal_group *, u32);
 extern int iscsit_ta_t10_pi(struct iscsi_portal_group *, u32);
 extern int iscsit_ta_fabric_prot_type(struct iscsi_portal_group *, u32);
+extern int iscsit_ta_tpg_enabled_sendtargets(struct iscsi_portal_group *, u32);
 
 #endif /* ISCSI_TARGET_TPG_H */
index a2bff07..428b0d9 100644 (file)
@@ -233,6 +233,7 @@ struct iscsi_r2t *iscsit_get_holder_for_r2tsn(
 
 static inline int iscsit_check_received_cmdsn(struct iscsi_session *sess, u32 cmdsn)
 {
+       u32 max_cmdsn;
        int ret;
 
        /*
@@ -241,10 +242,10 @@ static inline int iscsit_check_received_cmdsn(struct iscsi_session *sess, u32 cm
         * or order CmdSNs due to multiple connection sessions and/or
         * CRC failures.
         */
-       if (iscsi_sna_gt(cmdsn, sess->max_cmd_sn)) {
+       max_cmdsn = atomic_read(&sess->max_cmd_sn);
+       if (iscsi_sna_gt(cmdsn, max_cmdsn)) {
                pr_err("Received CmdSN: 0x%08x is greater than"
-                      " MaxCmdSN: 0x%08x, ignoring.\n", cmdsn,
-                      sess->max_cmd_sn);
+                      " MaxCmdSN: 0x%08x, ignoring.\n", cmdsn, max_cmdsn);
                ret = CMDSN_MAXCMDSN_OVERRUN;
 
        } else if (cmdsn == sess->exp_cmd_sn) {
@@ -1371,6 +1372,33 @@ int tx_data(
        return iscsit_do_tx_data(conn, &c);
 }
 
+static bool sockaddr_equal(struct sockaddr_storage *x, struct sockaddr_storage *y)
+{
+       switch (x->ss_family) {
+       case AF_INET: {
+               struct sockaddr_in *sinx = (struct sockaddr_in *)x;
+               struct sockaddr_in *siny = (struct sockaddr_in *)y;
+               if (sinx->sin_addr.s_addr != siny->sin_addr.s_addr)
+                       return false;
+               if (sinx->sin_port != siny->sin_port)
+                       return false;
+               break;
+       }
+       case AF_INET6: {
+               struct sockaddr_in6 *sinx = (struct sockaddr_in6 *)x;
+               struct sockaddr_in6 *siny = (struct sockaddr_in6 *)y;
+               if (!ipv6_addr_equal(&sinx->sin6_addr, &siny->sin6_addr))
+                       return false;
+               if (sinx->sin6_port != siny->sin6_port)
+                       return false;
+               break;
+       }
+       default:
+               return false;
+       }
+       return true;
+}
+
 void iscsit_collect_login_stats(
        struct iscsi_conn *conn,
        u8 status_class,
@@ -1387,7 +1415,7 @@ void iscsit_collect_login_stats(
        ls = &tiqn->login_stats;
 
        spin_lock(&ls->lock);
-       if (!strcmp(conn->login_ip, ls->last_intr_fail_ip_addr) &&
+       if (sockaddr_equal(&conn->login_sockaddr, &ls->last_intr_fail_sockaddr) &&
            ((get_jiffies_64() - ls->last_fail_time) < 10)) {
                /* We already have the failure info for this login */
                spin_unlock(&ls->lock);
@@ -1427,8 +1455,7 @@ void iscsit_collect_login_stats(
 
                ls->last_intr_fail_ip_family = conn->login_family;
 
-               snprintf(ls->last_intr_fail_ip_addr, IPV6_ADDRESS_SPACE,
-                               "%s", conn->login_ip);
+               ls->last_intr_fail_sockaddr = conn->login_sockaddr;
                ls->last_fail_time = get_jiffies_64();
        }
 
index a556bde..5bc85ff 100644 (file)
@@ -526,7 +526,7 @@ static inline struct tcm_loop_tpg *tl_tpg(struct se_portal_group *se_tpg)
 static char *tcm_loop_get_endpoint_wwn(struct se_portal_group *se_tpg)
 {
        /*
-        * Return the passed NAA identifier for the SAS Target Port
+        * Return the passed NAA identifier for the Target Port
         */
        return &tl_tpg(se_tpg)->tl_hba->tl_wwn_address[0];
 }
@@ -845,7 +845,7 @@ static int tcm_loop_make_nexus(
                transport_free_session(tl_nexus->se_sess);
                goto out;
        }
-       /* Now, register the SAS I_T Nexus as active. */
+       /* Now, register the I_T Nexus as active. */
        transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl,
                        tl_nexus->se_sess, tl_nexus);
        tl_tpg->tl_nexus = tl_nexus;
@@ -884,7 +884,7 @@ static int tcm_loop_drop_nexus(
                " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tpg->tl_hba),
                tl_nexus->se_sess->se_node_acl->initiatorname);
        /*
-        * Release the SCSI I_T Nexus to the emulated SAS Target Port
+        * Release the SCSI I_T Nexus to the emulated Target Port
         */
        transport_deregister_session(tl_nexus->se_sess);
        tpg->tl_nexus = NULL;
@@ -1034,6 +1034,11 @@ static ssize_t tcm_loop_tpg_store_transport_status(
        }
        if (!strncmp(page, "offline", 7)) {
                tl_tpg->tl_transport_status = TCM_TRANSPORT_OFFLINE;
+               if (tl_tpg->tl_nexus) {
+                       struct se_session *tl_sess = tl_tpg->tl_nexus->se_sess;
+
+                       core_allocate_nexus_loss_ua(tl_sess->se_node_acl);
+               }
                return count;
        }
        return -EINVAL;
@@ -1077,7 +1082,7 @@ static struct se_portal_group *tcm_loop_make_naa_tpg(
        tl_tpg->tl_hba = tl_hba;
        tl_tpg->tl_tpgt = tpgt;
        /*
-        * Register the tl_tpg as a emulated SAS TCM Target Endpoint
+        * Register the tl_tpg as a emulated TCM Target Endpoint
         */
        ret = core_tpg_register(wwn, &tl_tpg->tl_se_tpg, tl_hba->tl_proto_id);
        if (ret < 0)
@@ -1102,11 +1107,11 @@ static void tcm_loop_drop_naa_tpg(
        tl_hba = tl_tpg->tl_hba;
        tpgt = tl_tpg->tl_tpgt;
        /*
-        * Release the I_T Nexus for the Virtual SAS link if present
+        * Release the I_T Nexus for the Virtual target link if present
         */
        tcm_loop_drop_nexus(tl_tpg);
        /*
-        * Deregister the tl_tpg as a emulated SAS TCM Target Endpoint
+        * Deregister the tl_tpg as a emulated TCM Target Endpoint
         */
        core_tpg_deregister(se_tpg);
 
@@ -1199,8 +1204,9 @@ static void tcm_loop_drop_scsi_hba(
                                struct tcm_loop_hba, tl_hba_wwn);
 
        pr_debug("TCM_Loop_ConfigFS: Deallocating emulated Target"
-               " SAS Address: %s at Linux/SCSI Host ID: %d\n",
-               tl_hba->tl_wwn_address, tl_hba->sh->host_no);
+               " %s Address: %s at Linux/SCSI Host ID: %d\n",
+               tcm_loop_dump_proto_id(tl_hba), tl_hba->tl_wwn_address,
+               tl_hba->sh->host_no);
        /*
         * Call device_unregister() on the original tl_hba->dev.
         * tcm_loop_fabric_scsi.c:tcm_loop_release_adapter() will
index 09e682b..dcc424a 100644 (file)
@@ -620,8 +620,6 @@ struct se_lun_acl *core_dev_init_initiator_node_lun_acl(
 
        lacl->mapped_lun = mapped_lun;
        lacl->se_lun_nacl = nacl;
-       snprintf(lacl->initiatorname, TRANSPORT_IQN_LEN, "%s",
-                nacl->initiatorname);
 
        return lacl;
 }
@@ -656,7 +654,7 @@ int core_dev_add_initiator_node_lun_acl(
                " InitiatorNode: %s\n", tpg->se_tpg_tfo->get_fabric_name(),
                tpg->se_tpg_tfo->tpg_get_tag(tpg), lun->unpacked_lun, lacl->mapped_lun,
                (lun_access & TRANSPORT_LUNFLAGS_READ_WRITE) ? "RW" : "RO",
-               lacl->initiatorname);
+               nacl->initiatorname);
        /*
         * Check to see if there are any existing persistent reservation APTPL
         * pre-registrations that need to be enabled for this LUN ACL..
@@ -688,7 +686,7 @@ int core_dev_del_initiator_node_lun_acl(
                " InitiatorNode: %s Mapped LUN: %llu\n",
                tpg->se_tpg_tfo->get_fabric_name(),
                tpg->se_tpg_tfo->tpg_get_tag(tpg), lun->unpacked_lun,
-               lacl->initiatorname, lacl->mapped_lun);
+               nacl->initiatorname, lacl->mapped_lun);
 
        return 0;
 }
@@ -701,7 +699,7 @@ void core_dev_free_initiator_node_lun_acl(
                " Mapped LUN: %llu\n", tpg->se_tpg_tfo->get_fabric_name(),
                tpg->se_tpg_tfo->tpg_get_tag(tpg),
                tpg->se_tpg_tfo->get_fabric_name(),
-               lacl->initiatorname, lacl->mapped_lun);
+               lacl->se_lun_nacl->initiatorname, lacl->mapped_lun);
 
        kfree(lacl);
 }
@@ -754,7 +752,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
        dev->dev_link_magic = SE_DEV_LINK_MAGIC;
        dev->se_hba = hba;
        dev->transport = hba->backend->ops;
-       dev->prot_length = sizeof(struct se_dif_v1_tuple);
+       dev->prot_length = sizeof(struct t10_pi_tuple);
        dev->hba_index = hba->hba_index;
 
        INIT_LIST_HEAD(&dev->dev_list);
@@ -771,7 +769,6 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name)
        spin_lock_init(&dev->se_tmr_lock);
        spin_lock_init(&dev->qf_cmd_lock);
        sema_init(&dev->caw_sem, 1);
-       atomic_set(&dev->dev_ordered_id, 0);
        INIT_LIST_HEAD(&dev->t10_wwn.t10_vpd_list);
        spin_lock_init(&dev->t10_wwn.t10_vpd_lock);
        INIT_LIST_HEAD(&dev->t10_pr.registration_list);
index 48a3698..be42429 100644 (file)
@@ -203,7 +203,7 @@ static ssize_t target_fabric_mappedlun_store_write_protect(
        pr_debug("%s_ConfigFS: Changed Initiator ACL: %s"
                " Mapped LUN: %llu Write Protect bit to %s\n",
                se_tpg->se_tpg_tfo->get_fabric_name(),
-               lacl->initiatorname, lacl->mapped_lun, (op) ? "ON" : "OFF");
+               se_nacl->initiatorname, lacl->mapped_lun, (op) ? "ON" : "OFF");
 
        return count;
 
index be9cefc..9522960 100644 (file)
@@ -184,3 +184,8 @@ core_delete_hba(struct se_hba *hba)
        kfree(hba);
        return 0;
 }
+
+bool target_sense_desc_format(struct se_device *dev)
+{
+       return dev->transport->get_blocks(dev) > U32_MAX;
+}
index e318ddb..0b4b2a6 100644 (file)
@@ -154,6 +154,38 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd)
        return 0;
 }
 
+static sense_reason_t
+sbc_emulate_startstop(struct se_cmd *cmd)
+{
+       unsigned char *cdb = cmd->t_task_cdb;
+
+       /*
+        * See sbc3r36 section 5.25
+        * Immediate bit should be set since there is nothing to complete
+        * POWER CONDITION MODIFIER 0h
+        */
+       if (!(cdb[1] & 1) || cdb[2] || cdb[3])
+               return TCM_INVALID_CDB_FIELD;
+
+       /*
+        * See sbc3r36 section 5.25
+        * POWER CONDITION 0h START_VALID - process START and LOEJ
+        */
+       if (cdb[4] >> 4 & 0xf)
+               return TCM_INVALID_CDB_FIELD;
+
+       /*
+        * See sbc3r36 section 5.25
+        * LOEJ 0h - nothing to load or unload
+        * START 1h - we are ready
+        */
+       if (!(cdb[4] & 1) || (cdb[4] & 2) || (cdb[4] & 4))
+               return TCM_INVALID_CDB_FIELD;
+
+       target_complete_cmd(cmd, SAM_STAT_GOOD);
+       return 0;
+}
+
 sector_t sbc_get_write_same_sectors(struct se_cmd *cmd)
 {
        u32 num_blocks;
@@ -960,6 +992,9 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                               " than 1\n", sectors);
                        return TCM_INVALID_CDB_FIELD;
                }
+               if (sbc_check_dpofua(dev, cmd, cdb))
+                       return TCM_INVALID_CDB_FIELD;
+
                /*
                 * Double size because we have two buffers, note that
                 * zero is not an error..
@@ -1069,6 +1104,10 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops)
                size = 0;
                cmd->execute_cmd = sbc_emulate_noop;
                break;
+       case START_STOP:
+               size = 0;
+               cmd->execute_cmd = sbc_emulate_startstop;
+               break;
        default:
                ret = spc_parse_cdb(cmd, &size);
                if (ret)
@@ -1191,7 +1230,7 @@ void
 sbc_dif_generate(struct se_cmd *cmd)
 {
        struct se_device *dev = cmd->se_dev;
-       struct se_dif_v1_tuple *sdt;
+       struct t10_pi_tuple *sdt;
        struct scatterlist *dsg = cmd->t_data_sg, *psg;
        sector_t sector = cmd->t_task_lba;
        void *daddr, *paddr;
@@ -1203,7 +1242,7 @@ sbc_dif_generate(struct se_cmd *cmd)
                daddr = kmap_atomic(sg_page(dsg)) + dsg->offset;
 
                for (j = 0; j < psg->length;
-                               j += sizeof(struct se_dif_v1_tuple)) {
+                               j += sizeof(*sdt)) {
                        __u16 crc;
                        unsigned int avail;
 
@@ -1256,7 +1295,7 @@ sbc_dif_generate(struct se_cmd *cmd)
 }
 
 static sense_reason_t
-sbc_dif_v1_verify(struct se_cmd *cmd, struct se_dif_v1_tuple *sdt,
+sbc_dif_v1_verify(struct se_cmd *cmd, struct t10_pi_tuple *sdt,
                  __u16 crc, sector_t sector, unsigned int ei_lba)
 {
        __be16 csum;
@@ -1346,7 +1385,7 @@ sbc_dif_verify(struct se_cmd *cmd, sector_t start, unsigned int sectors,
               unsigned int ei_lba, struct scatterlist *psg, int psg_off)
 {
        struct se_device *dev = cmd->se_dev;
-       struct se_dif_v1_tuple *sdt;
+       struct t10_pi_tuple *sdt;
        struct scatterlist *dsg = cmd->t_data_sg;
        sector_t sector = start;
        void *daddr, *paddr;
@@ -1361,7 +1400,7 @@ sbc_dif_verify(struct se_cmd *cmd, sector_t start, unsigned int sectors,
 
                for (i = psg_off; i < psg->length &&
                                sector < start + sectors;
-                               i += sizeof(struct se_dif_v1_tuple)) {
+                               i += sizeof(*sdt)) {
                        __u16 crc;
                        unsigned int avail;
 
index f87d4ce..9413e1a 100644 (file)
@@ -484,8 +484,8 @@ static sense_reason_t
 spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
 {
        struct se_device *dev = cmd->se_dev;
-       int have_tp = 0;
-       int opt, min;
+       u32 mtl = 0;
+       int have_tp = 0, opt, min;
 
        /*
         * Following spc3r22 section 6.5.3 Block Limits VPD page, when
@@ -516,8 +516,15 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
 
        /*
         * Set MAXIMUM TRANSFER LENGTH
+        *
+        * XXX: Currently assumes single PAGE_SIZE per scatterlist for fabrics
+        * enforcing maximum HW scatter-gather-list entry limit
         */
-       put_unaligned_be32(dev->dev_attrib.hw_max_sectors, &buf[8]);
+       if (cmd->se_tfo->max_data_sg_nents) {
+               mtl = (cmd->se_tfo->max_data_sg_nents * PAGE_SIZE) /
+                      dev->dev_attrib.block_size;
+       }
+       put_unaligned_be32(min_not_zero(mtl, dev->dev_attrib.hw_max_sectors), &buf[8]);
 
        /*
         * Set OPTIMAL TRANSFER LENGTH
@@ -768,7 +775,12 @@ static int spc_modesense_control(struct se_cmd *cmd, u8 pc, u8 *p)
        if (pc == 1)
                goto out;
 
-       p[2] = 2;
+       /* GLTSD: No implicit save of log parameters */
+       p[2] = (1 << 1);
+       if (target_sense_desc_format(dev))
+               /* D_SENSE: Descriptor format sense data for 64bit sectors */
+               p[2] |= (1 << 2);
+
        /*
         * From spc4r23, 7.4.7 Control mode page
         *
@@ -1151,6 +1163,7 @@ static sense_reason_t spc_emulate_request_sense(struct se_cmd *cmd)
        unsigned char *rbuf;
        u8 ua_asc = 0, ua_ascq = 0;
        unsigned char buf[SE_SENSE_BUF];
+       bool desc_format = target_sense_desc_format(cmd->se_dev);
 
        memset(buf, 0, SE_SENSE_BUF);
 
@@ -1164,32 +1177,11 @@ static sense_reason_t spc_emulate_request_sense(struct se_cmd *cmd)
        if (!rbuf)
                return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
 
-       if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) {
-               /*
-                * CURRENT ERROR, UNIT ATTENTION
-                */
-               buf[0] = 0x70;
-               buf[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION;
-
-               /*
-                * The Additional Sense Code (ASC) from the UNIT ATTENTION
-                */
-               buf[SPC_ASC_KEY_OFFSET] = ua_asc;
-               buf[SPC_ASCQ_KEY_OFFSET] = ua_ascq;
-               buf[7] = 0x0A;
-       } else {
-               /*
-                * CURRENT ERROR, NO SENSE
-                */
-               buf[0] = 0x70;
-               buf[SPC_SENSE_KEY_OFFSET] = NO_SENSE;
-
-               /*
-                * NO ADDITIONAL SENSE INFORMATION
-                */
-               buf[SPC_ASC_KEY_OFFSET] = 0x00;
-               buf[7] = 0x0A;
-       }
+       if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq))
+               scsi_build_sense_buffer(desc_format, buf, UNIT_ATTENTION,
+                                       ua_asc, ua_ascq);
+       else
+               scsi_build_sense_buffer(desc_format, buf, NO_SENSE, 0x0, 0x0);
 
        memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length));
        transport_kunmap_data_sg(cmd);
@@ -1418,9 +1410,6 @@ spc_parse_cdb(struct se_cmd *cmd, unsigned int *size)
                }
                break;
        default:
-               pr_warn("TARGET_CORE[%s]: Unsupported SCSI Opcode"
-                       " 0x%02x, sending CHECK_CONDITION.\n",
-                       cmd->se_tfo->get_fabric_name(), cdb[0]);
                return TCM_UNSUPPORTED_SCSI_OPCODE;
        }
 
index babde4a..2d0381d 100644 (file)
@@ -41,6 +41,7 @@
 #include "target_core_internal.h"
 #include "target_core_alua.h"
 #include "target_core_pr.h"
+#include "target_core_ua.h"
 
 extern struct se_device *g_lun0_dev;
 
@@ -83,6 +84,22 @@ struct se_node_acl *core_tpg_get_initiator_node_acl(
 }
 EXPORT_SYMBOL(core_tpg_get_initiator_node_acl);
 
+void core_allocate_nexus_loss_ua(
+       struct se_node_acl *nacl)
+{
+       struct se_dev_entry *deve;
+
+       if (!nacl)
+               return;
+
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link)
+               core_scsi3_ua_allocate(deve, 0x29,
+                       ASCQ_29H_NEXUS_LOSS_OCCURRED);
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL(core_allocate_nexus_loss_ua);
+
 /*     core_tpg_add_node_to_devs():
  *
  *
index ce8574b..5bacc7b 100644 (file)
@@ -39,6 +39,7 @@
 #include <net/sock.h>
 #include <net/tcp.h>
 #include <scsi/scsi_proto.h>
+#include <scsi/scsi_common.h>
 
 #include <target/target_core_base.h>
 #include <target/target_core_backend.h>
@@ -1074,6 +1075,55 @@ transport_set_vpd_ident(struct t10_vpd *vpd, unsigned char *page_83)
 }
 EXPORT_SYMBOL(transport_set_vpd_ident);
 
+static sense_reason_t
+target_check_max_data_sg_nents(struct se_cmd *cmd, struct se_device *dev,
+                              unsigned int size)
+{
+       u32 mtl;
+
+       if (!cmd->se_tfo->max_data_sg_nents)
+               return TCM_NO_SENSE;
+       /*
+        * Check if fabric enforced maximum SGL entries per I/O descriptor
+        * exceeds se_cmd->data_length.  If true, set SCF_UNDERFLOW_BIT +
+        * residual_count and reduce original cmd->data_length to maximum
+        * length based on single PAGE_SIZE entry scatter-lists.
+        */
+       mtl = (cmd->se_tfo->max_data_sg_nents * PAGE_SIZE);
+       if (cmd->data_length > mtl) {
+               /*
+                * If an existing CDB overflow is present, calculate new residual
+                * based on CDB size minus fabric maximum transfer length.
+                *
+                * If an existing CDB underflow is present, calculate new residual
+                * based on original cmd->data_length minus fabric maximum transfer
+                * length.
+                *
+                * Otherwise, set the underflow residual based on cmd->data_length
+                * minus fabric maximum transfer length.
+                */
+               if (cmd->se_cmd_flags & SCF_OVERFLOW_BIT) {
+                       cmd->residual_count = (size - mtl);
+               } else if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) {
+                       u32 orig_dl = size + cmd->residual_count;
+                       cmd->residual_count = (orig_dl - mtl);
+               } else {
+                       cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;
+                       cmd->residual_count = (cmd->data_length - mtl);
+               }
+               cmd->data_length = mtl;
+               /*
+                * Reset sbc_check_prot() calculated protection payload
+                * length based upon the new smaller MTL.
+                */
+               if (cmd->prot_length) {
+                       u32 sectors = (mtl / dev->dev_attrib.block_size);
+                       cmd->prot_length = dev->prot_length * sectors;
+               }
+       }
+       return TCM_NO_SENSE;
+}
+
 sense_reason_t
 target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
 {
@@ -1087,9 +1137,9 @@ target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
                        " 0x%02x\n", cmd->se_tfo->get_fabric_name(),
                                cmd->data_length, size, cmd->t_task_cdb[0]);
 
-               if (cmd->data_direction == DMA_TO_DEVICE) {
-                       pr_err("Rejecting underflow/overflow"
-                                       " WRITE data\n");
+               if (cmd->data_direction == DMA_TO_DEVICE &&
+                   cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) {
+                       pr_err("Rejecting underflow/overflow WRITE data\n");
                        return TCM_INVALID_CDB_FIELD;
                }
                /*
@@ -1119,7 +1169,7 @@ target_cmd_size_check(struct se_cmd *cmd, unsigned int size)
                }
        }
 
-       return 0;
+       return target_check_max_data_sg_nents(cmd, dev, size);
 
 }
 
@@ -1177,14 +1227,7 @@ transport_check_alloc_task_attr(struct se_cmd *cmd)
                        " emulation is not supported\n");
                return TCM_INVALID_CDB_FIELD;
        }
-       /*
-        * Used to determine when ORDERED commands should go from
-        * Dormant to Active status.
-        */
-       cmd->se_ordered_id = atomic_inc_return(&dev->dev_ordered_id);
-       pr_debug("Allocated se_ordered_id: %u for Task Attr: 0x%02x on %s\n",
-                       cmd->se_ordered_id, cmd->sam_task_attr,
-                       dev->transport->name);
+
        return 0;
 }
 
@@ -1246,6 +1289,11 @@ target_setup_cmd_from_cdb(struct se_cmd *cmd, unsigned char *cdb)
        }
 
        ret = dev->transport->parse_cdb(cmd);
+       if (ret == TCM_UNSUPPORTED_SCSI_OPCODE)
+               pr_warn_ratelimited("%s/%s: Unsupported SCSI Opcode 0x%02x, sending CHECK_CONDITION.\n",
+                                   cmd->se_tfo->get_fabric_name(),
+                                   cmd->se_sess->se_node_acl->initiatorname,
+                                   cmd->t_task_cdb[0]);
        if (ret)
                return ret;
 
@@ -1693,8 +1741,7 @@ void transport_generic_request_failure(struct se_cmd *cmd,
 
 check_stop:
        transport_lun_remove_cmd(cmd);
-       if (!transport_cmd_check_stop_to_fabric(cmd))
-               ;
+       transport_cmd_check_stop_to_fabric(cmd);
        return;
 
 queue_full:
@@ -1767,16 +1814,14 @@ static bool target_handle_task_attr(struct se_cmd *cmd)
         */
        switch (cmd->sam_task_attr) {
        case TCM_HEAD_TAG:
-               pr_debug("Added HEAD_OF_QUEUE for CDB: 0x%02x, "
-                        "se_ordered_id: %u\n",
-                        cmd->t_task_cdb[0], cmd->se_ordered_id);
+               pr_debug("Added HEAD_OF_QUEUE for CDB: 0x%02x\n",
+                        cmd->t_task_cdb[0]);
                return false;
        case TCM_ORDERED_TAG:
                atomic_inc_mb(&dev->dev_ordered_sync);
 
-               pr_debug("Added ORDERED for CDB: 0x%02x to ordered list, "
-                        " se_ordered_id: %u\n",
-                        cmd->t_task_cdb[0], cmd->se_ordered_id);
+               pr_debug("Added ORDERED for CDB: 0x%02x to ordered list\n",
+                        cmd->t_task_cdb[0]);
 
                /*
                 * Execute an ORDERED command if no other older commands
@@ -1800,10 +1845,8 @@ static bool target_handle_task_attr(struct se_cmd *cmd)
        list_add_tail(&cmd->se_delayed_node, &dev->delayed_cmd_list);
        spin_unlock(&dev->delayed_cmd_lock);
 
-       pr_debug("Added CDB: 0x%02x Task Attr: 0x%02x to"
-               " delayed CMD list, se_ordered_id: %u\n",
-               cmd->t_task_cdb[0], cmd->sam_task_attr,
-               cmd->se_ordered_id);
+       pr_debug("Added CDB: 0x%02x Task Attr: 0x%02x to delayed CMD listn",
+               cmd->t_task_cdb[0], cmd->sam_task_attr);
        return true;
 }
 
@@ -1888,20 +1931,18 @@ static void transport_complete_task_attr(struct se_cmd *cmd)
        if (cmd->sam_task_attr == TCM_SIMPLE_TAG) {
                atomic_dec_mb(&dev->simple_cmds);
                dev->dev_cur_ordered_id++;
-               pr_debug("Incremented dev->dev_cur_ordered_id: %u for"
-                       " SIMPLE: %u\n", dev->dev_cur_ordered_id,
-                       cmd->se_ordered_id);
+               pr_debug("Incremented dev->dev_cur_ordered_id: %u for SIMPLE\n",
+                        dev->dev_cur_ordered_id);
        } else if (cmd->sam_task_attr == TCM_HEAD_TAG) {
                dev->dev_cur_ordered_id++;
-               pr_debug("Incremented dev_cur_ordered_id: %u for"
-                       " HEAD_OF_QUEUE: %u\n", dev->dev_cur_ordered_id,
-                       cmd->se_ordered_id);
+               pr_debug("Incremented dev_cur_ordered_id: %u for HEAD_OF_QUEUE\n",
+                        dev->dev_cur_ordered_id);
        } else if (cmd->sam_task_attr == TCM_ORDERED_TAG) {
                atomic_dec_mb(&dev->dev_ordered_sync);
 
                dev->dev_cur_ordered_id++;
-               pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED:"
-                       " %u\n", dev->dev_cur_ordered_id, cmd->se_ordered_id);
+               pr_debug("Incremented dev_cur_ordered_id: %u for ORDERED\n",
+                        dev->dev_cur_ordered_id);
        }
 
        target_restart_delayed_cmds(dev);
@@ -2615,37 +2656,159 @@ bool transport_wait_for_tasks(struct se_cmd *cmd)
 }
 EXPORT_SYMBOL(transport_wait_for_tasks);
 
-static int transport_get_sense_codes(
-       struct se_cmd *cmd,
-       u8 *asc,
-       u8 *ascq)
+struct sense_info {
+       u8 key;
+       u8 asc;
+       u8 ascq;
+       bool add_sector_info;
+};
+
+static const struct sense_info sense_info_table[] = {
+       [TCM_NO_SENSE] = {
+               .key = NOT_READY
+       },
+       [TCM_NON_EXISTENT_LUN] = {
+               .key = ILLEGAL_REQUEST,
+               .asc = 0x25 /* LOGICAL UNIT NOT SUPPORTED */
+       },
+       [TCM_UNSUPPORTED_SCSI_OPCODE] = {
+               .key = ILLEGAL_REQUEST,
+               .asc = 0x20, /* INVALID COMMAND OPERATION CODE */
+       },
+       [TCM_SECTOR_COUNT_TOO_MANY] = {
+               .key = ILLEGAL_REQUEST,
+               .asc = 0x20, /* INVALID COMMAND OPERATION CODE */
+       },
+       [TCM_UNKNOWN_MODE_PAGE] = {
+               .key = ILLEGAL_REQUEST,
+               .asc = 0x24, /* INVALID FIELD IN CDB */
+       },
+       [TCM_CHECK_CONDITION_ABORT_CMD] = {
+               .key = ABORTED_COMMAND,
+               .asc = 0x29, /* BUS DEVICE RESET FUNCTION OCCURRED */
+               .ascq = 0x03,
+       },
+       [TCM_INCORRECT_AMOUNT_OF_DATA] = {
+               .key = ABORTED_COMMAND,
+               .asc = 0x0c, /* WRITE ERROR */
+               .ascq = 0x0d, /* NOT ENOUGH UNSOLICITED DATA */
+       },
+       [TCM_INVALID_CDB_FIELD] = {
+               .key = ILLEGAL_REQUEST,
+               .asc = 0x24, /* INVALID FIELD IN CDB */
+       },
+       [TCM_INVALID_PARAMETER_LIST] = {
+               .key = ILLEGAL_REQUEST,
+               .asc = 0x26, /* INVALID FIELD IN PARAMETER LIST */
+       },
+       [TCM_PARAMETER_LIST_LENGTH_ERROR] = {
+               .key = ILLEGAL_REQUEST,
+               .asc = 0x1a, /* PARAMETER LIST LENGTH ERROR */
+       },
+       [TCM_UNEXPECTED_UNSOLICITED_DATA] = {
+               .key = ILLEGAL_REQUEST,
+               .asc = 0x0c, /* WRITE ERROR */
+               .ascq = 0x0c, /* UNEXPECTED_UNSOLICITED_DATA */
+       },
+       [TCM_SERVICE_CRC_ERROR] = {
+               .key = ABORTED_COMMAND,
+               .asc = 0x47, /* PROTOCOL SERVICE CRC ERROR */
+               .ascq = 0x05, /* N/A */
+       },
+       [TCM_SNACK_REJECTED] = {
+               .key = ABORTED_COMMAND,
+               .asc = 0x11, /* READ ERROR */
+               .ascq = 0x13, /* FAILED RETRANSMISSION REQUEST */
+       },
+       [TCM_WRITE_PROTECTED] = {
+               .key = DATA_PROTECT,
+               .asc = 0x27, /* WRITE PROTECTED */
+       },
+       [TCM_ADDRESS_OUT_OF_RANGE] = {
+               .key = ILLEGAL_REQUEST,
+               .asc = 0x21, /* LOGICAL BLOCK ADDRESS OUT OF RANGE */
+       },
+       [TCM_CHECK_CONDITION_UNIT_ATTENTION] = {
+               .key = UNIT_ATTENTION,
+       },
+       [TCM_CHECK_CONDITION_NOT_READY] = {
+               .key = NOT_READY,
+       },
+       [TCM_MISCOMPARE_VERIFY] = {
+               .key = MISCOMPARE,
+               .asc = 0x1d, /* MISCOMPARE DURING VERIFY OPERATION */
+               .ascq = 0x00,
+       },
+       [TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED] = {
+               .key = ABORTED_COMMAND,
+               .asc = 0x10,
+               .ascq = 0x01, /* LOGICAL BLOCK GUARD CHECK FAILED */
+               .add_sector_info = true,
+       },
+       [TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED] = {
+               .key = ABORTED_COMMAND,
+               .asc = 0x10,
+               .ascq = 0x02, /* LOGICAL BLOCK APPLICATION TAG CHECK FAILED */
+               .add_sector_info = true,
+       },
+       [TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED] = {
+               .key = ABORTED_COMMAND,
+               .asc = 0x10,
+               .ascq = 0x03, /* LOGICAL BLOCK REFERENCE TAG CHECK FAILED */
+               .add_sector_info = true,
+       },
+       [TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE] = {
+               /*
+                * Returning ILLEGAL REQUEST would cause immediate IO errors on
+                * Solaris initiators.  Returning NOT READY instead means the
+                * operations will be retried a finite number of times and we
+                * can survive intermittent errors.
+                */
+               .key = NOT_READY,
+               .asc = 0x08, /* LOGICAL UNIT COMMUNICATION FAILURE */
+       },
+};
+
+static int translate_sense_reason(struct se_cmd *cmd, sense_reason_t reason)
 {
-       *asc = cmd->scsi_asc;
-       *ascq = cmd->scsi_ascq;
+       const struct sense_info *si;
+       u8 *buffer = cmd->sense_buffer;
+       int r = (__force int)reason;
+       u8 asc, ascq;
+       bool desc_format = target_sense_desc_format(cmd->se_dev);
 
-       return 0;
-}
+       if (r < ARRAY_SIZE(sense_info_table) && sense_info_table[r].key)
+               si = &sense_info_table[r];
+       else
+               si = &sense_info_table[(__force int)
+                                      TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE];
 
-static
-void transport_err_sector_info(unsigned char *buffer, sector_t bad_sector)
-{
-       /* Place failed LBA in sense data information descriptor 0. */
-       buffer[SPC_ADD_SENSE_LEN_OFFSET] = 0xc;
-       buffer[SPC_DESC_TYPE_OFFSET] = 0; /* Information */
-       buffer[SPC_ADDITIONAL_DESC_LEN_OFFSET] = 0xa;
-       buffer[SPC_VALIDITY_OFFSET] = 0x80;
+       if (reason == TCM_CHECK_CONDITION_UNIT_ATTENTION) {
+               core_scsi3_ua_for_check_condition(cmd, &asc, &ascq);
+               WARN_ON_ONCE(asc == 0);
+       } else if (si->asc == 0) {
+               WARN_ON_ONCE(cmd->scsi_asc == 0);
+               asc = cmd->scsi_asc;
+               ascq = cmd->scsi_ascq;
+       } else {
+               asc = si->asc;
+               ascq = si->ascq;
+       }
+
+       scsi_build_sense_buffer(desc_format, buffer, si->key, asc, ascq);
+       if (si->add_sector_info)
+               return scsi_set_sense_information(buffer,
+                                                 cmd->scsi_sense_length,
+                                                 cmd->bad_sector);
 
-       /* Descriptor Information: failing sector */
-       put_unaligned_be64(bad_sector, &buffer[12]);
+       return 0;
 }
 
 int
 transport_send_check_condition_and_sense(struct se_cmd *cmd,
                sense_reason_t reason, int from_transport)
 {
-       unsigned char *buffer = cmd->sense_buffer;
        unsigned long flags;
-       u8 asc = 0, ascq = 0;
 
        spin_lock_irqsave(&cmd->t_state_lock, flags);
        if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) {
@@ -2655,243 +2818,17 @@ transport_send_check_condition_and_sense(struct se_cmd *cmd,
        cmd->se_cmd_flags |= SCF_SENT_CHECK_CONDITION;
        spin_unlock_irqrestore(&cmd->t_state_lock, flags);
 
-       if (!reason && from_transport)
-               goto after_reason;
+       if (!from_transport) {
+               int rc;
 
-       if (!from_transport)
                cmd->se_cmd_flags |= SCF_EMULATED_TASK_SENSE;
-
-       /*
-        * Actual SENSE DATA, see SPC-3 7.23.2  SPC_SENSE_KEY_OFFSET uses
-        * SENSE KEY values from include/scsi/scsi.h
-        */
-       switch (reason) {
-       case TCM_NO_SENSE:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* Not Ready */
-               buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY;
-               /* NO ADDITIONAL SENSE INFORMATION */
-               buffer[SPC_ASC_KEY_OFFSET] = 0;
-               buffer[SPC_ASCQ_KEY_OFFSET] = 0;
-               break;
-       case TCM_NON_EXISTENT_LUN:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
-               /* LOGICAL UNIT NOT SUPPORTED */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x25;
-               break;
-       case TCM_UNSUPPORTED_SCSI_OPCODE:
-       case TCM_SECTOR_COUNT_TOO_MANY:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
-               /* INVALID COMMAND OPERATION CODE */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x20;
-               break;
-       case TCM_UNKNOWN_MODE_PAGE:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
-               /* INVALID FIELD IN CDB */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x24;
-               break;
-       case TCM_CHECK_CONDITION_ABORT_CMD:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ABORTED COMMAND */
-               buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
-               /* BUS DEVICE RESET FUNCTION OCCURRED */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x29;
-               buffer[SPC_ASCQ_KEY_OFFSET] = 0x03;
-               break;
-       case TCM_INCORRECT_AMOUNT_OF_DATA:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ABORTED COMMAND */
-               buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
-               /* WRITE ERROR */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x0c;
-               /* NOT ENOUGH UNSOLICITED DATA */
-               buffer[SPC_ASCQ_KEY_OFFSET] = 0x0d;
-               break;
-       case TCM_INVALID_CDB_FIELD:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
-               /* INVALID FIELD IN CDB */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x24;
-               break;
-       case TCM_INVALID_PARAMETER_LIST:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
-               /* INVALID FIELD IN PARAMETER LIST */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x26;
-               break;
-       case TCM_PARAMETER_LIST_LENGTH_ERROR:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
-               /* PARAMETER LIST LENGTH ERROR */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x1a;
-               break;
-       case TCM_UNEXPECTED_UNSOLICITED_DATA:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ABORTED COMMAND */
-               buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
-               /* WRITE ERROR */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x0c;
-               /* UNEXPECTED_UNSOLICITED_DATA */
-               buffer[SPC_ASCQ_KEY_OFFSET] = 0x0c;
-               break;
-       case TCM_SERVICE_CRC_ERROR:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ABORTED COMMAND */
-               buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
-               /* PROTOCOL SERVICE CRC ERROR */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x47;
-               /* N/A */
-               buffer[SPC_ASCQ_KEY_OFFSET] = 0x05;
-               break;
-       case TCM_SNACK_REJECTED:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ABORTED COMMAND */
-               buffer[SPC_SENSE_KEY_OFFSET] = ABORTED_COMMAND;
-               /* READ ERROR */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x11;
-               /* FAILED RETRANSMISSION REQUEST */
-               buffer[SPC_ASCQ_KEY_OFFSET] = 0x13;
-               break;
-       case TCM_WRITE_PROTECTED:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* DATA PROTECT */
-               buffer[SPC_SENSE_KEY_OFFSET] = DATA_PROTECT;
-               /* WRITE PROTECTED */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x27;
-               break;
-       case TCM_ADDRESS_OUT_OF_RANGE:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
-               /* LOGICAL BLOCK ADDRESS OUT OF RANGE */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x21;
-               break;
-       case TCM_CHECK_CONDITION_UNIT_ATTENTION:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* UNIT ATTENTION */
-               buffer[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION;
-               core_scsi3_ua_for_check_condition(cmd, &asc, &ascq);
-               buffer[SPC_ASC_KEY_OFFSET] = asc;
-               buffer[SPC_ASCQ_KEY_OFFSET] = ascq;
-               break;
-       case TCM_CHECK_CONDITION_NOT_READY:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* Not Ready */
-               buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY;
-               transport_get_sense_codes(cmd, &asc, &ascq);
-               buffer[SPC_ASC_KEY_OFFSET] = asc;
-               buffer[SPC_ASCQ_KEY_OFFSET] = ascq;
-               break;
-       case TCM_MISCOMPARE_VERIFY:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               buffer[SPC_SENSE_KEY_OFFSET] = MISCOMPARE;
-               /* MISCOMPARE DURING VERIFY OPERATION */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x1d;
-               buffer[SPC_ASCQ_KEY_OFFSET] = 0x00;
-               break;
-       case TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
-               /* LOGICAL BLOCK GUARD CHECK FAILED */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x10;
-               buffer[SPC_ASCQ_KEY_OFFSET] = 0x01;
-               transport_err_sector_info(buffer, cmd->bad_sector);
-               break;
-       case TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
-               /* LOGICAL BLOCK APPLICATION TAG CHECK FAILED */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x10;
-               buffer[SPC_ASCQ_KEY_OFFSET] = 0x02;
-               transport_err_sector_info(buffer, cmd->bad_sector);
-               break;
-       case TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /* ILLEGAL REQUEST */
-               buffer[SPC_SENSE_KEY_OFFSET] = ILLEGAL_REQUEST;
-               /* LOGICAL BLOCK REFERENCE TAG CHECK FAILED */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x10;
-               buffer[SPC_ASCQ_KEY_OFFSET] = 0x03;
-               transport_err_sector_info(buffer, cmd->bad_sector);
-               break;
-       case TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE:
-       default:
-               /* CURRENT ERROR */
-               buffer[0] = 0x70;
-               buffer[SPC_ADD_SENSE_LEN_OFFSET] = 10;
-               /*
-                * Returning ILLEGAL REQUEST would cause immediate IO errors on
-                * Solaris initiators.  Returning NOT READY instead means the
-                * operations will be retried a finite number of times and we
-                * can survive intermittent errors.
-                */
-               buffer[SPC_SENSE_KEY_OFFSET] = NOT_READY;
-               /* LOGICAL UNIT COMMUNICATION FAILURE */
-               buffer[SPC_ASC_KEY_OFFSET] = 0x08;
-               break;
+               cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
+               cmd->scsi_sense_length  = TRANSPORT_SENSE_BUFFER;
+               rc = translate_sense_reason(cmd, reason);
+               if (rc)
+                       return rc;
        }
-       /*
-        * This code uses linux/include/scsi/scsi.h SAM status codes!
-        */
-       cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
-       /*
-        * Automatically padded, this value is encoded in the fabric's
-        * data_length response PDU containing the SCSI defined sense data.
-        */
-       cmd->scsi_sense_length  = TRANSPORT_SENSE_BUFFER;
 
-after_reason:
        trace_target_cmd_complete(cmd);
        return cmd->se_tfo->queue_status(cmd);
 }
index c448ef4..937cebf 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/parser.h>
 #include <linux/vmalloc.h>
 #include <linux/uio_driver.h>
+#include <linux/stringify.h>
 #include <net/genetlink.h>
 #include <scsi/scsi_common.h>
 #include <scsi/scsi_proto.h>
@@ -538,14 +539,8 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *
                UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size);
                pr_warn("TCMU: Userspace set UNKNOWN_OP flag on se_cmd %p\n",
                        cmd->se_cmd);
-               transport_generic_request_failure(cmd->se_cmd,
-                       TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE);
-               cmd->se_cmd = NULL;
-               kmem_cache_free(tcmu_cmd_cache, cmd);
-               return;
-       }
-
-       if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) {
+               entry->rsp.scsi_status = SAM_STAT_CHECK_CONDITION;
+       } else if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) {
                memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer,
                               se_cmd->scsi_sense_length);
 
@@ -577,7 +572,6 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *
 static unsigned int tcmu_handle_completions(struct tcmu_dev *udev)
 {
        struct tcmu_mailbox *mb;
-       LIST_HEAD(cpl_cmds);
        unsigned long flags;
        int handled = 0;
 
@@ -905,7 +899,7 @@ static int tcmu_configure_device(struct se_device *dev)
        WARN_ON(!PAGE_ALIGNED(udev->data_off));
        WARN_ON(udev->data_size % PAGE_SIZE);
 
-       info->version = xstr(TCMU_MAILBOX_VERSION);
+       info->version = __stringify(TCMU_MAILBOX_VERSION);
 
        info->mem[0].name = "tcm-user command & data buffer";
        info->mem[0].addr = (phys_addr_t) udev->mb_addr;
index 4515f52..47fe94e 100644 (file)
@@ -450,6 +450,8 @@ int target_xcopy_setup_pt(void)
        memset(&xcopy_pt_sess, 0, sizeof(struct se_session));
        INIT_LIST_HEAD(&xcopy_pt_sess.sess_list);
        INIT_LIST_HEAD(&xcopy_pt_sess.sess_acl_list);
+       INIT_LIST_HEAD(&xcopy_pt_sess.sess_cmd_list);
+       spin_lock_init(&xcopy_pt_sess.sess_cmd_lock);
 
        xcopy_pt_nacl.se_tpg = &xcopy_pt_tpg;
        xcopy_pt_nacl.nacl_sess = &xcopy_pt_sess;
@@ -644,7 +646,7 @@ static int target_xcopy_read_source(
        pr_debug("XCOPY: Built READ_16: LBA: %llu Sectors: %u Length: %u\n",
                (unsigned long long)src_lba, src_sectors, length);
 
-       transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length,
+       transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, length,
                              DMA_FROM_DEVICE, 0, &xpt_cmd->sense_buffer[0]);
        xop->src_pt_cmd = xpt_cmd;
 
@@ -704,7 +706,7 @@ static int target_xcopy_write_destination(
        pr_debug("XCOPY: Built WRITE_16: LBA: %llu Sectors: %u Length: %u\n",
                (unsigned long long)dst_lba, dst_sectors, length);
 
-       transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, NULL, length,
+       transport_init_se_cmd(se_cmd, &xcopy_pt_tfo, &xcopy_pt_sess, length,
                              DMA_TO_DEVICE, 0, &xpt_cmd->sense_buffer[0]);
        xop->dst_pt_cmd = xpt_cmd;
 
index 6803172..aa3caca 100644 (file)
@@ -255,7 +255,7 @@ static void ft_recv_seq(struct fc_seq *sp, struct fc_frame *fp, void *arg)
        struct ft_cmd *cmd = arg;
        struct fc_frame_header *fh;
 
-       if (unlikely(IS_ERR(fp))) {
+       if (IS_ERR(fp)) {
                /* XXX need to find cmd if queued */
                cmd->seq = NULL;
                cmd->aborted = true;
index 118938e..0390044 100644 (file)
@@ -340,6 +340,14 @@ config ACPI_THERMAL_REL
        tristate
        depends on ACPI
 
+config INTEL_PCH_THERMAL
+       tristate "Intel PCH Thermal Reporting Driver"
+       depends on X86 && PCI
+       help
+         Enable this to support thermal reporting on certain intel PCHs.
+         Thermal reporting device will provide temperature reading,
+         programmable trip points and other information.
+
 menu "Texas Instruments thermal drivers"
 source "drivers/thermal/ti-soc-thermal/Kconfig"
 endmenu
index 535dfee..26f1608 100644 (file)
@@ -41,6 +41,7 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL)   += intel_soc_dts_thermal.o
 obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL)  += intel_quark_dts_thermal.o
 obj-$(CONFIG_TI_SOC_THERMAL)   += ti-soc-thermal/
 obj-$(CONFIG_INT340X_THERMAL)  += int340x_thermal/
+obj-$(CONFIG_INTEL_PCH_THERMAL)        += intel_pch_thermal.o
 obj-$(CONFIG_ST_THERMAL)       += st/
 obj-$(CONFIG_TEGRA_SOCTHERM)   += tegra_soctherm.o
 obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
index 01255fd..26b8d32 100644 (file)
@@ -155,7 +155,7 @@ static bool armada_is_valid(struct armada_thermal_priv *priv)
 }
 
 static int armada_get_temp(struct thermal_zone_device *thermal,
-                         unsigned long *temp)
+                         int *temp)
 {
        struct armada_thermal_priv *priv = thermal->devdata;
        unsigned long reg;
index 2fb273c..652acd8 100644 (file)
@@ -107,8 +107,7 @@ static int db8500_cdev_unbind(struct thermal_zone_device *thermal,
 }
 
 /* Callback to get current temperature */
-static int db8500_sys_get_temp(struct thermal_zone_device *thermal,
-               unsigned long *temp)
+static int db8500_sys_get_temp(struct thermal_zone_device *thermal, int *temp)
 {
        struct db8500_thermal_zone *pzone = thermal->devdata;
 
@@ -180,7 +179,7 @@ static int db8500_sys_get_trip_type(struct thermal_zone_device *thermal,
 
 /* Callback to get trip point temperature */
 static int db8500_sys_get_trip_temp(struct thermal_zone_device *thermal,
-               int trip, unsigned long *temp)
+               int trip, int *temp)
 {
        struct db8500_thermal_zone *pzone = thermal->devdata;
        struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
@@ -195,7 +194,7 @@ static int db8500_sys_get_trip_temp(struct thermal_zone_device *thermal,
 
 /* Callback to get critical trip point temperature */
 static int db8500_sys_get_crit_temp(struct thermal_zone_device *thermal,
-               unsigned long *temp)
+               int *temp)
 {
        struct db8500_thermal_zone *pzone = thermal->devdata;
        struct db8500_thsens_platform_data *ptrips = pzone->trip_tab;
index 09f6e30..a0bc9de 100644 (file)
@@ -93,7 +93,7 @@ static int dove_init_sensor(const struct dove_thermal_priv *priv)
 }
 
 static int dove_get_temp(struct thermal_zone_device *thermal,
-                         unsigned long *temp)
+                         int *temp)
 {
        unsigned long reg;
        struct dove_thermal_priv *priv = thermal->devdata;
index c2c10bb..34fe365 100644 (file)
@@ -34,7 +34,7 @@
 static int get_trip_level(struct thermal_zone_device *tz)
 {
        int count = 0;
-       unsigned long trip_temp;
+       int trip_temp;
        enum thermal_trip_type trip_type;
 
        if (tz->trips == 0 || !tz->ops->get_trip_temp)
index c5dd76b..70836c5 100644 (file)
 
 static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
 {
-       long trip_temp;
-       unsigned long trip_hyst;
+       int trip_temp, trip_hyst;
        struct thermal_instance *instance;
 
        tz->ops->get_trip_temp(tz, trip, &trip_temp);
        tz->ops->get_trip_hyst(tz, trip, &trip_hyst);
 
-       dev_dbg(&tz->device, "Trip%d[temp=%ld]:temp=%d:hyst=%ld\n",
+       dev_dbg(&tz->device, "Trip%d[temp=%d]:temp=%d:hyst=%d\n",
                                trip, trip_temp, tz->temperature,
                                trip_hyst);
 
index b49f97c..36d0729 100644 (file)
@@ -155,7 +155,7 @@ static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data)
        mutex_unlock(&data->thermal_lock);
 }
 
-static int hisi_thermal_get_temp(void *_sensor, long *temp)
+static int hisi_thermal_get_temp(void *_sensor, int *temp)
 {
        struct hisi_thermal_sensor *sensor = _sensor;
        struct hisi_thermal_data *data = sensor->thermal;
@@ -178,7 +178,7 @@ static int hisi_thermal_get_temp(void *_sensor, long *temp)
        data->irq_bind_sensor = sensor_id;
        mutex_unlock(&data->thermal_lock);
 
-       dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%ld, thres=%d\n",
+       dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%d, thres=%d\n",
                sensor->id, data->irq_enabled, *temp, sensor->thres_temp);
        /*
         * Bind irq to sensor for two cases:
index fde4c28..4bec1d3 100644 (file)
@@ -98,10 +98,10 @@ struct imx_thermal_data {
        enum thermal_device_mode mode;
        struct regmap *tempmon;
        u32 c1, c2; /* See formula in imx_get_sensor_data() */
-       unsigned long temp_passive;
-       unsigned long temp_critical;
-       unsigned long alarm_temp;
-       unsigned long last_temp;
+       int temp_passive;
+       int temp_critical;
+       int alarm_temp;
+       int last_temp;
        bool irq_enabled;
        int irq;
        struct clk *thermal_clk;
@@ -109,7 +109,7 @@ struct imx_thermal_data {
 };
 
 static void imx_set_panic_temp(struct imx_thermal_data *data,
-                              signed long panic_temp)
+                              int panic_temp)
 {
        struct regmap *map = data->tempmon;
        int critical_value;
@@ -121,7 +121,7 @@ static void imx_set_panic_temp(struct imx_thermal_data *data,
 }
 
 static void imx_set_alarm_temp(struct imx_thermal_data *data,
-                              signed long alarm_temp)
+                              int alarm_temp)
 {
        struct regmap *map = data->tempmon;
        int alarm_value;
@@ -133,7 +133,7 @@ static void imx_set_alarm_temp(struct imx_thermal_data *data,
                        TEMPSENSE0_ALARM_VALUE_SHIFT);
 }
 
-static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
+static int imx_get_temp(struct thermal_zone_device *tz, int *temp)
 {
        struct imx_thermal_data *data = tz->devdata;
        struct regmap *map = data->tempmon;
@@ -189,13 +189,13 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
                if (data->alarm_temp == data->temp_critical &&
                        *temp < data->temp_passive) {
                        imx_set_alarm_temp(data, data->temp_passive);
-                       dev_dbg(&tz->device, "thermal alarm off: T < %lu\n",
+                       dev_dbg(&tz->device, "thermal alarm off: T < %d\n",
                                data->alarm_temp / 1000);
                }
        }
 
        if (*temp != data->last_temp) {
-               dev_dbg(&tz->device, "millicelsius: %ld\n", *temp);
+               dev_dbg(&tz->device, "millicelsius: %d\n", *temp);
                data->last_temp = *temp;
        }
 
@@ -262,8 +262,7 @@ static int imx_get_trip_type(struct thermal_zone_device *tz, int trip,
        return 0;
 }
 
-static int imx_get_crit_temp(struct thermal_zone_device *tz,
-                            unsigned long *temp)
+static int imx_get_crit_temp(struct thermal_zone_device *tz, int *temp)
 {
        struct imx_thermal_data *data = tz->devdata;
 
@@ -272,7 +271,7 @@ static int imx_get_crit_temp(struct thermal_zone_device *tz,
 }
 
 static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip,
-                            unsigned long *temp)
+                            int *temp)
 {
        struct imx_thermal_data *data = tz->devdata;
 
@@ -282,7 +281,7 @@ static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip,
 }
 
 static int imx_set_trip_temp(struct thermal_zone_device *tz, int trip,
-                            unsigned long temp)
+                            int temp)
 {
        struct imx_thermal_data *data = tz->devdata;
 
@@ -434,7 +433,7 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev)
 {
        struct imx_thermal_data *data = dev;
 
-       dev_dbg(&data->tz->device, "THERMAL ALARM: T > %lu\n",
+       dev_dbg(&data->tz->device, "THERMAL ALARM: T > %d\n",
                data->alarm_temp / 1000);
 
        thermal_zone_device_update(data->tz);
index 031018e..5836e55 100644 (file)
@@ -186,7 +186,7 @@ static int int3400_thermal_run_osc(acpi_handle handle,
 }
 
 static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
-                       unsigned long *temp)
+                       int *temp)
 {
        *temp = 20 * 1000; /* faked temp sensor with 20C */
        return 0;
index 1e25133..b9b2666 100644 (file)
@@ -20,7 +20,7 @@
 #include "int340x_thermal_zone.h"
 
 static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
-                                        unsigned long *temp)
+                                        int *temp)
 {
        struct int34x_thermal_zone *d = zone->devdata;
        unsigned long long tmp;
@@ -49,7 +49,7 @@ static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
 }
 
 static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
-                                        int trip, unsigned long *temp)
+                                        int trip, int *temp)
 {
        struct int34x_thermal_zone *d = zone->devdata;
        int i;
@@ -114,7 +114,7 @@ static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
 }
 
 static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
-                                     int trip, unsigned long temp)
+                                     int trip, int temp)
 {
        struct int34x_thermal_zone *d = zone->devdata;
        acpi_status status;
@@ -136,7 +136,7 @@ static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
 
 
 static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
-               int trip, unsigned long *temp)
+               int trip, int *temp)
 {
        struct int34x_thermal_zone *d = zone->devdata;
        acpi_status status;
@@ -163,7 +163,7 @@ static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
 };
 
 static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
-                                     unsigned long *temp)
+                                     int *temp)
 {
        unsigned long long r;
        acpi_status status;
index 9f38ab7..aaadf72 100644 (file)
@@ -21,7 +21,7 @@
 #define INT340X_THERMAL_MAX_ACT_TRIP_COUNT     10
 
 struct active_trip {
-       unsigned long temp;
+       int temp;
        int id;
        bool valid;
 };
@@ -31,11 +31,11 @@ struct int34x_thermal_zone {
        struct active_trip act_trips[INT340X_THERMAL_MAX_ACT_TRIP_COUNT];
        unsigned long *aux_trips;
        int aux_trip_nr;
-       unsigned long psv_temp;
+       int psv_temp;
        int psv_trip_id;
-       unsigned long crt_temp;
+       int crt_temp;
        int crt_trip_id;
-       unsigned long hot_temp;
+       int hot_temp;
        int hot_trip_id;
        struct thermal_zone_device *zone;
        struct thermal_zone_device_ops *override_ops;
index 3df3dc3..ccc0ad0 100644 (file)
@@ -145,7 +145,7 @@ static int get_tjmax(void)
        return -EINVAL;
 }
 
-static int read_temp_msr(unsigned long *temp)
+static int read_temp_msr(int *temp)
 {
        int cpu;
        u32 eax, edx;
@@ -177,7 +177,7 @@ err_ret:
 }
 
 static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
-                                        unsigned long *temp)
+                                        int *temp)
 {
        int ret;
 
diff --git a/drivers/thermal/intel_pch_thermal.c b/drivers/thermal/intel_pch_thermal.c
new file mode 100644 (file)
index 0000000..50c7da7
--- /dev/null
@@ -0,0 +1,283 @@
+/* intel_pch_thermal.c - Intel PCH Thermal driver
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * Authors:
+ *     Tushar Dave <tushar.n.dave@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/thermal.h>
+
+/* Intel PCH thermal Device IDs */
+#define PCH_THERMAL_DID_WPT    0x9CA4 /* Wildcat Point */
+
+/* Wildcat Point-LP  PCH Thermal registers */
+#define WPT_TEMP       0x0000  /* Temperature */
+#define WPT_TSC        0x04    /* Thermal Sensor Control */
+#define WPT_TSS        0x06    /* Thermal Sensor Status */
+#define WPT_TSEL       0x08    /* Thermal Sensor Enable and Lock */
+#define WPT_TSREL      0x0A    /* Thermal Sensor Report Enable and Lock */
+#define WPT_TSMIC      0x0C    /* Thermal Sensor SMI Control */
+#define WPT_CTT        0x0010  /* Catastrophic Trip Point */
+#define WPT_TAHV       0x0014  /* Thermal Alert High Value */
+#define WPT_TALV       0x0018  /* Thermal Alert Low Value */
+#define WPT_TL         0x00000040      /* Throttle Value */
+#define WPT_PHL        0x0060  /* PCH Hot Level */
+#define WPT_PHLC       0x62    /* PHL Control */
+#define WPT_TAS        0x80    /* Thermal Alert Status */
+#define WPT_TSPIEN     0x82    /* PCI Interrupt Event Enables */
+#define WPT_TSGPEN     0x84    /* General Purpose Event Enables */
+
+/*  Wildcat Point-LP  PCH Thermal Register bit definitions */
+#define WPT_TEMP_TSR   0x00ff  /* Temp TS Reading */
+#define WPT_TSC_CPDE   0x01    /* Catastrophic Power-Down Enable */
+#define WPT_TSS_TSDSS  0x10    /* Thermal Sensor Dynamic Shutdown Status */
+#define WPT_TSS_GPES   0x08    /* GPE status */
+#define WPT_TSEL_ETS   0x01    /* Enable TS */
+#define WPT_TSEL_PLDB  0x80    /* TSEL Policy Lock-Down Bit */
+#define WPT_TL_TOL     0x000001FF      /* T0 Level */
+#define WPT_TL_T1L     0x1ff00000      /* T1 Level */
+#define WPT_TL_TTEN    0x20000000      /* TT Enable */
+
+static char driver_name[] = "Intel PCH thermal driver";
+
+struct pch_thermal_device {
+       void __iomem *hw_base;
+       const struct pch_dev_ops *ops;
+       struct pci_dev *pdev;
+       struct thermal_zone_device *tzd;
+       int crt_trip_id;
+       unsigned long crt_temp;
+       int hot_trip_id;
+       unsigned long hot_temp;
+};
+
+static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
+{
+       u8 tsel;
+       u16 trip_temp;
+
+       *nr_trips = 0;
+
+       /* Check if BIOS has already enabled thermal sensor */
+       if (WPT_TSS_TSDSS & readb(ptd->hw_base + WPT_TSS))
+               goto read_trips;
+
+       tsel = readb(ptd->hw_base + WPT_TSEL);
+       /*
+        * When TSEL's Policy Lock-Down bit is 1, TSEL become RO.
+        * If so, thermal sensor cannot enable. Bail out.
+        */
+       if (tsel & WPT_TSEL_PLDB) {
+               dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
+               return -ENODEV;
+       }
+
+       writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
+       if (!(WPT_TSS_TSDSS & readb(ptd->hw_base + WPT_TSS))) {
+               dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
+               return -ENODEV;
+       }
+
+read_trips:
+       ptd->crt_trip_id = -1;
+       trip_temp = readw(ptd->hw_base + WPT_CTT);
+       trip_temp &= 0x1FF;
+       if (trip_temp) {
+               /* Resolution of 1/2 degree C and an offset of -50C */
+               ptd->crt_temp = trip_temp * 1000 / 2 - 50000;
+               ptd->crt_trip_id = 0;
+               ++(*nr_trips);
+       }
+
+       ptd->hot_trip_id = -1;
+       trip_temp = readw(ptd->hw_base + WPT_PHL);
+       trip_temp &= 0x1FF;
+       if (trip_temp) {
+               /* Resolution of 1/2 degree C and an offset of -50C */
+               ptd->hot_temp = trip_temp * 1000 / 2 - 50000;
+               ptd->hot_trip_id = *nr_trips;
+               ++(*nr_trips);
+       }
+
+       return 0;
+}
+
+static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp)
+{
+       u8 wpt_temp;
+
+       wpt_temp = WPT_TEMP_TSR & readl(ptd->hw_base + WPT_TEMP);
+
+       /* Resolution of 1/2 degree C and an offset of -50C */
+       *temp = (wpt_temp * 1000 / 2 - 50000);
+
+       return 0;
+}
+
+struct pch_dev_ops {
+       int (*hw_init)(struct pch_thermal_device *ptd, int *nr_trips);
+       int (*get_temp)(struct pch_thermal_device *ptd, int *temp);
+};
+
+
+/* dev ops for Wildcat Point */
+static struct pch_dev_ops pch_dev_ops_wpt = {
+       .hw_init = pch_wpt_init,
+       .get_temp = pch_wpt_get_temp,
+};
+
+static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
+{
+       struct pch_thermal_device *ptd = tzd->devdata;
+
+       return  ptd->ops->get_temp(ptd, temp);
+}
+
+static int pch_get_trip_type(struct thermal_zone_device *tzd, int trip,
+                            enum thermal_trip_type *type)
+{
+       struct pch_thermal_device *ptd = tzd->devdata;
+
+       if (ptd->crt_trip_id == trip)
+               *type = THERMAL_TRIP_CRITICAL;
+       else if (ptd->hot_trip_id == trip)
+               *type = THERMAL_TRIP_HOT;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *temp)
+{
+       struct pch_thermal_device *ptd = tzd->devdata;
+
+       if (ptd->crt_trip_id == trip)
+               *temp = ptd->crt_temp;
+       else if (ptd->hot_trip_id == trip)
+               *temp = ptd->hot_temp;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static struct thermal_zone_device_ops tzd_ops = {
+       .get_temp = pch_thermal_get_temp,
+       .get_trip_type = pch_get_trip_type,
+       .get_trip_temp = pch_get_trip_temp,
+};
+
+
+static int intel_pch_thermal_probe(struct pci_dev *pdev,
+                                  const struct pci_device_id *id)
+{
+       struct pch_thermal_device *ptd;
+       int err;
+       int nr_trips;
+       char *dev_name;
+
+       ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
+       if (!ptd)
+               return -ENOMEM;
+
+       switch (pdev->device) {
+       case PCH_THERMAL_DID_WPT:
+               ptd->ops = &pch_dev_ops_wpt;
+               dev_name = "pch_wildcat_point";
+               break;
+       default:
+               dev_err(&pdev->dev, "unknown pch thermal device\n");
+               return -ENODEV;
+       }
+
+       pci_set_drvdata(pdev, ptd);
+       ptd->pdev = pdev;
+
+       err = pci_enable_device(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "failed to enable pci device\n");
+               return err;
+       }
+
+       err = pci_request_regions(pdev, driver_name);
+       if (err) {
+               dev_err(&pdev->dev, "failed to request pci region\n");
+               goto error_disable;
+       }
+
+       ptd->hw_base = pci_ioremap_bar(pdev, 0);
+       if (!ptd->hw_base) {
+               err = -ENOMEM;
+               dev_err(&pdev->dev, "failed to map mem base\n");
+               goto error_release;
+       }
+
+       err = ptd->ops->hw_init(ptd, &nr_trips);
+       if (err)
+               goto error_cleanup;
+
+       ptd->tzd = thermal_zone_device_register(dev_name, nr_trips, 0, ptd,
+                                               &tzd_ops, NULL, 0, 0);
+       if (IS_ERR(ptd->tzd)) {
+               dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
+                       dev_name);
+               err = PTR_ERR(ptd->tzd);
+               goto error_cleanup;
+       }
+
+       return 0;
+
+error_cleanup:
+       iounmap(ptd->hw_base);
+error_release:
+       pci_release_regions(pdev);
+error_disable:
+       pci_disable_device(pdev);
+       dev_err(&pdev->dev, "pci device failed to probe\n");
+       return err;
+}
+
+static void intel_pch_thermal_remove(struct pci_dev *pdev)
+{
+       struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
+
+       thermal_zone_device_unregister(ptd->tzd);
+       iounmap(ptd->hw_base);
+       pci_set_drvdata(pdev, NULL);
+       pci_release_region(pdev, 0);
+       pci_disable_device(pdev);
+}
+
+static struct pci_device_id intel_pch_thermal_id[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT) },
+       { 0, },
+};
+MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
+
+static struct pci_driver intel_pch_thermal_driver = {
+       .name           = "intel_pch_thermal",
+       .id_table       = intel_pch_thermal_id,
+       .probe          = intel_pch_thermal_probe,
+       .remove         = intel_pch_thermal_remove,
+};
+
+module_pci_driver(intel_pch_thermal_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel PCH Thermal driver");
index 2ac0c70..6c79588 100644 (file)
@@ -693,11 +693,14 @@ static const struct x86_cpu_id intel_powerclamp_ids[] __initconst = {
        { X86_VENDOR_INTEL, 6, 0x3f},
        { X86_VENDOR_INTEL, 6, 0x45},
        { X86_VENDOR_INTEL, 6, 0x46},
+       { X86_VENDOR_INTEL, 6, 0x47},
        { X86_VENDOR_INTEL, 6, 0x4c},
        { X86_VENDOR_INTEL, 6, 0x4d},
+       { X86_VENDOR_INTEL, 6, 0x4e},
        { X86_VENDOR_INTEL, 6, 0x4f},
        { X86_VENDOR_INTEL, 6, 0x56},
        { X86_VENDOR_INTEL, 6, 0x57},
+       { X86_VENDOR_INTEL, 6, 0x5e},
        {}
 };
 MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
index 4434ec8..5ed90e6 100644 (file)
@@ -186,7 +186,7 @@ static int soc_dts_disable(struct thermal_zone_device *tzd)
        return ret;
 }
 
-static int _get_trip_temp(int trip, unsigned long *temp)
+static int _get_trip_temp(int trip, int *temp)
 {
        int status;
        u32 out;
@@ -212,19 +212,18 @@ static int _get_trip_temp(int trip, unsigned long *temp)
 }
 
 static inline int sys_get_trip_temp(struct thermal_zone_device *tzd,
-                               int trip, unsigned long *temp)
+                               int trip, int *temp)
 {
        return _get_trip_temp(trip, temp);
 }
 
-static inline int sys_get_crit_temp(struct thermal_zone_device *tzd,
-                               unsigned long *temp)
+static inline int sys_get_crit_temp(struct thermal_zone_device *tzd, int *temp)
 {
        return _get_trip_temp(QRK_DTS_ID_TP_CRITICAL, temp);
 }
 
 static int update_trip_temp(struct soc_sensor_entry *aux_entry,
-                               int trip, unsigned long temp)
+                               int trip, int temp)
 {
        u32 out;
        u32 temp_out;
@@ -272,7 +271,7 @@ failed:
 }
 
 static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
-                               unsigned long temp)
+                               int temp)
 {
        return update_trip_temp(tzd->devdata, trip, temp);
 }
@@ -289,7 +288,7 @@ static int sys_get_trip_type(struct thermal_zone_device *thermal,
 }
 
 static int sys_get_curr_temp(struct thermal_zone_device *tzd,
-                               unsigned long *temp)
+                               int *temp)
 {
        u32 out;
        int ret;
index 42e4b6a..5841d1d 100644 (file)
@@ -80,7 +80,7 @@ err_ret:
 }
 
 static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip,
-                            unsigned long *temp)
+                            int *temp)
 {
        int status;
        u32 out;
@@ -106,7 +106,7 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip,
 }
 
 static int update_trip_temp(struct intel_soc_dts_sensor_entry *dts,
-                           int thres_index, unsigned long temp,
+                           int thres_index, int temp,
                            enum thermal_trip_type trip_type)
 {
        int status;
@@ -196,7 +196,7 @@ err_restore_ptps:
 }
 
 static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
-                            unsigned long temp)
+                            int temp)
 {
        struct intel_soc_dts_sensor_entry *dts = tzd->devdata;
        struct intel_soc_dts_sensors *sensors = dts->sensors;
@@ -226,7 +226,7 @@ static int sys_get_trip_type(struct thermal_zone_device *tzd,
 }
 
 static int sys_get_curr_temp(struct thermal_zone_device *tzd,
-                            unsigned long *temp)
+                            int *temp)
 {
        int status;
        u32 out;
index 11041fe..8922366 100644 (file)
@@ -33,7 +33,7 @@ struct kirkwood_thermal_priv {
 };
 
 static int kirkwood_get_temp(struct thermal_zone_device *thermal,
-                         unsigned long *temp)
+                         int *temp)
 {
        unsigned long reg;
        struct kirkwood_thermal_priv *priv = thermal->devdata;
index b295b2b..42b7d42 100644 (file)
@@ -91,7 +91,7 @@ struct __thermal_zone {
 /***   DT thermal zone device callbacks   ***/
 
 static int of_thermal_get_temp(struct thermal_zone_device *tz,
-                              unsigned long *temp)
+                              int *temp)
 {
        struct __thermal_zone *data = tz->devdata;
 
@@ -177,7 +177,7 @@ EXPORT_SYMBOL_GPL(of_thermal_get_trip_points);
  * Return: zero on success, error code otherwise
  */
 static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
-                                   unsigned long temp)
+                                   int temp)
 {
        struct __thermal_zone *data = tz->devdata;
 
@@ -311,7 +311,7 @@ static int of_thermal_get_trip_type(struct thermal_zone_device *tz, int trip,
 }
 
 static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
-                                   unsigned long *temp)
+                                   int *temp)
 {
        struct __thermal_zone *data = tz->devdata;
 
@@ -324,7 +324,7 @@ static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
 }
 
 static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
-                                   unsigned long temp)
+                                   int temp)
 {
        struct __thermal_zone *data = tz->devdata;
 
@@ -338,7 +338,7 @@ static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
 }
 
 static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
-                                   unsigned long *hyst)
+                                   int *hyst)
 {
        struct __thermal_zone *data = tz->devdata;
 
@@ -351,7 +351,7 @@ static int of_thermal_get_trip_hyst(struct thermal_zone_device *tz, int trip,
 }
 
 static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
-                                   unsigned long hyst)
+                                   int hyst)
 {
        struct __thermal_zone *data = tz->devdata;
 
@@ -365,7 +365,7 @@ static int of_thermal_set_trip_hyst(struct thermal_zone_device *tz, int trip,
 }
 
 static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
-                                   unsigned long *temp)
+                                   int *temp)
 {
        struct __thermal_zone *data = tz->devdata;
        int i;
index 2516769..9c8a7aa 100644 (file)
@@ -92,8 +92,8 @@ struct power_allocator_params {
  * Return: The power budget for the next period.
  */
 static u32 pid_controller(struct thermal_zone_device *tz,
-                         unsigned long current_temp,
-                         unsigned long control_temp,
+                         int current_temp,
+                         int control_temp,
                          u32 max_allocatable_power)
 {
        s64 p, i, d, power_range;
@@ -102,7 +102,7 @@ static u32 pid_controller(struct thermal_zone_device *tz,
 
        max_power_frac = int_to_frac(max_allocatable_power);
 
-       err = ((s32)control_temp - (s32)current_temp);
+       err = control_temp - current_temp;
        err = int_to_frac(err);
 
        /* Calculate the proportional term */
@@ -223,8 +223,8 @@ static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
 }
 
 static int allocate_power(struct thermal_zone_device *tz,
-                         unsigned long current_temp,
-                         unsigned long control_temp)
+                         int current_temp,
+                         int control_temp)
 {
        struct thermal_instance *instance;
        struct power_allocator_params *params = tz->governor_data;
@@ -331,7 +331,7 @@ static int allocate_power(struct thermal_zone_device *tz,
                                      granted_power, total_granted_power,
                                      num_actors, power_range,
                                      max_allocatable_power, current_temp,
-                                     (s32)control_temp - (s32)current_temp);
+                                     control_temp - current_temp);
 
        kfree(req_power);
 unlock:
@@ -416,7 +416,7 @@ static int power_allocator_bind(struct thermal_zone_device *tz)
 {
        int ret;
        struct power_allocator_params *params;
-       unsigned long switch_on_temp, control_temp;
+       int switch_on_temp, control_temp;
        u32 temperature_threshold;
 
        if (!tz->tzp || !tz->tzp->sustainable_power) {
@@ -481,7 +481,7 @@ static void power_allocator_unbind(struct thermal_zone_device *tz)
 static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
 {
        int ret;
-       unsigned long switch_on_temp, control_temp, current_temp;
+       int switch_on_temp, control_temp, current_temp;
        struct power_allocator_params *params = tz->governor_data;
 
        /*
index c8d27b8..b677aad 100644 (file)
@@ -117,7 +117,7 @@ static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
        return 0;
 }
 
-static int qpnp_tm_get_temp(void *data, long *temp)
+static int qpnp_tm_get_temp(void *data, int *temp)
 {
        struct qpnp_tm_chip *chip = data;
        int ret, mili_celsius;
index fe4e767..5d4ae7d 100644 (file)
@@ -200,8 +200,7 @@ err_out_unlock:
        return ret;
 }
 
-static int rcar_thermal_get_temp(struct thermal_zone_device *zone,
-                                unsigned long *temp)
+static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp)
 {
        struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
 
@@ -235,7 +234,7 @@ static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone,
 }
 
 static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone,
-                                     int trip, unsigned long *temp)
+                                     int trip, int *temp)
 {
        struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone);
        struct device *dev = rcar_priv_to_dev(priv);
@@ -299,7 +298,7 @@ static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable)
 static void rcar_thermal_work(struct work_struct *work)
 {
        struct rcar_thermal_priv *priv;
-       unsigned long cctemp, nctemp;
+       int cctemp, nctemp;
 
        priv = container_of(work, struct rcar_thermal_priv, work.work);
 
index cd8f5f9..c89ffb2 100644 (file)
@@ -64,7 +64,7 @@ struct rockchip_tsadc_chip {
        void (*control)(void __iomem *reg, bool on);
 
        /* Per-sensor methods */
-       int (*get_temp)(int chn, void __iomem *reg, long *temp);
+       int (*get_temp)(int chn, void __iomem *reg, int *temp);
        void (*set_tshut_temp)(int chn, void __iomem *reg, long temp);
        void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);
 };
@@ -191,7 +191,7 @@ static u32 rk_tsadcv2_temp_to_code(long temp)
        return 0;
 }
 
-static long rk_tsadcv2_code_to_temp(u32 code)
+static int rk_tsadcv2_code_to_temp(u32 code)
 {
        unsigned int low = 0;
        unsigned int high = ARRAY_SIZE(v2_code_table) - 1;
@@ -277,7 +277,7 @@ static void rk_tsadcv2_control(void __iomem *regs, bool enable)
        writel_relaxed(val, regs + TSADCV2_AUTO_CON);
 }
 
-static int rk_tsadcv2_get_temp(int chn, void __iomem *regs, long *temp)
+static int rk_tsadcv2_get_temp(int chn, void __iomem *regs, int *temp)
 {
        u32 val;
 
@@ -366,7 +366,7 @@ static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev)
        return IRQ_HANDLED;
 }
 
-static int rockchip_thermal_get_temp(void *_sensor, long *out_temp)
+static int rockchip_thermal_get_temp(void *_sensor, int *out_temp)
 {
        struct rockchip_thermal_sensor *sensor = _sensor;
        struct rockchip_thermal_data *thermal = sensor->thermal;
@@ -374,7 +374,7 @@ static int rockchip_thermal_get_temp(void *_sensor, long *out_temp)
        int retval;
 
        retval = tsadc->get_temp(sensor->id, thermal->regs, out_temp);
-       dev_dbg(&thermal->pdev->dev, "sensor %d - temp: %ld, retval: %d\n",
+       dev_dbg(&thermal->pdev->dev, "sensor %d - temp: %d, retval: %d\n",
                sensor->id, *out_temp, retval);
 
        return retval;
index c96ff10..0bae8cc 100644 (file)
@@ -207,8 +207,7 @@ struct exynos_tmu_data {
        int (*tmu_initialize)(struct platform_device *pdev);
        void (*tmu_control)(struct platform_device *pdev, bool on);
        int (*tmu_read)(struct exynos_tmu_data *data);
-       void (*tmu_set_emulation)(struct exynos_tmu_data *data,
-                                 unsigned long temp);
+       void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp);
        void (*tmu_clear_irqs)(struct exynos_tmu_data *data);
 };
 
@@ -216,7 +215,7 @@ static void exynos_report_trigger(struct exynos_tmu_data *p)
 {
        char data[10], *envp[] = { data, NULL };
        struct thermal_zone_device *tz = p->tzd;
-       unsigned long temp;
+       int temp;
        unsigned int i;
 
        if (!tz) {
@@ -517,7 +516,7 @@ static int exynos5433_tmu_initialize(struct platform_device *pdev)
        struct thermal_zone_device *tz = data->tzd;
        unsigned int status, trim_info;
        unsigned int rising_threshold = 0, falling_threshold = 0;
-       unsigned long temp, temp_hist;
+       int temp, temp_hist;
        int ret = 0, threshold_code, i, sensor_id, cal_type;
 
        status = readb(data->base + EXYNOS_TMU_REG_STATUS);
@@ -610,7 +609,7 @@ static int exynos5440_tmu_initialize(struct platform_device *pdev)
        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
        unsigned int trim_info = 0, con, rising_threshold;
        int ret = 0, threshold_code;
-       unsigned long crit_temp = 0;
+       int crit_temp = 0;
 
        /*
         * For exynos5440 soc triminfo value is swapped between TMU0 and
@@ -663,7 +662,7 @@ static int exynos7_tmu_initialize(struct platform_device *pdev)
        unsigned int status, trim_info;
        unsigned int rising_threshold = 0, falling_threshold = 0;
        int ret = 0, threshold_code, i;
-       unsigned long temp, temp_hist;
+       int temp, temp_hist;
        unsigned int reg_off, bit_off;
 
        status = readb(data->base + EXYNOS_TMU_REG_STATUS);
@@ -876,7 +875,7 @@ static void exynos7_tmu_control(struct platform_device *pdev, bool on)
        writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
 }
 
-static int exynos_get_temp(void *p, long *temp)
+static int exynos_get_temp(void *p, int *temp)
 {
        struct exynos_tmu_data *data = p;
 
@@ -896,7 +895,7 @@ static int exynos_get_temp(void *p, long *temp)
 
 #ifdef CONFIG_THERMAL_EMULATION
 static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val,
-                           unsigned long temp)
+                           int temp)
 {
        if (temp) {
                temp /= MCELSIUS;
@@ -926,7 +925,7 @@ static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val,
 }
 
 static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data,
-                                        unsigned long temp)
+                                        int temp)
 {
        unsigned int val;
        u32 emul_con;
@@ -946,7 +945,7 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data,
 }
 
 static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data,
-                                        unsigned long temp)
+                                        int temp)
 {
        unsigned int val;
 
@@ -955,7 +954,7 @@ static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data,
        writel(val, data->base + EXYNOS5440_TMU_S0_7_DEBUG);
 }
 
-static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
+static int exynos_tmu_set_emulation(void *drv_data, int temp)
 {
        struct exynos_tmu_data *data = drv_data;
        int ret = -EINVAL;
@@ -978,7 +977,7 @@ out:
 #else
 #define exynos4412_tmu_set_emulation NULL
 #define exynos5440_tmu_set_emulation NULL
-static int exynos_tmu_set_emulation(void *drv_data,    unsigned long temp)
+static int exynos_tmu_set_emulation(void *drv_data, int temp)
        { return -EINVAL; }
 #endif /* CONFIG_THERMAL_EMULATION */
 
index bddb717..534dd91 100644 (file)
@@ -38,7 +38,7 @@ struct spear_thermal_dev {
 };
 
 static inline int thermal_get_temp(struct thermal_zone_device *thermal,
-                               unsigned long *temp)
+                               int *temp)
 {
        struct spear_thermal_dev *stdev = thermal->devdata;
 
index 88c759d..be637e6 100644 (file)
@@ -111,8 +111,7 @@ static int st_thermal_calibration(struct st_thermal_sensor *sensor)
 }
 
 /* Callback to get temperature from HW*/
-static int st_thermal_get_temp(struct thermal_zone_device *th,
-               unsigned long *temperature)
+static int st_thermal_get_temp(struct thermal_zone_device *th, int *temperature)
 {
        struct st_thermal_sensor *sensor = th->devdata;
        struct device *dev = sensor->dev;
@@ -159,7 +158,7 @@ static int st_thermal_get_trip_type(struct thermal_zone_device *th,
 }
 
 static int st_thermal_get_trip_temp(struct thermal_zone_device *th,
-                                   int trip, unsigned long *temp)
+                                   int trip, int *temp)
 {
        struct st_thermal_sensor *sensor = th->devdata;
        struct device *dev = sensor->dev;
index 5a0f12d..2f9f708 100644 (file)
@@ -113,7 +113,7 @@ static void update_passive_instance(struct thermal_zone_device *tz,
 
 static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
 {
-       long trip_temp;
+       int trip_temp;
        enum thermal_trip_type trip_type;
        enum thermal_trend trend;
        struct thermal_instance *instance;
@@ -135,7 +135,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
                trace_thermal_zone_trip(tz, trip, trip_type);
        }
 
-       dev_dbg(&tz->device, "Trip%d[type=%d,temp=%ld]:trend=%d,throttle=%d\n",
+       dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n",
                                trip, trip_type, trip_temp, trend, throttle);
 
        mutex_lock(&tz->lock);
index 9197fc0..74ea576 100644 (file)
@@ -293,7 +293,7 @@ static int enable_tsensor(struct tegra_soctherm *tegra,
  * H denotes an addition of 0.5 Celsius and N denotes negation
  * of the final value.
  */
-static long translate_temp(u16 val)
+static int translate_temp(u16 val)
 {
        long t;
 
@@ -306,7 +306,7 @@ static long translate_temp(u16 val)
        return t;
 }
 
-static int tegra_thermctl_get_temp(void *data, long *out_temp)
+static int tegra_thermctl_get_temp(void *data, int *out_temp)
 {
        struct tegra_thermctl_zone *zone = data;
        u32 val;
index 4ca211b..5e5fc70 100644 (file)
@@ -426,7 +426,7 @@ static void handle_non_critical_trips(struct thermal_zone_device *tz,
 static void handle_critical_trips(struct thermal_zone_device *tz,
                                int trip, enum thermal_trip_type trip_type)
 {
-       long trip_temp;
+       int trip_temp;
 
        tz->ops->get_trip_temp(tz, trip, &trip_temp);
 
@@ -465,7 +465,7 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
 }
 
 /**
- * thermal_zone_get_temp() - returns its the temperature of thermal zone
+ * thermal_zone_get_temp() - returns the temperature of a thermal zone
  * @tz: a valid pointer to a struct thermal_zone_device
  * @temp: a valid pointer to where to store the resulting temperature.
  *
@@ -474,14 +474,12 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
  *
  * Return: On success returns 0, an error code otherwise
  */
-int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
+int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
 {
        int ret = -EINVAL;
-#ifdef CONFIG_THERMAL_EMULATION
        int count;
-       unsigned long crit_temp = -1UL;
+       int crit_temp = INT_MAX;
        enum thermal_trip_type type;
-#endif
 
        if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
                goto exit;
@@ -489,25 +487,26 @@ int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
        mutex_lock(&tz->lock);
 
        ret = tz->ops->get_temp(tz, temp);
-#ifdef CONFIG_THERMAL_EMULATION
-       if (!tz->emul_temperature)
-               goto skip_emul;
-
-       for (count = 0; count < tz->trips; count++) {
-               ret = tz->ops->get_trip_type(tz, count, &type);
-               if (!ret && type == THERMAL_TRIP_CRITICAL) {
-                       ret = tz->ops->get_trip_temp(tz, count, &crit_temp);
-                       break;
-               }
-       }
 
-       if (ret)
-               goto skip_emul;
+       if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
+               for (count = 0; count < tz->trips; count++) {
+                       ret = tz->ops->get_trip_type(tz, count, &type);
+                       if (!ret && type == THERMAL_TRIP_CRITICAL) {
+                               ret = tz->ops->get_trip_temp(tz, count,
+                                               &crit_temp);
+                               break;
+                       }
+               }
 
-       if (*temp < crit_temp)
-               *temp = tz->emul_temperature;
-skip_emul:
-#endif
+               /*
+                * Only allow emulating a temperature when the real temperature
+                * is below the critical temperature so that the emulation code
+                * cannot hide critical conditions.
+                */
+               if (!ret && *temp < crit_temp)
+                       *temp = tz->emul_temperature;
+       }
        mutex_unlock(&tz->lock);
 exit:
        return ret;
@@ -516,8 +515,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
 
 static void update_temperature(struct thermal_zone_device *tz)
 {
-       long temp;
-       int ret;
+       int temp, ret;
 
        ret = thermal_zone_get_temp(tz, &temp);
        if (ret) {
@@ -577,15 +575,14 @@ static ssize_t
 temp_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct thermal_zone_device *tz = to_thermal_zone(dev);
-       long temperature;
-       int ret;
+       int temperature, ret;
 
        ret = thermal_zone_get_temp(tz, &temperature);
 
        if (ret)
                return ret;
 
-       return sprintf(buf, "%ld\n", temperature);
+       return sprintf(buf, "%d\n", temperature);
 }
 
 static ssize_t
@@ -689,7 +686,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
 {
        struct thermal_zone_device *tz = to_thermal_zone(dev);
        int trip, ret;
-       long temperature;
+       int temperature;
 
        if (!tz->ops->get_trip_temp)
                return -EPERM;
@@ -702,7 +699,7 @@ trip_point_temp_show(struct device *dev, struct device_attribute *attr,
        if (ret)
                return ret;
 
-       return sprintf(buf, "%ld\n", temperature);
+       return sprintf(buf, "%d\n", temperature);
 }
 
 static ssize_t
@@ -711,7 +708,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
 {
        struct thermal_zone_device *tz = to_thermal_zone(dev);
        int trip, ret;
-       unsigned long temperature;
+       int temperature;
 
        if (!tz->ops->set_trip_hyst)
                return -EPERM;
@@ -719,7 +716,7 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
        if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip))
                return -EINVAL;
 
-       if (kstrtoul(buf, 10, &temperature))
+       if (kstrtoint(buf, 10, &temperature))
                return -EINVAL;
 
        /*
@@ -738,7 +735,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
 {
        struct thermal_zone_device *tz = to_thermal_zone(dev);
        int trip, ret;
-       unsigned long temperature;
+       int temperature;
 
        if (!tz->ops->get_trip_hyst)
                return -EPERM;
@@ -748,7 +745,7 @@ trip_point_hyst_show(struct device *dev, struct device_attribute *attr,
 
        ret = tz->ops->get_trip_hyst(tz, trip, &temperature);
 
-       return ret ? ret : sprintf(buf, "%ld\n", temperature);
+       return ret ? ret : sprintf(buf, "%d\n", temperature);
 }
 
 static ssize_t
@@ -847,7 +844,27 @@ policy_show(struct device *dev, struct device_attribute *devattr, char *buf)
        return sprintf(buf, "%s\n", tz->governor->name);
 }
 
-#ifdef CONFIG_THERMAL_EMULATION
+static ssize_t
+available_policies_show(struct device *dev, struct device_attribute *devattr,
+                       char *buf)
+{
+       struct thermal_governor *pos;
+       ssize_t count = 0;
+       ssize_t size = PAGE_SIZE;
+
+       mutex_lock(&thermal_governor_lock);
+
+       list_for_each_entry(pos, &thermal_governor_list, governor_list) {
+               size = PAGE_SIZE - count;
+               count += scnprintf(buf + count, size, "%s ", pos->name);
+       }
+       count += scnprintf(buf + count, size, "\n");
+
+       mutex_unlock(&thermal_governor_lock);
+
+       return count;
+}
+
 static ssize_t
 emul_temp_store(struct device *dev, struct device_attribute *attr,
                     const char *buf, size_t count)
@@ -873,7 +890,6 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
        return ret ? ret : count;
 }
 static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store);
-#endif/*CONFIG_THERMAL_EMULATION*/
 
 static ssize_t
 sustainable_power_show(struct device *dev, struct device_attribute *devattr,
@@ -1032,6 +1048,7 @@ static DEVICE_ATTR(temp, 0444, temp_show, NULL);
 static DEVICE_ATTR(mode, 0644, mode_show, mode_store);
 static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store);
 static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store);
+static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL);
 
 /* sys I/F for cooling device */
 #define to_cooling_device(_dev)        \
@@ -1803,11 +1820,12 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
                        goto unregister;
        }
 
-#ifdef CONFIG_THERMAL_EMULATION
-       result = device_create_file(&tz->device, &dev_attr_emul_temp);
-       if (result)
-               goto unregister;
-#endif
+       if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) {
+               result = device_create_file(&tz->device, &dev_attr_emul_temp);
+               if (result)
+                       goto unregister;
+       }
+
        /* Create policy attribute */
        result = device_create_file(&tz->device, &dev_attr_policy);
        if (result)
@@ -1818,6 +1836,11 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
        if (result)
                goto unregister;
 
+       /* Create available_policies attribute */
+       result = device_create_file(&tz->device, &dev_attr_available_policies);
+       if (result)
+               goto unregister;
+
        /* Update 'this' zone's governor information */
        mutex_lock(&thermal_governor_lock);
 
@@ -1849,9 +1872,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
 
        INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check);
 
-       if (!tz->ops->get_temp)
-               thermal_zone_device_set_polling(tz, 0);
-
        thermal_zone_device_update(tz);
 
        return tz;
@@ -1918,6 +1938,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz)
        if (tz->ops->get_mode)
                device_remove_file(&tz->device, &dev_attr_mode);
        device_remove_file(&tz->device, &dev_attr_policy);
+       device_remove_file(&tz->device, &dev_attr_available_policies);
        remove_trip_attrs(tz);
        thermal_set_governor(tz, NULL);
 
index 1967bee..06fd2ed 100644 (file)
@@ -69,7 +69,7 @@ static DEVICE_ATTR(name, 0444, name_show, NULL);
 static ssize_t
 temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
-       long temperature;
+       int temperature;
        int ret;
        struct thermal_hwmon_attr *hwmon_attr
                        = container_of(attr, struct thermal_hwmon_attr, attr);
@@ -83,7 +83,7 @@ temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
        if (ret)
                return ret;
 
-       return sprintf(buf, "%ld\n", temperature);
+       return sprintf(buf, "%d\n", temperature);
 }
 
 static ssize_t
@@ -95,14 +95,14 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
                        = container_of(hwmon_attr, struct thermal_hwmon_temp,
                                       temp_crit);
        struct thermal_zone_device *tz = temp->tz;
-       long temperature;
+       int temperature;
        int ret;
 
        ret = tz->ops->get_trip_temp(tz, 0, &temperature);
        if (ret)
                return ret;
 
-       return sprintf(buf, "%ld\n", temperature);
+       return sprintf(buf, "%d\n", temperature);
 }
 
 
@@ -142,7 +142,7 @@ thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon,
 
 static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz)
 {
-       unsigned long temp;
+       int temp;
        return tz->ops->get_crit_temp && !tz->ops->get_crit_temp(tz, &temp);
 }
 
index c7c5b37..b213a12 100644 (file)
@@ -76,14 +76,14 @@ static inline int ti_thermal_hotspot_temperature(int t, int s, int c)
 
 /* thermal zone ops */
 /* Get temperature callback function for thermal zone */
-static inline int __ti_thermal_get_temp(void *devdata, long *temp)
+static inline int __ti_thermal_get_temp(void *devdata, int *temp)
 {
        struct thermal_zone_device *pcb_tz = NULL;
        struct ti_thermal_data *data = devdata;
        struct ti_bandgap *bgp;
        const struct ti_temp_sensor *s;
        int ret, tmp, slope, constant;
-       unsigned long pcb_temp;
+       int pcb_temp;
 
        if (!data)
                return 0;
@@ -119,7 +119,7 @@ static inline int __ti_thermal_get_temp(void *devdata, long *temp)
 }
 
 static inline int ti_thermal_get_temp(struct thermal_zone_device *thermal,
-                                     unsigned long *temp)
+                                     int *temp)
 {
        struct ti_thermal_data *data = thermal->devdata;
 
@@ -229,7 +229,7 @@ static int ti_thermal_get_trip_type(struct thermal_zone_device *thermal,
 
 /* Get trip temperature callback functions for thermal zone */
 static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal,
-                                   int trip, unsigned long *temp)
+                                   int trip, int *temp)
 {
        if (!ti_thermal_is_valid_trip(trip))
                return -EINVAL;
@@ -280,7 +280,7 @@ static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
 
 /* Get critical temperature callback functions for thermal zone */
 static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal,
-                                   unsigned long *temp)
+                                   int *temp)
 {
        /* shutdown zone */
        return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp);
index 50d1d2c..7fc919f 100644 (file)
@@ -164,7 +164,7 @@ err_ret:
        return err;
 }
 
-static int sys_get_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp)
+static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
 {
        u32 eax, edx;
        struct phy_dev_entry *phy_dev_entry;
@@ -175,7 +175,7 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd, unsigned long *tem
        if (eax & 0x80000000) {
                *temp = phy_dev_entry->tj_max -
                                ((eax >> 16) & 0x7f) * 1000;
-               pr_debug("sys_get_curr_temp %ld\n", *temp);
+               pr_debug("sys_get_curr_temp %d\n", *temp);
                return 0;
        }
 
@@ -183,7 +183,7 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd, unsigned long *tem
 }
 
 static int sys_get_trip_temp(struct thermal_zone_device *tzd,
-               int trip, unsigned long *temp)
+               int trip, int *temp)
 {
        u32 eax, edx;
        struct phy_dev_entry *phy_dev_entry;
@@ -214,13 +214,13 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd,
                *temp = phy_dev_entry->tj_max - thres_reg_value * 1000;
        else
                *temp = 0;
-       pr_debug("sys_get_trip_temp %ld\n", *temp);
+       pr_debug("sys_get_trip_temp %d\n", *temp);
 
        return 0;
 }
 
 static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
-                                                       unsigned long temp)
+                                                       int temp)
 {
        u32 l, h;
        struct phy_dev_entry *phy_dev_entry;
index 55c4b5b..c68edc1 100644 (file)
@@ -188,6 +188,15 @@ config AT91SAM9X_WATCHDOG
          Watchdog timer embedded into AT91SAM9X and AT91CAP9 chips. This will
          reboot your system when the timeout is reached.
 
+config SAMA5D4_WATCHDOG
+       tristate "Atmel SAMA5D4 Watchdog Timer"
+       depends on ARCH_AT91
+       select WATCHDOG_CORE
+       help
+         Atmel SAMA5D4 watchdog timer is embedded into SAMA5D4 chips.
+         Its Watchdog Timer Mode Register can be written more than once.
+         This will reboot your system when the timeout is reached.
+
 config CADENCE_WATCHDOG
        tristate "Cadence Watchdog Timer"
        depends on HAS_IOMEM
@@ -558,6 +567,17 @@ config DIGICOLOR_WATCHDOG
          To compile this driver as a module, choose M here: the
          module will be called digicolor_wdt.
 
+config LPC18XX_WATCHDOG
+       tristate "LPC18xx/43xx Watchdog"
+       depends on ARCH_LPC18XX || COMPILE_TEST
+       select WATCHDOG_CORE
+       help
+         Say Y here if to include support for the watchdog timer
+         in NXP LPC SoCs family, which includes LPC18xx/LPC43xx
+         processors.
+         To compile this driver as a module, choose M here: the
+         module will be called lpc18xx_wdt.
+
 # AVR32 Architecture
 
 config AT32AP700X_WDT
@@ -1334,7 +1354,7 @@ config MPC5200_WDT
 
 config 8xxx_WDT
        tristate "MPC8xxx Platform Watchdog Timer"
-       depends on PPC_8xx || PPC_83xx || PPC_86xx
+       depends on PPC_8xx || PPC_83xx || PPC_86xx || PPC_MPC512x
        select WATCHDOG_CORE
        help
          This driver is for a SoC level watchdog that exists on some
index 59ea9a1..0c616e3 100644 (file)
@@ -41,6 +41,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
 obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
 obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
 obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
+obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o
 obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o
 obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
 obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
@@ -66,6 +67,7 @@ obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
 obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o
 obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o
 obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o
+obj-$(CONFIG_LPC18XX_WATCHDOG) += lpc18xx_wdt.o
 
 # AVR32 Architecture
 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
index 9ba1153..e12a797 100644 (file)
@@ -244,7 +244,7 @@ static int at91wdt_probe(struct platform_device *pdev)
        }
 
        regmap_st = syscon_node_to_regmap(parent->of_node);
-       if (!regmap_st)
+       if (IS_ERR(regmap_st))
                return -ENODEV;
 
        res = misc_register(&at91wdt_miscdev);
index e4698f7..7e6acaf 100644 (file)
@@ -17,6 +17,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/clk.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -90,6 +91,7 @@ struct at91wdt {
        unsigned long heartbeat;        /* WDT heartbeat in jiffies */
        bool nowayout;
        unsigned int irq;
+       struct clk *sclk;
 };
 
 /* ......................................................................... */
@@ -352,15 +354,25 @@ static int __init at91wdt_probe(struct platform_device *pdev)
        if (IS_ERR(wdt->base))
                return PTR_ERR(wdt->base);
 
+       wdt->sclk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(wdt->sclk))
+               return PTR_ERR(wdt->sclk);
+
+       err = clk_prepare_enable(wdt->sclk);
+       if (err) {
+               dev_err(&pdev->dev, "Could not enable slow clock\n");
+               return err;
+       }
+
        if (pdev->dev.of_node) {
                err = of_at91wdt_init(pdev->dev.of_node, wdt);
                if (err)
-                       return err;
+                       goto err_clk;
        }
 
        err = at91_wdt_init(pdev, wdt);
        if (err)
-               return err;
+               goto err_clk;
 
        platform_set_drvdata(pdev, wdt);
 
@@ -368,6 +380,11 @@ static int __init at91wdt_probe(struct platform_device *pdev)
                wdt->wdd.timeout, wdt->nowayout);
 
        return 0;
+
+err_clk:
+       clk_disable_unprepare(wdt->sclk);
+
+       return err;
 }
 
 static int __exit at91wdt_remove(struct platform_device *pdev)
@@ -377,6 +394,7 @@ static int __exit at91wdt_remove(struct platform_device *pdev)
 
        pr_warn("I quit now, hardware will probably reboot!\n");
        del_timer(&wdt->timer);
+       clk_disable_unprepare(wdt->sclk);
 
        return 0;
 }
index c6fbb2e..b79a83b 100644 (file)
 
 #define AT91_WDT_MR            0x04                    /* Watchdog Mode Register */
 #define                AT91_WDT_WDV            (0xfff << 0)            /* Counter Value */
+#define                        AT91_WDT_SET_WDV(x)     ((x) & AT91_WDT_WDV)
 #define                AT91_WDT_WDFIEN         (1     << 12)           /* Fault Interrupt Enable */
 #define                AT91_WDT_WDRSTEN        (1     << 13)           /* Reset Processor */
 #define                AT91_WDT_WDRPROC        (1     << 14)           /* Timer Restart */
 #define                AT91_WDT_WDDIS          (1     << 15)           /* Watchdog Disable */
 #define                AT91_WDT_WDD            (0xfff << 16)           /* Delta Value */
+#define                        AT91_WDT_SET_WDD(x)     (((x) << 16) & AT91_WDT_WDD)
 #define                AT91_WDT_WDDBGHLT       (1     << 28)           /* Debug Halt */
 #define                AT91_WDT_WDIDLEHLT      (1     << 29)           /* Idle Halt */
 
index 7116968..66c3e65 100644 (file)
@@ -182,6 +182,7 @@ static int bcm2835_wdt_probe(struct platform_device *pdev)
        watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
        watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
        watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
+       bcm2835_wdt_wdd.parent = &pdev->dev;
        err = watchdog_register_device(&bcm2835_wdt_wdd);
        if (err) {
                dev_err(dev, "Failed to register watchdog device");
index b28a072..4064a43 100644 (file)
@@ -209,6 +209,7 @@ static int bcm47xx_wdt_probe(struct platform_device *pdev)
 
        wdt->wdd.info = &bcm47xx_wdt_info;
        wdt->wdd.timeout = WDT_DEFAULT_TIME;
+       wdt->wdd.parent = &pdev->dev;
        ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
        if (ret)
                goto err_timer;
index 22d8ae6..e0c9842 100644 (file)
@@ -319,6 +319,7 @@ static int bcm_kona_wdt_probe(struct platform_device *pdev)
        spin_lock_init(&wdt->lock);
        platform_set_drvdata(pdev, wdt);
        watchdog_set_drvdata(&bcm_kona_wdt_wdd, wdt);
+       bcm_kona_wdt_wdd.parent = &pdev->dev;
 
        ret = bcm_kona_wdt_set_timeout_reg(&bcm_kona_wdt_wdd, 0);
        if (ret) {
index e96b09b..04da4b6 100644 (file)
@@ -186,8 +186,6 @@ static int booke_wdt_stop(struct watchdog_device *wdog)
 static int booke_wdt_set_timeout(struct watchdog_device *wdt_dev,
                                 unsigned int timeout)
 {
-       if (timeout > MAX_WDT_TIMEOUT)
-               return -EINVAL;
        wdt_dev->timeout = timeout;
        booke_wdt_set(wdt_dev);
 
@@ -211,7 +209,6 @@ static struct watchdog_device booke_wdt_dev = {
        .info = &booke_wdt_info,
        .ops = &booke_wdt_ops,
        .min_timeout = 1,
-       .max_timeout = 0xFFFF
 };
 
 static void __exit booke_wdt_exit(void)
@@ -229,6 +226,7 @@ static int __init booke_wdt_init(void)
        booke_wdt_set_timeout(&booke_wdt_dev,
                              period_to_sec(booke_wdt_period));
        watchdog_set_nowayout(&booke_wdt_dev, nowayout);
+       booke_wdt_dev.max_timeout = MAX_WDT_TIMEOUT;
        if (booke_wdt_enabled)
                booke_wdt_start(&booke_wdt_dev);
 
index ce12f43..a099b77 100644 (file)
@@ -358,6 +358,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
        if (ret < 0)
                coh901327_wdt.timeout = 60;
 
+       coh901327_wdt.parent = &pdev->dev;
        ret = watchdog_register_device(&coh901327_wdt);
        if (ret == 0)
                dev_info(&pdev->dev,
index 2e95896..67e6797 100644 (file)
@@ -195,6 +195,7 @@ static int da9052_wdt_probe(struct platform_device *pdev)
        da9052_wdt->timeout = DA9052_DEF_TIMEOUT;
        da9052_wdt->info = &da9052_wdt_info;
        da9052_wdt->ops = &da9052_wdt_ops;
+       da9052_wdt->parent = &pdev->dev;
        watchdog_set_drvdata(da9052_wdt, driver_data);
 
        kref_init(&driver_data->kref);
index 495089d..04d1430 100644 (file)
@@ -161,6 +161,7 @@ static int da9055_wdt_probe(struct platform_device *pdev)
        da9055_wdt->timeout = DA9055_DEF_TIMEOUT;
        da9055_wdt->info = &da9055_wdt_info;
        da9055_wdt->ops = &da9055_wdt_ops;
+       da9055_wdt->parent = &pdev->dev;
        watchdog_set_nowayout(da9055_wdt, nowayout);
        watchdog_set_drvdata(da9055_wdt, driver_data);
 
index b3a870c..7386111 100644 (file)
@@ -210,6 +210,7 @@ static int da9062_wdt_probe(struct platform_device *pdev)
        wdt->wdtdev.max_timeout = DA9062_WDT_MAX_TIMEOUT;
        wdt->wdtdev.timeout = DA9062_WDG_DEFAULT_TIMEOUT;
        wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
+       wdt->wdtdev.parent = &pdev->dev;
 
        watchdog_set_drvdata(&wdt->wdtdev, wdt);
        dev_set_drvdata(&pdev->dev, wdt);
index e2fe2eb..6bf130b 100644 (file)
@@ -175,6 +175,7 @@ static int da9063_wdt_probe(struct platform_device *pdev)
        wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
        wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT;
        wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT;
+       wdt->wdtdev.parent = &pdev->dev;
 
        wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
 
index cfdf8a4..17454ca 100644 (file)
@@ -179,6 +179,7 @@ static int davinci_wdt_probe(struct platform_device *pdev)
        wdd->min_timeout        = 1;
        wdd->max_timeout        = MAX_HEARTBEAT;
        wdd->timeout            = DEFAULT_HEARTBEAT;
+       wdd->parent             = &pdev->dev;
 
        watchdog_init_timeout(wdd, heartbeat, dev);
 
index 31d8e49..50abe1b 100644 (file)
@@ -143,6 +143,7 @@ static int dc_wdt_probe(struct platform_device *pdev)
        }
        dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
        dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
+       dc_wdt_wdd.parent = &pdev->dev;
 
        spin_lock_init(&wdt->lock);
 
index 7a2cc71..0a4d7cc 100644 (file)
@@ -132,6 +132,7 @@ static int ep93xx_wdt_probe(struct platform_device *pdev)
        val = readl(mmio_base + EP93XX_WATCHDOG);
        ep93xx_wdt_wdd.bootstatus = (val & 0x01) ? WDIOF_CARDRESET : 0;
        ep93xx_wdt_wdd.timeout = timeout;
+       ep93xx_wdt_wdd.parent = &pdev->dev;
 
        watchdog_set_nowayout(&ep93xx_wdt_wdd, nowayout);
 
index 1687cc2..90d59d3 100644 (file)
@@ -50,12 +50,41 @@ static void gpio_wdt_disable(struct gpio_wdt_priv *priv)
                gpio_direction_input(priv->gpio);
 }
 
+static void gpio_wdt_hwping(unsigned long data)
+{
+       struct watchdog_device *wdd = (struct watchdog_device *)data;
+       struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
+
+       if (priv->armed && time_after(jiffies, priv->last_jiffies +
+                                     msecs_to_jiffies(wdd->timeout * 1000))) {
+               dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n");
+               return;
+       }
+
+       /* Restart timer */
+       mod_timer(&priv->timer, jiffies + priv->hw_margin);
+
+       switch (priv->hw_algo) {
+       case HW_ALGO_TOGGLE:
+               /* Toggle output pin */
+               priv->state = !priv->state;
+               gpio_set_value_cansleep(priv->gpio, priv->state);
+               break;
+       case HW_ALGO_LEVEL:
+               /* Pulse */
+               gpio_set_value_cansleep(priv->gpio, !priv->active_low);
+               udelay(1);
+               gpio_set_value_cansleep(priv->gpio, priv->active_low);
+               break;
+       }
+}
+
 static void gpio_wdt_start_impl(struct gpio_wdt_priv *priv)
 {
        priv->state = priv->active_low;
        gpio_direction_output(priv->gpio, priv->state);
        priv->last_jiffies = jiffies;
-       mod_timer(&priv->timer, priv->last_jiffies + priv->hw_margin);
+       gpio_wdt_hwping((unsigned long)&priv->wdd);
 }
 
 static int gpio_wdt_start(struct watchdog_device *wdd)
@@ -97,35 +126,6 @@ static int gpio_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
        return gpio_wdt_ping(wdd);
 }
 
-static void gpio_wdt_hwping(unsigned long data)
-{
-       struct watchdog_device *wdd = (struct watchdog_device *)data;
-       struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
-
-       if (priv->armed && time_after(jiffies, priv->last_jiffies +
-                                     msecs_to_jiffies(wdd->timeout * 1000))) {
-               dev_crit(wdd->dev, "Timer expired. System will reboot soon!\n");
-               return;
-       }
-
-       /* Restart timer */
-       mod_timer(&priv->timer, jiffies + priv->hw_margin);
-
-       switch (priv->hw_algo) {
-       case HW_ALGO_TOGGLE:
-               /* Toggle output pin */
-               priv->state = !priv->state;
-               gpio_set_value_cansleep(priv->gpio, priv->state);
-               break;
-       case HW_ALGO_LEVEL:
-               /* Pulse */
-               gpio_set_value_cansleep(priv->gpio, !priv->active_low);
-               udelay(1);
-               gpio_set_value_cansleep(priv->gpio, priv->active_low);
-               break;
-       }
-}
-
 static int gpio_wdt_notify_sys(struct notifier_block *nb, unsigned long code,
                               void *unused)
 {
@@ -182,10 +182,10 @@ static int gpio_wdt_probe(struct platform_device *pdev)
        ret = of_property_read_string(pdev->dev.of_node, "hw_algo", &algo);
        if (ret)
                return ret;
-       if (!strncmp(algo, "toggle", 6)) {
+       if (!strcmp(algo, "toggle")) {
                priv->hw_algo = HW_ALGO_TOGGLE;
                f = GPIOF_IN;
-       } else if (!strncmp(algo, "level", 5)) {
+       } else if (!strcmp(algo, "level")) {
                priv->hw_algo = HW_ALGO_LEVEL;
                f = priv->active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
        } else {
@@ -217,6 +217,7 @@ static int gpio_wdt_probe(struct platform_device *pdev)
        priv->wdd.ops           = &gpio_wdt_ops;
        priv->wdd.min_timeout   = SOFT_TIMEOUT_MIN;
        priv->wdd.max_timeout   = SOFT_TIMEOUT_MAX;
+       priv->wdd.parent        = &pdev->dev;
 
        if (watchdog_init_timeout(&priv->wdd, 0, &pdev->dev) < 0)
                priv->wdd.timeout = SOFT_TIMEOUT_DEF;
index 9bc39ae..78c2541 100644 (file)
@@ -267,6 +267,7 @@ static int ie6xx_wdt_probe(struct platform_device *pdev)
 
        ie6xx_wdt_dev.timeout = timeout;
        watchdog_set_nowayout(&ie6xx_wdt_dev, nowayout);
+       ie6xx_wdt_dev.parent = &pdev->dev;
 
        spin_lock_init(&ie6xx_wdt_data.unlock_sequence);
 
index 0f73621..15ab072 100644 (file)
@@ -316,6 +316,7 @@ static int pdc_wdt_remove(struct platform_device *pdev)
 {
        struct pdc_wdt_dev *pdc_wdt = platform_get_drvdata(pdev);
 
+       unregister_restart_handler(&pdc_wdt->restart_handler);
        pdc_wdt_stop(&pdc_wdt->wdt_dev);
        watchdog_unregister_device(&pdc_wdt->wdt_dev);
        clk_disable_unprepare(pdc_wdt->wdt_clk);
index 84f6701..0a436b5 100644 (file)
@@ -137,6 +137,7 @@ static int mid_wdt_probe(struct platform_device *pdev)
        wdt_dev->min_timeout = MID_WDT_TIMEOUT_MIN;
        wdt_dev->max_timeout = MID_WDT_TIMEOUT_MAX;
        wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT;
+       wdt_dev->parent = &pdev->dev;
 
        watchdog_set_drvdata(wdt_dev, &pdev->dev);
        platform_set_drvdata(pdev, wdt_dev);
index 4c2cc09..6a7d5c3 100644 (file)
@@ -174,6 +174,7 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
        jz4740_wdt->timeout = heartbeat;
        jz4740_wdt->min_timeout = 1;
        jz4740_wdt->max_timeout = MAX_HEARTBEAT;
+       jz4740_wdt->parent = &pdev->dev;
        watchdog_set_nowayout(jz4740_wdt, nowayout);
        watchdog_set_drvdata(jz4740_wdt, drvdata);
 
diff --git a/drivers/watchdog/lpc18xx_wdt.c b/drivers/watchdog/lpc18xx_wdt.c
new file mode 100644 (file)
index 0000000..ab7b8b1
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * NXP LPC18xx Watchdog Timer (WDT)
+ *
+ * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Notes
+ * -----
+ * The Watchdog consists of a fixed divide-by-4 clock pre-scaler and a 24-bit
+ * counter which decrements on every clock cycle.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+/* Registers */
+#define LPC18XX_WDT_MOD                        0x00
+#define LPC18XX_WDT_MOD_WDEN           BIT(0)
+#define LPC18XX_WDT_MOD_WDRESET                BIT(1)
+
+#define LPC18XX_WDT_TC                 0x04
+#define LPC18XX_WDT_TC_MIN             0xff
+#define LPC18XX_WDT_TC_MAX             0xffffff
+
+#define LPC18XX_WDT_FEED               0x08
+#define LPC18XX_WDT_FEED_MAGIC1                0xaa
+#define LPC18XX_WDT_FEED_MAGIC2                0x55
+
+#define LPC18XX_WDT_TV                 0x0c
+
+/* Clock pre-scaler */
+#define LPC18XX_WDT_CLK_DIV            4
+
+/* Timeout values in seconds */
+#define LPC18XX_WDT_DEF_TIMEOUT                30U
+
+static int heartbeat;
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds (default="
+                __MODULE_STRING(LPC18XX_WDT_DEF_TIMEOUT) ")");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct lpc18xx_wdt_dev {
+       struct watchdog_device  wdt_dev;
+       struct clk              *reg_clk;
+       struct clk              *wdt_clk;
+       unsigned long           clk_rate;
+       void __iomem            *base;
+       struct timer_list       timer;
+       struct notifier_block   restart_handler;
+       spinlock_t              lock;
+};
+
+static int lpc18xx_wdt_feed(struct watchdog_device *wdt_dev)
+{
+       struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
+       unsigned long flags;
+
+       /*
+        * An abort condition will occur if an interrupt happens during the feed
+        * sequence.
+        */
+       spin_lock_irqsave(&lpc18xx_wdt->lock, flags);
+       writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
+       writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
+       spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags);
+
+       return 0;
+}
+
+static void lpc18xx_wdt_timer_feed(unsigned long data)
+{
+       struct watchdog_device *wdt_dev = (struct watchdog_device *)data;
+       struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
+
+       lpc18xx_wdt_feed(wdt_dev);
+
+       /* Use safe value (1/2 of real timeout) */
+       mod_timer(&lpc18xx_wdt->timer, jiffies +
+                 msecs_to_jiffies((wdt_dev->timeout * MSEC_PER_SEC) / 2));
+}
+
+/*
+ * Since LPC18xx Watchdog cannot be disabled in hardware, we must keep feeding
+ * it with a timer until userspace watchdog software takes over.
+ */
+static int lpc18xx_wdt_stop(struct watchdog_device *wdt_dev)
+{
+       lpc18xx_wdt_timer_feed((unsigned long)wdt_dev);
+
+       return 0;
+}
+
+static void __lpc18xx_wdt_set_timeout(struct lpc18xx_wdt_dev *lpc18xx_wdt)
+{
+       unsigned int val;
+
+       val = DIV_ROUND_UP(lpc18xx_wdt->wdt_dev.timeout * lpc18xx_wdt->clk_rate,
+                          LPC18XX_WDT_CLK_DIV);
+       writel(val, lpc18xx_wdt->base + LPC18XX_WDT_TC);
+}
+
+static int lpc18xx_wdt_set_timeout(struct watchdog_device *wdt_dev,
+                                  unsigned int new_timeout)
+{
+       struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
+
+       lpc18xx_wdt->wdt_dev.timeout = new_timeout;
+       __lpc18xx_wdt_set_timeout(lpc18xx_wdt);
+
+       return 0;
+}
+
+static unsigned int lpc18xx_wdt_get_timeleft(struct watchdog_device *wdt_dev)
+{
+       struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
+       unsigned int val;
+
+       val = readl(lpc18xx_wdt->base + LPC18XX_WDT_TV);
+       return (val * LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate;
+}
+
+static int lpc18xx_wdt_start(struct watchdog_device *wdt_dev)
+{
+       struct lpc18xx_wdt_dev *lpc18xx_wdt = watchdog_get_drvdata(wdt_dev);
+       unsigned int val;
+
+       if (timer_pending(&lpc18xx_wdt->timer))
+               del_timer(&lpc18xx_wdt->timer);
+
+       val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD);
+       val |= LPC18XX_WDT_MOD_WDEN;
+       val |= LPC18XX_WDT_MOD_WDRESET;
+       writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD);
+
+       /*
+        * Setting the WDEN bit in the WDMOD register is not sufficient to
+        * enable the Watchdog. A valid feed sequence must be completed after
+        * setting WDEN before the Watchdog is capable of generating a reset.
+        */
+       lpc18xx_wdt_feed(wdt_dev);
+
+       return 0;
+}
+
+static struct watchdog_info lpc18xx_wdt_info = {
+       .identity       = "NXP LPC18xx Watchdog",
+       .options        = WDIOF_SETTIMEOUT |
+                         WDIOF_KEEPALIVEPING |
+                         WDIOF_MAGICCLOSE,
+};
+
+static const struct watchdog_ops lpc18xx_wdt_ops = {
+       .owner          = THIS_MODULE,
+       .start          = lpc18xx_wdt_start,
+       .stop           = lpc18xx_wdt_stop,
+       .ping           = lpc18xx_wdt_feed,
+       .set_timeout    = lpc18xx_wdt_set_timeout,
+       .get_timeleft   = lpc18xx_wdt_get_timeleft,
+};
+
+static int lpc18xx_wdt_restart(struct notifier_block *this, unsigned long mode,
+                              void *cmd)
+{
+       struct lpc18xx_wdt_dev *lpc18xx_wdt = container_of(this,
+                               struct lpc18xx_wdt_dev, restart_handler);
+       unsigned long flags;
+       int val;
+
+       /*
+        * Incorrect feed sequence causes immediate watchdog reset if enabled.
+        */
+       spin_lock_irqsave(&lpc18xx_wdt->lock, flags);
+
+       val = readl(lpc18xx_wdt->base + LPC18XX_WDT_MOD);
+       val |= LPC18XX_WDT_MOD_WDEN;
+       val |= LPC18XX_WDT_MOD_WDRESET;
+       writel(val, lpc18xx_wdt->base + LPC18XX_WDT_MOD);
+
+       writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
+       writel(LPC18XX_WDT_FEED_MAGIC2, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
+
+       writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
+       writel(LPC18XX_WDT_FEED_MAGIC1, lpc18xx_wdt->base + LPC18XX_WDT_FEED);
+
+       spin_unlock_irqrestore(&lpc18xx_wdt->lock, flags);
+
+       return NOTIFY_OK;
+}
+
+static int lpc18xx_wdt_probe(struct platform_device *pdev)
+{
+       struct lpc18xx_wdt_dev *lpc18xx_wdt;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       int ret;
+
+       lpc18xx_wdt = devm_kzalloc(dev, sizeof(*lpc18xx_wdt), GFP_KERNEL);
+       if (!lpc18xx_wdt)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       lpc18xx_wdt->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(lpc18xx_wdt->base))
+               return PTR_ERR(lpc18xx_wdt->base);
+
+       lpc18xx_wdt->reg_clk = devm_clk_get(dev, "reg");
+       if (IS_ERR(lpc18xx_wdt->reg_clk)) {
+               dev_err(dev, "failed to get the reg clock\n");
+               return PTR_ERR(lpc18xx_wdt->reg_clk);
+       }
+
+       lpc18xx_wdt->wdt_clk = devm_clk_get(dev, "wdtclk");
+       if (IS_ERR(lpc18xx_wdt->wdt_clk)) {
+               dev_err(dev, "failed to get the wdt clock\n");
+               return PTR_ERR(lpc18xx_wdt->wdt_clk);
+       }
+
+       ret = clk_prepare_enable(lpc18xx_wdt->reg_clk);
+       if (ret) {
+               dev_err(dev, "could not prepare or enable sys clock\n");
+               return ret;
+       }
+
+       ret = clk_prepare_enable(lpc18xx_wdt->wdt_clk);
+       if (ret) {
+               dev_err(dev, "could not prepare or enable wdt clock\n");
+               goto disable_reg_clk;
+       }
+
+       /* We use the clock rate to calculate timeouts */
+       lpc18xx_wdt->clk_rate = clk_get_rate(lpc18xx_wdt->wdt_clk);
+       if (lpc18xx_wdt->clk_rate == 0) {
+               dev_err(dev, "failed to get clock rate\n");
+               ret = -EINVAL;
+               goto disable_wdt_clk;
+       }
+
+       lpc18xx_wdt->wdt_dev.info = &lpc18xx_wdt_info;
+       lpc18xx_wdt->wdt_dev.ops = &lpc18xx_wdt_ops;
+
+       lpc18xx_wdt->wdt_dev.min_timeout = DIV_ROUND_UP(LPC18XX_WDT_TC_MIN *
+                               LPC18XX_WDT_CLK_DIV, lpc18xx_wdt->clk_rate);
+
+       lpc18xx_wdt->wdt_dev.max_timeout = (LPC18XX_WDT_TC_MAX *
+                               LPC18XX_WDT_CLK_DIV) / lpc18xx_wdt->clk_rate;
+
+       lpc18xx_wdt->wdt_dev.timeout = min(lpc18xx_wdt->wdt_dev.max_timeout,
+                                          LPC18XX_WDT_DEF_TIMEOUT);
+
+       spin_lock_init(&lpc18xx_wdt->lock);
+
+       lpc18xx_wdt->wdt_dev.parent = dev;
+       watchdog_set_drvdata(&lpc18xx_wdt->wdt_dev, lpc18xx_wdt);
+
+       ret = watchdog_init_timeout(&lpc18xx_wdt->wdt_dev, heartbeat, dev);
+
+       __lpc18xx_wdt_set_timeout(lpc18xx_wdt);
+
+       setup_timer(&lpc18xx_wdt->timer, lpc18xx_wdt_timer_feed,
+                   (unsigned long)&lpc18xx_wdt->wdt_dev);
+
+       watchdog_set_nowayout(&lpc18xx_wdt->wdt_dev, nowayout);
+
+       platform_set_drvdata(pdev, lpc18xx_wdt);
+
+       ret = watchdog_register_device(&lpc18xx_wdt->wdt_dev);
+       if (ret)
+               goto disable_wdt_clk;
+
+       lpc18xx_wdt->restart_handler.notifier_call = lpc18xx_wdt_restart;
+       lpc18xx_wdt->restart_handler.priority = 128;
+       ret = register_restart_handler(&lpc18xx_wdt->restart_handler);
+       if (ret)
+               dev_warn(dev, "failed to register restart handler: %d\n", ret);
+
+       return 0;
+
+disable_wdt_clk:
+       clk_disable_unprepare(lpc18xx_wdt->wdt_clk);
+disable_reg_clk:
+       clk_disable_unprepare(lpc18xx_wdt->reg_clk);
+       return ret;
+}
+
+static void lpc18xx_wdt_shutdown(struct platform_device *pdev)
+{
+       struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev);
+
+       lpc18xx_wdt_stop(&lpc18xx_wdt->wdt_dev);
+}
+
+static int lpc18xx_wdt_remove(struct platform_device *pdev)
+{
+       struct lpc18xx_wdt_dev *lpc18xx_wdt = platform_get_drvdata(pdev);
+
+       unregister_restart_handler(&lpc18xx_wdt->restart_handler);
+
+       dev_warn(&pdev->dev, "I quit now, hardware will probably reboot!\n");
+       del_timer(&lpc18xx_wdt->timer);
+
+       watchdog_unregister_device(&lpc18xx_wdt->wdt_dev);
+       clk_disable_unprepare(lpc18xx_wdt->wdt_clk);
+       clk_disable_unprepare(lpc18xx_wdt->reg_clk);
+
+       return 0;
+}
+
+static const struct of_device_id lpc18xx_wdt_match[] = {
+       { .compatible = "nxp,lpc1850-wwdt" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, lpc18xx_wdt_match);
+
+static struct platform_driver lpc18xx_wdt_driver = {
+       .driver = {
+               .name = "lpc18xx-wdt",
+               .of_match_table = lpc18xx_wdt_match,
+       },
+       .probe = lpc18xx_wdt_probe,
+       .remove = lpc18xx_wdt_remove,
+       .shutdown = lpc18xx_wdt_shutdown,
+};
+module_platform_driver(lpc18xx_wdt_driver);
+
+MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>");
+MODULE_DESCRIPTION("NXP LPC18xx Watchdog Timer Driver");
+MODULE_LICENSE("GPL v2");
index d193a5e..6901300 100644 (file)
@@ -197,6 +197,7 @@ static int a21_wdt_probe(struct platform_device *pdev)
        watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
        watchdog_set_nowayout(&a21_wdt, nowayout);
        watchdog_set_drvdata(&a21_wdt, drv);
+       a21_wdt.parent = &pdev->dev;
 
        reset = a21_wdt_get_bootstatus(drv);
        if (reset == 2)
index 59f0913..3aefdde 100644 (file)
@@ -130,6 +130,7 @@ static int menf21bmc_wdt_probe(struct platform_device *pdev)
        drv_data->wdt.info = &menf21bmc_wdt_info;
        drv_data->wdt.min_timeout = BMC_WD_TIMEOUT_MIN;
        drv_data->wdt.max_timeout = BMC_WD_TIMEOUT_MAX;
+       drv_data->wdt.parent = &pdev->dev;
        drv_data->i2c_client = i2c_client;
 
        /*
index 689381a..5f2273a 100644 (file)
@@ -50,8 +50,12 @@ struct mpc8xxx_wdt_type {
        bool hw_enabled;
 };
 
-static struct mpc8xxx_wdt __iomem *wd_base;
-static int mpc8xxx_wdt_init_late(void);
+struct mpc8xxx_wdt_ddata {
+       struct mpc8xxx_wdt __iomem *base;
+       struct watchdog_device wdd;
+       struct timer_list timer;
+       spinlock_t lock;
+};
 
 static u16 timeout = 0xffff;
 module_param(timeout, ushort, 0);
@@ -68,65 +72,59 @@ module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
                 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
-/*
- * We always prescale, but if someone really doesn't want to they can set this
- * to 0
- */
-static int prescale = 1;
-
-static DEFINE_SPINLOCK(wdt_spinlock);
-
-static void mpc8xxx_wdt_keepalive(void)
+static void mpc8xxx_wdt_keepalive(struct mpc8xxx_wdt_ddata *ddata)
 {
        /* Ping the WDT */
-       spin_lock(&wdt_spinlock);
-       out_be16(&wd_base->swsrr, 0x556c);
-       out_be16(&wd_base->swsrr, 0xaa39);
-       spin_unlock(&wdt_spinlock);
+       spin_lock(&ddata->lock);
+       out_be16(&ddata->base->swsrr, 0x556c);
+       out_be16(&ddata->base->swsrr, 0xaa39);
+       spin_unlock(&ddata->lock);
 }
 
-static struct watchdog_device mpc8xxx_wdt_dev;
-static void mpc8xxx_wdt_timer_ping(unsigned long arg);
-static DEFINE_TIMER(wdt_timer, mpc8xxx_wdt_timer_ping, 0,
-               (unsigned long)&mpc8xxx_wdt_dev);
-
 static void mpc8xxx_wdt_timer_ping(unsigned long arg)
 {
-       struct watchdog_device *w = (struct watchdog_device *)arg;
+       struct mpc8xxx_wdt_ddata *ddata = (void *)arg;
 
-       mpc8xxx_wdt_keepalive();
+       mpc8xxx_wdt_keepalive(ddata);
        /* We're pinging it twice faster than needed, just to be sure. */
-       mod_timer(&wdt_timer, jiffies + HZ * w->timeout / 2);
+       mod_timer(&ddata->timer, jiffies + HZ * ddata->wdd.timeout / 2);
 }
 
 static int mpc8xxx_wdt_start(struct watchdog_device *w)
 {
-       u32 tmp = SWCRR_SWEN;
+       struct mpc8xxx_wdt_ddata *ddata =
+               container_of(w, struct mpc8xxx_wdt_ddata, wdd);
+
+       u32 tmp = SWCRR_SWEN | SWCRR_SWPR;
 
        /* Good, fire up the show */
-       if (prescale)
-               tmp |= SWCRR_SWPR;
        if (reset)
                tmp |= SWCRR_SWRI;
 
        tmp |= timeout << 16;
 
-       out_be32(&wd_base->swcrr, tmp);
+       out_be32(&ddata->base->swcrr, tmp);
 
-       del_timer_sync(&wdt_timer);
+       del_timer_sync(&ddata->timer);
 
        return 0;
 }
 
 static int mpc8xxx_wdt_ping(struct watchdog_device *w)
 {
-       mpc8xxx_wdt_keepalive();
+       struct mpc8xxx_wdt_ddata *ddata =
+               container_of(w, struct mpc8xxx_wdt_ddata, wdd);
+
+       mpc8xxx_wdt_keepalive(ddata);
        return 0;
 }
 
 static int mpc8xxx_wdt_stop(struct watchdog_device *w)
 {
-       mod_timer(&wdt_timer, jiffies);
+       struct mpc8xxx_wdt_ddata *ddata =
+               container_of(w, struct mpc8xxx_wdt_ddata, wdd);
+
+       mod_timer(&ddata->timer, jiffies);
        return 0;
 }
 
@@ -143,53 +141,57 @@ static struct watchdog_ops mpc8xxx_wdt_ops = {
        .stop = mpc8xxx_wdt_stop,
 };
 
-static struct watchdog_device mpc8xxx_wdt_dev = {
-       .info = &mpc8xxx_wdt_info,
-       .ops = &mpc8xxx_wdt_ops,
-};
-
-static const struct of_device_id mpc8xxx_wdt_match[];
 static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
 {
        int ret;
-       const struct of_device_id *match;
-       struct device_node *np = ofdev->dev.of_node;
+       struct resource *res;
        const struct mpc8xxx_wdt_type *wdt_type;
+       struct mpc8xxx_wdt_ddata *ddata;
        u32 freq = fsl_get_sys_freq();
        bool enabled;
        unsigned int timeout_sec;
 
-       match = of_match_device(mpc8xxx_wdt_match, &ofdev->dev);
-       if (!match)
+       wdt_type = of_device_get_match_data(&ofdev->dev);
+       if (!wdt_type)
                return -EINVAL;
-       wdt_type = match->data;
 
        if (!freq || freq == -1)
                return -EINVAL;
 
-       wd_base = of_iomap(np, 0);
-       if (!wd_base)
+       ddata = devm_kzalloc(&ofdev->dev, sizeof(*ddata), GFP_KERNEL);
+       if (!ddata)
                return -ENOMEM;
 
-       enabled = in_be32(&wd_base->swcrr) & SWCRR_SWEN;
+       res = platform_get_resource(ofdev, IORESOURCE_MEM, 0);
+       ddata->base = devm_ioremap_resource(&ofdev->dev, res);
+       if (IS_ERR(ddata->base))
+               return PTR_ERR(ddata->base);
+
+       enabled = in_be32(&ddata->base->swcrr) & SWCRR_SWEN;
        if (!enabled && wdt_type->hw_enabled) {
                pr_info("could not be enabled in software\n");
-               ret = -ENOSYS;
-               goto err_unmap;
+               return -ENODEV;
        }
 
+       spin_lock_init(&ddata->lock);
+       setup_timer(&ddata->timer, mpc8xxx_wdt_timer_ping,
+                   (unsigned long)ddata);
+
+       ddata->wdd.info = &mpc8xxx_wdt_info,
+       ddata->wdd.ops = &mpc8xxx_wdt_ops,
+
        /* Calculate the timeout in seconds */
-       if (prescale)
-               timeout_sec = (timeout * wdt_type->prescaler) / freq;
-       else
-               timeout_sec = timeout / freq;
-
-       mpc8xxx_wdt_dev.timeout = timeout_sec;
-#ifdef MODULE
-       ret = mpc8xxx_wdt_init_late();
-       if (ret)
-               goto err_unmap;
-#endif
+       timeout_sec = (timeout * wdt_type->prescaler) / freq;
+
+       ddata->wdd.timeout = timeout_sec;
+
+       watchdog_set_nowayout(&ddata->wdd, nowayout);
+
+       ret = watchdog_register_device(&ddata->wdd);
+       if (ret) {
+               pr_err("cannot register watchdog device (err=%d)\n", ret);
+               return ret;
+       }
 
        pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d (%d seconds)\n",
                reset ? "reset" : "interrupt", timeout, timeout_sec);
@@ -200,21 +202,20 @@ static int mpc8xxx_wdt_probe(struct platform_device *ofdev)
         * userspace handles it.
         */
        if (enabled)
-               mod_timer(&wdt_timer, jiffies);
+               mod_timer(&ddata->timer, jiffies);
+
+       platform_set_drvdata(ofdev, ddata);
        return 0;
-err_unmap:
-       iounmap(wd_base);
-       wd_base = NULL;
-       return ret;
 }
 
 static int mpc8xxx_wdt_remove(struct platform_device *ofdev)
 {
+       struct mpc8xxx_wdt_ddata *ddata = platform_get_drvdata(ofdev);
+
        pr_crit("Watchdog removed, expect the %s soon!\n",
                reset ? "reset" : "machine check exception");
-       del_timer_sync(&wdt_timer);
-       watchdog_unregister_device(&mpc8xxx_wdt_dev);
-       iounmap(wd_base);
+       del_timer_sync(&ddata->timer);
+       watchdog_unregister_device(&ddata->wdd);
 
        return 0;
 }
@@ -253,31 +254,6 @@ static struct platform_driver mpc8xxx_wdt_driver = {
        },
 };
 
-/*
- * We do wdt initialization in two steps: arch_initcall probes the wdt
- * very early to start pinging the watchdog (misc devices are not yet
- * available), and later module_init() just registers the misc device.
- */
-static int mpc8xxx_wdt_init_late(void)
-{
-       int ret;
-
-       if (!wd_base)
-               return -ENODEV;
-
-       watchdog_set_nowayout(&mpc8xxx_wdt_dev, nowayout);
-
-       ret = watchdog_register_device(&mpc8xxx_wdt_dev);
-       if (ret) {
-               pr_err("cannot register watchdog device (err=%d)\n", ret);
-               return ret;
-       }
-       return 0;
-}
-#ifndef MODULE
-module_init(mpc8xxx_wdt_init_late);
-#endif
-
 static int __init mpc8xxx_wdt_init(void)
 {
        return platform_driver_register(&mpc8xxx_wdt_driver);
index 938b987..6ad9df9 100644 (file)
@@ -210,6 +210,14 @@ static int mtk_wdt_probe(struct platform_device *pdev)
        return 0;
 }
 
+static void mtk_wdt_shutdown(struct platform_device *pdev)
+{
+       struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev);
+
+       if (watchdog_active(&mtk_wdt->wdt_dev))
+               mtk_wdt_stop(&mtk_wdt->wdt_dev);
+}
+
 static int mtk_wdt_remove(struct platform_device *pdev)
 {
        struct mtk_wdt_dev *mtk_wdt = platform_get_drvdata(pdev);
@@ -221,17 +229,48 @@ static int mtk_wdt_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int mtk_wdt_suspend(struct device *dev)
+{
+       struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev);
+
+       if (watchdog_active(&mtk_wdt->wdt_dev))
+               mtk_wdt_stop(&mtk_wdt->wdt_dev);
+
+       return 0;
+}
+
+static int mtk_wdt_resume(struct device *dev)
+{
+       struct mtk_wdt_dev *mtk_wdt = dev_get_drvdata(dev);
+
+       if (watchdog_active(&mtk_wdt->wdt_dev)) {
+               mtk_wdt_start(&mtk_wdt->wdt_dev);
+               mtk_wdt_ping(&mtk_wdt->wdt_dev);
+       }
+
+       return 0;
+}
+#endif
+
 static const struct of_device_id mtk_wdt_dt_ids[] = {
        { .compatible = "mediatek,mt6589-wdt" },
        { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, mtk_wdt_dt_ids);
 
+static const struct dev_pm_ops mtk_wdt_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(mtk_wdt_suspend,
+                               mtk_wdt_resume)
+};
+
 static struct platform_driver mtk_wdt_driver = {
        .probe          = mtk_wdt_probe,
        .remove         = mtk_wdt_remove,
+       .shutdown       = mtk_wdt_shutdown,
        .driver         = {
                .name           = DRV_NAME,
+               .pm             = &mtk_wdt_pm_ops,
                .of_match_table = mtk_wdt_dt_ids,
        },
 };
index c028454..bd917bb 100644 (file)
@@ -294,6 +294,8 @@ static const struct pci_device_id tco_pci_tbl[] = {
          PCI_ANY_ID, PCI_ANY_ID, },
        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
          PCI_ANY_ID, PCI_ANY_ID, },
+       { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS,
+         PCI_ANY_ID, PCI_ANY_ID, },
        { 0, },                 /* End of list */
 };
 MODULE_DEVICE_TABLE(pci, tco_pci_tbl);
index de911c7..d96bee0 100644 (file)
@@ -253,6 +253,7 @@ static int omap_wdt_probe(struct platform_device *pdev)
        wdev->wdog.ops = &omap_wdt_ops;
        wdev->wdog.min_timeout = TIMER_MARGIN_MIN;
        wdev->wdog.max_timeout = TIMER_MARGIN_MAX;
+       wdev->wdog.parent = &pdev->dev;
 
        if (watchdog_init_timeout(&wdev->wdog, timer_margin, &pdev->dev) < 0)
                wdev->wdog.timeout = TIMER_MARGIN_DEFAULT;
index ef0c628..c6b8f4a 100644 (file)
@@ -567,6 +567,7 @@ static int orion_wdt_probe(struct platform_device *pdev)
 
        dev->wdt.timeout = wdt_max_duration;
        dev->wdt.max_timeout = wdt_max_duration;
+       dev->wdt.parent = &pdev->dev;
        watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
 
        platform_set_drvdata(pdev, &dev->wdt);
index b9c6049..4224b3e 100644 (file)
@@ -167,6 +167,7 @@ static int pnx4008_wdt_probe(struct platform_device *pdev)
 
        pnx4008_wdd.bootstatus = (readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
                        WDIOF_CARDRESET : 0;
+       pnx4008_wdd.parent = &pdev->dev;
        watchdog_set_nowayout(&pnx4008_wdd, nowayout);
 
        pnx4008_wdt_stop(&pnx4008_wdd); /* disable for now */
index aa03ca8..773dcfa 100644 (file)
@@ -171,6 +171,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
        wdt->wdd.ops = &qcom_wdt_ops;
        wdt->wdd.min_timeout = 1;
        wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
+       wdt->wdd.parent = &pdev->dev;
 
        /*
         * If 'timeout-sec' unspecified in devicetree, assume a 30 second
index b7c68e2..39cd51d 100644 (file)
@@ -127,6 +127,7 @@ static int retu_wdt_probe(struct platform_device *pdev)
        retu_wdt->timeout       = RETU_WDT_MAX_TIMER;
        retu_wdt->min_timeout   = 0;
        retu_wdt->max_timeout   = RETU_WDT_MAX_TIMER;
+       retu_wdt->parent        = &pdev->dev;
 
        watchdog_set_drvdata(retu_wdt, wdev);
        watchdog_set_nowayout(retu_wdt, nowayout);
index a6f7e2e..1967919 100644 (file)
@@ -161,6 +161,7 @@ static int rt288x_wdt_probe(struct platform_device *pdev)
        rt288x_wdt_dev.dev = &pdev->dev;
        rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
        rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
+       rt288x_wdt_dev.parent = &pdev->dev;
 
        watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout,
                              &pdev->dev);
index e89ae02..d781000 100644 (file)
@@ -607,6 +607,7 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
        watchdog_set_nowayout(&wdt->wdt_device, nowayout);
 
        wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
+       wdt->wdt_device.parent = &pdev->dev;
 
        ret = watchdog_register_device(&wdt->wdt_device);
        if (ret) {
diff --git a/drivers/watchdog/sama5d4_wdt.c b/drivers/watchdog/sama5d4_wdt.c
new file mode 100644 (file)
index 0000000..a49634c
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Driver for Atmel SAMA5D4 Watchdog Timer
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Licensed under GPLv2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+
+#include "at91sam9_wdt.h"
+
+/* minimum and maximum watchdog timeout, in seconds */
+#define MIN_WDT_TIMEOUT                1
+#define MAX_WDT_TIMEOUT                16
+#define WDT_DEFAULT_TIMEOUT    MAX_WDT_TIMEOUT
+
+#define WDT_SEC2TICKS(s)       ((s) ? (((s) << 8) - 1) : 0)
+
+struct sama5d4_wdt {
+       struct watchdog_device  wdd;
+       void __iomem            *reg_base;
+       u32     config;
+};
+
+static int wdt_timeout = WDT_DEFAULT_TIMEOUT;
+static bool nowayout = WATCHDOG_NOWAYOUT;
+
+module_param(wdt_timeout, int, 0);
+MODULE_PARM_DESC(wdt_timeout,
+       "Watchdog timeout in seconds. (default = "
+       __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");
+
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+       "Watchdog cannot be stopped once started (default="
+       __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+#define wdt_read(wdt, field) \
+       readl_relaxed((wdt)->reg_base + (field))
+
+#define wdt_write(wtd, field, val) \
+       writel_relaxed((val), (wdt)->reg_base + (field))
+
+static int sama5d4_wdt_start(struct watchdog_device *wdd)
+{
+       struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
+       u32 reg;
+
+       reg = wdt_read(wdt, AT91_WDT_MR);
+       reg &= ~AT91_WDT_WDDIS;
+       wdt_write(wdt, AT91_WDT_MR, reg);
+
+       return 0;
+}
+
+static int sama5d4_wdt_stop(struct watchdog_device *wdd)
+{
+       struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
+       u32 reg;
+
+       reg = wdt_read(wdt, AT91_WDT_MR);
+       reg |= AT91_WDT_WDDIS;
+       wdt_write(wdt, AT91_WDT_MR, reg);
+
+       return 0;
+}
+
+static int sama5d4_wdt_ping(struct watchdog_device *wdd)
+{
+       struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
+
+       wdt_write(wdt, AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT);
+
+       return 0;
+}
+
+static int sama5d4_wdt_set_timeout(struct watchdog_device *wdd,
+                                unsigned int timeout)
+{
+       struct sama5d4_wdt *wdt = watchdog_get_drvdata(wdd);
+       u32 value = WDT_SEC2TICKS(timeout);
+       u32 reg;
+
+       reg = wdt_read(wdt, AT91_WDT_MR);
+       reg &= ~AT91_WDT_WDV;
+       reg &= ~AT91_WDT_WDD;
+       reg |= AT91_WDT_SET_WDV(value);
+       reg |= AT91_WDT_SET_WDD(value);
+       wdt_write(wdt, AT91_WDT_MR, reg);
+
+       wdd->timeout = timeout;
+
+       return 0;
+}
+
+static const struct watchdog_info sama5d4_wdt_info = {
+       .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+       .identity = "Atmel SAMA5D4 Watchdog",
+};
+
+static struct watchdog_ops sama5d4_wdt_ops = {
+       .owner = THIS_MODULE,
+       .start = sama5d4_wdt_start,
+       .stop = sama5d4_wdt_stop,
+       .ping = sama5d4_wdt_ping,
+       .set_timeout = sama5d4_wdt_set_timeout,
+};
+
+static irqreturn_t sama5d4_wdt_irq_handler(int irq, void *dev_id)
+{
+       struct sama5d4_wdt *wdt = platform_get_drvdata(dev_id);
+
+       if (wdt_read(wdt, AT91_WDT_SR)) {
+               pr_crit("Atmel Watchdog Software Reset\n");
+               emergency_restart();
+               pr_crit("Reboot didn't succeed\n");
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int of_sama5d4_wdt_init(struct device_node *np, struct sama5d4_wdt *wdt)
+{
+       const char *tmp;
+
+       wdt->config = AT91_WDT_WDDIS;
+
+       if (!of_property_read_string(np, "atmel,watchdog-type", &tmp) &&
+           !strcmp(tmp, "software"))
+               wdt->config |= AT91_WDT_WDFIEN;
+       else
+               wdt->config |= AT91_WDT_WDRSTEN;
+
+       if (of_property_read_bool(np, "atmel,idle-halt"))
+               wdt->config |= AT91_WDT_WDIDLEHLT;
+
+       if (of_property_read_bool(np, "atmel,dbg-halt"))
+               wdt->config |= AT91_WDT_WDDBGHLT;
+
+       return 0;
+}
+
+static int sama5d4_wdt_init(struct sama5d4_wdt *wdt)
+{
+       struct watchdog_device *wdd = &wdt->wdd;
+       u32 value = WDT_SEC2TICKS(wdd->timeout);
+       u32 reg;
+
+       /*
+        * Because the fields WDV and WDD must not be modified when the WDDIS
+        * bit is set, so clear the WDDIS bit before writing the WDT_MR.
+        */
+       reg = wdt_read(wdt, AT91_WDT_MR);
+       reg &= ~AT91_WDT_WDDIS;
+       wdt_write(wdt, AT91_WDT_MR, reg);
+
+       reg = wdt->config;
+       reg |= AT91_WDT_SET_WDD(value);
+       reg |= AT91_WDT_SET_WDV(value);
+
+       wdt_write(wdt, AT91_WDT_MR, reg);
+
+       return 0;
+}
+
+static int sama5d4_wdt_probe(struct platform_device *pdev)
+{
+       struct watchdog_device *wdd;
+       struct sama5d4_wdt *wdt;
+       struct resource *res;
+       void __iomem *regs;
+       u32 irq = 0;
+       int ret;
+
+       wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
+       if (!wdt)
+               return -ENOMEM;
+
+       wdd = &wdt->wdd;
+       wdd->timeout = wdt_timeout;
+       wdd->info = &sama5d4_wdt_info;
+       wdd->ops = &sama5d4_wdt_ops;
+       wdd->min_timeout = MIN_WDT_TIMEOUT;
+       wdd->max_timeout = MAX_WDT_TIMEOUT;
+
+       watchdog_set_drvdata(wdd, wdt);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
+
+       wdt->reg_base = regs;
+
+       if (pdev->dev.of_node) {
+               irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+               if (!irq)
+                       dev_warn(&pdev->dev, "failed to get IRQ from DT\n");
+
+               ret = of_sama5d4_wdt_init(pdev->dev.of_node, wdt);
+               if (ret)
+                       return ret;
+       }
+
+       if ((wdt->config & AT91_WDT_WDFIEN) && irq) {
+               ret = devm_request_irq(&pdev->dev, irq, sama5d4_wdt_irq_handler,
+                                      IRQF_SHARED | IRQF_IRQPOLL |
+                                      IRQF_NO_SUSPEND, pdev->name, pdev);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "cannot register interrupt handler\n");
+                       return ret;
+               }
+       }
+
+       ret = watchdog_init_timeout(wdd, wdt_timeout, &pdev->dev);
+       if (ret) {
+               dev_err(&pdev->dev, "unable to set timeout value\n");
+               return ret;
+       }
+
+       ret = sama5d4_wdt_init(wdt);
+       if (ret)
+               return ret;
+
+       watchdog_set_nowayout(wdd, nowayout);
+
+       ret = watchdog_register_device(wdd);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register watchdog device\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, wdt);
+
+       dev_info(&pdev->dev, "initialized (timeout = %d sec, nowayout = %d)\n",
+                wdt_timeout, nowayout);
+
+       return 0;
+}
+
+static int sama5d4_wdt_remove(struct platform_device *pdev)
+{
+       struct sama5d4_wdt *wdt = platform_get_drvdata(pdev);
+
+       sama5d4_wdt_stop(&wdt->wdd);
+
+       watchdog_unregister_device(&wdt->wdd);
+
+       return 0;
+}
+
+static const struct of_device_id sama5d4_wdt_of_match[] = {
+       { .compatible = "atmel,sama5d4-wdt", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, sama5d4_wdt_of_match);
+
+static struct platform_driver sama5d4_wdt_driver = {
+       .probe          = sama5d4_wdt_probe,
+       .remove         = sama5d4_wdt_remove,
+       .driver         = {
+               .name   = "sama5d4_wdt",
+               .of_match_table = sama5d4_wdt_of_match,
+       }
+};
+module_platform_driver(sama5d4_wdt_driver);
+
+MODULE_AUTHOR("Atmel Corporation");
+MODULE_DESCRIPTION("Atmel SAMA5D4 Watchdog Timer driver");
+MODULE_LICENSE("GPL v2");
index 567458b..f908121 100644 (file)
@@ -252,6 +252,7 @@ static int sh_wdt_probe(struct platform_device *pdev)
 
        watchdog_set_nowayout(&sh_wdt_dev, nowayout);
        watchdog_set_drvdata(&sh_wdt_dev, wdt);
+       sh_wdt_dev.parent = &pdev->dev;
 
        spin_lock_init(&wdt->lock);
 
index 42fa5c0..d0578ab 100644 (file)
@@ -154,6 +154,7 @@ static int sirfsoc_wdt_probe(struct platform_device *pdev)
 
        watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
        watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
+       sirfsoc_wdd.parent = &pdev->dev;
 
        ret = watchdog_register_device(&sirfsoc_wdd);
        if (ret)
index 4e7fec3..01d8162 100644 (file)
@@ -226,6 +226,7 @@ sp805_wdt_probe(struct amba_device *adev, const struct amba_id *id)
        wdt->adev = adev;
        wdt->wdd.info = &wdt_info;
        wdt->wdd.ops = &wdt_ops;
+       wdt->wdd.parent = &adev->dev;
 
        spin_lock_init(&wdt->lock);
        watchdog_set_nowayout(&wdt->wdd, nowayout);
index 6785afd..14e9bad 100644 (file)
@@ -241,6 +241,7 @@ static int st_wdog_probe(struct platform_device *pdev)
                return -EINVAL;
        }
        st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+       st_wdog_dev.parent = &pdev->dev;
 
        ret = clk_prepare_enable(clk);
        if (ret) {
index e7f0d5b..3ee6128 100644 (file)
@@ -76,6 +76,7 @@ static int stmp3xxx_wdt_probe(struct platform_device *pdev)
        watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev);
 
        stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT);
+       stmp3xxx_wdd.parent = &pdev->dev;
 
        ret = watchdog_register_device(&stmp3xxx_wdd);
        if (ret < 0) {
index a29afb3..47bd8a1 100644 (file)
@@ -184,7 +184,7 @@ static int sunxi_wdt_start(struct watchdog_device *wdt_dev)
        /* Set system reset function */
        reg = readl(wdt_base + regs->wdt_cfg);
        reg &= ~(regs->wdt_reset_mask);
-       reg |= ~(regs->wdt_reset_val);
+       reg |= regs->wdt_reset_val;
        writel(reg, wdt_base + regs->wdt_cfg);
 
        /* Enable watchdog */
index 30451ea..7f97cdd 100644 (file)
@@ -218,6 +218,7 @@ static int tegra_wdt_probe(struct platform_device *pdev)
        wdd->ops = &tegra_wdt_ops;
        wdd->min_timeout = MIN_WDT_TIMEOUT;
        wdd->max_timeout = MAX_WDT_TIMEOUT;
+       wdd->parent = &pdev->dev;
 
        watchdog_set_drvdata(wdd, wdt);
 
index 2c1db6f..9bf3cc0 100644 (file)
@@ -83,6 +83,7 @@ static int twl4030_wdt_probe(struct platform_device *pdev)
        wdt->timeout            = 30;
        wdt->min_timeout        = 1;
        wdt->max_timeout        = 30;
+       wdt->parent = &pdev->dev;
 
        watchdog_set_nowayout(wdt, nowayout);
        platform_set_drvdata(pdev, wdt);
index 7f61593..c2da880 100644 (file)
@@ -131,6 +131,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
        txx9wdt.timeout = timeout;
        txx9wdt.min_timeout = 1;
        txx9wdt.max_timeout = WD_MAX_TIMEOUT;
+       txx9wdt.parent = &dev->dev;
        watchdog_set_nowayout(&txx9wdt, nowayout);
 
        ret = watchdog_register_device(&txx9wdt);
index 9de09ab..37c0843 100644 (file)
@@ -96,6 +96,7 @@ static int ux500_wdt_probe(struct platform_device *pdev)
                        ux500_wdt.max_timeout = WATCHDOG_MAX28;
        }
 
+       ux500_wdt.parent = &pdev->dev;
        watchdog_set_nowayout(&ux500_wdt, nowayout);
 
        /* disable auto off on sleep */
index 56369c4..5f9cbc3 100644 (file)
@@ -206,6 +206,7 @@ static int wdt_probe(struct pci_dev *pdev,
                timeout = WDT_TIMEOUT;
 
        wdt_dev.timeout = timeout;
+       wdt_dev.parent = &pdev->dev;
        watchdog_set_nowayout(&wdt_dev, nowayout);
        if (readl(wdt_mem) & VIA_WDT_FIRED)
                wdt_dev.bootstatus |= WDIOF_CARDRESET;
index 2fa17e7..8d1184a 100644 (file)
@@ -215,6 +215,7 @@ static int wm831x_wdt_probe(struct platform_device *pdev)
 
        wm831x_wdt->info = &wm831x_wdt_info;
        wm831x_wdt->ops = &wm831x_wdt_ops;
+       wm831x_wdt->parent = &pdev->dev;
        watchdog_set_nowayout(wm831x_wdt, nowayout);
        watchdog_set_drvdata(wm831x_wdt, driver_data);
 
index 34d272a..4ab4b83 100644 (file)
@@ -151,6 +151,7 @@ static int wm8350_wdt_probe(struct platform_device *pdev)
 
        watchdog_set_nowayout(&wm8350_wdt, nowayout);
        watchdog_set_drvdata(&wm8350_wdt, wm8350);
+       wm8350_wdt.parent = &pdev->dev;
 
        /* Default to 4s timeout */
        wm8350_wdt_set_timeout(&wm8350_wdt, 4);
index e4905fb..8f20d60 100644 (file)
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -142,7 +142,8 @@ static int nsfs_show_path(struct seq_file *seq, struct dentry *dentry)
        struct inode *inode = d_inode(dentry);
        const struct proc_ns_operations *ns_ops = dentry->d_fsdata;
 
-       return seq_printf(seq, "%s:[%lu]", ns_ops->name, inode->i_ino);
+       seq_printf(seq, "%s:[%lu]", ns_ops->name, inode->i_ino);
+       return 0;
 }
 
 static const struct super_operations nsfs_ops = {
index d0e436d..ce12e0b 100644 (file)
@@ -1776,7 +1776,7 @@ static int dlm_process_recovery_data(struct dlm_ctxt *dlm,
                                     struct dlm_migratable_lockres *mres)
 {
        struct dlm_migratable_lock *ml;
-       struct list_head *queue;
+       struct list_head *queue, *iter;
        struct list_head *tmpq = NULL;
        struct dlm_lock *newlock = NULL;
        struct dlm_lockstatus *lksb = NULL;
@@ -1821,7 +1821,9 @@ static int dlm_process_recovery_data(struct dlm_ctxt *dlm,
                        spin_lock(&res->spinlock);
                        for (j = DLM_GRANTED_LIST; j <= DLM_BLOCKED_LIST; j++) {
                                tmpq = dlm_list_idx_to_ptr(res, j);
-                               list_for_each_entry(lock, tmpq, list) {
+                               list_for_each(iter, tmpq) {
+                                       lock = list_entry(iter,
+                                                 struct dlm_lock, list);
                                        if (lock->ml.cookie == ml->cookie)
                                                break;
                                        lock = NULL;
index 263b125..225586e 100644 (file)
@@ -372,16 +372,16 @@ EXPORT_SYMBOL(seq_release);
  *     @esc:   set of characters that need escaping
  *
  *     Puts string into buffer, replacing each occurrence of character from
- *     @esc with usual octal escape.  Returns 0 in case of success, -1 - in
- *     case of overflow.
+ *     @esc with usual octal escape.
+ *     Use seq_has_overflowed() to check for errors.
  */
-int seq_escape(struct seq_file *m, const char *s, const char *esc)
+void seq_escape(struct seq_file *m, const char *s, const char *esc)
 {
        char *end = m->buf + m->size;
-        char *p;
+       char *p;
        char c;
 
-        for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) {
+       for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) {
                if (!strchr(esc, c)) {
                        *p++ = c;
                        continue;
@@ -394,14 +394,13 @@ int seq_escape(struct seq_file *m, const char *s, const char *esc)
                        continue;
                }
                seq_set_overflow(m);
-               return -1;
-        }
+               return;
+       }
        m->count = p - m->buf;
-        return 0;
 }
 EXPORT_SYMBOL(seq_escape);
 
-int seq_vprintf(struct seq_file *m, const char *f, va_list args)
+void seq_vprintf(struct seq_file *m, const char *f, va_list args)
 {
        int len;
 
@@ -409,24 +408,20 @@ int seq_vprintf(struct seq_file *m, const char *f, va_list args)
                len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
                if (m->count + len < m->size) {
                        m->count += len;
-                       return 0;
+                       return;
                }
        }
        seq_set_overflow(m);
-       return -1;
 }
 EXPORT_SYMBOL(seq_vprintf);
 
-int seq_printf(struct seq_file *m, const char *f, ...)
+void seq_printf(struct seq_file *m, const char *f, ...)
 {
-       int ret;
        va_list args;
 
        va_start(args, f);
-       ret = seq_vprintf(m, f, args);
+       seq_vprintf(m, f, args);
        va_end(args);
-
-       return ret;
 }
 EXPORT_SYMBOL(seq_printf);
 
@@ -664,26 +659,25 @@ int seq_open_private(struct file *filp, const struct seq_operations *ops,
 }
 EXPORT_SYMBOL(seq_open_private);
 
-int seq_putc(struct seq_file *m, char c)
+void seq_putc(struct seq_file *m, char c)
 {
-       if (m->count < m->size) {
-               m->buf[m->count++] = c;
-               return 0;
-       }
-       return -1;
+       if (m->count >= m->size)
+               return;
+
+       m->buf[m->count++] = c;
 }
 EXPORT_SYMBOL(seq_putc);
 
-int seq_puts(struct seq_file *m, const char *s)
+void seq_puts(struct seq_file *m, const char *s)
 {
        int len = strlen(s);
-       if (m->count + len < m->size) {
-               memcpy(m->buf + m->count, s, len);
-               m->count += len;
-               return 0;
+
+       if (m->count + len >= m->size) {
+               seq_set_overflow(m);
+               return;
        }
-       seq_set_overflow(m);
-       return -1;
+       memcpy(m->buf + m->count, s, len);
+       m->count += len;
 }
 EXPORT_SYMBOL(seq_puts);
 
@@ -694,8 +688,8 @@ EXPORT_SYMBOL(seq_puts);
  * This routine is very quick when you show lots of numbers.
  * In usual cases, it will be better to use seq_printf(). It's easier to read.
  */
-int seq_put_decimal_ull(struct seq_file *m, char delimiter,
-                       unsigned long long num)
+void seq_put_decimal_ull(struct seq_file *m, char delimiter,
+                        unsigned long long num)
 {
        int len;
 
@@ -707,35 +701,33 @@ int seq_put_decimal_ull(struct seq_file *m, char delimiter,
 
        if (num < 10) {
                m->buf[m->count++] = num + '0';
-               return 0;
+               return;
        }
 
        len = num_to_str(m->buf + m->count, m->size - m->count, num);
        if (!len)
                goto overflow;
        m->count += len;
-       return 0;
+       return;
+
 overflow:
        seq_set_overflow(m);
-       return -1;
 }
 EXPORT_SYMBOL(seq_put_decimal_ull);
 
-int seq_put_decimal_ll(struct seq_file *m, char delimiter,
-                       long long num)
+void seq_put_decimal_ll(struct seq_file *m, char delimiter, long long num)
 {
        if (num < 0) {
                if (m->count + 3 >= m->size) {
                        seq_set_overflow(m);
-                       return -1;
+                       return;
                }
                if (delimiter)
                        m->buf[m->count++] = delimiter;
                num = -num;
                delimiter = '-';
        }
-       return seq_put_decimal_ull(m, delimiter, num);
-
+       seq_put_decimal_ull(m, delimiter, num);
 }
 EXPORT_SYMBOL(seq_put_decimal_ll);
 
index fda728e..91c08f6 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/shrinker.h>
 #include <linux/resource.h>
 #include <linux/page_ext.h>
+#include <linux/err.h>
 
 struct mempolicy;
 struct anon_vma;
@@ -1214,6 +1215,49 @@ long get_user_pages_unlocked(struct task_struct *tsk, struct mm_struct *mm,
                    int write, int force, struct page **pages);
 int get_user_pages_fast(unsigned long start, int nr_pages, int write,
                        struct page **pages);
+
+/* Container for pinned pfns / pages */
+struct frame_vector {
+       unsigned int nr_allocated;      /* Number of frames we have space for */
+       unsigned int nr_frames; /* Number of frames stored in ptrs array */
+       bool got_ref;           /* Did we pin pages by getting page ref? */
+       bool is_pfns;           /* Does array contain pages or pfns? */
+       void *ptrs[0];          /* Array of pinned pfns / pages. Use
+                                * pfns_vector_pages() or pfns_vector_pfns()
+                                * for access */
+};
+
+struct frame_vector *frame_vector_create(unsigned int nr_frames);
+void frame_vector_destroy(struct frame_vector *vec);
+int get_vaddr_frames(unsigned long start, unsigned int nr_pfns,
+                    bool write, bool force, struct frame_vector *vec);
+void put_vaddr_frames(struct frame_vector *vec);
+int frame_vector_to_pages(struct frame_vector *vec);
+void frame_vector_to_pfns(struct frame_vector *vec);
+
+static inline unsigned int frame_vector_count(struct frame_vector *vec)
+{
+       return vec->nr_frames;
+}
+
+static inline struct page **frame_vector_pages(struct frame_vector *vec)
+{
+       if (vec->is_pfns) {
+               int err = frame_vector_to_pages(vec);
+
+               if (err)
+                       return ERR_PTR(err);
+       }
+       return (struct page **)(vec->ptrs);
+}
+
+static inline unsigned long *frame_vector_pfns(struct frame_vector *vec)
+{
+       if (!vec->is_pfns)
+               frame_vector_to_pfns(vec);
+       return (unsigned long *)(vec->ptrs);
+}
+
 struct kvec;
 int get_kernel_pages(const struct kvec *iov, int nr_pages, int write,
                        struct page **pages);
index b02f72b..f798e2a 100644 (file)
@@ -522,10 +522,9 @@ static inline int ntb_mw_clear_trans(struct ntb_dev *ntb, int idx)
  * @speed:     OUT - The link speed expressed as PCIe generation number.
  * @width:     OUT - The link width expressed as the number of PCIe lanes.
  *
- * Set the translation of a memory window.  The peer may access local memory
- * through the window starting at the address, up to the size.  The address
- * must be aligned to the alignment specified by ntb_mw_get_range().  The size
- * must be aligned to the size alignment specified by ntb_mw_get_range().
+ * Get the current state of the ntb link.  It is recommended to query the link
+ * state once after every link event.  It is safe to query the link state in
+ * the context of the link event callback.
  *
  * Return: One if the link is up, zero if the link is down, otherwise a
  *             negative value indicating the error number.
@@ -795,7 +794,7 @@ static inline int ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
 }
 
 /**
- * ntb_peer_db_clear() - clear bits in the local doorbell register
+ * ntb_peer_db_clear() - clear bits in the peer doorbell register
  * @ntb:       NTB device context.
  * @db_bits:   Doorbell bits to clear.
  *
index 2862861..7243eb9 100644 (file)
@@ -83,3 +83,4 @@ void *ntb_transport_rx_remove(struct ntb_transport_qp *qp, unsigned int *len);
 void ntb_transport_link_up(struct ntb_transport_qp *qp);
 void ntb_transport_link_down(struct ntb_transport_qp *qp);
 bool ntb_transport_link_query(struct ntb_transport_qp *qp);
+unsigned int ntb_transport_tx_free_entry(struct ntb_transport_qp *qp);
index cab7ba5..e817722 100644 (file)
@@ -34,6 +34,7 @@ bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp);
 
 int dev_pm_opp_get_opp_count(struct device *dev);
 unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev);
+struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev);
 
 struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
                                              unsigned long freq,
@@ -80,6 +81,11 @@ static inline unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)
        return 0;
 }
 
+static inline struct dev_pm_opp *dev_pm_opp_get_suspend_opp(struct device *dev)
+{
+       return NULL;
+}
+
 static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
                                        unsigned long freq, bool available)
 {
index adeadbd..dde00de 100644 (file)
@@ -114,13 +114,18 @@ int seq_open(struct file *, const struct seq_operations *);
 ssize_t seq_read(struct file *, char __user *, size_t, loff_t *);
 loff_t seq_lseek(struct file *, loff_t, int);
 int seq_release(struct inode *, struct file *);
-int seq_escape(struct seq_file *, const char *, const char *);
-int seq_putc(struct seq_file *m, char c);
-int seq_puts(struct seq_file *m, const char *s);
 int seq_write(struct seq_file *seq, const void *data, size_t len);
 
-__printf(2, 3) int seq_printf(struct seq_file *, const char *, ...);
-__printf(2, 0) int seq_vprintf(struct seq_file *, const char *, va_list args);
+__printf(2, 0)
+void seq_vprintf(struct seq_file *m, const char *fmt, va_list args);
+__printf(2, 3)
+void seq_printf(struct seq_file *m, const char *fmt, ...);
+void seq_putc(struct seq_file *m, char c);
+void seq_puts(struct seq_file *m, const char *s);
+void seq_put_decimal_ull(struct seq_file *m, char delimiter,
+                        unsigned long long num);
+void seq_put_decimal_ll(struct seq_file *m, char delimiter, long long num);
+void seq_escape(struct seq_file *m, const char *s, const char *esc);
 
 void seq_hex_dump(struct seq_file *m, const char *prefix_str, int prefix_type,
                  int rowsize, int groupsize, const void *buf, size_t len,
@@ -138,10 +143,6 @@ int single_release(struct inode *, struct file *);
 void *__seq_open_private(struct file *, const struct seq_operations *, int);
 int seq_open_private(struct file *, const struct seq_operations *, int);
 int seq_release_private(struct inode *, struct file *);
-int seq_put_decimal_ull(struct seq_file *m, char delimiter,
-                       unsigned long long num);
-int seq_put_decimal_ll(struct seq_file *m, char delimiter,
-                       long long num);
 
 static inline struct user_namespace *seq_user_ns(struct seq_file *seq)
 {
index 0800131..a460e2e 100644 (file)
@@ -885,4 +885,6 @@ asmlinkage long sys_execveat(int dfd, const char __user *filename,
                        const char __user *const __user *argv,
                        const char __user *const __user *envp, int flags);
 
+asmlinkage long sys_membarrier(int cmd, int flags);
+
 #endif
index 037e9df..17292fe 100644 (file)
@@ -92,23 +92,19 @@ struct thermal_zone_device_ops {
                     struct thermal_cooling_device *);
        int (*unbind) (struct thermal_zone_device *,
                       struct thermal_cooling_device *);
-       int (*get_temp) (struct thermal_zone_device *, unsigned long *);
+       int (*get_temp) (struct thermal_zone_device *, int *);
        int (*get_mode) (struct thermal_zone_device *,
                         enum thermal_device_mode *);
        int (*set_mode) (struct thermal_zone_device *,
                enum thermal_device_mode);
        int (*get_trip_type) (struct thermal_zone_device *, int,
                enum thermal_trip_type *);
-       int (*get_trip_temp) (struct thermal_zone_device *, int,
-                             unsigned long *);
-       int (*set_trip_temp) (struct thermal_zone_device *, int,
-                             unsigned long);
-       int (*get_trip_hyst) (struct thermal_zone_device *, int,
-                             unsigned long *);
-       int (*set_trip_hyst) (struct thermal_zone_device *, int,
-                             unsigned long);
-       int (*get_crit_temp) (struct thermal_zone_device *, unsigned long *);
-       int (*set_emul_temp) (struct thermal_zone_device *, unsigned long);
+       int (*get_trip_temp) (struct thermal_zone_device *, int, int *);
+       int (*set_trip_temp) (struct thermal_zone_device *, int, int);
+       int (*get_trip_hyst) (struct thermal_zone_device *, int, int *);
+       int (*set_trip_hyst) (struct thermal_zone_device *, int, int);
+       int (*get_crit_temp) (struct thermal_zone_device *, int *);
+       int (*set_emul_temp) (struct thermal_zone_device *, int);
        int (*get_trend) (struct thermal_zone_device *, int,
                          enum thermal_trend *);
        int (*notify) (struct thermal_zone_device *, int,
@@ -332,9 +328,9 @@ struct thermal_genl_event {
  *                temperature.
  */
 struct thermal_zone_of_device_ops {
-       int (*get_temp)(void *, long *);
+       int (*get_temp)(void *, int *);
        int (*get_trend)(void *, long *);
-       int (*set_emul_temp)(void *, unsigned long);
+       int (*set_emul_temp)(void *, int);
 };
 
 /**
@@ -406,7 +402,7 @@ thermal_of_cooling_device_register(struct device_node *np, char *, void *,
                                   const struct thermal_cooling_device_ops *);
 void thermal_cooling_device_unregister(struct thermal_cooling_device *);
 struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
-int thermal_zone_get_temp(struct thermal_zone_device *tz, unsigned long *temp);
+int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
 
 int get_tz_trend(struct thermal_zone_device *, int);
 struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
@@ -457,7 +453,7 @@ static inline struct thermal_zone_device *thermal_zone_get_zone_by_name(
                const char *name)
 { return ERR_PTR(-ENODEV); }
 static inline int thermal_zone_get_temp(
-               struct thermal_zone_device *tz, unsigned long *temp)
+               struct thermal_zone_device *tz, int *temp)
 { return -ENODEV; }
 static inline int get_tz_trend(struct thermal_zone_device *tz, int trip)
 { return -ENODEV; }
index 9f36641..6513c7e 100644 (file)
@@ -15,6 +15,7 @@
 #define _MEDIA_VIDEOBUF2_MEMOPS_H
 
 #include <media/videobuf2-core.h>
+#include <linux/mm.h>
 
 /**
  * struct vb2_vmarea_handler - common vma refcount tracking handler
@@ -31,11 +32,9 @@ struct vb2_vmarea_handler {
 
 extern const struct vm_operations_struct vb2_common_vm_ops;
 
-int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size,
-                          struct vm_area_struct **res_vma, dma_addr_t *res_pa);
-
-struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma);
-void vb2_put_vma(struct vm_area_struct *vma);
-
+struct frame_vector *vb2_create_framevec(unsigned long start,
+                                        unsigned long length,
+                                        bool write);
+void vb2_destroy_framevec(struct frame_vector *vec);
 
 #endif
index 676b03b..11571b2 100644 (file)
@@ -61,4 +61,9 @@ static inline bool scsi_sense_valid(const struct scsi_sense_hdr *sshdr)
 extern bool scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
                                 struct scsi_sense_hdr *sshdr);
 
+extern void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq);
+int scsi_set_sense_information(u8 *buf, int buf_len, u64 info);
+extern const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len,
+                                      int desc_type);
+
 #endif /* _SCSI_COMMON_H_ */
index 50c2a36..fe89d7c 100644 (file)
@@ -196,34 +196,13 @@ struct scsi_device {
        struct execute_work     ew; /* used to get process context on put */
        struct work_struct      requeue_work;
 
-       struct scsi_dh_data     *scsi_dh_data;
+       struct scsi_device_handler *handler;
+       void                    *handler_data;
+
        enum scsi_device_state sdev_state;
        unsigned long           sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
 
-typedef void (*activate_complete)(void *, int);
-struct scsi_device_handler {
-       /* Used by the infrastructure */
-       struct list_head list; /* list of scsi_device_handlers */
-
-       /* Filled by the hardware handler */
-       struct module *module;
-       const char *name;
-       int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *);
-       struct scsi_dh_data *(*attach)(struct scsi_device *);
-       void (*detach)(struct scsi_device *);
-       int (*activate)(struct scsi_device *, activate_complete, void *);
-       int (*prep_fn)(struct scsi_device *, struct request *);
-       int (*set_params)(struct scsi_device *, const char *);
-       bool (*match)(struct scsi_device *);
-};
-
-struct scsi_dh_data {
-       struct scsi_device_handler *scsi_dh;
-       struct scsi_device *sdev;
-       struct kref kref;
-};
-
 #define        to_scsi_device(d)       \
        container_of(d, struct scsi_device, sdev_gendev)
 #define        class_to_sdev(d)        \
index 620c723..85d7317 100644 (file)
@@ -55,11 +55,26 @@ enum {
        SCSI_DH_NOSYS,
        SCSI_DH_DRIVER_MAX,
 };
-#if defined(CONFIG_SCSI_DH) || defined(CONFIG_SCSI_DH_MODULE)
+
+typedef void (*activate_complete)(void *, int);
+struct scsi_device_handler {
+       /* Used by the infrastructure */
+       struct list_head list; /* list of scsi_device_handlers */
+
+       /* Filled by the hardware handler */
+       struct module *module;
+       const char *name;
+       int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *);
+       int (*attach)(struct scsi_device *);
+       void (*detach)(struct scsi_device *);
+       int (*activate)(struct scsi_device *, activate_complete, void *);
+       int (*prep_fn)(struct scsi_device *, struct request *);
+       int (*set_params)(struct scsi_device *, const char *);
+};
+
+#ifdef CONFIG_SCSI_DH
 extern int scsi_dh_activate(struct request_queue *, activate_complete, void *);
-extern int scsi_dh_handler_exist(const char *);
 extern int scsi_dh_attach(struct request_queue *, const char *);
-extern void scsi_dh_detach(struct request_queue *);
 extern const char *scsi_dh_attached_handler_name(struct request_queue *, gfp_t);
 extern int scsi_dh_set_params(struct request_queue *, const char *);
 #else
@@ -69,18 +84,10 @@ static inline int scsi_dh_activate(struct request_queue *req,
        fn(data, 0);
        return 0;
 }
-static inline int scsi_dh_handler_exist(const char *name)
-{
-       return 0;
-}
 static inline int scsi_dh_attach(struct request_queue *req, const char *name)
 {
        return SCSI_DH_NOSYS;
 }
-static inline void scsi_dh_detach(struct request_queue *q)
-{
-       return;
-}
 static inline const char *scsi_dh_attached_handler_name(struct request_queue *q,
                                                        gfp_t gfp)
 {
index 8d1d7fa..dbb8c64 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/scatterlist.h>
 
 #include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_common.h>
 struct scsi_device;
 struct Scsi_Host;
 
@@ -21,14 +22,9 @@ static inline bool scsi_sense_is_deferred(const struct scsi_sense_hdr *sshdr)
        return ((sshdr->response_code >= 0x70) && (sshdr->response_code & 1));
 }
 
-extern const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len,
-                                      int desc_type);
-
 extern int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len,
                                   u64 * info_out);
 
-extern void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq);
-
 extern int scsi_ioctl_reset(struct scsi_device *, int __user *);
 
 struct scsi_eh_save {
index 0aedbb2..373d334 100644 (file)
@@ -62,6 +62,8 @@
 /* T10 protection information disabled by default */
 #define TA_DEFAULT_T10_PI              0
 #define TA_DEFAULT_FABRIC_PROT_TYPE    0
+/* TPG status needs to be enabled to return sendtargets discovery endpoint info */
+#define TA_DEFAULT_TPG_ENABLED_SENDTARGETS 1
 
 #define ISCSI_IOV_DATA_BUFFER          5
 
@@ -517,7 +519,6 @@ struct iscsi_conn {
        u16                     cid;
        /* Remote TCP Port */
        u16                     login_port;
-       u16                     local_port;
        int                     net_size;
        int                     login_family;
        u32                     auth_id;
@@ -527,9 +528,8 @@ struct iscsi_conn {
        u32                     exp_statsn;
        /* Per connection status sequence number */
        u32                     stat_sn;
-#define IPV6_ADDRESS_SPACE                             48
-       unsigned char           login_ip[IPV6_ADDRESS_SPACE];
-       unsigned char           local_ip[IPV6_ADDRESS_SPACE];
+       struct sockaddr_storage login_sockaddr;
+       struct sockaddr_storage local_sockaddr;
        int                     conn_usage_count;
        int                     conn_waiting_on_uc;
        atomic_t                check_immediate_queue;
@@ -636,7 +636,7 @@ struct iscsi_session {
        /* session wide counter: expected command sequence number */
        u32                     exp_cmd_sn;
        /* session wide counter: maximum allowed command sequence number */
-       u32                     max_cmd_sn;
+       atomic_t                max_cmd_sn;
        struct list_head        sess_ooo_cmdsn_list;
 
        /* LIO specific session ID */
@@ -764,6 +764,7 @@ struct iscsi_tpg_attrib {
        u32                     default_erl;
        u8                      t10_pi;
        u32                     fabric_prot_type;
+       u32                     tpg_enabled_sendtargets;
        struct iscsi_portal_group *tpg;
 };
 
@@ -776,12 +777,10 @@ struct iscsi_np {
        enum iscsi_timer_flags_table np_login_timer_flags;
        u32                     np_exports;
        enum np_flags_table     np_flags;
-       unsigned char           np_ip[IPV6_ADDRESS_SPACE];
-       u16                     np_port;
        spinlock_t              np_thread_lock;
        struct completion       np_restart_comp;
        struct socket           *np_socket;
-       struct __kernel_sockaddr_storage np_sockaddr;
+       struct sockaddr_storage np_sockaddr;
        struct task_struct      *np_thread;
        struct timer_list       np_login_timer;
        void                    *np_context;
index 3ff76b4..e615bb4 100644 (file)
@@ -50,7 +50,7 @@ struct iscsi_login_stats {
        u64             last_fail_time;         /* time stamp (jiffies) */
        u32             last_fail_type;
        int             last_intr_fail_ip_family;
-       unsigned char   last_intr_fail_ip_addr[IPV6_ADDRESS_SPACE];
+       struct sockaddr_storage last_intr_fail_sockaddr;
        char            last_intr_fail_name[224];
 } ____cacheline_aligned;
 
index e6bb166..90e37fa 100644 (file)
@@ -9,7 +9,7 @@ struct iscsit_transport {
        int priv_size;
        struct module *owner;
        struct list_head t_node;
-       int (*iscsit_setup_np)(struct iscsi_np *, struct __kernel_sockaddr_storage *);
+       int (*iscsit_setup_np)(struct iscsi_np *, struct sockaddr_storage *);
        int (*iscsit_accept_np)(struct iscsi_np *, struct iscsi_conn *);
        void (*iscsit_free_np)(struct iscsi_np *);
        void (*iscsit_wait_conn)(struct iscsi_conn *);
index 1e5c8f9..56cf8e4 100644 (file)
@@ -93,4 +93,6 @@ bool  target_lun_is_rdonly(struct se_cmd *);
 sense_reason_t passthrough_parse_cdb(struct se_cmd *cmd,
        sense_reason_t (*exec_cmd)(struct se_cmd *cmd));
 
+bool target_sense_desc_format(struct se_device *dev);
+
 #endif /* TARGET_CORE_BACKEND_H */
index 17ae2d6..ac9bf1c 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/blkdev.h>
 #include <linux/percpu_ida.h>
+#include <linux/t10-pi.h>
 #include <net/sock.h>
 #include <net/tcp.h>
 
@@ -426,12 +427,6 @@ enum target_core_dif_check {
        TARGET_DIF_CHECK_REFTAG = 0x1 << 2,
 };
 
-struct se_dif_v1_tuple {
-       __be16                  guard_tag;
-       __be16                  app_tag;
-       __be32                  ref_tag;
-};
-
 /* for sam_task_attr */
 #define TCM_SIMPLE_TAG 0x20
 #define TCM_HEAD_TAG   0x21
@@ -444,6 +439,9 @@ struct se_cmd {
        u8                      scsi_asc;
        u8                      scsi_ascq;
        u16                     scsi_sense_length;
+       unsigned                cmd_wait_set:1;
+       unsigned                unknown_data_length:1;
+       bool                    state_active:1;
        u64                     tag; /* SAM command identifier aka task tag */
        /* Delay for ALUA Active/NonOptimized state access in milliseconds */
        int                     alua_nonop_delay;
@@ -455,11 +453,8 @@ struct se_cmd {
        unsigned int            map_tag;
        /* Transport protocol dependent state, see transport_state_table */
        enum transport_state_table t_state;
-       unsigned                cmd_wait_set:1;
-       unsigned                unknown_data_length:1;
        /* See se_cmd_flags_table */
        u32                     se_cmd_flags;
-       u32                     se_ordered_id;
        /* Total size in bytes associated with command */
        u32                     data_length;
        u32                     residual_count;
@@ -477,7 +472,6 @@ struct se_cmd {
        struct se_tmr_req       *se_tmr_req;
        struct list_head        se_cmd_list;
        struct completion       cmd_wait_comp;
-       struct kref             cmd_kref;
        const struct target_core_fabric_ops *se_tfo;
        sense_reason_t          (*execute_cmd)(struct se_cmd *);
        sense_reason_t (*transport_complete_callback)(struct se_cmd *, bool);
@@ -497,6 +491,7 @@ struct se_cmd {
 #define CMD_T_REQUEST_STOP     (1 << 8)
 #define CMD_T_BUSY             (1 << 9)
        spinlock_t              t_state_lock;
+       struct kref             cmd_kref;
        struct completion       t_transport_stop_comp;
 
        struct work_struct      work;
@@ -509,8 +504,10 @@ struct se_cmd {
        struct scatterlist      *t_bidi_data_sg;
        unsigned int            t_bidi_data_nents;
 
+       /* Used for lun->lun_ref counting */
+       int                     lun_ref_active;
+
        struct list_head        state_list;
-       bool                    state_active;
 
        /* old task stop completion, consider merging with some of the above */
        struct completion       task_stop_comp;
@@ -518,20 +515,17 @@ struct se_cmd {
        /* backend private data */
        void                    *priv;
 
-       /* Used for lun->lun_ref counting */
-       int                     lun_ref_active;
-
        /* DIF related members */
        enum target_prot_op     prot_op;
        enum target_prot_type   prot_type;
        u8                      prot_checks;
+       bool                    prot_pto;
        u32                     prot_length;
        u32                     reftag_seed;
        struct scatterlist      *t_prot_sg;
        unsigned int            t_prot_nents;
        sense_reason_t          pi_err;
        sector_t                bad_sector;
-       bool                    prot_pto;
 };
 
 struct se_ua {
@@ -598,7 +592,6 @@ struct se_ml_stat_grps {
 };
 
 struct se_lun_acl {
-       char                    initiatorname[TRANSPORT_IQN_LEN];
        u64                     mapped_lun;
        struct se_node_acl      *se_lun_nacl;
        struct se_lun           *se_lun;
@@ -685,7 +678,6 @@ struct se_lun {
 #define SE_LUN_LINK_MAGIC                      0xffff7771
        u32                     lun_link_magic;
        u32                     lun_access;
-       u32                     lun_flags;
        u32                     lun_index;
 
        /* RELATIVE TARGET PORT IDENTIFER */
@@ -751,7 +743,6 @@ struct se_device {
        atomic_long_t           write_bytes;
        /* Active commands on this virtual SE device */
        atomic_t                simple_cmds;
-       atomic_t                dev_ordered_id;
        atomic_t                dev_ordered_sync;
        atomic_t                dev_qf_count;
        u32                     export_count;
index 18afef9..7fb2557 100644 (file)
@@ -5,6 +5,19 @@ struct target_core_fabric_ops {
        struct module *module;
        const char *name;
        size_t node_acl_size;
+       /*
+        * Limits number of scatterlist entries per SCF_SCSI_DATA_CDB payload.
+        * Setting this value tells target-core to enforce this limit, and
+        * report as INQUIRY EVPD=b0 MAXIMUM TRANSFER LENGTH.
+        *
+        * target-core will currently reset se_cmd->data_length to this
+        * maximum size, and set UNDERFLOW residual count if length exceeds
+        * this limit.
+        *
+        * XXX: Not all initiator hosts honor this block-limit EVPD
+        * XXX: Currently assumes single PAGE_SIZE per scatterlist entry
+        */
+       u32 max_data_sg_nents;
        char *(*get_fabric_name)(void);
        char *(*tpg_get_wwn)(struct se_portal_group *);
        u16 (*tpg_get_tag)(struct se_portal_group *);
@@ -152,6 +165,7 @@ int transport_generic_handle_tmr(struct se_cmd *);
 void   transport_generic_request_failure(struct se_cmd *, sense_reason_t);
 void   __target_execute_cmd(struct se_cmd *);
 int    transport_lookup_tmr_lun(struct se_cmd *, u64);
+void   core_allocate_nexus_loss_ua(struct se_node_acl *acl);
 
 struct se_node_acl *core_tpg_get_initiator_node_acl(struct se_portal_group *tpg,
                unsigned char *);
index 12e1321..5afae8f 100644 (file)
@@ -11,7 +11,7 @@ TRACE_EVENT(thermal_power_allocator,
                 u32 total_req_power, u32 *granted_power,
                 u32 total_granted_power, size_t num_actors,
                 u32 power_range, u32 max_allocatable_power,
-                unsigned long current_temp, s32 delta_temp),
+                int current_temp, s32 delta_temp),
        TP_ARGS(tz, req_power, total_req_power, granted_power,
                total_granted_power, num_actors, power_range,
                max_allocatable_power, current_temp, delta_temp),
@@ -24,7 +24,7 @@ TRACE_EVENT(thermal_power_allocator,
                __field(size_t,        num_actors               )
                __field(u32,           power_range              )
                __field(u32,           max_allocatable_power    )
-               __field(unsigned long, current_temp             )
+               __field(int,           current_temp             )
                __field(s32,           delta_temp               )
        ),
        TP_fast_assign(
@@ -42,7 +42,7 @@ TRACE_EVENT(thermal_power_allocator,
                __entry->delta_temp = delta_temp;
        ),
 
-       TP_printk("thermal_zone_id=%d req_power={%s} total_req_power=%u granted_power={%s} total_granted_power=%u power_range=%u max_allocatable_power=%u current_temperature=%lu delta_temperature=%d",
+       TP_printk("thermal_zone_id=%d req_power={%s} total_req_power=%u granted_power={%s} total_granted_power=%u power_range=%u max_allocatable_power=%u current_temperature=%d delta_temperature=%d",
                __entry->tz_id,
                __print_array(__get_dynamic_array(req_power),
                               __entry->num_actors, 4),
index e016bd9..8da542a 100644 (file)
@@ -709,9 +709,11 @@ __SYSCALL(__NR_memfd_create, sys_memfd_create)
 __SYSCALL(__NR_bpf, sys_bpf)
 #define __NR_execveat 281
 __SC_COMP(__NR_execveat, sys_execveat, compat_sys_execveat)
+#define __NR_membarrier 282
+__SYSCALL(__NR_membarrier, sys_membarrier)
 
 #undef __NR_syscalls
-#define __NR_syscalls 282
+#define __NR_syscalls 283
 
 /*
  * All syscalls below here should go away really,
index 70ff1d9..f7b2db4 100644 (file)
@@ -252,6 +252,7 @@ header-y += mdio.h
 header-y += media.h
 header-y += media-bus-format.h
 header-y += mei.h
+header-y += membarrier.h
 header-y += memfd.h
 header-y += mempolicy.h
 header-y += meye.h
diff --git a/include/uapi/linux/membarrier.h b/include/uapi/linux/membarrier.h
new file mode 100644 (file)
index 0000000..e0b108b
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef _UAPI_LINUX_MEMBARRIER_H
+#define _UAPI_LINUX_MEMBARRIER_H
+
+/*
+ * linux/membarrier.h
+ *
+ * membarrier system call API
+ *
+ * Copyright (c) 2010, 2015 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/**
+ * enum membarrier_cmd - membarrier system call command
+ * @MEMBARRIER_CMD_QUERY:   Query the set of supported commands. It returns
+ *                          a bitmask of valid commands.
+ * @MEMBARRIER_CMD_SHARED:  Execute a memory barrier on all running threads.
+ *                          Upon return from system call, the caller thread
+ *                          is ensured that all running threads have passed
+ *                          through a state where all memory accesses to
+ *                          user-space addresses match program order between
+ *                          entry to and return from the system call
+ *                          (non-running threads are de facto in such a
+ *                          state). This covers threads from all processes
+ *                          running on the system. This command returns 0.
+ *
+ * Command to be passed to the membarrier system call. The commands need to
+ * be a single bit each, except for MEMBARRIER_CMD_QUERY which is assigned to
+ * the value 0.
+ */
+enum membarrier_cmd {
+       MEMBARRIER_CMD_QUERY = 0,
+       MEMBARRIER_CMD_SHARED = (1 << 0),
+};
+
+#endif /* _UAPI_LINUX_MEMBARRIER_H */
index b67f99d..95c6521 100644 (file)
 #define TCMU_MAILBOX_VERSION 2
 #define ALIGN_SIZE 64 /* Should be enough for most CPUs */
 
-/* See https://gcc.gnu.org/onlinedocs/cpp/Stringification.html */
-#define xstr(s) str(s)
-#define str(s) #s
-
 struct tcmu_mailbox {
        __u16 version;
        __u16 flags;
index 02da9f1..c24b6f7 100644 (file)
@@ -1602,6 +1602,18 @@ config PCI_QUIRKS
          bugs/quirks. Disable this only if your target machine is
          unaffected by PCI quirks.
 
+config MEMBARRIER
+       bool "Enable membarrier() system call" if EXPERT
+       default y
+       help
+         Enable the membarrier() system call that allows issuing memory
+         barriers across all running threads, which can be used to distribute
+         the cost of user-space memory barriers asymmetrically by transforming
+         pairs of memory barriers into pairs consisting of membarrier() and a
+         compiler barrier.
+
+         If unsure, say Y.
+
 config EMBEDDED
        bool "Embedded system"
        option allnoconfig_y
index d498841..53abf00 100644 (file)
@@ -100,6 +100,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
 obj-$(CONFIG_JUMP_LABEL) += jump_label.o
 obj-$(CONFIG_CONTEXT_TRACKING) += context_tracking.o
 obj-$(CONFIG_TORTURE_TEST) += torture.o
+obj-$(CONFIG_MEMBARRIER) += membarrier.o
 
 obj-$(CONFIG_HAS_IOMEM) += memremap.o
 
index 9656a3c..009cc9a 100644 (file)
@@ -180,7 +180,7 @@ EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
  * low power state that may have caused some blocks in the same power domain
  * to reset.
  *
- * Must be called after cpu_pm_exit has been called on all cpus in the power
+ * Must be called after cpu_cluster_pm_enter has been called for the power
  * domain, and before cpu_pm_exit has been called on any cpu in the power
  * domain. Notified drivers can include VFP co-processor, interrupt controller
  * and its PM extensions, local CPU timers context save/restore which
diff --git a/kernel/membarrier.c b/kernel/membarrier.c
new file mode 100644 (file)
index 0000000..536c727
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010, 2015 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * membarrier system call
+ *
+ * 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/syscalls.h>
+#include <linux/membarrier.h>
+
+/*
+ * Bitmask made from a "or" of all commands within enum membarrier_cmd,
+ * except MEMBARRIER_CMD_QUERY.
+ */
+#define MEMBARRIER_CMD_BITMASK (MEMBARRIER_CMD_SHARED)
+
+/**
+ * sys_membarrier - issue memory barriers on a set of threads
+ * @cmd:   Takes command values defined in enum membarrier_cmd.
+ * @flags: Currently needs to be 0. For future extensions.
+ *
+ * If this system call is not implemented, -ENOSYS is returned. If the
+ * command specified does not exist, or if the command argument is invalid,
+ * this system call returns -EINVAL. For a given command, with flags argument
+ * set to 0, this system call is guaranteed to always return the same value
+ * until reboot.
+ *
+ * All memory accesses performed in program order from each targeted thread
+ * is guaranteed to be ordered with respect to sys_membarrier(). If we use
+ * the semantic "barrier()" to represent a compiler barrier forcing memory
+ * accesses to be performed in program order across the barrier, and
+ * smp_mb() to represent explicit memory barriers forcing full memory
+ * ordering across the barrier, we have the following ordering table for
+ * each pair of barrier(), sys_membarrier() and smp_mb():
+ *
+ * The pair ordering is detailed as (O: ordered, X: not ordered):
+ *
+ *                        barrier()   smp_mb() sys_membarrier()
+ *        barrier()          X           X            O
+ *        smp_mb()           X           O            O
+ *        sys_membarrier()   O           O            O
+ */
+SYSCALL_DEFINE2(membarrier, int, cmd, int, flags)
+{
+       if (unlikely(flags))
+               return -EINVAL;
+       switch (cmd) {
+       case MEMBARRIER_CMD_QUERY:
+               return MEMBARRIER_CMD_BITMASK;
+       case MEMBARRIER_CMD_SHARED:
+               if (num_online_cpus() > 1)
+                       synchronize_sched();
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
index 03c3875..a02decf 100644 (file)
@@ -245,3 +245,6 @@ cond_syscall(sys_bpf);
 
 /* execveat */
 cond_syscall(sys_execveat);
+
+/* membarrier */
+cond_syscall(sys_membarrier);
index 6413d02..0d9fdcd 100644 (file)
@@ -677,3 +677,6 @@ config ZONE_DEVICE
          mapping in an O_DIRECT operation, among other things.
 
          If FS_DAX is enabled, then say Y.
+
+config FRAME_VECTOR
+       bool
index 56f8eed..2ed4319 100644 (file)
@@ -80,3 +80,4 @@ obj-$(CONFIG_PAGE_EXTENSION) += page_ext.o
 obj-$(CONFIG_CMA_DEBUGFS) += cma_debug.o
 obj-$(CONFIG_USERFAULTFD) += userfaultfd.o
 obj-$(CONFIG_IDLE_PAGE_TRACKING) += page_idle.o
+obj-$(CONFIG_FRAME_VECTOR) += frame_vector.o
index 23f744d..17ae14b 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
 #include <asm/fixmap.h>
+#include <asm/early_ioremap.h>
 
 #ifdef CONFIG_MMU
 static int early_ioremap_debug __initdata;
diff --git a/mm/frame_vector.c b/mm/frame_vector.c
new file mode 100644 (file)
index 0000000..cdabcb9
--- /dev/null
@@ -0,0 +1,230 @@
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/sched.h>
+
+/*
+ * get_vaddr_frames() - map virtual addresses to pfns
+ * @start:     starting user address
+ * @nr_frames: number of pages / pfns from start to map
+ * @write:     whether pages will be written to by the caller
+ * @force:     whether to force write access even if user mapping is
+ *             readonly. See description of the same argument of
+               get_user_pages().
+ * @vec:       structure which receives pages / pfns of the addresses mapped.
+ *             It should have space for at least nr_frames entries.
+ *
+ * This function maps virtual addresses from @start and fills @vec structure
+ * with page frame numbers or page pointers to corresponding pages (choice
+ * depends on the type of the vma underlying the virtual address). If @start
+ * belongs to a normal vma, the function grabs reference to each of the pages
+ * to pin them in memory. If @start belongs to VM_IO | VM_PFNMAP vma, we don't
+ * touch page structures and the caller must make sure pfns aren't reused for
+ * anything else while he is using them.
+ *
+ * The function returns number of pages mapped which may be less than
+ * @nr_frames. In particular we stop mapping if there are more vmas of
+ * different type underlying the specified range of virtual addresses.
+ * When the function isn't able to map a single page, it returns error.
+ *
+ * This function takes care of grabbing mmap_sem as necessary.
+ */
+int get_vaddr_frames(unsigned long start, unsigned int nr_frames,
+                    bool write, bool force, struct frame_vector *vec)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       int ret = 0;
+       int err;
+       int locked;
+
+       if (nr_frames == 0)
+               return 0;
+
+       if (WARN_ON_ONCE(nr_frames > vec->nr_allocated))
+               nr_frames = vec->nr_allocated;
+
+       down_read(&mm->mmap_sem);
+       locked = 1;
+       vma = find_vma_intersection(mm, start, start + 1);
+       if (!vma) {
+               ret = -EFAULT;
+               goto out;
+       }
+       if (!(vma->vm_flags & (VM_IO | VM_PFNMAP))) {
+               vec->got_ref = true;
+               vec->is_pfns = false;
+               ret = get_user_pages_locked(current, mm, start, nr_frames,
+                       write, force, (struct page **)(vec->ptrs), &locked);
+               goto out;
+       }
+
+       vec->got_ref = false;
+       vec->is_pfns = true;
+       do {
+               unsigned long *nums = frame_vector_pfns(vec);
+
+               while (ret < nr_frames && start + PAGE_SIZE <= vma->vm_end) {
+                       err = follow_pfn(vma, start, &nums[ret]);
+                       if (err) {
+                               if (ret == 0)
+                                       ret = err;
+                               goto out;
+                       }
+                       start += PAGE_SIZE;
+                       ret++;
+               }
+               /*
+                * We stop if we have enough pages or if VMA doesn't completely
+                * cover the tail page.
+                */
+               if (ret >= nr_frames || start < vma->vm_end)
+                       break;
+               vma = find_vma_intersection(mm, start, start + 1);
+       } while (vma && vma->vm_flags & (VM_IO | VM_PFNMAP));
+out:
+       if (locked)
+               up_read(&mm->mmap_sem);
+       if (!ret)
+               ret = -EFAULT;
+       if (ret > 0)
+               vec->nr_frames = ret;
+       return ret;
+}
+EXPORT_SYMBOL(get_vaddr_frames);
+
+/**
+ * put_vaddr_frames() - drop references to pages if get_vaddr_frames() acquired
+ *                     them
+ * @vec:       frame vector to put
+ *
+ * Drop references to pages if get_vaddr_frames() acquired them. We also
+ * invalidate the frame vector so that it is prepared for the next call into
+ * get_vaddr_frames().
+ */
+void put_vaddr_frames(struct frame_vector *vec)
+{
+       int i;
+       struct page **pages;
+
+       if (!vec->got_ref)
+               goto out;
+       pages = frame_vector_pages(vec);
+       /*
+        * frame_vector_pages() might needed to do a conversion when
+        * get_vaddr_frames() got pages but vec was later converted to pfns.
+        * But it shouldn't really fail to convert pfns back...
+        */
+       if (WARN_ON(IS_ERR(pages)))
+               goto out;
+       for (i = 0; i < vec->nr_frames; i++)
+               put_page(pages[i]);
+       vec->got_ref = false;
+out:
+       vec->nr_frames = 0;
+}
+EXPORT_SYMBOL(put_vaddr_frames);
+
+/**
+ * frame_vector_to_pages - convert frame vector to contain page pointers
+ * @vec:       frame vector to convert
+ *
+ * Convert @vec to contain array of page pointers.  If the conversion is
+ * successful, return 0. Otherwise return an error. Note that we do not grab
+ * page references for the page structures.
+ */
+int frame_vector_to_pages(struct frame_vector *vec)
+{
+       int i;
+       unsigned long *nums;
+       struct page **pages;
+
+       if (!vec->is_pfns)
+               return 0;
+       nums = frame_vector_pfns(vec);
+       for (i = 0; i < vec->nr_frames; i++)
+               if (!pfn_valid(nums[i]))
+                       return -EINVAL;
+       pages = (struct page **)nums;
+       for (i = 0; i < vec->nr_frames; i++)
+               pages[i] = pfn_to_page(nums[i]);
+       vec->is_pfns = false;
+       return 0;
+}
+EXPORT_SYMBOL(frame_vector_to_pages);
+
+/**
+ * frame_vector_to_pfns - convert frame vector to contain pfns
+ * @vec:       frame vector to convert
+ *
+ * Convert @vec to contain array of pfns.
+ */
+void frame_vector_to_pfns(struct frame_vector *vec)
+{
+       int i;
+       unsigned long *nums;
+       struct page **pages;
+
+       if (vec->is_pfns)
+               return;
+       pages = (struct page **)(vec->ptrs);
+       nums = (unsigned long *)pages;
+       for (i = 0; i < vec->nr_frames; i++)
+               nums[i] = page_to_pfn(pages[i]);
+       vec->is_pfns = true;
+}
+EXPORT_SYMBOL(frame_vector_to_pfns);
+
+/**
+ * frame_vector_create() - allocate & initialize structure for pinned pfns
+ * @nr_frames: number of pfns slots we should reserve
+ *
+ * Allocate and initialize struct pinned_pfns to be able to hold @nr_pfns
+ * pfns.
+ */
+struct frame_vector *frame_vector_create(unsigned int nr_frames)
+{
+       struct frame_vector *vec;
+       int size = sizeof(struct frame_vector) + sizeof(void *) * nr_frames;
+
+       if (WARN_ON_ONCE(nr_frames == 0))
+               return NULL;
+       /*
+        * This is absurdly high. It's here just to avoid strange effects when
+        * arithmetics overflows.
+        */
+       if (WARN_ON_ONCE(nr_frames > INT_MAX / sizeof(void *) / 2))
+               return NULL;
+       /*
+        * Avoid higher order allocations, use vmalloc instead. It should
+        * be rare anyway.
+        */
+       if (size <= PAGE_SIZE)
+               vec = kmalloc(size, GFP_KERNEL);
+       else
+               vec = vmalloc(size);
+       if (!vec)
+               return NULL;
+       vec->nr_allocated = nr_frames;
+       vec->nr_frames = 0;
+       return vec;
+}
+EXPORT_SYMBOL(frame_vector_create);
+
+/**
+ * frame_vector_destroy() - free memory allocated to carry frame vector
+ * @vec:       Frame vector to free
+ *
+ * Free structure allocated by frame_vector_create() to carry frames.
+ */
+void frame_vector_destroy(struct frame_vector *vec)
+{
+       /* Make sure put_vaddr_frames() got called properly... */
+       VM_BUG_ON(vec->nr_frames > 0);
+       kvfree(vec);
+}
+EXPORT_SYMBOL(frame_vector_destroy);
index fd0db01..10d23ca 100644 (file)
@@ -86,7 +86,7 @@ static void write_cert(X509 *x509)
                ERR(!wb, "%s", cert_dst);
        }
        X509_NAME_oneline(X509_get_subject_name(x509), buf, sizeof(buf));
-       ERR(!i2d_X509_bio(wb, x509), cert_dst);
+       ERR(!i2d_X509_bio(wb, x509), "%s", cert_dst);
        if (kbuild_verbose)
                fprintf(stderr, "Extracted cert: %s\n", buf);
 }
index 0501511..89b05e2 100644 (file)
@@ -6,6 +6,7 @@ TARGETS += firmware
 TARGETS += ftrace
 TARGETS += futex
 TARGETS += kcmp
+TARGETS += membarrier
 TARGETS += memfd
 TARGETS += memory-hotplug
 TARGETS += mount
diff --git a/tools/testing/selftests/membarrier/.gitignore b/tools/testing/selftests/membarrier/.gitignore
new file mode 100644 (file)
index 0000000..020c44f
--- /dev/null
@@ -0,0 +1 @@
+membarrier_test
diff --git a/tools/testing/selftests/membarrier/Makefile b/tools/testing/selftests/membarrier/Makefile
new file mode 100644 (file)
index 0000000..877a503
--- /dev/null
@@ -0,0 +1,11 @@
+CFLAGS += -g -I../../../../usr/include/
+
+all:
+       $(CC) $(CFLAGS) membarrier_test.c -o membarrier_test
+
+TEST_PROGS := membarrier_test
+
+include ../lib.mk
+
+clean:
+       $(RM) membarrier_test
diff --git a/tools/testing/selftests/membarrier/membarrier_test.c b/tools/testing/selftests/membarrier/membarrier_test.c
new file mode 100644 (file)
index 0000000..dde3125
--- /dev/null
@@ -0,0 +1,121 @@
+#define _GNU_SOURCE
+#define __EXPORTED_HEADERS__
+
+#include <linux/membarrier.h>
+#include <asm-generic/unistd.h>
+#include <sys/syscall.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "../kselftest.h"
+
+enum test_membarrier_status {
+       TEST_MEMBARRIER_PASS = 0,
+       TEST_MEMBARRIER_FAIL,
+       TEST_MEMBARRIER_SKIP,
+};
+
+static int sys_membarrier(int cmd, int flags)
+{
+       return syscall(__NR_membarrier, cmd, flags);
+}
+
+static enum test_membarrier_status test_membarrier_cmd_fail(void)
+{
+       int cmd = -1, flags = 0;
+
+       if (sys_membarrier(cmd, flags) != -1) {
+               printf("membarrier: Wrong command should fail but passed.\n");
+               return TEST_MEMBARRIER_FAIL;
+       }
+       return TEST_MEMBARRIER_PASS;
+}
+
+static enum test_membarrier_status test_membarrier_flags_fail(void)
+{
+       int cmd = MEMBARRIER_CMD_QUERY, flags = 1;
+
+       if (sys_membarrier(cmd, flags) != -1) {
+               printf("membarrier: Wrong flags should fail but passed.\n");
+               return TEST_MEMBARRIER_FAIL;
+       }
+       return TEST_MEMBARRIER_PASS;
+}
+
+static enum test_membarrier_status test_membarrier_success(void)
+{
+       int cmd = MEMBARRIER_CMD_SHARED, flags = 0;
+
+       if (sys_membarrier(cmd, flags) != 0) {
+               printf("membarrier: Executing MEMBARRIER_CMD_SHARED failed. %s.\n",
+                               strerror(errno));
+               return TEST_MEMBARRIER_FAIL;
+       }
+
+       printf("membarrier: MEMBARRIER_CMD_SHARED success.\n");
+       return TEST_MEMBARRIER_PASS;
+}
+
+static enum test_membarrier_status test_membarrier(void)
+{
+       enum test_membarrier_status status;
+
+       status = test_membarrier_cmd_fail();
+       if (status)
+               return status;
+       status = test_membarrier_flags_fail();
+       if (status)
+               return status;
+       status = test_membarrier_success();
+       if (status)
+               return status;
+       return TEST_MEMBARRIER_PASS;
+}
+
+static enum test_membarrier_status test_membarrier_query(void)
+{
+       int flags = 0, ret;
+
+       printf("membarrier MEMBARRIER_CMD_QUERY ");
+       ret = sys_membarrier(MEMBARRIER_CMD_QUERY, flags);
+       if (ret < 0) {
+               printf("failed. %s.\n", strerror(errno));
+               switch (errno) {
+               case ENOSYS:
+                       /*
+                        * It is valid to build a kernel with
+                        * CONFIG_MEMBARRIER=n. However, this skips the tests.
+                        */
+                       return TEST_MEMBARRIER_SKIP;
+               case EINVAL:
+               default:
+                       return TEST_MEMBARRIER_FAIL;
+               }
+       }
+       if (!(ret & MEMBARRIER_CMD_SHARED)) {
+               printf("command MEMBARRIER_CMD_SHARED is not supported.\n");
+               return TEST_MEMBARRIER_FAIL;
+       }
+       printf("syscall available.\n");
+       return TEST_MEMBARRIER_PASS;
+}
+
+int main(int argc, char **argv)
+{
+       switch (test_membarrier_query()) {
+       case TEST_MEMBARRIER_FAIL:
+               return ksft_exit_fail();
+       case TEST_MEMBARRIER_SKIP:
+               return ksft_exit_skip();
+       }
+       switch (test_membarrier()) {
+       case TEST_MEMBARRIER_FAIL:
+               return ksft_exit_fail();
+       case TEST_MEMBARRIER_SKIP:
+               return ksft_exit_skip();
+       }
+
+       printf("membarrier: tests done!\n");
+       return ksft_exit_pass();
+}