Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf
authorDavid S. Miller <davem@davemloft.net>
Thu, 23 Nov 2017 17:33:01 +0000 (02:33 +0900)
committerDavid S. Miller <davem@davemloft.net>
Thu, 23 Nov 2017 17:33:01 +0000 (02:33 +0900)
Daniel Borkmann says:

====================
pull-request: bpf 2017-11-23

The following pull-request contains BPF updates for your *net* tree.

The main changes are:

1) Several BPF offloading fixes, from Jakub. Among others:

    - Limit offload to cls_bpf and XDP program types only.
    - Move device validation into the driver and don't make
      any assumptions about the device in the classifier due
      to shared blocks semantics.
    - Don't pass offloaded XDP program into the driver when
      it should be run in native XDP instead. Offloaded ones
      are not JITed for the host in such cases.
    - Don't destroy device offload state when moved to
      another namespace.
    - Revert dumping offload info into user space for now,
      since ifindex alone is not sufficient. This will be
      redone properly for bpf-next tree.

2) Fix test_verifier to avoid using bpf_probe_write_user()
   helper in test cases, since it's dumping a warning into
   kernel log which may confuse users when only running tests.
   Switch to use bpf_trace_printk() instead, from Yonghong.

3) Several fixes for correcting ARG_CONST_SIZE_OR_ZERO semantics
   before it becomes uabi, from Gianluca. More specifically:

    - Add a type ARG_PTR_TO_MEM_OR_NULL that is used only
      by bpf_csum_diff(), where the argument is either a
      valid pointer or NULL. The subsequent ARG_CONST_SIZE_OR_ZERO
      then enforces a valid pointer in case of non-0 size
      or a valid pointer or NULL in case of size 0. Given
      that, the semantics for ARG_PTR_TO_MEM in combination
      with ARG_CONST_SIZE_OR_ZERO are now such that in case
      of size 0, the pointer must always be valid and cannot
      be NULL. This fix in semantics allows for bpf_probe_read()
      to drop the recently added size == 0 check in the helper
      that would become part of uabi otherwise once released.
      At the same time we can then fix bpf_probe_read_str() and
      bpf_perf_event_output() to use ARG_CONST_SIZE_OR_ZERO
      instead of ARG_CONST_SIZE in order to fix recently
      reported issues by Arnaldo et al, where LLVM optimizes
      two boundary checks into a single one for unknown
      variables where the verifier looses track of the variable
      bounds and thus rejects valid programs otherwise.

4) A fix for the verifier for the case when it detects
   comparison of two constants where the branch is guaranteed
   to not be taken at runtime. Verifier will rightfully prune
   the exploration of such paths, but we still pass the program
   to JITs, where they would complain about using reserved
   fields, etc. Track such dead instructions and sanitize
   them with mov r0,r0. Rejection is not possible since LLVM
   may generate them for valid C code and doesn't do as much
   data flow analysis as verifier. For bpf-next we might
   implement removal of such dead code and adjust branches
   instead. Fix from Alexei.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
176 files changed:
Documentation/ABI/testing/dell-smbios-wmi [new file with mode: 0644]
Documentation/ABI/testing/sysfs-platform-dell-smbios [new file with mode: 0644]
Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt [new file with mode: 0644]
Documentation/admin-guide/thunderbolt.rst
Documentation/devicetree/bindings/display/google,goldfish-fb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/trivial-devices.txt
Documentation/devicetree/bindings/usb/usb-device.txt
Documentation/switchtec.txt
MAINTAINERS
drivers/block/rbd.c
drivers/ide/ide-pnp.c
drivers/net/ethernet/mellanox/mlxsw/spectrum.c
drivers/net/ethernet/mellanox/mlxsw/spectrum.h
drivers/net/ethernet/netronome/nfp/flower/offload.c
drivers/net/ethernet/realtek/r8169.c
drivers/net/phy/cortina.c
drivers/net/tap.c
drivers/net/tun.c
drivers/net/usb/qmi_wwan.c
drivers/net/wireless/mac80211_hwsim.c
drivers/ntb/hw/Kconfig
drivers/ntb/hw/Makefile
drivers/ntb/hw/idt/ntb_hw_idt.c
drivers/ntb/hw/intel/ntb_hw_intel.c
drivers/ntb/hw/mscc/Kconfig [new file with mode: 0644]
drivers/ntb/hw/mscc/Makefile [new file with mode: 0644]
drivers/ntb/hw/mscc/ntb_hw_switchtec.c [new file with mode: 0644]
drivers/ntb/ntb_transport.c
drivers/ntb/test/ntb_perf.c
drivers/ntb/test/ntb_tool.c
drivers/of/base.c
drivers/of/of_pci.c
drivers/of/unittest-data/Makefile
drivers/of/unittest-data/testcases.dts
drivers/pci/switch/switchtec.c
drivers/platform/x86/Kconfig
drivers/platform/x86/Makefile
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/dell-laptop.c
drivers/platform/x86/dell-smbios-smm.c [new file with mode: 0644]
drivers/platform/x86/dell-smbios-wmi.c [new file with mode: 0644]
drivers/platform/x86/dell-smbios.c
drivers/platform/x86/dell-smbios.h
drivers/platform/x86/dell-smo8800.c
drivers/platform/x86/dell-wmi-descriptor.c [new file with mode: 0644]
drivers/platform/x86/dell-wmi-descriptor.h [new file with mode: 0644]
drivers/platform/x86/dell-wmi.c
drivers/platform/x86/fujitsu-laptop.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/hp_accel.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel-hid.c
drivers/platform/x86/intel-wmi-thunderbolt.c [new file with mode: 0644]
drivers/platform/x86/intel_cht_int33fe.c
drivers/platform/x86/intel_ips.c
drivers/platform/x86/intel_ips.h
drivers/platform/x86/intel_punit_ipc.c
drivers/platform/x86/intel_telemetry_core.c
drivers/platform/x86/intel_telemetry_debugfs.c
drivers/platform/x86/intel_telemetry_pltdrv.c
drivers/platform/x86/intel_turbo_max_3.c
drivers/platform/x86/mlx-platform.c
drivers/platform/x86/peaq-wmi.c
drivers/platform/x86/silead_dmi.c
drivers/platform/x86/sony-laptop.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/wmi.c
drivers/video/fbdev/Kconfig
drivers/video/fbdev/Makefile
drivers/video/fbdev/aty/atyfb_base.c
drivers/video/fbdev/aty/radeon_base.c
drivers/video/fbdev/aty/radeon_pm.c
drivers/video/fbdev/au1200fb.c
drivers/video/fbdev/cirrusfb.c
drivers/video/fbdev/controlfb.h
drivers/video/fbdev/core/fbcon.c
drivers/video/fbdev/core/fbcon.h
drivers/video/fbdev/dnfb.c
drivers/video/fbdev/goldfishfb.c
drivers/video/fbdev/igafb.c [deleted file]
drivers/video/fbdev/intelfb/intelfbhw.c
drivers/video/fbdev/matrox/matroxfb_base.c
drivers/video/fbdev/mxsfb.c
drivers/video/fbdev/omap/hwa742.c
drivers/video/fbdev/omap2/omapfb/dss/dsi.c
drivers/video/fbdev/omap2/omapfb/omapfb-main.c
drivers/video/fbdev/pxa3xx-gcu.c
drivers/video/fbdev/sa1100fb.c
drivers/video/fbdev/sa1100fb.h
drivers/video/fbdev/sis/init301.c
drivers/video/fbdev/sis/sis_main.c
drivers/video/fbdev/sm501fb.c
drivers/video/fbdev/udlfb.c
fs/ceph/caps.c
fs/ceph/inode.c
fs/ceph/locks.c
fs/ceph/mds_client.c
fs/ceph/super.c
fs/ceph/super.h
fs/lockd/svc.c
fs/nfs_common/grace.c
fs/nfsd/fault_inject.c
fs/nfsd/netns.h
fs/nfsd/nfs3xdr.c
fs/nfsd/nfs4layouts.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfssvc.c
fs/nfsd/state.h
fs/nfsd/xdr4.h
fs/orangefs/acl.c
fs/orangefs/dir.c
fs/orangefs/file.c
fs/orangefs/inode.c
fs/orangefs/namei.c
fs/orangefs/orangefs-debug.h
fs/orangefs/orangefs-kernel.h
fs/orangefs/orangefs-utils.c
fs/orangefs/super.c
fs/orangefs/symlink.c
include/linux/fs.h
include/linux/netdev_features.h
include/linux/netdevice.h
include/linux/ntb.h
include/linux/printk.h
include/linux/skbuff.h
include/linux/sunrpc/svc.h
include/linux/switchtec.h [new file with mode: 0644]
include/linux/virtio_net.h
include/linux/wmi.h
include/net/ipv6.h
include/trace/events/sunrpc.h
include/uapi/linux/wmi.h [new file with mode: 0644]
include/video/iga.h [deleted file]
kernel/printk/printk.c
kernel/printk/printk_safe.c
net/ceph/ceph_hash.c
net/ceph/crypto.c
net/ceph/messenger.c
net/ceph/mon_client.c
net/core/dev.c
net/ipv4/af_inet.c
net/ipv4/udp_offload.c
net/ipv6/output_core.c
net/ipv6/route.c
net/ipv6/udp_offload.c
net/mac80211/agg-rx.c
net/mac80211/agg-tx.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/led.c
net/mac80211/main.c
net/mac80211/mesh.c
net/mac80211/mesh.h
net/mac80211/mesh_hwmp.c
net/mac80211/mesh_pathtbl.c
net/mac80211/mlme.c
net/mac80211/ocb.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/openvswitch/datapath.c
net/openvswitch/flow.c
net/sched/act_csum.c
net/sched/cls_api.c
net/smc/smc_core.c
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/svc_xprt.c
net/sunrpc/xprtrdma/svc_rdma_backchannel.c
net/sunrpc/xprtrdma/svc_rdma_transport.c
net/tipc/group.c
net/wireless/nl80211.c
net/wireless/reg.c
security/integrity/ima/ima_appraise.c
tools/Makefile
tools/wmi/Makefile [new file with mode: 0644]
tools/wmi/dell-smbios-example.c [new file with mode: 0644]

diff --git a/Documentation/ABI/testing/dell-smbios-wmi b/Documentation/ABI/testing/dell-smbios-wmi
new file mode 100644 (file)
index 0000000..fc919ce
--- /dev/null
@@ -0,0 +1,41 @@
+What:          /dev/wmi/dell-smbios
+Date:          November 2017
+KernelVersion: 4.15
+Contact:       "Mario Limonciello" <mario.limonciello@dell.com>
+Description:
+               Perform SMBIOS calls on supported Dell machines.
+               through the Dell ACPI-WMI interface.
+
+               IOCTL's and buffer formats are defined in:
+               <uapi/linux/wmi.h>
+
+               1) To perform an SMBIOS call from userspace, you'll need to
+               first determine the minimum size of the calling interface
+               buffer for your machine.
+               Platforms that contain larger buffers can return larger
+               objects from the system firmware.
+               Commonly this size is either 4k or 32k.
+
+               To determine the size of the buffer read() a u64 dword from
+               the WMI character device /dev/wmi/dell-smbios.
+
+               2) After you've determined the minimum size of the calling
+               interface buffer, you can allocate a structure that represents
+               the structure documented above.
+
+               3) In the 'length' object store the size of the buffer you
+               determined above and allocated.
+
+               4) In this buffer object, prepare as necessary for the SMBIOS
+               call you're interested in.  Typically SMBIOS buffers have
+               "class", "select", and "input" defined to values that coincide
+               with the data you are interested in.
+               Documenting class/select/input values is outside of the scope
+               of this documentation. Check with the libsmbios project for
+               further documentation on these values.
+
+               6) Run the call by using ioctl() as described in the header.
+
+               7) The output will be returned in the buffer object.
+
+               8) Be sure to free up your allocated object.
diff --git a/Documentation/ABI/testing/sysfs-platform-dell-smbios b/Documentation/ABI/testing/sysfs-platform-dell-smbios
new file mode 100644 (file)
index 0000000..205d3b6
--- /dev/null
@@ -0,0 +1,21 @@
+What:          /sys/devices/platform/<platform>/tokens/*
+Date:          November 2017
+KernelVersion: 4.15
+Contact:       "Mario Limonciello" <mario.limonciello@dell.com>
+Description:
+               A read-only description of Dell platform tokens
+               available on the machine.
+
+               Each token attribute is available as a pair of
+               sysfs attributes readable by a process with
+               CAP_SYS_ADMIN.
+
+               For example the token ID "5" would be available
+               as the following attributes:
+
+               0005_location
+               0005_value
+
+               Tokens will vary from machine to machine, and
+               only tokens available on that machine will be
+               displayed.
diff --git a/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt b/Documentation/ABI/testing/sysfs-platform-intel-wmi-thunderbolt
new file mode 100644 (file)
index 0000000..8af6505
--- /dev/null
@@ -0,0 +1,11 @@
+What:          /sys/devices/platform/<platform>/force_power
+Date:          September 2017
+KernelVersion: 4.15
+Contact:       "Mario Limonciello" <mario.limonciello@dell.com>
+Description:
+               Modify the platform force power state, influencing
+               Thunderbolt controllers to turn on or off when no
+               devices are connected (write-only)
+               There are two available states:
+                   * 0 -> Force power disabled
+                   * 1 -> Force power enabled
index 5c62d11d77e86f1709591ba4f3b2abd0e5530a32..de50a8561774249351515662404e2a1f8328aba6 100644 (file)
@@ -221,3 +221,18 @@ The driver will create one virtual ethernet interface per Thunderbolt
 port which are named like ``thunderbolt0`` and so on. From this point
 you can either use standard userspace tools like ``ifconfig`` to
 configure the interface or let your GUI to handle it automatically.
+
+Forcing power
+-------------
+Many OEMs include a method that can be used to force the power of a
+thunderbolt controller to an "On" state even if nothing is connected.
+If supported by your machine this will be exposed by the WMI bus with
+a sysfs attribute called "force_power".
+
+For example the intel-wmi-thunderbolt driver exposes this attribute in:
+  /sys/devices/platform/PNP0C14:00/wmi_bus/wmi_bus-PNP0C14:00/86CCFD48-205E-4A77-9C48-2021CBEDE341/force_power
+
+  To force the power to on, write 1 to this attribute file.
+  To disable force power, write 0 to this attribute file.
+
+Note: it's currently not possible to query the force power state of a platform.
diff --git a/Documentation/devicetree/bindings/display/google,goldfish-fb.txt b/Documentation/devicetree/bindings/display/google,goldfish-fb.txt
new file mode 100644 (file)
index 0000000..751fa9f
--- /dev/null
@@ -0,0 +1,17 @@
+Android Goldfish framebuffer
+
+Android Goldfish framebuffer device used by Android emulator.
+
+Required properties:
+
+- compatible : should contain "google,goldfish-fb"
+- reg        : <registers mapping>
+- interrupts : <interrupt mapping>
+
+Example:
+
+       display-controller@1f008000 {
+               compatible = "google,goldfish-fb";
+               interrupts = <0x10>;
+               reg = <0x1f008000 0x100>;
+       };
index 27dce08edd733b0a4c85cb2ac71baa90edb043b5..678039d4d5e57f96eacd9c9b5a0e8ce72a1d4dd4 100644 (file)
@@ -55,7 +55,6 @@ epson,rx8010          I2C-BUS INTERFACE REAL TIME CLOCK MODULE
 epson,rx8581           I2C-BUS INTERFACE REAL TIME CLOCK MODULE
 emmicro,em3027         EM Microelectronic EM3027 Real-time Clock
 fsl,mag3110            MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
-fsl,mc13892            MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
 fsl,mma7660            MMA7660FC: 3-Axis Orientation/Motion Detection Sensor
 fsl,mma8450            MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
 fsl,mpl3115            MPL3115: Absolute Digital Pressure Sensor
index ce02cebac26afdc80d99600725b3aaec24cb01e9..1b27cebb47f4286f8fac429f1361e6cba240d469 100644 (file)
@@ -4,24 +4,35 @@ Usually, we only use device tree for hard wired USB device.
 The reference binding doc is from:
 http://www.devicetree.org/open-firmware/bindings/usb/usb-1_0.ps
 
+
 Required properties:
-- compatible: usbVID,PID. The textual representation of VID, PID shall
-  be in lower case hexadecimal with leading zeroes suppressed. The
-  other compatible strings from the above standard binding could also
-  be used, but a device adhering to this binding may leave out all except
-  for usbVID,PID.
-- reg: the port number which this device is connecting to, the range
-  is 1-31.
+- compatible: "usbVID,PID", where VID is the vendor id and PID the product id.
+  The textual representation of VID and PID shall be in lower case hexadecimal
+  with leading zeroes suppressed. The other compatible strings from the above
+  standard binding could also be used, but a device adhering to this binding
+  may leave out all except for "usbVID,PID".
+- reg: the number of the USB hub port or the USB host-controller port to which
+  this device is attached. The range is 1-255.
+
+
+Required properties for hub nodes with device nodes:
+- #address-cells: shall be 1
+- #size-cells: shall be 0
 
-Example:
 
-&usb1 {
+Required properties for host-controller nodes with device nodes:
+- #address-cells: shall be 1
+- #size-cells: shall be 0
+
+
+Example:
 
+&usb1 {        /* host controller */
        #address-cells = <1>;
        #size-cells = <0>;
 
-       hub: genesys@1 {
+       hub@1 { /* hub connected to port 1 */
                compatible = "usb5e3,608";
                reg = <1>;
        };
-}
+};
index a0a9c7b3d4d546cc79cdbc39c6555371a4843675..f788264921ffa21415169997225ff40a70469bd7 100644 (file)
@@ -78,3 +78,15 @@ The following IOCTLs are also supported by the device:
   between PCI Function Framework number (used by the event system)
   and Switchtec Logic Port ID and Partition number (which is more
   user friendly).
+
+
+Non-Transparent Bridge (NTB) Driver
+===================================
+
+An NTB driver is provided for the switchtec hardware in switchtec_ntb.
+Currently, it only supports switches configured with exactly 2
+partitions. It also requires the following configuration settings:
+
+* Both partitions must be able to access each other's GAS spaces.
+  Thus, the bits in the GAS Access Vector under Management Settings
+  must be set to support this.
index 16137acd7f2fdb856951e0873273c180580cd2b0..44512c346206708bfc9b90e14e8e733a8aa81018 100644 (file)
@@ -384,6 +384,7 @@ ACPI WMI DRIVER
 L:     platform-driver-x86@vger.kernel.org
 S:     Orphan
 F:     drivers/platform/x86/wmi.c
+F:     include/uapi/linux/wmi.h
 
 AD1889 ALSA SOUND DRIVER
 M:     Thibaut Varene <T-Bone@parisc-linux.org>
@@ -4030,6 +4031,26 @@ M:       "Maciej W. Rozycki" <macro@linux-mips.org>
 S:     Maintained
 F:     drivers/net/fddi/defxx.*
 
+DELL SMBIOS DRIVER
+M:     Pali Rohár <pali.rohar@gmail.com>
+M:     Mario Limonciello <mario.limonciello@dell.com>
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     drivers/platform/x86/dell-smbios.*
+
+DELL SMBIOS SMM DRIVER
+M:     Mario Limonciello <mario.limonciello@dell.com>
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     drivers/platform/x86/dell-smbios-smm.c
+
+DELL SMBIOS WMI DRIVER
+M:     Mario Limonciello <mario.limonciello@dell.com>
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     drivers/platform/x86/dell-smbios-wmi.c
+F:     tools/wmi/dell-smbios-example.c
+
 DELL LAPTOP DRIVER
 M:     Matthew Garrett <mjg59@srcf.ucam.org>
 M:     Pali Rohár <pali.rohar@gmail.com>
@@ -4059,12 +4080,17 @@ S:      Maintained
 F:     Documentation/dcdbas.txt
 F:     drivers/firmware/dcdbas.*
 
-DELL WMI EXTRAS DRIVER
+DELL WMI NOTIFICATIONS DRIVER
 M:     Matthew Garrett <mjg59@srcf.ucam.org>
 M:     Pali Rohár <pali.rohar@gmail.com>
 S:     Maintained
 F:     drivers/platform/x86/dell-wmi.c
 
+DELL WMI DESCRIPTOR DRIVER
+M:     Mario Limonciello <mario.limonciello@dell.com>
+S:     Maintained
+F:     drivers/platform/x86/dell-wmi-descriptor.c
+
 DELTA ST MEDIA DRIVER
 M:     Hugues Fruchet <hugues.fruchet@st.com>
 L:     linux-media@vger.kernel.org
@@ -7181,6 +7207,11 @@ F:       Documentation/wimax/README.i2400m
 F:     drivers/net/wimax/i2400m/
 F:     include/uapi/linux/wimax/i2400m.h
 
+INTEL WMI THUNDERBOLT FORCE POWER DRIVER
+M:     Mario Limonciello <mario.limonciello@dell.com>
+S:     Maintained
+F:     drivers/platform/x86/intel-wmi-thunderbolt.c
+
 INTEL(R) TRACE HUB
 M:     Alexander Shishkin <alexander.shishkin@linux.intel.com>
 S:     Supported
@@ -7442,7 +7473,7 @@ JFS FILESYSTEM
 M:     Dave Kleikamp <shaggy@kernel.org>
 L:     jfs-discussion@lists.sourceforge.net
 W:     http://jfs.sourceforge.net/
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/shaggy/jfs-2.6.git
+T:     git git://github.com/kleikamp/linux-shaggy.git
 S:     Maintained
 F:     Documentation/filesystems/jfs.txt
 F:     fs/jfs/
@@ -9695,12 +9726,11 @@ S:      Supported
 F:     drivers/ntb/hw/idt/
 
 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
+W:     https://github.com/davejiang/linux/wiki
+T:     git https://github.com/davejiang/linux.git
 F:     drivers/ntb/hw/intel/
 
 NTFS FILESYSTEM
@@ -10412,6 +10442,8 @@ F:      Documentation/switchtec.txt
 F:     Documentation/ABI/testing/sysfs-class-switchtec
 F:     drivers/pci/switch/switchtec*
 F:     include/uapi/linux/switchtec_ioctl.h
+F:     include/linux/switchtec.h
+F:     drivers/ntb/hw/mscc/
 
 PCI DRIVER FOR MVEBU (Marvell Armada 370 and Armada XP SOC support)
 M:     Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
@@ -10630,6 +10662,12 @@ S:     Maintained
 F:     crypto/pcrypt.c
 F:     include/crypto/pcrypt.h
 
+PEAQ WMI HOTKEYS DRIVER
+M:     Hans de Goede <hdegoede@redhat.com>
+L:     platform-driver-x86@vger.kernel.org
+S:     Maintained
+F:     drivers/platform/x86/peaq-wmi.c
+
 PER-CPU MEMORY ALLOCATOR
 M:     Tejun Heo <tj@kernel.org>
 M:     Christoph Lameter <cl@linux.com>
index adc877dfef5c2c65ff572e864ef2ef19ad5f5f24..38fc5f397fdede04ebaf4f3f9c2e89118ee6b235 100644 (file)
@@ -348,7 +348,6 @@ struct rbd_client_id {
 struct rbd_mapping {
        u64                     size;
        u64                     features;
-       bool                    read_only;
 };
 
 /*
@@ -450,12 +449,11 @@ static DEFINE_IDA(rbd_dev_id_ida);
 static struct workqueue_struct *rbd_wq;
 
 /*
- * Default to false for now, as single-major requires >= 0.75 version of
- * userspace rbd utility.
+ * single-major requires >= 0.75 version of userspace rbd utility.
  */
-static bool single_major = false;
+static bool single_major = true;
 module_param(single_major, bool, S_IRUGO);
-MODULE_PARM_DESC(single_major, "Use a single major number for all rbd devices (default: false)");
+MODULE_PARM_DESC(single_major, "Use a single major number for all rbd devices (default: true)");
 
 static int rbd_img_request_submit(struct rbd_img_request *img_request);
 
@@ -608,9 +606,6 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
        struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
        bool removing = false;
 
-       if ((mode & FMODE_WRITE) && rbd_dev->mapping.read_only)
-               return -EROFS;
-
        spin_lock_irq(&rbd_dev->lock);
        if (test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags))
                removing = true;
@@ -640,46 +635,24 @@ static void rbd_release(struct gendisk *disk, fmode_t mode)
 
 static int rbd_ioctl_set_ro(struct rbd_device *rbd_dev, unsigned long arg)
 {
-       int ret = 0;
-       int val;
-       bool ro;
-       bool ro_changed = false;
+       int ro;
 
-       /* get_user() may sleep, so call it before taking rbd_dev->lock */
-       if (get_user(val, (int __user *)(arg)))
+       if (get_user(ro, (int __user *)arg))
                return -EFAULT;
 
-       ro = val ? true : false;
-       /* Snapshot doesn't allow to write*/
+       /* Snapshots can't be marked read-write */
        if (rbd_dev->spec->snap_id != CEPH_NOSNAP && !ro)
                return -EROFS;
 
-       spin_lock_irq(&rbd_dev->lock);
-       /* prevent others open this device */
-       if (rbd_dev->open_count > 1) {
-               ret = -EBUSY;
-               goto out;
-       }
-
-       if (rbd_dev->mapping.read_only != ro) {
-               rbd_dev->mapping.read_only = ro;
-               ro_changed = true;
-       }
-
-out:
-       spin_unlock_irq(&rbd_dev->lock);
-       /* set_disk_ro() may sleep, so call it after releasing rbd_dev->lock */
-       if (ret == 0 && ro_changed)
-               set_disk_ro(rbd_dev->disk, ro ? 1 : 0);
-
-       return ret;
+       /* Let blkdev_roset() handle it */
+       return -ENOTTY;
 }
 
 static int rbd_ioctl(struct block_device *bdev, fmode_t mode,
                        unsigned int cmd, unsigned long arg)
 {
        struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
-       int ret = 0;
+       int ret;
 
        switch (cmd) {
        case BLKROSET:
@@ -4050,15 +4023,8 @@ static void rbd_queue_workfn(struct work_struct *work)
                goto err_rq;
        }
 
-       /* Only reads are allowed to a read-only device */
-
-       if (op_type != OBJ_OP_READ) {
-               if (rbd_dev->mapping.read_only) {
-                       result = -EROFS;
-                       goto err_rq;
-               }
-               rbd_assert(rbd_dev->spec->snap_id == CEPH_NOSNAP);
-       }
+       rbd_assert(op_type == OBJ_OP_READ ||
+                  rbd_dev->spec->snap_id == CEPH_NOSNAP);
 
        /*
         * Quit early if the mapped snapshot no longer exists.  It's
@@ -4423,7 +4389,6 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
        /* enable the discard support */
        queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
        q->limits.discard_granularity = segment_size;
-       q->limits.discard_alignment = segment_size;
        blk_queue_max_discard_sectors(q, segment_size / SECTOR_SIZE);
        blk_queue_max_write_zeroes_sectors(q, segment_size / SECTOR_SIZE);
 
@@ -5994,7 +5959,7 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
                goto err_out_disk;
 
        set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE);
-       set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only);
+       set_disk_ro(rbd_dev->disk, rbd_dev->opts->read_only);
 
        ret = dev_set_name(&rbd_dev->dev, "%d", rbd_dev->dev_id);
        if (ret)
@@ -6145,7 +6110,6 @@ static ssize_t do_rbd_add(struct bus_type *bus,
        struct rbd_options *rbd_opts = NULL;
        struct rbd_spec *spec = NULL;
        struct rbd_client *rbdc;
-       bool read_only;
        int rc;
 
        if (!try_module_get(THIS_MODULE))
@@ -6194,11 +6158,8 @@ static ssize_t do_rbd_add(struct bus_type *bus,
        }
 
        /* If we are mapping a snapshot it must be marked read-only */
-
-       read_only = rbd_dev->opts->read_only;
        if (rbd_dev->spec->snap_id != CEPH_NOSNAP)
-               read_only = true;
-       rbd_dev->mapping.read_only = read_only;
+               rbd_dev->opts->read_only = true;
 
        rc = rbd_dev_device_setup(rbd_dev);
        if (rc)
index f5f2b62471da919e4ffd859b8a96ae738d38cea8..859ddab9448fe690a16f2c2d6fad9e243a3ea1ee 100644 (file)
@@ -22,7 +22,7 @@
 #define DRV_NAME "ide-pnp"
 
 /* Add your devices here :)) */
-static struct pnp_device_id idepnp_devices[] = {
+static const struct pnp_device_id idepnp_devices[] = {
        /* Generic ESDI/IDE/ATA compatible hard disk controller */
        {.id = "PNP0600", .driver_data = 0},
        {.id = ""}
index 2d46ec84ebdffbea5032a1f670befab4f585b22c..2d0897b7d86035286666e38ad4e41ab63fb4746b 100644 (file)
@@ -3142,13 +3142,17 @@ static int mlxsw_sp_ports_create(struct mlxsw_sp *mlxsw_sp)
        if (!mlxsw_sp->ports)
                return -ENOMEM;
 
-       mlxsw_sp->port_to_module = kcalloc(max_ports, sizeof(u8), GFP_KERNEL);
+       mlxsw_sp->port_to_module = kmalloc_array(max_ports, sizeof(int),
+                                                GFP_KERNEL);
        if (!mlxsw_sp->port_to_module) {
                err = -ENOMEM;
                goto err_port_to_module_alloc;
        }
 
        for (i = 1; i < max_ports; i++) {
+               /* Mark as invalid */
+               mlxsw_sp->port_to_module[i] = -1;
+
                err = mlxsw_sp_port_module_info_get(mlxsw_sp, i, &module,
                                                    &width, &lane);
                if (err)
@@ -3216,6 +3220,8 @@ static void mlxsw_sp_port_unsplit_create(struct mlxsw_sp *mlxsw_sp,
 
        for (i = 0; i < count; i++) {
                local_port = base_port + i * 2;
+               if (mlxsw_sp->port_to_module[local_port] < 0)
+                       continue;
                module = mlxsw_sp->port_to_module[local_port];
 
                mlxsw_sp_port_create(mlxsw_sp, local_port, false, module,
index 58cf222fb98576426a9dd654a31d7f033a11daa4..432ab9b12b7f59ded586a663f0a686741740463d 100644 (file)
@@ -152,7 +152,7 @@ struct mlxsw_sp {
        const struct mlxsw_bus_info *bus_info;
        unsigned char base_mac[ETH_ALEN];
        struct mlxsw_sp_upper *lags;
-       u8 *port_to_module;
+       int *port_to_module;
        struct mlxsw_sp_sb *sb;
        struct mlxsw_sp_bridge *bridge;
        struct mlxsw_sp_router *router;
index f5d73b83dcc2132e80bc400b77cde31b4fe30897..553f94f55dce64ba9cdb661ff9bdb04faf41acac 100644 (file)
@@ -315,6 +315,7 @@ err_free_flow:
  * @app:       Pointer to the APP handle
  * @netdev:    netdev structure.
  * @flow:      TC flower classifier offload structure.
+ * @egress:    NFP netdev is the egress.
  *
  * Adds a new flow to the repeated hash structure and action payload.
  *
index 2cb3622c4acc93ad669f1f09b92e816e4d86a095..fc0d5fa65ad4c1ca02073e2d1d1554df68443e86 100644 (file)
@@ -2030,21 +2030,6 @@ out:
        return ret;
 }
 
-static int rtl8169_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
-       struct rtl8169_private *tp = netdev_priv(dev);
-       int ret;
-
-       del_timer_sync(&tp->timer);
-
-       rtl_lock_work(tp);
-       ret = rtl8169_set_speed(dev, cmd->autoneg, ethtool_cmd_speed(cmd),
-                               cmd->duplex, cmd->advertising);
-       rtl_unlock_work(tp);
-
-       return ret;
-}
-
 static netdev_features_t rtl8169_fix_features(struct net_device *dev,
        netdev_features_t features)
 {
@@ -2171,6 +2156,27 @@ static int rtl8169_get_link_ksettings(struct net_device *dev,
        return rc;
 }
 
+static int rtl8169_set_link_ksettings(struct net_device *dev,
+                                     const struct ethtool_link_ksettings *cmd)
+{
+       struct rtl8169_private *tp = netdev_priv(dev);
+       int rc;
+       u32 advertising;
+
+       if (!ethtool_convert_link_mode_to_legacy_u32(&advertising,
+           cmd->link_modes.advertising))
+               return -EINVAL;
+
+       del_timer_sync(&tp->timer);
+
+       rtl_lock_work(tp);
+       rc = rtl8169_set_speed(dev, cmd->base.autoneg, cmd->base.speed,
+                              cmd->base.duplex, advertising);
+       rtl_unlock_work(tp);
+
+       return rc;
+}
+
 static void rtl8169_get_regs(struct net_device *dev, struct ethtool_regs *regs,
                             void *p)
 {
@@ -2591,7 +2597,6 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
        .get_link               = ethtool_op_get_link,
        .get_coalesce           = rtl_get_coalesce,
        .set_coalesce           = rtl_set_coalesce,
-       .set_settings           = rtl8169_set_settings,
        .get_msglevel           = rtl8169_get_msglevel,
        .set_msglevel           = rtl8169_set_msglevel,
        .get_regs               = rtl8169_get_regs,
@@ -2603,6 +2608,7 @@ static const struct ethtool_ops rtl8169_ethtool_ops = {
        .get_ts_info            = ethtool_op_get_ts_info,
        .nway_reset             = rtl8169_nway_reset,
        .get_link_ksettings     = rtl8169_get_link_ksettings,
+       .set_link_ksettings     = rtl8169_set_link_ksettings,
 };
 
 static void rtl8169_get_mac_version(struct rtl8169_private *tp,
index 72f4228a63bb0a9fb0f3baa993fa85fc8ad6f823..9442db2218348713e87faf91ccc25f4a8444526a 100644 (file)
@@ -116,3 +116,7 @@ static struct mdio_device_id __maybe_unused cortina_tbl[] = {
 };
 
 MODULE_DEVICE_TABLE(mdio, cortina_tbl);
+
+MODULE_DESCRIPTION("Cortina EDC CDR 10G Ethernet PHY driver");
+MODULE_AUTHOR("NXP");
+MODULE_LICENSE("GPL");
index b13890953ebb92515b3924f511714942a912b120..e9489b88407ce1677385fe480592958b57d02c8d 100644 (file)
@@ -1077,7 +1077,7 @@ static long tap_ioctl(struct file *file, unsigned int cmd,
        case TUNSETOFFLOAD:
                /* let the user check for future flags */
                if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
-                           TUN_F_TSO_ECN))
+                           TUN_F_TSO_ECN | TUN_F_UFO))
                        return -EINVAL;
 
                rtnl_lock();
index 5a2ea78a008f9d22440452e014ca5f4a1dd5968a..6a7bde9bc4b292e349dd92830de494185f7fdc39 100644 (file)
@@ -2370,6 +2370,8 @@ static int set_offload(struct tun_struct *tun, unsigned long arg)
                                features |= NETIF_F_TSO6;
                        arg &= ~(TUN_F_TSO4|TUN_F_TSO6);
                }
+
+               arg &= ~TUN_F_UFO;
        }
 
        /* This gives the user a way to test for new features in future by
index 720a3a248070ccd9db305d3c26349392448f0027..c750cf7c042b004ecfbbce64aefb3d0f1d512c82 100644 (file)
@@ -1239,6 +1239,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)},    /* SIMCom 7230E */
        {QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0  Mini PCIe */
        {QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */
+       {QMI_FIXED_INTF(0x2c7c, 0x0296, 4)},    /* Quectel BG96 */
 
        /* 4. Gobi 1000 devices */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},    /* Acer Gobi Modem Device */
index 7c3600643c7fdb540e71696e928acedaee3ccccf..10b075a46b266218c53d1e5674c1789e1e0f3d80 100644 (file)
@@ -3108,6 +3108,7 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
 {
        struct hwsim_new_radio_params param = { 0 };
        const char *hwname = NULL;
+       int ret;
 
        param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
        param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
@@ -3147,7 +3148,9 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
                param.regd = hwsim_world_regdom_custom[idx];
        }
 
-       return mac80211_hwsim_new_radio(info, &param);
+       ret = mac80211_hwsim_new_radio(info, &param);
+       kfree(hwname);
+       return ret;
 }
 
 static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
index a89243c9fdd33acf6a583b0004513bbf45474001..e51b581fd102e8db1f0b0a0ed597f789a4ba9929 100644 (file)
@@ -1,3 +1,4 @@
 source "drivers/ntb/hw/amd/Kconfig"
 source "drivers/ntb/hw/idt/Kconfig"
 source "drivers/ntb/hw/intel/Kconfig"
+source "drivers/ntb/hw/mscc/Kconfig"
index 87332c3905f075dedd86ae7b86ea6ffd5dcb8d23..923c442db750a16caa3e1bad74cc168ecb9fcfc0 100644 (file)
@@ -1,3 +1,4 @@
 obj-$(CONFIG_NTB_AMD)  += amd/
 obj-$(CONFIG_NTB_IDT)  += idt/
 obj-$(CONFIG_NTB_INTEL)        += intel/
+obj-$(CONFIG_NTB_SWITCHTEC) += mscc/
index d44d7ef38fe88fef82b7ff5420e117cd73d43edd..0cd79f367f7cc51d962679a236cfe56beec66ff9 100644 (file)
@@ -2628,35 +2628,35 @@ static void idt_pci_remove(struct pci_dev *pdev)
 /*
  * IDT PCIe-switch models ports configuration structures
  */
-static struct idt_89hpes_cfg idt_89hpes24nt6ag2_config = {
+static const struct idt_89hpes_cfg idt_89hpes24nt6ag2_config = {
        .name = "89HPES24NT6AG2",
        .port_cnt = 6, .ports = {0, 2, 4, 6, 8, 12}
 };
-static struct idt_89hpes_cfg idt_89hpes32nt8ag2_config = {
+static const struct idt_89hpes_cfg idt_89hpes32nt8ag2_config = {
        .name = "89HPES32NT8AG2",
        .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
 };
-static struct idt_89hpes_cfg idt_89hpes32nt8bg2_config = {
+static const struct idt_89hpes_cfg idt_89hpes32nt8bg2_config = {
        .name = "89HPES32NT8BG2",
        .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
 };
-static struct idt_89hpes_cfg idt_89hpes12nt12g2_config = {
+static const struct idt_89hpes_cfg idt_89hpes12nt12g2_config = {
        .name = "89HPES12NT12G2",
        .port_cnt = 3, .ports = {0, 8, 16}
 };
-static struct idt_89hpes_cfg idt_89hpes16nt16g2_config = {
+static const struct idt_89hpes_cfg idt_89hpes16nt16g2_config = {
        .name = "89HPES16NT16G2",
        .port_cnt = 4, .ports = {0, 8, 12, 16}
 };
-static struct idt_89hpes_cfg idt_89hpes24nt24g2_config = {
+static const struct idt_89hpes_cfg idt_89hpes24nt24g2_config = {
        .name = "89HPES24NT24G2",
        .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
 };
-static struct idt_89hpes_cfg idt_89hpes32nt24ag2_config = {
+static const struct idt_89hpes_cfg idt_89hpes32nt24ag2_config = {
        .name = "89HPES32NT24AG2",
        .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
 };
-static struct idt_89hpes_cfg idt_89hpes32nt24bg2_config = {
+static const struct idt_89hpes_cfg idt_89hpes32nt24bg2_config = {
        .name = "89HPES32NT24BG2",
        .port_cnt = 8, .ports = {0, 2, 4, 6, 8, 12, 16, 20}
 };
index 2557e2c05b90c8ca770d176f0cde26162b9a2df5..4de074a86073604abc59353fb509c55f5ef0b4cf 100644 (file)
@@ -1742,89 +1742,18 @@ static int skx_setup_b2b_mw(struct intel_ntb_dev *ndev,
 {
        struct pci_dev *pdev;
        void __iomem *mmio;
-       resource_size_t bar_size;
        phys_addr_t bar_addr;
-       int b2b_bar;
-       u8 bar_sz;
 
        pdev = ndev->ntb.pdev;
        mmio = ndev->self_mmio;
 
-       if (ndev->b2b_idx == UINT_MAX) {
-               dev_dbg(&pdev->dev, "not using b2b mw\n");
-               b2b_bar = 0;
-               ndev->b2b_off = 0;
-       } else {
-               b2b_bar = ndev_mw_to_bar(ndev, ndev->b2b_idx);
-               if (b2b_bar < 0)
-                       return -EIO;
-
-               dev_dbg(&pdev->dev, "using b2b mw bar %d\n", b2b_bar);
-
-               bar_size = pci_resource_len(ndev->ntb.pdev, b2b_bar);
-
-               dev_dbg(&pdev->dev, "b2b bar size %#llx\n", bar_size);
-
-               if (b2b_mw_share && ((bar_size >> 1) >= XEON_B2B_MIN_SIZE)) {
-                       dev_dbg(&pdev->dev, "b2b using first half of bar\n");
-                       ndev->b2b_off = bar_size >> 1;
-               } else if (bar_size >= XEON_B2B_MIN_SIZE) {
-                       dev_dbg(&pdev->dev, "b2b using whole bar\n");
-                       ndev->b2b_off = 0;
-                       --ndev->mw_count;
-               } else {
-                       dev_dbg(&pdev->dev, "b2b bar size is too small\n");
-                       return -EIO;
-               }
-       }
-
-       /*
-        * Reset the secondary bar sizes to match the primary bar sizes,
-        * except disable or halve the size of the b2b secondary bar.
-        */
-       pci_read_config_byte(pdev, SKX_IMBAR1SZ_OFFSET, &bar_sz);
-       dev_dbg(&pdev->dev, "IMBAR1SZ %#x\n", bar_sz);
-       if (b2b_bar == 1) {
-               if (ndev->b2b_off)
-                       bar_sz -= 1;
-               else
-                       bar_sz = 0;
-       }
-
-       pci_write_config_byte(pdev, SKX_EMBAR1SZ_OFFSET, bar_sz);
-       pci_read_config_byte(pdev, SKX_EMBAR1SZ_OFFSET, &bar_sz);
-       dev_dbg(&pdev->dev, "EMBAR1SZ %#x\n", bar_sz);
-
-       pci_read_config_byte(pdev, SKX_IMBAR2SZ_OFFSET, &bar_sz);
-       dev_dbg(&pdev->dev, "IMBAR2SZ %#x\n", bar_sz);
-       if (b2b_bar == 2) {
-               if (ndev->b2b_off)
-                       bar_sz -= 1;
-               else
-                       bar_sz = 0;
-       }
-
-       pci_write_config_byte(pdev, SKX_EMBAR2SZ_OFFSET, bar_sz);
-       pci_read_config_byte(pdev, SKX_EMBAR2SZ_OFFSET, &bar_sz);
-       dev_dbg(&pdev->dev, "EMBAR2SZ %#x\n", bar_sz);
-
-       /* SBAR01 hit by first part of the b2b bar */
-       if (b2b_bar == 0)
-               bar_addr = addr->bar0_addr;
-       else if (b2b_bar == 1)
-               bar_addr = addr->bar2_addr64;
-       else if (b2b_bar == 2)
-               bar_addr = addr->bar4_addr64;
-       else
-               return -EIO;
-
        /* setup incoming bar limits == base addrs (zero length windows) */
-       bar_addr = addr->bar2_addr64 + (b2b_bar == 1 ? ndev->b2b_off : 0);
+       bar_addr = addr->bar2_addr64;
        iowrite64(bar_addr, mmio + SKX_IMBAR1XLMT_OFFSET);
        bar_addr = ioread64(mmio + SKX_IMBAR1XLMT_OFFSET);
        dev_dbg(&pdev->dev, "IMBAR1XLMT %#018llx\n", bar_addr);
 
-       bar_addr = addr->bar4_addr64 + (b2b_bar == 2 ? ndev->b2b_off : 0);
+       bar_addr = addr->bar4_addr64;
        iowrite64(bar_addr, mmio + SKX_IMBAR2XLMT_OFFSET);
        bar_addr = ioread64(mmio + SKX_IMBAR2XLMT_OFFSET);
        dev_dbg(&pdev->dev, "IMBAR2XLMT %#018llx\n", bar_addr);
diff --git a/drivers/ntb/hw/mscc/Kconfig b/drivers/ntb/hw/mscc/Kconfig
new file mode 100644 (file)
index 0000000..013ed67
--- /dev/null
@@ -0,0 +1,9 @@
+config NTB_SWITCHTEC
+       tristate "MicroSemi Switchtec Non-Transparent Bridge Support"
+       select PCI_SW_SWITCHTEC
+       help
+        Enables NTB support for Switchtec PCI switches. This also
+        selects the Switchtec management driver as they share the same
+        hardware interface.
+
+        If unsure, say N.
diff --git a/drivers/ntb/hw/mscc/Makefile b/drivers/ntb/hw/mscc/Makefile
new file mode 100644 (file)
index 0000000..064686e
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_NTB_SWITCHTEC) += ntb_hw_switchtec.o
diff --git a/drivers/ntb/hw/mscc/ntb_hw_switchtec.c b/drivers/ntb/hw/mscc/ntb_hw_switchtec.c
new file mode 100644 (file)
index 0000000..afe8ed6
--- /dev/null
@@ -0,0 +1,1216 @@
+/*
+ * Microsemi Switchtec(tm) PCIe Management Driver
+ * Copyright (c) 2017, Microsemi Corporation
+ *
+ * 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/switchtec.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/interrupt.h>
+#include <linux/ntb.h>
+
+MODULE_DESCRIPTION("Microsemi Switchtec(tm) NTB Driver");
+MODULE_VERSION("0.1");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Microsemi Corporation");
+
+static ulong max_mw_size = SZ_2M;
+module_param(max_mw_size, ulong, 0644);
+MODULE_PARM_DESC(max_mw_size,
+       "Max memory window size reported to the upper layer");
+
+static bool use_lut_mws;
+module_param(use_lut_mws, bool, 0644);
+MODULE_PARM_DESC(use_lut_mws,
+                "Enable the use of the LUT based memory windows");
+
+#ifndef ioread64
+#ifdef readq
+#define ioread64 readq
+#else
+#define ioread64 _ioread64
+static inline u64 _ioread64(void __iomem *mmio)
+{
+       u64 low, high;
+
+       low = ioread32(mmio);
+       high = ioread32(mmio + sizeof(u32));
+       return low | (high << 32);
+}
+#endif
+#endif
+
+#ifndef iowrite64
+#ifdef writeq
+#define iowrite64 writeq
+#else
+#define iowrite64 _iowrite64
+static inline void _iowrite64(u64 val, void __iomem *mmio)
+{
+       iowrite32(val, mmio);
+       iowrite32(val >> 32, mmio + sizeof(u32));
+}
+#endif
+#endif
+
+#define SWITCHTEC_NTB_MAGIC 0x45CC0001
+#define MAX_MWS     128
+
+struct shared_mw {
+       u32 magic;
+       u32 link_sta;
+       u32 partition_id;
+       u64 mw_sizes[MAX_MWS];
+       u32 spad[128];
+};
+
+#define MAX_DIRECT_MW ARRAY_SIZE(((struct ntb_ctrl_regs *)(0))->bar_entry)
+#define LUT_SIZE SZ_64K
+
+struct switchtec_ntb {
+       struct ntb_dev ntb;
+       struct switchtec_dev *stdev;
+
+       int self_partition;
+       int peer_partition;
+
+       int doorbell_irq;
+       int message_irq;
+
+       struct ntb_info_regs __iomem *mmio_ntb;
+       struct ntb_ctrl_regs __iomem *mmio_ctrl;
+       struct ntb_dbmsg_regs __iomem *mmio_dbmsg;
+       struct ntb_ctrl_regs __iomem *mmio_self_ctrl;
+       struct ntb_ctrl_regs __iomem *mmio_peer_ctrl;
+       struct ntb_dbmsg_regs __iomem *mmio_self_dbmsg;
+
+       struct shared_mw *self_shared;
+       struct shared_mw __iomem *peer_shared;
+       dma_addr_t self_shared_dma;
+
+       u64 db_mask;
+       u64 db_valid_mask;
+       int db_shift;
+       int db_peer_shift;
+
+       /* synchronize rmw access of db_mask and hw reg */
+       spinlock_t db_mask_lock;
+
+       int nr_direct_mw;
+       int nr_lut_mw;
+       int direct_mw_to_bar[MAX_DIRECT_MW];
+
+       int peer_nr_direct_mw;
+       int peer_nr_lut_mw;
+       int peer_direct_mw_to_bar[MAX_DIRECT_MW];
+
+       bool link_is_up;
+       enum ntb_speed link_speed;
+       enum ntb_width link_width;
+};
+
+static struct switchtec_ntb *ntb_sndev(struct ntb_dev *ntb)
+{
+       return container_of(ntb, struct switchtec_ntb, ntb);
+}
+
+static int switchtec_ntb_part_op(struct switchtec_ntb *sndev,
+                                struct ntb_ctrl_regs __iomem *ctl,
+                                u32 op, int wait_status)
+{
+       static const char * const op_text[] = {
+               [NTB_CTRL_PART_OP_LOCK] = "lock",
+               [NTB_CTRL_PART_OP_CFG] = "configure",
+               [NTB_CTRL_PART_OP_RESET] = "reset",
+       };
+
+       int i;
+       u32 ps;
+       int status;
+
+       switch (op) {
+       case NTB_CTRL_PART_OP_LOCK:
+               status = NTB_CTRL_PART_STATUS_LOCKING;
+               break;
+       case NTB_CTRL_PART_OP_CFG:
+               status = NTB_CTRL_PART_STATUS_CONFIGURING;
+               break;
+       case NTB_CTRL_PART_OP_RESET:
+               status = NTB_CTRL_PART_STATUS_RESETTING;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       iowrite32(op, &ctl->partition_op);
+
+       for (i = 0; i < 1000; i++) {
+               if (msleep_interruptible(50) != 0) {
+                       iowrite32(NTB_CTRL_PART_OP_RESET, &ctl->partition_op);
+                       return -EINTR;
+               }
+
+               ps = ioread32(&ctl->partition_status) & 0xFFFF;
+
+               if (ps != status)
+                       break;
+       }
+
+       if (ps == wait_status)
+               return 0;
+
+       if (ps == status) {
+               dev_err(&sndev->stdev->dev,
+                       "Timed out while peforming %s (%d). (%08x)",
+                       op_text[op], op,
+                       ioread32(&ctl->partition_status));
+
+               return -ETIMEDOUT;
+       }
+
+       return -EIO;
+}
+
+static int switchtec_ntb_send_msg(struct switchtec_ntb *sndev, int idx,
+                                 u32 val)
+{
+       if (idx < 0 || idx >= ARRAY_SIZE(sndev->mmio_self_dbmsg->omsg))
+               return -EINVAL;
+
+       iowrite32(val, &sndev->mmio_self_dbmsg->omsg[idx].msg);
+
+       return 0;
+}
+
+static int switchtec_ntb_mw_count(struct ntb_dev *ntb, int pidx)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+       int nr_direct_mw = sndev->peer_nr_direct_mw;
+       int nr_lut_mw = sndev->peer_nr_lut_mw - 1;
+
+       if (pidx != NTB_DEF_PEER_IDX)
+               return -EINVAL;
+
+       if (!use_lut_mws)
+               nr_lut_mw = 0;
+
+       return nr_direct_mw + nr_lut_mw;
+}
+
+static int lut_index(struct switchtec_ntb *sndev, int mw_idx)
+{
+       return mw_idx - sndev->nr_direct_mw + 1;
+}
+
+static int peer_lut_index(struct switchtec_ntb *sndev, int mw_idx)
+{
+       return mw_idx - sndev->peer_nr_direct_mw + 1;
+}
+
+static int switchtec_ntb_mw_get_align(struct ntb_dev *ntb, int pidx,
+                                     int widx, resource_size_t *addr_align,
+                                     resource_size_t *size_align,
+                                     resource_size_t *size_max)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+       int lut;
+       resource_size_t size;
+
+       if (pidx != NTB_DEF_PEER_IDX)
+               return -EINVAL;
+
+       lut = widx >= sndev->peer_nr_direct_mw;
+       size = ioread64(&sndev->peer_shared->mw_sizes[widx]);
+
+       if (size == 0)
+               return -EINVAL;
+
+       if (addr_align)
+               *addr_align = lut ? size : SZ_4K;
+
+       if (size_align)
+               *size_align = lut ? size : SZ_4K;
+
+       if (size_max)
+               *size_max = size;
+
+       return 0;
+}
+
+static void switchtec_ntb_mw_clr_direct(struct switchtec_ntb *sndev, int idx)
+{
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+       int bar = sndev->peer_direct_mw_to_bar[idx];
+       u32 ctl_val;
+
+       ctl_val = ioread32(&ctl->bar_entry[bar].ctl);
+       ctl_val &= ~NTB_CTRL_BAR_DIR_WIN_EN;
+       iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
+       iowrite32(0, &ctl->bar_entry[bar].win_size);
+       iowrite64(sndev->self_partition, &ctl->bar_entry[bar].xlate_addr);
+}
+
+static void switchtec_ntb_mw_clr_lut(struct switchtec_ntb *sndev, int idx)
+{
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+
+       iowrite64(0, &ctl->lut_entry[peer_lut_index(sndev, idx)]);
+}
+
+static void switchtec_ntb_mw_set_direct(struct switchtec_ntb *sndev, int idx,
+                                       dma_addr_t addr, resource_size_t size)
+{
+       int xlate_pos = ilog2(size);
+       int bar = sndev->peer_direct_mw_to_bar[idx];
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+       u32 ctl_val;
+
+       ctl_val = ioread32(&ctl->bar_entry[bar].ctl);
+       ctl_val |= NTB_CTRL_BAR_DIR_WIN_EN;
+
+       iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
+       iowrite32(xlate_pos | size, &ctl->bar_entry[bar].win_size);
+       iowrite64(sndev->self_partition | addr,
+                 &ctl->bar_entry[bar].xlate_addr);
+}
+
+static void switchtec_ntb_mw_set_lut(struct switchtec_ntb *sndev, int idx,
+                                    dma_addr_t addr, resource_size_t size)
+{
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+
+       iowrite64((NTB_CTRL_LUT_EN | (sndev->self_partition << 1) | addr),
+                 &ctl->lut_entry[peer_lut_index(sndev, idx)]);
+}
+
+static int switchtec_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int widx,
+                                     dma_addr_t addr, resource_size_t size)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+       int xlate_pos = ilog2(size);
+       int nr_direct_mw = sndev->peer_nr_direct_mw;
+       int rc;
+
+       if (pidx != NTB_DEF_PEER_IDX)
+               return -EINVAL;
+
+       dev_dbg(&sndev->stdev->dev, "MW %d: part %d addr %pad size %pap",
+               widx, pidx, &addr, &size);
+
+       if (widx >= switchtec_ntb_mw_count(ntb, pidx))
+               return -EINVAL;
+
+       if (xlate_pos < 12)
+               return -EINVAL;
+
+       rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK,
+                                  NTB_CTRL_PART_STATUS_LOCKED);
+       if (rc)
+               return rc;
+
+       if (addr == 0 || size == 0) {
+               if (widx < nr_direct_mw)
+                       switchtec_ntb_mw_clr_direct(sndev, widx);
+               else
+                       switchtec_ntb_mw_clr_lut(sndev, widx);
+       } else {
+               if (widx < nr_direct_mw)
+                       switchtec_ntb_mw_set_direct(sndev, widx, addr, size);
+               else
+                       switchtec_ntb_mw_set_lut(sndev, widx, addr, size);
+       }
+
+       rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG,
+                                  NTB_CTRL_PART_STATUS_NORMAL);
+
+       if (rc == -EIO) {
+               dev_err(&sndev->stdev->dev,
+                       "Hardware reported an error configuring mw %d: %08x",
+                       widx, ioread32(&ctl->bar_error));
+
+               if (widx < nr_direct_mw)
+                       switchtec_ntb_mw_clr_direct(sndev, widx);
+               else
+                       switchtec_ntb_mw_clr_lut(sndev, widx);
+
+               switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG,
+                                     NTB_CTRL_PART_STATUS_NORMAL);
+       }
+
+       return rc;
+}
+
+static int switchtec_ntb_peer_mw_count(struct ntb_dev *ntb)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       return sndev->nr_direct_mw + (use_lut_mws ? sndev->nr_lut_mw - 1 : 0);
+}
+
+static int switchtec_ntb_direct_get_addr(struct switchtec_ntb *sndev,
+                                        int idx, phys_addr_t *base,
+                                        resource_size_t *size)
+{
+       int bar = sndev->direct_mw_to_bar[idx];
+       size_t offset = 0;
+
+       if (bar < 0)
+               return -EINVAL;
+
+       if (idx == 0) {
+               /*
+                * This is the direct BAR shared with the LUTs
+                * which means the actual window will be offset
+                * by the size of all the LUT entries.
+                */
+
+               offset = LUT_SIZE * sndev->nr_lut_mw;
+       }
+
+       if (base)
+               *base = pci_resource_start(sndev->ntb.pdev, bar) + offset;
+
+       if (size) {
+               *size = pci_resource_len(sndev->ntb.pdev, bar) - offset;
+               if (offset && *size > offset)
+                       *size = offset;
+
+               if (*size > max_mw_size)
+                       *size = max_mw_size;
+       }
+
+       return 0;
+}
+
+static int switchtec_ntb_lut_get_addr(struct switchtec_ntb *sndev,
+                                     int idx, phys_addr_t *base,
+                                     resource_size_t *size)
+{
+       int bar = sndev->direct_mw_to_bar[0];
+       int offset;
+
+       offset = LUT_SIZE * lut_index(sndev, idx);
+
+       if (base)
+               *base = pci_resource_start(sndev->ntb.pdev, bar) + offset;
+
+       if (size)
+               *size = LUT_SIZE;
+
+       return 0;
+}
+
+static int switchtec_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int idx,
+                                         phys_addr_t *base,
+                                         resource_size_t *size)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       if (idx < sndev->nr_direct_mw)
+               return switchtec_ntb_direct_get_addr(sndev, idx, base, size);
+       else if (idx < switchtec_ntb_peer_mw_count(ntb))
+               return switchtec_ntb_lut_get_addr(sndev, idx, base, size);
+       else
+               return -EINVAL;
+}
+
+static void switchtec_ntb_part_link_speed(struct switchtec_ntb *sndev,
+                                         int partition,
+                                         enum ntb_speed *speed,
+                                         enum ntb_width *width)
+{
+       struct switchtec_dev *stdev = sndev->stdev;
+
+       u32 pff = ioread32(&stdev->mmio_part_cfg[partition].vep_pff_inst_id);
+       u32 linksta = ioread32(&stdev->mmio_pff_csr[pff].pci_cap_region[13]);
+
+       if (speed)
+               *speed = (linksta >> 16) & 0xF;
+
+       if (width)
+               *width = (linksta >> 20) & 0x3F;
+}
+
+static void switchtec_ntb_set_link_speed(struct switchtec_ntb *sndev)
+{
+       enum ntb_speed self_speed, peer_speed;
+       enum ntb_width self_width, peer_width;
+
+       if (!sndev->link_is_up) {
+               sndev->link_speed = NTB_SPEED_NONE;
+               sndev->link_width = NTB_WIDTH_NONE;
+               return;
+       }
+
+       switchtec_ntb_part_link_speed(sndev, sndev->self_partition,
+                                     &self_speed, &self_width);
+       switchtec_ntb_part_link_speed(sndev, sndev->peer_partition,
+                                     &peer_speed, &peer_width);
+
+       sndev->link_speed = min(self_speed, peer_speed);
+       sndev->link_width = min(self_width, peer_width);
+}
+
+enum {
+       LINK_MESSAGE = 0,
+       MSG_LINK_UP = 1,
+       MSG_LINK_DOWN = 2,
+       MSG_CHECK_LINK = 3,
+};
+
+static void switchtec_ntb_check_link(struct switchtec_ntb *sndev)
+{
+       int link_sta;
+       int old = sndev->link_is_up;
+
+       link_sta = sndev->self_shared->link_sta;
+       if (link_sta) {
+               u64 peer = ioread64(&sndev->peer_shared->magic);
+
+               if ((peer & 0xFFFFFFFF) == SWITCHTEC_NTB_MAGIC)
+                       link_sta = peer >> 32;
+               else
+                       link_sta = 0;
+       }
+
+       sndev->link_is_up = link_sta;
+       switchtec_ntb_set_link_speed(sndev);
+
+       if (link_sta != old) {
+               switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_CHECK_LINK);
+               ntb_link_event(&sndev->ntb);
+               dev_info(&sndev->stdev->dev, "ntb link %s",
+                        link_sta ? "up" : "down");
+       }
+}
+
+static void switchtec_ntb_link_notification(struct switchtec_dev *stdev)
+{
+       struct switchtec_ntb *sndev = stdev->sndev;
+
+       switchtec_ntb_check_link(sndev);
+}
+
+static u64 switchtec_ntb_link_is_up(struct ntb_dev *ntb,
+                                   enum ntb_speed *speed,
+                                   enum ntb_width *width)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       if (speed)
+               *speed = sndev->link_speed;
+       if (width)
+               *width = sndev->link_width;
+
+       return sndev->link_is_up;
+}
+
+static int switchtec_ntb_link_enable(struct ntb_dev *ntb,
+                                    enum ntb_speed max_speed,
+                                    enum ntb_width max_width)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       dev_dbg(&sndev->stdev->dev, "enabling link");
+
+       sndev->self_shared->link_sta = 1;
+       switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_UP);
+
+       switchtec_ntb_check_link(sndev);
+
+       return 0;
+}
+
+static int switchtec_ntb_link_disable(struct ntb_dev *ntb)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       dev_dbg(&sndev->stdev->dev, "disabling link");
+
+       sndev->self_shared->link_sta = 0;
+       switchtec_ntb_send_msg(sndev, LINK_MESSAGE, MSG_LINK_UP);
+
+       switchtec_ntb_check_link(sndev);
+
+       return 0;
+}
+
+static u64 switchtec_ntb_db_valid_mask(struct ntb_dev *ntb)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       return sndev->db_valid_mask;
+}
+
+static int switchtec_ntb_db_vector_count(struct ntb_dev *ntb)
+{
+       return 1;
+}
+
+static u64 switchtec_ntb_db_vector_mask(struct ntb_dev *ntb, int db_vector)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       if (db_vector < 0 || db_vector > 1)
+               return 0;
+
+       return sndev->db_valid_mask;
+}
+
+static u64 switchtec_ntb_db_read(struct ntb_dev *ntb)
+{
+       u64 ret;
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       ret = ioread64(&sndev->mmio_self_dbmsg->idb) >> sndev->db_shift;
+
+       return ret & sndev->db_valid_mask;
+}
+
+static int switchtec_ntb_db_clear(struct ntb_dev *ntb, u64 db_bits)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       iowrite64(db_bits << sndev->db_shift, &sndev->mmio_self_dbmsg->idb);
+
+       return 0;
+}
+
+static int switchtec_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits)
+{
+       unsigned long irqflags;
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       if (db_bits & ~sndev->db_valid_mask)
+               return -EINVAL;
+
+       spin_lock_irqsave(&sndev->db_mask_lock, irqflags);
+
+       sndev->db_mask |= db_bits << sndev->db_shift;
+       iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask);
+
+       spin_unlock_irqrestore(&sndev->db_mask_lock, irqflags);
+
+       return 0;
+}
+
+static int switchtec_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits)
+{
+       unsigned long irqflags;
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       if (db_bits & ~sndev->db_valid_mask)
+               return -EINVAL;
+
+       spin_lock_irqsave(&sndev->db_mask_lock, irqflags);
+
+       sndev->db_mask &= ~(db_bits << sndev->db_shift);
+       iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask);
+
+       spin_unlock_irqrestore(&sndev->db_mask_lock, irqflags);
+
+       return 0;
+}
+
+static u64 switchtec_ntb_db_read_mask(struct ntb_dev *ntb)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       return (sndev->db_mask >> sndev->db_shift) & sndev->db_valid_mask;
+}
+
+static int switchtec_ntb_peer_db_addr(struct ntb_dev *ntb,
+                                     phys_addr_t *db_addr,
+                                     resource_size_t *db_size)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+       unsigned long offset;
+
+       offset = (unsigned long)sndev->mmio_self_dbmsg->odb -
+               (unsigned long)sndev->stdev->mmio;
+
+       offset += sndev->db_shift / 8;
+
+       if (db_addr)
+               *db_addr = pci_resource_start(ntb->pdev, 0) + offset;
+       if (db_size)
+               *db_size = sizeof(u32);
+
+       return 0;
+}
+
+static int switchtec_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       iowrite64(db_bits << sndev->db_peer_shift,
+                 &sndev->mmio_self_dbmsg->odb);
+
+       return 0;
+}
+
+static int switchtec_ntb_spad_count(struct ntb_dev *ntb)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       return ARRAY_SIZE(sndev->self_shared->spad);
+}
+
+static u32 switchtec_ntb_spad_read(struct ntb_dev *ntb, int idx)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       if (idx < 0 || idx >= ARRAY_SIZE(sndev->self_shared->spad))
+               return 0;
+
+       if (!sndev->self_shared)
+               return 0;
+
+       return sndev->self_shared->spad[idx];
+}
+
+static int switchtec_ntb_spad_write(struct ntb_dev *ntb, int idx, u32 val)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       if (idx < 0 || idx >= ARRAY_SIZE(sndev->self_shared->spad))
+               return -EINVAL;
+
+       if (!sndev->self_shared)
+               return -EIO;
+
+       sndev->self_shared->spad[idx] = val;
+
+       return 0;
+}
+
+static u32 switchtec_ntb_peer_spad_read(struct ntb_dev *ntb, int pidx,
+                                       int sidx)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       if (pidx != NTB_DEF_PEER_IDX)
+               return -EINVAL;
+
+       if (sidx < 0 || sidx >= ARRAY_SIZE(sndev->peer_shared->spad))
+               return 0;
+
+       if (!sndev->peer_shared)
+               return 0;
+
+       return ioread32(&sndev->peer_shared->spad[sidx]);
+}
+
+static int switchtec_ntb_peer_spad_write(struct ntb_dev *ntb, int pidx,
+                                        int sidx, u32 val)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+
+       if (pidx != NTB_DEF_PEER_IDX)
+               return -EINVAL;
+
+       if (sidx < 0 || sidx >= ARRAY_SIZE(sndev->peer_shared->spad))
+               return -EINVAL;
+
+       if (!sndev->peer_shared)
+               return -EIO;
+
+       iowrite32(val, &sndev->peer_shared->spad[sidx]);
+
+       return 0;
+}
+
+static int switchtec_ntb_peer_spad_addr(struct ntb_dev *ntb, int pidx,
+                                       int sidx, phys_addr_t *spad_addr)
+{
+       struct switchtec_ntb *sndev = ntb_sndev(ntb);
+       unsigned long offset;
+
+       if (pidx != NTB_DEF_PEER_IDX)
+               return -EINVAL;
+
+       offset = (unsigned long)&sndev->peer_shared->spad[sidx] -
+               (unsigned long)sndev->stdev->mmio;
+
+       if (spad_addr)
+               *spad_addr = pci_resource_start(ntb->pdev, 0) + offset;
+
+       return 0;
+}
+
+static const struct ntb_dev_ops switchtec_ntb_ops = {
+       .mw_count               = switchtec_ntb_mw_count,
+       .mw_get_align           = switchtec_ntb_mw_get_align,
+       .mw_set_trans           = switchtec_ntb_mw_set_trans,
+       .peer_mw_count          = switchtec_ntb_peer_mw_count,
+       .peer_mw_get_addr       = switchtec_ntb_peer_mw_get_addr,
+       .link_is_up             = switchtec_ntb_link_is_up,
+       .link_enable            = switchtec_ntb_link_enable,
+       .link_disable           = switchtec_ntb_link_disable,
+       .db_valid_mask          = switchtec_ntb_db_valid_mask,
+       .db_vector_count        = switchtec_ntb_db_vector_count,
+       .db_vector_mask         = switchtec_ntb_db_vector_mask,
+       .db_read                = switchtec_ntb_db_read,
+       .db_clear               = switchtec_ntb_db_clear,
+       .db_set_mask            = switchtec_ntb_db_set_mask,
+       .db_clear_mask          = switchtec_ntb_db_clear_mask,
+       .db_read_mask           = switchtec_ntb_db_read_mask,
+       .peer_db_addr           = switchtec_ntb_peer_db_addr,
+       .peer_db_set            = switchtec_ntb_peer_db_set,
+       .spad_count             = switchtec_ntb_spad_count,
+       .spad_read              = switchtec_ntb_spad_read,
+       .spad_write             = switchtec_ntb_spad_write,
+       .peer_spad_read         = switchtec_ntb_peer_spad_read,
+       .peer_spad_write        = switchtec_ntb_peer_spad_write,
+       .peer_spad_addr         = switchtec_ntb_peer_spad_addr,
+};
+
+static void switchtec_ntb_init_sndev(struct switchtec_ntb *sndev)
+{
+       u64 part_map;
+
+       sndev->ntb.pdev = sndev->stdev->pdev;
+       sndev->ntb.topo = NTB_TOPO_SWITCH;
+       sndev->ntb.ops = &switchtec_ntb_ops;
+
+       sndev->self_partition = sndev->stdev->partition;
+
+       sndev->mmio_ntb = sndev->stdev->mmio_ntb;
+       part_map = ioread64(&sndev->mmio_ntb->ep_map);
+       part_map &= ~(1 << sndev->self_partition);
+       sndev->peer_partition = ffs(part_map) - 1;
+
+       dev_dbg(&sndev->stdev->dev, "Partition ID %d of %d (%llx)",
+               sndev->self_partition, sndev->stdev->partition_count,
+               part_map);
+
+       sndev->mmio_ctrl = (void * __iomem)sndev->mmio_ntb +
+               SWITCHTEC_NTB_REG_CTRL_OFFSET;
+       sndev->mmio_dbmsg = (void * __iomem)sndev->mmio_ntb +
+               SWITCHTEC_NTB_REG_DBMSG_OFFSET;
+
+       sndev->mmio_self_ctrl = &sndev->mmio_ctrl[sndev->self_partition];
+       sndev->mmio_peer_ctrl = &sndev->mmio_ctrl[sndev->peer_partition];
+       sndev->mmio_self_dbmsg = &sndev->mmio_dbmsg[sndev->self_partition];
+}
+
+static int map_bars(int *map, struct ntb_ctrl_regs __iomem *ctrl)
+{
+       int i;
+       int cnt = 0;
+
+       for (i = 0; i < ARRAY_SIZE(ctrl->bar_entry); i++) {
+               u32 r = ioread32(&ctrl->bar_entry[i].ctl);
+
+               if (r & NTB_CTRL_BAR_VALID)
+                       map[cnt++] = i;
+       }
+
+       return cnt;
+}
+
+static void switchtec_ntb_init_mw(struct switchtec_ntb *sndev)
+{
+       sndev->nr_direct_mw = map_bars(sndev->direct_mw_to_bar,
+                                      sndev->mmio_self_ctrl);
+
+       sndev->nr_lut_mw = ioread16(&sndev->mmio_self_ctrl->lut_table_entries);
+       sndev->nr_lut_mw = rounddown_pow_of_two(sndev->nr_lut_mw);
+
+       dev_dbg(&sndev->stdev->dev, "MWs: %d direct, %d lut",
+               sndev->nr_direct_mw, sndev->nr_lut_mw);
+
+       sndev->peer_nr_direct_mw = map_bars(sndev->peer_direct_mw_to_bar,
+                                           sndev->mmio_peer_ctrl);
+
+       sndev->peer_nr_lut_mw =
+               ioread16(&sndev->mmio_peer_ctrl->lut_table_entries);
+       sndev->peer_nr_lut_mw = rounddown_pow_of_two(sndev->peer_nr_lut_mw);
+
+       dev_dbg(&sndev->stdev->dev, "Peer MWs: %d direct, %d lut",
+               sndev->peer_nr_direct_mw, sndev->peer_nr_lut_mw);
+
+}
+
+/*
+ * There are 64 doorbells in the switch hardware but this is
+ * shared among all partitions. So we must split them in half
+ * (32 for each partition). However, the message interrupts are
+ * also shared with the top 4 doorbells so we just limit this to
+ * 28 doorbells per partition
+ */
+static void switchtec_ntb_init_db(struct switchtec_ntb *sndev)
+{
+       sndev->db_valid_mask = 0x0FFFFFFF;
+
+       if (sndev->self_partition < sndev->peer_partition) {
+               sndev->db_shift = 0;
+               sndev->db_peer_shift = 32;
+       } else {
+               sndev->db_shift = 32;
+               sndev->db_peer_shift = 0;
+       }
+
+       sndev->db_mask = 0x0FFFFFFFFFFFFFFFULL;
+       iowrite64(~sndev->db_mask, &sndev->mmio_self_dbmsg->idb_mask);
+       iowrite64(sndev->db_valid_mask << sndev->db_peer_shift,
+                 &sndev->mmio_self_dbmsg->odb_mask);
+}
+
+static void switchtec_ntb_init_msgs(struct switchtec_ntb *sndev)
+{
+       int i;
+       u32 msg_map = 0;
+
+       for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++) {
+               int m = i | sndev->peer_partition << 2;
+
+               msg_map |= m << i * 8;
+       }
+
+       iowrite32(msg_map, &sndev->mmio_self_dbmsg->msg_map);
+
+       for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++)
+               iowrite64(NTB_DBMSG_IMSG_STATUS | NTB_DBMSG_IMSG_MASK,
+                         &sndev->mmio_self_dbmsg->imsg[i]);
+}
+
+static int switchtec_ntb_init_req_id_table(struct switchtec_ntb *sndev)
+{
+       int rc = 0;
+       u16 req_id;
+       u32 error;
+
+       req_id = ioread16(&sndev->mmio_ntb->requester_id);
+
+       if (ioread32(&sndev->mmio_self_ctrl->req_id_table_size) < 2) {
+               dev_err(&sndev->stdev->dev,
+                       "Not enough requester IDs available.");
+               return -EFAULT;
+       }
+
+       rc = switchtec_ntb_part_op(sndev, sndev->mmio_self_ctrl,
+                                  NTB_CTRL_PART_OP_LOCK,
+                                  NTB_CTRL_PART_STATUS_LOCKED);
+       if (rc)
+               return rc;
+
+       iowrite32(NTB_PART_CTRL_ID_PROT_DIS,
+                 &sndev->mmio_self_ctrl->partition_ctrl);
+
+       /*
+        * Root Complex Requester ID (which is 0:00.0)
+        */
+       iowrite32(0 << 16 | NTB_CTRL_REQ_ID_EN,
+                 &sndev->mmio_self_ctrl->req_id_table[0]);
+
+       /*
+        * Host Bridge Requester ID (as read from the mmap address)
+        */
+       iowrite32(req_id << 16 | NTB_CTRL_REQ_ID_EN,
+                 &sndev->mmio_self_ctrl->req_id_table[1]);
+
+       rc = switchtec_ntb_part_op(sndev, sndev->mmio_self_ctrl,
+                                  NTB_CTRL_PART_OP_CFG,
+                                  NTB_CTRL_PART_STATUS_NORMAL);
+       if (rc == -EIO) {
+               error = ioread32(&sndev->mmio_self_ctrl->req_id_error);
+               dev_err(&sndev->stdev->dev,
+                       "Error setting up the requester ID table: %08x",
+                       error);
+       }
+
+       return rc;
+}
+
+static void switchtec_ntb_init_shared(struct switchtec_ntb *sndev)
+{
+       int i;
+
+       memset(sndev->self_shared, 0, LUT_SIZE);
+       sndev->self_shared->magic = SWITCHTEC_NTB_MAGIC;
+       sndev->self_shared->partition_id = sndev->stdev->partition;
+
+       for (i = 0; i < sndev->nr_direct_mw; i++) {
+               int bar = sndev->direct_mw_to_bar[i];
+               resource_size_t sz = pci_resource_len(sndev->stdev->pdev, bar);
+
+               if (i == 0)
+                       sz = min_t(resource_size_t, sz,
+                                  LUT_SIZE * sndev->nr_lut_mw);
+
+               sndev->self_shared->mw_sizes[i] = sz;
+       }
+
+       for (i = 0; i < sndev->nr_lut_mw; i++) {
+               int idx = sndev->nr_direct_mw + i;
+
+               sndev->self_shared->mw_sizes[idx] = LUT_SIZE;
+       }
+}
+
+static int switchtec_ntb_init_shared_mw(struct switchtec_ntb *sndev)
+{
+       struct ntb_ctrl_regs __iomem *ctl = sndev->mmio_peer_ctrl;
+       int bar = sndev->direct_mw_to_bar[0];
+       u32 ctl_val;
+       int rc;
+
+       sndev->self_shared = dma_zalloc_coherent(&sndev->stdev->pdev->dev,
+                                                LUT_SIZE,
+                                                &sndev->self_shared_dma,
+                                                GFP_KERNEL);
+       if (!sndev->self_shared) {
+               dev_err(&sndev->stdev->dev,
+                       "unable to allocate memory for shared mw");
+               return -ENOMEM;
+       }
+
+       switchtec_ntb_init_shared(sndev);
+
+       rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_LOCK,
+                                  NTB_CTRL_PART_STATUS_LOCKED);
+       if (rc)
+               goto unalloc_and_exit;
+
+       ctl_val = ioread32(&ctl->bar_entry[bar].ctl);
+       ctl_val &= 0xFF;
+       ctl_val |= NTB_CTRL_BAR_LUT_WIN_EN;
+       ctl_val |= ilog2(LUT_SIZE) << 8;
+       ctl_val |= (sndev->nr_lut_mw - 1) << 14;
+       iowrite32(ctl_val, &ctl->bar_entry[bar].ctl);
+
+       iowrite64((NTB_CTRL_LUT_EN | (sndev->self_partition << 1) |
+                  sndev->self_shared_dma),
+                 &ctl->lut_entry[0]);
+
+       rc = switchtec_ntb_part_op(sndev, ctl, NTB_CTRL_PART_OP_CFG,
+                                  NTB_CTRL_PART_STATUS_NORMAL);
+       if (rc) {
+               u32 bar_error, lut_error;
+
+               bar_error = ioread32(&ctl->bar_error);
+               lut_error = ioread32(&ctl->lut_error);
+               dev_err(&sndev->stdev->dev,
+                       "Error setting up shared MW: %08x / %08x",
+                       bar_error, lut_error);
+               goto unalloc_and_exit;
+       }
+
+       sndev->peer_shared = pci_iomap(sndev->stdev->pdev, bar, LUT_SIZE);
+       if (!sndev->peer_shared) {
+               rc = -ENOMEM;
+               goto unalloc_and_exit;
+       }
+
+       dev_dbg(&sndev->stdev->dev, "Shared MW Ready");
+       return 0;
+
+unalloc_and_exit:
+       dma_free_coherent(&sndev->stdev->pdev->dev, LUT_SIZE,
+                         sndev->self_shared, sndev->self_shared_dma);
+
+       return rc;
+}
+
+static void switchtec_ntb_deinit_shared_mw(struct switchtec_ntb *sndev)
+{
+       if (sndev->peer_shared)
+               pci_iounmap(sndev->stdev->pdev, sndev->peer_shared);
+
+       if (sndev->self_shared)
+               dma_free_coherent(&sndev->stdev->pdev->dev, LUT_SIZE,
+                                 sndev->self_shared,
+                                 sndev->self_shared_dma);
+}
+
+static irqreturn_t switchtec_ntb_doorbell_isr(int irq, void *dev)
+{
+       struct switchtec_ntb *sndev = dev;
+
+       dev_dbg(&sndev->stdev->dev, "doorbell\n");
+
+       ntb_db_event(&sndev->ntb, 0);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t switchtec_ntb_message_isr(int irq, void *dev)
+{
+       int i;
+       struct switchtec_ntb *sndev = dev;
+
+       for (i = 0; i < ARRAY_SIZE(sndev->mmio_self_dbmsg->imsg); i++) {
+               u64 msg = ioread64(&sndev->mmio_self_dbmsg->imsg[i]);
+
+               if (msg & NTB_DBMSG_IMSG_STATUS) {
+                       dev_dbg(&sndev->stdev->dev, "message: %d %08x\n", i,
+                               (u32)msg);
+                       iowrite8(1, &sndev->mmio_self_dbmsg->imsg[i].status);
+
+                       if (i == LINK_MESSAGE)
+                               switchtec_ntb_check_link(sndev);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int switchtec_ntb_init_db_msg_irq(struct switchtec_ntb *sndev)
+{
+       int i;
+       int rc;
+       int doorbell_irq = 0;
+       int message_irq = 0;
+       int event_irq;
+       int idb_vecs = sizeof(sndev->mmio_self_dbmsg->idb_vec_map);
+
+       event_irq = ioread32(&sndev->stdev->mmio_part_cfg->vep_vector_number);
+
+       while (doorbell_irq == event_irq)
+               doorbell_irq++;
+       while (message_irq == doorbell_irq ||
+              message_irq == event_irq)
+               message_irq++;
+
+       dev_dbg(&sndev->stdev->dev, "irqs - event: %d, db: %d, msgs: %d",
+               event_irq, doorbell_irq, message_irq);
+
+       for (i = 0; i < idb_vecs - 4; i++)
+               iowrite8(doorbell_irq,
+                        &sndev->mmio_self_dbmsg->idb_vec_map[i]);
+
+       for (; i < idb_vecs; i++)
+               iowrite8(message_irq,
+                        &sndev->mmio_self_dbmsg->idb_vec_map[i]);
+
+       sndev->doorbell_irq = pci_irq_vector(sndev->stdev->pdev, doorbell_irq);
+       sndev->message_irq = pci_irq_vector(sndev->stdev->pdev, message_irq);
+
+       rc = request_irq(sndev->doorbell_irq,
+                        switchtec_ntb_doorbell_isr, 0,
+                        "switchtec_ntb_doorbell", sndev);
+       if (rc)
+               return rc;
+
+       rc = request_irq(sndev->message_irq,
+                        switchtec_ntb_message_isr, 0,
+                        "switchtec_ntb_message", sndev);
+       if (rc) {
+               free_irq(sndev->doorbell_irq, sndev);
+               return rc;
+       }
+
+       return 0;
+}
+
+static void switchtec_ntb_deinit_db_msg_irq(struct switchtec_ntb *sndev)
+{
+       free_irq(sndev->doorbell_irq, sndev);
+       free_irq(sndev->message_irq, sndev);
+}
+
+static int switchtec_ntb_add(struct device *dev,
+                            struct class_interface *class_intf)
+{
+       struct switchtec_dev *stdev = to_stdev(dev);
+       struct switchtec_ntb *sndev;
+       int rc;
+
+       stdev->sndev = NULL;
+
+       if (stdev->pdev->class != MICROSEMI_NTB_CLASSCODE)
+               return -ENODEV;
+
+       if (stdev->partition_count != 2)
+               dev_warn(dev, "ntb driver only supports 2 partitions");
+
+       sndev = kzalloc_node(sizeof(*sndev), GFP_KERNEL, dev_to_node(dev));
+       if (!sndev)
+               return -ENOMEM;
+
+       sndev->stdev = stdev;
+       switchtec_ntb_init_sndev(sndev);
+       switchtec_ntb_init_mw(sndev);
+       switchtec_ntb_init_db(sndev);
+       switchtec_ntb_init_msgs(sndev);
+
+       rc = switchtec_ntb_init_req_id_table(sndev);
+       if (rc)
+               goto free_and_exit;
+
+       rc = switchtec_ntb_init_shared_mw(sndev);
+       if (rc)
+               goto free_and_exit;
+
+       rc = switchtec_ntb_init_db_msg_irq(sndev);
+       if (rc)
+               goto deinit_shared_and_exit;
+
+       rc = ntb_register_device(&sndev->ntb);
+       if (rc)
+               goto deinit_and_exit;
+
+       stdev->sndev = sndev;
+       stdev->link_notifier = switchtec_ntb_link_notification;
+       dev_info(dev, "NTB device registered");
+
+       return 0;
+
+deinit_and_exit:
+       switchtec_ntb_deinit_db_msg_irq(sndev);
+deinit_shared_and_exit:
+       switchtec_ntb_deinit_shared_mw(sndev);
+free_and_exit:
+       kfree(sndev);
+       dev_err(dev, "failed to register ntb device: %d", rc);
+       return rc;
+}
+
+void switchtec_ntb_remove(struct device *dev,
+                         struct class_interface *class_intf)
+{
+       struct switchtec_dev *stdev = to_stdev(dev);
+       struct switchtec_ntb *sndev = stdev->sndev;
+
+       if (!sndev)
+               return;
+
+       stdev->link_notifier = NULL;
+       stdev->sndev = NULL;
+       ntb_unregister_device(&sndev->ntb);
+       switchtec_ntb_deinit_db_msg_irq(sndev);
+       switchtec_ntb_deinit_shared_mw(sndev);
+       kfree(sndev);
+       dev_info(dev, "ntb device unregistered");
+}
+
+static struct class_interface switchtec_interface  = {
+       .add_dev = switchtec_ntb_add,
+       .remove_dev = switchtec_ntb_remove,
+};
+
+static int __init switchtec_ntb_init(void)
+{
+       switchtec_interface.class = switchtec_class;
+       return class_interface_register(&switchtec_interface);
+}
+module_init(switchtec_ntb_init);
+
+static void __exit switchtec_ntb_exit(void)
+{
+       class_interface_unregister(&switchtec_interface);
+}
+module_exit(switchtec_ntb_exit);
index f58d8e3053236ad4608e5552052214b0694a3a95..045e3dd4750e572a033dbb0cf2683296b2d0e6c7 100644 (file)
@@ -191,8 +191,6 @@ struct ntb_transport_qp {
 struct ntb_transport_mw {
        phys_addr_t phys_addr;
        resource_size_t phys_size;
-       resource_size_t xlat_align;
-       resource_size_t xlat_align_size;
        void __iomem *vbase;
        size_t xlat_size;
        size_t buff_size;
@@ -687,13 +685,20 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw,
        struct ntb_transport_mw *mw = &nt->mw_vec[num_mw];
        struct pci_dev *pdev = nt->ndev->pdev;
        size_t xlat_size, buff_size;
+       resource_size_t xlat_align;
+       resource_size_t xlat_align_size;
        int rc;
 
        if (!size)
                return -EINVAL;
 
-       xlat_size = round_up(size, mw->xlat_align_size);
-       buff_size = round_up(size, mw->xlat_align);
+       rc = ntb_mw_get_align(nt->ndev, PIDX, num_mw, &xlat_align,
+                             &xlat_align_size, NULL);
+       if (rc)
+               return rc;
+
+       xlat_size = round_up(size, xlat_align_size);
+       buff_size = round_up(size, xlat_align);
 
        /* No need to re-setup */
        if (mw->xlat_size == xlat_size)
@@ -722,7 +727,7 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw,
         * is a requirement of the hardware. It is recommended to setup CMA
         * for BAR sizes equal or greater than 4MB.
         */
-       if (!IS_ALIGNED(mw->dma_addr, mw->xlat_align)) {
+       if (!IS_ALIGNED(mw->dma_addr, xlat_align)) {
                dev_err(&pdev->dev, "DMA memory %pad is not aligned\n",
                        &mw->dma_addr);
                ntb_free_mw(nt, num_mw);
@@ -1104,11 +1109,6 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev)
        for (i = 0; i < mw_count; i++) {
                mw = &nt->mw_vec[i];
 
-               rc = ntb_mw_get_align(ndev, PIDX, i, &mw->xlat_align,
-                                     &mw->xlat_align_size, NULL);
-               if (rc)
-                       goto err1;
-
                rc = ntb_peer_mw_get_addr(ndev, i, &mw->phys_addr,
                                          &mw->phys_size);
                if (rc)
index 759f772fa00c6a3970eacaaa05de88155bcf6c8d..427112cf101aa814ee210cd77d59b97c8ff90f33 100644 (file)
@@ -108,8 +108,6 @@ MODULE_PARM_DESC(on_node, "Run threads only on NTB device node (default: true)")
 struct perf_mw {
        phys_addr_t     phys_addr;
        resource_size_t phys_size;
-       resource_size_t xlat_align;
-       resource_size_t xlat_align_size;
        void __iomem    *vbase;
        size_t          xlat_size;
        size_t          buf_size;
@@ -472,13 +470,20 @@ static int perf_set_mw(struct perf_ctx *perf, resource_size_t size)
 {
        struct perf_mw *mw = &perf->mw;
        size_t xlat_size, buf_size;
+       resource_size_t xlat_align;
+       resource_size_t xlat_align_size;
        int rc;
 
        if (!size)
                return -EINVAL;
 
-       xlat_size = round_up(size, mw->xlat_align_size);
-       buf_size = round_up(size, mw->xlat_align);
+       rc = ntb_mw_get_align(perf->ntb, PIDX, 0, &xlat_align,
+                             &xlat_align_size, NULL);
+       if (rc)
+               return rc;
+
+       xlat_size = round_up(size, xlat_align_size);
+       buf_size = round_up(size, xlat_align);
 
        if (mw->xlat_size == xlat_size)
                return 0;
@@ -567,11 +572,6 @@ static int perf_setup_mw(struct ntb_dev *ntb, struct perf_ctx *perf)
 
        mw = &perf->mw;
 
-       rc = ntb_mw_get_align(ntb, PIDX, 0, &mw->xlat_align,
-                             &mw->xlat_align_size, NULL);
-       if (rc)
-               return rc;
-
        rc = ntb_peer_mw_get_addr(ntb, 0, &mw->phys_addr, &mw->phys_size);
        if (rc)
                return rc;
index a69815c45ce6f2137d9f409bad0b8742fa530df4..91526a986caab7cc69c328df15a145c14f10899b 100644 (file)
@@ -753,9 +753,9 @@ static ssize_t tool_peer_mw_trans_read(struct file *filep,
 
        phys_addr_t base;
        resource_size_t mw_size;
-       resource_size_t align_addr;
-       resource_size_t align_size;
-       resource_size_t max_size;
+       resource_size_t align_addr = 0;
+       resource_size_t align_size = 0;
+       resource_size_t max_size = 0;
 
        buf_size = min_t(size_t, size, 512);
 
index f2e649ff746f546b153d5ff1f64dc70958490c56..26618ba8f92a55e8d42db822b507bb8e8827e49f 100644 (file)
@@ -761,10 +761,10 @@ EXPORT_SYMBOL(of_find_node_opts_by_path);
 
 /**
  *     of_find_node_by_name - Find a node by its "name" property
- *     @from:  The node to start searching from or NULL, the node
+ *     @from:  The node to start searching from or NULL; the node
  *             you pass will not be searched, only the next one
- *             will; typically, you pass what the previous call
- *             returned. of_node_put() will be called on it
+ *             will. Typically, you pass what the previous call
+ *             returned. of_node_put() will be called on @from.
  *     @name:  The name string to match against
  *
  *     Returns a node pointer with refcount incremented, use
index e9ec931f5b9a565c7963dc8f3cf1b1c686effa23..a7b1cb6c2f657798a103f6f55acf3ef6f265377c 100644 (file)
@@ -374,7 +374,7 @@ int of_pci_map_rid(struct device_node *np, u32 rid,
 
                pr_debug("%pOF: %s, using mask %08x, rid-base: %08x, out-base: %08x, length: %08x, rid: %08x -> %08x\n",
                        np, map_name, map_mask, rid_base, out_base,
-                       rid_len, rid, *id_out);
+                       rid_len, rid, masked_rid - rid_base + out_base);
                return 0;
        }
 
index 3031fc2f18f6fd6b8bb5ae8bd17b9c72ed733b4f..32389acfa6164eb69dcd014f18ce12e58cb90050 100644 (file)
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
+DTC_FLAGS_testcases := -Wno-interrupts_property
 obj-y += testcases.dtb.o
 
 targets += testcases.dtb testcases.dtb.S
index ce49463d9d32b69bd6374ac3ebecdfe08c75b020..55fe0ee20109fd68e7bc663f8f00f78618a5abd6 100644 (file)
@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /dts-v1/;
+/plugin/;
+
 / {
        testcase-data {
                changeset {
 #include "tests-match.dtsi"
 #include "tests-platform.dtsi"
 #include "tests-overlay.dtsi"
-
-/*
- * phandle fixup data - generated by dtc patches that aren't upstream.
- * This data must be regenerated whenever phandle references are modified in
- * the testdata tree.
- *
- * The format of this data may be subject to change. For the time being consider
- * this a kernel-internal data format.
- */
-/ { __local_fixups__ {
-       testcase-data {
-               phandle-tests {
-                       consumer-a {
-                               phandle-list = <0x00000000 0x00000008
-                                               0x00000018 0x00000028
-                                               0x00000034 0x00000038>;
-                               phandle-list-bad-args = <0x00000000 0x0000000c>;
-                       };
-               };
-               interrupts {
-                       intmap0 {
-                               interrupt-map = <0x00000004 0x00000010
-                                                0x00000024 0x00000034>;
-                       };
-                       intmap1 {
-                               interrupt-map = <0x0000000c>;
-                       };
-                       interrupts0 {
-                               interrupt-parent = <0x00000000>;
-                       };
-                       interrupts1 {
-                               interrupt-parent = <0x00000000>;
-                       };
-                       interrupts-extended0 {
-                               interrupts-extended = <0x00000000 0x00000008
-                                                      0x00000018 0x00000024
-                                                      0x0000002c 0x00000034
-                                                      0x0000003c>;
-                       };
-               };
-               testcase-device1 {
-                       interrupt-parent = <0x00000000>;
-               };
-               testcase-device2 {
-                       interrupt-parent = <0x00000000>;
-               };
-               overlay2 {
-                       fragment@0 {
-                               target = <0x00000000>;
-                       };
-               };
-               overlay3 {
-                       fragment@0 {
-                               target = <0x00000000>;
-                       };
-               };
-               overlay4 {
-                       fragment@0 {
-                               target = <0x00000000>;
-                       };
-               };
-       };
-}; };
index da45dbea20ce6af4d15ed8b758c13b4437b093f8..730cc897b94da0251dac161192fd1bf1fc47b324 100644 (file)
@@ -13,6 +13,7 @@
  *
  */
 
+#include <linux/switchtec.h>
 #include <linux/switchtec_ioctl.h>
 
 #include <linux/interrupt.h>
@@ -20,8 +21,6 @@
 #include <linux/fs.h>
 #include <linux/uaccess.h>
 #include <linux/poll.h>
-#include <linux/pci.h>
-#include <linux/cdev.h>
 #include <linux/wait.h>
 
 MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver");
@@ -34,265 +33,10 @@ module_param(max_devices, int, 0644);
 MODULE_PARM_DESC(max_devices, "max number of switchtec device instances");
 
 static dev_t switchtec_devt;
-static struct class *switchtec_class;
 static DEFINE_IDA(switchtec_minor_ida);
 
-#define MICROSEMI_VENDOR_ID         0x11f8
-#define MICROSEMI_NTB_CLASSCODE     0x068000
-#define MICROSEMI_MGMT_CLASSCODE    0x058000
-
-#define SWITCHTEC_MRPC_PAYLOAD_SIZE 1024
-#define SWITCHTEC_MAX_PFF_CSR 48
-
-#define SWITCHTEC_EVENT_OCCURRED BIT(0)
-#define SWITCHTEC_EVENT_CLEAR    BIT(0)
-#define SWITCHTEC_EVENT_EN_LOG   BIT(1)
-#define SWITCHTEC_EVENT_EN_CLI   BIT(2)
-#define SWITCHTEC_EVENT_EN_IRQ   BIT(3)
-#define SWITCHTEC_EVENT_FATAL    BIT(4)
-
-enum {
-       SWITCHTEC_GAS_MRPC_OFFSET       = 0x0000,
-       SWITCHTEC_GAS_TOP_CFG_OFFSET    = 0x1000,
-       SWITCHTEC_GAS_SW_EVENT_OFFSET   = 0x1800,
-       SWITCHTEC_GAS_SYS_INFO_OFFSET   = 0x2000,
-       SWITCHTEC_GAS_FLASH_INFO_OFFSET = 0x2200,
-       SWITCHTEC_GAS_PART_CFG_OFFSET   = 0x4000,
-       SWITCHTEC_GAS_NTB_OFFSET        = 0x10000,
-       SWITCHTEC_GAS_PFF_CSR_OFFSET    = 0x134000,
-};
-
-struct mrpc_regs {
-       u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
-       u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
-       u32 cmd;
-       u32 status;
-       u32 ret_value;
-} __packed;
-
-enum mrpc_status {
-       SWITCHTEC_MRPC_STATUS_INPROGRESS = 1,
-       SWITCHTEC_MRPC_STATUS_DONE = 2,
-       SWITCHTEC_MRPC_STATUS_ERROR = 0xFF,
-       SWITCHTEC_MRPC_STATUS_INTERRUPTED = 0x100,
-};
-
-struct sw_event_regs {
-       u64 event_report_ctrl;
-       u64 reserved1;
-       u64 part_event_bitmap;
-       u64 reserved2;
-       u32 global_summary;
-       u32 reserved3[3];
-       u32 stack_error_event_hdr;
-       u32 stack_error_event_data;
-       u32 reserved4[4];
-       u32 ppu_error_event_hdr;
-       u32 ppu_error_event_data;
-       u32 reserved5[4];
-       u32 isp_error_event_hdr;
-       u32 isp_error_event_data;
-       u32 reserved6[4];
-       u32 sys_reset_event_hdr;
-       u32 reserved7[5];
-       u32 fw_exception_hdr;
-       u32 reserved8[5];
-       u32 fw_nmi_hdr;
-       u32 reserved9[5];
-       u32 fw_non_fatal_hdr;
-       u32 reserved10[5];
-       u32 fw_fatal_hdr;
-       u32 reserved11[5];
-       u32 twi_mrpc_comp_hdr;
-       u32 twi_mrpc_comp_data;
-       u32 reserved12[4];
-       u32 twi_mrpc_comp_async_hdr;
-       u32 twi_mrpc_comp_async_data;
-       u32 reserved13[4];
-       u32 cli_mrpc_comp_hdr;
-       u32 cli_mrpc_comp_data;
-       u32 reserved14[4];
-       u32 cli_mrpc_comp_async_hdr;
-       u32 cli_mrpc_comp_async_data;
-       u32 reserved15[4];
-       u32 gpio_interrupt_hdr;
-       u32 gpio_interrupt_data;
-       u32 reserved16[4];
-} __packed;
-
-enum {
-       SWITCHTEC_CFG0_RUNNING = 0x04,
-       SWITCHTEC_CFG1_RUNNING = 0x05,
-       SWITCHTEC_IMG0_RUNNING = 0x03,
-       SWITCHTEC_IMG1_RUNNING = 0x07,
-};
-
-struct sys_info_regs {
-       u32 device_id;
-       u32 device_version;
-       u32 firmware_version;
-       u32 reserved1;
-       u32 vendor_table_revision;
-       u32 table_format_version;
-       u32 partition_id;
-       u32 cfg_file_fmt_version;
-       u16 cfg_running;
-       u16 img_running;
-       u32 reserved2[57];
-       char vendor_id[8];
-       char product_id[16];
-       char product_revision[4];
-       char component_vendor[8];
-       u16 component_id;
-       u8 component_revision;
-} __packed;
-
-struct flash_info_regs {
-       u32 flash_part_map_upd_idx;
-
-       struct active_partition_info {
-               u32 address;
-               u32 build_version;
-               u32 build_string;
-       } active_img;
-
-       struct active_partition_info active_cfg;
-       struct active_partition_info inactive_img;
-       struct active_partition_info inactive_cfg;
-
-       u32 flash_length;
-
-       struct partition_info {
-               u32 address;
-               u32 length;
-       } cfg0;
-
-       struct partition_info cfg1;
-       struct partition_info img0;
-       struct partition_info img1;
-       struct partition_info nvlog;
-       struct partition_info vendor[8];
-};
-
-struct ntb_info_regs {
-       u8  partition_count;
-       u8  partition_id;
-       u16 reserved1;
-       u64 ep_map;
-       u16 requester_id;
-} __packed;
-
-struct part_cfg_regs {
-       u32 status;
-       u32 state;
-       u32 port_cnt;
-       u32 usp_port_mode;
-       u32 usp_pff_inst_id;
-       u32 vep_pff_inst_id;
-       u32 dsp_pff_inst_id[47];
-       u32 reserved1[11];
-       u16 vep_vector_number;
-       u16 usp_vector_number;
-       u32 port_event_bitmap;
-       u32 reserved2[3];
-       u32 part_event_summary;
-       u32 reserved3[3];
-       u32 part_reset_hdr;
-       u32 part_reset_data[5];
-       u32 mrpc_comp_hdr;
-       u32 mrpc_comp_data[5];
-       u32 mrpc_comp_async_hdr;
-       u32 mrpc_comp_async_data[5];
-       u32 dyn_binding_hdr;
-       u32 dyn_binding_data[5];
-       u32 reserved4[159];
-} __packed;
-
-enum {
-       SWITCHTEC_PART_CFG_EVENT_RESET = 1 << 0,
-       SWITCHTEC_PART_CFG_EVENT_MRPC_CMP = 1 << 1,
-       SWITCHTEC_PART_CFG_EVENT_MRPC_ASYNC_CMP = 1 << 2,
-       SWITCHTEC_PART_CFG_EVENT_DYN_PART_CMP = 1 << 3,
-};
-
-struct pff_csr_regs {
-       u16 vendor_id;
-       u16 device_id;
-       u32 pci_cfg_header[15];
-       u32 pci_cap_region[48];
-       u32 pcie_cap_region[448];
-       u32 indirect_gas_window[128];
-       u32 indirect_gas_window_off;
-       u32 reserved[127];
-       u32 pff_event_summary;
-       u32 reserved2[3];
-       u32 aer_in_p2p_hdr;
-       u32 aer_in_p2p_data[5];
-       u32 aer_in_vep_hdr;
-       u32 aer_in_vep_data[5];
-       u32 dpc_hdr;
-       u32 dpc_data[5];
-       u32 cts_hdr;
-       u32 cts_data[5];
-       u32 reserved3[6];
-       u32 hotplug_hdr;
-       u32 hotplug_data[5];
-       u32 ier_hdr;
-       u32 ier_data[5];
-       u32 threshold_hdr;
-       u32 threshold_data[5];
-       u32 power_mgmt_hdr;
-       u32 power_mgmt_data[5];
-       u32 tlp_throttling_hdr;
-       u32 tlp_throttling_data[5];
-       u32 force_speed_hdr;
-       u32 force_speed_data[5];
-       u32 credit_timeout_hdr;
-       u32 credit_timeout_data[5];
-       u32 link_state_hdr;
-       u32 link_state_data[5];
-       u32 reserved4[174];
-} __packed;
-
-struct switchtec_dev {
-       struct pci_dev *pdev;
-       struct device dev;
-       struct cdev cdev;
-
-       int partition;
-       int partition_count;
-       int pff_csr_count;
-       char pff_local[SWITCHTEC_MAX_PFF_CSR];
-
-       void __iomem *mmio;
-       struct mrpc_regs __iomem *mmio_mrpc;
-       struct sw_event_regs __iomem *mmio_sw_event;
-       struct sys_info_regs __iomem *mmio_sys_info;
-       struct flash_info_regs __iomem *mmio_flash_info;
-       struct ntb_info_regs __iomem *mmio_ntb;
-       struct part_cfg_regs __iomem *mmio_part_cfg;
-       struct part_cfg_regs __iomem *mmio_part_cfg_all;
-       struct pff_csr_regs __iomem *mmio_pff_csr;
-
-       /*
-        * The mrpc mutex must be held when accessing the other
-        * mrpc_ fields, alive flag and stuser->state field
-        */
-       struct mutex mrpc_mutex;
-       struct list_head mrpc_queue;
-       int mrpc_busy;
-       struct work_struct mrpc_work;
-       struct delayed_work mrpc_timeout;
-       bool alive;
-
-       wait_queue_head_t event_wq;
-       atomic_t event_cnt;
-};
-
-static struct switchtec_dev *to_stdev(struct device *dev)
-{
-       return container_of(dev, struct switchtec_dev, dev);
-}
+struct class *switchtec_class;
+EXPORT_SYMBOL_GPL(switchtec_class);
 
 enum mrpc_state {
        MRPC_IDLE = 0,
@@ -1234,6 +978,49 @@ static const struct file_operations switchtec_fops = {
        .compat_ioctl = switchtec_dev_ioctl,
 };
 
+static void link_event_work(struct work_struct *work)
+{
+       struct switchtec_dev *stdev;
+
+       stdev = container_of(work, struct switchtec_dev, link_event_work);
+
+       if (stdev->link_notifier)
+               stdev->link_notifier(stdev);
+}
+
+static void check_link_state_events(struct switchtec_dev *stdev)
+{
+       int idx;
+       u32 reg;
+       int count;
+       int occurred = 0;
+
+       for (idx = 0; idx < stdev->pff_csr_count; idx++) {
+               reg = ioread32(&stdev->mmio_pff_csr[idx].link_state_hdr);
+               dev_dbg(&stdev->dev, "link_state: %d->%08x\n", idx, reg);
+               count = (reg >> 5) & 0xFF;
+
+               if (count != stdev->link_event_count[idx]) {
+                       occurred = 1;
+                       stdev->link_event_count[idx] = count;
+               }
+       }
+
+       if (occurred)
+               schedule_work(&stdev->link_event_work);
+}
+
+static void enable_link_state_events(struct switchtec_dev *stdev)
+{
+       int idx;
+
+       for (idx = 0; idx < stdev->pff_csr_count; idx++) {
+               iowrite32(SWITCHTEC_EVENT_CLEAR |
+                         SWITCHTEC_EVENT_EN_IRQ,
+                         &stdev->mmio_pff_csr[idx].link_state_hdr);
+       }
+}
+
 static void stdev_release(struct device *dev)
 {
        struct switchtec_dev *stdev = to_stdev(dev);
@@ -1286,6 +1073,7 @@ static struct switchtec_dev *stdev_create(struct pci_dev *pdev)
        stdev->mrpc_busy = 0;
        INIT_WORK(&stdev->mrpc_work, mrpc_event_work);
        INIT_DELAYED_WORK(&stdev->mrpc_timeout, mrpc_timeout_work);
+       INIT_WORK(&stdev->link_event_work, link_event_work);
        init_waitqueue_head(&stdev->event_wq);
        atomic_set(&stdev->event_cnt, 0);
 
@@ -1329,6 +1117,9 @@ static int mask_event(struct switchtec_dev *stdev, int eid, int idx)
        if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ))
                return 0;
 
+       if (eid == SWITCHTEC_IOCTL_EVENT_LINK_STATE)
+               return 0;
+
        dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr);
        hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED);
        iowrite32(hdr, hdr_reg);
@@ -1348,6 +1139,7 @@ static int mask_all_events(struct switchtec_dev *stdev, int eid)
                for (idx = 0; idx < stdev->pff_csr_count; idx++) {
                        if (!stdev->pff_local[idx])
                                continue;
+
                        count += mask_event(stdev, eid, idx);
                }
        } else {
@@ -1372,6 +1164,8 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev)
                iowrite32(reg, &stdev->mmio_part_cfg->mrpc_comp_hdr);
        }
 
+       check_link_state_events(stdev);
+
        for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++)
                event_count += mask_all_events(stdev, eid);
 
@@ -1481,6 +1275,9 @@ static int switchtec_pci_probe(struct pci_dev *pdev,
        struct switchtec_dev *stdev;
        int rc;
 
+       if (pdev->class == MICROSEMI_NTB_CLASSCODE)
+               request_module_nowait("ntb_hw_switchtec");
+
        stdev = stdev_create(pdev);
        if (IS_ERR(stdev))
                return PTR_ERR(stdev);
@@ -1498,6 +1295,7 @@ static int switchtec_pci_probe(struct pci_dev *pdev,
        iowrite32(SWITCHTEC_EVENT_CLEAR |
                  SWITCHTEC_EVENT_EN_IRQ,
                  &stdev->mmio_part_cfg->mrpc_comp_hdr);
+       enable_link_state_events(stdev);
 
        rc = cdev_device_add(&stdev->cdev, &stdev->dev);
        if (rc)
index 09dac11337d10bf6abebb836a6db93b6abc5dd4b..2c745e8ccad6c190882725d8864d4f96ae2e68be 100644 (file)
@@ -93,12 +93,31 @@ config ASUS_LAPTOP
 
 config DELL_SMBIOS
        tristate
-       select DCDBAS
+
+config DELL_SMBIOS_WMI
+       tristate "Dell SMBIOS calling interface (WMI implementation)"
+       depends on ACPI_WMI
+       select DELL_WMI_DESCRIPTOR
+       select DELL_SMBIOS
+       ---help---
+       This provides an implementation for the Dell SMBIOS calling interface
+       communicated over ACPI-WMI.
+
+       If you have a Dell computer from >2007 you should say Y or M here.
+       If you aren't sure and this module doesn't work for your computer
+       it just won't load.
+
+config DELL_SMBIOS_SMM
+       tristate "Dell SMBIOS calling interface (SMM implementation)"
+       depends on DCDBAS
+       select DELL_SMBIOS
        ---help---
-       This module provides common functions for kernel modules using
-       Dell SMBIOS.
+       This provides an implementation for the Dell SMBIOS calling interface
+       communicated over SMI/SMM.
 
-       If you have a Dell laptop, say Y or M here.
+       If you have a Dell computer from <=2017 you should say Y or M here.
+       If you aren't sure and this module doesn't work for your computer
+       it just won't load.
 
 config DELL_LAPTOP
        tristate "Dell Laptop Extras"
@@ -116,11 +135,12 @@ config DELL_LAPTOP
        laptops (except for some models covered by the Compal driver).
 
 config DELL_WMI
-       tristate "Dell WMI extras"
+       tristate "Dell WMI notifications"
        depends on ACPI_WMI
        depends on DMI
        depends on INPUT
        depends on ACPI_VIDEO || ACPI_VIDEO = n
+       select DELL_WMI_DESCRIPTOR
        select DELL_SMBIOS
        select INPUT_SPARSEKMAP
        ---help---
@@ -129,6 +149,10 @@ config DELL_WMI
          To compile this driver as a module, choose M here: the module will
          be called dell-wmi.
 
+config DELL_WMI_DESCRIPTOR
+       tristate
+       depends on ACPI_WMI
+
 config DELL_WMI_AIO
        tristate "WMI Hotkeys for Dell All-In-One series"
        depends on ACPI_WMI
@@ -426,7 +450,6 @@ config THINKPAD_ACPI_ALSA_SUPPORT
 config THINKPAD_ACPI_DEBUGFACILITIES
        bool "Maintainer debug facilities"
        depends on THINKPAD_ACPI
-       default n
        ---help---
          Enables extra stuff in the thinkpad-acpi which is completely useless
          for normal use.  Read the driver source to find out what it does.
@@ -437,7 +460,6 @@ config THINKPAD_ACPI_DEBUGFACILITIES
 config THINKPAD_ACPI_DEBUG
        bool "Verbose debug mode"
        depends on THINKPAD_ACPI
-       default n
        ---help---
          Enables extra debugging information, at the expense of a slightly
          increase in driver size.
@@ -447,7 +469,6 @@ config THINKPAD_ACPI_DEBUG
 config THINKPAD_ACPI_UNSAFE_LEDS
        bool "Allow control of important LEDs (unsafe)"
        depends on THINKPAD_ACPI
-       default n
        ---help---
          Overriding LED state on ThinkPads can mask important
          firmware alerts (like critical battery condition), or misled
@@ -515,7 +536,6 @@ config SENSORS_HDAPS
        tristate "Thinkpad Hard Drive Active Protection System (hdaps)"
        depends on INPUT
        select INPUT_POLLDEV
-       default n
        help
          This driver provides support for the IBM Hard Drive Active Protection
          System (hdaps), which provides an accelerometer and other misc. data.
@@ -658,6 +678,18 @@ config WMI_BMOF
          To compile this driver as a module, choose M here: the module will
          be called wmi-bmof.
 
+config INTEL_WMI_THUNDERBOLT
+       tristate "Intel WMI thunderbolt force power driver"
+       depends on ACPI_WMI
+       ---help---
+         Say Y here if you want to be able to use the WMI interface on select
+         systems to force the power control of Intel Thunderbolt controllers.
+         This is useful for updating the firmware when devices are not plugged
+         into the controller.
+
+         To compile this driver as a module, choose M here: the module will
+         be called intel-wmi-thunderbolt.
+
 config MSI_WMI
        tristate "MSI WMI extras"
        depends on ACPI_WMI
@@ -763,7 +795,6 @@ config TOSHIBA_HAPS
 
 config TOSHIBA_WMI
        tristate "Toshiba WMI Hotkeys Driver (EXPERIMENTAL)"
-       default n
        depends on ACPI_WMI
        depends on INPUT
        select INPUT_SPARSEKMAP
@@ -785,7 +816,6 @@ config ACPI_CMPC
        depends on RFKILL || RFKILL=n
        select INPUT
        select BACKLIGHT_CLASS_DEVICE
-       default n
        help
          Support for Intel Classmate PC ACPI devices, including some
          keys as input device, backlight device, tablet and accelerometer
@@ -793,7 +823,7 @@ config ACPI_CMPC
 
 config INTEL_CHT_INT33FE
        tristate "Intel Cherry Trail ACPI INT33FE Driver"
-       depends on X86 && ACPI && I2C
+       depends on X86 && ACPI && I2C && REGULATOR
        ---help---
          This driver add support for the INT33FE ACPI device found on
          some Intel Cherry Trail devices.
@@ -804,6 +834,10 @@ config INTEL_CHT_INT33FE
          This driver instantiates i2c-clients for these, so that standard
          i2c drivers for these chips can bind to the them.
 
+         If you enable this driver it is advised to also select
+         CONFIG_TYPEC_FUSB302=m, CONFIG_CHARGER_BQ24190=m and
+         CONFIG_BATTERY_MAX17042=m.
+
 config INTEL_INT0002_VGPIO
        tristate "Intel ACPI INT0002 Virtual GPIO driver"
        depends on GPIOLIB && ACPI
@@ -892,7 +926,6 @@ config INTEL_IPS
 
 config INTEL_IMR
        bool "Intel Isolated Memory Region support"
-       default n
        depends on X86_INTEL_QUARK && IOSF_MBI
        ---help---
          This option provides a means to manipulate Isolated Memory Regions.
@@ -1088,7 +1121,6 @@ config INTEL_PUNIT_IPC
 
 config INTEL_TELEMETRY
        tristate "Intel SoC Telemetry Driver"
-       default n
        depends on INTEL_PMC_IPC && INTEL_PUNIT_IPC && X86_64
        ---help---
          This driver provides interfaces to configure and use
@@ -1111,7 +1143,6 @@ config MLX_PLATFORM
 
 config MLX_CPLD_PLATFORM
        tristate "Mellanox platform hotplug driver support"
-       default n
        select HWMON
        select I2C
        ---help---
index f9e3ae683bbe3729757d36f19e02df3c2c5ea8f9..c32b34a724679435d134615c42a9c3f3855cf463 100644 (file)
@@ -13,8 +13,11 @@ obj-$(CONFIG_MSI_LAPTOP)     += msi-laptop.o
 obj-$(CONFIG_ACPI_CMPC)                += classmate-laptop.o
 obj-$(CONFIG_COMPAL_LAPTOP)    += compal-laptop.o
 obj-$(CONFIG_DELL_SMBIOS)      += dell-smbios.o
+obj-$(CONFIG_DELL_SMBIOS_WMI)  += dell-smbios-wmi.o
+obj-$(CONFIG_DELL_SMBIOS_SMM)  += dell-smbios-smm.o
 obj-$(CONFIG_DELL_LAPTOP)      += dell-laptop.o
 obj-$(CONFIG_DELL_WMI)         += dell-wmi.o
+obj-$(CONFIG_DELL_WMI_DESCRIPTOR)      += dell-wmi-descriptor.o
 obj-$(CONFIG_DELL_WMI_AIO)     += dell-wmi-aio.o
 obj-$(CONFIG_DELL_WMI_LED)     += dell-wmi-led.o
 obj-$(CONFIG_DELL_SMO8800)     += dell-smo8800.o
@@ -40,6 +43,7 @@ obj-$(CONFIG_PEAQ_WMI)                += peaq-wmi.o
 obj-$(CONFIG_SURFACE3_WMI)     += surface3-wmi.o
 obj-$(CONFIG_TOPSTAR_LAPTOP)   += topstar-laptop.o
 obj-$(CONFIG_WMI_BMOF)         += wmi-bmof.o
+obj-$(CONFIG_INTEL_WMI_THUNDERBOLT)    += intel-wmi-thunderbolt.o
 
 # toshiba_acpi must link after wmi to ensure that wmi devices are found
 # before toshiba_acpi initializes
index 48e1541dc8d4efa3cf24e9c0df5861aeaa431798..a32c5c00e0e7bdf9868e81c655f30f8fb6cc34be 100644 (file)
@@ -119,6 +119,7 @@ MODULE_LICENSE("GPL");
 #define ASUS_WMI_DEVID_BRIGHTNESS      0x00050012
 #define ASUS_WMI_DEVID_KBD_BACKLIGHT   0x00050021
 #define ASUS_WMI_DEVID_LIGHT_SENSOR    0x00050022 /* ?? */
+#define ASUS_WMI_DEVID_LIGHTBAR                0x00050025
 
 /* Misc */
 #define ASUS_WMI_DEVID_CAMERA          0x00060013
@@ -148,6 +149,7 @@ MODULE_LICENSE("GPL");
 #define ASUS_WMI_DSTS_BIOS_BIT         0x00040000
 #define ASUS_WMI_DSTS_BRIGHTNESS_MASK  0x000000FF
 #define ASUS_WMI_DSTS_MAX_BRIGTH_MASK  0x0000FF00
+#define ASUS_WMI_DSTS_LIGHTBAR_MASK    0x0000000F
 
 #define ASUS_FAN_DESC                  "cpu_fan"
 #define ASUS_FAN_MFUN                  0x13
@@ -222,10 +224,13 @@ struct asus_wmi {
        int tpd_led_wk;
        struct led_classdev kbd_led;
        int kbd_led_wk;
+       struct led_classdev lightbar_led;
+       int lightbar_led_wk;
        struct workqueue_struct *led_workqueue;
        struct work_struct tpd_led_work;
        struct work_struct kbd_led_work;
        struct work_struct wlan_led_work;
+       struct work_struct lightbar_led_work;
 
        struct asus_rfkill wlan;
        struct asus_rfkill bluetooth;
@@ -567,6 +572,48 @@ static enum led_brightness wlan_led_get(struct led_classdev *led_cdev)
        return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
 }
 
+static void lightbar_led_update(struct work_struct *work)
+{
+       struct asus_wmi *asus;
+       int ctrl_param;
+
+       asus = container_of(work, struct asus_wmi, lightbar_led_work);
+
+       ctrl_param = asus->lightbar_led_wk;
+       asus_wmi_set_devstate(ASUS_WMI_DEVID_LIGHTBAR, ctrl_param, NULL);
+}
+
+static void lightbar_led_set(struct led_classdev *led_cdev,
+                            enum led_brightness value)
+{
+       struct asus_wmi *asus;
+
+       asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
+
+       asus->lightbar_led_wk = !!value;
+       queue_work(asus->led_workqueue, &asus->lightbar_led_work);
+}
+
+static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev)
+{
+       struct asus_wmi *asus;
+       u32 result;
+
+       asus = container_of(led_cdev, struct asus_wmi, lightbar_led);
+       asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);
+
+       return result & ASUS_WMI_DSTS_LIGHTBAR_MASK;
+}
+
+static int lightbar_led_presence(struct asus_wmi *asus)
+{
+       u32 result;
+
+       asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_LIGHTBAR, &result);
+
+       return result & ASUS_WMI_DSTS_PRESENCE_BIT;
+}
+
 static void asus_wmi_led_exit(struct asus_wmi *asus)
 {
        if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
@@ -575,6 +622,8 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)
                led_classdev_unregister(&asus->tpd_led);
        if (!IS_ERR_OR_NULL(asus->wlan_led.dev))
                led_classdev_unregister(&asus->wlan_led);
+       if (!IS_ERR_OR_NULL(asus->lightbar_led.dev))
+               led_classdev_unregister(&asus->lightbar_led);
        if (asus->led_workqueue)
                destroy_workqueue(asus->led_workqueue);
 }
@@ -630,6 +679,20 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
 
                rv = led_classdev_register(&asus->platform_device->dev,
                                           &asus->wlan_led);
+               if (rv)
+                       goto error;
+       }
+
+       if (lightbar_led_presence(asus)) {
+               INIT_WORK(&asus->lightbar_led_work, lightbar_led_update);
+
+               asus->lightbar_led.name = "asus::lightbar";
+               asus->lightbar_led.brightness_set = lightbar_led_set;
+               asus->lightbar_led.brightness_get = lightbar_led_get;
+               asus->lightbar_led.max_brightness = 1;
+
+               rv = led_classdev_register(&asus->platform_device->dev,
+                                          &asus->lightbar_led);
        }
 
 error:
index f42159fd20318beb15b6495fa9c3027e47326c2a..2d704361f67284304b17a781ea8b45da617211be 100644 (file)
 #include "dell-rbtn.h"
 #include "dell-smbios.h"
 
-#define BRIGHTNESS_TOKEN 0x7d
-#define KBD_LED_OFF_TOKEN 0x01E1
-#define KBD_LED_ON_TOKEN 0x01E2
-#define KBD_LED_AUTO_TOKEN 0x01E3
-#define KBD_LED_AUTO_25_TOKEN 0x02EA
-#define KBD_LED_AUTO_50_TOKEN 0x02EB
-#define KBD_LED_AUTO_75_TOKEN 0x02EC
-#define KBD_LED_AUTO_100_TOKEN 0x02F6
-#define GLOBAL_MIC_MUTE_ENABLE 0x0364
-#define GLOBAL_MIC_MUTE_DISABLE 0x0365
-#define KBD_LED_AC_TOKEN 0x0451
-
 struct quirk_entry {
        u8 touchpad_led;
 
@@ -85,6 +73,7 @@ static struct platform_driver platform_driver = {
        }
 };
 
+static struct calling_interface_buffer *buffer;
 static struct platform_device *platform_device;
 static struct backlight_device *dell_backlight_device;
 static struct rfkill *wifi_rfkill;
@@ -283,6 +272,27 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
        { }
 };
 
+void dell_set_arguments(u32 arg0, u32 arg1, u32 arg2, u32 arg3)
+{
+       memset(buffer, 0, sizeof(struct calling_interface_buffer));
+       buffer->input[0] = arg0;
+       buffer->input[1] = arg1;
+       buffer->input[2] = arg2;
+       buffer->input[3] = arg3;
+}
+
+int dell_send_request(u16 class, u16 select)
+{
+       int ret;
+
+       buffer->cmd_class = class;
+       buffer->cmd_select = select;
+       ret = dell_smbios_call(buffer);
+       if (ret != 0)
+               return ret;
+       return dell_smbios_error(buffer->output[0]);
+}
+
 /*
  * Derived from information in smbios-wireless-ctl:
  *
@@ -405,7 +415,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
 
 static int dell_rfkill_set(void *data, bool blocked)
 {
-       struct calling_interface_buffer *buffer;
        int disable = blocked ? 1 : 0;
        unsigned long radio = (unsigned long)data;
        int hwswitch_bit = (unsigned long)data - 1;
@@ -413,20 +422,16 @@ static int dell_rfkill_set(void *data, bool blocked)
        int status;
        int ret;
 
-       buffer = dell_smbios_get_buffer();
-
-       dell_smbios_send_request(17, 11);
-       ret = buffer->output[0];
+       dell_set_arguments(0, 0, 0, 0);
+       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       if (ret)
+               return ret;
        status = buffer->output[1];
 
-       if (ret != 0)
-               goto out;
-
-       dell_smbios_clear_buffer();
-
-       buffer->input[0] = 0x2;
-       dell_smbios_send_request(17, 11);
-       ret = buffer->output[0];
+       dell_set_arguments(0x2, 0, 0, 0);
+       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       if (ret)
+               return ret;
        hwswitch = buffer->output[1];
 
        /* If the hardware switch controls this radio, and the hardware
@@ -435,28 +440,19 @@ static int dell_rfkill_set(void *data, bool blocked)
            (status & BIT(0)) && !(status & BIT(16)))
                disable = 1;
 
-       dell_smbios_clear_buffer();
-
-       buffer->input[0] = (1 | (radio<<8) | (disable << 16));
-       dell_smbios_send_request(17, 11);
-       ret = buffer->output[0];
-
- out:
-       dell_smbios_release_buffer();
-       return dell_smbios_error(ret);
+       dell_set_arguments(1 | (radio<<8) | (disable << 16), 0, 0, 0);
+       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       return ret;
 }
 
-/* Must be called with the buffer held */
 static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
-                                       int status,
-                                       struct calling_interface_buffer *buffer)
+                                       int status)
 {
        if (status & BIT(0)) {
                /* Has hw-switch, sync sw_state to BIOS */
                int block = rfkill_blocked(rfkill);
-               dell_smbios_clear_buffer();
-               buffer->input[0] = (1 | (radio << 8) | (block << 16));
-               dell_smbios_send_request(17, 11);
+               dell_set_arguments(1 | (radio << 8) | (block << 16), 0, 0, 0);
+               dell_send_request(CLASS_INFO, SELECT_RFKILL);
        } else {
                /* No hw-switch, sync BIOS state to sw_state */
                rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
@@ -472,32 +468,23 @@ static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
 
 static void dell_rfkill_query(struct rfkill *rfkill, void *data)
 {
-       struct calling_interface_buffer *buffer;
        int radio = ((unsigned long)data & 0xF);
        int hwswitch;
        int status;
        int ret;
 
-       buffer = dell_smbios_get_buffer();
-
-       dell_smbios_send_request(17, 11);
-       ret = buffer->output[0];
+       dell_set_arguments(0, 0, 0, 0);
+       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
        status = buffer->output[1];
 
        if (ret != 0 || !(status & BIT(0))) {
-               dell_smbios_release_buffer();
                return;
        }
 
-       dell_smbios_clear_buffer();
-
-       buffer->input[0] = 0x2;
-       dell_smbios_send_request(17, 11);
-       ret = buffer->output[0];
+       dell_set_arguments(0, 0x2, 0, 0);
+       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
        hwswitch = buffer->output[1];
 
-       dell_smbios_release_buffer();
-
        if (ret != 0)
                return;
 
@@ -513,27 +500,23 @@ static struct dentry *dell_laptop_dir;
 
 static int dell_debugfs_show(struct seq_file *s, void *data)
 {
-       struct calling_interface_buffer *buffer;
        int hwswitch_state;
        int hwswitch_ret;
        int status;
        int ret;
 
-       buffer = dell_smbios_get_buffer();
-
-       dell_smbios_send_request(17, 11);
-       ret = buffer->output[0];
+       dell_set_arguments(0, 0, 0, 0);
+       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       if (ret)
+               return ret;
        status = buffer->output[1];
 
-       dell_smbios_clear_buffer();
-
-       buffer->input[0] = 0x2;
-       dell_smbios_send_request(17, 11);
-       hwswitch_ret = buffer->output[0];
+       dell_set_arguments(0, 0x2, 0, 0);
+       hwswitch_ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
+       if (hwswitch_ret)
+               return hwswitch_ret;
        hwswitch_state = buffer->output[1];
 
-       dell_smbios_release_buffer();
-
        seq_printf(s, "return:\t%d\n", ret);
        seq_printf(s, "status:\t0x%X\n", status);
        seq_printf(s, "Bit 0 : Hardware switch supported:   %lu\n",
@@ -613,46 +596,36 @@ static const struct file_operations dell_debugfs_fops = {
 
 static void dell_update_rfkill(struct work_struct *ignored)
 {
-       struct calling_interface_buffer *buffer;
        int hwswitch = 0;
        int status;
        int ret;
 
-       buffer = dell_smbios_get_buffer();
-
-       dell_smbios_send_request(17, 11);
-       ret = buffer->output[0];
+       dell_set_arguments(0, 0, 0, 0);
+       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
        status = buffer->output[1];
 
        if (ret != 0)
-               goto out;
-
-       dell_smbios_clear_buffer();
+               return;
 
-       buffer->input[0] = 0x2;
-       dell_smbios_send_request(17, 11);
-       ret = buffer->output[0];
+       dell_set_arguments(0, 0x2, 0, 0);
+       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
 
        if (ret == 0 && (status & BIT(0)))
                hwswitch = buffer->output[1];
 
        if (wifi_rfkill) {
                dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
-               dell_rfkill_update_sw_state(wifi_rfkill, 1, status, buffer);
+               dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
        }
        if (bluetooth_rfkill) {
                dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
                                            hwswitch);
-               dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status,
-                                           buffer);
+               dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
        }
        if (wwan_rfkill) {
                dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
-               dell_rfkill_update_sw_state(wwan_rfkill, 3, status, buffer);
+               dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
        }
-
- out:
-       dell_smbios_release_buffer();
 }
 static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
 
@@ -696,7 +669,6 @@ static struct notifier_block dell_laptop_rbtn_notifier = {
 
 static int __init dell_setup_rfkill(void)
 {
-       struct calling_interface_buffer *buffer;
        int status, ret, whitelisted;
        const char *product;
 
@@ -712,11 +684,9 @@ static int __init dell_setup_rfkill(void)
        if (!force_rfkill && !whitelisted)
                return 0;
 
-       buffer = dell_smbios_get_buffer();
-       dell_smbios_send_request(17, 11);
-       ret = buffer->output[0];
+       dell_set_arguments(0, 0, 0, 0);
+       ret = dell_send_request(CLASS_INFO, SELECT_RFKILL);
        status = buffer->output[1];
-       dell_smbios_release_buffer();
 
        /* dell wireless info smbios call is not supported */
        if (ret != 0)
@@ -869,7 +839,6 @@ static void dell_cleanup_rfkill(void)
 
 static int dell_send_intensity(struct backlight_device *bd)
 {
-       struct calling_interface_buffer *buffer;
        struct calling_interface_token *token;
        int ret;
 
@@ -877,24 +846,17 @@ static int dell_send_intensity(struct backlight_device *bd)
        if (!token)
                return -ENODEV;
 
-       buffer = dell_smbios_get_buffer();
-       buffer->input[0] = token->location;
-       buffer->input[1] = bd->props.brightness;
-
+       dell_set_arguments(token->location, bd->props.brightness, 0, 0);
        if (power_supply_is_system_supplied() > 0)
-               dell_smbios_send_request(1, 2);
+               ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_AC);
        else
-               dell_smbios_send_request(1, 1);
+               ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_BAT);
 
-       ret = dell_smbios_error(buffer->output[0]);
-
-       dell_smbios_release_buffer();
        return ret;
 }
 
 static int dell_get_intensity(struct backlight_device *bd)
 {
-       struct calling_interface_buffer *buffer;
        struct calling_interface_token *token;
        int ret;
 
@@ -902,20 +864,14 @@ static int dell_get_intensity(struct backlight_device *bd)
        if (!token)
                return -ENODEV;
 
-       buffer = dell_smbios_get_buffer();
-       buffer->input[0] = token->location;
-
+       dell_set_arguments(token->location, 0, 0, 0);
        if (power_supply_is_system_supplied() > 0)
-               dell_smbios_send_request(0, 2);
+               ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC);
        else
-               dell_smbios_send_request(0, 1);
+               ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_BAT);
 
-       if (buffer->output[0])
-               ret = dell_smbios_error(buffer->output[0]);
-       else
+       if (ret == 0)
                ret = buffer->output[1];
-
-       dell_smbios_release_buffer();
        return ret;
 }
 
@@ -1179,20 +1135,13 @@ static DEFINE_MUTEX(kbd_led_mutex);
 
 static int kbd_get_info(struct kbd_info *info)
 {
-       struct calling_interface_buffer *buffer;
        u8 units;
        int ret;
 
-       buffer = dell_smbios_get_buffer();
-
-       buffer->input[0] = 0x0;
-       dell_smbios_send_request(4, 11);
-       ret = buffer->output[0];
-
-       if (ret) {
-               ret = dell_smbios_error(ret);
-               goto out;
-       }
+       dell_set_arguments(0, 0, 0, 0);
+       ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
+       if (ret)
+               return ret;
 
        info->modes = buffer->output[1] & 0xFFFF;
        info->type = (buffer->output[1] >> 24) & 0xFF;
@@ -1209,8 +1158,6 @@ static int kbd_get_info(struct kbd_info *info)
        if (units & BIT(3))
                info->days = (buffer->output[3] >> 24) & 0xFF;
 
- out:
-       dell_smbios_release_buffer();
        return ret;
 }
 
@@ -1269,19 +1216,12 @@ static int kbd_set_level(struct kbd_state *state, u8 level)
 
 static int kbd_get_state(struct kbd_state *state)
 {
-       struct calling_interface_buffer *buffer;
        int ret;
 
-       buffer = dell_smbios_get_buffer();
-
-       buffer->input[0] = 0x1;
-       dell_smbios_send_request(4, 11);
-       ret = buffer->output[0];
-
-       if (ret) {
-               ret = dell_smbios_error(ret);
-               goto out;
-       }
+       dell_set_arguments(0x1, 0, 0, 0);
+       ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
+       if (ret)
+               return ret;
 
        state->mode_bit = ffs(buffer->output[1] & 0xFFFF);
        if (state->mode_bit != 0)
@@ -1296,31 +1236,27 @@ static int kbd_get_state(struct kbd_state *state)
        state->timeout_value_ac = (buffer->output[2] >> 24) & 0x3F;
        state->timeout_unit_ac = (buffer->output[2] >> 30) & 0x3;
 
- out:
-       dell_smbios_release_buffer();
        return ret;
 }
 
 static int kbd_set_state(struct kbd_state *state)
 {
-       struct calling_interface_buffer *buffer;
        int ret;
+       u32 input1;
+       u32 input2;
+
+       input1 = BIT(state->mode_bit) & 0xFFFF;
+       input1 |= (state->triggers & 0xFF) << 16;
+       input1 |= (state->timeout_value & 0x3F) << 24;
+       input1 |= (state->timeout_unit & 0x3) << 30;
+       input2 = state->als_setting & 0xFF;
+       input2 |= (state->level & 0xFF) << 16;
+       input2 |= (state->timeout_value_ac & 0x3F) << 24;
+       input2 |= (state->timeout_unit_ac & 0x3) << 30;
+       dell_set_arguments(0x2, input1, input2, 0);
+       ret = dell_send_request(CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT);
 
-       buffer = dell_smbios_get_buffer();
-       buffer->input[0] = 0x2;
-       buffer->input[1] = BIT(state->mode_bit) & 0xFFFF;
-       buffer->input[1] |= (state->triggers & 0xFF) << 16;
-       buffer->input[1] |= (state->timeout_value & 0x3F) << 24;
-       buffer->input[1] |= (state->timeout_unit & 0x3) << 30;
-       buffer->input[2] = state->als_setting & 0xFF;
-       buffer->input[2] |= (state->level & 0xFF) << 16;
-       buffer->input[2] |= (state->timeout_value_ac & 0x3F) << 24;
-       buffer->input[2] |= (state->timeout_unit_ac & 0x3) << 30;
-       dell_smbios_send_request(4, 11);
-       ret = buffer->output[0];
-       dell_smbios_release_buffer();
-
-       return dell_smbios_error(ret);
+       return ret;
 }
 
 static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
@@ -1345,7 +1281,6 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
 
 static int kbd_set_token_bit(u8 bit)
 {
-       struct calling_interface_buffer *buffer;
        struct calling_interface_token *token;
        int ret;
 
@@ -1356,19 +1291,14 @@ static int kbd_set_token_bit(u8 bit)
        if (!token)
                return -EINVAL;
 
-       buffer = dell_smbios_get_buffer();
-       buffer->input[0] = token->location;
-       buffer->input[1] = token->value;
-       dell_smbios_send_request(1, 0);
-       ret = buffer->output[0];
-       dell_smbios_release_buffer();
+       dell_set_arguments(token->location, token->value, 0, 0);
+       ret = dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
 
-       return dell_smbios_error(ret);
+       return ret;
 }
 
 static int kbd_get_token_bit(u8 bit)
 {
-       struct calling_interface_buffer *buffer;
        struct calling_interface_token *token;
        int ret;
        int val;
@@ -1380,15 +1310,12 @@ static int kbd_get_token_bit(u8 bit)
        if (!token)
                return -EINVAL;
 
-       buffer = dell_smbios_get_buffer();
-       buffer->input[0] = token->location;
-       dell_smbios_send_request(0, 0);
-       ret = buffer->output[0];
+       dell_set_arguments(token->location, 0, 0, 0);
+       ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_STD);
        val = buffer->output[1];
-       dell_smbios_release_buffer();
 
        if (ret)
-               return dell_smbios_error(ret);
+               return ret;
 
        return (val == token->value);
 }
@@ -2102,7 +2029,6 @@ static struct notifier_block dell_laptop_notifier = {
 
 int dell_micmute_led_set(int state)
 {
-       struct calling_interface_buffer *buffer;
        struct calling_interface_token *token;
 
        if (state == 0)
@@ -2115,11 +2041,8 @@ int dell_micmute_led_set(int state)
        if (!token)
                return -ENODEV;
 
-       buffer = dell_smbios_get_buffer();
-       buffer->input[0] = token->location;
-       buffer->input[1] = token->value;
-       dell_smbios_send_request(1, 0);
-       dell_smbios_release_buffer();
+       dell_set_arguments(token->location, token->value, 0, 0);
+       dell_send_request(CLASS_TOKEN_WRITE, SELECT_TOKEN_STD);
 
        return state;
 }
@@ -2127,7 +2050,6 @@ EXPORT_SYMBOL_GPL(dell_micmute_led_set);
 
 static int __init dell_init(void)
 {
-       struct calling_interface_buffer *buffer;
        struct calling_interface_token *token;
        int max_intensity = 0;
        int ret;
@@ -2151,6 +2073,11 @@ static int __init dell_init(void)
        if (ret)
                goto fail_platform_device2;
 
+       buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL);
+       if (!buffer)
+               goto fail_buffer;
+
+
        ret = dell_setup_rfkill();
 
        if (ret) {
@@ -2175,12 +2102,10 @@ static int __init dell_init(void)
 
        token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
        if (token) {
-               buffer = dell_smbios_get_buffer();
-               buffer->input[0] = token->location;
-               dell_smbios_send_request(0, 2);
-               if (buffer->output[0] == 0)
+               dell_set_arguments(token->location, 0, 0, 0);
+               ret = dell_send_request(CLASS_TOKEN_READ, SELECT_TOKEN_AC);
+               if (ret)
                        max_intensity = buffer->output[3];
-               dell_smbios_release_buffer();
        }
 
        if (max_intensity) {
@@ -2214,6 +2139,8 @@ static int __init dell_init(void)
 fail_get_brightness:
        backlight_device_unregister(dell_backlight_device);
 fail_backlight:
+       kfree(buffer);
+fail_buffer:
        dell_cleanup_rfkill();
 fail_rfkill:
        platform_device_del(platform_device);
@@ -2233,6 +2160,7 @@ static void __exit dell_exit(void)
                touchpad_led_exit();
        kbd_led_exit();
        backlight_device_unregister(dell_backlight_device);
+       kfree(buffer);
        dell_cleanup_rfkill();
        if (platform_device) {
                platform_device_unregister(platform_device);
diff --git a/drivers/platform/x86/dell-smbios-smm.c b/drivers/platform/x86/dell-smbios-smm.c
new file mode 100644 (file)
index 0000000..89f65c4
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ *  SMI methods for use with dell-smbios
+ *
+ *  Copyright (c) Red Hat <mjg@redhat.com>
+ *  Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
+ *  Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
+ *  Copyright (c) 2017 Dell 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.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/dmi.h>
+#include <linux/gfp.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include "../../firmware/dcdbas.h"
+#include "dell-smbios.h"
+
+static int da_command_address;
+static int da_command_code;
+static struct calling_interface_buffer *buffer;
+struct platform_device *platform_device;
+static DEFINE_MUTEX(smm_mutex);
+
+static const struct dmi_system_id dell_device_table[] __initconst = {
+       {
+               .ident = "Dell laptop",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /*Notebook*/
+               },
+       },
+       {
+               .ident = "Dell Computer Corporation",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+                       DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
+               },
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(dmi, dell_device_table);
+
+static void __init parse_da_table(const struct dmi_header *dm)
+{
+       struct calling_interface_structure *table =
+               container_of(dm, struct calling_interface_structure, header);
+
+       /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
+        * 6 bytes of entry
+        */
+       if (dm->length < 17)
+               return;
+
+       da_command_address = table->cmdIOAddress;
+       da_command_code = table->cmdIOCode;
+}
+
+static void __init find_cmd_address(const struct dmi_header *dm, void *dummy)
+{
+       switch (dm->type) {
+       case 0xda: /* Calling interface */
+               parse_da_table(dm);
+               break;
+       }
+}
+
+int dell_smbios_smm_call(struct calling_interface_buffer *input)
+{
+       struct smi_cmd command;
+       size_t size;
+
+       size = sizeof(struct calling_interface_buffer);
+       command.magic = SMI_CMD_MAGIC;
+       command.command_address = da_command_address;
+       command.command_code = da_command_code;
+       command.ebx = virt_to_phys(buffer);
+       command.ecx = 0x42534931;
+
+       mutex_lock(&smm_mutex);
+       memcpy(buffer, input, size);
+       dcdbas_smi_request(&command);
+       memcpy(input, buffer, size);
+       mutex_unlock(&smm_mutex);
+       return 0;
+}
+
+/* When enabled this indicates that SMM won't work */
+static bool test_wsmt_enabled(void)
+{
+       struct calling_interface_token *wsmt;
+
+       /* if token doesn't exist, SMM will work */
+       wsmt = dell_smbios_find_token(WSMT_EN_TOKEN);
+       if (!wsmt)
+               return false;
+
+       /* If token exists, try to access over SMM but set a dummy return.
+        * - If WSMT disabled it will be overwritten by SMM
+        * - If WSMT enabled then dummy value will remain
+        */
+       buffer->cmd_class = CLASS_TOKEN_READ;
+       buffer->cmd_select = SELECT_TOKEN_STD;
+       memset(buffer, 0, sizeof(struct calling_interface_buffer));
+       buffer->input[0] = wsmt->location;
+       buffer->output[0] = 99;
+       dell_smbios_smm_call(buffer);
+       if (buffer->output[0] == 99)
+               return true;
+
+       return false;
+}
+
+static int __init dell_smbios_smm_init(void)
+{
+       int ret;
+       /*
+        * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
+        * is passed to SMI handler.
+        */
+       buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
+       if (!buffer)
+               return -ENOMEM;
+
+       dmi_walk(find_cmd_address, NULL);
+
+       if (test_wsmt_enabled()) {
+               pr_debug("Disabling due to WSMT enabled\n");
+               ret = -ENODEV;
+               goto fail_wsmt;
+       }
+
+       platform_device = platform_device_alloc("dell-smbios", 1);
+       if (!platform_device) {
+               ret = -ENOMEM;
+               goto fail_platform_device_alloc;
+       }
+
+       ret = platform_device_add(platform_device);
+       if (ret)
+               goto fail_platform_device_add;
+
+       ret = dell_smbios_register_device(&platform_device->dev,
+                                         &dell_smbios_smm_call);
+       if (ret)
+               goto fail_register;
+
+       return 0;
+
+fail_register:
+       platform_device_del(platform_device);
+
+fail_platform_device_add:
+       platform_device_put(platform_device);
+
+fail_wsmt:
+fail_platform_device_alloc:
+       free_page((unsigned long)buffer);
+       return ret;
+}
+
+static void __exit dell_smbios_smm_exit(void)
+{
+       if (platform_device) {
+               dell_smbios_unregister_device(&platform_device->dev);
+               platform_device_unregister(platform_device);
+               free_page((unsigned long)buffer);
+       }
+}
+
+subsys_initcall(dell_smbios_smm_init);
+module_exit(dell_smbios_smm_exit);
+
+MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
+MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
+MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
+MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
+MODULE_DESCRIPTION("Dell SMBIOS communications over SMI");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell-smbios-wmi.c b/drivers/platform/x86/dell-smbios-wmi.c
new file mode 100644 (file)
index 0000000..0cab1f9
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ *  WMI methods for use with dell-smbios
+ *
+ *  Copyright (c) 2017 Dell 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.
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/dmi.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/wmi.h>
+#include "dell-smbios.h"
+#include "dell-wmi-descriptor.h"
+
+static DEFINE_MUTEX(call_mutex);
+static DEFINE_MUTEX(list_mutex);
+static int wmi_supported;
+
+struct misc_bios_flags_structure {
+       struct dmi_header header;
+       u16 flags0;
+} __packed;
+#define FLAG_HAS_ACPI_WMI 0x02
+
+#define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
+
+struct wmi_smbios_priv {
+       struct dell_wmi_smbios_buffer *buf;
+       struct list_head list;
+       struct wmi_device *wdev;
+       struct device *child;
+       u32 req_buf_size;
+};
+static LIST_HEAD(wmi_list);
+
+static inline struct wmi_smbios_priv *get_first_smbios_priv(void)
+{
+       return list_first_entry_or_null(&wmi_list,
+                                       struct wmi_smbios_priv,
+                                       list);
+}
+
+static int run_smbios_call(struct wmi_device *wdev)
+{
+       struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+       struct wmi_smbios_priv *priv;
+       struct acpi_buffer input;
+       union acpi_object *obj;
+       acpi_status status;
+
+       priv = dev_get_drvdata(&wdev->dev);
+       input.length = priv->req_buf_size - sizeof(u64);
+       input.pointer = &priv->buf->std;
+
+       dev_dbg(&wdev->dev, "evaluating: %u/%u [%x,%x,%x,%x]\n",
+               priv->buf->std.cmd_class, priv->buf->std.cmd_select,
+               priv->buf->std.input[0], priv->buf->std.input[1],
+               priv->buf->std.input[2], priv->buf->std.input[3]);
+
+       status = wmidev_evaluate_method(wdev, 0, 1, &input, &output);
+       if (ACPI_FAILURE(status))
+               return -EIO;
+       obj = (union acpi_object *)output.pointer;
+       if (obj->type != ACPI_TYPE_BUFFER) {
+               dev_dbg(&wdev->dev, "received type: %d\n", obj->type);
+               if (obj->type == ACPI_TYPE_INTEGER)
+                       dev_dbg(&wdev->dev, "SMBIOS call failed: %llu\n",
+                               obj->integer.value);
+               return -EIO;
+       }
+       memcpy(&priv->buf->std, obj->buffer.pointer, obj->buffer.length);
+       dev_dbg(&wdev->dev, "result: [%08x,%08x,%08x,%08x]\n",
+               priv->buf->std.output[0], priv->buf->std.output[1],
+               priv->buf->std.output[2], priv->buf->std.output[3]);
+
+       return 0;
+}
+
+int dell_smbios_wmi_call(struct calling_interface_buffer *buffer)
+{
+       struct wmi_smbios_priv *priv;
+       size_t difference;
+       size_t size;
+       int ret;
+
+       mutex_lock(&call_mutex);
+       priv = get_first_smbios_priv();
+       if (!priv) {
+               ret = -ENODEV;
+               goto out_wmi_call;
+       }
+
+       size = sizeof(struct calling_interface_buffer);
+       difference = priv->req_buf_size - sizeof(u64) - size;
+
+       memset(&priv->buf->ext, 0, difference);
+       memcpy(&priv->buf->std, buffer, size);
+       ret = run_smbios_call(priv->wdev);
+       memcpy(buffer, &priv->buf->std, size);
+out_wmi_call:
+       mutex_unlock(&call_mutex);
+
+       return ret;
+}
+
+static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd,
+                                  struct wmi_ioctl_buffer *arg)
+{
+       struct wmi_smbios_priv *priv;
+       int ret = 0;
+
+       switch (cmd) {
+       case DELL_WMI_SMBIOS_CMD:
+               mutex_lock(&call_mutex);
+               priv = dev_get_drvdata(&wdev->dev);
+               if (!priv) {
+                       ret = -ENODEV;
+                       goto fail_smbios_cmd;
+               }
+               memcpy(priv->buf, arg, priv->req_buf_size);
+               if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) {
+                       dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n",
+                               priv->buf->std.cmd_class,
+                               priv->buf->std.cmd_select,
+                               priv->buf->std.input[0]);
+                       ret = -EFAULT;
+                       goto fail_smbios_cmd;
+               }
+               ret = run_smbios_call(priv->wdev);
+               if (ret)
+                       goto fail_smbios_cmd;
+               memcpy(arg, priv->buf, priv->req_buf_size);
+fail_smbios_cmd:
+               mutex_unlock(&call_mutex);
+               break;
+       default:
+               ret = -ENOIOCTLCMD;
+       }
+       return ret;
+}
+
+static int dell_smbios_wmi_probe(struct wmi_device *wdev)
+{
+       struct wmi_smbios_priv *priv;
+       int count;
+       int ret;
+
+       ret = dell_wmi_get_descriptor_valid();
+       if (ret)
+               return ret;
+
+       priv = devm_kzalloc(&wdev->dev, sizeof(struct wmi_smbios_priv),
+                           GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       /* WMI buffer size will be either 4k or 32k depending on machine */
+       if (!dell_wmi_get_size(&priv->req_buf_size))
+               return -EPROBE_DEFER;
+
+       /* add in the length object we will use internally with ioctl */
+       priv->req_buf_size += sizeof(u64);
+       ret = set_required_buffer_size(wdev, priv->req_buf_size);
+       if (ret)
+               return ret;
+
+       count = get_order(priv->req_buf_size);
+       priv->buf = (void *)__get_free_pages(GFP_KERNEL, count);
+       if (!priv->buf)
+               return -ENOMEM;
+
+       /* ID is used by dell-smbios to set priority of drivers */
+       wdev->dev.id = 1;
+       ret = dell_smbios_register_device(&wdev->dev, &dell_smbios_wmi_call);
+       if (ret)
+               goto fail_register;
+
+       priv->wdev = wdev;
+       dev_set_drvdata(&wdev->dev, priv);
+       mutex_lock(&list_mutex);
+       list_add_tail(&priv->list, &wmi_list);
+       mutex_unlock(&list_mutex);
+
+       return 0;
+
+fail_register:
+       free_pages((unsigned long)priv->buf, count);
+       return ret;
+}
+
+static int dell_smbios_wmi_remove(struct wmi_device *wdev)
+{
+       struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev);
+       int count;
+
+       mutex_lock(&call_mutex);
+       mutex_lock(&list_mutex);
+       list_del(&priv->list);
+       mutex_unlock(&list_mutex);
+       dell_smbios_unregister_device(&wdev->dev);
+       count = get_order(priv->req_buf_size);
+       free_pages((unsigned long)priv->buf, count);
+       mutex_unlock(&call_mutex);
+       return 0;
+}
+
+static const struct wmi_device_id dell_smbios_wmi_id_table[] = {
+       { .guid_string = DELL_WMI_SMBIOS_GUID },
+       { },
+};
+
+static void __init parse_b1_table(const struct dmi_header *dm)
+{
+       struct misc_bios_flags_structure *flags =
+       container_of(dm, struct misc_bios_flags_structure, header);
+
+       /* 4 bytes header, 8 bytes flags */
+       if (dm->length < 12)
+               return;
+       if (dm->handle != 0xb100)
+               return;
+       if ((flags->flags0 & FLAG_HAS_ACPI_WMI))
+               wmi_supported = 1;
+}
+
+static void __init find_b1(const struct dmi_header *dm, void *dummy)
+{
+       switch (dm->type) {
+       case 0xb1: /* misc bios flags */
+               parse_b1_table(dm);
+               break;
+       }
+}
+
+static struct wmi_driver dell_smbios_wmi_driver = {
+       .driver = {
+               .name = "dell-smbios",
+       },
+       .probe = dell_smbios_wmi_probe,
+       .remove = dell_smbios_wmi_remove,
+       .id_table = dell_smbios_wmi_id_table,
+       .filter_callback = dell_smbios_wmi_filter,
+};
+
+static int __init init_dell_smbios_wmi(void)
+{
+       dmi_walk(find_b1, NULL);
+
+       if (!wmi_supported)
+               return -ENODEV;
+
+       return wmi_driver_register(&dell_smbios_wmi_driver);
+}
+
+static void __exit exit_dell_smbios_wmi(void)
+{
+       wmi_driver_unregister(&dell_smbios_wmi_driver);
+}
+
+module_init(init_dell_smbios_wmi);
+module_exit(exit_dell_smbios_wmi);
+
+MODULE_ALIAS("wmi:" DELL_WMI_SMBIOS_GUID);
+MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
+MODULE_DESCRIPTION("Dell SMBIOS communications over WMI");
+MODULE_LICENSE("GPL");
index 0a5723468bfffdd6f16df829e5131c90d7e38034..6a60db515bdabcb490410b35ea85bb7a3fd621f1 100644 (file)
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/capability.h>
 #include <linux/dmi.h>
 #include <linux/err.h>
-#include <linux/gfp.h>
 #include <linux/mutex.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
-#include <linux/io.h>
-#include "../../firmware/dcdbas.h"
 #include "dell-smbios.h"
 
-struct calling_interface_structure {
-       struct dmi_header header;
-       u16 cmdIOAddress;
-       u8 cmdIOCode;
-       u32 supportedCmds;
-       struct calling_interface_token tokens[];
-} __packed;
-
-static struct calling_interface_buffer *buffer;
-static DEFINE_MUTEX(buffer_mutex);
-
-static int da_command_address;
-static int da_command_code;
+static u32 da_supported_commands;
 static int da_num_tokens;
+static struct platform_device *platform_device;
 static struct calling_interface_token *da_tokens;
+static struct device_attribute *token_location_attrs;
+static struct device_attribute *token_value_attrs;
+static struct attribute **token_attrs;
+static DEFINE_MUTEX(smbios_mutex);
+
+struct smbios_device {
+       struct list_head list;
+       struct device *device;
+       int (*call_fn)(struct calling_interface_buffer *);
+};
+
+struct smbios_call {
+       u32 need_capability;
+       int cmd_class;
+       int cmd_select;
+};
+
+/* calls that are whitelisted for given capabilities */
+static struct smbios_call call_whitelist[] = {
+       /* generally tokens are allowed, but may be further filtered or
+        * restricted by token blacklist or whitelist
+        */
+       {CAP_SYS_ADMIN, CLASS_TOKEN_READ,       SELECT_TOKEN_STD},
+       {CAP_SYS_ADMIN, CLASS_TOKEN_READ,       SELECT_TOKEN_AC},
+       {CAP_SYS_ADMIN, CLASS_TOKEN_READ,       SELECT_TOKEN_BAT},
+       {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE,      SELECT_TOKEN_STD},
+       {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE,      SELECT_TOKEN_AC},
+       {CAP_SYS_ADMIN, CLASS_TOKEN_WRITE,      SELECT_TOKEN_BAT},
+       /* used by userspace: fwupdate */
+       {CAP_SYS_ADMIN, CLASS_ADMIN_PROP,       SELECT_ADMIN_PROP},
+       /* used by userspace: fwupd */
+       {CAP_SYS_ADMIN, CLASS_INFO,             SELECT_DOCK},
+       {CAP_SYS_ADMIN, CLASS_FLASH_INTERFACE,  SELECT_FLASH_INTERFACE},
+};
+
+/* calls that are explicitly blacklisted */
+static struct smbios_call call_blacklist[] = {
+       {0x0000, 01, 07}, /* manufacturing use */
+       {0x0000, 06, 05}, /* manufacturing use */
+       {0x0000, 11, 03}, /* write once */
+       {0x0000, 11, 07}, /* write once */
+       {0x0000, 11, 11}, /* write once */
+       {0x0000, 19, -1}, /* diagnostics */
+       /* handled by kernel: dell-laptop */
+       {0x0000, CLASS_INFO, SELECT_RFKILL},
+       {0x0000, CLASS_KBD_BACKLIGHT, SELECT_KBD_BACKLIGHT},
+};
+
+struct token_range {
+       u32 need_capability;
+       u16 min;
+       u16 max;
+};
+
+/* tokens that are whitelisted for given capabilities */
+static struct token_range token_whitelist[] = {
+       /* used by userspace: fwupdate */
+       {CAP_SYS_ADMIN, CAPSULE_EN_TOKEN,       CAPSULE_DIS_TOKEN},
+       /* can indicate to userspace that WMI is needed */
+       {0x0000,        WSMT_EN_TOKEN,          WSMT_DIS_TOKEN}
+};
+
+/* tokens that are explicitly blacklisted */
+static struct token_range token_blacklist[] = {
+       {0x0000, 0x0058, 0x0059}, /* ME use */
+       {0x0000, 0x00CD, 0x00D0}, /* raid shadow copy */
+       {0x0000, 0x013A, 0x01FF}, /* sata shadow copy */
+       {0x0000, 0x0175, 0x0176}, /* write once */
+       {0x0000, 0x0195, 0x0197}, /* diagnostics */
+       {0x0000, 0x01DC, 0x01DD}, /* manufacturing use */
+       {0x0000, 0x027D, 0x0284}, /* diagnostics */
+       {0x0000, 0x02E3, 0x02E3}, /* manufacturing use */
+       {0x0000, 0x02FF, 0x02FF}, /* manufacturing use */
+       {0x0000, 0x0300, 0x0302}, /* manufacturing use */
+       {0x0000, 0x0325, 0x0326}, /* manufacturing use */
+       {0x0000, 0x0332, 0x0335}, /* fan control */
+       {0x0000, 0x0350, 0x0350}, /* manufacturing use */
+       {0x0000, 0x0363, 0x0363}, /* manufacturing use */
+       {0x0000, 0x0368, 0x0368}, /* manufacturing use */
+       {0x0000, 0x03F6, 0x03F7}, /* manufacturing use */
+       {0x0000, 0x049E, 0x049F}, /* manufacturing use */
+       {0x0000, 0x04A0, 0x04A3}, /* disagnostics */
+       {0x0000, 0x04E6, 0x04E7}, /* manufacturing use */
+       {0x0000, 0x4000, 0x7FFF}, /* internal BIOS use */
+       {0x0000, 0x9000, 0x9001}, /* internal BIOS use */
+       {0x0000, 0xA000, 0xBFFF}, /* write only */
+       {0x0000, 0xEFF0, 0xEFFF}, /* internal BIOS use */
+       /* handled by kernel: dell-laptop */
+       {0x0000, BRIGHTNESS_TOKEN,      BRIGHTNESS_TOKEN},
+       {0x0000, KBD_LED_OFF_TOKEN,     KBD_LED_AUTO_TOKEN},
+       {0x0000, KBD_LED_AC_TOKEN,      KBD_LED_AC_TOKEN},
+       {0x0000, KBD_LED_AUTO_25_TOKEN, KBD_LED_AUTO_75_TOKEN},
+       {0x0000, KBD_LED_AUTO_100_TOKEN,        KBD_LED_AUTO_100_TOKEN},
+       {0x0000, GLOBAL_MIC_MUTE_ENABLE,        GLOBAL_MIC_MUTE_DISABLE},
+};
+
+static LIST_HEAD(smbios_device_list);
 
 int dell_smbios_error(int value)
 {
@@ -55,42 +141,175 @@ int dell_smbios_error(int value)
 }
 EXPORT_SYMBOL_GPL(dell_smbios_error);
 
-struct calling_interface_buffer *dell_smbios_get_buffer(void)
+int dell_smbios_register_device(struct device *d, void *call_fn)
 {
-       mutex_lock(&buffer_mutex);
-       dell_smbios_clear_buffer();
-       return buffer;
+       struct smbios_device *priv;
+
+       priv = devm_kzalloc(d, sizeof(struct smbios_device), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+       get_device(d);
+       priv->device = d;
+       priv->call_fn = call_fn;
+       mutex_lock(&smbios_mutex);
+       list_add_tail(&priv->list, &smbios_device_list);
+       mutex_unlock(&smbios_mutex);
+       dev_dbg(d, "Added device: %s\n", d->driver->name);
+       return 0;
 }
-EXPORT_SYMBOL_GPL(dell_smbios_get_buffer);
+EXPORT_SYMBOL_GPL(dell_smbios_register_device);
 
-void dell_smbios_clear_buffer(void)
+void dell_smbios_unregister_device(struct device *d)
 {
-       memset(buffer, 0, sizeof(struct calling_interface_buffer));
+       struct smbios_device *priv;
+
+       mutex_lock(&smbios_mutex);
+       list_for_each_entry(priv, &smbios_device_list, list) {
+               if (priv->device == d) {
+                       list_del(&priv->list);
+                       put_device(d);
+                       break;
+               }
+       }
+       mutex_unlock(&smbios_mutex);
+       dev_dbg(d, "Remove device: %s\n", d->driver->name);
 }
-EXPORT_SYMBOL_GPL(dell_smbios_clear_buffer);
+EXPORT_SYMBOL_GPL(dell_smbios_unregister_device);
 
-void dell_smbios_release_buffer(void)
+int dell_smbios_call_filter(struct device *d,
+                           struct calling_interface_buffer *buffer)
 {
-       mutex_unlock(&buffer_mutex);
+       u16 t = 0;
+       int i;
+
+       /* can't make calls over 30 */
+       if (buffer->cmd_class > 30) {
+               dev_dbg(d, "class too big: %u\n", buffer->cmd_class);
+               return -EINVAL;
+       }
+
+       /* supported calls on the particular system */
+       if (!(da_supported_commands & (1 << buffer->cmd_class))) {
+               dev_dbg(d, "invalid command, supported commands: 0x%8x\n",
+                       da_supported_commands);
+               return -EINVAL;
+       }
+
+       /* match against call blacklist  */
+       for (i = 0; i < ARRAY_SIZE(call_blacklist); i++) {
+               if (buffer->cmd_class != call_blacklist[i].cmd_class)
+                       continue;
+               if (buffer->cmd_select != call_blacklist[i].cmd_select &&
+                   call_blacklist[i].cmd_select != -1)
+                       continue;
+               dev_dbg(d, "blacklisted command: %u/%u\n",
+                       buffer->cmd_class, buffer->cmd_select);
+               return -EINVAL;
+       }
+
+       /* if a token call, find token ID */
+
+       if ((buffer->cmd_class == CLASS_TOKEN_READ ||
+            buffer->cmd_class == CLASS_TOKEN_WRITE) &&
+            buffer->cmd_select < 3) {
+               /* find the matching token ID */
+               for (i = 0; i < da_num_tokens; i++) {
+                       if (da_tokens[i].location != buffer->input[0])
+                               continue;
+                       t = da_tokens[i].tokenID;
+                       break;
+               }
+
+               /* token call; but token didn't exist */
+               if (!t) {
+                       dev_dbg(d, "token at location %04x doesn't exist\n",
+                               buffer->input[0]);
+                       return -EINVAL;
+               }
+
+               /* match against token blacklist */
+               for (i = 0; i < ARRAY_SIZE(token_blacklist); i++) {
+                       if (!token_blacklist[i].min || !token_blacklist[i].max)
+                               continue;
+                       if (t >= token_blacklist[i].min &&
+                           t <= token_blacklist[i].max)
+                               return -EINVAL;
+               }
+
+               /* match against token whitelist */
+               for (i = 0; i < ARRAY_SIZE(token_whitelist); i++) {
+                       if (!token_whitelist[i].min || !token_whitelist[i].max)
+                               continue;
+                       if (t < token_whitelist[i].min ||
+                           t > token_whitelist[i].max)
+                               continue;
+                       if (!token_whitelist[i].need_capability ||
+                           capable(token_whitelist[i].need_capability)) {
+                               dev_dbg(d, "whitelisted token: %x\n", t);
+                               return 0;
+                       }
+
+               }
+       }
+       /* match against call whitelist */
+       for (i = 0; i < ARRAY_SIZE(call_whitelist); i++) {
+               if (buffer->cmd_class != call_whitelist[i].cmd_class)
+                       continue;
+               if (buffer->cmd_select != call_whitelist[i].cmd_select)
+                       continue;
+               if (!call_whitelist[i].need_capability ||
+                   capable(call_whitelist[i].need_capability)) {
+                       dev_dbg(d, "whitelisted capable command: %u/%u\n",
+                       buffer->cmd_class, buffer->cmd_select);
+                       return 0;
+               }
+               dev_dbg(d, "missing capability %d for %u/%u\n",
+                       call_whitelist[i].need_capability,
+                       buffer->cmd_class, buffer->cmd_select);
+
+       }
+
+       /* not in a whitelist, only allow processes with capabilities */
+       if (capable(CAP_SYS_RAWIO)) {
+               dev_dbg(d, "Allowing %u/%u due to CAP_SYS_RAWIO\n",
+                       buffer->cmd_class, buffer->cmd_select);
+               return 0;
+       }
+
+       return -EACCES;
 }
-EXPORT_SYMBOL_GPL(dell_smbios_release_buffer);
+EXPORT_SYMBOL_GPL(dell_smbios_call_filter);
 
-void dell_smbios_send_request(int class, int select)
+int dell_smbios_call(struct calling_interface_buffer *buffer)
 {
-       struct smi_cmd command;
+       int (*call_fn)(struct calling_interface_buffer *) = NULL;
+       struct device *selected_dev = NULL;
+       struct smbios_device *priv;
+       int ret;
 
-       command.magic = SMI_CMD_MAGIC;
-       command.command_address = da_command_address;
-       command.command_code = da_command_code;
-       command.ebx = virt_to_phys(buffer);
-       command.ecx = 0x42534931;
+       mutex_lock(&smbios_mutex);
+       list_for_each_entry(priv, &smbios_device_list, list) {
+               if (!selected_dev || priv->device->id >= selected_dev->id) {
+                       dev_dbg(priv->device, "Trying device ID: %d\n",
+                               priv->device->id);
+                       call_fn = priv->call_fn;
+                       selected_dev = priv->device;
+               }
+       }
+
+       if (!selected_dev) {
+               ret = -ENODEV;
+               pr_err("No dell-smbios drivers are loaded\n");
+               goto out_smbios_call;
+       }
 
-       buffer->class = class;
-       buffer->select = select;
+       ret = call_fn(buffer);
 
-       dcdbas_smi_request(&command);
+out_smbios_call:
+       mutex_unlock(&smbios_mutex);
+       return ret;
 }
-EXPORT_SYMBOL_GPL(dell_smbios_send_request);
+EXPORT_SYMBOL_GPL(dell_smbios_call);
 
 struct calling_interface_token *dell_smbios_find_token(int tokenid)
 {
@@ -139,8 +358,7 @@ static void __init parse_da_table(const struct dmi_header *dm)
        if (dm->length < 17)
                return;
 
-       da_command_address = table->cmdIOAddress;
-       da_command_code = table->cmdIOCode;
+       da_supported_commands = table->supportedCmds;
 
        new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
                                 sizeof(struct calling_interface_token),
@@ -156,6 +374,27 @@ static void __init parse_da_table(const struct dmi_header *dm)
        da_num_tokens += tokens;
 }
 
+static void zero_duplicates(struct device *dev)
+{
+       int i, j;
+
+       for (i = 0; i < da_num_tokens; i++) {
+               if (da_tokens[i].tokenID == 0)
+                       continue;
+               for (j = i+1; j < da_num_tokens; j++) {
+                       if (da_tokens[j].tokenID == 0)
+                               continue;
+                       if (da_tokens[i].tokenID == da_tokens[j].tokenID) {
+                               dev_dbg(dev, "Zeroing dup token ID %x(%x/%x)\n",
+                                       da_tokens[j].tokenID,
+                                       da_tokens[j].location,
+                                       da_tokens[j].value);
+                               da_tokens[j].tokenID = 0;
+                       }
+               }
+       }
+}
+
 static void __init find_tokens(const struct dmi_header *dm, void *dummy)
 {
        switch (dm->type) {
@@ -169,10 +408,160 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
        }
 }
 
+static int match_attribute(struct device *dev,
+                          struct device_attribute *attr)
+{
+       int i;
+
+       for (i = 0; i < da_num_tokens * 2; i++) {
+               if (!token_attrs[i])
+                       continue;
+               if (strcmp(token_attrs[i]->name, attr->attr.name) == 0)
+                       return i/2;
+       }
+       dev_dbg(dev, "couldn't match: %s\n", attr->attr.name);
+       return -EINVAL;
+}
+
+static ssize_t location_show(struct device *dev,
+                            struct device_attribute *attr, char *buf)
+{
+       int i;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       i = match_attribute(dev, attr);
+       if (i > 0)
+               return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].location);
+       return 0;
+}
+
+static ssize_t value_show(struct device *dev,
+                         struct device_attribute *attr, char *buf)
+{
+       int i;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       i = match_attribute(dev, attr);
+       if (i > 0)
+               return scnprintf(buf, PAGE_SIZE, "%08x", da_tokens[i].value);
+       return 0;
+}
+
+static struct attribute_group smbios_attribute_group = {
+       .name = "tokens"
+};
+
+static struct platform_driver platform_driver = {
+       .driver = {
+               .name = "dell-smbios",
+       },
+};
+
+static int build_tokens_sysfs(struct platform_device *dev)
+{
+       char *location_name;
+       char *value_name;
+       size_t size;
+       int ret;
+       int i, j;
+
+       /* (number of tokens  + 1 for null terminated */
+       size = sizeof(struct device_attribute) * (da_num_tokens + 1);
+       token_location_attrs = kzalloc(size, GFP_KERNEL);
+       if (!token_location_attrs)
+               return -ENOMEM;
+       token_value_attrs = kzalloc(size, GFP_KERNEL);
+       if (!token_value_attrs)
+               goto out_allocate_value;
+
+       /* need to store both location and value + terminator*/
+       size = sizeof(struct attribute *) * ((2 * da_num_tokens) + 1);
+       token_attrs = kzalloc(size, GFP_KERNEL);
+       if (!token_attrs)
+               goto out_allocate_attrs;
+
+       for (i = 0, j = 0; i < da_num_tokens; i++) {
+               /* skip empty */
+               if (da_tokens[i].tokenID == 0)
+                       continue;
+               /* add location */
+               location_name = kasprintf(GFP_KERNEL, "%04x_location",
+                                         da_tokens[i].tokenID);
+               if (location_name == NULL)
+                       goto out_unwind_strings;
+               sysfs_attr_init(&token_location_attrs[i].attr);
+               token_location_attrs[i].attr.name = location_name;
+               token_location_attrs[i].attr.mode = 0444;
+               token_location_attrs[i].show = location_show;
+               token_attrs[j++] = &token_location_attrs[i].attr;
+
+               /* add value */
+               value_name = kasprintf(GFP_KERNEL, "%04x_value",
+                                      da_tokens[i].tokenID);
+               if (value_name == NULL)
+                       goto loop_fail_create_value;
+               sysfs_attr_init(&token_value_attrs[i].attr);
+               token_value_attrs[i].attr.name = value_name;
+               token_value_attrs[i].attr.mode = 0444;
+               token_value_attrs[i].show = value_show;
+               token_attrs[j++] = &token_value_attrs[i].attr;
+               continue;
+
+loop_fail_create_value:
+               kfree(value_name);
+               goto out_unwind_strings;
+       }
+       smbios_attribute_group.attrs = token_attrs;
+
+       ret = sysfs_create_group(&dev->dev.kobj, &smbios_attribute_group);
+       if (ret)
+               goto out_unwind_strings;
+       return 0;
+
+out_unwind_strings:
+       for (i = i-1; i > 0; i--) {
+               kfree(token_location_attrs[i].attr.name);
+               kfree(token_value_attrs[i].attr.name);
+       }
+       kfree(token_attrs);
+out_allocate_attrs:
+       kfree(token_value_attrs);
+out_allocate_value:
+       kfree(token_location_attrs);
+
+       return -ENOMEM;
+}
+
+static void free_group(struct platform_device *pdev)
+{
+       int i;
+
+       sysfs_remove_group(&pdev->dev.kobj,
+                               &smbios_attribute_group);
+       for (i = 0; i < da_num_tokens; i++) {
+               kfree(token_location_attrs[i].attr.name);
+               kfree(token_value_attrs[i].attr.name);
+       }
+       kfree(token_attrs);
+       kfree(token_value_attrs);
+       kfree(token_location_attrs);
+}
+
 static int __init dell_smbios_init(void)
 {
+       const struct dmi_device *valid;
        int ret;
 
+       valid = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, "Dell System", NULL);
+       if (!valid) {
+               pr_err("Unable to run on non-Dell system\n");
+               return -ENODEV;
+       }
+
        dmi_walk(find_tokens, NULL);
 
        if (!da_tokens)  {
@@ -180,27 +569,52 @@ static int __init dell_smbios_init(void)
                return -ENODEV;
        }
 
-       /*
-        * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
-        * is passed to SMI handler.
-        */
-       buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
-       if (!buffer) {
+       ret = platform_driver_register(&platform_driver);
+       if (ret)
+               goto fail_platform_driver;
+
+       platform_device = platform_device_alloc("dell-smbios", 0);
+       if (!platform_device) {
                ret = -ENOMEM;
-               goto fail_buffer;
+               goto fail_platform_device_alloc;
        }
+       ret = platform_device_add(platform_device);
+       if (ret)
+               goto fail_platform_device_add;
+
+       /* duplicate tokens will cause problems building sysfs files */
+       zero_duplicates(&platform_device->dev);
+
+       ret = build_tokens_sysfs(platform_device);
+       if (ret)
+               goto fail_create_group;
 
        return 0;
 
-fail_buffer:
+fail_create_group:
+       platform_device_del(platform_device);
+
+fail_platform_device_add:
+       platform_device_put(platform_device);
+
+fail_platform_device_alloc:
+       platform_driver_unregister(&platform_driver);
+
+fail_platform_driver:
        kfree(da_tokens);
        return ret;
 }
 
 static void __exit dell_smbios_exit(void)
 {
+       mutex_lock(&smbios_mutex);
+       if (platform_device) {
+               free_group(platform_device);
+               platform_device_unregister(platform_device);
+               platform_driver_unregister(&platform_driver);
+       }
        kfree(da_tokens);
-       free_page((unsigned long)buffer);
+       mutex_unlock(&smbios_mutex);
 }
 
 subsys_initcall(dell_smbios_init);
index 45cbc2292cd3d703fae5763d05641c1d3e7ec666..138d478d9adc91bdceee7194567cbe40718ec1f7 100644 (file)
 #ifndef _DELL_SMBIOS_H_
 #define _DELL_SMBIOS_H_
 
-struct notifier_block;
+#include <linux/device.h>
+#include <uapi/linux/wmi.h>
 
-/* This structure will be modified by the firmware when we enter
- * system management mode, hence the volatiles */
+/* Classes and selects used only in kernel drivers */
+#define CLASS_KBD_BACKLIGHT 4
+#define SELECT_KBD_BACKLIGHT 11
 
-struct calling_interface_buffer {
-       u16 class;
-       u16 select;
-       volatile u32 input[4];
-       volatile u32 output[4];
-} __packed;
+/* Tokens used in kernel drivers, any of these
+ * should be filtered from userspace access
+ */
+#define BRIGHTNESS_TOKEN       0x007d
+#define KBD_LED_AC_TOKEN       0x0451
+#define KBD_LED_OFF_TOKEN      0x01E1
+#define KBD_LED_ON_TOKEN       0x01E2
+#define KBD_LED_AUTO_TOKEN     0x01E3
+#define KBD_LED_AUTO_25_TOKEN  0x02EA
+#define KBD_LED_AUTO_50_TOKEN  0x02EB
+#define KBD_LED_AUTO_75_TOKEN  0x02EC
+#define KBD_LED_AUTO_100_TOKEN 0x02F6
+#define GLOBAL_MIC_MUTE_ENABLE 0x0364
+#define GLOBAL_MIC_MUTE_DISABLE        0x0365
+
+struct notifier_block;
 
 struct calling_interface_token {
        u16 tokenID;
@@ -37,12 +49,21 @@ struct calling_interface_token {
        };
 };
 
-int dell_smbios_error(int value);
+struct calling_interface_structure {
+       struct dmi_header header;
+       u16 cmdIOAddress;
+       u8 cmdIOCode;
+       u32 supportedCmds;
+       struct calling_interface_token tokens[];
+} __packed;
 
-struct calling_interface_buffer *dell_smbios_get_buffer(void);
-void dell_smbios_clear_buffer(void);
-void dell_smbios_release_buffer(void);
-void dell_smbios_send_request(int class, int select);
+int dell_smbios_register_device(struct device *d, void *call_fn);
+void dell_smbios_unregister_device(struct device *d);
+
+int dell_smbios_error(int value);
+int dell_smbios_call_filter(struct device *d,
+       struct calling_interface_buffer *buffer);
+int dell_smbios_call(struct calling_interface_buffer *buffer);
 
 struct calling_interface_token *dell_smbios_find_token(int tokenid);
 
index 37e646034ef8c07dbeec5317b64433be6aa1bf49..1d87237bc731b05c4d4706b560fdc45dc407a241 100644 (file)
@@ -90,7 +90,7 @@ static ssize_t smo8800_misc_read(struct file *file, char __user *buf,
                                         struct smo8800_device, miscdev);
 
        u32 data = 0;
-       unsigned char byte_data = 0;
+       unsigned char byte_data;
        ssize_t retval = 1;
 
        if (count < 1)
@@ -103,7 +103,6 @@ static ssize_t smo8800_misc_read(struct file *file, char __user *buf,
        if (retval)
                return retval;
 
-       byte_data = 1;
        retval = 1;
 
        if (data < 255)
diff --git a/drivers/platform/x86/dell-wmi-descriptor.c b/drivers/platform/x86/dell-wmi-descriptor.c
new file mode 100644 (file)
index 0000000..4dfef1f
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Dell WMI descriptor driver
+ *
+ * Copyright (C) 2017 Dell Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/wmi.h>
+#include "dell-wmi-descriptor.h"
+
+#define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
+
+struct descriptor_priv {
+       struct list_head list;
+       u32 interface_version;
+       u32 size;
+};
+static int descriptor_valid = -EPROBE_DEFER;
+static LIST_HEAD(wmi_list);
+static DEFINE_MUTEX(list_mutex);
+
+int dell_wmi_get_descriptor_valid(void)
+{
+       if (!wmi_has_guid(DELL_WMI_DESCRIPTOR_GUID))
+               return -ENODEV;
+
+       return descriptor_valid;
+}
+EXPORT_SYMBOL_GPL(dell_wmi_get_descriptor_valid);
+
+bool dell_wmi_get_interface_version(u32 *version)
+{
+       struct descriptor_priv *priv;
+       bool ret = false;
+
+       mutex_lock(&list_mutex);
+       priv = list_first_entry_or_null(&wmi_list,
+                                       struct descriptor_priv,
+                                       list);
+       if (priv) {
+               *version = priv->interface_version;
+               ret = true;
+       }
+       mutex_unlock(&list_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dell_wmi_get_interface_version);
+
+bool dell_wmi_get_size(u32 *size)
+{
+       struct descriptor_priv *priv;
+       bool ret = false;
+
+       mutex_lock(&list_mutex);
+       priv = list_first_entry_or_null(&wmi_list,
+                                       struct descriptor_priv,
+                                       list);
+       if (priv) {
+               *size = priv->size;
+               ret = true;
+       }
+       mutex_unlock(&list_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dell_wmi_get_size);
+
+/*
+ * Descriptor buffer is 128 byte long and contains:
+ *
+ *       Name             Offset  Length  Value
+ * Vendor Signature          0       4    "DELL"
+ * Object Signature          4       4    " WMI"
+ * WMI Interface Version     8       4    <version>
+ * WMI buffer length        12       4    <length>
+ */
+static int dell_wmi_descriptor_probe(struct wmi_device *wdev)
+{
+       union acpi_object *obj = NULL;
+       struct descriptor_priv *priv;
+       u32 *buffer;
+       int ret;
+
+       obj = wmidev_block_query(wdev, 0);
+       if (!obj) {
+               dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
+               ret = -EIO;
+               goto out;
+       }
+
+       if (obj->type != ACPI_TYPE_BUFFER) {
+               dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
+               ret = -EINVAL;
+               descriptor_valid = ret;
+               goto out;
+       }
+
+       /* Although it's not technically a failure, this would lead to
+        * unexpected behavior
+        */
+       if (obj->buffer.length != 128) {
+               dev_err(&wdev->dev,
+                       "Dell descriptor buffer has unexpected length (%d)\n",
+                       obj->buffer.length);
+               ret = -EINVAL;
+               descriptor_valid = ret;
+               goto out;
+       }
+
+       buffer = (u32 *)obj->buffer.pointer;
+
+       if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) {
+               dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n",
+                       buffer);
+               ret = -EINVAL;
+               descriptor_valid = ret;
+               goto out;
+       }
+       descriptor_valid = 0;
+
+       if (buffer[2] != 0 && buffer[2] != 1)
+               dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n",
+                       (unsigned long) buffer[2]);
+
+       priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv),
+       GFP_KERNEL);
+
+       if (!priv) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       priv->interface_version = buffer[2];
+       priv->size = buffer[3];
+       ret = 0;
+       dev_set_drvdata(&wdev->dev, priv);
+       mutex_lock(&list_mutex);
+       list_add_tail(&priv->list, &wmi_list);
+       mutex_unlock(&list_mutex);
+
+       dev_dbg(&wdev->dev, "Detected Dell WMI interface version %lu and buffer size %lu\n",
+               (unsigned long) priv->interface_version,
+               (unsigned long) priv->size);
+
+out:
+       kfree(obj);
+       return ret;
+}
+
+static int dell_wmi_descriptor_remove(struct wmi_device *wdev)
+{
+       struct descriptor_priv *priv = dev_get_drvdata(&wdev->dev);
+
+       mutex_lock(&list_mutex);
+       list_del(&priv->list);
+       mutex_unlock(&list_mutex);
+       return 0;
+}
+
+static const struct wmi_device_id dell_wmi_descriptor_id_table[] = {
+       { .guid_string = DELL_WMI_DESCRIPTOR_GUID },
+       { },
+};
+
+static struct wmi_driver dell_wmi_descriptor_driver = {
+       .driver = {
+               .name = "dell-wmi-descriptor",
+       },
+       .probe = dell_wmi_descriptor_probe,
+       .remove = dell_wmi_descriptor_remove,
+       .id_table = dell_wmi_descriptor_id_table,
+};
+
+module_wmi_driver(dell_wmi_descriptor_driver);
+
+MODULE_ALIAS("wmi:" DELL_WMI_DESCRIPTOR_GUID);
+MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
+MODULE_DESCRIPTION("Dell WMI descriptor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/dell-wmi-descriptor.h b/drivers/platform/x86/dell-wmi-descriptor.h
new file mode 100644 (file)
index 0000000..1e8cb96
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ *  Dell WMI descriptor driver
+ *
+ *  Copyright (c) 2017 Dell 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.
+ */
+
+#ifndef _DELL_WMI_DESCRIPTOR_H_
+#define _DELL_WMI_DESCRIPTOR_H_
+
+#include <linux/wmi.h>
+
+/* possible return values:
+ *  -ENODEV: Descriptor GUID missing from WMI bus
+ *  -EPROBE_DEFER: probing for dell-wmi-descriptor not yet run
+ *  0: valid descriptor, successfully probed
+ *  < 0: invalid descriptor, don't probe dependent devices
+ */
+int dell_wmi_get_descriptor_valid(void);
+
+bool dell_wmi_get_interface_version(u32 *version);
+bool dell_wmi_get_size(u32 *size);
+
+#endif
index 28d9f8696081bcb063a09e9b1c481ede0259e049..39d2f451848332d8346201b9cc1cd08bb5e10427 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/wmi.h>
 #include <acpi/video.h>
 #include "dell-smbios.h"
+#include "dell-wmi-descriptor.h"
 
 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
 MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
@@ -46,12 +47,10 @@ MODULE_DESCRIPTION("Dell laptop WMI hotkeys driver");
 MODULE_LICENSE("GPL");
 
 #define DELL_EVENT_GUID "9DBB5994-A997-11DA-B012-B622A1EF5492"
-#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
 
 static bool wmi_requires_smbios_request;
 
 MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
-MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID);
 
 struct dell_wmi_priv {
        struct input_dev *input_dev;
@@ -618,78 +617,6 @@ static void dell_wmi_input_destroy(struct wmi_device *wdev)
        input_unregister_device(priv->input_dev);
 }
 
-/*
- * Descriptor buffer is 128 byte long and contains:
- *
- *       Name             Offset  Length  Value
- * Vendor Signature          0       4    "DELL"
- * Object Signature          4       4    " WMI"
- * WMI Interface Version     8       4    <version>
- * WMI buffer length        12       4    4096
- */
-static int dell_wmi_check_descriptor_buffer(struct wmi_device *wdev)
-{
-       struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev);
-       union acpi_object *obj = NULL;
-       struct wmi_device *desc_dev;
-       u32 *buffer;
-       int ret;
-
-       desc_dev = wmidev_get_other_guid(wdev, DELL_DESCRIPTOR_GUID);
-       if (!desc_dev) {
-               dev_err(&wdev->dev, "Dell WMI descriptor does not exist\n");
-               return -ENODEV;
-       }
-
-       obj = wmidev_block_query(desc_dev, 0);
-       if (!obj) {
-               dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n");
-               ret = -EIO;
-               goto out;
-       }
-
-       if (obj->type != ACPI_TYPE_BUFFER) {
-               dev_err(&wdev->dev, "Dell descriptor has wrong type\n");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       if (obj->buffer.length != 128) {
-               dev_err(&wdev->dev,
-                       "Dell descriptor buffer has invalid length (%d)\n",
-                       obj->buffer.length);
-               if (obj->buffer.length < 16) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-       }
-
-       buffer = (u32 *)obj->buffer.pointer;
-
-       if (buffer[0] != 0x4C4C4544 && buffer[1] != 0x494D5720)
-               dev_warn(&wdev->dev, "Dell descriptor buffer has invalid signature (%*ph)\n",
-                       8, buffer);
-
-       if (buffer[2] != 0 && buffer[2] != 1)
-               dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%d)\n",
-                       buffer[2]);
-
-       if (buffer[3] != 4096)
-               dev_warn(&wdev->dev, "Dell descriptor buffer has invalid buffer length (%d)\n",
-                       buffer[3]);
-
-       priv->interface_version = buffer[2];
-       ret = 0;
-
-       dev_info(&wdev->dev, "Detected Dell WMI interface version %u\n",
-               priv->interface_version);
-
-out:
-       kfree(obj);
-       put_device(&desc_dev->dev);
-       return ret;
-}
-
 /*
  * According to Dell SMBIOS documentation:
  *
@@ -711,13 +638,16 @@ static int dell_wmi_events_set_enabled(bool enable)
        struct calling_interface_buffer *buffer;
        int ret;
 
-       buffer = dell_smbios_get_buffer();
+       buffer = kzalloc(sizeof(struct calling_interface_buffer), GFP_KERNEL);
+       buffer->cmd_class = CLASS_INFO;
+       buffer->cmd_select = SELECT_APP_REGISTRATION;
        buffer->input[0] = 0x10000;
        buffer->input[1] = 0x51534554;
        buffer->input[3] = enable;
-       dell_smbios_send_request(17, 3);
-       ret = buffer->output[0];
-       dell_smbios_release_buffer();
+       ret = dell_smbios_call(buffer);
+       if (ret == 0)
+               ret = buffer->output[0];
+       kfree(buffer);
 
        return dell_smbios_error(ret);
 }
@@ -725,7 +655,11 @@ static int dell_wmi_events_set_enabled(bool enable)
 static int dell_wmi_probe(struct wmi_device *wdev)
 {
        struct dell_wmi_priv *priv;
-       int err;
+       int ret;
+
+       ret = dell_wmi_get_descriptor_valid();
+       if (ret)
+               return ret;
 
        priv = devm_kzalloc(
                &wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL);
@@ -733,9 +667,8 @@ static int dell_wmi_probe(struct wmi_device *wdev)
                return -ENOMEM;
        dev_set_drvdata(&wdev->dev, priv);
 
-       err = dell_wmi_check_descriptor_buffer(wdev);
-       if (err)
-               return err;
+       if (!dell_wmi_get_interface_version(&priv->interface_version))
+               return -EPROBE_DEFER;
 
        return dell_wmi_input_setup(wdev);
 }
index 56a8195096a229c6975d3f78746ecbc4c6169660..2cfbd3fa5136002362bdc558c14d00e40ed56e22 100644 (file)
@@ -691,6 +691,7 @@ static enum led_brightness eco_led_get(struct led_classdev *cdev)
 
 static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
 {
+       struct fujitsu_laptop *priv = acpi_driver_data(device);
        struct led_classdev *led;
        int result;
 
@@ -724,12 +725,15 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
        }
 
        /*
-        * BTNI bit 24 seems to indicate the presence of a radio toggle
-        * button in place of a slide switch, and all such machines appear
-        * to also have an RF LED.  Therefore use bit 24 as an indicator
-        * that an RF LED is present.
+        * Some Fujitsu laptops have a radio toggle button in place of a slide
+        * switch and all such machines appear to also have an RF LED.  Based on
+        * comparing DSDT tables of four Fujitsu Lifebook models (E744, E751,
+        * S7110, S8420; the first one has a radio toggle button, the other
+        * three have slide switches), bit 17 of flags_supported (the value
+        * returned by method S000 of ACPI device FUJ02E3) seems to indicate
+        * whether given model has a radio toggle button.
         */
-       if (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) {
+       if (priv->flags_supported & BIT(17)) {
                led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL);
                if (!led)
                        return -ENOMEM;
index b4ed3dc983d5229c7e1726da58789a937a297d39..b4224389febebe4688ea2195d8a3a786ba3c2081 100644 (file)
@@ -297,7 +297,7 @@ static int hp_wmi_hw_state(int mask)
        if (state < 0)
                return state;
 
-       return state & 0x1;
+       return !!(state & mask);
 }
 
 static int __init hp_wmi_bios_2008_later(void)
index 493d8910a74e2eca10812c3202a74cdb21ed37cd..7b12abe86b94f3f71d1fc9cb85d5828554ee8cfb 100644 (file)
@@ -240,6 +240,7 @@ static const struct dmi_system_id lis3lv02d_dmi_ids[] = {
        AXIS_DMI_MATCH("HDX18", "HP HDX 18", x_inverted),
        AXIS_DMI_MATCH("HPB432x", "HP ProBook 432", xy_rotated_left),
        AXIS_DMI_MATCH("HPB440G3", "HP ProBook 440 G3", x_inverted_usd),
+       AXIS_DMI_MATCH("HPB440G4", "HP ProBook 440 G4", x_inverted),
        AXIS_DMI_MATCH("HPB442x", "HP ProBook 442", xy_rotated_left),
        AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted),
        AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
index fe98d4ac0df37040c3da8ad470bf7a33c5477fbe..53ab4e0f896255614b2462d5323a03e53959a73e 100644 (file)
@@ -1166,6 +1166,13 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
                        DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 910-13IKB"),
                },
        },
+       {
+               .ident = "Lenovo YOGA 920-13IKB",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 920-13IKB"),
+               },
+       },
        {}
 };
 
index e34fd70b67afe4d2573f8c4c39dcc4e1290ef97e..f470279c4c100424815c45dacc1304193677ae8a 100644 (file)
@@ -226,6 +226,24 @@ wakeup:
                return;
        }
 
+       /*
+        * Needed for suspend to work on some platforms that don't expose
+        * the 5-button array, but still send notifies with power button
+        * event code to this device object on power button actions.
+        *
+        * Report the power button press; catch and ignore the button release.
+        */
+       if (!priv->array) {
+               if (event == 0xce) {
+                       input_report_key(priv->input_dev, KEY_POWER, 1);
+                       input_sync(priv->input_dev);
+                       return;
+               }
+
+               if (event == 0xcf)
+                       return;
+       }
+
        /* 0xC0 is for HID events, other values are for 5 button array */
        if (event != 0xc0) {
                if (!priv->array ||
diff --git a/drivers/platform/x86/intel-wmi-thunderbolt.c b/drivers/platform/x86/intel-wmi-thunderbolt.c
new file mode 100644 (file)
index 0000000..c2257bd
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * WMI Thunderbolt driver
+ *
+ * Copyright (C) 2017 Dell Inc. All Rights Reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/wmi.h>
+
+#define INTEL_WMI_THUNDERBOLT_GUID "86CCFD48-205E-4A77-9C48-2021CBEDE341"
+
+static ssize_t force_power_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       struct acpi_buffer input;
+       acpi_status status;
+       u8 mode;
+
+       input.length = sizeof(u8);
+       input.pointer = &mode;
+       mode = hex_to_bin(buf[0]);
+       if (mode == 0 || mode == 1) {
+               status = wmi_evaluate_method(INTEL_WMI_THUNDERBOLT_GUID, 0, 1,
+                                            &input, NULL);
+               if (ACPI_FAILURE(status))
+                       return -ENODEV;
+       } else {
+               return -EINVAL;
+       }
+       return count;
+}
+
+static DEVICE_ATTR_WO(force_power);
+
+static struct attribute *tbt_attrs[] = {
+       &dev_attr_force_power.attr,
+       NULL
+};
+
+static const struct attribute_group tbt_attribute_group = {
+       .attrs = tbt_attrs,
+};
+
+static int intel_wmi_thunderbolt_probe(struct wmi_device *wdev)
+{
+       int ret;
+
+       ret = sysfs_create_group(&wdev->dev.kobj, &tbt_attribute_group);
+       kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE);
+       return ret;
+}
+
+static int intel_wmi_thunderbolt_remove(struct wmi_device *wdev)
+{
+       sysfs_remove_group(&wdev->dev.kobj, &tbt_attribute_group);
+       kobject_uevent(&wdev->dev.kobj, KOBJ_CHANGE);
+       return 0;
+}
+
+static const struct wmi_device_id intel_wmi_thunderbolt_id_table[] = {
+       { .guid_string = INTEL_WMI_THUNDERBOLT_GUID },
+       { },
+};
+
+static struct wmi_driver intel_wmi_thunderbolt_driver = {
+       .driver = {
+               .name = "intel-wmi-thunderbolt",
+       },
+       .probe = intel_wmi_thunderbolt_probe,
+       .remove = intel_wmi_thunderbolt_remove,
+       .id_table = intel_wmi_thunderbolt_id_table,
+};
+
+module_wmi_driver(intel_wmi_thunderbolt_driver);
+
+MODULE_ALIAS("wmi:" INTEL_WMI_THUNDERBOLT_GUID);
+MODULE_AUTHOR("Mario Limonciello <mario.limonciello@dell.com>");
+MODULE_DESCRIPTION("Intel WMI Thunderbolt force power driver");
+MODULE_LICENSE("GPL");
index da706e2c4232cf25d40aefd52f54a910ab437e6f..380ef7ec094f309d42e30a08c97924b22c8496ce 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
 #define EXPECTED_PTYPE         4
@@ -34,6 +35,42 @@ struct cht_int33fe_data {
        struct i2c_client *pi3usb30532;
 };
 
+/*
+ * Grrr I severly dislike buggy BIOS-es. At least one BIOS enumerates
+ * the max17047 both through the INT33FE ACPI device (it is right there
+ * in the resources table) as well as through a separate MAX17047 device.
+ *
+ * These helpers are used to work around this by checking if an i2c-client
+ * for the max17047 has already been registered.
+ */
+static int cht_int33fe_check_for_max17047(struct device *dev, void *data)
+{
+       struct i2c_client **max17047 = data;
+       struct acpi_device *adev;
+       const char *hid;
+
+       adev = ACPI_COMPANION(dev);
+       if (!adev)
+               return 0;
+
+       hid = acpi_device_hid(adev);
+
+       /* The MAX17047 ACPI node doesn't have an UID, so we don't check that */
+       if (strcmp(hid, "MAX17047"))
+               return 0;
+
+       *max17047 = to_i2c_client(dev);
+       return 1;
+}
+
+static struct i2c_client *cht_int33fe_find_max17047(void)
+{
+       struct i2c_client *max17047 = NULL;
+
+       i2c_for_each_dev(&max17047, cht_int33fe_check_for_max17047);
+       return max17047;
+}
+
 static const char * const max17047_suppliers[] = { "bq24190-charger" };
 
 static const struct property_entry max17047_props[] = {
@@ -41,14 +78,25 @@ static const struct property_entry max17047_props[] = {
        { }
 };
 
+static const struct property_entry fusb302_props[] = {
+       PROPERTY_ENTRY_STRING("fcs,extcon-name", "cht_wcove_pwrsrc"),
+       PROPERTY_ENTRY_U32("fcs,max-sink-microvolt", 12000000),
+       PROPERTY_ENTRY_U32("fcs,max-sink-microamp",   3000000),
+       PROPERTY_ENTRY_U32("fcs,max-sink-microwatt", 36000000),
+       { }
+};
+
 static int cht_int33fe_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
        struct i2c_board_info board_info;
        struct cht_int33fe_data *data;
+       struct i2c_client *max17047;
+       struct regulator *regulator;
        unsigned long long ptyp;
        acpi_status status;
        int fusb302_irq;
+       int ret;
 
        status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp);
        if (ACPI_FAILURE(status)) {
@@ -63,6 +111,34 @@ static int cht_int33fe_probe(struct i2c_client *client)
        if (ptyp != EXPECTED_PTYPE)
                return -ENODEV;
 
+       /* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */
+       if (!acpi_dev_present("INT34D3", "1", 3)) {
+               dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n",
+                       EXPECTED_PTYPE);
+               return -ENODEV;
+       }
+
+       /*
+        * We expect the WC PMIC to be paired with a TI bq24292i charger-IC.
+        * We check for the bq24292i vbus regulator here, this has 2 purposes:
+        * 1) The bq24292i allows charging with up to 12V, setting the fusb302's
+        *    max-snk voltage to 12V with another charger-IC is not good.
+        * 2) For the fusb302 driver to get the bq24292i vbus regulator, the
+        *    regulator-map, which is part of the bq24292i regulator_init_data,
+        *    must be registered before the fusb302 is instantiated, otherwise
+        *    it will end up with a dummy-regulator.
+        * Note "cht_wc_usb_typec_vbus" comes from the regulator_init_data
+        * which is defined in i2c-cht-wc.c from where the bq24292i i2c-client
+        * gets instantiated. We use regulator_get_optional here so that we
+        * don't end up getting a dummy-regulator ourselves.
+        */
+       regulator = regulator_get_optional(dev, "cht_wc_usb_typec_vbus");
+       if (IS_ERR(regulator)) {
+               ret = PTR_ERR(regulator);
+               return (ret == -ENODEV) ? -EPROBE_DEFER : ret;
+       }
+       regulator_put(regulator);
+
        /* The FUSB302 uses the irq at index 1 and is the only irq user */
        fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1);
        if (fusb302_irq < 0) {
@@ -75,16 +151,31 @@ static int cht_int33fe_probe(struct i2c_client *client)
        if (!data)
                return -ENOMEM;
 
-       memset(&board_info, 0, sizeof(board_info));
-       strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
-       board_info.properties = max17047_props;
-
-       data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
-       if (!data->max17047)
-               return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
+       /* Work around BIOS bug, see comment on cht_int33fe_find_max17047 */
+       max17047 = cht_int33fe_find_max17047();
+       if (max17047) {
+               /* Pre-existing i2c-client for the max17047, add device-props */
+               ret = device_add_properties(&max17047->dev, max17047_props);
+               if (ret)
+                       return ret;
+               /* And re-probe to get the new device-props applied. */
+               ret = device_reprobe(&max17047->dev);
+               if (ret)
+                       dev_warn(dev, "Reprobing max17047 error: %d\n", ret);
+       } else {
+               memset(&board_info, 0, sizeof(board_info));
+               strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
+               board_info.dev_name = "max17047";
+               board_info.properties = max17047_props;
+               data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
+               if (!data->max17047)
+                       return -EPROBE_DEFER; /* Wait for i2c-adapter to load */
+       }
 
        memset(&board_info, 0, sizeof(board_info));
-       strlcpy(board_info.type, "fusb302", I2C_NAME_SIZE);
+       strlcpy(board_info.type, "typec_fusb302", I2C_NAME_SIZE);
+       board_info.dev_name = "fusb302";
+       board_info.properties = fusb302_props;
        board_info.irq = fusb302_irq;
 
        data->fusb302 = i2c_acpi_new_device(dev, 2, &board_info);
@@ -92,6 +183,7 @@ static int cht_int33fe_probe(struct i2c_client *client)
                goto out_unregister_max17047;
 
        memset(&board_info, 0, sizeof(board_info));
+       board_info.dev_name = "pi3usb30532";
        strlcpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE);
 
        data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info);
@@ -106,7 +198,8 @@ out_unregister_fusb302:
        i2c_unregister_device(data->fusb302);
 
 out_unregister_max17047:
-       i2c_unregister_device(data->max17047);
+       if (data->max17047)
+               i2c_unregister_device(data->max17047);
 
        return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
 }
@@ -117,7 +210,8 @@ static int cht_int33fe_remove(struct i2c_client *i2c)
 
        i2c_unregister_device(data->pi3usb30532);
        i2c_unregister_device(data->fusb302);
-       i2c_unregister_device(data->max17047);
+       if (data->max17047)
+               i2c_unregister_device(data->max17047);
 
        return 0;
 }
index 58dcee562d6417be3e1d7ee52fc91c384469cffa..a0c95853fd3f98abbe1979ad2478d6b781c7b046 100644 (file)
  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  * more details.
  *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  *
@@ -259,8 +255,6 @@ static const int IPS_SAMPLE_WINDOW = 5000; /* 5s moving window of samples */
 
 /* Per-SKU limits */
 struct ips_mcp_limits {
-       int cpu_family;
-       int cpu_model; /* includes extended model... */
        int mcp_power_limit; /* mW units */
        int core_power_limit;
        int mch_power_limit;
@@ -295,11 +289,14 @@ static struct ips_mcp_limits ips_ulv_limits = {
 };
 
 struct ips_driver {
-       struct pci_dev *dev;
-       void *regmap;
+       struct device *dev;
+       void __iomem *regmap;
+       int irq;
+
        struct task_struct *monitor;
        struct task_struct *adjust;
        struct dentry *debug_root;
+       struct timer_list timer;
 
        /* Average CPU core temps (all averages in .01 degrees C for precision) */
        u16 ctv1_avg_temp;
@@ -594,7 +591,7 @@ static void ips_disable_gpu_turbo(struct ips_driver *ips)
                return;
 
        if (!ips->gpu_turbo_disable())
-               dev_err(&ips->dev->dev, "failed to disable graphics turbo\n");
+               dev_err(ips->dev, "failed to disable graphics turbo\n");
        else
                ips->__gpu_turbo_on = false;
 }
@@ -649,8 +646,7 @@ static bool cpu_exceeded(struct ips_driver *ips, int cpu)
        spin_unlock_irqrestore(&ips->turbo_status_lock, flags);
 
        if (ret)
-               dev_info(&ips->dev->dev,
-                        "CPU power or thermal limit exceeded\n");
+               dev_info(ips->dev, "CPU power or thermal limit exceeded\n");
 
        return ret;
 }
@@ -769,7 +765,7 @@ static int ips_adjust(void *data)
        struct ips_driver *ips = data;
        unsigned long flags;
 
-       dev_dbg(&ips->dev->dev, "starting ips-adjust thread\n");
+       dev_dbg(ips->dev, "starting ips-adjust thread\n");
 
        /*
         * Adjust CPU and GPU clamps every 5s if needed.  Doing it more
@@ -816,7 +812,7 @@ sleep:
                schedule_timeout_interruptible(msecs_to_jiffies(IPS_ADJUST_PERIOD));
        } while (!kthread_should_stop());
 
-       dev_dbg(&ips->dev->dev, "ips-adjust thread stopped\n");
+       dev_dbg(ips->dev, "ips-adjust thread stopped\n");
 
        return 0;
 }
@@ -942,9 +938,10 @@ static u32 calc_avg_power(struct ips_driver *ips, u32 *array)
        return avg;
 }
 
-static void monitor_timeout(unsigned long arg)
+static void monitor_timeout(struct timer_list *t)
 {
-       wake_up_process((struct task_struct *)arg);
+       struct ips_driver *ips = from_timer(ips, t, timer);
+       wake_up_process(ips->monitor);
 }
 
 /**
@@ -961,7 +958,6 @@ static void monitor_timeout(unsigned long arg)
 static int ips_monitor(void *data)
 {
        struct ips_driver *ips = data;
-       struct timer_list timer;
        unsigned long seqno_timestamp, expire, last_msecs, last_sample_period;
        int i;
        u32 *cpu_samples, *mchp_samples, old_cpu_power;
@@ -976,7 +972,7 @@ static int ips_monitor(void *data)
        mchp_samples = kzalloc(sizeof(u32) * IPS_SAMPLE_COUNT, GFP_KERNEL);
        if (!mcp_samples || !ctv1_samples || !ctv2_samples || !mch_samples ||
                        !cpu_samples || !mchp_samples) {
-               dev_err(&ips->dev->dev,
+               dev_err(ips->dev,
                        "failed to allocate sample array, ips disabled\n");
                kfree(mcp_samples);
                kfree(ctv1_samples);
@@ -1049,8 +1045,7 @@ static int ips_monitor(void *data)
        schedule_timeout_interruptible(msecs_to_jiffies(IPS_SAMPLE_PERIOD));
        last_sample_period = IPS_SAMPLE_PERIOD;
 
-       setup_deferrable_timer_on_stack(&timer, monitor_timeout,
-                                       (unsigned long)current);
+       timer_setup(&ips->timer, monitor_timeout, TIMER_DEFERRABLE);
        do {
                u32 cpu_val, mch_val;
                u16 val;
@@ -1097,7 +1092,8 @@ static int ips_monitor(void *data)
                        ITV_ME_SEQNO_SHIFT;
                if (cur_seqno == last_seqno &&
                    time_after(jiffies, seqno_timestamp + HZ)) {
-                       dev_warn(&ips->dev->dev, "ME failed to update for more than 1s, likely hung\n");
+                       dev_warn(ips->dev,
+                                "ME failed to update for more than 1s, likely hung\n");
                } else {
                        seqno_timestamp = get_jiffies_64();
                        last_seqno = cur_seqno;
@@ -1107,7 +1103,7 @@ static int ips_monitor(void *data)
                expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD);
 
                __set_current_state(TASK_INTERRUPTIBLE);
-               mod_timer(&timer, expire);
+               mod_timer(&ips->timer, expire);
                schedule();
 
                /* Calculate actual sample period for power averaging */
@@ -1116,10 +1112,9 @@ static int ips_monitor(void *data)
                        last_sample_period = 1;
        } while (!kthread_should_stop());
 
-       del_timer_sync(&timer);
-       destroy_timer_on_stack(&timer);
+       del_timer_sync(&ips->timer);
 
-       dev_dbg(&ips->dev->dev, "ips-monitor thread stopped\n");
+       dev_dbg(ips->dev, "ips-monitor thread stopped\n");
 
        return 0;
 }
@@ -1128,17 +1123,17 @@ static int ips_monitor(void *data)
 #define THM_DUMPW(reg) \
        { \
        u16 val = thm_readw(reg); \
-       dev_dbg(&ips->dev->dev, #reg ": 0x%04x\n", val); \
+       dev_dbg(ips->dev, #reg ": 0x%04x\n", val); \
        }
 #define THM_DUMPL(reg) \
        { \
        u32 val = thm_readl(reg); \
-       dev_dbg(&ips->dev->dev, #reg ": 0x%08x\n", val); \
+       dev_dbg(ips->dev, #reg ": 0x%08x\n", val); \
        }
 #define THM_DUMPQ(reg) \
        { \
        u64 val = thm_readq(reg); \
-       dev_dbg(&ips->dev->dev, #reg ": 0x%016x\n", val); \
+       dev_dbg(ips->dev, #reg ": 0x%016x\n", val); \
        }
 
 static void dump_thermal_info(struct ips_driver *ips)
@@ -1146,7 +1141,7 @@ static void dump_thermal_info(struct ips_driver *ips)
        u16 ptl;
 
        ptl = thm_readw(THM_PTL);
-       dev_dbg(&ips->dev->dev, "Processor temp limit: %d\n", ptl);
+       dev_dbg(ips->dev, "Processor temp limit: %d\n", ptl);
 
        THM_DUMPW(THM_CTA);
        THM_DUMPW(THM_TRC);
@@ -1175,8 +1170,8 @@ static irqreturn_t ips_irq_handler(int irq, void *arg)
        if (!tses && !tes)
                return IRQ_NONE;
 
-       dev_info(&ips->dev->dev, "TSES: 0x%02x\n", tses);
-       dev_info(&ips->dev->dev, "TES: 0x%02x\n", tes);
+       dev_info(ips->dev, "TSES: 0x%02x\n", tses);
+       dev_info(ips->dev, "TES: 0x%02x\n", tes);
 
        /* STS update from EC? */
        if (tes & 1) {
@@ -1214,8 +1209,8 @@ static irqreturn_t ips_irq_handler(int irq, void *arg)
 
        /* Thermal trip */
        if (tses) {
-               dev_warn(&ips->dev->dev,
-                        "thermal trip occurred, tses: 0x%04x\n", tses);
+               dev_warn(ips->dev, "thermal trip occurred, tses: 0x%04x\n",
+                        tses);
                thm_writeb(THM_TSES, tses);
        }
 
@@ -1330,8 +1325,7 @@ static void ips_debugfs_init(struct ips_driver *ips)
 
        ips->debug_root = debugfs_create_dir("ips", NULL);
        if (!ips->debug_root) {
-               dev_err(&ips->dev->dev,
-                       "failed to create debugfs entries: %ld\n",
+               dev_err(ips->dev, "failed to create debugfs entries: %ld\n",
                        PTR_ERR(ips->debug_root));
                return;
        }
@@ -1345,8 +1339,7 @@ static void ips_debugfs_init(struct ips_driver *ips)
                                          ips->debug_root, node,
                                          &ips_debugfs_ops);
                if (!ent) {
-                       dev_err(&ips->dev->dev,
-                               "failed to create debug file: %ld\n",
+                       dev_err(ips->dev, "failed to create debug file: %ld\n",
                                PTR_ERR(ent));
                        goto err_cleanup;
                }
@@ -1373,8 +1366,8 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips)
        u16 tdp;
 
        if (!(boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 37)) {
-               dev_info(&ips->dev->dev, "Non-IPS CPU detected.\n");
-               goto out;
+               dev_info(ips->dev, "Non-IPS CPU detected.\n");
+               return NULL;
        }
 
        rdmsrl(IA32_MISC_ENABLE, misc_en);
@@ -1395,8 +1388,8 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips)
        else if (strstr(boot_cpu_data.x86_model_id, "CPU       U"))
                limits = &ips_ulv_limits;
        else {
-               dev_info(&ips->dev->dev, "No CPUID match found.\n");
-               goto out;
+               dev_info(ips->dev, "No CPUID match found.\n");
+               return NULL;
        }
 
        rdmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_power);
@@ -1404,12 +1397,12 @@ static struct ips_mcp_limits *ips_detect_cpu(struct ips_driver *ips)
 
        /* Sanity check TDP against CPU */
        if (limits->core_power_limit != (tdp / 8) * 1000) {
-               dev_info(&ips->dev->dev, "CPU TDP doesn't match expected value (found %d, expected %d)\n",
+               dev_info(ips->dev,
+                        "CPU TDP doesn't match expected value (found %d, expected %d)\n",
                         tdp / 8, limits->core_power_limit / 1000);
                limits->core_power_limit = (tdp / 8) * 1000;
        }
 
-out:
        return limits;
 }
 
@@ -1459,7 +1452,7 @@ ips_gpu_turbo_enabled(struct ips_driver *ips)
 {
        if (!ips->gpu_busy && late_i915_load) {
                if (ips_get_i915_syms(ips)) {
-                       dev_info(&ips->dev->dev,
+                       dev_info(ips->dev,
                                 "i915 driver attached, reenabling gpu turbo\n");
                        ips->gpu_turbo_enabled = !(thm_readl(THM_HTS) & HTS_GTD_DIS);
                }
@@ -1480,8 +1473,7 @@ ips_link_to_i915_driver(void)
 EXPORT_SYMBOL_GPL(ips_link_to_i915_driver);
 
 static const struct pci_device_id ips_id_table[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
-                    PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_THERMAL_SENSOR), },
        { 0, }
 };
 
@@ -1517,62 +1509,45 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id)
        if (dmi_check_system(ips_blacklist))
                return -ENODEV;
 
-       ips = kzalloc(sizeof(struct ips_driver), GFP_KERNEL);
+       ips = devm_kzalloc(&dev->dev, sizeof(*ips), GFP_KERNEL);
        if (!ips)
                return -ENOMEM;
 
-       pci_set_drvdata(dev, ips);
-       ips->dev = dev;
+       spin_lock_init(&ips->turbo_status_lock);
+       ips->dev = &dev->dev;
 
        ips->limits = ips_detect_cpu(ips);
        if (!ips->limits) {
                dev_info(&dev->dev, "IPS not supported on this CPU\n");
-               ret = -ENXIO;
-               goto error_free;
+               return -ENXIO;
        }
 
-       spin_lock_init(&ips->turbo_status_lock);
-
-       ret = pci_enable_device(dev);
+       ret = pcim_enable_device(dev);
        if (ret) {
                dev_err(&dev->dev, "can't enable PCI device, aborting\n");
-               goto error_free;
+               return ret;
        }
 
-       if (!pci_resource_start(dev, 0)) {
-               dev_err(&dev->dev, "TBAR not assigned, aborting\n");
-               ret = -ENXIO;
-               goto error_free;
-       }
-
-       ret = pci_request_regions(dev, "ips thermal sensor");
+       ret = pcim_iomap_regions(dev, 1 << 0, pci_name(dev));
        if (ret) {
-               dev_err(&dev->dev, "thermal resource busy, aborting\n");
-               goto error_free;
-       }
-
-
-       ips->regmap = ioremap(pci_resource_start(dev, 0),
-                             pci_resource_len(dev, 0));
-       if (!ips->regmap) {
                dev_err(&dev->dev, "failed to map thermal regs, aborting\n");
-               ret = -EBUSY;
-               goto error_release;
+               return ret;
        }
+       ips->regmap = pcim_iomap_table(dev)[0];
+
+       pci_set_drvdata(dev, ips);
 
        tse = thm_readb(THM_TSE);
        if (tse != TSE_EN) {
                dev_err(&dev->dev, "thermal device not enabled (0x%02x), aborting\n", tse);
-               ret = -ENXIO;
-               goto error_unmap;
+               return -ENXIO;
        }
 
        trc = thm_readw(THM_TRC);
        trc_required_mask = TRC_CORE1_EN | TRC_CORE_PWR | TRC_MCH_EN;
        if ((trc & trc_required_mask) != trc_required_mask) {
                dev_err(&dev->dev, "thermal reporting for required devices not enabled, aborting\n");
-               ret = -ENXIO;
-               goto error_unmap;
+               return -ENXIO;
        }
 
        if (trc & TRC_CORE2_EN)
@@ -1602,20 +1577,23 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id)
        rdmsrl(PLATFORM_INFO, platform_info);
        if (!(platform_info & PLATFORM_TDP)) {
                dev_err(&dev->dev, "platform indicates TDP override unavailable, aborting\n");
-               ret = -ENODEV;
-               goto error_unmap;
+               return -ENODEV;
        }
 
        /*
         * IRQ handler for ME interaction
         * Note: don't use MSI here as the PCH has bugs.
         */
-       pci_disable_msi(dev);
-       ret = request_irq(dev->irq, ips_irq_handler, IRQF_SHARED, "ips",
-                         ips);
+       ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_LEGACY);
+       if (ret < 0)
+               return ret;
+
+       ips->irq = pci_irq_vector(dev, 0);
+
+       ret = request_irq(ips->irq, ips_irq_handler, IRQF_SHARED, "ips", ips);
        if (ret) {
                dev_err(&dev->dev, "request irq failed, aborting\n");
-               goto error_unmap;
+               return ret;
        }
 
        /* Enable aux, hot & critical interrupts */
@@ -1672,13 +1650,8 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id)
 error_thread_cleanup:
        kthread_stop(ips->adjust);
 error_free_irq:
-       free_irq(ips->dev->irq, ips);
-error_unmap:
-       iounmap(ips->regmap);
-error_release:
-       pci_release_regions(dev);
-error_free:
-       kfree(ips);
+       free_irq(ips->irq, ips);
+       pci_free_irq_vectors(dev);
        return ret;
 }
 
@@ -1709,27 +1682,20 @@ static void ips_remove(struct pci_dev *dev)
        wrmsrl(TURBO_POWER_CURRENT_LIMIT, turbo_override);
        wrmsrl(TURBO_POWER_CURRENT_LIMIT, ips->orig_turbo_limit);
 
-       free_irq(ips->dev->irq, ips);
+       free_irq(ips->irq, ips);
+       pci_free_irq_vectors(dev);
        if (ips->adjust)
                kthread_stop(ips->adjust);
        if (ips->monitor)
                kthread_stop(ips->monitor);
-       iounmap(ips->regmap);
-       pci_release_regions(dev);
-       kfree(ips);
        dev_dbg(&dev->dev, "IPS driver removed\n");
 }
 
-static void ips_shutdown(struct pci_dev *dev)
-{
-}
-
 static struct pci_driver ips_pci_driver = {
        .name = "intel ips",
        .id_table = ips_id_table,
        .probe = ips_probe,
        .remove = ips_remove,
-       .shutdown = ips_shutdown,
 };
 
 module_pci_driver(ips_pci_driver);
index 73299beff5b358b8ba5825d5d83d88d1182e333b..60f4e3ddbe9f5b3f84e02030a57b98793141e615 100644 (file)
  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  * more details.
  *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
  * The full GNU General Public License is included in this distribution in
  * the file called "COPYING".
  */
index a47a41fc10ad77c427158811857da4bc21cd747f..b5b890127479f8f586880762828bbd447a383ddf 100644 (file)
@@ -252,28 +252,28 @@ static int intel_punit_get_bars(struct platform_device *pdev)
         * - GTDRIVER_IPC BASE_IFACE
         */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-       if (res) {
+       if (res && resource_size(res) > 1) {
                addr = devm_ioremap_resource(&pdev->dev, res);
                if (!IS_ERR(addr))
                        punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
-       if (res) {
+       if (res && resource_size(res) > 1) {
                addr = devm_ioremap_resource(&pdev->dev, res);
                if (!IS_ERR(addr))
                        punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
-       if (res) {
+       if (res && resource_size(res) > 1) {
                addr = devm_ioremap_resource(&pdev->dev, res);
                if (!IS_ERR(addr))
                        punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr;
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 5);
-       if (res) {
+       if (res && resource_size(res) > 1) {
                addr = devm_ioremap_resource(&pdev->dev, res);
                if (!IS_ERR(addr))
                        punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr;
index 0d4c3808a6d892f38b0bf6f3ae87dc9f8ae8dba0..f378621b5fe9d86632a853a33599b1efff88b05a 100644 (file)
@@ -15,9 +15,8 @@
  * Telemetry Framework provides platform related PM and performance statistics.
  * This file provides the core telemetry API implementation.
  */
-#include <linux/module.h>
-#include <linux/init.h>
 #include <linux/device.h>
+#include <linux/module.h>
 
 #include <asm/intel_telemetry.h>
 
index d4fc42b4cbebfedeeaf39e8c65e28b2d0a8b6c0a..4249e8267bbcfc321bd83ac678c20de8f967b584 100644 (file)
  * /sys/kernel/debug/telemetry/ioss_race_verbosity: Write and Change Tracing
  *                             Verbosity via firmware
  */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
 #include <linux/debugfs.h>
-#include <linux/seq_file.h>
+#include <linux/device.h>
 #include <linux/io.h>
-#include <linux/uaccess.h>
+#include <linux/module.h>
 #include <linux/pci.h>
+#include <linux/seq_file.h>
 #include <linux/suspend.h>
 
 #include <asm/cpu_device_id.h>
@@ -76,8 +74,6 @@
 #define TELEM_IOSS_DX_D0IX_EVTS                25
 #define TELEM_IOSS_PG_EVTS             30
 
-#define TELEM_EVT_LEN(x) (sizeof(x)/sizeof((x)[0]))
-
 #define TELEM_DEBUGFS_CPU(model, data) \
        { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, (unsigned long)&data}
 
@@ -304,13 +300,13 @@ static struct telemetry_debugfs_conf telem_apl_debugfs_conf = {
        .ioss_d0ix_data = telem_apl_ioss_d0ix_data,
        .ioss_pg_data = telem_apl_ioss_pg_data,
 
-       .pss_idle_evts = TELEM_EVT_LEN(telem_apl_pss_idle_data),
-       .pcs_idle_blkd_evts = TELEM_EVT_LEN(telem_apl_pcs_idle_blkd_data),
-       .pcs_s0ix_blkd_evts = TELEM_EVT_LEN(telem_apl_pcs_s0ix_blkd_data),
-       .pss_ltr_evts = TELEM_EVT_LEN(telem_apl_pss_ltr_data),
-       .pss_wakeup_evts = TELEM_EVT_LEN(telem_apl_pss_wakeup),
-       .ioss_d0ix_evts = TELEM_EVT_LEN(telem_apl_ioss_d0ix_data),
-       .ioss_pg_evts = TELEM_EVT_LEN(telem_apl_ioss_pg_data),
+       .pss_idle_evts = ARRAY_SIZE(telem_apl_pss_idle_data),
+       .pcs_idle_blkd_evts = ARRAY_SIZE(telem_apl_pcs_idle_blkd_data),
+       .pcs_s0ix_blkd_evts = ARRAY_SIZE(telem_apl_pcs_s0ix_blkd_data),
+       .pss_ltr_evts = ARRAY_SIZE(telem_apl_pss_ltr_data),
+       .pss_wakeup_evts = ARRAY_SIZE(telem_apl_pss_wakeup),
+       .ioss_d0ix_evts = ARRAY_SIZE(telem_apl_ioss_d0ix_data),
+       .ioss_pg_evts = ARRAY_SIZE(telem_apl_ioss_pg_data),
 
        .pstates_id = TELEM_APL_PSS_PSTATES_ID,
        .pss_idle_id = TELEM_APL_PSS_IDLE_ID,
index e0424d5a795a5e0a5baa969546df60252c9a230e..2f889d6c270e85c50fd8296af3636cc216e84ed9 100644 (file)
  * It used the PUNIT and PMC IPC interfaces for configuring the counters.
  * The accumulated results are fetched from SRAM.
  */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
+
 #include <linux/io.h>
-#include <linux/uaccess.h>
-#include <linux/pci.h>
-#include <linux/suspend.h>
+#include <linux/module.h>
 #include <linux/platform_device.h>
 
 #include <asm/cpu_device_id.h>
@@ -256,7 +250,7 @@ static int telemetry_check_evtid(enum telemetry_unit telem_unit,
                break;
 
        default:
-               pr_err("Unknown Telemetry action Specified %d\n", action);
+               pr_err("Unknown Telemetry action specified %d\n", action);
                return -EINVAL;
        }
 
@@ -659,7 +653,7 @@ static int telemetry_setup(struct platform_device *pdev)
        ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
                                        TELEM_RESET);
        if (ret) {
-               dev_err(&pdev->dev, "TELEMTRY Setup Failed\n");
+               dev_err(&pdev->dev, "TELEMETRY Setup Failed\n");
                return ret;
        }
        return 0;
@@ -685,7 +679,7 @@ static int telemetry_plt_update_events(struct telemetry_evtconfig pss_evtconfig,
        ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
                                        TELEM_UPDATE);
        if (ret)
-               pr_err("TELEMTRY Config Failed\n");
+               pr_err("TELEMETRY Config Failed\n");
 
        return ret;
 }
@@ -822,7 +816,7 @@ static int telemetry_plt_reset_events(void)
        ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
                                        TELEM_RESET);
        if (ret)
-               pr_err("TELEMTRY Reset Failed\n");
+               pr_err("TELEMETRY Reset Failed\n");
 
        return ret;
 }
@@ -885,7 +879,7 @@ static int telemetry_plt_add_events(u8 num_pss_evts, u8 num_ioss_evts,
        ret = telemetry_setup_evtconfig(pss_evtconfig, ioss_evtconfig,
                                        TELEM_ADD);
        if (ret)
-               pr_err("TELEMTRY ADD Failed\n");
+               pr_err("TELEMETRY ADD Failed\n");
 
        return ret;
 }
@@ -1195,7 +1189,7 @@ static int telemetry_pltdrv_probe(struct platform_device *pdev)
 
        ret = telemetry_set_pltdata(&telm_pltops, telm_conf);
        if (ret) {
-               dev_err(&pdev->dev, "TELEMTRY Set Pltops Failed.\n");
+               dev_err(&pdev->dev, "TELEMETRY Set Pltops Failed.\n");
                goto out;
        }
 
@@ -1210,7 +1204,7 @@ out:
                iounmap(telm_conf->pss_config.regmap);
        if (telm_conf->ioss_config.regmap)
                iounmap(telm_conf->ioss_config.regmap);
-       dev_err(&pdev->dev, "TELEMTRY Setup Failed.\n");
+       dev_err(&pdev->dev, "TELEMETRY Setup Failed.\n");
 
        return ret;
 }
@@ -1234,7 +1228,6 @@ static struct platform_driver telemetry_soc_driver = {
 
 static int __init telemetry_module_init(void)
 {
-       pr_info(DRIVER_NAME ": version %s loaded\n", DRIVER_VERSION);
        return platform_driver_register(&telemetry_soc_driver);
 }
 
index 4f60d8e32a0a538b9632d809d5e52186007d1db3..d4ea01805879b4c9049b1657fad6f6ddb9de27d9 100644 (file)
@@ -125,6 +125,7 @@ static int itmt_legacy_cpu_online(unsigned int cpu)
 
 static const struct x86_cpu_id itmt_legacy_cpu_ids[] = {
        ICPU(INTEL_FAM6_BROADWELL_X),
+       ICPU(INTEL_FAM6_SKYLAKE_X),
        {}
 };
 
index 4f3de2a8c4dfe4fd594a44d9fa9081a9157b9069..504256c3660d84e15444d265ca1fd9747f4fc0dc 100644 (file)
@@ -216,8 +216,8 @@ static struct resource mlxplat_mlxcpld_resources[] = {
        [0] = DEFINE_RES_IRQ_NAMED(17, "mlxcpld-hotplug"),
 };
 
-struct platform_device *mlxplat_dev;
-struct mlxcpld_hotplug_platform_data *mlxplat_hotplug;
+static struct platform_device *mlxplat_dev;
+static struct mlxcpld_hotplug_platform_data *mlxplat_hotplug;
 
 static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
 {
index bc98ef95514a1770c285ed54b4913d7c41ff5ad6..9b9e1f39bbfbb8a75a643783072080bc10b10735 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/dmi.h>
 #include <linux/input-polldev.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -64,8 +65,23 @@ static void peaq_wmi_poll(struct input_polled_dev *dev)
        }
 }
 
+/* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */
+static const struct dmi_system_id peaq_dmi_table[] __initconst = {
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"),
+               },
+       },
+       {}
+};
+
 static int __init peaq_wmi_init(void)
 {
+       /* WMI GUID is not unique, also check for a DMI match */
+       if (!dmi_check_system(peaq_dmi_table))
+               return -ENODEV;
+
        if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID))
                return -ENODEV;
 
@@ -86,9 +102,6 @@ static int __init peaq_wmi_init(void)
 
 static void __exit peaq_wmi_exit(void)
 {
-       if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID))
-               return;
-
        input_unregister_polled_device(peaq_poll_dev);
 }
 
index 1157a7b646d66c17682e985eb22d81e6bea7ab45..266535c2a72f21dc7309962542bd71b68fe9b98f 100644 (file)
@@ -58,6 +58,7 @@ static const struct property_entry dexp_ursus_7w_props[] = {
        PROPERTY_ENTRY_U32("touchscreen-size-y", 630),
        PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-dexp-ursus-7w.fw"),
        PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
        { }
 };
 
@@ -72,6 +73,7 @@ static const struct property_entry surftab_wintron70_st70416_6_props[] = {
        PROPERTY_ENTRY_STRING("firmware-name",
                              "gsl1686-surftab-wintron70-st70416-6.fw"),
        PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
        { }
 };
 
@@ -83,6 +85,8 @@ static const struct silead_ts_dmi_data surftab_wintron70_st70416_6_data = {
 static const struct property_entry gp_electronic_t701_props[] = {
        PROPERTY_ENTRY_U32("touchscreen-size-x", 960),
        PROPERTY_ENTRY_U32("touchscreen-size-y", 640),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
        PROPERTY_ENTRY_STRING("firmware-name",
                              "gsl1680-gp-electronic-t701.fw"),
        { }
@@ -114,6 +118,7 @@ static const struct property_entry pov_mobii_wintab_p800w_props[] = {
        PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
        PROPERTY_ENTRY_STRING("firmware-name",
                              "gsl3692-pov-mobii-wintab-p800w.fw"),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
        { }
 };
 
@@ -136,6 +141,36 @@ static const struct silead_ts_dmi_data itworks_tw891_data = {
        .properties     = itworks_tw891_props,
 };
 
+static const struct property_entry chuwi_hi8_pro_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1728),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1148),
+       PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+       PROPERTY_ENTRY_STRING("firmware-name", "gsl3680-chuwi-hi8-pro.fw"),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
+       { }
+};
+
+static const struct silead_ts_dmi_data chuwi_hi8_pro_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = chuwi_hi8_pro_props,
+};
+
+static const struct property_entry digma_citi_e200_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
+       PROPERTY_ENTRY_U32("touchscreen-size-y", 1500),
+       PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
+       PROPERTY_ENTRY_STRING("firmware-name",
+                             "gsl1686-digma_citi_e200.fw"),
+       PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+       PROPERTY_ENTRY_BOOL("silead,home-button"),
+       { }
+};
+
+static const struct silead_ts_dmi_data digma_citi_e200_data = {
+       .acpi_name      = "MSSL1680:00",
+       .properties     = digma_citi_e200_props,
+};
+
 static const struct dmi_system_id silead_ts_dmi_table[] = {
        {
                /* CUBE iwork8 Air */
@@ -219,6 +254,23 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "TW891"),
                },
        },
+       {
+               /* Chuwi Hi8 Pro */
+               .driver_data = (void *)&chuwi_hi8_pro_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Hampoo"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X1D3_C806N"),
+               },
+       },
+       {
+               /* Digma Citi E200 */
+               .driver_data = (void *)&digma_citi_e200_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Digma"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CITI E200"),
+                       DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+               },
+       },
        { },
 };
 
index a16cea2be9c34a0c83d17a6ce7a35a1704956682..62aa2c37b8d2821450008fb820cbd7be9552f6a7 100644 (file)
@@ -1627,7 +1627,7 @@ static const struct rfkill_ops sony_rfkill_ops = {
 static int sony_nc_setup_rfkill(struct acpi_device *device,
                                enum sony_nc_rfkill nc_type)
 {
-       int err = 0;
+       int err;
        struct rfkill *rfk;
        enum rfkill_type type;
        const char *name;
@@ -1660,17 +1660,19 @@ static int sony_nc_setup_rfkill(struct acpi_device *device,
        if (!rfk)
                return -ENOMEM;
 
-       if (sony_call_snc_handle(sony_rfkill_handle, 0x200, &result) < 0) {
+       err = sony_call_snc_handle(sony_rfkill_handle, 0x200, &result);
+       if (err < 0) {
                rfkill_destroy(rfk);
-               return -1;
+               return err;
        }
        hwblock = !(result & 0x1);
 
-       if (sony_call_snc_handle(sony_rfkill_handle,
-                               sony_rfkill_address[nc_type],
-                               &result) < 0) {
+       err = sony_call_snc_handle(sony_rfkill_handle,
+                                  sony_rfkill_address[nc_type],
+                                  &result);
+       if (err < 0) {
                rfkill_destroy(rfk);
-               return -1;
+               return err;
        }
        swblock = !(result & 0x2);
 
index 3887dfeafc964522e43c077534bf62e0747f7dcf..117be48ff4de9a3a70df898565cf6495e92d7ddd 100644 (file)
@@ -310,8 +310,7 @@ static struct {
        enum {
                TP_HOTKEY_TABLET_NONE = 0,
                TP_HOTKEY_TABLET_USES_MHKG,
-               /* X1 Yoga 2016, seen on BIOS N1FET44W */
-               TP_HOTKEY_TABLET_USES_CMMD,
+               TP_HOTKEY_TABLET_USES_GMMS,
        } hotkey_tablet;
        u32 kbdlight:1;
        u32 light:1;
@@ -2044,8 +2043,28 @@ static void hotkey_poll_setup(const bool may_warn);
 
 /* HKEY.MHKG() return bits */
 #define TP_HOTKEY_TABLET_MASK (1 << 3)
-/* ThinkPad X1 Yoga (2016) */
-#define TP_EC_CMMD_TABLET_MODE 0x6
+enum {
+       TP_ACPI_MULTI_MODE_INVALID      = 0,
+       TP_ACPI_MULTI_MODE_UNKNOWN      = 1 << 0,
+       TP_ACPI_MULTI_MODE_LAPTOP       = 1 << 1,
+       TP_ACPI_MULTI_MODE_TABLET       = 1 << 2,
+       TP_ACPI_MULTI_MODE_FLAT         = 1 << 3,
+       TP_ACPI_MULTI_MODE_STAND        = 1 << 4,
+       TP_ACPI_MULTI_MODE_TENT         = 1 << 5,
+       TP_ACPI_MULTI_MODE_STAND_TENT   = 1 << 6,
+};
+
+enum {
+       /* The following modes are considered tablet mode for the purpose of
+        * reporting the status to userspace. i.e. in all these modes it makes
+        * sense to disable the laptop input devices such as touchpad and
+        * keyboard.
+        */
+       TP_ACPI_MULTI_MODE_TABLET_LIKE  = TP_ACPI_MULTI_MODE_TABLET |
+                                         TP_ACPI_MULTI_MODE_STAND |
+                                         TP_ACPI_MULTI_MODE_TENT |
+                                         TP_ACPI_MULTI_MODE_STAND_TENT,
+};
 
 static int hotkey_get_wlsw(void)
 {
@@ -2066,6 +2085,90 @@ static int hotkey_get_wlsw(void)
        return (status) ? TPACPI_RFK_RADIO_ON : TPACPI_RFK_RADIO_OFF;
 }
 
+static int hotkey_gmms_get_tablet_mode(int s, int *has_tablet_mode)
+{
+       int type = (s >> 16) & 0xffff;
+       int value = s & 0xffff;
+       int mode = TP_ACPI_MULTI_MODE_INVALID;
+       int valid_modes = 0;
+
+       if (has_tablet_mode)
+               *has_tablet_mode = 0;
+
+       switch (type) {
+       case 1:
+               valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
+                             TP_ACPI_MULTI_MODE_TABLET |
+                             TP_ACPI_MULTI_MODE_STAND_TENT;
+               break;
+       case 2:
+               valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
+                             TP_ACPI_MULTI_MODE_FLAT |
+                             TP_ACPI_MULTI_MODE_TABLET |
+                             TP_ACPI_MULTI_MODE_STAND |
+                             TP_ACPI_MULTI_MODE_TENT;
+               break;
+       case 3:
+               valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
+                             TP_ACPI_MULTI_MODE_FLAT;
+               break;
+       case 4:
+               valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
+                             TP_ACPI_MULTI_MODE_TABLET |
+                             TP_ACPI_MULTI_MODE_STAND |
+                             TP_ACPI_MULTI_MODE_TENT;
+               break;
+       case 5:
+               valid_modes = TP_ACPI_MULTI_MODE_LAPTOP |
+                             TP_ACPI_MULTI_MODE_FLAT |
+                             TP_ACPI_MULTI_MODE_TABLET |
+                             TP_ACPI_MULTI_MODE_STAND |
+                             TP_ACPI_MULTI_MODE_TENT;
+               break;
+       default:
+               pr_err("Unknown multi mode status type %d with value 0x%04X, please report this to %s\n",
+                      type, value, TPACPI_MAIL);
+               return 0;
+       }
+
+       if (has_tablet_mode && (valid_modes & TP_ACPI_MULTI_MODE_TABLET_LIKE))
+               *has_tablet_mode = 1;
+
+       switch (value) {
+       case 1:
+               mode = TP_ACPI_MULTI_MODE_LAPTOP;
+               break;
+       case 2:
+               mode = TP_ACPI_MULTI_MODE_FLAT;
+               break;
+       case 3:
+               mode = TP_ACPI_MULTI_MODE_TABLET;
+               break;
+       case 4:
+               if (type == 1)
+                       mode = TP_ACPI_MULTI_MODE_STAND_TENT;
+               else
+                       mode = TP_ACPI_MULTI_MODE_STAND;
+               break;
+       case 5:
+               mode = TP_ACPI_MULTI_MODE_TENT;
+               break;
+       default:
+               if (type == 5 && value == 0xffff) {
+                       pr_warn("Multi mode status is undetected, assuming laptop\n");
+                       return 0;
+               }
+       }
+
+       if (!(mode & valid_modes)) {
+               pr_err("Unknown/reserved multi mode value 0x%04X for type %d, please report this to %s\n",
+                      value, type, TPACPI_MAIL);
+               return 0;
+       }
+
+       return !!(mode & TP_ACPI_MULTI_MODE_TABLET_LIKE);
+}
+
 static int hotkey_get_tablet_mode(int *status)
 {
        int s;
@@ -2077,11 +2180,11 @@ static int hotkey_get_tablet_mode(int *status)
 
                *status = ((s & TP_HOTKEY_TABLET_MASK) != 0);
                break;
-       case TP_HOTKEY_TABLET_USES_CMMD:
-               if (!acpi_evalf(ec_handle, &s, "CMMD", "d"))
+       case TP_HOTKEY_TABLET_USES_GMMS:
+               if (!acpi_evalf(hkey_handle, &s, "GMMS", "dd", 0))
                        return -EIO;
 
-               *status = (s == TP_EC_CMMD_TABLET_MODE);
+               *status = hotkey_gmms_get_tablet_mode(s, NULL);
                break;
        default:
                break;
@@ -3113,16 +3216,19 @@ static int hotkey_init_tablet_mode(void)
        int in_tablet_mode = 0, res;
        char *type = NULL;
 
-       if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) {
+       if (acpi_evalf(hkey_handle, &res, "GMMS", "qdd", 0)) {
+               int has_tablet_mode;
+
+               in_tablet_mode = hotkey_gmms_get_tablet_mode(res,
+                                                            &has_tablet_mode);
+               if (has_tablet_mode)
+                       tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_GMMS;
+               type = "GMMS";
+       } else if (acpi_evalf(hkey_handle, &res, "MHKG", "qd")) {
                /* For X41t, X60t, X61t Tablets... */
                tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_MHKG;
                in_tablet_mode = !!(res & TP_HOTKEY_TABLET_MASK);
                type = "MHKG";
-       } else if (acpi_evalf(ec_handle, &res, "CMMD", "qd")) {
-               /* For X1 Yoga (2016) */
-               tp_features.hotkey_tablet = TP_HOTKEY_TABLET_USES_CMMD;
-               in_tablet_mode = res == TP_EC_CMMD_TABLET_MODE;
-               type = "CMMD";
        }
 
        if (!tp_features.hotkey_tablet)
index 0765b1797d4c0d35cd58f588d8c4717bf6b1179d..791449a2370f4f10af698dbaf27bb3749d6d263a 100644 (file)
 
 #define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
 
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/types.h>
+#include <linux/acpi.h>
 #include <linux/device.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
 #include <linux/list.h>
-#include <linux/acpi.h>
-#include <linux/slab.h>
+#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/wmi.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
 #include <linux/uuid.h>
+#include <linux/wmi.h>
+#include <uapi/linux/wmi.h>
 
 ACPI_MODULE_NAME("wmi");
 MODULE_AUTHOR("Carlos Corbacho");
@@ -69,9 +72,12 @@ struct wmi_block {
        struct wmi_device dev;
        struct list_head list;
        struct guid_block gblock;
+       struct miscdevice char_dev;
+       struct mutex char_mutex;
        struct acpi_device *acpi_device;
        wmi_notify_handler handler;
        void *handler_data;
+       u64 req_buf_size;
 
        bool read_takes_no_args;
 };
@@ -188,6 +194,25 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
 /*
  * Exported WMI functions
  */
+
+/**
+ * set_required_buffer_size - Sets the buffer size needed for performing IOCTL
+ * @wdev: A wmi bus device from a driver
+ * @instance: Instance index
+ *
+ * Allocates memory needed for buffer, stores the buffer size in that memory
+ */
+int set_required_buffer_size(struct wmi_device *wdev, u64 length)
+{
+       struct wmi_block *wblock;
+
+       wblock = container_of(wdev, struct wmi_block, dev);
+       wblock->req_buf_size = length;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(set_required_buffer_size);
+
 /**
  * wmi_evaluate_method - Evaluate a WMI method
  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
@@ -200,6 +225,28 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
  */
 acpi_status wmi_evaluate_method(const char *guid_string, u8 instance,
 u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
+{
+       struct wmi_block *wblock = NULL;
+
+       if (!find_guid(guid_string, &wblock))
+               return AE_ERROR;
+       return wmidev_evaluate_method(&wblock->dev, instance, method_id,
+                                     in, out);
+}
+EXPORT_SYMBOL_GPL(wmi_evaluate_method);
+
+/**
+ * wmidev_evaluate_method - Evaluate a WMI method
+ * @wdev: A wmi bus device from a driver
+ * @instance: Instance index
+ * @method_id: Method ID to call
+ * &in: Buffer containing input for the method call
+ * &out: Empty buffer to return the method results
+ *
+ * Call an ACPI-WMI method
+ */
+acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance,
+       u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
 {
        struct guid_block *block = NULL;
        struct wmi_block *wblock = NULL;
@@ -209,9 +256,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
        union acpi_object params[3];
        char method[5] = "WM";
 
-       if (!find_guid(guid_string, &wblock))
-               return AE_ERROR;
-
+       wblock = container_of(wdev, struct wmi_block, dev);
        block = &wblock->gblock;
        handle = wblock->acpi_device->handle;
 
@@ -246,7 +291,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out)
 
        return status;
 }
-EXPORT_SYMBOL_GPL(wmi_evaluate_method);
+EXPORT_SYMBOL_GPL(wmidev_evaluate_method);
 
 static acpi_status __query_block(struct wmi_block *wblock, u8 instance,
                                 struct acpi_buffer *out)
@@ -348,23 +393,6 @@ union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance)
 }
 EXPORT_SYMBOL_GPL(wmidev_block_query);
 
-struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev,
-                                        const char *guid_string)
-{
-       struct wmi_block *this_wb = container_of(wdev, struct wmi_block, dev);
-       struct wmi_block *other_wb;
-
-       if (!find_guid(guid_string, &other_wb))
-               return NULL;
-
-       if (other_wb->acpi_device != this_wb->acpi_device)
-               return NULL;
-
-       get_device(&other_wb->dev.dev);
-       return &other_wb->dev;
-}
-EXPORT_SYMBOL_GPL(wmidev_get_other_guid);
-
 /**
  * wmi_set_block - Write to a WMI block
  * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba
@@ -761,6 +789,113 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver)
 
        return 0;
 }
+static int wmi_char_open(struct inode *inode, struct file *filp)
+{
+       const char *driver_name = filp->f_path.dentry->d_iname;
+       struct wmi_block *wblock = NULL;
+       struct wmi_block *next = NULL;
+
+       list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
+               if (!wblock->dev.dev.driver)
+                       continue;
+               if (strcmp(driver_name, wblock->dev.dev.driver->name) == 0) {
+                       filp->private_data = wblock;
+                       break;
+               }
+       }
+
+       if (!filp->private_data)
+               return -ENODEV;
+
+       return nonseekable_open(inode, filp);
+}
+
+static ssize_t wmi_char_read(struct file *filp, char __user *buffer,
+       size_t length, loff_t *offset)
+{
+       struct wmi_block *wblock = filp->private_data;
+
+       return simple_read_from_buffer(buffer, length, offset,
+                                      &wblock->req_buf_size,
+                                      sizeof(wblock->req_buf_size));
+}
+
+static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       struct wmi_ioctl_buffer __user *input =
+               (struct wmi_ioctl_buffer __user *) arg;
+       struct wmi_block *wblock = filp->private_data;
+       struct wmi_ioctl_buffer *buf = NULL;
+       struct wmi_driver *wdriver = NULL;
+       int ret;
+
+       if (_IOC_TYPE(cmd) != WMI_IOC)
+               return -ENOTTY;
+
+       /* make sure we're not calling a higher instance than exists*/
+       if (_IOC_NR(cmd) >= wblock->gblock.instance_count)
+               return -EINVAL;
+
+       mutex_lock(&wblock->char_mutex);
+       buf = wblock->handler_data;
+       if (get_user(buf->length, &input->length)) {
+               dev_dbg(&wblock->dev.dev, "Read length from user failed\n");
+               ret = -EFAULT;
+               goto out_ioctl;
+       }
+       /* if it's too small, abort */
+       if (buf->length < wblock->req_buf_size) {
+               dev_err(&wblock->dev.dev,
+                       "Buffer %lld too small, need at least %lld\n",
+                       buf->length, wblock->req_buf_size);
+               ret = -EINVAL;
+               goto out_ioctl;
+       }
+       /* if it's too big, warn, driver will only use what is needed */
+       if (buf->length > wblock->req_buf_size)
+               dev_warn(&wblock->dev.dev,
+                       "Buffer %lld is bigger than required %lld\n",
+                       buf->length, wblock->req_buf_size);
+
+       /* copy the structure from userspace */
+       if (copy_from_user(buf, input, wblock->req_buf_size)) {
+               dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n",
+                       wblock->req_buf_size);
+               ret = -EFAULT;
+               goto out_ioctl;
+       }
+
+       /* let the driver do any filtering and do the call */
+       wdriver = container_of(wblock->dev.dev.driver,
+                              struct wmi_driver, driver);
+       if (!try_module_get(wdriver->driver.owner)) {
+               ret = -EBUSY;
+               goto out_ioctl;
+       }
+       ret = wdriver->filter_callback(&wblock->dev, cmd, buf);
+       module_put(wdriver->driver.owner);
+       if (ret)
+               goto out_ioctl;
+
+       /* return the result (only up to our internal buffer size) */
+       if (copy_to_user(input, buf, wblock->req_buf_size)) {
+               dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n",
+                       wblock->req_buf_size);
+               ret = -EFAULT;
+       }
+
+out_ioctl:
+       mutex_unlock(&wblock->char_mutex);
+       return ret;
+}
+
+static const struct file_operations wmi_fops = {
+       .owner          = THIS_MODULE,
+       .read           = wmi_char_read,
+       .open           = wmi_char_open,
+       .unlocked_ioctl = wmi_ioctl,
+       .compat_ioctl   = wmi_ioctl,
+};
 
 static int wmi_dev_probe(struct device *dev)
 {
@@ -768,16 +903,63 @@ static int wmi_dev_probe(struct device *dev)
        struct wmi_driver *wdriver =
                container_of(dev->driver, struct wmi_driver, driver);
        int ret = 0;
+       int count;
+       char *buf;
 
        if (ACPI_FAILURE(wmi_method_enable(wblock, 1)))
                dev_warn(dev, "failed to enable device -- probing anyway\n");
 
        if (wdriver->probe) {
                ret = wdriver->probe(dev_to_wdev(dev));
-               if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0)))
-                       dev_warn(dev, "failed to disable device\n");
+               if (ret != 0)
+                       goto probe_failure;
+       }
+
+       /* driver wants a character device made */
+       if (wdriver->filter_callback) {
+               /* check that required buffer size declared by driver or MOF */
+               if (!wblock->req_buf_size) {
+                       dev_err(&wblock->dev.dev,
+                               "Required buffer size not set\n");
+                       ret = -EINVAL;
+                       goto probe_failure;
+               }
+
+               count = get_order(wblock->req_buf_size);
+               wblock->handler_data = (void *)__get_free_pages(GFP_KERNEL,
+                                                               count);
+               if (!wblock->handler_data) {
+                       ret = -ENOMEM;
+                       goto probe_failure;
+               }
+
+               buf = kmalloc(strlen(wdriver->driver.name) + 4, GFP_KERNEL);
+               if (!buf) {
+                       ret = -ENOMEM;
+                       goto probe_string_failure;
+               }
+               sprintf(buf, "wmi/%s", wdriver->driver.name);
+               wblock->char_dev.minor = MISC_DYNAMIC_MINOR;
+               wblock->char_dev.name = buf;
+               wblock->char_dev.fops = &wmi_fops;
+               wblock->char_dev.mode = 0444;
+               ret = misc_register(&wblock->char_dev);
+               if (ret) {
+                       dev_warn(dev, "failed to register char dev: %d", ret);
+                       ret = -ENOMEM;
+                       goto probe_misc_failure;
+               }
        }
 
+       return 0;
+
+probe_misc_failure:
+       kfree(buf);
+probe_string_failure:
+       kfree(wblock->handler_data);
+probe_failure:
+       if (ACPI_FAILURE(wmi_method_enable(wblock, 0)))
+               dev_warn(dev, "failed to disable device\n");
        return ret;
 }
 
@@ -788,6 +970,13 @@ static int wmi_dev_remove(struct device *dev)
                container_of(dev->driver, struct wmi_driver, driver);
        int ret = 0;
 
+       if (wdriver->filter_callback) {
+               misc_deregister(&wblock->char_dev);
+               kfree(wblock->char_dev.name);
+               free_pages((unsigned long)wblock->handler_data,
+                          get_order(wblock->req_buf_size));
+       }
+
        if (wdriver->remove)
                ret = wdriver->remove(dev_to_wdev(dev));
 
@@ -844,6 +1033,7 @@ static int wmi_create_device(struct device *wmi_bus_dev,
 
        if (gblock->flags & ACPI_WMI_METHOD) {
                wblock->dev.dev.type = &wmi_type_method;
+               mutex_init(&wblock->char_mutex);
                goto out_init;
        }
 
@@ -1145,7 +1335,7 @@ static int acpi_wmi_remove(struct platform_device *device)
        acpi_remove_address_space_handler(acpi_device->handle,
                                ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
        wmi_free_devices(acpi_device);
-       device_unregister((struct device *)dev_get_drvdata(&device->dev));
+       device_destroy(&wmi_bus_class, MKDEV(0, 0));
 
        return 0;
 }
@@ -1199,7 +1389,7 @@ static int acpi_wmi_probe(struct platform_device *device)
        return 0;
 
 err_remove_busdev:
-       device_unregister(wmi_bus_dev);
+       device_destroy(&wmi_bus_class, MKDEV(0, 0));
 
 err_remove_notify_handler:
        acpi_remove_notify_handler(acpi_device->handle, ACPI_DEVICE_NOTIFY,
@@ -1264,8 +1454,8 @@ err_unreg_class:
 static void __exit acpi_wmi_exit(void)
 {
        platform_driver_unregister(&acpi_wmi_driver);
-       class_unregister(&wmi_bus_class);
        bus_unregister(&wmi_bus_type);
+       class_unregister(&wmi_bus_class);
 }
 
 subsys_initcall(acpi_wmi_init);
index 5e58f5ec0a28e449afa8813a652b1aa3469e0721..2f615b7f1c9f61e1e04a4857069f7a450d757130 100644 (file)
@@ -905,16 +905,6 @@ config FB_LEO
          This is the frame buffer device driver for the SBUS-based Sun ZX
          (leo) frame buffer cards.
 
-config FB_IGA
-       bool "IGA 168x display support"
-       depends on (FB = y) && SPARC32
-       select FB_CFB_FILLRECT
-       select FB_CFB_COPYAREA
-       select FB_CFB_IMAGEBLIT
-       help
-         This is the framebuffer device for the INTERGRAPHICS 1680 and
-         successor frame buffer cards.
-
 config FB_XVR500
        bool "Sun XVR-500 3DLABS Wildcat support"
        depends on (FB = y) && PCI && SPARC64
index 8895536a20d648723197affff38d1bbf8fc140a8..115961e0721b8ba1aaab4dcfb3b4032a50760637 100644 (file)
@@ -65,7 +65,6 @@ obj-$(CONFIG_FB_HGA)              += hgafb.o
 obj-$(CONFIG_FB_XVR500)           += sunxvr500.o
 obj-$(CONFIG_FB_XVR2500)          += sunxvr2500.o
 obj-$(CONFIG_FB_XVR1000)          += sunxvr1000.o
-obj-$(CONFIG_FB_IGA)              += igafb.o
 obj-$(CONFIG_FB_APOLLO)           += dnfb.o
 obj-$(CONFIG_FB_Q40)              += q40fb.o
 obj-$(CONFIG_FB_TGA)              += tgafb.o
index 3ec72f19114badf5cb26fe1cb530c04916b9d8c0..a9a8272f7a6eeda70a8e8d1a3c8b6381bbfaea41 100644 (file)
@@ -2272,10 +2272,10 @@ static void aty_bl_exit(struct backlight_device *bd)
 
 static void aty_calc_mem_refresh(struct atyfb_par *par, int xclk)
 {
-       const int ragepro_tbl[] = {
+       static const int ragepro_tbl[] = {
                44, 50, 55, 66, 75, 80, 100
        };
-       const int ragexl_tbl[] = {
+       static const int ragexl_tbl[] = {
                50, 66, 75, 83, 90, 95, 100, 105,
                110, 115, 120, 125, 133, 143, 166
        };
index 1e2ec360f8c16da91b8ee9e921452ef4bda77834..4d77daeecf99b4d45b7876921379b8e1c95ec71d 100644 (file)
@@ -1454,9 +1454,9 @@ static void radeon_write_pll_regs(struct radeonfb_info *rinfo, struct radeon_reg
 /*
  * Timer function for delayed LVDS panel power up/down
  */
-static void radeon_lvds_timer_func(unsigned long data)
+static void radeon_lvds_timer_func(struct timer_list *t)
 {
-       struct radeonfb_info *rinfo = (struct radeonfb_info *)data;
+       struct radeonfb_info *rinfo = from_timer(rinfo, t, lvds_timer);
 
        radeon_engine_idle();
 
@@ -1534,7 +1534,7 @@ void radeon_write_mode (struct radeonfb_info *rinfo, struct radeon_regs *mode,
 static void radeon_calc_pll_regs(struct radeonfb_info *rinfo, struct radeon_regs *regs,
                                 unsigned long freq)
 {
-       const struct {
+       static const struct {
                int divider;
                int bitvalue;
        } *post_div,
@@ -2291,9 +2291,7 @@ static int radeonfb_pci_register(struct pci_dev *pdev,
        rinfo->pdev = pdev;
        
        spin_lock_init(&rinfo->reg_lock);
-       init_timer(&rinfo->lvds_timer);
-       rinfo->lvds_timer.function = radeon_lvds_timer_func;
-       rinfo->lvds_timer.data = (unsigned long)rinfo;
+       timer_setup(&rinfo->lvds_timer, radeon_lvds_timer_func, 0);
 
        c1 = ent->device >> 8;
        c2 = ent->device & 0xff;
index f7c253dd5899f1e887abe7c768b1681a51b4162d..7137c12cbcee30ce60bbdcc27c62fdaf0c9cfbdc 100644 (file)
@@ -1208,9 +1208,11 @@ static void radeon_pm_enable_dll_m10(struct radeonfb_info *rinfo)
        case 1:
                if (mc & 0x4)
                        break;
+               /* fall through */
        case 2:
                dll_sleep_mask |= MDLL_R300_RDCK__MRDCKB_SLEEP;
                dll_reset_mask |= MDLL_R300_RDCK__MRDCKB_RESET;
+               /* fall through */
        case 0:
                dll_sleep_mask |= MDLL_R300_RDCK__MRDCKA_SLEEP;
                dll_reset_mask |= MDLL_R300_RDCK__MRDCKA_RESET;
@@ -1219,6 +1221,7 @@ static void radeon_pm_enable_dll_m10(struct radeonfb_info *rinfo)
        case 1:
                if (!(mc & 0x4))
                        break;
+               /* fall through */
        case 2:
                dll_sleep_mask |= MDLL_R300_RDCK__MRDCKD_SLEEP;
                dll_reset_mask |= MDLL_R300_RDCK__MRDCKD_RESET;
index 5f04b4096c428883be0b4032fb9b055916cda5f5..87d5a62bf6ca446fb8141b602c38839363dc120e 100644 (file)
@@ -1518,7 +1518,7 @@ static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id)
 static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev)
 {
        struct fb_info *fbi = fbdev->fb_info;
-       int bpp;
+       int bpp, ret;
 
        fbi->fbops = &au1200fb_fb_ops;
 
@@ -1546,15 +1546,14 @@ static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev)
        }
 
        fbi->pseudo_palette = kcalloc(16, sizeof(u32), GFP_KERNEL);
-       if (!fbi->pseudo_palette) {
+       if (!fbi->pseudo_palette)
                return -ENOMEM;
-       }
 
-       if (fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0) < 0) {
+       ret = fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0);
+       if (ret < 0) {
                print_err("Fail to allocate colormap (%d entries)",
-                          AU1200_LCD_NBR_PALETTE_ENTRIES);
-               kfree(fbi->pseudo_palette);
-               return -EFAULT;
+                         AU1200_LCD_NBR_PALETTE_ENTRIES);
+               return ret;
        }
 
        strncpy(fbi->fix.id, "AU1200", sizeof(fbi->fix.id));
@@ -1668,10 +1667,6 @@ static int au1200fb_drv_probe(struct platform_device *dev)
        printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name);
        printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name);
 
-       /* shut gcc up */
-       ret = 0;
-       fbdev = NULL;
-
        for (plane = 0; plane < device_count; ++plane) {
                bpp = winbpp(win->w[plane].mode_winctrl1);
                if (win->w[plane].xres == 0)
@@ -1681,8 +1676,10 @@ static int au1200fb_drv_probe(struct platform_device *dev)
 
                fbi = framebuffer_alloc(sizeof(struct au1200fb_device),
                                        &dev->dev);
-               if (!fbi)
+               if (!fbi) {
+                       ret = -ENOMEM;
                        goto failed;
+               }
 
                _au1200fb_infos[plane] = fbi;
                fbdev = fbi->par;
@@ -1701,7 +1698,8 @@ static int au1200fb_drv_probe(struct platform_device *dev)
                if (!fbdev->fb_mem) {
                        print_err("fail to allocate frambuffer (size: %dK))",
                                  fbdev->fb_len / 1024);
-                       return -ENOMEM;
+                       ret = -ENOMEM;
+                       goto failed;
                }
 
                /*
@@ -1718,7 +1716,8 @@ static int au1200fb_drv_probe(struct platform_device *dev)
                print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024);
 
                /* Init FB data */
-               if ((ret = au1200fb_init_fbinfo(fbdev)) < 0)
+               ret = au1200fb_init_fbinfo(fbdev);
+               if (ret < 0)
                        goto failed;
 
                /* Register new framebuffer */
@@ -1758,21 +1757,26 @@ static int au1200fb_drv_probe(struct platform_device *dev)
        return 0;
 
 failed:
-       /* NOTE: This only does the current plane/window that failed; others are still active */
-       if (fbi) {
+       for (plane = 0; plane < device_count; ++plane) {
+               fbi = _au1200fb_infos[plane];
+               if (!fbi)
+                       break;
+
+               /* Clean up all probe data */
+               unregister_framebuffer(fbi);
                if (fbi->cmap.len != 0)
                        fb_dealloc_cmap(&fbi->cmap);
                kfree(fbi->pseudo_palette);
+
+               framebuffer_release(fbi);
+               _au1200fb_infos[plane] = NULL;
        }
-       if (plane == 0)
-               free_irq(AU1200_LCD_INT, (void*)dev);
        return ret;
 }
 
 static int au1200fb_drv_remove(struct platform_device *dev)
 {
        struct au1200fb_platdata *pd = platform_get_drvdata(dev);
-       struct au1200fb_device *fbdev;
        struct fb_info *fbi;
        int plane;
 
@@ -1781,7 +1785,6 @@ static int au1200fb_drv_remove(struct platform_device *dev)
 
        for (plane = 0; plane < device_count; ++plane)  {
                fbi = _au1200fb_infos[plane];
-               fbdev = fbi->par;
 
                /* Clean up all probe data */
                unregister_framebuffer(fbi);
index d992aa5eb3f0dc6557b23b4ec3e4868301c5ede8..b3be06dd290882e5c7d75db66977e73f6dcae128 100644 (file)
@@ -1477,10 +1477,12 @@ static void init_vgachip(struct fb_info *info)
                mdelay(100);
                /* mode */
                vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
-       case BT_GD5480:  /* fall through */
+               /* fall through */
+       case BT_GD5480:
                /* from Klaus' NetBSD driver: */
                vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
-       case BT_ALPINE:  /* fall through */
+               /* fall through */
+       case BT_ALPINE:
                /* put blitter into 542x compat */
                vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
                break;
index 6026c60fc1007e007ec568d23ad26fa3910795e6..261522fabdac89ae2644089aba612dee880b4d24 100644 (file)
@@ -141,5 +141,7 @@ static struct max_cmodes control_mac_modes[] = {
        {{ 1, 2}},      /* 1152x870, 75Hz */
        {{ 0, 1}},      /* 1280x960, 75Hz */
        {{ 0, 1}},      /* 1280x1024, 75Hz */
+       {{ 1, 2}},      /* 1152x768, 60Hz */
+       {{ 0, 1}},      /* 1600x1024, 60Hz */
 };
 
index 04612f938bab1fc3eecf457d070fe501b8a1a3de..929ca472c5242ef50bd9653cffe315d72daac2aa 100644 (file)
@@ -395,10 +395,10 @@ static void fb_flashcursor(struct work_struct *work)
        console_unlock();
 }
 
-static void cursor_timer_handler(unsigned long dev_addr)
+static void cursor_timer_handler(struct timer_list *t)
 {
-       struct fb_info *info = (struct fb_info *) dev_addr;
-       struct fbcon_ops *ops = info->fbcon_par;
+       struct fbcon_ops *ops = from_timer(ops, t, cursor_timer);
+       struct fb_info *info = ops->info;
 
        queue_work(system_power_efficient_wq, &info->queue);
        mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies);
@@ -414,8 +414,7 @@ static void fbcon_add_cursor_timer(struct fb_info *info)
                if (!info->queue.func)
                        INIT_WORK(&info->queue, fb_flashcursor);
 
-               setup_timer(&ops->cursor_timer, cursor_timer_handler,
-                           (unsigned long) info);
+               timer_setup(&ops->cursor_timer, cursor_timer_handler, 0);
                mod_timer(&ops->cursor_timer, jiffies + ops->cur_blink_jiffies);
                ops->flags |= FBCON_FLAGS_CURSOR_TIMER;
        }
@@ -714,6 +713,7 @@ static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info,
 
        if (!err) {
                ops->cur_blink_jiffies = HZ / 5;
+               ops->info = info;
                info->fbcon_par = ops;
 
                if (vc)
@@ -962,6 +962,7 @@ static const char *fbcon_startup(void)
        ops->graphics = 1;
        ops->cur_rotate = -1;
        ops->cur_blink_jiffies = HZ / 5;
+       ops->info = info;
        info->fbcon_par = ops;
        if (initial_rotation != -1)
                p->con_rotate = initial_rotation;
index 18f3ac14423706adc006f37bee55b1241627f99d..9f7744fbc962dc1c19f6b2f4b9d518126fedf0cc 100644 (file)
@@ -69,6 +69,7 @@ struct fbcon_ops {
        struct timer_list cursor_timer; /* Cursor timer */
        struct fb_cursor cursor_state;
        struct display *p;
+       struct fb_info *info;
         int    currcon;                        /* Current VC. */
        int    cur_blink_jiffies;
        int    cursor_flash;
index 7b1492d34e989ab92a1a4819192afc00294af4d7..5505fa00c6348a26597aaa6f65a90092aff5c14c 100644 (file)
@@ -115,7 +115,7 @@ static struct fb_ops dn_fb_ops = {
        .fb_imageblit   = cfb_imageblit,
 };
 
-struct fb_var_screeninfo dnfb_var = {
+static const struct fb_var_screeninfo dnfb_var = {
        .xres           = 1280,
        .yres           = 1024,
        .xres_virtual   = 2048,
@@ -242,16 +242,13 @@ static int dnfb_probe(struct platform_device *dev)
        info->screen_base = (u_char *) info->fix.smem_start;
 
        err = fb_alloc_cmap(&info->cmap, 2, 0);
-       if (err < 0) {
-               framebuffer_release(info);
-               return err;
-       }
+       if (err < 0)
+               goto release_framebuffer;
 
        err = register_framebuffer(info);
        if (err < 0) {
                fb_dealloc_cmap(&info->cmap);
-               framebuffer_release(info);
-               return err;
+               goto release_framebuffer;
        }
        platform_set_drvdata(dev, info);
 
@@ -265,6 +262,10 @@ static int dnfb_probe(struct platform_device *dev)
 
        printk("apollo frame buffer alive and kicking !\n");
        return err;
+
+release_framebuffer:
+       framebuffer_release(info);
+       return err;
 }
 
 static struct platform_driver dnfb_driver = {
index 7f6c9e6cfc6c99d8d9912db5d2f78242923f51c5..3b70044773b67566b6c7ebf516bc23dde166f95b 100644 (file)
@@ -304,12 +304,18 @@ static int goldfish_fb_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct of_device_id goldfish_fb_of_match[] = {
+       { .compatible = "google,goldfish-fb", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, goldfish_fb_of_match);
 
 static struct platform_driver goldfish_fb_driver = {
        .probe          = goldfish_fb_probe,
        .remove         = goldfish_fb_remove,
        .driver = {
-               .name = "goldfish_fb"
+               .name = "goldfish_fb",
+               .of_match_table = goldfish_fb_of_match,
        }
 };
 
diff --git a/drivers/video/fbdev/igafb.c b/drivers/video/fbdev/igafb.c
deleted file mode 100644 (file)
index 486f188..0000000
+++ /dev/null
@@ -1,579 +0,0 @@
-/*
- *  linux/drivers/video/igafb.c -- Frame buffer device for IGA 1682
- *
- *      Copyright (C) 1998  Vladimir Roganov and Gleb Raiko
- *
- *  This driver is partly based on the Frame buffer device for ATI Mach64
- *  and partially on VESA-related code.
- *
- *      Copyright (C) 1997-1998  Geert Uytterhoeven
- *      Copyright (C) 1998  Bernd Harries
- *      Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
- *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License. See the file COPYING in the main directory of this archive for
- *  more details.
- */
-
-/******************************************************************************
-
-  TODO:
-       Despite of IGA Card has advanced graphic acceleration, 
-       initial version is almost dummy and does not support it.
-       Support for video modes and acceleration must be added
-       together with accelerated X-Windows driver implementation.
-
-       Most important thing at this moment is that we have working
-       JavaEngine1  console & X  with new console interface.
-
-******************************************************************************/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/fb.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/nvram.h>
-
-#include <asm/io.h>
-
-#ifdef CONFIG_SPARC
-#include <asm/prom.h>
-#include <asm/pcic.h>
-#endif
-
-#include <video/iga.h>
-
-struct pci_mmap_map {
-    unsigned long voff;
-    unsigned long poff;
-    unsigned long size;
-    unsigned long prot_flag;
-    unsigned long prot_mask;
-};
-
-struct iga_par {
-       struct pci_mmap_map *mmap_map;
-       unsigned long frame_buffer_phys;
-       unsigned long io_base;
-};
-
-struct fb_info fb_info;
-
-struct fb_fix_screeninfo igafb_fix __initdata = {
-        .id            = "IGA 1682",
-       .type           = FB_TYPE_PACKED_PIXELS,
-       .mmio_len       = 1000
-};
-
-struct fb_var_screeninfo default_var = {
-       /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
-       .xres           = 640,
-       .yres           = 480,
-       .xres_virtual   = 640,
-       .yres_virtual   = 480,
-       .bits_per_pixel = 8,
-       .red            = {0, 8, 0 },
-       .green          = {0, 8, 0 },
-       .blue           = {0, 8, 0 },
-       .height         = -1,
-       .width          = -1,
-       .accel_flags    = FB_ACCEL_NONE,
-       .pixclock       = 39722,
-       .left_margin    = 48,
-       .right_margin   = 16,
-       .upper_margin   = 33,
-       .lower_margin   = 10,
-       .hsync_len      = 96,
-       .vsync_len      = 2,
-       .vmode          = FB_VMODE_NONINTERLACED
-};
-
-#ifdef CONFIG_SPARC
-struct fb_var_screeninfo default_var_1024x768 __initdata = {
-       /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */
-       .xres           = 1024,
-       .yres           = 768,
-       .xres_virtual   = 1024,
-       .yres_virtual   = 768,
-       .bits_per_pixel = 8,
-       .red            = {0, 8, 0 },
-       .green          = {0, 8, 0 },
-       .blue           = {0, 8, 0 },
-       .height         = -1,
-       .width          = -1,
-       .accel_flags    = FB_ACCEL_NONE,
-       .pixclock       = 12699,
-       .left_margin    = 176,
-       .right_margin   = 16,
-       .upper_margin   = 28,
-       .lower_margin   = 1,
-       .hsync_len      = 96,
-       .vsync_len      = 3,
-       .vmode          = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-};
-
-struct fb_var_screeninfo default_var_1152x900 __initdata = {
-       /* 1152x900, 76 Hz, Non-Interlaced (110.0 MHz dotclock) */
-       .xres           = 1152,
-       .yres           = 900,
-       .xres_virtual   = 1152,
-       .yres_virtual   = 900,
-       .bits_per_pixel = 8,
-       .red            = { 0, 8, 0 },
-       .green          = { 0, 8, 0 },
-       .blue           = { 0, 8, 0 },
-       .height         = -1,
-       .width          = -1,
-       .accel_flags    = FB_ACCEL_NONE,
-       .pixclock       = 9091,
-       .left_margin    = 234,
-       .right_margin   = 24,
-       .upper_margin   = 34,
-       .lower_margin   = 3,
-       .hsync_len      = 100,
-       .vsync_len      = 3,
-       .vmode          = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-};
-
-struct fb_var_screeninfo default_var_1280x1024 __initdata = {
-       /* 1280x1024, 75 Hz, Non-Interlaced (135.00 MHz dotclock) */
-       .xres           = 1280,
-       .yres           = 1024,
-       .xres_virtual   = 1280,
-       .yres_virtual   = 1024,
-       .bits_per_pixel = 8,
-       .red            = {0, 8, 0 }, 
-       .green          = {0, 8, 0 },
-       .blue           = {0, 8, 0 },
-       .height         = -1,
-       .width          = -1,
-       .accel_flags    = 0,
-       .pixclock       = 7408,
-       .left_margin    = 248,
-       .right_margin   = 16,
-       .upper_margin   = 38,
-       .lower_margin   = 1,
-       .hsync_len      = 144,
-       .vsync_len      = 3,
-       .vmode          = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
-};
-
-/*
- *   Memory-mapped I/O functions for Sparc PCI
- *
- * On sparc we happen to access I/O with memory mapped functions too.
- */ 
-#define pci_inb(par, reg)        readb(par->io_base+(reg))
-#define pci_outb(par, val, reg)  writeb(val, par->io_base+(reg))
-
-static inline unsigned int iga_inb(struct iga_par *par, unsigned int reg,
-                                  unsigned int idx)
-{
-        pci_outb(par, idx, reg);
-        return pci_inb(par, reg + 1);
-}
-
-static inline void iga_outb(struct iga_par *par, unsigned char val,
-                           unsigned int reg, unsigned int idx )
-{
-        pci_outb(par, idx, reg);
-        pci_outb(par, val, reg+1);
-}
-
-#endif /* CONFIG_SPARC */
-
-/*
- *  Very important functionality for the JavaEngine1 computer:
- *  make screen border black (usign special IGA registers) 
- */
-static void iga_blank_border(struct iga_par *par)
-{
-        int i;
-#if 0
-       /*
-        * PROM does this for us, so keep this code as a reminder
-        * about required read from 0x3DA and writing of 0x20 in the end.
-        */
-       (void) pci_inb(par, 0x3DA);             /* required for every access */
-       pci_outb(par, IGA_IDX_VGA_OVERSCAN, IGA_ATTR_CTL);
-       (void) pci_inb(par, IGA_ATTR_CTL+1);
-       pci_outb(par, 0x38, IGA_ATTR_CTL);
-       pci_outb(par, 0x20, IGA_ATTR_CTL);      /* re-enable visual */
-#endif
-       /*
-        * This does not work as it was designed because the overscan
-        * color is looked up in the palette. Therefore, under X11
-        * overscan changes color.
-        */
-       for (i=0; i < 3; i++)
-               iga_outb(par, 0, IGA_EXT_CNTRL, IGA_IDX_OVERSCAN_COLOR + i);
-}
-
-#ifdef CONFIG_SPARC
-static int igafb_mmap(struct fb_info *info,
-                     struct vm_area_struct *vma)
-{
-       struct iga_par *par = (struct iga_par *)info->par;
-       unsigned int size, page, map_size = 0;
-       unsigned long map_offset = 0;
-       int i;
-
-       if (!par->mmap_map)
-               return -ENXIO;
-
-       size = vma->vm_end - vma->vm_start;
-
-       /* Each page, see which map applies */
-       for (page = 0; page < size; ) {
-               map_size = 0;
-               for (i = 0; par->mmap_map[i].size; i++) {
-                       unsigned long start = par->mmap_map[i].voff;
-                       unsigned long end = start + par->mmap_map[i].size;
-                       unsigned long offset = (vma->vm_pgoff << PAGE_SHIFT) + page;
-
-                       if (start > offset)
-                               continue;
-                       if (offset >= end)
-                               continue;
-
-                       map_size = par->mmap_map[i].size - (offset - start);
-                       map_offset = par->mmap_map[i].poff + (offset - start);
-                       break;
-               }
-               if (!map_size) {
-                       page += PAGE_SIZE;
-                       continue;
-               }
-               if (page + map_size > size)
-                       map_size = size - page;
-
-               pgprot_val(vma->vm_page_prot) &= ~(par->mmap_map[i].prot_mask);
-               pgprot_val(vma->vm_page_prot) |= par->mmap_map[i].prot_flag;
-
-               if (remap_pfn_range(vma, vma->vm_start + page,
-                       map_offset >> PAGE_SHIFT, map_size, vma->vm_page_prot))
-                       return -EAGAIN;
-
-               page += map_size;
-       }
-
-       if (!map_size)
-               return -EINVAL;
-
-       vma->vm_flags |= VM_IO;
-       return 0;
-}
-#endif /* CONFIG_SPARC */
-
-static int igafb_setcolreg(unsigned regno, unsigned red, unsigned green,
-                           unsigned blue, unsigned transp,
-                           struct fb_info *info)
-{
-        /*
-         *  Set a single color register. The values supplied are
-         *  already rounded down to the hardware's capabilities
-         *  (according to the entries in the `var' structure). Return
-         *  != 0 for invalid regno.
-         */
-       struct iga_par *par = (struct iga_par *)info->par;
-
-        if (regno >= info->cmap.len)
-                return 1;
-
-       pci_outb(par, regno, DAC_W_INDEX);
-       pci_outb(par, red,   DAC_DATA);
-       pci_outb(par, green, DAC_DATA);
-       pci_outb(par, blue,  DAC_DATA);
-
-       if (regno < 16) {
-               switch (info->var.bits_per_pixel) {
-               case 16:
-                       ((u16*)(info->pseudo_palette))[regno] = 
-                               (regno << 10) | (regno << 5) | regno;
-                       break;
-               case 24:
-                       ((u32*)(info->pseudo_palette))[regno] = 
-                               (regno << 16) | (regno << 8) | regno;
-               break;
-               case 32:
-                       { int i;
-                       i = (regno << 8) | regno;
-                       ((u32*)(info->pseudo_palette))[regno] = (i << 16) | i;
-                       }
-                       break;
-               }
-       }
-       return 0;
-}
-
-/*
- * Framebuffer option structure
- */
-static struct fb_ops igafb_ops = {
-       .owner          = THIS_MODULE,
-       .fb_setcolreg   = igafb_setcolreg,
-       .fb_fillrect    = cfb_fillrect,
-       .fb_copyarea    = cfb_copyarea,
-       .fb_imageblit   = cfb_imageblit,
-#ifdef CONFIG_SPARC
-       .fb_mmap        = igafb_mmap,
-#endif
-};
-
-static int __init iga_init(struct fb_info *info, struct iga_par *par)
-{
-        char vramsz = iga_inb(par, IGA_EXT_CNTRL, IGA_IDX_EXT_BUS_CNTL) 
-                                                        & MEM_SIZE_ALIAS;
-       int video_cmap_len;
-
-        switch (vramsz) {
-        case MEM_SIZE_1M:
-                info->fix.smem_len = 0x100000;
-                break;
-        case MEM_SIZE_2M:
-                info->fix.smem_len = 0x200000;
-                break;
-        case MEM_SIZE_4M:
-        case MEM_SIZE_RESERVED:
-                info->fix.smem_len = 0x400000;
-                break;
-        }
-
-        if (info->var.bits_per_pixel > 8) 
-                video_cmap_len = 16;
-        else 
-                video_cmap_len = 256;
-
-       info->fbops = &igafb_ops;
-       info->flags = FBINFO_DEFAULT;
-
-       fb_alloc_cmap(&info->cmap, video_cmap_len, 0);
-
-       if (register_framebuffer(info) < 0)
-               return 0;
-
-       fb_info(info, "%s frame buffer device at 0x%08lx [%dMB VRAM]\n",
-               info->fix.id, par->frame_buffer_phys, info->fix.smem_len >> 20);
-
-       iga_blank_border(par); 
-       return 1;
-}
-
-static int __init igafb_init(void)
-{
-        struct fb_info *info;
-        struct pci_dev *pdev;
-        struct iga_par *par;
-       unsigned long addr;
-       int size, iga2000 = 0;
-
-       if (fb_get_options("igafb", NULL))
-               return -ENODEV;
-
-        pdev = pci_get_device(PCI_VENDOR_ID_INTERG,
-                               PCI_DEVICE_ID_INTERG_1682, 0);
-       if (pdev == NULL) {
-               /*
-                * XXX We tried to use cyber2000fb.c for IGS 2000.
-                * But it does not initialize the chip in JavaStation-E, alas.
-                */
-               pdev = pci_get_device(PCI_VENDOR_ID_INTERG, 0x2000, 0);
-               if(pdev == NULL) {
-                       return -ENXIO;
-               }
-               iga2000 = 1;
-       }
-       /* We leak a reference here but as it cannot be unloaded this is
-          fine. If you write unload code remember to free it in unload */
-       
-       size = sizeof(struct iga_par) + sizeof(u32)*16;
-
-       info = framebuffer_alloc(size, &pdev->dev);
-        if (!info) {
-                printk("igafb_init: can't alloc fb_info\n");
-                pci_dev_put(pdev);
-                return -ENOMEM;
-        }
-
-       par = info->par;
-
-       if ((addr = pdev->resource[0].start) == 0) {
-                printk("igafb_init: no memory start\n");
-               kfree(info);
-               pci_dev_put(pdev);
-               return -ENXIO;
-       }
-
-       if ((info->screen_base = ioremap(addr, 1024*1024*2)) == 0) {
-                printk("igafb_init: can't remap %lx[2M]\n", addr);
-               kfree(info);
-               pci_dev_put(pdev);
-               return -ENXIO;
-       }
-
-       par->frame_buffer_phys = addr & PCI_BASE_ADDRESS_MEM_MASK;
-
-#ifdef CONFIG_SPARC
-       /*
-        * The following is sparc specific and this is why:
-        *
-        * IGS2000 has its I/O memory mapped and we want
-        * to generate memory cycles on PCI, e.g. do ioremap(),
-        * then readb/writeb() as in Documentation/io-mapping.txt.
-        *
-        * IGS1682 is more traditional, it responds to PCI I/O
-        * cycles, so we want to access it with inb()/outb().
-        *
-        * On sparc, PCIC converts CPU memory access within
-        * phys window 0x3000xxxx into PCI I/O cycles. Therefore
-        * we may use readb/writeb to access them with IGS1682.
-        *
-        * We do not take io_base_phys from resource[n].start
-        * on IGS1682 because that chip is BROKEN. It does not
-        * have a base register for I/O. We just "know" what its
-        * I/O addresses are.
-        */
-       if (iga2000) {
-               igafb_fix.mmio_start = par->frame_buffer_phys | 0x00800000;
-       } else {
-               igafb_fix.mmio_start = 0x30000000;      /* XXX */
-       }
-       if ((par->io_base = (int) ioremap(igafb_fix.mmio_start, igafb_fix.smem_len)) == 0) {
-                printk("igafb_init: can't remap %lx[4K]\n", igafb_fix.mmio_start);
-               iounmap((void *)info->screen_base);
-               kfree(info);
-               pci_dev_put(pdev);
-               return -ENXIO;
-       }
-
-       /*
-        * Figure mmap addresses from PCI config space.
-        * We need two regions: for video memory and for I/O ports.
-        * Later one can add region for video coprocessor registers.
-        * However, mmap routine loops until size != 0, so we put
-        * one additional region with size == 0. 
-        */
-
-       par->mmap_map = kzalloc(4 * sizeof(*par->mmap_map), GFP_ATOMIC);
-       if (!par->mmap_map) {
-               printk("igafb_init: can't alloc mmap_map\n");
-               iounmap((void *)par->io_base);
-               iounmap(info->screen_base);
-               kfree(info);
-               pci_dev_put(pdev);
-               return -ENOMEM;
-       }
-
-       /*
-        * Set default vmode and cmode from PROM properties.
-        */
-       {
-               struct device_node *dp = pci_device_to_OF_node(pdev);
-                int node = dp->node;
-                int width = prom_getintdefault(node, "width", 1024);
-                int height = prom_getintdefault(node, "height", 768);
-                int depth = prom_getintdefault(node, "depth", 8);
-                switch (width) {
-                    case 1024:
-                        if (height == 768)
-                            default_var = default_var_1024x768;
-                        break;
-                    case 1152:
-                        if (height == 900)
-                            default_var = default_var_1152x900;
-                        break;
-                    case 1280:
-                        if (height == 1024)
-                            default_var = default_var_1280x1024;
-                        break;
-                    default:
-                        break;
-                }
-
-                switch (depth) {
-                    case 8:
-                        default_var.bits_per_pixel = 8;
-                        break;
-                    case 16:
-                        default_var.bits_per_pixel = 16;
-                        break;
-                    case 24:
-                        default_var.bits_per_pixel = 24;
-                        break;
-                    case 32:
-                        default_var.bits_per_pixel = 32;
-                        break;
-                    default:
-                        break;
-                }
-            }
-
-#endif
-       igafb_fix.smem_start = (unsigned long) info->screen_base;
-       igafb_fix.line_length = default_var.xres*(default_var.bits_per_pixel/8);
-       igafb_fix.visual = default_var.bits_per_pixel <= 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
-
-       info->var = default_var;
-       info->fix = igafb_fix;
-       info->pseudo_palette = (void *)(par + 1);
-
-       if (!iga_init(info, par)) {
-               iounmap((void *)par->io_base);
-               iounmap(info->screen_base);
-               kfree(par->mmap_map);
-               kfree(info);
-               return -ENODEV;
-        }
-
-#ifdef CONFIG_SPARC
-           /*
-            * Add /dev/fb mmap values.
-            */
-           
-           /* First region is for video memory */
-           par->mmap_map[0].voff = 0x0;  
-           par->mmap_map[0].poff = par->frame_buffer_phys & PAGE_MASK;
-           par->mmap_map[0].size = info->fix.smem_len & PAGE_MASK;
-           par->mmap_map[0].prot_mask = SRMMU_CACHE;
-           par->mmap_map[0].prot_flag = SRMMU_WRITE;
-
-           /* Second region is for I/O ports */
-           par->mmap_map[1].voff = par->frame_buffer_phys & PAGE_MASK;
-           par->mmap_map[1].poff = info->fix.smem_start & PAGE_MASK;
-           par->mmap_map[1].size = PAGE_SIZE * 2; /* X wants 2 pages */
-           par->mmap_map[1].prot_mask = SRMMU_CACHE;
-           par->mmap_map[1].prot_flag = SRMMU_WRITE;
-#endif /* CONFIG_SPARC */
-
-       return 0;
-}
-
-static int __init igafb_setup(char *options)
-{
-    char *this_opt;
-
-    if (!options || !*options)
-        return 0;
-
-    while ((this_opt = strsep(&options, ",")) != NULL) {
-    }
-    return 0;
-}
-
-module_init(igafb_init);
-MODULE_LICENSE("GPL");
-static struct pci_device_id igafb_pci_tbl[] = {
-       { PCI_VENDOR_ID_INTERG, PCI_DEVICE_ID_INTERG_1682,
-         PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
-       { }
-};
-
-MODULE_DEVICE_TABLE(pci, igafb_pci_tbl);
index d31ed4e2c46f1020ab84e8899337c3bd82fabd18..83fec573cceb78f6fa9b5a13e1787bdd2fcb5e41 100644 (file)
@@ -937,15 +937,11 @@ static int calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2,
 {
        u32 m1, m2, n, p1, p2, n1, testm;
        u32 f_vco, p, p_best = 0, m, f_out = 0;
-       u32 err_max, err_target, err_best = 10000000;
-       u32 n_best = 0, m_best = 0, f_best, f_err;
+       u32 err_best = 10000000;
+       u32 n_best = 0, m_best = 0, f_err;
        u32 p_min, p_max, p_inc, div_max;
        struct pll_min_max *pll = &plls[index];
 
-       /* Accept 0.5% difference, but aim for 0.1% */
-       err_max = 5 * clock / 1000;
-       err_target = clock / 1000;
-
        DBG_MSG("Clock is %d\n", clock);
 
        div_max = pll->max_vco / clock;
@@ -992,7 +988,6 @@ static int calc_pll_params(int index, int clock, u32 *retm1, u32 *retm2,
                                        m_best = testm;
                                        n_best = n;
                                        p_best = p;
-                                       f_best = f_out;
                                        err_best = f_err;
                                }
                        }
index b9b284d79631d35b11f65536d516314b6157b956..838869c6490c2e4dcf87a4b35d5103d62976657b 100644 (file)
@@ -2056,7 +2056,7 @@ static int matroxfb_probe(struct pci_dev* pdev, const struct pci_device_id* dumm
 
        minfo = kzalloc(sizeof(*minfo), GFP_KERNEL);
        if (!minfo)
-               return -1;
+               return -ENOMEM;
 
        minfo->pcidev = pdev;
        minfo->dead = 0;
index 7846f0e8bbbb55f9ad691b733b6d2333452a672e..79b1dc7f042b220277a997169efdee2757784554 100644 (file)
 #define STMLCDIF_24BIT 3 /** pixel data bus to the display is of 24 bit width */
 
 #define MXSFB_SYNC_DATA_ENABLE_HIGH_ACT        (1 << 6)
-#define MXSFB_SYNC_DOTCLK_FALLING_ACT  (1 << 7) /* negtive edge sampling */
+#define MXSFB_SYNC_DOTCLK_FALLING_ACT  (1 << 7) /* negative edge sampling */
 
 enum mxsfb_devtype {
        MXSFB_V3,
@@ -788,7 +788,16 @@ static int mxsfb_init_fbinfo_dt(struct mxsfb_info *host,
 
        if (vm.flags & DISPLAY_FLAGS_DE_HIGH)
                host->sync |= MXSFB_SYNC_DATA_ENABLE_HIGH_ACT;
-       if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+
+       /*
+        * The PIXDATA flags of the display_flags enum are controller
+        * centric, e.g. NEGEDGE means drive data on negative edge.
+        * However, the drivers flag is display centric: Sample the
+        * data on negative (falling) edge. Therefore, check for the
+        * POSEDGE flag:
+        * drive on positive edge => sample on negative edge
+        */
+       if (vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
                host->sync |= MXSFB_SYNC_DOTCLK_FALLING_ACT;
 
 put_display_node:
index a4ee65b8f9187f8788aec8ae9a92e0169319c38c..6199d48061938c536e7f9803e4286d548e160f15 100644 (file)
@@ -474,7 +474,7 @@ static void auto_update_complete(void *data)
                          jiffies + HWA742_AUTO_UPDATE_TIME);
 }
 
-static void hwa742_update_window_auto(unsigned long arg)
+static void hwa742_update_window_auto(struct timer_list *unused)
 {
        LIST_HEAD(req_list);
        struct hwa742_request *last;
@@ -1002,9 +1002,7 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
        hwa742.auto_update_window.height = fbdev->panel->y_res;
        hwa742.auto_update_window.format = 0;
 
-       init_timer(&hwa742.auto_update_timer);
-       hwa742.auto_update_timer.function = hwa742_update_window_auto;
-       hwa742.auto_update_timer.data = 0;
+       timer_setup(&hwa742.auto_update_timer, hwa742_update_window_auto, 0);
 
        hwa742.prev_color_mode = -1;
        hwa742.prev_flags = 0;
index 30d49f3800b334b0a3f6b8ed29ea437d30cb632e..8e1d60d48dbb0edb507093581bd45833bd563d0c 100644 (file)
@@ -3988,7 +3988,7 @@ static void dsi_update_screen_dispc(struct platform_device *dsidev)
 }
 
 #ifdef DSI_CATCH_MISSING_TE
-static void dsi_te_timeout(unsigned long arg)
+static void dsi_te_timeout(struct timer_list *unused)
 {
        DSSERR("TE not received for 250ms!\n");
 }
@@ -5298,9 +5298,7 @@ static int dsi_bind(struct device *dev, struct device *master, void *data)
                             dsi_framedone_timeout_work_callback);
 
 #ifdef DSI_CATCH_MISSING_TE
-       init_timer(&dsi->te_timer);
-       dsi->te_timer.function = dsi_te_timeout;
-       dsi->te_timer.data = 0;
+       timer_setup(&dsi->te_timer, dsi_te_timeout, 0);
 #endif
 
        res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "proto");
index 1d7c012f09dbb70cb0c5d1aed909272c94c4b4b3..e08e5664e330f84adcb99afbfda605556611fe64 100644 (file)
@@ -1477,7 +1477,7 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,
 static int omapfb_parse_vram_param(const char *param, int max_entries,
                unsigned long *sizes, unsigned long *paddrs)
 {
-       int fbnum;
+       unsigned int fbnum;
        unsigned long size;
        unsigned long paddr = 0;
        char *p, *start;
index 933619da1a94b94e97c5c5f81b082b2eab7385df..55fbb432c05352271380f25fd5da5c5502a7af51 100644 (file)
@@ -512,28 +512,26 @@ pxa3xx_gcu_mmap(struct file *file, struct vm_area_struct *vma)
 
 #ifdef PXA3XX_GCU_DEBUG_TIMER
 static struct timer_list pxa3xx_gcu_debug_timer;
+static struct pxa3xx_gcu_priv *debug_timer_priv;
 
-static void pxa3xx_gcu_debug_timedout(unsigned long ptr)
+static void pxa3xx_gcu_debug_timedout(struct timer_list *unused)
 {
-       struct pxa3xx_gcu_priv *priv = (struct pxa3xx_gcu_priv *) ptr;
+       struct pxa3xx_gcu_priv *priv = debug_timer_priv;
 
        QERROR("Timer DUMP");
 
-       /* init the timer structure */
-       init_timer(&pxa3xx_gcu_debug_timer);
-       pxa3xx_gcu_debug_timer.function = pxa3xx_gcu_debug_timedout;
-       pxa3xx_gcu_debug_timer.data = ptr;
-       pxa3xx_gcu_debug_timer.expires = jiffies + 5*HZ; /* one second */
-
-       add_timer(&pxa3xx_gcu_debug_timer);
+       mod_timer(&pxa3xx_gcu_debug_timer, jiffies + 5 * HZ);
 }
 
-static void pxa3xx_gcu_init_debug_timer(void)
+static void pxa3xx_gcu_init_debug_timer(struct pxa3xx_gcu_priv *priv)
 {
-       pxa3xx_gcu_debug_timedout((unsigned long) &pxa3xx_gcu_debug_timer);
+       /* init the timer structure */
+       debug_timer_priv = priv;
+       timer_setup(&pxa3xx_gcu_debug_timer, pxa3xx_gcu_debug_timedout, 0);
+       pxa3xx_gcu_debug_timedout(NULL);
 }
 #else
-static inline void pxa3xx_gcu_init_debug_timer(void) {}
+static inline void pxa3xx_gcu_init_debug_timer(struct pxa3xx_gcu_priv *priv) {}
 #endif
 
 static int
@@ -670,7 +668,7 @@ static int pxa3xx_gcu_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, priv);
        priv->resource_mem = r;
        pxa3xx_gcu_reset(priv);
-       pxa3xx_gcu_init_debug_timer();
+       pxa3xx_gcu_init_debug_timer(priv);
 
        dev_info(dev, "registered @0x%p, DMA 0x%p (%d bytes), IRQ %d\n",
                        (void *) r->start, (void *) priv->shared_phys,
index fc2aaa5aca2347e705c6eb1623ec188b5262e498..15ae50063296ed823836066b7e3a4a26a2021d9b 100644 (file)
@@ -323,13 +323,11 @@ sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
                 * according to the RGB bitfield information.
                 */
                if (regno < 16) {
-                       u32 *pal = fbi->fb.pseudo_palette;
-
                        val  = chan_to_field(red, &fbi->fb.var.red);
                        val |= chan_to_field(green, &fbi->fb.var.green);
                        val |= chan_to_field(blue, &fbi->fb.var.blue);
 
-                       pal[regno] = val;
+                       fbi->pseudo_palette[regno] = val;
                        ret = 0;
                }
                break;
@@ -1132,12 +1130,10 @@ static struct sa1100fb_info *sa1100fb_init_fbinfo(struct device *dev)
        struct sa1100fb_info *fbi;
        unsigned i;
 
-       fbi = kmalloc(sizeof(struct sa1100fb_info) + sizeof(u32) * 16,
-                     GFP_KERNEL);
+       fbi = devm_kzalloc(dev, sizeof(struct sa1100fb_info), GFP_KERNEL);
        if (!fbi)
                return NULL;
 
-       memset(fbi, 0, sizeof(struct sa1100fb_info));
        fbi->dev = dev;
 
        strcpy(fbi->fb.fix.id, SA1100_NAME);
@@ -1159,7 +1155,7 @@ static struct sa1100fb_info *sa1100fb_init_fbinfo(struct device *dev)
        fbi->fb.fbops           = &sa1100fb_ops;
        fbi->fb.flags           = FBINFO_DEFAULT;
        fbi->fb.monspecs        = monspecs;
-       fbi->fb.pseudo_palette  = (fbi + 1);
+       fbi->fb.pseudo_palette  = fbi->pseudo_palette;
 
        fbi->rgb[RGB_4]         = &rgb_4;
        fbi->rgb[RGB_8]         = &rgb_8;
@@ -1218,48 +1214,42 @@ static int sa1100fb_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        irq = platform_get_irq(pdev, 0);
-       if (irq < 0 || !res)
+       if (irq < 0)
                return -EINVAL;
 
-       if (!request_mem_region(res->start, resource_size(res), "LCD"))
-               return -EBUSY;
-
        fbi = sa1100fb_init_fbinfo(&pdev->dev);
-       ret = -ENOMEM;
        if (!fbi)
-               goto failed;
-
-       fbi->clk = clk_get(&pdev->dev, NULL);
-       if (IS_ERR(fbi->clk)) {
-               ret = PTR_ERR(fbi->clk);
-               fbi->clk = NULL;
-               goto failed;
-       }
+               return -ENOMEM;
 
-       fbi->base = ioremap(res->start, resource_size(res));
-       if (!fbi->base)
-               goto failed;
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       fbi->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(fbi->base))
+               return PTR_ERR(fbi->base);
 
-       /* Initialize video memory */
-       ret = sa1100fb_map_video_memory(fbi);
-       if (ret)
-               goto failed;
+       fbi->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(fbi->clk))
+               return PTR_ERR(fbi->clk);
 
-       ret = request_irq(irq, sa1100fb_handle_irq, 0, "LCD", fbi);
+       ret = devm_request_irq(&pdev->dev, irq, sa1100fb_handle_irq, 0,
+                              "LCD", fbi);
        if (ret) {
                dev_err(&pdev->dev, "request_irq failed: %d\n", ret);
-               goto failed;
+               return ret;
        }
 
        if (machine_is_shannon()) {
-               ret = gpio_request_one(SHANNON_GPIO_DISP_EN,
+               ret = devm_gpio_request_one(&pdev->dev, SHANNON_GPIO_DISP_EN,
                        GPIOF_OUT_INIT_LOW, "display enable");
                if (ret)
-                       goto err_free_irq;
+                       return ret;
        }
 
+       /* Initialize video memory */
+       ret = sa1100fb_map_video_memory(fbi);
+       if (ret)
+               return ret;
+
        /*
         * This makes sure that our colour bitfield
         * descriptors are correctly initialised.
@@ -1269,8 +1259,11 @@ static int sa1100fb_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, fbi);
 
        ret = register_framebuffer(&fbi->fb);
-       if (ret < 0)
-               goto err_reg_fb;
+       if (ret < 0) {
+               dma_free_wc(fbi->dev, fbi->map_size, fbi->map_cpu,
+                           fbi->map_dma);
+               return ret;
+       }
 
 #ifdef CONFIG_CPU_FREQ
        fbi->freq_transition.notifier_call = sa1100fb_freq_transition;
@@ -1281,20 +1274,6 @@ static int sa1100fb_probe(struct platform_device *pdev)
 
        /* This driver cannot be unloaded at the moment */
        return 0;
-
- err_reg_fb:
-       if (machine_is_shannon())
-               gpio_free(SHANNON_GPIO_DISP_EN);
- err_free_irq:
-       free_irq(irq, fbi);
- failed:
-       if (fbi)
-               iounmap(fbi->base);
-       if (fbi->clk)
-               clk_put(fbi->clk);
-       kfree(fbi);
-       release_mem_region(res->start, resource_size(res));
-       return ret;
 }
 
 static struct platform_driver sa1100fb_driver = {
index 0139d13377a5efaf5e6667c4db7ef8ce99531257..7a1a9ca33cec55d81d579c31e7b2a3320491a168 100644 (file)
@@ -69,6 +69,8 @@ struct sa1100fb_info {
 
        const struct sa1100fb_mach_info *inf;
        struct clk *clk;
+
+       u32 pseudo_palette[16];
 };
 
 #define TO_INF(ptr,member)     container_of(ptr,struct sa1100fb_info,member)
index 1ec9c3e0e1d85092f4e06b3a2db04d9836e36a1f..02ee752d5000567148cf24a174471b559cfc80b1 100644 (file)
@@ -6486,7 +6486,7 @@ SiS_SetTVSpecial(struct SiS_Private *SiS_Pr, unsigned short ModeNo)
 
   if(!(SiS_Pr->SiS_TVMode & TVSetPAL)) {
      if(SiS_Pr->SiS_TVMode & TVSetNTSC1024) {
-        const unsigned char specialtv[] = {
+        static const unsigned char specialtv[] = {
                0xa7,0x07,0xf2,0x6e,0x17,0x8b,0x73,0x53,
                0x13,0x40,0x34,0xf4,0x63,0xbb,0xcc,0x7a,
                0x58,0xe4,0x73,0xda,0x13
index e92303823a4b083987090920011c79bb7b45c001..ecdd054d89510d0d68281c37ac7eb9caa9bb2c8e 100644 (file)
@@ -1702,6 +1702,7 @@ static int        sisfb_ioctl(struct fb_info *info, unsigned int cmd,
                if(ivideo->warncount++ < 10)
                        printk(KERN_INFO
                                "sisfb: Deprecated ioctl call received - update your application!\n");
+               /* fall through */
           case SISFB_GET_INFO:  /* For communication with X driver */
                ivideo->sisfb_infoblock.sisfb_id         = SISFB_ID;
                ivideo->sisfb_infoblock.sisfb_version    = VER_MAJOR;
@@ -1755,6 +1756,7 @@ static int        sisfb_ioctl(struct fb_info *info, unsigned int cmd,
                if(ivideo->warncount++ < 10)
                        printk(KERN_INFO
                                "sisfb: Deprecated ioctl call received - update your application!\n");
+               /* fall through */
           case SISFB_GET_VBRSTATUS:
                if(sisfb_CheckVBRetrace(ivideo))
                        return put_user((u32)1, argp);
@@ -1765,6 +1767,7 @@ static int        sisfb_ioctl(struct fb_info *info, unsigned int cmd,
                if(ivideo->warncount++ < 10)
                        printk(KERN_INFO
                                "sisfb: Deprecated ioctl call received - update your application!\n");
+               /* fall through */
           case SISFB_GET_AUTOMAXIMIZE:
                if(ivideo->sisfb_max)
                        return put_user((u32)1, argp);
@@ -1775,6 +1778,7 @@ static int        sisfb_ioctl(struct fb_info *info, unsigned int cmd,
                if(ivideo->warncount++ < 10)
                        printk(KERN_INFO
                                "sisfb: Deprecated ioctl call received - update your application!\n");
+               /* fall through */
           case SISFB_SET_AUTOMAXIMIZE:
                if(get_user(gpu32, argp))
                        return -EFAULT;
index 076dd2711630e1f78c3bf19572915b0b2b9831ee..6f0a19501c6a8d959e1f4a994e705934808d4c77 100644 (file)
@@ -1008,6 +1008,7 @@ static int sm501fb_blank_crt(int blank_mode, struct fb_info *info)
        case FB_BLANK_POWERDOWN:
                ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE;
                sm501_misc_control(fbi->dev->parent, SM501_MISC_DAC_POWER, 0);
+               /* fall through */
 
        case FB_BLANK_NORMAL:
                ctrl |= SM501_DC_CRT_CONTROL_BLANK;
@@ -1889,6 +1890,9 @@ static void sm501_free_init_fb(struct sm501fb_info *info,
 {
        struct fb_info *fbi = info->fb[head];
 
+       if (!fbi)
+               return;
+
        fb_dealloc_cmap(&fbi->cmap);
 }
 
@@ -2076,8 +2080,10 @@ static int sm501fb_remove(struct platform_device *pdev)
        sm501_free_init_fb(info, HEAD_CRT);
        sm501_free_init_fb(info, HEAD_PANEL);
 
-       unregister_framebuffer(fbinfo_crt);
-       unregister_framebuffer(fbinfo_pnl);
+       if (fbinfo_crt)
+               unregister_framebuffer(fbinfo_crt);
+       if (fbinfo_pnl)
+               unregister_framebuffer(fbinfo_pnl);
 
        sm501fb_stop(info);
        kfree(info);
@@ -2094,8 +2100,12 @@ static int sm501fb_suspend_fb(struct sm501fb_info *info,
                              enum sm501_controller head)
 {
        struct fb_info *fbi = info->fb[head];
-       struct sm501fb_par *par = fbi->par;
+       struct sm501fb_par *par;
+
+       if (!fbi)
+               return 0;
 
+       par = fbi->par;
        if (par->screen.size == 0)
                return 0;
 
@@ -2141,8 +2151,12 @@ static void sm501fb_resume_fb(struct sm501fb_info *info,
                              enum sm501_controller head)
 {
        struct fb_info *fbi = info->fb[head];
-       struct sm501fb_par *par = fbi->par;
+       struct sm501fb_par *par;
+
+       if (!fbi)
+               return;
 
+       par = fbi->par;
        if (par->screen.size == 0)
                return;
 
index ef08a104fb42c6dafe3c88d32ca18b7430ede759..d44f14242016e07682134c4f2bfa4832e6286462 100644 (file)
@@ -769,11 +769,11 @@ static int dlfb_get_edid(struct dlfb_data *dev, char *edid, int len)
 
        for (i = 0; i < len; i++) {
                ret = usb_control_msg(dev->udev,
-                                   usb_rcvctrlpipe(dev->udev, 0), (0x02),
-                                   (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2,
-                                   HZ);
-               if (ret < 1) {
-                       pr_err("Read EDID byte %d failed err %x\n", i, ret);
+                                     usb_rcvctrlpipe(dev->udev, 0), 0x02,
+                                     (0x80 | (0x02 << 5)), i << 8, 0xA1,
+                                     rbuf, 2, USB_CTRL_GET_TIMEOUT);
+               if (ret < 2) {
+                       pr_err("Read EDID byte %d failed: %d\n", i, ret);
                        i--;
                        break;
                }
index ff5d32cf9578f77aa28bebd2634fd61cb3d27064..a14b2c974c9eacea27943fe7a995e21ce4e1bb3a 100644 (file)
@@ -1160,7 +1160,7 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap,
        struct ceph_inode_info *ci = cap->ci;
        struct inode *inode = &ci->vfs_inode;
        struct cap_msg_args arg;
-       int held, revoking, dropping;
+       int held, revoking;
        int wake = 0;
        int delayed = 0;
        int ret;
@@ -1168,7 +1168,6 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap,
        held = cap->issued | cap->implemented;
        revoking = cap->implemented & ~cap->issued;
        retain &= ~revoking;
-       dropping = cap->issued & ~retain;
 
        dout("__send_cap %p cap %p session %p %s -> %s (revoking %s)\n",
             inode, cap, cap->session,
@@ -1712,7 +1711,7 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags,
 
        /* if we are unmounting, flush any unused caps immediately. */
        if (mdsc->stopping)
-               is_delayed = 1;
+               is_delayed = true;
 
        spin_lock(&ci->i_ceph_lock);
 
@@ -3189,8 +3188,8 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid,
        int dirty = le32_to_cpu(m->dirty);
        int cleaned = 0;
        bool drop = false;
-       bool wake_ci = 0;
-       bool wake_mdsc = 0;
+       bool wake_ci = false;
+       bool wake_mdsc = false;
 
        list_for_each_entry_safe(cf, tmp_cf, &ci->i_cap_flush_list, i_list) {
                if (cf->tid == flush_tid)
index f2550a076edc4e65da6e36354ef9ba2ba517d184..ab81652198c48e1e90a5545cb06089a6fa30da1a 100644 (file)
@@ -493,6 +493,7 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
        ci->i_wb_ref = 0;
        ci->i_wrbuffer_ref = 0;
        ci->i_wrbuffer_ref_head = 0;
+       atomic_set(&ci->i_filelock_ref, 0);
        ci->i_shared_gen = 0;
        ci->i_rdcache_gen = 0;
        ci->i_rdcache_revoking = 0;
@@ -786,7 +787,6 @@ static int fill_inode(struct inode *inode, struct page *locked_page,
 
        /* update inode */
        ci->i_version = le64_to_cpu(info->version);
-       inode->i_version++;
        inode->i_rdev = le32_to_cpu(info->rdev);
        inode->i_blkbits = fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1;
 
@@ -1185,6 +1185,7 @@ retry_lookup:
                                    ceph_snap(d_inode(dn)) != tvino.snap)) {
                                dout(" dn %p points to wrong inode %p\n",
                                     dn, d_inode(dn));
+                               ceph_dir_clear_ordered(dir);
                                d_delete(dn);
                                dput(dn);
                                goto retry_lookup;
@@ -1322,6 +1323,7 @@ retry_lookup:
                        dout(" %p links to %p %llx.%llx, not %llx.%llx\n",
                             dn, d_inode(dn), ceph_vinop(d_inode(dn)),
                             ceph_vinop(in));
+                       ceph_dir_clear_ordered(dir);
                        d_invalidate(dn);
                        have_lease = false;
                }
@@ -1573,6 +1575,7 @@ retry_lookup:
                            ceph_snap(d_inode(dn)) != tvino.snap)) {
                        dout(" dn %p points to wrong inode %p\n",
                             dn, d_inode(dn));
+                       __ceph_dir_clear_ordered(ci);
                        d_delete(dn);
                        dput(dn);
                        goto retry_lookup;
@@ -1597,7 +1600,9 @@ retry_lookup:
                                 &req->r_caps_reservation);
                if (ret < 0) {
                        pr_err("fill_inode badness on %p\n", in);
-                       if (d_really_is_negative(dn))
+                       if (d_really_is_positive(dn))
+                               __ceph_dir_clear_ordered(ci);
+                       else
                                iput(in);
                        d_drop(dn);
                        err = ret;
index e7cce412f2cf7b5362606f2286291290a218b9c8..9e66f69ee8a5ecc9e8455465f232bf70529e59a8 100644 (file)
@@ -30,19 +30,52 @@ void __init ceph_flock_init(void)
        get_random_bytes(&lock_secret, sizeof(lock_secret));
 }
 
+static void ceph_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
+{
+       struct inode *inode = file_inode(src->fl_file);
+       atomic_inc(&ceph_inode(inode)->i_filelock_ref);
+}
+
+static void ceph_fl_release_lock(struct file_lock *fl)
+{
+       struct inode *inode = file_inode(fl->fl_file);
+       struct ceph_inode_info *ci = ceph_inode(inode);
+       if (atomic_dec_and_test(&ci->i_filelock_ref)) {
+               /* clear error when all locks are released */
+               spin_lock(&ci->i_ceph_lock);
+               ci->i_ceph_flags &= ~CEPH_I_ERROR_FILELOCK;
+               spin_unlock(&ci->i_ceph_lock);
+       }
+}
+
+static const struct file_lock_operations ceph_fl_lock_ops = {
+       .fl_copy_lock = ceph_fl_copy_lock,
+       .fl_release_private = ceph_fl_release_lock,
+};
+
 /**
  * Implement fcntl and flock locking functions.
  */
-static int ceph_lock_message(u8 lock_type, u16 operation, struct file *file,
+static int ceph_lock_message(u8 lock_type, u16 operation, struct inode *inode,
                             int cmd, u8 wait, struct file_lock *fl)
 {
-       struct inode *inode = file_inode(file);
        struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
        struct ceph_mds_request *req;
        int err;
        u64 length = 0;
        u64 owner;
 
+       if (operation == CEPH_MDS_OP_SETFILELOCK) {
+               /*
+                * increasing i_filelock_ref closes race window between
+                * handling request reply and adding file_lock struct to
+                * inode. Otherwise, auth caps may get trimmed in the
+                * window. Caller function will decrease the counter.
+                */
+               fl->fl_ops = &ceph_fl_lock_ops;
+               atomic_inc(&ceph_inode(inode)->i_filelock_ref);
+       }
+
        if (operation != CEPH_MDS_OP_SETFILELOCK || cmd == CEPH_LOCK_UNLOCK)
                wait = 0;
 
@@ -180,10 +213,12 @@ static int ceph_lock_wait_for_completion(struct ceph_mds_client *mdsc,
  */
 int ceph_lock(struct file *file, int cmd, struct file_lock *fl)
 {
-       u8 lock_cmd;
-       int err;
-       u8 wait = 0;
+       struct inode *inode = file_inode(file);
+       struct ceph_inode_info *ci = ceph_inode(inode);
+       int err = 0;
        u16 op = CEPH_MDS_OP_SETFILELOCK;
+       u8 wait = 0;
+       u8 lock_cmd;
 
        if (!(fl->fl_flags & FL_POSIX))
                return -ENOLCK;
@@ -199,6 +234,26 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl)
        else if (IS_SETLKW(cmd))
                wait = 1;
 
+       spin_lock(&ci->i_ceph_lock);
+       if (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) {
+               err = -EIO;
+       } else if (op == CEPH_MDS_OP_SETFILELOCK) {
+               /*
+                * increasing i_filelock_ref closes race window between
+                * handling request reply and adding file_lock struct to
+                * inode. Otherwise, i_auth_cap may get trimmed in the
+                * window. Caller function will decrease the counter.
+                */
+               fl->fl_ops = &ceph_fl_lock_ops;
+               atomic_inc(&ci->i_filelock_ref);
+       }
+       spin_unlock(&ci->i_ceph_lock);
+       if (err < 0) {
+               if (op == CEPH_MDS_OP_SETFILELOCK && F_UNLCK == fl->fl_type)
+                       posix_lock_file(file, fl, NULL);
+               return err;
+       }
+
        if (F_RDLCK == fl->fl_type)
                lock_cmd = CEPH_LOCK_SHARED;
        else if (F_WRLCK == fl->fl_type)
@@ -206,16 +261,16 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl)
        else
                lock_cmd = CEPH_LOCK_UNLOCK;
 
-       err = ceph_lock_message(CEPH_LOCK_FCNTL, op, file, lock_cmd, wait, fl);
+       err = ceph_lock_message(CEPH_LOCK_FCNTL, op, inode, lock_cmd, wait, fl);
        if (!err) {
-               if (op != CEPH_MDS_OP_GETFILELOCK) {
+               if (op == CEPH_MDS_OP_SETFILELOCK) {
                        dout("mds locked, locking locally");
                        err = posix_lock_file(file, fl, NULL);
-                       if (err && (CEPH_MDS_OP_SETFILELOCK == op)) {
+                       if (err) {
                                /* undo! This should only happen if
                                 * the kernel detects local
                                 * deadlock. */
-                               ceph_lock_message(CEPH_LOCK_FCNTL, op, file,
+                               ceph_lock_message(CEPH_LOCK_FCNTL, op, inode,
                                                  CEPH_LOCK_UNLOCK, 0, fl);
                                dout("got %d on posix_lock_file, undid lock",
                                     err);
@@ -227,9 +282,11 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl)
 
 int ceph_flock(struct file *file, int cmd, struct file_lock *fl)
 {
-       u8 lock_cmd;
-       int err;
+       struct inode *inode = file_inode(file);
+       struct ceph_inode_info *ci = ceph_inode(inode);
+       int err = 0;
        u8 wait = 0;
+       u8 lock_cmd;
 
        if (!(fl->fl_flags & FL_FLOCK))
                return -ENOLCK;
@@ -239,6 +296,21 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl)
 
        dout("ceph_flock, fl_file: %p", fl->fl_file);
 
+       spin_lock(&ci->i_ceph_lock);
+       if (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) {
+               err = -EIO;
+       } else {
+               /* see comment in ceph_lock */
+               fl->fl_ops = &ceph_fl_lock_ops;
+               atomic_inc(&ci->i_filelock_ref);
+       }
+       spin_unlock(&ci->i_ceph_lock);
+       if (err < 0) {
+               if (F_UNLCK == fl->fl_type)
+                       locks_lock_file_wait(file, fl);
+               return err;
+       }
+
        if (IS_SETLKW(cmd))
                wait = 1;
 
@@ -250,13 +322,13 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl)
                lock_cmd = CEPH_LOCK_UNLOCK;
 
        err = ceph_lock_message(CEPH_LOCK_FLOCK, CEPH_MDS_OP_SETFILELOCK,
-                               file, lock_cmd, wait, fl);
+                               inode, lock_cmd, wait, fl);
        if (!err) {
                err = locks_lock_file_wait(file, fl);
                if (err) {
                        ceph_lock_message(CEPH_LOCK_FLOCK,
                                          CEPH_MDS_OP_SETFILELOCK,
-                                         file, CEPH_LOCK_UNLOCK, 0, fl);
+                                         inode, CEPH_LOCK_UNLOCK, 0, fl);
                        dout("got %d on locks_lock_file_wait, undid lock", err);
                }
        }
@@ -288,6 +360,37 @@ void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count)
             *flock_count, *fcntl_count);
 }
 
+/*
+ * Given a pointer to a lock, convert it to a ceph filelock
+ */
+static int lock_to_ceph_filelock(struct file_lock *lock,
+                                struct ceph_filelock *cephlock)
+{
+       int err = 0;
+       cephlock->start = cpu_to_le64(lock->fl_start);
+       cephlock->length = cpu_to_le64(lock->fl_end - lock->fl_start + 1);
+       cephlock->client = cpu_to_le64(0);
+       cephlock->pid = cpu_to_le64((u64)lock->fl_pid);
+       cephlock->owner = cpu_to_le64(secure_addr(lock->fl_owner));
+
+       switch (lock->fl_type) {
+       case F_RDLCK:
+               cephlock->type = CEPH_LOCK_SHARED;
+               break;
+       case F_WRLCK:
+               cephlock->type = CEPH_LOCK_EXCL;
+               break;
+       case F_UNLCK:
+               cephlock->type = CEPH_LOCK_UNLOCK;
+               break;
+       default:
+               dout("Have unknown lock type %d", lock->fl_type);
+               err = -EINVAL;
+       }
+
+       return err;
+}
+
 /**
  * Encode the flock and fcntl locks for the given inode into the ceph_filelock
  * array. Must be called with inode->i_lock already held.
@@ -356,50 +459,22 @@ int ceph_locks_to_pagelist(struct ceph_filelock *flocks,
        if (err)
                goto out_fail;
 
-       err = ceph_pagelist_append(pagelist, flocks,
-                                  num_fcntl_locks * sizeof(*flocks));
-       if (err)
-               goto out_fail;
+       if (num_fcntl_locks > 0) {
+               err = ceph_pagelist_append(pagelist, flocks,
+                                          num_fcntl_locks * sizeof(*flocks));
+               if (err)
+                       goto out_fail;
+       }
 
        nlocks = cpu_to_le32(num_flock_locks);
        err = ceph_pagelist_append(pagelist, &nlocks, sizeof(nlocks));
        if (err)
                goto out_fail;
 
-       err = ceph_pagelist_append(pagelist,
-                                  &flocks[num_fcntl_locks],
-                                  num_flock_locks * sizeof(*flocks));
-out_fail:
-       return err;
-}
-
-/*
- * Given a pointer to a lock, convert it to a ceph filelock
- */
-int lock_to_ceph_filelock(struct file_lock *lock,
-                         struct ceph_filelock *cephlock)
-{
-       int err = 0;
-       cephlock->start = cpu_to_le64(lock->fl_start);
-       cephlock->length = cpu_to_le64(lock->fl_end - lock->fl_start + 1);
-       cephlock->client = cpu_to_le64(0);
-       cephlock->pid = cpu_to_le64((u64)lock->fl_pid);
-       cephlock->owner = cpu_to_le64(secure_addr(lock->fl_owner));
-
-       switch (lock->fl_type) {
-       case F_RDLCK:
-               cephlock->type = CEPH_LOCK_SHARED;
-               break;
-       case F_WRLCK:
-               cephlock->type = CEPH_LOCK_EXCL;
-               break;
-       case F_UNLCK:
-               cephlock->type = CEPH_LOCK_UNLOCK;
-               break;
-       default:
-               dout("Have unknown lock type %d", lock->fl_type);
-               err = -EINVAL;
+       if (num_flock_locks > 0) {
+               err = ceph_pagelist_append(pagelist, &flocks[num_fcntl_locks],
+                                          num_flock_locks * sizeof(*flocks));
        }
-
+out_fail:
        return err;
 }
index 0687ab3c32674d863213186dc4554d17ad219bf3..ab69dcb70e8ae342733f589338c02dc226f95356 100644 (file)
@@ -1039,22 +1039,23 @@ void ceph_mdsc_open_export_target_sessions(struct ceph_mds_client *mdsc,
  * session caps
  */
 
-/* caller holds s_cap_lock, we drop it */
-static void cleanup_cap_releases(struct ceph_mds_client *mdsc,
-                                struct ceph_mds_session *session)
-       __releases(session->s_cap_lock)
+static void detach_cap_releases(struct ceph_mds_session *session,
+                               struct list_head *target)
 {
-       LIST_HEAD(tmp_list);
-       list_splice_init(&session->s_cap_releases, &tmp_list);
+       lockdep_assert_held(&session->s_cap_lock);
+
+       list_splice_init(&session->s_cap_releases, target);
        session->s_num_cap_releases = 0;
-       spin_unlock(&session->s_cap_lock);
+       dout("dispose_cap_releases mds%d\n", session->s_mds);
+}
 
-       dout("cleanup_cap_releases mds%d\n", session->s_mds);
-       while (!list_empty(&tmp_list)) {
+static void dispose_cap_releases(struct ceph_mds_client *mdsc,
+                                struct list_head *dispose)
+{
+       while (!list_empty(dispose)) {
                struct ceph_cap *cap;
                /* zero out the in-progress message */
-               cap = list_first_entry(&tmp_list,
-                                       struct ceph_cap, session_caps);
+               cap = list_first_entry(dispose, struct ceph_cap, session_caps);
                list_del(&cap->session_caps);
                ceph_put_cap(mdsc, cap);
        }
@@ -1215,6 +1216,13 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
                }
                spin_unlock(&mdsc->cap_dirty_lock);
 
+               if (atomic_read(&ci->i_filelock_ref) > 0) {
+                       /* make further file lock syscall return -EIO */
+                       ci->i_ceph_flags |= CEPH_I_ERROR_FILELOCK;
+                       pr_warn_ratelimited(" dropping file locks for %p %lld\n",
+                                           inode, ceph_ino(inode));
+               }
+
                if (!ci->i_dirty_caps && ci->i_prealloc_cap_flush) {
                        list_add(&ci->i_prealloc_cap_flush->i_list, &to_remove);
                        ci->i_prealloc_cap_flush = NULL;
@@ -1244,6 +1252,8 @@ static void remove_session_caps(struct ceph_mds_session *session)
 {
        struct ceph_fs_client *fsc = session->s_mdsc->fsc;
        struct super_block *sb = fsc->sb;
+       LIST_HEAD(dispose);
+
        dout("remove_session_caps on %p\n", session);
        iterate_session_caps(session, remove_session_caps_cb, fsc);
 
@@ -1278,10 +1288,12 @@ static void remove_session_caps(struct ceph_mds_session *session)
        }
 
        // drop cap expires and unlock s_cap_lock
-       cleanup_cap_releases(session->s_mdsc, session);
+       detach_cap_releases(session, &dispose);
 
        BUG_ON(session->s_nr_caps > 0);
        BUG_ON(!list_empty(&session->s_cap_flushing));
+       spin_unlock(&session->s_cap_lock);
+       dispose_cap_releases(session->s_mdsc, &dispose);
 }
 
 /*
@@ -1462,6 +1474,11 @@ static int trim_caps_cb(struct inode *inode, struct ceph_cap *cap, void *arg)
                        goto out;
                if ((used | wanted) & CEPH_CAP_ANY_WR)
                        goto out;
+               /* Note: it's possible that i_filelock_ref becomes non-zero
+                * after dropping auth caps. It doesn't hurt because reply
+                * of lock mds request will re-add auth caps. */
+               if (atomic_read(&ci->i_filelock_ref) > 0)
+                       goto out;
        }
        /* The inode has cached pages, but it's no longer used.
         * we can safely drop it */
@@ -2827,7 +2844,7 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap,
                struct ceph_mds_cap_reconnect v2;
                struct ceph_mds_cap_reconnect_v1 v1;
        } rec;
-       struct ceph_inode_info *ci;
+       struct ceph_inode_info *ci = cap->ci;
        struct ceph_reconnect_state *recon_state = arg;
        struct ceph_pagelist *pagelist = recon_state->pagelist;
        char *path;
@@ -2836,8 +2853,6 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap,
        u64 snap_follows;
        struct dentry *dentry;
 
-       ci = cap->ci;
-
        dout(" adding %p ino %llx.%llx cap %p %lld %s\n",
             inode, ceph_vinop(inode), cap, cap->cap_id,
             ceph_cap_string(cap->issued));
@@ -2870,7 +2885,8 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap,
                rec.v2.issued = cpu_to_le32(cap->issued);
                rec.v2.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
                rec.v2.pathbase = cpu_to_le64(pathbase);
-               rec.v2.flock_len = 0;
+               rec.v2.flock_len = (__force __le32)
+                       ((ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) ? 0 : 1);
        } else {
                rec.v1.cap_id = cpu_to_le64(cap->cap_id);
                rec.v1.wanted = cpu_to_le32(__ceph_caps_wanted(ci));
@@ -2894,26 +2910,37 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap,
 
        if (recon_state->msg_version >= 2) {
                int num_fcntl_locks, num_flock_locks;
-               struct ceph_filelock *flocks;
+               struct ceph_filelock *flocks = NULL;
                size_t struct_len, total_len = 0;
                u8 struct_v = 0;
 
 encode_again:
-               ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks);
-               flocks = kmalloc((num_fcntl_locks+num_flock_locks) *
-                                sizeof(struct ceph_filelock), GFP_NOFS);
-               if (!flocks) {
-                       err = -ENOMEM;
-                       goto out_free;
+               if (rec.v2.flock_len) {
+                       ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks);
+               } else {
+                       num_fcntl_locks = 0;
+                       num_flock_locks = 0;
                }
-               err = ceph_encode_locks_to_buffer(inode, flocks,
-                                                 num_fcntl_locks,
-                                                 num_flock_locks);
-               if (err) {
+               if (num_fcntl_locks + num_flock_locks > 0) {
+                       flocks = kmalloc((num_fcntl_locks + num_flock_locks) *
+                                        sizeof(struct ceph_filelock), GFP_NOFS);
+                       if (!flocks) {
+                               err = -ENOMEM;
+                               goto out_free;
+                       }
+                       err = ceph_encode_locks_to_buffer(inode, flocks,
+                                                         num_fcntl_locks,
+                                                         num_flock_locks);
+                       if (err) {
+                               kfree(flocks);
+                               flocks = NULL;
+                               if (err == -ENOSPC)
+                                       goto encode_again;
+                               goto out_free;
+                       }
+               } else {
                        kfree(flocks);
-                       if (err == -ENOSPC)
-                               goto encode_again;
-                       goto out_free;
+                       flocks = NULL;
                }
 
                if (recon_state->msg_version >= 3) {
@@ -2993,6 +3020,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc,
        int s_nr_caps;
        struct ceph_pagelist *pagelist;
        struct ceph_reconnect_state recon_state;
+       LIST_HEAD(dispose);
 
        pr_info("mds%d reconnect start\n", mds);
 
@@ -3026,7 +3054,9 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc,
         */
        session->s_cap_reconnect = 1;
        /* drop old cap expires; we're about to reestablish that state */
-       cleanup_cap_releases(mdsc, session);
+       detach_cap_releases(session, &dispose);
+       spin_unlock(&session->s_cap_lock);
+       dispose_cap_releases(mdsc, &dispose);
 
        /* trim unused caps to reduce MDS's cache rejoin time */
        if (mdsc->fsc->sb->s_root)
@@ -3857,14 +3887,14 @@ void ceph_mdsc_handle_fsmap(struct ceph_mds_client *mdsc, struct ceph_msg *msg)
                goto err_out;
        }
        return;
+
 bad:
        pr_err("error decoding fsmap\n");
 err_out:
        mutex_lock(&mdsc->mutex);
-       mdsc->mdsmap_err = -ENOENT;
+       mdsc->mdsmap_err = err;
        __wake_requests(mdsc, &mdsc->waiting_for_map);
        mutex_unlock(&mdsc->mutex);
-       return;
 }
 
 /*
index e4082afedcb15a447ee7fd344aa2a3def391d43c..fe9fbb3f13f7c7128c00e103888cbbd8d1136717 100644 (file)
@@ -84,8 +84,9 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf)
        buf->f_ffree = -1;
        buf->f_namelen = NAME_MAX;
 
-       /* leave fsid little-endian, regardless of host endianness */
-       fsid = *(u64 *)(&monmap->fsid) ^ *((u64 *)&monmap->fsid + 1);
+       /* Must convert the fsid, for consistent values across arches */
+       fsid = le64_to_cpu(*(__le64 *)(&monmap->fsid)) ^
+              le64_to_cpu(*((__le64 *)&monmap->fsid + 1));
        buf->f_fsid.val[0] = fsid & 0xffffffff;
        buf->f_fsid.val[1] = fsid >> 32;
 
index 3e27a28aa44adfd2da34ff995ff9481ac749d043..2beeec07fa76ce199e7d461831b9b93a705419ee 100644 (file)
@@ -352,6 +352,7 @@ struct ceph_inode_info {
        int i_pin_ref;
        int i_rd_ref, i_rdcache_ref, i_wr_ref, i_wb_ref;
        int i_wrbuffer_ref, i_wrbuffer_ref_head;
+       atomic_t i_filelock_ref;
        u32 i_shared_gen;       /* increment each time we get FILE_SHARED */
        u32 i_rdcache_gen;      /* incremented each time we get FILE_CACHE. */
        u32 i_rdcache_revoking; /* RDCACHE gen to async invalidate, if any */
@@ -487,6 +488,8 @@ static inline struct inode *ceph_find_inode(struct super_block *sb,
 #define CEPH_I_KICK_FLUSH      (1 << 9)  /* kick flushing caps */
 #define CEPH_I_FLUSH_SNAPS     (1 << 10) /* need flush snapss */
 #define CEPH_I_ERROR_WRITE     (1 << 11) /* have seen write errors */
+#define CEPH_I_ERROR_FILELOCK  (1 << 12) /* have seen file lock errors */
+
 
 /*
  * We set the ERROR_WRITE bit when we start seeing write errors on an inode
@@ -1011,7 +1014,6 @@ extern int ceph_encode_locks_to_buffer(struct inode *inode,
 extern int ceph_locks_to_pagelist(struct ceph_filelock *flocks,
                                  struct ceph_pagelist *pagelist,
                                  int num_fcntl_locks, int num_flock_locks);
-extern int lock_to_ceph_filelock(struct file_lock *fl, struct ceph_filelock *c);
 
 /* debugfs.c */
 extern int ceph_fs_debugfs_init(struct ceph_fs_client *client);
index b837fb7e290a6a63346f1ad7f00e63f2db8a5382..a8e3777c94dc6c44ae050168bf7906eecf71d085 100644 (file)
@@ -369,6 +369,7 @@ static int lockd_start_svc(struct svc_serv *serv)
                printk(KERN_WARNING
                        "lockd_up: svc_rqst allocation failed, error=%d\n",
                        error);
+               lockd_unregister_notifiers();
                goto out_rqst;
        }
 
@@ -459,13 +460,16 @@ int lockd_up(struct net *net)
        }
 
        error = lockd_up_net(serv, net);
-       if (error < 0)
-               goto err_net;
+       if (error < 0) {
+               lockd_unregister_notifiers();
+               goto err_put;
+       }
 
        error = lockd_start_svc(serv);
-       if (error < 0)
-               goto err_start;
-
+       if (error < 0) {
+               lockd_down_net(serv, net);
+               goto err_put;
+       }
        nlmsvc_users++;
        /*
         * Note: svc_serv structures have an initial use count of 1,
@@ -476,12 +480,6 @@ err_put:
 err_create:
        mutex_unlock(&nlmsvc_mutex);
        return error;
-
-err_start:
-       lockd_down_net(serv, net);
-err_net:
-       lockd_unregister_notifiers();
-       goto err_put;
 }
 EXPORT_SYMBOL_GPL(lockd_up);
 
index 420d3a0ab258fb2b312310e50081bbab30f2c2ae..897b299db55e01e291641b35d25f4c903962fec5 100644 (file)
@@ -55,14 +55,7 @@ locks_end_grace(struct lock_manager *lm)
 }
 EXPORT_SYMBOL_GPL(locks_end_grace);
 
-/**
- * locks_in_grace
- *
- * Lock managers call this function to determine when it is OK for them
- * to answer ordinary lock requests, and when they should accept only
- * lock reclaims.
- */
-int
+static bool
 __state_in_grace(struct net *net, bool open)
 {
        struct list_head *grace_list = net_generic(net, grace_net_id);
@@ -78,15 +71,22 @@ __state_in_grace(struct net *net, bool open)
        return false;
 }
 
-int locks_in_grace(struct net *net)
+/**
+ * locks_in_grace
+ *
+ * Lock managers call this function to determine when it is OK for them
+ * to answer ordinary lock requests, and when they should accept only
+ * lock reclaims.
+ */
+bool locks_in_grace(struct net *net)
 {
-       return __state_in_grace(net, 0);
+       return __state_in_grace(net, false);
 }
 EXPORT_SYMBOL_GPL(locks_in_grace);
 
-int opens_in_grace(struct net *net)
+bool opens_in_grace(struct net *net)
 {
-       return __state_in_grace(net, 1);
+       return __state_in_grace(net, true);
 }
 EXPORT_SYMBOL_GPL(opens_in_grace);
 
index 6dfede6d172aa276ba99544cf561cf4744220ff7..84831253203dda4a4926db4a532098ffee8a1f4c 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/nsproxy.h>
 #include <linux/sunrpc/addr.h>
 #include <linux/uaccess.h>
+#include <linux/kernel.h>
 
 #include "state.h"
 #include "netns.h"
@@ -126,8 +127,6 @@ static struct nfsd_fault_inject_op inject_ops[] = {
        },
 };
 
-#define NUM_INJECT_OPS (sizeof(inject_ops)/sizeof(struct nfsd_fault_inject_op))
-
 int nfsd_fault_inject_init(void)
 {
        unsigned int i;
@@ -138,7 +137,7 @@ int nfsd_fault_inject_init(void)
        if (!debug_dir)
                goto fail;
 
-       for (i = 0; i < NUM_INJECT_OPS; i++) {
+       for (i = 0; i < ARRAY_SIZE(inject_ops); i++) {
                op = &inject_ops[i];
                if (!debugfs_create_file(op->file, mode, debug_dir, op, &fops_nfsd))
                        goto fail;
index 3714231a9d0fb71e4e440a9f8efa7113839c4392..1c91391f48055699bb5c9ffbe64ca31d709c7518 100644 (file)
@@ -107,7 +107,7 @@ struct nfsd_net {
        bool lockd_up;
 
        /* Time of server startup */
-       struct timeval nfssvc_boot;
+       struct timespec64 nfssvc_boot;
 
        /*
         * Max number of connections this nfsd container will allow. Defaults
index f38acd9054419606e3abd25060599960d38c6f2c..2758480555faa504b1aafc204ea549361bf3b932 100644 (file)
@@ -748,8 +748,9 @@ nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p)
        if (resp->status == 0) {
                *p++ = htonl(resp->count);
                *p++ = htonl(resp->committed);
-               *p++ = htonl(nn->nfssvc_boot.tv_sec);
-               *p++ = htonl(nn->nfssvc_boot.tv_usec);
+               /* unique identifier, y2038 overflow can be ignored */
+               *p++ = htonl((u32)nn->nfssvc_boot.tv_sec);
+               *p++ = htonl(nn->nfssvc_boot.tv_nsec);
        }
        return xdr_ressize_check(rqstp, p);
 }
@@ -1119,8 +1120,9 @@ nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p)
        p = encode_wcc_data(rqstp, p, &resp->fh);
        /* Write verifier */
        if (resp->status == 0) {
-               *p++ = htonl(nn->nfssvc_boot.tv_sec);
-               *p++ = htonl(nn->nfssvc_boot.tv_usec);
+               /* unique identifier, y2038 overflow can be ignored */
+               *p++ = htonl((u32)nn->nfssvc_boot.tv_sec);
+               *p++ = htonl(nn->nfssvc_boot.tv_nsec);
        }
        return xdr_ressize_check(rqstp, p);
 }
index ea45d954e8d7c53cbb3db6dcbf8ac3958b314a90..7d888369f85a4194b0ddf0c2202bb693fe9cac99 100644 (file)
@@ -336,7 +336,7 @@ nfsd4_recall_file_layout(struct nfs4_layout_stateid *ls)
 
        trace_layout_recall(&ls->ls_stid.sc_stateid);
 
-       atomic_inc(&ls->ls_stid.sc_count);
+       refcount_inc(&ls->ls_stid.sc_count);
        nfsd4_run_cb(&ls->ls_recall);
 
 out_unlock:
@@ -441,7 +441,7 @@ nfsd4_insert_layout(struct nfsd4_layoutget *lgp, struct nfs4_layout_stateid *ls)
                        goto done;
        }
 
-       atomic_inc(&ls->ls_stid.sc_count);
+       refcount_inc(&ls->ls_stid.sc_count);
        list_add_tail(&new->lo_perstate, &ls->ls_layouts);
        new = NULL;
 done:
index 8487486ec4963efb72477e7cf2f19616108f12f2..008ea0b627d02d5a06f8b3febb793627e11b70c3 100644 (file)
@@ -485,9 +485,6 @@ static __be32
 nfsd4_getfh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
            union nfsd4_op_u *u)
 {
-       if (!cstate->current_fh.fh_dentry)
-               return nfserr_nofilehandle;
-
        u->getfh = &cstate->current_fh;
        return nfs_ok;
 }
@@ -535,9 +532,6 @@ static __be32
 nfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
             union nfsd4_op_u *u)
 {
-       if (!cstate->current_fh.fh_dentry)
-               return nfserr_nofilehandle;
-
        fh_dup2(&cstate->save_fh, &cstate->current_fh);
        if (HAS_STATE_ID(cstate, CURRENT_STATE_ID_FLAG)) {
                memcpy(&cstate->save_stateid, &cstate->current_stateid, sizeof(stateid_t));
@@ -570,10 +564,11 @@ static void gen_boot_verifier(nfs4_verifier *verifier, struct net *net)
 
        /*
         * This is opaque to client, so no need to byte-swap. Use
-        * __force to keep sparse happy
+        * __force to keep sparse happy. y2038 time_t overflow is
+        * irrelevant in this usage.
         */
        verf[0] = (__force __be32)nn->nfssvc_boot.tv_sec;
-       verf[1] = (__force __be32)nn->nfssvc_boot.tv_usec;
+       verf[1] = (__force __be32)nn->nfssvc_boot.tv_nsec;
        memcpy(verifier->data, verf, sizeof(verifier->data));
 }
 
@@ -703,10 +698,8 @@ nfsd4_link(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
           union nfsd4_op_u *u)
 {
        struct nfsd4_link *link = &u->link;
-       __be32 status = nfserr_nofilehandle;
+       __be32 status;
 
-       if (!cstate->save_fh.fh_dentry)
-               return status;
        status = nfsd_link(rqstp, &cstate->current_fh,
                           link->li_name, link->li_namelen, &cstate->save_fh);
        if (!status)
@@ -850,10 +843,8 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
             union nfsd4_op_u *u)
 {
        struct nfsd4_rename *rename = &u->rename;
-       __be32 status = nfserr_nofilehandle;
+       __be32 status;
 
-       if (!cstate->save_fh.fh_dentry)
-               return status;
        if (opens_in_grace(SVC_NET(rqstp)) &&
                !(cstate->save_fh.fh_export->ex_flags & NFSEXP_NOSUBTREECHECK))
                return nfserr_grace;
index 0c04f81aa63b225b2207b226b1113e1973ec1e1b..b82817767b9da4ea6e8e3fc0cde8e6f068756ca7 100644 (file)
@@ -359,7 +359,7 @@ put_nfs4_file(struct nfs4_file *fi)
 {
        might_lock(&state_lock);
 
-       if (atomic_dec_and_lock(&fi->fi_ref, &state_lock)) {
+       if (refcount_dec_and_lock(&fi->fi_ref, &state_lock)) {
                hlist_del_rcu(&fi->fi_hash);
                spin_unlock(&state_lock);
                WARN_ON_ONCE(!list_empty(&fi->fi_clnt_odstate));
@@ -568,7 +568,7 @@ alloc_clnt_odstate(struct nfs4_client *clp)
        co = kmem_cache_zalloc(odstate_slab, GFP_KERNEL);
        if (co) {
                co->co_client = clp;
-               atomic_set(&co->co_odcount, 1);
+               refcount_set(&co->co_odcount, 1);
        }
        return co;
 }
@@ -586,7 +586,7 @@ static inline void
 get_clnt_odstate(struct nfs4_clnt_odstate *co)
 {
        if (co)
-               atomic_inc(&co->co_odcount);
+               refcount_inc(&co->co_odcount);
 }
 
 static void
@@ -598,7 +598,7 @@ put_clnt_odstate(struct nfs4_clnt_odstate *co)
                return;
 
        fp = co->co_file;
-       if (atomic_dec_and_lock(&co->co_odcount, &fp->fi_lock)) {
+       if (refcount_dec_and_lock(&co->co_odcount, &fp->fi_lock)) {
                list_del(&co->co_perfile);
                spin_unlock(&fp->fi_lock);
 
@@ -656,7 +656,7 @@ struct nfs4_stid *nfs4_alloc_stid(struct nfs4_client *cl, struct kmem_cache *sla
        stid->sc_stateid.si_opaque.so_id = new_id;
        stid->sc_stateid.si_opaque.so_clid = cl->cl_clientid;
        /* Will be incremented before return to client: */
-       atomic_set(&stid->sc_count, 1);
+       refcount_set(&stid->sc_count, 1);
        spin_lock_init(&stid->sc_lock);
 
        /*
@@ -813,7 +813,7 @@ nfs4_put_stid(struct nfs4_stid *s)
 
        might_lock(&clp->cl_lock);
 
-       if (!atomic_dec_and_lock(&s->sc_count, &clp->cl_lock)) {
+       if (!refcount_dec_and_lock(&s->sc_count, &clp->cl_lock)) {
                wake_up_all(&close_wq);
                return;
        }
@@ -913,7 +913,7 @@ hash_delegation_locked(struct nfs4_delegation *dp, struct nfs4_file *fp)
        if (status)
                return status;
        ++fp->fi_delegees;
-       atomic_inc(&dp->dl_stid.sc_count);
+       refcount_inc(&dp->dl_stid.sc_count);
        dp->dl_stid.sc_type = NFS4_DELEG_STID;
        list_add(&dp->dl_perfile, &fp->fi_delegations);
        list_add(&dp->dl_perclnt, &clp->cl_delegations);
@@ -1214,7 +1214,7 @@ static void put_ol_stateid_locked(struct nfs4_ol_stateid *stp,
 
        WARN_ON_ONCE(!list_empty(&stp->st_locks));
 
-       if (!atomic_dec_and_test(&s->sc_count)) {
+       if (!refcount_dec_and_test(&s->sc_count)) {
                wake_up_all(&close_wq);
                return;
        }
@@ -1439,8 +1439,10 @@ free_session_slots(struct nfsd4_session *ses)
 {
        int i;
 
-       for (i = 0; i < ses->se_fchannel.maxreqs; i++)
+       for (i = 0; i < ses->se_fchannel.maxreqs; i++) {
+               free_svc_cred(&ses->se_slots[i]->sl_cred);
                kfree(ses->se_slots[i]);
+       }
 }
 
 /*
@@ -1472,6 +1474,11 @@ static u32 nfsd4_get_drc_mem(struct nfsd4_channel_attrs *ca)
        spin_lock(&nfsd_drc_lock);
        avail = min((unsigned long)NFSD_MAX_MEM_PER_SESSION,
                    nfsd_drc_max_mem - nfsd_drc_mem_used);
+       /*
+        * Never use more than a third of the remaining memory,
+        * unless it's the only way to give this client a slot:
+        */
+       avail = clamp_t(int, avail, slotsize, avail/3);
        num = min_t(int, num, avail / slotsize);
        nfsd_drc_mem_used += num * slotsize;
        spin_unlock(&nfsd_drc_lock);
@@ -2072,7 +2079,7 @@ find_stateid_by_type(struct nfs4_client *cl, stateid_t *t, char typemask)
        s = find_stateid_locked(cl, t);
        if (s != NULL) {
                if (typemask & s->sc_type)
-                       atomic_inc(&s->sc_count);
+                       refcount_inc(&s->sc_count);
                else
                        s = NULL;
        }
@@ -2287,14 +2294,18 @@ nfsd4_store_cache_entry(struct nfsd4_compoundres *resp)
 
        dprintk("--> %s slot %p\n", __func__, slot);
 
+       slot->sl_flags |= NFSD4_SLOT_INITIALIZED;
        slot->sl_opcnt = resp->opcnt;
        slot->sl_status = resp->cstate.status;
+       free_svc_cred(&slot->sl_cred);
+       copy_cred(&slot->sl_cred, &resp->rqstp->rq_cred);
 
-       slot->sl_flags |= NFSD4_SLOT_INITIALIZED;
-       if (nfsd4_not_cached(resp)) {
-               slot->sl_datalen = 0;
+       if (!nfsd4_cache_this(resp)) {
+               slot->sl_flags &= ~NFSD4_SLOT_CACHED;
                return;
        }
+       slot->sl_flags |= NFSD4_SLOT_CACHED;
+
        base = resp->cstate.data_offset;
        slot->sl_datalen = buf->len - base;
        if (read_bytes_from_xdr_buf(buf, base, slot->sl_data, slot->sl_datalen))
@@ -2321,8 +2332,16 @@ nfsd4_enc_sequence_replay(struct nfsd4_compoundargs *args,
        op = &args->ops[resp->opcnt - 1];
        nfsd4_encode_operation(resp, op);
 
-       /* Return nfserr_retry_uncached_rep in next operation. */
-       if (args->opcnt > 1 && !(slot->sl_flags & NFSD4_SLOT_CACHETHIS)) {
+       if (slot->sl_flags & NFSD4_SLOT_CACHED)
+               return op->status;
+       if (args->opcnt == 1) {
+               /*
+                * The original operation wasn't a solo sequence--we
+                * always cache those--so this retry must not match the
+                * original:
+                */
+               op->status = nfserr_seq_false_retry;
+       } else {
                op = &args->ops[resp->opcnt++];
                op->status = nfserr_retry_uncached_rep;
                nfsd4_encode_operation(resp, op);
@@ -2986,6 +3005,34 @@ static bool nfsd4_request_too_big(struct svc_rqst *rqstp,
        return xb->len > session->se_fchannel.maxreq_sz;
 }
 
+static bool replay_matches_cache(struct svc_rqst *rqstp,
+                struct nfsd4_sequence *seq, struct nfsd4_slot *slot)
+{
+       struct nfsd4_compoundargs *argp = rqstp->rq_argp;
+
+       if ((bool)(slot->sl_flags & NFSD4_SLOT_CACHETHIS) !=
+           (bool)seq->cachethis)
+               return false;
+       /*
+        * If there's an error than the reply can have fewer ops than
+        * the call.  But if we cached a reply with *more* ops than the
+        * call you're sending us now, then this new call is clearly not
+        * really a replay of the old one:
+        */
+       if (slot->sl_opcnt < argp->opcnt)
+               return false;
+       /* This is the only check explicitly called by spec: */
+       if (!same_creds(&rqstp->rq_cred, &slot->sl_cred))
+               return false;
+       /*
+        * There may be more comparisons we could actually do, but the
+        * spec doesn't require us to catch every case where the calls
+        * don't match (that would require caching the call as well as
+        * the reply), so we don't bother.
+        */
+       return true;
+}
+
 __be32
 nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                union nfsd4_op_u *u)
@@ -3045,6 +3092,9 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                status = nfserr_seq_misordered;
                if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED))
                        goto out_put_session;
+               status = nfserr_seq_false_retry;
+               if (!replay_matches_cache(rqstp, seq, slot))
+                       goto out_put_session;
                cstate->slot = slot;
                cstate->session = session;
                cstate->clp = clp;
@@ -3351,7 +3401,7 @@ static void nfsd4_init_file(struct knfsd_fh *fh, unsigned int hashval,
 {
        lockdep_assert_held(&state_lock);
 
-       atomic_set(&fp->fi_ref, 1);
+       refcount_set(&fp->fi_ref, 1);
        spin_lock_init(&fp->fi_lock);
        INIT_LIST_HEAD(&fp->fi_stateids);
        INIT_LIST_HEAD(&fp->fi_delegations);
@@ -3514,7 +3564,7 @@ nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
                        continue;
                if (local->st_stateowner == &oo->oo_owner) {
                        ret = local;
-                       atomic_inc(&ret->st_stid.sc_count);
+                       refcount_inc(&ret->st_stid.sc_count);
                        break;
                }
        }
@@ -3573,7 +3623,7 @@ init_open_stateid(struct nfs4_file *fp, struct nfsd4_open *open)
                goto out_unlock;
 
        open->op_stp = NULL;
-       atomic_inc(&stp->st_stid.sc_count);
+       refcount_inc(&stp->st_stid.sc_count);
        stp->st_stid.sc_type = NFS4_OPEN_STID;
        INIT_LIST_HEAD(&stp->st_locks);
        stp->st_stateowner = nfs4_get_stateowner(&oo->oo_owner);
@@ -3621,7 +3671,7 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
         * there should be no danger of the refcount going back up again at
         * this point.
         */
-       wait_event(close_wq, atomic_read(&s->st_stid.sc_count) == 2);
+       wait_event(close_wq, refcount_read(&s->st_stid.sc_count) == 2);
 
        release_all_access(s);
        if (s->st_stid.sc_file) {
@@ -3647,7 +3697,7 @@ find_file_locked(struct knfsd_fh *fh, unsigned int hashval)
 
        hlist_for_each_entry_rcu(fp, &file_hashtbl[hashval], fi_hash) {
                if (fh_match(&fp->fi_fhandle, fh)) {
-                       if (atomic_inc_not_zero(&fp->fi_ref))
+                       if (refcount_inc_not_zero(&fp->fi_ref))
                                return fp;
                }
        }
@@ -3783,7 +3833,7 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
         * lock) we know the server hasn't removed the lease yet, we know
         * it's safe to take a reference.
         */
-       atomic_inc(&dp->dl_stid.sc_count);
+       refcount_inc(&dp->dl_stid.sc_count);
        nfsd4_run_cb(&dp->dl_recall);
 }
 
@@ -3966,7 +4016,8 @@ static struct nfs4_delegation *find_deleg_stateid(struct nfs4_client *cl, statei
 {
        struct nfs4_stid *ret;
 
-       ret = find_stateid_by_type(cl, s, NFS4_DELEG_STID);
+       ret = find_stateid_by_type(cl, s,
+                               NFS4_DELEG_STID|NFS4_REVOKED_DELEG_STID);
        if (!ret)
                return NULL;
        return delegstateid(ret);
@@ -3989,6 +4040,12 @@ nfs4_check_deleg(struct nfs4_client *cl, struct nfsd4_open *open,
        deleg = find_deleg_stateid(cl, &open->op_delegate_stateid);
        if (deleg == NULL)
                goto out;
+       if (deleg->dl_stid.sc_type == NFS4_REVOKED_DELEG_STID) {
+               nfs4_put_stid(&deleg->dl_stid);
+               if (cl->cl_minorversion)
+                       status = nfserr_deleg_revoked;
+               goto out;
+       }
        flags = share_access_to_flags(open->op_share_access);
        status = nfs4_check_delegmode(deleg, flags);
        if (status) {
@@ -4858,6 +4915,16 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
                     struct nfs4_stid **s, struct nfsd_net *nn)
 {
        __be32 status;
+       bool return_revoked = false;
+
+       /*
+        *  only return revoked delegations if explicitly asked.
+        *  otherwise we report revoked or bad_stateid status.
+        */
+       if (typemask & NFS4_REVOKED_DELEG_STID)
+               return_revoked = true;
+       else if (typemask & NFS4_DELEG_STID)
+               typemask |= NFS4_REVOKED_DELEG_STID;
 
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
                return nfserr_bad_stateid;
@@ -4872,6 +4939,12 @@ nfsd4_lookup_stateid(struct nfsd4_compound_state *cstate,
        *s = find_stateid_by_type(cstate->clp, stateid, typemask);
        if (!*s)
                return nfserr_bad_stateid;
+       if (((*s)->sc_type == NFS4_REVOKED_DELEG_STID) && !return_revoked) {
+               nfs4_put_stid(*s);
+               if (cstate->minorversion)
+                       return nfserr_deleg_revoked;
+               return nfserr_bad_stateid;
+       }
        return nfs_ok;
 }
 
@@ -5071,7 +5144,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                ret = nfserr_locks_held;
                break;
        case NFS4_LOCK_STID:
-               atomic_inc(&s->sc_count);
+               refcount_inc(&s->sc_count);
                spin_unlock(&cl->cl_lock);
                ret = nfsd4_free_lock_stateid(stateid, s);
                goto out;
@@ -5578,7 +5651,7 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
 
        lockdep_assert_held(&clp->cl_lock);
 
-       atomic_inc(&stp->st_stid.sc_count);
+       refcount_inc(&stp->st_stid.sc_count);
        stp->st_stid.sc_type = NFS4_LOCK_STID;
        stp->st_stateowner = nfs4_get_stateowner(&lo->lo_owner);
        get_nfs4_file(fp);
@@ -5604,7 +5677,7 @@ find_lock_stateid(struct nfs4_lockowner *lo, struct nfs4_file *fp)
 
        list_for_each_entry(lst, &lo->lo_owner.so_stateids, st_perstateowner) {
                if (lst->st_stid.sc_file == fp) {
-                       atomic_inc(&lst->st_stid.sc_count);
+                       refcount_inc(&lst->st_stid.sc_count);
                        return lst;
                }
        }
@@ -7006,8 +7079,8 @@ nfs4_state_start_net(struct net *net)
        nn->nfsd4_manager.block_opens = true;
        locks_start_grace(net, &nn->nfsd4_manager);
        nfsd4_client_tracking_init(net);
-       printk(KERN_INFO "NFSD: starting %ld-second grace period (net %p)\n",
-              nn->nfsd4_grace, net);
+       printk(KERN_INFO "NFSD: starting %ld-second grace period (net %x)\n",
+              nn->nfsd4_grace, net->ns.inum);
        queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ);
        return 0;
 }
index e02bd278312463af174de08b017c445eb4e86b5b..33117d4ffce0753e987ff8fc67b03c719b180676 100644 (file)
@@ -447,7 +447,7 @@ void nfsd_reset_versions(void)
  */
 static void set_max_drc(void)
 {
-       #define NFSD_DRC_SIZE_SHIFT     10
+       #define NFSD_DRC_SIZE_SHIFT     7
        nfsd_drc_max_mem = (nr_free_buffer_pages()
                                        >> NFSD_DRC_SIZE_SHIFT) * PAGE_SIZE;
        nfsd_drc_mem_used = 0;
@@ -517,7 +517,7 @@ int nfsd_create_serv(struct net *net)
                register_inet6addr_notifier(&nfsd_inet6addr_notifier);
 #endif
        }
-       do_gettimeofday(&nn->nfssvc_boot);              /* record boot time */
+       ktime_get_real_ts64(&nn->nfssvc_boot); /* record boot time */
        return 0;
 }
 
index 005c911b34ac4553a2c02da05b4e5d975b660710..f3772ea8ba0d394f95c302584093d57fc19e37d7 100644 (file)
@@ -36,6 +36,7 @@
 #define _NFSD4_STATE_H
 
 #include <linux/idr.h>
+#include <linux/refcount.h>
 #include <linux/sunrpc/svc_xprt.h>
 #include "nfsfh.h"
 
@@ -83,7 +84,7 @@ struct nfsd4_callback_ops {
  * fields that are of general use to any stateid.
  */
 struct nfs4_stid {
-       atomic_t                sc_count;
+       refcount_t              sc_count;
 #define NFS4_OPEN_STID 1
 #define NFS4_LOCK_STID 2
 #define NFS4_DELEG_STID 4
@@ -169,11 +170,13 @@ static inline struct nfs4_delegation *delegstateid(struct nfs4_stid *s)
 struct nfsd4_slot {
        u32     sl_seqid;
        __be32  sl_status;
+       struct svc_cred sl_cred;
        u32     sl_datalen;
        u16     sl_opcnt;
 #define NFSD4_SLOT_INUSE       (1 << 0)
 #define NFSD4_SLOT_CACHETHIS   (1 << 1)
 #define NFSD4_SLOT_INITIALIZED (1 << 2)
+#define NFSD4_SLOT_CACHED      (1 << 3)
        u8      sl_flags;
        char    sl_data[];
 };
@@ -465,7 +468,7 @@ struct nfs4_clnt_odstate {
        struct nfs4_client      *co_client;
        struct nfs4_file        *co_file;
        struct list_head        co_perfile;
-       atomic_t                co_odcount;
+       refcount_t              co_odcount;
 };
 
 /*
@@ -481,7 +484,7 @@ struct nfs4_clnt_odstate {
  * the global state_lock spinlock.
  */
 struct nfs4_file {
-       atomic_t                fi_ref;
+       refcount_t              fi_ref;
        spinlock_t              fi_lock;
        struct hlist_node       fi_hash;        /* hash on fi_fhandle */
        struct list_head        fi_stateids;
@@ -634,7 +637,7 @@ struct nfs4_file *find_file(struct knfsd_fh *fh);
 void put_nfs4_file(struct nfs4_file *fi);
 static inline void get_nfs4_file(struct nfs4_file *fi)
 {
-       atomic_inc(&fi->fi_ref);
+       refcount_inc(&fi->fi_ref);
 }
 struct file *find_any_file(struct nfs4_file *f);
 
index 1e4edbf70052bf5f6c8c0af06e0d7bf1812e80a5..bc29511b6405275522a09db2c596991cdd99e710 100644 (file)
@@ -649,9 +649,18 @@ static inline bool nfsd4_is_solo_sequence(struct nfsd4_compoundres *resp)
        return resp->opcnt == 1 && args->ops[0].opnum == OP_SEQUENCE;
 }
 
-static inline bool nfsd4_not_cached(struct nfsd4_compoundres *resp)
+/*
+ * The session reply cache only needs to cache replies that the client
+ * actually asked us to.  But it's almost free for us to cache compounds
+ * consisting of only a SEQUENCE op, so we may as well cache those too.
+ * Also, the protocol doesn't give us a convenient response in the case
+ * of a replay of a solo SEQUENCE op that wasn't cached
+ * (RETRY_UNCACHED_REP can only be returned in the second op of a
+ * compound).
+ */
+static inline bool nfsd4_cache_this(struct nfsd4_compoundres *resp)
 {
-       return !(resp->cstate.slot->sl_flags & NFSD4_SLOT_CACHETHIS)
+       return (resp->cstate.slot->sl_flags & NFSD4_SLOT_CACHETHIS)
                || nfsd4_is_solo_sequence(resp);
 }
 
index c2d8233b1e826cc99abb615d55c1b590bc89d0b8..480ea059a6802785c78a291c14e7ca01abdcf548 100644 (file)
@@ -155,13 +155,11 @@ int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 
 int orangefs_init_acl(struct inode *inode, struct inode *dir)
 {
-       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
        struct posix_acl *default_acl, *acl;
        umode_t mode = inode->i_mode;
+       struct iattr iattr;
        int error = 0;
 
-       ClearModeFlag(orangefs_inode);
-
        error = posix_acl_create(dir, &mode, &default_acl, &acl);
        if (error)
                return error;
@@ -180,9 +178,11 @@ int orangefs_init_acl(struct inode *inode, struct inode *dir)
 
        /* If mode of the inode was changed, then do a forcible ->setattr */
        if (mode != inode->i_mode) {
-               SetModeFlag(orangefs_inode);
+               memset(&iattr, 0, sizeof iattr);
                inode->i_mode = mode;
-               orangefs_flush_inode(inode);
+               iattr.ia_mode = mode;
+               iattr.ia_valid |= ATTR_MODE;
+               orangefs_inode_setattr(inode, &iattr);
        }
 
        return error;
index a8cc588d6224f3b48c2e9be7f7881612889ef2bd..e2c2699d8016274dbe2225b14c0ab8828ceae00f 100644 (file)
@@ -386,7 +386,6 @@ static int orangefs_dir_release(struct inode *inode, struct file *file)
 {
        struct orangefs_dir *od = file->private_data;
        struct orangefs_dir_part *part = od->part;
-       orangefs_flush_inode(inode);
        while (part) {
                struct orangefs_dir_part *next = part->next;
                vfree(part);
index e4a8e6a7eb17b6aaa4b0ab5bf34e16b514c0ea82..1668fd645c453609473f9d6ab89b30499300d940 100644 (file)
@@ -383,9 +383,15 @@ out:
                if (type == ORANGEFS_IO_READ) {
                        file_accessed(file);
                } else {
-                       SetMtimeFlag(orangefs_inode);
-                       inode->i_mtime = current_time(inode);
-                       mark_inode_dirty_sync(inode);
+                       file_update_time(file);
+                       /*
+                        * Must invalidate to ensure write loop doesn't
+                        * prevent kernel from reading updated
+                        * attribute.  Size probably changed because of
+                        * the write, and other clients could update
+                        * any other attribute.
+                        */
+                       orangefs_inode->getattr_time = jiffies - 1;
                }
        }
 
@@ -615,8 +621,6 @@ static int orangefs_file_release(struct inode *inode, struct file *file)
                     "orangefs_file_release: called on %pD\n",
                     file);
 
-       orangefs_flush_inode(inode);
-
        /*
         * remove all associated inode pages from the page cache and
         * readahead cache (if any); this forces an expensive refresh of
@@ -666,8 +670,6 @@ static int orangefs_fsync(struct file *file,
                     ret);
 
        op_release(new_op);
-
-       orangefs_flush_inode(file_inode(file));
        return ret;
 }
 
index 28825a5b6d098f5fbfad1741e0476f7601f41a56..fe1d705ad91fac90c8d496a1c78611e42da9d1d1 100644 (file)
@@ -290,6 +290,22 @@ int orangefs_permission(struct inode *inode, int mask)
        return generic_permission(inode, mask);
 }
 
+int orangefs_update_time(struct inode *inode, struct timespec *time, int flags)
+{
+       struct iattr iattr;
+       gossip_debug(GOSSIP_INODE_DEBUG, "orangefs_update_time: %pU\n",
+           get_khandle_from_ino(inode));
+       generic_update_time(inode, time, flags);
+       memset(&iattr, 0, sizeof iattr);
+        if (flags & S_ATIME)
+               iattr.ia_valid |= ATTR_ATIME;
+       if (flags & S_CTIME)
+               iattr.ia_valid |= ATTR_CTIME;
+       if (flags & S_MTIME)
+               iattr.ia_valid |= ATTR_MTIME;
+       return orangefs_inode_setattr(inode, &iattr);
+}
+
 /* ORANGEDS2 implementation of VFS inode operations for files */
 const struct inode_operations orangefs_file_inode_operations = {
        .get_acl = orangefs_get_acl,
@@ -298,6 +314,7 @@ const struct inode_operations orangefs_file_inode_operations = {
        .getattr = orangefs_getattr,
        .listxattr = orangefs_listxattr,
        .permission = orangefs_permission,
+       .update_time = orangefs_update_time,
 };
 
 static int orangefs_init_iops(struct inode *inode)
index 7e9e5d0ea3bc24a9b8f270497c3319a1f5bd0848..c98bba2dbc94c3547782f7f485bbdd4793d354f4 100644 (file)
@@ -22,7 +22,9 @@ static int orangefs_create(struct inode *dir,
 {
        struct orangefs_inode_s *parent = ORANGEFS_I(dir);
        struct orangefs_kernel_op_s *new_op;
+       struct orangefs_object_kref ref;
        struct inode *inode;
+       struct iattr iattr;
        int ret;
 
        gossip_debug(GOSSIP_NAME_DEBUG, "%s: %pd\n",
@@ -55,8 +57,10 @@ static int orangefs_create(struct inode *dir,
        if (ret < 0)
                goto out;
 
-       inode = orangefs_new_inode(dir->i_sb, dir, S_IFREG | mode, 0,
-                               &new_op->downcall.resp.create.refn);
+       ref = new_op->downcall.resp.create.refn;
+       op_release(new_op);
+
+       inode = orangefs_new_inode(dir->i_sb, dir, S_IFREG | mode, 0, &ref);
        if (IS_ERR(inode)) {
                gossip_err("%s: Failed to allocate inode for file :%pd:\n",
                           __func__,
@@ -82,12 +86,13 @@ static int orangefs_create(struct inode *dir,
                     __func__,
                     dentry);
 
-       SetMtimeFlag(parent);
        dir->i_mtime = dir->i_ctime = current_time(dir);
+       memset(&iattr, 0, sizeof iattr);
+       iattr.ia_valid |= ATTR_MTIME;
+       orangefs_inode_setattr(dir, &iattr);
        mark_inode_dirty_sync(dir);
        ret = 0;
 out:
-       op_release(new_op);
        gossip_debug(GOSSIP_NAME_DEBUG,
                     "%s: %pd: returning %d\n",
                     __func__,
@@ -221,6 +226,7 @@ static int orangefs_unlink(struct inode *dir, struct dentry *dentry)
        struct inode *inode = dentry->d_inode;
        struct orangefs_inode_s *parent = ORANGEFS_I(dir);
        struct orangefs_kernel_op_s *new_op;
+       struct iattr iattr;
        int ret;
 
        gossip_debug(GOSSIP_NAME_DEBUG,
@@ -253,8 +259,10 @@ static int orangefs_unlink(struct inode *dir, struct dentry *dentry)
        if (!ret) {
                drop_nlink(inode);
 
-               SetMtimeFlag(parent);
                dir->i_mtime = dir->i_ctime = current_time(dir);
+               memset(&iattr, 0, sizeof iattr);
+               iattr.ia_valid |= ATTR_MTIME;
+               orangefs_inode_setattr(dir, &iattr);
                mark_inode_dirty_sync(dir);
        }
        return ret;
@@ -266,7 +274,9 @@ static int orangefs_symlink(struct inode *dir,
 {
        struct orangefs_inode_s *parent = ORANGEFS_I(dir);
        struct orangefs_kernel_op_s *new_op;
+       struct orangefs_object_kref ref;
        struct inode *inode;
+       struct iattr iattr;
        int mode = 755;
        int ret;
 
@@ -307,8 +317,10 @@ static int orangefs_symlink(struct inode *dir,
                goto out;
        }
 
-       inode = orangefs_new_inode(dir->i_sb, dir, S_IFLNK | mode, 0,
-                               &new_op->downcall.resp.sym.refn);
+       ref = new_op->downcall.resp.sym.refn;
+       op_release(new_op);
+
+       inode = orangefs_new_inode(dir->i_sb, dir, S_IFLNK | mode, 0, &ref);
        if (IS_ERR(inode)) {
                gossip_err
                    ("*** Failed to allocate orangefs symlink inode\n");
@@ -331,12 +343,13 @@ static int orangefs_symlink(struct inode *dir,
                     get_khandle_from_ino(inode),
                     dentry);
 
-       SetMtimeFlag(parent);
        dir->i_mtime = dir->i_ctime = current_time(dir);
+       memset(&iattr, 0, sizeof iattr);
+       iattr.ia_valid |= ATTR_MTIME;
+       orangefs_inode_setattr(dir, &iattr);
        mark_inode_dirty_sync(dir);
        ret = 0;
 out:
-       op_release(new_op);
        return ret;
 }
 
@@ -344,7 +357,9 @@ static int orangefs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
 {
        struct orangefs_inode_s *parent = ORANGEFS_I(dir);
        struct orangefs_kernel_op_s *new_op;
+       struct orangefs_object_kref ref;
        struct inode *inode;
+       struct iattr iattr;
        int ret;
 
        new_op = op_alloc(ORANGEFS_VFS_OP_MKDIR);
@@ -373,8 +388,10 @@ static int orangefs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
                goto out;
        }
 
-       inode = orangefs_new_inode(dir->i_sb, dir, S_IFDIR | mode, 0,
-                               &new_op->downcall.resp.mkdir.refn);
+       ref = new_op->downcall.resp.mkdir.refn;
+       op_release(new_op);
+
+       inode = orangefs_new_inode(dir->i_sb, dir, S_IFDIR | mode, 0, &ref);
        if (IS_ERR(inode)) {
                gossip_err("*** Failed to allocate orangefs dir inode\n");
                ret = PTR_ERR(inode);
@@ -400,11 +417,12 @@ static int orangefs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
         * NOTE: we have no good way to keep nlink consistent for directories
         * across clients; keep constant at 1.
         */
-       SetMtimeFlag(parent);
        dir->i_mtime = dir->i_ctime = current_time(dir);
+       memset(&iattr, 0, sizeof iattr);
+       iattr.ia_valid |= ATTR_MTIME;
+       orangefs_inode_setattr(dir, &iattr);
        mark_inode_dirty_sync(dir);
 out:
-       op_release(new_op);
        return ret;
 }
 
@@ -470,4 +488,5 @@ const struct inode_operations orangefs_dir_inode_operations = {
        .getattr = orangefs_getattr,
        .listxattr = orangefs_listxattr,
        .permission = orangefs_permission,
+       .update_time = orangefs_update_time,
 };
index b6001bb28f5a94f6d86a17a76b3245525748be9c..c7db56a31b9209076975f6380aad69761cccfde9 100644 (file)
 
 #ifdef __KERNEL__
 #include <linux/types.h>
+#include <linux/kernel.h>
 #else
 #include <stdint.h>
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
 #endif
 
 #define        GOSSIP_NO_DEBUG                 (__u64)0
@@ -88,6 +90,6 @@ static struct __keyword_mask_s s_kmod_keyword_mask_map[] = {
 };
 
 static const int num_kmod_keyword_mask_map = (int)
-       (sizeof(s_kmod_keyword_mask_map) / sizeof(struct __keyword_mask_s));
+       (ARRAY_SIZE(s_kmod_keyword_mask_map));
 
 #endif /* __ORANGEFS_DEBUG_H */
index f44d5eb74fcc84f37fe69606f63b6a50dd171dba..97adf7d100b5fe498f70345d1fcdfc525f1d2309 100644 (file)
@@ -209,37 +209,10 @@ struct orangefs_inode_s {
        struct inode vfs_inode;
        sector_t last_failed_block_index_read;
 
-       /*
-        * State of in-memory attributes not yet flushed to disk associated
-        * with this object
-        */
-       unsigned long pinode_flags;
-
        unsigned long getattr_time;
        u32 getattr_mask;
 };
 
-#define P_ATIME_FLAG 0
-#define P_MTIME_FLAG 1
-#define P_CTIME_FLAG 2
-#define P_MODE_FLAG  3
-
-#define ClearAtimeFlag(pinode) clear_bit(P_ATIME_FLAG, &(pinode)->pinode_flags)
-#define SetAtimeFlag(pinode)   set_bit(P_ATIME_FLAG, &(pinode)->pinode_flags)
-#define AtimeFlag(pinode)      test_bit(P_ATIME_FLAG, &(pinode)->pinode_flags)
-
-#define ClearMtimeFlag(pinode) clear_bit(P_MTIME_FLAG, &(pinode)->pinode_flags)
-#define SetMtimeFlag(pinode)   set_bit(P_MTIME_FLAG, &(pinode)->pinode_flags)
-#define MtimeFlag(pinode)      test_bit(P_MTIME_FLAG, &(pinode)->pinode_flags)
-
-#define ClearCtimeFlag(pinode) clear_bit(P_CTIME_FLAG, &(pinode)->pinode_flags)
-#define SetCtimeFlag(pinode)   set_bit(P_CTIME_FLAG, &(pinode)->pinode_flags)
-#define CtimeFlag(pinode)      test_bit(P_CTIME_FLAG, &(pinode)->pinode_flags)
-
-#define ClearModeFlag(pinode) clear_bit(P_MODE_FLAG, &(pinode)->pinode_flags)
-#define SetModeFlag(pinode)   set_bit(P_MODE_FLAG, &(pinode)->pinode_flags)
-#define ModeFlag(pinode)      test_bit(P_MODE_FLAG, &(pinode)->pinode_flags)
-
 /* per superblock private orangefs info */
 struct orangefs_sb_info_s {
        struct orangefs_khandle root_khandle;
@@ -436,6 +409,8 @@ int orangefs_getattr(const struct path *path, struct kstat *stat,
 
 int orangefs_permission(struct inode *inode, int mask);
 
+int orangefs_update_time(struct inode *, struct timespec *, int);
+
 /*
  * defined in xattr.c
  */
@@ -478,8 +453,6 @@ bool __is_daemon_in_service(void);
  */
 __s32 fsid_of_op(struct orangefs_kernel_op_s *op);
 
-int orangefs_flush_inode(struct inode *inode);
-
 ssize_t orangefs_inode_getxattr(struct inode *inode,
                             const char *name,
                             void *buffer,
index f82336496311c532c3150f1e85b1caf59de03abb..97fe93129f38872eb40e7828bf6824d3b348dfae 100644 (file)
@@ -4,6 +4,7 @@
  *
  * See COPYING in top-level directory.
  */
+#include <linux/kernel.h>
 #include "protocol.h"
 #include "orangefs-kernel.h"
 #include "orangefs-dev-proto.h"
@@ -437,89 +438,8 @@ int orangefs_inode_setattr(struct inode *inode, struct iattr *iattr)
 
        op_release(new_op);
 
-       /*
-        * successful setattr should clear the atime, mtime and
-        * ctime flags.
-        */
-       if (ret == 0) {
-               ClearAtimeFlag(orangefs_inode);
-               ClearMtimeFlag(orangefs_inode);
-               ClearCtimeFlag(orangefs_inode);
-               ClearModeFlag(orangefs_inode);
+       if (ret == 0)
                orangefs_inode->getattr_time = jiffies - 1;
-       }
-
-       return ret;
-}
-
-int orangefs_flush_inode(struct inode *inode)
-{
-       /*
-        * If it is a dirty inode, this function gets called.
-        * Gather all the information that needs to be setattr'ed
-        * Right now, this will only be used for mode, atime, mtime
-        * and/or ctime.
-        */
-       struct iattr wbattr;
-       int ret;
-       int mtime_flag;
-       int ctime_flag;
-       int atime_flag;
-       int mode_flag;
-       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
-
-       memset(&wbattr, 0, sizeof(wbattr));
-
-       /*
-        * check inode flags up front, and clear them if they are set.  This
-        * will prevent multiple processes from all trying to flush the same
-        * inode if they call close() simultaneously
-        */
-       mtime_flag = MtimeFlag(orangefs_inode);
-       ClearMtimeFlag(orangefs_inode);
-       ctime_flag = CtimeFlag(orangefs_inode);
-       ClearCtimeFlag(orangefs_inode);
-       atime_flag = AtimeFlag(orangefs_inode);
-       ClearAtimeFlag(orangefs_inode);
-       mode_flag = ModeFlag(orangefs_inode);
-       ClearModeFlag(orangefs_inode);
-
-       /*  -- Lazy atime,mtime and ctime update --
-        * Note: all times are dictated by server in the new scheme
-        * and not by the clients
-        *
-        * Also mode updates are being handled now..
-        */
-
-       if (mtime_flag)
-               wbattr.ia_valid |= ATTR_MTIME;
-       if (ctime_flag)
-               wbattr.ia_valid |= ATTR_CTIME;
-       if (atime_flag)
-               wbattr.ia_valid |= ATTR_ATIME;
-
-       if (mode_flag) {
-               wbattr.ia_mode = inode->i_mode;
-               wbattr.ia_valid |= ATTR_MODE;
-       }
-
-       gossip_debug(GOSSIP_UTILS_DEBUG,
-                    "*********** orangefs_flush_inode: %pU "
-                    "(ia_valid %d)\n",
-                    get_khandle_from_ino(inode),
-                    wbattr.ia_valid);
-       if (wbattr.ia_valid == 0) {
-               gossip_debug(GOSSIP_UTILS_DEBUG,
-                            "orangefs_flush_inode skipping setattr()\n");
-               return 0;
-       }
-
-       gossip_debug(GOSSIP_UTILS_DEBUG,
-                    "orangefs_flush_inode (%pU) writing mode %o\n",
-                    get_khandle_from_ino(inode),
-                    inode->i_mode);
-
-       ret = orangefs_inode_setattr(inode, &wbattr);
 
        return ret;
 }
@@ -606,7 +526,7 @@ int orangefs_normalize_to_errno(__s32 error_code)
        /* Convert ORANGEFS encoded errno values into regular errno values. */
        } else if ((-error_code) & ORANGEFS_ERROR_BIT) {
                i = (-error_code) & ~(ORANGEFS_ERROR_BIT|ORANGEFS_ERROR_CLASS_BITS);
-               if (i < sizeof(PINT_errno_mapping)/sizeof(*PINT_errno_mapping))
+               if (i < ARRAY_SIZE(PINT_errno_mapping))
                        error_code = -PINT_errno_mapping[i];
                else
                        error_code = -EINVAL;
index 47ebd9bfd1a1be6c638261c4167d530cd5cb3b2a..366750eef2019201146b5fe3a21f2f3a2bfa4da4 100644 (file)
@@ -99,8 +99,6 @@ static void orangefs_inode_cache_ctor(void *req)
 
        inode_init_once(&orangefs_inode->vfs_inode);
        init_rwsem(&orangefs_inode->xattr_sem);
-
-       orangefs_inode->vfs_inode.i_version = 1;
 }
 
 static struct inode *orangefs_alloc_inode(struct super_block *sb)
@@ -119,7 +117,6 @@ static struct inode *orangefs_alloc_inode(struct super_block *sb)
        orangefs_inode->refn.fs_id = ORANGEFS_FS_ID_NULL;
        orangefs_inode->last_failed_block_index_read = 0;
        memset(orangefs_inode->link_target, 0, sizeof(orangefs_inode->link_target));
-       orangefs_inode->pinode_flags = 0;
 
        gossip_debug(GOSSIP_SUPER_DEBUG,
                     "orangefs_alloc_inode: allocated %p\n",
@@ -299,21 +296,9 @@ void fsid_key_table_finalize(void)
 {
 }
 
-/* Called whenever the VFS dirties the inode in response to atime updates */
-static void orangefs_dirty_inode(struct inode *inode, int flags)
-{
-       struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
-
-       gossip_debug(GOSSIP_SUPER_DEBUG,
-                    "orangefs_dirty_inode: %pU\n",
-                    get_khandle_from_ino(inode));
-       SetAtimeFlag(orangefs_inode);
-}
-
 static const struct super_operations orangefs_s_ops = {
        .alloc_inode = orangefs_alloc_inode,
        .destroy_inode = orangefs_destroy_inode,
-       .dirty_inode = orangefs_dirty_inode,
        .drop_inode = generic_delete_inode,
        .statfs = orangefs_statfs,
        .remount_fs = orangefs_remount_fs,
index d856cdf917634cd97abdec56bd5daab45a6f7d9a..db107fe91ab398462f23622ea5bcf6ddd3151458 100644 (file)
@@ -15,4 +15,5 @@ const struct inode_operations orangefs_symlink_inode_operations = {
        .getattr = orangefs_getattr,
        .listxattr = orangefs_listxattr,
        .permission = orangefs_permission,
+       .update_time = orangefs_update_time,
 };
index e9379e258d6464587bd25b31edaf62856c44b96d..2995a271ec466c54117025cf819ce65931d5166c 100644 (file)
@@ -971,8 +971,8 @@ struct lock_manager {
 struct net;
 void locks_start_grace(struct net *, struct lock_manager *);
 void locks_end_grace(struct lock_manager *);
-int locks_in_grace(struct net *);
-int opens_in_grace(struct net *);
+bool locks_in_grace(struct net *);
+bool opens_in_grace(struct net *);
 
 /* that will die - we need it for nfs_lock_info */
 #include <linux/nfs_fs_i.h>
index dc8b4896b77b090e8329bdee9766033a6a3b95fb..b1b0ca7ccb2bacac5d997f97f86848e928bc9da7 100644 (file)
@@ -54,8 +54,9 @@ enum {
        NETIF_F_GSO_TUNNEL_REMCSUM_BIT, /* ... TUNNEL with TSO & REMCSUM */
        NETIF_F_GSO_SCTP_BIT,           /* ... SCTP fragmentation */
        NETIF_F_GSO_ESP_BIT,            /* ... ESP with TSO */
+       NETIF_F_GSO_UDP_BIT,            /* ... UFO, deprecated except tuntap */
        /**/NETIF_F_GSO_LAST =          /* last bit, see GSO_MASK */
-               NETIF_F_GSO_ESP_BIT,
+               NETIF_F_GSO_UDP_BIT,
 
        NETIF_F_FCOE_CRC_BIT,           /* FCoE CRC32 */
        NETIF_F_SCTP_CRC_BIT,           /* SCTP checksum offload */
@@ -132,6 +133,7 @@ enum {
 #define NETIF_F_GSO_TUNNEL_REMCSUM __NETIF_F(GSO_TUNNEL_REMCSUM)
 #define NETIF_F_GSO_SCTP       __NETIF_F(GSO_SCTP)
 #define NETIF_F_GSO_ESP                __NETIF_F(GSO_ESP)
+#define NETIF_F_GSO_UDP                __NETIF_F(GSO_UDP)
 #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER)
 #define NETIF_F_HW_VLAN_STAG_RX        __NETIF_F(HW_VLAN_STAG_RX)
 #define NETIF_F_HW_VLAN_STAG_TX        __NETIF_F(HW_VLAN_STAG_TX)
index 6b274bfe489f61332ebd503fcd28dedf0f79b42e..ef789e1d679efd349ed0b20c315defac1f1fb27c 100644 (file)
@@ -4140,6 +4140,7 @@ static inline bool net_gso_ok(netdev_features_t features, int gso_type)
        BUILD_BUG_ON(SKB_GSO_TUNNEL_REMCSUM != (NETIF_F_GSO_TUNNEL_REMCSUM >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_SCTP    != (NETIF_F_GSO_SCTP >> NETIF_F_GSO_SHIFT));
        BUILD_BUG_ON(SKB_GSO_ESP != (NETIF_F_GSO_ESP >> NETIF_F_GSO_SHIFT));
+       BUILD_BUG_ON(SKB_GSO_UDP != (NETIF_F_GSO_UDP >> NETIF_F_GSO_SHIFT));
 
        return (features & feature) == feature;
 }
index 609e232c00da824739f461dceb1abc4481404f7c..c308964777eb98cd2edc6c36e1a75d9e3d3861f1 100644 (file)
@@ -70,6 +70,7 @@ struct pci_dev;
  * @NTB_TOPO_SEC:      On secondary side of remote ntb.
  * @NTB_TOPO_B2B_USD:  On primary side of local ntb upstream of remote ntb.
  * @NTB_TOPO_B2B_DSD:  On primary side of local ntb downstream of remote ntb.
+ * @NTB_TOPO_SWITCH:   Connected via a switch which supports ntb.
  */
 enum ntb_topo {
        NTB_TOPO_NONE = -1,
@@ -77,6 +78,7 @@ enum ntb_topo {
        NTB_TOPO_SEC,
        NTB_TOPO_B2B_USD,
        NTB_TOPO_B2B_DSD,
+       NTB_TOPO_SWITCH,
 };
 
 static inline int ntb_topo_is_b2b(enum ntb_topo topo)
@@ -97,6 +99,7 @@ static inline char *ntb_topo_string(enum ntb_topo topo)
        case NTB_TOPO_SEC:      return "NTB_TOPO_SEC";
        case NTB_TOPO_B2B_USD:  return "NTB_TOPO_B2B_USD";
        case NTB_TOPO_B2B_DSD:  return "NTB_TOPO_B2B_DSD";
+       case NTB_TOPO_SWITCH:   return "NTB_TOPO_SWITCH";
        }
        return "NTB_TOPO_INVALID";
 }
@@ -730,7 +733,8 @@ static inline int ntb_link_disable(struct ntb_dev *ntb)
  * Hardware and topology may support a different number of memory windows.
  * Moreover different peer devices can support different number of memory
  * windows. Simply speaking this method returns the number of possible inbound
- * memory windows to share with specified peer device.
+ * memory windows to share with specified peer device. Note: this may return
+ * zero if the link is not up yet.
  *
  * Return: the number of memory windows.
  */
@@ -751,7 +755,7 @@ static inline int ntb_mw_count(struct ntb_dev *ntb, int pidx)
  * Get the alignments of an inbound memory window with specified index.
  * NULL may be given for any output parameter if the value is not needed.
  * The alignment and size parameters may be used for allocation of proper
- * shared memory.
+ * shared memory. Note: this must only be called when the link is up.
  *
  * Return: Zero on success, otherwise a negative error number.
  */
@@ -760,6 +764,9 @@ static inline int ntb_mw_get_align(struct ntb_dev *ntb, int pidx, int widx,
                                   resource_size_t *size_align,
                                   resource_size_t *size_max)
 {
+       if (!(ntb_link_is_up(ntb, NULL, NULL) & (1 << pidx)))
+               return -ENOTCONN;
+
        return ntb->ops->mw_get_align(ntb, pidx, widx, addr_align, size_align,
                                      size_max);
 }
index 905bba92f01598ae535b2d2cd737d1a78e739476..e9b603ee99532266a735555ffb784a939f18f5cf 100644 (file)
@@ -132,10 +132,8 @@ struct va_format {
  */
 #define no_printk(fmt, ...)                            \
 ({                                                     \
-       do {                                            \
-               if (0)                                  \
-                       printk(fmt, ##__VA_ARGS__);     \
-       } while (0);                                    \
+       if (0)                                          \
+               printk(fmt, ##__VA_ARGS__);             \
        0;                                              \
 })
 
index ed06e1c28fc72739774ee0dc83ec001825da0138..bc486ef23f20f91ce3ed183e935399d1e4c55e18 100644 (file)
@@ -568,6 +568,8 @@ enum {
        SKB_GSO_SCTP = 1 << 14,
 
        SKB_GSO_ESP = 1 << 15,
+
+       SKB_GSO_UDP = 1 << 16,
 };
 
 #if BITS_PER_LONG > 32
index 3b9f0d1dbb808587b608c0941edc43e55d002bae..786ae2255f0566bc50b44368303a9daba049678c 100644 (file)
@@ -47,6 +47,7 @@ struct svc_pool {
        struct svc_pool_stats   sp_stats;       /* statistics on pool operation */
 #define        SP_TASK_PENDING         (0)             /* still work to do even if no
                                                 * xprt is queued. */
+#define SP_CONGESTED           (1)
        unsigned long           sp_flags;
 } ____cacheline_aligned_in_smp;
 
diff --git a/include/linux/switchtec.h b/include/linux/switchtec.h
new file mode 100644 (file)
index 0000000..09d73d0
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Microsemi Switchtec PCIe Driver
+ * Copyright (c) 2017, Microsemi Corporation
+ *
+ * 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.
+ *
+ */
+
+#ifndef _SWITCHTEC_H
+#define _SWITCHTEC_H
+
+#include <linux/pci.h>
+#include <linux/cdev.h>
+
+#define MICROSEMI_VENDOR_ID         0x11f8
+#define MICROSEMI_NTB_CLASSCODE     0x068000
+#define MICROSEMI_MGMT_CLASSCODE    0x058000
+
+#define SWITCHTEC_MRPC_PAYLOAD_SIZE 1024
+#define SWITCHTEC_MAX_PFF_CSR 48
+
+#define SWITCHTEC_EVENT_OCCURRED BIT(0)
+#define SWITCHTEC_EVENT_CLEAR    BIT(0)
+#define SWITCHTEC_EVENT_EN_LOG   BIT(1)
+#define SWITCHTEC_EVENT_EN_CLI   BIT(2)
+#define SWITCHTEC_EVENT_EN_IRQ   BIT(3)
+#define SWITCHTEC_EVENT_FATAL    BIT(4)
+
+enum {
+       SWITCHTEC_GAS_MRPC_OFFSET       = 0x0000,
+       SWITCHTEC_GAS_TOP_CFG_OFFSET    = 0x1000,
+       SWITCHTEC_GAS_SW_EVENT_OFFSET   = 0x1800,
+       SWITCHTEC_GAS_SYS_INFO_OFFSET   = 0x2000,
+       SWITCHTEC_GAS_FLASH_INFO_OFFSET = 0x2200,
+       SWITCHTEC_GAS_PART_CFG_OFFSET   = 0x4000,
+       SWITCHTEC_GAS_NTB_OFFSET        = 0x10000,
+       SWITCHTEC_GAS_PFF_CSR_OFFSET    = 0x134000,
+};
+
+struct mrpc_regs {
+       u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
+       u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
+       u32 cmd;
+       u32 status;
+       u32 ret_value;
+} __packed;
+
+enum mrpc_status {
+       SWITCHTEC_MRPC_STATUS_INPROGRESS = 1,
+       SWITCHTEC_MRPC_STATUS_DONE = 2,
+       SWITCHTEC_MRPC_STATUS_ERROR = 0xFF,
+       SWITCHTEC_MRPC_STATUS_INTERRUPTED = 0x100,
+};
+
+struct sw_event_regs {
+       u64 event_report_ctrl;
+       u64 reserved1;
+       u64 part_event_bitmap;
+       u64 reserved2;
+       u32 global_summary;
+       u32 reserved3[3];
+       u32 stack_error_event_hdr;
+       u32 stack_error_event_data;
+       u32 reserved4[4];
+       u32 ppu_error_event_hdr;
+       u32 ppu_error_event_data;
+       u32 reserved5[4];
+       u32 isp_error_event_hdr;
+       u32 isp_error_event_data;
+       u32 reserved6[4];
+       u32 sys_reset_event_hdr;
+       u32 reserved7[5];
+       u32 fw_exception_hdr;
+       u32 reserved8[5];
+       u32 fw_nmi_hdr;
+       u32 reserved9[5];
+       u32 fw_non_fatal_hdr;
+       u32 reserved10[5];
+       u32 fw_fatal_hdr;
+       u32 reserved11[5];
+       u32 twi_mrpc_comp_hdr;
+       u32 twi_mrpc_comp_data;
+       u32 reserved12[4];
+       u32 twi_mrpc_comp_async_hdr;
+       u32 twi_mrpc_comp_async_data;
+       u32 reserved13[4];
+       u32 cli_mrpc_comp_hdr;
+       u32 cli_mrpc_comp_data;
+       u32 reserved14[4];
+       u32 cli_mrpc_comp_async_hdr;
+       u32 cli_mrpc_comp_async_data;
+       u32 reserved15[4];
+       u32 gpio_interrupt_hdr;
+       u32 gpio_interrupt_data;
+       u32 reserved16[4];
+} __packed;
+
+enum {
+       SWITCHTEC_CFG0_RUNNING = 0x04,
+       SWITCHTEC_CFG1_RUNNING = 0x05,
+       SWITCHTEC_IMG0_RUNNING = 0x03,
+       SWITCHTEC_IMG1_RUNNING = 0x07,
+};
+
+struct sys_info_regs {
+       u32 device_id;
+       u32 device_version;
+       u32 firmware_version;
+       u32 reserved1;
+       u32 vendor_table_revision;
+       u32 table_format_version;
+       u32 partition_id;
+       u32 cfg_file_fmt_version;
+       u16 cfg_running;
+       u16 img_running;
+       u32 reserved2[57];
+       char vendor_id[8];
+       char product_id[16];
+       char product_revision[4];
+       char component_vendor[8];
+       u16 component_id;
+       u8 component_revision;
+} __packed;
+
+struct flash_info_regs {
+       u32 flash_part_map_upd_idx;
+
+       struct active_partition_info {
+               u32 address;
+               u32 build_version;
+               u32 build_string;
+       } active_img;
+
+       struct active_partition_info active_cfg;
+       struct active_partition_info inactive_img;
+       struct active_partition_info inactive_cfg;
+
+       u32 flash_length;
+
+       struct partition_info {
+               u32 address;
+               u32 length;
+       } cfg0;
+
+       struct partition_info cfg1;
+       struct partition_info img0;
+       struct partition_info img1;
+       struct partition_info nvlog;
+       struct partition_info vendor[8];
+};
+
+enum {
+       SWITCHTEC_NTB_REG_INFO_OFFSET   = 0x0000,
+       SWITCHTEC_NTB_REG_CTRL_OFFSET   = 0x4000,
+       SWITCHTEC_NTB_REG_DBMSG_OFFSET  = 0x64000,
+};
+
+struct ntb_info_regs {
+       u8  partition_count;
+       u8  partition_id;
+       u16 reserved1;
+       u64 ep_map;
+       u16 requester_id;
+} __packed;
+
+struct part_cfg_regs {
+       u32 status;
+       u32 state;
+       u32 port_cnt;
+       u32 usp_port_mode;
+       u32 usp_pff_inst_id;
+       u32 vep_pff_inst_id;
+       u32 dsp_pff_inst_id[47];
+       u32 reserved1[11];
+       u16 vep_vector_number;
+       u16 usp_vector_number;
+       u32 port_event_bitmap;
+       u32 reserved2[3];
+       u32 part_event_summary;
+       u32 reserved3[3];
+       u32 part_reset_hdr;
+       u32 part_reset_data[5];
+       u32 mrpc_comp_hdr;
+       u32 mrpc_comp_data[5];
+       u32 mrpc_comp_async_hdr;
+       u32 mrpc_comp_async_data[5];
+       u32 dyn_binding_hdr;
+       u32 dyn_binding_data[5];
+       u32 reserved4[159];
+} __packed;
+
+enum {
+       NTB_CTRL_PART_OP_LOCK = 0x1,
+       NTB_CTRL_PART_OP_CFG = 0x2,
+       NTB_CTRL_PART_OP_RESET = 0x3,
+
+       NTB_CTRL_PART_STATUS_NORMAL = 0x1,
+       NTB_CTRL_PART_STATUS_LOCKED = 0x2,
+       NTB_CTRL_PART_STATUS_LOCKING = 0x3,
+       NTB_CTRL_PART_STATUS_CONFIGURING = 0x4,
+       NTB_CTRL_PART_STATUS_RESETTING = 0x5,
+
+       NTB_CTRL_BAR_VALID = 1 << 0,
+       NTB_CTRL_BAR_DIR_WIN_EN = 1 << 4,
+       NTB_CTRL_BAR_LUT_WIN_EN = 1 << 5,
+
+       NTB_CTRL_REQ_ID_EN = 1 << 0,
+
+       NTB_CTRL_LUT_EN = 1 << 0,
+
+       NTB_PART_CTRL_ID_PROT_DIS = 1 << 0,
+};
+
+struct ntb_ctrl_regs {
+       u32 partition_status;
+       u32 partition_op;
+       u32 partition_ctrl;
+       u32 bar_setup;
+       u32 bar_error;
+       u16 lut_table_entries;
+       u16 lut_table_offset;
+       u32 lut_error;
+       u16 req_id_table_size;
+       u16 req_id_table_offset;
+       u32 req_id_error;
+       u32 reserved1[7];
+       struct {
+               u32 ctl;
+               u32 win_size;
+               u64 xlate_addr;
+       } bar_entry[6];
+       u32 reserved2[216];
+       u32 req_id_table[256];
+       u32 reserved3[512];
+       u64 lut_entry[512];
+} __packed;
+
+#define NTB_DBMSG_IMSG_STATUS BIT_ULL(32)
+#define NTB_DBMSG_IMSG_MASK   BIT_ULL(40)
+
+struct ntb_dbmsg_regs {
+       u32 reserved1[1024];
+       u64 odb;
+       u64 odb_mask;
+       u64 idb;
+       u64 idb_mask;
+       u8  idb_vec_map[64];
+       u32 msg_map;
+       u32 reserved2;
+       struct {
+               u32 msg;
+               u32 status;
+       } omsg[4];
+
+       struct {
+               u32 msg;
+               u8  status;
+               u8  mask;
+               u8  src;
+               u8  reserved;
+       } imsg[4];
+
+       u8 reserved3[3928];
+       u8 msix_table[1024];
+       u8 reserved4[3072];
+       u8 pba[24];
+       u8 reserved5[4072];
+} __packed;
+
+enum {
+       SWITCHTEC_PART_CFG_EVENT_RESET = 1 << 0,
+       SWITCHTEC_PART_CFG_EVENT_MRPC_CMP = 1 << 1,
+       SWITCHTEC_PART_CFG_EVENT_MRPC_ASYNC_CMP = 1 << 2,
+       SWITCHTEC_PART_CFG_EVENT_DYN_PART_CMP = 1 << 3,
+};
+
+struct pff_csr_regs {
+       u16 vendor_id;
+       u16 device_id;
+       u32 pci_cfg_header[15];
+       u32 pci_cap_region[48];
+       u32 pcie_cap_region[448];
+       u32 indirect_gas_window[128];
+       u32 indirect_gas_window_off;
+       u32 reserved[127];
+       u32 pff_event_summary;
+       u32 reserved2[3];
+       u32 aer_in_p2p_hdr;
+       u32 aer_in_p2p_data[5];
+       u32 aer_in_vep_hdr;
+       u32 aer_in_vep_data[5];
+       u32 dpc_hdr;
+       u32 dpc_data[5];
+       u32 cts_hdr;
+       u32 cts_data[5];
+       u32 reserved3[6];
+       u32 hotplug_hdr;
+       u32 hotplug_data[5];
+       u32 ier_hdr;
+       u32 ier_data[5];
+       u32 threshold_hdr;
+       u32 threshold_data[5];
+       u32 power_mgmt_hdr;
+       u32 power_mgmt_data[5];
+       u32 tlp_throttling_hdr;
+       u32 tlp_throttling_data[5];
+       u32 force_speed_hdr;
+       u32 force_speed_data[5];
+       u32 credit_timeout_hdr;
+       u32 credit_timeout_data[5];
+       u32 link_state_hdr;
+       u32 link_state_data[5];
+       u32 reserved4[174];
+} __packed;
+
+struct switchtec_ntb;
+
+struct switchtec_dev {
+       struct pci_dev *pdev;
+       struct device dev;
+       struct cdev cdev;
+
+       int partition;
+       int partition_count;
+       int pff_csr_count;
+       char pff_local[SWITCHTEC_MAX_PFF_CSR];
+
+       void __iomem *mmio;
+       struct mrpc_regs __iomem *mmio_mrpc;
+       struct sw_event_regs __iomem *mmio_sw_event;
+       struct sys_info_regs __iomem *mmio_sys_info;
+       struct flash_info_regs __iomem *mmio_flash_info;
+       struct ntb_info_regs __iomem *mmio_ntb;
+       struct part_cfg_regs __iomem *mmio_part_cfg;
+       struct part_cfg_regs __iomem *mmio_part_cfg_all;
+       struct pff_csr_regs __iomem *mmio_pff_csr;
+
+       /*
+        * The mrpc mutex must be held when accessing the other
+        * mrpc_ fields, alive flag and stuser->state field
+        */
+       struct mutex mrpc_mutex;
+       struct list_head mrpc_queue;
+       int mrpc_busy;
+       struct work_struct mrpc_work;
+       struct delayed_work mrpc_timeout;
+       bool alive;
+
+       wait_queue_head_t event_wq;
+       atomic_t event_cnt;
+
+       struct work_struct link_event_work;
+       void (*link_notifier)(struct switchtec_dev *stdev);
+       u8 link_event_count[SWITCHTEC_MAX_PFF_CSR];
+
+       struct switchtec_ntb *sndev;
+};
+
+static inline struct switchtec_dev *to_stdev(struct device *dev)
+{
+       return container_of(dev, struct switchtec_dev, dev);
+}
+
+extern struct class *switchtec_class;
+
+#endif
index 210034c896e31e725c6de6bfb33b0406c3b1927a..f144216febc642fd70512df9dddefe1a7f119478 100644 (file)
@@ -9,7 +9,7 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
                                        const struct virtio_net_hdr *hdr,
                                        bool little_endian)
 {
-       unsigned short gso_type = 0;
+       unsigned int gso_type = 0;
 
        if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
                switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
@@ -19,6 +19,9 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb,
                case VIRTIO_NET_HDR_GSO_TCPV6:
                        gso_type = SKB_GSO_TCPV6;
                        break;
+               case VIRTIO_NET_HDR_GSO_UDP:
+                       gso_type = SKB_GSO_UDP;
+                       break;
                default:
                        return -EINVAL;
                }
index cd0d7734dc49838eef044dc1a3ad2c47286a72ac..4757cb5077e538feb793cb06bb3d7fcb335430b9 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <linux/device.h>
 #include <linux/acpi.h>
+#include <uapi/linux/wmi.h>
 
 struct wmi_device {
        struct device dev;
@@ -26,13 +27,17 @@ struct wmi_device {
        bool setable;
 };
 
+/* evaluate the ACPI method associated with this device */
+extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev,
+                                         u8 instance, u32 method_id,
+                                         const struct acpi_buffer *in,
+                                         struct acpi_buffer *out);
+
 /* Caller must kfree the result. */
 extern union acpi_object *wmidev_block_query(struct wmi_device *wdev,
                                             u8 instance);
 
-/* Gets another device on the same bus.  Caller must put_device the result. */
-extern struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev,
-                                               const char *guid_string);
+extern int set_required_buffer_size(struct wmi_device *wdev, u64 length);
 
 struct wmi_device_id {
        const char *guid_string;
@@ -45,6 +50,8 @@ struct wmi_driver {
        int (*probe)(struct wmi_device *wdev);
        int (*remove)(struct wmi_device *wdev);
        void (*notify)(struct wmi_device *device, union acpi_object *data);
+       long (*filter_callback)(struct wmi_device *wdev, unsigned int cmd,
+                               struct wmi_ioctl_buffer *arg);
 };
 
 extern int __must_check __wmi_driver_register(struct wmi_driver *driver,
index ec14f0d5a3a189f5d461f71e530e425c83d64e0b..f73797e2fa60c51a81c8d7a0e231bd2be0137119 100644 (file)
@@ -767,6 +767,7 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add
 __be32 ipv6_select_ident(struct net *net,
                         const struct in6_addr *daddr,
                         const struct in6_addr *saddr);
+__be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb);
 
 int ip6_dst_hoplimit(struct dst_entry *dst);
 
index ecbdbfe86eb6620350fe5eca369906cd3ea60823..8c153f68509e297225db7614f8e706479476bb9c 100644 (file)
@@ -486,20 +486,22 @@ TRACE_EVENT(svc_recv,
        TP_ARGS(rqst, status),
 
        TP_STRUCT__entry(
-               __field(struct sockaddr *, addr)
                __field(u32, xid)
                __field(int, status)
                __field(unsigned long, flags)
+               __dynamic_array(unsigned char, addr, rqst->rq_addrlen)
        ),
 
        TP_fast_assign(
-               __entry->addr = (struct sockaddr *)&rqst->rq_addr;
                __entry->xid = status > 0 ? be32_to_cpu(rqst->rq_xid) : 0;
                __entry->status = status;
                __entry->flags = rqst->rq_flags;
+               memcpy(__get_dynamic_array(addr),
+                       &rqst->rq_addr, rqst->rq_addrlen);
        ),
 
-       TP_printk("addr=%pIScp xid=0x%08x status=%d flags=%s", __entry->addr,
+       TP_printk("addr=%pIScp xid=0x%08x status=%d flags=%s",
+                       (struct sockaddr *)__get_dynamic_array(addr),
                        __entry->xid, __entry->status,
                        show_rqstp_flags(__entry->flags))
 );
@@ -544,22 +546,23 @@ DECLARE_EVENT_CLASS(svc_rqst_status,
        TP_ARGS(rqst, status),
 
        TP_STRUCT__entry(
-               __field(struct sockaddr *, addr)
                __field(u32, xid)
-               __field(int, dropme)
                __field(int, status)
                __field(unsigned long, flags)
+               __dynamic_array(unsigned char, addr, rqst->rq_addrlen)
        ),
 
        TP_fast_assign(
-               __entry->addr = (struct sockaddr *)&rqst->rq_addr;
                __entry->xid = be32_to_cpu(rqst->rq_xid);
                __entry->status = status;
                __entry->flags = rqst->rq_flags;
+               memcpy(__get_dynamic_array(addr),
+                       &rqst->rq_addr, rqst->rq_addrlen);
        ),
 
        TP_printk("addr=%pIScp rq_xid=0x%08x status=%d flags=%s",
-               __entry->addr, __entry->xid,
+               (struct sockaddr *)__get_dynamic_array(addr),
+               __entry->xid,
                __entry->status, show_rqstp_flags(__entry->flags))
 );
 
diff --git a/include/uapi/linux/wmi.h b/include/uapi/linux/wmi.h
new file mode 100644 (file)
index 0000000..7a92e9e
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ *  User API methods for ACPI-WMI mapping driver
+ *
+ *  Copyright (C) 2017 Dell, 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.
+ */
+#ifndef _UAPI_LINUX_WMI_H
+#define _UAPI_LINUX_WMI_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/* WMI bus will filter all WMI vendor driver requests through this IOC */
+#define WMI_IOC 'W'
+
+/* All ioctl requests through WMI should declare their size followed by
+ * relevant data objects
+ */
+struct wmi_ioctl_buffer {
+       __u64   length;
+       __u8    data[];
+};
+
+/* This structure may be modified by the firmware when we enter
+ * system management mode through SMM, hence the volatiles
+ */
+struct calling_interface_buffer {
+       __u16 cmd_class;
+       __u16 cmd_select;
+       volatile __u32 input[4];
+       volatile __u32 output[4];
+} __packed;
+
+struct dell_wmi_extensions {
+       __u32 argattrib;
+       __u32 blength;
+       __u8 data[];
+} __packed;
+
+struct dell_wmi_smbios_buffer {
+       __u64 length;
+       struct calling_interface_buffer std;
+       struct dell_wmi_extensions      ext;
+} __packed;
+
+/* Whitelisted smbios class/select commands */
+#define CLASS_TOKEN_READ       0
+#define CLASS_TOKEN_WRITE      1
+#define SELECT_TOKEN_STD       0
+#define SELECT_TOKEN_BAT       1
+#define SELECT_TOKEN_AC                2
+#define CLASS_FLASH_INTERFACE  7
+#define SELECT_FLASH_INTERFACE 3
+#define CLASS_ADMIN_PROP       10
+#define SELECT_ADMIN_PROP      3
+#define CLASS_INFO             17
+#define SELECT_RFKILL          11
+#define SELECT_APP_REGISTRATION        3
+#define SELECT_DOCK            22
+
+/* whitelisted tokens */
+#define CAPSULE_EN_TOKEN       0x0461
+#define CAPSULE_DIS_TOKEN      0x0462
+#define WSMT_EN_TOKEN          0x04EC
+#define WSMT_DIS_TOKEN         0x04ED
+
+/* Dell SMBIOS calling IOCTL command used by dell-smbios-wmi */
+#define DELL_WMI_SMBIOS_CMD    _IOWR(WMI_IOC, 0, struct dell_wmi_smbios_buffer)
+
+#endif
diff --git a/include/video/iga.h b/include/video/iga.h
deleted file mode 100644 (file)
index 83ca184..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* $Id: iga.h,v 1.2 1999/09/11 22:56:31 zaitcev Exp $
- * iga1682.h: Sparc/PCI iga1682 driver constants etc.
- *
- * Copyleft 1998 V. Roganov and G. Raiko
- */
-
-#ifndef _IGA1682_H
-#define _IGA1682_H 1
-
-#define IGA_ATTR_CTL                   0x3C0
-#define   IGA_IDX_VGA_OVERSCAN         0x11
-#define DAC_W_INDEX                     0x03C8
-#define DAC_DATA                        0x03C9
-#define IGA_EXT_CNTRL                   0x3CE
-#define   IGA_IDX_EXT_BUS_CNTL          0x30
-#define     MEM_SIZE_ALIAS              0x3
-#define     MEM_SIZE_1M                 0x0
-#define     MEM_SIZE_2M                 0x1
-#define     MEM_SIZE_4M                 0x2
-#define     MEM_SIZE_RESERVED           0x3
-#define   IGA_IDX_OVERSCAN_COLOR        0x58
-#define   IGA_IDX_EXT_MEM_2             0x72
-
-#endif /* !(_IGA1682_H) */
index 512f7c2baedd59bef5300e361c22e92b7a4845a0..5d81206a572d721e7d96b129f160a4e16d2e2f2e 100644 (file)
@@ -2190,7 +2190,7 @@ again:
                }
 
                if (console_seq < log_first_seq) {
-                       len = sprintf(text, "** %u printk messages dropped ** ",
+                       len = sprintf(text, "** %u printk messages dropped **\n",
                                      (unsigned)(log_first_seq - console_seq));
 
                        /* messages are gone, move to first one */
index 724d9292d4b9614e62b78e7e0c27e0e8fb0dd49c..3e3c2004bb232661bae666e00e4645fad438aee6 100644 (file)
@@ -72,7 +72,7 @@ static void queue_flush_work(struct printk_safe_seq_buf *s)
  * have dedicated buffers, because otherwise printk-safe preempted by
  * NMI-printk would have overwritten the NMI messages.
  *
- * The messages are fushed from irq work (or from panic()), possibly,
+ * The messages are flushed from irq work (or from panic()), possibly,
  * from other CPU, concurrently with printk_safe_log_store(). Should this
  * happen, printk_safe_log_store() will notice the buffer->len mismatch
  * and repeat the write.
index 67bb1f11e613ca4bcd4f635e096c00c9a61804a5..9a5850f264ed2fa1396fbb35db002e9e50309dbd 100644 (file)
@@ -47,28 +47,38 @@ unsigned int ceph_str_hash_rjenkins(const char *str, unsigned int length)
 
        /* handle the last 11 bytes */
        c = c + length;
-       switch (len) {            /* all the case statements fall through */
+       switch (len) {
        case 11:
                c = c + ((__u32)k[10] << 24);
+               /* fall through */
        case 10:
                c = c + ((__u32)k[9] << 16);
+               /* fall through */
        case 9:
                c = c + ((__u32)k[8] << 8);
                /* the first byte of c is reserved for the length */
+               /* fall through */
        case 8:
                b = b + ((__u32)k[7] << 24);
+               /* fall through */
        case 7:
                b = b + ((__u32)k[6] << 16);
+               /* fall through */
        case 6:
                b = b + ((__u32)k[5] << 8);
+               /* fall through */
        case 5:
                b = b + k[4];
+               /* fall through */
        case 4:
                a = a + ((__u32)k[3] << 24);
+               /* fall through */
        case 3:
                a = a + ((__u32)k[2] << 16);
+               /* fall through */
        case 2:
                a = a + ((__u32)k[1] << 8);
+               /* fall through */
        case 1:
                a = a + k[0];
                /* case 0: nothing left to add */
index 489610ac1cddad2e284515def1bb2185f620d3aa..bf9d079cbafd6e89d56ef5f10d81727bd5cbd42b 100644 (file)
@@ -37,7 +37,9 @@ static int set_secret(struct ceph_crypto_key *key, void *buf)
                return -ENOTSUPP;
        }
 
-       WARN_ON(!key->len);
+       if (!key->len)
+               return -EINVAL;
+
        key->key = kmemdup(buf, key->len, GFP_NOIO);
        if (!key->key) {
                ret = -ENOMEM;
index ad93342c90d72cad59d4797608940b293f2a71fa..8a4d3758030b73d3b9ca24b09f91b1e65be0844e 100644 (file)
@@ -430,6 +430,7 @@ static void ceph_sock_state_change(struct sock *sk)
        switch (sk->sk_state) {
        case TCP_CLOSE:
                dout("%s TCP_CLOSE\n", __func__);
+               /* fall through */
        case TCP_CLOSE_WAIT:
                dout("%s TCP_CLOSE_WAIT\n", __func__);
                con_sock_state_closing(con);
index 9ae1bab8c05db7005d5c345dcb62f6e4b9e145fb..1547107f48544e9690319e4fe9415968448a9f87 100644 (file)
@@ -1279,9 +1279,10 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con,
 
                /*
                 * Older OSDs don't set reply tid even if the orignal
-                * request had a non-zero tid.  Workaround this weirdness
-                * by falling through to the allocate case.
+                * request had a non-zero tid.  Work around this weirdness
+                * by allocating a new message.
                 */
+               /* fall through */
        case CEPH_MSG_MON_MAP:
        case CEPH_MSG_MDS_MAP:
        case CEPH_MSG_OSD_MAP:
index 5e2ba133fba7e0af87e5b14485ad6cc7613db00c..07ed21d64f92b39da9b683aa432efde6a14afdf0 100644 (file)
@@ -2746,7 +2746,8 @@ EXPORT_SYMBOL(skb_mac_gso_segment);
 static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path)
 {
        if (tx_path)
-               return skb->ip_summed != CHECKSUM_PARTIAL;
+               return skb->ip_summed != CHECKSUM_PARTIAL &&
+                      skb->ip_summed != CHECKSUM_UNNECESSARY;
 
        return skb->ip_summed == CHECKSUM_NONE;
 }
index ce4aa827be059fe83ec36f3fe1c31f2068a95d8b..f00499a469271fb2165c8ca25fe3b4538e45a01e 100644 (file)
@@ -1223,9 +1223,10 @@ EXPORT_SYMBOL(inet_sk_rebuild_header);
 struct sk_buff *inet_gso_segment(struct sk_buff *skb,
                                 netdev_features_t features)
 {
-       bool fixedid = false, gso_partial, encap;
+       bool udpfrag = false, fixedid = false, gso_partial, encap;
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        const struct net_offload *ops;
+       unsigned int offset = 0;
        struct iphdr *iph;
        int proto, tot_len;
        int nhoff;
@@ -1260,6 +1261,7 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb,
        segs = ERR_PTR(-EPROTONOSUPPORT);
 
        if (!skb->encapsulation || encap) {
+               udpfrag = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP);
                fixedid = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TCP_FIXEDID);
 
                /* fixed ID is invalid if DF bit is not set */
@@ -1279,7 +1281,13 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb,
        skb = segs;
        do {
                iph = (struct iphdr *)(skb_mac_header(skb) + nhoff);
-               if (skb_is_gso(skb)) {
+               if (udpfrag) {
+                       iph->frag_off = htons(offset >> 3);
+                       if (skb->next)
+                               iph->frag_off |= htons(IP_MF);
+                       offset += skb->len - nhoff - ihl;
+                       tot_len = skb->len - nhoff;
+               } else if (skb_is_gso(skb)) {
                        if (!fixedid) {
                                iph->id = htons(id);
                                id += skb_shinfo(skb)->gso_segs;
index e360d55be5554d1bee56d3f493752ba9ae2c8015..01801b77bd0da45764fd0e9a80f22b0e46633934 100644 (file)
@@ -187,16 +187,57 @@ out_unlock:
 }
 EXPORT_SYMBOL(skb_udp_tunnel_segment);
 
-static struct sk_buff *udp4_tunnel_segment(struct sk_buff *skb,
-                                          netdev_features_t features)
+static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
+                                        netdev_features_t features)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
+       unsigned int mss;
+       __wsum csum;
+       struct udphdr *uh;
+       struct iphdr *iph;
 
        if (skb->encapsulation &&
            (skb_shinfo(skb)->gso_type &
-            (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM)))
+            (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) {
                segs = skb_udp_tunnel_segment(skb, features, false);
+               goto out;
+       }
+
+       if (!pskb_may_pull(skb, sizeof(struct udphdr)))
+               goto out;
+
+       mss = skb_shinfo(skb)->gso_size;
+       if (unlikely(skb->len <= mss))
+               goto out;
+
+       /* Do software UFO. Complete and fill in the UDP checksum as
+        * HW cannot do checksum of UDP packets sent as multiple
+        * IP fragments.
+        */
 
+       uh = udp_hdr(skb);
+       iph = ip_hdr(skb);
+
+       uh->check = 0;
+       csum = skb_checksum(skb, 0, skb->len, 0);
+       uh->check = udp_v4_check(skb->len, iph->saddr, iph->daddr, csum);
+       if (uh->check == 0)
+               uh->check = CSUM_MANGLED_0;
+
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       /* If there is no outer header we can fake a checksum offload
+        * due to the fact that we have already done the checksum in
+        * software prior to segmenting the frame.
+        */
+       if (!skb->encap_hdr_csum)
+               features |= NETIF_F_HW_CSUM;
+
+       /* Fragment the skb. IP headers of the fragments are updated in
+        * inet_gso_segment()
+        */
+       segs = skb_segment(skb, features);
+out:
        return segs;
 }
 
@@ -330,7 +371,7 @@ static int udp4_gro_complete(struct sk_buff *skb, int nhoff)
 
 static const struct net_offload udpv4_offload = {
        .callbacks = {
-               .gso_segment = udp4_tunnel_segment,
+               .gso_segment = udp4_ufo_fragment,
                .gro_receive  = udp4_gro_receive,
                .gro_complete = udp4_gro_complete,
        },
index 4a7e5ffa51083112fa3927cfe0c5f7d36cd60235..4fe7c90962ddae3356200376aa911bab6d75bb48 100644 (file)
@@ -31,6 +31,37 @@ static u32 __ipv6_select_ident(struct net *net, u32 hashrnd,
        return id;
 }
 
+/* This function exists only for tap drivers that must support broken
+ * clients requesting UFO without specifying an IPv6 fragment ID.
+ *
+ * This is similar to ipv6_select_ident() but we use an independent hash
+ * seed to limit information leakage.
+ *
+ * The network header must be set before calling this.
+ */
+__be32 ipv6_proxy_select_ident(struct net *net, struct sk_buff *skb)
+{
+       static u32 ip6_proxy_idents_hashrnd __read_mostly;
+       struct in6_addr buf[2];
+       struct in6_addr *addrs;
+       u32 id;
+
+       addrs = skb_header_pointer(skb,
+                                  skb_network_offset(skb) +
+                                  offsetof(struct ipv6hdr, saddr),
+                                  sizeof(buf), buf);
+       if (!addrs)
+               return 0;
+
+       net_get_random_once(&ip6_proxy_idents_hashrnd,
+                           sizeof(ip6_proxy_idents_hashrnd));
+
+       id = __ipv6_select_ident(net, ip6_proxy_idents_hashrnd,
+                                &addrs[1], &addrs[0]);
+       return htonl(id);
+}
+EXPORT_SYMBOL_GPL(ipv6_proxy_select_ident);
+
 __be32 ipv6_select_ident(struct net *net,
                         const struct in6_addr *daddr,
                         const struct in6_addr *saddr)
index 05eb7bc36156a3e571f728ad33d61b1a41a81a1c..7a8d1500d374b4089e623ed2b20d68110cff498e 100644 (file)
@@ -472,6 +472,11 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
                                &match->rt6i_siblings, rt6i_siblings) {
                        route_choosen--;
                        if (route_choosen == 0) {
+                               struct inet6_dev *idev = sibling->rt6i_idev;
+
+                               if (!netif_carrier_ok(sibling->dst.dev) &&
+                                   idev->cnf.ignore_routes_with_linkdown)
+                                       break;
                                if (rt6_score_route(sibling, oif, strict) < 0)
                                        break;
                                match = sibling;
@@ -1019,7 +1024,7 @@ static struct net_device *ip6_rt_get_dev_rcu(struct rt6_info *rt)
 {
        struct net_device *dev = rt->dst.dev;
 
-       if (rt->rt6i_flags & RTF_LOCAL) {
+       if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) {
                /* for copies of local routes, dst->dev needs to be the
                 * device if it is a master device, the master device if
                 * device is enslaved, and the loopback as the default
index 455fd4e39333233289e9a844de512f200119ff1a..a0f89ad76f9d2233b9e048418069aacd92ac6a25 100644 (file)
 #include <net/ip6_checksum.h>
 #include "ip6_offload.h"
 
-static struct sk_buff *udp6_tunnel_segment(struct sk_buff *skb,
-                                          netdev_features_t features)
+static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
+                                        netdev_features_t features)
 {
        struct sk_buff *segs = ERR_PTR(-EINVAL);
+       unsigned int mss;
+       unsigned int unfrag_ip6hlen, unfrag_len;
+       struct frag_hdr *fptr;
+       u8 *packet_start, *prevhdr;
+       u8 nexthdr;
+       u8 frag_hdr_sz = sizeof(struct frag_hdr);
+       __wsum csum;
+       int tnl_hlen;
+       int err;
+
+       mss = skb_shinfo(skb)->gso_size;
+       if (unlikely(skb->len <= mss))
+               goto out;
 
        if (skb->encapsulation && skb_shinfo(skb)->gso_type &
            (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))
                segs = skb_udp_tunnel_segment(skb, features, true);
+       else {
+               const struct ipv6hdr *ipv6h;
+               struct udphdr *uh;
+
+               if (!pskb_may_pull(skb, sizeof(struct udphdr)))
+                       goto out;
+
+               /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
+                * do checksum of UDP packets sent as multiple IP fragments.
+                */
+
+               uh = udp_hdr(skb);
+               ipv6h = ipv6_hdr(skb);
+
+               uh->check = 0;
+               csum = skb_checksum(skb, 0, skb->len, 0);
+               uh->check = udp_v6_check(skb->len, &ipv6h->saddr,
+                                         &ipv6h->daddr, csum);
+               if (uh->check == 0)
+                       uh->check = CSUM_MANGLED_0;
+
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+               /* If there is no outer header we can fake a checksum offload
+                * due to the fact that we have already done the checksum in
+                * software prior to segmenting the frame.
+                */
+               if (!skb->encap_hdr_csum)
+                       features |= NETIF_F_HW_CSUM;
+
+               /* Check if there is enough headroom to insert fragment header. */
+               tnl_hlen = skb_tnl_header_len(skb);
+               if (skb->mac_header < (tnl_hlen + frag_hdr_sz)) {
+                       if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz))
+                               goto out;
+               }
+
+               /* Find the unfragmentable header and shift it left by frag_hdr_sz
+                * bytes to insert fragment header.
+                */
+               err = ip6_find_1stfragopt(skb, &prevhdr);
+               if (err < 0)
+                       return ERR_PTR(err);
+               unfrag_ip6hlen = err;
+               nexthdr = *prevhdr;
+               *prevhdr = NEXTHDR_FRAGMENT;
+               unfrag_len = (skb_network_header(skb) - skb_mac_header(skb)) +
+                            unfrag_ip6hlen + tnl_hlen;
+               packet_start = (u8 *) skb->head + SKB_GSO_CB(skb)->mac_offset;
+               memmove(packet_start-frag_hdr_sz, packet_start, unfrag_len);
+
+               SKB_GSO_CB(skb)->mac_offset -= frag_hdr_sz;
+               skb->mac_header -= frag_hdr_sz;
+               skb->network_header -= frag_hdr_sz;
+
+               fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
+               fptr->nexthdr = nexthdr;
+               fptr->reserved = 0;
+               fptr->identification = ipv6_proxy_select_ident(dev_net(skb->dev), skb);
+
+               /* Fragment the skb. ipv6 header and the remaining fields of the
+                * fragment header are updated in ipv6_gso_segment()
+                */
+               segs = skb_segment(skb, features);
+       }
 
+out:
        return segs;
 }
 
@@ -75,7 +154,7 @@ static int udp6_gro_complete(struct sk_buff *skb, int nhoff)
 
 static const struct net_offload udpv6_offload = {
        .callbacks = {
-               .gso_segment    =       udp6_tunnel_segment,
+               .gso_segment    =       udp6_ufo_fragment,
                .gro_receive    =       udp6_gro_receive,
                .gro_complete   =       udp6_gro_complete,
        },
index 88cc1ae935ead5f1a2b4a46d2d3af3fbf4cefd8d..d444752dbf40789cfb0d93d3d572b6bd04263671 100644 (file)
@@ -151,21 +151,17 @@ EXPORT_SYMBOL(ieee80211_stop_rx_ba_session);
  * After accepting the AddBA Request we activated a timer,
  * resetting it after each frame that arrives from the originator.
  */
-static void sta_rx_agg_session_timer_expired(unsigned long data)
+static void sta_rx_agg_session_timer_expired(struct timer_list *t)
 {
-       /* not an elegant detour, but there is no choice as the timer passes
-        * only one argument, and various sta_info are needed here, so init
-        * flow in sta_info_create gives the TID as data, while the timer_to_id
-        * array gives the sta through container_of */
-       u8 *ptid = (u8 *)data;
-       u8 *timer_to_id = ptid - *ptid;
-       struct sta_info *sta = container_of(timer_to_id, struct sta_info,
-                                        timer_to_tid[0]);
+       struct tid_ampdu_rx *tid_rx_timer =
+               from_timer(tid_rx_timer, t, session_timer);
+       struct sta_info *sta = tid_rx_timer->sta;
+       u8 tid = tid_rx_timer->tid;
        struct tid_ampdu_rx *tid_rx;
        unsigned long timeout;
 
        rcu_read_lock();
-       tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[*ptid]);
+       tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
        if (!tid_rx) {
                rcu_read_unlock();
                return;
@@ -180,21 +176,18 @@ static void sta_rx_agg_session_timer_expired(unsigned long data)
        rcu_read_unlock();
 
        ht_dbg(sta->sdata, "RX session timer expired on %pM tid %d\n",
-              sta->sta.addr, (u16)*ptid);
+              sta->sta.addr, tid);
 
-       set_bit(*ptid, sta->ampdu_mlme.tid_rx_timer_expired);
+       set_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired);
        ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
 }
 
-static void sta_rx_agg_reorder_timer_expired(unsigned long data)
+static void sta_rx_agg_reorder_timer_expired(struct timer_list *t)
 {
-       u8 *ptid = (u8 *)data;
-       u8 *timer_to_id = ptid - *ptid;
-       struct sta_info *sta = container_of(timer_to_id, struct sta_info,
-                       timer_to_tid[0]);
+       struct tid_ampdu_rx *tid_rx = from_timer(tid_rx, t, reorder_timer);
 
        rcu_read_lock();
-       ieee80211_release_reorder_timeout(sta, *ptid);
+       ieee80211_release_reorder_timeout(tid_rx->sta, tid_rx->tid);
        rcu_read_unlock();
 }
 
@@ -356,14 +349,12 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
        spin_lock_init(&tid_agg_rx->reorder_lock);
 
        /* rx timer */
-       setup_deferrable_timer(&tid_agg_rx->session_timer,
-                              sta_rx_agg_session_timer_expired,
-                              (unsigned long)&sta->timer_to_tid[tid]);
+       timer_setup(&tid_agg_rx->session_timer,
+                   sta_rx_agg_session_timer_expired, TIMER_DEFERRABLE);
 
        /* rx reorder timer */
-       setup_timer(&tid_agg_rx->reorder_timer,
-                   sta_rx_agg_reorder_timer_expired,
-                   (unsigned long)&sta->timer_to_tid[tid]);
+       timer_setup(&tid_agg_rx->reorder_timer,
+                   sta_rx_agg_reorder_timer_expired, 0);
 
        /* prepare reordering buffer */
        tid_agg_rx->reorder_buf =
@@ -399,6 +390,8 @@ void ___ieee80211_start_rx_ba_session(struct sta_info *sta,
        tid_agg_rx->auto_seq = auto_seq;
        tid_agg_rx->started = false;
        tid_agg_rx->reorder_buf_filtered = 0;
+       tid_agg_rx->tid = tid;
+       tid_agg_rx->sta = sta;
        status = WLAN_STATUS_SUCCESS;
 
        /* activate it for RX */
index bef516ec47f94c19f57da37d80c744bb534deeb4..5f8ab5be369fe9705744473b5dc5928a643d50d1 100644 (file)
@@ -330,6 +330,11 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
 
        spin_lock_bh(&sta->lock);
 
+       /* free struct pending for start, if present */
+       tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
+       kfree(tid_tx);
+       sta->ampdu_mlme.tid_start_tx[tid] = NULL;
+
        tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
        if (!tid_tx) {
                spin_unlock_bh(&sta->lock);
@@ -422,15 +427,12 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
  * add Block Ack response will arrive from the recipient.
  * If this timer expires sta_addba_resp_timer_expired will be executed.
  */
-static void sta_addba_resp_timer_expired(unsigned long data)
+static void sta_addba_resp_timer_expired(struct timer_list *t)
 {
-       /* not an elegant detour, but there is no choice as the timer passes
-        * only one argument, and both sta_info and TID are needed, so init
-        * flow in sta_info_create gives the TID as data, while the timer_to_id
-        * array gives the sta through container_of */
-       u16 tid = *(u8 *)data;
-       struct sta_info *sta = container_of((void *)data,
-               struct sta_info, timer_to_tid[tid]);
+       struct tid_ampdu_tx *tid_tx_timer =
+               from_timer(tid_tx_timer, t, addba_resp_timer);
+       struct sta_info *sta = tid_tx_timer->sta;
+       u8 tid = tid_tx_timer->tid;
        struct tid_ampdu_tx *tid_tx;
 
        /* check if the TID waits for addBA response */
@@ -525,21 +527,17 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
  * After accepting the AddBA Response we activated a timer,
  * resetting it after each frame that we send.
  */
-static void sta_tx_agg_session_timer_expired(unsigned long data)
+static void sta_tx_agg_session_timer_expired(struct timer_list *t)
 {
-       /* not an elegant detour, but there is no choice as the timer passes
-        * only one argument, and various sta_info are needed here, so init
-        * flow in sta_info_create gives the TID as data, while the timer_to_id
-        * array gives the sta through container_of */
-       u8 *ptid = (u8 *)data;
-       u8 *timer_to_id = ptid - *ptid;
-       struct sta_info *sta = container_of(timer_to_id, struct sta_info,
-                                        timer_to_tid[0]);
+       struct tid_ampdu_tx *tid_tx_timer =
+               from_timer(tid_tx_timer, t, session_timer);
+       struct sta_info *sta = tid_tx_timer->sta;
+       u8 tid = tid_tx_timer->tid;
        struct tid_ampdu_tx *tid_tx;
        unsigned long timeout;
 
        rcu_read_lock();
-       tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[*ptid]);
+       tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
        if (!tid_tx || test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
                rcu_read_unlock();
                return;
@@ -555,9 +553,9 @@ static void sta_tx_agg_session_timer_expired(unsigned long data)
        rcu_read_unlock();
 
        ht_dbg(sta->sdata, "tx session timer expired on %pM tid %d\n",
-              sta->sta.addr, (u16)*ptid);
+              sta->sta.addr, tid);
 
-       ieee80211_stop_tx_ba_session(&sta->sta, *ptid);
+       ieee80211_stop_tx_ba_session(&sta->sta, tid);
 }
 
 int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
@@ -670,16 +668,15 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
        __set_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
 
        tid_tx->timeout = timeout;
+       tid_tx->sta = sta;
+       tid_tx->tid = tid;
 
        /* response timer */
-       setup_timer(&tid_tx->addba_resp_timer,
-                   sta_addba_resp_timer_expired,
-                   (unsigned long)&sta->timer_to_tid[tid]);
+       timer_setup(&tid_tx->addba_resp_timer, sta_addba_resp_timer_expired, 0);
 
        /* tx timer */
-       setup_deferrable_timer(&tid_tx->session_timer,
-                              sta_tx_agg_session_timer_expired,
-                              (unsigned long)&sta->timer_to_tid[tid]);
+       timer_setup(&tid_tx->session_timer,
+                   sta_tx_agg_session_timer_expired, TIMER_DEFERRABLE);
 
        /* assign a dialog token */
        sta->ampdu_mlme.dialog_token_allocator++;
index e9c6aa3ed05b8ddb8cf03decce82c00e59b4c0a7..db07e0de9a0374229857f5a2fb4e081928ea936f 100644 (file)
@@ -1711,10 +1711,10 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
        sdata_unlock(sdata);
 }
 
-static void ieee80211_ibss_timer(unsigned long data)
+static void ieee80211_ibss_timer(struct timer_list *t)
 {
        struct ieee80211_sub_if_data *sdata =
-               (struct ieee80211_sub_if_data *) data;
+               from_timer(sdata, t, u.ibss.timer);
 
        ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
@@ -1723,8 +1723,7 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
 
-       setup_timer(&ifibss->timer, ieee80211_ibss_timer,
-                   (unsigned long) sdata);
+       timer_setup(&ifibss->timer, ieee80211_ibss_timer, 0);
        INIT_LIST_HEAD(&ifibss->incomplete_stations);
        spin_lock_init(&ifibss->incomplete_lock);
        INIT_WORK(&ifibss->csa_connection_drop_work,
index 68f874e73561e8fe4d1a2ba379b26205e9c2cdb4..885d00b419119a2bef3fd0b7c3f00f92c2a968ad 100644 (file)
@@ -1057,6 +1057,7 @@ struct tpt_led_trigger {
        const struct ieee80211_tpt_blink *blink_table;
        unsigned int blink_table_len;
        struct timer_list timer;
+       struct ieee80211_local *local;
        unsigned long prev_traffic;
        unsigned long tx_bytes, rx_bytes;
        unsigned int active, want;
@@ -1932,7 +1933,7 @@ static inline int ieee80211_ac_from_tid(int tid)
 
 void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
 void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
-void ieee80211_dynamic_ps_timer(unsigned long data);
+void ieee80211_dynamic_ps_timer(struct timer_list *t);
 void ieee80211_send_nullfunc(struct ieee80211_local *local,
                             struct ieee80211_sub_if_data *sdata,
                             bool powersave);
index 0505845b7ab836c15888a06b3b7d76995d46e685..ba0b507ea6910f2d645f1fecfcb5d09877e2340c 100644 (file)
@@ -248,10 +248,10 @@ static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
        return DIV_ROUND_UP(delta, 1024 / 8);
 }
 
-static void tpt_trig_timer(unsigned long data)
+static void tpt_trig_timer(struct timer_list *t)
 {
-       struct ieee80211_local *local = (void *)data;
-       struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+       struct tpt_led_trigger *tpt_trig = from_timer(tpt_trig, t, timer);
+       struct ieee80211_local *local = tpt_trig->local;
        struct led_classdev *led_cdev;
        unsigned long on, off, tpt;
        int i;
@@ -306,8 +306,9 @@ __ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
        tpt_trig->blink_table = blink_table;
        tpt_trig->blink_table_len = blink_table_len;
        tpt_trig->want = flags;
+       tpt_trig->local = local;
 
-       setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
+       timer_setup(&tpt_trig->timer, tpt_trig_timer, 0);
 
        local->tpt_led_trigger = tpt_trig;
 
@@ -326,7 +327,7 @@ static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
        tpt_trig_traffic(local, tpt_trig);
        tpt_trig->running = true;
 
-       tpt_trig_timer((unsigned long)local);
+       tpt_trig_timer(&tpt_trig->timer);
        mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
 }
 
index 8aa1f5b6a05145b0838b494abdf2b2c5b0aa17ab..e054a2fd8d38bd00c0e181ca01180b19c3ffb132 100644 (file)
@@ -633,8 +633,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
                  ieee80211_dynamic_ps_enable_work);
        INIT_WORK(&local->dynamic_ps_disable_work,
                  ieee80211_dynamic_ps_disable_work);
-       setup_timer(&local->dynamic_ps_timer,
-                   ieee80211_dynamic_ps_timer, (unsigned long) local);
+       timer_setup(&local->dynamic_ps_timer, ieee80211_dynamic_ps_timer, 0);
 
        INIT_WORK(&local->sched_scan_stopped_work,
                  ieee80211_sched_scan_stopped_work);
index 7a76c4a6df306574f8189f132c148295ac14119e..5e27364e10acf3420e8f192b09488ab4bc252d09 100644 (file)
@@ -37,9 +37,10 @@ void ieee80211s_stop(void)
        kmem_cache_destroy(rm_cache);
 }
 
-static void ieee80211_mesh_housekeeping_timer(unsigned long data)
+static void ieee80211_mesh_housekeeping_timer(struct timer_list *t)
 {
-       struct ieee80211_sub_if_data *sdata = (void *) data;
+       struct ieee80211_sub_if_data *sdata =
+               from_timer(sdata, t, u.mesh.housekeeping_timer);
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 
@@ -528,18 +529,18 @@ int mesh_add_vht_oper_ie(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-static void ieee80211_mesh_path_timer(unsigned long data)
+static void ieee80211_mesh_path_timer(struct timer_list *t)
 {
        struct ieee80211_sub_if_data *sdata =
-               (struct ieee80211_sub_if_data *) data;
+               from_timer(sdata, t, u.mesh.mesh_path_timer);
 
        ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
 
-static void ieee80211_mesh_path_root_timer(unsigned long data)
+static void ieee80211_mesh_path_root_timer(struct timer_list *t)
 {
        struct ieee80211_sub_if_data *sdata =
-               (struct ieee80211_sub_if_data *) data;
+               from_timer(sdata, t, u.mesh.mesh_path_root_timer);
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
 
        set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
@@ -1442,9 +1443,8 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
        static u8 zero_addr[ETH_ALEN] = {};
 
-       setup_timer(&ifmsh->housekeeping_timer,
-                   ieee80211_mesh_housekeeping_timer,
-                   (unsigned long) sdata);
+       timer_setup(&ifmsh->housekeeping_timer,
+                   ieee80211_mesh_housekeeping_timer, 0);
 
        ifmsh->accepting_plinks = true;
        atomic_set(&ifmsh->mpaths, 0);
@@ -1458,12 +1458,9 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
 
        mesh_pathtbl_init(sdata);
 
-       setup_timer(&ifmsh->mesh_path_timer,
-                   ieee80211_mesh_path_timer,
-                   (unsigned long) sdata);
-       setup_timer(&ifmsh->mesh_path_root_timer,
-                   ieee80211_mesh_path_root_timer,
-                   (unsigned long) sdata);
+       timer_setup(&ifmsh->mesh_path_timer, ieee80211_mesh_path_timer, 0);
+       timer_setup(&ifmsh->mesh_path_root_timer,
+                   ieee80211_mesh_path_root_timer, 0);
        INIT_LIST_HEAD(&ifmsh->preq_queue.list);
        skb_queue_head_init(&ifmsh->ps.bc_buf);
        spin_lock_init(&ifmsh->mesh_preq_queue_lock);
index 465b7853edc0b1c85a2145f2b248c81f86fb92eb..ee56f18cad3f7e89e1c60fe4829dab7bfa1ef340 100644 (file)
@@ -296,7 +296,7 @@ void mesh_path_tx_pending(struct mesh_path *mpath);
 int mesh_pathtbl_init(struct ieee80211_sub_if_data *sdata);
 void mesh_pathtbl_unregister(struct ieee80211_sub_if_data *sdata);
 int mesh_path_del(struct ieee80211_sub_if_data *sdata, const u8 *addr);
-void mesh_path_timer(unsigned long data);
+void mesh_path_timer(struct timer_list *t);
 void mesh_path_flush_by_nexthop(struct sta_info *sta);
 void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata,
                             struct sk_buff *skb);
index 146ec6c0f12f86f1de27db23e51326b05bf3e643..4f7826d7b47cd8c63acc14479dacdd379abe9021 100644 (file)
@@ -1194,9 +1194,9 @@ endlookup:
        return err;
 }
 
-void mesh_path_timer(unsigned long data)
+void mesh_path_timer(struct timer_list *t)
 {
-       struct mesh_path *mpath = (void *) data;
+       struct mesh_path *mpath = from_timer(mpath, t, timer);
        struct ieee80211_sub_if_data *sdata = mpath->sdata;
        int ret;
 
index 97269caafecd7b52e644e9bb645d305fdfb67196..86c8dfef56a4c8f021b68aa723ced9447b5b9602 100644 (file)
@@ -399,8 +399,7 @@ struct mesh_path *mesh_path_new(struct ieee80211_sub_if_data *sdata,
        skb_queue_head_init(&new_mpath->frame_queue);
        new_mpath->exp_time = jiffies;
        spin_lock_init(&new_mpath->state_lock);
-       setup_timer(&new_mpath->timer, mesh_path_timer,
-                   (unsigned long) new_mpath);
+       timer_setup(&new_mpath->timer, mesh_path_timer, 0);
 
        return new_mpath;
 }
index e4ededa1909d86590bb1f96116f61951acefcd6c..04460440d731423fe37204d4422db05783820bf2 100644 (file)
@@ -1066,10 +1066,10 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
 }
 EXPORT_SYMBOL(ieee80211_chswitch_done);
 
-static void ieee80211_chswitch_timer(unsigned long data)
+static void ieee80211_chswitch_timer(struct timer_list *t)
 {
        struct ieee80211_sub_if_data *sdata =
-               (struct ieee80211_sub_if_data *) data;
+               from_timer(sdata, t, u.mgd.chswitch_timer);
 
        ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);
 }
@@ -1577,9 +1577,9 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
        }
 }
 
-void ieee80211_dynamic_ps_timer(unsigned long data)
+void ieee80211_dynamic_ps_timer(struct timer_list *t)
 {
-       struct ieee80211_local *local = (void *) data;
+       struct ieee80211_local *local = from_timer(local, t, dynamic_ps_timer);
 
        ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
 }
@@ -3711,10 +3711,10 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        sdata_unlock(sdata);
 }
 
-static void ieee80211_sta_timer(unsigned long data)
+static void ieee80211_sta_timer(struct timer_list *t)
 {
        struct ieee80211_sub_if_data *sdata =
-               (struct ieee80211_sub_if_data *) data;
+               from_timer(sdata, t, u.mgd.timer);
 
        ieee80211_queue_work(&sdata->local->hw, &sdata->work);
 }
@@ -3991,10 +3991,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
        sdata_unlock(sdata);
 }
 
-static void ieee80211_sta_bcn_mon_timer(unsigned long data)
+static void ieee80211_sta_bcn_mon_timer(struct timer_list *t)
 {
        struct ieee80211_sub_if_data *sdata =
-               (struct ieee80211_sub_if_data *) data;
+               from_timer(sdata, t, u.mgd.bcn_mon_timer);
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
        if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
@@ -4005,10 +4005,10 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
                             &sdata->u.mgd.beacon_connection_loss_work);
 }
 
-static void ieee80211_sta_conn_mon_timer(unsigned long data)
+static void ieee80211_sta_conn_mon_timer(struct timer_list *t)
 {
        struct ieee80211_sub_if_data *sdata =
-               (struct ieee80211_sub_if_data *) data;
+               from_timer(sdata, t, u.mgd.conn_mon_timer);
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
 
@@ -4139,14 +4139,10 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
        INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
                          ieee80211_tdls_peer_del_work);
-       setup_timer(&ifmgd->timer, ieee80211_sta_timer,
-                   (unsigned long) sdata);
-       setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
-                   (unsigned long) sdata);
-       setup_timer(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer,
-                   (unsigned long) sdata);
-       setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
-                   (unsigned long) sdata);
+       timer_setup(&ifmgd->timer, ieee80211_sta_timer, 0);
+       timer_setup(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, 0);
+       timer_setup(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, 0);
+       timer_setup(&ifmgd->chswitch_timer, ieee80211_chswitch_timer, 0);
        INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
                          ieee80211_sta_handle_tspec_ac_params_wk);
 
index 88e6ebbbe24f5562eb69df3c92ca132d15f5733b..d351dc1162beef273c8db1a6902db6216a97bfb7 100644 (file)
@@ -150,9 +150,10 @@ void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata)
        sdata_unlock(sdata);
 }
 
-static void ieee80211_ocb_housekeeping_timer(unsigned long data)
+static void ieee80211_ocb_housekeeping_timer(struct timer_list *t)
 {
-       struct ieee80211_sub_if_data *sdata = (void *)data;
+       struct ieee80211_sub_if_data *sdata =
+               from_timer(sdata, t, u.ocb.housekeeping_timer);
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
 
@@ -165,9 +166,8 @@ void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
 
-       setup_timer(&ifocb->housekeeping_timer,
-                   ieee80211_ocb_housekeeping_timer,
-                   (unsigned long)sdata);
+       timer_setup(&ifocb->housekeeping_timer,
+                   ieee80211_ocb_housekeeping_timer, 0);
        INIT_LIST_HEAD(&ifocb->incomplete_stations);
        spin_lock_init(&ifocb->incomplete_lock);
 }
index a3060e55122c666eb3eedb6c8c93714e0783cab8..0c5627f8a104e17fb54f55c09da597ef84af5be3 100644 (file)
@@ -379,14 +379,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        if (sta_prepare_rate_control(local, sta, gfp))
                goto free_txq;
 
-       for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
-               /*
-                * timer_to_tid must be initialized with identity mapping
-                * to enable session_timer's data differentiation. See
-                * sta_rx_agg_session_timer_expired for usage.
-                */
-               sta->timer_to_tid[i] = i;
-       }
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                skb_queue_head_init(&sta->ps_tx_buf[i]);
                skb_queue_head_init(&sta->tx_filtered[i]);
@@ -1064,9 +1056,9 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
        return ret;
 }
 
-static void sta_info_cleanup(unsigned long data)
+static void sta_info_cleanup(struct timer_list *t)
 {
-       struct ieee80211_local *local = (struct ieee80211_local *) data;
+       struct ieee80211_local *local = from_timer(local, t, sta_cleanup);
        struct sta_info *sta;
        bool timer_needed = false;
 
@@ -1098,8 +1090,7 @@ int sta_info_init(struct ieee80211_local *local)
        mutex_init(&local->sta_mtx);
        INIT_LIST_HEAD(&local->sta_list);
 
-       setup_timer(&local->sta_cleanup, sta_info_cleanup,
-                   (unsigned long)local);
+       timer_setup(&local->sta_cleanup, sta_info_cleanup, 0);
        return 0;
 }
 
index 5c54acd10562a66df8aa093b9352d097cadb17b9..cd53619435b641c446ed4e0e69eda1d97714f54a 100644 (file)
@@ -126,6 +126,8 @@ enum ieee80211_agg_stop_reason {
        AGG_STOP_DESTROY_STA,
 };
 
+struct sta_info;
+
 /**
  * struct tid_ampdu_tx - TID aggregation information (Tx).
  *
@@ -133,8 +135,10 @@ enum ieee80211_agg_stop_reason {
  * @session_timer: check if we keep Tx-ing on the TID (by timeout value)
  * @addba_resp_timer: timer for peer's response to addba request
  * @pending: pending frames queue -- use sta's spinlock to protect
+ * @sta: station we are attached to
  * @dialog_token: dialog token for aggregation session
  * @timeout: session timeout value to be filled in ADDBA requests
+ * @tid: TID number
  * @state: session state (see above)
  * @last_tx: jiffies of last tx activity
  * @stop_initiator: initiator of a session stop
@@ -158,6 +162,7 @@ struct tid_ampdu_tx {
        struct timer_list session_timer;
        struct timer_list addba_resp_timer;
        struct sk_buff_head pending;
+       struct sta_info *sta;
        unsigned long state;
        unsigned long last_tx;
        u16 timeout;
@@ -169,6 +174,7 @@ struct tid_ampdu_tx {
        u16 failed_bar_ssn;
        bool bar_pending;
        bool amsdu;
+       u8 tid;
 };
 
 /**
@@ -181,12 +187,14 @@ struct tid_ampdu_tx {
  * @reorder_time: jiffies when skb was added
  * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value)
  * @reorder_timer: releases expired frames from the reorder buffer.
+ * @sta: station we are attached to
  * @last_rx: jiffies of last rx activity
  * @head_seq_num: head sequence number in reordering buffer.
  * @stored_mpdu_num: number of MPDUs in reordering buffer
  * @ssn: Starting Sequence Number expected to be aggregated.
  * @buf_size: buffer size for incoming A-MPDUs
  * @timeout: reset timer value (in TUs).
+ * @tid: TID number
  * @rcu_head: RCU head used for freeing this struct
  * @reorder_lock: serializes access to reorder buffer, see below.
  * @auto_seq: used for offloaded BA sessions to automatically pick head_seq_and
@@ -208,6 +216,7 @@ struct tid_ampdu_rx {
        u64 reorder_buf_filtered;
        struct sk_buff_head *reorder_buf;
        unsigned long *reorder_time;
+       struct sta_info *sta;
        struct timer_list session_timer;
        struct timer_list reorder_timer;
        unsigned long last_rx;
@@ -216,6 +225,7 @@ struct tid_ampdu_rx {
        u16 ssn;
        u16 buf_size;
        u16 timeout;
+       u8 tid;
        u8 auto_seq:1,
           removed:1,
           started:1;
@@ -447,7 +457,6 @@ struct ieee80211_sta_rx_stats {
  *     plus one for non-QoS frames)
  * @tid_seq: per-TID sequence numbers for sending to this STA
  * @ampdu_mlme: A-MPDU state machine state
- * @timer_to_tid: identity mapping to ID timers
  * @mesh: mesh STA information
  * @debugfs_dir: debug filesystem directory dentry
  * @dead: set to true when sta is unlinked
@@ -554,7 +563,6 @@ struct sta_info {
         * Aggregation information, locked with lock.
         */
        struct sta_ampdu_mlme ampdu_mlme;
-       u8 timer_to_tid[IEEE80211_NUM_TIDS];
 
 #ifdef CONFIG_MAC80211_DEBUGFS
        struct dentry *debugfs_dir;
index 0dab33fb9844cd0b2207c2d816780391fb08baa3..99cfafc2a139bac19f8ca78524060fcee3308b89 100644 (file)
@@ -308,6 +308,8 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
                             const struct dp_upcall_info *upcall_info,
                                 uint32_t cutlen)
 {
+       unsigned short gso_type = skb_shinfo(skb)->gso_type;
+       struct sw_flow_key later_key;
        struct sk_buff *segs, *nskb;
        int err;
 
@@ -318,9 +320,21 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
        if (segs == NULL)
                return -EINVAL;
 
+       if (gso_type & SKB_GSO_UDP) {
+               /* The initial flow key extracted by ovs_flow_key_extract()
+                * in this case is for a first fragment, so we need to
+                * properly mark later fragments.
+                */
+               later_key = *key;
+               later_key.ip.frag = OVS_FRAG_TYPE_LATER;
+       }
+
        /* Queue all of the segments. */
        skb = segs;
        do {
+               if (gso_type & SKB_GSO_UDP && skb != segs)
+                       key = &later_key;
+
                err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);
                if (err)
                        break;
index 864ddb1e3642bd26d8ca8a9153c9f16d5d766680..dbe2379329c5517fb164b6024d40fabebe7855c8 100644 (file)
@@ -631,7 +631,8 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
                        key->ip.frag = OVS_FRAG_TYPE_LATER;
                        return 0;
                }
-               if (nh->frag_off & htons(IP_MF))
+               if (nh->frag_off & htons(IP_MF) ||
+                       skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
                        key->ip.frag = OVS_FRAG_TYPE_FIRST;
                else
                        key->ip.frag = OVS_FRAG_TYPE_NONE;
@@ -747,6 +748,9 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
 
                if (key->ip.frag == OVS_FRAG_TYPE_LATER)
                        return 0;
+               if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
+                       key->ip.frag = OVS_FRAG_TYPE_FIRST;
+
                /* Transport layer. */
                if (key->ip.proto == NEXTHDR_TCP) {
                        if (tcphdr_ok(skb)) {
index 1c40caadcff959ba0c6cec6b8e32f7b459c42cfa..d836f998117b2417548b22a73940300405ce65b8 100644 (file)
@@ -229,6 +229,9 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, unsigned int ihl,
        const struct iphdr *iph;
        u16 ul;
 
+       if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
+               return 1;
+
        /*
         * Support both UDP and UDPLITE checksum algorithms, Don't use
         * udph->len to get the real length without any protocol check,
@@ -282,6 +285,9 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, unsigned int ihl,
        const struct ipv6hdr *ip6h;
        u16 ul;
 
+       if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP)
+               return 1;
+
        /*
         * Support both UDP and UDPLITE checksum algorithms, Don't use
         * udph->len to get the real length without any protocol check,
index ab255b421781b86d5b76dd1e67b8473e5a928af0..7d97f612c9b94d17ef2f9c454426922105ccc146 100644 (file)
@@ -205,13 +205,14 @@ static void tcf_chain_head_change(struct tcf_chain *chain,
 
 static void tcf_chain_flush(struct tcf_chain *chain)
 {
-       struct tcf_proto *tp;
+       struct tcf_proto *tp = rtnl_dereference(chain->filter_chain);
 
        tcf_chain_head_change(chain, NULL);
-       while ((tp = rtnl_dereference(chain->filter_chain)) != NULL) {
+       while (tp) {
                RCU_INIT_POINTER(chain->filter_chain, tp->next);
-               tcf_chain_put(chain);
                tcf_proto_destroy(tp);
+               tp = rtnl_dereference(chain->filter_chain);
+               tcf_chain_put(chain);
        }
 }
 
index 2578fbd95664af84ab6b20aeaf4902d52a7ec265..94f21116dac5eff94f99e2254a9eaef7d8378608 100644 (file)
@@ -562,7 +562,7 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_rmb)
 {
        struct smc_connection *conn = &smc->conn;
        struct smc_link_group *lgr = conn->lgr;
-       struct smc_buf_desc *buf_desc = NULL;
+       struct smc_buf_desc *buf_desc = ERR_PTR(-ENOMEM);
        struct list_head *buf_list;
        int bufsize, bufsize_short;
        int sk_buf_size;
@@ -575,7 +575,7 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_rmb)
                /* use socket send buffer size (w/o overhead) as start value */
                sk_buf_size = smc->sk.sk_sndbuf / 2;
 
-       for (bufsize_short = smc_compress_bufsize(smc->sk.sk_sndbuf / 2);
+       for (bufsize_short = smc_compress_bufsize(sk_buf_size);
             bufsize_short >= 0; bufsize_short--) {
 
                if (is_rmb) {
index 7b1ee5a0b03cd10d167a6ca522243c4285996151..73165e9ca5bfd2c2a928f7d1c16569cd1b59e65a 100644 (file)
@@ -855,11 +855,13 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
                return stat;
        if (integ_len > buf->len)
                return stat;
-       if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len))
-               BUG();
+       if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len)) {
+               WARN_ON_ONCE(1);
+               return stat;
+       }
        /* copy out mic... */
        if (read_u32_from_xdr_buf(buf, integ_len, &mic.len))
-               BUG();
+               return stat;
        if (mic.len > RPC_MAX_AUTH_SIZE)
                return stat;
        mic.data = kmalloc(mic.len, GFP_KERNEL);
@@ -1611,8 +1613,10 @@ svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp)
        BUG_ON(integ_len % 4);
        *p++ = htonl(integ_len);
        *p++ = htonl(gc->gc_seq);
-       if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, integ_len))
-               BUG();
+       if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, integ_len)) {
+               WARN_ON_ONCE(1);
+               goto out_err;
+       }
        if (resbuf->tail[0].iov_base == NULL) {
                if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE)
                        goto out_err;
index 71de77bd44236dee6bd7ea1e86b8e317aee65060..e8e0831229cfcce48b2d6802493e80a429aa108b 100644 (file)
@@ -250,9 +250,9 @@ void svc_add_new_perm_xprt(struct svc_serv *serv, struct svc_xprt *new)
        svc_xprt_received(new);
 }
 
-int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
-                   struct net *net, const int family,
-                   const unsigned short port, int flags)
+static int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
+                           struct net *net, const int family,
+                           const unsigned short port, int flags)
 {
        struct svc_xprt_class *xcl;
 
@@ -380,7 +380,6 @@ void svc_xprt_do_enqueue(struct svc_xprt *xprt)
        struct svc_pool *pool;
        struct svc_rqst *rqstp = NULL;
        int cpu;
-       bool queued = false;
 
        if (!svc_xprt_has_something_to_do(xprt))
                goto out;
@@ -401,58 +400,25 @@ void svc_xprt_do_enqueue(struct svc_xprt *xprt)
 
        atomic_long_inc(&pool->sp_stats.packets);
 
-redo_search:
+       dprintk("svc: transport %p put into queue\n", xprt);
+       spin_lock_bh(&pool->sp_lock);
+       list_add_tail(&xprt->xpt_ready, &pool->sp_sockets);
+       pool->sp_stats.sockets_queued++;
+       spin_unlock_bh(&pool->sp_lock);
+
        /* find a thread for this xprt */
        rcu_read_lock();
        list_for_each_entry_rcu(rqstp, &pool->sp_all_threads, rq_all) {
-               /* Do a lockless check first */
-               if (test_bit(RQ_BUSY, &rqstp->rq_flags))
+               if (test_and_set_bit(RQ_BUSY, &rqstp->rq_flags))
                        continue;
-
-               /*
-                * Once the xprt has been queued, it can only be dequeued by
-                * the task that intends to service it. All we can do at that
-                * point is to try to wake this thread back up so that it can
-                * do so.
-                */
-               if (!queued) {
-                       spin_lock_bh(&rqstp->rq_lock);
-                       if (test_and_set_bit(RQ_BUSY, &rqstp->rq_flags)) {
-                               /* already busy, move on... */
-                               spin_unlock_bh(&rqstp->rq_lock);
-                               continue;
-                       }
-
-                       /* this one will do */
-                       rqstp->rq_xprt = xprt;
-                       svc_xprt_get(xprt);
-                       spin_unlock_bh(&rqstp->rq_lock);
-               }
-               rcu_read_unlock();
-
                atomic_long_inc(&pool->sp_stats.threads_woken);
                wake_up_process(rqstp->rq_task);
-               put_cpu();
-               goto out;
-       }
-       rcu_read_unlock();
-
-       /*
-        * We didn't find an idle thread to use, so we need to queue the xprt.
-        * Do so and then search again. If we find one, we can't hook this one
-        * up to it directly but we can wake the thread up in the hopes that it
-        * will pick it up once it searches for a xprt to service.
-        */
-       if (!queued) {
-               queued = true;
-               dprintk("svc: transport %p put into queue\n", xprt);
-               spin_lock_bh(&pool->sp_lock);
-               list_add_tail(&xprt->xpt_ready, &pool->sp_sockets);
-               pool->sp_stats.sockets_queued++;
-               spin_unlock_bh(&pool->sp_lock);
-               goto redo_search;
+               goto out_unlock;
        }
+       set_bit(SP_CONGESTED, &pool->sp_flags);
        rqstp = NULL;
+out_unlock:
+       rcu_read_unlock();
        put_cpu();
 out:
        trace_svc_xprt_do_enqueue(xprt, rqstp);
@@ -721,38 +687,25 @@ rqst_should_sleep(struct svc_rqst *rqstp)
 
 static struct svc_xprt *svc_get_next_xprt(struct svc_rqst *rqstp, long timeout)
 {
-       struct svc_xprt *xprt;
        struct svc_pool         *pool = rqstp->rq_pool;
        long                    time_left = 0;
 
        /* rq_xprt should be clear on entry */
        WARN_ON_ONCE(rqstp->rq_xprt);
 
-       /* Normally we will wait up to 5 seconds for any required
-        * cache information to be provided.
-        */
-       rqstp->rq_chandle.thread_wait = 5*HZ;
-
-       xprt = svc_xprt_dequeue(pool);
-       if (xprt) {
-               rqstp->rq_xprt = xprt;
-
-               /* As there is a shortage of threads and this request
-                * had to be queued, don't allow the thread to wait so
-                * long for cache updates.
-                */
-               rqstp->rq_chandle.thread_wait = 1*HZ;
-               clear_bit(SP_TASK_PENDING, &pool->sp_flags);
-               return xprt;
-       }
+       rqstp->rq_xprt = svc_xprt_dequeue(pool);
+       if (rqstp->rq_xprt)
+               goto out_found;
 
        /*
         * We have to be able to interrupt this wait
         * to bring down the daemons ...
         */
        set_current_state(TASK_INTERRUPTIBLE);
+       smp_mb__before_atomic();
+       clear_bit(SP_CONGESTED, &pool->sp_flags);
        clear_bit(RQ_BUSY, &rqstp->rq_flags);
-       smp_mb();
+       smp_mb__after_atomic();
 
        if (likely(rqst_should_sleep(rqstp)))
                time_left = schedule_timeout(timeout);
@@ -761,13 +714,11 @@ static struct svc_xprt *svc_get_next_xprt(struct svc_rqst *rqstp, long timeout)
 
        try_to_freeze();
 
-       spin_lock_bh(&rqstp->rq_lock);
        set_bit(RQ_BUSY, &rqstp->rq_flags);
-       spin_unlock_bh(&rqstp->rq_lock);
-
-       xprt = rqstp->rq_xprt;
-       if (xprt != NULL)
-               return xprt;
+       smp_mb__after_atomic();
+       rqstp->rq_xprt = svc_xprt_dequeue(pool);
+       if (rqstp->rq_xprt)
+               goto out_found;
 
        if (!time_left)
                atomic_long_inc(&pool->sp_stats.threads_timedout);
@@ -775,6 +726,15 @@ static struct svc_xprt *svc_get_next_xprt(struct svc_rqst *rqstp, long timeout)
        if (signalled() || kthread_should_stop())
                return ERR_PTR(-EINTR);
        return ERR_PTR(-EAGAIN);
+out_found:
+       /* Normally we will wait up to 5 seconds for any required
+        * cache information to be provided.
+        */
+       if (!test_bit(SP_CONGESTED, &pool->sp_flags))
+               rqstp->rq_chandle.thread_wait = 5*HZ;
+       else
+               rqstp->rq_chandle.thread_wait = 1*HZ;
+       return rqstp->rq_xprt;
 }
 
 static void svc_add_new_temp_xprt(struct svc_serv *serv, struct svc_xprt *newxpt)
index 992594b7cc6b699d75614ca45bbf4631c5e1ede2..af7893501e40acdbaf678a373f721264cf398029 100644 (file)
@@ -133,6 +133,10 @@ static int svc_rdma_bc_sendto(struct svcxprt_rdma *rdma,
        if (ret)
                goto out_err;
 
+       /* Bump page refcnt so Send completion doesn't release
+        * the rq_buffer before all retransmits are complete.
+        */
+       get_page(virt_to_page(rqst->rq_buffer));
        ret = svc_rdma_post_send_wr(rdma, ctxt, 1, 0);
        if (ret)
                goto out_unmap;
@@ -165,7 +169,6 @@ xprt_rdma_bc_allocate(struct rpc_task *task)
                return -EINVAL;
        }
 
-       /* svc_rdma_sendto releases this page */
        page = alloc_page(RPCRDMA_DEF_GFP);
        if (!page)
                return -ENOMEM;
@@ -184,6 +187,7 @@ xprt_rdma_bc_free(struct rpc_task *task)
 {
        struct rpc_rqst *rqst = task->tk_rqstp;
 
+       put_page(virt_to_page(rqst->rq_buffer));
        kfree(rqst->rq_rbuffer);
 }
 
index 5caf8e722a118659f8b9e8c3531f60a8e738158b..46ec069150d50ff53e93a7f17b0d716fa80503a2 100644 (file)
@@ -290,6 +290,7 @@ static void qp_event_handler(struct ib_event *event, void *context)
                        ib_event_msg(event->event), event->event,
                        event->element.qp);
                set_bit(XPT_CLOSE, &xprt->xpt_flags);
+               svc_xprt_enqueue(xprt);
                break;
        }
 }
@@ -322,8 +323,7 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc)
        set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags);
        if (test_bit(RDMAXPRT_CONN_PENDING, &xprt->sc_flags))
                goto out;
-       svc_xprt_enqueue(&xprt->sc_xprt);
-       goto out;
+       goto out_enqueue;
 
 flushed:
        if (wc->status != IB_WC_WR_FLUSH_ERR)
@@ -333,6 +333,8 @@ flushed:
        set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
        svc_rdma_put_context(ctxt, 1);
 
+out_enqueue:
+       svc_xprt_enqueue(&xprt->sc_xprt);
 out:
        svc_xprt_put(&xprt->sc_xprt);
 }
@@ -358,6 +360,7 @@ void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc)
 
        if (unlikely(wc->status != IB_WC_SUCCESS)) {
                set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
+               svc_xprt_enqueue(&xprt->sc_xprt);
                if (wc->status != IB_WC_WR_FLUSH_ERR)
                        pr_err("svcrdma: Send: %s (%u/0x%x)\n",
                               ib_wc_status_msg(wc->status),
@@ -569,8 +572,10 @@ static int rdma_listen_handler(struct rdma_cm_id *cma_id,
        case RDMA_CM_EVENT_DEVICE_REMOVAL:
                dprintk("svcrdma: Device removal xprt=%p, cm_id=%p\n",
                        xprt, cma_id);
-               if (xprt)
+               if (xprt) {
                        set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
+                       svc_xprt_enqueue(&xprt->sc_xprt);
+               }
                break;
 
        default:
index 7821085a7dd87cb81b1b5e3f5c4d33273c65e3d9..12777cac638a6918a626953647a1bf803ced89ad 100644 (file)
@@ -539,8 +539,8 @@ void tipc_group_filter_msg(struct tipc_group *grp, struct sk_buff_head *inputq,
                        tipc_group_proto_xmit(grp, m, GRP_ACK_MSG, xmitq);
 
                if (leave) {
-                       tipc_group_delete_member(grp, m);
                        __skb_queue_purge(defq);
+                       tipc_group_delete_member(grp, m);
                        break;
                }
                if (!update)
index a0e1951227fab1297ee8929dddb3a4a911ef2a56..b1ac23ca20c86be0af71e9a1ba92cc99d8d5a967 100644 (file)
@@ -2605,10 +2605,32 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
                        goto nla_put_failure;
        }
 
-       if (wdev->ssid_len) {
-               if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
+       wdev_lock(wdev);
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_AP:
+               if (wdev->ssid_len &&
+                   nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
                        goto nla_put_failure;
+               break;
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_ADHOC: {
+               const u8 *ssid_ie;
+               if (!wdev->current_bss)
+                       break;
+               ssid_ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
+                                              WLAN_EID_SSID);
+               if (!ssid_ie)
+                       break;
+               if (nla_put(msg, NL80211_ATTR_SSID, ssid_ie[1], ssid_ie + 2))
+                       goto nla_put_failure;
+               break;
+               }
+       default:
+               /* nothing */
+               break;
        }
+       wdev_unlock(wdev);
 
        genlmsg_end(msg, hdr);
        return 0;
index 3871998059de7beb9a2bff9a5321a8eb81bfcff4..78e71b0390be90bc16655d380fa6869391c52729 100644 (file)
@@ -3644,27 +3644,14 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy,
        }
 }
 
-int __init regulatory_init(void)
+static int __init regulatory_init_db(void)
 {
-       int err = 0;
+       int err;
 
        err = load_builtin_regdb_keys();
        if (err)
                return err;
 
-       reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0);
-       if (IS_ERR(reg_pdev))
-               return PTR_ERR(reg_pdev);
-
-       spin_lock_init(&reg_requests_lock);
-       spin_lock_init(&reg_pending_beacons_lock);
-       spin_lock_init(&reg_indoor_lock);
-
-       rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom);
-
-       user_alpha2[0] = '9';
-       user_alpha2[1] = '7';
-
        /* We always try to get an update for the static regdomain */
        err = regulatory_hint_core(cfg80211_world_regdom->alpha2);
        if (err) {
@@ -3692,6 +3679,31 @@ int __init regulatory_init(void)
 
        return 0;
 }
+#ifndef MODULE
+late_initcall(regulatory_init_db);
+#endif
+
+int __init regulatory_init(void)
+{
+       reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0);
+       if (IS_ERR(reg_pdev))
+               return PTR_ERR(reg_pdev);
+
+       spin_lock_init(&reg_requests_lock);
+       spin_lock_init(&reg_pending_beacons_lock);
+       spin_lock_init(&reg_indoor_lock);
+
+       rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom);
+
+       user_alpha2[0] = '9';
+       user_alpha2[1] = '7';
+
+#ifdef MODULE
+       return regulatory_init_db();
+#else
+       return 0;
+#endif
+}
 
 void regulatory_exit(void)
 {
index ec7dfa02c0519483818c40cce40ba0993fbd4712..65fbcf3c32c735b9547a9f77b4a94bd7666d87fd 100644 (file)
@@ -320,6 +320,9 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
        if (iint->flags & IMA_DIGSIG)
                return;
 
+       if (iint->ima_file_status != INTEGRITY_PASS)
+               return;
+
        rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo);
        if (rc < 0)
                return;
index c03b4f69d5b737defdff632340c4b6095cb0ce57..be02c8b904dba1f3e9135d22d7393895e853f9d3 100644 (file)
@@ -30,6 +30,7 @@ help:
        @echo '  usb                    - USB testing tools'
        @echo '  virtio                 - vhost test module'
        @echo '  vm                     - misc vm tools'
+       @echo '  wmi                    - WMI interface examples'
        @echo '  x86_energy_perf_policy - Intel energy policy tool'
        @echo ''
        @echo 'You can do:'
@@ -58,7 +59,7 @@ acpi: FORCE
 cpupower: FORCE
        $(call descend,power/$@)
 
-cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds: FORCE
+cgroup firewire hv guest spi usb virtio vm bpf iio gpio objtool leds wmi: FORCE
        $(call descend,$@)
 
 liblockdep: FORCE
@@ -93,7 +94,7 @@ kvm_stat: FORCE
 all: acpi cgroup cpupower gpio hv firewire liblockdep \
                perf selftests spi turbostat usb \
                virtio vm bpf x86_energy_perf_policy \
-               tmon freefall iio objtool kvm_stat
+               tmon freefall iio objtool kvm_stat wmi
 
 acpi_install:
        $(call descend,power/$(@:_install=),install)
@@ -101,7 +102,7 @@ acpi_install:
 cpupower_install:
        $(call descend,power/$(@:_install=),install)
 
-cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install:
+cgroup_install firewire_install gpio_install hv_install iio_install perf_install spi_install usb_install virtio_install vm_install bpf_install objtool_install wmi_install:
        $(call descend,$(@:_install=),install)
 
 liblockdep_install:
@@ -126,7 +127,8 @@ install: acpi_install cgroup_install cpupower_install gpio_install \
                hv_install firewire_install iio_install liblockdep_install \
                perf_install selftests_install turbostat_install usb_install \
                virtio_install vm_install bpf_install x86_energy_perf_policy_install \
-               tmon_install freefall_install objtool_install kvm_stat_install
+               tmon_install freefall_install objtool_install kvm_stat_install \
+               wmi_install
 
 acpi_clean:
        $(call descend,power/acpi,clean)
@@ -134,7 +136,7 @@ acpi_clean:
 cpupower_clean:
        $(call descend,power/cpupower,clean)
 
-cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean:
+cgroup_clean hv_clean firewire_clean spi_clean usb_clean virtio_clean vm_clean wmi_clean bpf_clean iio_clean gpio_clean objtool_clean leds_clean:
        $(call descend,$(@:_clean=),clean)
 
 liblockdep_clean:
@@ -172,6 +174,6 @@ clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean \
                perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \
                vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \
                freefall_clean build_clean libbpf_clean libsubcmd_clean liblockdep_clean \
-               gpio_clean objtool_clean leds_clean
+               gpio_clean objtool_clean leds_clean wmi_clean
 
 .PHONY: FORCE
diff --git a/tools/wmi/Makefile b/tools/wmi/Makefile
new file mode 100644 (file)
index 0000000..e664f11
--- /dev/null
@@ -0,0 +1,18 @@
+PREFIX ?= /usr
+SBINDIR ?= sbin
+INSTALL ?= install
+CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include
+CC = $(CROSS_COMPILE)gcc
+
+TARGET = dell-smbios-example
+
+all: $(TARGET)
+
+%: %.c
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+clean:
+       $(RM) $(TARGET)
+
+install: dell-smbios-example
+       $(INSTALL) -D -m 755 $(TARGET) $(DESTDIR)$(PREFIX)/$(SBINDIR)/$(TARGET)
diff --git a/tools/wmi/dell-smbios-example.c b/tools/wmi/dell-smbios-example.c
new file mode 100644 (file)
index 0000000..9d3bde0
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ *  Sample application for SMBIOS communication over WMI interface
+ *  Performs the following:
+ *  - Simple cmd_class/cmd_select lookup for TPM information
+ *  - Simple query of known tokens and their values
+ *  - Simple activation of a token
+ *
+ *  Copyright (C) 2017 Dell, 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+/* if uapi header isn't installed, this might not yet exist */
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+#include <linux/wmi.h>
+
+/* It would be better to discover these using udev, but for a simple
+ * application they're hardcoded
+ */
+static const char *ioctl_devfs = "/dev/wmi/dell-smbios";
+static const char *token_sysfs =
+                       "/sys/bus/platform/devices/dell-smbios.0/tokens";
+
+static void show_buffer(struct dell_wmi_smbios_buffer *buffer)
+{
+       printf("Call: %x/%x [%x,%x,%x,%x]\nResults: [%8x,%8x,%8x,%8x]\n",
+       buffer->std.cmd_class, buffer->std.cmd_select,
+       buffer->std.input[0], buffer->std.input[1],
+       buffer->std.input[2], buffer->std.input[3],
+       buffer->std.output[0], buffer->std.output[1],
+       buffer->std.output[2], buffer->std.output[3]);
+}
+
+static int run_wmi_smbios_cmd(struct dell_wmi_smbios_buffer *buffer)
+{
+       int fd;
+       int ret;
+
+       fd = open(ioctl_devfs, O_NONBLOCK);
+       ret = ioctl(fd, DELL_WMI_SMBIOS_CMD, buffer);
+       close(fd);
+       return ret;
+}
+
+static int find_token(__u16 token, __u16 *location, __u16 *value)
+{
+       char location_sysfs[60];
+       char value_sysfs[57];
+       char buf[4096];
+       FILE *f;
+       int ret;
+
+       ret = sprintf(value_sysfs, "%s/%04x_value", token_sysfs, token);
+       if (ret < 0) {
+               printf("sprintf value failed\n");
+               return 2;
+       }
+       f = fopen(value_sysfs, "rb");
+       if (!f) {
+               printf("failed to open %s\n", value_sysfs);
+               return 2;
+       }
+       fread(buf, 1, 4096, f);
+       fclose(f);
+       *value = (__u16) strtol(buf, NULL, 16);
+
+       ret = sprintf(location_sysfs, "%s/%04x_location", token_sysfs, token);
+       if (ret < 0) {
+               printf("sprintf location failed\n");
+               return 1;
+       }
+       f = fopen(location_sysfs, "rb");
+       if (!f) {
+               printf("failed to open %s\n", location_sysfs);
+               return 2;
+       }
+       fread(buf, 1, 4096, f);
+       fclose(f);
+       *location = (__u16) strtol(buf, NULL, 16);
+
+       if (*location)
+               return 0;
+       return 2;
+}
+
+static int token_is_active(__u16 *location, __u16 *cmpvalue,
+                          struct dell_wmi_smbios_buffer *buffer)
+{
+       int ret;
+
+       buffer->std.cmd_class = CLASS_TOKEN_READ;
+       buffer->std.cmd_select = SELECT_TOKEN_STD;
+       buffer->std.input[0] = *location;
+       ret = run_wmi_smbios_cmd(buffer);
+       if (ret != 0 || buffer->std.output[0] != 0)
+               return ret;
+       ret = (buffer->std.output[1] == *cmpvalue);
+       return ret;
+}
+
+static int query_token(__u16 token, struct dell_wmi_smbios_buffer *buffer)
+{
+       __u16 location;
+       __u16 value;
+       int ret;
+
+       ret = find_token(token, &location, &value);
+       if (ret != 0) {
+               printf("unable to find token %04x\n", token);
+               return 1;
+       }
+       return token_is_active(&location, &value, buffer);
+}
+
+static int activate_token(struct dell_wmi_smbios_buffer *buffer,
+                  __u16 token)
+{
+       __u16 location;
+       __u16 value;
+       int ret;
+
+       ret = find_token(token, &location, &value);
+       if (ret != 0) {
+               printf("unable to find token %04x\n", token);
+               return 1;
+       }
+       buffer->std.cmd_class = CLASS_TOKEN_WRITE;
+       buffer->std.cmd_select = SELECT_TOKEN_STD;
+       buffer->std.input[0] = location;
+       buffer->std.input[1] = 1;
+       ret = run_wmi_smbios_cmd(buffer);
+       return ret;
+}
+
+static int query_buffer_size(__u64 *buffer_size)
+{
+       FILE *f;
+
+       f = fopen(ioctl_devfs, "rb");
+       if (!f)
+               return -EINVAL;
+       fread(buffer_size, sizeof(__u64), 1, f);
+       fclose(f);
+       return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+       struct dell_wmi_smbios_buffer *buffer;
+       int ret;
+       __u64 value = 0;
+
+       ret = query_buffer_size(&value);
+       if (ret == EXIT_FAILURE || !value) {
+               printf("Unable to read buffer size\n");
+               goto out;
+       }
+       printf("Detected required buffer size %lld\n", value);
+
+       buffer = malloc(value);
+       if (buffer == NULL) {
+               printf("failed to alloc memory for ioctl\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+       buffer->length = value;
+
+       /* simple SMBIOS call for looking up TPM info */
+       buffer->std.cmd_class = CLASS_FLASH_INTERFACE;
+       buffer->std.cmd_select = SELECT_FLASH_INTERFACE;
+       buffer->std.input[0] = 2;
+       ret = run_wmi_smbios_cmd(buffer);
+       if (ret) {
+               printf("smbios ioctl failed: %d\n", ret);
+               ret = EXIT_FAILURE;
+               goto out;
+       }
+       show_buffer(buffer);
+
+       /* query some tokens */
+       ret = query_token(CAPSULE_EN_TOKEN, buffer);
+       printf("UEFI Capsule enabled token is: %d\n", ret);
+       ret = query_token(CAPSULE_DIS_TOKEN, buffer);
+       printf("UEFI Capsule disabled token is: %d\n", ret);
+
+       /* activate UEFI capsule token if disabled */
+       if (ret) {
+               printf("Enabling UEFI capsule token");
+               if (activate_token(buffer, CAPSULE_EN_TOKEN)) {
+                       printf("activate failed\n");
+                       ret = -1;
+                       goto out;
+               }
+       }
+       ret = EXIT_SUCCESS;
+out:
+       free(buffer);
+       return ret;
+}