Merge branch 'android-msm-marlin-3.18-nyc-mr2' into android-msm-marlin-3.18-oc
November 2017.1
Bug: 65558892
Change-Id: I416cc8f5064539eeb8d39234c009a549aca5945d
Signed-off-by: Andrew Lehmer <alehmer@google.com>
diff --git a/.mailmap b/.mailmap
index 5be04db..fb30eb1 100644
--- a/.mailmap
+++ b/.mailmap
@@ -79,6 +79,7 @@
Linas Vepstas <linas@austin.ibm.com>
Mark Brown <broonie@sirena.org.uk>
Matthieu CASTET <castet.matthieu@free.fr>
+Mauro Carvalho Chehab <mchehab@kernel.org> <maurochehab@gmail.com> <mchehab@infradead.org> <mchehab@redhat.com> <m.chehab@samsung.com> <mchehab@osg.samsung.com> <mchehab@s-opensource.com>
Mayuresh Janorkar <mayur@ti.com>
Michael Buesch <m@bues.ch>
Michel Dänzer <michel@tungstengraphics.com>
diff --git a/CREDITS b/CREDITS
index bb62788..89d2297 100644
--- a/CREDITS
+++ b/CREDITS
@@ -636,6 +636,7 @@
N: Mauro Carvalho Chehab
E: m.chehab@samsung.org
+E: mchehab@osg.samsung.com
E: mchehab@infradead.org
D: Media subsystem (V4L/DVB) drivers and core
D: EDAC drivers and EDAC 3.0 core rework
diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec
new file mode 100644
index 0000000..a7ac417
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-typec
@@ -0,0 +1,276 @@
+USB Type-C port devices (eg. /sys/class/typec/port0/)
+
+What: /sys/class/typec/<port>/data_role
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ The supported USB data roles. This attribute can be used for
+ requesting data role swapping on the port. Swapping is supported
+ as synchronous operation, so write(2) to the attribute will not
+ return until the operation has finished. The attribute is
+ notified about role changes so that poll(2) on the attribute
+ wakes up. Change on the role will also generate uevent
+ KOBJ_CHANGE on the port. The current role is show in brackets,
+ for example "[host] device" when DRP port is in host mode.
+
+ Valid values: host, device
+
+What: /sys/class/typec/<port>/power_role
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ The supported power roles. This attribute can be used to request
+ power role swap on the port when the port supports USB Power
+ Delivery. Swapping is supported as synchronous operation, so
+ write(2) to the attribute will not return until the operation
+ has finished. The attribute is notified about role changes so
+ that poll(2) on the attribute wakes up. Change on the role will
+ also generate uevent KOBJ_CHANGE. The current role is show in
+ brackets, for example "[source] sink" when in source mode.
+
+ Valid values: source, sink
+
+What: /sys/class/typec/<port>/vconn_source
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Shows is the port VCONN Source. This attribute can be used to
+ request VCONN swap to change the VCONN Source during connection
+ when both the port and the partner support USB Power Delivery.
+ Swapping is supported as synchronous operation, so write(2) to
+ the attribute will not return until the operation has finished.
+ The attribute is notified about VCONN source changes so that
+ poll(2) on the attribute wakes up. Change on VCONN source also
+ generates uevent KOBJ_CHANGE.
+
+ Valid values:
+ - "no" when the port is not the VCONN Source
+ - "yes" when the port is the VCONN Source
+
+What: /sys/class/typec/<port>/power_operation_mode
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Shows the current power operational mode the port is in. The
+ power operation mode means current level for VBUS. In case USB
+ Power Delivery communication is used for negotiating the levels,
+ power operation mode should show "usb_power_delivery".
+
+ Valid values:
+ - default
+ - 1.5A
+ - 3.0A
+ - usb_power_delivery
+
+What: /sys/class/typec/<port>/preferred_role
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ The user space can notify the driver about the preferred role.
+ It should be handled as enabling of Try.SRC or Try.SNK, as
+ defined in USB Type-C specification, in the port drivers. By
+ default the preferred role should come from the platform.
+
+ Valid values: source, sink, none (to remove preference)
+
+What: /sys/class/typec/<port>/supported_accessory_modes
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Space separated list of accessory modes, defined in the USB
+ Type-C specification, the port supports.
+
+What: /sys/class/typec/<port>/usb_power_delivery_revision
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Revision number of the supported USB Power Delivery
+ specification, or 0 when USB Power Delivery is not supported.
+
+What: /sys/class/typec/<port>/usb_typec_revision
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Revision number of the supported USB Type-C specification.
+
+
+USB Type-C partner devices (eg. /sys/class/typec/port0-partner/)
+
+What: /sys/class/typec/<port>-partner/accessory_mode
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Shows the Accessory Mode name when the partner is an Accessory.
+ The Accessory Modes are defined in USB Type-C Specification.
+
+What: /sys/class/typec/<port>-partner/supports_usb_power_delivery
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Shows if the partner supports USB Power Delivery communication:
+ Valid values: yes, no
+
+What: /sys/class/typec/<port>-partner>/identity/
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ This directory appears only if the port device driver is capable
+ of showing the result of Discover Identity USB power delivery
+ command. That will not always be possible even when USB power
+ delivery is supported, for example when USB power delivery
+ communication for the port is mostly handled in firmware. If the
+ directory exists, it will have an attribute file for every VDO
+ in Discover Identity command result.
+
+What: /sys/class/typec/<port>-partner/identity/id_header
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ ID Header VDO part of Discover Identity command result. The
+ value will show 0 until Discover Identity command result becomes
+ available. The value can be polled.
+
+What: /sys/class/typec/<port>-partner/identity/cert_stat
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Cert Stat VDO part of Discover Identity command result. The
+ value will show 0 until Discover Identity command result becomes
+ available. The value can be polled.
+
+What: /sys/class/typec/<port>-partner/identity/product
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Product VDO part of Discover Identity command result. The value
+ will show 0 until Discover Identity command result becomes
+ available. The value can be polled.
+
+
+USB Type-C cable devices (eg. /sys/class/typec/port0-cable/)
+
+Note: Electronically Marked Cables will have a device also for one cable plug
+(eg. /sys/class/typec/port0-plug0). If the cable is active and has also SOP
+Double Prime controller (USB Power Deliver specification ch. 2.4) it will have
+second device also for the other plug. Both plugs may have alternate modes as
+described in USB Type-C and USB Power Delivery specifications.
+
+What: /sys/class/typec/<port>-cable/type
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Shows if the cable is active.
+ Valid values: active, passive
+
+What: /sys/class/typec/<port>-cable/plug_type
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Shows type of the plug on the cable:
+ - type-a - Standard A
+ - type-b - Standard B
+ - type-c
+ - captive
+
+What: /sys/class/typec/<port>-cable/identity/
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ This directory appears only if the port device driver is capable
+ of showing the result of Discover Identity USB power delivery
+ command. That will not always be possible even when USB power
+ delivery is supported. If the directory exists, it will have an
+ attribute for every VDO returned by Discover Identity command.
+
+What: /sys/class/typec/<port>-cable/identity/id_header
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ ID Header VDO part of Discover Identity command result. The
+ value will show 0 until Discover Identity command result becomes
+ available. The value can be polled.
+
+What: /sys/class/typec/<port>-cable/identity/cert_stat
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Cert Stat VDO part of Discover Identity command result. The
+ value will show 0 until Discover Identity command result becomes
+ available. The value can be polled.
+
+What: /sys/class/typec/<port>-cable/identity/product
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Product VDO part of Discover Identity command result. The value
+ will show 0 until Discover Identity command result becomes
+ available. The value can be polled.
+
+
+Alternate Mode devices.
+
+The alternate modes will have Standard or Vendor ID (SVID) assigned by USB-IF.
+The ports, partners and cable plugs can have alternate modes. A supported SVID
+will consist of a set of modes. Every SVID a port/partner/plug supports will
+have a device created for it, and every supported mode for a supported SVID will
+have its own directory under that device. Below <dev> refers to the device for
+the alternate mode.
+
+What: /sys/class/typec/<port|partner|cable>/<dev>/svid
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ The SVID (Standard or Vendor ID) assigned by USB-IF for this
+ alternate mode.
+
+What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Every supported mode will have its own directory. The name of
+ a mode will be "mode<index>" (for example mode1), where <index>
+ is the actual index to the mode VDO returned by Discover Modes
+ USB power delivery command.
+
+What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/description
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Shows description of the mode. The description is optional for
+ the drivers, just like with the Billboard Devices.
+
+What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/vdo
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Shows the VDO in hexadecimal returned by Discover Modes command
+ for this mode.
+
+What: /sys/class/typec/<port|partner|cable>/<dev>/mode<index>/active
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Shows if the mode is active or not. The attribute can be used
+ for entering/exiting the mode with partners and cable plugs, and
+ with the port alternate modes it can be used for disabling
+ support for specific alternate modes. Entering/exiting modes is
+ supported as synchronous operation so write(2) to the attribute
+ does not return until the enter/exit mode operation has
+ finished. The attribute is notified when the mode is
+ entered/exited so poll(2) on the attribute wakes up.
+ Entering/exiting a mode will also generate uevent KOBJ_CHANGE.
+
+ Valid values: yes, no
+
+What: /sys/class/typec/<port>/<dev>/mode<index>/supported_roles
+Date: February 2017
+Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+ Space separated list of the supported roles.
+
+ This attribute is available for the devices describing the
+ alternate modes a port supports, and it will not be exposed with
+ the devices presenting the alternate modes the partners or cable
+ plugs support.
+
+ Valid values: source, sink
diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl
index 502170b..0aa3e6a 100644
--- a/Documentation/DocBook/80211.tmpl
+++ b/Documentation/DocBook/80211.tmpl
@@ -138,6 +138,7 @@
!Finclude/net/cfg80211.h cfg80211_ibss_joined
!Finclude/net/cfg80211.h cfg80211_connect_result
!Finclude/net/cfg80211.h cfg80211_connect_bss
+!Finclude/net/cfg80211.h cfg80211_connect_timeout
!Finclude/net/cfg80211.h cfg80211_roamed
!Finclude/net/cfg80211.h cfg80211_disconnected
!Finclude/net/cfg80211.h cfg80211_ready_on_channel
@@ -156,8 +157,9 @@
!Finclude/net/cfg80211.h cfg80211_scan_request
!Finclude/net/cfg80211.h cfg80211_scan_done
!Finclude/net/cfg80211.h cfg80211_bss
-!Finclude/net/cfg80211.h cfg80211_inform_bss_width_frame
-!Finclude/net/cfg80211.h cfg80211_inform_bss_width
+!Finclude/net/cfg80211.h cfg80211_inform_bss
+!Finclude/net/cfg80211.h cfg80211_inform_bss_frame_data
+!Finclude/net/cfg80211.h cfg80211_inform_bss_data
!Finclude/net/cfg80211.h cfg80211_unlink_bss
!Finclude/net/cfg80211.h cfg80211_find_ie
!Finclude/net/cfg80211.h ieee80211_bss_get_ie
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 6883a1b..fc75959 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -1,4 +1,4 @@
-subdir-y := accounting arm auxdisplay blackfin connector \
- filesystems filesystems ia64 laptops mic misc-devices \
+subdir-y := accounting auxdisplay blackfin connector \
+ filesystems filesystems ia64 laptops misc-devices \
networking pcmcia prctl ptp spi timers vDSO video4linux \
watchdog
diff --git a/Documentation/arm/CCN.txt b/Documentation/arm/CCN.txt
index 0632b3a..715776f 100644
--- a/Documentation/arm/CCN.txt
+++ b/Documentation/arm/CCN.txt
@@ -38,7 +38,7 @@
/ # perf list | grep ccn
ccn/cycles/ [Kernel PMU event]
<...>
- ccn/xp_valid_flit/ [Kernel PMU event]
+ ccn/xp_valid_flit,xp=?,port=?,vc=?,dir=?/ [Kernel PMU event]
<...>
/ # perf stat -C 0 -e ccn/cycles/,ccn/xp_valid_flit,xp=1,port=0,vc=1,dir=1/ \
diff --git a/Documentation/arm/Makefile b/Documentation/arm/Makefile
deleted file mode 100644
index 732c770..0000000
--- a/Documentation/arm/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := SH-Mobile
diff --git a/Documentation/arm/SH-Mobile/Makefile b/Documentation/arm/SH-Mobile/Makefile
deleted file mode 100644
index bca8a7e..0000000
--- a/Documentation/arm/SH-Mobile/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-# List of programs to build
-hostprogs-y := vrl4
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_vrl4.o += -I$(objtree)/usr/include -I$(srctree)/tools/include
diff --git a/Documentation/arm/SH-Mobile/vrl4.c b/Documentation/arm/SH-Mobile/vrl4.c
deleted file mode 100644
index f4cd8ad..0000000
--- a/Documentation/arm/SH-Mobile/vrl4.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * vrl4 format generator
- *
- * Copyright (C) 2010 Simon Horman
- *
- * 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.
- */
-
-/*
- * usage: vrl4 < zImage > out
- * dd if=out of=/dev/sdx bs=512 seek=1 # Write the image to sector 1
- *
- * Reads a zImage from stdin and writes a vrl4 image to stdout.
- * In practice this means writing a padded vrl4 header to stdout followed
- * by the zImage.
- *
- * The padding places the zImage at ALIGN bytes into the output.
- * The vrl4 uses ALIGN + START_BASE as the start_address.
- * This is where the mask ROM will jump to after verifying the header.
- *
- * The header sets copy_size to min(sizeof(zImage), MAX_BOOT_PROG_LEN) + ALIGN.
- * That is, the mask ROM will load the padded header (ALIGN bytes)
- * And then MAX_BOOT_PROG_LEN bytes of the image, or the entire image,
- * whichever is smaller.
- *
- * The zImage is not modified in any way.
- */
-
-#define _BSD_SOURCE
-#include <endian.h>
-#include <unistd.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <errno.h>
-#include <tools/endian.h>
-
-struct hdr {
- uint32_t magic1;
- uint32_t reserved1;
- uint32_t magic2;
- uint32_t reserved2;
- uint16_t copy_size;
- uint16_t boot_options;
- uint32_t reserved3;
- uint32_t start_address;
- uint32_t reserved4;
- uint32_t reserved5;
- char reserved6[308];
-};
-
-#define DECLARE_HDR(h) \
- struct hdr (h) = { \
- .magic1 = htole32(0xea000000), \
- .reserved1 = htole32(0x56), \
- .magic2 = htole32(0xe59ff008), \
- .reserved3 = htole16(0x1) }
-
-/* Align to 512 bytes, the MMCIF sector size */
-#define ALIGN_BITS 9
-#define ALIGN (1 << ALIGN_BITS)
-
-#define START_BASE 0xe55b0000
-
-/*
- * With an alignment of 512 the header uses the first sector.
- * There is a 128 sector (64kbyte) limit on the data loaded by the mask ROM.
- * So there are 127 sectors left for the boot programme. But in practice
- * Only a small portion of a zImage is needed, 16 sectors should be more
- * than enough.
- *
- * Note that this sets how much of the zImage is copied by the mask ROM.
- * The entire zImage is present after the header and is loaded
- * by the code in the boot program (which is the first portion of the zImage).
- */
-#define MAX_BOOT_PROG_LEN (16 * 512)
-
-#define ROUND_UP(x) ((x + ALIGN - 1) & ~(ALIGN - 1))
-
-static ssize_t do_read(int fd, void *buf, size_t count)
-{
- size_t offset = 0;
- ssize_t l;
-
- while (offset < count) {
- l = read(fd, buf + offset, count - offset);
- if (!l)
- break;
- if (l < 0) {
- if (errno == EAGAIN || errno == EWOULDBLOCK)
- continue;
- perror("read");
- return -1;
- }
- offset += l;
- }
-
- return offset;
-}
-
-static ssize_t do_write(int fd, const void *buf, size_t count)
-{
- size_t offset = 0;
- ssize_t l;
-
- while (offset < count) {
- l = write(fd, buf + offset, count - offset);
- if (l < 0) {
- if (errno == EAGAIN || errno == EWOULDBLOCK)
- continue;
- perror("write");
- return -1;
- }
- offset += l;
- }
-
- return offset;
-}
-
-static ssize_t write_zero(int fd, size_t len)
-{
- size_t i = len;
-
- while (i--) {
- const char x = 0;
- if (do_write(fd, &x, 1) < 0)
- return -1;
- }
-
- return len;
-}
-
-int main(void)
-{
- DECLARE_HDR(hdr);
- char boot_program[MAX_BOOT_PROG_LEN];
- size_t aligned_hdr_len, alligned_prog_len;
- ssize_t prog_len;
-
- prog_len = do_read(0, boot_program, sizeof(boot_program));
- if (prog_len <= 0)
- return -1;
-
- aligned_hdr_len = ROUND_UP(sizeof(hdr));
- hdr.start_address = htole32(START_BASE + aligned_hdr_len);
- alligned_prog_len = ROUND_UP(prog_len);
- hdr.copy_size = htole16(aligned_hdr_len + alligned_prog_len);
-
- if (do_write(1, &hdr, sizeof(hdr)) < 0)
- return -1;
- if (write_zero(1, aligned_hdr_len - sizeof(hdr)) < 0)
- return -1;
-
- if (do_write(1, boot_program, prog_len) < 0)
- return 1;
-
- /* Write out the rest of the kernel */
- while (1) {
- prog_len = do_read(0, boot_program, sizeof(boot_program));
- if (prog_len < 0)
- return 1;
- if (prog_len == 0)
- break;
- if (do_write(1, boot_program, prog_len) < 0)
- return 1;
- }
-
- return 0;
-}
diff --git a/Documentation/arm/SH-Mobile/zboot-rom-mmcif.txt b/Documentation/arm/SH-Mobile/zboot-rom-mmcif.txt
deleted file mode 100644
index efff8ae..0000000
--- a/Documentation/arm/SH-Mobile/zboot-rom-mmcif.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-ROM-able zImage boot from MMC
------------------------------
-
-An ROM-able zImage compiled with ZBOOT_ROM_MMCIF may be written to MMC and
-SuperH Mobile ARM will to boot directly from the MMCIF hardware block.
-
-This is achieved by the mask ROM loading the first portion of the image into
-MERAM and then jumping to it. This portion contains loader code which
-copies the entire image to SDRAM and jumps to it. From there the zImage
-boot code proceeds as normal, uncompressing the image into its final
-location and then jumping to it.
-
-This code has been tested on an AP4EB board using the developer 1A eMMC
-boot mode which is configured using the following jumper settings.
-The board used for testing required a patched mask ROM in order for
-this mode to function.
-
- 8 7 6 5 4 3 2 1
- x|x|x|x|x| |x|
-S4 -+-+-+-+-+-+-+-
- | | | | |x| |x on
-
-The zImage must be written to the MMC card at sector 1 (512 bytes) in
-vrl4 format. A utility vrl4 is supplied to accomplish this.
-
-e.g.
- vrl4 < zImage | dd of=/dev/sdX bs=512 seek=1
-
-A dual-voltage MMC 4.0 card was used for testing.
diff --git a/Documentation/arm/SH-Mobile/zboot-rom-sdhi.txt b/Documentation/arm/SH-Mobile/zboot-rom-sdhi.txt
deleted file mode 100644
index 4419598..0000000
--- a/Documentation/arm/SH-Mobile/zboot-rom-sdhi.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-ROM-able zImage boot from eSD
------------------------------
-
-An ROM-able zImage compiled with ZBOOT_ROM_SDHI may be written to eSD and
-SuperH Mobile ARM will to boot directly from the SDHI hardware block.
-
-This is achieved by the mask ROM loading the first portion of the image into
-MERAM and then jumping to it. This portion contains loader code which
-copies the entire image to SDRAM and jumps to it. From there the zImage
-boot code proceeds as normal, uncompressing the image into its final
-location and then jumping to it.
-
-This code has been tested on an mackerel board using the developer 1A eSD
-boot mode which is configured using the following jumper settings.
-
- 8 7 6 5 4 3 2 1
- x|x|x|x| |x|x|
-S4 -+-+-+-+-+-+-+-
- | | | |x| | |x on
-
-The eSD card needs to be present in SDHI slot 1 (CN7).
-As such S1 and S33 also need to be configured as per
-the notes in arch/arm/mach-shmobile/board-mackerel.c.
-
-A partial zImage must be written to physical partition #1 (boot)
-of the eSD at sector 0 in vrl4 format. A utility vrl4 is supplied to
-accomplish this.
-
-e.g.
- vrl4 < zImage | dd of=/dev/sdX bs=512 count=17
-
-A full copy of _the same_ zImage should be written to physical partition #1
-(boot) of the eSD at sector 0. This should _not_ be in vrl4 format.
-
- vrl4 < zImage | dd of=/dev/sdX bs=512
-
-Note: The commands above assume that the physical partition has been
-switched. No such facility currently exists in the Linux Kernel.
-
-Physical partitions are described in the eSD specification. At the time of
-writing they are not the same as partitions that are typically configured
-using fdisk and visible through /proc/partitions
diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt
index 4ab09f2..e6d5a4a 100644
--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt
+++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt
@@ -30,6 +30,10 @@
- target-supply : regulator for SATA target power
- phys : reference to the SATA PHY node
- phy-names : must be "sata-phy"
+- ports-implemented : Mask that indicates which ports that the HBA supports
+ are available for software to use. Useful if PORTS_IMPL
+ is not programmed by the BIOS, which is true with
+ some embedded SOC's.
Required properties when using sub-nodes:
- #address-cells : number of cells to encode an address
diff --git a/Documentation/devicetree/bindings/crypto/samsung-sss.txt b/Documentation/devicetree/bindings/crypto/samsung-sss.txt
index a6dafa8..7a5ca56 100644
--- a/Documentation/devicetree/bindings/crypto/samsung-sss.txt
+++ b/Documentation/devicetree/bindings/crypto/samsung-sss.txt
@@ -23,10 +23,8 @@
- "samsung,exynos4210-secss" for Exynos4210, Exynos4212, Exynos4412, Exynos5250,
Exynos5260 and Exynos5420 SoCs.
- reg : Offset and length of the register set for the module
-- interrupts : interrupt specifiers of SSS module interrupts, should contain
- following entries:
- - first : feed control interrupt (required for all variants),
- - second : hash interrupt (required only for samsung,s5pv210-secss).
+- interrupts : interrupt specifiers of SSS module interrupts (one feed
+ control interrupt).
- clocks : list of clock phandle and specifier pairs for all clocks listed in
clock-names property.
diff --git a/Documentation/devicetree/bindings/misc/memory-state-time.txt b/Documentation/devicetree/bindings/misc/memory-state-time.txt
new file mode 100644
index 0000000..c99a506
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/memory-state-time.txt
@@ -0,0 +1,8 @@
+Memory bandwidth and frequency state tracking
+
+Required properties:
+- compatible : should be:
+ "memory-state-time"
+- freq-tbl: Should contain entries with each frequency in Hz.
+- bw-buckets: Should contain upper-bound limits for each bandwidth bucket in Mbps.
+ Must match the framework power_profile.xml for the device.
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 20b8644..480c62d 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2957,6 +2957,13 @@
quiescent states. Units are jiffies, minimum
value is one, and maximum value is HZ.
+ rcutree.kthread_prio= [KNL,BOOT]
+ Set the SCHED_FIFO priority of the RCU
+ per-CPU kthreads (rcuc/N). This value is also
+ used for the priority of the RCU boost threads
+ (rcub/N). Valid values are 1-99 and the default
+ is 1 (the least-favored priority).
+
rcutree.rcu_nocb_leader_stride= [KNL]
Set the number of NOCB kthread groups, which
defaults to the square root of the number of
@@ -3180,6 +3187,10 @@
ro [KNL] Mount root device read-only on boot
+ rodata= [KNL]
+ on Mark read-only kernel memory as read-only (default).
+ off Leave read-only kernel memory writable for debugging.
+
root= [KNL] Root filesystem
See name_to_dev_t comment in init/do_mounts.c.
@@ -3674,6 +3685,8 @@
sector if the number is odd);
i = IGNORE_DEVICE (don't bind to this
device);
+ j = NO_REPORT_LUNS (don't use report luns
+ command, uas only);
l = NOT_LOCKABLE (don't try to lock and
unlock ejectable media);
m = MAX_SECTORS_64 (don't transfer more
diff --git a/Documentation/mic/Makefile b/Documentation/mic/Makefile
deleted file mode 100644
index a191d45..0000000
--- a/Documentation/mic/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := mpssd
diff --git a/Documentation/mic/mpssd/Makefile b/Documentation/mic/mpssd/Makefile
deleted file mode 100644
index 0f31568..0000000
--- a/Documentation/mic/mpssd/Makefile
+++ /dev/null
@@ -1,19 +0,0 @@
-# List of programs to build
-hostprogs-y := mpssd
-
-mpssd-objs := mpssd.o sysfs.o
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS += -I$(objtree)/usr/include -I$(srctree)/tools/include
-
-ifdef DEBUG
-HOSTCFLAGS += -DDEBUG=$(DEBUG)
-endif
-
-HOSTLOADLIBES_mpssd := -lpthread
-
-install:
- install mpssd /usr/sbin/mpssd
- install micctrl /usr/sbin/micctrl
diff --git a/Documentation/module-signing.txt b/Documentation/module-signing.txt
index 09c2382..e3af8c2 100644
--- a/Documentation/module-signing.txt
+++ b/Documentation/module-signing.txt
@@ -239,3 +239,9 @@
the private key to sign modules and compromise the operating system. The
private key must be either destroyed or moved to a secure location and not kept
in the root node of the kernel source tree.
+
+If you use the same private key to sign modules for multiple kernel
+configurations, you must ensure that the module version information is
+sufficient to prevent loading a module into a different kernel. Either
+set CONFIG_MODVERSIONS=y or ensure that each configuration has a different
+kernel release string by changing EXTRAVERSION or CONFIG_LOCALVERSION.
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 37b68fd..9e8727f 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -1281,11 +1281,20 @@
Functional default: enabled if accept_ra is enabled.
disabled if accept_ra is disabled.
+accept_ra_rt_info_min_plen - INTEGER
+ Minimum prefix length of Route Information in RA.
+
+ Route Information w/ prefix smaller than this variable shall
+ be ignored.
+
+ Functional default: 0 if accept_ra_rtr_pref is enabled.
+ -1 if accept_ra_rtr_pref is disabled.
+
accept_ra_rt_info_max_plen - INTEGER
Maximum prefix length of Route Information in RA.
- Route Information w/ prefix larger than or equal to this
- variable shall be ignored.
+ Route Information w/ prefix larger than this variable shall
+ be ignored.
Functional default: 0 if accept_ra_rtr_pref is enabled.
-1 if accept_ra_rtr_pref is disabled.
diff --git a/Documentation/networking/netlink_mmap.txt b/Documentation/networking/netlink_mmap.txt
deleted file mode 100644
index c6af4ba..0000000
--- a/Documentation/networking/netlink_mmap.txt
+++ /dev/null
@@ -1,339 +0,0 @@
-This file documents how to use memory mapped I/O with netlink.
-
-Author: Patrick McHardy <kaber@trash.net>
-
-Overview
---------
-
-Memory mapped netlink I/O can be used to increase throughput and decrease
-overhead of unicast receive and transmit operations. Some netlink subsystems
-require high throughput, these are mainly the netfilter subsystems
-nfnetlink_queue and nfnetlink_log, but it can also help speed up large
-dump operations of f.i. the routing database.
-
-Memory mapped netlink I/O used two circular ring buffers for RX and TX which
-are mapped into the processes address space.
-
-The RX ring is used by the kernel to directly construct netlink messages into
-user-space memory without copying them as done with regular socket I/O,
-additionally as long as the ring contains messages no recvmsg() or poll()
-syscalls have to be issued by user-space to get more message.
-
-The TX ring is used to process messages directly from user-space memory, the
-kernel processes all messages contained in the ring using a single sendmsg()
-call.
-
-Usage overview
---------------
-
-In order to use memory mapped netlink I/O, user-space needs three main changes:
-
-- ring setup
-- conversion of the RX path to get messages from the ring instead of recvmsg()
-- conversion of the TX path to construct messages into the ring
-
-Ring setup is done using setsockopt() to provide the ring parameters to the
-kernel, then a call to mmap() to map the ring into the processes address space:
-
-- setsockopt(fd, SOL_NETLINK, NETLINK_RX_RING, ¶ms, sizeof(params));
-- setsockopt(fd, SOL_NETLINK, NETLINK_TX_RING, ¶ms, sizeof(params));
-- ring = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)
-
-Usage of either ring is optional, but even if only the RX ring is used the
-mapping still needs to be writable in order to update the frame status after
-processing.
-
-Conversion of the reception path involves calling poll() on the file
-descriptor, once the socket is readable the frames from the ring are
-processed in order until no more messages are available, as indicated by
-a status word in the frame header.
-
-On kernel side, in order to make use of memory mapped I/O on receive, the
-originating netlink subsystem needs to support memory mapped I/O, otherwise
-it will use an allocated socket buffer as usual and the contents will be
- copied to the ring on transmission, nullifying most of the performance gains.
-Dumps of kernel databases automatically support memory mapped I/O.
-
-Conversion of the transmit path involves changing message construction to
-use memory from the TX ring instead of (usually) a buffer declared on the
-stack and setting up the frame header appropriately. Optionally poll() can
-be used to wait for free frames in the TX ring.
-
-Structured and definitions for using memory mapped I/O are contained in
-<linux/netlink.h>.
-
-RX and TX rings
-----------------
-
-Each ring contains a number of continuous memory blocks, containing frames of
-fixed size dependent on the parameters used for ring setup.
-
-Ring: [ block 0 ]
- [ frame 0 ]
- [ frame 1 ]
- [ block 1 ]
- [ frame 2 ]
- [ frame 3 ]
- ...
- [ block n ]
- [ frame 2 * n ]
- [ frame 2 * n + 1 ]
-
-The blocks are only visible to the kernel, from the point of view of user-space
-the ring just contains the frames in a continuous memory zone.
-
-The ring parameters used for setting up the ring are defined as follows:
-
-struct nl_mmap_req {
- unsigned int nm_block_size;
- unsigned int nm_block_nr;
- unsigned int nm_frame_size;
- unsigned int nm_frame_nr;
-};
-
-Frames are grouped into blocks, where each block is a continuous region of memory
-and holds nm_block_size / nm_frame_size frames. The total number of frames in
-the ring is nm_frame_nr. The following invariants hold:
-
-- frames_per_block = nm_block_size / nm_frame_size
-
-- nm_frame_nr = frames_per_block * nm_block_nr
-
-Some parameters are constrained, specifically:
-
-- nm_block_size must be a multiple of the architectures memory page size.
- The getpagesize() function can be used to get the page size.
-
-- nm_frame_size must be equal or larger to NL_MMAP_HDRLEN, IOW a frame must be
- able to hold at least the frame header
-
-- nm_frame_size must be smaller or equal to nm_block_size
-
-- nm_frame_size must be a multiple of NL_MMAP_MSG_ALIGNMENT
-
-- nm_frame_nr must equal the actual number of frames as specified above.
-
-When the kernel can't allocate physically continuous memory for a ring block,
-it will fall back to use physically discontinuous memory. This might affect
-performance negatively, in order to avoid this the nm_frame_size parameter
-should be chosen to be as small as possible for the required frame size and
-the number of blocks should be increased instead.
-
-Ring frames
-------------
-
-Each frames contain a frame header, consisting of a synchronization word and some
-meta-data, and the message itself.
-
-Frame: [ header message ]
-
-The frame header is defined as follows:
-
-struct nl_mmap_hdr {
- unsigned int nm_status;
- unsigned int nm_len;
- __u32 nm_group;
- /* credentials */
- __u32 nm_pid;
- __u32 nm_uid;
- __u32 nm_gid;
-};
-
-- nm_status is used for synchronizing processing between the kernel and user-
- space and specifies ownership of the frame as well as the operation to perform
-
-- nm_len contains the length of the message contained in the data area
-
-- nm_group specified the destination multicast group of message
-
-- nm_pid, nm_uid and nm_gid contain the netlink pid, UID and GID of the sending
- process. These values correspond to the data available using SOCK_PASSCRED in
- the SCM_CREDENTIALS cmsg.
-
-The possible values in the status word are:
-
-- NL_MMAP_STATUS_UNUSED:
- RX ring: frame belongs to the kernel and contains no message
- for user-space. Approriate action is to invoke poll()
- to wait for new messages.
-
- TX ring: frame belongs to user-space and can be used for
- message construction.
-
-- NL_MMAP_STATUS_RESERVED:
- RX ring only: frame is currently used by the kernel for message
- construction and contains no valid message yet.
- Appropriate action is to invoke poll() to wait for
- new messages.
-
-- NL_MMAP_STATUS_VALID:
- RX ring: frame contains a valid message. Approriate action is
- to process the message and release the frame back to
- the kernel by setting the status to
- NL_MMAP_STATUS_UNUSED or queue the frame by setting the
- status to NL_MMAP_STATUS_SKIP.
-
- TX ring: the frame contains a valid message from user-space to
- be processed by the kernel. After completing processing
- the kernel will release the frame back to user-space by
- setting the status to NL_MMAP_STATUS_UNUSED.
-
-- NL_MMAP_STATUS_COPY:
- RX ring only: a message is ready to be processed but could not be
- stored in the ring, either because it exceeded the
- frame size or because the originating subsystem does
- not support memory mapped I/O. Appropriate action is
- to invoke recvmsg() to receive the message and release
- the frame back to the kernel by setting the status to
- NL_MMAP_STATUS_UNUSED.
-
-- NL_MMAP_STATUS_SKIP:
- RX ring only: user-space queued the message for later processing, but
- processed some messages following it in the ring. The
- kernel should skip this frame when looking for unused
- frames.
-
-The data area of a frame begins at a offset of NL_MMAP_HDRLEN relative to the
-frame header.
-
-TX limitations
---------------
-
-Kernel processing usually involves validation of the message received by
-user-space, then processing its contents. The kernel must assure that
-userspace is not able to modify the message contents after they have been
-validated. In order to do so, the message is copied from the ring frame
-to an allocated buffer if either of these conditions is false:
-
-- only a single mapping of the ring exists
-- the file descriptor is not shared between processes
-
-This means that for threaded programs, the kernel will fall back to copying.
-
-Example
--------
-
-Ring setup:
-
- unsigned int block_size = 16 * getpagesize();
- struct nl_mmap_req req = {
- .nm_block_size = block_size,
- .nm_block_nr = 64,
- .nm_frame_size = 16384,
- .nm_frame_nr = 64 * block_size / 16384,
- };
- unsigned int ring_size;
- void *rx_ring, *tx_ring;
-
- /* Configure ring parameters */
- if (setsockopt(fd, SOL_NETLINK, NETLINK_RX_RING, &req, sizeof(req)) < 0)
- exit(1);
- if (setsockopt(fd, SOL_NETLINK, NETLINK_TX_RING, &req, sizeof(req)) < 0)
- exit(1)
-
- /* Calculate size of each individual ring */
- ring_size = req.nm_block_nr * req.nm_block_size;
-
- /* Map RX/TX rings. The TX ring is located after the RX ring */
- rx_ring = mmap(NULL, 2 * ring_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0);
- if ((long)rx_ring == -1L)
- exit(1);
- tx_ring = rx_ring + ring_size:
-
-Message reception:
-
-This example assumes some ring parameters of the ring setup are available.
-
- unsigned int frame_offset = 0;
- struct nl_mmap_hdr *hdr;
- struct nlmsghdr *nlh;
- unsigned char buf[16384];
- ssize_t len;
-
- while (1) {
- struct pollfd pfds[1];
-
- pfds[0].fd = fd;
- pfds[0].events = POLLIN | POLLERR;
- pfds[0].revents = 0;
-
- if (poll(pfds, 1, -1) < 0 && errno != -EINTR)
- exit(1);
-
- /* Check for errors. Error handling omitted */
- if (pfds[0].revents & POLLERR)
- <handle error>
-
- /* If no new messages, poll again */
- if (!(pfds[0].revents & POLLIN))
- continue;
-
- /* Process all frames */
- while (1) {
- /* Get next frame header */
- hdr = rx_ring + frame_offset;
-
- if (hdr->nm_status == NL_MMAP_STATUS_VALID) {
- /* Regular memory mapped frame */
- nlh = (void *)hdr + NL_MMAP_HDRLEN;
- len = hdr->nm_len;
-
- /* Release empty message immediately. May happen
- * on error during message construction.
- */
- if (len == 0)
- goto release;
- } else if (hdr->nm_status == NL_MMAP_STATUS_COPY) {
- /* Frame queued to socket receive queue */
- len = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
- if (len <= 0)
- break;
- nlh = buf;
- } else
- /* No more messages to process, continue polling */
- break;
-
- process_msg(nlh);
-release:
- /* Release frame back to the kernel */
- hdr->nm_status = NL_MMAP_STATUS_UNUSED;
-
- /* Advance frame offset to next frame */
- frame_offset = (frame_offset + frame_size) % ring_size;
- }
- }
-
-Message transmission:
-
-This example assumes some ring parameters of the ring setup are available.
-A single message is constructed and transmitted, to send multiple messages
-at once they would be constructed in consecutive frames before a final call
-to sendto().
-
- unsigned int frame_offset = 0;
- struct nl_mmap_hdr *hdr;
- struct nlmsghdr *nlh;
- struct sockaddr_nl addr = {
- .nl_family = AF_NETLINK,
- };
-
- hdr = tx_ring + frame_offset;
- if (hdr->nm_status != NL_MMAP_STATUS_UNUSED)
- /* No frame available. Use poll() to avoid. */
- exit(1);
-
- nlh = (void *)hdr + NL_MMAP_HDRLEN;
-
- /* Build message */
- build_message(nlh);
-
- /* Fill frame header: length and status need to be set */
- hdr->nm_len = nlh->nlmsg_len;
- hdr->nm_status = NL_MMAP_STATUS_VALID;
-
- if (sendto(fd, NULL, 0, 0, &addr, sizeof(addr)) < 0)
- exit(1);
-
- /* Advance frame offset to next frame */
- frame_offset = (frame_offset + frame_size) % ring_size;
diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt
index 5a615c14..cb12026 100644
--- a/Documentation/printk-formats.txt
+++ b/Documentation/printk-formats.txt
@@ -45,6 +45,11 @@
users. The behaviour of %pK depends on the kptr_restrict sysctl - see
Documentation/sysctl/kernel.txt for more details.
+ %pP 0x01234567 or 0x0123456789abcdef
+
+ For printing kernel pointers which should always be shown, even to
+ unprivileged users.
+
Struct Resources:
%pr [mem 0x60000000-0x6fffffff flags 0x2200] or
diff --git a/Documentation/scsi/scsi_eh.txt b/Documentation/scsi/scsi_eh.txt
index a0c85110..689ab9b 100644
--- a/Documentation/scsi/scsi_eh.txt
+++ b/Documentation/scsi/scsi_eh.txt
@@ -263,19 +263,23 @@
3. scmd recovered
ACTION: scsi_eh_finish_cmd() is invoked to EH-finish scmd
- - shost->host_failed--
- clear scmd->eh_eflags
- scsi_setup_cmd_retry()
- move from local eh_work_q to local eh_done_q
LOCKING: none
+ CONCURRENCY: at most one thread per separate eh_work_q to
+ keep queue manipulation lockless
4. EH completes
ACTION: scsi_eh_flush_done_q() retries scmds or notifies upper
- layer of failure.
+ layer of failure. May be called concurrently but must have
+ a no more than one thread per separate eh_work_q to
+ manipulate the queue locklessly
- scmd is removed from eh_done_q and scmd->eh_entry is cleared
- if retry is necessary, scmd is requeued using
scsi_queue_insert()
- otherwise, scsi_finish_command() is invoked for scmd
+ - zero shost->host_failed
LOCKING: queue or finish function performs appropriate locking
diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt
index 88152f2..302b5ed 100644
--- a/Documentation/sysctl/fs.txt
+++ b/Documentation/sysctl/fs.txt
@@ -32,6 +32,8 @@
- nr_open
- overflowuid
- overflowgid
+- pipe-user-pages-hard
+- pipe-user-pages-soft
- protected_hardlinks
- protected_symlinks
- suid_dumpable
@@ -159,6 +161,27 @@
==============================================================
+pipe-user-pages-hard:
+
+Maximum total number of pages a non-privileged user may allocate for pipes.
+Once this limit is reached, no new pipes may be allocated until usage goes
+below the limit again. When set to 0, no limit is applied, which is the default
+setting.
+
+==============================================================
+
+pipe-user-pages-soft:
+
+Maximum total number of pages a non-privileged user may allocate for pipes
+before the pipe size gets limited to a single page. Once this limit is reached,
+new pipes will be limited to a single page in size for this user in order to
+limit total memory usage, and trying to increase them using fcntl() will be
+denied until usage goes below the limit again. The default value allows to
+allocate up to 1024 pipes at their default size. When set to 0, no limit is
+applied.
+
+==============================================================
+
protected_hardlinks:
A long-standing class of security issues is the hardlink-based
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index 7fc2d02..8bc4f80 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -58,6 +58,7 @@
- panic_on_oops
- panic_on_stackoverflow
- panic_on_unrecovered_nmi
+- panic_on_rcu_stall
- perf_cpu_time_max_percent
- perf_event_paranoid
- pid_max
@@ -397,6 +398,15 @@
When kptr_restrict is set to (2), kernel pointers printed using
%pK will be replaced with 0's regardless of privileges.
+When kptr_restrict is set to (3), kernel pointers printed using
+%p and %pK will be replaced with 0's regardless of privileges,
+however kernel pointers printed using %pP will continue to be printed.
+
+When kptr_restrict is set to (4), kernel pointers printed with
+%p, %pK, %pa, and %p[rR] will be replaced with 0's regardless of
+privileges. Kernel pointers printed using %pP will continue to be
+printed.
+
==============================================================
kstack_depth_to_print: (X86 only)
@@ -590,6 +600,17 @@
==============================================================
+panic_on_rcu_stall:
+
+When set to 1, calls panic() after RCU stall detection messages. This
+is useful to define the root cause of RCU stalls using a vmcore.
+
+0: do not panic() when RCU stall takes place, default behavior.
+
+1: panic() after printing RCU stall messages.
+
+==============================================================
+
perf_cpu_time_max_percent:
Hints to the kernel how much CPU time it should be allowed to
diff --git a/Documentation/usb/typec.rst b/Documentation/usb/typec.rst
new file mode 100644
index 0000000..e9b4ad4
--- /dev/null
+++ b/Documentation/usb/typec.rst
@@ -0,0 +1,185 @@
+
+USB Type-C connector class
+==========================
+
+Introduction
+------------
+
+The typec class is meant for describing the USB Type-C ports in a system to the
+user space in unified fashion. The class is designed to provide nothing else
+except the user space interface implementation in hope that it can be utilized
+on as many platforms as possible.
+
+The platforms are expected to register every USB Type-C port they have with the
+class. In a normal case the registration will be done by a USB Type-C or PD PHY
+driver, but it may be a driver for firmware interface such as UCSI, driver for
+USB PD controller or even driver for Thunderbolt3 controller. This document
+considers the component registering the USB Type-C ports with the class as "port
+driver".
+
+On top of showing the capabilities, the class also offer user space control over
+the roles and alternate modes of ports, partners and cable plugs when the port
+driver is capable of supporting those features.
+
+The class provides an API for the port drivers described in this document. The
+attributes are described in Documentation/ABI/testing/sysfs-class-typec.
+
+User space interface
+--------------------
+Every port will be presented as its own device under /sys/class/typec/. The
+first port will be named "port0", the second "port1" and so on.
+
+When connected, the partner will be presented also as its own device under
+/sys/class/typec/. The parent of the partner device will always be the port it
+is attached to. The partner attached to port "port0" will be named
+"port0-partner". Full path to the device would be
+/sys/class/typec/port0/port0-partner/.
+
+The cable and the two plugs on it may also be optionally presented as their own
+devices under /sys/class/typec/. The cable attached to the port "port0" port
+will be named port0-cable and the plug on the SOP Prime end (see USB Power
+Delivery Specification ch. 2.4) will be named "port0-plug0" and on the SOP
+Double Prime end "port0-plug1". The parent of a cable will always be the port,
+and the parent of the cable plugs will always be the cable.
+
+If the port, partner or cable plug support Alternate Modes, every supported
+Alternate Mode SVID will have their own device describing them. The Alternate
+Modes will not be attached to the typec class. The parent of an alternate mode
+will be the device that supports it, so for example an alternate mode of
+port0-partner will bees presented under /sys/class/typec/port0-partner/. Every
+mode that is supported will have its own group under the Alternate Mode device
+named "mode<index>", for example /sys/class/typec/port0/<alternate mode>/mode1/.
+The requests for entering/exiting a mode can be done with "active" attribute
+file in that group.
+
+Driver API
+----------
+
+Registering the ports
+~~~~~~~~~~~~~~~~~~~~~
+
+The port drivers will describe every Type-C port they control with struct
+typec_capability data structure, and register them with the following API:
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_register_port typec_unregister_port
+
+When registering the ports, the prefer_role member in struct typec_capability
+deserves special notice. If the port that is being registered does not have
+initial role preference, which means the port does not execute Try.SNK or
+Try.SRC by default, the member must have value TYPEC_NO_PREFERRED_ROLE.
+Otherwise if the port executes Try.SNK by default the member must have value
+TYPEC_DEVICE and with Try.SRC the value must be TYPEC_HOST.
+
+Registering Partners
+~~~~~~~~~~~~~~~~~~~~
+
+After successful connection of a partner, the port driver needs to register the
+partner with the class. Details about the partner need to be described in struct
+typec_partner_desc. The class copies the details of the partner during
+registration. The class offers the following API for registering/unregistering
+partners.
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_register_partner typec_unregister_partner
+
+The class will provide a handle to struct typec_partner if the registration was
+successful, or NULL.
+
+If the partner is USB Power Delivery capable, and the port driver is able to
+show the result of Discover Identity command, the partner descriptor structure
+should include handle to struct usb_pd_identity instance. The class will then
+create a sysfs directory for the identity under the partner device. The result
+of Discover Identity command can then be reported with the following API:
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_partner_set_identity
+
+Registering Cables
+~~~~~~~~~~~~~~~~~~
+
+After successful connection of a cable that supports USB Power Delivery
+Structured VDM "Discover Identity", the port driver needs to register the cable
+and one or two plugs, depending if there is CC Double Prime controller present
+in the cable or not. So a cable capable of SOP Prime communication, but not SOP
+Double Prime communication, should only have one plug registered. For more
+information about SOP communication, please read chapter about it from the
+latest USB Power Delivery specification.
+
+The plugs are represented as their own devices. The cable is registered first,
+followed by registration of the cable plugs. The cable will be the parent device
+for the plugs. Details about the cable need to be described in struct
+typec_cable_desc and about a plug in struct typec_plug_desc. The class copies
+the details during registration. The class offers the following API for
+registering/unregistering cables and their plugs:
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_register_cable typec_unregister_cable typec_register_plug
+ typec_unregister_plug
+
+The class will provide a handle to struct typec_cable and struct typec_plug if
+the registration is successful, or NULL if it isn't.
+
+If the cable is USB Power Delivery capable, and the port driver is able to show
+the result of Discover Identity command, the cable descriptor structure should
+include handle to struct usb_pd_identity instance. The class will then create a
+sysfs directory for the identity under the cable device. The result of Discover
+Identity command can then be reported with the following API:
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_cable_set_identity
+
+Notifications
+~~~~~~~~~~~~~
+
+When the partner has executed a role change, or when the default roles change
+during connection of a partner or cable, the port driver must use the following
+APIs to report it to the class:
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_set_data_role typec_set_pwr_role typec_set_vconn_role
+ typec_set_pwr_opmode
+
+Alternate Modes
+~~~~~~~~~~~~~~~
+
+USB Type-C Ports, Partners and Cable Plugs may support Alternate Modes with USB
+Type-C. Each Alternate Mode will have SVID, which is either a Standard ID given
+by USB-IF or vendor ID. Each supported SVID can have 1 - 6 modes. The class
+provides struct typec_mode_desc for describing individual mode of a SVID, and
+struct typec_altmode_desc which is a container of all the modes of a SVID that a
+port, partner or cable plug supports.
+
+Ports that support Alternate Modes need to register each SVID they support with
+the following API:
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_port_register_altmode
+
+If a partner or cable plug provides a list of SVIDs as response to USB Power
+Delivery Structured VDM Discover SVIDs message, each SVID needs to be
+registered.
+
+API for the partners:
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_partner_register_altmode
+
+API for the Cable Plugs:
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_plug_register_altmode
+
+So ports, partners and cable plugs will register the alternate modes with their
+own functions, but the registration will always return a handle to struct
+typec_altmode on success, or NULL. The unregistration will happen with the same
+function:
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_unregister_altmode
+
+If a partner or cable plug enters or exits a mode, the port driver needs to
+notify the class with the following API:
+
+.. kernel-doc:: drivers/usb/typec/typec.c
+ :functions: typec_altmode_update_active
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index 702bb25..20e6898 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -1919,6 +1919,7 @@
PPC | KVM_REG_PPC_TM_VSCR | 32
PPC | KVM_REG_PPC_TM_DSCR | 64
PPC | KVM_REG_PPC_TM_TAR | 64
+ PPC | KVM_REG_PPC_TM_XER | 64
| |
MIPS | KVM_REG_MIPS_R0 | 64
...
diff --git a/MAINTAINERS b/MAINTAINERS
index 25f8c27..9b91bd2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1837,7 +1837,8 @@
F: net/ax25/
AZ6007 DVB DRIVER
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -2207,7 +2208,8 @@
F: fs/btrfs/
BTTV VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -2728,7 +2730,8 @@
F: include/media/cx2341x*
CX88 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -3411,7 +3414,8 @@
EDAC-CORE
M: Doug Thompson <dougthompson@xmission.com>
M: Borislav Petkov <bp@alien8.de>
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Supported
@@ -3460,7 +3464,8 @@
F: drivers/edac/e7xxx_edac.c
EDAC-GHES
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
@@ -3488,21 +3493,24 @@
F: drivers/edac/i5000_edac.c
EDAC-I5400
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i5400_edac.c
EDAC-I7300
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
F: drivers/edac/i7300_edac.c
EDAC-I7CORE
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
@@ -3545,7 +3553,8 @@
F: drivers/edac/r82600_edac.c
EDAC-SBRIDGE
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-edac@vger.kernel.org
W: bluesmoke.sourceforge.net
S: Maintained
@@ -3567,8 +3576,8 @@
F: arch/ia64/kernel/efi.c
F: arch/x86/boot/compressed/eboot.[ch]
F: arch/x86/include/asm/efi.h
-F: arch/x86/platform/efi/*
-F: drivers/firmware/efi/*
+F: arch/x86/platform/efi/
+F: drivers/firmware/efi/
F: include/linux/efi*.h
EFI VARIABLE FILESYSTEM
@@ -3605,7 +3614,8 @@
F: drivers/net/ethernet/ibm/ehea/
EM28XX VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -5971,7 +5981,8 @@
F: drivers/media/radio/radio-maxiradio*
MEDIA INPUT INFRASTRUCTURE (V4L/DVB)
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
P: LinuxTV.org Project
L: linux-media@vger.kernel.org
W: http://linuxtv.org
@@ -8030,7 +8041,8 @@
F: drivers/media/i2c/saa6588*
SAA7134 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -8488,7 +8500,8 @@
F: drivers/media/radio/si4713/radio-usb-si4713.c
SIANO DVB DRIVER
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -9177,7 +9190,8 @@
F: drivers/media/i2c/tda9840*
TEA5761 TUNER DRIVER
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -9185,7 +9199,8 @@
F: drivers/media/tuners/tea5761.*
TEA5767 TUNER DRIVER
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -9498,7 +9513,8 @@
F: mm/shmem.c
TM6000 VIDEO4LINUX DRIVER
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
@@ -9866,6 +9882,15 @@
F: include/linux/usb.h
F: include/linux/usb/
+USB TYPEC SUBSYSTEM
+M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: Documentation/ABI/testing/sysfs-class-typec
+F: Documentation/usb/typec.rst
+F: drivers/usb/typec/
+F: include/linux/usb/typec.h
+
USB UHCI DRIVER
M: Alan Stern <stern@rowland.harvard.edu>
L: linux-usb@vger.kernel.org
@@ -10319,7 +10344,8 @@
F: arch/x86/kernel/cpu/mcheck/*
XC2028/3028 TUNER DRIVER
-M: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
+M: Mauro Carvalho Chehab <mchehab@s-opensource.com>
+M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-media@vger.kernel.org
W: http://linuxtv.org
T: git git://linuxtv.org/media_tree.git
diff --git a/Makefile b/Makefile
index cedf406..7945718 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 3
PATCHLEVEL = 18
-SUBLEVEL = 31
+SUBLEVEL = 52
EXTRAVERSION =
NAME = Shuffling Zombie Juror
@@ -376,7 +376,7 @@
LDFLAGS_MODULE =
CFLAGS_KERNEL =
AFLAGS_KERNEL =
-CFLAGS_GCOV = -fprofile-arcs -ftest-coverage
+CFLAGS_GCOV = -fprofile-arcs -ftest-coverage -fno-tree-loop-im
CFLAGS_KCOV = -fsanitize-coverage=trace-pc
@@ -611,6 +611,9 @@
include $(srctree)/arch/$(SRCARCH)/Makefile
KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks,)
+KBUILD_CFLAGS += $(call cc-disable-warning,frame-address,)
+KBUILD_CFLAGS += $(call cc-option,-fno-PIE)
+KBUILD_AFLAGS += $(call cc-option,-fno-PIE)
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,)
@@ -695,9 +698,10 @@
KBUILD_CFLAGS += $(call cc-option, -fcatch-undefined-behavior)
else
-# This warning generated too much noise in a regular build.
-# Use make W=1 to enable this warning (see scripts/Makefile.build)
+# These warnings generated too much noise in a regular build.
+# Use make W=1 to enable them (see scripts/Makefile.build)
KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable)
+KBUILD_CFLAGS += $(call cc-disable-warning, unused-const-variable)
endif
ifdef CONFIG_FRAME_POINTER
diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg
index 073e14c..065b3bb 100644
--- a/android/configs/android-base.cfg
+++ b/android/configs/android-base.cfg
@@ -8,6 +8,7 @@
# CONFIG_SYSVIPC is not set
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_ASHMEM=y
diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg
index 960b9de..7c2a051 100644
--- a/android/configs/android-recommended.cfg
+++ b/android/configs/android-recommended.cfg
@@ -114,6 +114,7 @@
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_UHID=y
CONFIG_UID_STAT=y
+CONFIG_MEMORY_STATE_TIME=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_HIDDEV=y
diff --git a/arch/Kconfig b/arch/Kconfig
index fe8f018..b99f7254 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -411,6 +411,15 @@
endchoice
+config HAVE_ARCH_WITHIN_STACK_FRAMES
+ bool
+ help
+ An architecture should select this if it can walk the kernel stack
+ frames to determine if an object is part of either the arguments
+ or local variables (i.e. that it excludes saved return addresses,
+ and similar) by implementing an inline arch_within_stack_frames(),
+ which is used by CONFIG_HARDENED_USERCOPY.
+
config HAVE_CONTEXT_TRACKING
bool
help
diff --git a/arch/arc/Makefile b/arch/arc/Makefile
index 10bc3d4..dada919 100644
--- a/arch/arc/Makefile
+++ b/arch/arc/Makefile
@@ -34,7 +34,6 @@
cflags-$(CONFIG_ARC_HAS_LLSC) += -mlock
cflags-$(CONFIG_ARC_HAS_SWAPE) += -mswape
cflags-$(CONFIG_ARC_HAS_RTSC) += -mrtsc
-cflags-$(CONFIG_ARC_DW2_UNWIND) += -fasynchronous-unwind-tables
# By default gcc 4.8 generates dwarf4 which kernel unwinder can't grok
ifeq ($(atleast_gcc48),y)
diff --git a/arch/arc/include/asm/entry.h b/arch/arc/include/asm/entry.h
index 8840810..81cdbc3 100644
--- a/arch/arc/include/asm/entry.h
+++ b/arch/arc/include/asm/entry.h
@@ -143,8 +143,6 @@
POP r13
.endm
-#define OFF_USER_R25_FROM_R24 (SZ_CALLEE_REGS + SZ_PT_REGS - 8)/4
-
/*--------------------------------------------------------------
* Collect User Mode callee regs as struct callee_regs - needed by
* fork/do_signal/unaligned-access-emulation.
@@ -157,12 +155,13 @@
*-------------------------------------------------------------*/
.macro SAVE_CALLEE_SAVED_USER
+ mov r12, sp ; save SP as ref to pt_regs
SAVE_R13_TO_R24
#ifdef CONFIG_ARC_CURR_IN_REG
- ; Retrieve orig r25 and save it on stack
- ld.as r12, [sp, OFF_USER_R25_FROM_R24]
- st.a r12, [sp, -4]
+ ; Retrieve orig r25 and save it with rest of callee_regs
+ ld r12, [r12, PT_user_r25]
+ PUSH r12
#else
PUSH r25
#endif
@@ -209,12 +208,16 @@
.macro RESTORE_CALLEE_SAVED_USER
#ifdef CONFIG_ARC_CURR_IN_REG
- ld.ab r12, [sp, 4]
- st.as r12, [sp, OFF_USER_R25_FROM_R24]
+ POP r12
#else
POP r25
#endif
RESTORE_R24_TO_R13
+
+ ; SP is back to start of pt_regs
+#ifdef CONFIG_ARC_CURR_IN_REG
+ st r12, [sp, PT_user_r25]
+#endif
.endm
/*--------------------------------------------------------------
diff --git a/arch/arc/include/asm/irqflags.h b/arch/arc/include/asm/irqflags.h
index 742816f..ec8276d 100644
--- a/arch/arc/include/asm/irqflags.h
+++ b/arch/arc/include/asm/irqflags.h
@@ -159,10 +159,10 @@
.endm
.macro IRQ_ENABLE scratch
+ TRACE_ASM_IRQ_ENABLE
lr \scratch, [status32]
or \scratch, \scratch, (STATUS_E1_MASK | STATUS_E2_MASK)
flag \scratch
- TRACE_ASM_IRQ_ENABLE
.endm
#endif /* __ASSEMBLY__ */
diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h
index 30c9baf..08770c7 100644
--- a/arch/arc/include/asm/uaccess.h
+++ b/arch/arc/include/asm/uaccess.h
@@ -83,7 +83,10 @@
"2: ;nop\n" \
" .section .fixup, \"ax\"\n" \
" .align 4\n" \
- "3: mov %0, %3\n" \
+ "3: # return -EFAULT\n" \
+ " mov %0, %3\n" \
+ " # zero out dst ptr\n" \
+ " mov %1, 0\n" \
" j 2b\n" \
" .previous\n" \
" .section __ex_table, \"a\"\n" \
@@ -101,7 +104,11 @@
"2: ;nop\n" \
" .section .fixup, \"ax\"\n" \
" .align 4\n" \
- "3: mov %0, %3\n" \
+ "3: # return -EFAULT\n" \
+ " mov %0, %3\n" \
+ " # zero out dst ptr\n" \
+ " mov %1, 0\n" \
+ " mov %R1, 0\n" \
" j 2b\n" \
" .previous\n" \
" .section __ex_table, \"a\"\n" \
diff --git a/arch/arc/include/uapi/asm/elf.h b/arch/arc/include/uapi/asm/elf.h
index 0f99ac8..0037a58 100644
--- a/arch/arc/include/uapi/asm/elf.h
+++ b/arch/arc/include/uapi/asm/elf.h
@@ -13,8 +13,15 @@
/* Machine specific ELF Hdr flags */
#define EF_ARC_OSABI_MSK 0x00000f00
-#define EF_ARC_OSABI_ORIG 0x00000000 /* MUST be zero for back-compat */
-#define EF_ARC_OSABI_CURRENT 0x00000300 /* v3 (no legacy syscalls) */
+
+#define EF_ARC_OSABI_V3 0x00000300 /* v3 (no legacy syscalls) */
+#define EF_ARC_OSABI_V4 0x00000400 /* v4 (64bit data any reg align) */
+
+#if __GNUC__ < 6
+#define EF_ARC_OSABI_CURRENT EF_ARC_OSABI_V3
+#else
+#define EF_ARC_OSABI_CURRENT EF_ARC_OSABI_V4
+#endif
typedef unsigned long elf_greg_t;
typedef unsigned long elf_fpregset_t;
diff --git a/arch/arc/kernel/arcksyms.c b/arch/arc/kernel/arcksyms.c
index 4d9e777..000dd04 100644
--- a/arch/arc/kernel/arcksyms.c
+++ b/arch/arc/kernel/arcksyms.c
@@ -28,6 +28,7 @@
extern void __divdf3(void);
extern void __floatunsidf(void);
extern void __floatunsisf(void);
+extern void __udivdi3(void);
EXPORT_SYMBOL(__ashldi3);
EXPORT_SYMBOL(__ashrdi3);
@@ -45,6 +46,7 @@
EXPORT_SYMBOL(__divdf3);
EXPORT_SYMBOL(__floatunsidf);
EXPORT_SYMBOL(__floatunsisf);
+EXPORT_SYMBOL(__udivdi3);
/* ARC optimised assembler routines */
EXPORT_SYMBOL(memset);
diff --git a/arch/arc/kernel/asm-offsets.c b/arch/arc/kernel/asm-offsets.c
index 6c3aa0e..5f14311 100644
--- a/arch/arc/kernel/asm-offsets.c
+++ b/arch/arc/kernel/asm-offsets.c
@@ -59,5 +59,7 @@
DEFINE(SZ_CALLEE_REGS, sizeof(struct callee_regs));
DEFINE(SZ_PT_REGS, sizeof(struct pt_regs));
+ DEFINE(PT_user_r25, offsetof(struct pt_regs, user_r25));
+
return 0;
}
diff --git a/arch/arc/kernel/process.c b/arch/arc/kernel/process.c
index fdd8971..f45b987 100644
--- a/arch/arc/kernel/process.c
+++ b/arch/arc/kernel/process.c
@@ -223,7 +223,7 @@
return 0;
eflags = x->e_flags;
- if ((eflags & EF_ARC_OSABI_MSK) < EF_ARC_OSABI_CURRENT) {
+ if ((eflags & EF_ARC_OSABI_MSK) != EF_ARC_OSABI_CURRENT) {
pr_err("ABI mismatch - you need newer toolchain\n");
force_sigsegv(SIGSEGV, current);
return 0;
diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c
index 252bf60..e5ec478 100644
--- a/arch/arc/kernel/setup.c
+++ b/arch/arc/kernel/setup.c
@@ -234,8 +234,10 @@
cpu->dccm.base_addr, TO_KB(cpu->dccm.sz),
cpu->iccm.base_addr, TO_KB(cpu->iccm.sz));
- n += scnprintf(buf + n, len - n,
- "OS ABI [v3]\t: no-legacy-syscalls\n");
+ n += scnprintf(buf + n, len - n, "OS ABI [v%d]\t: %s\n",
+ EF_ARC_OSABI_CURRENT >> 8,
+ EF_ARC_OSABI_CURRENT == EF_ARC_OSABI_V3 ?
+ "no-legacy-syscalls" : "64-bit data any register aligned");
return buf;
}
diff --git a/arch/arc/kernel/stacktrace.c b/arch/arc/kernel/stacktrace.c
index fb98769..3e349ae 100644
--- a/arch/arc/kernel/stacktrace.c
+++ b/arch/arc/kernel/stacktrace.c
@@ -131,7 +131,7 @@
* prelogue is setup (callee regs saved and then fp set and not other
* way around
*/
- pr_warn("CONFIG_ARC_DW2_UNWIND needs to be enabled\n");
+ pr_warn_once("CONFIG_ARC_DW2_UNWIND needs to be enabled\n");
return 0;
#endif
diff --git a/arch/arc/mm/cache_arc700.c b/arch/arc/mm/cache_arc700.c
index 8c3a3e0..2147ca2 100644
--- a/arch/arc/mm/cache_arc700.c
+++ b/arch/arc/mm/cache_arc700.c
@@ -155,6 +155,15 @@
printk(arc_cache_mumbojumbo(0, str, sizeof(str)));
+ /*
+ * Only master CPU needs to execute rest of function:
+ * - Assume SMP so all cores will have same cache config so
+ * any geomtry checks will be same for all
+ * - IOC setup / dma callbacks only need to be setup once
+ */
+ if (cpu)
+ return;
+
if (IS_ENABLED(CONFIG_ARC_HAS_ICACHE)) {
struct cpuinfo_arc_cache *ic = &cpuinfo_arc700[cpu].icache;
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1677b82..ef70a77 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -30,6 +30,7 @@
select HAVE_ARCH_AUDITSYSCALL if (AEABI && !OABI_COMPAT)
select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL
select HAVE_ARCH_KGDB
+ select HAVE_ARCH_HARDENED_USERCOPY
select HAVE_ARCH_MMAP_RND_BITS if MMU
select HAVE_ARCH_SECCOMP_FILTER if (AEABI && !OABI_COMPAT)
select HAVE_ARCH_TRACEHOOK
diff --git a/arch/arm/boot/dts/kirkwood-ib62x0.dts b/arch/arm/boot/dts/kirkwood-ib62x0.dts
index bfa5edd..2c1e7f0 100644
--- a/arch/arm/boot/dts/kirkwood-ib62x0.dts
+++ b/arch/arm/boot/dts/kirkwood-ib62x0.dts
@@ -113,7 +113,7 @@
partition@e0000 {
label = "u-boot environment";
- reg = <0xe0000 0x100000>;
+ reg = <0xe0000 0x20000>;
};
partition@100000 {
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index ed411ed..acb7aad 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.dtsi
@@ -1856,7 +1856,7 @@
label = "dsps";
};
- qcom,smd-rpm {
+ qcom,smd-rpm-summary {
compatible = "qcom,smd";
qcom,smd-edge = <15>;
qcom,smd-irq-offset = <0x0>;
diff --git a/arch/arm/boot/dts/sun4i-a10-a1000.dts b/arch/arm/boot/dts/sun4i-a10-a1000.dts
index 9e99ade..0b43a18 100644
--- a/arch/arm/boot/dts/sun4i-a10-a1000.dts
+++ b/arch/arm/boot/dts/sun4i-a10-a1000.dts
@@ -139,6 +139,7 @@
regulator-name = "emac-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
+ startup-delay-us = <20000>;
enable-active-high;
gpio = <&pio 7 15 0>;
};
diff --git a/arch/arm/boot/dts/sun4i-a10-hackberry.dts b/arch/arm/boot/dts/sun4i-a10-hackberry.dts
index 891ea44..8ad4354b 100644
--- a/arch/arm/boot/dts/sun4i-a10-hackberry.dts
+++ b/arch/arm/boot/dts/sun4i-a10-hackberry.dts
@@ -120,6 +120,7 @@
regulator-name = "emac-3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
+ startup-delay-us = <20000>;
enable-active-high;
gpio = <&pio 7 19 0>;
};
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index 3b782f7..870280d 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -509,7 +509,6 @@
int set_memory_nx(unsigned long addr, int numpages);
#ifdef CONFIG_DEBUG_RODATA
-void mark_rodata_ro(void);
void set_kernel_text_rw(void);
void set_kernel_text_ro(void);
#else
diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index 51a70b5..6516d26 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -117,7 +117,7 @@
/* The ARM override for dma_max_pfn() */
static inline unsigned long dma_max_pfn(struct device *dev)
{
- return PHYS_PFN_OFFSET + dma_to_pfn(dev, *dev->dma_mask);
+ return dma_to_pfn(dev, *dev->dma_mask);
}
#define dma_max_pfn(dev) dma_max_pfn(dev)
diff --git a/arch/arm/include/asm/floppy.h b/arch/arm/include/asm/floppy.h
index f488255..85a34cc 100644
--- a/arch/arm/include/asm/floppy.h
+++ b/arch/arm/include/asm/floppy.h
@@ -17,7 +17,7 @@
#define fd_outb(val,port) \
do { \
- if ((port) == FD_DOR) \
+ if ((port) == (u32)FD_DOR) \
fd_setdor((val)); \
else \
outb((val),(port)); \
diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h
index 53e69da..63139e2 100644
--- a/arch/arm/include/asm/futex.h
+++ b/arch/arm/include/asm/futex.h
@@ -22,8 +22,11 @@
#ifdef CONFIG_SMP
#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
+({ \
+ unsigned int __ua_flags; \
smp_mb(); \
prefetchw(uaddr); \
+ __ua_flags = uaccess_save_and_enable(); \
__asm__ __volatile__( \
"1: ldrex %1, [%3]\n" \
" " insn "\n" \
@@ -34,12 +37,15 @@
__futex_atomic_ex_table("%5") \
: "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \
: "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \
- : "cc", "memory")
+ : "cc", "memory"); \
+ uaccess_restore(__ua_flags); \
+})
static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval)
{
+ unsigned int __ua_flags;
int ret;
u32 val;
@@ -49,6 +55,7 @@
smp_mb();
/* Prefetching cannot fault */
prefetchw(uaddr);
+ __ua_flags = uaccess_save_and_enable();
__asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
"1: ldrex %1, [%4]\n"
" teq %1, %2\n"
@@ -61,6 +68,7 @@
: "=&r" (ret), "=&r" (val)
: "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
: "cc", "memory");
+ uaccess_restore(__ua_flags);
smp_mb();
*uval = val;
@@ -73,6 +81,8 @@
#include <asm/domain.h>
#define __futex_atomic_op(insn, ret, oldval, tmp, uaddr, oparg) \
+({ \
+ unsigned int __ua_flags = uaccess_save_and_enable(); \
__asm__ __volatile__( \
"1: " TUSER(ldr) " %1, [%3]\n" \
" " insn "\n" \
@@ -81,12 +91,15 @@
__futex_atomic_ex_table("%5") \
: "=&r" (ret), "=&r" (oldval), "=&r" (tmp) \
: "r" (uaddr), "r" (oparg), "Ir" (-EFAULT) \
- : "cc", "memory")
+ : "cc", "memory"); \
+ uaccess_restore(__ua_flags); \
+})
static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
u32 oldval, u32 newval)
{
+ unsigned int __ua_flags;
int ret = 0;
u32 val;
@@ -102,6 +115,7 @@
: "+r" (ret), "=&r" (val)
: "r" (oldval), "r" (newval), "r" (uaddr), "Ir" (-EFAULT)
: "cc", "memory");
+ uaccess_restore(__ua_flags);
*uval = val;
return ret;
diff --git a/arch/arm/include/asm/kvm_emulate.h b/arch/arm/include/asm/kvm_emulate.h
index 66ce176..7b01523 100644
--- a/arch/arm/include/asm/kvm_emulate.h
+++ b/arch/arm/include/asm/kvm_emulate.h
@@ -38,6 +38,16 @@
vcpu->arch.hcr = HCR_GUEST_MASK;
}
+static inline unsigned long vcpu_get_hcr(struct kvm_vcpu *vcpu)
+{
+ return vcpu->arch.hcr;
+}
+
+static inline void vcpu_set_hcr(struct kvm_vcpu *vcpu, unsigned long hcr)
+{
+ vcpu->arch.hcr = hcr;
+}
+
static inline bool vcpu_mode_is_32bit(struct kvm_vcpu *vcpu)
{
return 1;
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
index 53036e21..dc7a8f0 100644
--- a/arch/arm/include/asm/kvm_host.h
+++ b/arch/arm/include/asm/kvm_host.h
@@ -125,9 +125,6 @@
* Anything that is not used directly from assembly code goes
* here.
*/
- /* dcache set/way operation pending */
- int last_pcpu;
- cpumask_t require_dcache_flush;
/* Don't run the guest on this vcpu */
bool pause;
diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
index 16d9d78..5de64c0 100644
--- a/arch/arm/include/asm/kvm_mmu.h
+++ b/arch/arm/include/asm/kvm_mmu.h
@@ -245,7 +245,8 @@
#define kvm_virt_to_phys(x) virt_to_idmap((unsigned long)(x))
-void stage2_flush_vm(struct kvm *kvm);
+void kvm_set_way_flush(struct kvm_vcpu *vcpu);
+void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled);
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm/include/asm/pgtable-2level.h b/arch/arm/include/asm/pgtable-2level.h
index f027941..67a251a 100644
--- a/arch/arm/include/asm/pgtable-2level.h
+++ b/arch/arm/include/asm/pgtable-2level.h
@@ -163,6 +163,7 @@
#define pmd_large(pmd) (pmd_val(pmd) & 2)
#define pmd_bad(pmd) (pmd_val(pmd) & 2)
+#define pmd_present(pmd) (pmd_val(pmd))
#define copy_pmd(pmdpd,pmdps) \
do { \
diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h
index a31ecdad..b5ef8c7 100644
--- a/arch/arm/include/asm/pgtable-3level.h
+++ b/arch/arm/include/asm/pgtable-3level.h
@@ -212,6 +212,7 @@
: !!(pmd_val(pmd) & (val)))
#define pmd_isclear(pmd, val) (!(pmd_val(pmd) & (val)))
+#define pmd_present(pmd) (pmd_isset((pmd), L_PMD_SECT_VALID))
#define pmd_young(pmd) (pmd_isset((pmd), PMD_SECT_AF))
#define pte_special(pte) (pte_isset((pte), L_PTE_SPECIAL))
static inline pte_t pte_mkspecial(pte_t pte)
@@ -257,8 +258,11 @@
#define pfn_pmd(pfn,prot) (__pmd(((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot)))
#define mk_pmd(page,prot) pfn_pmd(page_to_pfn(page),prot)
-/* represent a notpresent pmd by zero, this is used by pmdp_invalidate */
-#define pmd_mknotpresent(pmd) (__pmd(0))
+/* represent a notpresent pmd by faulting entry, this is used by pmdp_invalidate */
+static inline pmd_t pmd_mknotpresent(pmd_t pmd)
+{
+ return __pmd(pmd_val(pmd) & ~L_PMD_SECT_VALID);
+}
static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
{
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index 3b30062..e42bbd9 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -182,7 +182,6 @@
#define pgd_offset_k(addr) pgd_offset(&init_mm, addr)
#define pmd_none(pmd) (!pmd_val(pmd))
-#define pmd_present(pmd) (pmd_val(pmd))
static inline pte_t *pmd_page_vaddr(pmd_t pmd)
{
diff --git a/arch/arm/include/asm/psci.h b/arch/arm/include/asm/psci.h
index a44f69b..1e95781 100644
--- a/arch/arm/include/asm/psci.h
+++ b/arch/arm/include/asm/psci.h
@@ -37,7 +37,7 @@
extern struct psci_operations psci_ops;
extern struct smp_operations psci_smp_ops;
-#if defined(CONFIG_SMP) && defined(CONFIG_ARM_PSCI)
+#ifdef CONFIG_ARM_PSCI
int psci_init(void);
bool psci_smp_available(void);
#else
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index 4767eb9..b0413e8 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -50,6 +50,21 @@
extern int fixup_exception(struct pt_regs *regs);
/*
+ * These two functions allow hooking accesses to userspace to increase
+ * system integrity by ensuring that the kernel can not inadvertantly
+ * perform such accesses (eg, via list poison values) which could then
+ * be exploited for priviledge escalation.
+ */
+static inline unsigned int uaccess_save_and_enable(void)
+{
+ return 0;
+}
+
+static inline void uaccess_restore(unsigned int flags)
+{
+}
+
+/*
* These two are intentionally not defined anywhere - if the kernel
* code generates any references to them, that's a bug.
*/
@@ -165,6 +180,7 @@
register typeof(x) __r2 asm("r2"); \
register unsigned long __l asm("r1") = __limit; \
register int __e asm("r0"); \
+ unsigned int __ua_flags = uaccess_save_and_enable(); \
switch (sizeof(*(__p))) { \
case 1: \
if (sizeof((x)) >= 8) \
@@ -192,6 +208,7 @@
break; \
default: __e = __get_user_bad(); break; \
} \
+ uaccess_restore(__ua_flags); \
x = (typeof(*(p))) __r2; \
__e; \
})
@@ -224,6 +241,7 @@
register const typeof(*(p)) __user *__p asm("r0") = __tmp_p; \
register unsigned long __l asm("r1") = __limit; \
register int __e asm("r0"); \
+ unsigned int __ua_flags = uaccess_save_and_enable(); \
switch (sizeof(*(__p))) { \
case 1: \
__put_user_x(__r2, __p, __e, __l, 1); \
@@ -239,6 +257,7 @@
break; \
default: __e = __put_user_bad(); break; \
} \
+ uaccess_restore(__ua_flags); \
__e; \
})
@@ -300,14 +319,17 @@
do { \
unsigned long __gu_addr = (unsigned long)(ptr); \
unsigned long __gu_val; \
+ unsigned int __ua_flags; \
__chk_user_ptr(ptr); \
might_fault(); \
+ __ua_flags = uaccess_save_and_enable(); \
switch (sizeof(*(ptr))) { \
case 1: __get_user_asm_byte(__gu_val,__gu_addr,err); break; \
case 2: __get_user_asm_half(__gu_val,__gu_addr,err); break; \
case 4: __get_user_asm_word(__gu_val,__gu_addr,err); break; \
default: (__gu_val) = __get_user_bad(); \
} \
+ uaccess_restore(__ua_flags); \
(x) = (__typeof__(*(ptr)))__gu_val; \
} while (0)
@@ -381,9 +403,11 @@
#define __put_user_err(x,ptr,err) \
do { \
unsigned long __pu_addr = (unsigned long)(ptr); \
+ unsigned int __ua_flags; \
__typeof__(*(ptr)) __pu_val = (x); \
__chk_user_ptr(ptr); \
might_fault(); \
+ __ua_flags = uaccess_save_and_enable(); \
switch (sizeof(*(ptr))) { \
case 1: __put_user_asm_byte(__pu_val,__pu_addr,err); break; \
case 2: __put_user_asm_half(__pu_val,__pu_addr,err); break; \
@@ -391,6 +415,7 @@
case 8: __put_user_asm_dword(__pu_val,__pu_addr,err); break; \
default: __put_user_bad(); \
} \
+ uaccess_restore(__ua_flags); \
} while (0)
#define __put_user_asm_byte(x,__pu_addr,err) \
@@ -474,11 +499,57 @@
#ifdef CONFIG_MMU
-extern unsigned long __must_check __copy_from_user(void *to, const void __user *from, unsigned long n);
-extern unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n);
-extern unsigned long __must_check __copy_to_user_std(void __user *to, const void *from, unsigned long n);
-extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n);
-extern unsigned long __must_check __clear_user_std(void __user *addr, unsigned long n);
+extern unsigned long __must_check
+arm_copy_from_user(void *to, const void __user *from, unsigned long n);
+
+static inline unsigned long __must_check
+__copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+ unsigned int __ua_flags;
+
+ check_object_size(to, n, false);
+ __ua_flags = uaccess_save_and_enable();
+ n = arm_copy_from_user(to, from, n);
+ uaccess_restore(__ua_flags);
+ return n;
+}
+
+extern unsigned long __must_check
+arm_copy_to_user(void __user *to, const void *from, unsigned long n);
+extern unsigned long __must_check
+__copy_to_user_std(void __user *to, const void *from, unsigned long n);
+
+static inline unsigned long __must_check
+__copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+#ifndef CONFIG_UACCESS_WITH_MEMCPY
+ unsigned int __ua_flags;
+
+ check_object_size(from, n, true);
+ __ua_flags = uaccess_save_and_enable();
+ n = arm_copy_to_user(to, from, n);
+ uaccess_restore(__ua_flags);
+ return n;
+#else
+ check_object_size(from, n, true);
+ return arm_copy_to_user(to, from, n);
+#endif
+}
+
+extern unsigned long __must_check
+arm_clear_user(void __user *addr, unsigned long n);
+extern unsigned long __must_check
+__clear_user_std(void __user *addr, unsigned long n);
+
+static inline unsigned long __must_check
+__clear_user(void __user *addr, unsigned long n)
+{
+ unsigned int __ua_flags = uaccess_save_and_enable();
+ n = arm_clear_user(addr, n);
+ uaccess_restore(__ua_flags);
+ return n;
+}
+
#else
#define __copy_from_user(to,from,n) (memcpy(to, (void __force *)from, n), 0)
#define __copy_to_user(to,from,n) (memcpy((void __force *)to, from, n), 0)
@@ -511,6 +582,7 @@
return n;
}
+/* These are from lib/ code, and use __get_user() and friends */
extern long strncpy_from_user(char *dest, const char __user *src, long count);
extern __must_check long strlen_user(const char __user *str);
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index a88671c..a35d72d3 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -91,9 +91,9 @@
#ifdef CONFIG_MMU
EXPORT_SYMBOL(copy_page);
-EXPORT_SYMBOL(__copy_from_user);
-EXPORT_SYMBOL(__copy_to_user);
-EXPORT_SYMBOL(__clear_user);
+EXPORT_SYMBOL(arm_copy_from_user);
+EXPORT_SYMBOL(arm_copy_to_user);
+EXPORT_SYMBOL(arm_clear_user);
EXPORT_SYMBOL(__get_user_1);
EXPORT_SYMBOL(__get_user_2);
diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S
index cc176b6..c6c66dd 100644
--- a/arch/arm/kernel/head-nommu.S
+++ b/arch/arm/kernel/head-nommu.S
@@ -77,13 +77,12 @@
orr r6, r6, #(1 << MPU_RSR_EN) @ Set region enabled bit
bl __setup_mpu
#endif
- ldr r13, =__mmap_switched @ address to jump to after
- @ initialising sctlr
adr lr, BSYM(1f) @ return (PIC) address
ARM( add pc, r10, #PROCINFO_INITFUNC )
THUMB( add r12, r10, #PROCINFO_INITFUNC )
THUMB( ret r12 )
- 1: b __after_proc_init
+1: bl __after_proc_init
+ b __mmap_switched
ENDPROC(stext)
#ifdef CONFIG_SMP
@@ -106,8 +105,7 @@
movs r10, r5 @ invalid processor?
beq __error_p @ yes, error 'p'
- adr r4, __secondary_data
- ldmia r4, {r7, r12}
+ ldr r7, __secondary_data
#ifdef CONFIG_ARM_MPU
/* Use MPU region info supplied by __cpu_up */
@@ -115,23 +113,19 @@
bl __setup_mpu @ Initialize the MPU
#endif
- adr lr, BSYM(__after_proc_init) @ return address
- mov r13, r12 @ __secondary_switched address
+ adr lr, BSYM(1f) @ return (PIC) address
ARM( add pc, r10, #PROCINFO_INITFUNC )
THUMB( add r12, r10, #PROCINFO_INITFUNC )
THUMB( ret r12 )
-ENDPROC(secondary_startup)
-
-ENTRY(__secondary_switched)
+1: bl __after_proc_init
ldr sp, [r7, #8] @ set up the stack pointer
mov fp, #0
b secondary_start_kernel
-ENDPROC(__secondary_switched)
+ENDPROC(secondary_startup)
.type __secondary_data, %object
__secondary_data:
.long secondary_data
- .long __secondary_switched
#endif /* CONFIG_SMP */
/*
@@ -164,7 +158,7 @@
#endif
mcr p15, 0, r0, c1, c0, 0 @ write control reg
#endif /* CONFIG_CPU_CP15 */
- ret r13
+ ret lr
ENDPROC(__after_proc_init)
.ltorg
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index ef9119f..4d93758 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -733,8 +733,8 @@
if (ret)
return ret;
- vfp_flush_hwstate(thread);
thread->vfpstate.hard = new_vfp;
+ vfp_flush_hwstate(thread);
return 0;
}
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index c31e057..29a09ac 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -775,7 +775,7 @@
struct resource *res;
kernel_code.start = virt_to_phys(_text);
- kernel_code.end = virt_to_phys(_etext - 1);
+ kernel_code.end = virt_to_phys(__init_begin - 1);
kernel_data.start = virt_to_phys(_sdata);
kernel_data.end = virt_to_phys(_end - 1);
diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c
index e90a314..eb821e7 100644
--- a/arch/arm/kernel/sys_oabi-compat.c
+++ b/arch/arm/kernel/sys_oabi-compat.c
@@ -193,15 +193,44 @@
pid_t l_pid;
} __attribute__ ((packed,aligned(4)));
+static long do_locks(unsigned int fd, unsigned int cmd,
+ unsigned long arg)
+{
+ struct flock64 kernel;
+ struct oabi_flock64 user;
+ mm_segment_t fs;
+ long ret;
+
+ if (copy_from_user(&user, (struct oabi_flock64 __user *)arg,
+ sizeof(user)))
+ return -EFAULT;
+ kernel.l_type = user.l_type;
+ kernel.l_whence = user.l_whence;
+ kernel.l_start = user.l_start;
+ kernel.l_len = user.l_len;
+ kernel.l_pid = user.l_pid;
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = sys_fcntl64(fd, cmd, (unsigned long)&kernel);
+ set_fs(fs);
+
+ if (!ret && (cmd == F_GETLK64 || cmd == F_OFD_GETLK)) {
+ user.l_type = kernel.l_type;
+ user.l_whence = kernel.l_whence;
+ user.l_start = kernel.l_start;
+ user.l_len = kernel.l_len;
+ user.l_pid = kernel.l_pid;
+ if (copy_to_user((struct oabi_flock64 __user *)arg,
+ &user, sizeof(user)))
+ ret = -EFAULT;
+ }
+ return ret;
+}
+
asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
unsigned long arg)
{
- struct oabi_flock64 user;
- struct flock64 kernel;
- mm_segment_t fs = USER_DS; /* initialized to kill a warning */
- unsigned long local_arg = arg;
- int ret;
-
switch (cmd) {
case F_OFD_GETLK:
case F_OFD_SETLK:
@@ -209,39 +238,11 @@
case F_GETLK64:
case F_SETLK64:
case F_SETLKW64:
- if (copy_from_user(&user, (struct oabi_flock64 __user *)arg,
- sizeof(user)))
- return -EFAULT;
- kernel.l_type = user.l_type;
- kernel.l_whence = user.l_whence;
- kernel.l_start = user.l_start;
- kernel.l_len = user.l_len;
- kernel.l_pid = user.l_pid;
- local_arg = (unsigned long)&kernel;
- fs = get_fs();
- set_fs(KERNEL_DS);
+ return do_locks(fd, cmd, arg);
+
+ default:
+ return sys_fcntl64(fd, cmd, arg);
}
-
- ret = sys_fcntl64(fd, cmd, local_arg);
-
- switch (cmd) {
- case F_GETLK64:
- if (!ret) {
- user.l_type = kernel.l_type;
- user.l_whence = kernel.l_whence;
- user.l_start = kernel.l_start;
- user.l_len = kernel.l_len;
- user.l_pid = kernel.l_pid;
- if (copy_to_user((struct oabi_flock64 __user *)arg,
- &user, sizeof(user)))
- ret = -EFAULT;
- }
- case F_SETLK64:
- case F_SETLKW64:
- set_fs(fs);
- }
-
- return ret;
}
struct oabi_epoll_event {
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index a503cdf..aa00a19 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -123,6 +123,8 @@
#ifdef CONFIG_DEBUG_RODATA
. = ALIGN(1<<SECTION_SHIFT);
#endif
+ _etext = .; /* End of text section */
+
RO_DATA(PAGE_SIZE)
. = ALIGN(4);
@@ -153,8 +155,6 @@
NOTES
- _etext = .; /* End of text and rodata section */
-
#ifndef CONFIG_XIP_KERNEL
# ifdef CONFIG_ARM_KERNMEM_PERMS
. = ALIGN(1<<SECTION_SHIFT);
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index ed5834e..151813f 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -153,8 +153,6 @@
{
int i;
- kvm_free_stage2_pgd(kvm);
-
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
if (kvm->vcpus[i]) {
kvm_arch_vcpu_free(kvm->vcpus[i]);
@@ -251,6 +249,7 @@
kvm_mmu_free_memory_caches(vcpu);
kvm_timer_vcpu_terminate(vcpu);
kvm_vgic_vcpu_destroy(vcpu);
+ kvm_vcpu_uninit(vcpu);
kmem_cache_free(kvm_vcpu_cache, vcpu);
}
@@ -280,15 +279,6 @@
vcpu->cpu = cpu;
vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state);
- /*
- * Check whether this vcpu requires the cache to be flushed on
- * this physical CPU. This is a consequence of doing dcache
- * operations by set/way on this vcpu. We do it here to be in
- * a non-preemptible section.
- */
- if (cpumask_test_and_clear_cpu(cpu, &vcpu->arch.require_dcache_flush))
- flush_cache_all(); /* We'd really want v7_flush_dcache_all() */
-
kvm_arm_set_running_vcpu(vcpu);
}
@@ -540,7 +530,6 @@
ret = kvm_call_hyp(__kvm_vcpu_run, vcpu);
vcpu->mode = OUTSIDE_GUEST_MODE;
- vcpu->arch.last_pcpu = smp_processor_id();
kvm_guest_exit();
trace_kvm_exit(*vcpu_pc(vcpu));
/*
diff --git a/arch/arm/kvm/coproc.c b/arch/arm/kvm/coproc.c
index 7928dbd..f3d88dc 100644
--- a/arch/arm/kvm/coproc.c
+++ b/arch/arm/kvm/coproc.c
@@ -189,82 +189,40 @@
return true;
}
-/* See note at ARM ARM B1.14.4 */
+/*
+ * See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized).
+ */
static bool access_dcsw(struct kvm_vcpu *vcpu,
const struct coproc_params *p,
const struct coproc_reg *r)
{
- unsigned long val;
- int cpu;
-
if (!p->is_write)
return read_from_write_only(vcpu, p);
- cpu = get_cpu();
-
- cpumask_setall(&vcpu->arch.require_dcache_flush);
- cpumask_clear_cpu(cpu, &vcpu->arch.require_dcache_flush);
-
- /* If we were already preempted, take the long way around */
- if (cpu != vcpu->arch.last_pcpu) {
- flush_cache_all();
- goto done;
- }
-
- val = *vcpu_reg(vcpu, p->Rt1);
-
- switch (p->CRm) {
- case 6: /* Upgrade DCISW to DCCISW, as per HCR.SWIO */
- case 14: /* DCCISW */
- asm volatile("mcr p15, 0, %0, c7, c14, 2" : : "r" (val));
- break;
-
- case 10: /* DCCSW */
- asm volatile("mcr p15, 0, %0, c7, c10, 2" : : "r" (val));
- break;
- }
-
-done:
- put_cpu();
-
+ kvm_set_way_flush(vcpu);
return true;
}
/*
* Generic accessor for VM registers. Only called as long as HCR_TVM
- * is set.
+ * is set. If the guest enables the MMU, we stop trapping the VM
+ * sys_regs and leave it in complete control of the caches.
+ *
+ * Used by the cpu-specific code.
*/
-static bool access_vm_reg(struct kvm_vcpu *vcpu,
- const struct coproc_params *p,
- const struct coproc_reg *r)
+bool access_vm_reg(struct kvm_vcpu *vcpu,
+ const struct coproc_params *p,
+ const struct coproc_reg *r)
{
+ bool was_enabled = vcpu_has_cache_enabled(vcpu);
+
BUG_ON(!p->is_write);
vcpu->arch.cp15[r->reg] = *vcpu_reg(vcpu, p->Rt1);
if (p->is_64bit)
vcpu->arch.cp15[r->reg + 1] = *vcpu_reg(vcpu, p->Rt2);
- return true;
-}
-
-/*
- * SCTLR accessor. Only called as long as HCR_TVM is set. If the
- * guest enables the MMU, we stop trapping the VM sys_regs and leave
- * it in complete control of the caches.
- *
- * Used by the cpu-specific code.
- */
-bool access_sctlr(struct kvm_vcpu *vcpu,
- const struct coproc_params *p,
- const struct coproc_reg *r)
-{
- access_vm_reg(vcpu, p, r);
-
- if (vcpu_has_cache_enabled(vcpu)) { /* MMU+Caches enabled? */
- vcpu->arch.hcr &= ~HCR_TVM;
- stage2_flush_vm(vcpu->kvm);
- }
-
+ kvm_toggle_cache(vcpu, was_enabled);
return true;
}
diff --git a/arch/arm/kvm/coproc.h b/arch/arm/kvm/coproc.h
index 1a44bbe..88d24a3 100644
--- a/arch/arm/kvm/coproc.h
+++ b/arch/arm/kvm/coproc.h
@@ -153,8 +153,8 @@
#define is64 .is_64 = true
#define is32 .is_64 = false
-bool access_sctlr(struct kvm_vcpu *vcpu,
- const struct coproc_params *p,
- const struct coproc_reg *r);
+bool access_vm_reg(struct kvm_vcpu *vcpu,
+ const struct coproc_params *p,
+ const struct coproc_reg *r);
#endif /* __ARM_KVM_COPROC_LOCAL_H__ */
diff --git a/arch/arm/kvm/coproc_a15.c b/arch/arm/kvm/coproc_a15.c
index e6f4ae4..a713675 100644
--- a/arch/arm/kvm/coproc_a15.c
+++ b/arch/arm/kvm/coproc_a15.c
@@ -34,7 +34,7 @@
static const struct coproc_reg a15_regs[] = {
/* SCTLR: swapped by interrupt.S. */
{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
- access_sctlr, reset_val, c1_SCTLR, 0x00C50078 },
+ access_vm_reg, reset_val, c1_SCTLR, 0x00C50078 },
};
static struct kvm_coproc_target_table a15_target_table = {
diff --git a/arch/arm/kvm/coproc_a7.c b/arch/arm/kvm/coproc_a7.c
index 17fc7cd..b19e46d1b 100644
--- a/arch/arm/kvm/coproc_a7.c
+++ b/arch/arm/kvm/coproc_a7.c
@@ -37,7 +37,7 @@
static const struct coproc_reg a7_regs[] = {
/* SCTLR: swapped by interrupt.S. */
{ CRn( 1), CRm( 0), Op1( 0), Op2( 0), is32,
- access_sctlr, reset_val, c1_SCTLR, 0x00C50878 },
+ access_vm_reg, reset_val, c1_SCTLR, 0x00C50878 },
};
static struct kvm_coproc_target_table a7_target_table = {
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 3535480..70ccd1e 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -256,6 +256,14 @@
next = kvm_pgd_addr_end(addr, end);
if (!pgd_none(*pgd))
unmap_puds(kvm, pgd, addr, next);
+ /*
+ * If we are dealing with a large range in
+ * stage2 table, release the kvm->mmu_lock
+ * to prevent starvation and lockup detector
+ * warnings.
+ */
+ if (kvm && (next != end))
+ cond_resched_lock(&kvm->mmu_lock);
} while (pgd++, addr = next, addr != end);
}
@@ -330,7 +338,7 @@
* Go through the stage 2 page tables and invalidate any cache lines
* backing memory already mapped to the VM.
*/
-void stage2_flush_vm(struct kvm *kvm)
+static void stage2_flush_vm(struct kvm *kvm)
{
struct kvm_memslots *slots;
struct kvm_memory_slot *memslot;
@@ -693,6 +701,7 @@
*/
static void unmap_stage2_range(struct kvm *kvm, phys_addr_t start, u64 size)
{
+ assert_spin_locked(&kvm->mmu_lock);
unmap_range(kvm, kvm->arch.pgd, start, size);
}
@@ -777,7 +786,10 @@
if (kvm->arch.pgd == NULL)
return;
+ spin_lock(&kvm->mmu_lock);
unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE);
+ spin_unlock(&kvm->mmu_lock);
+
kvm_free_hwpgd(kvm_get_hwpgd(kvm));
if (KVM_PREALLOC_LEVEL > 0)
kfree(kvm->arch.pgd);
@@ -841,11 +853,14 @@
VM_BUG_ON(pmd_present(*pmd) && pmd_pfn(*pmd) != pmd_pfn(*new_pmd));
old_pmd = *pmd;
- kvm_set_pmd(pmd, *new_pmd);
- if (pmd_present(old_pmd))
+ if (pmd_present(old_pmd)) {
+ pmd_clear(pmd);
kvm_tlb_flush_vmid_ipa(kvm, addr);
- else
+ } else {
get_page(virt_to_page(pmd));
+ }
+
+ kvm_set_pmd(pmd, *new_pmd);
return 0;
}
@@ -882,12 +897,14 @@
/* Create 2nd stage page table mapping - Level 3 */
old_pte = *pte;
- kvm_set_pte(pte, *new_pte);
- if (pte_present(old_pte))
+ if (pte_present(old_pte)) {
+ kvm_set_pte(pte, __pte(0));
kvm_tlb_flush_vmid_ipa(kvm, addr);
- else
+ } else {
get_page(virt_to_page(pte));
+ }
+ kvm_set_pte(pte, *new_pte);
return 0;
}
@@ -1402,6 +1419,7 @@
(KVM_PHYS_SIZE >> PAGE_SHIFT))
return -EFAULT;
+ down_read(¤t->mm->mmap_sem);
/*
* A memory region could potentially cover multiple VMAs, and any holes
* between them, so iterate over all of them to find out if we can map
@@ -1459,6 +1477,8 @@
else
stage2_flush_memslot(kvm, memslot);
spin_unlock(&kvm->mmu_lock);
+
+ up_read(¤t->mm->mmap_sem);
return ret;
}
@@ -1488,6 +1508,7 @@
void kvm_arch_flush_shadow_all(struct kvm *kvm)
{
+ kvm_free_stage2_pgd(kvm);
}
void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
@@ -1500,3 +1521,71 @@
unmap_stage2_range(kvm, gpa, size);
spin_unlock(&kvm->mmu_lock);
}
+
+/*
+ * See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized).
+ *
+ * Main problems:
+ * - S/W ops are local to a CPU (not broadcast)
+ * - We have line migration behind our back (speculation)
+ * - System caches don't support S/W at all (damn!)
+ *
+ * In the face of the above, the best we can do is to try and convert
+ * S/W ops to VA ops. Because the guest is not allowed to infer the
+ * S/W to PA mapping, it can only use S/W to nuke the whole cache,
+ * which is a rather good thing for us.
+ *
+ * Also, it is only used when turning caches on/off ("The expected
+ * usage of the cache maintenance instructions that operate by set/way
+ * is associated with the cache maintenance instructions associated
+ * with the powerdown and powerup of caches, if this is required by
+ * the implementation.").
+ *
+ * We use the following policy:
+ *
+ * - If we trap a S/W operation, we enable VM trapping to detect
+ * caches being turned on/off, and do a full clean.
+ *
+ * - We flush the caches on both caches being turned on and off.
+ *
+ * - Once the caches are enabled, we stop trapping VM ops.
+ */
+void kvm_set_way_flush(struct kvm_vcpu *vcpu)
+{
+ unsigned long hcr = vcpu_get_hcr(vcpu);
+
+ /*
+ * If this is the first time we do a S/W operation
+ * (i.e. HCR_TVM not set) flush the whole memory, and set the
+ * VM trapping.
+ *
+ * Otherwise, rely on the VM trapping to wait for the MMU +
+ * Caches to be turned off. At that point, we'll be able to
+ * clean the caches again.
+ */
+ if (!(hcr & HCR_TVM)) {
+ trace_kvm_set_way_flush(*vcpu_pc(vcpu),
+ vcpu_has_cache_enabled(vcpu));
+ stage2_flush_vm(vcpu->kvm);
+ vcpu_set_hcr(vcpu, hcr | HCR_TVM);
+ }
+}
+
+void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled)
+{
+ bool now_enabled = vcpu_has_cache_enabled(vcpu);
+
+ /*
+ * If switching the MMU+caches on, need to invalidate the caches.
+ * If switching it off, need to clean the caches.
+ * Clean + invalidate does the trick always.
+ */
+ if (now_enabled != was_enabled)
+ stage2_flush_vm(vcpu->kvm);
+
+ /* Caches are now on, stop trapping VM ops (until a S/W op) */
+ if (now_enabled)
+ vcpu_set_hcr(vcpu, vcpu_get_hcr(vcpu) & ~HCR_TVM);
+
+ trace_kvm_toggle_cache(*vcpu_pc(vcpu), was_enabled, now_enabled);
+}
diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h
index b1d640f..b6a6e71 100644
--- a/arch/arm/kvm/trace.h
+++ b/arch/arm/kvm/trace.h
@@ -223,6 +223,45 @@
__entry->vcpu_pc, __entry->r0, __entry->imm)
);
+TRACE_EVENT(kvm_set_way_flush,
+ TP_PROTO(unsigned long vcpu_pc, bool cache),
+ TP_ARGS(vcpu_pc, cache),
+
+ TP_STRUCT__entry(
+ __field( unsigned long, vcpu_pc )
+ __field( bool, cache )
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_pc = vcpu_pc;
+ __entry->cache = cache;
+ ),
+
+ TP_printk("S/W flush at 0x%016lx (cache %s)",
+ __entry->vcpu_pc, __entry->cache ? "on" : "off")
+);
+
+TRACE_EVENT(kvm_toggle_cache,
+ TP_PROTO(unsigned long vcpu_pc, bool was, bool now),
+ TP_ARGS(vcpu_pc, was, now),
+
+ TP_STRUCT__entry(
+ __field( unsigned long, vcpu_pc )
+ __field( bool, was )
+ __field( bool, now )
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_pc = vcpu_pc;
+ __entry->was = was;
+ __entry->now = now;
+ ),
+
+ TP_printk("VM op at 0x%016lx (cache was %s, now %s)",
+ __entry->vcpu_pc, __entry->was ? "on" : "off",
+ __entry->now ? "on" : "off")
+);
+
#endif /* _TRACE_KVM_H */
#undef TRACE_INCLUDE_PATH
diff --git a/arch/arm/lib/clear_user.S b/arch/arm/lib/clear_user.S
index 14a0d98..a6d7bb9 100644
--- a/arch/arm/lib/clear_user.S
+++ b/arch/arm/lib/clear_user.S
@@ -12,14 +12,14 @@
.text
-/* Prototype: int __clear_user(void *addr, size_t sz)
+/* Prototype: unsigned long arm_clear_user(void *addr, size_t sz)
* Purpose : clear some user memory
* Params : addr - user memory address to clear
* : sz - number of bytes to clear
* Returns : number of bytes NOT cleared
*/
ENTRY(__clear_user_std)
-WEAK(__clear_user)
+WEAK(arm_clear_user)
stmfd sp!, {r1, lr}
mov r2, #0
cmp r1, #4
@@ -44,7 +44,7 @@
USER( strnebt r2, [r0])
mov r0, #0
ldmfd sp!, {r1, pc}
-ENDPROC(__clear_user)
+ENDPROC(arm_clear_user)
ENDPROC(__clear_user_std)
.pushsection .fixup,"ax"
diff --git a/arch/arm/lib/copy_from_user.S b/arch/arm/lib/copy_from_user.S
index 66a477a..70bf0e9 100644
--- a/arch/arm/lib/copy_from_user.S
+++ b/arch/arm/lib/copy_from_user.S
@@ -16,7 +16,7 @@
/*
* Prototype:
*
- * size_t __copy_from_user(void *to, const void *from, size_t n)
+ * size_t arm_copy_from_user(void *to, const void *from, size_t n)
*
* Purpose:
*
@@ -84,11 +84,11 @@
.text
-ENTRY(__copy_from_user)
+ENTRY(arm_copy_from_user)
#include "copy_template.S"
-ENDPROC(__copy_from_user)
+ENDPROC(arm_copy_from_user)
.pushsection .fixup,"ax"
.align 0
diff --git a/arch/arm/lib/copy_to_user.S b/arch/arm/lib/copy_to_user.S
index d066df6..a610ffa 100644
--- a/arch/arm/lib/copy_to_user.S
+++ b/arch/arm/lib/copy_to_user.S
@@ -16,7 +16,7 @@
/*
* Prototype:
*
- * size_t __copy_to_user(void *to, const void *from, size_t n)
+ * size_t arm_copy_to_user(void *to, const void *from, size_t n)
*
* Purpose:
*
@@ -88,11 +88,11 @@
.text
ENTRY(__copy_to_user_std)
-WEAK(__copy_to_user)
+WEAK(arm_copy_to_user)
#include "copy_template.S"
-ENDPROC(__copy_to_user)
+ENDPROC(arm_copy_to_user)
ENDPROC(__copy_to_user_std)
.pushsection .fixup,"ax"
diff --git a/arch/arm/lib/uaccess_with_memcpy.c b/arch/arm/lib/uaccess_with_memcpy.c
index 3e58d71..b3e6808 100644
--- a/arch/arm/lib/uaccess_with_memcpy.c
+++ b/arch/arm/lib/uaccess_with_memcpy.c
@@ -88,6 +88,7 @@
static unsigned long noinline
__copy_to_user_memcpy(void __user *to, const void *from, unsigned long n)
{
+ unsigned long ua_flags;
int atomic;
if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
@@ -118,7 +119,9 @@
if (tocopy > n)
tocopy = n;
+ ua_flags = uaccess_save_and_enable();
memcpy((void *)to, from, tocopy);
+ uaccess_restore(ua_flags);
to += tocopy;
from += tocopy;
n -= tocopy;
@@ -136,7 +139,7 @@
}
unsigned long
-__copy_to_user(void __user *to, const void *from, unsigned long n)
+arm_copy_to_user(void __user *to, const void *from, unsigned long n)
{
/*
* This test is stubbed out of the main function above to keep
@@ -145,14 +148,21 @@
* With frame pointer disabled, tail call optimization kicks in
* as well making this test almost invisible.
*/
- if (n < 64)
- return __copy_to_user_std(to, from, n);
- return __copy_to_user_memcpy(to, from, n);
+ if (n < 64) {
+ unsigned long ua_flags = uaccess_save_and_enable();
+ n = __copy_to_user_std(to, from, n);
+ uaccess_restore(ua_flags);
+ } else {
+ n = __copy_to_user_memcpy(to, from, n);
+ }
+ return n;
}
static unsigned long noinline
__clear_user_memset(void __user *addr, unsigned long n)
{
+ unsigned long ua_flags;
+
if (unlikely(segment_eq(get_fs(), KERNEL_DS))) {
memset((void *)addr, 0, n);
return 0;
@@ -175,7 +185,9 @@
if (tocopy > n)
tocopy = n;
+ ua_flags = uaccess_save_and_enable();
memset((void *)addr, 0, tocopy);
+ uaccess_restore(ua_flags);
addr += tocopy;
n -= tocopy;
@@ -190,12 +202,17 @@
return n;
}
-unsigned long __clear_user(void __user *addr, unsigned long n)
+unsigned long arm_clear_user(void __user *addr, unsigned long n)
{
/* See rational for this in __copy_to_user() above. */
- if (n < 64)
- return __clear_user_std(addr, n);
- return __clear_user_memset(addr, n);
+ if (n < 64) {
+ unsigned long ua_flags = uaccess_save_and_enable();
+ n = __clear_user_std(addr, n);
+ uaccess_restore(ua_flags);
+ } else {
+ n = __clear_user_memset(addr, n);
+ }
+ return n;
}
#if 0
diff --git a/arch/arm/mach-cns3xxx/Makefile b/arch/arm/mach-cns3xxx/Makefile
index a1ff108..fd61056 100644
--- a/arch/arm/mach-cns3xxx/Makefile
+++ b/arch/arm/mach-cns3xxx/Makefile
@@ -2,4 +2,5 @@
cns3xxx-y += core.o pm.o
cns3xxx-$(CONFIG_ATAGS) += devices.o
cns3xxx-$(CONFIG_PCI) += pcie.o
+CFLAGS_pcie.o += -Wframe-larger-than=1536 # override default 1024, this is safe here
cns3xxx-$(CONFIG_MACH_CNS3420VB) += cns3420vb.o
diff --git a/arch/arm/mach-imx/pm-imx6.c b/arch/arm/mach-imx/pm-imx6.c
index 7ffdae6..7b66240 100644
--- a/arch/arm/mach-imx/pm-imx6.c
+++ b/arch/arm/mach-imx/pm-imx6.c
@@ -292,7 +292,7 @@
val |= 0x3 << BP_CLPCR_STBY_COUNT;
val |= BM_CLPCR_VSTBY;
val |= BM_CLPCR_SBYOS;
- if (cpu_is_imx6sl())
+ if (cpu_is_imx6sl() || cpu_is_imx6sx())
val |= BM_CLPCR_BYPASS_PMIC_READY;
if (cpu_is_imx6sl() || cpu_is_imx6sx())
val |= BM_CLPCR_BYP_MMDC_CH0_LPM_HS;
diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c
index 01efe13..9e5c29d 100644
--- a/arch/arm/mach-mvebu/coherency.c
+++ b/arch/arm/mach-mvebu/coherency.c
@@ -315,22 +315,16 @@
}
/*
- * This ioremap hook is used on Armada 375/38x to ensure that PCIe
- * memory areas are mapped as MT_UNCACHED instead of MT_DEVICE. This
- * is needed as a workaround for a deadlock issue between the PCIe
- * interface and the cache controller.
+ * This ioremap hook is used on Armada 375/38x to ensure that all MMIO
+ * areas are mapped as MT_UNCACHED instead of MT_DEVICE. This is
+ * needed for the HW I/O coherency mechanism to work properly without
+ * deadlock.
*/
static void __iomem *
-armada_pcie_wa_ioremap_caller(phys_addr_t phys_addr, size_t size,
- unsigned int mtype, void *caller)
+armada_wa_ioremap_caller(phys_addr_t phys_addr, size_t size,
+ unsigned int mtype, void *caller)
{
- struct resource pcie_mem;
-
- mvebu_mbus_get_pcie_mem_aperture(&pcie_mem);
-
- if (pcie_mem.start <= phys_addr && (phys_addr + size) <= pcie_mem.end)
- mtype = MT_UNCACHED;
-
+ mtype = MT_UNCACHED;
return __arm_ioremap_caller(phys_addr, size, mtype, caller);
}
@@ -339,7 +333,7 @@
struct device_node *cache_dn;
coherency_cpu_base = of_iomap(np, 0);
- arch_ioremap_caller = armada_pcie_wa_ioremap_caller;
+ arch_ioremap_caller = armada_wa_ioremap_caller;
/*
* We should switch the PL310 to I/O coherency mode only if
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index e18709d..38e1bdc 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -34,6 +34,7 @@
#include "pm.h"
#include "control.h"
#include "common.h"
+#include "soc.h"
/* Mach specific information to be recorded in the C-state driver_data */
struct omap3_idle_statedata {
@@ -322,6 +323,69 @@
.safe_state_index = 0,
};
+/*
+ * Numbers based on measurements made in October 2009 for PM optimized kernel
+ * with CPU freq enabled on device Nokia N900. Assumes OPP2 (main idle OPP,
+ * and worst case latencies).
+ */
+static struct cpuidle_driver omap3430_idle_driver = {
+ .name = "omap3430_idle",
+ .owner = THIS_MODULE,
+ .states = {
+ {
+ .enter = omap3_enter_idle_bm,
+ .exit_latency = 110 + 162,
+ .target_residency = 5,
+ .name = "C1",
+ .desc = "MPU ON + CORE ON",
+ },
+ {
+ .enter = omap3_enter_idle_bm,
+ .exit_latency = 106 + 180,
+ .target_residency = 309,
+ .name = "C2",
+ .desc = "MPU ON + CORE ON",
+ },
+ {
+ .enter = omap3_enter_idle_bm,
+ .exit_latency = 107 + 410,
+ .target_residency = 46057,
+ .name = "C3",
+ .desc = "MPU RET + CORE ON",
+ },
+ {
+ .enter = omap3_enter_idle_bm,
+ .exit_latency = 121 + 3374,
+ .target_residency = 46057,
+ .name = "C4",
+ .desc = "MPU OFF + CORE ON",
+ },
+ {
+ .enter = omap3_enter_idle_bm,
+ .exit_latency = 855 + 1146,
+ .target_residency = 46057,
+ .name = "C5",
+ .desc = "MPU RET + CORE RET",
+ },
+ {
+ .enter = omap3_enter_idle_bm,
+ .exit_latency = 7580 + 4134,
+ .target_residency = 484329,
+ .name = "C6",
+ .desc = "MPU OFF + CORE RET",
+ },
+ {
+ .enter = omap3_enter_idle_bm,
+ .exit_latency = 7505 + 15274,
+ .target_residency = 484329,
+ .name = "C7",
+ .desc = "MPU OFF + CORE OFF",
+ },
+ },
+ .state_count = ARRAY_SIZE(omap3_idle_data),
+ .safe_state_index = 0,
+};
+
/* Public functions */
/**
@@ -340,5 +404,8 @@
if (!mpu_pd || !core_pd || !per_pd || !cam_pd)
return -ENODEV;
- return cpuidle_register(&omap3_idle_driver, NULL);
+ if (cpu_is_omap3430())
+ return cpuidle_register(&omap3430_idle_driver, NULL);
+ else
+ return cpuidle_register(&omap3_idle_driver, NULL);
}
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index bb9a148..e67ffbc 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -1439,9 +1439,7 @@
(sf & SYSC_HAS_CLOCKACTIVITY))
_set_clockactivity(oh, oh->class->sysc->clockact, &v);
- /* If the cached value is the same as the new value, skip the write */
- if (oh->_sysc_cache != v)
- _write_sysconfig(v, oh);
+ _write_sysconfig(v, oh);
/*
* Set the autoidle bit only after setting the smartidle bit
@@ -1504,7 +1502,9 @@
_set_master_standbymode(oh, idlemode, &v);
}
- _write_sysconfig(v, oh);
+ /* If the cached value is the same as the new value, skip the write */
+ if (oh->_sysc_cache != v)
+ _write_sysconfig(v, oh);
}
/**
diff --git a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
index 2a78b09..e74ddb3 100644
--- a/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_3xxx_data.c
@@ -724,8 +724,20 @@
* display serial interface controller
*/
+static struct omap_hwmod_class_sysconfig omap3xxx_dsi_sysc = {
+ .rev_offs = 0x0000,
+ .sysc_offs = 0x0010,
+ .syss_offs = 0x0014,
+ .sysc_flags = (SYSC_HAS_AUTOIDLE | SYSC_HAS_CLOCKACTIVITY |
+ SYSC_HAS_ENAWAKEUP | SYSC_HAS_SIDLEMODE |
+ SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
+ .idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART),
+ .sysc_fields = &omap_hwmod_sysc_type1,
+};
+
static struct omap_hwmod_class omap3xxx_dsi_hwmod_class = {
.name = "dsi",
+ .sysc = &omap3xxx_dsi_sysc,
};
static struct omap_hwmod_irq_info omap3xxx_dsi1_irqs[] = {
diff --git a/arch/arm/mach-s3c64xx/dev-audio.c b/arch/arm/mach-s3c64xx/dev-audio.c
index ff780a8..9a42736 100644
--- a/arch/arm/mach-s3c64xx/dev-audio.c
+++ b/arch/arm/mach-s3c64xx/dev-audio.c
@@ -54,12 +54,12 @@
static struct resource s3c64xx_iis0_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_IIS0, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_I2S0_OUT),
- [2] = DEFINE_RES_DMA(DMACH_I2S0_IN),
};
-static struct s3c_audio_pdata i2sv3_pdata = {
+static struct s3c_audio_pdata i2s0_pdata = {
.cfg_gpio = s3c64xx_i2s_cfg_gpio,
+ .dma_playback = DMACH_I2S0_OUT,
+ .dma_capture = DMACH_I2S0_IN,
};
struct platform_device s3c64xx_device_iis0 = {
@@ -68,15 +68,19 @@
.num_resources = ARRAY_SIZE(s3c64xx_iis0_resource),
.resource = s3c64xx_iis0_resource,
.dev = {
- .platform_data = &i2sv3_pdata,
+ .platform_data = &i2s0_pdata,
},
};
EXPORT_SYMBOL(s3c64xx_device_iis0);
static struct resource s3c64xx_iis1_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_IIS1, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_I2S1_OUT),
- [2] = DEFINE_RES_DMA(DMACH_I2S1_IN),
+};
+
+static struct s3c_audio_pdata i2s1_pdata = {
+ .cfg_gpio = s3c64xx_i2s_cfg_gpio,
+ .dma_playback = DMACH_I2S1_OUT,
+ .dma_capture = DMACH_I2S1_IN,
};
struct platform_device s3c64xx_device_iis1 = {
@@ -85,19 +89,19 @@
.num_resources = ARRAY_SIZE(s3c64xx_iis1_resource),
.resource = s3c64xx_iis1_resource,
.dev = {
- .platform_data = &i2sv3_pdata,
+ .platform_data = &i2s1_pdata,
},
};
EXPORT_SYMBOL(s3c64xx_device_iis1);
static struct resource s3c64xx_iisv4_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_IISV4, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_HSI_I2SV40_TX),
- [2] = DEFINE_RES_DMA(DMACH_HSI_I2SV40_RX),
};
static struct s3c_audio_pdata i2sv4_pdata = {
.cfg_gpio = s3c64xx_i2s_cfg_gpio,
+ .dma_playback = DMACH_HSI_I2SV40_TX,
+ .dma_capture = DMACH_HSI_I2SV40_RX,
.type = {
.i2s = {
.quirks = QUIRK_PRI_6CHAN,
@@ -142,12 +146,12 @@
static struct resource s3c64xx_pcm0_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_PCM0, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_PCM0_TX),
- [2] = DEFINE_RES_DMA(DMACH_PCM0_RX),
};
static struct s3c_audio_pdata s3c_pcm0_pdata = {
.cfg_gpio = s3c64xx_pcm_cfg_gpio,
+ .dma_capture = DMACH_PCM0_RX,
+ .dma_playback = DMACH_PCM0_TX,
};
struct platform_device s3c64xx_device_pcm0 = {
@@ -163,12 +167,12 @@
static struct resource s3c64xx_pcm1_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_PCM1, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_PCM1_TX),
- [2] = DEFINE_RES_DMA(DMACH_PCM1_RX),
};
static struct s3c_audio_pdata s3c_pcm1_pdata = {
.cfg_gpio = s3c64xx_pcm_cfg_gpio,
+ .dma_playback = DMACH_PCM1_TX,
+ .dma_capture = DMACH_PCM1_RX,
};
struct platform_device s3c64xx_device_pcm1 = {
@@ -196,13 +200,14 @@
static struct resource s3c64xx_ac97_resource[] = {
[0] = DEFINE_RES_MEM(S3C64XX_PA_AC97, SZ_256),
- [1] = DEFINE_RES_DMA(DMACH_AC97_PCMOUT),
- [2] = DEFINE_RES_DMA(DMACH_AC97_PCMIN),
- [3] = DEFINE_RES_DMA(DMACH_AC97_MICIN),
- [4] = DEFINE_RES_IRQ(IRQ_AC97),
+ [1] = DEFINE_RES_IRQ(IRQ_AC97),
};
-static struct s3c_audio_pdata s3c_ac97_pdata;
+static struct s3c_audio_pdata s3c_ac97_pdata = {
+ .dma_playback = DMACH_AC97_PCMOUT,
+ .dma_capture = DMACH_AC97_PCMIN,
+ .dma_capture_mic = DMACH_AC97_MICIN,
+};
static u64 s3c64xx_ac97_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-s3c64xx/include/mach/dma.h b/arch/arm/mach-s3c64xx/include/mach/dma.h
index 059b1fc8..41a3048 100644
--- a/arch/arm/mach-s3c64xx/include/mach/dma.h
+++ b/arch/arm/mach-s3c64xx/include/mach/dma.h
@@ -14,38 +14,38 @@
#define S3C64XX_DMA_CHAN(name) ((unsigned long)(name))
/* DMA0/SDMA0 */
-#define DMACH_UART0 S3C64XX_DMA_CHAN("uart0_tx")
-#define DMACH_UART0_SRC2 S3C64XX_DMA_CHAN("uart0_rx")
-#define DMACH_UART1 S3C64XX_DMA_CHAN("uart1_tx")
-#define DMACH_UART1_SRC2 S3C64XX_DMA_CHAN("uart1_rx")
-#define DMACH_UART2 S3C64XX_DMA_CHAN("uart2_tx")
-#define DMACH_UART2_SRC2 S3C64XX_DMA_CHAN("uart2_rx")
-#define DMACH_UART3 S3C64XX_DMA_CHAN("uart3_tx")
-#define DMACH_UART3_SRC2 S3C64XX_DMA_CHAN("uart3_rx")
-#define DMACH_PCM0_TX S3C64XX_DMA_CHAN("pcm0_tx")
-#define DMACH_PCM0_RX S3C64XX_DMA_CHAN("pcm0_rx")
-#define DMACH_I2S0_OUT S3C64XX_DMA_CHAN("i2s0_tx")
-#define DMACH_I2S0_IN S3C64XX_DMA_CHAN("i2s0_rx")
+#define DMACH_UART0 "uart0_tx"
+#define DMACH_UART0_SRC2 "uart0_rx"
+#define DMACH_UART1 "uart1_tx"
+#define DMACH_UART1_SRC2 "uart1_rx"
+#define DMACH_UART2 "uart2_tx"
+#define DMACH_UART2_SRC2 "uart2_rx"
+#define DMACH_UART3 "uart3_tx"
+#define DMACH_UART3_SRC2 "uart3_rx"
+#define DMACH_PCM0_TX "pcm0_tx"
+#define DMACH_PCM0_RX "pcm0_rx"
+#define DMACH_I2S0_OUT "i2s0_tx"
+#define DMACH_I2S0_IN "i2s0_rx"
#define DMACH_SPI0_TX S3C64XX_DMA_CHAN("spi0_tx")
#define DMACH_SPI0_RX S3C64XX_DMA_CHAN("spi0_rx")
-#define DMACH_HSI_I2SV40_TX S3C64XX_DMA_CHAN("i2s2_tx")
-#define DMACH_HSI_I2SV40_RX S3C64XX_DMA_CHAN("i2s2_rx")
+#define DMACH_HSI_I2SV40_TX "i2s2_tx"
+#define DMACH_HSI_I2SV40_RX "i2s2_rx"
/* DMA1/SDMA1 */
-#define DMACH_PCM1_TX S3C64XX_DMA_CHAN("pcm1_tx")
-#define DMACH_PCM1_RX S3C64XX_DMA_CHAN("pcm1_rx")
-#define DMACH_I2S1_OUT S3C64XX_DMA_CHAN("i2s1_tx")
-#define DMACH_I2S1_IN S3C64XX_DMA_CHAN("i2s1_rx")
+#define DMACH_PCM1_TX "pcm1_tx"
+#define DMACH_PCM1_RX "pcm1_rx"
+#define DMACH_I2S1_OUT "i2s1_tx"
+#define DMACH_I2S1_IN "i2s1_rx"
#define DMACH_SPI1_TX S3C64XX_DMA_CHAN("spi1_tx")
#define DMACH_SPI1_RX S3C64XX_DMA_CHAN("spi1_rx")
-#define DMACH_AC97_PCMOUT S3C64XX_DMA_CHAN("ac97_out")
-#define DMACH_AC97_PCMIN S3C64XX_DMA_CHAN("ac97_in")
-#define DMACH_AC97_MICIN S3C64XX_DMA_CHAN("ac97_mic")
-#define DMACH_PWM S3C64XX_DMA_CHAN("pwm")
-#define DMACH_IRDA S3C64XX_DMA_CHAN("irda")
-#define DMACH_EXTERNAL S3C64XX_DMA_CHAN("external")
-#define DMACH_SECURITY_RX S3C64XX_DMA_CHAN("sec_rx")
-#define DMACH_SECURITY_TX S3C64XX_DMA_CHAN("sec_tx")
+#define DMACH_AC97_PCMOUT "ac97_out"
+#define DMACH_AC97_PCMIN "ac97_in"
+#define DMACH_AC97_MICIN "ac97_mic"
+#define DMACH_PWM "pwm"
+#define DMACH_IRDA "irda"
+#define DMACH_EXTERNAL "external"
+#define DMACH_SECURITY_RX "sec_rx"
+#define DMACH_SECURITY_TX "sec_tx"
enum dma_ch {
DMACH_MAX = 32
diff --git a/arch/arm/mach-socfpga/headsmp.S b/arch/arm/mach-socfpga/headsmp.S
index f65ea0a..a2b1c4a 100644
--- a/arch/arm/mach-socfpga/headsmp.S
+++ b/arch/arm/mach-socfpga/headsmp.S
@@ -12,6 +12,7 @@
#include <asm/memory.h>
.arch armv7-a
+ .arm
ENTRY(secondary_trampoline)
/* CPU1 will always fetch from 0x0 when it is brought out of reset.
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 5e65ca8..2d5ac8f 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -136,73 +136,6 @@
dsb(st);
}
-#ifdef CONFIG_CACHE_PL310
-static inline void cache_wait(void __iomem *reg, unsigned long mask)
-{
- /* cache operations by line are atomic on PL310 */
-}
-#else
-#define cache_wait l2c_wait_mask
-#endif
-
-static inline void cache_sync(void)
-{
- void __iomem *base = l2x0_base;
-
- writel_relaxed(0, base + sync_reg_offset);
- cache_wait(base + L2X0_CACHE_SYNC, 1);
-}
-
-#if defined(CONFIG_PL310_ERRATA_588369) || defined(CONFIG_PL310_ERRATA_727915)
-static inline void debug_writel(unsigned long val)
-{
- l2c_set_debug(l2x0_base, val);
-}
-#else
-/* Optimised out for non-errata case */
-static inline void debug_writel(unsigned long val)
-{
-}
-#endif
-
-static void l2x0_cache_sync(void)
-{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&l2x0_lock, flags);
- cache_sync();
- raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-}
-
-static void __l2x0_flush_all(void)
-{
- debug_writel(0x03);
- __l2c_op_way(l2x0_base + L2X0_CLEAN_INV_WAY);
- cache_sync();
- debug_writel(0x00);
-}
-
-static void l2x0_flush_all(void)
-{
- unsigned long flags;
-
- /* clean all ways */
- raw_spin_lock_irqsave(&l2x0_lock, flags);
- __l2x0_flush_all();
- raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-}
-
-static void l2x0_disable(void)
-{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&l2x0_lock, flags);
- __l2x0_flush_all();
- l2c_write_sec(0, l2x0_base, L2X0_CTRL);
- dsb(st);
- raw_spin_unlock_irqrestore(&l2x0_lock, flags);
-}
-
static void l2c_save(void __iomem *base)
{
l2x0_saved_regs.aux_ctrl = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
@@ -1257,14 +1190,15 @@
static void aurora_pa_range(unsigned long start, unsigned long end,
unsigned long offset)
{
+ void __iomem *base = l2x0_base;
unsigned long flags;
raw_spin_lock_irqsave(&l2x0_lock, flags);
- writel_relaxed(start, l2x0_base + AURORA_RANGE_BASE_ADDR_REG);
- writel_relaxed(end, l2x0_base + offset);
+ writel_relaxed(start, base + AURORA_RANGE_BASE_ADDR_REG);
+ writel_relaxed(end, base + offset);
raw_spin_unlock_irqrestore(&l2x0_lock, flags);
- cache_sync();
+ writel_relaxed(0, base + AURORA_SYNC_REG);
}
static void aurora_inv_range(unsigned long start, unsigned long end)
@@ -1324,6 +1258,37 @@
}
}
+static void aurora_flush_all(void)
+{
+ void __iomem *base = l2x0_base;
+ unsigned long flags;
+
+ /* clean all ways */
+ raw_spin_lock_irqsave(&l2x0_lock, flags);
+ __l2c_op_way(base + L2X0_CLEAN_INV_WAY);
+ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+
+ writel_relaxed(0, base + AURORA_SYNC_REG);
+}
+
+static void aurora_cache_sync(void)
+{
+ writel_relaxed(0, l2x0_base + AURORA_SYNC_REG);
+}
+
+static void aurora_disable(void)
+{
+ void __iomem *base = l2x0_base;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&l2x0_lock, flags);
+ __l2c_op_way(base + L2X0_CLEAN_INV_WAY);
+ writel_relaxed(0, base + AURORA_SYNC_REG);
+ l2c_write_sec(0, base, L2X0_CTRL);
+ dsb(st);
+ raw_spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
static void aurora_save(void __iomem *base)
{
l2x0_saved_regs.ctrl = readl_relaxed(base + L2X0_CTRL);
@@ -1398,9 +1363,9 @@
.inv_range = aurora_inv_range,
.clean_range = aurora_clean_range,
.flush_range = aurora_flush_range,
- .flush_all = l2x0_flush_all,
- .disable = l2x0_disable,
- .sync = l2x0_cache_sync,
+ .flush_all = aurora_flush_all,
+ .disable = aurora_disable,
+ .sync = aurora_cache_sync,
.resume = aurora_resume,
},
};
diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c
index 83c7d15..8b67db8c 100644
--- a/arch/arm/plat-samsung/devs.c
+++ b/arch/arm/plat-samsung/devs.c
@@ -65,6 +65,7 @@
#include <linux/platform_data/usb-ohci-s3c2410.h>
#include <plat/usb-phy.h>
#include <plat/regs-spi.h>
+#include <linux/platform_data/asoc-s3c.h>
#include <linux/platform_data/spi-s3c64xx.h>
static u64 samsung_device_dma_mask = DMA_BIT_MASK(32);
@@ -74,9 +75,12 @@
static struct resource s3c_ac97_resource[] = {
[0] = DEFINE_RES_MEM(S3C2440_PA_AC97, S3C2440_SZ_AC97),
[1] = DEFINE_RES_IRQ(IRQ_S3C244X_AC97),
- [2] = DEFINE_RES_DMA_NAMED(DMACH_PCM_OUT, "PCM out"),
- [3] = DEFINE_RES_DMA_NAMED(DMACH_PCM_IN, "PCM in"),
- [4] = DEFINE_RES_DMA_NAMED(DMACH_MIC_IN, "Mic in"),
+};
+
+static struct s3c_audio_pdata s3c_ac97_pdata = {
+ .dma_playback = (void *)DMACH_PCM_OUT,
+ .dma_capture = (void *)DMACH_PCM_IN,
+ .dma_capture_mic = (void *)DMACH_MIC_IN,
};
struct platform_device s3c_device_ac97 = {
@@ -87,6 +91,7 @@
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &s3c_ac97_pdata,
}
};
#endif /* CONFIG_CPU_S3C2440 */
diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c
index 0e15f01..76e2ec6 100644
--- a/arch/arm/xen/enlighten.c
+++ b/arch/arm/xen/enlighten.c
@@ -295,8 +295,7 @@
* for secondary CPUs as they are brought up.
* For uniformity we use VCPUOP_register_vcpu_info even on cpu0.
*/
- xen_vcpu_info = __alloc_percpu(sizeof(struct vcpu_info),
- sizeof(struct vcpu_info));
+ xen_vcpu_info = alloc_percpu(struct vcpu_info);
if (xen_vcpu_info == NULL)
return -ENOMEM;
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 3080a75..08dc963 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -14,6 +14,8 @@
select ARCH_HAS_UBSAN_SANITIZE_ALL
select ARM_AMBA
select ARM_ARCH_TIMER
+ select HAVE_KERNEL_GZIP
+ select HAVE_KERNEL_LZ4
select ARM_GIC
select AUDIT_ARCH_COMPAT_GENERIC
select ARM_GIC_V2M if PCI_MSI
@@ -27,7 +29,7 @@
select GENERIC_ALLOCATOR
select EDAC_SUPPORT
select GENERIC_CLOCKEVENTS
- select GENERIC_CLOCKEVENTS_BROADCAST if SMP
+ select GENERIC_CLOCKEVENTS_BROADCAST
select GENERIC_CPU_AUTOPROBE
select GENERIC_EARLY_IOREMAP
select GENERIC_IOMAP
@@ -42,6 +44,7 @@
select HARDIRQS_SW_RESEND
select HAVE_ALIGNED_STRUCT_PAGE if SLUB
select HAVE_ARCH_AUDITSYSCALL
+ select HAVE_ARCH_HARDENED_USERCOPY
select HAVE_ARCH_JUMP_LABEL
select HAVE_ARCH_KASAN if SPARSEMEM_VMEMMAP && !(ARM64_16K_PAGES && ARM64_VA_BITS_48)
select HAVE_ARCH_KGDB
@@ -203,6 +206,9 @@
endif
+config SMP
+ def_bool y
+
config SWIOTLB
def_bool y
@@ -614,22 +620,8 @@
and spin_unlock to ensure that the core waiting on the lock
wakes up from WFE.
-config SMP
- bool "Symmetric Multi-Processing"
- help
- This enables support for systems with more than one CPU. If
- you say N here, the kernel will run on single and
- multiprocessor machines, but will use only one CPU of a
- multiprocessor machine. If you say Y here, the kernel will run
- on many, but not all, single processor machines. On a single
- processor machine, the kernel will run faster if you say N
- here.
-
- If you don't know what to do here, say N.
-
config SCHED_MC
bool "Multi-core scheduler support"
- depends on SMP
help
Multi-core scheduler support improves the CPU scheduler's decision
making when dealing with multi-core CPU chips at a cost of slightly
@@ -637,7 +629,6 @@
config SCHED_SMT
bool "SMT scheduler support"
- depends on SMP
help
Improves the CPU scheduler's decision making when dealing with
MultiThreading at a cost of slightly increased overhead in some
@@ -661,8 +652,6 @@
config NR_CPUS
int "Maximum number of CPUs (2-64)"
- range 2 64
- depends on SMP
# These have to remain sorted largest to smallest
default "8"
@@ -675,7 +664,6 @@
config HOTPLUG_CPU
bool "Support for hot-pluggable CPUs"
- depends on SMP
help
Say Y here to experiment with turning CPUs off and on. CPUs
can be controlled through /sys/devices/system/cpu.
@@ -795,20 +783,6 @@
default "14" if (ARM64_64K_PAGES && TRANSPARENT_HUGEPAGE)
default "11"
-config ARM64_PAN
- bool "Enable support for Privileged Access Never (PAN)"
- default y
- help
- Privileged Access Never (PAN; part of the ARMv8.1 Extensions)
- prevents the kernel or hypervisor from accessing user-space (EL0)
- memory directly.
-
- Choosing this option will cause any unprotected (not using
- copy_to_user et al) memory access to fail with a permission fault.
-
- The feature is detected at runtime, and will remain as a 'nop'
- instruction if the cpu does not implement the feature.
-
menuconfig ARMV8_DEPRECATED
bool "Emulate deprecated/obsolete ARMv8 instructions"
depends on COMPAT
@@ -877,6 +851,53 @@
endif
+config ARM64_SW_TTBR0_PAN
+ bool "Emulate Priviledged Access Never using TTBR0_EL1 switching"
+ help
+ Enabling this option prevents the kernel from accessing
+ user-space memory directly by pointing TTBR0_EL1 to a reserved
+ zeroed area and reserved ASID. The user access routines
+ restore the valid TTBR0_EL1 temporarily.
+
+menu "ARMv8.1 architectural features"
+
+config ARM64_PAN
+ bool "Enable support for Privileged Access Never (PAN)"
+ default y
+ help
+ Privileged Access Never (PAN; part of the ARMv8.1 Extensions)
+ prevents the kernel or hypervisor from accessing user-space (EL0)
+ memory directly.
+
+ Choosing this option will cause any unprotected (not using
+ copy_to_user et al) memory access to fail with a permission fault.
+
+ The feature is detected at runtime, and will remain as a 'nop'
+ instruction if the cpu does not implement the feature.
+
+endmenu
+
+config ARM64_UAO
+ bool "Enable support for User Access Override (UAO)"
+ default y
+ help
+ User Access Override (UAO; part of the ARMv8.2 Extensions)
+ causes the 'unprivileged' variant of the load/store instructions to
+ be overriden to be privileged.
+
+ This option changes get_user() and friends to use the 'unprivileged'
+ variant of the load/store instructions. This ensures that user-space
+ really did have access to the supplied memory. When addr_limit is
+ set to kernel memory the UAO bit will be set, allowing privileged
+ access to kernel memory.
+
+ Choosing this option will cause copy_to_user() et al to use user-space
+ memory permissions.
+
+ The feature is detected at runtime, the kernel will use the
+ regular load/store instructions if the cpu does not implement the
+ feature.
+
endmenu
menu "Boot options"
@@ -1024,6 +1045,24 @@
endmenu
+menu "Expermimental Security Enhancements"
+
+config KUSER_HELPERS_SELECTIVE_DISABLE
+ bool "Disable KUSER_HELPERS selectively"
+ depends on ARM64
+ help
+ Kuser_Helpers is a set of functions in the vector page at a fixed address that
+ are used to support applications compiled for legacy ARM <7. These functions
+ being at a fixed address creates an ASLR bypass.
+
+ Do not enable this feature unless you know what you are doing, this is
+ experimental, and will break any application that uses the Kuser_Helpers or
+ registers a signal handler and uses the kernel trampolines.
+
+ If unsure, say N.
+
+endmenu
+
source "net/Kconfig"
source "drivers/Kconfig"
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 3a583c0..8c84f8c 100755
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -64,11 +64,14 @@
libs-y := arch/arm64/lib/ $(libs-y)
libs-$(CONFIG_EFI_STUB) += drivers/firmware/efi/libstub/
+suffix_$(CONFIG_KERNEL_GZIP) = gz
+suffix_$(CONFIG_KERNEL_LZ4) = lz4
+
# Default target when executing plain make
ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y)
-KBUILD_IMAGE := Image.gz-dtb
+KBUILD_IMAGE := Image.$(suffix_y)-dtb
else
-KBUILD_IMAGE := Image.gz
+KBUILD_IMAGE := Image.$(suffix_y)
endif
KBUILD_DTBS := dtbs
@@ -94,7 +97,7 @@
dtbs_install:
$(Q)$(MAKE) $(dtbinst)=$(boot)/dts
-Image.gz-dtb: vmlinux scripts dtbs
+Image-dtb Image.gz-dtb Image.lz4-dtb: vmlinux scripts dtbs
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
PHONY += vdso_install
@@ -108,6 +111,7 @@
define archhelp
echo '* Image.gz - Compressed kernel image (arch/$(ARCH)/boot/Image.gz)'
+ echo '* Image.lz4 - Compressed kernel image (arch/$(ARCH)/boot/Image.lz4)'
echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
echo '* dtbs - Build device tree blobs for enabled boards'
echo ' dtbs_install - Install dtbs to $(INSTALL_DTBS_PATH)'
diff --git a/arch/arm64/boot/.gitignore b/arch/arm64/boot/.gitignore
index eb35511..651eae0 100644
--- a/arch/arm64/boot/.gitignore
+++ b/arch/arm64/boot/.gitignore
@@ -1,3 +1,5 @@
Image
Image.gz
Image.gz-dtb
+Image.lz4
+Image.lz4-dtb
diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile
index c698d62..b7cf2a4 100644
--- a/arch/arm64/boot/Makefile
+++ b/arch/arm64/boot/Makefile
@@ -16,7 +16,7 @@
include $(srctree)/arch/arm64/boot/dts/htc/Makefile
-targets := Image Image.gz
+targets := Image Image.gz Image.lz4
DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES))
ifneq ($(DTB_NAMES),)
@@ -32,9 +32,15 @@
$(obj)/Image.gz: $(obj)/Image FORCE
$(call if_changed,gzip)
+$(obj)/Image.lz4: $(obj)/Image FORCE
+ $(call if_changed,lz4)
+
$(obj)/Image.gz-dtb: $(obj)/Image.gz $(DTB_OBJS) FORCE
$(call if_changed,cat)
+$(obj)/Image.lz4-dtb: $(obj)/Image.lz4 $(DTB_OBJS) FORCE
+ $(call if_changed,cat)
+
install: $(obj)/Image
$(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
$(obj)/Image System.map "$(INSTALL_PATH)"
diff --git a/arch/arm64/boot/dts/htc/msm8996-htc-common.dtsi b/arch/arm64/boot/dts/htc/msm8996-htc-common.dtsi
index 4fedb4b..bd10e6f 100644
--- a/arch/arm64/boot/dts/htc/msm8996-htc-common.dtsi
+++ b/arch/arm64/boot/dts/htc/msm8996-htc-common.dtsi
@@ -49,6 +49,22 @@
reg = <0 0x83500000 0 0x00100000>;
};
};
+
+ firmware {
+ android {
+ compatible = "android,firmware";
+ fstab {
+ compatible = "android,fstab";
+ vendor {
+ compatible = "android,vendor";
+ dev = "/dev/block/platform/soc/624000.ufshc/by-name/vendor";
+ type = "ext4";
+ mnt_flags = "ro,barrier=1,discard";
+ fsmgr_flags = "wait,slotselect,verify";
+ };
+ };
+ };
+ };
};
&soc {
diff --git a/arch/arm64/boot/dts/htc/msm8996-htc-pm.dtsi b/arch/arm64/boot/dts/htc/msm8996-htc-pm.dtsi
index 3904d2c..d30286b 100644
--- a/arch/arm64/boot/dts/htc/msm8996-htc-pm.dtsi
+++ b/arch/arm64/boot/dts/htc/msm8996-htc-pm.dtsi
@@ -52,14 +52,4 @@
reg = <0x290000 0x4>;
reg-names = "rpm_data_base";
};
-
- cpu_fuse@70134 {
- compatible = "qcom,cpucx-8996";
- reg = <0x70134 0x4>;
- };
-
- cpu_fuse@70148 {
- compatible = "qcom,cpumx-8996";
- reg = <0x70148 0x4>;
- };
};
diff --git a/arch/arm64/boot/dts/htc/msm8996-htc_marlin.dtsi b/arch/arm64/boot/dts/htc/msm8996-htc_marlin.dtsi
index c829eff..7fd9ade 100644
--- a/arch/arm64/boot/dts/htc/msm8996-htc_marlin.dtsi
+++ b/arch/arm64/boot/dts/htc/msm8996-htc_marlin.dtsi
@@ -238,7 +238,7 @@
qcom,fg-iterm-ma = <345>;
qcom,fg-chg-iterm-ma = <50>;
qcom,fg-cc-cv-threshold-mv = <4390>;
- qcom,ext-sense-type = <1>;
+ qcom,ext-sense-type;
qcom,cold-hot-jeita-hysteresis = <20 30>;
qcom,fg-cutoff-voltage-mv = <3250>;
qcom,irq-volt-empty-mv = <3050>;
diff --git a/arch/arm64/boot/dts/htc/msm8996-htc_sailfish.dtsi b/arch/arm64/boot/dts/htc/msm8996-htc_sailfish.dtsi
index 310dc7b..31dd055 100644
--- a/arch/arm64/boot/dts/htc/msm8996-htc_sailfish.dtsi
+++ b/arch/arm64/boot/dts/htc/msm8996-htc_sailfish.dtsi
@@ -54,11 +54,11 @@
qcom,fg-iterm-ma = <277>;
qcom,fg-chg-iterm-ma = <50>;
qcom,fg-cc-cv-threshold-mv = <4390>;
- qcom,ext-sense-type = <1>;
+ qcom,ext-sense-type;
qcom,cold-hot-jeita-hysteresis = <20 30>;
qcom,fg-cutoff-voltage-mv = <3250>;
qcom,irq-volt-empty-mv = <3050>;
- /delete-property/ qcom,capacity-learning-feedback;
+ qcom,capacity-learning-feedback;
};
/{
diff --git a/arch/arm64/boot/dts/htc/msm8996-touch-s1.dtsi b/arch/arm64/boot/dts/htc/msm8996-touch-s1.dtsi
index ede7626..90bd738 100755
--- a/arch/arm64/boot/dts/htc/msm8996-touch-s1.dtsi
+++ b/arch/arm64/boot/dts/htc/msm8996-touch-s1.dtsi
@@ -182,7 +182,7 @@
pr_number = <2433782>;
sensor_id = <0x1>;
config = [
- 53 31 76 32 00 19 00 00
+ 53 31 76 32 00 21 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
@@ -233,7 +233,7 @@
00 80 04 1B 00 00 FF E8
03 FF FF BF 0A B8 0B 00
C0 80 04 10 0A 18 00 79
- 08 0F 00 00 C8 00 03 00
+ 08 FF FF 00 C8 00 03 00
03 03 07 07 03 C8 00 01
04 83 36 00 16 00 00 00
02 00 01 2D 84 35 00 16
@@ -251,11 +251,11 @@
05 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 21 00 06 00 28
- 00 0A 19 00 01 1B 01 00
- 78 00 28 00 28 00 3C 00
+ 00 32 19 00 01 1B 01 00
+ 78 00 28 00 28 00 AF 00
1E 03 0C 0C 14 28 04 00
00 00 00 00 00 00 00 00
- 00 15 00 80 07 01 07 07
+ 00 15 02 80 07 01 07 07
1D 1D 00 00 07 07 01 07
07 1D 1D 00 00 07 04 04
50 00 07 03 00 36 22 00
diff --git a/arch/arm64/configs/marlin_defconfig b/arch/arm64/configs/marlin_defconfig
index 0b122f2..f79b2e4 100644
--- a/arch/arm64/configs/marlin_defconfig
+++ b/arch/arm64/configs/marlin_defconfig
@@ -1,14 +1,20 @@
+CONFIG_KERNEL_LZ4=y
+# CONFIG_USELIB is not set
CONFIG_AUDIT=y
-# CONFIG_AUDITSYSCALL is not set
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_IRQ_TIME_ACCOUNTING=y
CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_FAST_NO_HZ=y
CONFIG_RCU_BOOST=y
CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_CPU_MAX_BUF_SHIFT=15
CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
@@ -21,9 +27,7 @@
# CONFIG_PID_NS is not set
CONFIG_SCHED_TUNE=y
CONFIG_BLK_DEV_INITRD=y
-CONFIG_RD_BZIP2=y
-CONFIG_RD_LZMA=y
-CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+# CONFIG_SYSFS_SYSCALL is not set
CONFIG_KALLSYMS_ALL=y
CONFIG_EMBEDDED=y
# CONFIG_SLUB_DEBUG is not set
@@ -31,12 +35,16 @@
CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_ARCH_MMAP_RND_BITS=24
CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
CONFIG_PARTITION_ADVANCED=y
+CONFIG_IOSCHED_TEST=y
CONFIG_ARCH_MSM=y
CONFIG_ARCH_MSM8996=y
CONFIG_PCI_MSM=y
CONFIG_ENABLE_FP_SIMD_SETTINGS=y
-CONFIG_SMP=y
CONFIG_SCHED_MC=y
CONFIG_NR_CPUS=4
CONFIG_PREEMPT=y
@@ -49,6 +57,9 @@
CONFIG_SECCOMP=y
CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
+CONFIG_CP15_BARRIER_EMULATION=y
+CONFIG_SETEND_EMULATION=y
+CONFIG_ARM64_SW_TTBR0_PAN=y
CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_COMPAT=y
@@ -65,6 +76,7 @@
CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_CPU_FREQ_GOV_SCHED=y
+# CONFIG_ARM64_ERRATUM_843419 is not set
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
@@ -72,6 +84,7 @@
CONFIG_XFRM_STATISTICS=y
CONFIG_NET_KEY=y
CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_ROUTE_VERBOSE=y
@@ -83,6 +96,7 @@
# CONFIG_INET_XFRM_MODE_BEET is not set
# CONFIG_INET_LRO is not set
CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IPV6=y
CONFIG_IPV6_ROUTER_PREF=y
CONFIG_IPV6_ROUTE_INFO=y
CONFIG_IPV6_OPTIMISTIC_DAD=y
@@ -93,6 +107,7 @@
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_IPV6_SUBTREES=y
CONFIG_NETFILTER=y
+CONFIG_BRIDGE_NETFILTER=y
CONFIG_NF_CONNTRACK=y
CONFIG_NF_CONNTRACK_SECMARK=y
CONFIG_NF_CONNTRACK_EVENTS=y
@@ -212,6 +227,7 @@
CONFIG_BT_BNEP_PROTO_FILTER=y
CONFIG_BT_HIDP=y
CONFIG_BT_BDA=y
+CONFIG_MSM_BT_POWER=y
CONFIG_CFG80211=y
CONFIG_CFG80211_INTERNAL_REGDB=y
CONFIG_RFKILL=y
@@ -228,14 +244,12 @@
CONFIG_UID_STAT=y
CONFIG_QSEECOM=y
CONFIG_HDCP_QSEECOM=y
-CONFIG_UID_CPUTIME=y
-CONFIG_TI_DRV2667=y
-CONFIG_EEPROM_AT24=y
+CONFIG_UID_SYS_STATS=y
+CONFIG_MEMORY_STATE_TIME=y
CONFIG_FPR_FPC=y
CONFIG_SCSI=y
CONFIG_BLK_DEV_SD=y
CONFIG_CHR_DEV_SG=y
-CONFIG_CHR_DEV_SCH=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
@@ -243,6 +257,7 @@
CONFIG_SCSI_UFSHCD_PLATFORM=y
CONFIG_SCSI_UFS_QCOM=y
CONFIG_SCSI_UFS_QCOM_ICE=y
+CONFIG_SCSI_UFS_TEST=y
CONFIG_MD=y
CONFIG_BLK_DEV_DM=y
CONFIG_DM_CRYPT=y
@@ -268,13 +283,21 @@
CONFIG_PPPOPNS=y
CONFIG_PPP_ASYNC=y
CONFIG_PPP_SYNC_TTY=y
+CONFIG_USB_RTL8150=y
+CONFIG_USB_RTL8152=y
CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
CONFIG_WCNSS_MEM_PRE_ALLOC=y
CONFIG_CNSS_CRYPTO=y
CONFIG_CNSS=y
CONFIG_CLD_LL_CORE=y
+CONFIG_BUS_AUTO_SUSPEND=y
CONFIG_HTC_WLAN_NV=y
CONFIG_WLAN_FEATURE_RX_WAKELOCK=y
+CONFIG_CNSS_GENL=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_KEYRESET=y
CONFIG_KEYBOARD_GPIO=y
@@ -288,7 +311,6 @@
CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_HTC_v26=y
CONFIG_INPUT_MISC=y
CONFIG_STMVL53L0=y
-CONFIG_INPUT_HBTP_INPUT=y
CONFIG_INPUT_KEYCHORD=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=y
@@ -300,6 +322,8 @@
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
CONFIG_SERIAL_MSM_SMD=y
+CONFIG_DIAG_CHAR=y
+CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_MSM_LEGACY=y
# CONFIG_DEVPORT is not set
CONFIG_MSM_SMD_PKT=y
@@ -381,6 +405,7 @@
CONFIG_MSMB_JPEG=y
CONFIG_MSM_FD=y
CONFIG_MSM_JPEGDMA=y
+# CONFIG_VGA_ARB is not set
CONFIG_MSM_KGSL=y
CONFIG_FB=y
CONFIG_FB_MSM=y
@@ -391,6 +416,8 @@
CONFIG_FB_MSM_MDSS_HDMI_PANEL=y
CONFIG_FB_MSM_MDSS_XLOG_DEBUG=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_USB_AUDIO=y
@@ -408,18 +435,7 @@
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_STORAGE=y
-CONFIG_USB_STORAGE_DATAFAB=y
-CONFIG_USB_STORAGE_FREECOM=y
-CONFIG_USB_STORAGE_ISD200=y
-CONFIG_USB_STORAGE_USBAT=y
-CONFIG_USB_STORAGE_SDDR09=y
-CONFIG_USB_STORAGE_SDDR55=y
-CONFIG_USB_STORAGE_JUMPSHOT=y
-CONFIG_USB_STORAGE_ALAUDA=y
-CONFIG_USB_STORAGE_KARMA=y
-CONFIG_USB_STORAGE_CYPRESS_ATACB=y
CONFIG_USB_DWC3=y
-CONFIG_FUSB_30X=y
CONFIG_USB_SERIAL=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_NOP_USB_XCEIV=y
@@ -432,17 +448,7 @@
CONFIG_USB_GADGET_VBUS_DRAW=500
CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=4
CONFIG_USB_G_ANDROID=y
-CONFIG_MMC=y
-CONFIG_MMC_PERF_PROFILING=y
-CONFIG_MMC_CLKGATE=y
-CONFIG_MMC_PARANOID_SD_INIT=y
-CONFIG_MMC_BLOCK_MINORS=32
-CONFIG_MMC_TEST=y
-CONFIG_MMC_SDHCI=y
-CONFIG_MMC_SDHCI_PLTFM=y
-CONFIG_MMC_SDHCI_MSM=y
-CONFIG_MMC_SDHCI_MSM_ICE=y
-CONFIG_MMC_CQ_HCI=y
+CONFIG_TYPEC_FUSB302=y
CONFIG_LEDS_QPNP=y
CONFIG_LEDS_QPNP_FLASH=y
CONFIG_LEDS_QPNP_WLED=y
@@ -466,7 +472,6 @@
CONFIG_ASHMEM=y
CONFIG_ANDROID_TIMED_GPIO=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
-CONFIG_SW_SYNC_USER=y
CONFIG_ION=y
CONFIG_ION_MSM=y
CONFIG_QCA_CLD_WLAN=y
@@ -477,6 +482,7 @@
CONFIG_WLAN_FEATURE_LPSS=y
CONFIG_QCOM_VOWIFI_11R=y
CONFIG_WLAN_FEATURE_NAN=y
+CONFIG_WLAN_FEATURE_NAN_DATAPATH=y
CONFIG_QCOM_TDLS=y
CONFIG_QCOM_LTE_COEX=y
CONFIG_WLAN_FEATURE_MEMDUMP=y
@@ -495,7 +501,6 @@
CONFIG_SPS_SUPPORT_NDP_BAM=y
CONFIG_IPA=y
CONFIG_RMNET_IPA=y
-CONFIG_SEEMP_CORE=y
CONFIG_QPNP_HAPTIC=y
CONFIG_GPIO_USB_DETECT=y
CONFIG_MSM_SPMI=y
@@ -527,7 +532,6 @@
CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
CONFIG_MSM_GLINK_SMD_XPRT=y
CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
-CONFIG_MSM_SMEM_LOGGING=y
CONFIG_MSM_SMP2P=y
CONFIG_MSM_SMP2P_TEST=y
CONFIG_MSM_SPM=y
@@ -554,9 +558,6 @@
CONFIG_REMOTE_SPINLOCK_MSM=y
CONFIG_IOMMU_IO_PGTABLE_FAST=y
CONFIG_ARM_SMMU=y
-CONFIG_IOMMU_DEBUG=y
-CONFIG_IOMMU_DEBUG_TRACKING=y
-CONFIG_IOMMU_TESTS=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
CONFIG_IIO=y
@@ -571,12 +572,14 @@
CONFIG_HTC_NC_GPIO_PIN_SETTING=y
CONFIG_HTC_DEBUG_BOOTLOADER_LOG=y
CONFIG_MSM_TZ_LOG=y
-CONFIG_MOBICORE_DRIVER=y
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_QFMT_V2=y
CONFIG_FUSE_FS=y
-CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
@@ -596,10 +599,14 @@
CONFIG_LOG_BUF_MAGIC=y
CONFIG_DEBUG_INFO=y
CONFIG_MAGIC_SYSRQ=y
+CONFIG_LOCKUP_DETECTOR=y
CONFIG_PANIC_TIMEOUT=5
CONFIG_PANIC_ON_RECURSIVE_FAULT=y
CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=3
CONFIG_IPC_LOGGING=y
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_CPU_FREQ_SWITCH_PROFILER=y
@@ -610,14 +617,15 @@
CONFIG_SECURITY=y
CONFIG_SECURITY_NETWORK=y
CONFIG_LSM_MMAP_MIN_ADDR=4096
+CONFIG_HARDENED_USERCOPY=y
CONFIG_SECURITY_SELINUX=y
# CONFIG_INTEGRITY is not set
-# CONFIG_DEBUG_PREEMPT is not set
CONFIG_CRYPTO_NULL=y
CONFIG_CRYPTO_XCBC=y
CONFIG_CRYPTO_MD4=y
CONFIG_CRYPTO_SHA512=y
CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_CRYPTO_DEV_QCRYPTO=y
CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
CONFIG_CRYPTO_DEV_QCEDEV=y
@@ -637,7 +645,3 @@
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
CONFIG_QMI_ENCDEC=y
-CONFIG_LOCKUP_DETECTOR=y
-CONFIG_HARD_LOCKUP_DETECTOR_OTHER_CPU=y
-CONFIG_RCU_CPU_STALL_TIMEOUT=3
-CONFIG_SCHED_STACK_END_CHECK=y
diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c
index 5f63a79..31d014f 100644
--- a/arch/arm64/crypto/aes-glue.c
+++ b/arch/arm64/crypto/aes-glue.c
@@ -205,7 +205,7 @@
err = blkcipher_walk_done(desc, &walk,
walk.nbytes % AES_BLOCK_SIZE);
}
- if (nbytes) {
+ if (walk.nbytes % AES_BLOCK_SIZE) {
u8 *tdst = walk.dst.virt.addr + blocks * AES_BLOCK_SIZE;
u8 *tsrc = walk.src.virt.addr + blocks * AES_BLOCK_SIZE;
u8 __aligned(8) tail[AES_BLOCK_SIZE];
diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h
index 4e3d4c8..fa16d56 100644
--- a/arch/arm64/include/asm/alternative.h
+++ b/arch/arm64/include/asm/alternative.h
@@ -1,8 +1,11 @@
#ifndef __ASM_ALTERNATIVE_H
#define __ASM_ALTERNATIVE_H
+#include <asm/cpufeature.h>
+
#ifndef __ASSEMBLY__
+#include <linux/init.h>
#include <linux/kconfig.h>
#include <linux/types.h>
#include <linux/stddef.h>
@@ -16,7 +19,8 @@
u8 alt_len; /* size of new instruction(s), <= orig_len */
};
-void apply_alternatives(void);
+void __init apply_alternatives_all(void);
+void apply_alternatives(void *start, size_t length);
void free_alternatives_memory(void);
#define ALTINSTR_ENTRY(feature) \
@@ -62,6 +66,8 @@
#else
+#include <asm/assembler.h>
+
.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
.word \orig_offset - .
.word \alt_offset - .
@@ -129,6 +135,74 @@
alternative_insn insn1, insn2, cap, IS_ENABLED(cfg)
+/*
+ * Generate the assembly for UAO alternatives with exception table entries.
+ * This is complicated as there is no post-increment or pair versions of the
+ * unprivileged instructions, and USER() only works for single instructions.
+ */
+#ifdef CONFIG_ARM64_UAO
+ .macro uao_ldp l, reg1, reg2, addr, post_inc
+ alternative_if_not ARM64_HAS_UAO
+8888: ldp \reg1, \reg2, [\addr], \post_inc;
+8889: nop;
+ nop;
+ alternative_else
+ ldtr \reg1, [\addr];
+ ldtr \reg2, [\addr, #8];
+ add \addr, \addr, \post_inc;
+ alternative_endif
+
+ .section __ex_table,"a";
+ .align 3;
+ .quad 8888b,\l;
+ .quad 8889b,\l;
+ .previous;
+ .endm
+
+ .macro uao_stp l, reg1, reg2, addr, post_inc
+ alternative_if_not ARM64_HAS_UAO
+8888: stp \reg1, \reg2, [\addr], \post_inc;
+8889: nop;
+ nop;
+ alternative_else
+ sttr \reg1, [\addr];
+ sttr \reg2, [\addr, #8];
+ add \addr, \addr, \post_inc;
+ alternative_endif
+
+ .section __ex_table,"a";
+ .align 3;
+ .quad 8888b,\l;
+ .quad 8889b,\l;
+ .previous
+ .endm
+
+ .macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc
+ alternative_if_not ARM64_HAS_UAO
+8888: \inst \reg, [\addr], \post_inc;
+ nop;
+ alternative_else
+ \alt_inst \reg, [\addr];
+ add \addr, \addr, \post_inc;
+ alternative_endif
+
+ .section __ex_table,"a";
+ .align 3;
+ .quad 8888b,\l;
+ .previous
+ .endm
+#else
+ .macro uao_ldp l, reg1, reg2, addr, post_inc
+ USER(\l, ldp \reg1, \reg2, [\addr], \post_inc)
+ .endm
+ .macro uao_stp l, reg1, reg2, addr, post_inc
+ USER(\l, stp \reg1, \reg2, [\addr], \post_inc)
+ .endm
+ .macro uao_user_alternative l, inst, alt_inst, reg, addr, post_inc
+ USER(\l, \inst \reg, [\addr], \post_inc)
+ .endm
+#endif
+
#endif /* __ASSEMBLY__ */
/*
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 3579988..0ea2d57 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -1,5 +1,5 @@
/*
- * Based on arch/arm/include/asm/assembler.h
+ * Based on arch/arm/include/asm/assembler.h, arch/arm/mm/proc-macros.S
*
* Copyright (C) 1996-2000 Russell King
* Copyright (C) 2012 ARM Ltd.
@@ -23,6 +23,8 @@
#ifndef __ASM_ASSEMBLER_H
#define __ASM_ASSEMBLER_H
+#include <asm/asm-offsets.h>
+#include <asm/pgtable-hwdef.h>
#include <asm/ptrace.h>
#include <asm/thread_info.h>
@@ -49,6 +51,15 @@
msr daifclr, #2
.endm
+ .macro save_and_disable_irq, flags
+ mrs \flags, daif
+ msr daifset, #2
+ .endm
+
+ .macro restore_irq, flags
+ msr daif, \flags
+ .endm
+
/*
* Save/disable and restore interrupts.
*/
@@ -103,9 +114,7 @@
* SMP data memory barrier
*/
.macro smp_dmb, opt
-#ifdef CONFIG_SMP
dmb \opt
-#endif
.endm
#define USER(l, x...) \
@@ -208,6 +217,92 @@
.endm
/*
+ * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm)
+ */
+ .macro vma_vm_mm, rd, rn
+ ldr \rd, [\rn, #VMA_VM_MM]
+ .endm
+
+/*
+ * mmid - get context id from mm pointer (mm->context.id)
+ */
+ .macro mmid, rd, rn
+ ldr \rd, [\rn, #MM_CONTEXT_ID]
+ .endm
+
+/*
+ * dcache_line_size - get the minimum D-cache line size from the CTR register.
+ */
+ .macro dcache_line_size, reg, tmp
+ mrs \tmp, ctr_el0 // read CTR
+ ubfm \tmp, \tmp, #16, #19 // cache line size encoding
+ mov \reg, #4 // bytes per word
+ lsl \reg, \reg, \tmp // actual cache line size
+ .endm
+
+/*
+ * icache_line_size - get the minimum I-cache line size from the CTR register.
+ */
+ .macro icache_line_size, reg, tmp
+ mrs \tmp, ctr_el0 // read CTR
+ and \tmp, \tmp, #0xf // cache line size encoding
+ mov \reg, #4 // bytes per word
+ lsl \reg, \reg, \tmp // actual cache line size
+ .endm
+
+/*
+ * tcr_set_idmap_t0sz - update TCR.T0SZ so that we can load the ID map
+ */
+ .macro tcr_set_idmap_t0sz, valreg, tmpreg
+#ifndef CONFIG_ARM64_VA_BITS_48
+ ldr_l \tmpreg, idmap_t0sz
+ bfi \valreg, \tmpreg, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH
+#endif
+ .endm
+
+/*
+ * Macro to perform a data cache maintenance for the interval
+ * [kaddr, kaddr + size)
+ *
+ * op: operation passed to dc instruction
+ * domain: domain used in dsb instruciton
+ * kaddr: starting virtual address of the region
+ * size: size of the region
+ * Corrupts: kaddr, size, tmp1, tmp2
+ */
+ .macro dcache_by_line_op op, domain, kaddr, size, tmp1, tmp2
+ dcache_line_size \tmp1, \tmp2
+ add \size, \kaddr, \size
+ sub \tmp2, \tmp1, #1
+ bic \kaddr, \kaddr, \tmp2
+9998: dc \op, \kaddr
+ add \kaddr, \kaddr, \tmp1
+ cmp \kaddr, \size
+ b.lo 9998b
+ dsb \domain
+ .endm
+
+/*
+ * reset_pmuserenr_el0 - reset PMUSERENR_EL0 if PMUv3 present
+ */
+ .macro reset_pmuserenr_el0, tmpreg
+ mrs \tmpreg, id_aa64dfr0_el1 // Check ID_AA64DFR0_EL1 PMUVer
+ sbfx \tmpreg, \tmpreg, #8, #4
+ cmp \tmpreg, #1 // Skip if no PMU present
+ b.lt 9000f
+ msr pmuserenr_el0, xzr // Disable PMU access from EL0
+9000:
+ .endm
+
+/*
+ * Return the current thread_info.
+ */
+ .macro get_thread_info, rd
+ mov \rd, sp
+ and \rd, \rd, #~(THREAD_SIZE - 1) // top of stack
+ .endm
+
+/*
* Annotate a function as position independent, i.e., safe to be called before
* the kernel virtual mapping is activated.
*/
diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index 6389d60..a2c3035 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -32,27 +32,8 @@
#define rmb() dsb(ld)
#define wmb() dsb(st)
-#ifndef CONFIG_SMP
-#define smp_mb() barrier()
-#define smp_rmb() barrier()
-#define smp_wmb() barrier()
-
-#define smp_store_release(p, v) \
-do { \
- compiletime_assert_atomic_type(*p); \
- barrier(); \
- ACCESS_ONCE(*p) = (v); \
-} while (0)
-
-#define smp_load_acquire(p) \
-({ \
- typeof(*p) ___p1 = ACCESS_ONCE(*p); \
- compiletime_assert_atomic_type(*p); \
- barrier(); \
- ___p1; \
-})
-
-#else
+#define dma_rmb() dmb(oshld)
+#define dma_wmb() dmb(oshst)
#define smp_mb() dmb(ish)
#define smp_rmb() dmb(ishld)
@@ -90,8 +71,6 @@
___p1; \
})
-#endif
-
#define read_barrier_depends() do { } while(0)
#define smp_read_barrier_depends() do { } while(0)
diff --git a/arch/arm64/include/asm/cacheflush.h b/arch/arm64/include/asm/cacheflush.h
index 8539fe3..e9c549e 100644
--- a/arch/arm64/include/asm/cacheflush.h
+++ b/arch/arm64/include/asm/cacheflush.h
@@ -128,6 +128,13 @@
#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
extern void flush_dcache_page(struct page *);
+static inline void __local_flush_icache_all(void)
+{
+ asm("ic iallu");
+ dsb(nsh);
+ isb();
+}
+
static inline void __flush_icache_all(void)
{
asm("ic ialluis");
@@ -161,10 +168,6 @@
int set_memory_x(unsigned long addr, int numpages);
int set_memory_nx(unsigned long addr, int numpages);
-#ifdef CONFIG_DEBUG_RODATA
-void mark_rodata_ro(void);
-#endif
-
#ifdef CONFIG_KERNEL_TEXT_RDONLY
void set_kernel_text_ro(void);
#else
diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h
index 0564430..13a6103 100644
--- a/arch/arm64/include/asm/cpu.h
+++ b/arch/arm64/include/asm/cpu.h
@@ -30,13 +30,17 @@
u32 reg_dczid;
u32 reg_midr;
+ u64 reg_id_aa64dfr0;
+ u64 reg_id_aa64dfr1;
u64 reg_id_aa64isar0;
u64 reg_id_aa64isar1;
u64 reg_id_aa64mmfr0;
u64 reg_id_aa64mmfr1;
+ u64 reg_id_aa64mmfr2;
u64 reg_id_aa64pfr0;
u64 reg_id_aa64pfr1;
+ u32 reg_id_dfr0;
u32 reg_id_isar0;
u32 reg_id_isar1;
u32 reg_id_isar2;
@@ -49,6 +53,10 @@
u32 reg_id_mmfr3;
u32 reg_id_pfr0;
u32 reg_id_pfr1;
+
+ u32 reg_mvfr0;
+ u32 reg_mvfr1;
+ u32 reg_mvfr2;
};
DECLARE_PER_CPU(struct cpuinfo_arm64, cpu_data);
@@ -56,4 +64,8 @@
void cpuinfo_store_cpu(void);
void __init cpuinfo_store_boot_cpu(void);
+void __init init_cpu_features(struct cpuinfo_arm64 *info);
+void update_cpu_features(int cpu, struct cpuinfo_arm64 *info,
+ struct cpuinfo_arm64 *boot);
+
#endif /* __ASM_CPU_H */
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 46c3389..a0789bf 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -10,6 +10,7 @@
#define __ASM_CPUFEATURE_H
#include <asm/hwcap.h>
+#include <asm/sysreg.h>
/*
* In the arm64 world (as in the ARM world), elf_hwcap is used both internally
@@ -26,15 +27,55 @@
#define ARM64_WORKAROUND_845719 2
#define ARM64_HAS_SYSREG_GIC_CPUIF 3
#define ARM64_HAS_PAN 4
-#define ARM64_NCAPS 5
+#define ARM64_HAS_UAO 5
+#define ARM64_ALT_PAN_NOT_UAO 6
+
+#define ARM64_NCAPS 7
#ifndef __ASSEMBLY__
+#include <linux/kernel.h>
+
+/* CPU feature register tracking */
+enum ftr_type {
+ FTR_EXACT, /* Use a predefined safe value */
+ FTR_LOWER_SAFE, /* Smaller value is safe */
+ FTR_HIGHER_SAFE,/* Bigger value is safe */
+};
+
+#define FTR_STRICT true /* SANITY check strict matching required */
+#define FTR_NONSTRICT false /* SANITY check ignored */
+
+#define FTR_SIGNED true /* Value should be treated as signed */
+#define FTR_UNSIGNED false /* Value should be treated as unsigned */
+
+struct arm64_ftr_bits {
+ bool sign; /* Value is signed ? */
+ bool strict; /* CPU Sanity check: strict matching required ? */
+ enum ftr_type type;
+ u8 shift;
+ u8 width;
+ s64 safe_val; /* safe value for discrete features */
+};
+
+/*
+ * @arm64_ftr_reg - Feature register
+ * @strict_mask Bits which should match across all CPUs for sanity.
+ * @sys_val Safe value across the CPUs (system view)
+ */
+struct arm64_ftr_reg {
+ u32 sys_id;
+ const char *name;
+ u64 strict_mask;
+ u64 sys_val;
+ struct arm64_ftr_bits *ftr_bits;
+};
+
struct arm64_cpu_capabilities {
const char *desc;
u16 capability;
bool (*matches)(const struct arm64_cpu_capabilities *);
- void (*enable)(void);
+ void (*enable)(void *); /* Called on all active CPUs */
union {
struct { /* To be used for erratum handling only */
u32 midr_model;
@@ -42,8 +83,11 @@
};
struct { /* Feature register checking */
+ u32 sys_reg;
int field_pos;
int min_field_value;
+ int hwcap_type;
+ unsigned long hwcap;
};
};
};
@@ -71,22 +115,79 @@
__set_bit(num, cpu_hwcaps);
}
-static inline int __attribute_const__ cpuid_feature_extract_field(u64 features,
- int field)
+static inline int __attribute_const__
+cpuid_feature_extract_field_width(u64 features, int field, int width)
{
- return (s64)(features << (64 - 4 - field)) >> (64 - 4);
+ return (s64)(features << (64 - width - field)) >> (64 - width);
}
+static inline int __attribute_const__
+cpuid_feature_extract_field(u64 features, int field)
+{
+ return cpuid_feature_extract_field_width(features, field, 4);
+}
-void check_cpu_capabilities(const struct arm64_cpu_capabilities *caps,
+static inline unsigned int __attribute_const__
+cpuid_feature_extract_unsigned_field_width(u64 features, int field, int width)
+{
+ return (u64)(features << (64 - width - field)) >> (64 - width);
+}
+
+static inline unsigned int __attribute_const__
+cpuid_feature_extract_unsigned_field(u64 features, int field)
+{
+ return cpuid_feature_extract_unsigned_field_width(features, field, 4);
+}
+
+static inline u64 arm64_ftr_mask(struct arm64_ftr_bits *ftrp)
+{
+ return (u64)GENMASK(ftrp->shift + ftrp->width - 1, ftrp->shift);
+}
+
+static inline s64 arm64_ftr_value(struct arm64_ftr_bits *ftrp, u64 val)
+{
+ return ftrp->sign ?
+ cpuid_feature_extract_field_width(val, ftrp->shift, ftrp->width) :
+ cpuid_feature_extract_unsigned_field_width(val, ftrp->shift, ftrp->width);
+}
+
+static inline bool id_aa64mmfr0_mixed_endian_el0(u64 mmfr0)
+{
+ return cpuid_feature_extract_field(mmfr0, ID_AA64MMFR0_BIGENDEL_SHIFT) == 0x1 ||
+ cpuid_feature_extract_field(mmfr0, ID_AA64MMFR0_BIGENDEL0_SHIFT) == 0x1;
+}
+
+void __init setup_cpu_features(void);
+
+void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps,
const char *info);
void check_local_cpu_errata(void);
-void check_local_cpu_features(void);
-bool cpu_supports_mixed_endian_el0(void);
-bool system_supports_mixed_endian_el0(void);
-bool cpu_supports_mixed_endian_el0(void);
-bool system_supports_mixed_endian_el0(void);
+#ifdef CONFIG_HOTPLUG_CPU
+void verify_local_cpu_capabilities(void);
+#else
+static inline void verify_local_cpu_capabilities(void)
+{
+}
+#endif
+
+u64 read_system_reg(u32 id);
+
+static inline bool cpu_supports_mixed_endian_el0(void)
+{
+ return id_aa64mmfr0_mixed_endian_el0(read_cpuid(SYS_ID_AA64MMFR0_EL1));
+}
+
+static inline bool system_supports_mixed_endian_el0(void)
+{
+ return id_aa64mmfr0_mixed_endian_el0(read_system_reg(SYS_ID_AA64MMFR0_EL1));
+}
+
+static inline bool system_uses_ttbr0_pan(void)
+{
+ return IS_ENABLED(CONFIG_ARM64_SW_TTBR0_PAN) &&
+ !cpus_have_cap(ARM64_HAS_PAN);
+}
#endif /* __ASSEMBLY__ */
diff --git a/arch/arm64/include/asm/cputable.h b/arch/arm64/include/asm/cputable.h
deleted file mode 100644
index e3bd983..0000000
--- a/arch/arm64/include/asm/cputable.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * arch/arm64/include/asm/cputable.h
- *
- * Copyright (C) 2012 ARM Ltd.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-#ifndef __ASM_CPUTABLE_H
-#define __ASM_CPUTABLE_H
-
-struct cpu_info {
- unsigned int cpu_id_val;
- unsigned int cpu_id_mask;
- const char *cpu_name;
- unsigned long (*cpu_setup)(void);
-};
-
-extern struct cpu_info *lookup_processor_type(unsigned int);
-
-#endif
diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
index 6eafdfb..d0edb77 100644
--- a/arch/arm64/include/asm/cputype.h
+++ b/arch/arm64/include/asm/cputype.h
@@ -36,12 +36,6 @@
#define MMFR0_16KGRAN_SHFT 20
#define MMFR0_EL1_16KGRAN_MASK (MMFR0_16KGRAN_SIZE << MMFR0_16KGRAN_SHFT)
-#define read_cpuid(reg) ({ \
- u64 __val; \
- asm("mrs %0, " #reg : "=r" (__val)); \
- __val; \
-})
-
#define MIDR_REVISION_MASK 0xf
#define MIDR_REVISION(midr) ((midr) & MIDR_REVISION_MASK)
#define MIDR_PARTNUM_SHIFT 4
@@ -80,17 +74,16 @@
#define APM_CPU_PART_POTENZA 0x000
-#define ID_AA64MMFR0_BIGENDEL0_SHIFT 16
-#define ID_AA64MMFR0_BIGENDEL0_MASK (0xf << ID_AA64MMFR0_BIGENDEL0_SHIFT)
-#define ID_AA64MMFR0_BIGENDEL0(mmfr0) \
- (((mmfr0) & ID_AA64MMFR0_BIGENDEL0_MASK) >> ID_AA64MMFR0_BIGENDEL0_SHIFT)
-#define ID_AA64MMFR0_BIGEND_SHIFT 8
-#define ID_AA64MMFR0_BIGEND_MASK (0xf << ID_AA64MMFR0_BIGEND_SHIFT)
-#define ID_AA64MMFR0_BIGEND(mmfr0) \
- (((mmfr0) & ID_AA64MMFR0_BIGEND_MASK) >> ID_AA64MMFR0_BIGEND_SHIFT)
-
#ifndef __ASSEMBLY__
+#include <asm/sysreg.h>
+
+#define read_cpuid(reg) ({ \
+ u64 __val; \
+ asm("mrs_s %0, " __stringify(reg) : "=r" (__val)); \
+ __val; \
+})
+
/*
* The CPU ID never changes at run time, so we might as well tell the
* compiler that it's constant. Use this function to read the CPU ID
@@ -98,12 +91,12 @@
*/
static inline u32 __attribute_const__ read_cpuid_id(void)
{
- return read_cpuid(MIDR_EL1);
+ return read_cpuid(SYS_MIDR_EL1);
}
static inline u64 __attribute_const__ read_cpuid_mpidr(void)
{
- return read_cpuid(MPIDR_EL1);
+ return read_cpuid(SYS_MPIDR_EL1);
}
static inline unsigned int __attribute_const__ read_cpuid_implementor(void)
@@ -118,13 +111,7 @@
static inline u32 __attribute_const__ read_cpuid_cachetype(void)
{
- return read_cpuid(CTR_EL0);
-}
-
-static inline bool id_aa64mmfr0_mixed_endian_el0(u64 mmfr0)
-{
- return (ID_AA64MMFR0_BIGEND(mmfr0) == 0x1) ||
- (ID_AA64MMFR0_BIGENDEL0(mmfr0) == 0x1);
+ return read_cpuid(SYS_CTR_EL0);
}
#endif /* __ASSEMBLY__ */
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
index 7baf2cc..ef57220 100644
--- a/arch/arm64/include/asm/efi.h
+++ b/arch/arm64/include/asm/efi.h
@@ -6,10 +6,8 @@
#ifdef CONFIG_EFI
extern void efi_init(void);
-extern void efi_virtmap_init(void);
#else
#define efi_init()
-#define efi_virtmap_init()
#endif
#define efi_call_virt(f, ...) \
@@ -53,23 +51,17 @@
#define EFI_ALLOC_ALIGN SZ_64K
/*
- * On ARM systems, virtually remapped UEFI runtime services are set up in three
+ * On ARM systems, virtually remapped UEFI runtime services are set up in two
* distinct stages:
* - The stub retrieves the final version of the memory map from UEFI, populates
* the virt_addr fields and calls the SetVirtualAddressMap() [SVAM] runtime
* service to communicate the new mapping to the firmware (Note that the new
* mapping is not live at this time)
- * - During early boot, the page tables are allocated and populated based on the
- * virt_addr fields in the memory map, but only if all descriptors with the
- * EFI_MEMORY_RUNTIME attribute have a non-zero value for virt_addr. If this
- * succeeds, the EFI_VIRTMAP flag is set to indicate that the virtual mappings
- * have been installed successfully.
- * - During an early initcall(), the UEFI Runtime Services are enabled and the
- * EFI_RUNTIME_SERVICES bit set if some conditions are met, i.e., we need a
- * non-early mapping of the UEFI system table, and we need to have the virtmap
- * installed.
+ * - During an early initcall(), the EFI system table is permanently remapped
+ * and the virtual remapping of the UEFI Runtime Services regions is loaded
+ * into a private set of page tables. If this all succeeds, the Runtime
+ * Services are enabled and the EFI_RUNTIME_SERVICES bit set.
*/
-#define EFI_VIRTMAP EFI_ARCH_1
void efi_virtmap_load(void);
void efi_virtmap_unload(void);
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 72674f4..7cff572 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -54,4 +54,90 @@
#define ESR_EL1_EC_BKPT32 (0x38)
#define ESR_EL1_EC_BRK64 (0x3C)
+#define ESR_ELx_EC_UNKNOWN (0x00)
+#define ESR_ELx_EC_WFx (0x01)
+/* Unallocated EC: 0x02 */
+#define ESR_ELx_EC_CP15_32 (0x03)
+#define ESR_ELx_EC_CP15_64 (0x04)
+#define ESR_ELx_EC_CP14_MR (0x05)
+#define ESR_ELx_EC_CP14_LS (0x06)
+#define ESR_ELx_EC_FP_ASIMD (0x07)
+#define ESR_ELx_EC_CP10_ID (0x08)
+/* Unallocated EC: 0x09 - 0x0B */
+#define ESR_ELx_EC_CP14_64 (0x0C)
+/* Unallocated EC: 0x0d */
+#define ESR_ELx_EC_ILL (0x0E)
+/* Unallocated EC: 0x0F - 0x10 */
+#define ESR_ELx_EC_SVC32 (0x11)
+#define ESR_ELx_EC_HVC32 (0x12)
+#define ESR_ELx_EC_SMC32 (0x13)
+/* Unallocated EC: 0x14 */
+#define ESR_ELx_EC_SVC64 (0x15)
+#define ESR_ELx_EC_HVC64 (0x16)
+#define ESR_ELx_EC_SMC64 (0x17)
+#define ESR_ELx_EC_SYS64 (0x18)
+/* Unallocated EC: 0x19 - 0x1E */
+#define ESR_ELx_EC_IMP_DEF (0x1f)
+#define ESR_ELx_EC_IABT_LOW (0x20)
+#define ESR_ELx_EC_IABT_CUR (0x21)
+#define ESR_ELx_EC_PC_ALIGN (0x22)
+/* Unallocated EC: 0x23 */
+#define ESR_ELx_EC_DABT_LOW (0x24)
+#define ESR_ELx_EC_DABT_CUR (0x25)
+#define ESR_ELx_EC_SP_ALIGN (0x26)
+/* Unallocated EC: 0x27 */
+#define ESR_ELx_EC_FP_EXC32 (0x28)
+/* Unallocated EC: 0x29 - 0x2B */
+#define ESR_ELx_EC_FP_EXC64 (0x2C)
+/* Unallocated EC: 0x2D - 0x2E */
+#define ESR_ELx_EC_SERROR (0x2F)
+#define ESR_ELx_EC_BREAKPT_LOW (0x30)
+#define ESR_ELx_EC_BREAKPT_CUR (0x31)
+#define ESR_ELx_EC_SOFTSTP_LOW (0x32)
+#define ESR_ELx_EC_SOFTSTP_CUR (0x33)
+#define ESR_ELx_EC_WATCHPT_LOW (0x34)
+#define ESR_ELx_EC_WATCHPT_CUR (0x35)
+/* Unallocated EC: 0x36 - 0x37 */
+#define ESR_ELx_EC_BKPT32 (0x38)
+/* Unallocated EC: 0x39 */
+#define ESR_ELx_EC_VECTOR32 (0x3A)
+/* Unallocted EC: 0x3B */
+#define ESR_ELx_EC_BRK64 (0x3C)
+/* Unallocated EC: 0x3D - 0x3F */
+#define ESR_ELx_EC_MAX (0x3F)
+
+#define ESR_ELx_EC_SHIFT (26)
+#define ESR_ELx_EC_MASK (UL(0x3F) << ESR_ELx_EC_SHIFT)
+#define ESR_ELx_EC(esr) (((esr) & ESR_ELx_EC_MASK) >> ESR_ELx_EC_SHIFT)
+
+#define ESR_ELx_IL (UL(1) << 25)
+#define ESR_ELx_ISS_MASK (ESR_ELx_IL - 1)
+#define ESR_ELx_ISV (UL(1) << 24)
+#define ESR_ELx_SAS_SHIFT (22)
+#define ESR_ELx_SAS (UL(3) << ESR_ELx_SAS_SHIFT)
+#define ESR_ELx_SSE (UL(1) << 21)
+#define ESR_ELx_SRT_SHIFT (16)
+#define ESR_ELx_SRT_MASK (UL(0x1F) << ESR_ELx_SRT_SHIFT)
+#define ESR_ELx_SF (UL(1) << 15)
+#define ESR_ELx_AR (UL(1) << 14)
+#define ESR_ELx_EA (UL(1) << 9)
+#define ESR_ELx_CM (UL(1) << 8)
+#define ESR_ELx_S1PTW (UL(1) << 7)
+#define ESR_ELx_WNR (UL(1) << 6)
+#define ESR_ELx_FSC (0x3F)
+#define ESR_ELx_FSC_TYPE (0x3C)
+#define ESR_ELx_FSC_EXTABT (0x10)
+#define ESR_ELx_FSC_FAULT (0x04)
+#define ESR_ELx_FSC_PERM (0x0C)
+#define ESR_ELx_CV (UL(1) << 24)
+#define ESR_ELx_COND_SHIFT (20)
+#define ESR_ELx_COND_MASK (UL(0xF) << ESR_ELx_COND_SHIFT)
+#define ESR_ELx_WFx_ISS_WFE (UL(1) << 0)
+
+#ifndef __ASSEMBLY__
+#include <asm/types.h>
+
+const char *esr_get_class_string(u32 esr);
+#endif /* __ASSEMBLY */
+
#endif /* __ASM_ESR_H */
diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h
index 6673462..63996bd 100644
--- a/arch/arm64/include/asm/futex.h
+++ b/arch/arm64/include/asm/futex.h
@@ -27,9 +27,9 @@
#include <asm/sysreg.h>
#define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg) \
+do { \
+ uaccess_enable(); \
asm volatile( \
- ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN) \
"1: ldxr %w1, %2\n" \
insn "\n" \
"2: stlxr %w3, %w0, %2\n" \
@@ -45,11 +45,11 @@
" .align 3\n" \
" .quad 1b, 4b, 2b, 4b\n" \
" .popsection\n" \
- ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN) \
: "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp) \
: "r" (oparg), "Ir" (-EFAULT) \
- : "memory")
+ : "memory"); \
+ uaccess_disable(); \
+} while (0)
static inline int
futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
@@ -119,6 +119,7 @@
if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
return -EFAULT;
+ uaccess_enable();
asm volatile("// futex_atomic_cmpxchg_inatomic\n"
"1: ldxr %w1, %2\n"
" sub %w3, %w1, %w4\n"
@@ -138,6 +139,7 @@
: "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp)
: "r" (oldval), "r" (newval), "Ir" (-EFAULT)
: "memory");
+ uaccess_disable();
*uval = val;
return ret;
diff --git a/arch/arm64/include/asm/hardirq.h b/arch/arm64/include/asm/hardirq.h
index 064ebec..5f60f83 100644
--- a/arch/arm64/include/asm/hardirq.h
+++ b/arch/arm64/include/asm/hardirq.h
@@ -24,9 +24,7 @@
typedef struct {
unsigned int __softirq_pending;
-#ifdef CONFIG_SMP
unsigned int ipi_irqs[NR_IPI];
-#endif
} ____cacheline_aligned irq_cpustat_t;
#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
@@ -34,10 +32,8 @@
#define __inc_irq_stat(cpu, member) __IRQ_STAT(cpu, member)++
#define __get_irq_stat(cpu, member) __IRQ_STAT(cpu, member)
-#ifdef CONFIG_SMP
u64 smp_irq_stat_cpu(unsigned int cpu);
#define arch_irq_stat_cpu smp_irq_stat_cpu
-#endif
#define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h
index 52b484b..77667c3 100644
--- a/arch/arm64/include/asm/hw_breakpoint.h
+++ b/arch/arm64/include/asm/hw_breakpoint.h
@@ -65,7 +65,11 @@
/* Lengths */
#define ARM_BREAKPOINT_LEN_1 0x1
#define ARM_BREAKPOINT_LEN_2 0x3
+#define ARM_BREAKPOINT_LEN_3 0x7
#define ARM_BREAKPOINT_LEN_4 0xf
+#define ARM_BREAKPOINT_LEN_5 0x1f
+#define ARM_BREAKPOINT_LEN_6 0x3f
+#define ARM_BREAKPOINT_LEN_7 0x7f
#define ARM_BREAKPOINT_LEN_8 0xff
/* Kernel stepping */
@@ -107,7 +111,7 @@
struct pmu;
extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
- int *gen_len, int *gen_type);
+ int *gen_len, int *gen_type, int *offset);
extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h
index 0ad7351..400b80b 100644
--- a/arch/arm64/include/asm/hwcap.h
+++ b/arch/arm64/include/asm/hwcap.h
@@ -52,6 +52,14 @@
extern unsigned int compat_elf_hwcap, compat_elf_hwcap2;
#endif
+enum {
+ CAP_HWCAP = 1,
+#ifdef CONFIG_COMPAT
+ CAP_COMPAT_HWCAP,
+ CAP_COMPAT_HWCAP2,
+#endif
+};
+
extern unsigned long elf_hwcap;
#endif
#endif
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index e2ff32a..30e50eb 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -264,8 +264,10 @@
__AARCH64_INSN_FUNCS(bics, 0x7F200000, 0x6A200000)
__AARCH64_INSN_FUNCS(b, 0xFC000000, 0x14000000)
__AARCH64_INSN_FUNCS(bl, 0xFC000000, 0x94000000)
-__AARCH64_INSN_FUNCS(cbz, 0xFE000000, 0x34000000)
-__AARCH64_INSN_FUNCS(cbnz, 0xFE000000, 0x35000000)
+__AARCH64_INSN_FUNCS(cbz, 0x7F000000, 0x34000000)
+__AARCH64_INSN_FUNCS(cbnz, 0x7F000000, 0x35000000)
+__AARCH64_INSN_FUNCS(tbz, 0x7F000000, 0x36000000)
+__AARCH64_INSN_FUNCS(tbnz, 0x7F000000, 0x37000000)
__AARCH64_INSN_FUNCS(bcond, 0xFF000010, 0x54000000)
__AARCH64_INSN_FUNCS(svc, 0xFFE0001F, 0xD4000001)
__AARCH64_INSN_FUNCS(hvc, 0xFFE0001F, 0xD4000002)
@@ -279,10 +281,12 @@
#undef __AARCH64_INSN_FUNCS
bool aarch64_insn_is_nop(u32 insn);
+bool aarch64_insn_is_branch_imm(u32 insn);
int aarch64_insn_read(void *addr, u32 *insnp);
int aarch64_insn_write(void *addr, u32 insn);
enum aarch64_insn_encoding_class aarch64_get_insn_class(u32 insn);
+u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn);
u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
u32 insn, u64 imm);
u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr,
@@ -348,6 +352,8 @@
int shift,
enum aarch64_insn_variant variant,
enum aarch64_insn_logic_type type);
+s32 aarch64_get_branch_offset(u32 insn);
+u32 aarch64_set_branch_offset(u32 insn, s32 offset);
bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn);
diff --git a/arch/arm64/include/asm/irq_work.h b/arch/arm64/include/asm/irq_work.h
index b4f6b19..8e24ef3 100644
--- a/arch/arm64/include/asm/irq_work.h
+++ b/arch/arm64/include/asm/irq_work.h
@@ -1,8 +1,6 @@
#ifndef __ASM_IRQ_WORK_H
#define __ASM_IRQ_WORK_H
-#ifdef CONFIG_SMP
-
#include <asm/smp.h>
static inline bool arch_irq_work_has_interrupt(void)
@@ -10,13 +8,4 @@
return !!__smp_cross_call;
}
-#else
-
-static inline bool arch_irq_work_has_interrupt(void)
-{
- return false;
-}
-
-#endif
-
#endif /* __ASM_IRQ_WORK_H */
diff --git a/arch/arm64/include/asm/kernel-pgtable.h b/arch/arm64/include/asm/kernel-pgtable.h
new file mode 100644
index 0000000..591c0b0
--- /dev/null
+++ b/arch/arm64/include/asm/kernel-pgtable.h
@@ -0,0 +1,73 @@
+/*
+ * Kernel page table mapping
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ASM_KERNEL_PGTABLE_H
+#define __ASM_KERNEL_PGTABLE_H
+
+#include <asm/pgtable.h>
+
+/*
+ * The idmap and swapper page tables need some space reserved in the kernel
+ * image. Both require pgd, pud (4 levels only) and pmd tables to (section)
+ * map the kernel. With the 64K page configuration, swapper and idmap need to
+ * map to pte level. The swapper also maps the FDT (see __create_page_tables
+ * for more information). Note that the number of ID map translation levels
+ * could be increased on the fly if system RAM is out of reach for the default
+ * VA range, so 3 pages are reserved in all cases.
+ */
+#ifdef CONFIG_ARM64_64K_PAGES
+#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS)
+#else
+#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS - 1)
+#endif
+
+#define SWAPPER_DIR_SIZE (SWAPPER_PGTABLE_LEVELS * PAGE_SIZE)
+#define IDMAP_DIR_SIZE (3 * PAGE_SIZE)
+
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+#define RESERVED_TTBR0_SIZE (PAGE_SIZE)
+#else
+#define RESERVED_TTBR0_SIZE (0)
+#endif
+
+/* Initial memory map size */
+#ifdef CONFIG_ARM64_64K_PAGES
+#define SWAPPER_BLOCK_SHIFT PAGE_SHIFT
+#define SWAPPER_BLOCK_SIZE PAGE_SIZE
+#define SWAPPER_TABLE_SHIFT PMD_SHIFT
+#else
+#define SWAPPER_BLOCK_SHIFT SECTION_SHIFT
+#define SWAPPER_BLOCK_SIZE SECTION_SIZE
+#define SWAPPER_TABLE_SHIFT PUD_SHIFT
+#endif
+
+
+/*
+ * Initial memory map attributes.
+ */
+#define SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
+#define SWAPPER_PMD_FLAGS (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S)
+
+#ifdef CONFIG_ARM64_64K_PAGES
+#define SWAPPER_MM_MMUFLAGS (PTE_ATTRINDX(MT_NORMAL) | SWAPPER_PTE_FLAGS)
+#else
+#define SWAPPER_MM_MMUFLAGS (PMD_ATTRINDX(MT_NORMAL) | SWAPPER_PMD_FLAGS)
+#endif
+
+
+#endif /* __ASM_KERNEL_PGTABLE_H */
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index 865a7e2..3cb4c856 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -45,6 +45,16 @@
vcpu->arch.hcr_el2 &= ~HCR_RW;
}
+static inline unsigned long vcpu_get_hcr(struct kvm_vcpu *vcpu)
+{
+ return vcpu->arch.hcr_el2;
+}
+
+static inline void vcpu_set_hcr(struct kvm_vcpu *vcpu, unsigned long hcr)
+{
+ vcpu->arch.hcr_el2 = hcr;
+}
+
static inline unsigned long *vcpu_pc(const struct kvm_vcpu *vcpu)
{
return (unsigned long *)&vcpu_gp_regs(vcpu)->regs.pc;
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index dbd3212..806a121 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -116,9 +116,6 @@
* Anything that is not used directly from assembly code goes
* here.
*/
- /* dcache set/way operation pending */
- int last_pcpu;
- cpumask_t require_dcache_flush;
/* Don't run the guest */
bool pause;
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index 9a05c0d..390bf12 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -245,7 +245,8 @@
#define kvm_virt_to_phys(x) __virt_to_phys((unsigned long)(x))
-void stage2_flush_vm(struct kvm *kvm);
+void kvm_set_way_flush(struct kvm_vcpu *vcpu);
+void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled);
#endif /* __ASSEMBLY__ */
#endif /* __ARM64_KVM_MMU_H__ */
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index e4a2ef9..c8b6889 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -147,7 +147,11 @@
#define ARCH_PFN_OFFSET ((unsigned long)PHYS_PFN_OFFSET)
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
-#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
+#define _virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
+
+#define _virt_addr_is_linear(kaddr) (((u64)(kaddr)) >= PAGE_OFFSET)
+#define virt_addr_valid(kaddr) (_virt_addr_is_linear(kaddr) && \
+ _virt_addr_valid(kaddr))
#endif
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index 1e13ce1..77c3851 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -17,21 +17,20 @@
#define __ASM_MMU_H
typedef struct {
- unsigned int id;
- raw_spinlock_t id_lock;
- void *vdso;
+ atomic64_t id;
+ void *vdso;
} mm_context_t;
-#define INIT_MM_CONTEXT(name) \
- .context.id_lock = __RAW_SPIN_LOCK_UNLOCKED(name.context.id_lock),
-
-#define ASID(mm) ((mm)->context.id & 0xffff)
+/*
+ * This macro is only used by the TLBI code, which cannot race with an
+ * ASID change and therefore doesn't need to reload the counter using
+ * atomic64_read.
+ */
+#define ASID(mm) ((mm)->context.id.counter & 0xffff)
extern void paging_init(void);
-extern void setup_mm_for_reboot(void);
extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt);
extern void init_mem_pgprot(void);
-extern void mem_text_write_kernel_word(u32 *addr, u32 word);
extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys,
unsigned long virt, phys_addr_t size,
pgprot_t prot);
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index 19b8304..7d31238 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -23,19 +23,13 @@
#include <linux/sched.h>
#include <asm/cacheflush.h>
+#include <asm/cpufeature.h>
#include <asm/proc-fns.h>
#include <asm-generic/mm_hooks.h>
#include <asm/cputype.h>
#include <asm/pgtable.h>
#include <linux/msm_rtb.h>
-#define MAX_ASID_BITS 16
-
-extern unsigned int cpu_last_asid;
-
-void __init_new_context(struct task_struct *tsk, struct mm_struct *mm);
-void __new_context(struct mm_struct *mm);
-
#ifdef CONFIG_PID_IN_CONTEXTIDR
static inline void contextidr_thread_switch(struct task_struct *next)
{
@@ -60,7 +54,7 @@
*/
static inline void cpu_set_reserved_ttbr0(void)
{
- unsigned long ttbr = page_to_phys(empty_zero_page);
+ unsigned long ttbr = virt_to_phys(empty_zero_page);
asm(
" msr ttbr0_el1, %0 // set TTBR0\n"
@@ -69,66 +63,51 @@
: "r" (ttbr));
}
-static inline void switch_new_context(struct mm_struct *mm)
+/*
+ * TCR.T0SZ value to use when the ID map is active. Usually equals
+ * TCR_T0SZ(VA_BITS), unless system RAM is positioned very high in
+ * physical memory, in which case it will be smaller.
+ */
+extern u64 idmap_t0sz;
+
+static inline bool __cpu_uses_extended_idmap(void)
{
- unsigned long flags;
-
- __new_context(mm);
-
- local_irq_save(flags);
- cpu_switch_mm(mm->pgd, mm);
- local_irq_restore(flags);
+ return (!IS_ENABLED(CONFIG_ARM64_VA_BITS_48) &&
+ unlikely(idmap_t0sz != TCR_T0SZ(VA_BITS)));
}
-static inline void check_and_switch_context(struct mm_struct *mm,
- struct task_struct *tsk)
+/*
+ * Set TCR.T0SZ to its default value (based on VA_BITS)
+ */
+static inline void cpu_set_default_tcr_t0sz(void)
{
- /*
- * Required during context switch to avoid speculative page table
- * walking with the wrong TTBR.
- */
- cpu_set_reserved_ttbr0();
+ unsigned long tcr;
- if (!((mm->context.id ^ cpu_last_asid) >> MAX_ASID_BITS))
- /*
- * The ASID is from the current generation, just switch to the
- * new pgd. This condition is only true for calls from
- * context_switch() and interrupts are already disabled.
- */
- cpu_switch_mm(mm->pgd, mm);
- else if (irqs_disabled())
- /*
- * Defer the new ASID allocation until after the context
- * switch critical region since __new_context() cannot be
- * called with interrupts disabled.
- */
- set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM);
- else
- /*
- * That is a direct call to switch_mm() or activate_mm() with
- * interrupts enabled and a new context.
- */
- switch_new_context(mm);
+ if (!__cpu_uses_extended_idmap())
+ return;
+
+ asm volatile (
+ " mrs %0, tcr_el1 ;"
+ " bfi %0, %1, %2, %3 ;"
+ " msr tcr_el1, %0 ;"
+ " isb"
+ : "=&r" (tcr)
+ : "r"(TCR_T0SZ(VA_BITS)), "I"(TCR_T0SZ_OFFSET), "I"(TCR_TxSZ_WIDTH));
}
-#define init_new_context(tsk,mm) (__init_new_context(tsk,mm),0)
+/*
+ * It would be nice to return ASIDs back to the allocator, but unfortunately
+ * that introduces a race with a generation rollover where we could erroneously
+ * free an ASID allocated in a future generation. We could workaround this by
+ * freeing the ASID from the context of the dying mm (e.g. in arch_exit_mmap),
+ * but we'd then need to make sure that we didn't dirty any TLBs afterwards.
+ * Setting a reserved TTBR0 or EPD0 would work, but it all gets ugly when you
+ * take CPU migration into account.
+ */
#define destroy_context(mm) do { } while(0)
+void check_and_switch_context(struct mm_struct *mm, unsigned int cpu);
-#define finish_arch_post_lock_switch \
- finish_arch_post_lock_switch
-static inline void finish_arch_post_lock_switch(void)
-{
- if (test_and_clear_thread_flag(TIF_SWITCH_MM)) {
- struct mm_struct *mm = current->mm;
- unsigned long flags;
-
- __new_context(mm);
-
- local_irq_save(flags);
- cpu_switch_mm(mm->pgd, mm);
- local_irq_restore(flags);
- }
-}
+#define init_new_context(tsk,mm) ({ atomic64_set(&(mm)->context.id, 0); 0; })
/*
* This is called when "tsk" is about to enter lazy TLB mode.
@@ -144,15 +123,24 @@
{
}
-/*
- * This is the actual mm switch as far as the scheduler
- * is concerned. No registers are touched. We avoid
- * calling the CPU specific function when the mm hasn't
- * actually changed.
- */
-static inline void
-switch_mm(struct mm_struct *prev, struct mm_struct *next,
- struct task_struct *tsk)
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+static inline void update_saved_ttbr0(struct task_struct *tsk,
+ struct mm_struct *mm)
+{
+ if (system_uses_ttbr0_pan()) {
+ BUG_ON(mm->pgd == swapper_pg_dir);
+ task_thread_info(tsk)->ttbr0 =
+ virt_to_phys(mm->pgd) | ASID(mm) << 48;
+ }
+}
+#else
+static inline void update_saved_ttbr0(struct task_struct *tsk,
+ struct mm_struct *mm)
+{
+}
+#endif
+
+static inline void __switch_mm(struct mm_struct *next)
{
unsigned int cpu = smp_processor_id();
@@ -165,11 +153,27 @@
return;
}
- if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next)
- check_and_switch_context(next, tsk);
+ check_and_switch_context(next, cpu);
+}
+
+static inline void
+switch_mm(struct mm_struct *prev, struct mm_struct *next,
+ struct task_struct *tsk)
+{
+ if (prev != next)
+ __switch_mm(next);
+
+ /*
+ * Update the saved TTBR0_EL1 of the scheduled-in task as the previous
+ * value may have not been initialised yet (activate_mm caller) or the
+ * ASID has changed since the last run (following the context switch
+ * of another thread of the same process).
+ */
+ if (next != &init_mm)
+ update_saved_ttbr0(tsk, next);
}
#define deactivate_mm(tsk,mm) do { } while (0)
-#define activate_mm(prev,next) switch_mm(prev, next, NULL)
+#define activate_mm(prev,next) switch_mm(prev, next, current)
#endif
diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
index 8fc8fa2..da32354 100644
--- a/arch/arm64/include/asm/page.h
+++ b/arch/arm64/include/asm/page.h
@@ -20,29 +20,19 @@
#define __ASM_PAGE_H
/* PAGE_SHIFT determines the page size */
+/* CONT_SHIFT determines the number of pages which can be tracked together */
#ifdef CONFIG_ARM64_64K_PAGES
#define PAGE_SHIFT 16
+#define CONT_SHIFT 5
#else
#define PAGE_SHIFT 12
+#define CONT_SHIFT 4
#endif
-#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
+#define PAGE_SIZE (_AC(1, UL) << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
-/*
- * The idmap and swapper page tables need some space reserved in the kernel
- * image. Both require pgd, pud (4 levels only) and pmd tables to (section)
- * map the kernel. With the 64K page configuration, swapper and idmap need to
- * map to pte level. The swapper also maps the FDT (see __create_page_tables
- * for more information).
- */
-#ifdef CONFIG_ARM64_64K_PAGES
-#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS)
-#else
-#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS - 1)
-#endif
-
-#define SWAPPER_DIR_SIZE (SWAPPER_PGTABLE_LEVELS * PAGE_SIZE)
-#define IDMAP_DIR_SIZE (SWAPPER_DIR_SIZE)
+#define CONT_SIZE (_AC(1, UL) << (CONT_SHIFT + PAGE_SHIFT))
+#define CONT_MASK (~(CONT_SIZE-1))
#ifndef __ASSEMBLY__
diff --git a/arch/arm64/include/asm/percpu.h b/arch/arm64/include/asm/percpu.h
index 4fde8c1..0a456be 100644
--- a/arch/arm64/include/asm/percpu.h
+++ b/arch/arm64/include/asm/percpu.h
@@ -16,8 +16,6 @@
#ifndef __ASM_PERCPU_H
#define __ASM_PERCPU_H
-#ifdef CONFIG_SMP
-
static inline void set_my_cpu_offset(unsigned long off)
{
asm volatile("msr tpidr_el1, %0" :: "r" (off) : "memory");
@@ -38,12 +36,6 @@
}
#define __my_cpu_offset __my_cpu_offset()
-#else /* !CONFIG_SMP */
-
-#define set_my_cpu_offset(x) do { } while (0)
-
-#endif /* CONFIG_SMP */
-
#define PERCPU_OP(op, asm_op) \
static inline unsigned long __percpu_##op(void *ptr, \
unsigned long val, int size) \
diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h
index f40ad1f..5e50782 100644
--- a/arch/arm64/include/asm/pgtable-hwdef.h
+++ b/arch/arm64/include/asm/pgtable-hwdef.h
@@ -77,7 +77,6 @@
* Section
*/
#define PMD_SECT_VALID (_AT(pmdval_t, 1) << 0)
-#define PMD_SECT_PROT_NONE (_AT(pmdval_t, 1) << 58)
#define PMD_SECT_USER (_AT(pmdval_t, 1) << 6) /* AP[1] */
#define PMD_SECT_RDONLY (_AT(pmdval_t, 1) << 7) /* AP[2] */
#define PMD_SECT_S (_AT(pmdval_t, 3) << 8)
@@ -142,7 +141,12 @@
/*
* TCR flags.
*/
-#define TCR_TxSZ(x) (((UL(64) - (x)) << 16) | ((UL(64) - (x)) << 0))
+#define TCR_T0SZ_OFFSET 0
+#define TCR_T1SZ_OFFSET 16
+#define TCR_T0SZ(x) ((UL(64) - (x)) << TCR_T0SZ_OFFSET)
+#define TCR_T1SZ(x) ((UL(64) - (x)) << TCR_T1SZ_OFFSET)
+#define TCR_TxSZ(x) (TCR_T0SZ(x) | TCR_T1SZ(x))
+#define TCR_TxSZ_WIDTH 6
#define TCR_IRGN_NC ((UL(0) << 8) | (UL(0) << 24))
#define TCR_IRGN_WBWA ((UL(1) << 8) | (UL(1) << 24))
#define TCR_IRGN_WT ((UL(2) << 8) | (UL(2) << 24))
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index e0221e8..abe6186 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -63,13 +63,8 @@
extern void __pud_error(const char *file, int line, unsigned long val);
extern void __pgd_error(const char *file, int line, unsigned long val);
-#ifdef CONFIG_SMP
#define PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)
#define PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S)
-#else
-#define PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF)
-#define PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF)
-#endif
#define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRE))
#define PROT_NORMAL_NC (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL_NC))
@@ -120,8 +115,8 @@
* ZERO_PAGE is a global shared page that is always zero: used
* for zero-mapped memory areas etc..
*/
-extern struct page *empty_zero_page;
-#define ZERO_PAGE(vaddr) (empty_zero_page)
+extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
+#define ZERO_PAGE(vaddr) virt_to_page(empty_zero_page)
#define pte_ERROR(pte) __pte_error(__FILE__, __LINE__, pte_val(pte))
@@ -293,6 +288,8 @@
#endif /* CONFIG_HAVE_RCU_TABLE_FREE */
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
+#define pmd_present(pmd) pte_present(pmd_pte(pmd))
+#define pmd_dirty(pmd) pte_dirty(pmd_pte(pmd))
#define pmd_young(pmd) pte_young(pmd_pte(pmd))
#define pmd_wrprotect(pmd) pte_pmd(pte_wrprotect(pmd_pte(pmd)))
#define pmd_mksplitting(pmd) pte_pmd(pte_mkspecial(pmd_pte(pmd)))
@@ -300,7 +297,7 @@
#define pmd_mkwrite(pmd) pte_pmd(pte_mkwrite(pmd_pte(pmd)))
#define pmd_mkdirty(pmd) pte_pmd(pte_mkdirty(pmd_pte(pmd)))
#define pmd_mkyoung(pmd) pte_pmd(pte_mkyoung(pmd_pte(pmd)))
-#define pmd_mknotpresent(pmd) (__pmd(pmd_val(pmd) & ~PMD_TYPE_MASK))
+#define pmd_mknotpresent(pmd) (__pmd(pmd_val(pmd) & ~PMD_SECT_VALID))
#define __HAVE_ARCH_PMD_WRITE
#define pmd_write(pmd) pte_write(pmd_pte(pmd))
@@ -340,7 +337,6 @@
unsigned long size, pgprot_t vma_prot);
#define pmd_none(pmd) (!pmd_val(pmd))
-#define pmd_present(pmd) (pmd_val(pmd))
#define pmd_bad(pmd) (!(pmd_val(pmd) & 2))
@@ -351,9 +347,12 @@
#ifdef CONFIG_ARM64_64K_PAGES
#define pud_sect(pud) (0)
+#define pud_table(pud) (1)
#else
#define pud_sect(pud) ((pud_val(pud) & PUD_TYPE_MASK) == \
PUD_TYPE_SECT)
+#define pud_table(pud) ((pud_val(pud) & PUD_TYPE_MASK) == \
+ PUD_TYPE_TABLE)
#endif
static inline void set_pmd(pmd_t *pmdp, pmd_t pmd)
@@ -523,6 +522,21 @@
#define pgtable_cache_init() do { } while (0)
+/*
+ * On AArch64, the cache coherency is handled via the set_pte_at() function.
+ */
+static inline void update_mmu_cache(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep)
+{
+ /*
+ * We don't do anything here, so there's a very small chance of
+ * us retaking a user fault which we just fixed up. The alternative
+ * is doing a dsb(ishst), but that penalises the fastpath.
+ */
+}
+
+#define update_mmu_cache_pmd(vma, address, pmd) do { } while (0)
+
#endif /* !__ASSEMBLY__ */
#endif /* __ASM_PGTABLE_H */
diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h
index 9a8fd84..b5240e8 100644
--- a/arch/arm64/include/asm/proc-fns.h
+++ b/arch/arm64/include/asm/proc-fns.h
@@ -28,18 +28,18 @@
struct mm_struct;
struct cpu_suspend_ctx;
-extern void cpu_cache_off(void);
extern void cpu_do_idle(void);
extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
-extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
-void cpu_soft_restart(phys_addr_t cpu_reset,
- unsigned long addr) __attribute__((noreturn));
extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
#include <asm/memory.h>
-#define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
+#define cpu_switch_mm(pgd,mm) \
+do { \
+ BUG_ON(pgd == swapper_pg_dir); \
+ cpu_do_switch_mm(virt_to_phys(pgd),mm); \
+} while (0)
#define cpu_get_pgd() \
({ \
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index bd4e7c7..aea291b 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -169,6 +169,7 @@
#endif
-void cpu_enable_pan(void);
+void cpu_enable_pan(void *__unused);
+void cpu_enable_uao(void *__unused);
#endif /* __ASM_PROCESSOR_H */
diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
index d6dd9fd..f9c9aba 100644
--- a/arch/arm64/include/asm/ptrace.h
+++ b/arch/arm64/include/asm/ptrace.h
@@ -21,6 +21,8 @@
#include <uapi/asm/ptrace.h>
+#define _PSR_PAN_BIT 22
+
/* Current Exception Level values, as contained in CurrentEL */
#define CurrentEL_EL1 (1 << 2)
#define CurrentEL_EL2 (2 << 2)
@@ -116,6 +118,8 @@
};
u64 orig_x0;
u64 syscallno;
+ u64 orig_addr_limit;
+ u64 unused; // maintain 16 byte alignment
};
#define arch_has_single_step() (1)
@@ -183,11 +187,7 @@
#define instruction_pointer(regs) ((unsigned long)(regs)->pc)
-#ifdef CONFIG_SMP
extern unsigned long profile_pc(struct pt_regs *regs);
-#else
-#define profile_pc(regs) instruction_pointer(regs)
-#endif
#endif /* __ASSEMBLY__ */
#endif
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index 69d5553..c78ede6 100644
--- a/arch/arm64/include/asm/smp.h
+++ b/arch/arm64/include/asm/smp.h
@@ -20,10 +20,6 @@
#include <linux/cpumask.h>
#include <linux/thread_info.h>
-#ifndef CONFIG_SMP
-# error "<asm/smp.h> included in non-SMP build"
-#endif
-
#define raw_smp_processor_id() (current_thread_info()->cpu)
struct seq_file;
diff --git a/arch/arm64/include/asm/spinlock.h b/arch/arm64/include/asm/spinlock.h
index bb866d5..1675b81c 100644
--- a/arch/arm64/include/asm/spinlock.h
+++ b/arch/arm64/include/asm/spinlock.h
@@ -242,4 +242,14 @@
#define arch_read_relax(lock) cpu_relax()
#define arch_write_relax(lock) cpu_relax()
+/*
+ * Accesses appearing in program order before a spin_lock() operation
+ * can be reordered with accesses inside the critical section, by virtue
+ * of arch_spin_lock being constructed using acquire semantics.
+ *
+ * In cases where this is problematic (e.g. try_to_wake_up), an
+ * smp_mb__before_spinlock() can restore the required ordering.
+ */
+#define smp_mb__before_spinlock() smp_mb()
+
#endif /* __ASM_SPINLOCK_H */
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index a7f3d4b..0cc436b 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -20,10 +20,9 @@
#ifndef __ASM_SYSREG_H
#define __ASM_SYSREG_H
-#include <asm/opcodes.h>
+#include <linux/stringify.h>
-#define SCTLR_EL1_CP15BEN (0x1 << 5)
-#define SCTLR_EL1_SED (0x1 << 8)
+#include <asm/opcodes.h>
/*
* ARMv8 ARM reserves the following encoding for system registers:
@@ -38,11 +37,146 @@
#define sys_reg(op0, op1, crn, crm, op2) \
((((op0)&3)<<19)|((op1)<<16)|((crn)<<12)|((crm)<<8)|((op2)<<5))
-#define REG_PSTATE_PAN_IMM sys_reg(0, 0, 4, 0, 4)
-#define SCTLR_EL1_SPAN (1 << 23)
+#define SYS_MIDR_EL1 sys_reg(3, 0, 0, 0, 0)
+#define SYS_MPIDR_EL1 sys_reg(3, 0, 0, 0, 5)
+#define SYS_REVIDR_EL1 sys_reg(3, 0, 0, 0, 6)
+
+#define SYS_ID_PFR0_EL1 sys_reg(3, 0, 0, 1, 0)
+#define SYS_ID_PFR1_EL1 sys_reg(3, 0, 0, 1, 1)
+#define SYS_ID_DFR0_EL1 sys_reg(3, 0, 0, 1, 2)
+#define SYS_ID_MMFR0_EL1 sys_reg(3, 0, 0, 1, 4)
+#define SYS_ID_MMFR1_EL1 sys_reg(3, 0, 0, 1, 5)
+#define SYS_ID_MMFR2_EL1 sys_reg(3, 0, 0, 1, 6)
+#define SYS_ID_MMFR3_EL1 sys_reg(3, 0, 0, 1, 7)
+
+#define SYS_ID_ISAR0_EL1 sys_reg(3, 0, 0, 2, 0)
+#define SYS_ID_ISAR1_EL1 sys_reg(3, 0, 0, 2, 1)
+#define SYS_ID_ISAR2_EL1 sys_reg(3, 0, 0, 2, 2)
+#define SYS_ID_ISAR3_EL1 sys_reg(3, 0, 0, 2, 3)
+#define SYS_ID_ISAR4_EL1 sys_reg(3, 0, 0, 2, 4)
+#define SYS_ID_ISAR5_EL1 sys_reg(3, 0, 0, 2, 5)
+#define SYS_ID_MMFR4_EL1 sys_reg(3, 0, 0, 2, 6)
+
+#define SYS_MVFR0_EL1 sys_reg(3, 0, 0, 3, 0)
+#define SYS_MVFR1_EL1 sys_reg(3, 0, 0, 3, 1)
+#define SYS_MVFR2_EL1 sys_reg(3, 0, 0, 3, 2)
+
+#define SYS_ID_AA64PFR0_EL1 sys_reg(3, 0, 0, 4, 0)
+#define SYS_ID_AA64PFR1_EL1 sys_reg(3, 0, 0, 4, 1)
+
+#define SYS_ID_AA64DFR0_EL1 sys_reg(3, 0, 0, 5, 0)
+#define SYS_ID_AA64DFR1_EL1 sys_reg(3, 0, 0, 5, 1)
+
+#define SYS_ID_AA64ISAR0_EL1 sys_reg(3, 0, 0, 6, 0)
+#define SYS_ID_AA64ISAR1_EL1 sys_reg(3, 0, 0, 6, 1)
+
+#define SYS_ID_AA64MMFR0_EL1 sys_reg(3, 0, 0, 7, 0)
+#define SYS_ID_AA64MMFR1_EL1 sys_reg(3, 0, 0, 7, 1)
+#define SYS_ID_AA64MMFR2_EL1 sys_reg(3, 0, 0, 7, 2)
+
+#define SYS_CNTFRQ_EL0 sys_reg(3, 3, 14, 0, 0)
+#define SYS_CTR_EL0 sys_reg(3, 3, 0, 0, 1)
+#define SYS_DCZID_EL0 sys_reg(3, 3, 0, 0, 7)
+
+#define REG_PSTATE_PAN_IMM sys_reg(0, 0, 4, 0, 4)
+#define REG_PSTATE_UAO_IMM sys_reg(0, 0, 4, 0, 3)
#define SET_PSTATE_PAN(x) __inst_arm(0xd5000000 | REG_PSTATE_PAN_IMM |\
(!!x)<<8 | 0x1f)
+#define SET_PSTATE_UAO(x) __inst_arm(0xd5000000 | REG_PSTATE_UAO_IMM |\
+ (!!x)<<8 | 0x1f)
+
+/* SCTLR_EL1 */
+#define SCTLR_EL1_CP15BEN (0x1 << 5)
+#define SCTLR_EL1_SED (0x1 << 8)
+#define SCTLR_EL1_SPAN (0x1 << 23)
+
+
+/* id_aa64isar0 */
+#define ID_AA64ISAR0_RDM_SHIFT 28
+#define ID_AA64ISAR0_ATOMICS_SHIFT 20
+#define ID_AA64ISAR0_CRC32_SHIFT 16
+#define ID_AA64ISAR0_SHA2_SHIFT 12
+#define ID_AA64ISAR0_SHA1_SHIFT 8
+#define ID_AA64ISAR0_AES_SHIFT 4
+
+/* id_aa64pfr0 */
+#define ID_AA64PFR0_GIC_SHIFT 24
+#define ID_AA64PFR0_ASIMD_SHIFT 20
+#define ID_AA64PFR0_FP_SHIFT 16
+#define ID_AA64PFR0_EL3_SHIFT 12
+#define ID_AA64PFR0_EL2_SHIFT 8
+#define ID_AA64PFR0_EL1_SHIFT 4
+#define ID_AA64PFR0_EL0_SHIFT 0
+
+#define ID_AA64PFR0_FP_NI 0xf
+#define ID_AA64PFR0_FP_SUPPORTED 0x0
+#define ID_AA64PFR0_ASIMD_NI 0xf
+#define ID_AA64PFR0_ASIMD_SUPPORTED 0x0
+#define ID_AA64PFR0_EL1_64BIT_ONLY 0x1
+#define ID_AA64PFR0_EL0_64BIT_ONLY 0x1
+
+/* id_aa64mmfr0 */
+#define ID_AA64MMFR0_TGRAN4_SHIFT 28
+#define ID_AA64MMFR0_TGRAN64_SHIFT 24
+#define ID_AA64MMFR0_TGRAN16_SHIFT 20
+#define ID_AA64MMFR0_BIGENDEL0_SHIFT 16
+#define ID_AA64MMFR0_SNSMEM_SHIFT 12
+#define ID_AA64MMFR0_BIGENDEL_SHIFT 8
+#define ID_AA64MMFR0_ASID_SHIFT 4
+#define ID_AA64MMFR0_PARANGE_SHIFT 0
+
+#define ID_AA64MMFR0_TGRAN4_NI 0xf
+#define ID_AA64MMFR0_TGRAN4_SUPPORTED 0x0
+#define ID_AA64MMFR0_TGRAN64_NI 0xf
+#define ID_AA64MMFR0_TGRAN64_SUPPORTED 0x0
+#define ID_AA64MMFR0_TGRAN16_NI 0x0
+#define ID_AA64MMFR0_TGRAN16_SUPPORTED 0x1
+
+/* id_aa64mmfr1 */
+#define ID_AA64MMFR1_PAN_SHIFT 20
+#define ID_AA64MMFR1_LOR_SHIFT 16
+#define ID_AA64MMFR1_HPD_SHIFT 12
+#define ID_AA64MMFR1_VHE_SHIFT 8
+#define ID_AA64MMFR1_VMIDBITS_SHIFT 4
+#define ID_AA64MMFR1_HADBS_SHIFT 0
+
+/* id_aa64mmfr2 */
+#define ID_AA64MMFR2_UAO_SHIFT 4
+
+/* id_aa64dfr0 */
+#define ID_AA64DFR0_CTX_CMPS_SHIFT 28
+#define ID_AA64DFR0_WRPS_SHIFT 20
+#define ID_AA64DFR0_BRPS_SHIFT 12
+#define ID_AA64DFR0_PMUVER_SHIFT 8
+#define ID_AA64DFR0_TRACEVER_SHIFT 4
+#define ID_AA64DFR0_DEBUGVER_SHIFT 0
+
+#define ID_ISAR5_RDM_SHIFT 24
+#define ID_ISAR5_CRC32_SHIFT 16
+#define ID_ISAR5_SHA2_SHIFT 12
+#define ID_ISAR5_SHA1_SHIFT 8
+#define ID_ISAR5_AES_SHIFT 4
+#define ID_ISAR5_SEVL_SHIFT 0
+
+#define MVFR0_FPROUND_SHIFT 28
+#define MVFR0_FPSHVEC_SHIFT 24
+#define MVFR0_FPSQRT_SHIFT 20
+#define MVFR0_FPDIVIDE_SHIFT 16
+#define MVFR0_FPTRAP_SHIFT 12
+#define MVFR0_FPDP_SHIFT 8
+#define MVFR0_FPSP_SHIFT 4
+#define MVFR0_SIMD_SHIFT 0
+
+#define MVFR1_SIMDFMAC_SHIFT 28
+#define MVFR1_FPHP_SHIFT 24
+#define MVFR1_SIMDHP_SHIFT 20
+#define MVFR1_SIMDSP_SHIFT 16
+#define MVFR1_SIMDINT_SHIFT 12
+#define MVFR1_SIMDLS_SHIFT 8
+#define MVFR1_FPDNAN_SHIFT 4
+#define MVFR1_FPFTZ_SHIFT 0
+
#ifdef __ASSEMBLY__
@@ -61,6 +195,8 @@
#else
+#include <linux/types.h>
+
asm(
" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n"
" .equ __reg_num_x\\num, \\num\n"
@@ -85,6 +221,23 @@
val |= set;
asm volatile("msr sctlr_el1, %0" : : "r" (val));
}
+
+/*
+ * Unlike read_cpuid, calls to read_sysreg are never expected to be
+ * optimized away or replaced with synthetic values.
+ */
+#define read_sysreg(r) ({ \
+ u64 __val; \
+ asm volatile("mrs %0, " __stringify(r) : "=r" (__val)); \
+ __val; \
+})
+
+#define write_sysreg(v, r) do { \
+ u64 __val = (u64)v; \
+ asm volatile("msr " __stringify(r) ", %0" \
+ : : "r" (__val)); \
+} while (0)
+
#endif
#endif /* __ASM_SYSREG_H */
diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
index b9a8734..fb87ac5 100644
--- a/arch/arm64/include/asm/system_misc.h
+++ b/arch/arm64/include/asm/system_misc.h
@@ -41,7 +41,6 @@
extern void show_pte(struct mm_struct *mm, unsigned long addr);
extern void __show_regs(struct pt_regs *);
-void soft_restart(unsigned long);
extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);
extern char* (*arch_read_hardware_id)(void);
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index e727d81..d9f04cc 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -46,6 +46,9 @@
struct thread_info {
unsigned long flags; /* low level flags */
mm_segment_t addr_limit; /* address limit */
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ u64 ttbr0; /* saved TTBR0_EL1 */
+#endif
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
struct restart_block restart_block;
diff --git a/arch/arm64/include/asm/tlb.h b/arch/arm64/include/asm/tlb.h
index 3a0242c..ffdaea7 100644
--- a/arch/arm64/include/asm/tlb.h
+++ b/arch/arm64/include/asm/tlb.h
@@ -37,12 +37,21 @@
static inline void tlb_flush(struct mmu_gather *tlb)
{
- if (tlb->fullmm) {
- flush_tlb_mm(tlb->mm);
- } else {
- struct vm_area_struct vma = { .vm_mm = tlb->mm, };
- flush_tlb_range(&vma, tlb->start, tlb->end);
- }
+ struct vm_area_struct vma = { .vm_mm = tlb->mm, };
+
+ /*
+ * The ASID allocator will either invalidate the ASID or mark
+ * it as used.
+ */
+ if (tlb->fullmm)
+ return;
+
+ /*
+ * The intermediate page table levels are already handled by
+ * the __(pte|pmd|pud)_free_tlb() functions, so last level
+ * TLBI is sufficient here.
+ */
+ __flush_tlb_range(&vma, tlb->start, tlb->end, true);
}
static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
index c61e264..72753b3 100644
--- a/arch/arm64/include/asm/tlbflush.h
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -24,17 +24,10 @@
#include <linux/sched.h>
#include <asm/cputype.h>
-extern void __cpu_flush_user_tlb_range(unsigned long, unsigned long, struct vm_area_struct *);
-extern void __cpu_flush_kern_tlb_range(unsigned long, unsigned long);
-
-extern struct cpu_tlb_fns cpu_tlb;
-
/*
* TLB Management
* ==============
*
- * The arch/arm64/mm/tlb.S files implement these methods.
- *
* The TLB specific code is expected to perform whatever tests it needs
* to determine if it should invalidate the TLB for each call. Start
* addresses are inclusive and end addresses are exclusive; it is safe to
@@ -70,6 +63,14 @@
* only require the D-TLB to be invalidated.
* - kaddr - Kernel virtual memory address
*/
+static inline void local_flush_tlb_all(void)
+{
+ dsb(nshst);
+ asm("tlbi vmalle1");
+ dsb(nsh);
+ isb();
+}
+
static inline void flush_tlb_all(void)
{
dsb(ishst);
@@ -86,7 +87,7 @@
dsb();
isb();
#else
- unsigned long asid = (unsigned long)ASID(mm) << 48;
+ unsigned long asid = ASID(mm) << 48;
dsb(ishst);
asm("tlbi aside1is, %0" : : "r" (asid));
@@ -103,32 +104,60 @@
dsb();
isb();
#else
- unsigned long addr = uaddr >> 12 |
- ((unsigned long)ASID(vma->vm_mm) << 48);
+ unsigned long addr = uaddr >> 12 | (ASID(vma->vm_mm) << 48);
dsb(ishst);
- asm("tlbi vae1is, %0" : : "r" (addr));
+ asm("tlbi vale1is, %0" : : "r" (addr));
dsb(ish);
#endif
}
+/*
+ * This is meant to avoid soft lock-ups on large TLB flushing ranges and not
+ * necessarily a performance improvement.
+ */
+#define MAX_TLB_RANGE (1024UL << PAGE_SHIFT)
+
static inline void __flush_tlb_range(struct vm_area_struct *vma,
- unsigned long start, unsigned long end)
+ unsigned long start, unsigned long end,
+ bool last_level)
{
- unsigned long asid = (unsigned long)ASID(vma->vm_mm) << 48;
+ unsigned long asid = ASID(vma->vm_mm) << 48;
unsigned long addr;
+
+ if ((end - start) > MAX_TLB_RANGE) {
+ flush_tlb_mm(vma->vm_mm);
+ return;
+ }
+
start = asid | (start >> 12);
end = asid | (end >> 12);
dsb(ishst);
- for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12))
- asm("tlbi vae1is, %0" : : "r"(addr));
+ for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) {
+ if (last_level)
+ asm("tlbi vale1is, %0" : : "r"(addr));
+ else
+ asm("tlbi vae1is, %0" : : "r"(addr));
+ }
dsb(ish);
}
-static inline void __flush_tlb_kernel_range(unsigned long start, unsigned long end)
+static inline void flush_tlb_range(struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
+{
+ __flush_tlb_range(vma, start, end, false);
+}
+
+static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end)
{
unsigned long addr;
+
+ if ((end - start) > MAX_TLB_RANGE) {
+ flush_tlb_all();
+ return;
+ }
+
start >>= 12;
end >>= 12;
@@ -140,55 +169,17 @@
}
/*
- * This is meant to avoid soft lock-ups on large TLB flushing ranges and not
- * necessarily a performance improvement.
- */
-#define MAX_TLB_RANGE (1024UL << PAGE_SHIFT)
-
-static inline void flush_tlb_range(struct vm_area_struct *vma,
- unsigned long start, unsigned long end)
-{
- if ((end - start) <= MAX_TLB_RANGE)
- __flush_tlb_range(vma, start, end);
- else
- flush_tlb_mm(vma->vm_mm);
-}
-
-static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end)
-{
- if ((end - start) <= MAX_TLB_RANGE)
- __flush_tlb_kernel_range(start, end);
- else
- flush_tlb_all();
-}
-
-/*
* Used to invalidate the TLB (walk caches) corresponding to intermediate page
* table levels (pgd/pud/pmd).
*/
static inline void __flush_tlb_pgtable(struct mm_struct *mm,
unsigned long uaddr)
{
- unsigned long addr = uaddr >> 12 | ((unsigned long)ASID(mm) << 48);
+ unsigned long addr = uaddr >> 12 | (ASID(mm) << 48);
- dsb(ishst);
asm("tlbi vae1is, %0" : : "r" (addr));
dsb(ish);
}
-/*
- * On AArch64, the cache coherency is handled via the set_pte_at() function.
- */
-static inline void update_mmu_cache(struct vm_area_struct *vma,
- unsigned long addr, pte_t *ptep)
-{
- /*
- * set_pte() does not have a DSB for user mappings, so make sure that
- * the page table write is visible.
- */
- dsb(ishst);
-}
-
-#define update_mmu_cache_pmd(vma, address, pmd) do { } while (0)
#endif
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
index 4ea11ba..3a5bbb4 100644
--- a/arch/arm64/include/asm/topology.h
+++ b/arch/arm64/include/asm/topology.h
@@ -1,8 +1,6 @@
#ifndef __ASM_TOPOLOGY_H
#define __ASM_TOPOLOGY_H
-#ifdef CONFIG_SMP
-
#include <linux/cpumask.h>
struct cpu_topology {
@@ -33,13 +31,6 @@
#define arch_scale_cpu_capacity scale_cpu_capacity
extern unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu);
-#else
-
-static inline void init_cpu_topology(void) { }
-static inline void store_cpu_topology(unsigned int cpuid) { }
-
-#endif
-
#include <asm-generic/topology.h>
#endif /* _ASM_ARM_TOPOLOGY_H */
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index cb8dab4..a08d498 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -18,6 +18,8 @@
#ifndef __ASM_UACCESS_H
#define __ASM_UACCESS_H
+#ifndef __ASSEMBLY__
+
/*
* User space memory access functions
*/
@@ -26,6 +28,7 @@
#include <asm/alternative.h>
#include <asm/cpufeature.h>
+#include <asm/kernel-pgtable.h>
#include <asm/ptrace.h>
#include <asm/sysreg.h>
#include <asm/errno.h>
@@ -64,6 +67,16 @@
static inline void set_fs(mm_segment_t fs)
{
current_thread_info()->addr_limit = fs;
+
+ /*
+ * Enable/disable UAO so that copy_to_user() etc can access
+ * kernel memory with the unprivileged instructions.
+ */
+ if (IS_ENABLED(CONFIG_ARM64_UAO) && fs == KERNEL_DS)
+ asm(ALTERNATIVE("nop", SET_PSTATE_UAO(1), ARM64_HAS_UAO));
+ else
+ asm(ALTERNATIVE("nop", SET_PSTATE_UAO(0), ARM64_HAS_UAO,
+ CONFIG_ARM64_UAO));
}
#define segment_eq(a,b) ((a) == (b))
@@ -106,6 +119,85 @@
#define user_addr_max get_fs
/*
+ * User access enabling/disabling.
+ */
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+static inline void uaccess_ttbr0_disable(void)
+{
+ unsigned long ttbr;
+
+ /* reserved_ttbr0 placed at the end of swapper_pg_dir */
+ ttbr = read_sysreg(ttbr1_el1) + SWAPPER_DIR_SIZE;
+ write_sysreg(ttbr, ttbr0_el1);
+ isb();
+}
+
+static inline void uaccess_ttbr0_enable(void)
+{
+ unsigned long flags;
+
+ /*
+ * Disable interrupts to avoid preemption between reading the 'ttbr0'
+ * variable and the MSR. A context switch could trigger an ASID
+ * roll-over and an update of 'ttbr0'.
+ */
+ local_irq_save(flags);
+ write_sysreg(current_thread_info()->ttbr0, ttbr0_el1);
+ isb();
+ local_irq_restore(flags);
+}
+#else
+static inline void uaccess_ttbr0_disable(void)
+{
+}
+
+static inline void uaccess_ttbr0_enable(void)
+{
+}
+#endif
+
+#define __uaccess_disable(alt) \
+do { \
+ if (system_uses_ttbr0_pan()) \
+ uaccess_ttbr0_disable(); \
+ else \
+ asm(ALTERNATIVE("nop", SET_PSTATE_PAN(1), alt, \
+ CONFIG_ARM64_PAN)); \
+} while (0)
+
+#define __uaccess_enable(alt) \
+do { \
+ if (system_uses_ttbr0_pan()) \
+ uaccess_ttbr0_enable(); \
+ else \
+ asm(ALTERNATIVE("nop", SET_PSTATE_PAN(0), alt, \
+ CONFIG_ARM64_PAN)); \
+} while (0)
+
+static inline void uaccess_disable(void)
+{
+ __uaccess_disable(ARM64_HAS_PAN);
+}
+
+static inline void uaccess_enable(void)
+{
+ __uaccess_enable(ARM64_HAS_PAN);
+}
+
+/*
+ * These functions are no-ops when UAO is present.
+ */
+static inline void uaccess_disable_not_uao(void)
+{
+ __uaccess_disable(ARM64_ALT_PAN_NOT_UAO);
+}
+
+static inline void uaccess_enable_not_uao(void)
+{
+ __uaccess_enable(ARM64_ALT_PAN_NOT_UAO);
+}
+
+/*
* The "__xxx" versions of the user access functions do not verify the address
* space - it must have been done previously with a separate "access_ok()"
* call.
@@ -113,9 +205,10 @@
* The "__xxx_error" versions set the third argument to -EFAULT if an error
* occurs, and leave it unchanged on success.
*/
-#define __get_user_asm(instr, reg, x, addr, err) \
+#define __get_user_asm(instr, alt_instr, reg, x, addr, err, feature) \
asm volatile( \
- "1: " instr " " reg "1, [%2]\n" \
+ "1:"ALTERNATIVE(instr " " reg "1, [%2]\n", \
+ alt_instr " " reg "1, [%2]\n", feature) \
"2:\n" \
" .section .fixup, \"ax\"\n" \
" .align 2\n" \
@@ -134,27 +227,29 @@
do { \
unsigned long __gu_val; \
__chk_user_ptr(ptr); \
- asm(ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)); \
+ uaccess_enable_not_uao(); \
switch (sizeof(*(ptr))) { \
case 1: \
- __get_user_asm("ldrb", "%w", __gu_val, (ptr), (err)); \
+ __get_user_asm("ldrb", "ldtrb", "%w", __gu_val, (ptr), \
+ (err), ARM64_HAS_UAO); \
break; \
case 2: \
- __get_user_asm("ldrh", "%w", __gu_val, (ptr), (err)); \
+ __get_user_asm("ldrh", "ldtrh", "%w", __gu_val, (ptr), \
+ (err), ARM64_HAS_UAO); \
break; \
case 4: \
- __get_user_asm("ldr", "%w", __gu_val, (ptr), (err)); \
+ __get_user_asm("ldr", "ldtr", "%w", __gu_val, (ptr), \
+ (err), ARM64_HAS_UAO); \
break; \
case 8: \
- __get_user_asm("ldr", "%", __gu_val, (ptr), (err)); \
+ __get_user_asm("ldr", "ldtr", "%", __gu_val, (ptr), \
+ (err), ARM64_HAS_UAO); \
break; \
default: \
BUILD_BUG(); \
} \
+ uaccess_disable_not_uao(); \
(x) = (__force __typeof__(*(ptr)))__gu_val; \
- asm(ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)); \
} while (0)
#define __get_user(x, ptr) \
@@ -181,9 +276,10 @@
((x) = 0, -EFAULT); \
})
-#define __put_user_asm(instr, reg, x, addr, err) \
+#define __put_user_asm(instr, alt_instr, reg, x, addr, err, feature) \
asm volatile( \
- "1: " instr " " reg "1, [%2]\n" \
+ "1:"ALTERNATIVE(instr " " reg "1, [%2]\n", \
+ alt_instr " " reg "1, [%2]\n", feature) \
"2:\n" \
" .section .fixup,\"ax\"\n" \
" .align 2\n" \
@@ -201,26 +297,28 @@
do { \
__typeof__(*(ptr)) __pu_val = (x); \
__chk_user_ptr(ptr); \
- asm(ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)); \
+ uaccess_enable_not_uao(); \
switch (sizeof(*(ptr))) { \
case 1: \
- __put_user_asm("strb", "%w", __pu_val, (ptr), (err)); \
+ __put_user_asm("strb", "sttrb", "%w", __pu_val, (ptr), \
+ (err), ARM64_HAS_UAO); \
break; \
case 2: \
- __put_user_asm("strh", "%w", __pu_val, (ptr), (err)); \
+ __put_user_asm("strh", "sttrh", "%w", __pu_val, (ptr), \
+ (err), ARM64_HAS_UAO); \
break; \
case 4: \
- __put_user_asm("str", "%w", __pu_val, (ptr), (err)); \
+ __put_user_asm("str", "sttr", "%w", __pu_val, (ptr), \
+ (err), ARM64_HAS_UAO); \
break; \
case 8: \
- __put_user_asm("str", "%", __pu_val, (ptr), (err)); \
+ __put_user_asm("str", "sttr", "%", __pu_val, (ptr), \
+ (err), ARM64_HAS_UAO); \
break; \
default: \
BUILD_BUG(); \
} \
- asm(ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)); \
+ uaccess_disable_not_uao(); \
} while (0)
#define __put_user(x, ptr) \
@@ -247,24 +345,39 @@
-EFAULT; \
})
-extern unsigned long __must_check __copy_from_user(void *to, const void __user *from, unsigned long n);
-extern unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n);
+extern unsigned long __must_check __arch_copy_from_user(void *to, const void __user *from, unsigned long n);
+extern unsigned long __must_check __arch_copy_to_user(void __user *to, const void *from, unsigned long n);
extern unsigned long __must_check __copy_in_user(void __user *to, const void __user *from, unsigned long n);
extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n);
+static inline unsigned long __must_check __copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+ check_object_size(to, n, false);
+ return __arch_copy_from_user(to, from, n);
+}
+
+static inline unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+ check_object_size(from, n, true);
+ return __arch_copy_to_user(to, from, n);
+}
+
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
{
- if (access_ok(VERIFY_READ, from, n))
- n = __copy_from_user(to, from, n);
- else /* security hole - plug it */
+ if (access_ok(VERIFY_READ, from, n)) {
+ check_object_size(to, n, false);
+ n = __arch_copy_from_user(to, from, n);
+ } else /* security hole - plug it */
memset(to, 0, n);
return n;
}
static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
{
- if (access_ok(VERIFY_WRITE, to, n))
- n = __copy_to_user(to, from, n);
+ if (access_ok(VERIFY_WRITE, to, n)) {
+ check_object_size(from, n, true);
+ n = __arch_copy_to_user(to, from, n);
+ }
return n;
}
@@ -290,4 +403,75 @@
extern __must_check long strlen_user(const char __user *str);
extern __must_check long strnlen_user(const char __user *str, long n);
+#else /* __ASSEMBLY__ */
+
+#include <asm/alternative.h>
+#include <asm/assembler.h>
+#include <asm/kernel-pgtable.h>
+#include <asm/page.h>
+
+/*
+ * User access enabling/disabling macros.
+ */
+ .macro uaccess_ttbr0_disable, tmp1
+ mrs \tmp1, ttbr1_el1 // swapper_pg_dir
+ add \tmp1, \tmp1, #SWAPPER_DIR_SIZE // reserved_ttbr0 at the end of swapper_pg_dir
+ msr ttbr0_el1, \tmp1 // set reserved TTBR0_EL1
+ isb
+ .endm
+
+ .macro uaccess_ttbr0_enable, tmp1
+ get_thread_info \tmp1
+ ldr \tmp1, [\tmp1, #TI_TTBR0] // load saved TTBR0_EL1
+ msr ttbr0_el1, \tmp1 // set the non-PAN TTBR0_EL1
+ isb
+ .endm
+
+/*
+ * These macros are no-ops when UAO is present.
+ */
+ .macro uaccess_disable_not_uao, tmp1
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+alternative_if_not ARM64_HAS_PAN
+ uaccess_ttbr0_disable \tmp1
+alternative_else
+ nop
+ nop
+ nop
+ nop
+alternative_endif
+#endif
+alternative_if_not ARM64_ALT_PAN_NOT_UAO
+ nop
+alternative_else
+ SET_PSTATE_PAN(1)
+alternative_endif
+ .endm
+
+ .macro uaccess_enable_not_uao, tmp1, tmp2
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+alternative_if_not ARM64_HAS_PAN
+ save_and_disable_irq \tmp2 // avoid preemption
+ uaccess_ttbr0_enable \tmp1
+ restore_irq \tmp2
+alternative_else
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+ nop
+alternative_endif
+#endif
+alternative_if_not ARM64_ALT_PAN_NOT_UAO
+ nop
+alternative_else
+ SET_PSTATE_PAN(0)
+alternative_endif
+ .endm
+
+#endif /* __ASSEMBLY__ */
+
#endif /* __ASM_UACCESS_H */
diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h
index 208db3d..b5c3933 100644
--- a/arch/arm64/include/uapi/asm/ptrace.h
+++ b/arch/arm64/include/uapi/asm/ptrace.h
@@ -45,6 +45,7 @@
#define PSR_A_BIT 0x00000100
#define PSR_D_BIT 0x00000200
#define PSR_PAN_BIT 0x00400000
+#define PSR_UAO_BIT 0x00800000
#define PSR_Q_BIT 0x08000000
#define PSR_V_BIT 0x10000000
#define PSR_C_BIT 0x20000000
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 4883b05..07110b5 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -15,18 +15,19 @@
CFLAGS_REMOVE_return_address.o = -pg
# Object file lists.
-arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
+arm64-obj-y := debug-monitors.o entry.o irq.o fpsimd.o \
entry-fpsimd.o process.o ptrace.o setup.o signal.o \
sys.o stacktrace.o time.o traps.o io.o vdso.o \
hyp-stub.o psci.o psci-call.o cpu_ops.o insn.o \
- return_address.o cpuinfo.o cpu_errata.o cpufeature.o alternative.o
+ return_address.o cpuinfo.o cpu_errata.o \
+ cpufeature.o alternative.o \
+ smp.o smp_spin_table.o topology.o
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
sys_compat.o \
../../arm/kernel/opcodes.o
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
-arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o
arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o perf_debug.o \
perf_trace_counters.o \
diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index fb92049..392c919 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -27,35 +27,135 @@
#include <asm/insn.h>
#include <linux/stop_machine.h>
+#define __ALT_PTR(a,f) (u32 *)((void *)&(a)->f + (a)->f)
+#define ALT_ORIG_PTR(a) __ALT_PTR(a, orig_offset)
+#define ALT_REPL_PTR(a) __ALT_PTR(a, alt_offset)
+
extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
-static int __apply_alternatives(void *dummy)
+struct alt_region {
+ struct alt_instr *begin;
+ struct alt_instr *end;
+};
+
+/*
+ * Check if the target PC is within an alternative block.
+ */
+static bool branch_insn_requires_update(struct alt_instr *alt, unsigned long pc)
+{
+ unsigned long replptr;
+
+ if (kernel_text_address(pc))
+ return 1;
+
+ replptr = (unsigned long)ALT_REPL_PTR(alt);
+ if (pc >= replptr && pc <= (replptr + alt->alt_len))
+ return 0;
+
+ /*
+ * Branching into *another* alternate sequence is doomed, and
+ * we're not even trying to fix it up.
+ */
+ BUG();
+}
+
+static u32 get_alt_insn(struct alt_instr *alt, u32 *insnptr, u32 *altinsnptr)
+{
+ u32 insn;
+
+ insn = le32_to_cpu(*altinsnptr);
+
+ if (aarch64_insn_is_branch_imm(insn)) {
+ s32 offset = aarch64_get_branch_offset(insn);
+ unsigned long target;
+
+ target = (unsigned long)altinsnptr + offset;
+
+ /*
+ * If we're branching inside the alternate sequence,
+ * do not rewrite the instruction, as it is already
+ * correct. Otherwise, generate the new instruction.
+ */
+ if (branch_insn_requires_update(alt, target)) {
+ offset = target - (unsigned long)insnptr;
+ insn = aarch64_set_branch_offset(insn, offset);
+ }
+ }
+
+ return insn;
+}
+
+static void __apply_alternatives(void *alt_region)
{
struct alt_instr *alt;
- u32 *origptr, *replptr, *endptr;
+ struct alt_region *region = alt_region;
+ u32 *origptr, *replptr;
- for (alt = __alt_instructions; alt < __alt_instructions_end; alt++) {
+ for (alt = region->begin; alt < region->end; alt++) {
+ u32 insn;
+ int i, nr_inst;
+
if (!cpus_have_cap(alt->cpufeature))
continue;
- BUG_ON(alt->alt_len != alt->orig_len);
+ BUG_ON(alt->alt_len > alt->orig_len);
pr_info_once("patching kernel code\n");
- origptr = (void *)&alt->orig_offset + alt->orig_offset;
- endptr = (void *)origptr + alt->orig_len;
- replptr = (void *)&alt->alt_offset + alt->alt_offset;
- for (; origptr < endptr; origptr++, replptr++)
- BUG_ON(aarch64_insn_patch_text_nosync(origptr, *replptr));
+ origptr = ALT_ORIG_PTR(alt);
+ replptr = ALT_REPL_PTR(alt);
+ nr_inst = alt->alt_len / sizeof(insn);
+
+ for (i = 0; i < nr_inst; i++) {
+ insn = get_alt_insn(alt, origptr + i, replptr + i);
+ *(origptr + i) = cpu_to_le32(insn);
+ }
+
+ flush_icache_range((uintptr_t)origptr,
+ (uintptr_t)(origptr + nr_inst));
+ }
+}
+
+/*
+ * We might be patching the stop_machine state machine, so implement a
+ * really simple polling protocol here.
+ */
+static int __apply_alternatives_multi_stop(void *unused)
+{
+ static int patched = 0;
+ struct alt_region region = {
+ .begin = __alt_instructions,
+ .end = __alt_instructions_end,
+ };
+
+ /* We always have a CPU 0 at this point (__init) */
+ if (smp_processor_id()) {
+ while (!READ_ONCE(patched))
+ cpu_relax();
+ } else {
+ BUG_ON(patched);
+ __apply_alternatives(®ion);
+ /* Barriers provided by the cache flushing */
+ WRITE_ONCE(patched, 1);
}
return 0;
}
-void apply_alternatives(void)
+void __init apply_alternatives_all(void)
{
/* better not try code patching on a live SMP system */
- stop_machine(__apply_alternatives, NULL, NULL);
+ stop_machine(__apply_alternatives_multi_stop, NULL, cpu_online_mask);
+}
+
+void apply_alternatives(void *start, size_t length)
+{
+ struct alt_region region = {
+ .begin = start,
+ .end = start + length,
+ };
+
+ __apply_alternatives(®ion);
}
void free_alternatives_memory(void)
diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c
index 8c50178..65919bf 100644
--- a/arch/arm64/kernel/arm64ksyms.c
+++ b/arch/arm64/kernel/arm64ksyms.c
@@ -34,8 +34,8 @@
EXPORT_SYMBOL(clear_page);
/* user mem (segment) */
-EXPORT_SYMBOL(__copy_from_user);
-EXPORT_SYMBOL(__copy_to_user);
+EXPORT_SYMBOL(__arch_copy_from_user);
+EXPORT_SYMBOL(__arch_copy_to_user);
EXPORT_SYMBOL(__clear_user);
EXPORT_SYMBOL(__copy_in_user);
diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c
index bcee7ab..b8b4c51 100644
--- a/arch/arm64/kernel/armv8_deprecated.c
+++ b/arch/arm64/kernel/armv8_deprecated.c
@@ -62,7 +62,7 @@
};
static LIST_HEAD(insn_emulation);
-static int nr_insn_emulated;
+static int nr_insn_emulated __initdata;
static DEFINE_RAW_SPINLOCK(insn_emulation_lock);
static void register_emulation_hooks(struct insn_emulation_ops *ops)
@@ -173,7 +173,7 @@
return ret;
}
-static void register_insn_emulation(struct insn_emulation_ops *ops)
+static void __init register_insn_emulation(struct insn_emulation_ops *ops)
{
unsigned long flags;
struct insn_emulation *insn;
@@ -237,7 +237,7 @@
{ }
};
-static void register_insn_emulation_sysctl(struct ctl_table *table)
+static void __init register_insn_emulation_sysctl(struct ctl_table *table)
{
unsigned long flags;
int i = 0;
@@ -281,9 +281,9 @@
* Error-checking SWP macros implemented using ldxr{b}/stxr{b}
*/
#define __user_swpX_asm(data, addr, res, temp, B) \
+do { \
+ uaccess_enable(); \
__asm__ __volatile__( \
- ALTERNATIVE("nop", SET_PSTATE_PAN(0), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN) \
" mov %w2, %w1\n" \
"0: ldxr"B" %w1, [%3]\n" \
"1: stxr"B" %w0, %w2, [%3]\n" \
@@ -300,11 +300,11 @@
" .quad 0b, 3b\n" \
" .quad 1b, 3b\n" \
" .popsection\n" \
- ALTERNATIVE("nop", SET_PSTATE_PAN(1), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN) \
: "=&r" (res), "+r" (data), "=&r" (temp) \
: "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \
- : "memory")
+ : "memory"); \
+ uaccess_disable(); \
+} while (0)
#define __user_swp_asm(data, addr, res, temp) \
__user_swpX_asm(data, addr, res, temp, "")
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 9a9fce0..9b80791 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -24,7 +24,6 @@
#include <linux/kvm_host.h>
#include <asm/thread_info.h>
#include <asm/memory.h>
-#include <asm/cputable.h>
#include <asm/smp_plat.h>
#include <asm/suspend.h>
#include <asm/vdso_datapage.h>
@@ -37,6 +36,9 @@
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ DEFINE(TI_TTBR0, offsetof(struct thread_info, ttbr0));
+#endif
DEFINE(TI_TASK, offsetof(struct thread_info, task));
DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
@@ -60,9 +62,10 @@
DEFINE(S_PC, offsetof(struct pt_regs, pc));
DEFINE(S_ORIG_X0, offsetof(struct pt_regs, orig_x0));
DEFINE(S_SYSCALLNO, offsetof(struct pt_regs, syscallno));
+ DEFINE(S_ORIG_ADDR_LIMIT, offsetof(struct pt_regs, orig_addr_limit));
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
BLANK();
- DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id));
+ DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id.counter));
BLANK();
DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm));
DEFINE(VMA_VM_FLAGS, offsetof(struct vm_area_struct, vm_flags));
@@ -71,9 +74,6 @@
BLANK();
DEFINE(PAGE_SZ, PAGE_SIZE);
BLANK();
- DEFINE(CPU_INFO_SZ, sizeof(struct cpu_info));
- DEFINE(CPU_INFO_SETUP, offsetof(struct cpu_info, cpu_setup));
- BLANK();
DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL);
DEFINE(DMA_TO_DEVICE, DMA_TO_DEVICE);
DEFINE(DMA_FROM_DEVICE, DMA_FROM_DEVICE);
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 8b2f02f..807d1c1 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -95,5 +95,5 @@
void check_local_cpu_errata(void)
{
- check_cpu_capabilities(arm64_errata, "enabling workaround for");
+ update_cpu_capabilities(arm64_errata, "enabling workaround for");
}
diff --git a/arch/arm64/kernel/cpu_ops.c b/arch/arm64/kernel/cpu_ops.c
index 24af532..d2107e5 100644
--- a/arch/arm64/kernel/cpu_ops.c
+++ b/arch/arm64/kernel/cpu_ops.c
@@ -18,11 +18,12 @@
#include <asm/cpu_ops.h>
#include <asm/smp_plat.h>
+#include <linux/cache.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/string.h>
-const struct cpu_operations *cpu_ops[NR_CPUS];
+const struct cpu_operations *cpu_ops[NR_CPUS] __ro_after_init;
extern struct of_cpu_method __cpu_method_of_table[];
static const struct of_cpu_method __cpu_method_of_table_sentinel
__used __section(__cpu_method_of_table_end);
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 978fa16..5834753 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -16,12 +16,589 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#define pr_fmt(fmt) "alternatives: " fmt
+#define pr_fmt(fmt) "CPU features: " fmt
+#include <linux/bsearch.h>
+#include <linux/sort.h>
#include <linux/types.h>
#include <asm/cpu.h>
#include <asm/cpufeature.h>
+#include <asm/cpu_ops.h>
#include <asm/processor.h>
+#include <asm/sysreg.h>
+
+unsigned long elf_hwcap __read_mostly;
+EXPORT_SYMBOL_GPL(elf_hwcap);
+
+#ifdef CONFIG_COMPAT
+#define COMPAT_ELF_HWCAP_DEFAULT \
+ (COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\
+ COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\
+ COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\
+ COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\
+ COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV|\
+ COMPAT_HWCAP_LPAE)
+unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT;
+unsigned int compat_elf_hwcap2 __read_mostly;
+#endif
+
+DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
+EXPORT_SYMBOL(cpu_hwcaps);
+
+#define __ARM64_FTR_BITS(SIGNED, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
+ { \
+ .sign = SIGNED, \
+ .strict = STRICT, \
+ .type = TYPE, \
+ .shift = SHIFT, \
+ .width = WIDTH, \
+ .safe_val = SAFE_VAL, \
+ }
+
+/* Define a feature with signed values */
+#define ARM64_FTR_BITS(STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
+ __ARM64_FTR_BITS(FTR_SIGNED, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL)
+
+/* Define a feature with unsigned value */
+#define U_ARM64_FTR_BITS(STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
+ __ARM64_FTR_BITS(FTR_UNSIGNED, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL)
+
+#define ARM64_FTR_END \
+ { \
+ .width = 0, \
+ }
+
+/* meta feature for alternatives */
+static bool __maybe_unused
+cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry);
+
+static struct arm64_ftr_bits ftr_id_aa64isar0[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64ISAR0_RDM_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 24, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_CRC32_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_SHA2_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_SHA1_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_AES_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* RAZ */
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 28, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_GIC_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_ASIMD_SHIFT, 4, ID_AA64PFR0_ASIMD_NI),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_FP_SHIFT, 4, ID_AA64PFR0_FP_NI),
+ /* Linux doesn't care about the EL3 */
+ ARM64_FTR_BITS(FTR_NONSTRICT, FTR_EXACT, ID_AA64PFR0_EL3_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_EL2_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_EL1_SHIFT, 4, ID_AA64PFR0_EL1_64BIT_ONLY),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_EL0_SHIFT, 4, ID_AA64PFR0_EL0_64BIT_ONLY),
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_id_aa64mmfr0[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN4_SHIFT, 4, ID_AA64MMFR0_TGRAN4_NI),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN64_SHIFT, 4, ID_AA64MMFR0_TGRAN64_NI),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN16_SHIFT, 4, ID_AA64MMFR0_TGRAN16_NI),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_BIGENDEL0_SHIFT, 4, 0),
+ /* Linux shouldn't care about secure memory */
+ ARM64_FTR_BITS(FTR_NONSTRICT, FTR_EXACT, ID_AA64MMFR0_SNSMEM_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_BIGENDEL_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_ASID_SHIFT, 4, 0),
+ /*
+ * Differing PARange is fine as long as all peripherals and memory are mapped
+ * within the minimum PARange of all CPUs
+ */
+ U_ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR0_PARANGE_SHIFT, 4, 0),
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_id_aa64mmfr1[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_PAN_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_LOR_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_HPD_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_VHE_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_VMIDBITS_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_HADBS_SHIFT, 4, 0),
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_id_aa64mmfr2[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_UAO_SHIFT, 4, 0),
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_ctr[] = {
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 31, 1, 1), /* RAO */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 28, 3, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_HIGHER_SAFE, 24, 4, 0), /* CWG */
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0), /* ERG */
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 1), /* DminLine */
+ /*
+ * Linux can handle differing I-cache policies. Userspace JITs will
+ * make use of *minLine
+ */
+ U_ARM64_FTR_BITS(FTR_NONSTRICT, FTR_EXACT, 14, 2, 0), /* L1Ip */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 10, 0), /* RAZ */
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0), /* IminLine */
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_id_mmfr0[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 28, 4, 0), /* InnerShr */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 24, 4, 0), /* FCSE */
+ ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, 20, 4, 0), /* AuxReg */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 16, 4, 0), /* TCM */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 12, 4, 0), /* ShareLvl */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 8, 4, 0), /* OuterShr */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 4, 0), /* PMSA */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* VMSA */
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_id_aa64dfr0[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_CTX_CMPS_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_WRPS_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_BRPS_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_TRACEVER_SHIFT, 4, 0),
+ U_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_mvfr2[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 8, 24, 0), /* RAZ */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 4, 0), /* FPMisc */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* SIMDMisc */
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_dczid[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 5, 27, 0), /* RAZ */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 1, 1), /* DZP */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0), /* BS */
+ ARM64_FTR_END,
+};
+
+
+static struct arm64_ftr_bits ftr_id_isar5[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_RDM_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 20, 4, 0), /* RAZ */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_CRC32_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_SHA2_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_SHA1_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_AES_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_SEVL_SHIFT, 4, 0),
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_id_mmfr4[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 8, 24, 0), /* RAZ */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 4, 0), /* ac2 */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* RAZ */
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_id_pfr0[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 16, 16, 0), /* RAZ */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 12, 4, 0), /* State3 */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 8, 4, 0), /* State2 */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 4, 0), /* State1 */
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* State0 */
+ ARM64_FTR_END,
+};
+
+/*
+ * Common ftr bits for a 32bit register with all hidden, strict
+ * attributes, with 4bit feature fields and a default safe value of
+ * 0. Covers the following 32bit registers:
+ * id_isar[0-4], id_mmfr[1-3], id_pfr1, mvfr[0-1]
+ */
+static struct arm64_ftr_bits ftr_generic_32bits[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 28, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 24, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 12, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 8, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 4, 4, 0),
+ ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0),
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_generic[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 64, 0),
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_generic32[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 32, 0),
+ ARM64_FTR_END,
+};
+
+static struct arm64_ftr_bits ftr_aa64raz[] = {
+ ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 64, 0),
+ ARM64_FTR_END,
+};
+
+#define ARM64_FTR_REG(id, table) \
+ { \
+ .sys_id = id, \
+ .name = #id, \
+ .ftr_bits = &((table)[0]), \
+ }
+
+static struct arm64_ftr_reg arm64_ftr_regs[] = {
+
+ /* Op1 = 0, CRn = 0, CRm = 1 */
+ ARM64_FTR_REG(SYS_ID_PFR0_EL1, ftr_id_pfr0),
+ ARM64_FTR_REG(SYS_ID_PFR1_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_ID_DFR0_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_ID_MMFR0_EL1, ftr_id_mmfr0),
+ ARM64_FTR_REG(SYS_ID_MMFR1_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_ID_MMFR2_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_ID_MMFR3_EL1, ftr_generic_32bits),
+
+ /* Op1 = 0, CRn = 0, CRm = 2 */
+ ARM64_FTR_REG(SYS_ID_ISAR0_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_ID_ISAR1_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_ID_ISAR2_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_ID_ISAR3_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_ID_ISAR4_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_ID_ISAR5_EL1, ftr_id_isar5),
+ ARM64_FTR_REG(SYS_ID_MMFR4_EL1, ftr_id_mmfr4),
+
+ /* Op1 = 0, CRn = 0, CRm = 3 */
+ ARM64_FTR_REG(SYS_MVFR0_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_MVFR1_EL1, ftr_generic_32bits),
+ ARM64_FTR_REG(SYS_MVFR2_EL1, ftr_mvfr2),
+
+ /* Op1 = 0, CRn = 0, CRm = 4 */
+ ARM64_FTR_REG(SYS_ID_AA64PFR0_EL1, ftr_id_aa64pfr0),
+ ARM64_FTR_REG(SYS_ID_AA64PFR1_EL1, ftr_aa64raz),
+
+ /* Op1 = 0, CRn = 0, CRm = 5 */
+ ARM64_FTR_REG(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0),
+ ARM64_FTR_REG(SYS_ID_AA64DFR1_EL1, ftr_generic),
+
+ /* Op1 = 0, CRn = 0, CRm = 6 */
+ ARM64_FTR_REG(SYS_ID_AA64ISAR0_EL1, ftr_id_aa64isar0),
+ ARM64_FTR_REG(SYS_ID_AA64ISAR1_EL1, ftr_aa64raz),
+
+ /* Op1 = 0, CRn = 0, CRm = 7 */
+ ARM64_FTR_REG(SYS_ID_AA64MMFR0_EL1, ftr_id_aa64mmfr0),
+ ARM64_FTR_REG(SYS_ID_AA64MMFR1_EL1, ftr_id_aa64mmfr1),
+ ARM64_FTR_REG(SYS_ID_AA64MMFR2_EL1, ftr_id_aa64mmfr2),
+
+ /* Op1 = 3, CRn = 0, CRm = 0 */
+ ARM64_FTR_REG(SYS_CTR_EL0, ftr_ctr),
+ ARM64_FTR_REG(SYS_DCZID_EL0, ftr_dczid),
+
+ /* Op1 = 3, CRn = 14, CRm = 0 */
+ ARM64_FTR_REG(SYS_CNTFRQ_EL0, ftr_generic32),
+};
+
+static int search_cmp_ftr_reg(const void *id, const void *regp)
+{
+ return (int)(unsigned long)id - (int)((const struct arm64_ftr_reg *)regp)->sys_id;
+}
+
+/*
+ * get_arm64_ftr_reg - Lookup a feature register entry using its
+ * sys_reg() encoding. With the array arm64_ftr_regs sorted in the
+ * ascending order of sys_id , we use binary search to find a matching
+ * entry.
+ *
+ * returns - Upon success, matching ftr_reg entry for id.
+ * - NULL on failure. It is upto the caller to decide
+ * the impact of a failure.
+ */
+static struct arm64_ftr_reg *get_arm64_ftr_reg(u32 sys_id)
+{
+ return bsearch((const void *)(unsigned long)sys_id,
+ arm64_ftr_regs,
+ ARRAY_SIZE(arm64_ftr_regs),
+ sizeof(arm64_ftr_regs[0]),
+ search_cmp_ftr_reg);
+}
+
+static u64 arm64_ftr_set_value(struct arm64_ftr_bits *ftrp, s64 reg, s64 ftr_val)
+{
+ u64 mask = arm64_ftr_mask(ftrp);
+
+ reg &= ~mask;
+ reg |= (ftr_val << ftrp->shift) & mask;
+ return reg;
+}
+
+static s64 arm64_ftr_safe_value(struct arm64_ftr_bits *ftrp, s64 new, s64 cur)
+{
+ s64 ret = 0;
+
+ switch (ftrp->type) {
+ case FTR_EXACT:
+ ret = ftrp->safe_val;
+ break;
+ case FTR_LOWER_SAFE:
+ ret = new < cur ? new : cur;
+ break;
+ case FTR_HIGHER_SAFE:
+ ret = new > cur ? new : cur;
+ break;
+ default:
+ BUG();
+ }
+
+ return ret;
+}
+
+static int __init sort_cmp_ftr_regs(const void *a, const void *b)
+{
+ return ((const struct arm64_ftr_reg *)a)->sys_id -
+ ((const struct arm64_ftr_reg *)b)->sys_id;
+}
+
+static void __init swap_ftr_regs(void *a, void *b, int size)
+{
+ struct arm64_ftr_reg tmp = *(struct arm64_ftr_reg *)a;
+ *(struct arm64_ftr_reg *)a = *(struct arm64_ftr_reg *)b;
+ *(struct arm64_ftr_reg *)b = tmp;
+}
+
+static void __init sort_ftr_regs(void)
+{
+ /* Keep the array sorted so that we can do the binary search */
+ sort(arm64_ftr_regs,
+ ARRAY_SIZE(arm64_ftr_regs),
+ sizeof(arm64_ftr_regs[0]),
+ sort_cmp_ftr_regs,
+ swap_ftr_regs);
+}
+
+/*
+ * Initialise the CPU feature register from Boot CPU values.
+ * Also initiliases the strict_mask for the register.
+ */
+static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
+{
+ u64 val = 0;
+ u64 strict_mask = ~0x0ULL;
+ struct arm64_ftr_bits *ftrp;
+ struct arm64_ftr_reg *reg = get_arm64_ftr_reg(sys_reg);
+
+ BUG_ON(!reg);
+
+ for (ftrp = reg->ftr_bits; ftrp->width; ftrp++) {
+ s64 ftr_new = arm64_ftr_value(ftrp, new);
+
+ val = arm64_ftr_set_value(ftrp, val, ftr_new);
+ if (!ftrp->strict)
+ strict_mask &= ~arm64_ftr_mask(ftrp);
+ }
+ reg->sys_val = val;
+ reg->strict_mask = strict_mask;
+}
+
+void __init init_cpu_features(struct cpuinfo_arm64 *info)
+{
+ /* Before we start using the tables, make sure it is sorted */
+ sort_ftr_regs();
+
+ init_cpu_ftr_reg(SYS_CTR_EL0, info->reg_ctr);
+ init_cpu_ftr_reg(SYS_DCZID_EL0, info->reg_dczid);
+ init_cpu_ftr_reg(SYS_CNTFRQ_EL0, info->reg_cntfrq);
+ init_cpu_ftr_reg(SYS_ID_AA64DFR0_EL1, info->reg_id_aa64dfr0);
+ init_cpu_ftr_reg(SYS_ID_AA64DFR1_EL1, info->reg_id_aa64dfr1);
+ init_cpu_ftr_reg(SYS_ID_AA64ISAR0_EL1, info->reg_id_aa64isar0);
+ init_cpu_ftr_reg(SYS_ID_AA64ISAR1_EL1, info->reg_id_aa64isar1);
+ init_cpu_ftr_reg(SYS_ID_AA64MMFR0_EL1, info->reg_id_aa64mmfr0);
+ init_cpu_ftr_reg(SYS_ID_AA64MMFR1_EL1, info->reg_id_aa64mmfr1);
+ init_cpu_ftr_reg(SYS_ID_AA64MMFR2_EL1, info->reg_id_aa64mmfr2);
+ init_cpu_ftr_reg(SYS_ID_AA64PFR0_EL1, info->reg_id_aa64pfr0);
+ init_cpu_ftr_reg(SYS_ID_AA64PFR1_EL1, info->reg_id_aa64pfr1);
+ init_cpu_ftr_reg(SYS_ID_DFR0_EL1, info->reg_id_dfr0);
+ init_cpu_ftr_reg(SYS_ID_ISAR0_EL1, info->reg_id_isar0);
+ init_cpu_ftr_reg(SYS_ID_ISAR1_EL1, info->reg_id_isar1);
+ init_cpu_ftr_reg(SYS_ID_ISAR2_EL1, info->reg_id_isar2);
+ init_cpu_ftr_reg(SYS_ID_ISAR3_EL1, info->reg_id_isar3);
+ init_cpu_ftr_reg(SYS_ID_ISAR4_EL1, info->reg_id_isar4);
+ init_cpu_ftr_reg(SYS_ID_ISAR5_EL1, info->reg_id_isar5);
+ init_cpu_ftr_reg(SYS_ID_MMFR0_EL1, info->reg_id_mmfr0);
+ init_cpu_ftr_reg(SYS_ID_MMFR1_EL1, info->reg_id_mmfr1);
+ init_cpu_ftr_reg(SYS_ID_MMFR2_EL1, info->reg_id_mmfr2);
+ init_cpu_ftr_reg(SYS_ID_MMFR3_EL1, info->reg_id_mmfr3);
+ init_cpu_ftr_reg(SYS_ID_PFR0_EL1, info->reg_id_pfr0);
+ init_cpu_ftr_reg(SYS_ID_PFR1_EL1, info->reg_id_pfr1);
+ init_cpu_ftr_reg(SYS_MVFR0_EL1, info->reg_mvfr0);
+ init_cpu_ftr_reg(SYS_MVFR1_EL1, info->reg_mvfr1);
+ init_cpu_ftr_reg(SYS_MVFR2_EL1, info->reg_mvfr2);
+}
+
+static void update_cpu_ftr_reg(struct arm64_ftr_reg *reg, u64 new)
+{
+ struct arm64_ftr_bits *ftrp;
+
+ for (ftrp = reg->ftr_bits; ftrp->width; ftrp++) {
+ s64 ftr_cur = arm64_ftr_value(ftrp, reg->sys_val);
+ s64 ftr_new = arm64_ftr_value(ftrp, new);
+
+ if (ftr_cur == ftr_new)
+ continue;
+ /* Find a safe value */
+ ftr_new = arm64_ftr_safe_value(ftrp, ftr_new, ftr_cur);
+ reg->sys_val = arm64_ftr_set_value(ftrp, reg->sys_val, ftr_new);
+ }
+
+}
+
+static int check_update_ftr_reg(u32 sys_id, int cpu, u64 val, u64 boot)
+{
+ struct arm64_ftr_reg *regp = get_arm64_ftr_reg(sys_id);
+
+ BUG_ON(!regp);
+ update_cpu_ftr_reg(regp, val);
+ if ((boot & regp->strict_mask) == (val & regp->strict_mask))
+ return 0;
+ pr_warn("SANITY CHECK: Unexpected variation in %s. Boot CPU: %#016llx, CPU%d: %#016llx\n",
+ regp->name, boot, cpu, val);
+ return 1;
+}
+
+/*
+ * Update system wide CPU feature registers with the values from a
+ * non-boot CPU. Also performs SANITY checks to make sure that there
+ * aren't any insane variations from that of the boot CPU.
+ */
+void update_cpu_features(int cpu,
+ struct cpuinfo_arm64 *info,
+ struct cpuinfo_arm64 *boot)
+{
+ int taint = 0;
+
+ /*
+ * The kernel can handle differing I-cache policies, but otherwise
+ * caches should look identical. Userspace JITs will make use of
+ * *minLine.
+ */
+ taint |= check_update_ftr_reg(SYS_CTR_EL0, cpu,
+ info->reg_ctr, boot->reg_ctr);
+
+ /*
+ * Userspace may perform DC ZVA instructions. Mismatched block sizes
+ * could result in too much or too little memory being zeroed if a
+ * process is preempted and migrated between CPUs.
+ */
+ taint |= check_update_ftr_reg(SYS_DCZID_EL0, cpu,
+ info->reg_dczid, boot->reg_dczid);
+
+ /* If different, timekeeping will be broken (especially with KVM) */
+ taint |= check_update_ftr_reg(SYS_CNTFRQ_EL0, cpu,
+ info->reg_cntfrq, boot->reg_cntfrq);
+
+ /*
+ * The kernel uses self-hosted debug features and expects CPUs to
+ * support identical debug features. We presently need CTX_CMPs, WRPs,
+ * and BRPs to be identical.
+ * ID_AA64DFR1 is currently RES0.
+ */
+ taint |= check_update_ftr_reg(SYS_ID_AA64DFR0_EL1, cpu,
+ info->reg_id_aa64dfr0, boot->reg_id_aa64dfr0);
+ taint |= check_update_ftr_reg(SYS_ID_AA64DFR1_EL1, cpu,
+ info->reg_id_aa64dfr1, boot->reg_id_aa64dfr1);
+ /*
+ * Even in big.LITTLE, processors should be identical instruction-set
+ * wise.
+ */
+ taint |= check_update_ftr_reg(SYS_ID_AA64ISAR0_EL1, cpu,
+ info->reg_id_aa64isar0, boot->reg_id_aa64isar0);
+ taint |= check_update_ftr_reg(SYS_ID_AA64ISAR1_EL1, cpu,
+ info->reg_id_aa64isar1, boot->reg_id_aa64isar1);
+
+ /*
+ * Differing PARange support is fine as long as all peripherals and
+ * memory are mapped within the minimum PARange of all CPUs.
+ * Linux should not care about secure memory.
+ */
+ taint |= check_update_ftr_reg(SYS_ID_AA64MMFR0_EL1, cpu,
+ info->reg_id_aa64mmfr0, boot->reg_id_aa64mmfr0);
+ taint |= check_update_ftr_reg(SYS_ID_AA64MMFR1_EL1, cpu,
+ info->reg_id_aa64mmfr1, boot->reg_id_aa64mmfr1);
+ taint |= check_update_ftr_reg(SYS_ID_AA64MMFR2_EL1, cpu,
+ info->reg_id_aa64mmfr2, boot->reg_id_aa64mmfr2);
+
+ /*
+ * EL3 is not our concern.
+ * ID_AA64PFR1 is currently RES0.
+ */
+ taint |= check_update_ftr_reg(SYS_ID_AA64PFR0_EL1, cpu,
+ info->reg_id_aa64pfr0, boot->reg_id_aa64pfr0);
+ taint |= check_update_ftr_reg(SYS_ID_AA64PFR1_EL1, cpu,
+ info->reg_id_aa64pfr1, boot->reg_id_aa64pfr1);
+
+ /*
+ * If we have AArch32, we care about 32-bit features for compat. These
+ * registers should be RES0 otherwise.
+ */
+ taint |= check_update_ftr_reg(SYS_ID_DFR0_EL1, cpu,
+ info->reg_id_dfr0, boot->reg_id_dfr0);
+ taint |= check_update_ftr_reg(SYS_ID_ISAR0_EL1, cpu,
+ info->reg_id_isar0, boot->reg_id_isar0);
+ taint |= check_update_ftr_reg(SYS_ID_ISAR1_EL1, cpu,
+ info->reg_id_isar1, boot->reg_id_isar1);
+ taint |= check_update_ftr_reg(SYS_ID_ISAR2_EL1, cpu,
+ info->reg_id_isar2, boot->reg_id_isar2);
+ taint |= check_update_ftr_reg(SYS_ID_ISAR3_EL1, cpu,
+ info->reg_id_isar3, boot->reg_id_isar3);
+ taint |= check_update_ftr_reg(SYS_ID_ISAR4_EL1, cpu,
+ info->reg_id_isar4, boot->reg_id_isar4);
+ taint |= check_update_ftr_reg(SYS_ID_ISAR5_EL1, cpu,
+ info->reg_id_isar5, boot->reg_id_isar5);
+
+ /*
+ * Regardless of the value of the AuxReg field, the AIFSR, ADFSR, and
+ * ACTLR formats could differ across CPUs and therefore would have to
+ * be trapped for virtualization anyway.
+ */
+ taint |= check_update_ftr_reg(SYS_ID_MMFR0_EL1, cpu,
+ info->reg_id_mmfr0, boot->reg_id_mmfr0);
+ taint |= check_update_ftr_reg(SYS_ID_MMFR1_EL1, cpu,
+ info->reg_id_mmfr1, boot->reg_id_mmfr1);
+ taint |= check_update_ftr_reg(SYS_ID_MMFR2_EL1, cpu,
+ info->reg_id_mmfr2, boot->reg_id_mmfr2);
+ taint |= check_update_ftr_reg(SYS_ID_MMFR3_EL1, cpu,
+ info->reg_id_mmfr3, boot->reg_id_mmfr3);
+ taint |= check_update_ftr_reg(SYS_ID_PFR0_EL1, cpu,
+ info->reg_id_pfr0, boot->reg_id_pfr0);
+ taint |= check_update_ftr_reg(SYS_ID_PFR1_EL1, cpu,
+ info->reg_id_pfr1, boot->reg_id_pfr1);
+ taint |= check_update_ftr_reg(SYS_MVFR0_EL1, cpu,
+ info->reg_mvfr0, boot->reg_mvfr0);
+ taint |= check_update_ftr_reg(SYS_MVFR1_EL1, cpu,
+ info->reg_mvfr1, boot->reg_mvfr1);
+ taint |= check_update_ftr_reg(SYS_MVFR2_EL1, cpu,
+ info->reg_mvfr2, boot->reg_mvfr2);
+
+ /*
+ * Mismatched CPU features are a recipe for disaster. Don't even
+ * pretend to support them.
+ */
+ WARN_TAINT_ONCE(taint, TAINT_CPU_OUT_OF_SPEC,
+ "Unsupported CPU feature variation.\n");
+}
+
+u64 read_system_reg(u32 id)
+{
+ struct arm64_ftr_reg *regp = get_arm64_ftr_reg(id);
+
+ /* We shouldn't get a request for an unsupported register */
+ BUG_ON(!regp);
+ return regp->sys_val;
+}
static bool
feature_matches(u64 reg, const struct arm64_cpu_capabilities *entry)
@@ -32,20 +609,11 @@
}
static bool
-has_id_aa64pfr0_feature(const struct arm64_cpu_capabilities *entry)
+has_cpuid_feature(const struct arm64_cpu_capabilities *entry)
{
u64 val;
- val = read_cpuid(id_aa64pfr0_el1);
- return feature_matches(val, entry);
-}
-
-static bool __maybe_unused
-has_id_aa64mmfr1_feature(const struct arm64_cpu_capabilities *entry)
-{
- u64 val;
-
- val = read_cpuid(id_aa64mmfr1_el1);
+ val = read_system_reg(entry->sys_reg);
return feature_matches(val, entry);
}
@@ -53,45 +621,319 @@
{
.desc = "GIC system register CPU interface",
.capability = ARM64_HAS_SYSREG_GIC_CPUIF,
- .matches = has_id_aa64pfr0_feature,
- .field_pos = 24,
+ .matches = has_cpuid_feature,
+ .sys_reg = SYS_ID_AA64PFR0_EL1,
+ .field_pos = ID_AA64PFR0_GIC_SHIFT,
.min_field_value = 1,
},
#ifdef CONFIG_ARM64_PAN
{
.desc = "Privileged Access Never",
.capability = ARM64_HAS_PAN,
- .matches = has_id_aa64mmfr1_feature,
- .field_pos = 20,
+ .matches = has_cpuid_feature,
+ .sys_reg = SYS_ID_AA64MMFR1_EL1,
+ .field_pos = ID_AA64MMFR1_PAN_SHIFT,
.min_field_value = 1,
.enable = cpu_enable_pan,
},
#endif /* CONFIG_ARM64_PAN */
+#ifdef CONFIG_ARM64_UAO
+ {
+ .desc = "User Access Override",
+ .capability = ARM64_HAS_UAO,
+ .matches = has_cpuid_feature,
+ .sys_reg = SYS_ID_AA64MMFR2_EL1,
+ .field_pos = ID_AA64MMFR2_UAO_SHIFT,
+ .min_field_value = 1,
+ .enable = cpu_enable_uao,
+ },
+#endif /* CONFIG_ARM64_UAO */
+#ifdef CONFIG_ARM64_PAN
+ {
+ .capability = ARM64_ALT_PAN_NOT_UAO,
+ .matches = cpufeature_pan_not_uao,
+ },
+#endif /* CONFIG_ARM64_PAN */
{},
};
-void check_cpu_capabilities(const struct arm64_cpu_capabilities *caps,
+#define HWCAP_CAP(reg, field, min_value, type, cap) \
+ { \
+ .desc = #cap, \
+ .matches = has_cpuid_feature, \
+ .sys_reg = reg, \
+ .field_pos = field, \
+ .min_field_value = min_value, \
+ .hwcap_type = type, \
+ .hwcap = cap, \
+ }
+
+static const struct arm64_cpu_capabilities arm64_hwcaps[] = {
+ HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_AES_SHIFT, 2, CAP_HWCAP, HWCAP_PMULL),
+ HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_AES_SHIFT, 1, CAP_HWCAP, HWCAP_AES),
+ HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA1_SHIFT, 1, CAP_HWCAP, HWCAP_SHA1),
+ HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA2_SHIFT, 1, CAP_HWCAP, HWCAP_SHA2),
+ HWCAP_CAP(SYS_ID_AA64ISAR0_EL1, ID_AA64ISAR0_CRC32_SHIFT, 1, CAP_HWCAP, HWCAP_CRC32),
+ HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_FP_SHIFT, 0, CAP_HWCAP, HWCAP_FP),
+ HWCAP_CAP(SYS_ID_AA64PFR0_EL1, ID_AA64PFR0_ASIMD_SHIFT, 0, CAP_HWCAP, HWCAP_ASIMD),
+#ifdef CONFIG_COMPAT
+ HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_AES_SHIFT, 2, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_PMULL),
+ HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_AES_SHIFT, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_AES),
+ HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_SHA1_SHIFT, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_SHA1),
+ HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_SHA2_SHIFT, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_SHA2),
+ HWCAP_CAP(SYS_ID_ISAR5_EL1, ID_ISAR5_CRC32_SHIFT, 1, CAP_COMPAT_HWCAP2, COMPAT_HWCAP2_CRC32),
+#endif
+ {},
+};
+
+static void __init cap_set_hwcap(const struct arm64_cpu_capabilities *cap)
+{
+ switch (cap->hwcap_type) {
+ case CAP_HWCAP:
+ elf_hwcap |= cap->hwcap;
+ break;
+#ifdef CONFIG_COMPAT
+ case CAP_COMPAT_HWCAP:
+ compat_elf_hwcap |= (u32)cap->hwcap;
+ break;
+ case CAP_COMPAT_HWCAP2:
+ compat_elf_hwcap2 |= (u32)cap->hwcap;
+ break;
+#endif
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+/* Check if we have a particular HWCAP enabled */
+static bool __maybe_unused cpus_have_hwcap(const struct arm64_cpu_capabilities *cap)
+{
+ bool rc;
+
+ switch (cap->hwcap_type) {
+ case CAP_HWCAP:
+ rc = (elf_hwcap & cap->hwcap) != 0;
+ break;
+#ifdef CONFIG_COMPAT
+ case CAP_COMPAT_HWCAP:
+ rc = (compat_elf_hwcap & (u32)cap->hwcap) != 0;
+ break;
+ case CAP_COMPAT_HWCAP2:
+ rc = (compat_elf_hwcap2 & (u32)cap->hwcap) != 0;
+ break;
+#endif
+ default:
+ WARN_ON(1);
+ rc = false;
+ }
+
+ return rc;
+}
+
+static void __init setup_cpu_hwcaps(void)
+{
+ int i;
+ const struct arm64_cpu_capabilities *hwcaps = arm64_hwcaps;
+
+ for (i = 0; hwcaps[i].matches; i++)
+ if (hwcaps[i].matches(&hwcaps[i]))
+ cap_set_hwcap(&hwcaps[i]);
+}
+
+void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps,
const char *info)
{
int i;
- for (i = 0; caps[i].desc; i++) {
+ for (i = 0; caps[i].matches; i++) {
if (!caps[i].matches(&caps[i]))
continue;
- if (!cpus_have_cap(caps[i].capability))
+ if (!cpus_have_cap(caps[i].capability) && caps[i].desc)
pr_info("%s %s\n", info, caps[i].desc);
cpus_set_cap(caps[i].capability);
}
+}
- /* second pass allows enable() to consider interacting capabilities */
- for (i = 0; caps[i].desc; i++) {
- if (cpus_have_cap(caps[i].capability) && caps[i].enable)
- caps[i].enable();
+/*
+ * Run through the enabled capabilities and enable() it on all active
+ * CPUs
+ */
+static void __init
+enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps)
+{
+ int i;
+
+ for (i = 0; caps[i].matches; i++)
+ if (caps[i].enable && cpus_have_cap(caps[i].capability))
+ on_each_cpu(caps[i].enable, NULL, true);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+/*
+ * Flag to indicate if we have computed the system wide
+ * capabilities based on the boot time active CPUs. This
+ * will be used to determine if a new booting CPU should
+ * go through the verification process to make sure that it
+ * supports the system capabilities, without using a hotplug
+ * notifier.
+ */
+static bool sys_caps_initialised;
+
+static inline void set_sys_caps_initialised(void)
+{
+ sys_caps_initialised = true;
+}
+
+/*
+ * __raw_read_system_reg() - Used by a STARTING cpu before cpuinfo is populated.
+ */
+static u64 __raw_read_system_reg(u32 sys_id)
+{
+ switch (sys_id) {
+ case SYS_ID_PFR0_EL1: return read_cpuid(SYS_ID_PFR0_EL1);
+ case SYS_ID_PFR1_EL1: return read_cpuid(SYS_ID_PFR1_EL1);
+ case SYS_ID_DFR0_EL1: return read_cpuid(SYS_ID_DFR0_EL1);
+ case SYS_ID_MMFR0_EL1: return read_cpuid(SYS_ID_MMFR0_EL1);
+ case SYS_ID_MMFR1_EL1: return read_cpuid(SYS_ID_MMFR1_EL1);
+ case SYS_ID_MMFR2_EL1: return read_cpuid(SYS_ID_MMFR2_EL1);
+ case SYS_ID_MMFR3_EL1: return read_cpuid(SYS_ID_MMFR3_EL1);
+ case SYS_ID_ISAR0_EL1: return read_cpuid(SYS_ID_ISAR0_EL1);
+ case SYS_ID_ISAR1_EL1: return read_cpuid(SYS_ID_ISAR1_EL1);
+ case SYS_ID_ISAR2_EL1: return read_cpuid(SYS_ID_ISAR2_EL1);
+ case SYS_ID_ISAR3_EL1: return read_cpuid(SYS_ID_ISAR3_EL1);
+ case SYS_ID_ISAR4_EL1: return read_cpuid(SYS_ID_ISAR4_EL1);
+ case SYS_ID_ISAR5_EL1: return read_cpuid(SYS_ID_ISAR4_EL1);
+ case SYS_MVFR0_EL1: return read_cpuid(SYS_MVFR0_EL1);
+ case SYS_MVFR1_EL1: return read_cpuid(SYS_MVFR1_EL1);
+ case SYS_MVFR2_EL1: return read_cpuid(SYS_MVFR2_EL1);
+
+ case SYS_ID_AA64PFR0_EL1: return read_cpuid(SYS_ID_AA64PFR0_EL1);
+ case SYS_ID_AA64PFR1_EL1: return read_cpuid(SYS_ID_AA64PFR0_EL1);
+ case SYS_ID_AA64DFR0_EL1: return read_cpuid(SYS_ID_AA64DFR0_EL1);
+ case SYS_ID_AA64DFR1_EL1: return read_cpuid(SYS_ID_AA64DFR0_EL1);
+ case SYS_ID_AA64MMFR0_EL1: return read_cpuid(SYS_ID_AA64MMFR0_EL1);
+ case SYS_ID_AA64MMFR1_EL1: return read_cpuid(SYS_ID_AA64MMFR1_EL1);
+ case SYS_ID_AA64MMFR2_EL1: return read_cpuid(SYS_ID_AA64MMFR2_EL1);
+ case SYS_ID_AA64ISAR0_EL1: return read_cpuid(SYS_ID_AA64ISAR0_EL1);
+ case SYS_ID_AA64ISAR1_EL1: return read_cpuid(SYS_ID_AA64ISAR1_EL1);
+
+ case SYS_CNTFRQ_EL0: return read_cpuid(SYS_CNTFRQ_EL0);
+ case SYS_CTR_EL0: return read_cpuid(SYS_CTR_EL0);
+ case SYS_DCZID_EL0: return read_cpuid(SYS_DCZID_EL0);
+ default:
+ BUG();
+ return 0;
}
}
-void check_local_cpu_features(void)
+/*
+ * Park the CPU which doesn't have the capability as advertised
+ * by the system.
+ */
+static void fail_incapable_cpu(char *cap_type,
+ const struct arm64_cpu_capabilities *cap)
{
- check_cpu_capabilities(arm64_features, "detected feature");
+ int cpu = smp_processor_id();
+
+ pr_crit("CPU%d: missing %s : %s\n", cpu, cap_type, cap->desc);
+ /* Mark this CPU absent */
+ set_cpu_present(cpu, 0);
+
+ /* Check if we can park ourselves */
+ if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_die)
+ cpu_ops[cpu]->cpu_die(cpu);
+ asm(
+ "1: wfe\n"
+ " wfi\n"
+ " b 1b");
+}
+
+/*
+ * Run through the enabled system capabilities and enable() it on this CPU.
+ * The capabilities were decided based on the available CPUs at the boot time.
+ * Any new CPU should match the system wide status of the capability. If the
+ * new CPU doesn't have a capability which the system now has enabled, we
+ * cannot do anything to fix it up and could cause unexpected failures. So
+ * we park the CPU.
+ */
+void verify_local_cpu_capabilities(void)
+{
+ int i;
+ const struct arm64_cpu_capabilities *caps;
+
+ /*
+ * If we haven't computed the system capabilities, there is nothing
+ * to verify.
+ */
+ if (!sys_caps_initialised)
+ return;
+
+ caps = arm64_features;
+ for (i = 0; caps[i].matches; i++) {
+ if (!cpus_have_cap(caps[i].capability) || !caps[i].sys_reg)
+ continue;
+ /*
+ * If the new CPU misses an advertised feature, we cannot proceed
+ * further, park the cpu.
+ */
+ if (!feature_matches(__raw_read_system_reg(caps[i].sys_reg), &caps[i]))
+ fail_incapable_cpu("arm64_features", &caps[i]);
+ if (caps[i].enable)
+ caps[i].enable(NULL);
+ }
+
+ for (i = 0, caps = arm64_hwcaps; caps[i].matches; i++) {
+ if (!cpus_have_hwcap(&caps[i]))
+ continue;
+ if (!feature_matches(__raw_read_system_reg(caps[i].sys_reg), &caps[i]))
+ fail_incapable_cpu("arm64_hwcaps", &caps[i]);
+ }
+}
+
+#else /* !CONFIG_HOTPLUG_CPU */
+
+static inline void set_sys_caps_initialised(void)
+{
+}
+
+#endif /* CONFIG_HOTPLUG_CPU */
+
+static void __init setup_feature_capabilities(void)
+{
+ update_cpu_capabilities(arm64_features, "detected feature:");
+ enable_cpu_capabilities(arm64_features);
+}
+
+void __init setup_cpu_features(void)
+{
+ u32 cwg;
+ int cls;
+
+ /* Set the CPU feature capabilies */
+ setup_feature_capabilities();
+ setup_cpu_hwcaps();
+
+ /* Advertise that we have computed the system capabilities */
+ set_sys_caps_initialised();
+
+ /*
+ * Check for sane CTR_EL0.CWG value.
+ */
+ cwg = cache_type_cwg();
+ cls = cache_line_size();
+ if (!cwg)
+ pr_warn("No Cache Writeback Granule information, assuming cache line size %d\n",
+ cls);
+ if (L1_CACHE_BYTES < cls)
+ pr_warn("L1_CACHE_BYTES smaller than the Cache Writeback Granule (%d < %d)\n",
+ L1_CACHE_BYTES, cls);
+}
+
+static bool __maybe_unused
+cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry)
+{
+ return (cpus_have_cap(ARM64_HAS_PAN) && !cpus_have_cap(ARM64_HAS_UAO));
}
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index e0c7ec5..81fe6cf 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -24,8 +24,11 @@
#include <linux/bug.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/personality.h>
#include <linux/preempt.h>
#include <linux/printk.h>
+#include <linux/seq_file.h>
+#include <linux/sched.h>
#include <linux/smp.h>
/*
@@ -35,7 +38,6 @@
*/
DEFINE_PER_CPU(struct cpuinfo_arm64, cpu_data);
static struct cpuinfo_arm64 boot_cpu_data;
-static bool mixed_endian_el0 = true;
static char *icache_policy_str[] = {
[ICACHE_POLICY_RESERVED] = "RESERVED/UNKNOWN",
@@ -46,6 +48,128 @@
unsigned long __icache_flags;
+static const char *hwcap_str[] = {
+ "fp",
+ "asimd",
+ "evtstrm",
+ "aes",
+ "pmull",
+ "sha1",
+ "sha2",
+ "crc32",
+ "atomics",
+ NULL
+};
+
+#ifdef CONFIG_COMPAT
+static const char *compat_hwcap_str[] = {
+ "swp",
+ "half",
+ "thumb",
+ "26bit",
+ "fastmult",
+ "fpa",
+ "vfp",
+ "edsp",
+ "java",
+ "iwmmxt",
+ "crunch",
+ "thumbee",
+ "neon",
+ "vfpv3",
+ "vfpv3d16",
+ "tls",
+ "vfpv4",
+ "idiva",
+ "idivt",
+ "vfpd32",
+ "lpae",
+ "evtstrm",
+ NULL
+};
+
+static const char *compat_hwcap2_str[] = {
+ "aes",
+ "pmull",
+ "sha1",
+ "sha2",
+ "crc32",
+ NULL
+};
+#endif /* CONFIG_COMPAT */
+
+static int c_show(struct seq_file *m, void *v)
+{
+ int i, j;
+
+ for_each_online_cpu(i) {
+ struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
+ u32 midr = cpuinfo->reg_midr;
+
+ /*
+ * glibc reads /proc/cpuinfo to determine the number of
+ * online processors, looking for lines beginning with
+ * "processor". Give glibc what it expects.
+ */
+ seq_printf(m, "processor\t: %d\n", i);
+
+ /*
+ * Dump out the common processor features in a single line.
+ * Userspace should read the hwcaps with getauxval(AT_HWCAP)
+ * rather than attempting to parse this, but there's a body of
+ * software which does already (at least for 32-bit).
+ */
+ seq_puts(m, "Features\t:");
+ if (personality(current->personality) == PER_LINUX32) {
+#ifdef CONFIG_COMPAT
+ for (j = 0; compat_hwcap_str[j]; j++)
+ if (compat_elf_hwcap & (1 << j))
+ seq_printf(m, " %s", compat_hwcap_str[j]);
+
+ for (j = 0; compat_hwcap2_str[j]; j++)
+ if (compat_elf_hwcap2 & (1 << j))
+ seq_printf(m, " %s", compat_hwcap2_str[j]);
+#endif /* CONFIG_COMPAT */
+ } else {
+ for (j = 0; hwcap_str[j]; j++)
+ if (elf_hwcap & (1 << j))
+ seq_printf(m, " %s", hwcap_str[j]);
+ }
+ seq_puts(m, "\n");
+
+ seq_printf(m, "CPU implementer\t: 0x%02x\n",
+ MIDR_IMPLEMENTOR(midr));
+ seq_printf(m, "CPU architecture: 8\n");
+ seq_printf(m, "CPU variant\t: 0x%x\n", MIDR_VARIANT(midr));
+ seq_printf(m, "CPU part\t: 0x%03x\n", MIDR_PARTNUM(midr));
+ seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
+ }
+
+ return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return *pos < 1 ? (void *)1 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return NULL;
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+const struct seq_operations cpuinfo_op = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = c_show
+};
+
static void cpuinfo_detect_icache_policy(struct cpuinfo_arm64 *info)
{
unsigned int cpu = smp_processor_id();
@@ -69,161 +193,56 @@
pr_debug("Detected %s I-cache on CPU%d\n", icache_policy_str[l1ip], cpu);
}
-bool cpu_supports_mixed_endian_el0(void)
-{
- return id_aa64mmfr0_mixed_endian_el0(read_cpuid(ID_AA64MMFR0_EL1));
-}
-
-bool system_supports_mixed_endian_el0(void)
-{
- return mixed_endian_el0;
-}
-
-static void update_mixed_endian_el0_support(struct cpuinfo_arm64 *info)
-{
- mixed_endian_el0 &= id_aa64mmfr0_mixed_endian_el0(info->reg_id_aa64mmfr0);
-}
-
-static void update_cpu_features(struct cpuinfo_arm64 *info)
-{
- update_mixed_endian_el0_support(info);
-}
-
-static int check_reg_mask(char *name, u64 mask, u64 boot, u64 cur, int cpu)
-{
- if ((boot & mask) == (cur & mask))
- return 0;
-
- pr_warn("SANITY CHECK: Unexpected variation in %s. Boot CPU: %#016lx, CPU%d: %#016lx\n",
- name, (unsigned long)boot, cpu, (unsigned long)cur);
-
- return 1;
-}
-
-#define CHECK_MASK(field, mask, boot, cur, cpu) \
- check_reg_mask(#field, mask, (boot)->reg_ ## field, (cur)->reg_ ## field, cpu)
-
-#define CHECK(field, boot, cur, cpu) \
- CHECK_MASK(field, ~0ULL, boot, cur, cpu)
-
-/*
- * Verify that CPUs don't have unexpected differences that will cause problems.
- */
-static void cpuinfo_sanity_check(struct cpuinfo_arm64 *cur)
-{
- unsigned int cpu = smp_processor_id();
- struct cpuinfo_arm64 *boot = &boot_cpu_data;
- unsigned int diff = 0;
-
- /*
- * The kernel can handle differing I-cache policies, but otherwise
- * caches should look identical. Userspace JITs will make use of
- * *minLine.
- */
- diff |= CHECK_MASK(ctr, 0xffff3fff, boot, cur, cpu);
-
- /*
- * Userspace may perform DC ZVA instructions. Mismatched block sizes
- * could result in too much or too little memory being zeroed if a
- * process is preempted and migrated between CPUs.
- */
- diff |= CHECK(dczid, boot, cur, cpu);
-
- /* If different, timekeeping will be broken (especially with KVM) */
- diff |= CHECK(cntfrq, boot, cur, cpu);
-
- /*
- * Even in big.LITTLE, processors should be identical instruction-set
- * wise.
- */
- diff |= CHECK(id_aa64isar0, boot, cur, cpu);
- diff |= CHECK(id_aa64isar1, boot, cur, cpu);
-
- /*
- * Differing PARange support is fine as long as all peripherals and
- * memory are mapped within the minimum PARange of all CPUs.
- * Linux should not care about secure memory.
- * ID_AA64MMFR1 is currently RES0.
- */
- diff |= CHECK_MASK(id_aa64mmfr0, 0xffffffffffff0ff0, boot, cur, cpu);
- diff |= CHECK(id_aa64mmfr1, boot, cur, cpu);
-
- /*
- * EL3 is not our concern.
- * ID_AA64PFR1 is currently RES0.
- */
- diff |= CHECK_MASK(id_aa64pfr0, 0xffffffffffff0fff, boot, cur, cpu);
- diff |= CHECK(id_aa64pfr1, boot, cur, cpu);
-
- /*
- * If we have AArch32, we care about 32-bit features for compat. These
- * registers should be RES0 otherwise.
- */
- diff |= CHECK(id_isar0, boot, cur, cpu);
- diff |= CHECK(id_isar1, boot, cur, cpu);
- diff |= CHECK(id_isar2, boot, cur, cpu);
- diff |= CHECK(id_isar3, boot, cur, cpu);
- diff |= CHECK(id_isar4, boot, cur, cpu);
- diff |= CHECK(id_isar5, boot, cur, cpu);
- diff |= CHECK(id_mmfr0, boot, cur, cpu);
- diff |= CHECK(id_mmfr1, boot, cur, cpu);
- diff |= CHECK(id_mmfr2, boot, cur, cpu);
- diff |= CHECK(id_mmfr3, boot, cur, cpu);
- diff |= CHECK(id_pfr0, boot, cur, cpu);
- diff |= CHECK(id_pfr1, boot, cur, cpu);
-
- /*
- * Mismatched CPU features are a recipe for disaster. Don't even
- * pretend to support them.
- */
- WARN_TAINT_ONCE(diff, TAINT_CPU_OUT_OF_SPEC,
- "Unsupported CPU feature variation.");
-}
-
static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
{
info->reg_cntfrq = arch_timer_get_cntfrq();
info->reg_ctr = read_cpuid_cachetype();
- info->reg_dczid = read_cpuid(DCZID_EL0);
+ info->reg_dczid = read_cpuid(SYS_DCZID_EL0);
info->reg_midr = read_cpuid_id();
- info->reg_id_aa64isar0 = read_cpuid(ID_AA64ISAR0_EL1);
- info->reg_id_aa64isar1 = read_cpuid(ID_AA64ISAR1_EL1);
+ info->reg_id_aa64dfr0 = read_cpuid(SYS_ID_AA64DFR0_EL1);
+ info->reg_id_aa64dfr1 = read_cpuid(SYS_ID_AA64DFR1_EL1);
+ info->reg_id_aa64isar0 = read_cpuid(SYS_ID_AA64ISAR0_EL1);
+ info->reg_id_aa64isar1 = read_cpuid(SYS_ID_AA64ISAR1_EL1);
/*
* Explicitly mask out 16KB granule since we donot
* want to support it
*/
- info->reg_id_aa64mmfr0 = read_cpuid(ID_AA64MMFR0_EL1) &
+ info->reg_id_aa64mmfr0 = read_cpuid(SYS_ID_AA64MMFR0_EL1) &
(~MMFR0_EL1_16KGRAN_MASK);
- info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1);
- info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1);
- info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1);
+ info->reg_id_aa64mmfr1 = read_cpuid(SYS_ID_AA64MMFR1_EL1);
+ info->reg_id_aa64mmfr2 = read_cpuid(SYS_ID_AA64MMFR2_EL1);
+ info->reg_id_aa64pfr0 = read_cpuid(SYS_ID_AA64PFR0_EL1);
+ info->reg_id_aa64pfr1 = read_cpuid(SYS_ID_AA64PFR1_EL1);
- info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1);
- info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1);
- info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1);
- info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1);
- info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1);
- info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1);
- info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1);
- info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1);
- info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1);
- info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1);
- info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1);
- info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1);
+ info->reg_id_dfr0 = read_cpuid(SYS_ID_DFR0_EL1);
+ info->reg_id_isar0 = read_cpuid(SYS_ID_ISAR0_EL1);
+ info->reg_id_isar1 = read_cpuid(SYS_ID_ISAR1_EL1);
+ info->reg_id_isar2 = read_cpuid(SYS_ID_ISAR2_EL1);
+ info->reg_id_isar3 = read_cpuid(SYS_ID_ISAR3_EL1);
+ info->reg_id_isar4 = read_cpuid(SYS_ID_ISAR4_EL1);
+ info->reg_id_isar5 = read_cpuid(SYS_ID_ISAR5_EL1);
+ info->reg_id_mmfr0 = read_cpuid(SYS_ID_MMFR0_EL1);
+ info->reg_id_mmfr1 = read_cpuid(SYS_ID_MMFR1_EL1);
+ info->reg_id_mmfr2 = read_cpuid(SYS_ID_MMFR2_EL1);
+ info->reg_id_mmfr3 = read_cpuid(SYS_ID_MMFR3_EL1);
+ info->reg_id_pfr0 = read_cpuid(SYS_ID_PFR0_EL1);
+ info->reg_id_pfr1 = read_cpuid(SYS_ID_PFR1_EL1);
+
+ info->reg_mvfr0 = read_cpuid(SYS_MVFR0_EL1);
+ info->reg_mvfr1 = read_cpuid(SYS_MVFR1_EL1);
+ info->reg_mvfr2 = read_cpuid(SYS_MVFR2_EL1);
cpuinfo_detect_icache_policy(info);
check_local_cpu_errata();
- check_local_cpu_features();
- update_cpu_features(info);
}
void cpuinfo_store_cpu(void)
{
struct cpuinfo_arm64 *info = this_cpu_ptr(&cpu_data);
__cpuinfo_store_cpu(info);
- cpuinfo_sanity_check(info);
+ update_cpu_features(smp_processor_id(), info, &boot_cpu_data);
}
void __init cpuinfo_store_boot_cpu(void)
@@ -232,6 +251,7 @@
__cpuinfo_store_cpu(info);
boot_cpu_data = *info;
+ init_cpu_features(&boot_cpu_data);
}
u64 __attribute_const__ icache_get_ccsidr(void)
diff --git a/arch/arm64/kernel/cputable.c b/arch/arm64/kernel/cputable.c
deleted file mode 100644
index fd3993c..0000000
--- a/arch/arm64/kernel/cputable.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * arch/arm64/kernel/cputable.c
- *
- * Copyright (C) 2012 ARM Ltd.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <linux/init.h>
-
-#include <asm/cputable.h>
-
-extern unsigned long __cpu_setup(void);
-
-struct cpu_info cpu_table[] = {
- {
- .cpu_id_val = 0x000f0000,
- .cpu_id_mask = 0x000f0000,
- .cpu_name = "AArch64 Processor",
- .cpu_setup = __cpu_setup,
- },
- { /* Empty */ },
-};
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index 3d1ce76..d35057c 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -26,14 +26,16 @@
#include <linux/stat.h>
#include <linux/uaccess.h>
-#include <asm/debug-monitors.h>
+#include <asm/cpufeature.h>
#include <asm/cputype.h>
+#include <asm/debug-monitors.h>
#include <asm/system_misc.h>
/* Determine debug architecture. */
u8 debug_monitors_arch(void)
{
- return read_cpuid(ID_AA64DFR0_EL1) & 0xf;
+ return cpuid_feature_extract_field(read_system_reg(SYS_ID_AA64DFR0_EL1),
+ ID_AA64DFR0_DEBUGVER_SHIFT);
}
/*
@@ -150,7 +152,6 @@
/* Clear the OS lock. */
on_each_cpu(clear_os_lock, NULL, 1);
isb();
- local_dbg_enable();
/* Register hotplug handler. */
__register_cpu_notifier(&os_lock_nb);
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index a98415b..348c44b 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -28,6 +28,7 @@
#include <linux/spinlock.h>
#include <asm/cacheflush.h>
+#include <asm/cpufeature.h>
#include <asm/efi.h>
#include <asm/tlbflush.h>
#include <asm/mmu_context.h>
@@ -38,6 +39,18 @@
static u64 efi_system_table;
+static pgd_t efi_pgd[PTRS_PER_PGD] __page_aligned_bss;
+
+static struct mm_struct efi_mm = {
+ .mm_rb = RB_ROOT,
+ .pgd = efi_pgd,
+ .mm_users = ATOMIC_INIT(2),
+ .mm_count = ATOMIC_INIT(1),
+ .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
+ .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
+ .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
+};
+
static int uefi_debug __initdata;
static int __init uefi_debug_setup(char *str)
{
@@ -213,6 +226,48 @@
return;
reserve_regions();
+ early_memunmap(memmap.map, params.mmap_size);
+}
+
+static bool __init efi_virtmap_init(void)
+{
+ efi_memory_desc_t *md;
+
+ init_new_context(NULL, &efi_mm);
+
+ for_each_efi_memory_desc(&memmap, md) {
+ u64 paddr, npages, size;
+ pgprot_t prot;
+
+ if (!(md->attribute & EFI_MEMORY_RUNTIME))
+ continue;
+ if (md->virt_addr == 0)
+ return false;
+
+ paddr = md->phys_addr;
+ npages = md->num_pages;
+ memrange_efi_to_native(&paddr, &npages);
+ size = npages << PAGE_SHIFT;
+
+ pr_info(" EFI remap 0x%016llx => %p\n",
+ md->phys_addr, (void *)md->virt_addr);
+
+ /*
+ * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
+ * executable, everything else can be mapped with the XN bits
+ * set.
+ */
+ if (!is_normal_ram(md))
+ prot = __pgprot(PROT_DEVICE_nGnRE);
+ else if (md->type == EFI_RUNTIME_SERVICES_CODE)
+ prot = PAGE_KERNEL_EXEC;
+ else
+ prot = PAGE_KERNEL;
+
+ create_pgd_mapping(&efi_mm, paddr, md->virt_addr, size,
+ __pgprot(pgprot_val(prot) | PTE_NG));
+ }
+ return true;
}
/*
@@ -229,19 +284,21 @@
return -1;
}
- mapsize = memmap.map_end - memmap.map;
-
if (efi_runtime_disabled()) {
pr_info("EFI runtime services will be disabled.\n");
return -1;
}
pr_info("Remapping and enabling EFI services.\n");
- /* replace early memmap mapping with permanent mapping */
+
+ mapsize = memmap.map_end - memmap.map;
memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map,
mapsize);
+ if (!memmap.map) {
+ pr_err("Failed to remap EFI memory map\n");
+ return -1;
+ }
memmap.map_end = memmap.map + mapsize;
-
efi.memmap = &memmap;
efi.systab = (__force void *)ioremap_cache(efi_system_table,
@@ -252,7 +309,7 @@
}
set_bit(EFI_SYSTEM_TABLES, &efi.flags);
- if (!efi_enabled(EFI_VIRTMAP)) {
+ if (!efi_virtmap_init()) {
pr_err("No UEFI virtual mapping was installed -- runtime services will not be available\n");
return -1;
}
@@ -281,25 +338,32 @@
}
core_initcall(arm64_dmi_init);
-static pgd_t efi_pgd[PTRS_PER_PGD] __page_aligned_bss;
-
-static struct mm_struct efi_mm = {
- .mm_rb = RB_ROOT,
- .pgd = efi_pgd,
- .mm_users = ATOMIC_INIT(2),
- .mm_count = ATOMIC_INIT(1),
- .mmap_sem = __RWSEM_INITIALIZER(efi_mm.mmap_sem),
- .page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
- .mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
- INIT_MM_CONTEXT(efi_mm)
-};
-
static void efi_set_pgd(struct mm_struct *mm)
{
- cpu_switch_mm(mm->pgd, mm);
- flush_tlb_all();
- if (icache_is_aivivt())
- __flush_icache_all();
+ __switch_mm(mm);
+
+ if (system_uses_ttbr0_pan()) {
+ if (mm != current->active_mm) {
+ /*
+ * Update the current thread's saved ttbr0 since it is
+ * restored as part of a return from exception. Set
+ * the hardware TTBR0_EL1 using cpu_switch_mm()
+ * directly to enable potential errata workarounds.
+ */
+ update_saved_ttbr0(current, mm);
+ cpu_switch_mm(mm->pgd, mm);
+ } else {
+ /*
+ * Defer the switch to the current thread's TTBR0_EL1
+ * until uaccess_enable(). Restore the current
+ * thread's saved ttbr0 corresponding to its active_mm
+ * (if different from init_mm).
+ */
+ cpu_set_reserved_ttbr0();
+ if (current->active_mm != &init_mm)
+ update_saved_ttbr0(current, current->active_mm);
+ }
+ }
}
void efi_virtmap_load(void)
@@ -313,47 +377,3 @@
efi_set_pgd(current->active_mm);
preempt_enable();
}
-
-void __init efi_virtmap_init(void)
-{
- efi_memory_desc_t *md;
-
- if (!efi_enabled(EFI_BOOT))
- return;
-
- for_each_efi_memory_desc(&memmap, md) {
- u64 paddr, npages, size;
- pgprot_t prot;
-
- if (!(md->attribute & EFI_MEMORY_RUNTIME))
- continue;
- if (WARN(md->virt_addr == 0,
- "UEFI virtual mapping incomplete or missing -- no entry found for 0x%llx\n",
- md->phys_addr))
- return;
-
- paddr = md->phys_addr;
- npages = md->num_pages;
- memrange_efi_to_native(&paddr, &npages);
- size = npages << PAGE_SHIFT;
-
- pr_info(" EFI remap 0x%016llx => %p\n",
- md->phys_addr, (void *)md->virt_addr);
-
- /*
- * Only regions of type EFI_RUNTIME_SERVICES_CODE need to be
- * executable, everything else can be mapped with the XN bits
- * set.
- */
- if (!is_normal_ram(md))
- prot = __pgprot(PROT_DEVICE_nGnRE);
- else if (md->type == EFI_RUNTIME_SERVICES_CODE)
- prot = PAGE_KERNEL_EXEC;
- else
- prot = PAGE_KERNEL;
-
- create_pgd_mapping(&efi_mm, paddr, md->virt_addr, size, prot);
- }
- set_bit(EFI_VIRTMAP, &efi.flags);
- early_memunmap(memmap.map, memmap.map_end - memmap.map);
-}
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index a173cc1..94af8b7 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -27,7 +27,10 @@
#include <asm/cpufeature.h>
#include <asm/errno.h>
#include <asm/esr.h>
+#include <asm/memory.h>
+#include <asm/ptrace.h>
#include <asm/thread_info.h>
+#include <asm/uaccess.h>
#include <asm/unistd.h>
/*
@@ -66,25 +69,26 @@
#define BAD_ERROR 3
.macro kernel_entry, el, regsize = 64
- sub sp, sp, #S_FRAME_SIZE - S_LR // room for LR, SP, SPSR, ELR
+ sub sp, sp, #S_FRAME_SIZE
.if \regsize == 32
mov w0, w0 // zero upper 32 bits of x0
.endif
- push x28, x29
- push x26, x27
- push x24, x25
- push x22, x23
- push x20, x21
- push x18, x19
- push x16, x17
- push x14, x15
- push x12, x13
- push x10, x11
- push x8, x9
- push x6, x7
- push x4, x5
- push x2, x3
- push x0, x1
+ stp x0, x1, [sp, #16 * 0]
+ stp x2, x3, [sp, #16 * 1]
+ stp x4, x5, [sp, #16 * 2]
+ stp x6, x7, [sp, #16 * 3]
+ stp x8, x9, [sp, #16 * 4]
+ stp x10, x11, [sp, #16 * 5]
+ stp x12, x13, [sp, #16 * 6]
+ stp x14, x15, [sp, #16 * 7]
+ stp x16, x17, [sp, #16 * 8]
+ stp x18, x19, [sp, #16 * 9]
+ stp x20, x21, [sp, #16 * 10]
+ stp x22, x23, [sp, #16 * 11]
+ stp x24, x25, [sp, #16 * 12]
+ stp x26, x27, [sp, #16 * 13]
+ stp x28, x29, [sp, #16 * 14]
+
.if \el == 0
mrs x21, sp_el0
get_thread_info tsk // Ensure MDSCR_EL1.SS is clear,
@@ -92,10 +96,45 @@
disable_step_tsk x19, x20 // exceptions when scheduling.
.else
add x21, sp, #S_FRAME_SIZE
- .endif
+ get_thread_info tsk
+ /* Save the task's original addr_limit and set USER_DS (TASK_SIZE_64) */
+ ldr x20, [tsk, #TI_ADDR_LIMIT]
+ str x20, [sp, #S_ORIG_ADDR_LIMIT]
+ mov x20, #TASK_SIZE_64
+ str x20, [tsk, #TI_ADDR_LIMIT]
+ ALTERNATIVE(nop, SET_PSTATE_UAO(0), ARM64_HAS_UAO, CONFIG_ARM64_UAO)
+ .endif /* \el == 0 */
mrs x22, elr_el1
mrs x23, spsr_el1
stp lr, x21, [sp, #S_LR]
+
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ /*
+ * Set the TTBR0 PAN bit in SPSR. When the exception is taken from
+ * EL0, there is no need to check the state of TTBR0_EL1 since
+ * accesses are always enabled.
+ * Note that the meaning of this bit differs from the ARMv8.1 PAN
+ * feature as all TTBR0_EL1 accesses are disabled, not just those to
+ * user mappings.
+ */
+alternative_if_not ARM64_HAS_PAN
+ nop
+alternative_else
+ b 1f // skip TTBR0 PAN
+alternative_endif
+
+ .if \el != 0
+ mrs x21, ttbr0_el1
+ tst x21, #0xffff << 48 // Check for the reserved ASID
+ orr x23, x23, #PSR_PAN_BIT // Set the emulated PAN in the saved SPSR
+ b.eq 1f // TTBR0 access already disabled
+ and x23, x23, #~PSR_PAN_BIT // Clear the emulated PAN in the saved SPSR
+ .endif
+
+ uaccess_ttbr0_disable x21
+1:
+#endif
+
stp x22, x23, [sp, #S_PC]
/*
@@ -115,64 +154,89 @@
*/
.endm
- .macro kernel_exit, el, ret = 0
+ .macro kernel_exit, el
+ .if \el != 0
+ /* Restore the task's original addr_limit. */
+ ldr x20, [sp, #S_ORIG_ADDR_LIMIT]
+ str x20, [tsk, #TI_ADDR_LIMIT]
+
+ /* No need to restore UAO, it will be restored from SPSR_EL1 */
+ .endif
+
ldp x21, x22, [sp, #S_PC] // load ELR, SPSR
.if \el == 0
ct_user_enter
- ldr x23, [sp, #S_SP] // load return stack pointer
+ .endif
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ /*
+ * Restore access to TTBR0_EL1. If returning to EL0, no need for SPSR
+ * PAN bit checking.
+ */
+alternative_if_not ARM64_HAS_PAN
+ nop
+alternative_else
+ b 2f // skip TTBR0 PAN
+alternative_endif
+
+ .if \el != 0
+ tbnz x22, #_PSR_PAN_BIT, 1f // Skip re-enabling TTBR0 access if previously disabled
+ .endif
+
+ uaccess_ttbr0_enable x0
+
+1:
+ .if \el != 0
+ and x22, x22, #~PSR_PAN_BIT // ARMv8.0 CPUs do not understand this bit
+ .endif
+2:
+#endif
+
+ .if \el == 0
+ ldr x23, [sp, #S_SP] // load return stack pointer
+ msr sp_el0, x23
#ifdef CONFIG_ARM64_ERRATUM_845719
- alternative_insn \
- "nop", \
- "tbz x22, #4, 1f", \
- ARM64_WORKAROUND_845719
+alternative_if_not ARM64_WORKAROUND_845719
+ nop
+ nop
#ifdef CONFIG_PID_IN_CONTEXTIDR
- alternative_insn \
- "nop; nop", \
- "mrs x29, contextidr_el1; msr contextidr_el1, x29; 1:", \
- ARM64_WORKAROUND_845719
+ nop
+#endif
+alternative_else
+ tbz x22, #4, 1f
+#ifdef CONFIG_PID_IN_CONTEXTIDR
+ mrs x29, contextidr_el1
+ msr contextidr_el1, x29
#else
- alternative_insn \
- "nop", \
- "msr contextidr_el1, xzr; 1:", \
- ARM64_WORKAROUND_845719
+ msr contextidr_el1, xzr
#endif
+1:
+alternative_endif
#endif
.endif
- .if \ret
- ldr x1, [sp, #S_X1] // preserve x0 (syscall return)
- add sp, sp, S_X2
- .else
- pop x0, x1
- .endif
- pop x2, x3 // load the rest of the registers
- pop x4, x5
- pop x6, x7
- pop x8, x9
+
msr elr_el1, x21 // set up the return data
msr spsr_el1, x22
- .if \el == 0
- msr sp_el0, x23
- .endif
- pop x10, x11
- pop x12, x13
- pop x14, x15
- pop x16, x17
- pop x18, x19
- pop x20, x21
- pop x22, x23
- pop x24, x25
- pop x26, x27
- pop x28, x29
- ldr lr, [sp], #S_FRAME_SIZE - S_LR // load LR and restore SP
+ ldp x0, x1, [sp, #16 * 0]
+ ldp x2, x3, [sp, #16 * 1]
+ ldp x4, x5, [sp, #16 * 2]
+ ldp x6, x7, [sp, #16 * 3]
+ ldp x8, x9, [sp, #16 * 4]
+ ldp x10, x11, [sp, #16 * 5]
+ ldp x12, x13, [sp, #16 * 6]
+ ldp x14, x15, [sp, #16 * 7]
+ ldp x16, x17, [sp, #16 * 8]
+ ldp x18, x19, [sp, #16 * 9]
+ ldp x20, x21, [sp, #16 * 10]
+ ldp x22, x23, [sp, #16 * 11]
+ ldp x24, x25, [sp, #16 * 12]
+ ldp x26, x27, [sp, #16 * 13]
+ ldp x28, x29, [sp, #16 * 14]
+ ldr lr, [sp, #S_LR]
+ add sp, sp, #S_FRAME_SIZE // restore sp
eret // return to kernel
.endm
- .macro get_thread_info, rd
- mov \rd, sp
- and \rd, \rd, #~(THREAD_SIZE - 1) // top of stack
- .endm
-
/*
* These are the registers used in the syscall handler, and allow us to
* have in theory up to 7 arguments to a function - x0 to x6.
@@ -234,7 +298,7 @@
* Invalid mode handlers
*/
.macro inv_entry, el, reason, regsize = 64
- kernel_entry el, \regsize
+ kernel_entry \el, \regsize
mov x0, sp
mov x1, #\reason
mrs x2, esr_el1
@@ -290,20 +354,27 @@
el1_sync:
kernel_entry 1
mrs x1, esr_el1 // read the syndrome register
- lsr x24, x1, #ESR_EL1_EC_SHIFT // exception class
- cmp x24, #ESR_EL1_EC_DABT_EL1 // data abort in EL1
+ lsr x24, x1, #ESR_ELx_EC_SHIFT // exception class
+ cmp x24, #ESR_ELx_EC_DABT_CUR // data abort in EL1
b.eq el1_da
- cmp x24, #ESR_EL1_EC_SYS64 // configurable trap
+ cmp x24, #ESR_ELx_EC_IABT_CUR // instruction abort in EL1
+ b.eq el1_ia
+ cmp x24, #ESR_ELx_EC_SYS64 // configurable trap
b.eq el1_undef
- cmp x24, #ESR_EL1_EC_SP_ALIGN // stack alignment exception
+ cmp x24, #ESR_ELx_EC_SP_ALIGN // stack alignment exception
b.eq el1_sp_pc
- cmp x24, #ESR_EL1_EC_PC_ALIGN // pc alignment exception
+ cmp x24, #ESR_ELx_EC_PC_ALIGN // pc alignment exception
b.eq el1_sp_pc
- cmp x24, #ESR_EL1_EC_UNKNOWN // unknown exception in EL1
+ cmp x24, #ESR_ELx_EC_UNKNOWN // unknown exception in EL1
b.eq el1_undef
- cmp x24, #ESR_EL1_EC_BREAKPT_EL1 // debug exception in EL1
+ cmp x24, #ESR_ELx_EC_BREAKPT_CUR // debug exception in EL1
b.ge el1_dbg
b el1_inv
+
+el1_ia:
+ /*
+ * Fall through to the Data abort case
+ */
el1_da:
/*
* Data abort handling
@@ -339,7 +410,7 @@
/*
* Debug exception handling
*/
- cmp x24, #ESR_EL1_EC_BRK64 // if BRK64
+ cmp x24, #ESR_ELx_EC_BRK64 // if BRK64
cinc x24, x24, eq // set bit '0'
tbz x24, #0, el1_inv // EL1 only
mrs x0, far_el1
@@ -396,26 +467,26 @@
el0_sync:
kernel_entry 0
mrs x25, esr_el1 // read the syndrome register
- lsr x24, x25, #ESR_EL1_EC_SHIFT // exception class
- cmp x24, #ESR_EL1_EC_SVC64 // SVC in 64-bit state
+ lsr x24, x25, #ESR_ELx_EC_SHIFT // exception class
+ cmp x24, #ESR_ELx_EC_SVC64 // SVC in 64-bit state
b.eq el0_svc
- cmp x24, #ESR_EL1_EC_DABT_EL0 // data abort in EL0
+ cmp x24, #ESR_ELx_EC_DABT_LOW // data abort in EL0
b.eq el0_da
- cmp x24, #ESR_EL1_EC_IABT_EL0 // instruction abort in EL0
+ cmp x24, #ESR_ELx_EC_IABT_LOW // instruction abort in EL0
b.eq el0_ia
- cmp x24, #ESR_EL1_EC_FP_ASIMD // FP/ASIMD access
+ cmp x24, #ESR_ELx_EC_FP_ASIMD // FP/ASIMD access
b.eq el0_fpsimd_acc
- cmp x24, #ESR_EL1_EC_FP_EXC64 // FP/ASIMD exception
+ cmp x24, #ESR_ELx_EC_FP_EXC64 // FP/ASIMD exception
b.eq el0_fpsimd_exc
- cmp x24, #ESR_EL1_EC_SYS64 // configurable trap
+ cmp x24, #ESR_ELx_EC_SYS64 // configurable trap
b.eq el0_undef
- cmp x24, #ESR_EL1_EC_SP_ALIGN // stack alignment exception
+ cmp x24, #ESR_ELx_EC_SP_ALIGN // stack alignment exception
b.eq el0_sp_pc
- cmp x24, #ESR_EL1_EC_PC_ALIGN // pc alignment exception
+ cmp x24, #ESR_ELx_EC_PC_ALIGN // pc alignment exception
b.eq el0_sp_pc
- cmp x24, #ESR_EL1_EC_UNKNOWN // unknown exception in EL0
+ cmp x24, #ESR_ELx_EC_UNKNOWN // unknown exception in EL0
b.eq el0_undef
- cmp x24, #ESR_EL1_EC_BREAKPT_EL0 // debug exception in EL0
+ cmp x24, #ESR_ELx_EC_BREAKPT_LOW // debug exception in EL0
b.ge el0_dbg
b el0_inv
@@ -424,30 +495,30 @@
el0_sync_compat:
kernel_entry 0, 32
mrs x25, esr_el1 // read the syndrome register
- lsr x24, x25, #ESR_EL1_EC_SHIFT // exception class
- cmp x24, #ESR_EL1_EC_SVC32 // SVC in 32-bit state
+ lsr x24, x25, #ESR_ELx_EC_SHIFT // exception class
+ cmp x24, #ESR_ELx_EC_SVC32 // SVC in 32-bit state
b.eq el0_svc_compat
- cmp x24, #ESR_EL1_EC_DABT_EL0 // data abort in EL0
+ cmp x24, #ESR_ELx_EC_DABT_LOW // data abort in EL0
b.eq el0_da
- cmp x24, #ESR_EL1_EC_IABT_EL0 // instruction abort in EL0
+ cmp x24, #ESR_ELx_EC_IABT_LOW // instruction abort in EL0
b.eq el0_ia
cmp x24, #ESR_EL1_EC_FP_ASIMD // FP/ASIMD access
b.eq el0_fpsimd_acc_compat
cmp x24, #ESR_EL1_EC_FP_EXC32 // FP/ASIMD exception
b.eq el0_fpsimd_exc
- cmp x24, #ESR_EL1_EC_UNKNOWN // unknown exception in EL0
+ cmp x24, #ESR_ELx_EC_UNKNOWN // unknown exception in EL0
b.eq el0_undef
- cmp x24, #ESR_EL1_EC_CP15_32 // CP15 MRC/MCR trap
+ cmp x24, #ESR_ELx_EC_CP15_32 // CP15 MRC/MCR trap
b.eq el0_undef
- cmp x24, #ESR_EL1_EC_CP15_64 // CP15 MRRC/MCRR trap
+ cmp x24, #ESR_ELx_EC_CP15_64 // CP15 MRRC/MCRR trap
b.eq el0_undef
- cmp x24, #ESR_EL1_EC_CP14_MR // CP14 MRC/MCR trap
+ cmp x24, #ESR_ELx_EC_CP14_MR // CP14 MRC/MCR trap
b.eq el0_undef
- cmp x24, #ESR_EL1_EC_CP14_LS // CP14 LDC/STC trap
+ cmp x24, #ESR_ELx_EC_CP14_LS // CP14 LDC/STC trap
b.eq el0_undef
- cmp x24, #ESR_EL1_EC_CP14_64 // CP14 MRRC/MCRR trap
+ cmp x24, #ESR_ELx_EC_CP14_64 // CP14 MRRC/MCRR trap
b.eq el0_undef
- cmp x24, #ESR_EL1_EC_BREAKPT_EL0 // debug exception in EL0
+ cmp x24, #ESR_ELx_EC_BREAKPT_LOW // debug exception in EL0
b.ge el0_dbg
b el0_inv
el0_svc_compat:
@@ -487,7 +558,7 @@
enable_dbg_and_irq
ct_user_exit
mov x0, x26
- orr x1, x25, #1 << 24 // use reserved ISS bit for instruction aborts
+ mov x1, x25
mov x2, sp
bl do_mem_abort
b ret_to_user
@@ -563,7 +634,7 @@
mov x0, sp
mov x1, #BAD_SYNC
mrs x2, esr_el1
- bl bad_mode
+ bl bad_el0_sync
b ret_to_user
ENDPROC(el0_sync)
@@ -648,17 +719,21 @@
*/
ret_fast_syscall:
disable_irq // disable interrupts
- ldr x1, [tsk, #TI_FLAGS]
+ str x0, [sp, #S_X0] // returned x0
+ ldr x1, [tsk, #TI_FLAGS] // re-check for syscall tracing
+ and x2, x1, #_TIF_SYSCALL_WORK
+ cbnz x2, ret_fast_syscall_trace
and x2, x1, #_TIF_WORK_MASK
- cbnz x2, fast_work_pending
+ cbnz x2, work_pending
enable_step_tsk x1, x2
- kernel_exit 0, ret = 1
+ kernel_exit 0
+ret_fast_syscall_trace:
+ enable_irq // enable interrupts
+ b __sys_trace_return_skipped // we already saved x0
/*
* Ok, we need to do extra processing, enter the slow path.
*/
-fast_work_pending:
- str x0, [sp, #S_X0] // returned x0
work_pending:
tbnz x1, #TIF_NEED_RESCHED, work_resched
/* TIF_SIGPENDING, TIF_NOTIFY_RESUME or TIF_FOREIGN_FPSTATE case */
@@ -682,7 +757,7 @@
cbnz x2, work_pending
enable_step_tsk x1, x2
no_work_pending:
- kernel_exit 0, ret = 0
+ kernel_exit 0
ENDPROC(ret_to_user)
/*
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index e220775..790d27e 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -203,6 +203,7 @@
void fpsimd_flush_thread(void)
{
memset(¤t->thread.fpsimd_state, 0, sizeof(struct fpsimd_state));
+ fpsimd_flush_task_state(current);
set_thread_flag(TIF_FOREIGN_FPSTATE);
}
@@ -333,7 +334,7 @@
.notifier_call = fpsimd_cpu_pm_notifier,
};
-static void fpsimd_pm_init(void)
+static void __init fpsimd_pm_init(void)
{
cpu_pm_register_notifier(&fpsimd_cpu_pm_notifier_block);
}
@@ -376,21 +377,15 @@
*/
static int __init fpsimd_init(void)
{
- u64 pfr = read_cpuid(ID_AA64PFR0_EL1);
-
- if (pfr & (0xf << 16)) {
+ if (elf_hwcap & HWCAP_FP) {
+ fpsimd_pm_init();
+ fpsimd_hotplug_init();
+ } else {
pr_notice("Floating-point is not implemented\n");
- return 0;
}
- elf_hwcap |= HWCAP_FP;
- if (pfr & (0xf << 20))
+ if (!(elf_hwcap & HWCAP_ASIMD))
pr_notice("Advanced SIMD is not implemented\n");
- else
- elf_hwcap |= HWCAP_ASIMD;
-
- fpsimd_pm_init();
- fpsimd_hotplug_init();
return 0;
}
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 04724cd..62254e4 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -29,6 +29,7 @@
#include <asm/asm-offsets.h>
#include <asm/cache.h>
#include <asm/cputype.h>
+#include <asm/kernel-pgtable.h>
#include <asm/memory.h>
#include <asm/thread_info.h>
#include <asm/pgtable-hwdef.h>
@@ -36,7 +37,7 @@
#include <asm/page.h>
#include <asm/virt.h>
-#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
+#define __PHYS_OFFSET (KERNEL_START - TEXT_OFFSET)
#if (TEXT_OFFSET & 0xfff) != 0
#error TEXT_OFFSET must be at least 4KB aligned
@@ -46,44 +47,10 @@
#error TEXT_OFFSET must be less than 2MB
#endif
- .macro pgtbl, ttb0, ttb1, virt_to_phys
- ldr \ttb1, =swapper_pg_dir
- ldr \ttb0, =idmap_pg_dir
- add \ttb1, \ttb1, \virt_to_phys
- add \ttb0, \ttb0, \virt_to_phys
- .endm
-
-#ifdef CONFIG_ARM64_64K_PAGES
-#define BLOCK_SHIFT PAGE_SHIFT
-#define BLOCK_SIZE PAGE_SIZE
-#define TABLE_SHIFT PMD_SHIFT
-#else
-#define BLOCK_SHIFT SECTION_SHIFT
-#define BLOCK_SIZE SECTION_SIZE
-#define TABLE_SHIFT PUD_SHIFT
-#endif
-
-#define KERNEL_START KERNEL_RAM_VADDR
+#define KERNEL_START _text
#define KERNEL_END _end
/*
- * Initial memory map attributes.
- */
-#ifndef CONFIG_SMP
-#define PTE_FLAGS PTE_TYPE_PAGE | PTE_AF
-#define PMD_FLAGS PMD_TYPE_SECT | PMD_SECT_AF
-#else
-#define PTE_FLAGS PTE_TYPE_PAGE | PTE_AF | PTE_SHARED
-#define PMD_FLAGS PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S
-#endif
-
-#ifdef CONFIG_ARM64_64K_PAGES
-#define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS
-#else
-#define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS
-#endif
-
-/*
* Kernel startup entry point.
* ---------------------------
*
@@ -240,40 +207,43 @@
#endif
ENTRY(stext)
- mov x21, x0 // x21=FDT
+ bl preserve_boot_args
bl el2_setup // Drop to EL1, w20=cpu_boot_mode
- bl __calc_phys_offset // x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET
+ adrp x24, __PHYS_OFFSET
bl set_cpu_boot_mode_flag
- mrs x22, midr_el1 // x22=cpuid
- mov x0, x22
- bl lookup_processor_type
- mov x23, x0 // x23=current cpu_table
- /*
- * __error_p may end up out of range for cbz if text areas are
- * aligned up to section sizes.
- */
- cbnz x23, 1f // invalid processor (x23=0)?
- b __error_p
-1:
+
bl __vet_fdt
bl __create_page_tables // x25=TTBR0, x26=TTBR1
/*
- * The following calls CPU specific code in a position independent
- * manner. See arch/arm64/mm/proc.S for details. x23 = base of
- * cpu_info structure selected by lookup_processor_type above.
+ * The following calls CPU setup code, see arch/arm64/mm/proc.S for
+ * details.
* On return, the CPU will be ready for the MMU to be turned on and
* the TCR will have been set.
*/
- ldr x27, __switch_data // address to jump to after
+ ldr x27, =__mmap_switched // address to jump to after
// MMU has been enabled
- adrp lr, __enable_mmu // return (PIC) address
- add lr, lr, #:lo12:__enable_mmu
- ldr x12, [x23, #CPU_INFO_SETUP]
- add x12, x12, x28 // __virt_to_phys
- br x12 // initialise processor
+ adr_l lr, __enable_mmu // return (PIC) address
+ b __cpu_setup // initialise processor
ENDPROC(stext)
/*
+ * Preserve the arguments passed by the bootloader in x0 .. x3
+ */
+preserve_boot_args:
+ mov x21, x0 // x21=FDT
+
+ adr_l x0, boot_args // record the contents of
+ stp x21, x1, [x0] // x0 .. x3 at kernel entry
+ stp x2, x3, [x0, #16]
+
+ dmb sy // needed before dc ivac with
+ // MMU off
+
+ add x1, x0, #0x20 // 4 x 8 bytes
+ b __inval_cache_range // tail call
+ENDPROC(preserve_boot_args)
+
+/*
* Determine validity of the x21 FDT pointer.
* The dtb must be 8-byte aligned and live in the first 512M of memory.
*/
@@ -322,7 +292,7 @@
.macro create_pgd_entry, tbl, virt, tmp1, tmp2
create_table_entry \tbl, \virt, PGDIR_SHIFT, PTRS_PER_PGD, \tmp1, \tmp2
#if SWAPPER_PGTABLE_LEVELS == 3
- create_table_entry \tbl, \virt, TABLE_SHIFT, PTRS_PER_PTE, \tmp1, \tmp2
+ create_table_entry \tbl, \virt, SWAPPER_TABLE_SHIFT, PTRS_PER_PTE, \tmp1, \tmp2
#endif
.endm
@@ -334,15 +304,15 @@
* Corrupts: phys, start, end, pstate
*/
.macro create_block_map, tbl, flags, phys, start, end
- lsr \phys, \phys, #BLOCK_SHIFT
- lsr \start, \start, #BLOCK_SHIFT
+ lsr \phys, \phys, #SWAPPER_BLOCK_SHIFT
+ lsr \start, \start, #SWAPPER_BLOCK_SHIFT
and \start, \start, #PTRS_PER_PTE - 1 // table index
- orr \phys, \flags, \phys, lsl #BLOCK_SHIFT // table entry
- lsr \end, \end, #BLOCK_SHIFT
+ orr \phys, \flags, \phys, lsl #SWAPPER_BLOCK_SHIFT // table entry
+ lsr \end, \end, #SWAPPER_BLOCK_SHIFT
and \end, \end, #PTRS_PER_PTE - 1 // table end index
9999: str \phys, [\tbl, \start, lsl #3] // store the entry
add \start, \start, #1 // next entry
- add \phys, \phys, #BLOCK_SIZE // next block
+ add \phys, \phys, #SWAPPER_BLOCK_SIZE // next block
cmp \start, \end
b.ls 9999b
.endm
@@ -356,7 +326,8 @@
* - pgd entry for fixed mappings (TTBR1)
*/
__create_page_tables:
- pgtbl x25, x26, x28 // idmap_pg_dir and swapper_pg_dir addresses
+ adrp x25, idmap_pg_dir
+ adrp x26, swapper_pg_dir
mov x27, lr
/*
@@ -364,14 +335,14 @@
* dirty cache lines being evicted.
*/
mov x0, x25
- add x1, x26, #SWAPPER_DIR_SIZE
+ add x1, x26, #SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE
bl __inval_cache_range
/*
* Clear the idmap and swapper page tables.
*/
mov x0, x25
- add x6, x26, #SWAPPER_DIR_SIZE
+ add x6, x26, #SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE
1: stp xzr, xzr, [x0], #16
stp xzr, xzr, [x0], #16
stp xzr, xzr, [x0], #16
@@ -379,18 +350,56 @@
cmp x0, x6
b.lo 1b
- ldr x7, =MM_MMUFLAGS
+ ldr x7, =SWAPPER_MM_MMUFLAGS
/*
* Create the identity mapping.
*/
mov x0, x25 // idmap_pg_dir
- ldr x3, =KERNEL_START
- add x3, x3, x28 // __pa(KERNEL_START)
+ adrp x3, KERNEL_START // __pa(KERNEL_START)
+
+#ifndef CONFIG_ARM64_VA_BITS_48
+#define EXTRA_SHIFT (PGDIR_SHIFT + PAGE_SHIFT - 3)
+#define EXTRA_PTRS (1 << (48 - EXTRA_SHIFT))
+
+ /*
+ * If VA_BITS < 48, it may be too small to allow for an ID mapping to be
+ * created that covers system RAM if that is located sufficiently high
+ * in the physical address space. So for the ID map, use an extended
+ * virtual range in that case, by configuring an additional translation
+ * level.
+ * First, we have to verify our assumption that the current value of
+ * VA_BITS was chosen such that all translation levels are fully
+ * utilised, and that lowering T0SZ will always result in an additional
+ * translation level to be configured.
+ */
+#if VA_BITS != EXTRA_SHIFT
+#error "Mismatch between VA_BITS and page size/number of translation levels"
+#endif
+
+ /*
+ * Calculate the maximum allowed value for TCR_EL1.T0SZ so that the
+ * entire kernel image can be ID mapped. As T0SZ == (64 - #bits used),
+ * this number conveniently equals the number of leading zeroes in
+ * the physical address of KERNEL_END.
+ */
+ adrp x5, KERNEL_END
+ clz x5, x5
+ cmp x5, TCR_T0SZ(VA_BITS) // default T0SZ small enough?
+ b.ge 1f // .. then skip additional level
+
+ adr_l x6, idmap_t0sz
+ str x5, [x6]
+ dmb sy
+ dc ivac, x6 // Invalidate potentially stale cache line
+
+ create_table_entry x0, x3, EXTRA_SHIFT, EXTRA_PTRS, x5, x6
+1:
+#endif
+
create_pgd_entry x0, x3, x5, x6
- ldr x6, =KERNEL_END
mov x5, x3 // __pa(KERNEL_START)
- add x6, x6, x28 // __pa(KERNEL_END)
+ adr_l x6, KERNEL_END // __pa(KERNEL_END)
create_block_map x0, x7, x3, x5, x6
/*
@@ -399,7 +408,7 @@
mov x0, x26 // swapper_pg_dir
mov x5, #PAGE_OFFSET
create_pgd_entry x0, x5, x3, x6
- ldr x6, =KERNEL_END
+ ldr x6, =KERNEL_END // __va(KERNEL_END)
mov x3, x24 // phys offset
create_block_map x0, x7, x3, x5, x6
@@ -425,7 +434,8 @@
* tables again to remove any speculatively loaded cache lines.
*/
mov x0, x25
- add x1, x26, #SWAPPER_DIR_SIZE
+ add x1, x26, #SWAPPER_DIR_SIZE + RESERVED_TTBR0_SIZE
+ dmb sy
bl __inval_cache_range
mov lr, x27
@@ -433,37 +443,22 @@
ENDPROC(__create_page_tables)
.ltorg
- .align 3
- .type __switch_data, %object
-__switch_data:
- .quad __mmap_switched
- .quad __bss_start // x6
- .quad __bss_stop // x7
- .quad processor_id // x4
- .quad __fdt_pointer // x5
- .quad memstart_addr // x6
- .quad init_thread_union + THREAD_START_SP // sp
-
/*
- * The following fragment of code is executed with the MMU on in MMU mode, and
- * uses absolute addresses; this is not position independent.
+ * The following fragment of code is executed with the MMU enabled.
*/
+ .set initial_sp, init_thread_union + THREAD_START_SP
__mmap_switched:
- adr x3, __switch_data + 8
+ // Clear BSS
+ adr_l x0, __bss_start
+ mov x1, xzr
+ adr_l x2, __bss_stop
+ sub x2, x2, x0
+ bl __pi_memset
+ dsb ishst // Make zero page visible to PTW
- ldp x6, x7, [x3], #16
-1: cmp x6, x7
- b.hs 2f
- str xzr, [x6], #8 // Clear BSS
- b 1b
-2:
- ldp x4, x5, [x3], #16
- ldr x6, [x3], #8
- ldr x16, [x3]
- mov sp, x16
- str x22, [x4] // Save processor ID
- str x21, [x5] // Save FDT pointer
- str x24, [x6] // Save PHYS_OFFSET
+ adr_l sp, initial_sp, x4
+ str_l x21, __fdt_pointer, x5 // Save FDT pointer
+ str_l x24, memstart_addr, x6 // Save PHYS_OFFSET
mov x29, #0
#ifdef CONFIG_KASAN
bl kasan_early_init
@@ -574,8 +569,7 @@
* in x20. See arch/arm64/include/asm/virt.h for more info.
*/
ENTRY(set_cpu_boot_mode_flag)
- ldr x1, =__boot_cpu_mode // Compute __boot_cpu_mode
- add x1, x1, x28
+ adr_l x1, __boot_cpu_mode
cmp w20, #BOOT_CPU_MODE_EL2
b.ne 1f
add x1, x1, #4
@@ -599,7 +593,6 @@
.long 0
.popsection
-#ifdef CONFIG_SMP
.align 3
1: .quad .
.quad secondary_holding_pen_release
@@ -610,15 +603,11 @@
*/
ENTRY(secondary_holding_pen)
bl el2_setup // Drop to EL1, w20=cpu_boot_mode
- bl __calc_phys_offset // x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET
bl set_cpu_boot_mode_flag
mrs x0, mpidr_el1
ldr x1, =MPIDR_HWID_BITMASK
and x0, x0, x1
- adr x1, 1b
- ldp x2, x3, [x1]
- sub x1, x1, x2
- add x3, x3, x1
+ adr_l x3, secondary_holding_pen_release
pen: ldr x4, [x3]
cmp x4, x0
b.eq secondary_startup
@@ -631,7 +620,6 @@
*/
ENTRY(secondary_entry)
bl el2_setup // Drop to EL1
- bl __calc_phys_offset // x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET
bl set_cpu_boot_mode_flag
b secondary_startup
ENDPROC(secondary_entry)
@@ -640,16 +628,9 @@
/*
* Common entry point for secondary CPUs.
*/
- mrs x22, midr_el1 // x22=cpuid
- mov x0, x22
- bl lookup_processor_type
- mov x23, x0 // x23=current cpu_table
- cbz x23, __error_p // invalid processor (x23=0)?
-
- pgtbl x25, x26, x28 // x25=TTBR0, x26=TTBR1
- ldr x12, [x23, #CPU_INFO_SETUP]
- add x12, x12, x28 // __virt_to_phys
- blr x12 // initialise processor
+ adrp x25, idmap_pg_dir
+ adrp x26, swapper_pg_dir
+ bl __cpu_setup // initialise processor
ldr x21, =secondary_data
ldr x27, =__secondary_switched // address to jump to after enabling the MMU
@@ -662,14 +643,14 @@
mov x29, #0
b secondary_start_kernel
ENDPROC(__secondary_switched)
-#endif /* CONFIG_SMP */
/*
- * Setup common bits before finally enabling the MMU. Essentially this is just
- * loading the page table pointer and vector base registers.
+ * Enable the MMU.
*
- * On entry to this code, x0 must contain the SCTLR_EL1 value for turning on
- * the MMU.
+ * x0 = SCTLR_EL1 value for turning on the MMU.
+ * x27 = *virtual* address to jump to upon completion
+ *
+ * other registers depend on the function called upon completion
*/
__enable_mmu:
ldr x5, =vectors
@@ -677,89 +658,15 @@
msr ttbr0_el1, x25 // load TTBR0
msr ttbr1_el1, x26 // load TTBR1
isb
- b __turn_mmu_on
-ENDPROC(__enable_mmu)
-
-/*
- * Enable the MMU. This completely changes the structure of the visible memory
- * space. You will not be able to trace execution through this.
- *
- * x0 = system control register
- * x27 = *virtual* address to jump to upon completion
- *
- * other registers depend on the function called upon completion
- *
- * We align the entire function to the smallest power of two larger than it to
- * ensure it fits within a single block map entry. Otherwise were PHYS_OFFSET
- * close to the end of a 512MB or 1GB block we might require an additional
- * table to map the entire function.
- */
- .align 4
-__turn_mmu_on:
msr sctlr_el1, x0
isb
+ /*
+ * Invalidate the local I-cache so that any instructions fetched
+ * speculatively from the PoC are discarded, since they may have
+ * been dynamically patched at the PoU.
+ */
+ ic iallu
+ dsb nsh
+ isb
br x27
-ENDPROC(__turn_mmu_on)
-
-/*
- * Calculate the start of physical memory.
- */
-__calc_phys_offset:
- adr x0, 1f
- ldp x1, x2, [x0]
- sub x28, x0, x1 // x28 = PHYS_OFFSET - PAGE_OFFSET
- add x24, x2, x28 // x24 = PHYS_OFFSET
- ret
-ENDPROC(__calc_phys_offset)
-
- .align 3
-1: .quad .
- .quad PAGE_OFFSET
-
-/*
- * Exception handling. Something went wrong and we can't proceed. We ought to
- * tell the user, but since we don't have any guarantee that we're even
- * running on the right architecture, we do virtually nothing.
- */
-__error_p:
-ENDPROC(__error_p)
-
-__error:
-1: nop
- b 1b
-ENDPROC(__error)
-
-/*
- * This function gets the processor ID in w0 and searches the cpu_table[] for
- * a match. It returns a pointer to the struct cpu_info it found. The
- * cpu_table[] must end with an empty (all zeros) structure.
- *
- * This routine can be called via C code and it needs to work with the MMU
- * both disabled and enabled (the offset is calculated automatically).
- */
-ENTRY(lookup_processor_type)
- adr x1, __lookup_processor_type_data
- ldp x2, x3, [x1]
- sub x1, x1, x2 // get offset between VA and PA
- add x3, x3, x1 // convert VA to PA
-1:
- ldp w5, w6, [x3] // load cpu_id_val and cpu_id_mask
- cbz w5, 2f // end of list?
- and w6, w6, w0
- cmp w5, w6
- b.eq 3f
- add x3, x3, #CPU_INFO_SZ
- b 1b
-2:
- mov x3, #0 // unknown processor
-3:
- mov x0, x3
- ret
-ENDPROC(lookup_processor_type)
-
- .align 3
- .type __lookup_processor_type_data, %object
-__lookup_processor_type_data:
- .quad .
- .quad cpu_table
- .size __lookup_processor_type_data, . - __lookup_processor_type_data
+ENDPROC(__enable_mmu)
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index db3b6fd..5519538 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -33,7 +33,9 @@
#include <asm/hw_breakpoint.h>
#include <asm/kdebug.h>
#include <asm/traps.h>
+#include <asm/cpufeature.h>
#include <asm/cputype.h>
+#include <asm/sysreg.h>
#include <asm/system_misc.h>
/* Breakpoint currently in use for each BRP. */
@@ -52,13 +54,17 @@
/* Determine number of BRP registers available. */
static int get_num_brps(void)
{
- return ((read_cpuid(ID_AA64DFR0_EL1) >> 12) & 0xf) + 1;
+ return 1 +
+ cpuid_feature_extract_field(read_system_reg(SYS_ID_AA64DFR0_EL1),
+ ID_AA64DFR0_BRPS_SHIFT);
}
/* Determine number of WRP registers available. */
static int get_num_wrps(void)
{
- return ((read_cpuid(ID_AA64DFR0_EL1) >> 20) & 0xf) + 1;
+ return 1 +
+ cpuid_feature_extract_field(read_system_reg(SYS_ID_AA64DFR0_EL1),
+ ID_AA64DFR0_WRPS_SHIFT);
}
int hw_breakpoint_slots(int type)
@@ -311,9 +317,21 @@
case ARM_BREAKPOINT_LEN_2:
len_in_bytes = 2;
break;
+ case ARM_BREAKPOINT_LEN_3:
+ len_in_bytes = 3;
+ break;
case ARM_BREAKPOINT_LEN_4:
len_in_bytes = 4;
break;
+ case ARM_BREAKPOINT_LEN_5:
+ len_in_bytes = 5;
+ break;
+ case ARM_BREAKPOINT_LEN_6:
+ len_in_bytes = 6;
+ break;
+ case ARM_BREAKPOINT_LEN_7:
+ len_in_bytes = 7;
+ break;
case ARM_BREAKPOINT_LEN_8:
len_in_bytes = 8;
break;
@@ -343,7 +361,7 @@
* to generic breakpoint descriptions.
*/
int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
- int *gen_len, int *gen_type)
+ int *gen_len, int *gen_type, int *offset)
{
/* Type */
switch (ctrl.type) {
@@ -363,17 +381,33 @@
return -EINVAL;
}
+ if (!ctrl.len)
+ return -EINVAL;
+ *offset = __ffs(ctrl.len);
+
/* Len */
- switch (ctrl.len) {
+ switch (ctrl.len >> *offset) {
case ARM_BREAKPOINT_LEN_1:
*gen_len = HW_BREAKPOINT_LEN_1;
break;
case ARM_BREAKPOINT_LEN_2:
*gen_len = HW_BREAKPOINT_LEN_2;
break;
+ case ARM_BREAKPOINT_LEN_3:
+ *gen_len = HW_BREAKPOINT_LEN_3;
+ break;
case ARM_BREAKPOINT_LEN_4:
*gen_len = HW_BREAKPOINT_LEN_4;
break;
+ case ARM_BREAKPOINT_LEN_5:
+ *gen_len = HW_BREAKPOINT_LEN_5;
+ break;
+ case ARM_BREAKPOINT_LEN_6:
+ *gen_len = HW_BREAKPOINT_LEN_6;
+ break;
+ case ARM_BREAKPOINT_LEN_7:
+ *gen_len = HW_BREAKPOINT_LEN_7;
+ break;
case ARM_BREAKPOINT_LEN_8:
*gen_len = HW_BREAKPOINT_LEN_8;
break;
@@ -417,9 +451,21 @@
case HW_BREAKPOINT_LEN_2:
info->ctrl.len = ARM_BREAKPOINT_LEN_2;
break;
+ case HW_BREAKPOINT_LEN_3:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_3;
+ break;
case HW_BREAKPOINT_LEN_4:
info->ctrl.len = ARM_BREAKPOINT_LEN_4;
break;
+ case HW_BREAKPOINT_LEN_5:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_5;
+ break;
+ case HW_BREAKPOINT_LEN_6:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_6;
+ break;
+ case HW_BREAKPOINT_LEN_7:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_7;
+ break;
case HW_BREAKPOINT_LEN_8:
info->ctrl.len = ARM_BREAKPOINT_LEN_8;
break;
@@ -511,18 +557,17 @@
default:
return -EINVAL;
}
-
- info->address &= ~alignment_mask;
- info->ctrl.len <<= offset;
} else {
if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE)
alignment_mask = 0x3;
else
alignment_mask = 0x7;
- if (info->address & alignment_mask)
- return -EINVAL;
+ offset = info->address & alignment_mask;
}
+ info->address &= ~alignment_mask;
+ info->ctrl.len <<= offset;
+
/*
* Disallow per-task kernel breakpoints since these would
* complicate the stepping code.
@@ -653,12 +698,47 @@
return 0;
}
+/*
+ * Arm64 hardware does not always report a watchpoint hit address that matches
+ * one of the watchpoints set. It can also report an address "near" the
+ * watchpoint if a single instruction access both watched and unwatched
+ * addresses. There is no straight-forward way, short of disassembling the
+ * offending instruction, to map that address back to the watchpoint. This
+ * function computes the distance of the memory access from the watchpoint as a
+ * heuristic for the likelyhood that a given access triggered the watchpoint.
+ *
+ * See Section D2.10.5 "Determining the memory location that caused a Watchpoint
+ * exception" of ARMv8 Architecture Reference Manual for details.
+ *
+ * The function returns the distance of the address from the bytes watched by
+ * the watchpoint. In case of an exact match, it returns 0.
+ */
+static u64 get_distance_from_watchpoint(unsigned long addr, u64 val,
+ struct arch_hw_breakpoint_ctrl *ctrl)
+{
+ u64 wp_low, wp_high;
+ u32 lens, lene;
+
+ lens = __ffs(ctrl->len);
+ lene = __fls(ctrl->len);
+
+ wp_low = val + lens;
+ wp_high = val + lene;
+ if (addr < wp_low)
+ return wp_low - addr;
+ else if (addr > wp_high)
+ return addr - wp_high;
+ else
+ return 0;
+}
+
static int watchpoint_handler(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
- int i, step = 0, *kernel_step, access;
+ int i, step = 0, *kernel_step, access, closest_match = 0;
+ u64 min_dist = -1, dist;
u32 ctrl_reg;
- u64 val, alignment_mask;
+ u64 val;
struct perf_event *wp, **slots;
struct debug_info *debug_info;
struct arch_hw_breakpoint *info;
@@ -667,35 +747,15 @@
slots = this_cpu_ptr(wp_on_reg);
debug_info = ¤t->thread.debug;
+ /*
+ * Find all watchpoints that match the reported address. If no exact
+ * match is found. Attribute the hit to the closest watchpoint.
+ */
+ rcu_read_lock();
for (i = 0; i < core_num_wrps; ++i) {
- rcu_read_lock();
-
wp = slots[i];
-
if (wp == NULL)
- goto unlock;
-
- info = counter_arch_bp(wp);
- /* AArch32 watchpoints are either 4 or 8 bytes aligned. */
- if (is_compat_task()) {
- if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
- alignment_mask = 0x7;
- else
- alignment_mask = 0x3;
- } else {
- alignment_mask = 0x7;
- }
-
- /* Check if the watchpoint value matches. */
- val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
- if (val != (addr & ~alignment_mask))
- goto unlock;
-
- /* Possible match, check the byte address select to confirm. */
- ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
- decode_ctrl_reg(ctrl_reg, &ctrl);
- if (!((1 << (addr & alignment_mask)) & ctrl.len))
- goto unlock;
+ continue;
/*
* Check that the access type matches.
@@ -704,18 +764,41 @@
access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W :
HW_BREAKPOINT_R;
if (!(access & hw_breakpoint_type(wp)))
- goto unlock;
+ continue;
+ /* Check if the watchpoint value and byte select match. */
+ val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
+ ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
+ decode_ctrl_reg(ctrl_reg, &ctrl);
+ dist = get_distance_from_watchpoint(addr, val, &ctrl);
+ if (dist < min_dist) {
+ min_dist = dist;
+ closest_match = i;
+ }
+ /* Is this an exact match? */
+ if (dist != 0)
+ continue;
+
+ info = counter_arch_bp(wp);
info->trigger = addr;
perf_bp_event(wp, regs);
/* Do we need to handle the stepping? */
if (!wp->overflow_handler)
step = 1;
-
-unlock:
- rcu_read_unlock();
}
+ if (min_dist > 0 && min_dist != -1) {
+ /* No exact match found. */
+ wp = slots[closest_match];
+ info = counter_arch_bp(wp);
+ info->trigger = addr;
+ perf_bp_event(wp, regs);
+
+ /* Do we need to handle the stepping? */
+ if (!wp->overflow_handler)
+ step = 1;
+ }
+ rcu_read_unlock();
if (!step)
return 0;
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index 3f71586..5293b5f 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -77,6 +77,14 @@
}
}
+bool aarch64_insn_is_branch_imm(u32 insn)
+{
+ return (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn) ||
+ aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn) ||
+ aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
+ aarch64_insn_is_bcond(insn));
+}
+
static DEFINE_SPINLOCK(patch_lock);
static void __kprobes *patch_map(void *addr, int fixmap)
@@ -266,23 +274,13 @@
return aarch64_insn_patch_text_sync(addrs, insns, cnt);
}
-u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
- u32 insn, u64 imm)
+static int __kprobes aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type,
+ u32 *maskp, int *shiftp)
{
- u32 immlo, immhi, lomask, himask, mask;
+ u32 mask;
int shift;
switch (type) {
- case AARCH64_INSN_IMM_ADR:
- lomask = 0x3;
- himask = 0x7ffff;
- immlo = imm & lomask;
- imm >>= 2;
- immhi = imm & himask;
- imm = (immlo << 24) | (immhi);
- mask = (lomask << 24) | (himask);
- shift = 5;
- break;
case AARCH64_INSN_IMM_26:
mask = BIT(26) - 1;
shift = 0;
@@ -321,9 +319,68 @@
shift = 16;
break;
default:
- pr_err("aarch64_insn_encode_immediate: unknown immediate encoding %d\n",
- type);
- return 0;
+ return -EINVAL;
+ }
+
+ *maskp = mask;
+ *shiftp = shift;
+
+ return 0;
+}
+
+#define ADR_IMM_HILOSPLIT 2
+#define ADR_IMM_SIZE SZ_2M
+#define ADR_IMM_LOMASK ((1 << ADR_IMM_HILOSPLIT) - 1)
+#define ADR_IMM_HIMASK ((ADR_IMM_SIZE >> ADR_IMM_HILOSPLIT) - 1)
+#define ADR_IMM_LOSHIFT 29
+#define ADR_IMM_HISHIFT 5
+
+u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn)
+{
+ u32 immlo, immhi, mask;
+ int shift;
+
+ switch (type) {
+ case AARCH64_INSN_IMM_ADR:
+ shift = 0;
+ immlo = (insn >> ADR_IMM_LOSHIFT) & ADR_IMM_LOMASK;
+ immhi = (insn >> ADR_IMM_HISHIFT) & ADR_IMM_HIMASK;
+ insn = (immhi << ADR_IMM_HILOSPLIT) | immlo;
+ mask = ADR_IMM_SIZE - 1;
+ break;
+ default:
+ if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) {
+ pr_err("aarch64_insn_decode_immediate: unknown immediate encoding %d\n",
+ type);
+ return 0;
+ }
+ }
+
+ return (insn >> shift) & mask;
+}
+
+u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
+ u32 insn, u64 imm)
+{
+ u32 immlo, immhi, mask;
+ int shift;
+
+ switch (type) {
+ case AARCH64_INSN_IMM_ADR:
+ shift = 0;
+ immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT;
+ imm >>= ADR_IMM_HILOSPLIT;
+ immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT;
+ imm = immlo | immhi;
+ mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) |
+ (ADR_IMM_HIMASK << ADR_IMM_HISHIFT));
+ break;
+ default:
+ if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) {
+ pr_err("aarch64_insn_encode_immediate: unknown immediate encoding %d\n",
+ type);
+ return 0;
+ }
}
/* Update the immediate field. */
@@ -1009,6 +1066,58 @@
return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift);
}
+/*
+ * Decode the imm field of a branch, and return the byte offset as a
+ * signed value (so it can be used when computing a new branch
+ * target).
+ */
+s32 aarch64_get_branch_offset(u32 insn)
+{
+ s32 imm;
+
+ if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn)) {
+ imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_26, insn);
+ return (imm << 6) >> 4;
+ }
+
+ if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
+ aarch64_insn_is_bcond(insn)) {
+ imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_19, insn);
+ return (imm << 13) >> 11;
+ }
+
+ if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn)) {
+ imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_14, insn);
+ return (imm << 18) >> 16;
+ }
+
+ /* Unhandled instruction */
+ BUG();
+}
+
+/*
+ * Encode the displacement of a branch in the imm field and return the
+ * updated instruction.
+ */
+u32 aarch64_set_branch_offset(u32 insn, s32 offset)
+{
+ if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn))
+ return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn,
+ offset >> 2);
+
+ if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
+ aarch64_insn_is_bcond(insn))
+ return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
+ offset >> 2);
+
+ if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn))
+ return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_14, insn,
+ offset >> 2);
+
+ /* Unhandled instruction */
+ BUG();
+}
+
bool aarch32_insn_is_wide(u32 insn)
{
return insn >= 0xe800;
diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c
index 3a6dbbb..ac0de50 100644
--- a/arch/arm64/kernel/irq.c
+++ b/arch/arm64/kernel/irq.c
@@ -36,9 +36,7 @@
int arch_show_interrupts(struct seq_file *p, int prec)
{
-#ifdef CONFIG_SMP
show_ipi_list(p, prec);
-#endif
seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count);
return 0;
}
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index 51128018..ca450c4 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -27,6 +27,7 @@
#include <linux/moduleloader.h>
#include <linux/vmalloc.h>
#include <asm/insn.h>
+#include <asm/sections.h>
#define AARCH64_INSN_IMM_MOVNZ AARCH64_INSN_IMM_MAX
#define AARCH64_INSN_IMM_MOVK AARCH64_INSN_IMM_16
@@ -406,3 +407,20 @@
me->name, (int)ELF64_R_TYPE(rel[i].r_info), val);
return -ENOEXEC;
}
+
+int module_finalize(const Elf_Ehdr *hdr,
+ const Elf_Shdr *sechdrs,
+ struct module *me)
+{
+ const Elf_Shdr *s, *se;
+ const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+ for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
+ if (strcmp(".altinstructions", secstrs + s->sh_name) == 0) {
+ apply_alternatives((void *)s->sh_addr, s->sh_size);
+ return 0;
+ }
+ }
+
+ return 0;
+}
diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c
index ce5836c..6f93c24 100644
--- a/arch/arm64/kernel/pci.c
+++ b/arch/arm64/kernel/pci.c
@@ -46,25 +46,3 @@
return 0;
}
-
-
-#ifdef CONFIG_PCI_DOMAINS_GENERIC
-static bool dt_domain_found = false;
-
-void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
-{
- int domain = of_get_pci_domain_nr(parent->of_node);
-
- if (domain >= 0) {
- dt_domain_found = true;
- } else if (dt_domain_found == true) {
- dev_err(parent, "Node %s is missing \"linux,pci-domain\" property in DT\n",
- parent->of_node->full_name);
- return;
- } else {
- domain = pci_get_new_domain_nr();
- }
-
- bus->domain_nr = domain;
-}
-#endif
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index a07d1f4..2ffbc86 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -1581,9 +1581,9 @@
static int __init init_hw_perf_events(void)
{
- u64 dfr = read_cpuid(ID_AA64DFR0_EL1);
+ u64 dfr = read_system_reg(SYS_ID_AA64DFR0_EL1);
- switch ((dfr >> 8) & 0xf) {
+ switch (cpuid_feature_extract_field(dfr, ID_AA64DFR0_PMUVER_SHIFT)) {
case 0x1: /* PMUv3 */
cpu_pmu = armv8_pmuv3_pmu_init();
break;
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index b6edb58..9d1fd51 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -44,6 +44,7 @@
#include <linux/personality.h>
#include <linux/notifier.h>
+#include <asm/alternative.h>
#include <asm/compat.h>
#include <asm/cacheflush.h>
#include <asm/fpsimd.h>
@@ -57,14 +58,6 @@
EXPORT_SYMBOL(__stack_chk_guard);
#endif
-void soft_restart(unsigned long addr)
-{
- setup_mm_for_reboot();
- cpu_soft_restart(virt_to_phys(cpu_reset), addr);
- /* Should never get here */
- BUG();
-}
-
/*
* Function pointers to optional machine specific functions
*/
@@ -145,9 +138,7 @@
/*
* Restart requires that the secondary CPUs stop performing any activity
- * while the primary CPU resets the system. Systems with a single CPU can
- * use soft_restart() as their machine descriptor's .restart hook, since that
- * will cause the only available CPU to reset. Systems with multiple CPUs must
+ * while the primary CPU resets the system. Systems with multiple CPUs must
* provide a HW restart implementation, to ensure that all CPUs reset at once.
* This is required so that any code running after reset on the primary CPU
* doesn't have to co-ordinate with other CPUs to ensure they aren't still
@@ -353,6 +344,9 @@
} else {
memset(childregs, 0, sizeof(struct pt_regs));
childregs->pstate = PSR_MODE_EL1h;
+ if (IS_ENABLED(CONFIG_ARM64_UAO) &&
+ cpus_have_cap(ARM64_HAS_UAO))
+ childregs->pstate |= PSR_UAO_BIT;
p->thread.cpu_context.x19 = stack_start;
p->thread.cpu_context.x20 = stk_sz;
}
@@ -388,6 +382,17 @@
: : "r" (tpidr), "r" (tpidrro));
}
+/* Restore the UAO state depending on next's addr_limit */
+static void uao_thread_switch(struct task_struct *next)
+{
+ if (IS_ENABLED(CONFIG_ARM64_UAO)) {
+ if (task_thread_info(next)->addr_limit == KERNEL_DS)
+ asm(ALTERNATIVE("nop", SET_PSTATE_UAO(1), ARM64_HAS_UAO));
+ else
+ asm(ALTERNATIVE("nop", SET_PSTATE_UAO(0), ARM64_HAS_UAO));
+ }
+}
+
/*
* Thread switching.
*/
@@ -400,6 +405,7 @@
tls_thread_switch(next);
hw_breakpoint_thread_switch(next);
contextidr_thread_switch(next);
+ uao_thread_switch(next);
/*
* Complete any pending TLB or cache maintenance on this CPU in case
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
index 898cd90..d8fd766 100644
--- a/arch/arm64/kernel/psci.c
+++ b/arch/arm64/kernel/psci.c
@@ -468,8 +468,6 @@
return init_fn(np);
}
-#ifdef CONFIG_SMP
-
static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu)
{
pr_info("Initializing psci_cpu_init\n");
@@ -566,7 +564,6 @@
return 0;
}
#endif
-#endif
static int psci_suspend_finisher(unsigned long state_id)
{
@@ -590,7 +587,6 @@
.cpu_init_idle = cpu_psci_cpu_init_idle,
.cpu_suspend = cpu_psci_cpu_suspend,
#endif
-#ifdef CONFIG_SMP
.cpu_init = cpu_psci_cpu_init,
#ifdef CONFIG_ARM64_CPU_SUSPEND
.cpu_suspend = cpu_psci_cpu_suspend,
@@ -602,6 +598,5 @@
.cpu_die = cpu_psci_cpu_die,
.cpu_kill = cpu_psci_cpu_kill,
#endif
-#endif
};
CPU_METHOD_OF_DECLARE(psci, "psci", &cpu_psci_ops);
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index d882b833..95fcbd5 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -220,13 +220,13 @@
struct arch_hw_breakpoint_ctrl ctrl,
struct perf_event_attr *attr)
{
- int err, len, type, disabled = !ctrl.enabled;
+ int err, len, type, offset, disabled = !ctrl.enabled;
attr->disabled = disabled;
if (disabled)
return 0;
- err = arch_bp_generic_fields(ctrl, &len, &type);
+ err = arch_bp_generic_fields(ctrl, &len, &type, &offset);
if (err)
return err;
@@ -245,6 +245,7 @@
attr->bp_len = len;
attr->bp_type = type;
+ attr->bp_addr += offset;
return 0;
}
@@ -297,7 +298,7 @@
if (IS_ERR(bp))
return PTR_ERR(bp);
- *addr = bp ? bp->attr.bp_addr : 0;
+ *addr = bp ? counter_arch_bp(bp)->address : 0;
return 0;
}
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index e2c6215..bf84618 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -27,7 +27,6 @@
#include <linux/console.h>
#include <linux/cache.h>
#include <linux/bootmem.h>
-#include <linux/seq_file.h>
#include <linux/screen_info.h>
#include <linux/init.h>
#include <linux/kexec.h>
@@ -40,18 +39,15 @@
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/memblock.h>
-#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/of_platform.h>
#include <linux/efi.h>
-#include <linux/personality.h>
#include <linux/dma-mapping.h>
#include <asm/fixmap.h>
#include <asm/cpu.h>
#include <asm/cputype.h>
#include <asm/elf.h>
-#include <asm/cputable.h>
#include <asm/cpufeature.h>
#include <asm/cpu_ops.h>
#include <asm/kasan.h>
@@ -65,36 +61,15 @@
#include <asm/psci.h>
#include <asm/efi.h>
-unsigned int processor_id;
-EXPORT_SYMBOL(processor_id);
-
-unsigned long elf_hwcap __read_mostly;
-EXPORT_SYMBOL_GPL(elf_hwcap);
-
-char* (*arch_read_hardware_id)(void);
-EXPORT_SYMBOL(arch_read_hardware_id);
-
-#ifdef CONFIG_COMPAT
-#define COMPAT_ELF_HWCAP_DEFAULT \
- (COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\
- COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\
- COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\
- COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\
- COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV|\
- COMPAT_HWCAP_LPAE)
-unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT;
-unsigned int compat_elf_hwcap2 __read_mostly;
-#endif
-
-DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS);
-
unsigned int boot_reason;
EXPORT_SYMBOL(boot_reason);
unsigned int cold_boot;
EXPORT_SYMBOL(cold_boot);
-static const char *cpu_name;
+char* (*arch_read_hardware_id)(void);
+EXPORT_SYMBOL(arch_read_hardware_id);
+
static const char *machine_name;
phys_addr_t __fdt_pointer __initdata;
@@ -131,6 +106,11 @@
printk("%s", buf);
}
+/*
+ * The recorded values of x0 .. x3 upon kernel entry.
+ */
+u64 __cacheline_aligned boot_args[4];
+
void __init smp_setup_processor_id(void)
{
/*
@@ -147,7 +127,6 @@
}
struct mpidr_hash mpidr_hash;
-#ifdef CONFIG_SMP
/**
* smp_build_mpidr_hash - Pre-compute shifts required at each affinity
* level in order to build a linear index from an
@@ -213,107 +192,6 @@
pr_warn("Large number of MPIDR hash buckets detected\n");
__flush_dcache_area(&mpidr_hash, sizeof(struct mpidr_hash));
}
-#endif
-
-static void __init setup_processor(void)
-{
- struct cpu_info *cpu_info;
- u64 features, block;
- u32 cwg;
- int cls;
-
- cpu_info = lookup_processor_type(read_cpuid_id());
- if (!cpu_info) {
- printk("CPU configuration botched (ID %08x), unable to continue.\n",
- read_cpuid_id());
- while (1);
- }
-
- cpu_name = cpu_info->cpu_name;
-
- printk("CPU: %s [%08x] revision %d\n",
- cpu_name, read_cpuid_id(), read_cpuid_id() & 15);
-
- sprintf(init_utsname()->machine, ELF_PLATFORM);
- elf_hwcap = 0;
-
- cpuinfo_store_boot_cpu();
-
- /*
- * Check for sane CTR_EL0.CWG value.
- */
- cwg = cache_type_cwg();
- cls = cache_line_size();
- if (!cwg)
- pr_warn("No Cache Writeback Granule information, assuming cache line size %d\n",
- cls);
- if (L1_CACHE_BYTES < cls)
- pr_warn("L1_CACHE_BYTES smaller than the Cache Writeback Granule (%d < %d)\n",
- L1_CACHE_BYTES, cls);
-
- /*
- * ID_AA64ISAR0_EL1 contains 4-bit wide signed feature blocks.
- * The blocks we test below represent incremental functionality
- * for non-negative values. Negative values are reserved.
- */
- features = read_cpuid(ID_AA64ISAR0_EL1);
- block = (features >> 4) & 0xf;
- if (!(block & 0x8)) {
- switch (block) {
- default:
- case 2:
- elf_hwcap |= HWCAP_PMULL;
- case 1:
- elf_hwcap |= HWCAP_AES;
- case 0:
- break;
- }
- }
-
- block = (features >> 8) & 0xf;
- if (block && !(block & 0x8))
- elf_hwcap |= HWCAP_SHA1;
-
- block = (features >> 12) & 0xf;
- if (block && !(block & 0x8))
- elf_hwcap |= HWCAP_SHA2;
-
- block = (features >> 16) & 0xf;
- if (block && !(block & 0x8))
- elf_hwcap |= HWCAP_CRC32;
-
-#ifdef CONFIG_COMPAT
- /*
- * ID_ISAR5_EL1 carries similar information as above, but pertaining to
- * the Aarch32 32-bit execution state.
- */
- features = read_cpuid(ID_ISAR5_EL1);
- block = (features >> 4) & 0xf;
- if (!(block & 0x8)) {
- switch (block) {
- default:
- case 2:
- compat_elf_hwcap2 |= COMPAT_HWCAP2_PMULL;
- case 1:
- compat_elf_hwcap2 |= COMPAT_HWCAP2_AES;
- case 0:
- break;
- }
- }
-
- block = (features >> 8) & 0xf;
- if (block && !(block & 0x8))
- compat_elf_hwcap2 |= COMPAT_HWCAP2_SHA1;
-
- block = (features >> 12) & 0xf;
- if (block && !(block & 0x8))
- compat_elf_hwcap2 |= COMPAT_HWCAP2_SHA2;
-
- block = (features >> 16) & 0xf;
- if (block && !(block & 0x8))
- compat_elf_hwcap2 |= COMPAT_HWCAP2_CRC32;
-#endif
-}
static void __init setup_machine_fdt(phys_addr_t dt_phys)
{
@@ -360,7 +238,7 @@
struct resource *res;
kernel_code.start = virt_to_phys(_text);
- kernel_code.end = virt_to_phys(_etext - 1);
+ kernel_code.end = virt_to_phys(__init_begin - 1);
kernel_data.start = virt_to_phys(_sdata);
kernel_data.end = virt_to_phys(_end - 1);
@@ -451,10 +329,11 @@
void __init setup_arch(char **cmdline_p)
{
- setup_processor();
+ pr_info("Boot CPU: AArch64 Processor [%08x]\n", read_cpuid_id());
setup_machine_fdt(__fdt_pointer);
+ sprintf(init_utsname()->machine, ELF_PLATFORM);
init_mm.start_code = (unsigned long) _text;
init_mm.end_code = (unsigned long) _etext;
init_mm.end_data = (unsigned long) _edata;
@@ -483,7 +362,6 @@
request_standard_resources();
- efi_virtmap_init();
early_ioremap_reset();
unflatten_device_tree();
@@ -492,9 +370,16 @@
cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
cpu_read_bootcpu_ops();
-#ifdef CONFIG_SMP
smp_init_cpus();
smp_build_mpidr_hash();
+
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ /*
+ * Make sure init_thread_info.ttbr0 always generates translation
+ * faults in case uaccess_enable() is inadvertently called by the init
+ * thread.
+ */
+ init_thread_info.ttbr0 = virt_to_phys(empty_zero_page);
#endif
#ifdef CONFIG_VT
@@ -505,6 +390,13 @@
#endif
#endif
init_random_pool();
+
+ if (boot_args[1] || boot_args[2] || boot_args[3]) {
+ pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n"
+ "\tx1: %016llx\n\tx2: %016llx\n\tx3: %016llx\n"
+ "This indicates a broken bootloader or old kernel\n",
+ boot_args[1], boot_args[2], boot_args[3]);
+ }
}
static int __init arm64_device_init(void)
@@ -527,255 +419,3 @@
return 0;
}
postcore_initcall(topology_init);
-
-static const char *hwcap_str[] = {
- "fp",
- "asimd",
- "evtstrm",
- "aes",
- "pmull",
- "sha1",
- "sha2",
- "crc32",
- NULL
-};
-
-#ifdef CONFIG_COMPAT
-static const char *compat_hwcap_str[] = {
- "swp",
- "half",
- "thumb",
- "26bit",
- "fastmult",
- "fpa",
- "vfp",
- "edsp",
- "java",
- "iwmmxt",
- "crunch",
- "thumbee",
- "neon",
- "vfpv3",
- "vfpv3d16",
- "tls",
- "vfpv4",
- "idiva",
- "idivt",
- "vfpd32",
- "lpae",
- "evtstrm",
- NULL
-};
-
-static const char *compat_hwcap2_str[] = {
- "aes",
- "pmull",
- "sha1",
- "sha2",
- "crc32",
- NULL
-};
-#endif /* CONFIG_COMPAT */
-
-static u32 cx_fuse_data = 0x0;
-static u32 mx_fuse_data = 0x0;
-
-static const u32 vddcx_pvs_retention_data[8] =
-{
- /* 000 */ 600000,
- /* 001 */ 550000,
- /* 010 */ 500000,
- /* 011 */ 450000,
- /* 100 */ 400000,
- /* 101 */ 400000, //limiting based on CR812560
- /* 110 */ 400000, //limiting based on CR812560
- /* 111 */ 600000
-};
-
-static const u32 vddmx_pvs_retention_data[8] =
-{
- /* 000 */ 700000,
- /* 001 */ 650000,
- /* 010 */ 580000,
- /* 011 */ 550000,
- /* 100 */ 490000,
- /* 101 */ 490000,
- /* 110 */ 490000,
- /* 111 */ 490000
-};
-
-static int read_cx_fuse_setting(void){
- if(cx_fuse_data != 0x0)
- /* 0x00070134[31:29] */
- return ((cx_fuse_data & (0x7 << 29)) >> 29);
- else
- return -ENOMEM;
-}
-
-static int read_mx_fuse_setting(void){
- if(mx_fuse_data != 0x0)
- /* 0x00070148[4:2] */
- return ((mx_fuse_data & (0x7 << 2)) >> 2);
- else
- return -ENOMEM;
-}
-
-static u32 Get_min_cx(void) {
- u32 lookup_val = 0;
- int mapping_data;
- mapping_data = read_cx_fuse_setting();
- if(mapping_data >= 0)
- lookup_val = vddcx_pvs_retention_data[mapping_data];
- return lookup_val;
-}
-
-static u32 Get_min_mx(void) {
- u32 lookup_val = 0;
- int mapping_data;
- mapping_data = read_mx_fuse_setting();
- if(mapping_data >= 0)
- lookup_val = vddmx_pvs_retention_data[mapping_data];
- return lookup_val;
-}
-
-extern u64* htc_target_quot[2];
-extern int htc_target_quot_len;
-static int c_show(struct seq_file *m, void *v)
-{
- int i, j, size;
-
- seq_printf(m, "Processor\t: %s rev %d (%s)\n",
- cpu_name, read_cpuid_id() & 15, ELF_PLATFORM);
- for_each_present_cpu(i) {
- struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
- u32 midr = cpuinfo->reg_midr;
-
- /*
- * glibc reads /proc/cpuinfo to determine the number of
- * online processors, looking for lines beginning with
- * "processor". Give glibc what it expects.
- */
-#ifdef CONFIG_SMP
- seq_printf(m, "processor\t: %d\n", i);
-#endif
-
- seq_printf(m, "min_vddcx\t: %d\n", Get_min_cx());
- seq_printf(m, "min_vddmx\t: %d\n", Get_min_mx());
- seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
- loops_per_jiffy / (500000UL/HZ),
- loops_per_jiffy / (5000UL/HZ) % 100);
-
- /*
- * Dump out the common processor features in a single line.
- * Userspace should read the hwcaps with getauxval(AT_HWCAP)
- * rather than attempting to parse this, but there's a body of
- * software which does already (at least for 32-bit).
- */
- seq_puts(m, "Features\t:");
- if (personality(current->personality) == PER_LINUX32) {
-#ifdef CONFIG_COMPAT
- for (j = 0; compat_hwcap_str[j]; j++)
- if (compat_elf_hwcap & (1 << j))
- seq_printf(m, " %s", compat_hwcap_str[j]);
-
- for (j = 0; compat_hwcap2_str[j]; j++)
- if (compat_elf_hwcap2 & (1 << j))
- seq_printf(m, " %s", compat_hwcap2_str[j]);
-#endif /* CONFIG_COMPAT */
- } else {
- for (j = 0; hwcap_str[j]; j++)
- if (elf_hwcap & (1 << j))
- seq_printf(m, " %s", hwcap_str[j]);
- }
- seq_puts(m, "\n");
-
- seq_printf(m, "CPU implementer\t: 0x%02x\n",
- MIDR_IMPLEMENTOR(midr));
- seq_printf(m, "CPU architecture: 8\n");
- seq_printf(m, "CPU variant\t: 0x%x\n", MIDR_VARIANT(midr));
- seq_printf(m, "CPU part\t: 0x%03x\n", MIDR_PARTNUM(midr));
- seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
- }
-
- size = sizeof(htc_target_quot)/sizeof(u64);
- seq_printf(m, "CPU param\t: ");
- for (i = 0; i < size; i++) {
- if(htc_target_quot[i]) {
- for(j = 0; j < htc_target_quot_len; j++)
- seq_printf(m, "%lld ", htc_target_quot[i][j]);
- }
- }
- seq_printf(m, "\n");
-
- if (!arch_read_hardware_id)
- seq_printf(m, "Hardware\t: %s\n", machine_name);
- else
- seq_printf(m, "Hardware\t: %s\n", arch_read_hardware_id());
-
- return 0;
-}
-
-static void *c_start(struct seq_file *m, loff_t *pos)
-{
- return *pos < 1 ? (void *)1 : NULL;
-}
-
-static void *c_next(struct seq_file *m, void *v, loff_t *pos)
-{
- ++*pos;
- return NULL;
-}
-
-static void c_stop(struct seq_file *m, void *v)
-{
-}
-
-const struct seq_operations cpuinfo_op = {
- .start = c_start,
- .next = c_next,
- .stop = c_stop,
- .show = c_show
-};
-
-void arch_setup_pdev_archdata(struct platform_device *pdev)
-{
- pdev->archdata.dma_mask = DMA_BIT_MASK(32);
- pdev->dev.dma_mask = &pdev->archdata.dma_mask;
-}
-
-static int msm8996_read_cx_fuse(void){
- void __iomem *addr;
- struct device_node *dn = of_find_compatible_node(NULL,
- NULL, "qcom,cpucx-8996");
- if (dn && (cx_fuse_data == 0x0)) {
- addr = of_iomap(dn, 0);
- if (!addr)
- return -ENOMEM;
- cx_fuse_data = readl_relaxed(addr);
- iounmap(addr);
- }
- else {
- return -ENOMEM;
- }
- return 0;
-}
-arch_initcall_sync(msm8996_read_cx_fuse);
-
-static int msm8996_read_mx_fuse(void){
- void __iomem *addr;
- struct device_node *dn = of_find_compatible_node(NULL,
- NULL, "qcom,cpumx-8996");
- if (dn && (mx_fuse_data == 0x0)) {
- addr = of_iomap(dn, 0);
- if (!addr)
- return -ENOMEM;
- mx_fuse_data = readl_relaxed(addr);
- iounmap(addr);
- }
- else {
- return -ENOMEM;
- }
- return 0;
-}
-
-arch_initcall_sync(msm8996_read_mx_fuse);
diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c
index 1a15823..347e07b 100644
--- a/arch/arm64/kernel/signal32.c
+++ b/arch/arm64/kernel/signal32.c
@@ -527,7 +527,7 @@
__put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err);
/* set the compat FSR WnR */
- __put_user_error(!!(current->thread.fault_code & ESR_EL1_WRITE) <<
+ __put_user_error(!!(current->thread.fault_code & ESR_ELx_WNR) <<
FSR_WRITE_SHIFT, &sf->uc.uc_mcontext.error_code, err);
__put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err);
__put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err);
diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S
index ede186c..becc201 100644
--- a/arch/arm64/kernel/sleep.S
+++ b/arch/arm64/kernel/sleep.S
@@ -82,7 +82,6 @@
str x2, [x0, #CPU_CTX_SP]
ldr x1, =sleep_save_sp
ldr x1, [x1, #SLEEP_SAVE_SP_VIRT]
-#ifdef CONFIG_SMP
mrs x7, mpidr_el1
ldr x9, =mpidr_hash
ldr x10, [x9, #MPIDR_HASH_MASK]
@@ -94,7 +93,6 @@
ldp w5, w6, [x9, #(MPIDR_HASH_SHIFTS + 8)]
compute_mpidr_hash x8, x3, x4, x5, x6, x7, x10
add x1, x1, x8, lsl #3
-#endif
bl __cpu_suspend_save
/*
* Grab suspend finisher in x20 and its argument in x19
@@ -134,6 +132,14 @@
ldr x3, =cpu_resume_after_mmu
msr sctlr_el1, x0 // restore sctlr_el1
isb
+ /*
+ * Invalidate the local I-cache so that any instructions fetched
+ * speculatively from the PoC are discarded, since they may have
+ * been dynamically patched at the PoU.
+ */
+ ic iallu
+ dsb nsh
+ isb
br x3 // global jump to virtual address
ENDPROC(cpu_resume_mmu)
cpu_resume_after_mmu:
@@ -149,7 +155,6 @@
ENTRY(cpu_resume)
bl el2_setup // if in EL2 drop to EL1 cleanly
-#ifdef CONFIG_SMP
mrs x1, mpidr_el1
adrp x8, mpidr_hash
add x8, x8, #:lo12:mpidr_hash // x8 = struct mpidr_hash phys address
@@ -159,9 +164,6 @@
ldp w5, w6, [x8, #(MPIDR_HASH_SHIFTS + 8)]
compute_mpidr_hash x7, x3, x4, x5, x6, x1, x2
/* x7 contains hash index, let's use it to grab context pointer */
-#else
- mov x7, xzr
-#endif
adrp x0, sleep_save_sp
add x0, x0, #:lo12:sleep_save_sp
ldr x0, [x0, #SLEEP_SAVE_SP_PHYS]
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 7067ed8..64165e6 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -145,21 +145,27 @@
*/
atomic_inc(&mm->mm_count);
current->active_mm = mm;
- cpumask_set_cpu(cpu, mm_cpumask(mm));
set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
- pr_debug("CPU%u: Booted secondary processor\n", cpu);
/*
* TTBR0 is only used for the identity mapping at this stage. Make it
* point to zero page to avoid speculatively fetching new entries.
*/
cpu_set_reserved_ttbr0();
- flush_tlb_all();
+ local_flush_tlb_all();
+ cpu_set_default_tcr_t0sz();
preempt_disable();
trace_hardirqs_off();
+ /*
+ * If the system has established the capabilities, make sure
+ * this CPU ticks all of those. If it doesn't, the CPU will
+ * fail to come online.
+ */
+ verify_local_cpu_capabilities();
+
if (cpu_ops[cpu]->cpu_postboot)
cpu_ops[cpu]->cpu_postboot();
@@ -180,10 +186,11 @@
* the CPU migration code to notice that the CPU is online
* before we continue.
*/
+ pr_info("CPU%u: Booted secondary processor [%08x]\n",
+ cpu, read_cpuid_id());
set_cpu_online(cpu, true);
complete(&cpu_running);
- local_dbg_enable();
local_irq_enable();
local_async_enable();
@@ -235,12 +242,6 @@
* OK - migrate IRQs away from this CPU
*/
migrate_irqs();
-
- /*
- * Remove this CPU from the vm mask set of all processes.
- */
- clear_tasks_mm_cpumask(cpu);
-
return 0;
}
@@ -323,11 +324,13 @@
void __init smp_cpus_done(unsigned int max_cpus)
{
pr_info("SMP: Total of %d processors activated.\n", num_online_cpus());
- apply_alternatives();
+ setup_cpu_features();
+ apply_alternatives_all();
}
void __init smp_prepare_boot_cpu(void)
{
+ cpuinfo_store_boot_cpu();
set_my_cpu_offset(per_cpu_offset(smp_processor_id()));
}
@@ -606,15 +609,18 @@
static void smp_send_all_cpu_backtrace(void)
{
- unsigned int this_cpu = smp_processor_id();
+ unsigned int this_cpu;
int i;
- if (test_and_set_bit(0, &backtrace_flag))
+ this_cpu = get_cpu();;
+ if (test_and_set_bit(0, &backtrace_flag)) {
/*
* If there is already a trigger_all_cpu_backtrace() in progress
* (backtrace_flag == 1), don't output double cpu dump infos.
*/
+ put_cpu();
return;
+ }
cpumask_copy(&backtrace_mask, cpu_online_mask);
cpu_clear(this_cpu, backtrace_mask);
@@ -626,6 +632,7 @@
if (!cpus_empty(backtrace_mask))
smp_cross_call_common(&backtrace_mask, IPI_CPU_BACKTRACE);
+ put_cpu();
/* Wait for up to 10 seconds for all other CPUs to do the backtrace */
for (i = 0; i < 10 * 1000; i++) {
if (cpumask_empty(&backtrace_mask))
diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c
index 23e35de..ab14fee 100644
--- a/arch/arm64/kernel/suspend.c
+++ b/arch/arm64/kernel/suspend.c
@@ -119,7 +119,7 @@
else
cpu_switch_mm(mm->pgd, mm);
- flush_tlb_all();
+ local_flush_tlb_all();
/*
* Restore per-cpu offset before any kernel
diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
index 1a7125c..59630e4 100644
--- a/arch/arm64/kernel/time.c
+++ b/arch/arm64/kernel/time.c
@@ -41,7 +41,6 @@
#include <asm/thread_info.h>
#include <asm/stacktrace.h>
-#ifdef CONFIG_SMP
unsigned long profile_pc(struct pt_regs *regs)
{
struct stackframe frame;
@@ -61,7 +60,6 @@
return frame.pc;
}
EXPORT_SYMBOL(profile_pc);
-#endif
void __init time_init(void)
{
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 7681c42..737ec18 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -33,6 +33,7 @@
#include <asm/atomic.h>
#include <asm/debug-monitors.h>
+#include <asm/esr.h>
#include <asm/traps.h>
#include <asm/stacktrace.h>
#include <asm/exception.h>
@@ -63,8 +64,7 @@
/*
* We need to switch to kernel mode so that we can use __get_user
- * to safely read from kernel space. Note that we now dump the
- * code first, just in case the backtrace kills us.
+ * to safely read from kernel space.
*/
fs = get_fs();
set_fs(KERNEL_DS);
@@ -101,21 +101,12 @@
stack + sizeof(struct pt_regs));
}
-static void dump_instr(const char *lvl, struct pt_regs *regs)
+static void __dump_instr(const char *lvl, struct pt_regs *regs)
{
unsigned long addr = instruction_pointer(regs);
- mm_segment_t fs;
char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
int i;
- /*
- * We need to switch to kernel mode so that we can use __get_user
- * to safely read from kernel space. Note that we now dump the
- * code first, just in case the backtrace kills us.
- */
- fs = get_fs();
- set_fs(KERNEL_DS);
-
for (i = -4; i < 1; i++) {
unsigned int val, bad;
@@ -129,15 +120,25 @@
}
}
printk("%sCode: %s\n", lvl, str);
+}
- set_fs(fs);
+static void dump_instr(const char *lvl, struct pt_regs *regs)
+{
+ if (!user_mode(regs)) {
+ mm_segment_t fs = get_fs();
+ set_fs(KERNEL_DS);
+ __dump_instr(lvl, regs);
+ set_fs(fs);
+ } else {
+ __dump_instr(lvl, regs);
+ }
}
static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
{
struct stackframe frame;
- pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
+ pr_debug("%s(regs = %pP tsk = %pP)\n", __func__, regs, tsk);
if (!tsk)
tsk = current;
@@ -190,11 +191,7 @@
#else
#define S_PREEMPT ""
#endif
-#ifdef CONFIG_SMP
#define S_SMP " SMP"
-#else
-#define S_SMP ""
-#endif
static int __die(const char *str, int err, struct thread_info *thread,
struct pt_regs *regs)
@@ -213,7 +210,7 @@
print_modules();
__show_regs(regs);
- pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n",
+ pr_emerg("Process %.*s (pid: %d, stack limit = 0x%pP)\n",
TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);
if (!user_mode(regs) || in_interrupt()) {
@@ -435,17 +432,79 @@
return sys_ni_syscall();
}
+static const char *esr_class_str[] = {
+ [0 ... ESR_ELx_EC_MAX] = "UNRECOGNIZED EC",
+ [ESR_ELx_EC_UNKNOWN] = "Unknown/Uncategorized",
+ [ESR_ELx_EC_WFx] = "WFI/WFE",
+ [ESR_ELx_EC_CP15_32] = "CP15 MCR/MRC",
+ [ESR_ELx_EC_CP15_64] = "CP15 MCRR/MRRC",
+ [ESR_ELx_EC_CP14_MR] = "CP14 MCR/MRC",
+ [ESR_ELx_EC_CP14_LS] = "CP14 LDC/STC",
+ [ESR_ELx_EC_FP_ASIMD] = "ASIMD",
+ [ESR_ELx_EC_CP10_ID] = "CP10 MRC/VMRS",
+ [ESR_ELx_EC_CP14_64] = "CP14 MCRR/MRRC",
+ [ESR_ELx_EC_ILL] = "PSTATE.IL",
+ [ESR_ELx_EC_SVC32] = "SVC (AArch32)",
+ [ESR_ELx_EC_HVC32] = "HVC (AArch32)",
+ [ESR_ELx_EC_SMC32] = "SMC (AArch32)",
+ [ESR_ELx_EC_SVC64] = "SVC (AArch64)",
+ [ESR_ELx_EC_HVC64] = "HVC (AArch64)",
+ [ESR_ELx_EC_SMC64] = "SMC (AArch64)",
+ [ESR_ELx_EC_SYS64] = "MSR/MRS (AArch64)",
+ [ESR_ELx_EC_IMP_DEF] = "EL3 IMP DEF",
+ [ESR_ELx_EC_IABT_LOW] = "IABT (lower EL)",
+ [ESR_ELx_EC_IABT_CUR] = "IABT (current EL)",
+ [ESR_ELx_EC_PC_ALIGN] = "PC Alignment",
+ [ESR_ELx_EC_DABT_LOW] = "DABT (lower EL)",
+ [ESR_ELx_EC_DABT_CUR] = "DABT (current EL)",
+ [ESR_ELx_EC_SP_ALIGN] = "SP Alignment",
+ [ESR_ELx_EC_FP_EXC32] = "FP (AArch32)",
+ [ESR_ELx_EC_FP_EXC64] = "FP (AArch64)",
+ [ESR_ELx_EC_SERROR] = "SError",
+ [ESR_ELx_EC_BREAKPT_LOW] = "Breakpoint (lower EL)",
+ [ESR_ELx_EC_BREAKPT_CUR] = "Breakpoint (current EL)",
+ [ESR_ELx_EC_SOFTSTP_LOW] = "Software Step (lower EL)",
+ [ESR_ELx_EC_SOFTSTP_CUR] = "Software Step (current EL)",
+ [ESR_ELx_EC_WATCHPT_LOW] = "Watchpoint (lower EL)",
+ [ESR_ELx_EC_WATCHPT_CUR] = "Watchpoint (current EL)",
+ [ESR_ELx_EC_BKPT32] = "BKPT (AArch32)",
+ [ESR_ELx_EC_VECTOR32] = "Vector catch (AArch32)",
+ [ESR_ELx_EC_BRK64] = "BRK (AArch64)",
+};
+
+const char *esr_get_class_string(u32 esr)
+{
+ return esr_class_str[ESR_ELx_EC(esr)];
+}
+
/*
- * bad_mode handles the impossible case in the exception vector.
+ * bad_mode handles the impossible case in the exception vector. This is always
+ * fatal.
*/
asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
{
+ console_verbose();
+
+ pr_crit("Bad mode in %s handler detected, code 0x%08x -- %s\n",
+ handler[reason], esr, esr_get_class_string(esr));
+
+ die("Oops - bad mode", regs, 0);
+ local_irq_disable();
+ panic("bad mode");
+}
+
+/*
+ * bad_el0_sync handles unexpected, but potentially recoverable synchronous
+ * exceptions taken from EL0. Unlike bad_mode, this returns.
+ */
+asmlinkage void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
+{
siginfo_t info;
void __user *pc = (void __user *)instruction_pointer(regs);
console_verbose();
- pr_crit("Bad mode in %s handler detected, code 0x%08x\n",
- handler[reason], esr);
+ pr_crit("Bad EL0 synchronous exception detected on CPU%d, code 0x%08x\n",
+ smp_processor_id(), esr);
__show_regs(regs);
info.si_signo = SIGILL;
@@ -453,13 +512,10 @@
info.si_code = ILL_ILLOPC;
info.si_addr = pc;
- if (esr >> ESR_EL1_EC_SHIFT == ESR_EL1_EC_SERROR) {
- pr_crit("System error detected. ESR.ISS = %08x\n",
- esr & 0xffffff);
- arm64_check_cache_ecc(NULL);
- }
+ current->thread.fault_address = 0;
+ current->thread.fault_code = 0;
- arm64_notify_die("Oops - bad mode", regs, &info, 0);
+ force_sig_info(info.si_signo, &info, current);
}
void __pte_error(const char *file, int line, unsigned long val)
diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c
index 32aeea0..b18b5dc 100644
--- a/arch/arm64/kernel/vdso.c
+++ b/arch/arm64/kernel/vdso.c
@@ -18,12 +18,13 @@
* Author: Will Deacon <will.deacon@arm.com>
*/
-#include <linux/kernel.h>
+#include <linux/cache.h>
#include <linux/clocksource.h>
#include <linux/elf.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gfp.h>
+#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/signal.h>
@@ -37,8 +38,7 @@
#include <asm/vdso_datapage.h>
extern char vdso_start, vdso_end;
-static unsigned long vdso_pages;
-static struct page **vdso_pagelist;
+static unsigned long vdso_pages __ro_after_init;
/*
* The vDSO data page.
@@ -53,7 +53,7 @@
/*
* Create and map the vectors page for AArch32 tasks.
*/
-static struct page *vectors_page[1];
+static struct page *vectors_page[1] __ro_after_init;
static int alloc_vectors_page(void)
{
@@ -109,11 +109,19 @@
}
#endif /* CONFIG_COMPAT */
-static struct vm_special_mapping vdso_spec[2];
+static struct vm_special_mapping vdso_spec[2] __ro_after_init = {
+ {
+ .name = "[vvar]",
+ },
+ {
+ .name = "[vdso]",
+ },
+};
static int __init vdso_init(void)
{
int i;
+ struct page **vdso_pagelist;
if (memcmp(&vdso_start, "\177ELF", 4)) {
pr_err("vDSO is not a valid ELF object!\n");
@@ -137,16 +145,8 @@
for (i = 0; i < vdso_pages; i++)
vdso_pagelist[i + 1] = virt_to_page(&vdso_start + i * PAGE_SIZE);
- /* Populate the special mapping structures */
- vdso_spec[0] = (struct vm_special_mapping) {
- .name = "[vvar]",
- .pages = vdso_pagelist,
- };
-
- vdso_spec[1] = (struct vm_special_mapping) {
- .name = "[vdso]",
- .pages = &vdso_pagelist[1],
- };
+ vdso_spec[0].pages = &vdso_pagelist[0];
+ vdso_spec[1].pages = &vdso_pagelist[1];
return 0;
}
diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile
index f6fe17d..b467fd0 100644
--- a/arch/arm64/kernel/vdso/Makefile
+++ b/arch/arm64/kernel/vdso/Makefile
@@ -15,6 +15,9 @@
ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \
$(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
+# Disable gcov profiling for VDSO code
+GCOV_PROFILE := n
+
# Workaround for bare-metal (ELF) toolchains that neglect to pass -shared
# down to collect2, resulting in silent corruption of the vDSO image.
ccflags-y += -Wl,-shared
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index b185d96..a38f929 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -5,6 +5,7 @@
*/
#include <asm-generic/vmlinux.lds.h>
+#include <asm/kernel-pgtable.h>
#include <asm/thread_info.h>
#include <asm/memory.h>
#include <asm/page.h>
@@ -103,11 +104,12 @@
}
ALIGN_DEBUG_RO
+ _etext = .; /* End of text section */
+
RO_DATA(PAGE_SIZE)
EXCEPTION_TABLE(8)
NOTES
ALIGN_DEBUG_RO
- _etext = .; /* End of text and rodata section */
ALIGN_DEBUG_RO_MIN(PAGE_SIZE)
__init_begin = .;
@@ -160,6 +162,11 @@
swapper_pg_dir = .;
. += SWAPPER_DIR_SIZE;
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ reserved_ttbr0 = .;
+ . += RESERVED_TTBR0_SIZE;
+#endif
+
_end = .;
STABS_DEBUG
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index 0b43265..6dbc6ac 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -52,7 +52,7 @@
{
u64 pfr0;
- pfr0 = read_cpuid(ID_AA64PFR0_EL1);
+ pfr0 = read_system_reg(SYS_ID_AA64PFR0_EL1);
return !!(pfr0 & 0x20);
}
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 3d7c2df..f4001cb 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -69,68 +69,31 @@
return ccsidr;
}
-static void do_dc_cisw(u32 val)
-{
- asm volatile("dc cisw, %x0" : : "r" (val));
- dsb(ish);
-}
-
-static void do_dc_csw(u32 val)
-{
- asm volatile("dc csw, %x0" : : "r" (val));
- dsb(ish);
-}
-
-/* See note at ARM ARM B1.14.4 */
+/*
+ * See note at ARMv7 ARM B1.14.4 (TL;DR: S/W ops are not easily virtualized).
+ */
static bool access_dcsw(struct kvm_vcpu *vcpu,
const struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
- unsigned long val;
- int cpu;
-
if (!p->is_write)
return read_from_write_only(vcpu, p);
- cpu = get_cpu();
-
- cpumask_setall(&vcpu->arch.require_dcache_flush);
- cpumask_clear_cpu(cpu, &vcpu->arch.require_dcache_flush);
-
- /* If we were already preempted, take the long way around */
- if (cpu != vcpu->arch.last_pcpu) {
- flush_cache_all();
- goto done;
- }
-
- val = *vcpu_reg(vcpu, p->Rt);
-
- switch (p->CRm) {
- case 6: /* Upgrade DCISW to DCCISW, as per HCR.SWIO */
- case 14: /* DCCISW */
- do_dc_cisw(val);
- break;
-
- case 10: /* DCCSW */
- do_dc_csw(val);
- break;
- }
-
-done:
- put_cpu();
-
+ kvm_set_way_flush(vcpu);
return true;
}
/*
* Generic accessor for VM registers. Only called as long as HCR_TVM
- * is set.
+ * is set. If the guest enables the MMU, we stop trapping the VM
+ * sys_regs and leave it in complete control of the caches.
*/
static bool access_vm_reg(struct kvm_vcpu *vcpu,
const struct sys_reg_params *p,
const struct sys_reg_desc *r)
{
unsigned long val;
+ bool was_enabled = vcpu_has_cache_enabled(vcpu);
BUG_ON(!p->is_write);
@@ -143,25 +106,7 @@
vcpu_cp15_64_low(vcpu, r->reg) = val & 0xffffffffUL;
}
- return true;
-}
-
-/*
- * SCTLR_EL1 accessor. Only called as long as HCR_TVM is set. If the
- * guest enables the MMU, we stop trapping the VM sys_regs and leave
- * it in complete control of the caches.
- */
-static bool access_sctlr(struct kvm_vcpu *vcpu,
- const struct sys_reg_params *p,
- const struct sys_reg_desc *r)
-{
- access_vm_reg(vcpu, p, r);
-
- if (vcpu_has_cache_enabled(vcpu)) { /* MMU+Caches enabled? */
- vcpu->arch.hcr_el2 &= ~HCR_TVM;
- stage2_flush_vm(vcpu->kvm);
- }
-
+ kvm_toggle_cache(vcpu, was_enabled);
return true;
}
@@ -377,7 +322,7 @@
NULL, reset_mpidr, MPIDR_EL1 },
/* SCTLR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b000),
- access_sctlr, reset_val, SCTLR_EL1, 0x00C50078 },
+ access_vm_reg, reset_val, SCTLR_EL1, 0x00C50078 },
/* CPACR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b010),
NULL, reset_val, CPACR_EL1, 0 },
@@ -509,13 +454,13 @@
if (p->is_write) {
return ignore_write(vcpu, p);
} else {
- u64 dfr = read_cpuid(ID_AA64DFR0_EL1);
- u64 pfr = read_cpuid(ID_AA64PFR0_EL1);
- u32 el3 = !!((pfr >> 12) & 0xf);
+ u64 dfr = read_system_reg(SYS_ID_AA64DFR0_EL1);
+ u64 pfr = read_system_reg(SYS_ID_AA64PFR0_EL1);
+ u32 el3 = !!cpuid_feature_extract_field(pfr, ID_AA64PFR0_EL3_SHIFT);
- *vcpu_reg(vcpu, p->Rt) = ((((dfr >> 20) & 0xf) << 28) |
- (((dfr >> 12) & 0xf) << 24) |
- (((dfr >> 28) & 0xf) << 20) |
+ *vcpu_reg(vcpu, p->Rt) = ((((dfr >> ID_AA64DFR0_WRPS_SHIFT) & 0xf) << 28) |
+ (((dfr >> ID_AA64DFR0_BRPS_SHIFT) & 0xf) << 24) |
+ (((dfr >> ID_AA64DFR0_CTX_CMPS_SHIFT) & 0xf) << 20) |
(6 << 16) | (el3 << 14) | (el3 << 12));
return true;
}
@@ -657,7 +602,7 @@
* register).
*/
static const struct sys_reg_desc cp15_regs[] = {
- { Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_sctlr, NULL, c1_SCTLR },
+ { Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_vm_reg, NULL, c1_SCTLR },
{ Op1( 0), CRn( 2), CRm( 0), Op2( 0), access_vm_reg, NULL, c2_TTBR0 },
{ Op1( 0), CRn( 2), CRm( 0), Op2( 1), access_vm_reg, NULL, c2_TTBR1 },
{ Op1( 0), CRn( 2), CRm( 0), Op2( 2), access_vm_reg, NULL, c2_TTBCR },
diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S
index a9723c7..08b5f18 100644
--- a/arch/arm64/lib/clear_user.S
+++ b/arch/arm64/lib/clear_user.S
@@ -17,10 +17,10 @@
*/
#include <linux/linkage.h>
-#include <asm/alternative.h>
#include <asm/assembler.h>
#include <asm/cpufeature.h>
#include <asm/sysreg.h>
+#include <asm/uaccess.h>
.text
@@ -33,29 +33,27 @@
* Alignment fixed up by hardware.
*/
ENTRY(__clear_user)
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(0)), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)
+ uaccess_enable_not_uao x2, x3
mov x2, x1 // save the size for fixup return
subs x1, x1, #8
b.mi 2f
1:
-USER(9f, str xzr, [x0], #8 )
+uao_user_alternative 9f, str, sttr, xzr, x0, 8
subs x1, x1, #8
b.pl 1b
2: adds x1, x1, #4
b.mi 3f
-USER(9f, str wzr, [x0], #4 )
+uao_user_alternative 9f, str, sttr, wzr, x0, 4
sub x1, x1, #4
3: adds x1, x1, #2
b.mi 4f
-USER(9f, strh wzr, [x0], #2 )
+uao_user_alternative 9f, strh, sttrh, wzr, x0, 2
sub x1, x1, #2
4: adds x1, x1, #1
b.mi 5f
-USER(9f, strb wzr, [x0] )
+uao_user_alternative 9f, strb, sttrb, wzr, x0, 0
5: mov x0, #0
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(1)), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)
+ uaccess_disable_not_uao x2
ret
ENDPROC(__clear_user)
diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S
index 1be9ef2..6505ec8 100644
--- a/arch/arm64/lib/copy_from_user.S
+++ b/arch/arm64/lib/copy_from_user.S
@@ -16,10 +16,11 @@
#include <linux/linkage.h>
-#include <asm/alternative.h>
#include <asm/assembler.h>
+#include <asm/cache.h>
#include <asm/cpufeature.h>
#include <asm/sysreg.h>
+#include <asm/uaccess.h>
/*
* Copy from user space to a kernel buffer (alignment handled by the hardware)
@@ -31,49 +32,56 @@
* Returns:
* x0 - bytes not copied
*/
-ENTRY(__copy_from_user)
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(0)), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)
- add x5, x1, x2 // upper user buffer boundary
- subs x2, x2, #16
- b.mi 1f
-0:
-USER(9f, ldp x3, x4, [x1], #16)
- subs x2, x2, #16
- stp x3, x4, [x0], #16
- b.pl 0b
-1: adds x2, x2, #8
- b.mi 2f
-USER(9f, ldr x3, [x1], #8 )
- sub x2, x2, #8
- str x3, [x0], #8
-2: adds x2, x2, #4
- b.mi 3f
-USER(9f, ldr w3, [x1], #4 )
- sub x2, x2, #4
- str w3, [x0], #4
-3: adds x2, x2, #2
- b.mi 4f
-USER(9f, ldrh w3, [x1], #2 )
- sub x2, x2, #2
- strh w3, [x0], #2
-4: adds x2, x2, #1
- b.mi 5f
-USER(9f, ldrb w3, [x1] )
- strb w3, [x0]
-5: mov x0, #0
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(1)), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)
+
+ .macro ldrb1 ptr, regB, val
+ uao_user_alternative 9998f, ldrb, ldtrb, \ptr, \regB, \val
+ .endm
+
+ .macro strb1 ptr, regB, val
+ strb \ptr, [\regB], \val
+ .endm
+
+ .macro ldrh1 ptr, regB, val
+ uao_user_alternative 9998f, ldrh, ldtrh, \ptr, \regB, \val
+ .endm
+
+ .macro strh1 ptr, regB, val
+ strh \ptr, [\regB], \val
+ .endm
+
+ .macro ldr1 ptr, regB, val
+ uao_user_alternative 9998f, ldr, ldtr, \ptr, \regB, \val
+ .endm
+
+ .macro str1 ptr, regB, val
+ str \ptr, [\regB], \val
+ .endm
+
+ .macro ldp1 ptr, regB, regC, val
+ uao_ldp 9998f, \ptr, \regB, \regC, \val
+ .endm
+
+ .macro stp1 ptr, regB, regC, val
+ stp \ptr, \regB, [\regC], \val
+ .endm
+
+end .req x5
+ENTRY(__arch_copy_from_user)
+ uaccess_enable_not_uao x3, x4
+ add end, x0, x2
+#include "copy_template.S"
+ uaccess_disable_not_uao x3
+ mov x0, #0 // Nothing to copy
ret
-ENDPROC(__copy_from_user)
+ENDPROC(__arch_copy_from_user)
.section .fixup,"ax"
.align 2
-9: sub x2, x5, x1
- mov x3, x2
-10: strb wzr, [x0], #1 // zero remaining buffer space
- subs x3, x3, #1
- b.ne 10b
- mov x0, x2 // bytes not copied
+9998:
+ sub x0, end, dst
+9999:
+ strb wzr, [dst], #1 // zero remaining buffer space
+ cmp dst, end
+ b.lo 9999b
ret
.previous
diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S
index 1b94661e..9b04ff3 100644
--- a/arch/arm64/lib/copy_in_user.S
+++ b/arch/arm64/lib/copy_in_user.S
@@ -18,10 +18,11 @@
#include <linux/linkage.h>
-#include <asm/alternative.h>
#include <asm/assembler.h>
+#include <asm/cache.h>
#include <asm/cpufeature.h>
#include <asm/sysreg.h>
+#include <asm/uaccess.h>
/*
* Copy from user space to user space (alignment handled by the hardware)
@@ -33,44 +34,50 @@
* Returns:
* x0 - bytes not copied
*/
+ .macro ldrb1 ptr, regB, val
+ uao_user_alternative 9998f, ldrb, ldtrb, \ptr, \regB, \val
+ .endm
+
+ .macro strb1 ptr, regB, val
+ uao_user_alternative 9998f, strb, sttrb, \ptr, \regB, \val
+ .endm
+
+ .macro ldrh1 ptr, regB, val
+ uao_user_alternative 9998f, ldrh, ldtrh, \ptr, \regB, \val
+ .endm
+
+ .macro strh1 ptr, regB, val
+ uao_user_alternative 9998f, strh, sttrh, \ptr, \regB, \val
+ .endm
+
+ .macro ldr1 ptr, regB, val
+ uao_user_alternative 9998f, ldr, ldtr, \ptr, \regB, \val
+ .endm
+
+ .macro str1 ptr, regB, val
+ uao_user_alternative 9998f, str, sttr, \ptr, \regB, \val
+ .endm
+
+ .macro ldp1 ptr, regB, regC, val
+ uao_ldp 9998f, \ptr, \regB, \regC, \val
+ .endm
+
+ .macro stp1 ptr, regB, regC, val
+ uao_stp 9998f, \ptr, \regB, \regC, \val
+ .endm
+
+end .req x5
ENTRY(__copy_in_user)
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(0)), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)
- add x5, x0, x2 // upper user buffer boundary
- subs x2, x2, #16
- b.mi 1f
-0:
-USER(9f, ldp x3, x4, [x1], #16)
- subs x2, x2, #16
-USER(9f, stp x3, x4, [x0], #16)
- b.pl 0b
-1: adds x2, x2, #8
- b.mi 2f
-USER(9f, ldr x3, [x1], #8 )
- sub x2, x2, #8
-USER(9f, str x3, [x0], #8 )
-2: adds x2, x2, #4
- b.mi 3f
-USER(9f, ldr w3, [x1], #4 )
- sub x2, x2, #4
-USER(9f, str w3, [x0], #4 )
-3: adds x2, x2, #2
- b.mi 4f
-USER(9f, ldrh w3, [x1], #2 )
- sub x2, x2, #2
-USER(9f, strh w3, [x0], #2 )
-4: adds x2, x2, #1
- b.mi 5f
-USER(9f, ldrb w3, [x1] )
-USER(9f, strb w3, [x0] )
-5: mov x0, #0
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(1)), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)
+ uaccess_enable_not_uao x3, x4
+ add end, x0, x2
+#include "copy_template.S"
+ uaccess_disable_not_uao x3
+ mov x0, #0
ret
ENDPROC(__copy_in_user)
.section .fixup,"ax"
.align 2
-9: sub x0, x5, x0 // bytes not copied
+9998: sub x0, end, dst // bytes not copied
ret
.previous
diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S
index a257b47..8077e4f 100644
--- a/arch/arm64/lib/copy_to_user.S
+++ b/arch/arm64/lib/copy_to_user.S
@@ -16,10 +16,11 @@
#include <linux/linkage.h>
-#include <asm/alternative.h>
#include <asm/assembler.h>
+#include <asm/cache.h>
#include <asm/cpufeature.h>
#include <asm/sysreg.h>
+#include <asm/uaccess.h>
/*
* Copy to user space from a kernel buffer (alignment handled by the hardware)
@@ -31,44 +32,50 @@
* Returns:
* x0 - bytes not copied
*/
-ENTRY(__copy_to_user)
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(0)), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)
- add x5, x0, x2 // upper user buffer boundary
- subs x2, x2, #16
- b.mi 1f
-0:
- ldp x3, x4, [x1], #16
- subs x2, x2, #16
-USER(9f, stp x3, x4, [x0], #16)
- b.pl 0b
-1: adds x2, x2, #8
- b.mi 2f
- ldr x3, [x1], #8
- sub x2, x2, #8
-USER(9f, str x3, [x0], #8 )
-2: adds x2, x2, #4
- b.mi 3f
- ldr w3, [x1], #4
- sub x2, x2, #4
-USER(9f, str w3, [x0], #4 )
-3: adds x2, x2, #2
- b.mi 4f
- ldrh w3, [x1], #2
- sub x2, x2, #2
-USER(9f, strh w3, [x0], #2 )
-4: adds x2, x2, #1
- b.mi 5f
- ldrb w3, [x1]
-USER(9f, strb w3, [x0] )
-5: mov x0, #0
-ALTERNATIVE("nop", __stringify(SET_PSTATE_PAN(1)), ARM64_HAS_PAN, \
- CONFIG_ARM64_PAN)
+ .macro ldrb1 ptr, regB, val
+ ldrb \ptr, [\regB], \val
+ .endm
+
+ .macro strb1 ptr, regB, val
+ uao_user_alternative 9998f, strb, sttrb, \ptr, \regB, \val
+ .endm
+
+ .macro ldrh1 ptr, regB, val
+ ldrh \ptr, [\regB], \val
+ .endm
+
+ .macro strh1 ptr, regB, val
+ uao_user_alternative 9998f, strh, sttrh, \ptr, \regB, \val
+ .endm
+
+ .macro ldr1 ptr, regB, val
+ ldr \ptr, [\regB], \val
+ .endm
+
+ .macro str1 ptr, regB, val
+ uao_user_alternative 9998f, str, sttr, \ptr, \regB, \val
+ .endm
+
+ .macro ldp1 ptr, regB, regC, val
+ ldp \ptr, \regB, [\regC], \val
+ .endm
+
+ .macro stp1 ptr, regB, regC, val
+ uao_stp 9998f, \ptr, \regB, \regC, \val
+ .endm
+
+end .req x5
+ENTRY(__arch_copy_to_user)
+ uaccess_enable_not_uao x3, x4
+ add end, x0, x2
+#include "copy_template.S"
+ uaccess_disable_not_uao x3
+ mov x0, #0
ret
-ENDPROC(__copy_to_user)
+ENDPROC(__arch_copy_to_user)
.section .fixup,"ax"
.align 2
-9: sub x0, x5, x0 // bytes not copied
+9998: sub x0, end, dst // bytes not copied
ret
.previous
diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S
index c0d6ea9..a0b5450 100644
--- a/arch/arm64/mm/cache.S
+++ b/arch/arm64/mm/cache.S
@@ -23,8 +23,7 @@
#include <asm/assembler.h>
#include <asm/cpufeature.h>
#include <asm/alternative.h>
-
-#include "proc-macros.S"
+#include <asm/uaccess.h>
/*
* flush_dcache_louis()
@@ -157,6 +156,7 @@
* - end - virtual end address of region
*/
ENTRY(__flush_cache_user_range)
+ uaccess_enable_not_uao x2, x3
dcache_line_size x2, x3
sub x3, x2, #1
bic x4, x0, x3
@@ -178,6 +178,7 @@
9: // ignore any faulting cache operation
dsb ish
isb
+ uaccess_disable_not_uao x1
ret
ENDPROC(flush_icache_range)
ENDPROC(__flush_cache_user_range)
@@ -247,7 +248,12 @@
dcache_line_size x2, x3
sub x3, x2, #1
bic x0, x0, x3
-1: alternative_insn "dc cvac, x0", "dc civac, x0", ARM64_WORKAROUND_CLEAN_CACHE
+1:
+alternative_if_not ARM64_WORKAROUND_CLEAN_CACHE
+ dc cvac, x0
+alternative_else
+ dc civac, x0
+alternative_endif
add x0, x0, x2
cmp x0, x1
b.lo 1b
@@ -261,6 +267,7 @@
* - end - virtual end address of region
*/
ENTRY(__dma_flush_range)
+ uaccess_enable_not_uao x2, x3
dcache_line_size x2, x3
sub x3, x2, #1
bic x0, x0, x3
@@ -269,6 +276,7 @@
cmp x0, x1
b.lo 1b
dsb sy
+ uaccess_disable_not_uao x1
ret
ENDPIPROC(__dma_flush_range)
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index 76c1e6c..43cff0e 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -17,151 +17,190 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <linux/init.h>
+#include <linux/bitops.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <linux/mm.h>
-#include <linux/smp.h>
-#include <linux/percpu.h>
+#include <asm/cpufeature.h>
#include <asm/mmu_context.h>
#include <asm/tlbflush.h>
-#include <asm/cachetype.h>
-#define asid_bits(reg) \
- (((read_cpuid(ID_AA64MMFR0_EL1) & 0xf0) >> 2) + 8)
-
-#define ASID_FIRST_VERSION (1 << MAX_ASID_BITS)
-
+static u32 asid_bits;
static DEFINE_RAW_SPINLOCK(cpu_asid_lock);
-unsigned int cpu_last_asid = ASID_FIRST_VERSION;
-/*
- * We fork()ed a process, and we need a new context for the child to run in.
- */
-void __init_new_context(struct task_struct *tsk, struct mm_struct *mm)
-{
- mm->context.id = 0;
- raw_spin_lock_init(&mm->context.id_lock);
-}
+static atomic64_t asid_generation;
+static unsigned long *asid_map;
-static void flush_context(void)
+static DEFINE_PER_CPU(atomic64_t, active_asids);
+static DEFINE_PER_CPU(u64, reserved_asids);
+static cpumask_t tlb_flush_pending;
+
+#define ASID_MASK (~GENMASK(asid_bits - 1, 0))
+#define ASID_FIRST_VERSION (1UL << asid_bits)
+#define NUM_USER_ASIDS ASID_FIRST_VERSION
+
+static void flush_context(unsigned int cpu)
{
- /* set the reserved TTBR0 before flushing the TLB */
- cpu_set_reserved_ttbr0();
- flush_tlb_all();
+ int i;
+ u64 asid;
+
+ /* Update the list of reserved ASIDs and the ASID bitmap. */
+ bitmap_clear(asid_map, 0, NUM_USER_ASIDS);
+
+ /*
+ * Ensure the generation bump is observed before we xchg the
+ * active_asids.
+ */
+ smp_wmb();
+
+ for_each_possible_cpu(i) {
+ asid = atomic64_xchg_relaxed(&per_cpu(active_asids, i), 0);
+ /*
+ * If this CPU has already been through a
+ * rollover, but hasn't run another task in
+ * the meantime, we must preserve its reserved
+ * ASID, as this is the only trace we have of
+ * the process it is still running.
+ */
+ if (asid == 0)
+ asid = per_cpu(reserved_asids, i);
+ __set_bit(asid & ~ASID_MASK, asid_map);
+ per_cpu(reserved_asids, i) = asid;
+ }
+
+ /* Queue a TLB invalidate and flush the I-cache if necessary. */
+ cpumask_setall(&tlb_flush_pending);
+
if (icache_is_aivivt())
__flush_icache_all();
}
-#ifdef CONFIG_SMP
+static int is_reserved_asid(u64 asid)
+{
+ int cpu;
+ for_each_possible_cpu(cpu)
+ if (per_cpu(reserved_asids, cpu) == asid)
+ return 1;
+ return 0;
+}
-static void set_mm_context(struct mm_struct *mm, unsigned int asid)
+static u64 new_context(struct mm_struct *mm, unsigned int cpu)
+{
+ static u32 cur_idx = 1;
+ u64 asid = atomic64_read(&mm->context.id);
+ u64 generation = atomic64_read(&asid_generation);
+
+ if (asid != 0) {
+ /*
+ * If our current ASID was active during a rollover, we
+ * can continue to use it and this was just a false alarm.
+ */
+ if (is_reserved_asid(asid))
+ return generation | (asid & ~ASID_MASK);
+
+ /*
+ * We had a valid ASID in a previous life, so try to re-use
+ * it if possible.
+ */
+ asid &= ~ASID_MASK;
+ if (!__test_and_set_bit(asid, asid_map))
+ goto bump_gen;
+ }
+
+ /*
+ * Allocate a free ASID. If we can't find one, take a note of the
+ * currently active ASIDs and mark the TLBs as requiring flushes.
+ * We always count from ASID #1, as we use ASID #0 when setting a
+ * reserved TTBR0 for the init_mm.
+ */
+ asid = find_next_zero_bit(asid_map, NUM_USER_ASIDS, cur_idx);
+ if (asid != NUM_USER_ASIDS)
+ goto set_asid;
+
+ /* We're out of ASIDs, so increment the global generation count */
+ generation = atomic64_add_return_relaxed(ASID_FIRST_VERSION,
+ &asid_generation);
+ flush_context(cpu);
+
+ /* We have at least 1 ASID per CPU, so this will always succeed */
+ asid = find_next_zero_bit(asid_map, NUM_USER_ASIDS, 1);
+
+set_asid:
+ __set_bit(asid, asid_map);
+ cur_idx = asid;
+
+bump_gen:
+ asid |= generation;
+ return asid;
+}
+
+void check_and_switch_context(struct mm_struct *mm, unsigned int cpu)
{
unsigned long flags;
+ u64 asid;
+
+ asid = atomic64_read(&mm->context.id);
/*
- * Locking needed for multi-threaded applications where the same
- * mm->context.id could be set from different CPUs during the
- * broadcast. This function is also called via IPI so the
- * mm->context.id_lock has to be IRQ-safe.
+ * The memory ordering here is subtle. We rely on the control
+ * dependency between the generation read and the update of
+ * active_asids to ensure that we are synchronised with a
+ * parallel rollover (i.e. this pairs with the smp_wmb() in
+ * flush_context).
*/
- raw_spin_lock_irqsave(&mm->context.id_lock, flags);
- if (likely((mm->context.id ^ cpu_last_asid) >> MAX_ASID_BITS)) {
- /*
- * Old version of ASID found. Set the new one and reset
- * mm_cpumask(mm).
- */
- mm->context.id = asid;
- cpumask_clear(mm_cpumask(mm));
- }
- raw_spin_unlock_irqrestore(&mm->context.id_lock, flags);
+ if (!((asid ^ atomic64_read(&asid_generation)) >> asid_bits)
+ && atomic64_xchg_relaxed(&per_cpu(active_asids, cpu), asid))
+ goto switch_mm_fastpath;
- /*
- * Set the mm_cpumask(mm) bit for the current CPU.
- */
- cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
-}
-
-/*
- * Reset the ASID on the current CPU. This function call is broadcast from the
- * CPU handling the ASID rollover and holding cpu_asid_lock.
- */
-static void reset_context(void *info)
-{
- unsigned int asid;
- unsigned int cpu = smp_processor_id();
- struct mm_struct *mm = current->active_mm;
-
- /*
- * current->active_mm could be init_mm for the idle thread immediately
- * after secondary CPU boot or hotplug. TTBR0_EL1 is already set to
- * the reserved value, so no need to reset any context.
- */
- if (mm == &init_mm)
- return;
-
- smp_rmb();
- asid = cpu_last_asid + cpu;
-
- flush_context();
- set_mm_context(mm, asid);
-
- /* set the new ASID */
- cpu_switch_mm(mm->pgd, mm);
-}
-
-#else
-
-static inline void set_mm_context(struct mm_struct *mm, unsigned int asid)
-{
- mm->context.id = asid;
- cpumask_copy(mm_cpumask(mm), cpumask_of(smp_processor_id()));
-}
-
-#endif
-
-void __new_context(struct mm_struct *mm)
-{
- unsigned int asid;
- unsigned int bits = asid_bits();
-
- raw_spin_lock(&cpu_asid_lock);
-#ifdef CONFIG_SMP
- /*
- * Check the ASID again, in case the change was broadcast from another
- * CPU before we acquired the lock.
- */
- if (!unlikely((mm->context.id ^ cpu_last_asid) >> MAX_ASID_BITS)) {
- cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
- raw_spin_unlock(&cpu_asid_lock);
- return;
- }
-#endif
- /*
- * At this point, it is guaranteed that the current mm (with an old
- * ASID) isn't active on any other CPU since the ASIDs are changed
- * simultaneously via IPI.
- */
- asid = ++cpu_last_asid;
-
- /*
- * If we've used up all our ASIDs, we need to start a new version and
- * flush the TLB.
- */
- if (unlikely((asid & ((1 << bits) - 1)) == 0)) {
- /* increment the ASID version */
- cpu_last_asid += (1 << MAX_ASID_BITS) - (1 << bits);
- if (cpu_last_asid == 0)
- cpu_last_asid = ASID_FIRST_VERSION;
- asid = cpu_last_asid + smp_processor_id();
- flush_context();
-#ifdef CONFIG_SMP
- smp_wmb();
- smp_call_function(reset_context, NULL, 1);
-#endif
- cpu_last_asid += NR_CPUS - 1;
+ raw_spin_lock_irqsave(&cpu_asid_lock, flags);
+ /* Check that our ASID belongs to the current generation. */
+ asid = atomic64_read(&mm->context.id);
+ if ((asid ^ atomic64_read(&asid_generation)) >> asid_bits) {
+ asid = new_context(mm, cpu);
+ atomic64_set(&mm->context.id, asid);
}
- set_mm_context(mm, asid);
- raw_spin_unlock(&cpu_asid_lock);
+ if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending))
+ local_flush_tlb_all();
+
+ atomic64_set(&per_cpu(active_asids, cpu), asid);
+ raw_spin_unlock_irqrestore(&cpu_asid_lock, flags);
+
+switch_mm_fastpath:
+ /*
+ * Defer TTBR0_EL1 setting for user threads to uaccess_enable() when
+ * emulating PAN.
+ */
+ if (!system_uses_ttbr0_pan())
+ cpu_switch_mm(mm->pgd, mm);
}
+
+static int asids_init(void)
+{
+ int fld = cpuid_feature_extract_field(read_cpuid(SYS_ID_AA64MMFR0_EL1), 4);
+
+ switch (fld) {
+ default:
+ pr_warn("Unknown ASID size (%d); assuming 8-bit\n", fld);
+ /* Fallthrough */
+ case 0:
+ asid_bits = 8;
+ break;
+ case 2:
+ asid_bits = 16;
+ }
+
+ /* If we end up with more CPUs than ASIDs, expect things to crash */
+ WARN_ON(NUM_USER_ASIDS < num_possible_cpus());
+ atomic64_set(&asid_generation, ASID_FIRST_VERSION);
+ asid_map = kzalloc(BITS_TO_LONGS(NUM_USER_ASIDS) * sizeof(*asid_map),
+ GFP_KERNEL);
+ if (!asid_map)
+ panic("Failed to allocate bitmap for %lu ASIDs\n",
+ NUM_USER_ASIDS);
+
+ pr_info("ASID allocator initialised with %lu entries\n", NUM_USER_ASIDS);
+ return 0;
+}
+early_initcall(asids_init);
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 264fdd4..713b212 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -52,7 +52,7 @@
static struct gen_pool *atomic_pool;
#define NO_KERNEL_MAPPING_DUMMY 0x2222
#define DEFAULT_DMA_COHERENT_POOL_SIZE SZ_256K
-static size_t atomic_pool_size = DEFAULT_DMA_COHERENT_POOL_SIZE;
+static size_t atomic_pool_size __initdata = DEFAULT_DMA_COHERENT_POOL_SIZE;
static int __init early_coherent_pool(char *p)
{
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 4561259..000903c 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -85,6 +85,11 @@
printk("\n");
}
+static bool is_el1_instruction_abort(unsigned int esr)
+{
+ return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_CUR;
+}
+
/*
* The kernel tried to access some page that wasn't present.
*/
@@ -93,8 +98,9 @@
{
/*
* Are we prepared to handle this kernel fault?
+ * We are almost certainly not prepared to handle instruction faults.
*/
- if (fixup_exception(regs))
+ if (!is_el1_instruction_abort(esr) && fixup_exception(regs))
return;
uncached_logk(LOGK_DIE, (void *)regs->pc);
@@ -162,8 +168,6 @@
#define VM_FAULT_BADMAP 0x010000
#define VM_FAULT_BADACCESS 0x020000
-#define ESR_LNX_EXEC (1 << 24)
-
static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
unsigned int mm_flags, unsigned long vm_flags,
struct task_struct *tsk)
@@ -202,6 +206,26 @@
return fault;
}
+static inline bool is_permission_fault(unsigned int esr, struct pt_regs *regs)
+{
+ unsigned int ec = ESR_ELx_EC(esr);
+ unsigned int fsc_type = esr & ESR_ELx_FSC_TYPE;
+
+ if (ec != ESR_ELx_EC_DABT_CUR && ec != ESR_ELx_EC_IABT_CUR)
+ return false;
+
+ if (system_uses_ttbr0_pan())
+ return fsc_type == ESR_ELx_FSC_FAULT &&
+ (regs->pstate & PSR_PAN_BIT);
+ else
+ return fsc_type == ESR_ELx_FSC_PERM;
+}
+
+static bool is_el0_instruction_abort(unsigned int esr)
+{
+ return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_LOW;
+}
+
static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
@@ -228,19 +252,24 @@
if (user_mode(regs))
mm_flags |= FAULT_FLAG_USER;
- if (esr & ESR_LNX_EXEC) {
+ if (is_el0_instruction_abort(esr)) {
vm_flags = VM_EXEC;
- } else if ((esr & ESR_EL1_WRITE) && !(esr & ESR_EL1_CM)) {
+ } else if ((esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM)) {
vm_flags = VM_WRITE;
mm_flags |= FAULT_FLAG_WRITE;
}
- /*
- * PAN bit set implies the fault happened in kernel space, but not
- * in the arch's user access functions.
- */
- if (IS_ENABLED(CONFIG_ARM64_PAN) && (regs->pstate & PSR_PAN_BIT))
- goto no_context;
+ if (addr < USER_DS && is_permission_fault(esr, regs)) {
+ /* regs->orig_addr_limit may be 0 if we entered from EL0 */
+ if (regs->orig_addr_limit == KERNEL_DS)
+ die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
+
+ if (is_el1_instruction_abort(esr))
+ die("Attempting to execute userspace memory", regs, esr);
+
+ if (!search_exception_tables(regs->pc))
+ die("Accessing user space memory outside uaccess.h routines", regs, esr);
+ }
/*
* As per x86, we may deadlock here. However, since the kernel only
@@ -397,7 +426,7 @@
return 1;
}
-static struct fault_info {
+static const struct fault_info {
int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs);
int sig;
int code;
@@ -423,10 +452,10 @@
{ do_bad, SIGBUS, 0, "asynchronous external abort" },
{ do_bad, SIGBUS, 0, "unknown 18" },
{ do_bad, SIGBUS, 0, "unknown 19" },
- { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
- { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
- { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
- { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous external abort (translation table walk)" },
{ do_bad, SIGBUS, 0, "synchronous parity error" },
{ do_bad, SIGBUS, 0, "asynchronous parity error" },
{ do_bad, SIGBUS, 0, "unknown 26" },
@@ -559,8 +588,21 @@
}
#ifdef CONFIG_ARM64_PAN
-void cpu_enable_pan(void)
+void cpu_enable_pan(void *__unused)
{
config_sctlr_el1(SCTLR_EL1_SPAN, 0);
}
#endif /* CONFIG_ARM64_PAN */
+
+#ifdef CONFIG_ARM64_UAO
+/*
+ * Kernel threads have fs=KERNEL_DS by default, and don't need to call
+ * set_fs(), devtmpfs in particular relies on this behaviour.
+ * We need to enable the feature at runtime (instead of adding it to
+ * PSR_MODE_EL1h) as the feature may not be implemented by the cpu.
+ */
+void cpu_enable_uao(void *__unused)
+{
+ asm(SET_PSTATE_UAO(1));
+}
+#endif /* CONFIG_ARM64_UAO */
diff --git a/arch/arm64/mm/flush.c b/arch/arm64/mm/flush.c
index b6f14e8..a90615b 100644
--- a/arch/arm64/mm/flush.c
+++ b/arch/arm64/mm/flush.c
@@ -60,24 +60,16 @@
unsigned long uaddr, void *dst, const void *src,
unsigned long len)
{
-#ifdef CONFIG_SMP
preempt_disable();
-#endif
memcpy(dst, src, len);
flush_ptrace_access(vma, page, uaddr, dst, len);
-#ifdef CONFIG_SMP
preempt_enable();
-#endif
}
void __sync_icache_dcache(pte_t pte, unsigned long addr)
{
struct page *page = pte_page(pte);
- /* no flushing needed for anonymous pages */
- if (!page_mapping(page))
- return;
-
if (!test_and_set_bit(PG_dcache_clean, &page->flags)) {
__flush_dcache_area(page_address(page),
PAGE_SIZE << compound_order(page));
@@ -102,7 +94,6 @@
/*
* Additional functions defined in assembly.
*/
-EXPORT_SYMBOL(flush_cache_all);
EXPORT_SYMBOL(flush_icache_range);
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index bed0646..09dc3a3 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -23,6 +23,7 @@
#include <linux/swap.h>
#include <linux/init.h>
#include <linux/bootmem.h>
+#include <linux/cache.h>
#include <linux/mman.h>
#include <linux/nodemask.h>
#include <linux/initrd.h>
@@ -43,7 +44,7 @@
#include "mm.h"
-phys_addr_t memstart_addr __read_mostly = 0;
+phys_addr_t memstart_addr __ro_after_init = 0;
#ifdef CONFIG_BLK_DEV_INITRD
static int __init early_initrd(char *p)
@@ -68,7 +69,7 @@
* currently assumes that for memory starting above 4G, 32-bit devices will
* use a DMA offset.
*/
-static phys_addr_t max_zone_dma_phys(void)
+static phys_addr_t __init max_zone_dma_phys(void)
{
phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32);
return min(offset + (1ULL << 32), memblock_end_of_DRAM());
@@ -124,11 +125,11 @@
#endif
#ifndef CONFIG_SPARSEMEM
-static void arm64_memory_present(void)
+static void __init arm64_memory_present(void)
{
}
#else
-static void arm64_memory_present(void)
+static void __init arm64_memory_present(void)
{
struct memblock_region *reg;
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index a606027..5df79ae 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -28,6 +28,7 @@
#include <linux/io.h>
#include <linux/dma-contiguous.h>
#include <linux/cma.h>
+#include <linux/slab.h>
#include <linux/stop_machine.h>
#include <asm/cputype.h>
@@ -42,98 +43,17 @@
#include "mm.h"
+u64 idmap_t0sz = TCR_T0SZ(VA_BITS);
+
/*
* Empty_zero_page is a special page that is used for zero-initialized data
* and COW.
*/
-struct page *empty_zero_page;
+unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)] __page_aligned_bss;
EXPORT_SYMBOL(empty_zero_page);
-struct cachepolicy {
- const char policy[16];
- u64 mair;
- u64 tcr;
-};
-
-static struct cachepolicy cache_policies[] __initdata = {
- {
- .policy = "uncached",
- .mair = 0x44, /* inner, outer non-cacheable */
- .tcr = TCR_IRGN_NC | TCR_ORGN_NC,
- }, {
- .policy = "writethrough",
- .mair = 0xaa, /* inner, outer write-through, read-allocate */
- .tcr = TCR_IRGN_WT | TCR_ORGN_WT,
- }, {
- .policy = "writeback",
- .mair = 0xee, /* inner, outer write-back, read-allocate */
- .tcr = TCR_IRGN_WBnWA | TCR_ORGN_WBnWA,
- }
-};
-
static bool __init dma_overlap(phys_addr_t start, phys_addr_t end);
-
-void mem_text_write_kernel_word(u32 *addr, u32 word)
-{
- *addr = word;
- flush_icache_range((unsigned long)addr,
- ((unsigned long)addr + sizeof(long)));
-}
-EXPORT_SYMBOL(mem_text_write_kernel_word);
-
-/*
- * These are useful for identifying cache coherency problems by allowing the
- * cache or the cache and writebuffer to be turned off. It changes the Normal
- * memory caching attributes in the MAIR_EL1 register.
- */
-static int __init early_cachepolicy(char *p)
-{
- int i;
- u64 tmp;
-
- for (i = 0; i < ARRAY_SIZE(cache_policies); i++) {
- int len = strlen(cache_policies[i].policy);
-
- if (memcmp(p, cache_policies[i].policy, len) == 0)
- break;
- }
- if (i == ARRAY_SIZE(cache_policies)) {
- pr_err("ERROR: unknown or unsupported cache policy: %s\n", p);
- return 0;
- }
-
- flush_cache_all();
-
- /*
- * Modify MT_NORMAL attributes in MAIR_EL1.
- */
- asm volatile(
- " mrs %0, mair_el1\n"
- " bfi %0, %1, %2, #8\n"
- " msr mair_el1, %0\n"
- " isb\n"
- : "=&r" (tmp)
- : "r" (cache_policies[i].mair), "i" (MT_NORMAL * 8));
-
- /*
- * Modify TCR PTW cacheability attributes.
- */
- asm volatile(
- " mrs %0, tcr_el1\n"
- " bic %0, %0, %2\n"
- " orr %0, %0, %1\n"
- " msr tcr_el1, %0\n"
- " isb\n"
- : "=&r" (tmp)
- : "r" (cache_policies[i].tcr), "r" (TCR_IRGN_MASK | TCR_ORGN_MASK));
-
- flush_cache_all();
-
- return 0;
-}
-early_param("cachepolicy", early_cachepolicy);
-
pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
unsigned long size, pgprot_t vma_prot)
{
@@ -241,8 +161,14 @@
* Check for previous table entries created during
* boot (__create_page_tables) and flush them.
*/
- if (!pmd_none(old_pmd))
+ if (!pmd_none(old_pmd)) {
flush_tlb_all();
+ if (pmd_table(old_pmd)) {
+ phys_addr_t table = __pa(pte_offset_map(&old_pmd, 0));
+ if (!WARN_ON_ONCE(slab_is_available()))
+ memblock_free(table, PAGE_SIZE);
+ }
+ }
} else {
alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys),
prot, alloc);
@@ -300,9 +226,12 @@
* Look up the old pmd table and free it.
*/
if (!pud_none(old_pud)) {
- phys_addr_t table = __pa(pmd_offset(&old_pud, 0));
- memblock_free(table, PAGE_SIZE);
flush_tlb_all();
+ if (pud_table(old_pud)) {
+ phys_addr_t table = __pa(pmd_offset(&old_pud, 0));
+ if (!WARN_ON_ONCE(slab_is_available()))
+ memblock_free(table, PAGE_SIZE);
+ }
}
} else {
alloc_init_pmd(mm, pud, addr, next, phys, prot, alloc, force_pages);
@@ -434,7 +363,7 @@
pgprot_t prot)
{
__create_mapping(mm, pgd_offset(mm, virt), phys, virt, size, prot,
- early_alloc, false);
+ late_alloc, false);
}
static void create_mapping_late(phys_addr_t phys, unsigned long virt,
@@ -641,9 +570,8 @@
void mark_rodata_ro(void)
{
create_mapping_late(__pa(_stext), (unsigned long)_stext,
- (unsigned long)_etext - (unsigned long)_stext,
+ (unsigned long)__init_begin - (unsigned long)_stext,
PAGE_KERNEL_EXEC | PTE_RDONLY);
-
}
#endif
@@ -660,44 +588,21 @@
*/
void __init paging_init(void)
{
- void *zero_page;
-
map_mem();
dma_contiguous_remap();
remap_pages();
fixup_executable();
- /*
- * Finally flush the caches and tlb to ensure that we're in a
- * consistent state.
- */
- flush_cache_all();
- flush_tlb_all();
-
- /* allocate the zero page. */
- zero_page = early_alloc(PAGE_SIZE);
-
bootmem_init();
- empty_zero_page = virt_to_page(zero_page);
-
/*
* TTBR0 is only used for the identity mapping at this stage. Make it
* point to zero page to avoid speculatively fetching new entries.
*/
cpu_set_reserved_ttbr0();
- flush_tlb_all();
set_kernel_text_ro();
- flush_tlb_all();
-}
-
-/*
- * Enable the identity mapping to allow the MMU disabling.
- */
-void setup_mm_for_reboot(void)
-{
- cpu_switch_mm(idmap_pg_dir, &init_mm);
- flush_tlb_all();
+ local_flush_tlb_all();
+ cpu_set_default_tcr_t0sz();
}
/*
diff --git a/arch/arm64/mm/proc-macros.S b/arch/arm64/mm/proc-macros.S
deleted file mode 100644
index 005d29e..0000000
--- a/arch/arm64/mm/proc-macros.S
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Based on arch/arm/mm/proc-macros.S
- *
- * Copyright (C) 2012 ARM Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <asm/asm-offsets.h>
-#include <asm/thread_info.h>
-
-/*
- * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm)
- */
- .macro vma_vm_mm, rd, rn
- ldr \rd, [\rn, #VMA_VM_MM]
- .endm
-
-/*
- * mmid - get context id from mm pointer (mm->context.id)
- */
- .macro mmid, rd, rn
- ldr \rd, [\rn, #MM_CONTEXT_ID]
- .endm
-
-/*
- * dcache_line_size - get the minimum D-cache line size from the CTR register.
- */
- .macro dcache_line_size, reg, tmp
- mrs \tmp, ctr_el0 // read CTR
- ubfm \tmp, \tmp, #16, #19 // cache line size encoding
- mov \reg, #4 // bytes per word
- lsl \reg, \reg, \tmp // actual cache line size
- .endm
-
-/*
- * icache_line_size - get the minimum I-cache line size from the CTR register.
- */
- .macro icache_line_size, reg, tmp
- mrs \tmp, ctr_el0 // read CTR
- and \tmp, \tmp, #0xf // cache line size encoding
- mov \reg, #4 // bytes per word
- lsl \reg, \reg, \tmp // actual cache line size
- .endm
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 9f0a2b6..d579089 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -23,22 +23,15 @@
#include <asm/assembler.h>
#include <asm/asm-offsets.h>
#include <asm/hwcap.h>
-#include <asm/pgtable-hwdef.h>
#include <asm/pgtable.h>
-#include "proc-macros.S"
-
#ifdef CONFIG_ARM64_64K_PAGES
#define TCR_TG_FLAGS TCR_TG0_64K | TCR_TG1_64K
#else
#define TCR_TG_FLAGS TCR_TG0_4K | TCR_TG1_4K
#endif
-#ifdef CONFIG_SMP
#define TCR_SMP_FLAGS TCR_SHARED
-#else
-#define TCR_SMP_FLAGS 0
-#endif
/* PTWs cacheable, inner/outer WBWA */
#define TCR_CACHE_FLAGS TCR_IRGN_WBWA | TCR_ORGN_WBWA
@@ -46,52 +39,6 @@
#define MAIR(attr, mt) ((attr) << ((mt) * 8))
/*
- * cpu_cache_off()
- *
- * Turn the CPU D-cache off.
- */
-ENTRY(cpu_cache_off)
- mrs x0, sctlr_el1
- bic x0, x0, #1 << 2 // clear SCTLR.C
- msr sctlr_el1, x0
- isb
- ret
-ENDPROC(cpu_cache_off)
-
-/*
- * cpu_reset(loc)
- *
- * Perform a soft reset of the system. Put the CPU into the same state
- * as it would be if it had been reset, and branch to what would be the
- * reset vector. It must be executed with the flat identity mapping.
- *
- * - loc - location to jump to for soft reset
- */
- .align 5
-ENTRY(cpu_reset)
- mrs x1, sctlr_el1
- bic x1, x1, #1
- msr sctlr_el1, x1 // disable the MMU
- isb
- ret x0
-ENDPROC(cpu_reset)
-
-ENTRY(cpu_soft_restart)
- /* Save address of cpu_reset() and reset address */
- mov x19, x0
- mov x20, x1
-
- /* Turn D-cache off */
- bl cpu_cache_off
-
- /* Push out all dirty data, and ensure cache is empty */
- bl flush_cache_all
-
- mov x0, x20
- ret x19
-ENDPROC(cpu_soft_restart)
-
-/*
* cpu_do_idle()
*
* Idle the processor (wait for interrupt).
@@ -156,6 +103,7 @@
msr cpacr_el1, x6
msr ttbr0_el1, x1
msr ttbr1_el1, x7
+ tcr_set_idmap_t0sz x8, x7
msr tcr_el1, x8
msr vbar_el1, x9
msr mdscr_el1, x10
@@ -179,7 +127,7 @@
* - pgd_phys - physical address of new TTB
*/
ENTRY(cpu_do_switch_mm)
- mmid w1, x1 // get mm->context.id
+ mmid x1, x1 // get mm->context.id
bfi x0, x1, #48, #16 // set the ASID
msr ttbr0_el1, x0 // set TTBR0
isb
@@ -195,13 +143,15 @@
* value of the SCTLR_EL1 register.
*/
ENTRY(__cpu_setup)
- ic iallu // I+BTB cache invalidate
- tlbi vmalle1is // invalidate I + D TLBs
- dsb ish
+ tlbi vmalle1 // Invalidate local TLB
+ dsb nsh
mov x0, #3 << 20
msr cpacr_el1, x0 // Enable FP/ASIMD
- msr mdscr_el1, xzr // Reset mdscr_el1
+ mov x0, #1 << 12 // Reset mdscr_el1 and disable
+ msr mdscr_el1, x0 // access to the DCC from EL0
+ isb // Unmask debug exceptions now,
+ enable_dbg // since this is per-cpu
/*
* Memory region attributes for LPAE:
*
@@ -233,6 +183,8 @@
*/
ldr x10, =TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \
TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0
+ tcr_set_idmap_t0sz x10, x9
+
/*
* Read the PARange bits from ID_AA64MMFR0_EL1 and set the IPS bits in
* TCR_EL1.
diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S
index 8bbe940..6d6e4af 100644
--- a/arch/arm64/xen/hypercall.S
+++ b/arch/arm64/xen/hypercall.S
@@ -49,6 +49,7 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#include <asm/uaccess.h>
#include <xen/interface/xen.h>
@@ -89,6 +90,24 @@
mov x2, x3
mov x3, x4
mov x4, x5
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ /*
+ * Privcmd calls are issued by the userspace. The kernel needs to
+ * enable access to TTBR0_EL1 as the hypervisor would issue stage 1
+ * translations to user memory via AT instructions. Since AT
+ * instructions are not affected by the PAN bit (ARMv8.1), we only
+ * need the explicit uaccess_enable/disable if the TTBR0 PAN emulation
+ * is enabled (it implies that hardware UAO and PAN disabled).
+ */
+ uaccess_enable_not_uao x6, x7
+#endif
hvc XEN_IMM
+
+#ifdef CONFIG_ARM64_SW_TTBR0_PAN
+ /*
+ * Disable userspace access from kernel once the hyp call completed.
+ */
+ uaccess_disable_not_uao x6
+#endif
ret
ENDPROC(privcmd_call);
diff --git a/arch/avr32/include/asm/uaccess.h b/arch/avr32/include/asm/uaccess.h
index 245b2ee2..a0a9b8c 100644
--- a/arch/avr32/include/asm/uaccess.h
+++ b/arch/avr32/include/asm/uaccess.h
@@ -74,7 +74,7 @@
extern __kernel_size_t copy_to_user(void __user *to, const void *from,
__kernel_size_t n);
-extern __kernel_size_t copy_from_user(void *to, const void __user *from,
+extern __kernel_size_t ___copy_from_user(void *to, const void __user *from,
__kernel_size_t n);
static inline __kernel_size_t __copy_to_user(void __user *to, const void *from,
@@ -88,6 +88,15 @@
{
return __copy_user(to, (const void __force *)from, n);
}
+static inline __kernel_size_t copy_from_user(void *to,
+ const void __user *from,
+ __kernel_size_t n)
+{
+ size_t res = ___copy_from_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
+}
#define __copy_to_user_inatomic __copy_to_user
#define __copy_from_user_inatomic __copy_from_user
diff --git a/arch/avr32/kernel/avr32_ksyms.c b/arch/avr32/kernel/avr32_ksyms.c
index d93ead0..7c6cf14 100644
--- a/arch/avr32/kernel/avr32_ksyms.c
+++ b/arch/avr32/kernel/avr32_ksyms.c
@@ -36,7 +36,7 @@
/*
* Userspace access stuff.
*/
-EXPORT_SYMBOL(copy_from_user);
+EXPORT_SYMBOL(___copy_from_user);
EXPORT_SYMBOL(copy_to_user);
EXPORT_SYMBOL(__copy_user);
EXPORT_SYMBOL(strncpy_from_user);
diff --git a/arch/avr32/lib/copy_user.S b/arch/avr32/lib/copy_user.S
index ea59c04b..0753734 100644
--- a/arch/avr32/lib/copy_user.S
+++ b/arch/avr32/lib/copy_user.S
@@ -23,13 +23,13 @@
*/
.text
.align 1
- .global copy_from_user
- .type copy_from_user, @function
-copy_from_user:
+ .global ___copy_from_user
+ .type ___copy_from_user, @function
+___copy_from_user:
branch_if_kernel r8, __copy_user
ret_if_privileged r8, r11, r10, r10
rjmp __copy_user
- .size copy_from_user, . - copy_from_user
+ .size ___copy_from_user, . - ___copy_from_user
.global copy_to_user
.type copy_to_user, @function
diff --git a/arch/blackfin/include/asm/uaccess.h b/arch/blackfin/include/asm/uaccess.h
index 57701c3..a992a78 100644
--- a/arch/blackfin/include/asm/uaccess.h
+++ b/arch/blackfin/include/asm/uaccess.h
@@ -177,11 +177,12 @@
static inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
{
- if (access_ok(VERIFY_READ, from, n))
+ if (likely(access_ok(VERIFY_READ, from, n))) {
memcpy(to, (const void __force *)from, n);
- else
- return n;
- return 0;
+ return 0;
+ }
+ memset(to, 0, n);
+ return n;
}
static inline unsigned long __must_check
diff --git a/arch/c6x/kernel/ptrace.c b/arch/c6x/kernel/ptrace.c
index 3c494e8..a511ac1 100644
--- a/arch/c6x/kernel/ptrace.c
+++ b/arch/c6x/kernel/ptrace.c
@@ -69,46 +69,6 @@
0, sizeof(*regs));
}
-static int gpr_set(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
-{
- int ret;
- struct pt_regs *regs = task_pt_regs(target);
-
- /* Don't copyin TSR or CSR */
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- ®s,
- 0, PT_TSR * sizeof(long));
- if (ret)
- return ret;
-
- ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
- PT_TSR * sizeof(long),
- (PT_TSR + 1) * sizeof(long));
- if (ret)
- return ret;
-
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- ®s,
- (PT_TSR + 1) * sizeof(long),
- PT_CSR * sizeof(long));
- if (ret)
- return ret;
-
- ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
- PT_CSR * sizeof(long),
- (PT_CSR + 1) * sizeof(long));
- if (ret)
- return ret;
-
- ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- ®s,
- (PT_CSR + 1) * sizeof(long), -1);
- return ret;
-}
-
enum c6x_regset {
REGSET_GPR,
};
@@ -120,7 +80,6 @@
.size = sizeof(u32),
.align = sizeof(u32),
.get = gpr_get,
- .set = gpr_set
},
};
diff --git a/arch/frv/include/asm/uaccess.h b/arch/frv/include/asm/uaccess.h
index 3ac9a59..87d9e34 100644
--- a/arch/frv/include/asm/uaccess.h
+++ b/arch/frv/include/asm/uaccess.h
@@ -263,19 +263,25 @@
extern long __memset_user(void *dst, unsigned long count);
extern long __memcpy_user(void *dst, const void *src, unsigned long count);
-#define clear_user(dst,count) __memset_user(____force(dst), (count))
+#define __clear_user(dst,count) __memset_user(____force(dst), (count))
#define __copy_from_user_inatomic(to, from, n) __memcpy_user((to), ____force(from), (n))
#define __copy_to_user_inatomic(to, from, n) __memcpy_user(____force(to), (from), (n))
#else
-#define clear_user(dst,count) (memset(____force(dst), 0, (count)), 0)
+#define __clear_user(dst,count) (memset(____force(dst), 0, (count)), 0)
#define __copy_from_user_inatomic(to, from, n) (memcpy((to), ____force(from), (n)), 0)
#define __copy_to_user_inatomic(to, from, n) (memcpy(____force(to), (from), (n)), 0)
#endif
-#define __clear_user clear_user
+static inline unsigned long __must_check
+clear_user(void __user *to, unsigned long n)
+{
+ if (likely(__access_ok(to, n)))
+ n = __clear_user(to, n);
+ return n;
+}
static inline unsigned long __must_check
__copy_to_user(void __user *to, const void *from, unsigned long n)
diff --git a/arch/hexagon/include/asm/cacheflush.h b/arch/hexagon/include/asm/cacheflush.h
index 49e0896..b86f9f3 100644
--- a/arch/hexagon/include/asm/cacheflush.h
+++ b/arch/hexagon/include/asm/cacheflush.h
@@ -21,10 +21,7 @@
#ifndef _ASM_CACHEFLUSH_H
#define _ASM_CACHEFLUSH_H
-#include <linux/cache.h>
-#include <linux/mm.h>
-#include <asm/string.h>
-#include <asm-generic/cacheflush.h>
+#include <linux/mm_types.h>
/* Cache flushing:
*
@@ -41,6 +38,20 @@
#define LINESIZE 32
#define LINEBITS 5
+#define flush_cache_all() do { } while (0)
+#define flush_cache_mm(mm) do { } while (0)
+#define flush_cache_dup_mm(mm) do { } while (0)
+#define flush_cache_range(vma, start, end) do { } while (0)
+#define flush_cache_page(vma, vmaddr, pfn) do { } while (0)
+#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 0
+#define flush_dcache_page(page) do { } while (0)
+#define flush_dcache_mmap_lock(mapping) do { } while (0)
+#define flush_dcache_mmap_unlock(mapping) do { } while (0)
+#define flush_icache_page(vma, pg) do { } while (0)
+#define flush_icache_user_range(vma, pg, adr, len) do { } while (0)
+#define flush_cache_vmap(start, end) do { } while (0)
+#define flush_cache_vunmap(start, end) do { } while (0)
+
/*
* Flush Dcache range through current map.
*/
@@ -49,7 +60,6 @@
/*
* Flush Icache range through current map.
*/
-#undef flush_icache_range
extern void flush_icache_range(unsigned long start, unsigned long end);
/*
@@ -79,19 +89,11 @@
/* generic_ptrace_pokedata doesn't wind up here, does it? */
}
-#undef copy_to_user_page
-static inline void copy_to_user_page(struct vm_area_struct *vma,
- struct page *page,
- unsigned long vaddr,
- void *dst, void *src, int len)
-{
- memcpy(dst, src, len);
- if (vma->vm_flags & VM_EXEC) {
- flush_icache_range((unsigned long) dst,
- (unsigned long) dst + len);
- }
-}
+void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
+ unsigned long vaddr, void *dst, void *src, int len);
+#define copy_from_user_page(vma, page, vaddr, dst, src, len) \
+ memcpy(dst, src, len)
extern void hexagon_inv_dcache_range(unsigned long start, unsigned long end);
extern void hexagon_clean_dcache_range(unsigned long start, unsigned long end);
diff --git a/arch/hexagon/include/asm/io.h b/arch/hexagon/include/asm/io.h
index 7029899..66f5e9a 100644
--- a/arch/hexagon/include/asm/io.h
+++ b/arch/hexagon/include/asm/io.h
@@ -24,14 +24,9 @@
#ifdef __KERNEL__
#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/vmalloc.h>
-#include <asm/string.h>
-#include <asm/mem-layout.h>
#include <asm/iomap.h>
#include <asm/page.h>
#include <asm/cacheflush.h>
-#include <asm/tlbflush.h>
/*
* We don't have PCI yet.
diff --git a/arch/hexagon/include/asm/uaccess.h b/arch/hexagon/include/asm/uaccess.h
index e4127e4..25fc904 100644
--- a/arch/hexagon/include/asm/uaccess.h
+++ b/arch/hexagon/include/asm/uaccess.h
@@ -102,7 +102,8 @@
{
long res = __strnlen_user(src, n);
- /* return from strnlen can't be zero -- that would be rubbish. */
+ if (unlikely(!res))
+ return -EFAULT;
if (res > n) {
copy_from_user(dst, src, n);
diff --git a/arch/hexagon/kernel/setup.c b/arch/hexagon/kernel/setup.c
index 0e7c1db..6981949 100644
--- a/arch/hexagon/kernel/setup.c
+++ b/arch/hexagon/kernel/setup.c
@@ -19,6 +19,7 @@
*/
#include <linux/init.h>
+#include <linux/delay.h>
#include <linux/bootmem.h>
#include <linux/mmzone.h>
#include <linux/mm.h>
diff --git a/arch/hexagon/mm/cache.c b/arch/hexagon/mm/cache.c
index 0c76c80..a7c6d82 100644
--- a/arch/hexagon/mm/cache.c
+++ b/arch/hexagon/mm/cache.c
@@ -127,3 +127,13 @@
local_irq_restore(flags);
mb();
}
+
+void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
+ unsigned long vaddr, void *dst, void *src, int len)
+{
+ memcpy(dst, src, len);
+ if (vma->vm_flags & VM_EXEC) {
+ flush_icache_range((unsigned long) dst,
+ (unsigned long) dst + len);
+ }
+}
diff --git a/arch/hexagon/mm/ioremap.c b/arch/hexagon/mm/ioremap.c
index 5905fd5..d27d672 100644
--- a/arch/hexagon/mm/ioremap.c
+++ b/arch/hexagon/mm/ioremap.c
@@ -20,6 +20,7 @@
#include <linux/io.h>
#include <linux/vmalloc.h>
+#include <linux/mm.h>
void __iomem *ioremap_nocache(unsigned long phys_addr, unsigned long size)
{
diff --git a/arch/ia64/include/asm/uaccess.h b/arch/ia64/include/asm/uaccess.h
index 449c8c0..810926c 100644
--- a/arch/ia64/include/asm/uaccess.h
+++ b/arch/ia64/include/asm/uaccess.h
@@ -262,17 +262,15 @@
__cu_len; \
})
-#define copy_from_user(to, from, n) \
-({ \
- void *__cu_to = (to); \
- const void __user *__cu_from = (from); \
- long __cu_len = (n); \
- \
- __chk_user_ptr(__cu_from); \
- if (__access_ok(__cu_from, __cu_len, get_fs())) \
- __cu_len = __copy_user((__force void __user *) __cu_to, __cu_from, __cu_len); \
- __cu_len; \
-})
+static inline unsigned long
+copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+ if (likely(__access_ok(from, n, get_fs())))
+ n = __copy_user((__force void __user *) to, from, n);
+ else
+ memset(to, 0, n);
+ return n;
+}
#define __copy_in_user(to, from, size) __copy_user((to), (from), (size))
diff --git a/arch/m32r/include/asm/uaccess.h b/arch/m32r/include/asm/uaccess.h
index 84fe7ba..c393e8f5 100644
--- a/arch/m32r/include/asm/uaccess.h
+++ b/arch/m32r/include/asm/uaccess.h
@@ -215,7 +215,7 @@
#define __get_user_nocheck(x,ptr,size) \
({ \
long __gu_err = 0; \
- unsigned long __gu_val; \
+ unsigned long __gu_val = 0; \
might_fault(); \
__get_user_size(__gu_val,(ptr),(size),__gu_err); \
(x) = (__typeof__(*(ptr)))__gu_val; \
diff --git a/arch/metag/include/asm/cmpxchg_lnkget.h b/arch/metag/include/asm/cmpxchg_lnkget.h
index 0154e28..2369ad3 100644
--- a/arch/metag/include/asm/cmpxchg_lnkget.h
+++ b/arch/metag/include/asm/cmpxchg_lnkget.h
@@ -73,7 +73,7 @@
" DCACHE [%2], %0\n"
#endif
"2:\n"
- : "=&d" (temp), "=&da" (retval)
+ : "=&d" (temp), "=&d" (retval)
: "da" (m), "bd" (old), "da" (new)
: "cc"
);
diff --git a/arch/metag/include/asm/uaccess.h b/arch/metag/include/asm/uaccess.h
index 0748b0a..9d52337 100644
--- a/arch/metag/include/asm/uaccess.h
+++ b/arch/metag/include/asm/uaccess.h
@@ -192,19 +192,21 @@
#define strlen_user(str) strnlen_user(str, 32767)
-extern unsigned long __must_check __copy_user_zeroing(void *to,
- const void __user *from,
- unsigned long n);
+extern unsigned long raw_copy_from_user(void *to, const void __user *from,
+ unsigned long n);
static inline unsigned long
copy_from_user(void *to, const void __user *from, unsigned long n)
{
- if (access_ok(VERIFY_READ, from, n))
- return __copy_user_zeroing(to, from, n);
- return n;
+ unsigned long res = n;
+ if (likely(access_ok(VERIFY_READ, from, n)))
+ res = raw_copy_from_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
}
-#define __copy_from_user(to, from, n) __copy_user_zeroing(to, from, n)
+#define __copy_from_user(to, from, n) raw_copy_from_user(to, from, n)
#define __copy_from_user_inatomic __copy_from_user
extern unsigned long __must_check __copy_user(void __user *to,
diff --git a/arch/metag/kernel/ptrace.c b/arch/metag/kernel/ptrace.c
index 7563628..5e2dc7d 100644
--- a/arch/metag/kernel/ptrace.c
+++ b/arch/metag/kernel/ptrace.c
@@ -24,6 +24,16 @@
* user_regset definitions.
*/
+static unsigned long user_txstatus(const struct pt_regs *regs)
+{
+ unsigned long data = (unsigned long)regs->ctx.Flags;
+
+ if (regs->ctx.SaveMask & TBICTX_CBUF_BIT)
+ data |= USER_GP_REGS_STATUS_CATCH_BIT;
+
+ return data;
+}
+
int metag_gp_regs_copyout(const struct pt_regs *regs,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
@@ -62,9 +72,7 @@
if (ret)
goto out;
/* TXSTATUS */
- data = (unsigned long)regs->ctx.Flags;
- if (regs->ctx.SaveMask & TBICTX_CBUF_BIT)
- data |= USER_GP_REGS_STATUS_CATCH_BIT;
+ data = user_txstatus(regs);
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&data, 4*25, 4*26);
if (ret)
@@ -119,6 +127,7 @@
if (ret)
goto out;
/* TXSTATUS */
+ data = user_txstatus(regs);
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&data, 4*25, 4*26);
if (ret)
@@ -244,6 +253,8 @@
unsigned long long *ptr;
int ret, i;
+ if (count < 4*13)
+ return -EINVAL;
/* Read the entire pipeline before making any changes */
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&rp, 0, 4*13);
@@ -303,7 +314,7 @@
const void *kbuf, const void __user *ubuf)
{
int ret;
- void __user *tls;
+ void __user *tls = target->thread.tls_ptr;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1);
if (ret)
diff --git a/arch/metag/lib/usercopy.c b/arch/metag/lib/usercopy.c
index b3ebfe9..2792fc6 100644
--- a/arch/metag/lib/usercopy.c
+++ b/arch/metag/lib/usercopy.c
@@ -29,7 +29,6 @@
COPY \
"1:\n" \
" .section .fixup,\"ax\"\n" \
- " MOV D1Ar1,#0\n" \
FIXUP \
" MOVT D1Ar1,#HI(1b)\n" \
" JUMP D1Ar1,#LO(1b)\n" \
@@ -260,27 +259,31 @@
"MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"22:\n" \
"MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #32\n" \
"23:\n" \
- "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "SUB %3, %3, #32\n" \
"24:\n" \
+ "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "25:\n" \
"MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "26:\n" \
"SUB %3, %3, #32\n" \
"DCACHE [%1+#-64], D0Ar6\n" \
"BR $Lloop"id"\n" \
\
"MOV RAPF, %1\n" \
- "25:\n" \
- "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "26:\n" \
- "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #32\n" \
"27:\n" \
"MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"28:\n" \
"MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %0, %0, #8\n" \
"29:\n" \
+ "SUB %3, %3, #32\n" \
+ "30:\n" \
+ "MGETL D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "31:\n" \
+ "MSETL [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "32:\n" \
+ "SUB %0, %0, #8\n" \
+ "33:\n" \
"SETL [%0++], D0.7, D1.7\n" \
"SUB %3, %3, #32\n" \
"1:" \
@@ -312,11 +315,15 @@
" .long 26b,3b\n" \
" .long 27b,3b\n" \
" .long 28b,3b\n" \
- " .long 29b,4b\n" \
+ " .long 29b,3b\n" \
+ " .long 30b,3b\n" \
+ " .long 31b,3b\n" \
+ " .long 32b,3b\n" \
+ " .long 33b,4b\n" \
" .previous\n" \
: "=r" (to), "=r" (from), "=r" (ret), "=d" (n) \
: "0" (to), "1" (from), "2" (ret), "3" (n) \
- : "D1Ar1", "D0Ar2", "memory")
+ : "D1Ar1", "D0Ar2", "cc", "memory")
/* rewind 'to' and 'from' pointers when a fault occurs
*
@@ -342,7 +349,7 @@
#define __asm_copy_to_user_64bit_rapf_loop(to, from, ret, n, id)\
__asm_copy_user_64bit_rapf_loop(to, from, ret, n, id, \
"LSR D0Ar2, D0Ar2, #8\n" \
- "AND D0Ar2, D0Ar2, #0x7\n" \
+ "ANDS D0Ar2, D0Ar2, #0x7\n" \
"ADDZ D0Ar2, D0Ar2, #4\n" \
"SUB D0Ar2, D0Ar2, #1\n" \
"MOV D1Ar1, #4\n" \
@@ -403,47 +410,55 @@
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"22:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #16\n" \
"23:\n" \
- "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "24:\n" \
- "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
"SUB %3, %3, #16\n" \
- "25:\n" \
+ "24:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "26:\n" \
+ "25:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "26:\n" \
"SUB %3, %3, #16\n" \
"27:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"28:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "29:\n" \
+ "SUB %3, %3, #16\n" \
+ "30:\n" \
+ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "31:\n" \
+ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "32:\n" \
"SUB %3, %3, #16\n" \
"DCACHE [%1+#-64], D0Ar6\n" \
"BR $Lloop"id"\n" \
\
"MOV RAPF, %1\n" \
- "29:\n" \
- "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "30:\n" \
- "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #16\n" \
- "31:\n" \
- "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
- "32:\n" \
- "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #16\n" \
"33:\n" \
"MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"34:\n" \
"MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %3, %3, #16\n" \
"35:\n" \
- "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "SUB %3, %3, #16\n" \
"36:\n" \
- "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
- "SUB %0, %0, #4\n" \
+ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
"37:\n" \
+ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "38:\n" \
+ "SUB %3, %3, #16\n" \
+ "39:\n" \
+ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "40:\n" \
+ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "41:\n" \
+ "SUB %3, %3, #16\n" \
+ "42:\n" \
+ "MGETD D0FrT, D0.5, D0.6, D0.7, [%1++]\n" \
+ "43:\n" \
+ "MSETD [%0++], D0FrT, D0.5, D0.6, D0.7\n" \
+ "44:\n" \
+ "SUB %0, %0, #4\n" \
+ "45:\n" \
"SETD [%0++], D0.7\n" \
"SUB %3, %3, #16\n" \
"1:" \
@@ -483,11 +498,19 @@
" .long 34b,3b\n" \
" .long 35b,3b\n" \
" .long 36b,3b\n" \
- " .long 37b,4b\n" \
+ " .long 37b,3b\n" \
+ " .long 38b,3b\n" \
+ " .long 39b,3b\n" \
+ " .long 40b,3b\n" \
+ " .long 41b,3b\n" \
+ " .long 42b,3b\n" \
+ " .long 43b,3b\n" \
+ " .long 44b,3b\n" \
+ " .long 45b,4b\n" \
" .previous\n" \
: "=r" (to), "=r" (from), "=r" (ret), "=d" (n) \
: "0" (to), "1" (from), "2" (ret), "3" (n) \
- : "D1Ar1", "D0Ar2", "memory")
+ : "D1Ar1", "D0Ar2", "cc", "memory")
/* rewind 'to' and 'from' pointers when a fault occurs
*
@@ -513,7 +536,7 @@
#define __asm_copy_to_user_32bit_rapf_loop(to, from, ret, n, id)\
__asm_copy_user_32bit_rapf_loop(to, from, ret, n, id, \
"LSR D0Ar2, D0Ar2, #8\n" \
- "AND D0Ar2, D0Ar2, #0x7\n" \
+ "ANDS D0Ar2, D0Ar2, #0x7\n" \
"ADDZ D0Ar2, D0Ar2, #4\n" \
"SUB D0Ar2, D0Ar2, #1\n" \
"MOV D1Ar1, #4\n" \
@@ -538,23 +561,31 @@
if ((unsigned long) src & 1) {
__asm_copy_to_user_1(dst, src, retn);
n--;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 1) {
/* Worst case - byte copy */
while (n > 0) {
__asm_copy_to_user_1(dst, src, retn);
n--;
+ if (retn)
+ return retn + n;
}
}
if (((unsigned long) src & 2) && n >= 2) {
__asm_copy_to_user_2(dst, src, retn);
n -= 2;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 2) {
/* Second worst case - word copy */
while (n >= 2) {
__asm_copy_to_user_2(dst, src, retn);
n -= 2;
+ if (retn)
+ return retn + n;
}
}
@@ -569,6 +600,8 @@
while (n >= 8) {
__asm_copy_to_user_8x64(dst, src, retn);
n -= 8;
+ if (retn)
+ return retn + n;
}
}
if (n >= RAPF_MIN_BUF_SIZE) {
@@ -581,6 +614,8 @@
while (n >= 8) {
__asm_copy_to_user_8x64(dst, src, retn);
n -= 8;
+ if (retn)
+ return retn + n;
}
}
#endif
@@ -588,11 +623,15 @@
while (n >= 16) {
__asm_copy_to_user_16(dst, src, retn);
n -= 16;
+ if (retn)
+ return retn + n;
}
while (n >= 4) {
__asm_copy_to_user_4(dst, src, retn);
n -= 4;
+ if (retn)
+ return retn + n;
}
switch (n) {
@@ -609,6 +648,10 @@
break;
}
+ /*
+ * If we get here, retn correctly reflects the number of failing
+ * bytes.
+ */
return retn;
}
EXPORT_SYMBOL(__copy_user);
@@ -617,16 +660,14 @@
__asm_copy_user_cont(to, from, ret, \
" GETB D1Ar1,[%1++]\n" \
"2: SETB [%0++],D1Ar1\n", \
- "3: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
+ "3: ADD %2,%2,#1\n", \
" .long 2b,3b\n")
#define __asm_copy_from_user_2x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
__asm_copy_user_cont(to, from, ret, \
" GETW D1Ar1,[%1++]\n" \
"2: SETW [%0++],D1Ar1\n" COPY, \
- "3: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
+ "3: ADD %2,%2,#2\n" FIXUP, \
" .long 2b,3b\n" TENTRY)
#define __asm_copy_from_user_2(to, from, ret) \
@@ -636,145 +677,26 @@
__asm_copy_from_user_2x_cont(to, from, ret, \
" GETB D1Ar1,[%1++]\n" \
"4: SETB [%0++],D1Ar1\n", \
- "5: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
+ "5: ADD %2,%2,#1\n", \
" .long 4b,5b\n")
#define __asm_copy_from_user_4x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
__asm_copy_user_cont(to, from, ret, \
" GETD D1Ar1,[%1++]\n" \
"2: SETD [%0++],D1Ar1\n" COPY, \
- "3: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
+ "3: ADD %2,%2,#4\n" FIXUP, \
" .long 2b,3b\n" TENTRY)
#define __asm_copy_from_user_4(to, from, ret) \
__asm_copy_from_user_4x_cont(to, from, ret, "", "", "")
-#define __asm_copy_from_user_5(to, from, ret) \
- __asm_copy_from_user_4x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "4: SETB [%0++],D1Ar1\n", \
- "5: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 4b,5b\n")
-
-#define __asm_copy_from_user_6x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_4x_cont(to, from, ret, \
- " GETW D1Ar1,[%1++]\n" \
- "4: SETW [%0++],D1Ar1\n" COPY, \
- "5: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
- " .long 4b,5b\n" TENTRY)
-
-#define __asm_copy_from_user_6(to, from, ret) \
- __asm_copy_from_user_6x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_7(to, from, ret) \
- __asm_copy_from_user_6x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "6: SETB [%0++],D1Ar1\n", \
- "7: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 6b,7b\n")
-
-#define __asm_copy_from_user_8x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_4x_cont(to, from, ret, \
- " GETD D1Ar1,[%1++]\n" \
- "4: SETD [%0++],D1Ar1\n" COPY, \
- "5: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
- " .long 4b,5b\n" TENTRY)
-
-#define __asm_copy_from_user_8(to, from, ret) \
- __asm_copy_from_user_8x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_9(to, from, ret) \
- __asm_copy_from_user_8x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "6: SETB [%0++],D1Ar1\n", \
- "7: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 6b,7b\n")
-
-#define __asm_copy_from_user_10x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_8x_cont(to, from, ret, \
- " GETW D1Ar1,[%1++]\n" \
- "6: SETW [%0++],D1Ar1\n" COPY, \
- "7: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
- " .long 6b,7b\n" TENTRY)
-
-#define __asm_copy_from_user_10(to, from, ret) \
- __asm_copy_from_user_10x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_11(to, from, ret) \
- __asm_copy_from_user_10x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "8: SETB [%0++],D1Ar1\n", \
- "9: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 8b,9b\n")
-
-#define __asm_copy_from_user_12x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_8x_cont(to, from, ret, \
- " GETD D1Ar1,[%1++]\n" \
- "6: SETD [%0++],D1Ar1\n" COPY, \
- "7: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
- " .long 6b,7b\n" TENTRY)
-
-#define __asm_copy_from_user_12(to, from, ret) \
- __asm_copy_from_user_12x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_13(to, from, ret) \
- __asm_copy_from_user_12x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "8: SETB [%0++],D1Ar1\n", \
- "9: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 8b,9b\n")
-
-#define __asm_copy_from_user_14x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_12x_cont(to, from, ret, \
- " GETW D1Ar1,[%1++]\n" \
- "8: SETW [%0++],D1Ar1\n" COPY, \
- "9: ADD %2,%2,#2\n" \
- " SETW [%0++],D1Ar1\n" FIXUP, \
- " .long 8b,9b\n" TENTRY)
-
-#define __asm_copy_from_user_14(to, from, ret) \
- __asm_copy_from_user_14x_cont(to, from, ret, "", "", "")
-
-#define __asm_copy_from_user_15(to, from, ret) \
- __asm_copy_from_user_14x_cont(to, from, ret, \
- " GETB D1Ar1,[%1++]\n" \
- "10: SETB [%0++],D1Ar1\n", \
- "11: ADD %2,%2,#1\n" \
- " SETB [%0++],D1Ar1\n", \
- " .long 10b,11b\n")
-
-#define __asm_copy_from_user_16x_cont(to, from, ret, COPY, FIXUP, TENTRY) \
- __asm_copy_from_user_12x_cont(to, from, ret, \
- " GETD D1Ar1,[%1++]\n" \
- "8: SETD [%0++],D1Ar1\n" COPY, \
- "9: ADD %2,%2,#4\n" \
- " SETD [%0++],D1Ar1\n" FIXUP, \
- " .long 8b,9b\n" TENTRY)
-
-#define __asm_copy_from_user_16(to, from, ret) \
- __asm_copy_from_user_16x_cont(to, from, ret, "", "", "")
-
#define __asm_copy_from_user_8x64(to, from, ret) \
asm volatile ( \
" GETL D0Ar2,D1Ar1,[%1++]\n" \
"2: SETL [%0++],D0Ar2,D1Ar1\n" \
"1:\n" \
" .section .fixup,\"ax\"\n" \
- " MOV D1Ar1,#0\n" \
- " MOV D0Ar2,#0\n" \
"3: ADD %2,%2,#8\n" \
- " SETL [%0++],D0Ar2,D1Ar1\n" \
" MOVT D0Ar2,#HI(1b)\n" \
" JUMP D0Ar2,#LO(1b)\n" \
" .previous\n" \
@@ -789,36 +711,57 @@
*
* Rationale:
* A fault occurs while reading from user buffer, which is the
- * source. Since the fault is at a single address, we only
- * need to rewind by 8 bytes.
+ * source.
* Since we don't write to kernel buffer until we read first,
* the kernel buffer is at the right state and needn't be
- * corrected.
+ * corrected, but the source must be rewound to the beginning of
+ * the block, which is LSM_STEP*8 bytes.
+ * LSM_STEP is bits 10:8 in TXSTATUS which is already read
+ * and stored in D0Ar2
+ *
+ * NOTE: If a fault occurs at the last operation in M{G,S}ETL
+ * LSM_STEP will be 0. ie: we do 4 writes in our case, if
+ * a fault happens at the 4th write, LSM_STEP will be 0
+ * instead of 4. The code copes with that.
*/
#define __asm_copy_from_user_64bit_rapf_loop(to, from, ret, n, id) \
__asm_copy_user_64bit_rapf_loop(to, from, ret, n, id, \
- "SUB %1, %1, #8\n")
+ "LSR D0Ar2, D0Ar2, #5\n" \
+ "ANDS D0Ar2, D0Ar2, #0x38\n" \
+ "ADDZ D0Ar2, D0Ar2, #32\n" \
+ "SUB %1, %1, D0Ar2\n")
/* rewind 'from' pointer when a fault occurs
*
* Rationale:
* A fault occurs while reading from user buffer, which is the
- * source. Since the fault is at a single address, we only
- * need to rewind by 4 bytes.
+ * source.
* Since we don't write to kernel buffer until we read first,
* the kernel buffer is at the right state and needn't be
- * corrected.
+ * corrected, but the source must be rewound to the beginning of
+ * the block, which is LSM_STEP*4 bytes.
+ * LSM_STEP is bits 10:8 in TXSTATUS which is already read
+ * and stored in D0Ar2
+ *
+ * NOTE: If a fault occurs at the last operation in M{G,S}ETL
+ * LSM_STEP will be 0. ie: we do 4 writes in our case, if
+ * a fault happens at the 4th write, LSM_STEP will be 0
+ * instead of 4. The code copes with that.
*/
#define __asm_copy_from_user_32bit_rapf_loop(to, from, ret, n, id) \
__asm_copy_user_32bit_rapf_loop(to, from, ret, n, id, \
- "SUB %1, %1, #4\n")
+ "LSR D0Ar2, D0Ar2, #6\n" \
+ "ANDS D0Ar2, D0Ar2, #0x1c\n" \
+ "ADDZ D0Ar2, D0Ar2, #16\n" \
+ "SUB %1, %1, D0Ar2\n")
-/* Copy from user to kernel, zeroing the bytes that were inaccessible in
- userland. The return-value is the number of bytes that were
- inaccessible. */
-unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
- unsigned long n)
+/*
+ * Copy from user to kernel. The return-value is the number of bytes that were
+ * inaccessible.
+ */
+unsigned long raw_copy_from_user(void *pdst, const void __user *psrc,
+ unsigned long n)
{
register char *dst asm ("A0.2") = pdst;
register const char __user *src asm ("A1.2") = psrc;
@@ -830,6 +773,8 @@
if ((unsigned long) src & 1) {
__asm_copy_from_user_1(dst, src, retn);
n--;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 1) {
/* Worst case - byte copy */
@@ -837,12 +782,14 @@
__asm_copy_from_user_1(dst, src, retn);
n--;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
if (((unsigned long) src & 2) && n >= 2) {
__asm_copy_from_user_2(dst, src, retn);
n -= 2;
+ if (retn)
+ return retn + n;
}
if ((unsigned long) dst & 2) {
/* Second worst case - word copy */
@@ -850,16 +797,10 @@
__asm_copy_from_user_2(dst, src, retn);
n -= 2;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
- /* We only need one check after the unalignment-adjustments,
- because if both adjustments were done, either both or
- neither reference had an exception. */
- if (retn != 0)
- goto copy_exception_bytes;
-
#ifdef USE_RAPF
/* 64 bit copy loop */
if (!(((unsigned long) src | (unsigned long) dst) & 7)) {
@@ -872,7 +813,7 @@
__asm_copy_from_user_8x64(dst, src, retn);
n -= 8;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
@@ -888,7 +829,7 @@
__asm_copy_from_user_8x64(dst, src, retn);
n -= 8;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
}
#endif
@@ -898,7 +839,7 @@
n -= 4;
if (retn)
- goto copy_exception_bytes;
+ return retn + n;
}
/* If we get here, there were no memory read faults. */
@@ -924,21 +865,8 @@
/* If we get here, retn correctly reflects the number of failing
bytes. */
return retn;
-
- copy_exception_bytes:
- /* We already have "retn" bytes cleared, and need to clear the
- remaining "n" bytes. A non-optimized simple byte-for-byte in-line
- memset is preferred here, since this isn't speed-critical code and
- we'd rather have this a leaf-function than calling memset. */
- {
- char *endp;
- for (endp = dst + n; dst < endp; dst++)
- *dst = 0;
- }
-
- return retn + n;
}
-EXPORT_SYMBOL(__copy_user_zeroing);
+EXPORT_SYMBOL(raw_copy_from_user);
#define __asm_clear_8x64(to, ret) \
asm volatile ( \
diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h
index 59a89a6..336be77 100644
--- a/arch/microblaze/include/asm/uaccess.h
+++ b/arch/microblaze/include/asm/uaccess.h
@@ -226,7 +226,7 @@
#define __get_user(x, ptr) \
({ \
- unsigned long __gu_val; \
+ unsigned long __gu_val = 0; \
/*unsigned long __gu_ptr = (unsigned long)(ptr);*/ \
long __gu_err; \
switch (sizeof(*(ptr))) { \
@@ -371,10 +371,13 @@
static inline long copy_from_user(void *to,
const void __user *from, unsigned long n)
{
+ unsigned long res = n;
might_fault();
- if (access_ok(VERIFY_READ, from, n))
- return __copy_from_user(to, from, n);
- return n;
+ if (likely(access_ok(VERIFY_READ, from, n)))
+ res = __copy_from_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
}
#define __copy_to_user(to, from, n) \
diff --git a/arch/mips/ath79/early_printk.c b/arch/mips/ath79/early_printk.c
index b955faf..d1adc59 100644
--- a/arch/mips/ath79/early_printk.c
+++ b/arch/mips/ath79/early_printk.c
@@ -31,13 +31,15 @@
} while (1);
}
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
static void prom_putchar_ar71xx(unsigned char ch)
{
void __iomem *base = (void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE));
- prom_putchar_wait(base + UART_LSR * 4, UART_LSR_THRE, UART_LSR_THRE);
+ prom_putchar_wait(base + UART_LSR * 4, BOTH_EMPTY, BOTH_EMPTY);
__raw_writel(ch, base + UART_TX * 4);
- prom_putchar_wait(base + UART_LSR * 4, UART_LSR_THRE, UART_LSR_THRE);
+ prom_putchar_wait(base + UART_LSR * 4, BOTH_EMPTY, BOTH_EMPTY);
}
static void prom_putchar_ar933x(unsigned char ch)
diff --git a/arch/mips/boot/elf2ecoff.c b/arch/mips/boot/elf2ecoff.c
index 8585078..6950fee 100644
--- a/arch/mips/boot/elf2ecoff.c
+++ b/arch/mips/boot/elf2ecoff.c
@@ -49,7 +49,8 @@
/*
* Some extra ELF definitions
*/
-#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */
+#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */
+#define PT_MIPS_ABIFLAGS 0x70000003 /* Records ABI related flags */
/* -------------------------------------------------------------------- */
@@ -267,7 +268,6 @@
Elf32_Ehdr ex;
Elf32_Phdr *ph;
Elf32_Shdr *sh;
- char *shstrtab;
int i, pad;
struct sect text, data, bss;
struct filehdr efh;
@@ -335,9 +335,6 @@
"sh");
if (must_convert_endian)
convert_elf_shdrs(sh, ex.e_shnum);
- /* Read in the section string table. */
- shstrtab = saveRead(infile, sh[ex.e_shstrndx].sh_offset,
- sh[ex.e_shstrndx].sh_size, "shstrtab");
/* Figure out if we can cram the program header into an ECOFF
header... Basically, we can't handle anything but loadable
@@ -351,7 +348,8 @@
/* Section types we can ignore... */
if (ph[i].p_type == PT_NULL || ph[i].p_type == PT_NOTE ||
ph[i].p_type == PT_PHDR
- || ph[i].p_type == PT_MIPS_REGINFO)
+ || ph[i].p_type == PT_MIPS_REGINFO
+ || ph[i].p_type == PT_MIPS_ABIFLAGS)
continue;
/* Section types we can't handle... */
else if (ph[i].p_type != PT_LOAD) {
diff --git a/arch/mips/configs/ip27_defconfig b/arch/mips/configs/ip27_defconfig
index 48e16d9..6ecc67f 100644
--- a/arch/mips/configs/ip27_defconfig
+++ b/arch/mips/configs/ip27_defconfig
@@ -206,7 +206,6 @@
# CONFIG_MLX4_DEBUG is not set
CONFIG_TEHUTI=m
CONFIG_BNX2X=m
-CONFIG_QLGE=m
CONFIG_SFC=m
CONFIG_BE2NET=m
CONFIG_LIBERTAS_THINFIRM=m
diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S
index 41a2fa1..c7953f2 100644
--- a/arch/mips/dec/int-handler.S
+++ b/arch/mips/dec/int-handler.S
@@ -146,7 +146,25 @@
/*
* Find irq with highest priority
*/
- PTR_LA t1,cpu_mask_nr_tbl
+ # open coded PTR_LA t1, cpu_mask_nr_tbl
+#if (_MIPS_SZPTR == 32)
+ # open coded la t1, cpu_mask_nr_tbl
+ lui t1, %hi(cpu_mask_nr_tbl)
+ addiu t1, %lo(cpu_mask_nr_tbl)
+
+#endif
+#if (_MIPS_SZPTR == 64)
+ # open coded dla t1, cpu_mask_nr_tbl
+ .set push
+ .set noat
+ lui t1, %highest(cpu_mask_nr_tbl)
+ lui AT, %hi(cpu_mask_nr_tbl)
+ daddiu t1, t1, %higher(cpu_mask_nr_tbl)
+ daddiu AT, AT, %lo(cpu_mask_nr_tbl)
+ dsll t1, 32
+ daddu t1, t1, AT
+ .set pop
+#endif
1: lw t2,(t1)
nop
and t2,t0
@@ -195,7 +213,25 @@
/*
* Find irq with highest priority
*/
- PTR_LA t1,asic_mask_nr_tbl
+ # open coded PTR_LA t1,asic_mask_nr_tbl
+#if (_MIPS_SZPTR == 32)
+ # open coded la t1, asic_mask_nr_tbl
+ lui t1, %hi(asic_mask_nr_tbl)
+ addiu t1, %lo(asic_mask_nr_tbl)
+
+#endif
+#if (_MIPS_SZPTR == 64)
+ # open coded dla t1, asic_mask_nr_tbl
+ .set push
+ .set noat
+ lui t1, %highest(asic_mask_nr_tbl)
+ lui AT, %hi(asic_mask_nr_tbl)
+ daddiu t1, t1, %higher(asic_mask_nr_tbl)
+ daddiu AT, AT, %lo(asic_mask_nr_tbl)
+ dsll t1, 32
+ daddu t1, t1, AT
+ .set pop
+#endif
2: lw t2,(t1)
nop
and t2,t0
diff --git a/arch/mips/include/asm/asmmacro.h b/arch/mips/include/asm/asmmacro.h
index 71fef0a..a7ef4fb 100644
--- a/arch/mips/include/asm/asmmacro.h
+++ b/arch/mips/include/asm/asmmacro.h
@@ -134,6 +134,7 @@
ldc1 $f28, THREAD_FPR28_LS64(\thread)
ldc1 $f30, THREAD_FPR30_LS64(\thread)
ctc1 \tmp, fcr31
+ .set pop
.endm
.macro fpu_restore_16odd thread
diff --git a/arch/mips/include/asm/compiler.h b/arch/mips/include/asm/compiler.h
index 71f5c5c..2af7d86 100644
--- a/arch/mips/include/asm/compiler.h
+++ b/arch/mips/include/asm/compiler.h
@@ -16,4 +16,17 @@
#define GCC_REG_ACCUM "accum"
#endif
+#ifdef CONFIG_CPU_MIPSR6
+#define MIPS_ISA_LEVEL "mips64r6"
+#define MIPS_ISA_ARCH_LEVEL MIPS_ISA_LEVEL
+#define MIPS_ISA_LEVEL_RAW mips64r6
+#define MIPS_ISA_ARCH_LEVEL_RAW MIPS_ISA_LEVEL_RAW
+#else
+/* MIPS64 is a superset of MIPS32 */
+#define MIPS_ISA_LEVEL "mips64r2"
+#define MIPS_ISA_ARCH_LEVEL "arch=r4000"
+#define MIPS_ISA_LEVEL_RAW mips64r2
+#define MIPS_ISA_ARCH_LEVEL_RAW MIPS_ISA_LEVEL_RAW
+#endif /* CONFIG_CPU_MIPSR6 */
+
#endif /* _ASM_COMPILER_H */
diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h
index 4e3205a..b369199 100644
--- a/arch/mips/include/asm/kvm_host.h
+++ b/arch/mips/include/asm/kvm_host.h
@@ -377,6 +377,7 @@
#define KVM_MIPS_GUEST_TLB_SIZE 64
struct kvm_vcpu_arch {
void *host_ebase, *guest_ebase;
+ int (*vcpu_run)(struct kvm_run *run, struct kvm_vcpu *vcpu);
unsigned long host_stack;
unsigned long host_gp;
@@ -717,7 +718,7 @@
uint32_t kvm_mips_read_count(struct kvm_vcpu *vcpu);
void kvm_mips_write_count(struct kvm_vcpu *vcpu, uint32_t count);
-void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare);
+void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare, bool ack);
void kvm_mips_init_count(struct kvm_vcpu *vcpu);
int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl);
int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume);
diff --git a/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h b/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h
index 2f82bfa..c9f5769 100644
--- a/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h
+++ b/arch/mips/include/asm/mach-paravirt/kernel-entry-init.h
@@ -11,11 +11,13 @@
#define CP0_EBASE $15, 1
.macro kernel_entry_setup
+#ifdef CONFIG_SMP
mfc0 t0, CP0_EBASE
andi t0, t0, 0x3ff # CPUNum
beqz t0, 1f
# CPUs other than zero goto smp_bootstrap
j smp_bootstrap
+#endif /* CONFIG_SMP */
1:
.endm
diff --git a/arch/mips/include/asm/msa.h b/arch/mips/include/asm/msa.h
index af5638b..38bbeda 100644
--- a/arch/mips/include/asm/msa.h
+++ b/arch/mips/include/asm/msa.h
@@ -67,6 +67,19 @@
_restore_msa(t);
}
+static inline void init_msa_upper(void)
+{
+ /*
+ * Check cpu_has_msa only if it's a constant. This will allow the
+ * compiler to optimise out code for CPUs without MSA without adding
+ * an extra redundant check for CPUs with MSA.
+ */
+ if (__builtin_constant_p(cpu_has_msa) && !cpu_has_msa)
+ return;
+
+ _init_msa_upper();
+}
+
#ifdef TOOLCHAIN_SUPPORTS_MSA
#define __BUILD_MSA_CTL_REG(name, cs) \
diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h
index 060fc2e..825dd09 100644
--- a/arch/mips/include/asm/pgtable.h
+++ b/arch/mips/include/asm/pgtable.h
@@ -596,7 +596,8 @@
static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
{
- pmd_val(pmd) = (pmd_val(pmd) & _PAGE_CHG_MASK) | pgprot_val(newprot);
+ pmd_val(pmd) = (pmd_val(pmd) & (_PAGE_CHG_MASK | _PAGE_HUGE)) |
+ (pgprot_val(newprot) & ~_PAGE_CHG_MASK);
return pmd;
}
diff --git a/arch/mips/include/asm/processor.h b/arch/mips/include/asm/processor.h
index f1df4cb..578ece1 100644
--- a/arch/mips/include/asm/processor.h
+++ b/arch/mips/include/asm/processor.h
@@ -51,7 +51,7 @@
* User space process size: 2GB. This is hardcoded into a few places,
* so don't change it unless you know what you are doing.
*/
-#define TASK_SIZE 0x7fff8000UL
+#define TASK_SIZE 0x80000000UL
#endif
#ifdef __KERNEL__
diff --git a/arch/mips/include/asm/uaccess.h b/arch/mips/include/asm/uaccess.h
index 22a5624..953a75a 100644
--- a/arch/mips/include/asm/uaccess.h
+++ b/arch/mips/include/asm/uaccess.h
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/thread_info.h>
+#include <linux/string.h>
#include <asm/asm-eva.h>
/*
@@ -1136,6 +1137,8 @@
__cu_len = __invoke_copy_from_user(__cu_to, \
__cu_from, \
__cu_len); \
+ } else { \
+ memset(__cu_to, 0, __cu_len); \
} \
} \
__cu_len; \
diff --git a/arch/mips/include/uapi/asm/siginfo.h b/arch/mips/include/uapi/asm/siginfo.h
index e811744..6e1218a 100644
--- a/arch/mips/include/uapi/asm/siginfo.h
+++ b/arch/mips/include/uapi/asm/siginfo.h
@@ -48,13 +48,13 @@
/* kill() */
struct {
- pid_t _pid; /* sender's pid */
+ __kernel_pid_t _pid; /* sender's pid */
__ARCH_SI_UID_T _uid; /* sender's uid */
} _kill;
/* POSIX.1b timers */
struct {
- timer_t _tid; /* timer id */
+ __kernel_timer_t _tid; /* timer id */
int _overrun; /* overrun count */
char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)];
sigval_t _sigval; /* same as below */
@@ -63,26 +63,26 @@
/* POSIX.1b signals */
struct {
- pid_t _pid; /* sender's pid */
+ __kernel_pid_t _pid; /* sender's pid */
__ARCH_SI_UID_T _uid; /* sender's uid */
sigval_t _sigval;
} _rt;
/* SIGCHLD */
struct {
- pid_t _pid; /* which child */
+ __kernel_pid_t _pid; /* which child */
__ARCH_SI_UID_T _uid; /* sender's uid */
int _status; /* exit code */
- clock_t _utime;
- clock_t _stime;
+ __kernel_clock_t _utime;
+ __kernel_clock_t _stime;
} _sigchld;
/* IRIX SIGCHLD */
struct {
- pid_t _pid; /* which child */
- clock_t _utime;
+ __kernel_pid_t _pid; /* which child */
+ __kernel_clock_t _utime;
int _status; /* exit code */
- clock_t _stime;
+ __kernel_clock_t _stime;
} _irix_sigchld;
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c
index c454525..9cfb580 100644
--- a/arch/mips/jz4740/board-qi_lb60.c
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -26,6 +26,7 @@
#include <linux/power/jz4740-battery.h>
#include <linux/power/gpio-charger.h>
+#include <asm/mach-jz4740/gpio.h>
#include <asm/mach-jz4740/jz4740_fb.h>
#include <asm/mach-jz4740/jz4740_mmc.h>
#include <asm/mach-jz4740/jz4740_nand.h>
diff --git a/arch/mips/jz4740/gpio.c b/arch/mips/jz4740/gpio.c
index 00b798d..000d2d9 100644
--- a/arch/mips/jz4740/gpio.c
+++ b/arch/mips/jz4740/gpio.c
@@ -27,6 +27,7 @@
#include <linux/seq_file.h>
#include <asm/mach-jz4740/base.h>
+#include <asm/mach-jz4740/gpio.h>
#include "irq.h"
diff --git a/arch/mips/jz4740/irq.h b/arch/mips/jz4740/irq.h
index 0f48720..486db78 100644
--- a/arch/mips/jz4740/irq.h
+++ b/arch/mips/jz4740/irq.h
@@ -16,7 +16,9 @@
#define __MIPS_JZ4740_IRQ_H__
#include <linux/irq.h>
+#include <asm/mach-jz4740/irq.h>
+struct irq_data;
extern void jz4740_irq_suspend(struct irq_data *data);
extern void jz4740_irq_resume(struct irq_data *data);
diff --git a/arch/mips/kernel/crash.c b/arch/mips/kernel/crash.c
index d2126468..26c7786 100644
--- a/arch/mips/kernel/crash.c
+++ b/arch/mips/kernel/crash.c
@@ -14,12 +14,22 @@
static cpumask_t cpus_in_crash = CPU_MASK_NONE;
#ifdef CONFIG_SMP
-static void crash_shutdown_secondary(void *ignore)
+static void crash_shutdown_secondary(void *passed_regs)
{
- struct pt_regs *regs;
+ struct pt_regs *regs = passed_regs;
int cpu = smp_processor_id();
- regs = task_pt_regs(current);
+ /*
+ * If we are passed registers, use those. Otherwise get the
+ * regs from the last interrupt, which should be correct, as
+ * we are in an interrupt. But if the regs are not there,
+ * pull them from the top of the stack. They are probably
+ * wrong, but we need something to keep from crashing again.
+ */
+ if (!regs)
+ regs = get_irq_regs();
+ if (!regs)
+ regs = task_pt_regs(current);
if (!cpu_online(cpu))
return;
diff --git a/arch/mips/kernel/kgdb.c b/arch/mips/kernel/kgdb.c
index 7afcc2f..37499a2 100644
--- a/arch/mips/kernel/kgdb.c
+++ b/arch/mips/kernel/kgdb.c
@@ -244,9 +244,6 @@
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
{
int reg;
- struct thread_info *ti = task_thread_info(p);
- unsigned long ksp = (unsigned long)ti + THREAD_SIZE - 32;
- struct pt_regs *regs = (struct pt_regs *)ksp - 1;
#if (KGDB_GDB_REG_SIZE == 32)
u32 *ptr = (u32 *)gdb_regs;
#else
@@ -254,25 +251,46 @@
#endif
for (reg = 0; reg < 16; reg++)
- *(ptr++) = regs->regs[reg];
+ *(ptr++) = 0;
/* S0 - S7 */
- for (reg = 16; reg < 24; reg++)
- *(ptr++) = regs->regs[reg];
+ *(ptr++) = p->thread.reg16;
+ *(ptr++) = p->thread.reg17;
+ *(ptr++) = p->thread.reg18;
+ *(ptr++) = p->thread.reg19;
+ *(ptr++) = p->thread.reg20;
+ *(ptr++) = p->thread.reg21;
+ *(ptr++) = p->thread.reg22;
+ *(ptr++) = p->thread.reg23;
for (reg = 24; reg < 28; reg++)
*(ptr++) = 0;
/* GP, SP, FP, RA */
- for (reg = 28; reg < 32; reg++)
- *(ptr++) = regs->regs[reg];
+ *(ptr++) = (long)p;
+ *(ptr++) = p->thread.reg29;
+ *(ptr++) = p->thread.reg30;
+ *(ptr++) = p->thread.reg31;
- *(ptr++) = regs->cp0_status;
- *(ptr++) = regs->lo;
- *(ptr++) = regs->hi;
- *(ptr++) = regs->cp0_badvaddr;
- *(ptr++) = regs->cp0_cause;
- *(ptr++) = regs->cp0_epc;
+ *(ptr++) = p->thread.cp0_status;
+
+ /* lo, hi */
+ *(ptr++) = 0;
+ *(ptr++) = 0;
+
+ /*
+ * BadVAddr, Cause
+ * Ideally these would come from the last exception frame up the stack
+ * but that requires unwinding, otherwise we can't know much for sure.
+ */
+ *(ptr++) = 0;
+ *(ptr++) = 0;
+
+ /*
+ * PC
+ * use return address (RA), i.e. the moment after return from resume()
+ */
+ *(ptr++) = p->thread.reg31;
}
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 636b074..7d09efd 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -437,7 +437,7 @@
*sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) {
regs = (struct pt_regs *)*sp;
pc = regs->cp0_epc;
- if (__kernel_text_address(pc)) {
+ if (!user_mode(regs) && __kernel_text_address(pc)) {
*sp = regs->regs[29];
*ra = regs->regs[31];
return pc;
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 9d1487d..7aaf0dc 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -444,7 +444,8 @@
&target->thread.fpu,
0, sizeof(elf_fpregset_t));
- for (i = 0; i < NUM_FPU_REGS; i++) {
+ BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
+ for (i = 0; i < NUM_FPU_REGS && count >= sizeof(elf_fpreg_t); i++) {
err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&fpr_val, i * sizeof(elf_fpreg_t),
(i + 1) * sizeof(elf_fpreg_t));
diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S
index a8eb657..9a47c12 100644
--- a/arch/mips/kernel/scall64-n32.S
+++ b/arch/mips/kernel/scall64-n32.S
@@ -353,7 +353,7 @@
PTR sys_ni_syscall /* available, was setaltroot */
PTR sys_add_key
PTR sys_request_key
- PTR sys_keyctl /* 6245 */
+ PTR compat_sys_keyctl /* 6245 */
PTR sys_set_thread_area
PTR sys_inotify_init
PTR sys_inotify_add_watch
diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S
index 6f8db9f..8be0757 100644
--- a/arch/mips/kernel/scall64-o32.S
+++ b/arch/mips/kernel/scall64-o32.S
@@ -491,7 +491,7 @@
PTR sys_ni_syscall /* available, was setaltroot */
PTR sys_add_key /* 4280 */
PTR sys_request_key
- PTR sys_keyctl
+ PTR compat_sys_keyctl
PTR sys_set_thread_area
PTR sys_inotify_init
PTR sys_inotify_add_watch /* 4285 */
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 2012a5a..aaa6442 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -141,7 +141,7 @@
if (!task)
task = current;
- if (raw_show_trace || !__kernel_text_address(pc)) {
+ if (raw_show_trace || user_mode(regs) || !__kernel_text_address(pc)) {
show_raw_backtrace(sp);
return;
}
@@ -1103,7 +1103,7 @@
err = init_fpu();
if (msa && !err) {
enable_msa();
- _init_msa_upper();
+ init_msa_upper();
set_thread_flag(TIF_USEDMSA);
set_thread_flag(TIF_MSA_CTX_LIVE);
}
@@ -1166,7 +1166,7 @@
*/
prior_msa = test_and_set_thread_flag(TIF_MSA_CTX_LIVE);
if (!prior_msa && was_fpu_owner) {
- _init_msa_upper();
+ init_msa_upper();
goto out;
}
@@ -1183,7 +1183,7 @@
* of each vector register such that it cannot see data left
* behind by another task.
*/
- _init_msa_upper();
+ init_msa_upper();
} else {
/* We need to restore the vector context. */
restore_msa(current);
diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c
index 838d3a6..f49289f 100644
--- a/arch/mips/kvm/emulate.c
+++ b/arch/mips/kvm/emulate.c
@@ -302,12 +302,31 @@
*/
static uint32_t kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now)
{
- ktime_t expires;
+ struct mips_coproc *cop0 = vcpu->arch.cop0;
+ ktime_t expires, threshold;
+ uint32_t count, compare;
int running;
- /* Is the hrtimer pending? */
+ /* Calculate the biased and scaled guest CP0_Count */
+ count = vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now);
+ compare = kvm_read_c0_guest_compare(cop0);
+
+ /*
+ * Find whether CP0_Count has reached the closest timer interrupt. If
+ * not, we shouldn't inject it.
+ */
+ if ((int32_t)(count - compare) < 0)
+ return count;
+
+ /*
+ * The CP0_Count we're going to return has already reached the closest
+ * timer interrupt. Quickly check if it really is a new interrupt by
+ * looking at whether the interval until the hrtimer expiry time is
+ * less than 1/4 of the timer period.
+ */
expires = hrtimer_get_expires(&vcpu->arch.comparecount_timer);
- if (ktime_compare(now, expires) >= 0) {
+ threshold = ktime_add_ns(now, vcpu->arch.count_period / 4);
+ if (ktime_before(expires, threshold)) {
/*
* Cancel it while we handle it so there's no chance of
* interference with the timeout handler.
@@ -329,8 +348,7 @@
}
}
- /* Return the biased and scaled guest CP0_Count */
- return vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now);
+ return count;
}
/**
@@ -420,32 +438,6 @@
}
/**
- * kvm_mips_update_hrtimer() - Update next expiry time of hrtimer.
- * @vcpu: Virtual CPU.
- *
- * Recalculates and updates the expiry time of the hrtimer. This can be used
- * after timer parameters have been altered which do not depend on the time that
- * the change occurs (in those cases kvm_mips_freeze_hrtimer() and
- * kvm_mips_resume_hrtimer() are used directly).
- *
- * It is guaranteed that no timer interrupts will be lost in the process.
- *
- * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running).
- */
-static void kvm_mips_update_hrtimer(struct kvm_vcpu *vcpu)
-{
- ktime_t now;
- uint32_t count;
-
- /*
- * freeze_hrtimer takes care of a timer interrupts <= count, and
- * resume_hrtimer the hrtimer takes care of a timer interrupts > count.
- */
- now = kvm_mips_freeze_hrtimer(vcpu, &count);
- kvm_mips_resume_hrtimer(vcpu, now, count);
-}
-
-/**
* kvm_mips_write_count() - Modify the count and update timer.
* @vcpu: Virtual CPU.
* @count: Guest CP0_Count value to set.
@@ -540,23 +532,42 @@
* kvm_mips_write_compare() - Modify compare and update timer.
* @vcpu: Virtual CPU.
* @compare: New CP0_Compare value.
+ * @ack: Whether to acknowledge timer interrupt.
*
* Update CP0_Compare to a new value and update the timeout.
+ * If @ack, atomically acknowledge any pending timer interrupt, otherwise ensure
+ * any pending timer interrupt is preserved.
*/
-void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare)
+void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare, bool ack)
{
struct mips_coproc *cop0 = vcpu->arch.cop0;
+ int dc;
+ u32 old_compare = kvm_read_c0_guest_compare(cop0);
+ ktime_t now;
+ uint32_t count;
/* if unchanged, must just be an ack */
- if (kvm_read_c0_guest_compare(cop0) == compare)
+ if (old_compare == compare) {
+ if (!ack)
+ return;
+ kvm_mips_callbacks->dequeue_timer_int(vcpu);
+ kvm_write_c0_guest_compare(cop0, compare);
return;
+ }
- /* Update compare */
+ /* freeze_hrtimer() takes care of timer interrupts <= count */
+ dc = kvm_mips_count_disabled(vcpu);
+ if (!dc)
+ now = kvm_mips_freeze_hrtimer(vcpu, &count);
+
+ if (ack)
+ kvm_mips_callbacks->dequeue_timer_int(vcpu);
+
kvm_write_c0_guest_compare(cop0, compare);
- /* Update timeout if count enabled */
- if (!kvm_mips_count_disabled(vcpu))
- kvm_mips_update_hrtimer(vcpu);
+ /* resume_hrtimer() takes care of timer interrupts > count */
+ if (!dc)
+ kvm_mips_resume_hrtimer(vcpu, now, count);
}
/**
@@ -741,15 +752,15 @@
struct mips_coproc *cop0 = vcpu->arch.cop0;
enum emulation_result er = EMULATE_DONE;
- if (kvm_read_c0_guest_status(cop0) & ST0_EXL) {
+ if (kvm_read_c0_guest_status(cop0) & ST0_ERL) {
+ kvm_clear_c0_guest_status(cop0, ST0_ERL);
+ vcpu->arch.pc = kvm_read_c0_guest_errorepc(cop0);
+ } else if (kvm_read_c0_guest_status(cop0) & ST0_EXL) {
kvm_debug("[%#lx] ERET to %#lx\n", vcpu->arch.pc,
kvm_read_c0_guest_epc(cop0));
kvm_clear_c0_guest_status(cop0, ST0_EXL);
vcpu->arch.pc = kvm_read_c0_guest_epc(cop0);
- } else if (kvm_read_c0_guest_status(cop0) & ST0_ERL) {
- kvm_clear_c0_guest_status(cop0, ST0_ERL);
- vcpu->arch.pc = kvm_read_c0_guest_errorepc(cop0);
} else {
kvm_err("[%#lx] ERET when MIPS_SR_EXL|MIPS_SR_ERL == 0\n",
vcpu->arch.pc);
@@ -1017,9 +1028,9 @@
/* If we are writing to COMPARE */
/* Clear pending timer interrupt, if any */
- kvm_mips_callbacks->dequeue_timer_int(vcpu);
kvm_mips_write_compare(vcpu,
- vcpu->arch.gprs[rt]);
+ vcpu->arch.gprs[rt],
+ true);
} else if ((rd == MIPS_CP0_STATUS) && (sel == 0)) {
kvm_write_c0_guest_status(cop0,
vcpu->arch.gprs[rt]);
diff --git a/arch/mips/kvm/interrupt.h b/arch/mips/kvm/interrupt.h
index 4ab4bdf..2143884 100644
--- a/arch/mips/kvm/interrupt.h
+++ b/arch/mips/kvm/interrupt.h
@@ -28,6 +28,7 @@
#define MIPS_EXC_MAX 12
/* XXXSL More to follow */
+extern char __kvm_mips_vcpu_run_end[];
extern char mips32_exception[], mips32_exceptionEnd[];
extern char mips32_GuestException[], mips32_GuestExceptionEnd[];
diff --git a/arch/mips/kvm/locore.S b/arch/mips/kvm/locore.S
index 4a68b17..21c25757 100644
--- a/arch/mips/kvm/locore.S
+++ b/arch/mips/kvm/locore.S
@@ -231,6 +231,7 @@
/* Jump to guest */
eret
+EXPORT(__kvm_mips_vcpu_run_end)
VECTOR(MIPSX(exception), unknown)
/* Find out what mode we came from and jump to the proper handler. */
diff --git a/arch/mips/kvm/mips.c b/arch/mips/kvm/mips.c
index a53eaf5..26059bf 100644
--- a/arch/mips/kvm/mips.c
+++ b/arch/mips/kvm/mips.c
@@ -306,6 +306,15 @@
memcpy(gebase + offset, mips32_GuestException,
mips32_GuestExceptionEnd - mips32_GuestException);
+#ifdef MODULE
+ offset += mips32_GuestExceptionEnd - mips32_GuestException;
+ memcpy(gebase + offset, (char *)__kvm_mips_vcpu_run,
+ __kvm_mips_vcpu_run_end - (char *)__kvm_mips_vcpu_run);
+ vcpu->arch.vcpu_run = gebase + offset;
+#else
+ vcpu->arch.vcpu_run = __kvm_mips_vcpu_run;
+#endif
+
/* Invalidate the icache for these ranges */
local_flush_icache_range((unsigned long)gebase,
(unsigned long)gebase + ALIGN(size, PAGE_SIZE));
@@ -392,7 +401,7 @@
/* Disable hardware page table walking while in guest */
htw_stop();
- r = __kvm_mips_vcpu_run(run, vcpu);
+ r = vcpu->arch.vcpu_run(run, vcpu);
/* Re-enable HTW before enabling interrupts */
htw_start();
diff --git a/arch/mips/kvm/tlb.c b/arch/mips/kvm/tlb.c
index bbcd822..b814f65 100644
--- a/arch/mips/kvm/tlb.c
+++ b/arch/mips/kvm/tlb.c
@@ -152,7 +152,7 @@
srcu_idx = srcu_read_lock(&kvm->srcu);
pfn = kvm_mips_gfn_to_pfn(kvm, gfn);
- if (kvm_mips_is_error_pfn(pfn)) {
+ if (is_error_noslot_pfn(pfn)) {
kvm_err("Couldn't get pfn for gfn %#" PRIx64 "!\n", gfn);
err = -EFAULT;
goto out;
diff --git a/arch/mips/kvm/trap_emul.c b/arch/mips/kvm/trap_emul.c
index 4372cc8..9bf7b2b 100644
--- a/arch/mips/kvm/trap_emul.c
+++ b/arch/mips/kvm/trap_emul.c
@@ -449,7 +449,7 @@
kvm_mips_write_count(vcpu, v);
break;
case KVM_REG_MIPS_CP0_COMPARE:
- kvm_mips_write_compare(vcpu, v);
+ kvm_mips_write_compare(vcpu, v, false);
break;
case KVM_REG_MIPS_CP0_CAUSE:
/*
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index cac529a..22a2e15 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -443,9 +443,11 @@
case spec_op:
switch (insn.r_format.func) {
case jalr_op:
- regs->regs[insn.r_format.rd] =
- regs->cp0_epc + dec_insn.pc_inc +
- dec_insn.next_pc_inc;
+ if (insn.r_format.rd != 0) {
+ regs->regs[insn.r_format.rd] =
+ regs->cp0_epc + dec_insn.pc_inc +
+ dec_insn.next_pc_inc;
+ }
/* Fall through */
case jr_op:
*contpc = regs->regs[insn.r_format.rs];
diff --git a/arch/mips/pci/ops-pmcmsp.c b/arch/mips/pci/ops-pmcmsp.c
index 50034f9..dd2d9f7 100644
--- a/arch/mips/pci/ops-pmcmsp.c
+++ b/arch/mips/pci/ops-pmcmsp.c
@@ -193,8 +193,6 @@
}
#endif /* CONFIG_PROC_FS && PCI_COUNTERS */
-static DEFINE_SPINLOCK(bpci_lock);
-
/*****************************************************************************
*
* STRUCT: pci_io_resource
@@ -368,7 +366,6 @@
struct msp_pci_regs *preg = (void *)PCI_BASE_REG;
unsigned char bus_num = bus->number;
unsigned char dev_fn = (unsigned char)devfn;
- unsigned long flags;
unsigned long intr;
unsigned long value;
static char pciirqflag;
@@ -401,10 +398,7 @@
}
#if defined(CONFIG_PMC_MSP7120_GW) || defined(CONFIG_PMC_MSP7120_EVAL)
- local_irq_save(flags);
vpe_status = dvpe();
-#else
- spin_lock_irqsave(&bpci_lock, flags);
#endif
/*
@@ -457,9 +451,6 @@
#if defined(CONFIG_PMC_MSP7120_GW) || defined(CONFIG_PMC_MSP7120_EVAL)
evpe(vpe_status);
- local_irq_restore(flags);
-#else
- spin_unlock_irqrestore(&bpci_lock, flags);
#endif
return -1;
@@ -467,9 +458,6 @@
#if defined(CONFIG_PMC_MSP7120_GW) || defined(CONFIG_PMC_MSP7120_EVAL)
evpe(vpe_status);
- local_irq_restore(flags);
-#else
- spin_unlock_irqrestore(&bpci_lock, flags);
#endif
return PCIBIOS_SUCCESSFUL;
diff --git a/arch/mips/ralink/prom.c b/arch/mips/ralink/prom.c
index 9c64f02..87312df 100644
--- a/arch/mips/ralink/prom.c
+++ b/arch/mips/ralink/prom.c
@@ -24,8 +24,10 @@
return soc_info.sys_type;
}
-static __init void prom_init_cmdline(int argc, char **argv)
+static __init void prom_init_cmdline(void)
{
+ int argc;
+ char **argv;
int i;
pr_debug("prom: fw_arg0=%08x fw_arg1=%08x fw_arg2=%08x fw_arg3=%08x\n",
@@ -54,14 +56,11 @@
void __init prom_init(void)
{
- int argc;
- char **argv;
-
prom_soc_init(&soc_info);
pr_info("SoC Type: %s\n", get_system_type());
- prom_init_cmdline(argc, argv);
+ prom_init_cmdline();
}
void __init prom_free_prom_memory(void)
diff --git a/arch/mips/sgi-ip22/Platform b/arch/mips/sgi-ip22/Platform
index b7a4b7e..e8f6b3a 100644
--- a/arch/mips/sgi-ip22/Platform
+++ b/arch/mips/sgi-ip22/Platform
@@ -25,7 +25,7 @@
# Simplified: what IP22 does at 128MB+ in ksegN, IP28 does at 512MB+ in xkphys
#
ifdef CONFIG_SGI_IP28
- ifeq ($(call cc-option-yn,-mr10k-cache-barrier=store), n)
+ ifeq ($(call cc-option-yn,-march=r10000 -mr10k-cache-barrier=store), n)
$(error gcc doesn't support needed option -mr10k-cache-barrier=store)
endif
endif
diff --git a/arch/mn10300/include/asm/uaccess.h b/arch/mn10300/include/asm/uaccess.h
index 5372787..4af43d9 100644
--- a/arch/mn10300/include/asm/uaccess.h
+++ b/arch/mn10300/include/asm/uaccess.h
@@ -181,6 +181,7 @@
"2:\n" \
" .section .fixup,\"ax\"\n" \
"3:\n\t" \
+ " mov 0,%1\n" \
" mov %3,%0\n" \
" jmp 2b\n" \
" .previous\n" \
diff --git a/arch/mn10300/lib/usercopy.c b/arch/mn10300/lib/usercopy.c
index 7826e6c..ce8899e 100644
--- a/arch/mn10300/lib/usercopy.c
+++ b/arch/mn10300/lib/usercopy.c
@@ -9,7 +9,7 @@
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
unsigned long
__generic_copy_to_user(void *to, const void *from, unsigned long n)
@@ -24,6 +24,8 @@
{
if (access_ok(VERIFY_READ, from, n))
__copy_user_zeroing(to, from, n);
+ else
+ memset(to, 0, n);
return n;
}
diff --git a/arch/openrisc/include/asm/uaccess.h b/arch/openrisc/include/asm/uaccess.h
index ab2e7a1..d441480 100644
--- a/arch/openrisc/include/asm/uaccess.h
+++ b/arch/openrisc/include/asm/uaccess.h
@@ -273,28 +273,20 @@
static inline unsigned long
copy_from_user(void *to, const void *from, unsigned long n)
{
- unsigned long over;
+ unsigned long res = n;
- if (access_ok(VERIFY_READ, from, n))
- return __copy_tofrom_user(to, from, n);
- if ((unsigned long)from < TASK_SIZE) {
- over = (unsigned long)from + n - TASK_SIZE;
- return __copy_tofrom_user(to, from, n - over) + over;
- }
- return n;
+ if (likely(access_ok(VERIFY_READ, from, n)))
+ res = __copy_tofrom_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
}
static inline unsigned long
copy_to_user(void *to, const void *from, unsigned long n)
{
- unsigned long over;
-
- if (access_ok(VERIFY_WRITE, to, n))
- return __copy_tofrom_user(to, from, n);
- if ((unsigned long)to < TASK_SIZE) {
- over = (unsigned long)to + n - TASK_SIZE;
- return __copy_tofrom_user(to, from, n - over) + over;
- }
+ if (likely(access_ok(VERIFY_WRITE, to, n)))
+ n = __copy_tofrom_user(to, from, n);
return n;
}
@@ -303,13 +295,8 @@
static inline __must_check unsigned long
clear_user(void *addr, unsigned long size)
{
-
- if (access_ok(VERIFY_WRITE, addr, size))
- return __clear_user(addr, size);
- if ((unsigned long)addr < TASK_SIZE) {
- unsigned long over = (unsigned long)addr + size - TASK_SIZE;
- return __clear_user(addr, size - over) + over;
- }
+ if (likely(access_ok(VERIFY_WRITE, addr, size)))
+ size = __clear_user(addr, size);
return size;
}
diff --git a/arch/parisc/include/asm/cache.h b/arch/parisc/include/asm/cache.h
index 47f11c7..03d4d5a 100644
--- a/arch/parisc/include/asm/cache.h
+++ b/arch/parisc/include/asm/cache.h
@@ -30,6 +30,9 @@
#define __read_mostly __attribute__((__section__(".data..read_mostly")))
+/* Read-only memory is marked before mark_rodata_ro() is called. */
+#define __ro_after_init __read_mostly
+
void parisc_cache_init(void); /* initializes cache-flushing */
void disable_sr_hashing_asm(int); /* low level support for above */
void disable_sr_hashing(void); /* turns off space register hashing */
diff --git a/arch/parisc/include/asm/cacheflush.h b/arch/parisc/include/asm/cacheflush.h
index de65f66..b188d1f 100644
--- a/arch/parisc/include/asm/cacheflush.h
+++ b/arch/parisc/include/asm/cacheflush.h
@@ -121,10 +121,6 @@
}
}
-#ifdef CONFIG_DEBUG_RODATA
-void mark_rodata_ro(void);
-#endif
-
#include <asm/kmap_types.h>
#define ARCH_HAS_KMAP
diff --git a/arch/parisc/include/asm/uaccess.h b/arch/parisc/include/asm/uaccess.h
index a5cb070..3c38f85 100644
--- a/arch/parisc/include/asm/uaccess.h
+++ b/arch/parisc/include/asm/uaccess.h
@@ -10,6 +10,7 @@
#include <asm-generic/uaccess-unaligned.h>
#include <linux/bug.h>
+#include <linux/string.h>
#define VERIFY_READ 0
#define VERIFY_WRITE 1
@@ -76,6 +77,7 @@
*/
struct exception_data {
unsigned long fault_ip;
+ unsigned long fault_gp;
unsigned long fault_space;
unsigned long fault_addr;
};
@@ -244,13 +246,14 @@
unsigned long n)
{
int sz = __compiletime_object_size(to);
- int ret = -EFAULT;
+ unsigned long ret = n;
if (likely(sz == -1 || !__builtin_constant_p(n) || sz >= n))
ret = __copy_from_user(to, from, n);
else
copy_from_user_overflow();
-
+ if (unlikely(ret))
+ memset(to + (n - ret), 0, ret);
return ret;
}
diff --git a/arch/parisc/include/uapi/asm/errno.h b/arch/parisc/include/uapi/asm/errno.h
index c0ae625..274d5bc 100644
--- a/arch/parisc/include/uapi/asm/errno.h
+++ b/arch/parisc/include/uapi/asm/errno.h
@@ -97,10 +97,10 @@
#define ENOTCONN 235 /* Transport endpoint is not connected */
#define ESHUTDOWN 236 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS 237 /* Too many references: cannot splice */
-#define EREFUSED ECONNREFUSED /* for HP's NFS apparently */
#define ETIMEDOUT 238 /* Connection timed out */
#define ECONNREFUSED 239 /* Connection refused */
-#define EREMOTERELEASE 240 /* Remote peer released connection */
+#define EREFUSED ECONNREFUSED /* for HP's NFS apparently */
+#define EREMOTERELEASE 240 /* Remote peer released connection */
#define EHOSTDOWN 241 /* Host is down */
#define EHOSTUNREACH 242 /* No route to host */
diff --git a/arch/parisc/kernel/asm-offsets.c b/arch/parisc/kernel/asm-offsets.c
index dcd5510..a0dc1e5 100644
--- a/arch/parisc/kernel/asm-offsets.c
+++ b/arch/parisc/kernel/asm-offsets.c
@@ -292,6 +292,7 @@
DEFINE(ASM_PT_INITIAL, PT_INITIAL);
BLANK();
DEFINE(EXCDATA_IP, offsetof(struct exception_data, fault_ip));
+ DEFINE(EXCDATA_GP, offsetof(struct exception_data, fault_gp));
DEFINE(EXCDATA_SPACE, offsetof(struct exception_data, fault_space));
DEFINE(EXCDATA_ADDR, offsetof(struct exception_data, fault_addr));
BLANK();
diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c
index 568b2c6..3cad8aa 100644
--- a/arch/parisc/kernel/parisc_ksyms.c
+++ b/arch/parisc/kernel/parisc_ksyms.c
@@ -47,11 +47,11 @@
EXPORT_SYMBOL(lclear_user);
EXPORT_SYMBOL(lstrnlen_user);
-/* Global fixups */
-extern void fixup_get_user_skip_1(void);
-extern void fixup_get_user_skip_2(void);
-extern void fixup_put_user_skip_1(void);
-extern void fixup_put_user_skip_2(void);
+/* Global fixups - defined as int to avoid creation of function pointers */
+extern int fixup_get_user_skip_1;
+extern int fixup_get_user_skip_2;
+extern int fixup_put_user_skip_1;
+extern int fixup_put_user_skip_2;
EXPORT_SYMBOL(fixup_get_user_skip_1);
EXPORT_SYMBOL(fixup_get_user_skip_2);
EXPORT_SYMBOL(fixup_put_user_skip_1);
diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c
index 9585c81..ce0b2b4 100644
--- a/arch/parisc/kernel/ptrace.c
+++ b/arch/parisc/kernel/ptrace.c
@@ -269,14 +269,19 @@
long do_syscall_trace_enter(struct pt_regs *regs)
{
- long ret = 0;
-
/* Do the secure computing check first. */
secure_computing_strict(regs->gr[20]);
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
- tracehook_report_syscall_entry(regs))
- ret = -1L;
+ tracehook_report_syscall_entry(regs)) {
+ /*
+ * Tracing decided this syscall should not happen or the
+ * debugger stored an invalid system call number. Skip
+ * the system call and the system call restart handling.
+ */
+ regs->gr[20] = -1UL;
+ goto out;
+ }
#ifdef CONFIG_64BIT
if (!is_compat_task())
@@ -290,7 +295,8 @@
regs->gr[24] & 0xffffffff,
regs->gr[23] & 0xffffffff);
- return ret ? : regs->gr[20];
+out:
+ return regs->gr[20];
}
void do_syscall_trace_exit(struct pt_regs *regs)
diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S
index 0b8d26d..8f13c7f 100644
--- a/arch/parisc/kernel/syscall.S
+++ b/arch/parisc/kernel/syscall.S
@@ -106,8 +106,6 @@
mtsp %r0,%sr4 /* get kernel space into sr4 */
mtsp %r0,%sr5 /* get kernel space into sr5 */
mtsp %r0,%sr6 /* get kernel space into sr6 */
- mfsp %sr7,%r1 /* save user sr7 */
- mtsp %r1,%sr3 /* and store it in sr3 */
#ifdef CONFIG_64BIT
/* for now we can *always* set the W bit on entry to the syscall
@@ -133,6 +131,14 @@
depdi 0, 31, 32, %r21
1:
#endif
+
+ /* We use a rsm/ssm pair to prevent sr3 from being clobbered
+ * by external interrupts.
+ */
+ mfsp %sr7,%r1 /* save user sr7 */
+ rsm PSW_SM_I, %r0 /* disable interrupts */
+ mtsp %r1,%sr3 /* and store it in sr3 */
+
mfctl %cr30,%r1
xor %r1,%r30,%r30 /* ye olde xor trick */
xor %r1,%r30,%r1
@@ -147,6 +153,7 @@
*/
mtsp %r0,%sr7 /* get kernel space into sr7 */
+ ssm PSW_SM_I, %r0 /* enable interrupts */
STREGM %r1,FRAME_SIZE(%r30) /* save r1 (usp) here for now */
mfctl %cr30,%r1 /* get task ptr in %r1 */
LDREG TI_TASK(%r1),%r1
@@ -342,8 +349,8 @@
stw %r21, -56(%r30) /* 6th argument */
#endif
- comiclr,>>= __NR_Linux_syscalls, %r20, %r0
- b,n .Lsyscall_nosys
+ comiclr,>> __NR_Linux_syscalls, %r20, %r0
+ b,n .Ltracesys_nosys
LDREGX %r20(%r19), %r19
@@ -359,6 +366,9 @@
be 0(%sr7,%r19)
ldo R%tracesys_exit(%r2),%r2
+.Ltracesys_nosys:
+ ldo -ENOSYS(%r0),%r28 /* set errno */
+
/* Do *not* call this function on the gateway page, because it
makes a direct call to syscall_trace. */
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c
index 47ee620..05aab13 100644
--- a/arch/parisc/kernel/traps.c
+++ b/arch/parisc/kernel/traps.c
@@ -802,6 +802,9 @@
if (fault_space == 0 && !in_atomic())
{
+ /* Clean up and return if in exception table. */
+ if (fixup_exception(regs))
+ return;
pdc_chassis_send_status(PDC_CHASSIS_DIRECT_PANIC);
parisc_terminate("Kernel Fault", regs, code, fault_address);
}
diff --git a/arch/parisc/kernel/unaligned.c b/arch/parisc/kernel/unaligned.c
index d7c0acb..8d49614 100644
--- a/arch/parisc/kernel/unaligned.c
+++ b/arch/parisc/kernel/unaligned.c
@@ -666,7 +666,7 @@
break;
}
- if (modify && R1(regs->iir))
+ if (ret == 0 && modify && R1(regs->iir))
regs->gr[R1(regs->iir)] = newbase;
@@ -677,6 +677,14 @@
if (ret)
{
+ /*
+ * The unaligned handler failed.
+ * If we were called by __get_user() or __put_user() jump
+ * to it's exception fixup handler instead of crashing.
+ */
+ if (!user_mode(regs) && fixup_exception(regs))
+ return;
+
printk(KERN_CRIT "Unaligned handler failed, ret = %d\n", ret);
die_if_kernel("Unaligned data reference", regs, 28);
diff --git a/arch/parisc/lib/fixup.S b/arch/parisc/lib/fixup.S
index f8c45cc..1052b74 100644
--- a/arch/parisc/lib/fixup.S
+++ b/arch/parisc/lib/fixup.S
@@ -26,6 +26,7 @@
#ifdef CONFIG_SMP
.macro get_fault_ip t1 t2
+ loadgp
addil LT%__per_cpu_offset,%r27
LDREG RT%__per_cpu_offset(%r1),\t1
/* t2 = smp_processor_id() */
@@ -38,16 +39,21 @@
LDREGX \t2(\t1),\t2
addil LT%exception_data,%r27
LDREG RT%exception_data(%r1),\t1
- /* t1 = &__get_cpu_var(exception_data) */
+ /* t1 = this_cpu_ptr(&exception_data) */
add,l \t1,\t2,\t1
+ /* %r27 = t1->fault_gp - restore gp */
+ LDREG EXCDATA_GP(\t1), %r27
/* t1 = t1->fault_ip */
LDREG EXCDATA_IP(\t1), \t1
.endm
#else
.macro get_fault_ip t1 t2
- /* t1 = &__get_cpu_var(exception_data) */
+ loadgp
+ /* t1 = this_cpu_ptr(&exception_data) */
addil LT%exception_data,%r27
LDREG RT%exception_data(%r1),\t2
+ /* %r27 = t2->fault_gp - restore gp */
+ LDREG EXCDATA_GP(\t2), %r27
/* t1 = t2->fault_ip */
LDREG EXCDATA_IP(\t2), \t1
.endm
diff --git a/arch/parisc/mm/fault.c b/arch/parisc/mm/fault.c
index e5120e6..50d64a7 100644
--- a/arch/parisc/mm/fault.c
+++ b/arch/parisc/mm/fault.c
@@ -151,6 +151,7 @@
struct exception_data *d;
d = this_cpu_ptr(&exception_data);
d->fault_ip = regs->iaoq[0];
+ d->fault_gp = regs->gr[27];
d->fault_space = regs->isr;
d->fault_addr = regs->ior;
diff --git a/arch/powerpc/boot/ps3-head.S b/arch/powerpc/boot/ps3-head.S
index b6fcbaf..3dc44b0 100644
--- a/arch/powerpc/boot/ps3-head.S
+++ b/arch/powerpc/boot/ps3-head.S
@@ -57,11 +57,6 @@
bctr
1:
- /* Save the value at addr zero for a null pointer write check later. */
-
- li r4, 0
- lwz r3, 0(r4)
-
/* Primary delays then goes to _zimage_start in wrapper. */
or 31, 31, 31 /* db16cyc */
diff --git a/arch/powerpc/boot/ps3.c b/arch/powerpc/boot/ps3.c
index 4ec2d86..a05558a 100644
--- a/arch/powerpc/boot/ps3.c
+++ b/arch/powerpc/boot/ps3.c
@@ -119,13 +119,12 @@
flush_cache((void *)0x100, 512);
}
-void platform_init(unsigned long null_check)
+void platform_init(void)
{
const u32 heapsize = 0x1000000 - (u32)_end; /* 16MiB */
void *chosen;
unsigned long ft_addr;
u64 rm_size;
- unsigned long val;
console_ops.write = ps3_console_write;
platform_ops.exit = ps3_exit;
@@ -153,11 +152,6 @@
printf(" flat tree at 0x%lx\n\r", ft_addr);
- val = *(unsigned long *)0;
-
- if (val != null_check)
- printf("null check failed: %lx != %lx\n\r", val, null_check);
-
((kernel_entry_t)0)(ft_addr, 0, NULL);
ps3_exit();
diff --git a/arch/powerpc/boot/zImage.lds.S b/arch/powerpc/boot/zImage.lds.S
index 861e721..f080abf 100644
--- a/arch/powerpc/boot/zImage.lds.S
+++ b/arch/powerpc/boot/zImage.lds.S
@@ -68,6 +68,7 @@
}
#ifdef CONFIG_PPC64_BOOT_WRAPPER
+ . = ALIGN(256);
.got :
{
__toc_start = .;
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index 0478556..9bb51b9 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -537,6 +537,7 @@
u64 tfiar;
u32 cr_tm;
+ u64 xer_tm;
u64 lr_tm;
u64 ctr_tm;
u64 amr_tm;
diff --git a/arch/powerpc/include/asm/pgtable-ppc64.h b/arch/powerpc/include/asm/pgtable-ppc64.h
index daf4add..59830c8 100644
--- a/arch/powerpc/include/asm/pgtable-ppc64.h
+++ b/arch/powerpc/include/asm/pgtable-ppc64.h
@@ -479,6 +479,7 @@
}
#define pmd_pfn(pmd) pte_pfn(pmd_pte(pmd))
+#define pmd_dirty(pmd) pte_dirty(pmd_pte(pmd))
#define pmd_young(pmd) pte_young(pmd_pte(pmd))
#define pmd_mkold(pmd) pte_pmd(pte_mkold(pmd_pte(pmd)))
#define pmd_wrprotect(pmd) pte_pmd(pte_wrprotect(pmd_pte(pmd)))
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index a68ee15..32fd9f6 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -703,7 +703,7 @@
#define MMCR0_FCWAIT 0x00000002UL /* freeze counter in WAIT state */
#define MMCR0_FCHV 0x00000001UL /* freeze conditions in hypervisor mode */
#define SPRN_MMCR1 798
-#define SPRN_MMCR2 769
+#define SPRN_MMCR2 785
#define SPRN_MMCRA 0x312
#define MMCRA_SDSYNC 0x80000000UL /* SDAR synced with SIAR */
#define MMCRA_SDAR_DCACHE_MISS 0x40000000UL
@@ -739,13 +739,13 @@
#define SPRN_PMC6 792
#define SPRN_PMC7 793
#define SPRN_PMC8 794
-#define SPRN_SIAR 780
-#define SPRN_SDAR 781
#define SPRN_SIER 784
#define SIER_SIPR 0x2000000 /* Sampled MSR_PR */
#define SIER_SIHV 0x1000000 /* Sampled MSR_HV */
#define SIER_SIAR_VALID 0x0400000 /* SIAR contents valid */
#define SIER_SDAR_VALID 0x0200000 /* SDAR contents valid */
+#define SPRN_SIAR 796
+#define SPRN_SDAR 797
#define SPRN_TACR 888
#define SPRN_TCSCR 889
#define SPRN_CSIGR 890
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index 9485b43..46c4865 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -323,30 +323,17 @@
static inline unsigned long copy_from_user(void *to,
const void __user *from, unsigned long n)
{
- unsigned long over;
-
- if (access_ok(VERIFY_READ, from, n))
+ if (likely(access_ok(VERIFY_READ, from, n)))
return __copy_tofrom_user((__force void __user *)to, from, n);
- if ((unsigned long)from < TASK_SIZE) {
- over = (unsigned long)from + n - TASK_SIZE;
- return __copy_tofrom_user((__force void __user *)to, from,
- n - over) + over;
- }
+ memset(to, 0, n);
return n;
}
static inline unsigned long copy_to_user(void __user *to,
const void *from, unsigned long n)
{
- unsigned long over;
-
if (access_ok(VERIFY_WRITE, to, n))
return __copy_tofrom_user(to, (__force void __user *)from, n);
- if ((unsigned long)to < TASK_SIZE) {
- over = (unsigned long)to + n - TASK_SIZE;
- return __copy_tofrom_user(to, (__force void __user *)from,
- n - over) + over;
- }
return n;
}
@@ -437,10 +424,6 @@
might_fault();
if (likely(access_ok(VERIFY_WRITE, addr, size)))
return __clear_user(addr, size);
- if ((unsigned long)addr < TASK_SIZE) {
- unsigned long over = (unsigned long)addr + size - TASK_SIZE;
- return __clear_user(addr, size - over) + over;
- }
return size;
}
diff --git a/arch/powerpc/include/asm/word-at-a-time.h b/arch/powerpc/include/asm/word-at-a-time.h
index 5b3a903..7043539 100644
--- a/arch/powerpc/include/asm/word-at-a-time.h
+++ b/arch/powerpc/include/asm/word-at-a-time.h
@@ -77,7 +77,7 @@
"andc %1,%1,%2\n\t"
"popcntd %0,%1"
: "=r" (leading_zero_bits), "=&r" (trailing_zero_bit_mask)
- : "r" (bits));
+ : "b" (bits));
return leading_zero_bits;
}
diff --git a/arch/powerpc/include/uapi/asm/cputable.h b/arch/powerpc/include/uapi/asm/cputable.h
index de2c0e4..67de80a 100644
--- a/arch/powerpc/include/uapi/asm/cputable.h
+++ b/arch/powerpc/include/uapi/asm/cputable.h
@@ -31,6 +31,7 @@
#define PPC_FEATURE_PSERIES_PERFMON_COMPAT \
0x00000040
+/* Reserved - do not use 0x00000004 */
#define PPC_FEATURE_TRUE_LE 0x00000002
#define PPC_FEATURE_PPC_LE 0x00000001
diff --git a/arch/powerpc/include/uapi/asm/kvm.h b/arch/powerpc/include/uapi/asm/kvm.h
index ab4d473..720b71a 100644
--- a/arch/powerpc/include/uapi/asm/kvm.h
+++ b/arch/powerpc/include/uapi/asm/kvm.h
@@ -587,6 +587,7 @@
#define KVM_REG_PPC_TM_VSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U32 | 0x67)
#define KVM_REG_PPC_TM_DSCR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x68)
#define KVM_REG_PPC_TM_TAR (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x69)
+#define KVM_REG_PPC_TM_XER (KVM_REG_PPC_TM | KVM_REG_SIZE_U64 | 0x6a)
/* PPC64 eXternal Interrupt Controller Specification */
#define KVM_DEV_XICS_GRP_SOURCES 1 /* 64-bit source attributes */
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
index 34f5552..ce068cb 100644
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -808,14 +808,25 @@
nb = aligninfo[instr].len;
flags = aligninfo[instr].flags;
- /* ldbrx/stdbrx overlap lfs/stfs in the DSISR unfortunately */
- if (IS_XFORM(instruction) && ((instruction >> 1) & 0x3ff) == 532) {
- nb = 8;
- flags = LD+SW;
- } else if (IS_XFORM(instruction) &&
- ((instruction >> 1) & 0x3ff) == 660) {
- nb = 8;
- flags = ST+SW;
+ /*
+ * Handle some cases which give overlaps in the DSISR values.
+ */
+ if (IS_XFORM(instruction)) {
+ switch (get_xop(instruction)) {
+ case 532: /* ldbrx */
+ nb = 8;
+ flags = LD+SW;
+ break;
+ case 660: /* stdbrx */
+ nb = 8;
+ flags = ST+SW;
+ break;
+ case 20: /* lwarx */
+ case 84: /* ldarx */
+ case 116: /* lharx */
+ case 276: /* lqarx */
+ return 0; /* not emulated ever */
+ }
}
/* Byteswap little endian loads and stores */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 9d7dede..a892c65 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -571,6 +571,7 @@
DEFINE(VCPU_VRS_TM, offsetof(struct kvm_vcpu, arch.vr_tm.vr));
DEFINE(VCPU_VRSAVE_TM, offsetof(struct kvm_vcpu, arch.vrsave_tm));
DEFINE(VCPU_CR_TM, offsetof(struct kvm_vcpu, arch.cr_tm));
+ DEFINE(VCPU_XER_TM, offsetof(struct kvm_vcpu, arch.xer_tm));
DEFINE(VCPU_LR_TM, offsetof(struct kvm_vcpu, arch.lr_tm));
DEFINE(VCPU_CTR_TM, offsetof(struct kvm_vcpu, arch.ctr_tm));
DEFINE(VCPU_AMR_TM, offsetof(struct kvm_vcpu, arch.amr_tm));
diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c
index 2248a19..f8402e1 100644
--- a/arch/powerpc/kernel/eeh.c
+++ b/arch/powerpc/kernel/eeh.c
@@ -646,7 +646,7 @@
/* Check if the request is finished successfully */
if (active_flag) {
rc = eeh_ops->wait_state(pe, PCI_BUS_RESET_WAIT_MSEC);
- if (rc <= 0)
+ if (rc < 0)
return rc;
if (rc & active_flag)
diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c
index 6535936..2fa2a44 100644
--- a/arch/powerpc/kernel/eeh_driver.c
+++ b/arch/powerpc/kernel/eeh_driver.c
@@ -188,6 +188,16 @@
if (!edev)
return NULL;
+ /*
+ * We cannot access the config space on some adapters.
+ * Otherwise, it will cause fenced PHB. We don't save
+ * the content in their config space and will restore
+ * from the initial config space saved when the EEH
+ * device is created.
+ */
+ if (edev->pe && (edev->pe->state & EEH_PE_CFG_RESTRICTED))
+ return NULL;
+
pdev = eeh_dev_to_pci_dev(edev);
if (!pdev)
return NULL;
@@ -327,6 +337,19 @@
if (!edev)
return NULL;
+ /*
+ * The content in the config space isn't saved because
+ * the blocked config space on some adapters. We have
+ * to restore the initial saved config space when the
+ * EEH device is created.
+ */
+ if (edev->pe && (edev->pe->state & EEH_PE_CFG_RESTRICTED)) {
+ if (list_is_last(&edev->list, &edev->pe->edevs))
+ eeh_pe_restore_bars(edev->pe);
+
+ return NULL;
+ }
+
pdev = eeh_dev_to_pci_dev(edev);
if (!pdev)
return NULL;
@@ -524,9 +547,6 @@
/* Save states */
eeh_pe_dev_traverse(pe, eeh_dev_save_state, NULL);
- /* Report error */
- eeh_pe_dev_traverse(pe, eeh_report_error, &result);
-
/* Issue reset */
eeh_pe_state_mark(pe, EEH_PE_CFG_BLOCKED);
ret = eeh_reset_pe(pe);
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 0905c8da..e233c0f 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -677,7 +677,7 @@
addi r8,r1,INT_FRAME_SIZE /* Get the kprobed function entry */
- lwz r3,GPR1(r1)
+ ld r3,GPR1(r1)
subi r3,r3,INT_FRAME_SIZE /* dst: Allocate a trampoline exception frame */
mr r4,r1 /* src: current exception frame */
mr r1,r3 /* Reroute the trampoline frame to r1 */
@@ -691,8 +691,8 @@
addi r6,r6,8
bdnz 2b
- /* Do real store operation to complete stwu */
- lwz r5,GPR1(r1)
+ /* Do real store operation to complete stdu */
+ ld r5,GPR1(r1)
std r8,0(r5)
/* Clear _TIF_EMULATE_STACK_STORE flag */
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 5e01984..f7487ea 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -938,11 +938,6 @@
#endif
STD_RELON_EXCEPTION_PSERIES(0x5700, 0x1700, altivec_assist)
- /* Other future vectors */
- .align 7
- .globl __end_interrupts
-__end_interrupts:
-
.align 7
system_call_entry_direct:
#if defined(CONFIG_RELOCATABLE)
@@ -1236,6 +1231,17 @@
STD_RELON_EXCEPTION_PSERIES_OOL(0xf60, facility_unavailable)
STD_RELON_EXCEPTION_HV_OOL(0xf80, hv_facility_unavailable)
+ /*
+ * The __end_interrupts marker must be past the out-of-line (OOL)
+ * handlers, so that they are copied to real address 0x100 when running
+ * a relocatable kernel. This ensures they can be reached from the short
+ * trampoline handlers (like 0x4f00, 0x4f20, etc.) which branch
+ * directly, without using LOAD_HANDLER().
+ */
+ .align 7
+ .globl __end_interrupts
+__end_interrupts:
+
#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
/*
* Data area reserved for FWNMI option.
diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S
index 283c603..df448d1 100644
--- a/arch/powerpc/kernel/idle_power7.S
+++ b/arch/powerpc/kernel/idle_power7.S
@@ -28,7 +28,7 @@
std r0,0(r1); \
ptesync; \
ld r0,0(r1); \
-1: cmp cr0,r0,r0; \
+1: cmpd cr0,r0,r0; \
bne 1b; \
IDLE_INST; \
b .
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 230da25..1c165dd 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -1224,6 +1224,16 @@
current->thread.regs = regs - 1;
}
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ /*
+ * Clear any transactional state, we're exec()ing. The cause is
+ * not important as there will never be a recheckpoint so it's not
+ * user visible.
+ */
+ if (MSR_TM_SUSPENDED(mfmsr()))
+ tm_reclaim_current(0);
+#endif
+
memset(regs->gpr, 0, sizeof(regs->gpr));
regs->ctr = 0;
regs->link = 0;
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 099f27e..269ae9b 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -149,17 +149,24 @@
unsigned long cpu_features; /* CPU_FTR_xxx bit */
unsigned long mmu_features; /* MMU_FTR_xxx bit */
unsigned int cpu_user_ftrs; /* PPC_FEATURE_xxx bit */
+ unsigned int cpu_user_ftrs2; /* PPC_FEATURE2_xxx bit */
unsigned char pabyte; /* byte number in ibm,pa-features */
unsigned char pabit; /* bit number (big-endian) */
unsigned char invert; /* if 1, pa bit set => clear feature */
} ibm_pa_features[] __initdata = {
- {0, 0, PPC_FEATURE_HAS_MMU, 0, 0, 0},
- {0, 0, PPC_FEATURE_HAS_FPU, 0, 1, 0},
- {CPU_FTR_CTRL, 0, 0, 0, 3, 0},
- {CPU_FTR_NOEXECUTE, 0, 0, 0, 6, 0},
- {CPU_FTR_NODSISRALIGN, 0, 0, 1, 1, 1},
- {0, MMU_FTR_CI_LARGE_PAGE, 0, 1, 2, 0},
- {CPU_FTR_REAL_LE, PPC_FEATURE_TRUE_LE, 5, 0, 0},
+ {0, 0, PPC_FEATURE_HAS_MMU, 0, 0, 0, 0},
+ {0, 0, PPC_FEATURE_HAS_FPU, 0, 0, 1, 0},
+ {CPU_FTR_CTRL, 0, 0, 0, 0, 3, 0},
+ {CPU_FTR_NOEXECUTE, 0, 0, 0, 0, 6, 0},
+ {CPU_FTR_NODSISRALIGN, 0, 0, 0, 1, 1, 1},
+ {0, MMU_FTR_CI_LARGE_PAGE, 0, 0, 1, 2, 0},
+ {CPU_FTR_REAL_LE, 0, PPC_FEATURE_TRUE_LE, 0, 5, 0, 0},
+ /*
+ * If the kernel doesn't support TM (ie CONFIG_PPC_TRANSACTIONAL_MEM=n),
+ * we don't want to turn on TM here, so we use the *_COMP versions
+ * which are 0 if the kernel doesn't support TM.
+ */
+ {CPU_FTR_TM_COMP, 0, 0, PPC_FEATURE2_HTM_COMP, 22, 0, 0},
};
static void __init scan_features(unsigned long node, const unsigned char *ftrs,
@@ -190,10 +197,12 @@
if (bit ^ fp->invert) {
cur_cpu_spec->cpu_features |= fp->cpu_features;
cur_cpu_spec->cpu_user_features |= fp->cpu_user_ftrs;
+ cur_cpu_spec->cpu_user_features2 |= fp->cpu_user_ftrs2;
cur_cpu_spec->mmu_features |= fp->mmu_features;
} else {
cur_cpu_spec->cpu_features &= ~fp->cpu_features;
cur_cpu_spec->cpu_user_features &= ~fp->cpu_user_ftrs;
+ cur_cpu_spec->cpu_user_features2 &= ~fp->cpu_user_ftrs2;
cur_cpu_spec->mmu_features &= ~fp->mmu_features;
}
}
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index f21897b..93f200f 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -376,7 +376,7 @@
#else
BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) !=
- offsetof(struct thread_fp_state, fpr[32][0]));
+ offsetof(struct thread_fp_state, fpr[32]));
return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.fp_state, 0, -1);
@@ -404,7 +404,7 @@
return 0;
#else
BUILD_BUG_ON(offsetof(struct thread_fp_state, fpscr) !=
- offsetof(struct thread_fp_state, fpr[32][0]));
+ offsetof(struct thread_fp_state, fpr[32]));
return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&target->thread.fp_state, 0, -1);
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index 4f3cfe1..3dac0e6 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -211,6 +211,15 @@
unsigned long lpcr = mfspr(SPRN_LPCR);
mtspr(SPRN_LPCR, lpcr | LPCR_AIL_3);
}
+
+ /*
+ * Fixup HFSCR:TM based on CPU features. The bit is set by our
+ * early asm init because at that point we haven't updated our
+ * CPU features from firmware and device-tree. Here we have,
+ * so let's do it.
+ */
+ if (cpu_has_feature(CPU_FTR_HVMODE) && !cpu_has_feature(CPU_FTR_TM_COMP))
+ mtspr(SPRN_HFSCR, mfspr(SPRN_HFSCR) & ~HFSCR_TM);
}
/*
diff --git a/arch/powerpc/kernel/tm.S b/arch/powerpc/kernel/tm.S
index 2a324f4..08225a3 100644
--- a/arch/powerpc/kernel/tm.S
+++ b/arch/powerpc/kernel/tm.S
@@ -110,17 +110,11 @@
std r3, STK_PARAM(R3)(r1)
SAVE_NVGPRS(r1)
- /* We need to setup MSR for VSX register save instructions. Here we
- * also clear the MSR RI since when we do the treclaim, we won't have a
- * valid kernel pointer for a while. We clear RI here as it avoids
- * adding another mtmsr closer to the treclaim. This makes the region
- * maked as non-recoverable wider than it needs to be but it saves on
- * inserting another mtmsrd later.
- */
+ /* We need to setup MSR for VSX register save instructions. */
mfmsr r14
mr r15, r14
ori r15, r15, MSR_FP
- li r16, MSR_RI
+ li r16, 0
ori r16, r16, MSR_EE /* IRQs hard off */
andc r15, r15, r16
oris r15, r15, MSR_VEC@h
@@ -176,7 +170,17 @@
1: tdeqi r6, 0
EMIT_BUG_ENTRY 1b,__FILE__,__LINE__,0
- /* The moment we treclaim, ALL of our GPRs will switch
+ /* Clear MSR RI since we are about to change r1, EE is already off. */
+ li r4, 0
+ mtmsrd r4, 1
+
+ /*
+ * BE CAREFUL HERE:
+ * At this point we can't take an SLB miss since we have MSR_RI
+ * off. Load only to/from the stack/paca which are in SLB bolted regions
+ * until we turn MSR RI back on.
+ *
+ * The moment we treclaim, ALL of our GPRs will switch
* to user register state. (FPRs, CCR etc. also!)
* Use an sprg and a tm_scratch in the PACA to shuffle.
*/
@@ -197,6 +201,11 @@
/* Store the PPR in r11 and reset to decent value */
std r11, GPR11(r1) /* Temporary stash */
+
+ /* Reset MSR RI so we can take SLB faults again */
+ li r11, MSR_RI
+ mtmsrd r11, 1
+
mfspr r11, SPRN_PPR
HMT_MEDIUM
@@ -329,8 +338,6 @@
*/
subi r7, r7, STACK_FRAME_OVERHEAD
- SET_SCRATCH0(r1)
-
mfmsr r6
/* R4 = original MSR to indicate whether thread used FP/Vector etc. */
@@ -397,11 +404,6 @@
ld r5, THREAD_TM_DSCR(r3)
ld r6, THREAD_TM_PPR(r3)
- /* Clear the MSR RI since we are about to change R1. EE is already off
- */
- li r4, 0
- mtmsrd r4, 1
-
REST_GPR(0, r7) /* GPR0 */
REST_2GPRS(2, r7) /* GPR2-3 */
REST_GPR(4, r7) /* GPR4 */
@@ -439,10 +441,34 @@
ld r6, _CCR(r7)
mtcr r6
- REST_GPR(1, r7) /* GPR1 */
- REST_GPR(5, r7) /* GPR5-7 */
REST_GPR(6, r7)
- ld r7, GPR7(r7)
+
+ /*
+ * Store r1 and r5 on the stack so that we can access them
+ * after we clear MSR RI.
+ */
+
+ REST_GPR(5, r7)
+ std r5, -8(r1)
+ ld r5, GPR1(r7)
+ std r5, -16(r1)
+
+ REST_GPR(7, r7)
+
+ /* Clear MSR RI since we are about to change r1. EE is already off */
+ li r5, 0
+ mtmsrd r5, 1
+
+ /*
+ * BE CAREFUL HERE:
+ * At this point we can't take an SLB miss since we have MSR_RI
+ * off. Load only to/from the stack/paca which are in SLB bolted regions
+ * until we turn MSR RI back on.
+ */
+
+ SET_SCRATCH0(r1)
+ ld r5, -8(r1)
+ ld r1, -16(r1)
/* Commit register state as checkpointed state: */
TRECHKPT
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index e63587d..ed86c26 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -1097,6 +1097,9 @@
case KVM_REG_PPC_TM_CR:
*val = get_reg_val(id, vcpu->arch.cr_tm);
break;
+ case KVM_REG_PPC_TM_XER:
+ *val = get_reg_val(id, vcpu->arch.xer_tm);
+ break;
case KVM_REG_PPC_TM_LR:
*val = get_reg_val(id, vcpu->arch.lr_tm);
break;
@@ -1304,6 +1307,9 @@
case KVM_REG_PPC_TM_CR:
vcpu->arch.cr_tm = set_reg_val(id, *val);
break;
+ case KVM_REG_PPC_TM_XER:
+ vcpu->arch.xer_tm = set_reg_val(id, *val);
+ break;
case KVM_REG_PPC_TM_LR:
vcpu->arch.lr_tm = set_reg_val(id, *val);
break;
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index 4329ea3..791ad03 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -632,112 +632,8 @@
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
BEGIN_FTR_SECTION
- b skip_tm
-END_FTR_SECTION_IFCLR(CPU_FTR_TM)
-
- /* Turn on TM/FP/VSX/VMX so we can restore them. */
- mfmsr r5
- li r6, MSR_TM >> 32
- sldi r6, r6, 32
- or r5, r5, r6
- ori r5, r5, MSR_FP
- oris r5, r5, (MSR_VEC | MSR_VSX)@h
- mtmsrd r5
-
- /*
- * The user may change these outside of a transaction, so they must
- * always be context switched.
- */
- ld r5, VCPU_TFHAR(r4)
- ld r6, VCPU_TFIAR(r4)
- ld r7, VCPU_TEXASR(r4)
- mtspr SPRN_TFHAR, r5
- mtspr SPRN_TFIAR, r6
- mtspr SPRN_TEXASR, r7
-
- ld r5, VCPU_MSR(r4)
- rldicl. r5, r5, 64 - MSR_TS_S_LG, 62
- beq skip_tm /* TM not active in guest */
-
- /* Make sure the failure summary is set, otherwise we'll program check
- * when we trechkpt. It's possible that this might have been not set
- * on a kvmppc_set_one_reg() call but we shouldn't let this crash the
- * host.
- */
- oris r7, r7, (TEXASR_FS)@h
- mtspr SPRN_TEXASR, r7
-
- /*
- * We need to load up the checkpointed state for the guest.
- * We need to do this early as it will blow away any GPRs, VSRs and
- * some SPRs.
- */
-
- mr r31, r4
- addi r3, r31, VCPU_FPRS_TM
- bl load_fp_state
- addi r3, r31, VCPU_VRS_TM
- bl load_vr_state
- mr r4, r31
- lwz r7, VCPU_VRSAVE_TM(r4)
- mtspr SPRN_VRSAVE, r7
-
- ld r5, VCPU_LR_TM(r4)
- lwz r6, VCPU_CR_TM(r4)
- ld r7, VCPU_CTR_TM(r4)
- ld r8, VCPU_AMR_TM(r4)
- ld r9, VCPU_TAR_TM(r4)
- mtlr r5
- mtcr r6
- mtctr r7
- mtspr SPRN_AMR, r8
- mtspr SPRN_TAR, r9
-
- /*
- * Load up PPR and DSCR values but don't put them in the actual SPRs
- * till the last moment to avoid running with userspace PPR and DSCR for
- * too long.
- */
- ld r29, VCPU_DSCR_TM(r4)
- ld r30, VCPU_PPR_TM(r4)
-
- std r2, PACATMSCRATCH(r13) /* Save TOC */
-
- /* Clear the MSR RI since r1, r13 are all going to be foobar. */
- li r5, 0
- mtmsrd r5, 1
-
- /* Load GPRs r0-r28 */
- reg = 0
- .rept 29
- ld reg, VCPU_GPRS_TM(reg)(r31)
- reg = reg + 1
- .endr
-
- mtspr SPRN_DSCR, r29
- mtspr SPRN_PPR, r30
-
- /* Load final GPRs */
- ld 29, VCPU_GPRS_TM(29)(r31)
- ld 30, VCPU_GPRS_TM(30)(r31)
- ld 31, VCPU_GPRS_TM(31)(r31)
-
- /* TM checkpointed state is now setup. All GPRs are now volatile. */
- TRECHKPT
-
- /* Now let's get back the state we need. */
- HMT_MEDIUM
- GET_PACA(r13)
- ld r29, HSTATE_DSCR(r13)
- mtspr SPRN_DSCR, r29
- ld r4, HSTATE_KVM_VCPU(r13)
- ld r1, HSTATE_HOST_R1(r13)
- ld r2, PACATMSCRATCH(r13)
-
- /* Set the MSR RI since we have our registers back. */
- li r5, MSR_RI
- mtmsrd r5, 1
-skip_tm:
+ bl kvmppc_restore_tm
+END_FTR_SECTION_IFSET(CPU_FTR_TM)
#endif
/* Load guest PMU registers */
@@ -828,12 +724,6 @@
/* Skip next section on POWER7 or PPC970 */
b 8f
END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
- /* Turn on TM so we can access TFHAR/TFIAR/TEXASR */
- mfmsr r8
- li r0, 1
- rldimi r8, r0, MSR_TM_LG, 63-MSR_TM_LG
- mtmsrd r8
-
/* Load up POWER8-specific registers */
ld r5, VCPU_IAMR(r4)
lwz r6, VCPU_PSPB(r4)
@@ -1354,106 +1244,8 @@
#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
BEGIN_FTR_SECTION
- b 2f
-END_FTR_SECTION_IFCLR(CPU_FTR_TM)
- /* Turn on TM. */
- mfmsr r8
- li r0, 1
- rldimi r8, r0, MSR_TM_LG, 63-MSR_TM_LG
- mtmsrd r8
-
- ld r5, VCPU_MSR(r9)
- rldicl. r5, r5, 64 - MSR_TS_S_LG, 62
- beq 1f /* TM not active in guest. */
-
- li r3, TM_CAUSE_KVM_RESCHED
-
- /* Clear the MSR RI since r1, r13 are all going to be foobar. */
- li r5, 0
- mtmsrd r5, 1
-
- /* All GPRs are volatile at this point. */
- TRECLAIM(R3)
-
- /* Temporarily store r13 and r9 so we have some regs to play with */
- SET_SCRATCH0(r13)
- GET_PACA(r13)
- std r9, PACATMSCRATCH(r13)
- ld r9, HSTATE_KVM_VCPU(r13)
-
- /* Get a few more GPRs free. */
- std r29, VCPU_GPRS_TM(29)(r9)
- std r30, VCPU_GPRS_TM(30)(r9)
- std r31, VCPU_GPRS_TM(31)(r9)
-
- /* Save away PPR and DSCR soon so don't run with user values. */
- mfspr r31, SPRN_PPR
- HMT_MEDIUM
- mfspr r30, SPRN_DSCR
- ld r29, HSTATE_DSCR(r13)
- mtspr SPRN_DSCR, r29
-
- /* Save all but r9, r13 & r29-r31 */
- reg = 0
- .rept 29
- .if (reg != 9) && (reg != 13)
- std reg, VCPU_GPRS_TM(reg)(r9)
- .endif
- reg = reg + 1
- .endr
- /* ... now save r13 */
- GET_SCRATCH0(r4)
- std r4, VCPU_GPRS_TM(13)(r9)
- /* ... and save r9 */
- ld r4, PACATMSCRATCH(r13)
- std r4, VCPU_GPRS_TM(9)(r9)
-
- /* Reload stack pointer and TOC. */
- ld r1, HSTATE_HOST_R1(r13)
- ld r2, PACATOC(r13)
-
- /* Set MSR RI now we have r1 and r13 back. */
- li r5, MSR_RI
- mtmsrd r5, 1
-
- /* Save away checkpinted SPRs. */
- std r31, VCPU_PPR_TM(r9)
- std r30, VCPU_DSCR_TM(r9)
- mflr r5
- mfcr r6
- mfctr r7
- mfspr r8, SPRN_AMR
- mfspr r10, SPRN_TAR
- std r5, VCPU_LR_TM(r9)
- stw r6, VCPU_CR_TM(r9)
- std r7, VCPU_CTR_TM(r9)
- std r8, VCPU_AMR_TM(r9)
- std r10, VCPU_TAR_TM(r9)
-
- /* Restore r12 as trap number. */
- lwz r12, VCPU_TRAP(r9)
-
- /* Save FP/VSX. */
- addi r3, r9, VCPU_FPRS_TM
- bl store_fp_state
- addi r3, r9, VCPU_VRS_TM
- bl store_vr_state
- mfspr r6, SPRN_VRSAVE
- stw r6, VCPU_VRSAVE_TM(r9)
-1:
- /*
- * We need to save these SPRs after the treclaim so that the software
- * error code is recorded correctly in the TEXASR. Also the user may
- * change these outside of a transaction, so they must always be
- * context switched.
- */
- mfspr r5, SPRN_TFHAR
- mfspr r6, SPRN_TFIAR
- mfspr r7, SPRN_TEXASR
- std r5, VCPU_TFHAR(r9)
- std r6, VCPU_TFIAR(r9)
- std r7, VCPU_TEXASR(r9)
-2:
+ bl kvmppc_save_tm
+END_FTR_SECTION_IFSET(CPU_FTR_TM)
#endif
/* Increment yield count if they have a VPA */
@@ -2181,6 +1973,13 @@
/* save FP state */
bl kvmppc_save_fp
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+BEGIN_FTR_SECTION
+ ld r9, HSTATE_KVM_VCPU(r13)
+ bl kvmppc_save_tm
+END_FTR_SECTION_IFSET(CPU_FTR_TM)
+#endif
+
/*
* Take a nap until a decrementer or external or doobell interrupt
* occurs, with PECE1, PECE0 and PECEDP set in LPCR. Also clear the
@@ -2220,6 +2019,12 @@
/* Woken by external or decrementer interrupt */
ld r1, HSTATE_HOST_R1(r13)
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+BEGIN_FTR_SECTION
+ bl kvmppc_restore_tm
+END_FTR_SECTION_IFSET(CPU_FTR_TM)
+#endif
+
/* load up FP state */
bl kvmppc_load_fp
@@ -2500,6 +2305,243 @@
mr r4,r31
blr
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+/*
+ * Save transactional state and TM-related registers.
+ * Called with r9 pointing to the vcpu struct.
+ * This can modify all checkpointed registers, but
+ * restores r1, r2 and r9 (vcpu pointer) before exit.
+ */
+kvmppc_save_tm:
+ mflr r0
+ std r0, PPC_LR_STKOFF(r1)
+
+ /* Turn on TM. */
+ mfmsr r8
+ li r0, 1
+ rldimi r8, r0, MSR_TM_LG, 63-MSR_TM_LG
+ mtmsrd r8
+
+ ld r5, VCPU_MSR(r9)
+ rldicl. r5, r5, 64 - MSR_TS_S_LG, 62
+ beq 1f /* TM not active in guest. */
+
+ std r1, HSTATE_HOST_R1(r13)
+ li r3, TM_CAUSE_KVM_RESCHED
+
+ /* Clear the MSR RI since r1, r13 are all going to be foobar. */
+ li r5, 0
+ mtmsrd r5, 1
+
+ /* All GPRs are volatile at this point. */
+ TRECLAIM(R3)
+
+ /* Temporarily store r13 and r9 so we have some regs to play with */
+ SET_SCRATCH0(r13)
+ GET_PACA(r13)
+ std r9, PACATMSCRATCH(r13)
+ ld r9, HSTATE_KVM_VCPU(r13)
+
+ /* Get a few more GPRs free. */
+ std r29, VCPU_GPRS_TM(29)(r9)
+ std r30, VCPU_GPRS_TM(30)(r9)
+ std r31, VCPU_GPRS_TM(31)(r9)
+
+ /* Save away PPR and DSCR soon so don't run with user values. */
+ mfspr r31, SPRN_PPR
+ HMT_MEDIUM
+ mfspr r30, SPRN_DSCR
+ ld r29, HSTATE_DSCR(r13)
+ mtspr SPRN_DSCR, r29
+
+ /* Save all but r9, r13 & r29-r31 */
+ reg = 0
+ .rept 29
+ .if (reg != 9) && (reg != 13)
+ std reg, VCPU_GPRS_TM(reg)(r9)
+ .endif
+ reg = reg + 1
+ .endr
+ /* ... now save r13 */
+ GET_SCRATCH0(r4)
+ std r4, VCPU_GPRS_TM(13)(r9)
+ /* ... and save r9 */
+ ld r4, PACATMSCRATCH(r13)
+ std r4, VCPU_GPRS_TM(9)(r9)
+
+ /* Reload stack pointer and TOC. */
+ ld r1, HSTATE_HOST_R1(r13)
+ ld r2, PACATOC(r13)
+
+ /* Set MSR RI now we have r1 and r13 back. */
+ li r5, MSR_RI
+ mtmsrd r5, 1
+
+ /* Save away checkpinted SPRs. */
+ std r31, VCPU_PPR_TM(r9)
+ std r30, VCPU_DSCR_TM(r9)
+ mflr r5
+ mfcr r6
+ mfctr r7
+ mfspr r8, SPRN_AMR
+ mfspr r10, SPRN_TAR
+ mfxer r11
+ std r5, VCPU_LR_TM(r9)
+ stw r6, VCPU_CR_TM(r9)
+ std r7, VCPU_CTR_TM(r9)
+ std r8, VCPU_AMR_TM(r9)
+ std r10, VCPU_TAR_TM(r9)
+ std r11, VCPU_XER_TM(r9)
+
+ /* Restore r12 as trap number. */
+ lwz r12, VCPU_TRAP(r9)
+
+ /* Save FP/VSX. */
+ addi r3, r9, VCPU_FPRS_TM
+ bl store_fp_state
+ addi r3, r9, VCPU_VRS_TM
+ bl store_vr_state
+ mfspr r6, SPRN_VRSAVE
+ stw r6, VCPU_VRSAVE_TM(r9)
+1:
+ /*
+ * We need to save these SPRs after the treclaim so that the software
+ * error code is recorded correctly in the TEXASR. Also the user may
+ * change these outside of a transaction, so they must always be
+ * context switched.
+ */
+ mfspr r5, SPRN_TFHAR
+ mfspr r6, SPRN_TFIAR
+ mfspr r7, SPRN_TEXASR
+ std r5, VCPU_TFHAR(r9)
+ std r6, VCPU_TFIAR(r9)
+ std r7, VCPU_TEXASR(r9)
+
+ ld r0, PPC_LR_STKOFF(r1)
+ mtlr r0
+ blr
+
+/*
+ * Restore transactional state and TM-related registers.
+ * Called with r4 pointing to the vcpu struct.
+ * This potentially modifies all checkpointed registers.
+ * It restores r1, r2, r4 from the PACA.
+ */
+kvmppc_restore_tm:
+ mflr r0
+ std r0, PPC_LR_STKOFF(r1)
+
+ /* Turn on TM/FP/VSX/VMX so we can restore them. */
+ mfmsr r5
+ li r6, MSR_TM >> 32
+ sldi r6, r6, 32
+ or r5, r5, r6
+ ori r5, r5, MSR_FP
+ oris r5, r5, (MSR_VEC | MSR_VSX)@h
+ mtmsrd r5
+
+ /*
+ * The user may change these outside of a transaction, so they must
+ * always be context switched.
+ */
+ ld r5, VCPU_TFHAR(r4)
+ ld r6, VCPU_TFIAR(r4)
+ ld r7, VCPU_TEXASR(r4)
+ mtspr SPRN_TFHAR, r5
+ mtspr SPRN_TFIAR, r6
+ mtspr SPRN_TEXASR, r7
+
+ ld r5, VCPU_MSR(r4)
+ rldicl. r5, r5, 64 - MSR_TS_S_LG, 62
+ beqlr /* TM not active in guest */
+ std r1, HSTATE_HOST_R1(r13)
+
+ /* Make sure the failure summary is set, otherwise we'll program check
+ * when we trechkpt. It's possible that this might have been not set
+ * on a kvmppc_set_one_reg() call but we shouldn't let this crash the
+ * host.
+ */
+ oris r7, r7, (TEXASR_FS)@h
+ mtspr SPRN_TEXASR, r7
+
+ /*
+ * We need to load up the checkpointed state for the guest.
+ * We need to do this early as it will blow away any GPRs, VSRs and
+ * some SPRs.
+ */
+
+ mr r31, r4
+ addi r3, r31, VCPU_FPRS_TM
+ bl load_fp_state
+ addi r3, r31, VCPU_VRS_TM
+ bl load_vr_state
+ mr r4, r31
+ lwz r7, VCPU_VRSAVE_TM(r4)
+ mtspr SPRN_VRSAVE, r7
+
+ ld r5, VCPU_LR_TM(r4)
+ lwz r6, VCPU_CR_TM(r4)
+ ld r7, VCPU_CTR_TM(r4)
+ ld r8, VCPU_AMR_TM(r4)
+ ld r9, VCPU_TAR_TM(r4)
+ ld r10, VCPU_XER_TM(r4)
+ mtlr r5
+ mtcr r6
+ mtctr r7
+ mtspr SPRN_AMR, r8
+ mtspr SPRN_TAR, r9
+ mtxer r10
+
+ /*
+ * Load up PPR and DSCR values but don't put them in the actual SPRs
+ * till the last moment to avoid running with userspace PPR and DSCR for
+ * too long.
+ */
+ ld r29, VCPU_DSCR_TM(r4)
+ ld r30, VCPU_PPR_TM(r4)
+
+ std r2, PACATMSCRATCH(r13) /* Save TOC */
+
+ /* Clear the MSR RI since r1, r13 are all going to be foobar. */
+ li r5, 0
+ mtmsrd r5, 1
+
+ /* Load GPRs r0-r28 */
+ reg = 0
+ .rept 29
+ ld reg, VCPU_GPRS_TM(reg)(r31)
+ reg = reg + 1
+ .endr
+
+ mtspr SPRN_DSCR, r29
+ mtspr SPRN_PPR, r30
+
+ /* Load final GPRs */
+ ld 29, VCPU_GPRS_TM(29)(r31)
+ ld 30, VCPU_GPRS_TM(30)(r31)
+ ld 31, VCPU_GPRS_TM(31)(r31)
+
+ /* TM checkpointed state is now setup. All GPRs are now volatile. */
+ TRECHKPT
+
+ /* Now let's get back the state we need. */
+ HMT_MEDIUM
+ GET_PACA(r13)
+ ld r29, HSTATE_DSCR(r13)
+ mtspr SPRN_DSCR, r29
+ ld r4, HSTATE_KVM_VCPU(r13)
+ ld r1, HSTATE_HOST_R1(r13)
+ ld r2, PACATMSCRATCH(r13)
+
+ /* Set the MSR RI since we have our registers back. */
+ li r5, MSR_RI
+ mtmsrd r5, 1
+
+ ld r0, PPC_LR_STKOFF(r1)
+ mtlr r0
+ blr
+#endif
+
/*
* We come here if we get any exception or interrupt while we are
* executing host real mode code while in guest MMU context.
diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
index 5cc2e7a..b379146 100644
--- a/arch/powerpc/kvm/emulate.c
+++ b/arch/powerpc/kvm/emulate.c
@@ -302,7 +302,6 @@
advance = 0;
printk(KERN_ERR "Couldn't emulate instruction 0x%08x "
"(op %d xop %d)\n", inst, get_op(inst), get_xop(inst));
- kvmppc_core_queue_program(vcpu, 0);
}
}
diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c
index 54651fc..51b6673 100644
--- a/arch/powerpc/lib/sstep.c
+++ b/arch/powerpc/lib/sstep.c
@@ -1806,8 +1806,6 @@
goto instr_done;
case LARX:
- if (regs->msr & MSR_LE)
- return 0;
if (op.ea & (size - 1))
break; /* can't handle misaligned */
err = -EFAULT;
@@ -1829,8 +1827,6 @@
goto ldst_done;
case STCX:
- if (regs->msr & MSR_LE)
- return 0;
if (op.ea & (size - 1))
break; /* can't handle misaligned */
err = -EFAULT;
@@ -1854,8 +1850,6 @@
goto ldst_done;
case LOAD:
- if (regs->msr & MSR_LE)
- return 0;
err = read_mem(®s->gpr[op.reg], op.ea, size, regs);
if (!err) {
if (op.type & SIGNEXT)
@@ -1866,8 +1860,6 @@
goto ldst_done;
case LOAD_FP:
- if (regs->msr & MSR_LE)
- return 0;
if (size == 4)
err = do_fp_load(op.reg, do_lfs, op.ea, size, regs);
else
@@ -1876,15 +1868,11 @@
#ifdef CONFIG_ALTIVEC
case LOAD_VMX:
- if (regs->msr & MSR_LE)
- return 0;
err = do_vec_load(op.reg, do_lvx, op.ea & ~0xfUL, regs);
goto ldst_done;
#endif
#ifdef CONFIG_VSX
case LOAD_VSX:
- if (regs->msr & MSR_LE)
- return 0;
err = do_vsx_load(op.reg, do_lxvd2x, op.ea, regs);
goto ldst_done;
#endif
@@ -1907,8 +1895,6 @@
goto instr_done;
case STORE:
- if (regs->msr & MSR_LE)
- return 0;
if ((op.type & UPDATE) && size == sizeof(long) &&
op.reg == 1 && op.update_reg == 1 &&
!(regs->msr & MSR_PR) &&
@@ -1920,8 +1906,6 @@
goto ldst_done;
case STORE_FP:
- if (regs->msr & MSR_LE)
- return 0;
if (size == 4)
err = do_fp_store(op.reg, do_stfs, op.ea, size, regs);
else
@@ -1930,15 +1914,11 @@
#ifdef CONFIG_ALTIVEC
case STORE_VMX:
- if (regs->msr & MSR_LE)
- return 0;
err = do_vec_store(op.reg, do_stvx, op.ea & ~0xfUL, regs);
goto ldst_done;
#endif
#ifdef CONFIG_VSX
case STORE_VSX:
- if (regs->msr & MSR_LE)
- return 0;
err = do_vsx_store(op.reg, do_stxvd2x, op.ea, regs);
goto ldst_done;
#endif
diff --git a/arch/powerpc/mm/copro_fault.c b/arch/powerpc/mm/copro_fault.c
index 1b5305d..575b871 100644
--- a/arch/powerpc/mm/copro_fault.c
+++ b/arch/powerpc/mm/copro_fault.c
@@ -102,6 +102,8 @@
switch (REGION_ID(ea)) {
case USER_REGION_ID:
pr_devel("%s: 0x%llx -- USER_REGION_ID\n", __func__, ea);
+ if (mm == NULL)
+ return 1;
psize = get_slice_psize(mm, ea);
ssize = user_segment_size(ea);
vsid = get_vsid(mm->context.id, ea, ssize);
diff --git a/arch/powerpc/mm/hash_native_64.c b/arch/powerpc/mm/hash_native_64.c
index ae4962a..42213a3 100644
--- a/arch/powerpc/mm/hash_native_64.c
+++ b/arch/powerpc/mm/hash_native_64.c
@@ -633,6 +633,10 @@
unsigned long psize = batch->psize;
int ssize = batch->ssize;
int i;
+ unsigned int use_local;
+
+ use_local = local && mmu_has_feature(MMU_FTR_TLBIEL) &&
+ mmu_psize_defs[psize].tlbiel && !cxl_ctx_in_use();
local_irq_save(flags);
@@ -659,8 +663,7 @@
} pte_iterate_hashed_end();
}
- if (mmu_has_feature(MMU_FTR_TLBIEL) &&
- mmu_psize_defs[psize].tlbiel && local) {
+ if (use_local) {
asm volatile("ptesync":::"memory");
for (i = 0; i < number; i++) {
vpn = batch->vpn[i];
diff --git a/arch/powerpc/mm/slb_low.S b/arch/powerpc/mm/slb_low.S
index 736d18b..4c48b48 100644
--- a/arch/powerpc/mm/slb_low.S
+++ b/arch/powerpc/mm/slb_low.S
@@ -113,7 +113,12 @@
END_MMU_FTR_SECTION_IFCLR(MMU_FTR_1T_SEGMENT)
b slb_finish_load_1T
-0:
+0: /*
+ * For userspace addresses, make sure this is region 0.
+ */
+ cmpdi r9, 0
+ bne 8f
+
/* when using slices, we extract the psize off the slice bitmaps
* and then we need to get the sllp encoding off the mmu_psize_defs
* array.
diff --git a/arch/powerpc/platforms/pseries/eeh_pseries.c b/arch/powerpc/platforms/pseries/eeh_pseries.c
index a6c7e19..5c80e02 100644
--- a/arch/powerpc/platforms/pseries/eeh_pseries.c
+++ b/arch/powerpc/platforms/pseries/eeh_pseries.c
@@ -642,29 +642,50 @@
{
int config_addr;
int ret;
+ /* Waiting 0.2s maximum before skipping configuration */
+ int max_wait = 200;
/* Figure out the PE address */
config_addr = pe->config_addr;
if (pe->addr)
config_addr = pe->addr;
- /* Use new configure-pe function, if supported */
- if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE) {
- ret = rtas_call(ibm_configure_pe, 3, 1, NULL,
- config_addr, BUID_HI(pe->phb->buid),
- BUID_LO(pe->phb->buid));
- } else if (ibm_configure_bridge != RTAS_UNKNOWN_SERVICE) {
- ret = rtas_call(ibm_configure_bridge, 3, 1, NULL,
- config_addr, BUID_HI(pe->phb->buid),
- BUID_LO(pe->phb->buid));
- } else {
- return -EFAULT;
+ while (max_wait > 0) {
+ /* Use new configure-pe function, if supported */
+ if (ibm_configure_pe != RTAS_UNKNOWN_SERVICE) {
+ ret = rtas_call(ibm_configure_pe, 3, 1, NULL,
+ config_addr, BUID_HI(pe->phb->buid),
+ BUID_LO(pe->phb->buid));
+ } else if (ibm_configure_bridge != RTAS_UNKNOWN_SERVICE) {
+ ret = rtas_call(ibm_configure_bridge, 3, 1, NULL,
+ config_addr, BUID_HI(pe->phb->buid),
+ BUID_LO(pe->phb->buid));
+ } else {
+ return -EFAULT;
+ }
+
+ if (!ret)
+ return ret;
+
+ /*
+ * If RTAS returns a delay value that's above 100ms, cut it
+ * down to 100ms in case firmware made a mistake. For more
+ * on how these delay values work see rtas_busy_delay_time
+ */
+ if (ret > RTAS_EXTENDED_DELAY_MIN+2 &&
+ ret <= RTAS_EXTENDED_DELAY_MAX)
+ ret = RTAS_EXTENDED_DELAY_MIN+2;
+
+ max_wait -= rtas_busy_delay_time(ret);
+
+ if (max_wait < 0)
+ break;
+
+ rtas_busy_delay(ret);
}
- if (ret)
- pr_warn("%s: Unable to configure bridge PHB#%d-PE#%x (%d)\n",
- __func__, pe->phb->global_number, pe->addr, ret);
-
+ pr_warn("%s: Unable to configure bridge PHB#%d-PE#%x (%d)\n",
+ __func__, pe->phb->global_number, pe->addr, ret);
return ret;
}
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 3e5bfda..1e1fa54 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -825,7 +825,8 @@
static int query_ddw(struct pci_dev *dev, const u32 *ddw_avail,
struct ddw_query_response *query)
{
- struct eeh_dev *edev;
+ struct device_node *dn;
+ struct pci_dn *pdn;
u32 cfg_addr;
u64 buid;
int ret;
@@ -836,11 +837,10 @@
* Retrieve them from the pci device, not the node with the
* dma-window property
*/
- edev = pci_dev_to_eeh_dev(dev);
- cfg_addr = edev->config_addr;
- if (edev->pe_config_addr)
- cfg_addr = edev->pe_config_addr;
- buid = edev->phb->buid;
+ dn = pci_device_to_OF_node(dev);
+ pdn = PCI_DN(dn);
+ buid = pdn->phb->buid;
+ cfg_addr = ((pdn->busno << 16) | (pdn->devfn << 8));
ret = rtas_call(ddw_avail[0], 3, 5, (u32 *)query,
cfg_addr, BUID_HI(buid), BUID_LO(buid));
@@ -854,7 +854,8 @@
struct ddw_create_response *create, int page_shift,
int window_shift)
{
- struct eeh_dev *edev;
+ struct device_node *dn;
+ struct pci_dn *pdn;
u32 cfg_addr;
u64 buid;
int ret;
@@ -865,11 +866,10 @@
* Retrieve them from the pci device, not the node with the
* dma-window property
*/
- edev = pci_dev_to_eeh_dev(dev);
- cfg_addr = edev->config_addr;
- if (edev->pe_config_addr)
- cfg_addr = edev->pe_config_addr;
- buid = edev->phb->buid;
+ dn = pci_device_to_OF_node(dev);
+ pdn = PCI_DN(dn);
+ buid = pdn->phb->buid;
+ cfg_addr = ((pdn->busno << 16) | (pdn->devfn << 8));
do {
/* extra outputs are LIOBN and dma-addr (hi, lo) */
diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c
index 57cbaff..d73c887 100644
--- a/arch/s390/boot/compressed/misc.c
+++ b/arch/s390/boot/compressed/misc.c
@@ -142,31 +142,34 @@
unsigned long decompress_kernel(void)
{
- unsigned long output_addr;
- unsigned char *output;
+ void *output, *kernel_end;
- output_addr = ((unsigned long) &_end + HEAP_SIZE + 4095UL) & -4096UL;
- check_ipl_parmblock((void *) 0, output_addr + SZ__bss_start);
- memset(&_bss, 0, &_ebss - &_bss);
- free_mem_ptr = (unsigned long)&_end;
- free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
- output = (unsigned char *) output_addr;
+ output = (void *) ALIGN((unsigned long) &_end + HEAP_SIZE, PAGE_SIZE);
+ kernel_end = output + SZ__bss_start;
+ check_ipl_parmblock((void *) 0, (unsigned long) kernel_end);
#ifdef CONFIG_BLK_DEV_INITRD
/*
* Move the initrd right behind the end of the decompressed
- * kernel image.
+ * kernel image. This also prevents initrd corruption caused by
+ * bss clearing since kernel_end will always be located behind the
+ * current bss section..
*/
- if (INITRD_START && INITRD_SIZE &&
- INITRD_START < (unsigned long) output + SZ__bss_start) {
- check_ipl_parmblock(output + SZ__bss_start,
- INITRD_START + INITRD_SIZE);
- memmove(output + SZ__bss_start,
- (void *) INITRD_START, INITRD_SIZE);
- INITRD_START = (unsigned long) output + SZ__bss_start;
+ if (INITRD_START && INITRD_SIZE && kernel_end > (void *) INITRD_START) {
+ check_ipl_parmblock(kernel_end, INITRD_SIZE);
+ memmove(kernel_end, (void *) INITRD_START, INITRD_SIZE);
+ INITRD_START = (unsigned long) kernel_end;
}
#endif
+ /*
+ * Clear bss section. free_mem_ptr and free_mem_end_ptr need to be
+ * initialized afterwards since they reside in bss.
+ */
+ memset(&_bss, 0, &_ebss - &_bss);
+ free_mem_ptr = (unsigned long) &_end;
+ free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
+
puts("Uncompressing Linux... ");
decompress(input_data, input_len, NULL, NULL, output, NULL, error);
puts("Ok, booting the kernel.\n");
diff --git a/arch/s390/include/asm/hugetlb.h b/arch/s390/include/asm/hugetlb.h
index 11eae5f..9787b61 100644
--- a/arch/s390/include/asm/hugetlb.h
+++ b/arch/s390/include/asm/hugetlb.h
@@ -14,6 +14,7 @@
#define is_hugepage_only_range(mm, addr, len) 0
#define hugetlb_free_pgd_range free_pgd_range
+#define hugepages_supported() (MACHINE_HAS_HPAGE)
void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pte);
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index d539e15..5848b4a 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -863,6 +863,8 @@
{
pgste_t pgste;
+ if (pte_present(entry))
+ pte_val(entry) &= ~_PAGE_UNUSED;
if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
pgste_val(pgste) &= ~_PGSTE_GPS_ZERO;
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index d559bdb..2006b95 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -74,7 +74,8 @@
#else /* CONFIG_64BIT */
-#define TASK_SIZE_OF(tsk) ((tsk)->mm->context.asce_limit)
+#define TASK_SIZE_OF(tsk) ((tsk)->mm ? \
+ (tsk)->mm->context.asce_limit : TASK_MAX_SIZE)
#define TASK_UNMAPPED_BASE (test_thread_flag(TIF_31BIT) ? \
(1UL << 30) : (1UL << 41))
#define TASK_SIZE TASK_SIZE_OF(current)
diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h
index cd4c68e..6e5fc2d 100644
--- a/arch/s390/include/asm/uaccess.h
+++ b/arch/s390/include/asm/uaccess.h
@@ -148,7 +148,7 @@
" jg 2b\n" \
".popsection\n" \
EX_TABLE(0b,3b) EX_TABLE(1b,3b) \
- : "=d" (__rc), "=Q" (*(to)) \
+ : "=d" (__rc), "+Q" (*(to)) \
: "d" (size), "Q" (*(from)), \
"d" (__reg0), "K" (-EFAULT) \
: "cc"); \
@@ -213,28 +213,28 @@
__chk_user_ptr(ptr); \
switch (sizeof(*(ptr))) { \
case 1: { \
- unsigned char __x; \
+ unsigned char __x = 0; \
__gu_err = __get_user_fn(&__x, ptr, \
sizeof(*(ptr))); \
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
break; \
}; \
case 2: { \
- unsigned short __x; \
+ unsigned short __x = 0; \
__gu_err = __get_user_fn(&__x, ptr, \
sizeof(*(ptr))); \
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
break; \
}; \
case 4: { \
- unsigned int __x; \
+ unsigned int __x = 0; \
__gu_err = __get_user_fn(&__x, ptr, \
sizeof(*(ptr))); \
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
break; \
}; \
case 8: { \
- unsigned long long __x; \
+ unsigned long long __x = 0; \
__gu_err = __get_user_fn(&__x, ptr, \
sizeof(*(ptr))); \
(x) = *(__force __typeof__(*(ptr)) *) &__x; \
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index 39badb9..f5ec059 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -2102,13 +2102,6 @@
S390_lowcore.program_new_psw.addr =
PSW_ADDR_AMODE | (unsigned long) s390_base_pgm_handler;
- /*
- * Clear subchannel ID and number to signal new kernel that no CCW or
- * SCSI IPL has been done (for kexec and kdump)
- */
- S390_lowcore.subchannel_id = 0;
- S390_lowcore.subchannel_nr = 0;
-
/* Store status at absolute zero */
store_status();
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index e80d9ff..ec3b505 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -822,10 +822,10 @@
{
struct sysinfo_3_2_2 *vmms;
- vmms = (struct sysinfo_3_2_2 *) alloc_page(GFP_KERNEL);
- if (vmms && stsi(vmms, 3, 2, 2) == 0 && vmms->count)
- add_device_randomness(&vmms, vmms->count);
- free_page((unsigned long) vmms);
+ vmms = (struct sysinfo_3_2_2 *) memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+ if (stsi(vmms, 3, 2, 2) == 0 && vmms->count)
+ add_device_randomness(&vmms->vm, sizeof(vmms->vm[0]) * vmms->count);
+ memblock_free((unsigned long) vmms, PAGE_SIZE);
}
/*
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 49e4d64..0ce029c 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -207,6 +207,9 @@
struct kvm_memory_slot *memslot;
int is_dirty = 0;
+ if (kvm_is_ucontrol(kvm))
+ return -EINVAL;
+
mutex_lock(&kvm->slots_lock);
r = -EINVAL;
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 1b79ca6..d0f5987 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -207,7 +207,7 @@
static void gmap_flush_tlb(struct gmap *gmap)
{
if (MACHINE_HAS_IDTE)
- __tlb_flush_asce(gmap->mm, gmap->asce);
+ __tlb_flush_idte(gmap->asce);
else
__tlb_flush_global();
}
@@ -246,7 +246,7 @@
/* Flush tlb. */
if (MACHINE_HAS_IDTE)
- __tlb_flush_asce(gmap->mm, gmap->asce);
+ __tlb_flush_idte(gmap->asce);
else
__tlb_flush_global();
@@ -1358,11 +1358,28 @@
*/
bool gmap_test_and_clear_dirty(unsigned long address, struct gmap *gmap)
{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
pte_t *pte;
spinlock_t *ptl;
bool dirty = false;
- pte = get_locked_pte(gmap->mm, address, &ptl);
+ pgd = pgd_offset(gmap->mm, address);
+ pud = pud_alloc(gmap->mm, pgd, address);
+ if (!pud)
+ return false;
+ pmd = pmd_alloc(gmap->mm, pud, address);
+ if (!pmd)
+ return false;
+ /* We can't run guests backed by huge pages, but userspace can
+ * still set them up and then try to migrate them without any
+ * migration support.
+ */
+ if (pmd_large(*pmd))
+ return true;
+
+ pte = pte_alloc_map_lock(gmap->mm, pmd, address, &ptl);
if (unlikely(!pte))
return false;
diff --git a/arch/score/include/asm/uaccess.h b/arch/score/include/asm/uaccess.h
index ab66ddd..69326df 100644
--- a/arch/score/include/asm/uaccess.h
+++ b/arch/score/include/asm/uaccess.h
@@ -158,7 +158,7 @@
__get_user_asm(val, "lw", ptr); \
break; \
case 8: \
- if ((copy_from_user((void *)&val, ptr, 8)) == 0) \
+ if (__copy_from_user((void *)&val, ptr, 8) == 0) \
__gu_err = 0; \
else \
__gu_err = -EFAULT; \
@@ -183,6 +183,8 @@
\
if (likely(access_ok(VERIFY_READ, __gu_ptr, size))) \
__get_user_common((x), size, __gu_ptr); \
+ else \
+ (x) = 0; \
\
__gu_err; \
})
@@ -196,6 +198,7 @@
"2:\n" \
".section .fixup,\"ax\"\n" \
"3:li %0, %4\n" \
+ "li %1, 0\n" \
"j 2b\n" \
".previous\n" \
".section __ex_table,\"a\"\n" \
@@ -293,35 +296,34 @@
static inline unsigned long
copy_from_user(void *to, const void *from, unsigned long len)
{
- unsigned long over;
+ unsigned long res = len;
- if (access_ok(VERIFY_READ, from, len))
- return __copy_tofrom_user(to, from, len);
+ if (likely(access_ok(VERIFY_READ, from, len)))
+ res = __copy_tofrom_user(to, from, len);
- if ((unsigned long)from < TASK_SIZE) {
- over = (unsigned long)from + len - TASK_SIZE;
- return __copy_tofrom_user(to, from, len - over) + over;
- }
- return len;
+ if (unlikely(res))
+ memset(to + (len - res), 0, res);
+
+ return res;
}
static inline unsigned long
copy_to_user(void *to, const void *from, unsigned long len)
{
- unsigned long over;
+ if (likely(access_ok(VERIFY_WRITE, to, len)))
+ len = __copy_tofrom_user(to, from, len);
- if (access_ok(VERIFY_WRITE, to, len))
- return __copy_tofrom_user(to, from, len);
-
- if ((unsigned long)to < TASK_SIZE) {
- over = (unsigned long)to + len - TASK_SIZE;
- return __copy_tofrom_user(to, from, len - over) + over;
- }
return len;
}
-#define __copy_from_user(to, from, len) \
- __copy_tofrom_user((to), (from), (len))
+static inline unsigned long
+__copy_from_user(void *to, const void *from, unsigned long len)
+{
+ unsigned long left = __copy_tofrom_user(to, from, len);
+ if (unlikely(left))
+ memset(to + (len - left), 0, left);
+ return left;
+}
#define __copy_to_user(to, from, len) \
__copy_tofrom_user((to), (from), (len))
@@ -335,17 +337,17 @@
static inline unsigned long
__copy_from_user_inatomic(void *to, const void *from, unsigned long len)
{
- return __copy_from_user(to, from, len);
+ return __copy_tofrom_user(to, from, len);
}
-#define __copy_in_user(to, from, len) __copy_from_user(to, from, len)
+#define __copy_in_user(to, from, len) __copy_tofrom_user(to, from, len)
static inline unsigned long
copy_in_user(void *to, const void *from, unsigned long len)
{
if (access_ok(VERIFY_READ, from, len) &&
access_ok(VERFITY_WRITE, to, len))
- return copy_from_user(to, from, len);
+ return __copy_tofrom_user(to, from, len);
}
/*
diff --git a/arch/sh/include/asm/uaccess.h b/arch/sh/include/asm/uaccess.h
index 9486376..c04cc18 100644
--- a/arch/sh/include/asm/uaccess.h
+++ b/arch/sh/include/asm/uaccess.h
@@ -151,7 +151,10 @@
__kernel_size_t __copy_size = (__kernel_size_t) n;
if (__copy_size && __access_ok(__copy_from, __copy_size))
- return __copy_user(to, from, __copy_size);
+ __copy_size = __copy_user(to, from, __copy_size);
+
+ if (unlikely(__copy_size))
+ memset(to + (n - __copy_size), 0, __copy_size);
return __copy_size;
}
diff --git a/arch/sh/include/asm/uaccess_64.h b/arch/sh/include/asm/uaccess_64.h
index 2e07e0f..a2f9d053 100644
--- a/arch/sh/include/asm/uaccess_64.h
+++ b/arch/sh/include/asm/uaccess_64.h
@@ -24,6 +24,7 @@
#define __get_user_size(x,ptr,size,retval) \
do { \
retval = 0; \
+ x = 0; \
switch (size) { \
case 1: \
retval = __get_user_asm_b((void *)&x, \
diff --git a/arch/sparc/include/asm/head_64.h b/arch/sparc/include/asm/head_64.h
index 10e9dab..f0700cf 100644
--- a/arch/sparc/include/asm/head_64.h
+++ b/arch/sparc/include/asm/head_64.h
@@ -15,6 +15,10 @@
#define PTREGS_OFF (STACK_BIAS + STACKFRAME_SZ)
+#define RTRAP_PSTATE (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV|PSTATE_IE)
+#define RTRAP_PSTATE_IRQOFF (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV)
+#define RTRAP_PSTATE_AG_IRQOFF (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV|PSTATE_AG)
+
#define __CHEETAH_ID 0x003e0014
#define __JALAPENO_ID 0x003e0016
#define __SERRANO_ID 0x003e0022
diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h
index bfeb626..9a465b0 100644
--- a/arch/sparc/include/asm/pgtable_64.h
+++ b/arch/sparc/include/asm/pgtable_64.h
@@ -666,14 +666,7 @@
return pte_pfn(pte);
}
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-static inline unsigned long pmd_young(pmd_t pmd)
-{
- pte_t pte = __pte(pmd_val(pmd));
-
- return pte_young(pte);
-}
-
+#define __HAVE_ARCH_PMD_WRITE
static inline unsigned long pmd_write(pmd_t pmd)
{
pte_t pte = __pte(pmd_val(pmd));
@@ -681,6 +674,21 @@
return pte_write(pte);
}
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+static inline unsigned long pmd_dirty(pmd_t pmd)
+{
+ pte_t pte = __pte(pmd_val(pmd));
+
+ return pte_dirty(pte);
+}
+
+static inline unsigned long pmd_young(pmd_t pmd)
+{
+ pte_t pte = __pte(pmd_val(pmd));
+
+ return pte_young(pte);
+}
+
static inline unsigned long pmd_trans_huge(pmd_t pmd)
{
pte_t pte = __pte(pmd_val(pmd));
diff --git a/arch/sparc/include/asm/ttable.h b/arch/sparc/include/asm/ttable.h
index 71b5a67..781b9f1d 100644
--- a/arch/sparc/include/asm/ttable.h
+++ b/arch/sparc/include/asm/ttable.h
@@ -589,8 +589,8 @@
restored; \
nop; nop; nop; nop; nop; nop; \
nop; nop; nop; nop; nop; \
- ba,a,pt %xcc, user_rtt_fill_fixup; \
- ba,a,pt %xcc, user_rtt_fill_fixup; \
+ ba,a,pt %xcc, user_rtt_fill_fixup_dax; \
+ ba,a,pt %xcc, user_rtt_fill_fixup_mna; \
ba,a,pt %xcc, user_rtt_fill_fixup;
@@ -652,8 +652,8 @@
restored; \
nop; nop; nop; nop; nop; \
nop; nop; nop; \
- ba,a,pt %xcc, user_rtt_fill_fixup; \
- ba,a,pt %xcc, user_rtt_fill_fixup; \
+ ba,a,pt %xcc, user_rtt_fill_fixup_dax; \
+ ba,a,pt %xcc, user_rtt_fill_fixup_mna; \
ba,a,pt %xcc, user_rtt_fill_fixup;
diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h
index 9634d08..79b0387 100644
--- a/arch/sparc/include/asm/uaccess_32.h
+++ b/arch/sparc/include/asm/uaccess_32.h
@@ -265,8 +265,10 @@
{
if (n && __access_ok((unsigned long) from, n))
return __copy_user((__force void __user *) to, from, n);
- else
+ else {
+ memset(to, 0, n);
return n;
+ }
}
static inline unsigned long __copy_from_user(void *to, const void __user *from, unsigned long n)
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 7cf9c6e..fdb1332 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -21,6 +21,7 @@
CFLAGS_REMOVE_pcr.o := -pg
endif
+obj-$(CONFIG_SPARC64) += urtt_fill.o
obj-$(CONFIG_SPARC32) += entry.o wof.o wuf.o
obj-$(CONFIG_SPARC32) += etrap_32.o
obj-$(CONFIG_SPARC32) += rtrap_32.o
diff --git a/arch/sparc/kernel/cherrs.S b/arch/sparc/kernel/cherrs.S
index 4ee1ad4..655628de 100644
--- a/arch/sparc/kernel/cherrs.S
+++ b/arch/sparc/kernel/cherrs.S
@@ -214,8 +214,7 @@
subcc %g1, %g2, %g1 ! Next cacheline
bge,pt %icc, 1b
nop
- ba,pt %xcc, dcpe_icpe_tl1_common
- nop
+ ba,a,pt %xcc, dcpe_icpe_tl1_common
do_dcpe_tl1_fatal:
sethi %hi(1f), %g7
@@ -224,8 +223,7 @@
mov 0x2, %o0
call cheetah_plus_parity_error
add %sp, PTREGS_OFF, %o1
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size do_dcpe_tl1,.-do_dcpe_tl1
.globl do_icpe_tl1
@@ -259,8 +257,7 @@
subcc %g1, %g2, %g1
bge,pt %icc, 1b
nop
- ba,pt %xcc, dcpe_icpe_tl1_common
- nop
+ ba,a,pt %xcc, dcpe_icpe_tl1_common
do_icpe_tl1_fatal:
sethi %hi(1f), %g7
@@ -269,8 +266,7 @@
mov 0x3, %o0
call cheetah_plus_parity_error
add %sp, PTREGS_OFF, %o1
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size do_icpe_tl1,.-do_icpe_tl1
.type dcpe_icpe_tl1_common,#function
@@ -456,7 +452,7 @@
cmp %g2, 0x63
be c_cee
nop
- ba,pt %xcc, c_deferred
+ ba,a,pt %xcc, c_deferred
.size __cheetah_log_error,.-__cheetah_log_error
/* Cheetah FECC trap handling, we get here from tl{0,1}_fecc
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index 33c02b15..a83707c 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -948,7 +948,24 @@
cmp %o0, 0
bne 3f
mov -ENOSYS, %o0
+
+ /* Syscall tracing can modify the registers. */
+ ld [%sp + STACKFRAME_SZ + PT_G1], %g1
+ sethi %hi(sys_call_table), %l7
+ ld [%sp + STACKFRAME_SZ + PT_I0], %i0
+ or %l7, %lo(sys_call_table), %l7
+ ld [%sp + STACKFRAME_SZ + PT_I1], %i1
+ ld [%sp + STACKFRAME_SZ + PT_I2], %i2
+ ld [%sp + STACKFRAME_SZ + PT_I3], %i3
+ ld [%sp + STACKFRAME_SZ + PT_I4], %i4
+ ld [%sp + STACKFRAME_SZ + PT_I5], %i5
+ cmp %g1, NR_syscalls
+ bgeu 3f
+ mov -ENOSYS, %o0
+
+ sll %g1, 2, %l4
mov %i0, %o0
+ ld [%l7 + %l4], %l7
mov %i1, %o1
mov %i2, %o2
mov %i3, %o3
diff --git a/arch/sparc/kernel/fpu_traps.S b/arch/sparc/kernel/fpu_traps.S
index a686482..336d275 100644
--- a/arch/sparc/kernel/fpu_traps.S
+++ b/arch/sparc/kernel/fpu_traps.S
@@ -100,8 +100,8 @@
fmuld %f0, %f2, %f26
faddd %f0, %f2, %f28
fmuld %f0, %f2, %f30
- b,pt %xcc, fpdis_exit
- nop
+ ba,a,pt %xcc, fpdis_exit
+
2: andcc %g5, FPRS_DU, %g0
bne,pt %icc, 3f
fzero %f32
@@ -144,8 +144,8 @@
fmuld %f32, %f34, %f58
faddd %f32, %f34, %f60
fmuld %f32, %f34, %f62
- ba,pt %xcc, fpdis_exit
- nop
+ ba,a,pt %xcc, fpdis_exit
+
3: mov SECONDARY_CONTEXT, %g3
add %g6, TI_FPREGS, %g1
@@ -197,8 +197,7 @@
fp_other_bounce:
call do_fpother
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size fp_other_bounce,.-fp_other_bounce
.align 32
diff --git a/arch/sparc/kernel/head_64.S b/arch/sparc/kernel/head_64.S
index 3d61fca..8ff5763 100644
--- a/arch/sparc/kernel/head_64.S
+++ b/arch/sparc/kernel/head_64.S
@@ -461,9 +461,8 @@
subcc %g3, 1, %g3
bne,pt %xcc, 41b
add %g1, 1, %g1
- mov SUN4V_CHIP_SPARC64X, %g4
ba,pt %xcc, 5f
- nop
+ mov SUN4V_CHIP_SPARC64X, %g4
49:
mov SUN4V_CHIP_UNKNOWN, %g4
@@ -548,8 +547,7 @@
stxa %g0, [%g7] ASI_DMMU
membar #Sync
- ba,pt %xcc, sun4u_continue
- nop
+ ba,a,pt %xcc, sun4u_continue
sun4v_init:
/* Set ctx 0 */
@@ -560,14 +558,12 @@
mov SECONDARY_CONTEXT, %g7
stxa %g0, [%g7] ASI_MMU
membar #Sync
- ba,pt %xcc, niagara_tlb_fixup
- nop
+ ba,a,pt %xcc, niagara_tlb_fixup
sun4u_continue:
BRANCH_IF_ANY_CHEETAH(g1, g7, cheetah_tlb_fixup)
- ba,pt %xcc, spitfire_tlb_fixup
- nop
+ ba,a,pt %xcc, spitfire_tlb_fixup
niagara_tlb_fixup:
mov 3, %g2 /* Set TLB type to hypervisor. */
@@ -639,8 +635,7 @@
call hypervisor_patch_cachetlbops
nop
- ba,pt %xcc, tlb_fixup_done
- nop
+ ba,a,pt %xcc, tlb_fixup_done
cheetah_tlb_fixup:
mov 2, %g2 /* Set TLB type to cheetah+. */
@@ -659,8 +654,7 @@
call cheetah_patch_cachetlbops
nop
- ba,pt %xcc, tlb_fixup_done
- nop
+ ba,a,pt %xcc, tlb_fixup_done
spitfire_tlb_fixup:
/* Set TLB type to spitfire. */
@@ -782,8 +776,7 @@
call %o1
add %sp, (2047 + 128), %o0
- ba,pt %xcc, 2f
- nop
+ ba,a,pt %xcc, 2f
1: sethi %hi(sparc64_ttable_tl0), %o0
set prom_set_trap_table_name, %g2
@@ -822,8 +815,7 @@
BRANCH_IF_ANY_CHEETAH(o2, o3, 1f)
- ba,pt %xcc, 2f
- nop
+ ba,a,pt %xcc, 2f
/* Disable STICK_INT interrupts. */
1:
diff --git a/arch/sparc/kernel/misctrap.S b/arch/sparc/kernel/misctrap.S
index 753b4f0..34b4933 100644
--- a/arch/sparc/kernel/misctrap.S
+++ b/arch/sparc/kernel/misctrap.S
@@ -18,8 +18,7 @@
109: or %g7, %lo(109b), %g7
call do_privact
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size __do_privact,.-__do_privact
.type do_mna,#function
@@ -46,8 +45,7 @@
mov %l5, %o2
call mem_address_unaligned
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size do_mna,.-do_mna
.type do_lddfmna,#function
@@ -65,8 +63,7 @@
mov %l5, %o2
call handle_lddfmna
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size do_lddfmna,.-do_lddfmna
.type do_stdfmna,#function
@@ -84,8 +81,7 @@
mov %l5, %o2
call handle_stdfmna
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size do_stdfmna,.-do_stdfmna
.type breakpoint_trap,#function
diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c
index b36365f4..f9288bf1 100644
--- a/arch/sparc/kernel/pci.c
+++ b/arch/sparc/kernel/pci.c
@@ -995,6 +995,23 @@
/* No special bus mastering setup handling */
}
+#ifdef CONFIG_PCI_IOV
+int pcibios_add_device(struct pci_dev *dev)
+{
+ struct pci_dev *pdev;
+
+ /* Add sriov arch specific initialization here.
+ * Copy dev_archdata from PF to VF
+ */
+ if (dev->is_virtfn) {
+ pdev = dev->physfn;
+ memcpy(&dev->dev.archdata, &pdev->dev.archdata,
+ sizeof(struct dev_archdata));
+ }
+ return 0;
+}
+#endif /* CONFIG_PCI_IOV */
+
static int __init pcibios_init(void)
{
pci_dfl_cache_line_size = 64 >> 2;
diff --git a/arch/sparc/kernel/ptrace_64.c b/arch/sparc/kernel/ptrace_64.c
index 9ddc492..c156617 100644
--- a/arch/sparc/kernel/ptrace_64.c
+++ b/arch/sparc/kernel/ptrace_64.c
@@ -311,7 +311,7 @@
}
if (!ret) {
- unsigned long y;
+ unsigned long y = regs->y;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
&y,
diff --git a/arch/sparc/kernel/rtrap_64.S b/arch/sparc/kernel/rtrap_64.S
index 39f0c66..8de386d 100644
--- a/arch/sparc/kernel/rtrap_64.S
+++ b/arch/sparc/kernel/rtrap_64.S
@@ -14,10 +14,6 @@
#include <asm/visasm.h>
#include <asm/processor.h>
-#define RTRAP_PSTATE (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV|PSTATE_IE)
-#define RTRAP_PSTATE_IRQOFF (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV)
-#define RTRAP_PSTATE_AG_IRQOFF (PSTATE_TSO|PSTATE_PEF|PSTATE_PRIV|PSTATE_AG)
-
#ifdef CONFIG_CONTEXT_TRACKING
# define SCHEDULE_USER schedule_user
#else
@@ -236,52 +232,17 @@
wrpr %g1, %cwp
ba,a,pt %xcc, user_rtt_fill_64bit
+user_rtt_fill_fixup_dax:
+ ba,pt %xcc, user_rtt_fill_fixup_common
+ mov 1, %g3
+
+user_rtt_fill_fixup_mna:
+ ba,pt %xcc, user_rtt_fill_fixup_common
+ mov 2, %g3
+
user_rtt_fill_fixup:
- rdpr %cwp, %g1
- add %g1, 1, %g1
- wrpr %g1, 0x0, %cwp
-
- rdpr %wstate, %g2
- sll %g2, 3, %g2
- wrpr %g2, 0x0, %wstate
-
- /* We know %canrestore and %otherwin are both zero. */
-
- sethi %hi(sparc64_kern_pri_context), %g2
- ldx [%g2 + %lo(sparc64_kern_pri_context)], %g2
- mov PRIMARY_CONTEXT, %g1
-
-661: stxa %g2, [%g1] ASI_DMMU
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- stxa %g2, [%g1] ASI_MMU
- .previous
-
- sethi %hi(KERNBASE), %g1
- flush %g1
-
- or %g4, FAULT_CODE_WINFIXUP, %g4
- stb %g4, [%g6 + TI_FAULT_CODE]
- stx %g5, [%g6 + TI_FAULT_ADDR]
-
- mov %g6, %l1
- wrpr %g0, 0x0, %tl
-
-661: nop
- .section .sun4v_1insn_patch, "ax"
- .word 661b
- SET_GL(0)
- .previous
-
- wrpr %g0, RTRAP_PSTATE, %pstate
-
- mov %l1, %g6
- ldx [%g6 + TI_TASK], %g4
- LOAD_PER_CPU_BASE(%g5, %g6, %g1, %g2, %g3)
- call do_sparc64_fault
- add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,pt %xcc, user_rtt_fill_fixup_common
+ clr %g3
user_rtt_pre_restore:
add %g1, 1, %g1
diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c
index 62deba7..9464626 100644
--- a/arch/sparc/kernel/signal32.c
+++ b/arch/sparc/kernel/signal32.c
@@ -138,12 +138,24 @@
return 0;
}
+/* Checks if the fp is valid. We always build signal frames which are
+ * 16-byte aligned, therefore we can always enforce that the restore
+ * frame has that property as well.
+ */
+static bool invalid_frame_pointer(void __user *fp, int fplen)
+{
+ if ((((unsigned long) fp) & 15) ||
+ ((unsigned long)fp) > 0x100000000ULL - fplen)
+ return true;
+ return false;
+}
+
void do_sigreturn32(struct pt_regs *regs)
{
struct signal_frame32 __user *sf;
compat_uptr_t fpu_save;
compat_uptr_t rwin_save;
- unsigned int psr;
+ unsigned int psr, ufp;
unsigned pc, npc;
sigset_t set;
compat_sigset_t seta;
@@ -158,11 +170,16 @@
sf = (struct signal_frame32 __user *) regs->u_regs[UREG_FP];
/* 1. Make sure we are not getting garbage from the user */
- if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) ||
- (((unsigned long) sf) & 3))
+ if (invalid_frame_pointer(sf, sizeof(*sf)))
goto segv;
- if (get_user(pc, &sf->info.si_regs.pc) ||
+ if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP]))
+ goto segv;
+
+ if (ufp & 0x7)
+ goto segv;
+
+ if (__get_user(pc, &sf->info.si_regs.pc) ||
__get_user(npc, &sf->info.si_regs.npc))
goto segv;
@@ -227,7 +244,7 @@
asmlinkage void do_rt_sigreturn32(struct pt_regs *regs)
{
struct rt_signal_frame32 __user *sf;
- unsigned int psr, pc, npc;
+ unsigned int psr, pc, npc, ufp;
compat_uptr_t fpu_save;
compat_uptr_t rwin_save;
sigset_t set;
@@ -242,11 +259,16 @@
sf = (struct rt_signal_frame32 __user *) regs->u_regs[UREG_FP];
/* 1. Make sure we are not getting garbage from the user */
- if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) ||
- (((unsigned long) sf) & 3))
+ if (invalid_frame_pointer(sf, sizeof(*sf)))
goto segv;
- if (get_user(pc, &sf->regs.pc) ||
+ if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
+ goto segv;
+
+ if (ufp & 0x7)
+ goto segv;
+
+ if (__get_user(pc, &sf->regs.pc) ||
__get_user(npc, &sf->regs.npc))
goto segv;
@@ -307,14 +329,6 @@
force_sig(SIGSEGV, current);
}
-/* Checks if the fp is valid */
-static int invalid_frame_pointer(void __user *fp, int fplen)
-{
- if ((((unsigned long) fp) & 7) || ((unsigned long)fp) > 0x100000000ULL - fplen)
- return 1;
- return 0;
-}
-
static void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize)
{
unsigned long sp;
diff --git a/arch/sparc/kernel/signal_32.c b/arch/sparc/kernel/signal_32.c
index 9ee72fc..8492291 100644
--- a/arch/sparc/kernel/signal_32.c
+++ b/arch/sparc/kernel/signal_32.c
@@ -60,10 +60,22 @@
#define SF_ALIGNEDSZ (((sizeof(struct signal_frame) + 7) & (~7)))
#define RT_ALIGNEDSZ (((sizeof(struct rt_signal_frame) + 7) & (~7)))
+/* Checks if the fp is valid. We always build signal frames which are
+ * 16-byte aligned, therefore we can always enforce that the restore
+ * frame has that property as well.
+ */
+static inline bool invalid_frame_pointer(void __user *fp, int fplen)
+{
+ if ((((unsigned long) fp) & 15) || !__access_ok((unsigned long)fp, fplen))
+ return true;
+
+ return false;
+}
+
asmlinkage void do_sigreturn(struct pt_regs *regs)
{
+ unsigned long up_psr, pc, npc, ufp;
struct signal_frame __user *sf;
- unsigned long up_psr, pc, npc;
sigset_t set;
__siginfo_fpu_t __user *fpu_save;
__siginfo_rwin_t __user *rwin_save;
@@ -77,10 +89,13 @@
sf = (struct signal_frame __user *) regs->u_regs[UREG_FP];
/* 1. Make sure we are not getting garbage from the user */
- if (!access_ok(VERIFY_READ, sf, sizeof(*sf)))
+ if (!invalid_frame_pointer(sf, sizeof(*sf)))
goto segv_and_exit;
- if (((unsigned long) sf) & 3)
+ if (get_user(ufp, &sf->info.si_regs.u_regs[UREG_FP]))
+ goto segv_and_exit;
+
+ if (ufp & 0x7)
goto segv_and_exit;
err = __get_user(pc, &sf->info.si_regs.pc);
@@ -127,7 +142,7 @@
asmlinkage void do_rt_sigreturn(struct pt_regs *regs)
{
struct rt_signal_frame __user *sf;
- unsigned int psr, pc, npc;
+ unsigned int psr, pc, npc, ufp;
__siginfo_fpu_t __user *fpu_save;
__siginfo_rwin_t __user *rwin_save;
sigset_t set;
@@ -135,8 +150,13 @@
synchronize_user_stack();
sf = (struct rt_signal_frame __user *) regs->u_regs[UREG_FP];
- if (!access_ok(VERIFY_READ, sf, sizeof(*sf)) ||
- (((unsigned long) sf) & 0x03))
+ if (!invalid_frame_pointer(sf, sizeof(*sf)))
+ goto segv;
+
+ if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
+ goto segv;
+
+ if (ufp & 0x7)
goto segv;
err = __get_user(pc, &sf->regs.pc);
@@ -178,15 +198,6 @@
force_sig(SIGSEGV, current);
}
-/* Checks if the fp is valid */
-static inline int invalid_frame_pointer(void __user *fp, int fplen)
-{
- if ((((unsigned long) fp) & 7) || !__access_ok((unsigned long)fp, fplen))
- return 1;
-
- return 0;
-}
-
static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize)
{
unsigned long sp = regs->u_regs[UREG_FP];
diff --git a/arch/sparc/kernel/signal_64.c b/arch/sparc/kernel/signal_64.c
index 1a69998..9acf982 100644
--- a/arch/sparc/kernel/signal_64.c
+++ b/arch/sparc/kernel/signal_64.c
@@ -52,7 +52,7 @@
unsigned char fenab;
int err;
- flush_user_windows();
+ synchronize_user_stack();
if (get_thread_wsaved() ||
(((unsigned long)ucp) & (sizeof(unsigned long)-1)) ||
(!__access_ok(ucp, sizeof(*ucp))))
@@ -234,6 +234,17 @@
goto out;
}
+/* Checks if the fp is valid. We always build rt signal frames which
+ * are 16-byte aligned, therefore we can always enforce that the
+ * restore frame has that property as well.
+ */
+static bool invalid_frame_pointer(void __user *fp)
+{
+ if (((unsigned long) fp) & 15)
+ return true;
+ return false;
+}
+
struct rt_signal_frame {
struct sparc_stackf ss;
siginfo_t info;
@@ -246,8 +257,8 @@
void do_rt_sigreturn(struct pt_regs *regs)
{
+ unsigned long tpc, tnpc, tstate, ufp;
struct rt_signal_frame __user *sf;
- unsigned long tpc, tnpc, tstate;
__siginfo_fpu_t __user *fpu_save;
__siginfo_rwin_t __user *rwin_save;
sigset_t set;
@@ -261,10 +272,16 @@
(regs->u_regs [UREG_FP] + STACK_BIAS);
/* 1. Make sure we are not getting garbage from the user */
- if (((unsigned long) sf) & 3)
+ if (invalid_frame_pointer(sf))
goto segv;
- err = get_user(tpc, &sf->regs.tpc);
+ if (get_user(ufp, &sf->regs.u_regs[UREG_FP]))
+ goto segv;
+
+ if ((ufp + STACK_BIAS) & 0x7)
+ goto segv;
+
+ err = __get_user(tpc, &sf->regs.tpc);
err |= __get_user(tnpc, &sf->regs.tnpc);
if (test_thread_flag(TIF_32BIT)) {
tpc &= 0xffffffff;
@@ -308,14 +325,6 @@
force_sig(SIGSEGV, current);
}
-/* Checks if the fp is valid */
-static int invalid_frame_pointer(void __user *fp)
-{
- if (((unsigned long) fp) & 15)
- return 1;
- return 0;
-}
-
static inline void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs, unsigned long framesize)
{
unsigned long sp = regs->u_regs[UREG_FP] + STACK_BIAS;
diff --git a/arch/sparc/kernel/sigutil_32.c b/arch/sparc/kernel/sigutil_32.c
index 0f6eebe..e5fe8ce 100644
--- a/arch/sparc/kernel/sigutil_32.c
+++ b/arch/sparc/kernel/sigutil_32.c
@@ -48,6 +48,10 @@
int restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
{
int err;
+
+ if (((unsigned long) fpu) & 3)
+ return -EFAULT;
+
#ifdef CONFIG_SMP
if (test_tsk_thread_flag(current, TIF_USEDFPU))
regs->psr &= ~PSR_EF;
@@ -97,7 +101,10 @@
struct thread_info *t = current_thread_info();
int i, wsaved, err;
- __get_user(wsaved, &rp->wsaved);
+ if (((unsigned long) rp) & 3)
+ return -EFAULT;
+
+ get_user(wsaved, &rp->wsaved);
if (wsaved > NSWINS)
return -EFAULT;
diff --git a/arch/sparc/kernel/sigutil_64.c b/arch/sparc/kernel/sigutil_64.c
index 387834a..36aadcb 100644
--- a/arch/sparc/kernel/sigutil_64.c
+++ b/arch/sparc/kernel/sigutil_64.c
@@ -37,7 +37,10 @@
unsigned long fprs;
int err;
- err = __get_user(fprs, &fpu->si_fprs);
+ if (((unsigned long) fpu) & 7)
+ return -EFAULT;
+
+ err = get_user(fprs, &fpu->si_fprs);
fprs_write(0);
regs->tstate &= ~TSTATE_PEF;
if (fprs & FPRS_DL)
@@ -72,7 +75,10 @@
struct thread_info *t = current_thread_info();
int i, wsaved, err;
- __get_user(wsaved, &rp->wsaved);
+ if (((unsigned long) rp) & 7)
+ return -EFAULT;
+
+ get_user(wsaved, &rp->wsaved);
if (wsaved > NSWINS)
return -EFAULT;
diff --git a/arch/sparc/kernel/spiterrs.S b/arch/sparc/kernel/spiterrs.S
index c357e40..4a73009 100644
--- a/arch/sparc/kernel/spiterrs.S
+++ b/arch/sparc/kernel/spiterrs.S
@@ -85,8 +85,7 @@
ba,pt %xcc, etraptl1
rd %pc, %g7
- ba,pt %xcc, 2f
- nop
+ ba,a,pt %xcc, 2f
1: ba,pt %xcc, etrap_irq
rd %pc, %g7
@@ -100,8 +99,7 @@
mov %l5, %o2
call spitfire_access_error
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size __spitfire_access_error,.-__spitfire_access_error
/* This is the trap handler entry point for ECC correctable
@@ -179,8 +177,7 @@
mov %l5, %o2
call spitfire_data_access_exception_tl1
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size __spitfire_data_access_exception_tl1,.-__spitfire_data_access_exception_tl1
.type __spitfire_data_access_exception,#function
@@ -200,8 +197,7 @@
mov %l5, %o2
call spitfire_data_access_exception
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size __spitfire_data_access_exception,.-__spitfire_data_access_exception
.type __spitfire_insn_access_exception_tl1,#function
@@ -220,8 +216,7 @@
mov %l5, %o2
call spitfire_insn_access_exception_tl1
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size __spitfire_insn_access_exception_tl1,.-__spitfire_insn_access_exception_tl1
.type __spitfire_insn_access_exception,#function
@@ -240,6 +235,5 @@
mov %l5, %o2
call spitfire_insn_access_exception
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
.size __spitfire_insn_access_exception,.-__spitfire_insn_access_exception
diff --git a/arch/sparc/kernel/syscalls.S b/arch/sparc/kernel/syscalls.S
index 33a17e7..6ec7531 100644
--- a/arch/sparc/kernel/syscalls.S
+++ b/arch/sparc/kernel/syscalls.S
@@ -148,7 +148,25 @@
add %sp, PTREGS_OFF, %o0
brnz,pn %o0, 3f
mov -ENOSYS, %o0
+
+ /* Syscall tracing can modify the registers. */
+ ldx [%sp + PTREGS_OFF + PT_V9_G1], %g1
+ sethi %hi(sys_call_table32), %l7
+ ldx [%sp + PTREGS_OFF + PT_V9_I0], %i0
+ or %l7, %lo(sys_call_table32), %l7
+ ldx [%sp + PTREGS_OFF + PT_V9_I1], %i1
+ ldx [%sp + PTREGS_OFF + PT_V9_I2], %i2
+ ldx [%sp + PTREGS_OFF + PT_V9_I3], %i3
+ ldx [%sp + PTREGS_OFF + PT_V9_I4], %i4
+ ldx [%sp + PTREGS_OFF + PT_V9_I5], %i5
+
+ cmp %g1, NR_syscalls
+ bgeu,pn %xcc, 3f
+ mov -ENOSYS, %o0
+
+ sll %g1, 2, %l4
srl %i0, 0, %o0
+ lduw [%l7 + %l4], %l7
srl %i4, 0, %o4
srl %i1, 0, %o1
srl %i2, 0, %o2
@@ -160,7 +178,25 @@
add %sp, PTREGS_OFF, %o0
brnz,pn %o0, 3f
mov -ENOSYS, %o0
+
+ /* Syscall tracing can modify the registers. */
+ ldx [%sp + PTREGS_OFF + PT_V9_G1], %g1
+ sethi %hi(sys_call_table64), %l7
+ ldx [%sp + PTREGS_OFF + PT_V9_I0], %i0
+ or %l7, %lo(sys_call_table64), %l7
+ ldx [%sp + PTREGS_OFF + PT_V9_I1], %i1
+ ldx [%sp + PTREGS_OFF + PT_V9_I2], %i2
+ ldx [%sp + PTREGS_OFF + PT_V9_I3], %i3
+ ldx [%sp + PTREGS_OFF + PT_V9_I4], %i4
+ ldx [%sp + PTREGS_OFF + PT_V9_I5], %i5
+
+ cmp %g1, NR_syscalls
+ bgeu,pn %xcc, 3f
+ mov -ENOSYS, %o0
+
+ sll %g1, 2, %l4
mov %i0, %o0
+ lduw [%l7 + %l4], %l7
mov %i1, %o1
mov %i2, %o2
mov %i3, %o3
diff --git a/arch/sparc/kernel/urtt_fill.S b/arch/sparc/kernel/urtt_fill.S
new file mode 100644
index 0000000..5604a2b0
--- /dev/null
+++ b/arch/sparc/kernel/urtt_fill.S
@@ -0,0 +1,98 @@
+#include <asm/thread_info.h>
+#include <asm/trap_block.h>
+#include <asm/spitfire.h>
+#include <asm/ptrace.h>
+#include <asm/head.h>
+
+ .text
+ .align 8
+ .globl user_rtt_fill_fixup_common
+user_rtt_fill_fixup_common:
+ rdpr %cwp, %g1
+ add %g1, 1, %g1
+ wrpr %g1, 0x0, %cwp
+
+ rdpr %wstate, %g2
+ sll %g2, 3, %g2
+ wrpr %g2, 0x0, %wstate
+
+ /* We know %canrestore and %otherwin are both zero. */
+
+ sethi %hi(sparc64_kern_pri_context), %g2
+ ldx [%g2 + %lo(sparc64_kern_pri_context)], %g2
+ mov PRIMARY_CONTEXT, %g1
+
+661: stxa %g2, [%g1] ASI_DMMU
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ stxa %g2, [%g1] ASI_MMU
+ .previous
+
+ sethi %hi(KERNBASE), %g1
+ flush %g1
+
+ mov %g4, %l4
+ mov %g5, %l5
+ brnz,pn %g3, 1f
+ mov %g3, %l3
+
+ or %g4, FAULT_CODE_WINFIXUP, %g4
+ stb %g4, [%g6 + TI_FAULT_CODE]
+ stx %g5, [%g6 + TI_FAULT_ADDR]
+1:
+ mov %g6, %l1
+ wrpr %g0, 0x0, %tl
+
+661: nop
+ .section .sun4v_1insn_patch, "ax"
+ .word 661b
+ SET_GL(0)
+ .previous
+
+ wrpr %g0, RTRAP_PSTATE, %pstate
+
+ mov %l1, %g6
+ ldx [%g6 + TI_TASK], %g4
+ LOAD_PER_CPU_BASE(%g5, %g6, %g1, %g2, %g3)
+
+ brnz,pn %l3, 1f
+ nop
+
+ call do_sparc64_fault
+ add %sp, PTREGS_OFF, %o0
+ ba,pt %xcc, rtrap
+ nop
+
+1: cmp %g3, 2
+ bne,pn %xcc, 2f
+ nop
+
+ sethi %hi(tlb_type), %g1
+ lduw [%g1 + %lo(tlb_type)], %g1
+ cmp %g1, 3
+ bne,pt %icc, 1f
+ add %sp, PTREGS_OFF, %o0
+ mov %l4, %o2
+ call sun4v_do_mna
+ mov %l5, %o1
+ ba,a,pt %xcc, rtrap
+1: mov %l4, %o1
+ mov %l5, %o2
+ call mem_address_unaligned
+ nop
+ ba,a,pt %xcc, rtrap
+
+2: sethi %hi(tlb_type), %g1
+ mov %l4, %o1
+ lduw [%g1 + %lo(tlb_type)], %g1
+ mov %l5, %o2
+ cmp %g1, 3
+ bne,pt %icc, 1f
+ add %sp, PTREGS_OFF, %o0
+ call sun4v_data_access_exception
+ nop
+ ba,a,pt %xcc, rtrap
+
+1: call spitfire_data_access_exception
+ nop
+ ba,a,pt %xcc, rtrap
diff --git a/arch/sparc/kernel/utrap.S b/arch/sparc/kernel/utrap.S
index b7f0f3f..c731e80 100644
--- a/arch/sparc/kernel/utrap.S
+++ b/arch/sparc/kernel/utrap.S
@@ -11,8 +11,7 @@
mov %l4, %o1
call bad_trap
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
invoke_utrap:
sllx %g3, 3, %g3
diff --git a/arch/sparc/kernel/vmlinux.lds.S b/arch/sparc/kernel/vmlinux.lds.S
index 0924305..7028b4d 100644
--- a/arch/sparc/kernel/vmlinux.lds.S
+++ b/arch/sparc/kernel/vmlinux.lds.S
@@ -33,6 +33,10 @@
jiffies = jiffies_64;
#endif
+#ifdef CONFIG_SPARC64
+ASSERT((swapper_tsb == 0x0000000000408000), "Error: sparc64 early assembler too large")
+#endif
+
SECTIONS
{
#ifdef CONFIG_SPARC64
diff --git a/arch/sparc/kernel/winfixup.S b/arch/sparc/kernel/winfixup.S
index 1e67ce9..855019a 100644
--- a/arch/sparc/kernel/winfixup.S
+++ b/arch/sparc/kernel/winfixup.S
@@ -32,8 +32,7 @@
rd %pc, %g7
call do_sparc64_fault
add %sp, PTREGS_OFF, %o0
- ba,pt %xcc, rtrap
- nop
+ ba,a,pt %xcc, rtrap
/* Be very careful about usage of the trap globals here.
* You cannot touch %g5 as that has the fault information.
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 2d91c62..7ad1baa 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -1377,7 +1377,7 @@
if ((long)addr < 0L) {
unsigned long pa = __pa(addr);
- if ((addr >> max_phys_bits) != 0UL)
+ if ((pa >> max_phys_bits) != 0UL)
return false;
return pfn_valid(pa >> PAGE_SHIFT);
@@ -2730,9 +2730,10 @@
* the Data-TLB for huge pages.
*/
if (tlb_type == cheetah_plus) {
+ bool need_context_reload = false;
unsigned long ctx;
- spin_lock(&ctx_alloc_lock);
+ spin_lock_irq(&ctx_alloc_lock);
ctx = mm->context.sparc64_ctx_val;
ctx &= ~CTX_PGSZ_MASK;
ctx |= CTX_PGSZ_BASE << CTX_PGSZ0_SHIFT;
@@ -2751,9 +2752,12 @@
* also executing in this address space.
*/
mm->context.sparc64_ctx_val = ctx;
- on_each_cpu(context_reload, mm, 0);
+ need_context_reload = true;
}
- spin_unlock(&ctx_alloc_lock);
+ spin_unlock_irq(&ctx_alloc_lock);
+
+ if (need_context_reload)
+ on_each_cpu(context_reload, mm, 0);
}
}
#endif
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 3d6cf30..3de18af 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -47,6 +47,8 @@
select ARCH_DISCARD_MEMBLOCK
select ARCH_WANT_OPTIONAL_GPIOLIB
select ARCH_WANT_FRAME_POINTERS
+ select HAVE_ARCH_HARDENED_USERCOPY
+ select HAVE_ARCH_WITHIN_STACK_FRAMES
select HAVE_DMA_ATTRS
select HAVE_DMA_CONTIGUOUS
select HAVE_KRETPROBES
@@ -303,6 +305,9 @@
config FIX_EARLYCON_MEM
def_bool y
+config DEBUG_RODATA
+ def_bool y
+
source "init/Kconfig"
source "kernel/Kconfig.freezer"
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index 61bd2ad..9922ecf 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -90,23 +90,12 @@
issues with the mapping of the EFI runtime regions into that
table.
-config DEBUG_RODATA
- bool "Write protect kernel read-only data structures"
- default y
- depends on DEBUG_KERNEL
- ---help---
- Mark the kernel read-only data as write-protected in the pagetables,
- in order to catch accidental (and incorrect) writes to such const
- data. This is recommended so that we can catch kernel bugs sooner.
- If in doubt, say "Y".
-
config DEBUG_RODATA_TEST
- bool "Testcase for the DEBUG_RODATA feature"
- depends on DEBUG_RODATA
+ bool "Testcase for the marking rodata read-only"
default y
---help---
- This option enables a testcase for the DEBUG_RODATA
- feature as well as for the change_page_attr() infrastructure.
+ This option enables a testcase for the setting rodata read-only
+ as well as for the change_page_attr() infrastructure.
If in doubt, say "N"
config DEBUG_SET_MODULE_RONX
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile
index f6eb6ee..cc8a6e8f 100644
--- a/arch/x86/boot/Makefile
+++ b/arch/x86/boot/Makefile
@@ -171,6 +171,9 @@
for i in lib lib64 share end ; do \
if [ -f /usr/$$i/syslinux/isolinux.bin ] ; then \
cp /usr/$$i/syslinux/isolinux.bin $(obj)/isoimage ; \
+ if [ -f /usr/$$i/syslinux/ldlinux.c32 ]; then \
+ cp /usr/$$i/syslinux/ldlinux.c32 $(obj)/isoimage ; \
+ fi ; \
break ; \
fi ; \
if [ $$i = end ] ; then exit 1 ; fi ; \
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index 04477d6..0ebfa0c 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -2,15 +2,14 @@
#define BOOT_COMPRESSED_MISC_H
/*
- * we have to be careful, because no indirections are allowed here, and
- * paravirt_ops is a kind of one. As it will only run in baremetal anyway,
- * we just keep it from happening
+ * Special hack: we have to be careful, because no indirections are allowed here,
+ * and paravirt_ops is a kind of one. As it will only run in baremetal anyway,
+ * we just keep it from happening. (This list needs to be extended when new
+ * paravirt and debugging variants are added.)
*/
#undef CONFIG_PARAVIRT
#undef CONFIG_KASAN
-#ifdef CONFIG_X86_32
-#define _ASM_X86_DESC_H 1
-#endif
+#undef CONFIG_PARAVIRT_SPINLOCKS
#include <linux/linkage.h>
#include <linux/screen_info.h>
diff --git a/arch/x86/configs/tiny.config b/arch/x86/configs/tiny.config
index 4e2ecfa..4b429df 100644
--- a/arch/x86/configs/tiny.config
+++ b/arch/x86/configs/tiny.config
@@ -1 +1,3 @@
CONFIG_NOHIGHMEM=y
+# CONFIG_HIGHMEM4G is not set
+# CONFIG_HIGHMEM64G is not set
diff --git a/arch/x86/crypto/ghash-clmulni-intel_glue.c b/arch/x86/crypto/ghash-clmulni-intel_glue.c
index de1d72e..2f5cd2f 100644
--- a/arch/x86/crypto/ghash-clmulni-intel_glue.c
+++ b/arch/x86/crypto/ghash-clmulni-intel_glue.c
@@ -218,6 +218,29 @@
}
}
+static int ghash_async_import(struct ahash_request *req, const void *in)
+{
+ struct ahash_request *cryptd_req = ahash_request_ctx(req);
+ struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
+ struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+ ghash_async_init(req);
+ memcpy(dctx, in, sizeof(*dctx));
+ return 0;
+
+}
+
+static int ghash_async_export(struct ahash_request *req, void *out)
+{
+ struct ahash_request *cryptd_req = ahash_request_ctx(req);
+ struct shash_desc *desc = cryptd_shash_desc(cryptd_req);
+ struct ghash_desc_ctx *dctx = shash_desc_ctx(desc);
+
+ memcpy(out, dctx, sizeof(*dctx));
+ return 0;
+
+}
+
static int ghash_async_digest(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
@@ -285,8 +308,11 @@
.final = ghash_async_final,
.setkey = ghash_async_setkey,
.digest = ghash_async_digest,
+ .export = ghash_async_export,
+ .import = ghash_async_import,
.halg = {
.digestsize = GHASH_DIGEST_SIZE,
+ .statesize = sizeof(struct ghash_desc_ctx),
.base = {
.cra_name = "ghash",
.cra_driver_name = "ghash-clmulni",
diff --git a/arch/x86/crypto/sha-mb/sha1_mb.c b/arch/x86/crypto/sha-mb/sha1_mb.c
index d42c9b7..418319b 100644
--- a/arch/x86/crypto/sha-mb/sha1_mb.c
+++ b/arch/x86/crypto/sha-mb/sha1_mb.c
@@ -457,10 +457,10 @@
req = cast_mcryptd_ctx_to_req(req_ctx);
if (irqs_disabled())
- rctx->complete(&req->base, ret);
+ req_ctx->complete(&req->base, ret);
else {
local_bh_disable();
- rctx->complete(&req->base, ret);
+ req_ctx->complete(&req->base, ret);
local_bh_enable();
}
}
diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h
index 0ab4f9f..3a45668 100644
--- a/arch/x86/include/asm/acpi.h
+++ b/arch/x86/include/asm/acpi.h
@@ -50,6 +50,7 @@
extern int (*__acpi_register_gsi)(struct device *dev, u32 gsi,
int trigger, int polarity);
+extern void (*__acpi_unregister_gsi)(u32 gsi);
static inline void disable_acpi(void)
{
diff --git a/arch/x86/include/asm/cacheflush.h b/arch/x86/include/asm/cacheflush.h
index 9863ee3..ba41e10 100644
--- a/arch/x86/include/asm/cacheflush.h
+++ b/arch/x86/include/asm/cacheflush.h
@@ -143,16 +143,10 @@
void clflush_cache_range(void *addr, unsigned int size);
-#ifdef CONFIG_DEBUG_RODATA
-void mark_rodata_ro(void);
extern const int rodata_test_data;
extern int kernel_set_to_readonly;
void set_kernel_text_rw(void);
void set_kernel_text_ro(void);
-#else
-static inline void set_kernel_text_rw(void) { }
-static inline void set_kernel_text_ro(void) { }
-#endif
#ifdef CONFIG_DEBUG_RODATA_TEST
int rodata_test(void);
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index ca3347a..2e5a79a 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -277,7 +277,7 @@
#define ARCH_DLINFO_IA32 \
do { \
- if (vdso32_enabled) { \
+ if (VDSO_CURRENT_BASE) { \
NEW_AUX_ENT(AT_SYSINFO, VDSO_ENTRY); \
NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE); \
} \
diff --git a/arch/x86/include/asm/hugetlb.h b/arch/x86/include/asm/hugetlb.h
index 68c0539..7aadd3c 100644
--- a/arch/x86/include/asm/hugetlb.h
+++ b/arch/x86/include/asm/hugetlb.h
@@ -4,6 +4,7 @@
#include <asm/page.h>
#include <asm-generic/hugetlb.h>
+#define hugepages_supported() cpu_has_pse
static inline int is_hugepage_only_range(struct mm_struct *mm,
unsigned long addr,
diff --git a/arch/x86/include/asm/kvm_para.h b/arch/x86/include/asm/kvm_para.h
index e62cf89..745c117 100644
--- a/arch/x86/include/asm/kvm_para.h
+++ b/arch/x86/include/asm/kvm_para.h
@@ -17,15 +17,8 @@
}
#endif /* CONFIG_KVM_GUEST */
-#ifdef CONFIG_DEBUG_RODATA
#define KVM_HYPERCALL \
ALTERNATIVE(".byte 0x0f,0x01,0xc1", ".byte 0x0f,0x01,0xd9", X86_FEATURE_VMMCALL)
-#else
-/* On AMD processors, vmcall will generate a trap that we will
- * then rewrite to the appropriate instruction.
- */
-#define KVM_HYPERCALL ".byte 0x0f,0x01,0xc1"
-#endif
/* For KVM hypercalls, a three-byte sequence of either the vmcall or the vmmcall
* instruction. The hypervisor may replace it with something else but only the
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index aa97a07..081d6f4 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -99,6 +99,11 @@
return pte_flags(pte) & _PAGE_ACCESSED;
}
+static inline int pmd_dirty(pmd_t pmd)
+{
+ return pmd_flags(pmd) & _PAGE_DIRTY;
+}
+
static inline int pmd_young(pmd_t pmd)
{
return pmd_flags(pmd) & _PAGE_ACCESSED;
diff --git a/arch/x86/include/asm/sections.h b/arch/x86/include/asm/sections.h
index 0a52424..13b6cdd 100644
--- a/arch/x86/include/asm/sections.h
+++ b/arch/x86/include/asm/sections.h
@@ -7,7 +7,7 @@
extern char __brk_base[], __brk_limit[];
extern struct exception_table_entry __stop___ex_table[];
-#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
+#if defined(CONFIG_X86_64)
extern char __end_rodata_hpage_align[];
#endif
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index c4d9694..a7e5af2 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -197,6 +197,50 @@
return ti;
}
+/*
+ * Walks up the stack frames to make sure that the specified object is
+ * entirely contained by a single stack frame.
+ *
+ * Returns:
+ * 1 if within a frame
+ * -1 if placed across a frame boundary (or outside stack)
+ * 0 unable to determine (no frame pointers, etc)
+ */
+static inline int arch_within_stack_frames(const void * const stack,
+ const void * const stackend,
+ const void *obj, unsigned long len)
+{
+#if defined(CONFIG_FRAME_POINTER)
+ const void *frame = NULL;
+ const void *oldframe;
+
+ oldframe = __builtin_frame_address(1);
+ if (oldframe)
+ frame = __builtin_frame_address(2);
+ /*
+ * low ----------------------------------------------> high
+ * [saved bp][saved ip][args][local vars][saved bp][saved ip]
+ * ^----------------^
+ * allow copies only within here
+ */
+ while (stack <= frame && frame < stackend) {
+ /*
+ * If obj + len extends past the last frame, this
+ * check won't pass and the next frame will be 0,
+ * causing us to bail out and correctly report
+ * the copy as invalid.
+ */
+ if (obj + len <= frame)
+ return obj >= oldframe + 2 * sizeof(void *) ? 1 : -1;
+ oldframe = frame;
+ frame = *(const void * const *)frame;
+ }
+ return -1;
+#else
+ return 0;
+#endif
+}
+
#else /* !__ASSEMBLY__ */
/* how to get the thread information struct from ASM */
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index cd79194..7e459b7 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -32,7 +32,7 @@
/* Initialize cr4 shadow for this CPU. */
static inline void cr4_init_shadow(void)
{
- this_cpu_write(cpu_tlbstate.cr4, __read_cr4());
+ this_cpu_write(cpu_tlbstate.cr4, __read_cr4_safe());
}
/* Set in this cpu's CR4. */
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 0d592e0..c158198 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -133,6 +133,9 @@
extern int __get_user_8(void);
extern int __get_user_bad(void);
+#define __uaccess_begin() stac()
+#define __uaccess_end() clac()
+
/*
* This is a type: either unsigned long, if the argument fits into
* that type, or otherwise unsigned long long.
@@ -179,8 +182,8 @@
asm volatile("call __get_user_%P3" \
: "=a" (__ret_gu), "=r" (__val_gu) \
: "0" (ptr), "i" (sizeof(*(ptr)))); \
- (x) = (__typeof__(*(ptr))) __val_gu; \
- __ret_gu; \
+ (x) = (__force __typeof__(*(ptr))) __val_gu; \
+ __builtin_expect(__ret_gu, 0); \
})
#define __put_user_x(size, x, ptr, __ret_pu) \
@@ -191,10 +194,10 @@
#ifdef CONFIG_X86_32
#define __put_user_asm_u64(x, addr, err, errret) \
- asm volatile(ASM_STAC "\n" \
+ asm volatile("\n" \
"1: movl %%eax,0(%2)\n" \
"2: movl %%edx,4(%2)\n" \
- "3: " ASM_CLAC "\n" \
+ "3:" \
".section .fixup,\"ax\"\n" \
"4: movl %3,%0\n" \
" jmp 3b\n" \
@@ -205,10 +208,10 @@
: "A" (x), "r" (addr), "i" (errret), "0" (err))
#define __put_user_asm_ex_u64(x, addr) \
- asm volatile(ASM_STAC "\n" \
+ asm volatile("\n" \
"1: movl %%eax,0(%1)\n" \
"2: movl %%edx,4(%1)\n" \
- "3: " ASM_CLAC "\n" \
+ "3:" \
_ASM_EXTABLE_EX(1b, 2b) \
_ASM_EXTABLE_EX(2b, 3b) \
: : "A" (x), "r" (addr))
@@ -275,7 +278,7 @@
__put_user_x(X, __pu_val, ptr, __ret_pu); \
break; \
} \
- __ret_pu; \
+ __builtin_expect(__ret_pu, 0); \
})
#define __put_user_size(x, ptr, size, retval, errret) \
@@ -301,6 +304,10 @@
} \
} while (0)
+/*
+ * This doesn't do __uaccess_begin/end - the exception handling
+ * around it must do that.
+ */
#define __put_user_size_ex(x, ptr, size) \
do { \
__chk_user_ptr(ptr); \
@@ -329,7 +336,7 @@
#define __get_user_asm_u64(x, ptr, retval, errret) \
__get_user_asm(x, ptr, retval, "q", "", "=r", errret)
#define __get_user_asm_ex_u64(x, ptr) \
- __get_user_asm_ex(x, ptr, "q", "", "=r")
+ __get_user_asm_ex(x, ptr, "q", "", "=&r")
#endif
#define __get_user_size(x, ptr, size, retval, errret) \
@@ -355,9 +362,9 @@
} while (0)
#define __get_user_asm(x, addr, err, itype, rtype, ltype, errret) \
- asm volatile(ASM_STAC "\n" \
+ asm volatile("\n" \
"1: mov"itype" %2,%"rtype"1\n" \
- "2: " ASM_CLAC "\n" \
+ "2:\n" \
".section .fixup,\"ax\"\n" \
"3: mov %3,%0\n" \
" xor"itype" %"rtype"1,%"rtype"1\n" \
@@ -367,18 +374,22 @@
: "=r" (err), ltype(x) \
: "m" (__m(addr)), "i" (errret), "0" (err))
+/*
+ * This doesn't do __uaccess_begin/end - the exception handling
+ * around it must do that.
+ */
#define __get_user_size_ex(x, ptr, size) \
do { \
__chk_user_ptr(ptr); \
switch (size) { \
case 1: \
- __get_user_asm_ex(x, ptr, "b", "b", "=q"); \
+ __get_user_asm_ex(x, ptr, "b", "b", "=&q"); \
break; \
case 2: \
- __get_user_asm_ex(x, ptr, "w", "w", "=r"); \
+ __get_user_asm_ex(x, ptr, "w", "w", "=&r"); \
break; \
case 4: \
- __get_user_asm_ex(x, ptr, "l", "k", "=r"); \
+ __get_user_asm_ex(x, ptr, "l", "k", "=&r"); \
break; \
case 8: \
__get_user_asm_ex_u64(x, ptr); \
@@ -391,23 +402,31 @@
#define __get_user_asm_ex(x, addr, itype, rtype, ltype) \
asm volatile("1: mov"itype" %1,%"rtype"0\n" \
"2:\n" \
- _ASM_EXTABLE_EX(1b, 2b) \
- : ltype(x) : "m" (__m(addr)))
+ ".section .fixup,\"ax\"\n" \
+ "3:xor"itype" %"rtype"0,%"rtype"0\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE_EX(1b, 3b) \
+ : ltype(x) : "m" (__m(addr)), "0" (0))
#define __put_user_nocheck(x, ptr, size) \
({ \
int __pu_err; \
+ __uaccess_begin(); \
__put_user_size((x), (ptr), (size), __pu_err, -EFAULT); \
- __pu_err; \
+ __uaccess_end(); \
+ __builtin_expect(__pu_err, 0); \
})
#define __get_user_nocheck(x, ptr, size) \
({ \
int __gu_err; \
unsigned long __gu_val; \
+ __uaccess_begin(); \
__get_user_size(__gu_val, (ptr), (size), __gu_err, -EFAULT); \
+ __uaccess_end(); \
(x) = (__force __typeof__(*(ptr)))__gu_val; \
- __gu_err; \
+ __builtin_expect(__gu_err, 0); \
})
/* FIXME: this hack is definitely wrong -AK */
@@ -420,9 +439,9 @@
* aliasing issues.
*/
#define __put_user_asm(x, addr, err, itype, rtype, ltype, errret) \
- asm volatile(ASM_STAC "\n" \
+ asm volatile("\n" \
"1: mov"itype" %"rtype"1,%2\n" \
- "2: " ASM_CLAC "\n" \
+ "2:\n" \
".section .fixup,\"ax\"\n" \
"3: mov %3,%0\n" \
" jmp 2b\n" \
@@ -442,11 +461,11 @@
*/
#define uaccess_try do { \
current_thread_info()->uaccess_err = 0; \
- stac(); \
+ __uaccess_begin(); \
barrier();
#define uaccess_catch(err) \
- clac(); \
+ __uaccess_end(); \
(err) |= (current_thread_info()->uaccess_err ? -EFAULT : 0); \
} while (0)
@@ -542,12 +561,13 @@
__typeof__(ptr) __uval = (uval); \
__typeof__(*(ptr)) __old = (old); \
__typeof__(*(ptr)) __new = (new); \
+ __uaccess_begin(); \
switch (size) { \
case 1: \
{ \
- asm volatile("\t" ASM_STAC "\n" \
+ asm volatile("\n" \
"1:\t" LOCK_PREFIX "cmpxchgb %4, %2\n" \
- "2:\t" ASM_CLAC "\n" \
+ "2:\n" \
"\t.section .fixup, \"ax\"\n" \
"3:\tmov %3, %0\n" \
"\tjmp 2b\n" \
@@ -561,9 +581,9 @@
} \
case 2: \
{ \
- asm volatile("\t" ASM_STAC "\n" \
+ asm volatile("\n" \
"1:\t" LOCK_PREFIX "cmpxchgw %4, %2\n" \
- "2:\t" ASM_CLAC "\n" \
+ "2:\n" \
"\t.section .fixup, \"ax\"\n" \
"3:\tmov %3, %0\n" \
"\tjmp 2b\n" \
@@ -577,9 +597,9 @@
} \
case 4: \
{ \
- asm volatile("\t" ASM_STAC "\n" \
+ asm volatile("\n" \
"1:\t" LOCK_PREFIX "cmpxchgl %4, %2\n" \
- "2:\t" ASM_CLAC "\n" \
+ "2:\n" \
"\t.section .fixup, \"ax\"\n" \
"3:\tmov %3, %0\n" \
"\tjmp 2b\n" \
@@ -596,9 +616,9 @@
if (!IS_ENABLED(CONFIG_X86_64)) \
__cmpxchg_wrong_size(); \
\
- asm volatile("\t" ASM_STAC "\n" \
+ asm volatile("\n" \
"1:\t" LOCK_PREFIX "cmpxchgq %4, %2\n" \
- "2:\t" ASM_CLAC "\n" \
+ "2:\n" \
"\t.section .fixup, \"ax\"\n" \
"3:\tmov %3, %0\n" \
"\tjmp 2b\n" \
@@ -613,6 +633,7 @@
default: \
__cmpxchg_wrong_size(); \
} \
+ __uaccess_end(); \
*__uval = __old; \
__ret; \
})
@@ -684,7 +705,7 @@
#endif
-static inline unsigned long __must_check
+static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
{
int sz = __compiletime_object_size(to);
@@ -709,9 +730,10 @@
* case, and do only runtime checking for non-constant sizes.
*/
- if (likely(sz < 0 || sz >= n))
+ if (likely(sz < 0 || sz >= n)) {
+ check_object_size(to, n, false);
n = _copy_from_user(to, from, n);
- else if(__builtin_constant_p(n))
+ } else if (__builtin_constant_p(n))
copy_from_user_overflow();
else
__copy_from_user_overflow(sz, n);
@@ -719,7 +741,7 @@
return n;
}
-static inline unsigned long __must_check
+static __always_inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long n)
{
int sz = __compiletime_object_size(from);
@@ -727,9 +749,10 @@
might_fault();
/* See the comment in copy_from_user() above. */
- if (likely(sz < 0 || sz >= n))
+ if (likely(sz < 0 || sz >= n)) {
+ check_object_size(from, n, true);
n = _copy_to_user(to, from, n);
- else if(__builtin_constant_p(n))
+ } else if (__builtin_constant_p(n))
copy_to_user_overflow();
else
__copy_to_user_overflow(sz, n);
@@ -740,5 +763,30 @@
#undef __copy_from_user_overflow
#undef __copy_to_user_overflow
+/*
+ * The "unsafe" user accesses aren't really "unsafe", but the naming
+ * is a big fat warning: you have to not only do the access_ok()
+ * checking before using them, but you have to surround them with the
+ * user_access_begin/end() pair.
+ */
+#define user_access_begin() __uaccess_begin()
+#define user_access_end() __uaccess_end()
+
+#define unsafe_put_user(x, ptr, err_label) \
+do { \
+ int __pu_err; \
+ __put_user_size((x), (ptr), sizeof(*(ptr)), __pu_err, -EFAULT); \
+ if (unlikely(__pu_err)) goto err_label; \
+} while (0)
+
+#define unsafe_get_user(x, ptr, err_label) \
+do { \
+ int __gu_err; \
+ unsigned long __gu_val; \
+ __get_user_size(__gu_val, (ptr), sizeof(*(ptr)), __gu_err, -EFAULT); \
+ (x) = (__force __typeof__(*(ptr)))__gu_val; \
+ if (unlikely(__gu_err)) goto err_label; \
+} while (0)
+
#endif /* _ASM_X86_UACCESS_H */
diff --git a/arch/x86/include/asm/uaccess_32.h b/arch/x86/include/asm/uaccess_32.h
index 3c03a5d..6990f6a 100644
--- a/arch/x86/include/asm/uaccess_32.h
+++ b/arch/x86/include/asm/uaccess_32.h
@@ -43,6 +43,7 @@
static __always_inline unsigned long __must_check
__copy_to_user_inatomic(void __user *to, const void *from, unsigned long n)
{
+ check_object_size(from, n, true);
if (__builtin_constant_p(n)) {
unsigned long ret;
@@ -137,6 +138,7 @@
__copy_from_user(void *to, const void __user *from, unsigned long n)
{
might_fault();
+ check_object_size(to, n, false);
if (__builtin_constant_p(n)) {
unsigned long ret;
diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h
index 12a26b9..12496f7 100644
--- a/arch/x86/include/asm/uaccess_64.h
+++ b/arch/x86/include/asm/uaccess_64.h
@@ -53,38 +53,53 @@
{
int ret = 0;
+ check_object_size(dst, size, false);
if (!__builtin_constant_p(size))
return copy_user_generic(dst, (__force void *)src, size);
switch (size) {
- case 1:__get_user_asm(*(u8 *)dst, (u8 __user *)src,
+ case 1:
+ __uaccess_begin();
+ __get_user_asm(*(u8 *)dst, (u8 __user *)src,
ret, "b", "b", "=q", 1);
+ __uaccess_end();
return ret;
- case 2:__get_user_asm(*(u16 *)dst, (u16 __user *)src,
+ case 2:
+ __uaccess_begin();
+ __get_user_asm(*(u16 *)dst, (u16 __user *)src,
ret, "w", "w", "=r", 2);
+ __uaccess_end();
return ret;
- case 4:__get_user_asm(*(u32 *)dst, (u32 __user *)src,
+ case 4:
+ __uaccess_begin();
+ __get_user_asm(*(u32 *)dst, (u32 __user *)src,
ret, "l", "k", "=r", 4);
+ __uaccess_end();
return ret;
- case 8:__get_user_asm(*(u64 *)dst, (u64 __user *)src,
+ case 8:
+ __uaccess_begin();
+ __get_user_asm(*(u64 *)dst, (u64 __user *)src,
ret, "q", "", "=r", 8);
+ __uaccess_end();
return ret;
case 10:
+ __uaccess_begin();
__get_user_asm(*(u64 *)dst, (u64 __user *)src,
ret, "q", "", "=r", 10);
- if (unlikely(ret))
- return ret;
- __get_user_asm(*(u16 *)(8 + (char *)dst),
- (u16 __user *)(8 + (char __user *)src),
- ret, "w", "w", "=r", 2);
+ if (likely(!ret))
+ __get_user_asm(*(u16 *)(8 + (char *)dst),
+ (u16 __user *)(8 + (char __user *)src),
+ ret, "w", "w", "=r", 2);
+ __uaccess_end();
return ret;
case 16:
+ __uaccess_begin();
__get_user_asm(*(u64 *)dst, (u64 __user *)src,
ret, "q", "", "=r", 16);
- if (unlikely(ret))
- return ret;
- __get_user_asm(*(u64 *)(8 + (char *)dst),
- (u64 __user *)(8 + (char __user *)src),
- ret, "q", "", "=r", 8);
+ if (likely(!ret))
+ __get_user_asm(*(u64 *)(8 + (char *)dst),
+ (u64 __user *)(8 + (char __user *)src),
+ ret, "q", "", "=r", 8);
+ __uaccess_end();
return ret;
default:
return copy_user_generic(dst, (__force void *)src, size);
@@ -103,38 +118,55 @@
{
int ret = 0;
+ check_object_size(src, size, true);
if (!__builtin_constant_p(size))
return copy_user_generic((__force void *)dst, src, size);
switch (size) {
- case 1:__put_user_asm(*(u8 *)src, (u8 __user *)dst,
+ case 1:
+ __uaccess_begin();
+ __put_user_asm(*(u8 *)src, (u8 __user *)dst,
ret, "b", "b", "iq", 1);
+ __uaccess_end();
return ret;
- case 2:__put_user_asm(*(u16 *)src, (u16 __user *)dst,
+ case 2:
+ __uaccess_begin();
+ __put_user_asm(*(u16 *)src, (u16 __user *)dst,
ret, "w", "w", "ir", 2);
+ __uaccess_end();
return ret;
- case 4:__put_user_asm(*(u32 *)src, (u32 __user *)dst,
+ case 4:
+ __uaccess_begin();
+ __put_user_asm(*(u32 *)src, (u32 __user *)dst,
ret, "l", "k", "ir", 4);
+ __uaccess_end();
return ret;
- case 8:__put_user_asm(*(u64 *)src, (u64 __user *)dst,
+ case 8:
+ __uaccess_begin();
+ __put_user_asm(*(u64 *)src, (u64 __user *)dst,
ret, "q", "", "er", 8);
+ __uaccess_end();
return ret;
case 10:
+ __uaccess_begin();
__put_user_asm(*(u64 *)src, (u64 __user *)dst,
ret, "q", "", "er", 10);
- if (unlikely(ret))
- return ret;
- asm("":::"memory");
- __put_user_asm(4[(u16 *)src], 4 + (u16 __user *)dst,
- ret, "w", "w", "ir", 2);
+ if (likely(!ret)) {
+ asm("":::"memory");
+ __put_user_asm(4[(u16 *)src], 4 + (u16 __user *)dst,
+ ret, "w", "w", "ir", 2);
+ }
+ __uaccess_end();
return ret;
case 16:
+ __uaccess_begin();
__put_user_asm(*(u64 *)src, (u64 __user *)dst,
ret, "q", "", "er", 16);
- if (unlikely(ret))
- return ret;
- asm("":::"memory");
- __put_user_asm(1[(u64 *)src], 1 + (u64 __user *)dst,
- ret, "q", "", "er", 8);
+ if (likely(!ret)) {
+ asm("":::"memory");
+ __put_user_asm(1[(u64 *)src], 1 + (u64 __user *)dst,
+ ret, "q", "", "er", 8);
+ }
+ __uaccess_end();
return ret;
default:
return copy_user_generic((__force void *)dst, src, size);
@@ -160,39 +192,47 @@
switch (size) {
case 1: {
u8 tmp;
+ __uaccess_begin();
__get_user_asm(tmp, (u8 __user *)src,
ret, "b", "b", "=q", 1);
if (likely(!ret))
__put_user_asm(tmp, (u8 __user *)dst,
ret, "b", "b", "iq", 1);
+ __uaccess_end();
return ret;
}
case 2: {
u16 tmp;
+ __uaccess_begin();
__get_user_asm(tmp, (u16 __user *)src,
ret, "w", "w", "=r", 2);
if (likely(!ret))
__put_user_asm(tmp, (u16 __user *)dst,
ret, "w", "w", "ir", 2);
+ __uaccess_end();
return ret;
}
case 4: {
u32 tmp;
+ __uaccess_begin();
__get_user_asm(tmp, (u32 __user *)src,
ret, "l", "k", "=r", 4);
if (likely(!ret))
__put_user_asm(tmp, (u32 __user *)dst,
ret, "l", "k", "ir", 4);
+ __uaccess_end();
return ret;
}
case 8: {
u64 tmp;
+ __uaccess_begin();
__get_user_asm(tmp, (u64 __user *)src,
ret, "q", "", "=r", 8);
if (likely(!ret))
__put_user_asm(tmp, (u64 __user *)dst,
ret, "q", "", "er", 8);
+ __uaccess_end();
return ret;
}
default:
diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c
index f04dbb3..29f0c55 100644
--- a/arch/x86/kernel/amd_nb.c
+++ b/arch/x86/kernel/amd_nb.c
@@ -69,8 +69,8 @@
while ((misc = next_northbridge(misc, amd_nb_misc_ids)) != NULL)
i++;
- if (i == 0)
- return 0;
+ if (!i)
+ return -ENODEV;
nb = kzalloc(i * sizeof(struct amd_northbridge), GFP_KERNEL);
if (!nb)
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 813d29d..a86afc3 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -603,6 +603,17 @@
set_cpu_bug(c, X86_BUG_AMD_TLB_MMATCH);
}
+#define MSR_AMD64_DE_CFG 0xC0011029
+
+static void init_amd_ln(struct cpuinfo_x86 *c)
+{
+ /*
+ * Apply erratum 665 fix unconditionally so machines without a BIOS
+ * fix work.
+ */
+ msr_set_bit(MSR_AMD64_DE_CFG, 31);
+}
+
static void init_amd_bd(struct cpuinfo_x86 *c)
{
u64 value;
@@ -672,6 +683,7 @@
case 6: init_amd_k7(c); break;
case 0xf: init_amd_k8(c); break;
case 0x10: init_amd_gh(c); break;
+ case 0x12: init_amd_ln(c); break;
case 0x15: init_amd_bd(c); break;
}
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 88635b3..69608a4f5 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -291,10 +291,9 @@
static __always_inline void setup_smap(struct cpuinfo_x86 *c)
{
- unsigned long eflags;
+ unsigned long eflags = native_save_fl();
/* This should have been cleared long ago */
- raw_local_save_flags(eflags);
BUG_ON(eflags & X86_EFLAGS_AC);
if (cpu_has(c, X86_FEATURE_SMAP)) {
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd.c b/arch/x86/kernel/cpu/mcheck/mce_amd.c
index 5d4999f..be121fb 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd.c
@@ -51,7 +51,7 @@
"load_store",
"insn_fetch",
"combined_unit",
- "",
+ "decode_unit",
"northbridge",
"execution_unit",
};
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index df11583..22fbeaf 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -2609,7 +2609,7 @@
c->idxmsk64 |= (1ULL << x86_pmu.num_counters) - 1;
}
c->idxmsk64 &=
- ~(~0UL << (INTEL_PMC_IDX_FIXED + x86_pmu.num_counters_fixed));
+ ~(~0ULL << (INTEL_PMC_IDX_FIXED + x86_pmu.num_counters_fixed));
c->weight = hweight64(c->idxmsk64);
}
}
diff --git a/arch/x86/kernel/cpu/perf_event_intel_lbr.c b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
index 45fa730..eafcdb1 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_lbr.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_lbr.c
@@ -268,6 +268,8 @@
cpuc->lbr_entries[i].to = msr_lastbranch.to;
cpuc->lbr_entries[i].mispred = 0;
cpuc->lbr_entries[i].predicted = 0;
+ cpuc->lbr_entries[i].in_tx = 0;
+ cpuc->lbr_entries[i].abort = 0;
cpuc->lbr_entries[i].reserved = 0;
}
cpuc->lbr_stack.nr = i;
diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c
index 2e1a685..2fa494f 100644
--- a/arch/x86/kernel/early-quirks.c
+++ b/arch/x86/kernel/early-quirks.c
@@ -11,7 +11,11 @@
#include <linux/pci.h>
#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
#include <linux/pci_ids.h>
+#include <linux/bcma/bcma.h>
+#include <linux/bcma/bcma_regs.h>
#include <drm/i915_drm.h>
#include <asm/pci-direct.h>
#include <asm/dma.h>
@@ -21,6 +25,9 @@
#include <asm/iommu.h>
#include <asm/gart.h>
#include <asm/irq_remapping.h>
+#include <asm/early_ioremap.h>
+
+#define dev_err(msg) pr_err("pci 0000:%02x:%02x.%d: %s", bus, slot, func, msg)
static void __init fix_hypertransport_config(int num, int slot, int func)
{
@@ -76,6 +83,13 @@
#ifdef CONFIG_ACPI
#ifdef CONFIG_X86_IO_APIC
/*
+ * Only applies to Nvidia root ports (bus 0) and not to
+ * Nvidia graphics cards with PCI ports on secondary buses.
+ */
+ if (num)
+ return;
+
+ /*
* All timer overrides on Nvidia are
* wrong unless HPET is enabled.
* Unfortunately that's not true on many Asus boards.
@@ -565,6 +579,61 @@
#endif
}
+#define BCM4331_MMIO_SIZE 16384
+#define BCM4331_PM_CAP 0x40
+#define bcma_aread32(reg) ioread32(mmio + 1 * BCMA_CORE_SIZE + reg)
+#define bcma_awrite32(reg, val) iowrite32(val, mmio + 1 * BCMA_CORE_SIZE + reg)
+
+static void __init apple_airport_reset(int bus, int slot, int func)
+{
+ void __iomem *mmio;
+ u16 pmcsr;
+ u64 addr;
+ int i;
+
+ if (!dmi_match(DMI_SYS_VENDOR, "Apple Inc."))
+ return;
+
+ /* Card may have been put into PCI_D3hot by grub quirk */
+ pmcsr = read_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL);
+
+ if ((pmcsr & PCI_PM_CTRL_STATE_MASK) != PCI_D0) {
+ pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
+ write_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL, pmcsr);
+ mdelay(10);
+
+ pmcsr = read_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL);
+ if ((pmcsr & PCI_PM_CTRL_STATE_MASK) != PCI_D0) {
+ dev_err("Cannot power up Apple AirPort card\n");
+ return;
+ }
+ }
+
+ addr = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0);
+ addr |= (u64)read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_1) << 32;
+ addr &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ mmio = early_ioremap(addr, BCM4331_MMIO_SIZE);
+ if (!mmio) {
+ dev_err("Cannot iomap Apple AirPort card\n");
+ return;
+ }
+
+ pr_info("Resetting Apple AirPort card (left enabled by EFI)\n");
+
+ for (i = 0; bcma_aread32(BCMA_RESET_ST) && i < 30; i++)
+ udelay(10);
+
+ bcma_awrite32(BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
+ bcma_aread32(BCMA_RESET_CTL);
+ udelay(1);
+
+ bcma_awrite32(BCMA_RESET_CTL, 0);
+ bcma_aread32(BCMA_RESET_CTL);
+ udelay(10);
+
+ early_iounmap(mmio, BCM4331_MMIO_SIZE);
+}
#define QFLAG_APPLY_ONCE 0x1
#define QFLAG_APPLIED 0x2
@@ -578,12 +647,6 @@
void (*f)(int num, int slot, int func);
};
-/*
- * Only works for devices on the root bus. If you add any devices
- * not on bus 0 readd another loop level in early_quirks(). But
- * be careful because at least the Nvidia quirk here relies on
- * only matching on bus 0.
- */
static struct chipset early_qrk[] __initdata = {
{ PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, nvidia_bugs },
@@ -609,9 +672,13 @@
*/
{ PCI_VENDOR_ID_INTEL, 0x0f00,
PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet},
+ { PCI_VENDOR_ID_BROADCOM, 0x4331,
+ PCI_CLASS_NETWORK_OTHER, PCI_ANY_ID, 0, apple_airport_reset},
{}
};
+static void __init early_pci_scan_bus(int bus);
+
/**
* check_dev_quirk - apply early quirks to a given PCI device
* @num: bus number
@@ -620,7 +687,7 @@
*
* Check the vendor & device ID against the early quirks table.
*
- * If the device is single function, let early_quirks() know so we don't
+ * If the device is single function, let early_pci_scan_bus() know so we don't
* poke at this device again.
*/
static int __init check_dev_quirk(int num, int slot, int func)
@@ -629,6 +696,7 @@
u16 vendor;
u16 device;
u8 type;
+ u8 sec;
int i;
class = read_pci_config_16(num, slot, func, PCI_CLASS_DEVICE);
@@ -656,25 +724,36 @@
type = read_pci_config_byte(num, slot, func,
PCI_HEADER_TYPE);
+
+ if ((type & 0x7f) == PCI_HEADER_TYPE_BRIDGE) {
+ sec = read_pci_config_byte(num, slot, func, PCI_SECONDARY_BUS);
+ if (sec > num)
+ early_pci_scan_bus(sec);
+ }
+
if (!(type & 0x80))
return -1;
return 0;
}
-void __init early_quirks(void)
+static void __init early_pci_scan_bus(int bus)
{
int slot, func;
- if (!early_pci_allowed())
- return;
-
/* Poor man's PCI discovery */
- /* Only scan the root bus */
for (slot = 0; slot < 32; slot++)
for (func = 0; func < 8; func++) {
/* Only probe function 0 on single fn devices */
- if (check_dev_quirk(0, slot, func))
+ if (check_dev_quirk(bus, slot, func))
break;
}
}
+
+void __init early_quirks(void)
+{
+ if (!early_pci_allowed())
+ return;
+
+ early_pci_scan_bus(0);
+}
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 3386dc9..6911d03 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -80,9 +80,9 @@
static unsigned long text_ip_addr(unsigned long ip)
{
/*
- * On x86_64, kernel text mappings are mapped read-only with
- * CONFIG_DEBUG_RODATA. So we use the kernel identity mapping instead
- * of the kernel text mapping to modify the kernel text.
+ * On x86_64, kernel text mappings are mapped read-only, so we use
+ * the kernel identity mapping instead of the kernel text mapping
+ * to modify the kernel text.
*
* For 32bit kernels, these mappings are same and we can use
* kernel identity mapping to modify code.
@@ -703,6 +703,18 @@
unsigned long return_hooker = (unsigned long)
&return_to_handler;
+ /*
+ * When resuming from suspend-to-ram, this function can be indirectly
+ * called from early CPU startup code while the CPU is in real mode,
+ * which would fail miserably. Make sure the stack pointer is a
+ * virtual address.
+ *
+ * This check isn't as accurate as virt_addr_valid(), but it should be
+ * good enough for this purpose, and it's fast.
+ */
+ if (unlikely((long)__builtin_frame_address(0) >= 0))
+ return;
+
if (unlikely(ftrace_graph_is_dead()))
return;
diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c
index 7ec1d5f..3bfb0c3 100644
--- a/arch/x86/kernel/kgdb.c
+++ b/arch/x86/kernel/kgdb.c
@@ -745,9 +745,7 @@
int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
{
int err;
-#ifdef CONFIG_DEBUG_RODATA
char opc[BREAK_INSTR_SIZE];
-#endif /* CONFIG_DEBUG_RODATA */
bpt->type = BP_BREAKPOINT;
err = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr,
@@ -756,7 +754,6 @@
return err;
err = probe_kernel_write((char *)bpt->bpt_addr,
arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
-#ifdef CONFIG_DEBUG_RODATA
if (!err)
return err;
/*
@@ -773,13 +770,12 @@
if (memcmp(opc, arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE))
return -EINVAL;
bpt->type = BP_POKE_BREAKPOINT;
-#endif /* CONFIG_DEBUG_RODATA */
+
return err;
}
int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
{
-#ifdef CONFIG_DEBUG_RODATA
int err;
char opc[BREAK_INSTR_SIZE];
@@ -796,8 +792,8 @@
if (err || memcmp(opc, bpt->saved_instr, BREAK_INSTR_SIZE))
goto knl_write;
return err;
+
knl_write:
-#endif /* CONFIG_DEBUG_RODATA */
return probe_kernel_write((char *)bpt->bpt_addr,
(char *)bpt->saved_instr, BREAK_INSTR_SIZE);
}
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index f2e281c..a78aa11 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -931,7 +931,19 @@
* normal page fault.
*/
regs->ip = (unsigned long)cur->addr;
+ /*
+ * Trap flag (TF) has been set here because this fault
+ * happened where the single stepping will be done.
+ * So clear it by resetting the current kprobe:
+ */
+ regs->flags &= ~X86_EFLAGS_TF;
+
+ /*
+ * If the TF flag was set before the kprobe hit,
+ * don't touch it:
+ */
regs->flags |= kcb->kprobe_old_flags;
+
if (kcb->kprobe_status == KPROBE_REENTER)
restore_previous_kprobe(kcb);
else
diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c
index 8d12f05..8819ec7 100644
--- a/arch/x86/kernel/paravirt.c
+++ b/arch/x86/kernel/paravirt.c
@@ -55,12 +55,12 @@
".popsection");
/* identity function, which can be inlined */
-u32 _paravirt_ident_32(u32 x)
+u32 notrace _paravirt_ident_32(u32 x)
{
return x;
}
-u64 _paravirt_ident_64(u64 x)
+u64 notrace _paravirt_ident_64(u64 x)
{
return x;
}
diff --git a/arch/x86/kernel/sysfb_efi.c b/arch/x86/kernel/sysfb_efi.c
index b285d4e..5da924b 100644
--- a/arch/x86/kernel/sysfb_efi.c
+++ b/arch/x86/kernel/sysfb_efi.c
@@ -106,14 +106,24 @@
continue;
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
resource_size_t start, end;
+ unsigned long flags;
+
+ flags = pci_resource_flags(dev, i);
+ if (!(flags & IORESOURCE_MEM))
+ continue;
+
+ if (flags & IORESOURCE_UNSET)
+ continue;
+
+ if (pci_resource_len(dev, i) == 0)
+ continue;
start = pci_resource_start(dev, i);
- if (start == 0)
- break;
end = pci_resource_end(dev, i);
if (screen_info.lfb_base >= start &&
screen_info.lfb_base < end) {
found_bar = 1;
+ break;
}
}
}
diff --git a/arch/x86/kernel/test_nx.c b/arch/x86/kernel/test_nx.c
index 3f92ce0..27538f1 100644
--- a/arch/x86/kernel/test_nx.c
+++ b/arch/x86/kernel/test_nx.c
@@ -142,7 +142,6 @@
* by the error message
*/
-#ifdef CONFIG_DEBUG_RODATA
/* Test 3: Check if the .rodata section is executable */
if (rodata_test_data != 0xC3) {
printk(KERN_ERR "test_nx: .rodata marker has invalid value\n");
@@ -151,7 +150,6 @@
printk(KERN_ERR "test_nx: .rodata section is executable\n");
ret = -ENODEV;
}
-#endif
#if 0
/* Test 4: Check if the .data section of a module is executable */
diff --git a/arch/x86/kernel/test_rodata.c b/arch/x86/kernel/test_rodata.c
index b79133a..4649620 100644
--- a/arch/x86/kernel/test_rodata.c
+++ b/arch/x86/kernel/test_rodata.c
@@ -76,5 +76,5 @@
}
MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Testcase for the DEBUG_RODATA infrastructure");
+MODULE_DESCRIPTION("Testcase for marking rodata as read-only");
MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
diff --git a/arch/x86/kernel/tsc_msr.c b/arch/x86/kernel/tsc_msr.c
index 92ae6ac..6aa0f4d 100644
--- a/arch/x86/kernel/tsc_msr.c
+++ b/arch/x86/kernel/tsc_msr.c
@@ -92,7 +92,7 @@
if (freq_desc_tables[cpu_index].msr_plat) {
rdmsr(MSR_PLATFORM_INFO, lo, hi);
- ratio = (lo >> 8) & 0x1f;
+ ratio = (lo >> 8) & 0xff;
} else {
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
ratio = (hi >> 8) & 0x1f;
diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S
index 49edf2d..4724bf1 100644
--- a/arch/x86/kernel/vmlinux.lds.S
+++ b/arch/x86/kernel/vmlinux.lds.S
@@ -41,29 +41,28 @@
jiffies_64 = jiffies;
#endif
-#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
+#if defined(CONFIG_X86_64)
/*
- * On 64-bit, align RODATA to 2MB so that even with CONFIG_DEBUG_RODATA
- * we retain large page mappings for boundaries spanning kernel text, rodata
- * and data sections.
+ * On 64-bit, align RODATA to 2MB so we retain large page mappings for
+ * boundaries spanning kernel text, rodata and data sections.
*
* However, kernel identity mappings will have different RWX permissions
* to the pages mapping to text and to the pages padding (which are freed) the
* text section. Hence kernel identity mappings will be broken to smaller
* pages. For 64-bit, kernel text and kernel identity mappings are different,
- * so we can enable protection checks that come with CONFIG_DEBUG_RODATA,
- * as well as retain 2MB large page mappings for kernel text.
+ * so we can enable protection checks as well as retain 2MB large page
+ * mappings for kernel text.
*/
-#define X64_ALIGN_DEBUG_RODATA_BEGIN . = ALIGN(HPAGE_SIZE);
+#define X64_ALIGN_RODATA_BEGIN . = ALIGN(HPAGE_SIZE);
-#define X64_ALIGN_DEBUG_RODATA_END \
+#define X64_ALIGN_RODATA_END \
. = ALIGN(HPAGE_SIZE); \
__end_rodata_hpage_align = .;
#else
-#define X64_ALIGN_DEBUG_RODATA_BEGIN
-#define X64_ALIGN_DEBUG_RODATA_END
+#define X64_ALIGN_RODATA_BEGIN
+#define X64_ALIGN_RODATA_END
#endif
@@ -112,13 +111,11 @@
EXCEPTION_TABLE(16) :text = 0x9090
-#if defined(CONFIG_DEBUG_RODATA)
/* .text should occupy whole number of pages */
. = ALIGN(PAGE_SIZE);
-#endif
- X64_ALIGN_DEBUG_RODATA_BEGIN
+ X64_ALIGN_RODATA_BEGIN
RO_DATA(PAGE_SIZE)
- X64_ALIGN_DEBUG_RODATA_END
+ X64_ALIGN_RODATA_END
/* Data */
.data : AT(ADDR(.data) - LOAD_OFFSET) {
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index d9c11f3..cf53e4a 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -6079,14 +6079,20 @@
}
page = nested_get_page(vcpu, vmptr);
- if (page == NULL ||
- *(u32 *)kmap(page) != VMCS12_REVISION) {
+ if (page == NULL) {
nested_vmx_failInvalid(vcpu);
+ skip_emulated_instruction(vcpu);
+ return 1;
+ }
+ if (*(u32 *)kmap(page) != VMCS12_REVISION) {
kunmap(page);
+ nested_release_page_clean(page);
+ nested_vmx_failInvalid(vcpu);
skip_emulated_instruction(vcpu);
return 1;
}
kunmap(page);
+ nested_release_page_clean(page);
vmx->nested.vmxon_ptr = vmptr;
break;
case EXIT_REASON_VMCLEAR:
@@ -7767,14 +7773,29 @@
put_cpu();
}
+/*
+ * Ensure that the current vmcs of the logical processor is the
+ * vmcs01 of the vcpu before calling free_nested().
+ */
+static void vmx_free_vcpu_nested(struct kvm_vcpu *vcpu)
+{
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
+ int r;
+
+ r = vcpu_load(vcpu);
+ BUG_ON(r);
+ vmx_load_vmcs01(vcpu);
+ free_nested(vmx);
+ vcpu_put(vcpu);
+}
+
static void vmx_free_vcpu(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
free_vpid(vmx);
leave_guest_mode(vcpu);
- vmx_load_vmcs01(vcpu);
- free_nested(vmx);
+ vmx_free_vcpu_nested(vcpu);
free_loaded_vmcs(vmx->loaded_vmcs);
kfree(vmx->guest_msrs);
kvm_vcpu_uninit(vcpu);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index d77189c..518c7a8 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -656,7 +656,6 @@
if ((!(xcr0 & XSTATE_BNDREGS)) != (!(xcr0 & XSTATE_BNDCSR)))
return 1;
- kvm_put_guest_xcr0(vcpu);
vcpu->arch.xcr0 = xcr0;
if ((xcr0 ^ old_xcr0) & XSTATE_EXTEND_MASK)
@@ -3112,6 +3111,11 @@
if (dbgregs->flags)
return -EINVAL;
+ if (dbgregs->dr6 & ~0xffffffffull)
+ return -EINVAL;
+ if (dbgregs->dr7 & ~0xffffffffull)
+ return -EINVAL;
+
memcpy(vcpu->arch.db, dbgregs->db, sizeof(vcpu->arch.db));
vcpu->arch.dr6 = dbgregs->dr6;
kvm_update_dr6(vcpu);
@@ -6056,12 +6060,10 @@
}
/* try to inject new event if pending */
- if (vcpu->arch.nmi_pending) {
- if (kvm_x86_ops->nmi_allowed(vcpu)) {
- --vcpu->arch.nmi_pending;
- vcpu->arch.nmi_injected = true;
- kvm_x86_ops->set_nmi(vcpu);
- }
+ if (vcpu->arch.nmi_pending && kvm_x86_ops->nmi_allowed(vcpu)) {
+ --vcpu->arch.nmi_pending;
+ vcpu->arch.nmi_injected = true;
+ kvm_x86_ops->set_nmi(vcpu);
} else if (kvm_cpu_has_injectable_intr(vcpu)) {
/*
* Because interrupts can be injected asynchronously, we are
@@ -6231,10 +6233,12 @@
if (inject_pending_event(vcpu, req_int_win) != 0)
req_immediate_exit = true;
/* enable NMI/IRQ window open exits if needed */
- else if (vcpu->arch.nmi_pending)
- kvm_x86_ops->enable_nmi_window(vcpu);
- else if (kvm_cpu_has_injectable_intr(vcpu) || req_int_win)
- kvm_x86_ops->enable_irq_window(vcpu);
+ else {
+ if (vcpu->arch.nmi_pending)
+ kvm_x86_ops->enable_nmi_window(vcpu);
+ if (kvm_cpu_has_injectable_intr(vcpu) || req_int_win)
+ kvm_x86_ops->enable_irq_window(vcpu);
+ }
if (kvm_lapic_enabled(vcpu)) {
/*
@@ -6259,8 +6263,6 @@
kvm_x86_ops->prepare_guest_switch(vcpu);
if (vcpu->fpu_active)
kvm_load_guest_fpu(vcpu);
- kvm_load_guest_xcr0(vcpu);
-
vcpu->mode = IN_GUEST_MODE;
srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
@@ -6283,6 +6285,8 @@
goto cancel_injection;
}
+ kvm_load_guest_xcr0(vcpu);
+
if (req_immediate_exit)
smp_send_reschedule(vcpu->cpu);
@@ -6331,6 +6335,8 @@
vcpu->mode = OUTSIDE_GUEST_MODE;
smp_wmb();
+ kvm_put_guest_xcr0(vcpu);
+
/* Interrupt is enabled by handle_external_intr() */
kvm_x86_ops->handle_external_intr(vcpu);
@@ -6972,7 +6978,6 @@
* and assume host would use all available bits.
* Guest xcr0 would be loaded later.
*/
- kvm_put_guest_xcr0(vcpu);
vcpu->guest_fpu_loaded = 1;
__kernel_fpu_begin();
fpu_restore_checking(&vcpu->arch.guest_fpu);
@@ -6981,8 +6986,6 @@
void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
{
- kvm_put_guest_xcr0(vcpu);
-
if (!vcpu->guest_fpu_loaded)
return;
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index 0a59a63..bd03a95 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -581,20 +581,40 @@
* is valid. The argument is a physical page number.
*
*
- * On x86, access has to be given to the first megabyte of ram because that area
- * contains bios code and data regions used by X and dosemu and similar apps.
- * Access has to be given to non-kernel-ram areas as well, these contain the PCI
- * mmio resources as well as potential bios/acpi data regions.
+ * On x86, access has to be given to the first megabyte of RAM because that
+ * area traditionally contains BIOS code and data regions used by X, dosemu,
+ * and similar apps. Since they map the entire memory range, the whole range
+ * must be allowed (for mapping), but any areas that would otherwise be
+ * disallowed are flagged as being "zero filled" instead of rejected.
+ * Access has to be given to non-kernel-ram areas as well, these contain the
+ * PCI mmio resources as well as potential bios/acpi data regions.
*/
int devmem_is_allowed(unsigned long pagenr)
{
- if (pagenr < 256)
- return 1;
- if (iomem_is_exclusive(pagenr << PAGE_SHIFT))
+ if (page_is_ram(pagenr)) {
+ /*
+ * For disallowed memory regions in the low 1MB range,
+ * request that the page be shown as all zeros.
+ */
+ if (pagenr < 256)
+ return 2;
+
return 0;
- if (!page_is_ram(pagenr))
- return 1;
- return 0;
+ }
+
+ /*
+ * This must follow RAM test, since System RAM is considered a
+ * restricted resource under CONFIG_STRICT_IOMEM.
+ */
+ if (iomem_is_exclusive(pagenr << PAGE_SHIFT)) {
+ /* Low 1MB bypasses iomem restrictions. */
+ if (pagenr < 256)
+ return 1;
+
+ return 0;
+ }
+
+ return 1;
}
void free_init_pages(char *what, unsigned long begin, unsigned long end)
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index c23ab1e..0f98553 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -871,7 +871,6 @@
return flag;
}
-#ifdef CONFIG_DEBUG_RODATA
const int rodata_test_data = 0xC3;
EXPORT_SYMBOL_GPL(rodata_test_data);
@@ -958,5 +957,3 @@
#endif
mark_nxdata_nx();
}
-#endif
-
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index fa77995..a30510f 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -1074,7 +1074,6 @@
mem_init_print_info(NULL);
}
-#ifdef CONFIG_DEBUG_RODATA
const int rodata_test_data = 0xC3;
EXPORT_SYMBOL_GPL(rodata_test_data);
@@ -1164,8 +1163,6 @@
(unsigned long) __va(__pa_symbol(_sdata)));
}
-#endif
-
int kern_addr_valid(unsigned long addr)
{
unsigned long above = ((long)addr) >> __VIRTUAL_MASK_SHIFT;
diff --git a/arch/x86/mm/kmmio.c b/arch/x86/mm/kmmio.c
index 637ab34..ddb2244 100644
--- a/arch/x86/mm/kmmio.c
+++ b/arch/x86/mm/kmmio.c
@@ -33,7 +33,7 @@
struct kmmio_fault_page {
struct list_head list;
struct kmmio_fault_page *release_next;
- unsigned long page; /* location of the fault page */
+ unsigned long addr; /* the requested address */
pteval_t old_presence; /* page presence prior to arming */
bool armed;
@@ -70,9 +70,16 @@
static struct list_head kmmio_page_table[KMMIO_PAGE_TABLE_SIZE];
static LIST_HEAD(kmmio_probes);
-static struct list_head *kmmio_page_list(unsigned long page)
+static struct list_head *kmmio_page_list(unsigned long addr)
{
- return &kmmio_page_table[hash_long(page, KMMIO_PAGE_HASH_BITS)];
+ unsigned int l;
+ pte_t *pte = lookup_address(addr, &l);
+
+ if (!pte)
+ return NULL;
+ addr &= page_level_mask(l);
+
+ return &kmmio_page_table[hash_long(addr, KMMIO_PAGE_HASH_BITS)];
}
/* Accessed per-cpu */
@@ -98,15 +105,19 @@
}
/* You must be holding RCU read lock. */
-static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long page)
+static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long addr)
{
struct list_head *head;
struct kmmio_fault_page *f;
+ unsigned int l;
+ pte_t *pte = lookup_address(addr, &l);
- page &= PAGE_MASK;
- head = kmmio_page_list(page);
+ if (!pte)
+ return NULL;
+ addr &= page_level_mask(l);
+ head = kmmio_page_list(addr);
list_for_each_entry_rcu(f, head, list) {
- if (f->page == page)
+ if (f->addr == addr)
return f;
}
return NULL;
@@ -137,10 +148,10 @@
static int clear_page_presence(struct kmmio_fault_page *f, bool clear)
{
unsigned int level;
- pte_t *pte = lookup_address(f->page, &level);
+ pte_t *pte = lookup_address(f->addr, &level);
if (!pte) {
- pr_err("no pte for page 0x%08lx\n", f->page);
+ pr_err("no pte for addr 0x%08lx\n", f->addr);
return -1;
}
@@ -156,7 +167,7 @@
return -1;
}
- __flush_tlb_one(f->page);
+ __flush_tlb_one(f->addr);
return 0;
}
@@ -176,12 +187,12 @@
int ret;
WARN_ONCE(f->armed, KERN_ERR pr_fmt("kmmio page already armed.\n"));
if (f->armed) {
- pr_warning("double-arm: page 0x%08lx, ref %d, old %d\n",
- f->page, f->count, !!f->old_presence);
+ pr_warning("double-arm: addr 0x%08lx, ref %d, old %d\n",
+ f->addr, f->count, !!f->old_presence);
}
ret = clear_page_presence(f, true);
- WARN_ONCE(ret < 0, KERN_ERR pr_fmt("arming 0x%08lx failed.\n"),
- f->page);
+ WARN_ONCE(ret < 0, KERN_ERR pr_fmt("arming at 0x%08lx failed.\n"),
+ f->addr);
f->armed = true;
return ret;
}
@@ -191,7 +202,7 @@
{
int ret = clear_page_presence(f, false);
WARN_ONCE(ret < 0,
- KERN_ERR "kmmio disarming 0x%08lx failed.\n", f->page);
+ KERN_ERR "kmmio disarming at 0x%08lx failed.\n", f->addr);
f->armed = false;
}
@@ -215,6 +226,12 @@
struct kmmio_context *ctx;
struct kmmio_fault_page *faultpage;
int ret = 0; /* default to fault not handled */
+ unsigned long page_base = addr;
+ unsigned int l;
+ pte_t *pte = lookup_address(addr, &l);
+ if (!pte)
+ return -EINVAL;
+ page_base &= page_level_mask(l);
/*
* Preemption is now disabled to prevent process switch during
@@ -227,7 +244,7 @@
preempt_disable();
rcu_read_lock();
- faultpage = get_kmmio_fault_page(addr);
+ faultpage = get_kmmio_fault_page(page_base);
if (!faultpage) {
/*
* Either this page fault is not caused by kmmio, or
@@ -239,7 +256,7 @@
ctx = &get_cpu_var(kmmio_ctx);
if (ctx->active) {
- if (addr == ctx->addr) {
+ if (page_base == ctx->addr) {
/*
* A second fault on the same page means some other
* condition needs handling by do_page_fault(), the
@@ -267,9 +284,9 @@
ctx->active++;
ctx->fpage = faultpage;
- ctx->probe = get_kmmio_probe(addr);
+ ctx->probe = get_kmmio_probe(page_base);
ctx->saved_flags = (regs->flags & (X86_EFLAGS_TF | X86_EFLAGS_IF));
- ctx->addr = addr;
+ ctx->addr = page_base;
if (ctx->probe && ctx->probe->pre_handler)
ctx->probe->pre_handler(ctx->probe, regs, addr);
@@ -354,12 +371,11 @@
}
/* You must be holding kmmio_lock. */
-static int add_kmmio_fault_page(unsigned long page)
+static int add_kmmio_fault_page(unsigned long addr)
{
struct kmmio_fault_page *f;
- page &= PAGE_MASK;
- f = get_kmmio_fault_page(page);
+ f = get_kmmio_fault_page(addr);
if (f) {
if (!f->count)
arm_kmmio_fault_page(f);
@@ -372,26 +388,25 @@
return -1;
f->count = 1;
- f->page = page;
+ f->addr = addr;
if (arm_kmmio_fault_page(f)) {
kfree(f);
return -1;
}
- list_add_rcu(&f->list, kmmio_page_list(f->page));
+ list_add_rcu(&f->list, kmmio_page_list(f->addr));
return 0;
}
/* You must be holding kmmio_lock. */
-static void release_kmmio_fault_page(unsigned long page,
+static void release_kmmio_fault_page(unsigned long addr,
struct kmmio_fault_page **release_list)
{
struct kmmio_fault_page *f;
- page &= PAGE_MASK;
- f = get_kmmio_fault_page(page);
+ f = get_kmmio_fault_page(addr);
if (!f)
return;
@@ -420,18 +435,27 @@
int ret = 0;
unsigned long size = 0;
const unsigned long size_lim = p->len + (p->addr & ~PAGE_MASK);
+ unsigned int l;
+ pte_t *pte;
spin_lock_irqsave(&kmmio_lock, flags);
if (get_kmmio_probe(p->addr)) {
ret = -EEXIST;
goto out;
}
+
+ pte = lookup_address(p->addr, &l);
+ if (!pte) {
+ ret = -EINVAL;
+ goto out;
+ }
+
kmmio_count++;
list_add_rcu(&p->list, &kmmio_probes);
while (size < size_lim) {
if (add_kmmio_fault_page(p->addr + size))
pr_err("Unable to set page fault.\n");
- size += PAGE_SIZE;
+ size += page_level_size(l);
}
out:
spin_unlock_irqrestore(&kmmio_lock, flags);
@@ -506,11 +530,17 @@
const unsigned long size_lim = p->len + (p->addr & ~PAGE_MASK);
struct kmmio_fault_page *release_list = NULL;
struct kmmio_delayed_release *drelease;
+ unsigned int l;
+ pte_t *pte;
+
+ pte = lookup_address(p->addr, &l);
+ if (!pte)
+ return;
spin_lock_irqsave(&kmmio_lock, flags);
while (size < size_lim) {
release_kmmio_fault_page(p->addr + size, &release_list);
- size += PAGE_SIZE;
+ size += page_level_size(l);
}
list_del_rcu(&p->list);
kmmio_count--;
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index e5545f2..29add6b 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -281,7 +281,7 @@
__pa_symbol(__end_rodata) >> PAGE_SHIFT))
pgprot_val(forbidden) |= _PAGE_RW;
-#if defined(CONFIG_X86_64) && defined(CONFIG_DEBUG_RODATA)
+#if defined(CONFIG_X86_64)
/*
* Once the kernel maps the text as RO (kernel_set_to_readonly is set),
* kernel text mappings for the large page aligned text, rodata sections
diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c
index 4f6844b..df8101f 100644
--- a/arch/x86/pci/xen.c
+++ b/arch/x86/pci/xen.c
@@ -434,6 +434,7 @@
* just how GSIs get registered.
*/
__acpi_register_gsi = acpi_register_gsi_xen_hvm;
+ __acpi_unregister_gsi = NULL;
#endif
#ifdef CONFIG_PCI_MSI
@@ -455,8 +456,12 @@
pci_msi_ignore_mask = 1;
#endif
__acpi_register_gsi = acpi_register_gsi_xen;
- /* Pre-allocate legacy irqs */
- for (irq = 0; irq < nr_legacy_irqs(); irq++) {
+ __acpi_unregister_gsi = NULL;
+ /*
+ * Pre-allocate the legacy IRQs. Use NR_LEGACY_IRQS here
+ * because we don't have a PIC and thus nr_legacy_irqs() is zero.
+ */
+ for (irq = 0; irq < NR_IRQS_LEGACY; irq++) {
int trigger, polarity;
if (acpi_get_override_irq(irq, &trigger, &polarity) == -1)
diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile
index f52e033..43653ba 100644
--- a/arch/x86/purgatory/Makefile
+++ b/arch/x86/purgatory/Makefile
@@ -12,6 +12,7 @@
KBUILD_CFLAGS := -fno-strict-aliasing -Wall -Wstrict-prototypes -fno-zero-initialized-in-bss -fno-builtin -ffreestanding -c -MD -Os -mcmodel=large
KBUILD_CFLAGS += -m$(BITS)
+KBUILD_CFLAGS += $(call cc-option,-fno-PIE)
$(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
$(call if_changed,ld)
diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl
index 9fe1b5d..3d05d00 100644
--- a/arch/x86/syscalls/syscall_32.tbl
+++ b/arch/x86/syscalls/syscall_32.tbl
@@ -294,7 +294,7 @@
# 285 sys_setaltroot
286 i386 add_key sys_add_key
287 i386 request_key sys_request_key
-288 i386 keyctl sys_keyctl
+288 i386 keyctl sys_keyctl compat_sys_keyctl
289 i386 ioprio_set sys_ioprio_set
290 i386 ioprio_get sys_ioprio_get
291 i386 inotify_init sys_inotify_init
diff --git a/arch/x86/vdso/vdso2c.h b/arch/x86/vdso/vdso2c.h
index 0224987..3f69326 100644
--- a/arch/x86/vdso/vdso2c.h
+++ b/arch/x86/vdso/vdso2c.h
@@ -140,7 +140,7 @@
fprintf(outfile, "#include <asm/vdso.h>\n");
fprintf(outfile, "\n");
fprintf(outfile,
- "static unsigned char raw_data[%lu] __page_aligned_data = {",
+ "static unsigned char raw_data[%lu] __ro_after_init __aligned(PAGE_SIZE) = {",
mapping_size);
for (j = 0; j < stripped_len; j++) {
if (j % 10 == 0)
diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c
index e904c27..566a51e 100644
--- a/arch/x86/vdso/vdso32-setup.c
+++ b/arch/x86/vdso/vdso32-setup.c
@@ -31,8 +31,10 @@
{
vdso32_enabled = simple_strtoul(s, NULL, 0);
- if (vdso32_enabled > 1)
+ if (vdso32_enabled > 1) {
pr_warn("vdso32 values other than 0 and 1 are no longer allowed; vdso disabled\n");
+ vdso32_enabled = 0;
+ }
return 1;
}
@@ -89,13 +91,18 @@
/* Register vsyscall32 into the ABI table */
#include <linux/sysctl.h>
+static const int zero;
+static const int one = 1;
+
static struct ctl_table abi_table2[] = {
{
.procname = "vsyscall32",
.data = &vdso32_enabled,
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = (int *)&zero,
+ .extra2 = (int *)&one,
},
{}
};
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index a8a1a3d..039d4e1 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -1147,7 +1147,7 @@
/* NOTE: The loop is more greedy than the cleanup_highmap variant.
* We include the PMD passed in on _both_ boundaries. */
- for (; vaddr <= vaddr_end && (pmd < (level2_kernel_pgt + PAGE_SIZE));
+ for (; vaddr <= vaddr_end && (pmd < (level2_kernel_pgt + PTRS_PER_PMD));
pmd++, vaddr += PMD_SIZE) {
if (pmd_none(*pmd))
continue;
diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c
index f473d26..d4da95f 100644
--- a/arch/x86/xen/time.c
+++ b/arch/x86/xen/time.c
@@ -362,11 +362,11 @@
WARN_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT);
single.timeout_abs_ns = get_abs_timeout(delta);
- single.flags = VCPU_SSHOTTMR_future;
+ /* Get an event anyway, even if the timeout is already expired */
+ single.flags = 0;
ret = HYPERVISOR_vcpu_op(VCPUOP_set_singleshot_timer, cpu, &single);
-
- BUG_ON(ret != 0 && ret != -ETIME);
+ BUG_ON(ret != 0);
return ret;
}
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c
index 06370cc..b70097e 100644
--- a/arch/xtensa/kernel/setup.c
+++ b/arch/xtensa/kernel/setup.c
@@ -133,6 +133,8 @@
__tagtable(BP_TAG_INITRD, parse_tag_initrd);
+#endif /* CONFIG_BLK_DEV_INITRD */
+
#ifdef CONFIG_OF
static int __init parse_tag_fdt(const bp_tag_t *tag)
@@ -145,8 +147,6 @@
#endif /* CONFIG_OF */
-#endif /* CONFIG_BLK_DEV_INITRD */
-
static int __init parse_tag_cmdline(const bp_tag_t* tag)
{
strlcpy(command_line, (char *)(tag->data), COMMAND_LINE_SIZE);
diff --git a/block/bio.c b/block/bio.c
index 363ed1e..51439c6 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1826,8 +1826,9 @@
* Allocates and returns a new bio which represents @sectors from the start of
* @bio, and updates @bio to represent the remaining sectors.
*
- * The newly allocated bio will point to @bio's bi_io_vec; it is the caller's
- * responsibility to ensure that @bio is not freed before the split.
+ * Unless this is a discard request the newly allocated bio will point
+ * to @bio's bi_io_vec; it is the caller's responsibility to ensure that
+ * @bio is not freed before the split.
*/
struct bio *bio_split(struct bio *bio, int sectors,
gfp_t gfp, struct bio_set *bs)
@@ -1837,7 +1838,15 @@
BUG_ON(sectors <= 0);
BUG_ON(sectors >= bio_sectors(bio));
- split = bio_clone_fast(bio, gfp, bs);
+ /*
+ * Discards need a mutable bio_vec to accommodate the payload
+ * required by the DSM TRIM and UNMAP commands.
+ */
+ if (bio->bi_rw & REQ_DISCARD)
+ split = bio_clone_bioset(bio, gfp, bs);
+ else
+ split = bio_clone_fast(bio, gfp, bs);
+
if (!split)
return NULL;
diff --git a/block/bsg.c b/block/bsg.c
index 276e869..fc60769 100644
--- a/block/bsg.c
+++ b/block/bsg.c
@@ -677,6 +677,9 @@
dprintk("%s: write %Zd bytes\n", bd->name, count);
+ if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
+ return -EINVAL;
+
bsg_set_block(bd, file);
bytes_written = 0;
diff --git a/block/scsi_ioctl.c b/block/scsi_ioctl.c
index b0c2a61..4a2735d 100644
--- a/block/scsi_ioctl.c
+++ b/block/scsi_ioctl.c
@@ -182,6 +182,9 @@
__set_bit(WRITE_16, filter->write_ok);
__set_bit(WRITE_LONG, filter->write_ok);
__set_bit(WRITE_LONG_2, filter->write_ok);
+ __set_bit(WRITE_SAME, filter->write_ok);
+ __set_bit(WRITE_SAME_16, filter->write_ok);
+ __set_bit(WRITE_SAME_32, filter->write_ok);
__set_bit(ERASE, filter->write_ok);
__set_bit(GPCMD_MODE_SELECT_10, filter->write_ok);
__set_bit(MODE_SELECT, filter->write_ok);
diff --git a/build.config b/build.config
index fa53eb4c..a65b153 100644
--- a/build.config
+++ b/build.config
@@ -4,9 +4,12 @@
DEFCONFIG=marlin_defconfig
EXTRA_CMDS='python build/buildinfo/buildinfo.py'
KERNEL_DIR=private/msm-google
+POST_DEFCONFIG_CMDS="check_defconfig"
LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin
+LZ4_PREBUILTS_BIN=prebuilts-master/misc/linux-x86/lz4
FILES="
arch/arm64/boot/Image.gz-dtb
+arch/arm64/boot/Image.lz4-dtb
vmlinux
System.map
compile.json
diff --git a/build.config.kasan b/build.config.kasan
index 3337b91..cd17179 100644
--- a/build.config.kasan
+++ b/build.config.kasan
@@ -6,9 +6,11 @@
-e CONFIG_KCOV \
-e CONFIG_SLUB \
-e CONFIG_SLUB_DEBUG \
- -e CONFIG_SLUB_DEBUG_ON \
+ -d CONFIG_SLUB_DEBUG_ON \
+ -e CONFIG_CC_OPTIMIZE_FOR_SIZE \
-d CONFIG_SLUB_DEBUG_PANIC_ON \
- -d CONFIG_KASAN_OUTLINE
+ -d CONFIG_KASAN_OUTLINE \
+ -d CONFIG_KERNEL_LZ4
(cd ${OUT_DIR} && \
make O=${OUT_DIR} $archsubarch CROSS_COMPILE=${CROSS_COMPILE} olddefconfig)
}
diff --git a/crypto/Makefile b/crypto/Makefile
index 1445b91..ea11cf8 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -47,6 +47,7 @@
obj-$(CONFIG_CRYPTO_SHA256) += sha256_generic.o
obj-$(CONFIG_CRYPTO_SHA512) += sha512_generic.o
obj-$(CONFIG_CRYPTO_WP512) += wp512.o
+CFLAGS_wp512.o := $(call cc-option,-fno-schedule-insns) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79149
obj-$(CONFIG_CRYPTO_TGR192) += tgr192.o
obj-$(CONFIG_CRYPTO_GF128MUL) += gf128mul.o
obj-$(CONFIG_CRYPTO_ECB) += ecb.o
@@ -68,6 +69,7 @@
obj-$(CONFIG_CRYPTO_TWOFISH) += twofish_generic.o
obj-$(CONFIG_CRYPTO_TWOFISH_COMMON) += twofish_common.o
obj-$(CONFIG_CRYPTO_SERPENT) += serpent_generic.o
+CFLAGS_serpent_generic.o := $(call cc-option,-fsched-pressure) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79149
obj-$(CONFIG_CRYPTO_AES) += aes_generic.o
obj-$(CONFIG_CRYPTO_CAMELLIA) += camellia_generic.o
obj-$(CONFIG_CRYPTO_CAST_COMMON) += cast_common.o
diff --git a/crypto/ahash.c b/crypto/ahash.c
index 51d48cd..a11220e 100644
--- a/crypto/ahash.c
+++ b/crypto/ahash.c
@@ -31,6 +31,7 @@
crypto_completion_t complete;
void *data;
u8 *result;
+ u32 flags;
void *ubuf[] CRYPTO_MINALIGN_ATTR;
};
@@ -68,8 +69,9 @@
struct scatterlist *sg;
sg = walk->sg;
- walk->pg = sg_page(sg);
walk->offset = sg->offset;
+ walk->pg = sg_page(walk->sg) + (walk->offset >> PAGE_SHIFT);
+ walk->offset = offset_in_page(walk->offset);
walk->entrylen = sg->length;
if (walk->entrylen > walk->total)
@@ -268,6 +270,8 @@
priv->result = req->result;
priv->complete = req->base.complete;
priv->data = req->base.data;
+ priv->flags = req->base.flags;
+
/*
* WARNING: We do not backup req->priv here! The req->priv
* is for internal use of the Crypto API and the
@@ -282,38 +286,44 @@
return 0;
}
-static void ahash_restore_req(struct ahash_request *req)
+static void ahash_restore_req(struct ahash_request *req, int err)
{
struct ahash_request_priv *priv = req->priv;
+ if (!err)
+ memcpy(priv->result, req->result,
+ crypto_ahash_digestsize(crypto_ahash_reqtfm(req)));
+
/* Restore the original crypto request. */
req->result = priv->result;
- req->base.complete = priv->complete;
- req->base.data = priv->data;
+
+ ahash_request_set_callback(req, priv->flags,
+ priv->complete, priv->data);
req->priv = NULL;
/* Free the req->priv.priv from the ADJUSTED request. */
kzfree(priv);
}
-static void ahash_op_unaligned_finish(struct ahash_request *req, int err)
+static void ahash_notify_einprogress(struct ahash_request *req)
{
struct ahash_request_priv *priv = req->priv;
+ struct crypto_async_request oreq;
- if (err == -EINPROGRESS)
- return;
+ oreq.data = priv->data;
- if (!err)
- memcpy(priv->result, req->result,
- crypto_ahash_digestsize(crypto_ahash_reqtfm(req)));
-
- ahash_restore_req(req);
+ priv->complete(&oreq, -EINPROGRESS);
}
static void ahash_op_unaligned_done(struct crypto_async_request *req, int err)
{
struct ahash_request *areq = req->data;
+ if (err == -EINPROGRESS) {
+ ahash_notify_einprogress(areq);
+ return;
+ }
+
/*
* Restore the original request, see ahash_op_unaligned() for what
* goes where.
@@ -324,7 +334,7 @@
*/
/* First copy req->result into req->priv.result */
- ahash_op_unaligned_finish(areq, err);
+ ahash_restore_req(areq, err);
/* Complete the ORIGINAL request. */
areq->base.complete(&areq->base, err);
@@ -340,7 +350,12 @@
return err;
err = op(req);
- ahash_op_unaligned_finish(req, err);
+ if (err == -EINPROGRESS ||
+ (err == -EBUSY && (ahash_request_flags(req) &
+ CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return err;
+
+ ahash_restore_req(req, err);
return err;
}
@@ -375,25 +390,14 @@
}
EXPORT_SYMBOL_GPL(crypto_ahash_digest);
-static void ahash_def_finup_finish2(struct ahash_request *req, int err)
-{
- struct ahash_request_priv *priv = req->priv;
-
- if (err == -EINPROGRESS)
- return;
-
- if (!err)
- memcpy(priv->result, req->result,
- crypto_ahash_digestsize(crypto_ahash_reqtfm(req)));
-
- ahash_restore_req(req);
-}
-
static void ahash_def_finup_done2(struct crypto_async_request *req, int err)
{
struct ahash_request *areq = req->data;
- ahash_def_finup_finish2(areq, err);
+ if (err == -EINPROGRESS)
+ return;
+
+ ahash_restore_req(areq, err);
areq->base.complete(&areq->base, err);
}
@@ -404,11 +408,15 @@
goto out;
req->base.complete = ahash_def_finup_done2;
- req->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
+
err = crypto_ahash_reqtfm(req)->final(req);
+ if (err == -EINPROGRESS ||
+ (err == -EBUSY && (ahash_request_flags(req) &
+ CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return err;
out:
- ahash_def_finup_finish2(req, err);
+ ahash_restore_req(req, err);
return err;
}
@@ -416,7 +424,16 @@
{
struct ahash_request *areq = req->data;
+ if (err == -EINPROGRESS) {
+ ahash_notify_einprogress(areq);
+ return;
+ }
+
+ areq->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
+
err = ahash_def_finup_finish1(areq, err);
+ if (areq->priv)
+ return;
areq->base.complete(&areq->base, err);
}
@@ -431,6 +448,11 @@
return err;
err = tfm->update(req);
+ if (err == -EINPROGRESS ||
+ (err == -EBUSY && (ahash_request_flags(req) &
+ CRYPTO_TFM_REQ_MAY_BACKLOG)))
+ return err;
+
return ahash_def_finup_finish1(req, err);
}
diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c
index 434af81..9eb8174a 100644
--- a/crypto/algif_hash.c
+++ b/crypto/algif_hash.c
@@ -195,7 +195,7 @@
struct alg_sock *ask = alg_sk(sk);
struct hash_ctx *ctx = ask->private;
struct ahash_request *req = &ctx->req;
- char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req))];
+ char state[crypto_ahash_statesize(crypto_ahash_reqtfm(req)) ? : 1];
struct sock *sk2;
struct alg_sock *ask2;
struct hash_ctx *ctx2;
@@ -283,8 +283,8 @@
return err;
}
-static int hash_sendmsg_nokey(struct socket *sock, struct msghdr *msg,
- size_t size)
+static int hash_sendmsg_nokey(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t size)
{
int err;
@@ -307,8 +307,8 @@
return hash_sendpage(sock, page, offset, size, flags);
}
-static int hash_recvmsg_nokey(struct socket *sock, struct msghdr *msg,
- size_t ignored, int flags)
+static int hash_recvmsg_nokey(struct kiocb *unused, struct socket *sock,
+ struct msghdr *msg, size_t ignored, int flags)
{
int err;
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 4870f28..05bfe56 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -14,6 +14,7 @@
select MPILIB
select PUBLIC_KEY_ALGO_RSA
select CRYPTO_HASH_INFO
+ select CRYPTO_AKCIPHER
help
This option provides support for asymmetric public key type handling.
If signature generation and/or verification are to be used,
diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c
index 1d29376..841a04c 100644
--- a/crypto/asymmetric_keys/pkcs7_trust.c
+++ b/crypto/asymmetric_keys/pkcs7_trust.c
@@ -174,6 +174,8 @@
int cached_ret = -ENOKEY;
int ret;
+ *_trusted = false;
+
for (p = pkcs7->certs; p; p = p->next)
p->seen = false;
diff --git a/crypto/blkcipher.c b/crypto/blkcipher.c
index 0122bec..f25799f 100644
--- a/crypto/blkcipher.c
+++ b/crypto/blkcipher.c
@@ -233,6 +233,8 @@
return blkcipher_walk_done(desc, walk, -EINVAL);
}
+ bsize = min(walk->walk_blocksize, n);
+
walk->flags &= ~(BLKCIPHER_WALK_SLOW | BLKCIPHER_WALK_COPY |
BLKCIPHER_WALK_DIFF);
if (!scatterwalk_aligned(&walk->in, walk->alignmask) ||
@@ -245,7 +247,6 @@
}
}
- bsize = min(walk->walk_blocksize, n);
n = scatterwalk_clamp(&walk->in, n);
n = scatterwalk_clamp(&walk->out, n);
diff --git a/crypto/cryptd.c b/crypto/cryptd.c
index 650afac1..fb0d140 100644
--- a/crypto/cryptd.c
+++ b/crypto/cryptd.c
@@ -565,9 +565,14 @@
static int cryptd_hash_import(struct ahash_request *req, const void *in)
{
- struct cryptd_hash_request_ctx *rctx = ahash_request_ctx(req);
+ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+ struct cryptd_hash_ctx *ctx = crypto_ahash_ctx(tfm);
+ struct shash_desc *desc = cryptd_shash_desc(req);
- return crypto_shash_import(&rctx->desc, in);
+ desc->tfm = ctx->child;
+ desc->flags = req->base.flags;
+
+ return crypto_shash_import(desc, in);
}
static int cryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb,
@@ -601,6 +606,7 @@
inst->alg.halg.base.cra_flags = CRYPTO_ALG_ASYNC;
inst->alg.halg.digestsize = salg->digestsize;
+ inst->alg.halg.statesize = salg->statesize;
inst->alg.halg.base.cra_ctxsize = sizeof(struct cryptd_hash_ctx);
inst->alg.halg.base.cra_init = cryptd_hash_init_tfm;
diff --git a/crypto/gcm.c b/crypto/gcm.c
index 2e403f6..ee3c29b 100644
--- a/crypto/gcm.c
+++ b/crypto/gcm.c
@@ -716,7 +716,9 @@
ghash_alg = crypto_find_alg(ghash_name, &crypto_ahash_type,
CRYPTO_ALG_TYPE_HASH,
- CRYPTO_ALG_TYPE_AHASH_MASK);
+ CRYPTO_ALG_TYPE_AHASH_MASK |
+ crypto_requires_sync(algt->type,
+ algt->mask));
if (IS_ERR(ghash_alg))
return ERR_CAST(ghash_alg);
diff --git a/crypto/mcryptd.c b/crypto/mcryptd.c
index a8e87044..02a1c10 100644
--- a/crypto/mcryptd.c
+++ b/crypto/mcryptd.c
@@ -505,6 +505,7 @@
inst->alg.halg.base.cra_flags = CRYPTO_ALG_ASYNC;
inst->alg.halg.digestsize = salg->digestsize;
+ inst->alg.halg.statesize = salg->statesize;
inst->alg.halg.base.cra_ctxsize = sizeof(struct mcryptd_hash_ctx);
inst->alg.halg.base.cra_init = mcryptd_hash_init_tfm;
diff --git a/crypto/scatterwalk.c b/crypto/scatterwalk.c
index 79ca2278..0ec7a6f 100644
--- a/crypto/scatterwalk.c
+++ b/crypto/scatterwalk.c
@@ -68,7 +68,8 @@
void scatterwalk_done(struct scatter_walk *walk, int out, int more)
{
- if (!(scatterwalk_pagelen(walk) & (PAGE_SIZE - 1)) || !more)
+ if (!more || walk->offset >= walk->sg->offset + walk->sg->length ||
+ !(walk->offset & (PAGE_SIZE - 1)))
scatterwalk_pagedone(walk, out, more);
}
EXPORT_SYMBOL_GPL(scatterwalk_done);
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 6d11522..d21802b 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -2,7 +2,6 @@
# Makefile for the Linux ACPI interpreter
#
-ccflags-y := -Os
ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT
#
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index 6ba8beb..ce115ed 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -24,9 +24,11 @@
ACPI_MODULE_NAME("platform");
static const struct acpi_device_id forbidden_id_list[] = {
- {"PNP0000", 0}, /* PIC */
- {"PNP0100", 0}, /* Timer */
- {"PNP0200", 0}, /* AT DMA Controller */
+ {"PNP0000", 0}, /* PIC */
+ {"PNP0100", 0}, /* Timer */
+ {"PNP0200", 0}, /* AT DMA Controller */
+ {"ACPI0009", 0}, /* IOxAPIC */
+ {"ACPI000A", 0}, /* IOAPIC */
{"", 0},
};
diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c
index 3c7f737..86ddd0b 100644
--- a/drivers/acpi/acpica/dsmethod.c
+++ b/drivers/acpi/acpica/dsmethod.c
@@ -412,6 +412,9 @@
obj_desc->method.mutex->mutex.
original_sync_level =
obj_desc->method.mutex->mutex.sync_level;
+
+ obj_desc->method.mutex->mutex.thread_id =
+ acpi_os_get_thread_id();
}
}
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 8383598..d23c200 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -138,7 +138,7 @@
unsigned int enable:1;
unsigned int dmi:1;
unsigned int cmdline:1;
- unsigned int default_disabling:1;
+ u8 default_disabling;
} osi_linux = {0, 0, 0, 0};
static u32 acpi_osi_handler(acpi_string interface, u32 supported)
@@ -1443,10 +1443,13 @@
if (*str == '!') {
str++;
if (*str == '\0') {
- osi_linux.default_disabling = 1;
+ /* Do not override acpi_osi=!* */
+ if (!osi_linux.default_disabling)
+ osi_linux.default_disabling =
+ ACPI_DISABLE_ALL_VENDOR_STRINGS;
return;
} else if (*str == '*') {
- acpi_update_interfaces(ACPI_DISABLE_ALL_STRINGS);
+ osi_linux.default_disabling = ACPI_DISABLE_ALL_STRINGS;
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
osi = &osi_setup_entries[i];
osi->enable = false;
@@ -1519,10 +1522,13 @@
acpi_status status;
if (osi_linux.default_disabling) {
- status = acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS);
+ status = acpi_update_interfaces(osi_linux.default_disabling);
if (ACPI_SUCCESS(status))
- printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors\n");
+ printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors%s\n",
+ osi_linux.default_disabling ==
+ ACPI_DISABLE_ALL_STRINGS ?
+ " and feature groups" : "");
}
for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) {
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index e0bcfb6..39b18f74 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -201,6 +201,7 @@
return -EINVAL;
/* The state of the list is 'on' IFF all resources are 'on'. */
+ cur_state = 0;
list_for_each_entry(entry, list, node) {
struct acpi_power_resource *resource = entry->resource;
acpi_handle handle = resource->device.handle;
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 06f1d59..83c2b8b 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -43,6 +43,9 @@
if (rc)
return rc;
+ of_property_read_u32(dev->of_node,
+ "ports-implemented", &hpriv->force_port_map);
+
if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci"))
hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ;
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index a1d1c0e..794448c 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -467,6 +467,7 @@
dev_info(dev, "forcing port_map 0x%x -> 0x%x\n",
port_map, hpriv->force_port_map);
port_map = hpriv->force_port_map;
+ hpriv->saved_port_map = port_map;
}
if (hpriv->mask_port_map) {
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 4ec95b7..0550c76 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -605,7 +605,7 @@
ata_scsi_port_error_handler(host, ap);
/* finish or retry handled scmd's and clean up */
- WARN_ON(host->host_failed || !list_empty(&eh_work_q));
+ WARN_ON(!list_empty(&eh_work_q));
DPRINTK("EXIT\n");
}
diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c
index cbc3de7..0038dc4 100644
--- a/drivers/ata/pata_hpt366.c
+++ b/drivers/ata/pata_hpt366.c
@@ -352,7 +352,7 @@
};
const struct ata_port_info *ppi[] = { &info_hpt366, NULL };
- void *hpriv = NULL;
+ const void *hpriv = NULL;
u32 reg1;
int rc;
@@ -383,7 +383,7 @@
break;
}
/* Now kick off ATA set up */
- return ata_pci_bmdma_init_one(dev, ppi, &hpt36x_sht, hpriv, 0);
+ return ata_pci_bmdma_init_one(dev, ppi, &hpt36x_sht, (void *)hpriv, 0);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index 4217f29..816087b 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -1175,7 +1175,7 @@
if (!(skb = atm_alloc_charge(vcc, len, GFP_ATOMIC))) {
if (vcc->vci < 32)
printk("Drop control packets\n");
- goto out_free_desc;
+ goto out_free_desc;
}
skb_put(skb,len);
// pwang_test
diff --git a/drivers/base/module.c b/drivers/base/module.c
index db930d3..2a21578 100644
--- a/drivers/base/module.c
+++ b/drivers/base/module.c
@@ -24,10 +24,12 @@
static void module_create_drivers_dir(struct module_kobject *mk)
{
- if (!mk || mk->drivers_dir)
- return;
+ static DEFINE_MUTEX(drivers_dir_mutex);
- mk->drivers_dir = kobject_create_and_add("drivers", &mk->kobj);
+ mutex_lock(&drivers_dir_mutex);
+ if (mk && !mk->drivers_dir)
+ mk->drivers_dir = kobject_create_and_add("drivers", &mk->kobj);
+ mutex_unlock(&drivers_dir_mutex);
}
void module_add_driver(struct module *mod, struct device_driver *drv)
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 94c6772..b697dd1 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -1296,14 +1296,15 @@
error = device_suspend_late(dev);
mutex_lock(&dpm_list_mtx);
+ if (!list_empty(&dev->power.entry))
+ list_move(&dev->power.entry, &dpm_late_early_list);
+
if (error) {
pm_dev_err(dev, state, " late", error);
dpm_save_failed_dev(dev_name(dev));
put_device(dev);
break;
}
- if (!list_empty(&dev->power.entry))
- list_move(&dev->power.entry, &dpm_late_early_list);
put_device(dev);
if (async_error)
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 67c7938..f6f1f90 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -1474,11 +1474,16 @@
goto out;
}
- ret = callback(dev);
+ ret = pm_runtime_set_active(dev);
if (ret)
goto out;
- pm_runtime_set_active(dev);
+ ret = callback(dev);
+ if (ret) {
+ pm_runtime_set_suspended(dev);
+ goto out;
+ }
+
pm_runtime_mark_last_busy(dev);
out:
pm_runtime_enable(dev);
diff --git a/drivers/base/regmap/regmap-spmi.c b/drivers/base/regmap/regmap-spmi.c
index d7026dc..b394aae 100644
--- a/drivers/base/regmap/regmap-spmi.c
+++ b/drivers/base/regmap/regmap-spmi.c
@@ -153,7 +153,7 @@
while (val_size) {
len = min_t(size_t, val_size, 8);
- err = spmi_ext_register_readl(context, addr, val, val_size);
+ err = spmi_ext_register_readl(context, addr, val, len);
if (err)
goto err_out;
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index b6412b2..150def7 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -8,8 +8,6 @@
#include <linux/bcma/bcma.h>
#include <linux/delay.h>
-#define BCMA_CORE_SIZE 0x1000
-
#define bcma_err(bus, fmt, ...) \
pr_err("bus%d: " fmt, (bus)->num, ##__VA_ARGS__)
#define bcma_warn(bus, fmt, ...) \
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c
index 434c77d..227deb0 100644
--- a/drivers/block/drbd/drbd_bitmap.c
+++ b/drivers/block/drbd/drbd_bitmap.c
@@ -479,8 +479,14 @@
* this masks out the remaining bits.
* Returns the number of bits cleared.
*/
+#ifndef BITS_PER_PAGE
#define BITS_PER_PAGE (1UL << (PAGE_SHIFT + 3))
#define BITS_PER_PAGE_MASK (BITS_PER_PAGE - 1)
+#else
+# if BITS_PER_PAGE != (1UL << (PAGE_SHIFT + 3))
+# error "ambiguous BITS_PER_PAGE"
+# endif
+#endif
#define BITS_PER_LONG_MASK (BITS_PER_LONG - 1)
static int bm_clear_surplus(struct drbd_bitmap *b)
{
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 973c185..a31b282 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -1802,7 +1802,7 @@
* do we need to block DRBD_SIG if sock == &meta.socket ??
* otherwise wake_asender() might interrupt some send_*Ack !
*/
- rv = kernel_sendmsg(sock, &msg, &iov, 1, size);
+ rv = kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
if (rv == -EAGAIN) {
if (we_should_drop_the_connection(connection, sock))
break;
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index d7e0b9b..93e542c 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -2946,9 +2946,15 @@
* is in progress nor error handling is active
*/
wait_event_interruptible(port->svc_wait, (port->flags) &&
- !(port->flags & MTIP_PF_PAUSE_IO));
+ (port->flags & MTIP_PF_SVC_THD_WORK));
- set_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags);
+ if (kthread_should_stop() ||
+ test_bit(MTIP_PF_SVC_THD_STOP_BIT, &port->flags))
+ goto st_out;
+
+ if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
+ &dd->dd_flag)))
+ goto st_out;
if (kthread_should_stop() ||
test_bit(MTIP_PF_SVC_THD_STOP_BIT, &port->flags))
@@ -2962,6 +2968,8 @@
&dd->dd_flag)))
goto st_out;
+ set_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags);
+
restart_eh:
/* Demux bits: start with error handling */
if (test_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags)) {
@@ -3004,10 +3012,8 @@
}
if (test_bit(MTIP_PF_REBUILD_BIT, &port->flags)) {
- if (mtip_ftl_rebuild_poll(dd) < 0)
- set_bit(MTIP_DDF_REBUILD_FAILED_BIT,
- &dd->dd_flag);
- clear_bit(MTIP_PF_REBUILD_BIT, &port->flags);
+ if (mtip_ftl_rebuild_poll(dd) == 0)
+ clear_bit(MTIP_PF_REBUILD_BIT, &port->flags);
}
}
@@ -3886,7 +3892,6 @@
mtip_hw_debugfs_init(dd);
-skip_create_disk:
memset(&dd->tags, 0, sizeof(dd->tags));
dd->tags.ops = &mtip_mq_ops;
dd->tags.nr_hw_queues = 1;
@@ -3916,6 +3921,7 @@
dd->disk->queue = dd->queue;
dd->queue->queuedata = dd;
+skip_create_disk:
/* Initialize the protocol layer. */
wait_for_rebuild = mtip_hw_get_identify(dd);
if (wait_for_rebuild < 0) {
@@ -4077,7 +4083,8 @@
dd->bdev = NULL;
}
if (dd->disk) {
- del_gendisk(dd->disk);
+ if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag))
+ del_gendisk(dd->disk);
if (dd->disk->queue) {
blk_cleanup_queue(dd->queue);
blk_mq_free_tag_set(&dd->tags);
@@ -4118,7 +4125,8 @@
dev_info(&dd->pdev->dev,
"Shutting down %s ...\n", dd->disk->disk_name);
- del_gendisk(dd->disk);
+ if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag))
+ del_gendisk(dd->disk);
if (dd->disk->queue) {
blk_cleanup_queue(dd->queue);
blk_mq_free_tag_set(&dd->tags);
diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h
index 7669526..578ad36 100644
--- a/drivers/block/mtip32xx/mtip32xx.h
+++ b/drivers/block/mtip32xx/mtip32xx.h
@@ -145,6 +145,11 @@
MTIP_PF_SR_CLEANUP_BIT = 7,
MTIP_PF_SVC_THD_STOP_BIT = 8,
+ MTIP_PF_SVC_THD_WORK = ((1 << MTIP_PF_EH_ACTIVE_BIT) |
+ (1 << MTIP_PF_ISSUE_CMDS_BIT) |
+ (1 << MTIP_PF_REBUILD_BIT) |
+ (1 << MTIP_PF_SVC_THD_STOP_BIT)),
+
/* below are bit numbers in 'dd_flag' defined in driver_data */
MTIP_DDF_SEC_LOCK_BIT = 0,
MTIP_DDF_REMOVE_PENDING_BIT = 1,
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index a98c41f..1012329 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -578,8 +578,8 @@
BUG_ON(nbd->magic != NBD_MAGIC);
if (unlikely(!nbd->sock)) {
- dev_err(disk_to_dev(nbd->disk),
- "Attempted send on closed socket\n");
+ dev_err_ratelimited(disk_to_dev(nbd->disk),
+ "Attempted send on closed socket\n");
req->errors++;
nbd_end_request(req);
spin_lock_irq(q->queue_lock);
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
index d48715b28..b041470 100644
--- a/drivers/block/paride/pd.c
+++ b/drivers/block/paride/pd.c
@@ -126,7 +126,7 @@
*/
#include <linux/types.h>
-static bool verbose = 0;
+static int verbose = 0;
static int major = PD_MAJOR;
static char *name = PD_NAME;
static int cluster = 64;
@@ -161,7 +161,7 @@
static DEFINE_MUTEX(pd_mutex);
static DEFINE_SPINLOCK(pd_lock);
-module_param(verbose, bool, 0);
+module_param(verbose, int, 0);
module_param(major, int, 0);
module_param(name, charp, 0);
module_param(cluster, int, 0);
diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c
index 2ce3dfd..876d0c3 100644
--- a/drivers/block/paride/pg.c
+++ b/drivers/block/paride/pg.c
@@ -137,7 +137,7 @@
*/
-static bool verbose = 0;
+static int verbose;
static int major = PG_MAJOR;
static char *name = PG_NAME;
static int disable = 0;
@@ -168,7 +168,7 @@
#include <asm/uaccess.h>
-module_param(verbose, bool, 0644);
+module_param(verbose, int, 0644);
module_param(major, int, 0);
module_param(name, charp, 0);
module_param_array(drive0, int, NULL, 0);
diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c
index 2596042..ada4505 100644
--- a/drivers/block/paride/pt.c
+++ b/drivers/block/paride/pt.c
@@ -117,7 +117,7 @@
*/
-static bool verbose = 0;
+static int verbose = 0;
static int major = PT_MAJOR;
static char *name = PT_NAME;
static int disable = 0;
@@ -152,7 +152,7 @@
#include <asm/uaccess.h>
-module_param(verbose, bool, 0);
+module_param(verbose, int, 0);
module_param(major, int, 0);
module_param(name, charp, 0);
module_param_array(drive0, int, NULL, 0);
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index c546a93..2f73587 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -1929,7 +1929,7 @@
osdc = &rbd_dev->rbd_client->client->osdc;
osd_req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false,
- GFP_ATOMIC);
+ GFP_NOIO);
if (!osd_req)
return NULL; /* ENOMEM */
@@ -1978,7 +1978,7 @@
rbd_dev = img_request->rbd_dev;
osdc = &rbd_dev->rbd_client->client->osdc;
osd_req = ceph_osdc_alloc_request(osdc, snapc, num_osd_ops,
- false, GFP_ATOMIC);
+ false, GFP_NOIO);
if (!osd_req)
return NULL; /* ENOMEM */
@@ -2470,7 +2470,7 @@
bio_chain_clone_range(&bio_list,
&bio_offset,
clone_size,
- GFP_ATOMIC);
+ GFP_NOIO);
if (!obj_request->bio_list)
goto out_unwind;
} else if (type == OBJ_REQUEST_PAGES) {
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 74fd256..ae3908e 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -541,13 +541,13 @@
if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) {
bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
- clear_page(mem);
+ memset(mem, 0, PAGE_SIZE);
return 0;
}
cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO);
if (size == PAGE_SIZE)
- copy_page(mem, cmem);
+ memcpy(mem, cmem, PAGE_SIZE);
else
ret = zcomp_decompress(zram->comp, cmem, size, mem);
zs_unmap_object(meta->mem_pool, handle);
@@ -726,7 +726,7 @@
if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) {
src = kmap_atomic(page);
- copy_page(cmem, src);
+ memcpy(cmem, src, PAGE_SIZE);
kunmap_atomic(src);
} else {
memcpy(cmem, src, clen);
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 620e416..19044d7 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -146,6 +146,7 @@
{ USB_DEVICE(0x04CA, 0x300f) },
{ USB_DEVICE(0x04CA, 0x3010) },
{ USB_DEVICE(0x04CA, 0x3014) },
+ { USB_DEVICE(0x04CA, 0x3018) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0930, 0x0220) },
{ USB_DEVICE(0x0930, 0x0227) },
@@ -173,6 +174,8 @@
{ USB_DEVICE(0x13d3, 0x3432) },
{ USB_DEVICE(0x13d3, 0x3472) },
{ USB_DEVICE(0x13d3, 0x3474) },
+ { USB_DEVICE(0x13d3, 0x3487) },
+ { USB_DEVICE(0x13d3, 0x3490) },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE02C) },
@@ -211,6 +214,7 @@
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
@@ -237,6 +241,8 @@
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3472), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3487), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3490), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 205335d..b5ae8e7 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -189,6 +189,7 @@
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x04ca, 0x3018), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
@@ -216,6 +217,8 @@
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3472), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3487), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x13d3, 0x3490), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 6653473..eaa646d 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -50,6 +50,7 @@
wait_queue_head_t read_wait;
struct sk_buff_head readq;
+ struct mutex open_mutex;
struct delayed_work open_timeout;
};
@@ -95,12 +96,15 @@
return 0;
}
-static int vhci_create_device(struct vhci_data *data, __u8 opcode)
+static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
{
struct hci_dev *hdev;
struct sk_buff *skb;
__u8 dev_type;
+ if (data->hdev)
+ return -EBADFD;
+
/* bits 0-1 are dev_type (BR/EDR or AMP) */
dev_type = opcode & 0x03;
@@ -159,6 +163,17 @@
return 0;
}
+static int vhci_create_device(struct vhci_data *data, __u8 opcode)
+{
+ int err;
+
+ mutex_lock(&data->open_mutex);
+ err = __vhci_create_device(data, opcode);
+ mutex_unlock(&data->open_mutex);
+
+ return err;
+}
+
static inline ssize_t vhci_get_user(struct vhci_data *data,
struct iov_iter *from)
{
@@ -197,11 +212,6 @@
break;
case HCI_VENDOR_PKT:
- if (data->hdev) {
- kfree_skb(skb);
- return -EBADFD;
- }
-
cancel_delayed_work_sync(&data->open_timeout);
opcode = *((__u8 *) skb->data);
@@ -328,6 +338,7 @@
skb_queue_head_init(&data->readq);
init_waitqueue_head(&data->read_wait);
+ mutex_init(&data->open_mutex);
INIT_DELAYED_WORK(&data->open_timeout, vhci_open_timeout);
file->private_data = data;
@@ -341,15 +352,18 @@
static int vhci_release(struct inode *inode, struct file *file)
{
struct vhci_data *data = file->private_data;
- struct hci_dev *hdev = data->hdev;
+ struct hci_dev *hdev;
cancel_delayed_work_sync(&data->open_timeout);
+ hdev = data->hdev;
+
if (hdev) {
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
+ skb_queue_purge(&data->readq);
file->private_data = NULL;
kfree(data);
diff --git a/drivers/bus/arm-ccn.c b/drivers/bus/arm-ccn.c
index 60397ec..4d523cf 100644
--- a/drivers/bus/arm-ccn.c
+++ b/drivers/bus/arm-ccn.c
@@ -183,6 +183,7 @@
struct arm_ccn_component *xp;
struct arm_ccn_dt dt;
+ int mn_id;
};
@@ -322,6 +323,7 @@
static ssize_t arm_ccn_pmu_event_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct arm_ccn *ccn = pmu_to_arm_ccn(dev_get_drvdata(dev));
struct arm_ccn_pmu_event *event = container_of(attr,
struct arm_ccn_pmu_event, attr);
ssize_t res;
@@ -336,6 +338,26 @@
if (event->mask)
res += snprintf(buf + res, PAGE_SIZE - res, ",mask=0x%x",
event->mask);
+
+ /* Arguments required by an event */
+ switch (event->type) {
+ case CCN_TYPE_CYCLES:
+ break;
+ case CCN_TYPE_XP:
+ res += snprintf(buf + res, PAGE_SIZE - res,
+ ",xp=?,port=?,vc=?,dir=?");
+ if (event->event == CCN_EVENT_WATCHPOINT)
+ res += snprintf(buf + res, PAGE_SIZE - res,
+ ",cmp_l=?,cmp_h=?,mask=?");
+ break;
+ case CCN_TYPE_MN:
+ res += snprintf(buf + res, PAGE_SIZE - res, ",node=%d", ccn->mn_id);
+ break;
+ default:
+ res += snprintf(buf + res, PAGE_SIZE - res, ",node=?");
+ break;
+ }
+
res += snprintf(buf + res, PAGE_SIZE - res, "\n");
return res;
@@ -360,9 +382,9 @@
}
static struct arm_ccn_pmu_event arm_ccn_pmu_events[] = {
- CCN_EVENT_MN(eobarrier, "dir=0,vc=0,cmp_h=0x1c00", CCN_IDX_MASK_OPCODE),
- CCN_EVENT_MN(ecbarrier, "dir=0,vc=0,cmp_h=0x1e00", CCN_IDX_MASK_OPCODE),
- CCN_EVENT_MN(dvmop, "dir=0,vc=0,cmp_h=0x2800", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(eobarrier, "dir=1,vc=0,cmp_h=0x1c00", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(ecbarrier, "dir=1,vc=0,cmp_h=0x1e00", CCN_IDX_MASK_OPCODE),
+ CCN_EVENT_MN(dvmop, "dir=1,vc=0,cmp_h=0x2800", CCN_IDX_MASK_OPCODE),
CCN_EVENT_HNI(txdatflits, "dir=1,vc=3", CCN_IDX_MASK_ANY),
CCN_EVENT_HNI(rxdatflits, "dir=0,vc=3", CCN_IDX_MASK_ANY),
CCN_EVENT_HNI(txreqflits, "dir=1,vc=0", CCN_IDX_MASK_ANY),
@@ -649,6 +671,12 @@
/* Validate node/xp vs topology */
switch (type) {
+ case CCN_TYPE_MN:
+ if (node_xp != ccn->mn_id) {
+ dev_warn(ccn->dev, "Invalid MN ID %d!\n", node_xp);
+ return -EINVAL;
+ }
+ break;
case CCN_TYPE_XP:
if (node_xp >= ccn->num_xps) {
dev_warn(ccn->dev, "Invalid XP ID %d!\n", node_xp);
@@ -804,6 +832,10 @@
struct arm_ccn_component *xp;
u32 val, dt_cfg;
+ /* Nothing to do for cycle counter */
+ if (hw->idx == CCN_IDX_PMU_CYCLE_COUNTER)
+ return;
+
if (CCN_CONFIG_TYPE(event->attr.config) == CCN_TYPE_XP)
xp = &ccn->xp[CCN_CONFIG_XP(event->attr.config)];
else
@@ -901,7 +933,7 @@
/* Comparison values */
writel(cmp_l & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_L(wp));
- writel((cmp_l >> 32) & 0xefffffff,
+ writel((cmp_l >> 32) & 0x7fffffff,
source->base + CCN_XP_DT_CMP_VAL_L(wp) + 4);
writel(cmp_h & 0xffffffff, source->base + CCN_XP_DT_CMP_VAL_H(wp));
writel((cmp_h >> 32) & 0x0fffffff,
@@ -909,7 +941,7 @@
/* Mask */
writel(mask_l & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_L(wp));
- writel((mask_l >> 32) & 0xefffffff,
+ writel((mask_l >> 32) & 0x7fffffff,
source->base + CCN_XP_DT_CMP_MASK_L(wp) + 4);
writel(mask_h & 0xffffffff, source->base + CCN_XP_DT_CMP_MASK_H(wp));
writel((mask_h >> 32) & 0x0fffffff,
@@ -1210,6 +1242,8 @@
switch (type) {
case CCN_TYPE_MN:
+ ccn->mn_id = id;
+ return 0;
case CCN_TYPE_DT:
return 0;
case CCN_TYPE_XP:
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e8fc493..a2fc1e7 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -590,12 +590,11 @@
config DEVPORT
bool "/dev/port character device"
- depends on !M68K
depends on ISA || PCI
default y
help
- Say Y here if you want to support the /dev/port device. The
- /dev/port device is similar to /dev/mem, but for I/O ports.
+ Say Y here if you want to support the /dev/port device. The /dev/port
+ device is similar to /dev/mem, but for I/O ports.
config DCC_TTY
tristate "DCC tty driver"
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 13a3afa..b7c5e7d 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -1359,7 +1359,7 @@
void diag_dci_notify_client(int peripheral_mask, int data, int proc)
{
- int stat;
+ int stat = 0;
struct siginfo info;
struct list_head *start, *temp;
struct diag_dci_client_tbl *entry = NULL;
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 37e17ca..d193a28 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -60,6 +60,10 @@
#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
#ifdef CONFIG_STRICT_DEVMEM
+static inline int page_is_allowed(unsigned long pfn)
+{
+ return devmem_is_allowed(pfn);
+}
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
u64 from = ((u64)pfn) << PAGE_SHIFT;
@@ -79,6 +83,10 @@
return 1;
}
#else
+static inline int page_is_allowed(unsigned long pfn)
+{
+ return 1;
+}
static inline int range_is_allowed(unsigned long pfn, unsigned long size)
{
return 1;
@@ -125,23 +133,31 @@
while (count > 0) {
unsigned long remaining;
+ int allowed;
sz = size_inside_page(p, count);
- if (!range_is_allowed(p >> PAGE_SHIFT, count))
+ allowed = page_is_allowed(p >> PAGE_SHIFT);
+ if (!allowed)
return -EPERM;
+ if (allowed == 2) {
+ /* Show zeros for restricted memory. */
+ remaining = clear_user(buf, sz);
+ } else {
+ /*
+ * On ia64 if a page has been mapped somewhere as
+ * uncached, then it must also be accessed uncached
+ * by the kernel or data corruption may occur.
+ */
+ ptr = xlate_dev_mem_ptr(p);
+ if (!ptr)
+ return -EFAULT;
- /*
- * On ia64 if a page has been mapped somewhere as uncached, then
- * it must also be accessed uncached by the kernel or data
- * corruption may occur.
- */
- ptr = xlate_dev_mem_ptr(p);
- if (!ptr)
- return -EFAULT;
+ remaining = copy_to_user(buf, ptr, sz);
- remaining = copy_to_user(buf, ptr, sz);
- unxlate_dev_mem_ptr(p, ptr);
+ unxlate_dev_mem_ptr(p, ptr);
+ }
+
if (remaining)
return -EFAULT;
@@ -184,30 +200,36 @@
#endif
while (count > 0) {
+ int allowed;
+
sz = size_inside_page(p, count);
- if (!range_is_allowed(p >> PAGE_SHIFT, sz))
+ allowed = page_is_allowed(p >> PAGE_SHIFT);
+ if (!allowed)
return -EPERM;
- /*
- * On ia64 if a page has been mapped somewhere as uncached, then
- * it must also be accessed uncached by the kernel or data
- * corruption may occur.
- */
- ptr = xlate_dev_mem_ptr(p);
- if (!ptr) {
- if (written)
- break;
- return -EFAULT;
- }
+ /* Skip actual writing when a page is marked as restricted. */
+ if (allowed == 1) {
+ /*
+ * On ia64 if a page has been mapped somewhere as
+ * uncached, then it must also be accessed uncached
+ * by the kernel or data corruption may occur.
+ */
+ ptr = xlate_dev_mem_ptr(p);
+ if (!ptr) {
+ if (written)
+ break;
+ return -EFAULT;
+ }
- copied = copy_from_user(ptr, buf, sz);
- unxlate_dev_mem_ptr(p, ptr);
- if (copied) {
- written += sz - copied;
- if (written)
- break;
- return -EFAULT;
+ copied = copy_from_user(ptr, buf, sz);
+ unxlate_dev_mem_ptr(p, ptr);
+ if (copied) {
+ written += sz - copied;
+ if (written)
+ break;
+ return -EFAULT;
+ }
}
buf += sz;
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index 6e09c1d..5480deb 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1129,6 +1129,8 @@
{
struct port *port;
struct scatterlist sg[1];
+ void *data;
+ int ret;
if (unlikely(early_put_chars))
return early_put_chars(vtermno, buf, count);
@@ -1137,8 +1139,14 @@
if (!port)
return -EPIPE;
- sg_init_one(sg, buf, count);
- return __send_to_port(port, sg, 1, count, (void *)buf, false);
+ data = kmemdup(buf, count, GFP_ATOMIC);
+ if (!data)
+ return -ENOMEM;
+
+ sg_init_one(sg, data, count);
+ ret = __send_to_port(port, sg, 1, count, data, false);
+ kfree(data);
+ return ret;
}
/*
@@ -1532,19 +1540,29 @@
spin_lock_irq(&port->inbuf_lock);
/* Remove unused data this port might have received. */
discard_port_data(port);
+ spin_unlock_irq(&port->inbuf_lock);
/* Remove buffers we queued up for the Host to send us data in. */
- while ((buf = virtqueue_detach_unused_buf(port->in_vq)))
- free_buf(buf, true);
- spin_unlock_irq(&port->inbuf_lock);
+ do {
+ spin_lock_irq(&port->inbuf_lock);
+ buf = virtqueue_detach_unused_buf(port->in_vq);
+ spin_unlock_irq(&port->inbuf_lock);
+ if (buf)
+ free_buf(buf, true);
+ } while (buf);
spin_lock_irq(&port->outvq_lock);
reclaim_consumed_buffers(port);
+ spin_unlock_irq(&port->outvq_lock);
/* Free pending buffers from the out-queue. */
- while ((buf = virtqueue_detach_unused_buf(port->out_vq)))
- free_buf(buf, true);
- spin_unlock_irq(&port->outvq_lock);
+ do {
+ spin_lock_irq(&port->outvq_lock);
+ buf = virtqueue_detach_unused_buf(port->out_vq);
+ spin_unlock_irq(&port->outvq_lock);
+ if (buf)
+ free_buf(buf, true);
+ } while (buf);
}
/*
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
index 0283a57..930a424 100644
--- a/drivers/clk/at91/clk-usb.c
+++ b/drivers/clk/at91/clk-usb.c
@@ -59,7 +59,7 @@
static long at91sam9x5_clk_usb_determine_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *best_parent_rate,
- struct clk_hw **best_parent_hw)
+ struct clk **best_parent_hw)
{
struct clk *parent = NULL;
long best_rate = -EINVAL;
@@ -91,7 +91,7 @@
best_rate = tmp_rate;
best_diff = tmp_diff;
*best_parent_rate = tmp_parent_rate;
- *best_parent_hw = __clk_get_hw(parent);
+ *best_parent_hw = parent;
}
if (!best_diff || tmp_rate < rate)
diff --git a/drivers/clk/msm/clock-alpha-pll.c b/drivers/clk/msm/clock-alpha-pll.c
index f7ba711..1a360b2 100644
--- a/drivers/clk/msm/clock-alpha-pll.c
+++ b/drivers/clk/msm/clock-alpha-pll.c
@@ -594,7 +594,7 @@
{
struct alpha_pll_clk *pll = to_alpha_pll_clk(c);
struct alpha_pll_masks *masks = pll->masks;
- unsigned long flags, freq_hz;
+ unsigned long flags = 0, freq_hz;
u32 regval, l_val;
int vco_val;
u64 a_val;
diff --git a/drivers/clk/msm/clock-cpu-8996.c b/drivers/clk/msm/clock-cpu-8996.c
index 3b865b1..b2f63e6 100644
--- a/drivers/clk/msm/clock-cpu-8996.c
+++ b/drivers/clk/msm/clock-cpu-8996.c
@@ -676,7 +676,7 @@
{
struct cpu_clk_8996 *cpuclk = to_cpu_clk_8996(c);
int ret, err_ret;
- unsigned long alt_pll_prev_rate;
+ unsigned long alt_pll_prev_rate = 0;
unsigned long alt_pll_rate;
unsigned long n_alt_freqs = cpuclk->n_alt_pll_freqs;
bool on_acd_leg = rate > MAX_PLL_MAIN_FREQ;
diff --git a/drivers/clk/msm/mdss/mdss-dsi-pll-util.c b/drivers/clk/msm/mdss/mdss-dsi-pll-util.c
index 6920295..6ea964b 100644
--- a/drivers/clk/msm/mdss/mdss-dsi-pll-util.c
+++ b/drivers/clk/msm/mdss/mdss-dsi-pll-util.c
@@ -446,7 +446,7 @@
int vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate)
{
- struct mdss_dsi_vco_calc vco_calc;
+ struct mdss_dsi_vco_calc vco_calc = { 0 };
struct mdss_pll_resources *dsi_pll_res = vco->priv;
int rc = 0;
diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c
index 007534f..9004a4e 100644
--- a/drivers/clk/qcom/gcc-msm8960.c
+++ b/drivers/clk/qcom/gcc-msm8960.c
@@ -2740,7 +2740,7 @@
},
.freq_tbl = clk_tbl_ce3,
.clkr = {
- .enable_reg = 0x2c08,
+ .enable_reg = 0x36c0,
.enable_mask = BIT(7),
.hw.init = &(struct clk_init_data){
.name = "ce3_src",
@@ -2756,7 +2756,7 @@
.halt_reg = 0x2fdc,
.halt_bit = 5,
.clkr = {
- .enable_reg = 0x36c4,
+ .enable_reg = 0x36cc,
.enable_mask = BIT(4),
.hw.init = &(struct clk_init_data){
.name = "ce3_core_clk",
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 880a266..a25261a 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -70,7 +70,7 @@
if (gate_offset >= 0) {
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
- return ERR_PTR(-ENOMEM);
+ goto err_gate;
gate->flags = gate_flags;
gate->reg = base + gate_offset;
@@ -82,7 +82,7 @@
if (div_width > 0) {
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
- return ERR_PTR(-ENOMEM);
+ goto err_div;
div->flags = div_flags;
div->reg = base + muxdiv_offset;
@@ -100,6 +100,11 @@
flags);
return clk;
+err_div:
+ kfree(gate);
+err_gate:
+ kfree(mux);
+ return ERR_PTR(-ENOMEM);
}
static struct clk *rockchip_clk_register_frac_branch(const char *name,
diff --git a/drivers/clk/versatile/clk-sp810.c b/drivers/clk/versatile/clk-sp810.c
index 5122ef2..e63c3ef 100644
--- a/drivers/clk/versatile/clk-sp810.c
+++ b/drivers/clk/versatile/clk-sp810.c
@@ -141,6 +141,7 @@
const char *parent_names[2];
char name[12];
struct clk_init_data init;
+ static int instance;
int i;
if (!sp810) {
@@ -172,7 +173,7 @@
init.num_parents = ARRAY_SIZE(parent_names);
for (i = 0; i < ARRAY_SIZE(sp810->timerclken); i++) {
- snprintf(name, ARRAY_SIZE(name), "timerclken%d", i);
+ snprintf(name, sizeof(name), "sp810_%d_%d", instance, i);
sp810->timerclken[i].sp810 = sp810;
sp810->timerclken[i].channel = i;
@@ -184,5 +185,6 @@
}
of_clk_add_provider(node, clk_sp810_timerclken_of_get, sp810);
+ instance++;
}
CLK_OF_DECLARE(sp810, "arm,sp810", clk_sp810_of_setup);
diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
index f4a9c00..501c959 100644
--- a/drivers/clocksource/sun4i_timer.c
+++ b/drivers/clocksource/sun4i_timer.c
@@ -120,12 +120,16 @@
.set_next_event = sun4i_clkevt_next_event,
};
+static void sun4i_timer_clear_interrupt(void)
+{
+ writel(TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_ST_REG);
+}
static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = (struct clock_event_device *)dev_id;
- writel(0x1, timer_base + TIMER_IRQ_ST_REG);
+ sun4i_timer_clear_interrupt();
evt->event_handler(evt);
return IRQ_HANDLED;
@@ -182,6 +186,9 @@
/* Make sure timer is stopped before playing with interrupts */
sun4i_clkevt_time_stop(0);
+ /* clear timer0 interrupt */
+ sun4i_timer_clear_interrupt();
+
sun4i_clockevent.cpumask = cpu_possible_mask;
sun4i_clockevent.irq = irq;
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 03c5758..b5eda23 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -2195,6 +2195,8 @@
else if (event == CPUFREQ_GOV_POLICY_EXIT)
policy->governor->initialized--;
} else {
+ if (event == CPUFREQ_GOV_POLICY_EXIT && ret == -EBUSY)
+ return ret;
/* Restore original values */
mutex_lock(&cpufreq_governor_lock);
if (event == CPUFREQ_GOV_STOP)
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index bd302b9..6a5bf2b 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -445,7 +445,7 @@
struct cpufreq_interactive_tunables *tunables =
ppol->policy->governor_data;
struct cpufreq_interactive_cpuinfo *pcpu;
- unsigned int new_freq;
+ unsigned int new_freq = 0;
unsigned int loadadjfreq = 0, tmploadadjfreq;
unsigned int index;
unsigned long flags;
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index 25dee6f..35c0d0d 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -9,24 +9,46 @@
* published by the Free Software Foundation.
*/
+#include <linux/atomic.h>
#include <linux/cpu.h>
#include <linux/cpufreq.h>
+#include <linux/cputime.h>
+#include <linux/hashtable.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/proc_fs.h>
+#include <linux/profile.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/sort.h>
-#include <linux/of.h>
-#include <linux/sched.h>
-#include <linux/cputime.h>
+
+#define UID_HASH_BITS 10
+
+DECLARE_HASHTABLE(uid_hash_table, UID_HASH_BITS);
static spinlock_t cpufreq_stats_lock;
+static DEFINE_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */
+static DEFINE_RT_MUTEX(uid_lock); /* uid_hash_table */
+
+struct uid_entry {
+ uid_t uid;
+ unsigned int dead_max_states;
+ unsigned int alive_max_states;
+ u64 *dead_time_in_state;
+ u64 *alive_time_in_state;
+ struct hlist_node hash;
+};
+
struct cpufreq_stats {
unsigned int cpu;
unsigned int total_trans;
unsigned long long last_time;
unsigned int max_state;
unsigned int state_num;
- unsigned int last_index;
+ atomic_t cpu_freq_i;
+ atomic_t all_freq_i;
u64 *time_in_state;
unsigned int *freq_table;
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
@@ -52,6 +74,7 @@
};
static struct all_freq_table *all_freq_table;
+static bool cpufreq_all_freq_init;
static DEFINE_PER_CPU(struct all_cpufreq_stats *, all_cpufreq_stats);
static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table);
@@ -62,6 +85,125 @@
ssize_t(*show) (struct cpufreq_stats *, char *);
};
+/* Caller must hold uid lock */
+static struct uid_entry *find_uid_entry(uid_t uid)
+{
+ struct uid_entry *uid_entry;
+
+ hash_for_each_possible(uid_hash_table, uid_entry, hash, uid) {
+ if (uid_entry->uid == uid)
+ return uid_entry;
+ }
+ return NULL;
+}
+
+/* Caller must hold uid lock */
+static struct uid_entry *find_or_register_uid(uid_t uid)
+{
+ struct uid_entry *uid_entry;
+
+ uid_entry = find_uid_entry(uid);
+ if (uid_entry)
+ return uid_entry;
+
+ uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
+ if (!uid_entry)
+ return NULL;
+
+ uid_entry->uid = uid;
+
+ hash_add(uid_hash_table, &uid_entry->hash, uid);
+
+ return uid_entry;
+}
+
+
+static int uid_time_in_state_show(struct seq_file *m, void *v)
+{
+ struct uid_entry *uid_entry;
+ struct task_struct *task, *temp;
+ unsigned long bkt, flags;
+ int i;
+
+ if (!all_freq_table || !cpufreq_all_freq_init)
+ return 0;
+
+ seq_puts(m, "uid:");
+ for (i = 0; i < all_freq_table->table_size; ++i)
+ seq_printf(m, " %d", all_freq_table->freq_table[i]);
+ seq_putc(m, '\n');
+
+ rt_mutex_lock(&uid_lock);
+
+ rcu_read_lock();
+ do_each_thread(temp, task) {
+
+ uid_entry = find_or_register_uid(from_kuid_munged(
+ current_user_ns(), task_uid(task)));
+ if (!uid_entry)
+ continue;
+
+ if (uid_entry->alive_max_states < task->max_states) {
+ uid_entry->alive_time_in_state = krealloc(
+ uid_entry->alive_time_in_state,
+ task->max_states *
+ sizeof(uid_entry->alive_time_in_state[0]),
+ GFP_ATOMIC);
+ memset(uid_entry->alive_time_in_state +
+ uid_entry->alive_max_states,
+ 0, (task->max_states -
+ uid_entry->alive_max_states) *
+ sizeof(uid_entry->alive_time_in_state[0]));
+ uid_entry->alive_max_states = task->max_states;
+ }
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ if (task->time_in_state) {
+ for (i = 0; i < task->max_states; ++i) {
+ uid_entry->alive_time_in_state[i] +=
+ atomic_read(&task->time_in_state[i]);
+ }
+ }
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+
+ } while_each_thread(temp, task);
+ rcu_read_unlock();
+
+ hash_for_each(uid_hash_table, bkt, uid_entry, hash) {
+ int max_states = uid_entry->dead_max_states;
+
+ if (uid_entry->alive_max_states > max_states)
+ max_states = uid_entry->alive_max_states;
+ if (max_states)
+ seq_printf(m, "%d:", uid_entry->uid);
+ for (i = 0; i < max_states; ++i) {
+ u64 total_time_in_state = 0;
+
+ if (uid_entry->dead_time_in_state &&
+ i < uid_entry->dead_max_states) {
+ total_time_in_state =
+ uid_entry->dead_time_in_state[i];
+ }
+ if (uid_entry->alive_time_in_state &&
+ i < uid_entry->alive_max_states) {
+ total_time_in_state +=
+ uid_entry->alive_time_in_state[i];
+ }
+ seq_printf(m, " %lu", (unsigned long)
+ cputime_to_clock_t(total_time_in_state));
+ }
+ if (max_states)
+ seq_putc(m, '\n');
+
+ kfree(uid_entry->alive_time_in_state);
+ uid_entry->alive_time_in_state = NULL;
+ uid_entry->alive_max_states = 0;
+ }
+
+ rt_mutex_unlock(&uid_lock);
+ return 0;
+}
+
static int cpufreq_stats_update(unsigned int cpu)
{
struct cpufreq_stats *stat;
@@ -77,17 +219,83 @@
return 0;
}
if (stat->time_in_state) {
- stat->time_in_state[stat->last_index] +=
- cur_time - stat->last_time;
+ int cpu_freq_i = atomic_read(&stat->cpu_freq_i);
+
+ stat->time_in_state[cpu_freq_i] += cur_time - stat->last_time;
if (all_stat)
- all_stat->time_in_state[stat->last_index] +=
- cur_time - stat->last_time;
+ all_stat->time_in_state[cpu_freq_i] +=
+ cur_time - stat->last_time;
}
stat->last_time = cur_time;
spin_unlock(&cpufreq_stats_lock);
return 0;
}
+void cpufreq_task_stats_init(struct task_struct *p)
+{
+ size_t alloc_size;
+ void *temp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ p->time_in_state = NULL;
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+ WRITE_ONCE(p->max_states, 0);
+
+ if (!all_freq_table || !cpufreq_all_freq_init)
+ return;
+
+ WRITE_ONCE(p->max_states, all_freq_table->table_size);
+
+ /* Create all_freq_table for clockticks in all possible freqs in all
+ * cpus
+ */
+ alloc_size = p->max_states * sizeof(p->time_in_state[0]);
+ temp = kzalloc(alloc_size, GFP_KERNEL);
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ p->time_in_state = temp;
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+}
+
+void cpufreq_task_stats_exit(struct task_struct *p)
+{
+ unsigned long flags;
+ void *temp;
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ temp = p->time_in_state;
+ p->time_in_state = NULL;
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+ kfree(temp);
+}
+
+int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns,
+ struct pid *pid, struct task_struct *p)
+{
+ int i;
+ cputime_t cputime;
+ unsigned long flags;
+
+ if (!all_freq_table || !cpufreq_all_freq_init || !p->time_in_state)
+ return 0;
+
+ spin_lock(&cpufreq_stats_lock);
+ for (i = 0; i < p->max_states; ++i) {
+ cputime = 0;
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ if (p->time_in_state)
+ cputime = atomic_read(&p->time_in_state[i]);
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+
+ seq_printf(m, "%d %lu\n", all_freq_table->freq_table[i],
+ (unsigned long)cputime_to_clock_t(cputime));
+ }
+ spin_unlock(&cpufreq_stats_lock);
+
+ return 0;
+}
+
static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf)
{
struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
@@ -126,20 +334,49 @@
return -1;
}
+/* Called without cpufreq_stats_lock held */
void acct_update_power(struct task_struct *task, cputime_t cputime) {
struct cpufreq_power_stats *powerstats;
struct cpufreq_stats *stats;
unsigned int cpu_num, curr;
+ int cpu_freq_i;
+ int all_freq_i;
+ unsigned long flags;
if (!task)
return;
+
cpu_num = task_cpu(task);
- powerstats = per_cpu(cpufreq_power_stats, cpu_num);
stats = per_cpu(cpufreq_stats_table, cpu_num);
- if (!powerstats || !stats)
+ if (!stats)
return;
- curr = powerstats->curr[stats->last_index];
+ all_freq_i = atomic_read(&stats->all_freq_i);
+
+ /* This function is called from a different context
+ * Interruptions in between reads/assignements are ok
+ */
+ if (all_freq_table && cpufreq_all_freq_init &&
+ !(task->flags & PF_EXITING) &&
+ all_freq_i != -1 && all_freq_i < READ_ONCE(task->max_states)) {
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ if (task->time_in_state) {
+ atomic64_add(cputime,
+ &task->time_in_state[all_freq_i]);
+ }
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+ }
+
+ powerstats = per_cpu(cpufreq_power_stats, cpu_num);
+ if (!powerstats)
+ return;
+
+ cpu_freq_i = atomic_read(&stats->cpu_freq_i);
+ if (cpu_freq_i == -1)
+ return;
+
+ curr = powerstats->curr[cpu_freq_i];
if (task->cpu_power != ULLONG_MAX)
task->cpu_power += curr * cputime_to_usecs(cputime);
}
@@ -196,7 +433,7 @@
index = get_index_all_cpufreq_stat(all_stat, freq);
if (index != -1) {
len += scnprintf(buf + len, PAGE_SIZE - len,
- "%llu\t\t", (unsigned long long)
+ "%lu\t\t", (unsigned long)
cputime64_to_clock_t(all_stat->time_in_state[index]));
} else {
len += scnprintf(buf + len, PAGE_SIZE - len,
@@ -359,12 +596,11 @@
}
static int __cpufreq_stats_create_table(struct cpufreq_policy *policy,
- struct cpufreq_frequency_table *table, int count)
+ int cpu, struct cpufreq_frequency_table *table, int count)
{
unsigned int i, ret = 0;
struct cpufreq_stats *stat;
unsigned int alloc_size;
- unsigned int cpu = policy->cpu;
struct cpufreq_frequency_table *pos;
if (per_cpu(cpufreq_stats_table, cpu))
@@ -374,8 +610,6 @@
return -ENOMEM;
ret = sysfs_create_group(&policy->kobj, &stats_attr_group);
- if (ret)
- goto error_out;
stat->cpu = cpu;
per_cpu(cpufreq_stats_table, cpu) = stat;
@@ -403,12 +637,11 @@
stat->state_num = i;
spin_lock(&cpufreq_stats_lock);
stat->last_time = get_jiffies_64();
- stat->last_index = freq_table_get_index(stat, policy->cur);
+ atomic_set(&stat->cpu_freq_i, freq_table_get_index(stat, policy->cur));
spin_unlock(&cpufreq_stats_lock);
return 0;
error_alloc:
sysfs_remove_group(&policy->kobj, &stats_attr_group);
-error_out:
kfree(stat);
per_cpu(cpufreq_stats_table, cpu) = NULL;
return ret;
@@ -505,7 +738,8 @@
static void add_all_freq_table(unsigned int freq)
{
unsigned int size;
- size = sizeof(unsigned int) * (all_freq_table->table_size + 1);
+ size = sizeof(all_freq_table->freq_table[0]) *
+ (all_freq_table->table_size + 1);
all_freq_table->freq_table = krealloc(all_freq_table->freq_table,
size, GFP_ATOMIC);
if (IS_ERR(all_freq_table->freq_table)) {
@@ -586,11 +820,32 @@
if (!per_cpu(cpufreq_power_stats, cpu))
cpufreq_powerstats_create(cpu, table, count);
- __cpufreq_stats_create_table(policy, table, count);
+ __cpufreq_stats_create_table(policy, cpu, table, count);
}
cpufreq_cpu_put(policy);
}
+void cpufreq_task_stats_remove_uids(uid_t uid_start, uid_t uid_end)
+{
+ struct uid_entry *uid_entry;
+ struct hlist_node *tmp;
+
+ rt_mutex_lock(&uid_lock);
+
+ for (; uid_start <= uid_end; uid_start++) {
+ hash_for_each_possible_safe(uid_hash_table, uid_entry, tmp,
+ hash, uid_start) {
+ if (uid_start == uid_entry->uid) {
+ hash_del(&uid_entry->hash);
+ kfree(uid_entry->dead_time_in_state);
+ kfree(uid_entry);
+ }
+ }
+ }
+
+ rt_mutex_unlock(&uid_lock);
+}
+
static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
unsigned long val, void *data)
{
@@ -620,7 +875,7 @@
}
if (val == CPUFREQ_CREATE_POLICY)
- ret = __cpufreq_stats_create_table(policy, table, count);
+ ret = __cpufreq_stats_create_table(policy, cpu, table, count);
else if (val == CPUFREQ_REMOVE_POLICY)
__cpufreq_stats_free_table(policy);
@@ -630,9 +885,11 @@
static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
unsigned long val, void *data)
{
+ int i;
struct cpufreq_freqs *freq = data;
struct cpufreq_stats *stat;
- int old_index, new_index;
+ int cpu_freq_old_i, cpu_freq_new_i;
+ int all_freq_old_i, all_freq_new_i;
if (val != CPUFREQ_POSTCHANGE)
return 0;
@@ -641,28 +898,106 @@
if (!stat)
return 0;
- old_index = stat->last_index;
- new_index = freq_table_get_index(stat, freq->new);
+ cpu_freq_old_i = atomic_read(&stat->cpu_freq_i);
+ cpu_freq_new_i = freq_table_get_index(stat, freq->new);
+
+ all_freq_old_i = atomic_read(&stat->all_freq_i);
+ for (i = 0; i < all_freq_table->table_size; ++i) {
+ if (all_freq_table->freq_table[i] == freq->new)
+ break;
+ }
+ if (i != all_freq_table->table_size)
+ all_freq_new_i = i;
+ else
+ all_freq_new_i = -1;
/* We can't do stat->time_in_state[-1]= .. */
- if (old_index == -1 || new_index == -1)
+ if (cpu_freq_old_i == -1 || cpu_freq_new_i == -1)
+ return 0;
+
+ if (all_freq_old_i == -1 || all_freq_new_i == -1)
return 0;
cpufreq_stats_update(freq->cpu);
- if (old_index == new_index)
+ if (cpu_freq_old_i == cpu_freq_new_i)
+ return 0;
+
+ if (all_freq_old_i == all_freq_new_i)
return 0;
spin_lock(&cpufreq_stats_lock);
- stat->last_index = new_index;
+ atomic_set(&stat->cpu_freq_i, cpu_freq_new_i);
+ atomic_set(&stat->all_freq_i, all_freq_new_i);
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
- stat->trans_table[old_index * stat->max_state + new_index]++;
+ stat->trans_table[cpu_freq_old_i * stat->max_state + cpu_freq_new_i]++;
#endif
stat->total_trans++;
spin_unlock(&cpufreq_stats_lock);
return 0;
}
+
+static int process_notifier(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ struct task_struct *task = v;
+ struct uid_entry *uid_entry;
+ unsigned long flags;
+ uid_t uid;
+ int i;
+
+ if (!task)
+ return NOTIFY_OK;
+
+ rt_mutex_lock(&uid_lock);
+
+ uid = from_kuid_munged(current_user_ns(), task_uid(task));
+ uid_entry = find_or_register_uid(uid);
+ if (!uid_entry) {
+ rt_mutex_unlock(&uid_lock);
+ pr_err("%s: failed to find uid %d\n", __func__, uid);
+ return NOTIFY_OK;
+ }
+
+ if (uid_entry->dead_max_states < task->max_states) {
+ uid_entry->dead_time_in_state = krealloc(
+ uid_entry->dead_time_in_state,
+ task->max_states *
+ sizeof(uid_entry->dead_time_in_state[0]),
+ GFP_ATOMIC);
+ memset(uid_entry->dead_time_in_state +
+ uid_entry->dead_max_states,
+ 0, (task->max_states - uid_entry->dead_max_states) *
+ sizeof(uid_entry->dead_time_in_state[0]));
+ uid_entry->dead_max_states = task->max_states;
+ }
+
+ spin_lock_irqsave(&task_time_in_state_lock, flags);
+ if (task->time_in_state) {
+ for (i = 0; i < task->max_states; ++i) {
+ uid_entry->dead_time_in_state[i] +=
+ atomic_read(&task->time_in_state[i]);
+ }
+ }
+ spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+
+ rt_mutex_unlock(&uid_lock);
+ return NOTIFY_OK;
+}
+
+static int uid_time_in_state_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, uid_time_in_state_show, PDE_DATA(inode));
+}
+
+static const struct file_operations uid_time_in_state_fops = {
+ .open = uid_time_in_state_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static struct notifier_block notifier_policy_block = {
.notifier_call = cpufreq_stat_notifier_policy
};
@@ -671,6 +1006,10 @@
.notifier_call = cpufreq_stat_notifier_trans
};
+static struct notifier_block process_notifier_block = {
+ .notifier_call = process_notifier,
+};
+
static int __init cpufreq_stats_init(void)
{
int ret;
@@ -682,11 +1021,18 @@
if (ret)
return ret;
+ create_all_freq_table();
+
get_online_cpus();
for_each_online_cpu(cpu)
cpufreq_stats_create_table(cpu);
put_online_cpus();
-
+ /* XXX TODO task support for time_in_state doesn't update freq
+ * info for tasks already initialized, so tasks initialized early
+ * (before cpufreq_stat_init is done) do not get time_in_state data
+ * and CPUFREQ_TRANSITION_NOTIFIER does not update freq info for
+ * tasks already created
+ */
ret = cpufreq_register_notifier(¬ifier_trans_block,
CPUFREQ_TRANSITION_NOTIFIER);
if (ret) {
@@ -699,7 +1045,6 @@
return ret;
}
- create_all_freq_table();
WARN_ON(cpufreq_get_global_kobject());
ret = sysfs_create_file(cpufreq_global_kobject,
&_attr_all_time_in_state.attr);
@@ -711,6 +1056,12 @@
if (ret)
pr_warn("Cannot create sysfs file for cpufreq current stats\n");
+ proc_create_data("uid_time_in_state", 0444, NULL,
+ &uid_time_in_state_fops, NULL);
+
+ profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
+
+ cpufreq_all_freq_init = true;
return 0;
}
static void __exit cpufreq_stats_exit(void)
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index c1da6e1..0fdb8c7 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -847,8 +847,11 @@
/* cpuinfo and default policy values */
policy->cpuinfo.min_freq = cpu->pstate.min_pstate * cpu->pstate.scaling;
- policy->cpuinfo.max_freq =
- cpu->pstate.turbo_pstate * cpu->pstate.scaling;
+ update_turbo_state();
+ policy->cpuinfo.max_freq = limits.turbo_disabled ?
+ cpu->pstate.max_pstate : cpu->pstate.turbo_pstate;
+ policy->cpuinfo.max_freq *= cpu->pstate.scaling;
+
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
cpumask_set_cpu(policy->cpu, policy->cpus);
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 06c0eb7..ea5bc6f2 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -139,7 +139,7 @@
/* The cpu is no longer idle or about to enter idle. */
sched_idle_set_state(NULL, -1);
- if (!cpuidle_state_is_coupled(dev, drv, entered_state))
+ if (!cpuidle_state_is_coupled(dev, drv, index))
local_irq_enable();
diff = ktime_to_us(ktime_sub(time_end, time_start));
@@ -364,6 +364,8 @@
list_del(&dev->device_list);
per_cpu(cpuidle_devices, dev->cpu) = NULL;
module_put(drv->owner);
+
+ dev->registered = 0;
}
static void __cpuidle_device_init(struct cpuidle_device *dev)
diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c
index 750511a..9a22651 100644
--- a/drivers/cpuidle/lpm-levels-of.c
+++ b/drivers/cpuidle/lpm-levels-of.c
@@ -97,7 +97,7 @@
bool probe_time)
{
int i, j;
- bool mode_avail;
+ bool mode_avail = false;
for (i = 0; i < cluster->nlevels; i++) {
struct power_params *pwr = &cluster->levels[i].pwr;
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index a80ea85..50297a2 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -597,7 +597,9 @@
/* Will read cryptlen */
append_math_add(desc, VARSEQINLEN, SEQINLEN, REG0, CAAM_CMD_SZ);
- aead_append_src_dst(desc, FIFOLD_TYPE_MSG1OUT2);
+ append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH | KEY_VLF |
+ FIFOLD_TYPE_MSG1OUT2 | FIFOLD_TYPE_LASTBOTH);
+ append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF);
/* Write ICV */
append_seq_store(desc, ctx->authsize, LDST_CLASS_2_CCB |
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
index 5408450..e3920af 100644
--- a/drivers/crypto/caam/caamhash.c
+++ b/drivers/crypto/caam/caamhash.c
@@ -1861,6 +1861,7 @@
template->name);
snprintf(alg->cra_driver_name, CRYPTO_MAX_ALG_NAME, "%s",
template->driver_name);
+ t_alg->ahash_alg.setkey = NULL;
}
alg->cra_module = THIS_MODULE;
alg->cra_init = caam_hash_cra_init;
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index 31000c8..a23062e 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -263,7 +263,8 @@
/* Try to run it through DECO0 */
ret = run_descriptor_deco0(ctrldev, desc, &status);
- if (ret || status) {
+ if (ret ||
+ (status && status != JRSTA_SSRC_JUMP_HALT_CC)) {
dev_err(ctrldev,
"Failed to deinstantiate RNG4 SH%d\n",
sh_idx);
diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c
index 4d18e27..e87d125 100644
--- a/drivers/crypto/caam/jr.c
+++ b/drivers/crypto/caam/jr.c
@@ -244,7 +244,7 @@
struct device *caam_jr_alloc(void)
{
struct caam_drv_private_jr *jrpriv, *min_jrpriv = NULL;
- struct device *dev = NULL;
+ struct device *dev = ERR_PTR(-ENODEV);
int min_tfm_cnt = INT_MAX;
int tfm_cnt;
diff --git a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
index 5c93afb..f10b499 100644
--- a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
+++ b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c
@@ -206,6 +206,9 @@
struct ccp_aes_cmac_req_ctx *rctx = ahash_request_ctx(req);
struct ccp_aes_cmac_exp_ctx state;
+ /* Don't let anything leak to 'out' */
+ memset(&state, 0, sizeof(state));
+
state.null_msg = rctx->null_msg;
memcpy(state.iv, rctx->iv, sizeof(state.iv));
state.buf_count = rctx->buf_count;
diff --git a/drivers/crypto/ccp/ccp-crypto-aes-xts.c b/drivers/crypto/ccp/ccp-crypto-aes-xts.c
index 0cc5594..8b294c2 100644
--- a/drivers/crypto/ccp/ccp-crypto-aes-xts.c
+++ b/drivers/crypto/ccp/ccp-crypto-aes-xts.c
@@ -123,6 +123,7 @@
struct ccp_ctx *ctx = crypto_tfm_ctx(req->base.tfm);
struct ccp_aes_req_ctx *rctx = ablkcipher_request_ctx(req);
unsigned int unit;
+ u32 unit_size;
int ret;
if (!ctx->u.aes.key_len)
@@ -134,11 +135,17 @@
if (!req->info)
return -EINVAL;
- for (unit = 0; unit < ARRAY_SIZE(unit_size_map); unit++)
- if (!(req->nbytes & (unit_size_map[unit].size - 1)))
- break;
+ unit_size = CCP_XTS_AES_UNIT_SIZE__LAST;
+ if (req->nbytes <= unit_size_map[0].size) {
+ for (unit = 0; unit < ARRAY_SIZE(unit_size_map); unit++) {
+ if (!(req->nbytes & (unit_size_map[unit].size - 1))) {
+ unit_size = unit_size_map[unit].value;
+ break;
+ }
+ }
+ }
- if ((unit_size_map[unit].value == CCP_XTS_AES_UNIT_SIZE__LAST) ||
+ if ((unit_size == CCP_XTS_AES_UNIT_SIZE__LAST) ||
(ctx->u.aes.key_len != AES_KEYSIZE_128)) {
/* Use the fallback to process the request for any
* unsupported unit sizes or key sizes
@@ -159,7 +166,7 @@
rctx->cmd.engine = CCP_ENGINE_XTS_AES_128;
rctx->cmd.u.xts.action = (encrypt) ? CCP_AES_ACTION_ENCRYPT
: CCP_AES_ACTION_DECRYPT;
- rctx->cmd.u.xts.unit_size = unit_size_map[unit].value;
+ rctx->cmd.u.xts.unit_size = unit_size;
rctx->cmd.u.xts.key = &ctx->u.aes.key_sg;
rctx->cmd.u.xts.key_len = ctx->u.aes.key_len;
rctx->cmd.u.xts.iv = &rctx->iv_sg;
diff --git a/drivers/crypto/ccp/ccp-crypto-sha.c b/drivers/crypto/ccp/ccp-crypto-sha.c
index b368e98..ced9f38 100644
--- a/drivers/crypto/ccp/ccp-crypto-sha.c
+++ b/drivers/crypto/ccp/ccp-crypto-sha.c
@@ -198,6 +198,9 @@
struct ccp_sha_req_ctx *rctx = ahash_request_ctx(req);
struct ccp_sha_exp_ctx state;
+ /* Don't let anything leak to 'out' */
+ memset(&state, 0, sizeof(state));
+
state.type = rctx->type;
state.msg_bits = rctx->msg_bits;
state.first = rctx->first;
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 4c38eab..22e7f84 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -2984,6 +2984,7 @@
struct qce_device *pce_dev = (struct qce_device *)data;
int ret = 0;
int last_seq;
+ unsigned long flags;
last_seq = atomic_read(&pce_dev->bunch_cmd_seq);
if (last_seq == 0 ||
@@ -2993,10 +2994,11 @@
return;
}
/* last bunch mode command time out */
+ local_irq_save(flags);
if (cmpxchg(&pce_dev->owner, QCE_OWNER_NONE, QCE_OWNER_TIMEOUT)
!= QCE_OWNER_NONE) {
mod_timer(&(pce_dev->timer), (jiffies + DELAY_IN_JIFFIES));
- return;
+ goto out_irq_restore;
}
del_timer(&(pce_dev->timer));
pce_dev->mode = IN_INTERRUPT_MODE;
@@ -3008,6 +3010,9 @@
pr_warn("pcedev %d: Failed to insert dummy req\n",
pce_dev->dev_no);
cmpxchg(&pce_dev->owner, QCE_OWNER_TIMEOUT, QCE_OWNER_NONE);
+out_irq_restore:
+ local_irq_restore(flags);
+ return;
}
void qce_get_driver_stats(void *handle)
diff --git a/drivers/crypto/s5p-sss.c b/drivers/crypto/s5p-sss.c
index 4197ad9..658fa53 100644
--- a/drivers/crypto/s5p-sss.c
+++ b/drivers/crypto/s5p-sss.c
@@ -149,7 +149,6 @@
/**
* struct samsung_aes_variant - platform specific SSS driver data
- * @has_hash_irq: true if SSS module uses hash interrupt, false otherwise
* @aes_offset: AES register offset from SSS module's base.
*
* Specifies platform specific configuration of SSS module.
@@ -157,7 +156,6 @@
* expansion of its usage.
*/
struct samsung_aes_variant {
- bool has_hash_irq;
unsigned int aes_offset;
};
@@ -178,7 +176,6 @@
struct clk *clk;
void __iomem *ioaddr;
void __iomem *aes_ioaddr;
- int irq_hash;
int irq_fc;
struct ablkcipher_request *req;
@@ -197,12 +194,10 @@
static struct s5p_aes_dev *s5p_dev;
static const struct samsung_aes_variant s5p_aes_data = {
- .has_hash_irq = true,
.aes_offset = 0x4000,
};
static const struct samsung_aes_variant exynos_aes_data = {
- .has_hash_irq = false,
.aes_offset = 0x200,
};
@@ -313,43 +308,55 @@
return err;
}
-static void s5p_aes_tx(struct s5p_aes_dev *dev)
+/*
+ * Returns true if new transmitting (output) data is ready and its
+ * address+length have to be written to device (by calling
+ * s5p_set_dma_outdata()). False otherwise.
+ */
+static bool s5p_aes_tx(struct s5p_aes_dev *dev)
{
int err = 0;
+ bool ret = false;
s5p_unset_outdata(dev);
if (!sg_is_last(dev->sg_dst)) {
err = s5p_set_outdata(dev, sg_next(dev->sg_dst));
- if (err) {
+ if (err)
s5p_aes_complete(dev, err);
- return;
- }
-
- s5p_set_dma_outdata(dev, dev->sg_dst);
+ else
+ ret = true;
} else {
s5p_aes_complete(dev, err);
dev->busy = true;
tasklet_schedule(&dev->tasklet);
}
+
+ return ret;
}
-static void s5p_aes_rx(struct s5p_aes_dev *dev)
+/*
+ * Returns true if new receiving (input) data is ready and its
+ * address+length have to be written to device (by calling
+ * s5p_set_dma_indata()). False otherwise.
+ */
+static bool s5p_aes_rx(struct s5p_aes_dev *dev)
{
int err;
+ bool ret = false;
s5p_unset_indata(dev);
if (!sg_is_last(dev->sg_src)) {
err = s5p_set_indata(dev, sg_next(dev->sg_src));
- if (err) {
+ if (err)
s5p_aes_complete(dev, err);
- return;
- }
-
- s5p_set_dma_indata(dev, dev->sg_src);
+ else
+ ret = true;
}
+
+ return ret;
}
static irqreturn_t s5p_aes_interrupt(int irq, void *dev_id)
@@ -358,18 +365,29 @@
struct s5p_aes_dev *dev = platform_get_drvdata(pdev);
uint32_t status;
unsigned long flags;
+ bool set_dma_tx = false;
+ bool set_dma_rx = false;
spin_lock_irqsave(&dev->lock, flags);
- if (irq == dev->irq_fc) {
- status = SSS_READ(dev, FCINTSTAT);
- if (status & SSS_FCINTSTAT_BRDMAINT)
- s5p_aes_rx(dev);
- if (status & SSS_FCINTSTAT_BTDMAINT)
- s5p_aes_tx(dev);
+ status = SSS_READ(dev, FCINTSTAT);
+ if (status & SSS_FCINTSTAT_BRDMAINT)
+ set_dma_rx = s5p_aes_rx(dev);
+ if (status & SSS_FCINTSTAT_BTDMAINT)
+ set_dma_tx = s5p_aes_tx(dev);
- SSS_WRITE(dev, FCINTPEND, status);
- }
+ SSS_WRITE(dev, FCINTPEND, status);
+
+ /*
+ * Writing length of DMA block (either receiving or transmitting)
+ * will start the operation immediately, so this should be done
+ * at the end (even after clearing pending interrupts to not miss the
+ * interrupt).
+ */
+ if (set_dma_tx)
+ s5p_set_dma_outdata(dev, dev->sg_dst);
+ if (set_dma_rx)
+ s5p_set_dma_indata(dev, dev->sg_src);
spin_unlock_irqrestore(&dev->lock, flags);
@@ -671,21 +689,6 @@
goto err_irq;
}
- if (variant->has_hash_irq) {
- pdata->irq_hash = platform_get_irq(pdev, 1);
- if (pdata->irq_hash < 0) {
- err = pdata->irq_hash;
- dev_warn(dev, "hash interrupt is not available.\n");
- goto err_irq;
- }
- err = devm_request_irq(dev, pdata->irq_hash, s5p_aes_interrupt,
- IRQF_SHARED, pdev->name, pdev);
- if (err < 0) {
- dev_warn(dev, "hash interrupt is not available.\n");
- goto err_irq;
- }
- }
-
pdata->busy = false;
pdata->variant = variant;
pdata->dev = dev;
diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c
index 8e5e018..3ff21c3 100644
--- a/drivers/crypto/ux500/hash/hash_core.c
+++ b/drivers/crypto/ux500/hash/hash_core.c
@@ -797,7 +797,7 @@
&device_data->state);
memmove(req_ctx->state.buffer,
device_data->state.buffer,
- HASH_BLOCK_SIZE / sizeof(u32));
+ HASH_BLOCK_SIZE);
if (ret) {
dev_err(device_data->dev,
"%s: hash_resume_state() failed!\n",
@@ -848,7 +848,7 @@
memmove(device_data->state.buffer,
req_ctx->state.buffer,
- HASH_BLOCK_SIZE / sizeof(u32));
+ HASH_BLOCK_SIZE);
if (ret) {
dev_err(device_data->dev, "%s: hash_save_state() failed!\n",
__func__);
diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
index d95e1d0..f070a8d 100644
--- a/drivers/dma/dw/core.c
+++ b/drivers/dma/dw/core.c
@@ -122,26 +122,14 @@
static void dwc_initialize(struct dw_dma_chan *dwc)
{
struct dw_dma *dw = to_dw_dma(dwc->chan.device);
- struct dw_dma_slave *dws = dwc->chan.private;
u32 cfghi = DWC_CFGH_FIFO_MODE;
u32 cfglo = DWC_CFGL_CH_PRIOR(dwc->priority);
if (dwc->initialized == true)
return;
- if (dws) {
- /*
- * We need controller-specific data to set up slave
- * transfers.
- */
- BUG_ON(!dws->dma_dev || dws->dma_dev != dw->dma.dev);
-
- cfghi |= DWC_CFGH_DST_PER(dws->dst_id);
- cfghi |= DWC_CFGH_SRC_PER(dws->src_id);
- } else {
- cfghi |= DWC_CFGH_DST_PER(dwc->dst_id);
- cfghi |= DWC_CFGH_SRC_PER(dwc->src_id);
- }
+ cfghi |= DWC_CFGH_DST_PER(dwc->dst_id);
+ cfghi |= DWC_CFGH_SRC_PER(dwc->src_id);
channel_writel(dwc, CFG_LO, cfglo);
channel_writel(dwc, CFG_HI, cfghi);
@@ -932,7 +920,7 @@
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma_slave *dws = param;
- if (!dws || dws->dma_dev != chan->device->dev)
+ if (dws->dma_dev != chan->device->dev)
return false;
/* We have to copy data since dws can be temporary storage */
@@ -1153,6 +1141,14 @@
* doesn't mean what you think it means), and status writeback.
*/
+ /*
+ * We need controller-specific data to set up slave transfers.
+ */
+ if (chan->private && !dw_dma_filter(chan, chan->private)) {
+ dev_warn(chan2dev(chan), "Wrong controller-specific data\n");
+ return -EINVAL;
+ }
+
/* Enable controller here if needed */
if (!dw->in_use)
dw_dma_on(dw);
@@ -1214,6 +1210,14 @@
spin_lock_irqsave(&dwc->lock, flags);
list_splice_init(&dwc->free_list, &list);
dwc->descs_allocated = 0;
+
+ /* Clear custom channel configuration */
+ dwc->src_id = 0;
+ dwc->dst_id = 0;
+
+ dwc->src_master = 0;
+ dwc->dst_master = 0;
+
dwc->initialized = false;
/* Disable interrupts */
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 88afc48c..3ed46c8 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -531,6 +531,10 @@
dev_err(sdma->dev, "Timeout waiting for CH0 ready\n");
}
+ /* Set bits of CONFIG register with dynamic context switching */
+ if (readl(sdma->regs + SDMA_H_CONFIG) == 0)
+ writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
+
return ret ? 0 : -ETIMEDOUT;
}
@@ -1399,9 +1403,6 @@
writel_relaxed(ccb_phys, sdma->regs + SDMA_H_C0PTR);
- /* Set bits of CONFIG register with given context switching mode */
- writel_relaxed(SDMA_H_CONFIG_CSM, sdma->regs + SDMA_H_CONFIG);
-
/* Initializes channel's priorities */
sdma_set_channel_priority(&sdma->channel[0], 7);
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 9cd0b30..f84ef75 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -1874,7 +1874,7 @@
i7_dev = get_i7core_dev(mce->socketid);
if (!i7_dev)
- return NOTIFY_BAD;
+ return NOTIFY_DONE;
mci = i7_dev->mci;
pvt = mci->pvt_info;
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index b97f5f0..6aa25a3 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -1264,7 +1264,7 @@
}
ch_way = TAD_CH(reg) + 1;
- sck_way = 1 << TAD_SOCK(reg);
+ sck_way = TAD_SOCK(reg);
if (ch_way == 3)
idx = addr >> 6;
@@ -1303,7 +1303,7 @@
switch(ch_way) {
case 2:
case 4:
- sck_xch = 1 << sck_way * (ch_way >> 1);
+ sck_xch = (1 << sck_way) * (ch_way >> 1);
break;
default:
sprintf(msg, "Invalid mirror set. Can't decode addr");
@@ -1339,7 +1339,7 @@
ch_addr = addr - offset;
ch_addr >>= (6 + shiftup);
- ch_addr /= ch_way * sck_way;
+ ch_addr /= sck_xch;
ch_addr <<= (6 + shiftup);
ch_addr |= addr & ((1 << (6 + shiftup)) - 1);
@@ -2047,7 +2047,7 @@
mci = get_mci_for_node_id(mce->socketid);
if (!mci)
- return NOTIFY_BAD;
+ return NOTIFY_DONE;
pvt = mci->pvt_info;
/*
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index 2c68da1..e7a085a 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -73,13 +73,13 @@
#define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30)
#define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff))
-#define fwnet_get_hdr_dg_size(h) (((h)->w0 & 0x0fff0000) >> 16)
+#define fwnet_get_hdr_dg_size(h) ((((h)->w0 & 0x0fff0000) >> 16) + 1)
#define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff))
#define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16)
-#define fwnet_set_hdr_lf(lf) ((lf) << 30)
+#define fwnet_set_hdr_lf(lf) ((lf) << 30)
#define fwnet_set_hdr_ether_type(et) (et)
-#define fwnet_set_hdr_dg_size(dgs) ((dgs) << 16)
+#define fwnet_set_hdr_dg_size(dgs) (((dgs) - 1) << 16)
#define fwnet_set_hdr_fg_off(fgo) (fgo)
#define fwnet_set_hdr_dgl(dgl) ((dgl) << 16)
@@ -591,6 +591,9 @@
int retval;
u16 ether_type;
+ if (len <= RFC2374_UNFRAG_HDR_SIZE)
+ return 0;
+
hdr.w0 = be32_to_cpu(buf[0]);
lf = fwnet_get_hdr_lf(&hdr);
if (lf == RFC2374_HDR_UNFRAG) {
@@ -615,7 +618,12 @@
return fwnet_finish_incoming_packet(net, skb, source_node_id,
is_broadcast, ether_type);
}
+
/* A datagram fragment has been received, now the fun begins. */
+
+ if (len <= RFC2374_FRAG_HDR_SIZE)
+ return 0;
+
hdr.w1 = ntohl(buf[1]);
buf += 2;
len -= RFC2374_FRAG_HDR_SIZE;
@@ -627,7 +635,10 @@
fg_off = fwnet_get_hdr_fg_off(&hdr);
}
datagram_label = fwnet_get_hdr_dgl(&hdr);
- dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */
+ dg_size = fwnet_get_hdr_dg_size(&hdr);
+
+ if (fg_off + len > dg_size)
+ return 0;
spin_lock_irqsave(&dev->lock, flags);
@@ -735,6 +746,22 @@
fw_send_response(card, r, rcode);
}
+static int gasp_source_id(__be32 *p)
+{
+ return be32_to_cpu(p[0]) >> 16;
+}
+
+static u32 gasp_specifier_id(__be32 *p)
+{
+ return (be32_to_cpu(p[0]) & 0xffff) << 8 |
+ (be32_to_cpu(p[1]) & 0xff000000) >> 24;
+}
+
+static u32 gasp_version(__be32 *p)
+{
+ return be32_to_cpu(p[1]) & 0xffffff;
+}
+
static void fwnet_receive_broadcast(struct fw_iso_context *context,
u32 cycle, size_t header_length, void *header, void *data)
{
@@ -744,9 +771,6 @@
__be32 *buf_ptr;
int retval;
u32 length;
- u16 source_node_id;
- u32 specifier_id;
- u32 ver;
unsigned long offset;
unsigned long flags;
@@ -763,22 +787,17 @@
spin_unlock_irqrestore(&dev->lock, flags);
- specifier_id = (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8
- | (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24;
- ver = be32_to_cpu(buf_ptr[1]) & 0xffffff;
- source_node_id = be32_to_cpu(buf_ptr[0]) >> 16;
-
- if (specifier_id == IANA_SPECIFIER_ID &&
- (ver == RFC2734_SW_VERSION
+ if (length > IEEE1394_GASP_HDR_SIZE &&
+ gasp_specifier_id(buf_ptr) == IANA_SPECIFIER_ID &&
+ (gasp_version(buf_ptr) == RFC2734_SW_VERSION
#if IS_ENABLED(CONFIG_IPV6)
- || ver == RFC3146_SW_VERSION
+ || gasp_version(buf_ptr) == RFC3146_SW_VERSION
#endif
- )) {
- buf_ptr += 2;
- length -= IEEE1394_GASP_HDR_SIZE;
- fwnet_incoming_packet(dev, buf_ptr, length, source_node_id,
+ ))
+ fwnet_incoming_packet(dev, buf_ptr + 2,
+ length - IEEE1394_GASP_HDR_SIZE,
+ gasp_source_id(buf_ptr),
context->card->generation, true);
- }
packet.payload_length = dev->rcv_buffer_size;
packet.interrupt = 1;
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index b7ba9d8..49eb145 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -157,6 +157,7 @@
{
generic_ops.get_variable = efi.get_variable;
generic_ops.set_variable = efi.set_variable;
+ generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking;
generic_ops.get_next_variable = efi.get_next_variable;
generic_ops.query_variable_store = efi_query_variable_store;
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index 7f2ea21..6f182fd 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -202,29 +202,44 @@
{ NULL_GUID, "", NULL },
};
+/*
+ * Check if @var_name matches the pattern given in @match_name.
+ *
+ * @var_name: an array of @len non-NUL characters.
+ * @match_name: a NUL-terminated pattern string, optionally ending in "*". A
+ * final "*" character matches any trailing characters @var_name,
+ * including the case when there are none left in @var_name.
+ * @match: on output, the number of non-wildcard characters in @match_name
+ * that @var_name matches, regardless of the return value.
+ * @return: whether @var_name fully matches @match_name.
+ */
static bool
variable_matches(const char *var_name, size_t len, const char *match_name,
int *match)
{
for (*match = 0; ; (*match)++) {
char c = match_name[*match];
- char u = var_name[*match];
- /* Wildcard in the matching name means we've matched */
- if (c == '*')
+ switch (c) {
+ case '*':
+ /* Wildcard in @match_name means we've matched. */
return true;
- /* Case sensitive match */
- if (!c && *match == len)
- return true;
+ case '\0':
+ /* @match_name has ended. Has @var_name too? */
+ return (*match == len);
- if (c != u)
+ default:
+ /*
+ * We've reached a non-wildcard char in @match_name.
+ * Continue only if there's an identical character in
+ * @var_name.
+ */
+ if (*match < len && c == var_name[*match])
+ continue;
return false;
-
- if (!c)
- return true;
+ }
}
- return true;
}
bool
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index fe7f92f..2115124 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -50,6 +50,7 @@
config OF_GPIO
def_bool y
depends on OF
+ depends on HAS_IOMEM
config GPIO_ACPI
def_bool y
diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c
index de0801e..b2826ee 100644
--- a/drivers/gpio/gpio-bcm-kona.c
+++ b/drivers/gpio/gpio-bcm-kona.c
@@ -549,11 +549,11 @@
/* disable interrupts and clear status */
for (i = 0; i < kona_gpio->num_bank; i++) {
/* Unlock the entire bank first */
- bcm_kona_gpio_write_lock_regs(kona_gpio, i, UNLOCK_CODE);
+ bcm_kona_gpio_write_lock_regs(reg_base, i, UNLOCK_CODE);
writel(0xffffffff, reg_base + GPIO_INT_MASK(i));
writel(0xffffffff, reg_base + GPIO_INT_STATUS(i));
/* Now re-lock the bank */
- bcm_kona_gpio_write_lock_regs(kona_gpio, i, LOCK_CODE);
+ bcm_kona_gpio_write_lock_regs(reg_base, i, LOCK_CODE);
}
}
diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c
index aa28c65..14945fd 100644
--- a/drivers/gpio/gpio-intel-mid.c
+++ b/drivers/gpio/gpio-intel-mid.c
@@ -17,7 +17,6 @@
* Moorestown platform Langwell chip.
* Medfield platform Penwell chip.
* Clovertrail platform Cloverview chip.
- * Merrifield platform Tangier chip.
*/
#include <linux/module.h>
@@ -64,10 +63,6 @@
/* intel_mid gpio driver data */
struct intel_mid_gpio_ddata {
u16 ngpio; /* number of gpio pins */
- u32 gplr_offset; /* offset of first GPLR register from base */
- u32 flis_base; /* base address of FLIS registers */
- u32 flis_len; /* length of FLIS registers */
- u32 (*get_flis_offset)(int gpio);
u32 chip_irq_type; /* chip interrupt type */
};
@@ -257,15 +252,6 @@
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
};
-static const struct intel_mid_gpio_ddata gpio_tangier = {
- .ngpio = 192,
- .gplr_offset = 4,
- .flis_base = 0xff0c0000,
- .flis_len = 0x8000,
- .get_flis_offset = NULL,
- .chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
-};
-
static const struct pci_device_id intel_gpio_ids[] = {
{
/* Lincroft */
@@ -292,11 +278,6 @@
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7),
.driver_data = (kernel_ulong_t)&gpio_cloverview_core,
},
- {
- /* Tangier */
- PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x1199),
- .driver_data = (kernel_ulong_t)&gpio_tangier,
- },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, intel_gpio_ids);
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index e2da64a..6e2720e 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -21,6 +21,7 @@
#ifdef CONFIG_OF_GPIO
#include <linux/of_platform.h>
#endif
+#include <asm/unaligned.h>
#define PCA953X_INPUT 0
#define PCA953X_OUTPUT 1
@@ -75,7 +76,7 @@
#define MAX_BANK 5
#define BANK_SZ 8
-#define NBANK(chip) (chip->gpio_chip.ngpio / BANK_SZ)
+#define NBANK(chip) DIV_ROUND_UP(chip->gpio_chip.ngpio, BANK_SZ)
struct pca953x_chip {
unsigned gpio_start;
@@ -154,7 +155,7 @@
switch (chip->chip_type) {
case PCA953X_TYPE:
ret = i2c_smbus_write_word_data(chip->client,
- reg << 1, (u16) *val);
+ reg << 1, cpu_to_le16(get_unaligned((u16 *)val)));
break;
case PCA957X_TYPE:
ret = i2c_smbus_write_byte_data(chip->client, reg << 1,
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 467acfc..9527dd0 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -361,7 +361,7 @@
spin_lock_irqsave(&gpio_lock, flags);
list_for_each_entry(chip, &gpio_chips, list)
- if (match(chip, data))
+ if (chip && match(chip, data))
break;
/* No match? */
diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
index 5c60ae5..ff68eef 100644
--- a/drivers/gpu/drm/ast/ast_fb.c
+++ b/drivers/gpu/drm/ast/ast_fb.c
@@ -335,18 +335,27 @@
ret = drm_fb_helper_init(dev, &afbdev->helper,
1, 1);
- if (ret) {
- kfree(afbdev);
- return ret;
- }
+ if (ret)
+ goto free;
- drm_fb_helper_single_add_all_connectors(&afbdev->helper);
+ ret = drm_fb_helper_single_add_all_connectors(&afbdev->helper);
+ if (ret)
+ goto fini;
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
- drm_fb_helper_initial_config(&afbdev->helper, 32);
+ ret = drm_fb_helper_initial_config(&afbdev->helper, 32);
+ if (ret)
+ goto fini;
+
return 0;
+
+fini:
+ drm_fb_helper_fini(&afbdev->helper);
+free:
+ kfree(afbdev);
+ return ret;
}
void ast_fbdev_fini(struct drm_device *dev)
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
index fd5c5f3..e53dbc9 100644
--- a/drivers/gpu/drm/ast/ast_main.c
+++ b/drivers/gpu/drm/ast/ast_main.c
@@ -223,7 +223,8 @@
ast_write32(ast, 0x10000, 0xfc600309);
do {
- ;
+ if (pci_channel_offline(dev->pdev))
+ return -EIO;
} while (ast_read32(ast, 0x10000) != 0x01);
data = ast_read32(ast, 0x10004);
@@ -429,7 +430,9 @@
ast_detect_chip(dev, &need_post);
if (ast->chip != AST1180) {
- ast_get_dram_info(dev);
+ ret = ast_get_dram_info(dev);
+ if (ret)
+ goto out_free;
ast->vram_size = ast_get_vram_info(dev);
DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size);
}
diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c
index 810c51d..30672a3 100644
--- a/drivers/gpu/drm/ast/ast_post.c
+++ b/drivers/gpu/drm/ast/ast_post.c
@@ -58,13 +58,9 @@
/* TODO 1180 */
} else {
ch = ast_io_read8(ast, AST_IO_VGA_ENABLE_PORT);
- if (ch) {
- ast_open_key(ast);
- ch = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xb6, 0xff);
- return ch & 0x04;
- }
+ return !!(ch & 0x01);
}
- return 0;
+ return false;
}
static const u8 extreginfo[] = { 0x0f, 0x04, 0x1c, 0xff };
@@ -375,8 +371,8 @@
pci_write_config_dword(ast->dev->pdev, 0x04, reg);
ast_enable_vga(dev);
- ast_enable_mmio(dev);
ast_open_key(ast);
+ ast_enable_mmio(dev);
ast_set_def_ext_reg(dev);
if (ast->chip == AST2300 || ast->chip == AST2400)
@@ -1630,12 +1626,44 @@
temp |= 0x73;
ast_write32(ast, 0x12008, temp);
+ param.dram_freq = 396;
param.dram_type = AST_DDR3;
+ temp = ast_mindwm(ast, 0x1e6e2070);
if (temp & 0x01000000)
param.dram_type = AST_DDR2;
- param.dram_chipid = ast->dram_type;
- param.dram_freq = ast->mclk;
- param.vram_size = ast->vram_size;
+ switch (temp & 0x18000000) {
+ case 0:
+ param.dram_chipid = AST_DRAM_512Mx16;
+ break;
+ default:
+ case 0x08000000:
+ param.dram_chipid = AST_DRAM_1Gx16;
+ break;
+ case 0x10000000:
+ param.dram_chipid = AST_DRAM_2Gx16;
+ break;
+ case 0x18000000:
+ param.dram_chipid = AST_DRAM_4Gx16;
+ break;
+ }
+ switch (temp & 0x0c) {
+ default:
+ case 0x00:
+ param.vram_size = AST_VIDMEM_SIZE_8M;
+ break;
+
+ case 0x04:
+ param.vram_size = AST_VIDMEM_SIZE_16M;
+ break;
+
+ case 0x08:
+ param.vram_size = AST_VIDMEM_SIZE_32M;
+ break;
+
+ case 0x0c:
+ param.vram_size = AST_VIDMEM_SIZE_64M;
+ break;
+ }
if (param.dram_type == AST_DDR3) {
get_ddr3_info(ast, ¶m);
diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
index fe95d31..9cdab0f 100644
--- a/drivers/gpu/drm/bochs/bochs_fbdev.c
+++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
@@ -197,12 +197,22 @@
if (ret)
return ret;
- drm_fb_helper_single_add_all_connectors(&bochs->fb.helper);
+ ret = drm_fb_helper_single_add_all_connectors(&bochs->fb.helper);
+ if (ret)
+ goto fini;
+
drm_helper_disable_unused_functions(bochs->dev);
- drm_fb_helper_initial_config(&bochs->fb.helper, 32);
+
+ ret = drm_fb_helper_initial_config(&bochs->fb.helper, 32);
+ if (ret)
+ goto fini;
bochs->fb.initialized = true;
return 0;
+
+fini:
+ drm_fb_helper_fini(&bochs->fb.helper);
+ return ret;
}
void bochs_fbdev_fini(struct bochs_device *bochs)
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
index d231b1c..b487c4e 100644
--- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
@@ -314,17 +314,17 @@
ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
- if (ret) {
- kfree(gfbdev);
+ if (ret)
return ret;
- }
- drm_fb_helper_single_add_all_connectors(&gfbdev->helper);
+
+ ret = drm_fb_helper_single_add_all_connectors(&gfbdev->helper);
+ if (ret)
+ return ret;
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(cdev->dev);
- drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel);
- return 0;
+ return drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel);
}
void cirrus_fbdev_fini(struct cirrus_device *cdev)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 0cd6e0d..18dd03b 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -4583,6 +4583,9 @@
unsigned long flags;
int ret = -EINVAL;
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return -EINVAL;
+
if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
page_flip->reserved != 0)
return -EINVAL;
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 563d3d2..c4f8e8f 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -1694,6 +1694,11 @@
req_payload.start_slot = cur_slots;
if (mgr->proposed_vcpis[i]) {
port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+ port = drm_dp_get_validated_port_ref(mgr, port);
+ if (!port) {
+ mutex_unlock(&mgr->payload_lock);
+ return -EINVAL;
+ }
req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots;
} else {
port = NULL;
@@ -1719,6 +1724,9 @@
mgr->payloads[i].payload_state = req_payload.payload_state;
}
cur_slots += req_payload.num_slots;
+
+ if (port)
+ drm_dp_put_port(port);
}
for (i = 0; i < mgr->max_payloads; i++) {
@@ -2005,6 +2013,8 @@
if (mgr->mst_primary) {
int sret;
+ u8 guid[16];
+
sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
if (sret != DP_RECEIVER_CAP_SIZE) {
DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
@@ -2019,6 +2029,16 @@
ret = -1;
goto out_unlock;
}
+
+ /* Some hubs forget their guids after they resume */
+ sret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
+ if (sret != 16) {
+ DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
+ ret = -1;
+ goto out_unlock;
+ }
+ drm_dp_check_mstb_guid(mgr->mst_primary, guid);
+
ret = 0;
} else
ret = -1;
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index e9a2827..1e41f46 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1453,7 +1453,6 @@
int n, int width, int height)
{
int c, o;
- struct drm_device *dev = fb_helper->dev;
struct drm_connector *connector;
struct drm_connector_helper_funcs *connector_funcs;
struct drm_encoder *encoder;
@@ -1472,7 +1471,7 @@
if (modes[n] == NULL)
return best_score;
- crtcs = kzalloc(dev->mode_config.num_connector *
+ crtcs = kzalloc(fb_helper->connector_count *
sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
if (!crtcs)
return best_score;
@@ -1518,7 +1517,7 @@
if (score > best_score) {
best_score = score;
memcpy(best_crtcs, crtcs,
- dev->mode_config.num_connector *
+ fb_helper->connector_count *
sizeof(struct drm_fb_helper_crtc *));
}
}
@@ -1638,7 +1637,7 @@
* RETURNS:
* Zero if everything went ok, nonzero otherwise.
*/
-bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
+int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
{
struct drm_device *dev = fb_helper->dev;
int count = 0;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 4c9f972..ba64d23 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -141,7 +141,7 @@
return 0;
err:
- list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
+ list_for_each_entry_continue_reverse(subdrv, &exynos_drm_subdrv_list, list) {
if (subdrv->close)
subdrv->close(dev, subdrv->dev, file);
}
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index ddd90dd..2d42ce6 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -593,6 +593,7 @@
{
struct psb_fbdev *fbdev;
struct drm_psb_private *dev_priv = dev->dev_private;
+ int ret;
fbdev = kzalloc(sizeof(struct psb_fbdev), GFP_KERNEL);
if (!fbdev) {
@@ -604,16 +605,29 @@
drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
- drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
- INTELFB_CONN_LIMIT);
+ ret = drm_fb_helper_init(dev, &fbdev->psb_fb_helper,
+ dev_priv->ops->crtcs, INTELFB_CONN_LIMIT);
+ if (ret)
+ goto free;
- drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper);
+ ret = drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper);
+ if (ret)
+ goto fini;
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
- drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32);
+ ret = drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32);
+ if (ret)
+ goto fini;
+
return 0;
+
+fini:
+ drm_fb_helper_fini(&fbdev->psb_fb_helper);
+free:
+ kfree(fbdev);
+ return ret;
}
static void psb_fbdev_fini(struct drm_device *dev)
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
index 87885d8..4869117 100644
--- a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
@@ -85,7 +85,7 @@
"RX Prot Violation",
"HS Generic Write FIFO Full",
"LP Generic Write FIFO Full",
- "Generic Read Data Avail"
+ "Generic Read Data Avail",
"Special Packet Sent",
"Tearing Effect",
};
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 6ec3a90..bdba0c1 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -464,6 +464,9 @@
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = psb_unlocked_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = drm_compat_ioctl,
+#endif
.mmap = drm_gem_mmap,
.poll = drm_poll,
.read = drm_read,
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 063b448..ccc1770 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -141,10 +141,11 @@
obj->madv == I915_MADV_DONTNEED ? " purgeable" : "");
if (obj->base.name)
seq_printf(m, " (name: %d)", obj->base.name);
- list_for_each_entry(vma, &obj->vma_list, vma_link)
+ list_for_each_entry(vma, &obj->vma_list, vma_link) {
if (vma->pin_count > 0)
pin_count++;
- seq_printf(m, " (pinned x %d)", pin_count);
+ }
+ seq_printf(m, " (pinned x %d)", pin_count);
if (obj->pin_display)
seq_printf(m, " (display)");
if (obj->fence_reg != I915_FENCE_REG_NONE)
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 728938f..b92844a 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -1745,6 +1745,7 @@
struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
ppgtt->base.cleanup(&ppgtt->base);
+ kfree(ppgtt);
}
if (drm_mm_initialized(&vm->mm)) {
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 9212e65..f0e8e2a 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -311,8 +311,14 @@
pipe_config->has_pch_encoder = true;
/* LPT FDI RX only supports 8bpc. */
- if (HAS_PCH_LPT(dev))
+ if (HAS_PCH_LPT(dev)) {
+ if (pipe_config->bw_constrained && pipe_config->pipe_bpp < 24) {
+ DRM_DEBUG_KMS("LPT only supports 24bpp\n");
+ return false;
+ }
+
pipe_config->pipe_bpp = 24;
+ }
/* FDI must always be 2.7 GHz */
if (HAS_DDI(dev)) {
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index b63d4fa..4b476aa 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -1515,12 +1515,6 @@
hsw_ddi_clock_get(encoder, pipe_config);
}
-static void intel_ddi_destroy(struct drm_encoder *encoder)
-{
- /* HDMI has nothing special to destroy, so we can go with this. */
- intel_dp_encoder_destroy(encoder);
-}
-
static bool intel_ddi_compute_config(struct intel_encoder *encoder,
struct intel_crtc_config *pipe_config)
{
@@ -1539,7 +1533,8 @@
}
static const struct drm_encoder_funcs intel_ddi_funcs = {
- .destroy = intel_ddi_destroy,
+ .reset = intel_dp_encoder_reset,
+ .destroy = intel_dp_encoder_destroy,
};
static struct intel_connector *
@@ -1612,6 +1607,7 @@
intel_encoder->post_disable = intel_ddi_post_disable;
intel_encoder->get_hw_state = intel_ddi_get_hw_state;
intel_encoder->get_config = intel_ddi_get_config;
+ intel_encoder->suspend = intel_dp_encoder_suspend;
intel_dig_port->port = port;
intel_dig_port->saved_port_bits = I915_READ(DDI_BUF_CTL(port)) &
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 3104d06..a915d72 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -4423,7 +4423,7 @@
kfree(intel_dig_port);
}
-static void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
+void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
@@ -4440,9 +4440,52 @@
pps_unlock(intel_dp);
}
-static void intel_dp_encoder_reset(struct drm_encoder *encoder)
+static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
{
- intel_edp_panel_vdd_sanitize(to_intel_encoder(encoder));
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct drm_device *dev = intel_dig_port->base.base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ enum intel_display_power_domain power_domain;
+
+ lockdep_assert_held(&dev_priv->pps_mutex);
+
+ if (!edp_have_panel_vdd(intel_dp))
+ return;
+
+ /*
+ * The VDD bit needs a power domain reference, so if the bit is
+ * already enabled when we boot or resume, grab this reference and
+ * schedule a vdd off, so we don't hold on to the reference
+ * indefinitely.
+ */
+ DRM_DEBUG_KMS("VDD left on by BIOS, adjusting state tracking\n");
+ power_domain = intel_display_port_power_domain(&intel_dig_port->base);
+ intel_display_power_get(dev_priv, power_domain);
+
+ edp_panel_vdd_schedule_off(intel_dp);
+}
+
+void intel_dp_encoder_reset(struct drm_encoder *encoder)
+{
+ struct intel_dp *intel_dp;
+
+ if (to_intel_encoder(encoder)->type != INTEL_OUTPUT_EDP)
+ return;
+
+ intel_dp = enc_to_intel_dp(encoder);
+
+ pps_lock(intel_dp);
+
+ /*
+ * Read out the current power sequencer assignment,
+ * in case the BIOS did something with it.
+ */
+ if (IS_VALLEYVIEW(encoder->dev))
+ vlv_initial_power_sequencer_setup(intel_dp);
+
+ intel_edp_panel_vdd_sanitize(intel_dp);
+
+ pps_unlock(intel_dp);
}
static const struct drm_connector_funcs intel_dp_connector_funcs = {
@@ -4924,37 +4967,6 @@
return downclock_mode;
}
-void intel_edp_panel_vdd_sanitize(struct intel_encoder *intel_encoder)
-{
- struct drm_device *dev = intel_encoder->base.dev;
- struct drm_i915_private *dev_priv = dev->dev_private;
- struct intel_dp *intel_dp;
- enum intel_display_power_domain power_domain;
-
- if (intel_encoder->type != INTEL_OUTPUT_EDP)
- return;
-
- intel_dp = enc_to_intel_dp(&intel_encoder->base);
-
- pps_lock(intel_dp);
-
- if (!edp_have_panel_vdd(intel_dp))
- goto out;
- /*
- * The VDD bit needs a power domain reference, so if the bit is
- * already enabled when we boot or resume, grab this reference and
- * schedule a vdd off, so we don't hold on to the reference
- * indefinitely.
- */
- DRM_DEBUG_KMS("VDD left on by BIOS, adjusting state tracking\n");
- power_domain = intel_display_port_power_domain(intel_encoder);
- intel_display_power_get(dev_priv, power_domain);
-
- edp_panel_vdd_schedule_off(intel_dp);
- out:
- pps_unlock(intel_dp);
-}
-
static bool intel_edp_init_connector(struct intel_dp *intel_dp,
struct intel_connector *intel_connector,
struct edp_power_seq *power_seq)
@@ -4975,7 +4987,9 @@
if (!is_edp(intel_dp))
return true;
- intel_edp_panel_vdd_sanitize(intel_encoder);
+ pps_lock(intel_dp);
+ intel_edp_panel_vdd_sanitize(intel_dp);
+ pps_unlock(intel_dp);
/* Cache DPCD and EDID for edp. */
intel_edp_panel_vdd_on(intel_dp);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 07ce046..c91a4fb 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -922,6 +922,8 @@
void intel_dp_complete_link_train(struct intel_dp *intel_dp);
void intel_dp_stop_link_train(struct intel_dp *intel_dp);
void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);
+void intel_dp_encoder_reset(struct drm_encoder *encoder);
+void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder);
void intel_dp_encoder_destroy(struct drm_encoder *encoder);
void intel_dp_check_link_status(struct intel_dp *intel_dp);
int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc);
@@ -933,7 +935,6 @@
void intel_edp_backlight_on(struct intel_dp *intel_dp);
void intel_edp_backlight_off(struct intel_dp *intel_dp);
void intel_edp_panel_vdd_on(struct intel_dp *intel_dp);
-void intel_edp_panel_vdd_sanitize(struct intel_encoder *intel_encoder);
void intel_edp_panel_on(struct intel_dp *intel_dp);
void intel_edp_panel_off(struct intel_dp *intel_dp);
void intel_edp_psr_enable(struct intel_dp *intel_dp);
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index c64f194..993df2dd 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2952,6 +2952,8 @@
if (IS_HASWELL(dev) || IS_BROADWELL(dev))
hw->wm_linetime[pipe] = I915_READ(PIPE_WM_LINETIME(pipe));
+ memset(active, 0, sizeof(*active));
+
active->pipe_enabled = intel_crtc_active(crtc);
if (active->pipe_enabled) {
diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
index 4415af3..c36b830 100644
--- a/drivers/gpu/drm/mgag200/mgag200_fb.c
+++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
@@ -303,14 +303,22 @@
if (ret)
return ret;
- drm_fb_helper_single_add_all_connectors(&mfbdev->helper);
+ ret = drm_fb_helper_single_add_all_connectors(&mfbdev->helper);
+ if (ret)
+ goto fini;
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(mdev->dev);
- drm_fb_helper_initial_config(&mfbdev->helper, bpp_sel);
+ ret = drm_fb_helper_initial_config(&mfbdev->helper, bpp_sel);
+ if (ret)
+ goto fini;
return 0;
+
+fini:
+ drm_fb_helper_fini(&mfbdev->helper);
+ return ret;
}
void mgag200_fbdev_fini(struct mga_device *mdev)
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index ab5bfd2..956710dd 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -245,17 +245,23 @@
goto fail;
}
- drm_fb_helper_single_add_all_connectors(helper);
+ ret = drm_fb_helper_single_add_all_connectors(helper);
+ if (ret)
+ goto fini;
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
- drm_fb_helper_initial_config(helper, 32);
+ ret = drm_fb_helper_initial_config(helper, 32);
+ if (ret)
+ goto fini;
priv->fbdev = helper;
return helper;
+fini:
+ drm_fb_helper_fini(helper);
fail:
kfree(fbdev);
return NULL;
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index cd0554f..4ff8c33 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -55,6 +55,14 @@
return submit;
}
+static inline unsigned long __must_check
+copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)
+{
+ if (access_ok(VERIFY_READ, from, n))
+ return __copy_from_user_inatomic(to, from, n);
+ return -EFAULT;
+}
+
static int submit_lookup_objects(struct msm_gem_submit *submit,
struct drm_msm_gem_submit *args, struct drm_file *file)
{
@@ -62,6 +70,7 @@
int ret = 0;
spin_lock(&file->table_lock);
+ pagefault_disable();
for (i = 0; i < args->nr_bos; i++) {
struct drm_msm_gem_submit_bo submit_bo;
@@ -70,10 +79,15 @@
void __user *userptr =
to_user_ptr(args->bos + (i * sizeof(submit_bo)));
- ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo));
- if (ret) {
- ret = -EFAULT;
- goto out_unlock;
+ ret = copy_from_user_inatomic(&submit_bo, userptr, sizeof(submit_bo));
+ if (unlikely(ret)) {
+ pagefault_enable();
+ spin_unlock(&file->table_lock);
+ ret = copy_from_user(&submit_bo, userptr, sizeof(submit_bo));
+ if (ret)
+ goto out;
+ spin_lock(&file->table_lock);
+ pagefault_disable();
}
if (submit_bo.flags & ~MSM_SUBMIT_BO_FLAGS) {
@@ -113,9 +127,12 @@
}
out_unlock:
- submit->nr_bos = i;
+ pagefault_enable();
spin_unlock(&file->table_lock);
+out:
+ submit->nr_bos = i;
+
return ret;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index ddcfc1d..993c9a0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -546,12 +546,12 @@
ret = drm_fb_helper_init(dev, &fbcon->helper,
dev->mode_config.num_crtc, 4);
- if (ret) {
- kfree(fbcon);
- return ret;
- }
+ if (ret)
+ goto free;
- drm_fb_helper_single_add_all_connectors(&fbcon->helper);
+ ret = drm_fb_helper_single_add_all_connectors(&fbcon->helper);
+ if (ret)
+ goto fini;
if (drm->device.info.ram_size <= 32 * 1024 * 1024)
preferred_bpp = 8;
@@ -564,8 +564,19 @@
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
- drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp);
+ ret = drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp);
+ if (ret)
+ goto fini;
+
+ if (fbcon->helper.fbdev)
+ fbcon->helper.fbdev->pixmap.buf_align = 4;
return 0;
+
+fini:
+ drm_fb_helper_fini(&fbcon->helper);
+free:
+ kfree(fbcon);
+ return ret;
}
void
diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c
index 4ef602c..ec97427 100644
--- a/drivers/gpu/drm/nouveau/nv04_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c
@@ -82,7 +82,6 @@
uint32_t fg;
uint32_t bg;
uint32_t dsize;
- uint32_t width;
uint32_t *data = (uint32_t *)image->data;
int ret;
@@ -93,9 +92,6 @@
if (ret)
return ret;
- width = ALIGN(image->width, 8);
- dsize = ALIGN(width * image->height, 32) >> 5;
-
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
fg = ((uint32_t *) info->pseudo_palette)[image->fg_color];
@@ -111,10 +107,11 @@
((image->dx + image->width) & 0xffff));
OUT_RING(chan, bg);
OUT_RING(chan, fg);
- OUT_RING(chan, (image->height << 16) | width);
+ OUT_RING(chan, (image->height << 16) | ALIGN(image->width, 8));
OUT_RING(chan, (image->height << 16) | image->width);
OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
+ dsize = ALIGN(ALIGN(image->width, 8) * image->height, 32) >> 5;
while (dsize) {
int iter_len = dsize > 128 ? 128 : dsize;
diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
index 394c89a..8462f72 100644
--- a/drivers/gpu/drm/nouveau/nv50_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
@@ -95,7 +95,7 @@
struct nouveau_fbdev *nfbdev = info->par;
struct nouveau_drm *drm = nouveau_drm(nfbdev->dev);
struct nouveau_channel *chan = drm->channel;
- uint32_t width, dwords, *data = (uint32_t *)image->data;
+ uint32_t dwords, *data = (uint32_t *)image->data;
uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
uint32_t *palette = info->pseudo_palette;
int ret;
@@ -107,9 +107,6 @@
if (ret)
return ret;
- width = ALIGN(image->width, 32);
- dwords = (width * image->height) >> 5;
-
BEGIN_NV04(chan, NvSub2D, 0x0814, 2);
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
@@ -128,6 +125,7 @@
OUT_RING(chan, 0);
OUT_RING(chan, image->dy);
+ dwords = ALIGN(ALIGN(image->width, 8) * image->height, 32) >> 5;
while (dwords) {
int push = dwords > 2047 ? 2047 : dwords;
diff --git a/drivers/gpu/drm/nouveau/nvc0_fbcon.c b/drivers/gpu/drm/nouveau/nvc0_fbcon.c
index 6124667..9055242 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fbcon.c
@@ -95,7 +95,7 @@
struct nouveau_fbdev *nfbdev = info->par;
struct nouveau_drm *drm = nouveau_drm(nfbdev->dev);
struct nouveau_channel *chan = drm->channel;
- uint32_t width, dwords, *data = (uint32_t *)image->data;
+ uint32_t dwords, *data = (uint32_t *)image->data;
uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
uint32_t *palette = info->pseudo_palette;
int ret;
@@ -107,9 +107,6 @@
if (ret)
return ret;
- width = ALIGN(image->width, 32);
- dwords = (width * image->height) >> 5;
-
BEGIN_NVC0(chan, NvSub2D, 0x0814, 2);
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
@@ -128,6 +125,7 @@
OUT_RING (chan, 0);
OUT_RING (chan, image->dy);
+ dwords = ALIGN(ALIGN(image->width, 8) * image->height, 32) >> 5;
while (dwords) {
int push = dwords > 2047 ? 2047 : dwords;
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index 8436c685..d292d24 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -334,17 +334,23 @@
goto fail;
}
- drm_fb_helper_single_add_all_connectors(helper);
+ ret = drm_fb_helper_single_add_all_connectors(helper);
+ if (ret)
+ goto fini;
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
- drm_fb_helper_initial_config(helper, 32);
+ ret = drm_fb_helper_initial_config(helper, 32);
+ if (ret)
+ goto fini;
priv->fbdev = helper;
return helper;
+fini:
+ drm_fb_helper_fini(helper);
fail:
kfree(fbdev);
return NULL;
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index 011b228..8e6e73c 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -343,10 +343,15 @@
qxl_bo_kunmap(user_bo);
+ qcrtc->cur_x += qcrtc->hot_spot_x - hot_x;
+ qcrtc->cur_y += qcrtc->hot_spot_y - hot_y;
+ qcrtc->hot_spot_x = hot_x;
+ qcrtc->hot_spot_y = hot_y;
+
cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
cmd->type = QXL_CURSOR_SET;
- cmd->u.set.position.x = qcrtc->cur_x;
- cmd->u.set.position.y = qcrtc->cur_y;
+ cmd->u.set.position.x = qcrtc->cur_x + qcrtc->hot_spot_x;
+ cmd->u.set.position.y = qcrtc->cur_y + qcrtc->hot_spot_y;
cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0);
@@ -409,8 +414,8 @@
cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
cmd->type = QXL_CURSOR_MOVE;
- cmd->u.position.x = qcrtc->cur_x;
- cmd->u.position.y = qcrtc->cur_y;
+ cmd->u.position.x = qcrtc->cur_x + qcrtc->hot_spot_x;
+ cmd->u.position.y = qcrtc->cur_y + qcrtc->hot_spot_y;
qxl_release_unmap(qdev, release, &cmd->release_info);
qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h
index e66143c..eef6676 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.h
+++ b/drivers/gpu/drm/qxl/qxl_drv.h
@@ -135,6 +135,8 @@
int index;
int cur_x;
int cur_y;
+ int hot_spot_x;
+ int hot_spot_y;
};
struct qxl_output {
diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
index 3d7c1d0..f778c0e 100644
--- a/drivers/gpu/drm/qxl/qxl_fb.c
+++ b/drivers/gpu/drm/qxl/qxl_fb.c
@@ -686,14 +686,24 @@
ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
qxl_num_crtc /* num_crtc - QXL supports just 1 */,
QXLFB_CONN_LIMIT);
- if (ret) {
- kfree(qfbdev);
- return ret;
- }
+ if (ret)
+ goto free;
- drm_fb_helper_single_add_all_connectors(&qfbdev->helper);
- drm_fb_helper_initial_config(&qfbdev->helper, bpp_sel);
+ ret = drm_fb_helper_single_add_all_connectors(&qfbdev->helper);
+ if (ret)
+ goto fini;
+
+ ret = drm_fb_helper_initial_config(&qfbdev->helper, bpp_sel);
+ if (ret)
+ goto fini;
+
return 0;
+
+fini:
+ drm_fb_helper_fini(&qfbdev->helper);
+free:
+ kfree(qfbdev);
+ return ret;
}
void qxl_fbdev_fini(struct qxl_device *qdev)
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index ce8cab5..e0780bf 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -586,7 +586,8 @@
if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev) || ASIC_IS_DCE8(rdev))
radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV;
/* use frac fb div on RS780/RS880 */
- if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880))
+ if (((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880))
+ && !radeon_crtc->ss_enabled)
radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV;
if (ASIC_IS_DCE32(rdev) && mode->clock > 165000)
radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV;
@@ -616,7 +617,9 @@
if (radeon_crtc->ss.refdiv) {
radeon_crtc->pll_flags |= RADEON_PLL_USE_REF_DIV;
radeon_crtc->pll_reference_div = radeon_crtc->ss.refdiv;
- if (ASIC_IS_AVIVO(rdev))
+ if (ASIC_IS_AVIVO(rdev) &&
+ rdev->family != CHIP_RS780 &&
+ rdev->family != CHIP_RS880)
radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV;
}
}
@@ -1730,6 +1733,7 @@
static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
struct drm_crtc *test_crtc;
struct radeon_crtc *test_radeon_crtc;
@@ -1739,6 +1743,10 @@
test_radeon_crtc = to_radeon_crtc(test_crtc);
if (test_radeon_crtc->encoder &&
ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_radeon_crtc->encoder))) {
+ /* PPLL2 is exclusive to UNIPHYA on DCE61 */
+ if (ASIC_IS_DCE61(rdev) && !ASIC_IS_DCE8(rdev) &&
+ test_radeon_crtc->pll_id == ATOM_PPLL2)
+ continue;
/* for DP use the same PLL for all */
if (test_radeon_crtc->pll_id != ATOM_PPLL_INVALID)
return test_radeon_crtc->pll_id;
@@ -1760,6 +1768,7 @@
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
struct drm_crtc *test_crtc;
struct radeon_crtc *test_radeon_crtc;
u32 adjusted_clock, test_adjusted_clock;
@@ -1775,6 +1784,10 @@
test_radeon_crtc = to_radeon_crtc(test_crtc);
if (test_radeon_crtc->encoder &&
!ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_radeon_crtc->encoder))) {
+ /* PPLL2 is exclusive to UNIPHYA on DCE61 */
+ if (ASIC_IS_DCE61(rdev) && !ASIC_IS_DCE8(rdev) &&
+ test_radeon_crtc->pll_id == ATOM_PPLL2)
+ continue;
/* check if we are already driving this connector with another crtc */
if (test_radeon_crtc->connector == radeon_crtc->connector) {
/* if we are, return that pll */
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index 0170110..e809d25 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -119,6 +119,7 @@
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
if (dig->backlight_level == 0)
atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_LCD_BLOFF, 0, 0);
else {
@@ -308,6 +309,10 @@
&& (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2)))
adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2;
+ /* vertical FP must be at least 1 */
+ if (mode->crtc_vsync_start == mode->crtc_vdisplay)
+ adjusted_mode->crtc_vsync_start++;
+
/* get the native mode for scaling */
if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
radeon_panel_mode_fixup(encoder, adjusted_mode);
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index a019ba2..3e8a0d6 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -1339,9 +1339,7 @@
void cayman_cp_int_cntl_setup(struct radeon_device *rdev,
int ring, u32 cp_int_cntl)
{
- u32 srbm_gfx_cntl = RREG32(SRBM_GFX_CNTL) & ~3;
-
- WREG32(SRBM_GFX_CNTL, srbm_gfx_cntl | (ring & 3));
+ WREG32(SRBM_GFX_CNTL, RINGID(ring));
WREG32(CP_INT_CNTL, cp_int_cntl);
}
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c
index 65a0c1c..fdac8d3 100644
--- a/drivers/gpu/drm/radeon/r600_dpm.c
+++ b/drivers/gpu/drm/radeon/r600_dpm.c
@@ -156,19 +156,20 @@
struct drm_device *dev = rdev->ddev;
struct drm_crtc *crtc;
struct radeon_crtc *radeon_crtc;
- u32 line_time_us, vblank_lines;
+ u32 vblank_in_pixels;
u32 vblank_time_us = 0xffffffff; /* if the displays are off, vblank time is max */
if (rdev->num_crtc && rdev->mode_info.mode_config_initialized) {
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
radeon_crtc = to_radeon_crtc(crtc);
if (crtc->enabled && radeon_crtc->enabled && radeon_crtc->hw_mode.clock) {
- line_time_us = (radeon_crtc->hw_mode.crtc_htotal * 1000) /
- radeon_crtc->hw_mode.clock;
- vblank_lines = radeon_crtc->hw_mode.crtc_vblank_end -
- radeon_crtc->hw_mode.crtc_vdisplay +
- (radeon_crtc->v_border * 2);
- vblank_time_us = vblank_lines * line_time_us;
+ vblank_in_pixels =
+ radeon_crtc->hw_mode.crtc_htotal *
+ (radeon_crtc->hw_mode.crtc_vblank_end -
+ radeon_crtc->hw_mode.crtc_vdisplay +
+ (radeon_crtc->v_border * 2));
+
+ vblank_time_us = vblank_in_pixels * 1000 / radeon_crtc->hw_mode.clock;
break;
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 882171b..ad0f9c7 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -1128,7 +1128,7 @@
le16_to_cpu(firmware_info->info.usReferenceClock);
p1pll->reference_div = 0;
- if (crev < 2)
+ if ((frev < 2) && (crev < 2))
p1pll->pll_out_min =
le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Output);
else
@@ -1137,7 +1137,7 @@
p1pll->pll_out_max =
le32_to_cpu(firmware_info->info.ulMaxPixelClockPLL_Output);
- if (crev >= 4) {
+ if (((frev < 2) && (crev >= 4)) || (frev >= 2)) {
p1pll->lcd_pll_out_min =
le16_to_cpu(firmware_info->info_14.usLcdMinPixelClockPLL_Output) * 100;
if (p1pll->lcd_pll_out_min == 0)
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
index 1523cf9..868247c 100644
--- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
+++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
@@ -10,6 +10,7 @@
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/pci.h>
+#include <linux/delay.h>
#include "radeon_acpi.h"
@@ -62,10 +63,6 @@
return radeon_atpx_priv.atpx_detected;
}
-bool radeon_has_atpx_dgpu_power_cntl(void) {
- return radeon_atpx_priv.atpx.functions.power_cntl;
-}
-
/**
* radeon_atpx_call - call an ATPX method
*
@@ -145,6 +142,10 @@
*/
static int radeon_atpx_validate(struct radeon_atpx *atpx)
{
+ /* make sure required functions are enabled */
+ /* dGPU power control is required */
+ atpx->functions.power_cntl = true;
+
if (atpx->functions.px_params) {
union acpi_object *info;
struct atpx_px_params output;
@@ -255,6 +256,10 @@
if (!info)
return -EIO;
kfree(info);
+
+ /* 200ms delay is required after off */
+ if (state == 0)
+ msleep(200);
}
return 0;
}
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 15f0906..4f686bb 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -1949,7 +1949,6 @@
DRM_MODE_SCALE_NONE);
/* no HPD on analog connectors */
radeon_connector->hpd.hpd = RADEON_HPD_NONE;
- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
connector->interlace_allowed = true;
connector->doublescan_allowed = true;
break;
@@ -2180,8 +2179,10 @@
}
if (radeon_connector->hpd.hpd == RADEON_HPD_NONE) {
- if (i2c_bus->valid)
- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+ if (i2c_bus->valid) {
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+ }
} else
connector->polled = DRM_CONNECTOR_POLL_HPD;
@@ -2257,7 +2258,6 @@
1);
/* no HPD on analog connectors */
radeon_connector->hpd.hpd = RADEON_HPD_NONE;
- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
connector->interlace_allowed = true;
connector->doublescan_allowed = true;
break;
@@ -2342,10 +2342,13 @@
}
if (radeon_connector->hpd.hpd == RADEON_HPD_NONE) {
- if (i2c_bus->valid)
- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+ if (i2c_bus->valid) {
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+ DRM_CONNECTOR_POLL_DISCONNECT;
+ }
} else
connector->polled = DRM_CONNECTOR_POLL_HPD;
+
connector->display_info.subpixel_order = subpixel_order;
drm_connector_register(connector);
}
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index eb5f88a..6b99d39 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -103,12 +103,6 @@
"LAST",
};
-#if defined(CONFIG_VGA_SWITCHEROO)
-bool radeon_has_atpx_dgpu_power_cntl(void);
-#else
-static inline bool radeon_has_atpx_dgpu_power_cntl(void) { return false; }
-#endif
-
#define RADEON_PX_QUIRK_DISABLE_PX (1 << 0)
#define RADEON_PX_QUIRK_LONG_WAKEUP (1 << 1)
@@ -605,6 +599,23 @@
/*
* GPU helpers function.
*/
+
+/**
+ * radeon_device_is_virtual - check if we are running is a virtual environment
+ *
+ * Check if the asic has been passed through to a VM (all asics).
+ * Used at driver startup.
+ * Returns true if virtual or false if not.
+ */
+static bool radeon_device_is_virtual(void)
+{
+#ifdef CONFIG_X86
+ return boot_cpu_has(X86_FEATURE_HYPERVISOR);
+#else
+ return false;
+#endif
+}
+
/**
* radeon_card_posted - check if the hw has already been initialized
*
@@ -618,6 +629,10 @@
{
uint32_t reg;
+ /* for pass through, always force asic_init */
+ if (radeon_device_is_virtual())
+ return false;
+
/* required for EFI mode on macbook2,1 which uses an r5xx asic */
if (efi_enabled(EFI_BOOT) &&
(rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE) &&
@@ -1401,7 +1416,7 @@
* ignore it */
vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
- if ((rdev->flags & RADEON_IS_PX) && radeon_has_atpx_dgpu_power_cntl())
+ if (rdev->flags & RADEON_IS_PX)
runtime = true;
vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, runtime);
if (runtime)
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index a5dc86a..6514ff2 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -365,18 +365,27 @@
ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
rdev->num_crtc,
RADEONFB_CONN_LIMIT);
- if (ret) {
- kfree(rfbdev);
- return ret;
- }
+ if (ret)
+ goto free;
- drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
+ ret = drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
+ if (ret)
+ goto fini;
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(rdev->ddev);
- drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
+ ret = drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
+ if (ret)
+ goto fini;
+
return 0;
+
+fini:
+ drm_fb_helper_fini(&rfbdev->helper);
+free:
+ kfree(rfbdev);
+ return ret;
}
void radeon_fbdev_fini(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index 33928b7..f361af7 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -212,6 +212,8 @@
{
struct radeon_bo *rbo = container_of(bo, struct radeon_bo, tbo);
+ if (radeon_ttm_tt_has_userptr(bo->ttm))
+ return -EPERM;
return drm_vma_node_verify_access(&rbo->gem_base.vma_node, filp);
}
@@ -238,8 +240,8 @@
rdev = radeon_get_rdev(bo->bdev);
ridx = radeon_copy_ring_index(rdev);
- old_start = old_mem->start << PAGE_SHIFT;
- new_start = new_mem->start << PAGE_SHIFT;
+ old_start = (u64)old_mem->start << PAGE_SHIFT;
+ new_start = (u64)new_mem->start << PAGE_SHIFT;
switch (old_mem->mem_type) {
case TTM_PL_VRAM:
diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c
index c6ad8a9..e684508 100644
--- a/drivers/gpu/drm/radeon/si_dpm.c
+++ b/drivers/gpu/drm/radeon/si_dpm.c
@@ -2922,7 +2922,12 @@
/* PITCAIRN - https://bugs.freedesktop.org/show_bug.cgi?id=76490 */
{ PCI_VENDOR_ID_ATI, 0x6810, 0x1462, 0x3036, 0, 120000 },
{ PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0xe271, 0, 120000 },
+ { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0x2015, 0, 120000 },
{ PCI_VENDOR_ID_ATI, 0x6810, 0x174b, 0xe271, 85000, 90000 },
+ { PCI_VENDOR_ID_ATI, 0x6811, 0x1762, 0x2015, 0, 120000 },
+ { PCI_VENDOR_ID_ATI, 0x6811, 0x1043, 0x2015, 0, 120000 },
+ { PCI_VENDOR_ID_ATI, 0x6811, 0x148c, 0x2015, 0, 120000 },
+ { PCI_VENDOR_ID_ATI, 0x6810, 0x1682, 0x9275, 0, 120000 },
{ 0, 0, 0, 0 },
};
@@ -2940,6 +2945,50 @@
int i;
struct si_dpm_quirk *p = si_dpm_quirk_list;
+ /* limit all SI kickers */
+ if (rdev->family == CHIP_PITCAIRN) {
+ if ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->device == 0x6810) ||
+ (rdev->pdev->device == 0x6811) ||
+ (rdev->pdev->device == 0x6816) ||
+ (rdev->pdev->device == 0x6817) ||
+ (rdev->pdev->device == 0x6806))
+ max_mclk = 120000;
+ } else if (rdev->family == CHIP_VERDE) {
+ if ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0x87) ||
+ (rdev->pdev->device == 0x6820) ||
+ (rdev->pdev->device == 0x6821) ||
+ (rdev->pdev->device == 0x6822) ||
+ (rdev->pdev->device == 0x6823) ||
+ (rdev->pdev->device == 0x682A) ||
+ (rdev->pdev->device == 0x682B)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ } else if (rdev->family == CHIP_OLAND) {
+ if ((rdev->pdev->revision == 0xC7) ||
+ (rdev->pdev->revision == 0x80) ||
+ (rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0x87) ||
+ (rdev->pdev->device == 0x6604) ||
+ (rdev->pdev->device == 0x6605)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ } else if (rdev->family == CHIP_HAINAN) {
+ if ((rdev->pdev->revision == 0x81) ||
+ (rdev->pdev->revision == 0x83) ||
+ (rdev->pdev->revision == 0xC3) ||
+ (rdev->pdev->device == 0x6664) ||
+ (rdev->pdev->device == 0x6665) ||
+ (rdev->pdev->device == 0x6667)) {
+ max_sclk = 75000;
+ max_mclk = 80000;
+ }
+ }
/* Apply dpm quirks */
while (p && p->chip_device != 0) {
if (rdev->pdev->vendor == p->chip_vendor &&
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index d395b0b..4ca3579 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1000,9 +1000,9 @@
return ret;
}
-static bool ttm_bo_mem_compat(struct ttm_placement *placement,
- struct ttm_mem_reg *mem,
- uint32_t *new_flags)
+bool ttm_bo_mem_compat(struct ttm_placement *placement,
+ struct ttm_mem_reg *mem,
+ uint32_t *new_flags)
{
int i;
@@ -1034,6 +1034,7 @@
return false;
}
+EXPORT_SYMBOL(ttm_bo_mem_compat);
int ttm_bo_validate(struct ttm_buffer_object *bo,
struct ttm_placement *placement,
@@ -1616,7 +1617,6 @@
struct ttm_buffer_object *bo;
int ret = -EBUSY;
int put_count;
- uint32_t swap_placement = (TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM);
spin_lock(&glob->lru_lock);
list_for_each_entry(bo, &glob->swap_lru, swap) {
@@ -1652,7 +1652,8 @@
if (unlikely(ret != 0))
goto out;
- if ((bo->mem.placement & swap_placement) != swap_placement) {
+ if (bo->mem.mem_type != TTM_PL_SYSTEM ||
+ bo->ttm->caching_state != tt_cached) {
struct ttm_mem_reg evict_mem;
evict_mem = bo->mem;
diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
index 12c8711..bc3cd30 100644
--- a/drivers/gpu/drm/ttm/ttm_object.c
+++ b/drivers/gpu/drm/ttm/ttm_object.c
@@ -179,7 +179,7 @@
if (unlikely(ret != 0))
goto out_err0;
- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, false);
if (unlikely(ret != 0))
goto out_err1;
@@ -318,7 +318,8 @@
int ttm_ref_object_add(struct ttm_object_file *tfile,
struct ttm_base_object *base,
- enum ttm_ref_type ref_type, bool *existed)
+ enum ttm_ref_type ref_type, bool *existed,
+ bool require_existed)
{
struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
struct ttm_ref_object *ref;
@@ -345,6 +346,9 @@
}
rcu_read_unlock();
+ if (require_existed)
+ return -EPERM;
+
ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref),
false, false);
if (unlikely(ret != 0))
@@ -635,7 +639,7 @@
prime = (struct ttm_prime_object *) dma_buf->priv;
base = &prime->base;
*handle = base->hash.key;
- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL, false);
dma_buf_put(dma_buf);
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
index 8cbcb45..cd8d183 100644
--- a/drivers/gpu/drm/udl/udl_fb.c
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -546,7 +546,7 @@
return ret;
out_gfree:
- drm_gem_object_unreference(&ufbdev->ufb.obj->base);
+ drm_gem_object_unreference_unlocked(&ufbdev->ufb.obj->base);
out:
return ret;
}
@@ -589,19 +589,27 @@
ret = drm_fb_helper_init(dev, &ufbdev->helper,
1, 1);
- if (ret) {
- kfree(ufbdev);
- return ret;
+ if (ret)
+ goto free;
- }
-
- drm_fb_helper_single_add_all_connectors(&ufbdev->helper);
+ ret = drm_fb_helper_single_add_all_connectors(&ufbdev->helper);
+ if (ret)
+ goto fini;
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
- drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel);
+ ret = drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel);
+ if (ret)
+ goto fini;
+
return 0;
+
+fini:
+ drm_fb_helper_fini(&ufbdev->helper);
+free:
+ kfree(ufbdev);
+ return ret;
}
void udl_fbdev_cleanup(struct drm_device *dev)
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index 8044f5f..6de963b 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -51,7 +51,7 @@
return ret;
}
- drm_gem_object_unreference(&obj->base);
+ drm_gem_object_unreference_unlocked(&obj->base);
*handle_p = handle;
return 0;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index 945f1e0..540b23b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -539,7 +539,7 @@
struct vmw_fence_obj **p_fence)
{
struct vmw_fence_obj *fence;
- int ret;
+ int ret;
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
if (unlikely(fence == NULL))
@@ -702,6 +702,41 @@
}
+/**
+ * vmw_fence_obj_lookup - Look up a user-space fence object
+ *
+ * @tfile: A struct ttm_object_file identifying the caller.
+ * @handle: A handle identifying the fence object.
+ * @return: A struct vmw_user_fence base ttm object on success or
+ * an error pointer on failure.
+ *
+ * The fence object is looked up and type-checked. The caller needs
+ * to have opened the fence object first, but since that happens on
+ * creation and fence objects aren't shareable, that's not an
+ * issue currently.
+ */
+static struct ttm_base_object *
+vmw_fence_obj_lookup(struct ttm_object_file *tfile, u32 handle)
+{
+ struct ttm_base_object *base = ttm_base_object_lookup(tfile, handle);
+
+ if (!base) {
+ pr_err("Invalid fence object handle 0x%08lx.\n",
+ (unsigned long)handle);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (base->refcount_release != vmw_user_fence_base_release) {
+ pr_err("Invalid fence object handle 0x%08lx.\n",
+ (unsigned long)handle);
+ ttm_base_object_unref(&base);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return base;
+}
+
+
int vmw_fence_obj_wait_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -727,13 +762,9 @@
arg->kernel_cookie = jiffies + wait_timeout;
}
- base = ttm_base_object_lookup(tfile, arg->handle);
- if (unlikely(base == NULL)) {
- printk(KERN_ERR "Wait invalid fence object handle "
- "0x%08lx.\n",
- (unsigned long)arg->handle);
- return -EINVAL;
- }
+ base = vmw_fence_obj_lookup(tfile, arg->handle);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
fence = &(container_of(base, struct vmw_user_fence, base)->fence);
@@ -772,13 +803,9 @@
struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
struct vmw_private *dev_priv = vmw_priv(dev);
- base = ttm_base_object_lookup(tfile, arg->handle);
- if (unlikely(base == NULL)) {
- printk(KERN_ERR "Fence signaled invalid fence object handle "
- "0x%08lx.\n",
- (unsigned long)arg->handle);
- return -EINVAL;
- }
+ base = vmw_fence_obj_lookup(tfile, arg->handle);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
fence = &(container_of(base, struct vmw_user_fence, base)->fence);
fman = fman_from_fence(fence);
@@ -1093,6 +1120,7 @@
(struct drm_vmw_fence_event_arg *) data;
struct vmw_fence_obj *fence = NULL;
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
+ struct ttm_object_file *tfile = vmw_fp->tfile;
struct drm_vmw_fence_rep __user *user_fence_rep =
(struct drm_vmw_fence_rep __user *)(unsigned long)
arg->fence_rep;
@@ -1106,24 +1134,18 @@
*/
if (arg->handle) {
struct ttm_base_object *base =
- ttm_base_object_lookup_for_ref(dev_priv->tdev,
- arg->handle);
+ vmw_fence_obj_lookup(tfile, arg->handle);
- if (unlikely(base == NULL)) {
- DRM_ERROR("Fence event invalid fence object handle "
- "0x%08lx.\n",
- (unsigned long)arg->handle);
- return -EINVAL;
- }
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
fence = &(container_of(base, struct vmw_user_fence,
base)->fence);
(void) vmw_fence_obj_reference(fence);
if (user_fence_rep != NULL) {
- bool existed;
-
ret = ttm_ref_object_add(vmw_fp->tfile, base,
- TTM_REF_USAGE, &existed);
+ TTM_REF_USAGE, NULL, false);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed to reference a fence "
"object.\n");
@@ -1166,8 +1188,7 @@
return 0;
out_no_create:
if (user_fence_rep != NULL)
- ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
- handle, TTM_REF_USAGE);
+ ttm_ref_object_base_unref(tfile, handle, TTM_REF_USAGE);
out_no_ref_obj:
vmw_fence_obj_unreference(&fence);
return ret;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
index 69c8ce23..df2fb96 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
@@ -106,8 +106,6 @@
param->value = dev_priv->max_mob_size;
break;
default:
- DRM_ERROR("Illegal vmwgfx get param request: %d\n",
- param->param);
return -EINVAL;
}
@@ -161,7 +159,7 @@
bool gb_objects = !!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS);
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
- if (unlikely(arg->pad64 != 0)) {
+ if (unlikely(arg->pad64 != 0 || arg->max_size == 0)) {
DRM_ERROR("Illegal GET_3D_CAP argument.\n");
return -EINVAL;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 026de7c..8f32d7c 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -588,7 +588,7 @@
return ret;
ret = ttm_ref_object_add(tfile, &user_bo->prime.base,
- TTM_REF_SYNCCPU_WRITE, &existed);
+ TTM_REF_SYNCCPU_WRITE, &existed, false);
if (ret != 0 || existed)
ttm_bo_synccpu_write_release(&user_bo->dma.base);
@@ -764,7 +764,7 @@
*handle = user_bo->prime.base.hash.key;
return ttm_ref_object_add(tfile, &user_bo->prime.base,
- TTM_REF_USAGE, NULL);
+ TTM_REF_USAGE, NULL, false);
}
/*
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index 4ecdbf3..75c3c2a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -708,11 +708,14 @@
128;
num_sizes = 0;
- for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
+ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
+ if (req->mip_levels[i] > DRM_VMW_MAX_MIP_LEVELS)
+ return -EINVAL;
num_sizes += req->mip_levels[i];
+ }
- if (num_sizes > DRM_VMW_MAX_SURFACE_FACES *
- DRM_VMW_MAX_MIP_LEVELS)
+ if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * DRM_VMW_MAX_MIP_LEVELS ||
+ num_sizes == 0)
return -EINVAL;
size = vmw_user_surface_size + 128 +
@@ -895,17 +898,16 @@
uint32_t handle;
struct ttm_base_object *base;
int ret;
+ bool require_exist = false;
if (handle_type == DRM_VMW_HANDLE_PRIME) {
ret = ttm_prime_fd_to_handle(tfile, u_handle, &handle);
if (unlikely(ret != 0))
return ret;
} else {
- if (unlikely(drm_is_render_client(file_priv))) {
- DRM_ERROR("Render client refused legacy "
- "surface reference.\n");
- return -EACCES;
- }
+ if (unlikely(drm_is_render_client(file_priv)))
+ require_exist = true;
+
handle = u_handle;
}
@@ -927,17 +929,14 @@
/*
* Make sure the surface creator has the same
- * authenticating master.
+ * authenticating master, or is already registered with us.
*/
if (drm_is_primary_client(file_priv) &&
- user_srf->master != file_priv->master) {
- DRM_ERROR("Trying to reference surface outside of"
- " master domain.\n");
- ret = -EACCES;
- goto out_bad_resource;
- }
+ user_srf->master != file_priv->master)
+ require_exist = true;
- ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
+ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL,
+ require_exist);
if (unlikely(ret != 0)) {
DRM_ERROR("Could not add a reference to a surface.\n");
goto out_bad_resource;
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index c24d4573..3154931 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -14,6 +14,7 @@
#include <linux/export.h>
#include <linux/vmalloc.h>
#include <asm/cacheflush.h>
+#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/kmemleak.h>
#include <linux/highmem.h>
@@ -598,6 +599,9 @@
* are not aligned to the cacheline size correctly.
*/
+ if (!memdesc->hostptr)
+ uaccess_enable_not_uao();
+
switch (_fixup_cache_range_op(op)) {
case KGSL_CACHE_OP_FLUSH:
dmac_flush_range(addr, addr + (size_t) size);
@@ -610,6 +614,9 @@
break;
}
+ if (!memdesc->hostptr)
+ uaccess_disable_not_uao();
+
return 0;
}
EXPORT_SYMBOL(kgsl_cache_range_op);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 94fdaeb8..34dda44 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1837,6 +1837,7 @@
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c
index aad8c16..0cd4f72 100644
--- a/drivers/hid/hid-elo.c
+++ b/drivers/hid/hid-elo.c
@@ -261,7 +261,7 @@
struct elo_priv *priv = hid_get_drvdata(hdev);
hid_hw_stop(hdev);
- flush_workqueue(wq);
+ cancel_delayed_work_sync(&priv->work);
kfree(priv);
}
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 984e43c..213f616 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -168,6 +168,7 @@
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
#define USB_DEVICE_ID_ATEN_CS682 0x2213
+#define USB_DEVICE_ID_ATEN_CS692 0x8021
#define USB_VENDOR_ID_ATMEL 0x03eb
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 1def4bb..8319b9a 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1122,7 +1122,7 @@
return;
/* report the usage code as scancode if the key status has changed */
- if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
+ if (usage->type == EV_KEY && (!!test_bit(usage->code, input->key)) != value)
input_event(input, EV_MSC, MSC_SCAN, usage->hid);
input_event(input, usage->type, usage->code, value);
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 0221761..eeb527d 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -28,6 +28,8 @@
#define UHID_NAME "uhid"
#define UHID_BUFSIZE 32
+static DEFINE_MUTEX(uhid_open_mutex);
+
struct uhid_device {
struct mutex devlock;
bool running;
@@ -51,10 +53,26 @@
u32 report_id;
u32 report_type;
struct uhid_event report_buf;
+ struct work_struct worker;
};
static struct miscdevice uhid_misc;
+static void uhid_device_add_worker(struct work_struct *work)
+{
+ struct uhid_device *uhid = container_of(work, struct uhid_device, worker);
+ int ret;
+
+ ret = hid_add_device(uhid->hid);
+ if (ret) {
+ hid_err(uhid->hid, "Cannot register HID device: error %d\n", ret);
+
+ hid_destroy_device(uhid->hid);
+ uhid->hid = NULL;
+ uhid->running = false;
+ }
+}
+
static void uhid_queue(struct uhid_device *uhid, struct uhid_event *ev)
{
__u8 newhead;
@@ -126,15 +144,26 @@
static int uhid_hid_open(struct hid_device *hid)
{
struct uhid_device *uhid = hid->driver_data;
+ int retval = 0;
- return uhid_queue_event(uhid, UHID_OPEN);
+ mutex_lock(&uhid_open_mutex);
+ if (!hid->open++) {
+ retval = uhid_queue_event(uhid, UHID_OPEN);
+ if (retval)
+ hid->open--;
+ }
+ mutex_unlock(&uhid_open_mutex);
+ return retval;
}
static void uhid_hid_close(struct hid_device *hid)
{
struct uhid_device *uhid = hid->driver_data;
- uhid_queue_event(uhid, UHID_CLOSE);
+ mutex_lock(&uhid_open_mutex);
+ if (!--hid->open)
+ uhid_queue_event(uhid, UHID_CLOSE);
+ mutex_unlock(&uhid_open_mutex);
}
static int uhid_hid_parse(struct hid_device *hid)
@@ -516,18 +545,14 @@
uhid->hid = hid;
uhid->running = true;
- ret = hid_add_device(hid);
- if (ret) {
- hid_err(hid, "Cannot register HID device\n");
- goto err_hid;
- }
+ /* Adding of a HID device is done through a worker, to allow HID drivers
+ * which use feature requests during .probe to work, without they would
+ * be blocked on devlock, which is held by uhid_char_write.
+ */
+ schedule_work(&uhid->worker);
return 0;
-err_hid:
- hid_destroy_device(hid);
- uhid->hid = NULL;
- uhid->running = false;
err_free:
kfree(uhid->rd_data);
uhid->rd_data = NULL;
@@ -568,6 +593,8 @@
uhid->running = false;
wake_up_interruptible(&uhid->report_wait);
+ cancel_work_sync(&uhid->worker);
+
hid_destroy_device(uhid->hid);
kfree(uhid->rd_data);
@@ -630,6 +657,7 @@
init_waitqueue_head(&uhid->waitq);
init_waitqueue_head(&uhid->report_wait);
uhid->running = false;
+ INIT_WORK(&uhid->worker, uhid_device_add_worker);
file->private_data = uhid;
nonseekable_open(inode, file);
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index e318980..d3fd973 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -939,14 +939,6 @@
return ret;
}
-static void usbhid_restart_queues(struct usbhid_device *usbhid)
-{
- if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
- usbhid_restart_out_queue(usbhid);
- if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
- usbhid_restart_ctrl_queue(usbhid);
-}
-
static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
@@ -1392,6 +1384,37 @@
usb_kill_urb(usbhid->urbout);
}
+static void hid_restart_io(struct hid_device *hid)
+{
+ struct usbhid_device *usbhid = hid->driver_data;
+ int clear_halt = test_bit(HID_CLEAR_HALT, &usbhid->iofl);
+ int reset_pending = test_bit(HID_RESET_PENDING, &usbhid->iofl);
+
+ spin_lock_irq(&usbhid->lock);
+ clear_bit(HID_SUSPENDED, &usbhid->iofl);
+ usbhid_mark_busy(usbhid);
+
+ if (clear_halt || reset_pending)
+ schedule_work(&usbhid->reset_work);
+ usbhid->retry_delay = 0;
+ spin_unlock_irq(&usbhid->lock);
+
+ if (reset_pending || !test_bit(HID_STARTED, &usbhid->iofl))
+ return;
+
+ if (!clear_halt) {
+ if (hid_start_in(hid) < 0)
+ hid_io_error(hid);
+ }
+
+ spin_lock_irq(&usbhid->lock);
+ if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl))
+ usbhid_restart_out_queue(usbhid);
+ if (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl))
+ usbhid_restart_ctrl_queue(usbhid);
+ spin_unlock_irq(&usbhid->lock);
+}
+
/* Treat USB reset pretty much the same as suspend/resume */
static int hid_pre_reset(struct usb_interface *intf)
{
@@ -1441,14 +1464,14 @@
return 1;
}
+ /* No need to do another reset or clear a halted endpoint */
spin_lock_irq(&usbhid->lock);
clear_bit(HID_RESET_PENDING, &usbhid->iofl);
+ clear_bit(HID_CLEAR_HALT, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
- status = hid_start_in(hid);
- if (status < 0)
- hid_io_error(hid);
- usbhid_restart_queues(usbhid);
+
+ hid_restart_io(hid);
return 0;
}
@@ -1471,25 +1494,9 @@
#ifdef CONFIG_PM
static int hid_resume_common(struct hid_device *hid, bool driver_suspended)
{
- struct usbhid_device *usbhid = hid->driver_data;
- int status;
+ int status = 0;
- spin_lock_irq(&usbhid->lock);
- clear_bit(HID_SUSPENDED, &usbhid->iofl);
- usbhid_mark_busy(usbhid);
-
- if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) ||
- test_bit(HID_RESET_PENDING, &usbhid->iofl))
- schedule_work(&usbhid->reset_work);
- usbhid->retry_delay = 0;
-
- usbhid_restart_queues(usbhid);
- spin_unlock_irq(&usbhid->lock);
-
- status = hid_start_in(hid);
- if (status < 0)
- hid_io_error(hid);
-
+ hid_restart_io(hid);
if (driver_suspended && hid->driver && hid->driver->resume)
status = hid->driver->resume(hid);
return status;
@@ -1558,12 +1565,8 @@
static int hid_resume(struct usb_interface *intf)
{
struct hid_device *hid = usb_get_intfdata (intf);
- struct usbhid_device *usbhid = hid->driver_data;
int status;
- if (!test_bit(HID_STARTED, &usbhid->iofl))
- return 0;
-
status = hid_resume_common(hid, true);
dev_dbg(&intf->dev, "resume status %d\n", status);
return 0;
@@ -1572,10 +1575,8 @@
static int hid_reset_resume(struct usb_interface *intf)
{
struct hid_device *hid = usb_get_intfdata(intf);
- struct usbhid_device *usbhid = hid->driver_data;
int status;
- clear_bit(HID_SUSPENDED, &usbhid->iofl);
status = hid_post_reset(intf);
if (status >= 0 && hid->driver && hid->driver->reset_resume) {
int ret = hid->driver->reset_resume(hid);
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index f7dd36e..999dff0 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -61,6 +61,7 @@
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS682, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS692, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FIGHTERSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE, HID_QUIRK_NOGET },
diff --git a/drivers/htc_debug/stability/htc_bootloader_log.c b/drivers/htc_debug/stability/htc_bootloader_log.c
index 7cdd409..d4e10b7 100755
--- a/drivers/htc_debug/stability/htc_bootloader_log.c
+++ b/drivers/htc_debug/stability/htc_bootloader_log.c
@@ -26,6 +26,8 @@
#define RAMLOG_COMPATIBLE_NAME "htc,bldr_log"
#define RAMLOG_LAST_RSE_NAME "bl_old_log"
#define RAMLOG_CUR_RSE_NAME "bl_log"
+#define BLDR_LAST_TZ_LOG_START_TAG "------ TZBSP DIAG RING BUFF, each line for an individual log ------"
+#define BLDR_LAST_TZ_LOG_END_TAG "---------------- END Extracted TZBSP Log --------------------------"
#define BOOT_DEBUG_MAGIC 0xAACCBBDD
@@ -37,7 +39,9 @@
};
char *bl_last_log_buf, *bl_cur_log_buf;
+char *bl_last_tz_log_buf;
unsigned long bl_last_log_buf_size, bl_cur_log_buf_size;
+size_t bl_last_tz_log_buf_size;
static int bldr_log_check_header(struct bldr_log_header *header, unsigned long bldr_log_size)
{
@@ -117,35 +121,63 @@
return len;
}
+/**
+ * Read last bootloader logs, current bootloader logs, kernel logs,
+ * last bootloader TZ logs in that order.
+ *
+ * Handle reads that overlap different regions so the file appears like one
+ * file to the reader.
+ */
ssize_t bldr_log_read(const void *lastk_buf, ssize_t lastk_size, char __user *userbuf,
size_t count, loff_t *ppos)
{
loff_t pos;
- unsigned long total;
+ ssize_t total_len = 0;
ssize_t len;
+ int i;
+
+ struct {
+ const char *buf;
+ const ssize_t size;
+ } log_regions[] = {
+ { .buf = bl_last_log_buf, .size = bl_last_log_buf_size },
+ { .buf = bl_cur_log_buf, .size = bl_cur_log_buf_size, },
+ { .buf = lastk_buf, .size = lastk_size },
+ { .buf = bl_last_tz_log_buf, .size = bl_last_tz_log_buf_size }
+ };
pos = *ppos;
- total = lastk_size + bl_last_log_buf_size + bl_cur_log_buf_size;
- if (pos < bl_last_log_buf_size) {
- len = simple_read_from_buffer(userbuf, count, &pos, bl_last_log_buf, bl_last_log_buf_size);
- } else if (pos < lastk_size + bl_last_log_buf_size) {
- pos -= bl_last_log_buf_size;
- len = simple_read_from_buffer(userbuf, count, &pos, lastk_buf, lastk_size);
- } else {
- pos -= (bl_last_log_buf_size + lastk_size);
- len = simple_read_from_buffer(userbuf, count, &pos, bl_cur_log_buf, bl_cur_log_buf_size);
+ if (pos < 0)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(log_regions); ++i) {
+ if (pos < log_regions[i].size && log_regions[i].buf != NULL) {
+ len = simple_read_from_buffer(userbuf, count, &pos,
+ log_regions[i].buf, log_regions[i].size);
+ if (len < 0)
+ return len;
+ count -= len;
+ userbuf += len;
+ total_len += len;
+ }
+ pos -= log_regions[i].size;
+ if (pos < 0)
+ break;
}
- if (len > 0)
- *ppos += len;
-
- return len;
+ *ppos += total_len;
+ return total_len;
}
int bldr_log_setup(phys_addr_t bldr_phy_addr, size_t bldr_log_size, bool is_last_bldr)
{
char *bldr_base;
int ret = 0;
+ char *bl_last_tz_log_start;
+ char *bl_last_tz_log_end;
if (!bldr_log_size) {
ret = EINVAL;
@@ -169,6 +201,30 @@
} else {
pr_info("bootloader_log: allocate buffer for last bootloader log, size: %zu\n", bldr_log_size);
bldr_log_parser(bldr_base, bl_last_log_buf, bldr_log_size, &bl_last_log_buf_size);
+
+ bl_last_tz_log_start = strnstr(bl_last_log_buf,
+ BLDR_LAST_TZ_LOG_START_TAG, bldr_log_size);
+ if (bl_last_tz_log_start == NULL)
+ goto _unmap;
+
+ bl_last_tz_log_end = strnstr(bl_last_log_buf,
+ BLDR_LAST_TZ_LOG_END_TAG, bldr_log_size);
+ if (bl_last_tz_log_end == NULL)
+ goto _unmap;
+
+ bl_last_tz_log_end += strlen(BLDR_LAST_TZ_LOG_END_TAG);
+ if (bl_last_tz_log_start >= bl_last_tz_log_end)
+ goto _unmap;
+
+ bl_last_tz_log_buf_size = bl_last_tz_log_end - bl_last_tz_log_start;
+ bl_last_tz_log_buf = kmalloc(bl_last_tz_log_buf_size, GFP_KERNEL);
+ if (!bl_last_tz_log_buf) {
+ bl_last_tz_log_buf_size = 0;
+ goto _unmap;
+ }
+
+ memcpy(bl_last_tz_log_buf, bl_last_tz_log_start,
+ bl_last_tz_log_buf_size);
}
} else {
bl_cur_log_buf = kmalloc(bldr_log_size, GFP_KERNEL);
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
index 5e90c5d..040bbfd 100644
--- a/drivers/hv/hv_balloon.c
+++ b/drivers/hv/hv_balloon.c
@@ -673,7 +673,7 @@
* If the pfn range we are dealing with is not in the current
* "hot add block", move on.
*/
- if ((start_pfn >= has->end_pfn))
+ if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
continue;
/*
* If the current hot add-request extends beyond
@@ -728,7 +728,7 @@
* If the pfn range we are dealing with is not in the current
* "hot add block", move on.
*/
- if ((start_pfn >= has->end_pfn))
+ if (start_pfn < has->start_pfn || start_pfn >= has->end_pfn)
continue;
old_covered_state = has->covered_end_pfn;
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index 3b9c9ef..2d1414d 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -283,10 +283,14 @@
u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
struct icmsg_negotiate *negop = NULL;
- vmbus_recvpacket(channel, hbeat_txf_buf,
- PAGE_SIZE, &recvlen, &requestid);
+ while (1) {
- if (recvlen > 0) {
+ vmbus_recvpacket(channel, hbeat_txf_buf,
+ PAGE_SIZE, &recvlen, &requestid);
+
+ if (!recvlen)
+ break;
+
icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[
sizeof(struct vmbuspipe_hdr)];
diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c
index 6361d12..14d45c7 100644
--- a/drivers/hv/ring_buffer.c
+++ b/drivers/hv/ring_buffer.c
@@ -103,19 +103,30 @@
* there is room for the producer to send the pending packet.
*/
-static bool hv_need_to_signal_on_read(u32 old_rd,
- struct hv_ring_buffer_info *rbi)
+static bool hv_need_to_signal_on_read(struct hv_ring_buffer_info *rbi)
{
- u32 prev_write_sz;
u32 cur_write_sz;
u32 r_size;
- u32 write_loc = rbi->ring_buffer->write_index;
+ u32 write_loc;
u32 read_loc = rbi->ring_buffer->read_index;
- u32 pending_sz = rbi->ring_buffer->pending_send_sz;
+ u32 pending_sz;
/*
- * If the other end is not blocked on write don't bother.
+ * Issue a full memory barrier before making the signaling decision.
+ * Here is the reason for having this barrier:
+ * If the reading of the pend_sz (in this function)
+ * were to be reordered and read before we commit the new read
+ * index (in the calling function) we could
+ * have a problem. If the host were to set the pending_sz after we
+ * have sampled pending_sz and go to sleep before we commit the
+ * read index, we could miss sending the interrupt. Issue a full
+ * memory barrier to address this.
*/
+ mb();
+
+ pending_sz = rbi->ring_buffer->pending_send_sz;
+ write_loc = rbi->ring_buffer->write_index;
+ /* If the other end is not blocked on write don't bother. */
if (pending_sz == 0)
return false;
@@ -123,22 +134,13 @@
cur_write_sz = write_loc >= read_loc ? r_size - (write_loc - read_loc) :
read_loc - write_loc;
- prev_write_sz = write_loc >= old_rd ? r_size - (write_loc - old_rd) :
- old_rd - write_loc;
-
-
- if ((prev_write_sz < pending_sz) && (cur_write_sz >= pending_sz))
+ if (cur_write_sz >= pending_sz)
return true;
return false;
}
-/*
- * hv_get_next_write_location()
- *
- * Get the next write location for the specified ring buffer
- *
- */
+/* Get the next write location for the specified ring buffer. */
static inline u32
hv_get_next_write_location(struct hv_ring_buffer_info *ring_info)
{
@@ -147,12 +149,7 @@
return next;
}
-/*
- * hv_set_next_write_location()
- *
- * Set the next write location for the specified ring buffer
- *
- */
+/* Set the next write location for the specified ring buffer. */
static inline void
hv_set_next_write_location(struct hv_ring_buffer_info *ring_info,
u32 next_write_location)
@@ -160,11 +157,7 @@
ring_info->ring_buffer->write_index = next_write_location;
}
-/*
- * hv_get_next_read_location()
- *
- * Get the next read location for the specified ring buffer
- */
+/* Get the next read location for the specified ring buffer. */
static inline u32
hv_get_next_read_location(struct hv_ring_buffer_info *ring_info)
{
@@ -174,10 +167,8 @@
}
/*
- * hv_get_next_readlocation_withoffset()
- *
* Get the next read location + offset for the specified ring buffer.
- * This allows the caller to skip
+ * This allows the caller to skip.
*/
static inline u32
hv_get_next_readlocation_withoffset(struct hv_ring_buffer_info *ring_info,
@@ -191,13 +182,7 @@
return next;
}
-/*
- *
- * hv_set_next_read_location()
- *
- * Set the next read location for the specified ring buffer
- *
- */
+/* Set the next read location for the specified ring buffer. */
static inline void
hv_set_next_read_location(struct hv_ring_buffer_info *ring_info,
u32 next_read_location)
@@ -206,12 +191,7 @@
}
-/*
- *
- * hv_get_ring_buffer()
- *
- * Get the start of the ring buffer
- */
+/* Get the start of the ring buffer. */
static inline void *
hv_get_ring_buffer(struct hv_ring_buffer_info *ring_info)
{
@@ -219,25 +199,14 @@
}
-/*
- *
- * hv_get_ring_buffersize()
- *
- * Get the size of the ring buffer
- */
+/* Get the size of the ring buffer. */
static inline u32
hv_get_ring_buffersize(struct hv_ring_buffer_info *ring_info)
{
return ring_info->ring_datasize;
}
-/*
- *
- * hv_get_ring_bufferindices()
- *
- * Get the read and write indices as u64 of the specified ring buffer
- *
- */
+/* Get the read and write indices as u64 of the specified ring buffer. */
static inline u64
hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info)
{
@@ -245,12 +214,8 @@
}
/*
- *
- * hv_copyfrom_ringbuffer()
- *
* Helper routine to copy to source from ring buffer.
* Assume there is enough room. Handles wrap-around in src case only!!
- *
*/
static u32 hv_copyfrom_ringbuffer(
struct hv_ring_buffer_info *ring_info,
@@ -282,12 +247,8 @@
/*
- *
- * hv_copyto_ringbuffer()
- *
* Helper routine to copy from source to ring buffer.
* Assume there is enough room. Handles wrap-around in dest case only!!
- *
*/
static u32 hv_copyto_ringbuffer(
struct hv_ring_buffer_info *ring_info,
@@ -313,13 +274,7 @@
return start_write_offset;
}
-/*
- *
- * hv_ringbuffer_get_debuginfo()
- *
- * Get various debug metrics for the specified ring buffer
- *
- */
+/* Get various debug metrics for the specified ring buffer. */
void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info)
{
@@ -342,13 +297,7 @@
}
}
-/*
- *
- * hv_ringbuffer_init()
- *
- *Initialize the ring buffer
- *
- */
+/* Initialize the ring buffer. */
int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
void *buffer, u32 buflen)
{
@@ -361,9 +310,7 @@
ring_info->ring_buffer->read_index =
ring_info->ring_buffer->write_index = 0;
- /*
- * Set the feature bit for enabling flow control.
- */
+ /* Set the feature bit for enabling flow control. */
ring_info->ring_buffer->feature_bits.value = 1;
ring_info->ring_size = buflen;
@@ -374,24 +321,12 @@
return 0;
}
-/*
- *
- * hv_ringbuffer_cleanup()
- *
- * Cleanup the ring buffer
- *
- */
+/* Cleanup the ring buffer. */
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
{
}
-/*
- *
- * hv_ringbuffer_write()
- *
- * Write to the ring buffer
- *
- */
+/* Write to the ring buffer. */
int hv_ringbuffer_write(struct hv_ring_buffer_info *outring_info,
struct kvec *kv_list, u32 kv_count, bool *signal)
{
@@ -416,10 +351,11 @@
&bytes_avail_toread,
&bytes_avail_towrite);
-
- /* If there is only room for the packet, assume it is full. */
- /* Otherwise, the next time around, we think the ring buffer */
- /* is empty since the read index == write index */
+ /*
+ * If there is only room for the packet, assume it is full.
+ * Otherwise, the next time around, we think the ring buffer
+ * is empty since the read index == write index.
+ */
if (bytes_avail_towrite <= totalbytes_towrite) {
spin_unlock_irqrestore(&outring_info->ring_lock, flags);
return -EAGAIN;
@@ -459,13 +395,7 @@
}
-/*
- *
- * hv_ringbuffer_peek()
- *
- * Read without advancing the read index
- *
- */
+/* Read without advancing the read index. */
int hv_ringbuffer_peek(struct hv_ring_buffer_info *Inring_info,
void *Buffer, u32 buflen)
{
@@ -502,13 +432,7 @@
}
-/*
- *
- * hv_ringbuffer_read()
- *
- * Read and advance the read index
- *
- */
+/* Read and advance the read index. */
int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer,
u32 buflen, u32 offset, bool *signal)
{
@@ -517,7 +441,6 @@
u32 next_read_location = 0;
u64 prev_indices = 0;
unsigned long flags;
- u32 old_read;
if (buflen <= 0)
return -EINVAL;
@@ -528,8 +451,6 @@
&bytes_avail_toread,
&bytes_avail_towrite);
- old_read = bytes_avail_toread;
-
/* Make sure there is something to read */
if (bytes_avail_toread < buflen) {
spin_unlock_irqrestore(&inring_info->ring_lock, flags);
@@ -550,9 +471,11 @@
sizeof(u64),
next_read_location);
- /* Make sure all reads are done before we update the read index since */
- /* the writer may start writing to the read area once the read index */
- /*is updated */
+ /*
+ * Make sure all reads are done before we update the read index since
+ * the writer may start writing to the read area once the read index
+ * is updated.
+ */
mb();
/* Update the read index */
@@ -560,7 +483,7 @@
spin_unlock_irqrestore(&inring_info->ring_lock, flags);
- *signal = hv_need_to_signal_on_read(old_read, inring_info);
+ *signal = hv_need_to_signal_on_read(inring_info);
return 0;
}
diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c
index f67d71e..159f50d 100644
--- a/drivers/hwmon/max1111.c
+++ b/drivers/hwmon/max1111.c
@@ -85,6 +85,9 @@
int max1111_read_channel(int channel)
{
+ if (!the_max1111 || !the_max1111->spi)
+ return -ENODEV;
+
return max1111_read(&the_max1111->spi->dev, channel);
}
EXPORT_SYMBOL(max1111_read_channel);
@@ -258,6 +261,9 @@
{
struct max1111_data *data = spi_get_drvdata(spi);
+#ifdef CONFIG_SHARPSL_PM
+ the_max1111 = NULL;
+#endif
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c
index 81e6263..91a6362 100644
--- a/drivers/i2c/busses/i2c-exynos5.c
+++ b/drivers/i2c/busses/i2c-exynos5.c
@@ -671,7 +671,9 @@
return -EIO;
}
- clk_prepare_enable(i2c->clk);
+ ret = clk_enable(i2c->clk);
+ if (ret)
+ return ret;
for (i = 0; i < num; i++, msgs++) {
stop = (i == num - 1);
@@ -695,7 +697,7 @@
}
out:
- clk_disable_unprepare(i2c->clk);
+ clk_disable(i2c->clk);
return ret;
}
@@ -747,7 +749,9 @@
return -ENOENT;
}
- clk_prepare_enable(i2c->clk);
+ ret = clk_prepare_enable(i2c->clk);
+ if (ret)
+ return ret;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
@@ -799,6 +803,10 @@
platform_set_drvdata(pdev, i2c);
+ clk_disable(i2c->clk);
+
+ return 0;
+
err_clk:
clk_disable_unprepare(i2c->clk);
return ret;
@@ -810,6 +818,8 @@
i2c_del_adapter(&i2c->adap);
+ clk_unprepare(i2c->clk);
+
return 0;
}
@@ -821,6 +831,8 @@
i2c->suspended = 1;
+ clk_unprepare(i2c->clk);
+
return 0;
}
@@ -830,7 +842,9 @@
struct exynos5_i2c *i2c = platform_get_drvdata(pdev);
int ret = 0;
- clk_prepare_enable(i2c->clk);
+ ret = clk_prepare_enable(i2c->clk);
+ if (ret)
+ return ret;
ret = exynos5_hsi2c_clock_setup(i2c);
if (ret) {
@@ -839,7 +853,7 @@
}
exynos5_i2c_init(i2c);
- clk_disable_unprepare(i2c->clk);
+ clk_disable(i2c->clk);
i2c->suspended = 0;
return 0;
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
index 98ba761..2254f85 100644
--- a/drivers/iio/accel/kxsd9.c
+++ b/drivers/iio/accel/kxsd9.c
@@ -160,11 +160,13 @@
if (ret < 0)
goto error_ret;
*val = ret;
+ ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
if (ret)
goto error_ret;
+ *val = 0;
*val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
ret = IIO_VAL_INT_PLUS_MICRO;
break;
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index b730864..3f8fc28 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -122,7 +122,9 @@
{
struct iio_dev *indio_dev = private;
struct tiadc_device *adc_dev = iio_priv(indio_dev);
- unsigned int status, config;
+ unsigned int status, config, adc_fsm;
+ unsigned short count = 0;
+
status = tiadc_readl(adc_dev, REG_IRQSTATUS);
/*
@@ -136,6 +138,15 @@
tiadc_writel(adc_dev, REG_CTRL, config);
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN
| IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES);
+
+ /* wait for idle state.
+ * ADC needs to finish the current conversion
+ * before disabling the module
+ */
+ do {
+ adc_fsm = tiadc_readl(adc_dev, REG_ADCFSM);
+ } while (adc_fsm != 0x10 && count++ < 100);
+
tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
return IRQ_HANDLED;
} else if (status & IRQENB_FIFO1THRES) {
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index bf5ef07..f03c3bf 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -160,6 +160,8 @@
int rc;
int irq;
+ init_waitqueue_head(&data->data_ready_queue);
+ clear_bit(0, &data->flags);
if (client->irq)
irq = client->irq;
else
@@ -175,8 +177,6 @@
return rc;
}
- init_waitqueue_head(&data->data_ready_queue);
- clear_bit(0, &data->flags);
data->eoc_irq = irq;
return rc;
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index f2f63933e..5befec1 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -48,6 +48,7 @@
#include <asm/uaccess.h>
+#include <rdma/ib.h>
#include <rdma/ib_cm.h>
#include <rdma/ib_user_cm.h>
#include <rdma/ib_marshall.h>
@@ -1104,6 +1105,9 @@
struct ib_ucm_cmd_hdr hdr;
ssize_t result;
+ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
+ return -EACCES;
+
if (len < sizeof(hdr))
return -EINVAL;
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 45d67e9..81dd84d 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -1487,6 +1487,9 @@
struct rdma_ucm_cmd_hdr hdr;
ssize_t ret;
+ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
+ return -EACCES;
+
if (len < sizeof(hdr))
return -EINVAL;
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index d3abb7e..c221310 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -48,6 +48,8 @@
#include <asm/uaccess.h>
+#include <rdma/ib.h>
+
#include "uverbs.h"
MODULE_AUTHOR("Roland Dreier");
@@ -610,6 +612,9 @@
struct ib_uverbs_cmd_hdr hdr;
__u32 flags;
+ if (WARN_ON_ONCE(!ib_safe_file_access(filp)))
+ return -EACCES;
+
if (count < sizeof hdr)
return -EINVAL;
diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/infiniband/hw/ehca/ehca_mrmw.c
index 3488e8c..62b61e9 100644
--- a/drivers/infiniband/hw/ehca/ehca_mrmw.c
+++ b/drivers/infiniband/hw/ehca/ehca_mrmw.c
@@ -1921,7 +1921,7 @@
u64 *kpage)
{
int ret = 0;
- u64 pgaddr, prev_pgaddr;
+ u64 pgaddr, prev_pgaddr = 0;
u32 j = 0;
int kpages_per_hwpage = pginfo->hwpage_size / PAGE_SIZE;
int nr_kpages = kpages_per_hwpage;
@@ -2417,6 +2417,7 @@
ehca_err(&shca->ib_device, "kpage alloc failed");
return -ENOMEM;
}
+ hret = H_SUCCESS;
for (top = 0; top < EHCA_MAP_ENTRIES; top++) {
if (!ehca_bmap_valid(ehca_bmap->top[top]))
continue;
diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c
index e65ee19..b33fa45 100644
--- a/drivers/infiniband/hw/mlx4/ah.c
+++ b/drivers/infiniband/hw/mlx4/ah.c
@@ -46,6 +46,7 @@
ah->av.ib.port_pd = cpu_to_be32(to_mpd(pd)->pdn | (ah_attr->port_num << 24));
ah->av.ib.g_slid = ah_attr->src_path_bits;
+ ah->av.ib.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
if (ah_attr->ah_flags & IB_AH_GRH) {
ah->av.ib.g_slid |= 0x80;
ah->av.ib.gid_index = ah_attr->grh.sgid_index;
@@ -63,7 +64,6 @@
!(1 << ah->av.ib.stat_rate & dev->caps.stat_rate_support))
--ah->av.ib.stat_rate;
}
- ah->av.ib.sl_tclass_flowlabel = cpu_to_be32(ah_attr->sl << 28);
return &ah->ibah;
}
diff --git a/drivers/infiniband/hw/mlx5/mem.c b/drivers/infiniband/hw/mlx5/mem.c
index dae07ea..0a9b2ae 100644
--- a/drivers/infiniband/hw/mlx5/mem.c
+++ b/drivers/infiniband/hw/mlx5/mem.c
@@ -69,7 +69,7 @@
for (k = 0; k < len; k++) {
if (!(i & mask)) {
tmp = (unsigned long)pfn;
- m = min(m, find_first_bit(&tmp, sizeof(tmp)));
+ m = min_t(unsigned long, m, find_first_bit(&tmp, sizeof(tmp)));
skip = 1 << m;
mask = skip - 1;
base = pfn;
diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c
index b15e34e..3ab8229 100644
--- a/drivers/infiniband/hw/qib/qib_file_ops.c
+++ b/drivers/infiniband/hw/qib/qib_file_ops.c
@@ -45,6 +45,8 @@
#include <linux/delay.h>
#include <linux/export.h>
+#include <rdma/ib.h>
+
#include "qib.h"
#include "qib_common.h"
#include "qib_user_sdma.h"
@@ -2058,6 +2060,9 @@
ssize_t ret = 0;
void *dest;
+ if (WARN_ON_ONCE(!ib_safe_file_access(fp)))
+ return -EACCES;
+
if (count < sizeof(cmd.type)) {
ret = -EINVAL;
goto bail;
diff --git a/drivers/infiniband/hw/qib/qib_qp.c b/drivers/infiniband/hw/qib/qib_qp.c
index c5b6e60..c231313 100644
--- a/drivers/infiniband/hw/qib/qib_qp.c
+++ b/drivers/infiniband/hw/qib/qib_qp.c
@@ -41,13 +41,13 @@
#include "qib.h"
-#define BITS_PER_PAGE (PAGE_SIZE*BITS_PER_BYTE)
-#define BITS_PER_PAGE_MASK (BITS_PER_PAGE-1)
+#define RVT_BITS_PER_PAGE (PAGE_SIZE*BITS_PER_BYTE)
+#define RVT_BITS_PER_PAGE_MASK (RVT_BITS_PER_PAGE-1)
static inline unsigned mk_qpn(struct qib_qpn_table *qpt,
struct qpn_map *map, unsigned off)
{
- return (map - qpt->map) * BITS_PER_PAGE + off;
+ return (map - qpt->map) * RVT_BITS_PER_PAGE + off;
}
static inline unsigned find_next_offset(struct qib_qpn_table *qpt,
@@ -59,7 +59,7 @@
if (((off & qpt->mask) >> 1) >= n)
off = (off | qpt->mask) + 2;
} else
- off = find_next_zero_bit(map->page, BITS_PER_PAGE, off);
+ off = find_next_zero_bit(map->page, RVT_BITS_PER_PAGE, off);
return off;
}
@@ -147,8 +147,8 @@
qpn = 2;
if (qpt->mask && ((qpn & qpt->mask) >> 1) >= dd->n_krcv_queues)
qpn = (qpn | qpt->mask) + 2;
- offset = qpn & BITS_PER_PAGE_MASK;
- map = &qpt->map[qpn / BITS_PER_PAGE];
+ offset = qpn & RVT_BITS_PER_PAGE_MASK;
+ map = &qpt->map[qpn / RVT_BITS_PER_PAGE];
max_scan = qpt->nmaps - !offset;
for (i = 0;;) {
if (unlikely(!map->page)) {
@@ -173,7 +173,7 @@
* We just need to be sure we don't loop
* forever.
*/
- } while (offset < BITS_PER_PAGE && qpn < QPN_MAX);
+ } while (offset < RVT_BITS_PER_PAGE && qpn < QPN_MAX);
/*
* In order to keep the number of pages allocated to a
* minimum, we scan the all existing pages before increasing
@@ -204,9 +204,9 @@
{
struct qpn_map *map;
- map = qpt->map + qpn / BITS_PER_PAGE;
+ map = qpt->map + qpn / RVT_BITS_PER_PAGE;
if (map->page)
- clear_bit(qpn & BITS_PER_PAGE_MASK, map->page);
+ clear_bit(qpn & RVT_BITS_PER_PAGE_MASK, map->page);
}
static inline unsigned qpn_hash(struct qib_ibdev *dev, u32 qpn)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 933efce..cdf0a78 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -1478,12 +1478,14 @@
ret = ipoib_set_mode(dev, buf);
- rtnl_unlock();
+ /* The assumption is that the function ipoib_set_mode returned
+ * with the rtnl held by it, if not the value -EBUSY returned,
+ * then no need to rtnl_unlock
+ */
+ if (ret != -EBUSY)
+ rtnl_unlock();
- if (!ret)
- return count;
-
- return ret;
+ return (!ret || ret == -EBUSY) ? count : ret;
}
static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, show_mode, set_mode);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 58b5aa3..483ddbd 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -236,8 +236,7 @@
priv->tx_wr.send_flags &= ~IB_SEND_IP_CSUM;
ipoib_flush_paths(dev);
- rtnl_lock();
- return 0;
+ return (!rtnl_trylock()) ? -EBUSY : 0;
}
if (!strcmp(buf, "datagram\n")) {
@@ -246,8 +245,7 @@
dev_set_mtu(dev, min(priv->mcast_mtu, dev->mtu));
rtnl_unlock();
ipoib_flush_paths(dev);
- rtnl_lock();
- return 0;
+ return (!rtnl_trylock()) ? -EBUSY : 0;
}
return -EINVAL;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index ffb83b5..6391ed0 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -523,8 +523,11 @@
if (!test_bit(IPOIB_MCAST_RUN, &priv->flags))
return;
- if (ib_query_port(priv->ca, priv->port, &port_attr) ||
- port_attr.state != IB_PORT_ACTIVE) {
+ if (ib_query_port(priv->ca, priv->port, &port_attr)) {
+ ipoib_dbg(priv, "ib_query_port() failed\n");
+ return;
+ }
+ if (port_attr.state != IB_PORT_ACTIVE) {
ipoib_dbg(priv, "port state is not ACTIVE (state = %d) suspending join task\n",
port_attr.state);
return;
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index 424783f..b08c212 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -73,7 +73,6 @@
if (cmd_dir == ISER_DIR_OUT) {
/* copy the unaligned sg the buffer which is used for RDMA */
- int i;
char *p, *from;
sgl = (struct scatterlist *)data->buf;
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index d004d6e..5bdad19 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -1052,7 +1052,7 @@
sge.length = ISER_RX_LOGIN_SIZE;
sge.lkey = ib_conn->device->mr->lkey;
- rx_wr.wr_id = (unsigned long)iser_conn->login_resp_buf;
+ rx_wr.wr_id = (uintptr_t)iser_conn->login_resp_buf;
rx_wr.sg_list = &sge;
rx_wr.num_sge = 1;
rx_wr.next = NULL;
@@ -1076,7 +1076,7 @@
for (rx_wr = ib_conn->rx_wr, i = 0; i < count; i++, rx_wr++) {
rx_desc = &iser_conn->rx_descs[my_rx_head];
- rx_wr->wr_id = (unsigned long)rx_desc;
+ rx_wr->wr_id = (uintptr_t)rx_desc;
rx_wr->sg_list = &rx_desc->rx_sg;
rx_wr->num_sge = 1;
rx_wr->next = rx_wr + 1;
@@ -1113,7 +1113,7 @@
DMA_TO_DEVICE);
send_wr.next = NULL;
- send_wr.wr_id = (unsigned long)tx_desc;
+ send_wr.wr_id = (uintptr_t)tx_desc;
send_wr.sg_list = tx_desc->tx_sg;
send_wr.num_sge = tx_desc->num_sge;
send_wr.opcode = IB_WR_SEND;
@@ -1163,6 +1163,7 @@
iser_handle_comp_error(struct ib_conn *ib_conn,
struct ib_wc *wc)
{
+ void *wr_id = (void *)(uintptr_t)wc->wr_id;
struct iser_conn *iser_conn = container_of(ib_conn, struct iser_conn,
ib_conn);
@@ -1171,8 +1172,8 @@
iscsi_conn_failure(iser_conn->iscsi_conn,
ISCSI_ERR_CONN_FAILED);
- if (is_iser_tx_desc(iser_conn, (void *)wc->wr_id)) {
- struct iser_tx_desc *desc = (struct iser_tx_desc *)wc->wr_id;
+ if (is_iser_tx_desc(iser_conn, wr_id)) {
+ struct iser_tx_desc *desc = wr_id;
if (desc->type == ISCSI_TX_DATAOUT)
kmem_cache_free(ig.desc_cache, desc);
@@ -1198,12 +1199,12 @@
ib_conn = wc->qp->qp_context;
if (wc->status == IB_WC_SUCCESS) {
if (wc->opcode == IB_WC_RECV) {
- rx_desc = (struct iser_rx_desc *)wc->wr_id;
+ rx_desc = (struct iser_rx_desc *)(uintptr_t)wc->wr_id;
iser_rcv_completion(rx_desc, wc->byte_len,
ib_conn);
} else
if (wc->opcode == IB_WC_SEND) {
- tx_desc = (struct iser_tx_desc *)wc->wr_id;
+ tx_desc = (struct iser_tx_desc *)(uintptr_t)wc->wr_id;
iser_snd_completion(tx_desc, ib_conn);
} else {
iser_err("Unknown wc opcode %d\n", wc->opcode);
diff --git a/drivers/input/joystick/iforce/iforce-usb.c b/drivers/input/joystick/iforce/iforce-usb.c
index d96aa27..db64adf 100644
--- a/drivers/input/joystick/iforce/iforce-usb.c
+++ b/drivers/input/joystick/iforce/iforce-usb.c
@@ -141,6 +141,9 @@
interface = intf->cur_altsetting;
+ if (interface->desc.bNumEndpoints < 2)
+ return -ENODEV;
+
epirq = &interface->endpoint[0].desc;
epout = &interface->endpoint[1].desc;
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index a028913..88357f8 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -189,6 +189,7 @@
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
{ 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
+ { 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE },
{ 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 },
{ 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
{ 0x15e4, 0x3f10, "Batarang Xbox 360 controller", 0, XTYPE_XBOX360 },
@@ -310,6 +311,7 @@
XPAD_XBOX360_VENDOR(0x1689), /* Razer Onza */
XPAD_XBOX360_VENDOR(0x24c6), /* PowerA Controllers */
XPAD_XBOX360_VENDOR(0x1532), /* Razer Sabertooth */
+ XPAD_XBOXONE_VENDOR(0x1532), /* Razer Wildcat */
XPAD_XBOX360_VENDOR(0x15e4), /* Numark X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x162e), /* Joytech X-Box 360 controllers */
{ }
@@ -1200,6 +1202,9 @@
int ep_irq_in_idx;
int i, error;
+ if (intf->cur_altsetting->desc.bNumEndpoints != 2)
+ return -ENODEV;
+
for (i = 0; xpad_device[i].idVendor; i++) {
if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
(le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct))
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 5107bed..77de7d6 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -395,28 +395,6 @@
.attrs = gpio_keys_attrs,
};
-#include <linux/sched.h>
-static int debug_key_bits = 0x0;
-static DEFINE_SPINLOCK(debug_key_lock);
-static void debug_combine_key(unsigned int code, int value)
-{
- unsigned long flags;
- int bits;
-
- value = !!value;
- code -= 0x72;
- if (code > 2)
- return;
-
- spin_lock_irqsave(&debug_key_lock, flags);
- bits = debug_key_bits =
- (debug_key_bits & ~(1 << code)) | value << code;
- spin_unlock_irqrestore(&debug_key_lock, flags);
-
- if (bits == 0b111)
- show_state_filter(TASK_UNINTERRUPTIBLE);
-}
-
static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
{
const struct gpio_keys_button *button = bdata->button;
@@ -431,14 +409,12 @@
pr_debug("[KEY] %s: key %x-%x, (%d) changed to %d\n",
__func__, type, button->code, button->gpio, button->value);
- debug_combine_key(button->code, button->value);
input_event(input, type, button->code, button->value);
}
} else {
pr_debug("[KEY] %s: key %x-%x, (%d) changed to %d\n",
__func__, type, button->code, button->gpio, !!state);
- debug_combine_key(button->code, !!state);
input_event(input, type, button->code, !!state);
}
input_sync(input);
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index 9757a58..5d39b96 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -376,7 +376,7 @@
/* Reset the KBC controller to clear all previous status.*/
reset_control_assert(kbc->rst);
udelay(100);
- reset_control_assert(kbc->rst);
+ reset_control_deassert(kbc->rst);
udelay(100);
tegra_kbc_config_pins(kbc);
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 9365535..50a7faa 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -675,6 +675,10 @@
int error = -ENOMEM;
interface = intf->cur_altsetting;
+
+ if (interface->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index cab87f5..e4b337b 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -597,7 +597,6 @@
}
haptics->input_dev->name = "drv260x:haptics";
- haptics->input_dev->dev.parent = client->dev.parent;
haptics->input_dev->close = drv260x_close;
input_set_drvdata(haptics->input_dev, haptics);
input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
index 9c0ea36..f4e8fbe 100644
--- a/drivers/input/misc/ims-pcu.c
+++ b/drivers/input/misc/ims-pcu.c
@@ -1667,6 +1667,10 @@
return -EINVAL;
alt = pcu->ctrl_intf->cur_altsetting;
+
+ if (alt->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
pcu->ep_ctrl = &alt->endpoint[0].desc;
pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl);
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
index a363ebbd..57f3d90 100644
--- a/drivers/input/misc/max8997_haptic.c
+++ b/drivers/input/misc/max8997_haptic.c
@@ -255,12 +255,14 @@
struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
const struct max8997_platform_data *pdata =
dev_get_platdata(iodev->dev);
- const struct max8997_haptic_platform_data *haptic_pdata =
- pdata->haptic_pdata;
+ const struct max8997_haptic_platform_data *haptic_pdata = NULL;
struct max8997_haptic *chip;
struct input_dev *input_dev;
int error;
+ if (pdata)
+ haptic_pdata = pdata->haptic_pdata;
+
if (!haptic_pdata) {
dev_err(&pdev->dev, "no haptic platform data\n");
return -EINVAL;
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index c91e3d3..88db9204b 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -94,7 +94,8 @@
if (of_property_read_u32(pdev->dev.of_node, "debounce", &kpd_delay))
kpd_delay = 15625;
- if (kpd_delay > 62500 || kpd_delay == 0) {
+ /* Valid range of pwr key trigger delay is 1/64 sec to 2 seconds. */
+ if (kpd_delay > USEC_PER_SEC * 2 || kpd_delay < USEC_PER_SEC / 64) {
dev_err(&pdev->dev, "invalid power key trigger delay\n");
return -EINVAL;
}
@@ -124,8 +125,8 @@
pwr->name = "pmic8xxx_pwrkey";
pwr->phys = "pmic8xxx_pwrkey/input0";
- delay = (kpd_delay << 10) / USEC_PER_SEC;
- delay = 1 + ilog2(delay);
+ delay = (kpd_delay << 6) / USEC_PER_SEC;
+ delay = ilog2(delay);
err = regmap_read(regmap, PON_CNTL_1, &pon_cntl);
if (err < 0) {
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 421e29e..5221450 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -895,9 +895,15 @@
}
#ifdef CONFIG_COMPAT
+
+#define UI_SET_PHYS_COMPAT _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t)
+
static long uinput_compat_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
+ if (cmd == UI_SET_PHYS_COMPAT)
+ cmd = UI_SET_PHYS;
+
return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
}
#endif
diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api.c b/drivers/input/misc/vl53L0/src/vl53l0_api.c
index f074543..6f75271 100644
--- a/drivers/input/misc/vl53L0/src/vl53l0_api.c
+++ b/drivers/input/misc/vl53L0/src/vl53l0_api.c
@@ -2172,7 +2172,7 @@
{
uint8_t InterruptConfig;
FixPoint1616_t ThresholdLow;
- FixPoint1616_t ThresholdHigh;
+ FixPoint1616_t ThresholdHigh = 0;
VL53L0_Error Status = VL53L0_ERROR_NONE;
InterruptConfig = VL53L0_GETDEVICESPECIFICPARAMETER(Dev,
diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c
index 79c964c..6e7ff95 100644
--- a/drivers/input/misc/yealink.c
+++ b/drivers/input/misc/yealink.c
@@ -875,6 +875,10 @@
int ret, pipe, i;
interface = intf->cur_altsetting;
+
+ if (interface->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index b3b2a13..47a1a20 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1115,6 +1115,7 @@
* Asus UX32VD 0x361f02 00, 15, 0e clickpad
* Avatar AVIU-145A2 0x361f00 ? clickpad
* Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons
+ * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons
* Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons
* Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**)
* Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons
@@ -1496,6 +1497,13 @@
},
},
{
+ /* Fujitsu LIFEBOOK E547 does not work with crc_enabled == 0 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E547"),
+ },
+ },
+ {
/* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
@@ -1550,12 +1558,7 @@
case 5:
etd->hw_version = 3;
break;
- case 6:
- case 7:
- case 8:
- case 9:
- case 10:
- case 13:
+ case 6 ... 14:
etd->hw_version = 4;
break;
default:
diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c
index 8d9ba0c..94ab494 100644
--- a/drivers/input/serio/gscps2.c
+++ b/drivers/input/serio/gscps2.c
@@ -40,7 +40,6 @@
MODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>, Thibaut Varene <varenet@parisc-linux.org>, Helge Deller <deller@gmx.de>");
MODULE_DESCRIPTION("HP GSC PS2 port driver");
MODULE_LICENSE("GPL");
-MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
#define PFX "gscps2.c: "
@@ -439,6 +438,7 @@
#endif
{ 0, } /* 0 terminated list */
};
+MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
static struct parisc_driver parisc_ps2_driver = {
.name = "gsc_ps2",
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 89daac1..3b9947b 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -120,6 +120,13 @@
},
},
{
+ /* Dell Embedded Box PC 3000 */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"),
+ },
+ },
+ {
/* OQO Model 01 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
@@ -588,6 +595,13 @@
DMI_MATCH(DMI_PRODUCT_NAME, "20046"),
},
},
+ {
+ /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"),
+ },
+ },
{ }
};
@@ -783,6 +797,13 @@
DMI_MATCH(DMI_PRODUCT_NAME, "P34"),
},
},
+ {
+ /* Schenker XMG C504 - Elantech touchpad */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "XMG"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "C504"),
+ },
+ },
{ }
};
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 804d2e0..1e25a06 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -1230,6 +1230,7 @@
serio->start = i8042_start;
serio->stop = i8042_stop;
serio->close = i8042_port_close;
+ serio->ps2_cmd_mutex = &i8042_mutex;
serio->port_data = port;
serio->dev.parent = &i8042_platform_device->dev;
strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name));
@@ -1257,6 +1258,7 @@
serio->write = i8042_aux_write;
serio->start = i8042_start;
serio->stop = i8042_stop;
+ serio->ps2_cmd_mutex = &i8042_mutex;
serio->port_data = port;
serio->dev.parent = &i8042_platform_device->dev;
if (idx < 0) {
@@ -1323,21 +1325,6 @@
}
}
-/*
- * Checks whether port belongs to i8042 controller.
- */
-bool i8042_check_port_owner(const struct serio *port)
-{
- int i;
-
- for (i = 0; i < I8042_NUM_PORTS; i++)
- if (i8042_ports[i].serio == port)
- return true;
-
- return false;
-}
-EXPORT_SYMBOL(i8042_check_port_owner);
-
static void i8042_free_irqs(void)
{
if (i8042_aux_irq_registered)
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index 7551699..ded0c6f 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -56,19 +56,17 @@
void ps2_begin_command(struct ps2dev *ps2dev)
{
- mutex_lock(&ps2dev->cmd_mutex);
+ struct mutex *m = ps2dev->serio->ps2_cmd_mutex ?: &ps2dev->cmd_mutex;
- if (i8042_check_port_owner(ps2dev->serio))
- i8042_lock_chip();
+ mutex_lock(m);
}
EXPORT_SYMBOL(ps2_begin_command);
void ps2_end_command(struct ps2dev *ps2dev)
{
- if (i8042_check_port_owner(ps2dev->serio))
- i8042_unlock_chip();
+ struct mutex *m = ps2dev->serio->ps2_cmd_mutex ?: &ps2dev->cmd_mutex;
- mutex_unlock(&ps2dev->cmd_mutex);
+ mutex_unlock(m);
}
EXPORT_SYMBOL(ps2_end_command);
diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c
index 8580456..a51de54 100644
--- a/drivers/input/tablet/gtco.c
+++ b/drivers/input/tablet/gtco.c
@@ -868,6 +868,14 @@
goto err_free_buf;
}
+ /* Sanity check that a device has an endpoint */
+ if (usbinterface->altsetting[0].desc.bNumEndpoints < 1) {
+ dev_err(&usbinterface->dev,
+ "Invalid number of endpoints\n");
+ error = -EINVAL;
+ goto err_free_urb;
+ }
+
/*
* The endpoint is always altsetting 0, we know this since we know
* this device only has one interrupt endpoint
@@ -889,7 +897,7 @@
* HID report descriptor
*/
if (usb_get_extra_descriptor(usbinterface->cur_altsetting,
- HID_DEVICE_TYPE, &hid_desc) != 0){
+ HID_DEVICE_TYPE, &hid_desc) != 0) {
dev_err(&usbinterface->dev,
"Can't retrieve exta USB descriptor to get hid report descriptor length\n");
error = -EIO;
diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c
index cd85205..df4bea9 100644
--- a/drivers/input/tablet/hanwang.c
+++ b/drivers/input/tablet/hanwang.c
@@ -340,6 +340,9 @@
int error;
int i;
+ if (intf->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL);
input_dev = input_allocate_device();
if (!hanwang || !input_dev) {
diff --git a/drivers/input/tablet/kbtab.c b/drivers/input/tablet/kbtab.c
index d2ac7c2..2812f92 100644
--- a/drivers/input/tablet/kbtab.c
+++ b/drivers/input/tablet/kbtab.c
@@ -122,6 +122,9 @@
struct input_dev *input_dev;
int error = -ENOMEM;
+ if (intf->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL);
input_dev = input_allocate_device();
if (!kbtab || !input_dev)
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index f1cb051..af96ffc 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -361,6 +361,9 @@
if (iface_desc->desc.bInterfaceClass != 0xFF)
return -ENODEV;
+ if (iface_desc->desc.bNumEndpoints < 5)
+ return -ENODEV;
+
/* Use endpoint #4 (0x86). */
endpoint = &iface_desc->endpoint[4].desc;
if (endpoint->bEndpointAddress != TOUCH_ENDPOINT)
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index 2792ca3..3ed0ce1 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -27,7 +27,7 @@
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-#define W8001_MAX_LENGTH 11
+#define W8001_MAX_LENGTH 13
#define W8001_LEAD_MASK 0x80
#define W8001_LEAD_BYTE 0x80
#define W8001_TAB_MASK 0x40
diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c
index 8ba48f5..ce1a855 100644
--- a/drivers/input/touchscreen/zforce_ts.c
+++ b/drivers/input/touchscreen/zforce_ts.c
@@ -359,8 +359,8 @@
point.coord_x = point.coord_y = 0;
}
- point.state = payload[9 * i + 5] & 0x03;
- point.id = (payload[9 * i + 5] & 0xfc) >> 2;
+ point.state = payload[9 * i + 5] & 0x0f;
+ point.id = (payload[9 * i + 5] & 0xf0) >> 4;
/* determine touch major, minor and orientation */
point.area_major = max(payload[9 * i + 6],
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index 0650b2b..6b37b9f 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -3450,11 +3450,11 @@
mask, sid);
return -ENODEV;
}
- }
- dev_dbg(smmu->dev,
- "\tstream matching with %u register groups, mask 0x%x",
- smmu->num_mapping_groups, mask);
+ dev_dbg(smmu->dev,
+ "\tstream matching with %u register groups, mask 0x%x",
+ smmu->num_mapping_groups, mask);
+ }
} else {
smmu->num_mapping_groups = (id >> ID0_NUMSIDB_SHIFT) &
ID0_NUMSIDB_MASK;
diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c
index 8a0643a..6b78e13 100644
--- a/drivers/iommu/dmar.c
+++ b/drivers/iommu/dmar.c
@@ -317,7 +317,9 @@
struct pci_dev *pdev = to_pci_dev(data);
struct dmar_pci_notify_info *info;
- /* Only care about add/remove events for physical functions */
+ /* Only care about add/remove events for physical functions.
+ * For VFs we actually do the lookup based on the corresponding
+ * PF in device_to_iommu() anyway. */
if (pdev->is_virtfn)
return NOTIFY_DONE;
if (action != BUS_NOTIFY_ADD_DEVICE && action != BUS_NOTIFY_DEL_DEVICE)
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 9e97328..9730e2c 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -696,7 +696,13 @@
int i;
if (dev_is_pci(dev)) {
+ struct pci_dev *pf_pdev;
+
pdev = to_pci_dev(dev);
+ /* VFs aren't listed in scope tables; we need to look up
+ * the PF instead to find the IOMMU. */
+ pf_pdev = pci_physfn(pdev);
+ dev = &pf_pdev->dev;
segment = pci_domain_nr(pdev->bus);
} else if (ACPI_COMPANION(dev))
dev = &ACPI_COMPANION(dev)->dev;
@@ -709,6 +715,13 @@
for_each_active_dev_scope(drhd->devices,
drhd->devices_cnt, i, tmp) {
if (tmp == dev) {
+ /* For a VF use its original BDF# not that of the PF
+ * which we used for the IOMMU lookup. Strictly speaking
+ * we could do this for all PCI devices; we only need to
+ * get the BDF# from the scope table for ACPI matches. */
+ if (pdev && pdev->is_virtfn)
+ goto got_pdev;
+
*bus = drhd->devices[i].bus;
*devfn = drhd->devices[i].devfn;
goto out;
@@ -1746,7 +1759,6 @@
static void domain_exit(struct dmar_domain *domain)
{
- struct dmar_drhd_unit *drhd;
struct page *freelist = NULL;
int i;
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 808a686..fe25808 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -1378,7 +1378,7 @@
struct dma_iommu_mapping *mapping;
struct iommu_debug_device *ddev = s->private;
struct device *dev = ddev->dev;
- int ret, fast = 1;
+ int ret = 0, fast = 1;
phys_addr_t pt_phys;
mapping = arm_iommu_create_mapping(&platform_bus_type, 0, SZ_1G * 4ULL);
@@ -1764,7 +1764,7 @@
const char __user *ubuf,
size_t count, loff_t *offset)
{
- ssize_t retval;
+ ssize_t retval = -EINVAL;
char *comma1;
char buf[100];
dma_addr_t iova;
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 7f51b99..9e65d59 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -207,7 +207,7 @@
return; /* No PM support in this redistributor */
}
- while (count--) {
+ while (--count) {
val = readl_relaxed(rbase + GICR_WAKER);
if (enable ^ (val & GICR_WAKER_ChildrenAsleep))
break;
@@ -472,6 +472,13 @@
uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
gic_write_eoir(irqnr);
#ifdef CONFIG_SMP
+ /*
+ * Unlike GICv2, we don't need an smp_rmb() here.
+ * The control dependency from gic_read_iar to
+ * the ISB in gic_write_eoir is enough to ensure
+ * that any shared data read by handle_IPI will
+ * be read after the ACK.
+ */
handle_IPI(irqnr, regs);
#else
WARN_ONCE(true, "Unexpected SGI received!\n");
@@ -669,15 +676,19 @@
return tlist;
}
+#define MPIDR_TO_SGI_AFFINITY(cluster_id, level) \
+ (MPIDR_AFFINITY_LEVEL(cluster_id, level) \
+ << ICC_SGI1R_AFFINITY_## level ##_SHIFT)
+
static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq)
{
u64 val;
- val = (MPIDR_AFFINITY_LEVEL(cluster_id, 3) << 48 |
- MPIDR_AFFINITY_LEVEL(cluster_id, 2) << 32 |
- irq << 24 |
- MPIDR_AFFINITY_LEVEL(cluster_id, 1) << 16 |
- tlist);
+ val = (MPIDR_TO_SGI_AFFINITY(cluster_id, 3) |
+ MPIDR_TO_SGI_AFFINITY(cluster_id, 2) |
+ irq << ICC_SGI1R_SGI_ID_SHIFT |
+ MPIDR_TO_SGI_AFFINITY(cluster_id, 1) |
+ tlist << ICC_SGI1R_TARGET_LIST_SHIFT);
pr_debug("CPU%d: ICC_SGI1R_EL1 %llx\n", smp_processor_id(), val);
gic_write_sgi1r(val);
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index f0365a2..84d2997 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -452,6 +452,14 @@
writel_relaxed_no_log(irqstat, cpu_base + GIC_CPU_EOI);
uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
#ifdef CONFIG_SMP
+ /*
+ * Ensure any shared data written by the CPU sending
+ * the IPI is read after we've read the ACK register
+ * on the GIC.
+ *
+ * Pairs with the write barrier in gic_raise_softirq
+ */
+ smp_rmb();
handle_IPI(irqnr, regs);
#endif
continue;
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index aecec6d..7f1c625 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -2317,6 +2317,9 @@
return -ENODEV;
}
+ if (hostif->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
dev_info(&udev->dev,
"%s: Device matched (Vendor: 0x%x, Product: 0x%x)\n",
__func__, le16_to_cpu(udev->descriptor.idVendor),
diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c
index ccd7d85..a77eea5 100644
--- a/drivers/isdn/hardware/mISDN/mISDNipac.c
+++ b/drivers/isdn/hardware/mISDN/mISDNipac.c
@@ -754,10 +754,10 @@
}
static int
-open_dchannel(struct isac_hw *isac, struct channel_req *rq)
+open_dchannel_caller(struct isac_hw *isac, struct channel_req *rq, void *caller)
{
pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__,
- isac->dch.dev.id, __builtin_return_address(1));
+ isac->dch.dev.id, caller);
if (rq->protocol != ISDN_P_TE_S0)
return -EINVAL;
if (rq->adr.channel == 1)
@@ -771,6 +771,12 @@
return 0;
}
+static int
+open_dchannel(struct isac_hw *isac, struct channel_req *rq)
+{
+ return open_dchannel_caller(isac, rq, __builtin_return_address(0));
+}
+
static const char *ISACVer[] =
{"2086/2186 V1.1", "2085 B1", "2085 B2",
"2085 V2.3"};
@@ -1548,7 +1554,7 @@
case OPEN_CHANNEL:
rq = arg;
if (rq->protocol == ISDN_P_TE_S0)
- err = open_dchannel(isac, rq);
+ err = open_dchannel_caller(isac, rq, __builtin_return_address(0));
else
err = open_bchannel(ipac, rq);
if (err)
diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c
index de69f68..7416755 100644
--- a/drivers/isdn/hardware/mISDN/w6692.c
+++ b/drivers/isdn/hardware/mISDN/w6692.c
@@ -1176,10 +1176,10 @@
}
static int
-open_dchannel(struct w6692_hw *card, struct channel_req *rq)
+open_dchannel(struct w6692_hw *card, struct channel_req *rq, void *caller)
{
pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__,
- card->dch.dev.id, __builtin_return_address(1));
+ card->dch.dev.id, caller);
if (rq->protocol != ISDN_P_TE_S0)
return -EINVAL;
if (rq->adr.channel == 1)
@@ -1207,7 +1207,7 @@
case OPEN_CHANNEL:
rq = arg;
if (rq->protocol == ISDN_P_TE_S0)
- err = open_dchannel(card, rq);
+ err = open_dchannel(card, rq, __builtin_return_address(0));
else
err = open_bchannel(card, rq);
if (err)
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
index dd93230..5675e77 100644
--- a/drivers/leds/leds-qpnp-flash.c
+++ b/drivers/leds/leds-qpnp-flash.c
@@ -523,14 +523,12 @@
goto unlock_mutex;
}
- ret = copy_from_user(kbuf, buf, count);
- if (!ret) {
+ if (copy_from_user(kbuf, buf, count)) {
pr_err("failed to copy data from user\n");
ret = -EFAULT;
goto free_buf;
}
- count -= ret;
*ppos += count;
kbuf[count] = '\0';
val = kbuf;
@@ -3304,6 +3302,7 @@
gpio_free(led->flash_strobe);
mutex_destroy(&led->flash_led_lock);
destroy_workqueue(led->ordered_workq);
+ kfree(led);
return rc;
}
diff --git a/drivers/mcb/mcb-parse.c b/drivers/mcb/mcb-parse.c
index 0049269..b0155b0 100644
--- a/drivers/mcb/mcb-parse.c
+++ b/drivers/mcb/mcb-parse.c
@@ -57,7 +57,7 @@
mdev->id = GDD_DEV(reg1);
mdev->rev = GDD_REV(reg1);
mdev->var = GDD_VAR(reg1);
- mdev->bar = GDD_BAR(reg1);
+ mdev->bar = GDD_BAR(reg2);
mdev->group = GDD_GRP(reg2);
mdev->inst = GDD_INS(reg2);
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 92254ab..ae65b27 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -435,6 +435,21 @@
If unsure, say N.
+config DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
+ bool "Prefetch size 128"
+
+config DM_VERITY_HASH_PREFETCH_MIN_SIZE
+ int "Verity hash prefetch minimum size"
+ depends on DM_VERITY
+ range 1 4096
+ default 128 if DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
+ default 1
+ ---help---
+ This sets minimum number of hash blocks to prefetch for dm-verity.
+ For devices like eMMC, having larger prefetch size like 128 can improve
+ performance with increased memory consumption for keeping more hashes
+ in RAM.
+
config DM_ANDROID_VERITY
bool "Android verity target support"
depends on DM_VERITY
@@ -444,6 +459,7 @@
depends on KEYS
depends on ASYMMETRIC_KEY_TYPE
depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ select DM_VERITY_HASH_PREFETCH_MIN_SIZE_128
---help---
This device-mapper target is virtually a VERITY target. This
target is setup by reading the metadata contents piggybacked
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 2a10283..a7a03a2 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1851,7 +1851,7 @@
free = roundup_pow_of_two(ca->sb.nbuckets) >> 10;
if (!init_fifo(&ca->free[RESERVE_BTREE], 8, GFP_KERNEL) ||
- !init_fifo(&ca->free[RESERVE_PRIO], prio_buckets(ca), GFP_KERNEL) ||
+ !init_fifo_exact(&ca->free[RESERVE_PRIO], prio_buckets(ca), GFP_KERNEL) ||
!init_fifo(&ca->free[RESERVE_MOVINGGC], free, GFP_KERNEL) ||
!init_fifo(&ca->free[RESERVE_NONE], free, GFP_KERNEL) ||
!init_fifo(&ca->free_inc, free << 2, GFP_KERNEL) ||
@@ -1876,7 +1876,7 @@
struct block_device *bdev, struct cache *ca)
{
char name[BDEVNAME_SIZE];
- const char *err = NULL;
+ const char *err = NULL; /* must be set for any error case */
int ret = 0;
memcpy(&ca->sb, sb, sizeof(struct cache_sb));
@@ -1893,8 +1893,13 @@
ca->discard = CACHE_DISCARD(&ca->sb);
ret = cache_alloc(sb, ca);
- if (ret != 0)
+ if (ret != 0) {
+ if (ret == -ENOMEM)
+ err = "cache_alloc(): -ENOMEM";
+ else
+ err = "cache_alloc(): unknown error";
goto err;
+ }
if (kobject_add(&ca->kobj, &part_to_dev(bdev->bd_part)->kobj, "bcache")) {
err = "error calling kobject_add";
diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c
index 30f58da..ec35338 100644
--- a/drivers/md/dm-android-verity.c
+++ b/drivers/md/dm-android-verity.c
@@ -116,6 +116,12 @@
return !strncmp(buildvariant, typeuserdebug, sizeof(typeuserdebug));
}
+static inline bool is_unlocked(void)
+{
+ static const char unlocked[] = "orange";
+
+ return !strncmp(verifiedbootstate, unlocked, sizeof(unlocked));
+}
static int table_extract_mpi_array(struct public_key_signature *pks,
const void *data, size_t len)
@@ -655,6 +661,28 @@
return err;
}
+static int create_linear_device(struct dm_target *ti, dev_t dev,
+ char *target_device)
+{
+ u64 device_size = 0;
+ int err = find_size(dev, &device_size);
+
+ if (err) {
+ DMERR("error finding bdev size");
+ handle_error();
+ return err;
+ }
+
+ ti->len = device_size;
+ err = add_as_linear_device(ti, target_device);
+ if (err) {
+ handle_error();
+ return err;
+ }
+ verity_enabled = false;
+ return 0;
+}
+
/*
* Target parameters:
* <key id> Key id of the public key in the system keyring.
@@ -668,7 +696,7 @@
dev_t uninitialized_var(dev);
struct android_metadata *metadata = NULL;
int err = 0, i, mode;
- char *key_id, *table_ptr, dummy, *target_device,
+ char *key_id = NULL, *table_ptr, dummy, *target_device,
*verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS];
/* One for specifying number of opt args and one for mode */
sector_t data_sectors;
@@ -678,7 +706,6 @@
struct fec_ecc_metadata uninitialized_var(ecc);
char buf[FEC_ARG_LENGTH], *buf_ptr;
unsigned long long tmpll;
- u64 device_size;
if (argc == 1) {
/* Use the default keyid */
@@ -706,23 +733,8 @@
return -EINVAL;
}
- if (is_eng()) {
- err = find_size(dev, &device_size);
- if (err) {
- DMERR("error finding bdev size");
- handle_error();
- return err;
- }
-
- ti->len = device_size;
- err = add_as_linear_device(ti, target_device);
- if (err) {
- handle_error();
- return err;
- }
- verity_enabled = false;
- return 0;
- }
+ if (is_eng())
+ return create_linear_device(ti, dev, target_device);
strreplace(key_id, '#', ' ');
@@ -737,6 +749,11 @@
err = extract_metadata(dev, &fec, &metadata, &verity_enabled);
if (err) {
+ /* Allow invalid metadata when the device is unlocked */
+ if (is_unlocked()) {
+ DMWARN("Allow invalid metadata when unlocked");
+ return create_linear_device(ti, dev, target_device);
+ }
DMERR("Error while extracting metadata");
handle_error();
goto free_metadata;
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index c33b497..a9448e3 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -395,6 +395,7 @@
* as if GFP_NOIO was specified.
*/
+ noio_flag = 0;
if (gfp_mask & __GFP_NORETRY)
noio_flag = memalloc_noio_save();
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 9c7a083b..f6be33a 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1432,7 +1432,7 @@
unsigned i;
int err;
- cc->tfms = kmalloc(cc->tfms_count * sizeof(struct crypto_ablkcipher *),
+ cc->tfms = kzalloc(cc->tfms_count * sizeof(struct crypto_ablkcipher *),
GFP_KERNEL);
if (!cc->tfms)
return -ENOMEM;
@@ -1481,12 +1481,15 @@
if (!cc->key_size && strcmp(key, "-"))
goto out;
+ /* clear the flag since following operations may invalidate previously valid key */
+ clear_bit(DM_CRYPT_KEY_VALID, &cc->flags);
+
if (cc->key_size && crypt_decode_key(cc->key, key, cc->key_size) < 0)
goto out;
- set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
-
r = crypt_setkey_allcpus(cc);
+ if (!r)
+ set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
out:
/* Hex key string not needed after here, so wipe it. */
@@ -1897,6 +1900,13 @@
return DM_MAPIO_REMAPPED;
}
+ /*
+ * Check if bio is too large, split as needed.
+ */
+ if (unlikely(bio->bi_iter.bi_size > (BIO_MAX_PAGES << PAGE_SHIFT)) &&
+ bio_data_dir(bio) == WRITE)
+ dm_accept_partial_bio(bio, ((BIO_MAX_PAGES << PAGE_SHIFT) >> SECTOR_SHIFT));
+
io = dm_per_bio_data(bio, cc->per_bio_data_size);
crypt_io_init(io, cc, bio, dm_target_offset(ti, bio->bi_iter.bi_sector));
io->ctx.req = (struct ablkcipher_request *)(io + 1);
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c
index b257e46..768c9fd 100644
--- a/drivers/md/dm-flakey.c
+++ b/drivers/md/dm-flakey.c
@@ -287,10 +287,14 @@
pb->bio_submitted = true;
/*
- * Map reads as normal.
+ * Error reads if neither corrupt_bio_byte or drop_writes are set.
+ * Otherwise, flakey_end_io() will decide if the reads should be modified.
*/
- if (bio_data_dir(bio) == READ)
+ if (bio_data_dir(bio) == READ) {
+ if (!fc->corrupt_bio_byte && !test_bit(DROP_WRITES, &fc->flags))
+ return -EIO;
goto map_bio;
+ }
/*
* Drop writes?
@@ -326,14 +330,22 @@
struct flakey_c *fc = ti->private;
struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
- /*
- * Corrupt successful READs while in down state.
- * If flags were specified, only corrupt those that match.
- */
- if (fc->corrupt_bio_byte && !error && pb->bio_submitted &&
- (bio_data_dir(bio) == READ) && (fc->corrupt_bio_rw == READ) &&
- all_corrupt_bio_flags_match(bio, fc))
- corrupt_bio_data(bio, fc);
+ if (!error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {
+ if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == READ) &&
+ all_corrupt_bio_flags_match(bio, fc)) {
+ /*
+ * Corrupt successful matching READs while in down state.
+ */
+ corrupt_bio_data(bio, fc);
+
+ } else if (!test_bit(DROP_WRITES, &fc->flags)) {
+ /*
+ * Error read during the down_interval if drop_writes
+ * wasn't configured.
+ */
+ return -EIO;
+ }
+ }
return error;
}
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 04a4a77..0f77878 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -702,37 +702,32 @@
tgt->type = dm_get_target_type(type);
if (!tgt->type) {
- DMERR("%s: %s: unknown target type", dm_device_name(t->md),
- type);
+ DMERR("%s: %s: unknown target type", dm_device_name(t->md), type);
return -EINVAL;
}
if (dm_target_needs_singleton(tgt->type)) {
if (t->num_targets) {
- DMERR("%s: target type %s must appear alone in table",
- dm_device_name(t->md), type);
- return -EINVAL;
+ tgt->error = "singleton target type must appear alone in table";
+ goto bad;
}
t->singleton = 1;
}
if (dm_target_always_writeable(tgt->type) && !(t->mode & FMODE_WRITE)) {
- DMERR("%s: target type %s may not be included in read-only tables",
- dm_device_name(t->md), type);
- return -EINVAL;
+ tgt->error = "target type may not be included in a read-only table";
+ goto bad;
}
if (t->immutable_target_type) {
if (t->immutable_target_type != tgt->type) {
- DMERR("%s: immutable target type %s cannot be mixed with other target types",
- dm_device_name(t->md), t->immutable_target_type->name);
- return -EINVAL;
+ tgt->error = "immutable target type cannot be mixed with other target types";
+ goto bad;
}
} else if (dm_target_is_immutable(tgt->type)) {
if (t->num_targets) {
- DMERR("%s: immutable target type %s cannot be mixed with other target types",
- dm_device_name(t->md), tgt->type->name);
- return -EINVAL;
+ tgt->error = "immutable target type cannot be mixed with other target types";
+ goto bad;
}
t->immutable_target_type = tgt->type;
}
@@ -747,7 +742,6 @@
*/
if (!adjoin(t, tgt)) {
tgt->error = "Gap in table";
- r = -EINVAL;
goto bad;
}
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index f431f59..7263001 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -501,6 +501,7 @@
container_of(work, struct dm_verity_prefetch_work, work);
struct dm_verity *v = pw->v;
int i;
+ sector_t prefetch_size;
for (i = v->levels - 2; i >= 0; i--) {
sector_t hash_block_start;
@@ -523,8 +524,14 @@
hash_block_end = v->hash_blocks - 1;
}
no_prefetch_cluster:
+ // for emmc, it is more efficient to send bigger read
+ prefetch_size = max((sector_t)CONFIG_DM_VERITY_HASH_PREFETCH_MIN_SIZE,
+ hash_block_end - hash_block_start + 1);
+ if ((hash_block_start + prefetch_size) >= (v->hash_start + v->hash_blocks)) {
+ prefetch_size = hash_block_end - hash_block_start + 1;
+ }
dm_bufio_prefetch(v->bufio, hash_block_start,
- hash_block_end - hash_block_start + 1);
+ prefetch_size);
}
kfree(pw);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index b7d5b2d..e754873 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1300,11 +1300,62 @@
}
EXPORT_SYMBOL_GPL(dm_accept_partial_bio);
+/*
+ * Flush current->bio_list when the target map method blocks.
+ * This fixes deadlocks in snapshot and possibly in other targets.
+ */
+struct dm_offload {
+ struct blk_plug plug;
+ struct blk_plug_cb cb;
+};
+
+static void flush_current_bio_list(struct blk_plug_cb *cb, bool from_schedule)
+{
+ struct dm_offload *o = container_of(cb, struct dm_offload, cb);
+ struct bio_list list;
+ struct bio *bio;
+
+ INIT_LIST_HEAD(&o->cb.list);
+
+ if (unlikely(!current->bio_list))
+ return;
+
+ list = *current->bio_list;
+ bio_list_init(current->bio_list);
+
+ while ((bio = bio_list_pop(&list))) {
+ struct bio_set *bs = bio->bi_pool;
+ if (unlikely(!bs) || bs == fs_bio_set) {
+ bio_list_add(current->bio_list, bio);
+ continue;
+ }
+
+ spin_lock(&bs->rescue_lock);
+ bio_list_add(&bs->rescue_list, bio);
+ queue_work(bs->rescue_workqueue, &bs->rescue_work);
+ spin_unlock(&bs->rescue_lock);
+ }
+}
+
+static void dm_offload_start(struct dm_offload *o)
+{
+ blk_start_plug(&o->plug);
+ o->cb.callback = flush_current_bio_list;
+ list_add(&o->cb.list, ¤t->plug->cb_list);
+}
+
+static void dm_offload_end(struct dm_offload *o)
+{
+ list_del(&o->cb.list);
+ blk_finish_plug(&o->plug);
+}
+
static void __map_bio(struct dm_target_io *tio)
{
int r;
sector_t sector;
struct mapped_device *md;
+ struct dm_offload o;
struct bio *clone = &tio->clone;
struct dm_target *ti = tio->ti;
@@ -1317,7 +1368,11 @@
*/
atomic_inc(&tio->io->io_count);
sector = clone->bi_iter.bi_sector;
+
+ dm_offload_start(&o);
r = ti->type->map(ti, clone);
+ dm_offload_end(&o);
+
if (r == DM_MAPIO_REMAPPED) {
/* the bio has been remapped so dispatch it */
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 6c169f1..a4b0b4e 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -282,6 +282,8 @@
* go away inside make_request
*/
sectors = bio_sectors(bio);
+ /* bio could be mergeable after passing to underlayer */
+ bio->bi_rw &= ~REQ_NOMERGE;
mddev->pers->make_request(mddev, bio);
cpu = part_stat_lock();
@@ -7407,6 +7409,9 @@
break;
j += sectors;
+ if (j > max_sectors)
+ /* when skipping, extra large numbers can be returned. */
+ j = max_sectors;
if (j > 2)
mddev->curr_resync = j;
mddev->curr_mark_cnt = io_sectors;
@@ -7466,11 +7471,17 @@
blk_finish_plug(&plug);
wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active));
+ if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
+ !test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
+ mddev->curr_resync > 3) {
+ mddev->curr_resync_completed = mddev->curr_resync;
+ sysfs_notify(&mddev->kobj, NULL, "sync_completed");
+ }
/* tell personality that we are finished */
mddev->pers->sync_request(mddev, max_sectors, &skipped, 1);
if (!test_bit(MD_RECOVERY_CHECK, &mddev->recovery) &&
- mddev->curr_resync > 2) {
+ mddev->curr_resync > 3) {
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
if (mddev->curr_resync >= mddev->recovery_cp) {
diff --git a/drivers/md/persistent-data/dm-space-map-metadata.c b/drivers/md/persistent-data/dm-space-map-metadata.c
index 199c9cc..023e9d8 100644
--- a/drivers/md/persistent-data/dm-space-map-metadata.c
+++ b/drivers/md/persistent-data/dm-space-map-metadata.c
@@ -764,16 +764,14 @@
memcpy(&smm->sm, &bootstrap_ops, sizeof(smm->sm));
r = sm_ll_new_metadata(&smm->ll, tm);
- if (r)
- return r;
-
- if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS)
- nr_blocks = DM_SM_METADATA_MAX_BLOCKS;
- r = sm_ll_extend(&smm->ll, nr_blocks);
- if (r)
- return r;
-
+ if (!r) {
+ if (nr_blocks > DM_SM_METADATA_MAX_BLOCKS)
+ nr_blocks = DM_SM_METADATA_MAX_BLOCKS;
+ r = sm_ll_extend(&smm->ll, nr_blocks);
+ }
memcpy(&smm->sm, &ops, sizeof(smm->sm));
+ if (r)
+ return r;
/*
* Now we need to update the newly created data structures with the
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 3c8ada4..0b6fa15 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -563,7 +563,7 @@
if (best_dist_disk < 0) {
if (is_badblock(rdev, this_sector, sectors,
&first_bad, &bad_sectors)) {
- if (first_bad < this_sector)
+ if (first_bad <= this_sector)
/* Cannot use this */
continue;
best_good_sectors = first_bad - this_sector;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 644f9e5..1b49827 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1171,6 +1171,8 @@
int max_sectors;
int sectors;
+ md_write_start(mddev, bio);
+
/*
* Register the new request and wait if the reconstruction
* thread has put up a bar for new requests.
@@ -1556,8 +1558,6 @@
return;
}
- md_write_start(mddev, bio);
-
do {
/*
@@ -1578,7 +1578,25 @@
split = bio;
}
+ /*
+ * If a bio is splitted, the first part of bio will pass
+ * barrier but the bio is queued in current->bio_list (see
+ * generic_make_request). If there is a raise_barrier() called
+ * here, the second part of bio can't pass barrier. But since
+ * the first part bio isn't dispatched to underlaying disks
+ * yet, the barrier is never released, hence raise_barrier will
+ * alays wait. We have a deadlock.
+ * Note, this only happens in read path. For write path, the
+ * first part of bio is dispatched in a schedule() call
+ * (because of blk plug) or offloaded to raid10d.
+ * Quitting from the function immediately can change the bio
+ * order queued in bio_list and avoid the deadlock.
+ */
__make_request(mddev, split);
+ if (split != bio && bio_data_dir(bio) == READ) {
+ generic_make_request(bio);
+ break;
+ }
} while (split != bio);
/* In case raid10d snuck in to freeze_array */
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index cdd8770..48096d0 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6203,6 +6203,15 @@
stripe = (stripe | (stripe-1)) + 1;
mddev->queue->limits.discard_alignment = stripe;
mddev->queue->limits.discard_granularity = stripe;
+
+ /*
+ * We use 16-bit counter of active stripes in bi_phys_segments
+ * (minus one for over-loaded initialization)
+ */
+ blk_queue_max_hw_sectors(mddev->queue, 0xfffe * STRIPE_SECTORS);
+ blk_queue_max_discard_sectors(mddev->queue,
+ 0xfffe * STRIPE_SECTORS);
+
/*
* unaligned part of discard request will be ignored, so can't
* guarantee discard_zeroes_data
diff --git a/drivers/media/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c
index 082c8b5..54927f0 100644
--- a/drivers/media/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb-core/dvb_ringbuffer.c
@@ -59,7 +59,13 @@
int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
{
- return (rbuf->pread == rbuf->pwrite);
+ /* smp_load_acquire() to load write pointer on reader side
+ * this pairs with smp_store_release() in dvb_ringbuffer_write(),
+ * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
+ *
+ * for memory barriers also see Documentation/circular-buffers.txt
+ */
+ return (rbuf->pread == smp_load_acquire(&rbuf->pwrite));
}
@@ -68,7 +74,12 @@
{
ssize_t free;
- free = rbuf->pread - rbuf->pwrite;
+ /* ACCESS_ONCE() to load read pointer on writer side
+ * this pairs with smp_store_release() in dvb_ringbuffer_read(),
+ * dvb_ringbuffer_read_user(), dvb_ringbuffer_flush(),
+ * or dvb_ringbuffer_reset()
+ */
+ free = ACCESS_ONCE(rbuf->pread) - rbuf->pwrite;
if (free <= 0)
free += rbuf->size;
return free-1;
@@ -80,7 +91,11 @@
{
ssize_t avail;
- avail = rbuf->pwrite - rbuf->pread;
+ /* smp_load_acquire() to load write pointer on reader side
+ * this pairs with smp_store_release() in dvb_ringbuffer_write(),
+ * dvb_ringbuffer_write_user(), or dvb_ringbuffer_reset()
+ */
+ avail = smp_load_acquire(&rbuf->pwrite) - rbuf->pread;
if (avail < 0)
avail += rbuf->size;
return avail;
@@ -90,14 +105,25 @@
void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf)
{
- rbuf->pread = rbuf->pwrite;
+ /* dvb_ringbuffer_flush() counts as read operation
+ * smp_load_acquire() to load write pointer
+ * smp_store_release() to update read pointer, this ensures that the
+ * correct pointer is visible for subsequent dvb_ringbuffer_free()
+ * calls on other cpu cores
+ */
+ smp_store_release(&rbuf->pread, smp_load_acquire(&rbuf->pwrite));
rbuf->error = 0;
}
EXPORT_SYMBOL(dvb_ringbuffer_flush);
void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf)
{
- rbuf->pread = rbuf->pwrite = 0;
+ /* dvb_ringbuffer_reset() counts as read and write operation
+ * smp_store_release() to update read pointer
+ */
+ smp_store_release(&rbuf->pread, 0);
+ /* smp_store_release() to update write pointer */
+ smp_store_release(&rbuf->pwrite, 0);
rbuf->error = 0;
}
@@ -123,12 +149,17 @@
return -EFAULT;
buf += split;
todo -= split;
- rbuf->pread = 0;
+ /* smp_store_release() for read pointer update to ensure
+ * that buf is not overwritten until read is complete,
+ * this pairs with ACCESS_ONCE() in dvb_ringbuffer_free()
+ */
+ smp_store_release(&rbuf->pread, 0);
}
if (copy_to_user(buf, rbuf->data+rbuf->pread, todo))
return -EFAULT;
- rbuf->pread = (rbuf->pread + todo) % rbuf->size;
+ /* smp_store_release() to update read pointer, see above */
+ smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size);
return len;
}
@@ -143,11 +174,16 @@
memcpy(buf, rbuf->data+rbuf->pread, split);
buf += split;
todo -= split;
- rbuf->pread = 0;
+ /* smp_store_release() for read pointer update to ensure
+ * that buf is not overwritten until read is complete,
+ * this pairs with ACCESS_ONCE() in dvb_ringbuffer_free()
+ */
+ smp_store_release(&rbuf->pread, 0);
}
memcpy(buf, rbuf->data+rbuf->pread, todo);
- rbuf->pread = (rbuf->pread + todo) % rbuf->size;
+ /* smp_store_release() to update read pointer, see above */
+ smp_store_release(&rbuf->pread, (rbuf->pread + todo) % rbuf->size);
}
@@ -162,10 +198,16 @@
memcpy(rbuf->data+rbuf->pwrite, buf, split);
buf += split;
todo -= split;
- rbuf->pwrite = 0;
+ /* smp_store_release() for write pointer update to ensure that
+ * written data is visible on other cpu cores before the pointer
+ * update, this pairs with smp_load_acquire() in
+ * dvb_ringbuffer_empty() or dvb_ringbuffer_avail()
+ */
+ smp_store_release(&rbuf->pwrite, 0);
}
memcpy(rbuf->data+rbuf->pwrite, buf, todo);
- rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+ /* smp_store_release() for write pointer update, see above */
+ smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size);
return len;
}
@@ -186,7 +228,12 @@
return -EFAULT;
buf += split;
todo -= split;
- rbuf->pwrite = 0;
+ /* smp_store_release() for write pointer update to ensure that
+ * written data is visible on other cpu cores before the pointer
+ * update, this pairs with smp_load_acquire() in
+ * dvb_ringbuffer_empty() or dvb_ringbuffer_avail()
+ */
+ smp_store_release(&rbuf->pwrite, 0);
}
if (copy_from_user(rbuf->data + rbuf->pwrite, buf, todo)) {
@@ -194,7 +241,8 @@
return -EFAULT;
}
- rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
+ /* smp_store_release() for write pointer update, see above */
+ smp_store_release(&rbuf->pwrite, (rbuf->pwrite + todo) % rbuf->size);
return len;
}
diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
index 3d5dd3e..c133d09 100644
--- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
+++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c
@@ -335,12 +335,12 @@
static int msm_fd_vbif_error_handler(void *handle, uint32_t error)
{
struct fd_ctx *ctx;
- struct msm_fd_device *fd;
+ struct msm_fd_device *fd = NULL;
struct msm_fd_buffer *active_buf;
int ret;
if (NULL == handle) {
- dev_err(fd->dev, "FD Ctx is null, Cannot recover\n");
+ pr_err("FD Ctx is null, Cannot recover\n");
return 0;
}
ctx = (struct fd_ctx *)handle;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
index 7862c76..3659496 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c
@@ -1335,7 +1335,7 @@
uint32_t debug_start_addr = 0;
uint32_t debug_end_addr = 0;
uint32_t debug_frame_id = 0;
- enum msm_isp_buffer_state debug_state;
+ enum msm_isp_buffer_state debug_state = 0;
unsigned long flags;
struct msm_isp_bufq *bufq = NULL;
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
index 54ab560..d01e829 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c
@@ -1653,7 +1653,7 @@
dma_addr_t paddr;
struct dual_vfe_resource *dual_vfe_res = NULL;
uint32_t vfe_id = 0;
- unsigned long flags;
+ unsigned long flags = 0;
if (stream_idx >= VFE_AXI_SRC_MAX) {
pr_err("%s: Invalid stream_idx", __func__);
@@ -3234,7 +3234,7 @@
int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg)
{
int rc = 0, i, j;
- struct msm_vfe_axi_stream *stream_info;
+ struct msm_vfe_axi_stream *stream_info = NULL;
struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data;
struct msm_vfe_axi_stream_update_cmd *update_cmd = arg;
struct msm_vfe_axi_stream_cfg_update_info *update_info = NULL;
@@ -3533,9 +3533,9 @@
if (vfe_dev->vt_enable) {
msm_isp_get_avtimer_ts(ts);
- time_stamp = &ts->vt_time;
- } else {
time_stamp = &ts->buf_time;
+ } else {
+ time_stamp = &ts->event_time;
}
frame_id = vfe_dev->axi_data.
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index e012cf4..05db300 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -184,19 +184,18 @@
void msm_isp_get_timestamp(struct msm_isp_timestamp *time_stamp, struct vfe_device *vfe_dev)
{
struct timespec ts;
- ktime_get_ts(&ts);
+
+ get_monotonic_boottime(&ts);
time_stamp->event_time.tv_sec = ts.tv_sec;
time_stamp->event_time.tv_usec = ts.tv_nsec/1000;
+ /* Initialize buf_time to be boottime as well */
+ time_stamp->buf_time = time_stamp->event_time;
+
if (vfe_dev->vt_enable) {
msm_isp_get_avtimer_ts(time_stamp);
time_stamp->buf_time.tv_sec = time_stamp->vt_time.tv_sec;
time_stamp->buf_time.tv_usec = time_stamp->vt_time.tv_usec;
- } else {
- get_monotonic_boottime(&ts);
- time_stamp->buf_time.tv_sec = ts.tv_sec;
- time_stamp->buf_time.tv_usec = ts.tv_nsec/1000;
}
-
}
static inline u32 msm_isp_evt_mask_to_isp_event(u32 evt_mask)
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 0947b28..715898c 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -1168,17 +1168,19 @@
struct msm_camera_private_ioctl_arg *k_ioctl,
void __user **tmp_compat_ioctl_ptr)
{
- struct msm_camera_private_ioctl_arg *up_ioctl_ptr =
- (struct msm_camera_private_ioctl_arg *)arg;
+ struct msm_camera_private_ioctl_arg u_ioctl;
if (WARN_ON(!arg || !k_ioctl || !tmp_compat_ioctl_ptr))
return -EIO;
- k_ioctl->id = up_ioctl_ptr->id;
- k_ioctl->size = up_ioctl_ptr->size;
- k_ioctl->result = up_ioctl_ptr->result;
- k_ioctl->reserved = up_ioctl_ptr->reserved;
- *tmp_compat_ioctl_ptr = compat_ptr(up_ioctl_ptr->ioctl_ptr);
+ if (copy_from_user(&u_ioctl, (void __user *)arg, sizeof(u_ioctl)))
+ return -EFAULT;
+
+ k_ioctl->id = u_ioctl.id;
+ k_ioctl->size = u_ioctl.size;
+ k_ioctl->result = u_ioctl.result;
+ k_ioctl->reserved = u_ioctl.reserved;
+ *tmp_compat_ioctl_ptr = compat_ptr(u_ioctl.ioctl_ptr);
return 0;
}
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index b4bcf2a..44a4352 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -1203,7 +1203,7 @@
{
struct msm_vidc_cb_cmd_done cmd_done = {0};
struct hfi_profile_level profile_level = {0};
- enum hal_h264_entropy entropy;
+ enum hal_h264_entropy entropy = 0;
struct buffer_requirements buff_req = { { {0} } };
dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO[%#x]\n",
diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c
index bb27156..90047a6 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.c
+++ b/drivers/media/platform/msm/vidc/msm_smem.c
@@ -475,7 +475,7 @@
}
handle = ion_import_dma_buf(client->clnt, fd);
ret = handle == priv;
- handle ? ion_free(client->clnt, handle) : 0;
+ (!IS_ERR_OR_NULL(handle)) ? ion_free(client->clnt, handle) : 0;
return ret;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index a639c28..c50b1ba 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -1062,11 +1062,11 @@
struct v4l2_event seq_changed_event = {0};
int rc = 0;
struct hfi_device *hdev;
- u32 *ptr = NULL;
+ u32 *ptr;
if (!event_notify) {
dprintk(VIDC_WARN, "Got an empty event from hfi\n");
- goto err_bad_event;
+ return;
}
inst = get_inst(get_vidc_core(event_notify->device_id),
@@ -3638,7 +3638,7 @@
*/
int msm_comm_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb)
{
- int rc, capture_count, output_count;
+ int rc = 0, capture_count, output_count;
struct msm_vidc_core *core;
struct hfi_device *hdev;
struct {
@@ -3669,6 +3669,7 @@
temp = kzalloc(sizeof(*temp), GFP_KERNEL);
if (!temp) {
dprintk(VIDC_ERR, "Out of memory\n");
+ rc = -ENOMEM;
goto err_no_mem;
}
@@ -3723,6 +3724,7 @@
kfree(ftbs.data);
ftbs.data = NULL;
+ rc = -ENOMEM;
goto err_no_mem;
}
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 6219325..b0235d8 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -2930,7 +2930,7 @@
int num_etbs, struct vidc_frame_data etbs[],
int num_ftbs, struct vidc_frame_data ftbs[])
{
- int rc = 0, c = 0;
+ int rc = -EINVAL, c = 0;
struct hal_session *session = sess;
struct venus_hfi_device *device;
struct hfi_cmd_session_sync_process_packet pkt;
@@ -4662,4 +4662,3 @@
err_venus_hfi_init:
return rc;
}
-
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c
index 4f81b4c..df33e72 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/s3c-camif/camif-capture.c
@@ -115,7 +115,7 @@
struct cam_sensor *sensor = &camif->sensor;
int err = 0;
- if (!on == camif->sensor.power_count)
+ if (camif->sensor.power_count == !on)
err = v4l2_subdev_call(sensor->sd, core, s_power, on);
if (!err)
sensor->power_count += on ? 1 : -1;
@@ -131,7 +131,7 @@
struct cam_sensor *sensor = &camif->sensor;
int err = 0;
- if (!on == camif->sensor.stream_count)
+ if (camif->sensor.stream_count == !on)
err = v4l2_subdev_call(sensor->sd, video, s_stream, on);
if (!err)
sensor->stream_count += on ? 1 : -1;
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 165bc86..ebef91c 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -1008,6 +1008,11 @@
return !strcmp(dev_name(dev), (char *)data);
}
+static void s5p_mfc_memdev_release(struct device *dev)
+{
+ dma_release_declared_memory(dev);
+}
+
static void *mfc_get_drv_data(struct platform_device *pdev);
static int s5p_mfc_alloc_memdevs(struct s5p_mfc_dev *dev)
@@ -1020,6 +1025,9 @@
mfc_err("Not enough memory\n");
return -ENOMEM;
}
+
+ dev_set_name(dev->mem_dev_l, "%s", "s5p-mfc-l");
+ dev->mem_dev_l->release = s5p_mfc_memdev_release;
device_initialize(dev->mem_dev_l);
of_property_read_u32_array(dev->plat_dev->dev.of_node,
"samsung,mfc-l", mem_info, 2);
@@ -1037,6 +1045,9 @@
mfc_err("Not enough memory\n");
return -ENOMEM;
}
+
+ dev_set_name(dev->mem_dev_r, "%s", "s5p-mfc-r");
+ dev->mem_dev_r->release = s5p_mfc_memdev_release;
device_initialize(dev->mem_dev_r);
of_property_read_u32_array(dev->plat_dev->dev.of_node,
"samsung,mfc-r", mem_info, 2);
diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c
index b7d3c8b..47f1a48 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/vsp1/vsp1_sru.c
@@ -154,6 +154,7 @@
mutex_lock(sru->ctrls.lock);
ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0)
& (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK);
+ vsp1_sru_write(sru, VI6_SRU_CTRL0, ctrl0);
mutex_unlock(sru->ctrls.lock);
vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c
index 84fa6e9..67314c0 100644
--- a/drivers/media/rc/ir-rc5-decoder.c
+++ b/drivers/media/rc/ir-rc5-decoder.c
@@ -29,7 +29,7 @@
#define RC5_BIT_START (1 * RC5_UNIT)
#define RC5_BIT_END (1 * RC5_UNIT)
#define RC5X_SPACE (4 * RC5_UNIT)
-#define RC5_TRAILER (10 * RC5_UNIT) /* In reality, approx 100 */
+#define RC5_TRAILER (6 * RC5_UNIT) /* In reality, approx 100 */
enum rc5_state {
STATE_INACTIVE,
diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c
index 4069234..cc92ae7 100644
--- a/drivers/media/usb/airspy/airspy.c
+++ b/drivers/media/usb/airspy/airspy.c
@@ -1072,7 +1072,7 @@
if (ret) {
dev_err(s->dev, "Failed to register as video device (%d)\n",
ret);
- goto err_unregister_v4l2_dev;
+ goto err_free_controls;
}
dev_info(s->dev, "Registered as %s\n",
video_device_node_name(&s->vdev));
@@ -1081,7 +1081,6 @@
err_free_controls:
v4l2_ctrl_handler_free(&s->hdl);
-err_unregister_v4l2_dev:
v4l2_device_unregister(&s->v4l2_dev);
err_free_mem:
kfree(s);
diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
index 1950f37..d429a6d 100644
--- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
+++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c
@@ -952,8 +952,9 @@
void dvb_usbv2_disconnect(struct usb_interface *intf)
{
struct dvb_usb_device *d = usb_get_intfdata(intf);
- const char *name = d->name;
- struct device dev = d->udev->dev;
+ const char *devname = kstrdup(dev_name(&d->udev->dev), GFP_KERNEL);
+ const char *drvname = d->name;
+
dev_dbg(&d->udev->dev, "%s: bInterfaceNumber=%d\n", __func__,
intf->cur_altsetting->desc.bInterfaceNumber);
@@ -962,8 +963,9 @@
dvb_usbv2_exit(d);
- dev_info(&dev, "%s: '%s' successfully deinitialized and disconnected\n",
- KBUILD_MODNAME, name);
+ pr_info("%s: '%s:%s' successfully deinitialized and disconnected\n",
+ KBUILD_MODNAME, drvname, devname);
+ kfree(devname);
}
EXPORT_SYMBOL(dvb_usbv2_disconnect);
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index 605b090..b128624 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -680,7 +680,7 @@
struct dvb_usb_device *d = purb->context;
struct dib0700_rc_response *poll_reply;
enum rc_type protocol;
- u32 uninitialized_var(keycode);
+ u32 keycode;
u8 toggle;
deb_info("%s()\n", __func__);
@@ -722,7 +722,8 @@
poll_reply->nec.data == 0x00 &&
poll_reply->nec.not_data == 0xff) {
poll_reply->data_state = 2;
- break;
+ rc_repeat(d->rc_dev);
+ goto resubmit;
}
if ((poll_reply->nec.data ^ poll_reply->nec.not_data) != 0xff) {
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
index 733a7ff..caad3b5 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
@@ -35,42 +35,51 @@
int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type)
{
- struct hexline hx;
- u8 reset;
- int ret,pos=0;
+ struct hexline *hx;
+ u8 *buf;
+ int ret, pos = 0;
+ u16 cpu_cs_register = cypress[type].cpu_cs_register;
+
+ buf = kmalloc(sizeof(*hx), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ hx = (struct hexline *)buf;
/* stop the CPU */
- reset = 1;
- if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1)
+ buf[0] = 1;
+ if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1)
err("could not stop the USB controller CPU.");
- while ((ret = dvb_usb_get_hexline(fw,&hx,&pos)) > 0) {
- deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n",hx.addr,hx.len,hx.chk);
- ret = usb_cypress_writemem(udev,hx.addr,hx.data,hx.len);
+ while ((ret = dvb_usb_get_hexline(fw, hx, &pos)) > 0) {
+ deb_fw("writing to address 0x%04x (buffer: 0x%02x %02x)\n", hx->addr, hx->len, hx->chk);
+ ret = usb_cypress_writemem(udev, hx->addr, hx->data, hx->len);
- if (ret != hx.len) {
+ if (ret != hx->len) {
err("error while transferring firmware "
"(transferred size: %d, block size: %d)",
- ret,hx.len);
+ ret, hx->len);
ret = -EINVAL;
break;
}
}
if (ret < 0) {
err("firmware download failed at %d with %d",pos,ret);
+ kfree(buf);
return ret;
}
if (ret == 0) {
/* restart the CPU */
- reset = 0;
- if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) {
+ buf[0] = 0;
+ if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1) {
err("could not restart the USB controller CPU.");
ret = -EINVAL;
}
} else
ret = -EIO;
+ kfree(buf);
+
return ret;
}
EXPORT_SYMBOL(usb_cypress_load_firmware);
diff --git a/drivers/media/usb/usbtv/usbtv-audio.c b/drivers/media/usb/usbtv/usbtv-audio.c
index 78c12d2..5dab024 100644
--- a/drivers/media/usb/usbtv/usbtv-audio.c
+++ b/drivers/media/usb/usbtv/usbtv-audio.c
@@ -278,6 +278,9 @@
{
struct usbtv *chip = container_of(work, struct usbtv, snd_trigger);
+ if (!chip->snd)
+ return;
+
if (atomic_read(&chip->snd_stream))
usbtv_audio_start(chip);
else
@@ -378,6 +381,8 @@
void usbtv_audio_free(struct usbtv *usbtv)
{
+ cancel_work_sync(&usbtv->snd_trigger);
+
if (usbtv->snd && usbtv->udev) {
snd_card_free(usbtv->snd);
usbtv->snd = NULL;
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 3c07af9..13d1090 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -1553,6 +1553,114 @@
return buffer;
}
+static struct uvc_video_chain *uvc_alloc_chain(struct uvc_device *dev)
+{
+ struct uvc_video_chain *chain;
+
+ chain = kzalloc(sizeof(*chain), GFP_KERNEL);
+ if (chain == NULL)
+ return NULL;
+
+ INIT_LIST_HEAD(&chain->entities);
+ mutex_init(&chain->ctrl_mutex);
+ chain->dev = dev;
+ v4l2_prio_init(&chain->prio);
+
+ return chain;
+}
+
+/*
+ * Fallback heuristic for devices that don't connect units and terminals in a
+ * valid chain.
+ *
+ * Some devices have invalid baSourceID references, causing uvc_scan_chain()
+ * to fail, but if we just take the entities we can find and put them together
+ * in the most sensible chain we can think of, turns out they do work anyway.
+ * Note: This heuristic assumes there is a single chain.
+ *
+ * At the time of writing, devices known to have such a broken chain are
+ * - Acer Integrated Camera (5986:055a)
+ * - Realtek rtl157a7 (0bda:57a7)
+ */
+static int uvc_scan_fallback(struct uvc_device *dev)
+{
+ struct uvc_video_chain *chain;
+ struct uvc_entity *iterm = NULL;
+ struct uvc_entity *oterm = NULL;
+ struct uvc_entity *entity;
+ struct uvc_entity *prev;
+
+ /*
+ * Start by locating the input and output terminals. We only support
+ * devices with exactly one of each for now.
+ */
+ list_for_each_entry(entity, &dev->entities, list) {
+ if (UVC_ENTITY_IS_ITERM(entity)) {
+ if (iterm)
+ return -EINVAL;
+ iterm = entity;
+ }
+
+ if (UVC_ENTITY_IS_OTERM(entity)) {
+ if (oterm)
+ return -EINVAL;
+ oterm = entity;
+ }
+ }
+
+ if (iterm == NULL || oterm == NULL)
+ return -EINVAL;
+
+ /* Allocate the chain and fill it. */
+ chain = uvc_alloc_chain(dev);
+ if (chain == NULL)
+ return -ENOMEM;
+
+ if (uvc_scan_chain_entity(chain, oterm) < 0)
+ goto error;
+
+ prev = oterm;
+
+ /*
+ * Add all Processing and Extension Units with two pads. The order
+ * doesn't matter much, use reverse list traversal to connect units in
+ * UVC descriptor order as we build the chain from output to input. This
+ * leads to units appearing in the order meant by the manufacturer for
+ * the cameras known to require this heuristic.
+ */
+ list_for_each_entry_reverse(entity, &dev->entities, list) {
+ if (entity->type != UVC_VC_PROCESSING_UNIT &&
+ entity->type != UVC_VC_EXTENSION_UNIT)
+ continue;
+
+ if (entity->num_pads != 2)
+ continue;
+
+ if (uvc_scan_chain_entity(chain, entity) < 0)
+ goto error;
+
+ prev->baSourceID[0] = entity->id;
+ prev = entity;
+ }
+
+ if (uvc_scan_chain_entity(chain, iterm) < 0)
+ goto error;
+
+ prev->baSourceID[0] = iterm->id;
+
+ list_add_tail(&chain->list, &dev->chains);
+
+ uvc_trace(UVC_TRACE_PROBE,
+ "Found a video chain by fallback heuristic (%s).\n",
+ uvc_print_chain(chain));
+
+ return 0;
+
+error:
+ kfree(chain);
+ return -EINVAL;
+}
+
/*
* Scan the device for video chains and register video devices.
*
@@ -1575,15 +1683,10 @@
if (term->chain.next || term->chain.prev)
continue;
- chain = kzalloc(sizeof(*chain), GFP_KERNEL);
+ chain = uvc_alloc_chain(dev);
if (chain == NULL)
return -ENOMEM;
- INIT_LIST_HEAD(&chain->entities);
- mutex_init(&chain->ctrl_mutex);
- chain->dev = dev;
- v4l2_prio_init(&chain->prio);
-
term->flags |= UVC_ENTITY_FLAG_DEFAULT;
if (uvc_scan_chain(chain, term) < 0) {
@@ -1597,6 +1700,9 @@
list_add_tail(&chain->list, &dev->chains);
}
+ if (list_empty(&dev->chains))
+ uvc_scan_fallback(dev);
+
if (list_empty(&dev->chains)) {
uvc_printk(KERN_INFO, "No valid video chain found.\n");
return -1;
diff --git a/drivers/memstick/host/rtsx_usb_ms.c b/drivers/memstick/host/rtsx_usb_ms.c
index a7282b7..021e425 100644
--- a/drivers/memstick/host/rtsx_usb_ms.c
+++ b/drivers/memstick/host/rtsx_usb_ms.c
@@ -524,6 +524,7 @@
int rc;
if (!host->req) {
+ pm_runtime_get_sync(ms_dev(host));
do {
rc = memstick_next_req(msh, &host->req);
dev_dbg(ms_dev(host), "next req %d\n", rc);
@@ -544,6 +545,7 @@
host->req->error);
}
} while (!rc);
+ pm_runtime_put(ms_dev(host));
}
}
@@ -570,6 +572,7 @@
dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n",
__func__, param, value);
+ pm_runtime_get_sync(ms_dev(host));
mutex_lock(&ucr->dev_mutex);
err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_MS_CARD);
@@ -635,6 +638,7 @@
}
out:
mutex_unlock(&ucr->dev_mutex);
+ pm_runtime_put(ms_dev(host));
/* power-on delay */
if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON)
@@ -681,6 +685,7 @@
int err;
for (;;) {
+ pm_runtime_get_sync(ms_dev(host));
mutex_lock(&ucr->dev_mutex);
/* Check pending MS card changes */
@@ -703,6 +708,7 @@
}
poll_again:
+ pm_runtime_put(ms_dev(host));
if (host->eject)
break;
diff --git a/drivers/message/i2o/i2o_config.c b/drivers/message/i2o/i2o_config.c
index 04bd3b6..67ceb30 100644
--- a/drivers/message/i2o/i2o_config.c
+++ b/drivers/message/i2o/i2o_config.c
@@ -772,7 +772,7 @@
#endif
-#ifdef CONFIG_I2O_EXT_ADAPTEC
+#if defined(CONFIG_I2O_EXT_ADAPTEC) && !defined(CONFIG_64BIT)
static int i2o_cfg_passthru(unsigned long arg)
{
struct i2o_cmd_passthru __user *cmd =
@@ -1045,7 +1045,7 @@
ret = i2o_cfg_evt_get(arg, fp);
break;
-#ifdef CONFIG_I2O_EXT_ADAPTEC
+#if defined(CONFIG_I2O_EXT_ADAPTEC) && !defined(CONFIG_64BIT)
case I2OPASSTHRU:
ret = i2o_cfg_passthru(arg);
break;
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c
index 532eacab..0f8cd6b 100644
--- a/drivers/mfd/omap-usb-tll.c
+++ b/drivers/mfd/omap-usb-tll.c
@@ -269,6 +269,8 @@
if (IS_ERR(tll->ch_clk[i]))
dev_dbg(dev, "can't get clock : %s\n", clkname);
+ else
+ clk_prepare(tll->ch_clk[i]);
}
pm_runtime_put_sync(dev);
@@ -301,9 +303,12 @@
tll_dev = NULL;
spin_unlock(&tll_lock);
- for (i = 0; i < tll->nch; i++)
- if (!IS_ERR(tll->ch_clk[i]))
+ for (i = 0; i < tll->nch; i++) {
+ if (!IS_ERR(tll->ch_clk[i])) {
+ clk_unprepare(tll->ch_clk[i]);
clk_put(tll->ch_clk[i]);
+ }
+ }
pm_runtime_disable(&pdev->dev);
return 0;
@@ -421,7 +426,7 @@
if (IS_ERR(tll->ch_clk[i]))
continue;
- r = clk_prepare_enable(tll->ch_clk[i]);
+ r = clk_enable(tll->ch_clk[i]);
if (r) {
dev_err(tll_dev,
"Error enabling ch %d clock: %d\n", i, r);
@@ -449,7 +454,7 @@
for (i = 0; i < tll->nch; i++) {
if (omap_usb_mode_needs_tll(pdata->port_mode[i])) {
if (!IS_ERR(tll->ch_clk[i]))
- clk_disable_unprepare(tll->ch_clk[i]);
+ clk_disable(tll->ch_clk[i]);
}
}
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 335bf16..9f109dd 100755
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -433,7 +433,7 @@
still useful.
config BMP085
- bool
+ tristate
depends on SYSFS
config BMP085_I2C
@@ -535,11 +535,12 @@
bus. System Configuration interface is one of the possible means
of generating transactions on this bus.
-config UID_CPUTIME
- tristate "Per-UID cpu time statistics"
+config UID_SYS_STATS
+ tristate "Per-UID statistics"
depends on PROFILING
help
Per UID based cpu time statistics exported to /proc/uid_cputime
+ Per UID based io statistics exported to /proc/uid_io
config USB_EXT_TYPE_C_PERICOM
tristate "USB Type-C charger detection support using Pericom chip"
@@ -581,6 +582,12 @@
and the driver provides an API to check if this interrupt
is available on the current PMIC chip.
+config MEMORY_STATE_TIME
+ tristate "Memory freq/bandwidth time statistics"
+ depends on PROFILING
+ help
+ Memory time statistics exported to /sys/kernel/memory_state_time
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 5a4dbe9..184bf7e 100755
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -62,10 +62,11 @@
obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/
-obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o
+obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o
obj-y += qcom/
obj-$(CONFIG_TI_DRV2667) += ti_drv2667.o
obj-$(CONFIG_USB_EXT_TYPE_C_PERICOM) += type-c-pericom.o
obj-$(CONFIG_USB_EXT_TYPE_C_TI) += type-c-ti.o
obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o
obj-$(CONFIG_FPR_FPC) += fpr_FingerprintCard/
+obj-$(CONFIG_MEMORY_STATE_TIME) += memory_state_time.o
diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c
index a43053d..46272b0 100644
--- a/drivers/misc/ad525x_dpot.c
+++ b/drivers/misc/ad525x_dpot.c
@@ -215,7 +215,7 @@
*/
value = swab16(value);
- if (dpot->uid == DPOT_UID(AD5271_ID))
+ if (dpot->uid == DPOT_UID(AD5274_ID))
value = value >> 2;
return value;
default:
diff --git a/drivers/misc/cxl/irq.c b/drivers/misc/cxl/irq.c
index 6fe4027..7355d9c 100644
--- a/drivers/misc/cxl/irq.c
+++ b/drivers/misc/cxl/irq.c
@@ -272,7 +272,6 @@
void cxl_unmap_irq(unsigned int virq, void *cookie)
{
free_irq(virq, cookie);
- irq_dispose_mapping(virq);
}
static int cxl_register_one_irq(struct cxl *adapter,
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c
index 1ca94e6..e9e6f7d 100644
--- a/drivers/misc/genwqe/card_utils.c
+++ b/drivers/misc/genwqe/card_utils.c
@@ -351,17 +351,27 @@
if (copy_from_user(sgl->lpage, user_addr + user_size -
sgl->lpage_size, sgl->lpage_size)) {
rc = -EFAULT;
- goto err_out1;
+ goto err_out2;
}
}
return 0;
+ err_out2:
+ __genwqe_free_consistent(cd, PAGE_SIZE, sgl->lpage,
+ sgl->lpage_dma_addr);
+ sgl->lpage = NULL;
+ sgl->lpage_dma_addr = 0;
err_out1:
__genwqe_free_consistent(cd, PAGE_SIZE, sgl->fpage,
sgl->fpage_dma_addr);
+ sgl->fpage = NULL;
+ sgl->fpage_dma_addr = 0;
err_out:
__genwqe_free_consistent(cd, sgl->sgl_size, sgl->sgl,
sgl->sgl_dma_addr);
+ sgl->sgl = NULL;
+ sgl->sgl_dma_addr = 0;
+ sgl->sgl_size = 0;
return -ENOMEM;
}
diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c
index 18fd4c2..6f293e3 100644
--- a/drivers/misc/hdcp.c
+++ b/drivers/misc/hdcp.c
@@ -1193,7 +1193,7 @@
struct hdcp_rcvd_msg_req *req_buf;
struct hdcp_rcvd_msg_rsp *rsp_buf;
uint32_t msglen;
- char *msg;
+ char *msg = NULL;
if (!handle || !handle->qseecom_handle ||
!handle->qseecom_handle->sbuf) {
diff --git a/drivers/misc/ioc4.c b/drivers/misc/ioc4.c
index 3336ddc..8758d03 100644
--- a/drivers/misc/ioc4.c
+++ b/drivers/misc/ioc4.c
@@ -144,9 +144,9 @@
{
union ioc4_int_out int_out;
union ioc4_gpcr gpcr;
- unsigned int state, last_state = 1;
+ unsigned int state, last_state;
uint64_t start, end, period;
- unsigned int count = 0;
+ unsigned int count;
/* Enable output */
gpcr.raw = 0;
@@ -167,19 +167,20 @@
mmiowb();
/* Check square wave period averaged over some number of cycles */
- do {
- int_out.raw = readl(&idd->idd_misc_regs->int_out.raw);
- state = int_out.fields.int_out;
- if (!last_state && state) {
- count++;
- if (count == IOC4_CALIBRATE_END) {
- end = ktime_get_ns();
- break;
- } else if (count == IOC4_CALIBRATE_DISCARD)
- start = ktime_get_ns();
- }
- last_state = state;
- } while (1);
+ start = ktime_get_ns();
+ state = 1; /* make sure the first read isn't a rising edge */
+ for (count = 0; count <= IOC4_CALIBRATE_END; count++) {
+ do { /* wait for a rising edge */
+ last_state = state;
+ int_out.raw = readl(&idd->idd_misc_regs->int_out.raw);
+ state = int_out.fields.int_out;
+ } while (last_state || !state);
+
+ /* discard the first few cycles */
+ if (count == IOC4_CALIBRATE_DISCARD)
+ start = ktime_get_ns();
+ }
+ end = ktime_get_ns();
/* Calculation rearranged to preserve intermediate precision.
* Logically:
diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c
index b5abe34..da9fb01 100644
--- a/drivers/misc/lkdtm.c
+++ b/drivers/misc/lkdtm.c
@@ -103,6 +103,7 @@
CT_EXEC_USERSPACE,
CT_ACCESS_USERSPACE,
CT_WRITE_RO,
+ CT_WRITE_RO_AFTER_INIT,
CT_WRITE_KERN,
};
@@ -140,6 +141,7 @@
"EXEC_USERSPACE",
"ACCESS_USERSPACE",
"WRITE_RO",
+ "WRITE_RO_AFTER_INIT",
"WRITE_KERN",
};
@@ -162,6 +164,7 @@
static u8 data_area[EXEC_SIZE];
static const unsigned long rodata = 0xAA55AA55;
+static unsigned long ro_after_init __ro_after_init = 0x55AA5500;
module_param(recur_count, int, 0644);
MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test");
@@ -497,11 +500,28 @@
break;
}
case CT_WRITE_RO: {
- unsigned long *ptr;
+ /* Explicitly cast away "const" for the test. */
+ unsigned long *ptr = (unsigned long *)&rodata;
- ptr = (unsigned long *)&rodata;
+ pr_info("attempting bad rodata write at %p\n", ptr);
+ *ptr ^= 0xabcd1234;
- pr_info("attempting bad write at %p\n", ptr);
+ break;
+ }
+ case CT_WRITE_RO_AFTER_INIT: {
+ unsigned long *ptr = &ro_after_init;
+
+ /*
+ * Verify we were written to during init. Since an Oops
+ * is considered a "success", a failure is to just skip the
+ * real test.
+ */
+ if ((*ptr & 0xAA) != 0xAA) {
+ pr_info("%p was NOT written during init!?\n", ptr);
+ break;
+ }
+
+ pr_info("attempting bad ro_after_init write at %p\n", ptr);
*ptr ^= 0xabcd1234;
break;
@@ -811,6 +831,9 @@
int n_debugfs_entries = 1; /* Assume only the direct entry */
int i;
+ /* Make sure we can write to __ro_after_init values during __init */
+ ro_after_init |= 0xAA;
+
/* Register debugfs interface */
lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL);
if (!lkdtm_debugfs_root) {
diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c
index c11a01a..2e76fd5 100644
--- a/drivers/misc/mei/hw-txe.c
+++ b/drivers/misc/mei/hw-txe.c
@@ -973,11 +973,13 @@
hisr = mei_txe_br_reg_read(hw, HISR_REG);
aliveness = mei_txe_aliveness_get(dev);
- if (hhisr & IPC_HHIER_SEC && aliveness)
+ if (hhisr & IPC_HHIER_SEC && aliveness) {
ipc_isr = mei_txe_sec_reg_read_silent(hw,
SEC_IPC_HOST_INT_STATUS_REG);
- else
+ } else {
ipc_isr = 0;
+ hhisr &= ~IPC_HHIER_SEC;
+ }
generated = generated ||
(hisr & HISR_INT_STS_MSK) ||
diff --git a/drivers/misc/memory_state_time.c b/drivers/misc/memory_state_time.c
new file mode 100644
index 0000000..34c797a
--- /dev/null
+++ b/drivers/misc/memory_state_time.c
@@ -0,0 +1,454 @@
+/* drivers/misc/memory_state_time.c
+ *
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/hashtable.h>
+#include <linux/kconfig.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/memory-state-time.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/time.h>
+#include <linux/timekeeping.h>
+#include <linux/workqueue.h>
+
+#define KERNEL_ATTR_RO(_name) \
+static struct kobj_attribute _name##_attr = __ATTR_RO(_name)
+
+#define KERNEL_ATTR_RW(_name) \
+static struct kobj_attribute _name##_attr = \
+ __ATTR(_name, 0644, _name##_show, _name##_store)
+
+#define FREQ_HASH_BITS 4
+DECLARE_HASHTABLE(freq_hash_table, FREQ_HASH_BITS);
+
+static DEFINE_MUTEX(mem_lock);
+
+#define TAG "memory_state_time"
+#define BW_NODE "/soc/memory-state-time"
+#define FREQ_TBL "freq-tbl"
+#define BW_TBL "bw-buckets"
+#define NUM_SOURCES "num-sources"
+
+#define LOWEST_FREQ 2
+
+static int curr_bw;
+static int curr_freq;
+static u32 *bw_buckets;
+static u32 *freq_buckets;
+static int num_freqs;
+static int num_buckets;
+static int registered_bw_sources;
+static u64 last_update;
+static bool init_success;
+static struct workqueue_struct *memory_wq;
+static u32 num_sources = 10;
+static int *bandwidths;
+
+struct freq_entry {
+ int freq;
+ u64 *buckets; /* Bandwidth buckets. */
+ struct hlist_node hash;
+};
+
+struct queue_container {
+ struct work_struct update_state;
+ int value;
+ u64 time_now;
+ int id;
+ struct mutex *lock;
+};
+
+static int find_bucket(int bw)
+{
+ int i;
+
+ if (bw_buckets != NULL) {
+ for (i = 0; i < num_buckets; i++) {
+ if (bw_buckets[i] > bw) {
+ pr_debug("Found bucket %d for bandwidth %d\n",
+ i, bw);
+ return i;
+ }
+ }
+ return num_buckets - 1;
+ }
+ return 0;
+}
+
+static u64 get_time_diff(u64 time_now)
+{
+ u64 ms;
+
+ ms = time_now - last_update;
+ last_update = time_now;
+ return ms;
+}
+
+static ssize_t show_stat_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i, j;
+ int len = 0;
+ struct freq_entry *freq_entry;
+
+ for (i = 0; i < num_freqs; i++) {
+ hash_for_each_possible(freq_hash_table, freq_entry, hash,
+ freq_buckets[i]) {
+ if (freq_entry->freq == freq_buckets[i]) {
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "%d ", freq_buckets[i]);
+ if (len >= PAGE_SIZE)
+ break;
+ for (j = 0; j < num_buckets; j++) {
+ len += scnprintf(buf + len,
+ PAGE_SIZE - len,
+ "%llu ",
+ freq_entry->buckets[j]);
+ }
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "\n");
+ }
+ }
+ }
+ pr_debug("Current Time: %llu\n", ktime_get_boot_ns());
+ return len;
+}
+KERNEL_ATTR_RO(show_stat);
+
+static void update_table(u64 time_now)
+{
+ struct freq_entry *freq_entry;
+
+ pr_debug("Last known bw %d freq %d\n", curr_bw, curr_freq);
+ hash_for_each_possible(freq_hash_table, freq_entry, hash, curr_freq) {
+ if (curr_freq == freq_entry->freq) {
+ freq_entry->buckets[find_bucket(curr_bw)]
+ += get_time_diff(time_now);
+ break;
+ }
+ }
+}
+
+static bool freq_exists(int freq)
+{
+ int i;
+
+ for (i = 0; i < num_freqs; i++) {
+ if (freq == freq_buckets[i])
+ return true;
+ }
+ return false;
+}
+
+static int calculate_total_bw(int bw, int index)
+{
+ int i;
+ int total_bw = 0;
+
+ pr_debug("memory_state_time New bw %d for id %d\n", bw, index);
+ bandwidths[index] = bw;
+ for (i = 0; i < registered_bw_sources; i++)
+ total_bw += bandwidths[i];
+ return total_bw;
+}
+
+static void freq_update_do_work(struct work_struct *work)
+{
+ struct queue_container *freq_state_update
+ = container_of(work, struct queue_container,
+ update_state);
+ if (freq_state_update) {
+ mutex_lock(&mem_lock);
+ update_table(freq_state_update->time_now);
+ curr_freq = freq_state_update->value;
+ mutex_unlock(&mem_lock);
+ kfree(freq_state_update);
+ }
+}
+
+static void bw_update_do_work(struct work_struct *work)
+{
+ struct queue_container *bw_state_update
+ = container_of(work, struct queue_container,
+ update_state);
+ if (bw_state_update) {
+ mutex_lock(&mem_lock);
+ update_table(bw_state_update->time_now);
+ curr_bw = calculate_total_bw(bw_state_update->value,
+ bw_state_update->id);
+ mutex_unlock(&mem_lock);
+ kfree(bw_state_update);
+ }
+}
+
+static void memory_state_freq_update(struct memory_state_update_block *ub,
+ int value)
+{
+ if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) {
+ if (freq_exists(value) && init_success) {
+ struct queue_container *freq_container
+ = kmalloc(sizeof(struct queue_container),
+ GFP_KERNEL);
+ if (!freq_container)
+ return;
+ INIT_WORK(&freq_container->update_state,
+ freq_update_do_work);
+ freq_container->time_now = ktime_get_boot_ns();
+ freq_container->value = value;
+ pr_debug("Scheduling freq update in work queue\n");
+ queue_work(memory_wq, &freq_container->update_state);
+ } else {
+ pr_debug("Freq does not exist.\n");
+ }
+ }
+}
+
+static void memory_state_bw_update(struct memory_state_update_block *ub,
+ int value)
+{
+ if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) {
+ if (init_success) {
+ struct queue_container *bw_container
+ = kmalloc(sizeof(struct queue_container),
+ GFP_KERNEL);
+ if (!bw_container)
+ return;
+ INIT_WORK(&bw_container->update_state,
+ bw_update_do_work);
+ bw_container->time_now = ktime_get_boot_ns();
+ bw_container->value = value;
+ bw_container->id = ub->id;
+ pr_debug("Scheduling bandwidth update in work queue\n");
+ queue_work(memory_wq, &bw_container->update_state);
+ }
+ }
+}
+
+struct memory_state_update_block *memory_state_register_frequency_source(void)
+{
+ struct memory_state_update_block *block;
+
+ if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) {
+ pr_debug("Allocating frequency source\n");
+ block = kmalloc(sizeof(struct memory_state_update_block),
+ GFP_KERNEL);
+ if (!block)
+ return NULL;
+ block->update_call = memory_state_freq_update;
+ return block;
+ }
+ pr_err("Config option disabled.\n");
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(memory_state_register_frequency_source);
+
+struct memory_state_update_block *memory_state_register_bandwidth_source(void)
+{
+ struct memory_state_update_block *block;
+
+ if (IS_ENABLED(CONFIG_MEMORY_STATE_TIME)) {
+ pr_debug("Allocating bandwidth source %d\n",
+ registered_bw_sources);
+ block = kmalloc(sizeof(struct memory_state_update_block),
+ GFP_KERNEL);
+ if (!block)
+ return NULL;
+ block->update_call = memory_state_bw_update;
+ if (registered_bw_sources < num_sources) {
+ block->id = registered_bw_sources++;
+ } else {
+ pr_err("Unable to allocate source; max number reached\n");
+ kfree(block);
+ return NULL;
+ }
+ return block;
+ }
+ pr_err("Config option disabled.\n");
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(memory_state_register_bandwidth_source);
+
+/* Buckets are designated by their maximum.
+ * Returns the buckets decided by the capability of the device.
+ */
+static int get_bw_buckets(struct device *dev)
+{
+ int ret, lenb;
+ struct device_node *node = dev->of_node;
+
+ of_property_read_u32(node, NUM_SOURCES, &num_sources);
+ if (of_find_property(node, BW_TBL, &lenb)) {
+ bandwidths = devm_kzalloc(dev,
+ sizeof(*bandwidths) * num_sources, GFP_KERNEL);
+ if (!bandwidths)
+ return -ENOMEM;
+ lenb /= sizeof(*bw_buckets);
+ bw_buckets = devm_kzalloc(dev, lenb * sizeof(*bw_buckets),
+ GFP_KERNEL);
+ if (!bw_buckets) {
+ devm_kfree(dev, bandwidths);
+ return -ENOMEM;
+ }
+ ret = of_property_read_u32_array(node, BW_TBL, bw_buckets,
+ lenb);
+ if (ret < 0) {
+ devm_kfree(dev, bandwidths);
+ devm_kfree(dev, bw_buckets);
+ pr_err("Unable to read bandwidth table from device tree.\n");
+ return ret;
+ }
+ }
+ curr_bw = 0;
+ num_buckets = lenb;
+ return 0;
+}
+
+/* Adds struct freq_entry nodes to the hashtable for each compatible frequency.
+ * Returns the supported number of frequencies.
+ */
+static int freq_buckets_init(struct device *dev)
+{
+ struct freq_entry *freq_entry;
+ int i;
+ int ret, lenf;
+ struct device_node *node = dev->of_node;
+
+ if (of_find_property(node, FREQ_TBL, &lenf)) {
+ lenf /= sizeof(*freq_buckets);
+ freq_buckets = devm_kzalloc(dev, lenf * sizeof(*freq_buckets),
+ GFP_KERNEL);
+ if (!freq_buckets)
+ return -ENOMEM;
+ pr_debug("freqs found len %d\n", lenf);
+ ret = of_property_read_u32_array(node, FREQ_TBL, freq_buckets,
+ lenf);
+ if (ret < 0) {
+ devm_kfree(dev, freq_buckets);
+ pr_err("Unable to read frequency table from device tree.\n");
+ return ret;
+ }
+ pr_debug("ret freq %d\n", ret);
+ }
+ num_freqs = lenf;
+ curr_freq = freq_buckets[LOWEST_FREQ];
+
+ for (i = 0; i < num_freqs; i++) {
+ freq_entry = devm_kzalloc(dev, sizeof(struct freq_entry),
+ GFP_KERNEL);
+ if (!freq_entry)
+ return -ENOMEM;
+ freq_entry->buckets = devm_kzalloc(dev, sizeof(u64)*num_buckets,
+ GFP_KERNEL);
+ if (!freq_entry->buckets) {
+ devm_kfree(dev, freq_entry);
+ return -ENOMEM;
+ }
+ pr_debug("memory_state_time Adding freq to ht %d\n",
+ freq_buckets[i]);
+ freq_entry->freq = freq_buckets[i];
+ hash_add(freq_hash_table, &freq_entry->hash, freq_buckets[i]);
+ }
+ return 0;
+}
+
+struct kobject *memory_kobj;
+EXPORT_SYMBOL_GPL(memory_kobj);
+
+static struct attribute *memory_attrs[] = {
+ &show_stat_attr.attr,
+ NULL
+};
+
+static struct attribute_group memory_attr_group = {
+ .attrs = memory_attrs,
+};
+
+static int memory_state_time_probe(struct platform_device *pdev)
+{
+ int error;
+
+ error = get_bw_buckets(&pdev->dev);
+ if (error)
+ return error;
+ error = freq_buckets_init(&pdev->dev);
+ if (error)
+ return error;
+ last_update = ktime_get_boot_ns();
+ init_success = true;
+
+ pr_debug("memory_state_time initialized with num_freqs %d\n",
+ num_freqs);
+ return 0;
+}
+
+static const struct of_device_id match_table[] = {
+ { .compatible = "memory-state-time" },
+ {}
+};
+
+static struct platform_driver memory_state_time_driver = {
+ .probe = memory_state_time_probe,
+ .driver = {
+ .name = "memory-state-time",
+ .of_match_table = match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init memory_state_time_init(void)
+{
+ int error;
+
+ hash_init(freq_hash_table);
+ memory_wq = create_singlethread_workqueue("memory_wq");
+ if (!memory_wq) {
+ pr_err("Unable to create workqueue.\n");
+ return -EINVAL;
+ }
+ /*
+ * Create sys/kernel directory for memory_state_time.
+ */
+ memory_kobj = kobject_create_and_add(TAG, kernel_kobj);
+ if (!memory_kobj) {
+ pr_err("Unable to allocate memory_kobj for sysfs directory.\n");
+ error = -ENOMEM;
+ goto wq;
+ }
+ error = sysfs_create_group(memory_kobj, &memory_attr_group);
+ if (error) {
+ pr_err("Unable to create sysfs folder.\n");
+ goto kobj;
+ }
+
+ error = platform_driver_register(&memory_state_time_driver);
+ if (error) {
+ pr_err("Unable to register memory_state_time platform driver.\n");
+ goto group;
+ }
+ return 0;
+
+group: sysfs_remove_group(memory_kobj, &memory_attr_group);
+kobj: kobject_put(memory_kobj);
+wq: destroy_workqueue(memory_wq);
+ return error;
+}
+module_init(memory_state_time_init);
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 3221659..6aab912 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1894,7 +1894,7 @@
{
int ret = 0;
int rc = 0;
- uint32_t lstnr;
+ uint32_t lstnr = 0;
unsigned long flags;
struct qseecom_client_listener_data_irsp send_data_rsp;
struct qseecom_client_listener_data_64bit_irsp send_data_rsp_64bit;
diff --git a/drivers/misc/uid_cputime.c b/drivers/misc/uid_cputime.c
deleted file mode 100644
index c751188..0000000
--- a/drivers/misc/uid_cputime.c
+++ /dev/null
@@ -1,254 +0,0 @@
-/* drivers/misc/uid_cputime.c
- *
- * Copyright (C) 2014 - 2015 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/atomic.h>
-#include <linux/err.h>
-#include <linux/hashtable.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/proc_fs.h>
-#include <linux/profile.h>
-#include <linux/sched.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-#define UID_HASH_BITS 10
-DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
-
-static DEFINE_MUTEX(uid_lock);
-static struct proc_dir_entry *parent;
-
-struct uid_entry {
- uid_t uid;
- cputime_t utime;
- cputime_t stime;
- cputime_t active_utime;
- cputime_t active_stime;
- unsigned long long active_power;
- unsigned long long power;
- struct hlist_node hash;
-};
-
-static struct uid_entry *find_uid_entry(uid_t uid)
-{
- struct uid_entry *uid_entry;
- hash_for_each_possible(hash_table, uid_entry, hash, uid) {
- if (uid_entry->uid == uid)
- return uid_entry;
- }
- return NULL;
-}
-
-static struct uid_entry *find_or_register_uid(uid_t uid)
-{
- struct uid_entry *uid_entry;
-
- uid_entry = find_uid_entry(uid);
- if (uid_entry)
- return uid_entry;
-
- uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
- if (!uid_entry)
- return NULL;
-
- uid_entry->uid = uid;
-
- hash_add(hash_table, &uid_entry->hash, uid);
-
- return uid_entry;
-}
-
-static int uid_stat_show(struct seq_file *m, void *v)
-{
- struct uid_entry *uid_entry;
- struct task_struct *task, *temp;
- cputime_t utime;
- cputime_t stime;
- unsigned long bkt;
-
- mutex_lock(&uid_lock);
-
- hash_for_each(hash_table, bkt, uid_entry, hash) {
- uid_entry->active_stime = 0;
- uid_entry->active_utime = 0;
- uid_entry->active_power = 0;
- }
-
- read_lock(&tasklist_lock);
- do_each_thread(temp, task) {
- uid_entry = find_or_register_uid(from_kuid_munged(
- current_user_ns(), task_uid(task)));
- if (!uid_entry) {
- read_unlock(&tasklist_lock);
- mutex_unlock(&uid_lock);
- pr_err("%s: failed to find the uid_entry for uid %d\n",
- __func__, from_kuid_munged(current_user_ns(),
- task_uid(task)));
- return -ENOMEM;
- }
- /* if this task is exiting, we have already accounted for the
- * time and power.
- */
- if (task->cpu_power == ULLONG_MAX)
- continue;
- task_cputime_adjusted(task, &utime, &stime);
- uid_entry->active_utime += utime;
- uid_entry->active_stime += stime;
- uid_entry->active_power += task->cpu_power;
- } while_each_thread(temp, task);
- read_unlock(&tasklist_lock);
-
- hash_for_each(hash_table, bkt, uid_entry, hash) {
- cputime_t total_utime = uid_entry->utime +
- uid_entry->active_utime;
- cputime_t total_stime = uid_entry->stime +
- uid_entry->active_stime;
- unsigned long long total_power = uid_entry->power +
- uid_entry->active_power;
- seq_printf(m, "%d: %llu %llu %llu\n", uid_entry->uid,
- (unsigned long long)jiffies_to_msecs(
- cputime_to_jiffies(total_utime)) * USEC_PER_MSEC,
- (unsigned long long)jiffies_to_msecs(
- cputime_to_jiffies(total_stime)) * USEC_PER_MSEC,
- total_power);
- }
-
- mutex_unlock(&uid_lock);
- return 0;
-}
-
-static int uid_stat_open(struct inode *inode, struct file *file)
-{
- return single_open(file, uid_stat_show, PDE_DATA(inode));
-}
-
-static const struct file_operations uid_stat_fops = {
- .open = uid_stat_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static int uid_remove_open(struct inode *inode, struct file *file)
-{
- return single_open(file, NULL, NULL);
-}
-
-static ssize_t uid_remove_write(struct file *file,
- const char __user *buffer, size_t count, loff_t *ppos)
-{
- struct uid_entry *uid_entry;
- struct hlist_node *tmp;
- char uids[128];
- char *start_uid, *end_uid = NULL;
- long int uid_start = 0, uid_end = 0;
-
- if (count >= sizeof(uids))
- count = sizeof(uids) - 1;
-
- if (copy_from_user(uids, buffer, count))
- return -EFAULT;
-
- uids[count] = '\0';
- end_uid = uids;
- start_uid = strsep(&end_uid, "-");
-
- if (!start_uid || !end_uid)
- return -EINVAL;
-
- if (kstrtol(start_uid, 10, &uid_start) != 0 ||
- kstrtol(end_uid, 10, &uid_end) != 0) {
- return -EINVAL;
- }
- mutex_lock(&uid_lock);
-
- for (; uid_start <= uid_end; uid_start++) {
- hash_for_each_possible_safe(hash_table, uid_entry, tmp,
- hash, (uid_t)uid_start) {
- if (uid_start == uid_entry->uid) {
- hash_del(&uid_entry->hash);
- kfree(uid_entry);
- }
- }
- }
-
- mutex_unlock(&uid_lock);
- return count;
-}
-
-static const struct file_operations uid_remove_fops = {
- .open = uid_remove_open,
- .release = single_release,
- .write = uid_remove_write,
-};
-
-static int process_notifier(struct notifier_block *self,
- unsigned long cmd, void *v)
-{
- struct task_struct *task = v;
- struct uid_entry *uid_entry;
- cputime_t utime, stime;
- uid_t uid;
-
- if (!task)
- return NOTIFY_OK;
-
- mutex_lock(&uid_lock);
- uid = from_kuid_munged(current_user_ns(), task_uid(task));
- uid_entry = find_or_register_uid(uid);
- if (!uid_entry) {
- pr_err("%s: failed to find uid %d\n", __func__, uid);
- goto exit;
- }
-
- task_cputime_adjusted(task, &utime, &stime);
- uid_entry->utime += utime;
- uid_entry->stime += stime;
- uid_entry->power += task->cpu_power;
- task->cpu_power = ULLONG_MAX;
-
-exit:
- mutex_unlock(&uid_lock);
- return NOTIFY_OK;
-}
-
-static struct notifier_block process_notifier_block = {
- .notifier_call = process_notifier,
-};
-
-static int __init proc_uid_cputime_init(void)
-{
- hash_init(hash_table);
-
- parent = proc_mkdir("uid_cputime", NULL);
- if (!parent) {
- pr_err("%s: failed to create proc entry\n", __func__);
- return -ENOMEM;
- }
-
- proc_create_data("remove_uid_range", S_IWUGO, parent, &uid_remove_fops,
- NULL);
-
- proc_create_data("show_uid_stat", S_IRUGO, parent, &uid_stat_fops,
- NULL);
-
- profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
-
- return 0;
-}
-
-early_initcall(proc_uid_cputime_init);
diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c
new file mode 100644
index 0000000..f1b806f
--- /dev/null
+++ b/drivers/misc/uid_sys_stats.c
@@ -0,0 +1,515 @@
+/* drivers/misc/uid_cputime.c
+ *
+ * Copyright (C) 2014 - 2015 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/atomic.h>
+#include <linux/err.h>
+#include <linux/hashtable.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/profile.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/rtmutex.h>
+#include <linux/cpufreq.h>
+
+#define UID_HASH_BITS 10
+DECLARE_HASHTABLE(hash_table, UID_HASH_BITS);
+
+static DEFINE_RT_MUTEX(uid_lock);
+static struct proc_dir_entry *cpu_parent;
+static struct proc_dir_entry *io_parent;
+static struct proc_dir_entry *proc_parent;
+
+struct io_stats {
+ u64 read_bytes;
+ u64 write_bytes;
+ u64 rchar;
+ u64 wchar;
+ u64 fsync;
+};
+
+#define UID_STATE_FOREGROUND 0
+#define UID_STATE_BACKGROUND 1
+#define UID_STATE_BUCKET_SIZE 2
+
+#define UID_STATE_TOTAL_CURR 2
+#define UID_STATE_TOTAL_LAST 3
+#define UID_STATE_DEAD_TASKS 4
+#define UID_STATE_SIZE 5
+
+struct uid_entry {
+ uid_t uid;
+ cputime_t utime;
+ cputime_t stime;
+ cputime_t active_utime;
+ cputime_t active_stime;
+ unsigned long long active_power;
+ unsigned long long power;
+ int state;
+ struct io_stats io[UID_STATE_SIZE];
+ struct hlist_node hash;
+};
+
+static struct uid_entry *find_uid_entry(uid_t uid)
+{
+ struct uid_entry *uid_entry;
+ hash_for_each_possible(hash_table, uid_entry, hash, uid) {
+ if (uid_entry->uid == uid)
+ return uid_entry;
+ }
+ return NULL;
+}
+
+static struct uid_entry *find_or_register_uid(uid_t uid)
+{
+ struct uid_entry *uid_entry;
+
+ uid_entry = find_uid_entry(uid);
+ if (uid_entry)
+ return uid_entry;
+
+ uid_entry = kzalloc(sizeof(struct uid_entry), GFP_ATOMIC);
+ if (!uid_entry)
+ return NULL;
+
+ uid_entry->uid = uid;
+
+ hash_add(hash_table, &uid_entry->hash, uid);
+
+ return uid_entry;
+}
+
+static int uid_cputime_show(struct seq_file *m, void *v)
+{
+ struct uid_entry *uid_entry;
+ struct task_struct *task, *temp;
+ cputime_t utime;
+ cputime_t stime;
+ unsigned long bkt;
+
+ rt_mutex_lock(&uid_lock);
+
+ hash_for_each(hash_table, bkt, uid_entry, hash) {
+ uid_entry->active_stime = 0;
+ uid_entry->active_utime = 0;
+ uid_entry->active_power = 0;
+ }
+
+ read_lock(&tasklist_lock);
+ do_each_thread(temp, task) {
+ uid_entry = find_or_register_uid(from_kuid_munged(
+ current_user_ns(), task_uid(task)));
+ if (!uid_entry) {
+ read_unlock(&tasklist_lock);
+ rt_mutex_unlock(&uid_lock);
+ pr_err("%s: failed to find the uid_entry for uid %d\n",
+ __func__, from_kuid_munged(current_user_ns(),
+ task_uid(task)));
+ return -ENOMEM;
+ }
+ /* if this task is exiting, we have already accounted for the
+ * time and power.
+ */
+ if (task->cpu_power == ULLONG_MAX)
+ continue;
+ task_cputime_adjusted(task, &utime, &stime);
+ uid_entry->active_utime += utime;
+ uid_entry->active_stime += stime;
+ uid_entry->active_power += task->cpu_power;
+ } while_each_thread(temp, task);
+ read_unlock(&tasklist_lock);
+
+ hash_for_each(hash_table, bkt, uid_entry, hash) {
+ cputime_t total_utime = uid_entry->utime +
+ uid_entry->active_utime;
+ cputime_t total_stime = uid_entry->stime +
+ uid_entry->active_stime;
+ unsigned long long total_power = uid_entry->power +
+ uid_entry->active_power;
+ seq_printf(m, "%d: %llu %llu %llu\n", uid_entry->uid,
+ (unsigned long long)jiffies_to_msecs(
+ cputime_to_jiffies(total_utime)) * USEC_PER_MSEC,
+ (unsigned long long)jiffies_to_msecs(
+ cputime_to_jiffies(total_stime)) * USEC_PER_MSEC,
+ total_power);
+ }
+
+ rt_mutex_unlock(&uid_lock);
+ return 0;
+}
+
+static int uid_cputime_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, uid_cputime_show, PDE_DATA(inode));
+}
+
+static const struct file_operations uid_cputime_fops = {
+ .open = uid_cputime_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int uid_remove_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, NULL, NULL);
+}
+
+static ssize_t uid_remove_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct uid_entry *uid_entry;
+ struct hlist_node *tmp;
+ char uids[128];
+ char *start_uid, *end_uid = NULL;
+ long int start = 0, end = 0;
+ uid_t uid_start, uid_end;
+
+ if (count >= sizeof(uids))
+ count = sizeof(uids) - 1;
+
+ if (copy_from_user(uids, buffer, count))
+ return -EFAULT;
+
+ uids[count] = '\0';
+ end_uid = uids;
+ start_uid = strsep(&end_uid, "-");
+
+ if (!start_uid || !end_uid)
+ return -EINVAL;
+
+ if (kstrtol(start_uid, 10, &start) != 0 ||
+ kstrtol(end_uid, 10, &end) != 0) {
+ return -EINVAL;
+ }
+
+#define UID_T_MAX (((uid_t)~0U)-1)
+ if ((start < 0) || (end < 0) ||
+ (start > UID_T_MAX) || (end > UID_T_MAX)) {
+ return -EINVAL;
+ }
+
+ uid_start = start;
+ uid_end = end;
+
+ /* TODO need to unify uid_sys_stats interface with uid_time_in_state.
+ * Here we are reusing remove_uid_range to reduce the number of
+ * sys calls made by userspace clients, remove_uid_range removes uids
+ * from both here as well as from cpufreq uid_time_in_state
+ */
+ cpufreq_task_stats_remove_uids(uid_start, uid_end);
+
+ rt_mutex_lock(&uid_lock);
+
+ for (; uid_start <= uid_end; uid_start++) {
+ hash_for_each_possible_safe(hash_table, uid_entry, tmp,
+ hash, uid_start) {
+ if (uid_start == uid_entry->uid) {
+ hash_del(&uid_entry->hash);
+ kfree(uid_entry);
+ }
+ }
+ }
+
+ rt_mutex_unlock(&uid_lock);
+
+ return count;
+}
+
+static const struct file_operations uid_remove_fops = {
+ .open = uid_remove_open,
+ .release = single_release,
+ .write = uid_remove_write,
+};
+
+static u64 compute_write_bytes(struct task_struct *task)
+{
+ if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
+ return 0;
+
+ return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
+}
+
+static void add_uid_io_stats(struct uid_entry *uid_entry,
+ struct task_struct *task, int slot)
+{
+ struct io_stats *io_slot = &uid_entry->io[slot];
+
+ io_slot->read_bytes += task->ioac.read_bytes;
+ io_slot->write_bytes += compute_write_bytes(task);
+ io_slot->rchar += task->ioac.rchar;
+ io_slot->wchar += task->ioac.wchar;
+ io_slot->fsync += task->ioac.syscfs;
+}
+
+static void compute_uid_io_bucket_stats(struct io_stats *io_bucket,
+ struct io_stats *io_curr,
+ struct io_stats *io_last,
+ struct io_stats *io_dead)
+{
+ io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes -
+ io_last->read_bytes;
+ io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes -
+ io_last->write_bytes;
+ io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar;
+ io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar;
+ io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync;
+
+ io_last->read_bytes = io_curr->read_bytes;
+ io_last->write_bytes = io_curr->write_bytes;
+ io_last->rchar = io_curr->rchar;
+ io_last->wchar = io_curr->wchar;
+ io_last->fsync = io_curr->fsync;
+
+ memset(io_dead, 0, sizeof(struct io_stats));
+}
+
+static void update_io_stats_all_locked(void)
+{
+ struct uid_entry *uid_entry;
+ struct task_struct *task, *temp;
+ struct user_namespace *user_ns = current_user_ns();
+ unsigned long bkt;
+ uid_t uid;
+
+ hash_for_each(hash_table, bkt, uid_entry, hash)
+ memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
+ sizeof(struct io_stats));
+
+ rcu_read_lock();
+ do_each_thread(temp, task) {
+ uid = from_kuid_munged(user_ns, task_uid(task));
+ uid_entry = find_or_register_uid(uid);
+ if (!uid_entry)
+ continue;
+ add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
+ } while_each_thread(temp, task);
+ rcu_read_unlock();
+
+ hash_for_each(hash_table, bkt, uid_entry, hash) {
+ compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state],
+ &uid_entry->io[UID_STATE_TOTAL_CURR],
+ &uid_entry->io[UID_STATE_TOTAL_LAST],
+ &uid_entry->io[UID_STATE_DEAD_TASKS]);
+ }
+}
+
+static void update_io_stats_uid_locked(struct uid_entry *uid_entry)
+{
+ struct task_struct *task, *temp;
+ struct user_namespace *user_ns = current_user_ns();
+
+ memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
+ sizeof(struct io_stats));
+
+ rcu_read_lock();
+ do_each_thread(temp, task) {
+ if (from_kuid_munged(user_ns, task_uid(task)) != uid_entry->uid)
+ continue;
+ add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR);
+ } while_each_thread(temp, task);
+ rcu_read_unlock();
+
+ compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state],
+ &uid_entry->io[UID_STATE_TOTAL_CURR],
+ &uid_entry->io[UID_STATE_TOTAL_LAST],
+ &uid_entry->io[UID_STATE_DEAD_TASKS]);
+}
+
+static int uid_io_show(struct seq_file *m, void *v)
+{
+ struct uid_entry *uid_entry;
+ unsigned long bkt;
+
+ rt_mutex_lock(&uid_lock);
+
+ update_io_stats_all_locked();
+
+ hash_for_each(hash_table, bkt, uid_entry, hash) {
+ seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
+ uid_entry->uid,
+ uid_entry->io[UID_STATE_FOREGROUND].rchar,
+ uid_entry->io[UID_STATE_FOREGROUND].wchar,
+ uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
+ uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
+ uid_entry->io[UID_STATE_BACKGROUND].rchar,
+ uid_entry->io[UID_STATE_BACKGROUND].wchar,
+ uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
+ uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
+ uid_entry->io[UID_STATE_FOREGROUND].fsync,
+ uid_entry->io[UID_STATE_BACKGROUND].fsync);
+ }
+
+ rt_mutex_unlock(&uid_lock);
+
+ return 0;
+}
+
+static int uid_io_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, uid_io_show, PDE_DATA(inode));
+}
+
+static const struct file_operations uid_io_fops = {
+ .open = uid_io_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int uid_procstat_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, NULL, NULL);
+}
+
+static ssize_t uid_procstat_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct uid_entry *uid_entry;
+ uid_t uid;
+ int argc, state;
+ char input[128];
+
+ if (count >= sizeof(input))
+ return -EINVAL;
+
+ if (copy_from_user(input, buffer, count))
+ return -EFAULT;
+
+ input[count] = '\0';
+
+ argc = sscanf(input, "%u %d", &uid, &state);
+ if (argc != 2)
+ return -EINVAL;
+
+ if (state != UID_STATE_BACKGROUND && state != UID_STATE_FOREGROUND)
+ return -EINVAL;
+
+ rt_mutex_lock(&uid_lock);
+
+ uid_entry = find_or_register_uid(uid);
+ if (!uid_entry) {
+ rt_mutex_unlock(&uid_lock);
+ return -EINVAL;
+ }
+
+ if (uid_entry->state == state) {
+ rt_mutex_unlock(&uid_lock);
+ return count;
+ }
+
+ update_io_stats_uid_locked(uid_entry);
+
+ uid_entry->state = state;
+
+ rt_mutex_unlock(&uid_lock);
+
+ return count;
+}
+
+static const struct file_operations uid_procstat_fops = {
+ .open = uid_procstat_open,
+ .release = single_release,
+ .write = uid_procstat_write,
+};
+
+static int process_notifier(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ struct task_struct *task = v;
+ struct uid_entry *uid_entry;
+ cputime_t utime, stime;
+ uid_t uid;
+
+ if (!task)
+ return NOTIFY_OK;
+
+ rt_mutex_lock(&uid_lock);
+ uid = from_kuid_munged(current_user_ns(), task_uid(task));
+ uid_entry = find_or_register_uid(uid);
+ if (!uid_entry) {
+ pr_err("%s: failed to find uid %d\n", __func__, uid);
+ goto exit;
+ }
+
+ task_cputime_adjusted(task, &utime, &stime);
+ uid_entry->utime += utime;
+ uid_entry->stime += stime;
+ uid_entry->power += task->cpu_power;
+ task->cpu_power = ULLONG_MAX;
+
+ add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS);
+
+exit:
+ rt_mutex_unlock(&uid_lock);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block process_notifier_block = {
+ .notifier_call = process_notifier,
+};
+
+static int __init proc_uid_sys_stats_init(void)
+{
+ hash_init(hash_table);
+
+ cpu_parent = proc_mkdir("uid_cputime", NULL);
+ if (!cpu_parent) {
+ pr_err("%s: failed to create uid_cputime proc entry\n",
+ __func__);
+ goto err;
+ }
+
+ proc_create_data("remove_uid_range", 0222, cpu_parent,
+ &uid_remove_fops, NULL);
+ proc_create_data("show_uid_stat", 0444, cpu_parent,
+ &uid_cputime_fops, NULL);
+
+ io_parent = proc_mkdir("uid_io", NULL);
+ if (!io_parent) {
+ pr_err("%s: failed to create uid_io proc entry\n",
+ __func__);
+ goto err;
+ }
+
+ proc_create_data("stats", 0444, io_parent,
+ &uid_io_fops, NULL);
+
+ proc_parent = proc_mkdir("uid_procstat", NULL);
+ if (!io_parent) {
+ pr_err("%s: failed to create uid_procstat proc entry\n",
+ __func__);
+ goto err;
+ }
+
+ proc_create_data("set", 0222, proc_parent,
+ &uid_procstat_fops, NULL);
+
+ profile_event_register(PROFILE_TASK_EXIT, &process_notifier_block);
+
+ return 0;
+
+err:
+ remove_proc_subtree("uid_cputime", NULL);
+ remove_proc_subtree("uid_io", NULL);
+ remove_proc_subtree("uid_procstat", NULL);
+ return -ENOMEM;
+}
+
+early_initcall(proc_uid_sys_stats_init);
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 4cdb395..dcc2bf9 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -809,7 +809,7 @@
{
struct mmc_blk_ioc_rpmb_data *idata;
struct mmc_blk_data *md;
- struct mmc_card *card;
+ struct mmc_card *card = NULL;
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct mmc_request mrq = {NULL};
@@ -965,7 +965,7 @@
cmd_done:
mmc_blk_put(md);
- if (card->cmdq_init)
+ if (card && card->cmdq_init)
wake_up(&card->host->cmdq_ctx.wait);
return err;
}
@@ -2492,7 +2492,7 @@
struct mmc_blk_data *md = mq->data;
struct mmc_packed *packed = mqrq->packed;
bool do_rel_wr, do_data_tag;
- u32 *packed_cmd_hdr;
+ __le32 *packed_cmd_hdr;
u8 hdr_blocks;
u8 i = 1;
@@ -2504,8 +2504,8 @@
packed_cmd_hdr = packed->cmd_hdr;
memset(packed_cmd_hdr, 0, sizeof(packed->cmd_hdr));
- packed_cmd_hdr[0] = (packed->nr_entries << 16) |
- (PACKED_CMD_WR << 8) | PACKED_CMD_VER;
+ packed_cmd_hdr[0] = cpu_to_le32((packed->nr_entries << 16) |
+ (PACKED_CMD_WR << 8) | PACKED_CMD_VER);
hdr_blocks = mmc_large_sector(card) ? 8 : 1;
/*
@@ -2519,14 +2519,14 @@
((brq->data.blocks * brq->data.blksz) >=
card->ext_csd.data_tag_unit_size);
/* Argument of CMD23 */
- packed_cmd_hdr[(i * 2)] =
+ packed_cmd_hdr[(i * 2)] = cpu_to_le32(
(do_rel_wr ? MMC_CMD23_ARG_REL_WR : 0) |
(do_data_tag ? MMC_CMD23_ARG_TAG_REQ : 0) |
- blk_rq_sectors(prq);
+ blk_rq_sectors(prq));
/* Argument of CMD18 or CMD25 */
- packed_cmd_hdr[((i * 2)) + 1] =
+ packed_cmd_hdr[((i * 2)) + 1] = cpu_to_le32(
mmc_card_blockaddr(card) ?
- blk_rq_pos(prq) : blk_rq_pos(prq) << 9;
+ blk_rq_pos(prq) : blk_rq_pos(prq) << 9);
packed->blocks += blk_rq_sectors(prq);
i++;
}
@@ -4015,11 +4015,12 @@
add_quirk_mmc, MMC_QUIRK_CMDQ_EMPTY_BEFORE_DCMD),
/*
- * Some Micron MMC cards needs longer data read timeout than
- * indicated in CSD.
+ * Some MMC cards need longer data read timeout than indicated in CSD.
*/
MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
MMC_QUIRK_LONG_READ_TIME),
+ MMC_FIXUP("008GE0", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_LONG_READ_TIME),
/*
* Some Samsung MMC cards need longer data read timeout than
@@ -4275,4 +4276,3 @@
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
-
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 52e9c21..1a74acb 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -24,7 +24,7 @@
struct mmc_packed {
struct list_head list;
- u32 cmd_hdr[1024];
+ __le32 cmd_hdr[1024];
unsigned int blocks;
u8 nr_entries;
u8 retries;
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 99c1d17..09aa4e6 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -177,7 +177,7 @@
struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
- int ret;
+ int ret = 0;
if (mmc_bus_manual_resume(host)) {
host->bus_resume_flags |= MMC_BUSRESUME_NEEDS_RESUME;
@@ -421,4 +421,3 @@
put_device(&card->dev);
}
-
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index e544173..41908bf 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1893,7 +1893,7 @@
/*
* Some cards require longer data read timeout than indicated in CSD.
* Address this by setting the read timeout to a "reasonably high"
- * value. For the cards tested, 300ms has proven enough. If necessary,
+ * value. For the cards tested, 600ms has proven enough. If necessary,
* this value can be increased if other problematic cards require this.
* Certain Hynix 5.x cards giving read timeout even with 300ms.
* Increasing further to max value (4s).
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 13ff833..790499f 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -403,6 +403,9 @@
}
}
+/* Minimum partition switch timeout in milliseconds */
+#define MMC_MIN_PART_SWITCH_TIME 300
+
/*
* Decode extended CSD.
*/
@@ -467,6 +470,10 @@
/* EXT_CSD value is in units of 10ms, but we store in ms */
card->ext_csd.part_time = 10 * ext_csd[EXT_CSD_PART_SWITCH_TIME];
+ /* Some eMMC set the value too low so set a minimum */
+ if (card->ext_csd.part_time &&
+ card->ext_csd.part_time < MMC_MIN_PART_SWITCH_TIME)
+ card->ext_csd.part_time = MMC_MIN_PART_SWITCH_TIME;
/* Sleep / awake timeout in 100ns units */
if (sa_shift > 0 && sa_shift <= 0x17)
@@ -672,6 +679,11 @@
card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT];
card->ext_csd.cmdq_support = ext_csd[EXT_CSD_CMDQ_SUPPORT];
card->ext_csd.fw_version = ext_csd[EXT_CSD_FW_VERSION];
+ card->ext_csd.pre_eol_info = ext_csd[EXT_CSD_PRE_EOL_INFO];
+ card->ext_csd.lifetime_est_a =
+ ext_csd[EXT_CSD_DEV_LIFE_TIME_EST_TYP_A];
+ card->ext_csd.lifetime_est_b =
+ ext_csd[EXT_CSD_DEV_LIFE_TIME_EST_TYP_B];
pr_info("%s: eMMC FW version: 0x%02x\n",
mmc_hostname(card->host),
card->ext_csd.fw_version);
@@ -804,6 +816,10 @@
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(prv, "0x%x\n", card->cid.prv);
+MMC_DEV_ATTR(rev, "%u\n", card->ext_csd.rev);
+MMC_DEV_ATTR(pre_eol_info, "%u\n", card->ext_csd.pre_eol_info);
+MMC_DEV_ATTR(lifetime_est_a, "%u\n", card->ext_csd.lifetime_est_a);
+MMC_DEV_ATTR(lifetime_est_b, "%u\n", card->ext_csd.lifetime_est_b);
MMC_DEV_ATTR(serial, "0x%08x\n", card->cid.serial);
MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
card->ext_csd.enhanced_area_offset);
@@ -825,6 +841,10 @@
&dev_attr_name.attr,
&dev_attr_oemid.attr,
&dev_attr_prv.attr,
+ &dev_attr_rev.attr,
+ &dev_attr_pre_eol_info.attr,
+ &dev_attr_lifetime_est_a.attr,
+ &dev_attr_lifetime_est_b.attr,
&dev_attr_serial.attr,
&dev_attr_enhanced_area_offset.attr,
&dev_attr_enhanced_area_size.attr,
diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c
index 30f9fb3..7733d975 100644
--- a/drivers/mmc/host/cmdq_hci.c
+++ b/drivers/mmc/host/cmdq_hci.c
@@ -777,7 +777,7 @@
unsigned long tag = 0, comp_status;
struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
unsigned long err_info = 0;
- struct mmc_request *mrq;
+ struct mmc_request *mrq = NULL;
int ret;
u32 dbr_set = 0;
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 88af827..a9e97a1 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -1138,11 +1138,6 @@
dev_dbg(sdmmc_dev(host), "%s\n", __func__);
mutex_lock(&ucr->dev_mutex);
- if (rtsx_usb_card_exclusive_check(ucr, RTSX_USB_SD_CARD)) {
- mutex_unlock(&ucr->dev_mutex);
- return;
- }
-
sd_set_power_mode(host, ios->power_mode);
sd_set_bus_width(host, ios->bus_width);
sd_set_timing(host, ios->timing, &host->ddr_mode);
@@ -1314,6 +1309,7 @@
container_of(work, struct rtsx_usb_sdmmc, led_work);
struct rtsx_ucr *ucr = host->ucr;
+ pm_runtime_get_sync(sdmmc_dev(host));
mutex_lock(&ucr->dev_mutex);
if (host->led.brightness == LED_OFF)
@@ -1322,6 +1318,7 @@
rtsx_usb_turn_on_led(ucr);
mutex_unlock(&ucr->dev_mutex);
+ pm_runtime_put(sdmmc_dev(host));
}
#endif
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index 9cccc0e..1de7056 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -180,7 +180,8 @@
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
.chip = &sdhci_acpi_chip_int,
.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
- MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR,
+ MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR |
+ MMC_CAP_WAIT_WHILE_BUSY,
.caps2 = MMC_CAP2_HC_ERASE_SZ,
.flags = SDHCI_ACPI_RUNTIME_PM,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC,
@@ -190,7 +191,8 @@
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
- .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
+ .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD |
+ MMC_CAP_WAIT_WHILE_BUSY,
.flags = SDHCI_ACPI_RUNTIME_PM,
.pm_caps = MMC_PM_KEEP_POWER,
.probe_slot = sdhci_acpi_sdio_probe_slot,
@@ -201,6 +203,7 @@
SDHCI_ACPI_RUNTIME_PM,
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
SDHCI_QUIRK2_STOP_WITH_TC,
+ .caps = MMC_CAP_WAIT_WHILE_BUSY,
.probe_slot = sdhci_acpi_sd_probe_slot,
};
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 587ee0e..4f1afb7 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -824,6 +824,7 @@
switch (uhs) {
case MMC_TIMING_UHS_SDR50:
+ case MMC_TIMING_UHS_DDR50:
pinctrl = imx_data->pins_100mhz;
break;
case MMC_TIMING_UHS_SDR104:
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 375f1b6..35a8d68 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1410,7 +1410,9 @@
return;
}
timeout--;
- udelay(1);
+ spin_unlock_irq(&host->lock);
+ usleep_range(900, 1100);
+ spin_lock_irq(&host->lock);
}
clk |= SDHCI_CLOCK_CARD_EN;
@@ -2498,7 +2500,27 @@
ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+ sdhci_do_reset(host, SDHCI_RESET_CMD);
+ sdhci_do_reset(host, SDHCI_RESET_DATA);
+
err = -EIO;
+
+ if (cmd.opcode != MMC_SEND_TUNING_BLOCK_HS200)
+ goto out;
+
+ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = MMC_STOP_TRANSMISSION;
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ cmd.busy_timeout = 50;
+ mmc_wait_for_cmd(mmc, &cmd, 0);
+
+ spin_lock_irqsave(&host->lock, flags);
+
goto out;
}
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index aac659b..99a8425 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -294,7 +294,7 @@
struct mmc_data *data)
{
struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu;
- struct sunxi_idma_des *pdes_pa = (struct sunxi_idma_des *)host->sg_dma;
+ dma_addr_t next_desc = host->sg_dma;
int i, max_len = (1 << host->idma_des_size_bits);
for (i = 0; i < data->sg_len; i++) {
@@ -306,8 +306,9 @@
else
pdes[i].buf_size = data->sg[i].length;
+ next_desc += sizeof(struct sunxi_idma_des);
pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]);
- pdes[i].buf_addr_ptr2 = (u32)&pdes_pa[i + 1];
+ pdes[i].buf_addr_ptr2 = (u32)next_desc;
}
pdes[0].config |= SDXC_IDMAC_DES0_FD;
diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c
index d2c386f..1d84335 100644
--- a/drivers/mmc/host/ushc.c
+++ b/drivers/mmc/host/ushc.c
@@ -426,6 +426,9 @@
struct ushc_data *ushc;
int ret;
+ if (intf->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev);
if (mmc == NULL)
return -ENOMEM;
diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c
index 8057f52..dd432f9 100644
--- a/drivers/mtd/bcm47xxpart.c
+++ b/drivers/mtd/bcm47xxpart.c
@@ -183,12 +183,10 @@
last_trx_part = curr_part - 1;
- /*
- * We have whole TRX scanned, skip to the next part. Use
- * roundown (not roundup), as the loop will increase
- * offset in next step.
- */
- offset = rounddown(offset + trx->length, blocksize);
+ /* Jump to the end of TRX */
+ offset = roundup(offset + trx->length, blocksize);
+ /* Next loop iteration will increase the offset */
+ offset -= blocksize;
continue;
}
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index 9f02c28..6bc1f94 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -110,6 +110,7 @@
config MTD_MAP_BANK_WIDTH_32
bool "Support 256-bit buswidth" if MTD_CFI_GEOMETRY
+ select MTD_COMPLEX_MAPPINGS if HAS_IOMEM
default n
help
If you wish to support CFI devices on a physical bus which is
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c
index 744ca5c..cf54420 100644
--- a/drivers/mtd/maps/pmcmsp-flash.c
+++ b/drivers/mtd/maps/pmcmsp-flash.c
@@ -139,15 +139,13 @@
}
msp_maps[i].bankwidth = 1;
- msp_maps[i].name = kmalloc(7, GFP_KERNEL);
+ msp_maps[i].name = kstrndup(flash_name, 7, GFP_KERNEL);
if (!msp_maps[i].name) {
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
goto cleanup_loop;
}
- msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7);
-
for (j = 0; j < pcnt; j++) {
part_name[5] = '0' + i;
part_name[7] = '0' + j;
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 3a69b1e..3e3aa17 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -97,14 +97,13 @@
if (req->cmd_flags & REQ_DISCARD)
return tr->discard(dev, block, nsect);
- switch(rq_data_dir(req)) {
- case READ:
+ if (rq_data_dir(req) == READ) {
for (; nsect > 0; nsect--, block++, buf += tr->blksize)
if (tr->readsect(dev, block, buf))
return -EIO;
rq_flush_dcache_pages(req);
return 0;
- case WRITE:
+ } else {
if (!tr->writesect)
return -EIO;
@@ -113,9 +112,6 @@
if (tr->writesect(dev, block, buf))
return -EIO;
return 0;
- default:
- printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
- return -EIO;
}
}
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
index b922c8e..0ba96f9 100644
--- a/drivers/mtd/nand/davinci_nand.c
+++ b/drivers/mtd/nand/davinci_nand.c
@@ -241,6 +241,9 @@
unsigned long flags;
u32 val;
+ /* Reset ECC hardware */
+ davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET);
+
spin_lock_irqsave(&davinci_nand_lock, flags);
/* Start 4-bit ECC calculation for read/write */
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 5b5c627..2e2da0a 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2424,7 +2424,7 @@
int cached = writelen > bytes && page != blockmask;
uint8_t *wbuf = buf;
int use_bufpoi;
- int part_pagewr = (column || writelen < (mtd->writesize - 1));
+ int part_pagewr = (column || writelen < mtd->writesize);
if (part_pagewr)
use_bufpoi = 1;
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 1e4f120..fcbe61b 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -1049,6 +1049,9 @@
goto out_detach;
}
+ /* Make device "available" before it becomes accessible via sysfs */
+ ubi_devices[ubi_num] = ubi;
+
err = uif_init(ubi, &ref);
if (err)
goto out_detach;
@@ -1093,7 +1096,6 @@
wake_up_process(ubi->bgt_thread);
spin_unlock(&ubi->wl_lock);
- ubi_devices[ubi_num] = ubi;
ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL);
return ubi_num;
@@ -1104,6 +1106,7 @@
ubi_assert(ref);
uif_close(ubi);
out_detach:
+ ubi_devices[ubi_num] = NULL;
ubi_wl_close(ubi);
ubi_free_internal_volumes(ubi);
vfree(ubi->vtbl);
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 5b9834c..96fddb0 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -426,8 +426,25 @@
pnum, vol_id, lnum);
err = -EBADMSG;
} else {
- err = -EINVAL;
- ubi_ro_mode(ubi);
+ /*
+ * Ending up here in the non-Fastmap case
+ * is a clear bug as the VID header had to
+ * be present at scan time to have it referenced.
+ * With fastmap the story is more complicated.
+ * Fastmap has the mapping info without the need
+ * of a full scan. So the LEB could have been
+ * unmapped, Fastmap cannot know this and keeps
+ * the LEB referenced.
+ * This is valid and works as the layer above UBI
+ * has to do bookkeeping about used/referenced
+ * LEBs in any case.
+ */
+ if (ubi->fast_attach) {
+ err = -EBADMSG;
+ } else {
+ err = -EINVAL;
+ ubi_ro_mode(ubi);
+ }
}
}
goto out_free;
diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c
index 263b439..627009b 100644
--- a/drivers/mtd/ubi/fastmap.c
+++ b/drivers/mtd/ubi/fastmap.c
@@ -513,10 +513,11 @@
unsigned long long ec = be64_to_cpu(ech->ec);
unmap_peb(ai, pnum);
dbg_bld("Adding PEB to free: %i", pnum);
+
if (err == UBI_IO_FF_BITFLIPS)
- add_aeb(ai, free, pnum, ec, 1);
- else
- add_aeb(ai, free, pnum, ec, 0);
+ scrub = 1;
+
+ add_aeb(ai, free, pnum, ec, scrub);
continue;
} else if (err == 0 || err == UBI_IO_BITFLIPS) {
dbg_bld("Found non empty PEB:%i in pool", pnum);
@@ -1058,6 +1059,7 @@
ubi_msg(ubi, "fastmap WL pool size: %d",
ubi->fm_wl_pool.max_size);
ubi->fm_disabled = 0;
+ ubi->fast_attach = 1;
ubi_free_vid_hdr(ubi, vh);
kfree(ech);
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index cb03899..ec27091 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -466,6 +466,7 @@
* @fm_eba_sem: allows ubi_update_fastmap() to block EBA table changes
* @fm_work: fastmap work queue
* @fm_work_scheduled: non-zero if fastmap work was scheduled
+ * @fast_attach: non-zero if UBI was attached by fastmap
*
* @used: RB-tree of used physical eraseblocks
* @erroneous: RB-tree of erroneous used physical eraseblocks
@@ -574,6 +575,7 @@
size_t fm_size;
struct work_struct fm_work;
int fm_work_scheduled;
+ int fast_attach;
/* Wear-leveling sub-system's stuff */
struct rb_root used;
diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c
index 0134ba3..3971256 100644
--- a/drivers/mtd/ubi/upd.c
+++ b/drivers/mtd/ubi/upd.c
@@ -148,11 +148,11 @@
return err;
}
- if (bytes == 0) {
- err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
- if (err)
- return err;
+ err = ubi_wl_flush(ubi, UBI_ALL, UBI_ALL);
+ if (err)
+ return err;
+ if (bytes == 0) {
err = clear_update_marker(ubi, vol, 0);
if (err)
return err;
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index 1ae17bb..3ea4c02 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -488,13 +488,6 @@
spin_unlock(&ubi->volumes_lock);
}
- /* Change volume table record */
- vtbl_rec = ubi->vtbl[vol_id];
- vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs);
- err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
- if (err)
- goto out_acc;
-
if (pebs < 0) {
for (i = 0; i < -pebs; i++) {
err = ubi_eba_unmap_leb(ubi, vol, reserved_pebs + i);
@@ -512,6 +505,24 @@
spin_unlock(&ubi->volumes_lock);
}
+ /*
+ * When we shrink a volume we have to flush all pending (erase) work.
+ * Otherwise it can happen that upon next attach UBI finds a LEB with
+ * lnum > highest_lnum and refuses to attach.
+ */
+ if (pebs < 0) {
+ err = ubi_wl_flush(ubi, vol_id, UBI_ALL);
+ if (err)
+ goto out_acc;
+ }
+
+ /* Change volume table record */
+ vtbl_rec = ubi->vtbl[vol_id];
+ vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs);
+ err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec);
+ if (err)
+ goto out_acc;
+
vol->reserved_pebs = reserved_pebs;
if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
vol->used_ebs = reserved_pebs;
diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
index 05e1aa0..2c4e54f 100644
--- a/drivers/net/can/at91_can.c
+++ b/drivers/net/can/at91_can.c
@@ -734,9 +734,10 @@
/* upper group completed, look again in lower */
if (priv->rx_next > get_mb_rx_low_last(priv) &&
- quota > 0 && mb > get_mb_rx_last(priv)) {
+ mb > get_mb_rx_last(priv)) {
priv->rx_next = get_mb_rx_first(priv);
- goto again;
+ if (quota > 0)
+ goto again;
}
return received;
diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c
index 6028582..05545761 100644
--- a/drivers/net/can/c_can/c_can.c
+++ b/drivers/net/can/c_can/c_can.c
@@ -331,9 +331,23 @@
priv->write_reg(priv, C_CAN_IFACE(MSGCTRL_REG, iface), ctrl);
- for (i = 0; i < frame->can_dlc; i += 2) {
- priv->write_reg(priv, C_CAN_IFACE(DATA1_REG, iface) + i / 2,
- frame->data[i] | (frame->data[i + 1] << 8));
+ if (priv->type == BOSCH_D_CAN) {
+ u32 data = 0, dreg = C_CAN_IFACE(DATA1_REG, iface);
+
+ for (i = 0; i < frame->can_dlc; i += 4, dreg += 2) {
+ data = (u32)frame->data[i];
+ data |= (u32)frame->data[i + 1] << 8;
+ data |= (u32)frame->data[i + 2] << 16;
+ data |= (u32)frame->data[i + 3] << 24;
+ priv->write_reg32(priv, dreg, data);
+ }
+ } else {
+ for (i = 0; i < frame->can_dlc; i += 2) {
+ priv->write_reg(priv,
+ C_CAN_IFACE(DATA1_REG, iface) + i / 2,
+ frame->data[i] |
+ (frame->data[i + 1] << 8));
+ }
}
}
@@ -401,10 +415,20 @@
} else {
int i, dreg = C_CAN_IFACE(DATA1_REG, iface);
- for (i = 0; i < frame->can_dlc; i += 2, dreg ++) {
- data = priv->read_reg(priv, dreg);
- frame->data[i] = data;
- frame->data[i + 1] = data >> 8;
+ if (priv->type == BOSCH_D_CAN) {
+ for (i = 0; i < frame->can_dlc; i += 4, dreg += 2) {
+ data = priv->read_reg32(priv, dreg);
+ frame->data[i] = data;
+ frame->data[i + 1] = data >> 8;
+ frame->data[i + 2] = data >> 16;
+ frame->data[i + 3] = data >> 24;
+ }
+ } else {
+ for (i = 0; i < frame->can_dlc; i += 2, dreg++) {
+ data = priv->read_reg(priv, dreg);
+ frame->data[i] = data;
+ frame->data[i + 1] = data >> 8;
+ }
}
}
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 573b53b..031b687 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/workqueue.h>
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/skb.h>
@@ -391,9 +392,8 @@
/*
* CAN device restart for bus-off recovery
*/
-static void can_restart(unsigned long data)
+static void can_restart(struct net_device *dev)
{
- struct net_device *dev = (struct net_device *)data;
struct can_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats;
struct sk_buff *skb;
@@ -433,6 +433,14 @@
netdev_err(dev, "Error %d during restart", err);
}
+static void can_restart_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct can_priv *priv = container_of(dwork, struct can_priv, restart_work);
+
+ can_restart(priv->dev);
+}
+
int can_restart_now(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
@@ -446,8 +454,8 @@
if (priv->state != CAN_STATE_BUS_OFF)
return -EBUSY;
- /* Runs as soon as possible in the timer context */
- mod_timer(&priv->restart_timer, jiffies);
+ cancel_delayed_work_sync(&priv->restart_work);
+ can_restart(dev);
return 0;
}
@@ -469,8 +477,8 @@
priv->can_stats.bus_off++;
if (priv->restart_ms)
- mod_timer(&priv->restart_timer,
- jiffies + (priv->restart_ms * HZ) / 1000);
+ schedule_delayed_work(&priv->restart_work,
+ msecs_to_jiffies(priv->restart_ms));
}
EXPORT_SYMBOL_GPL(can_bus_off);
@@ -577,6 +585,7 @@
return NULL;
priv = netdev_priv(dev);
+ priv->dev = dev;
if (echo_skb_max) {
priv->echo_skb_max = echo_skb_max;
@@ -586,7 +595,7 @@
priv->state = CAN_STATE_STOPPED;
- init_timer(&priv->restart_timer);
+ INIT_DELAYED_WORK(&priv->restart_work, can_restart_work);
return dev;
}
@@ -615,11 +624,17 @@
/* allow change of MTU according to the CANFD ability of the device */
switch (new_mtu) {
case CAN_MTU:
+ /* 'CANFD-only' controllers can not switch to CAN_MTU */
+ if (priv->ctrlmode_static & CAN_CTRLMODE_FD)
+ return -EINVAL;
+
priv->ctrlmode &= ~CAN_CTRLMODE_FD;
break;
case CANFD_MTU:
- if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD))
+ /* check for potential CANFD ability */
+ if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) &&
+ !(priv->ctrlmode_static & CAN_CTRLMODE_FD))
return -EINVAL;
priv->ctrlmode |= CAN_CTRLMODE_FD;
@@ -661,8 +676,6 @@
if (!netif_carrier_ok(dev))
netif_carrier_on(dev);
- setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev);
-
return 0;
}
EXPORT_SYMBOL_GPL(open_candev);
@@ -677,7 +690,7 @@
{
struct can_priv *priv = netdev_priv(dev);
- del_timer_sync(&priv->restart_timer);
+ cancel_delayed_work_sync(&priv->restart_work);
can_flush_echo_skb(dev);
}
EXPORT_SYMBOL_GPL(close_candev);
@@ -701,6 +714,38 @@
= { .len = sizeof(struct can_bittiming_const) },
};
+static int can_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+ bool is_can_fd = false;
+
+ /* Make sure that valid CAN FD configurations always consist of
+ * - nominal/arbitration bittiming
+ * - data bittiming
+ * - control mode with CAN_CTRLMODE_FD set
+ */
+
+ if (!data)
+ return 0;
+
+ if (data[IFLA_CAN_CTRLMODE]) {
+ struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
+
+ is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD;
+ }
+
+ if (is_can_fd) {
+ if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING])
+ return -EOPNOTSUPP;
+ }
+
+ if (data[IFLA_CAN_DATA_BITTIMING]) {
+ if (!is_can_fd || !data[IFLA_CAN_BITTIMING])
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int can_changelink(struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
@@ -732,19 +777,31 @@
if (data[IFLA_CAN_CTRLMODE]) {
struct can_ctrlmode *cm;
+ u32 ctrlstatic;
+ u32 maskedflags;
/* Do not allow changing controller mode while running */
if (dev->flags & IFF_UP)
return -EBUSY;
cm = nla_data(data[IFLA_CAN_CTRLMODE]);
+ ctrlstatic = priv->ctrlmode_static;
+ maskedflags = cm->flags & cm->mask;
- /* check whether changed bits are allowed to be modified */
- if (cm->mask & ~priv->ctrlmode_supported)
+ /* check whether provided bits are allowed to be passed */
+ if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic))
+ return -EOPNOTSUPP;
+
+ /* do not check for static fd-non-iso if 'fd' is disabled */
+ if (!(maskedflags & CAN_CTRLMODE_FD))
+ ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO;
+
+ /* make sure static options are provided by configuration */
+ if ((maskedflags & ctrlstatic) != ctrlstatic)
return -EOPNOTSUPP;
/* clear bits to be modified and copy the flag values */
priv->ctrlmode &= ~cm->mask;
- priv->ctrlmode |= (cm->flags & cm->mask);
+ priv->ctrlmode |= maskedflags;
/* CAN_CTRLMODE_FD can only be set when driver supports FD */
if (priv->ctrlmode & CAN_CTRLMODE_FD)
@@ -880,13 +937,20 @@
return -EOPNOTSUPP;
}
+static void can_dellink(struct net_device *dev, struct list_head *head)
+{
+ return;
+}
+
static struct rtnl_link_ops can_link_ops __read_mostly = {
.kind = "can",
.maxtype = IFLA_CAN_MAX,
.policy = can_policy,
.setup = can_setup,
+ .validate = can_validate,
.newlink = can_newlink,
.changelink = can_changelink,
+ .dellink = can_dellink,
.get_size = can_get_size,
.fill_info = can_fill_info,
.get_xstats_size = can_get_xstats_size,
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 9768ba6..0a28c50 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -1341,11 +1341,10 @@
struct flexcan_priv *priv = netdev_priv(dev);
int err;
- err = flexcan_chip_disable(priv);
- if (err)
- return err;
-
if (netif_running(dev)) {
+ err = flexcan_chip_disable(priv);
+ if (err)
+ return err;
netif_stop_queue(dev);
netif_device_detach(dev);
}
@@ -1358,13 +1357,17 @@
{
struct net_device *dev = dev_get_drvdata(device);
struct flexcan_priv *priv = netdev_priv(dev);
+ int err;
priv->can.state = CAN_STATE_ERROR_ACTIVE;
if (netif_running(dev)) {
netif_device_attach(dev);
netif_start_queue(dev);
+ err = flexcan_chip_enable(priv);
+ if (err)
+ return err;
}
- return flexcan_chip_enable(priv);
+ return 0;
}
static SIMPLE_DEV_PM_OPS(flexcan_pm_ops, flexcan_suspend, flexcan_resume);
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 2445298..62143cd 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -957,7 +957,7 @@
priv->can.do_get_berr_counter = m_can_get_berr_counter;
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.1 */
- priv->can.ctrlmode = CAN_CTRLMODE_FD_NON_ISO;
+ can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
/* CAN_CTRLMODE_FD_NON_ISO can not be changed with M_CAN IP v3.0.1 */
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c
index 84a09e8..5086ec9 100644
--- a/drivers/net/ethernet/atheros/atlx/atl2.c
+++ b/drivers/net/ethernet/atheros/atlx/atl2.c
@@ -1412,7 +1412,7 @@
err = -EIO;
- netdev->hw_features = NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_RX;
+ netdev->hw_features = NETIF_F_HW_VLAN_CTAG_RX;
netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX);
/* Init PHY as early as possible due to power saving issue */
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index bb27028..936448e 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -912,7 +912,7 @@
dev->stats.tx_bytes += tx_cb_ptr->skb->len;
dma_unmap_single(&dev->dev,
dma_unmap_addr(tx_cb_ptr, dma_addr),
- tx_cb_ptr->skb->len,
+ dma_unmap_len(tx_cb_ptr, dma_len),
DMA_TO_DEVICE);
bcmgenet_free_cb(tx_cb_ptr);
} else if (dma_unmap_addr(tx_cb_ptr, dma_addr)) {
@@ -1019,7 +1019,7 @@
}
dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping);
- dma_unmap_len_set(tx_cb_ptr, dma_len, skb->len);
+ dma_unmap_len_set(tx_cb_ptr, dma_len, skb_len);
length_status = (skb_len << DMA_BUFLENGTH_SHIFT) | dma_desc_flags |
(priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT) |
DMA_TX_APPEND_CRC;
@@ -2695,7 +2695,8 @@
bcmgenet_netif_stop(dev);
- phy_suspend(priv->phydev);
+ if (!device_may_wakeup(d))
+ phy_suspend(priv->phydev);
netif_device_detach(dev);
@@ -2784,7 +2785,8 @@
netif_device_attach(dev);
- phy_resume(priv->phydev);
+ if (!device_may_wakeup(d))
+ phy_resume(priv->phydev);
bcmgenet_netif_start(dev);
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 6fa8272..63c297c 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -12021,7 +12021,7 @@
int ret;
u32 offset, len, b_offset, odd_len;
u8 *buf;
- __be32 start, end;
+ __be32 start = 0, end;
if (tg3_flag(tp, NO_NVRAM) ||
eeprom->magic != TG3_EEPROM_MAGIC)
diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c
index 3b42556..88d2af8 100644
--- a/drivers/net/ethernet/dec/tulip/tulip_core.c
+++ b/drivers/net/ethernet/dec/tulip/tulip_core.c
@@ -98,8 +98,7 @@
#elif defined(__mips__)
static int csr0 = 0x00200000 | 0x4000;
#else
-#warning Processor architecture undefined!
-static int csr0 = 0x00A00000 | 0x4800;
+static int csr0;
#endif
/* Operational parameters that usually are not changed. */
@@ -1982,6 +1981,12 @@
pr_info("%s", version);
#endif
+ if (!csr0) {
+ pr_warn("tulip: unknown CPU architecture, using default csr0\n");
+ /* default to 8 longword cache line alignment */
+ csr0 = 0x00A00000 | 0x4800;
+ }
+
/* copy module parms into globals */
tulip_rx_copybreak = rx_copybreak;
tulip_max_interrupt_work = max_interrupt_work;
diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c
index 4061f9b..19063ac 100644
--- a/drivers/net/ethernet/dec/tulip/uli526x.c
+++ b/drivers/net/ethernet/dec/tulip/uli526x.c
@@ -1115,7 +1115,7 @@
netif_carrier_off(dev);
}
}
- db->init=0;
+ db->init = 0;
/* Timer active again */
db->timer.expires = ULI526X_TIMER_WUT;
diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c
index 6aa887e..0b080ee 100644
--- a/drivers/net/ethernet/dec/tulip/winbond-840.c
+++ b/drivers/net/ethernet/dec/tulip/winbond-840.c
@@ -907,7 +907,7 @@
#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC)
i |= 0x4800;
#else
-#warning Processor architecture undefined
+ dev_warn(&dev->dev, "unknown CPU architecture, using default csr0 setting\n");
i |= 0x4800;
#endif
iowrite32(i, ioaddr + PCIBusCfg);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 96ba23e..51f6529 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -1539,9 +1539,15 @@
struct fec_enet_private *fep = netdev_priv(ndev);
for_each_set_bit(queue_id, &fep->work_rx, FEC_ENET_MAX_RX_QS) {
- clear_bit(queue_id, &fep->work_rx);
- pkt_received += fec_enet_rx_queue(ndev,
+ int ret;
+
+ ret = fec_enet_rx_queue(ndev,
budget - pkt_received, queue_id);
+
+ if (ret < budget - pkt_received)
+ clear_bit(queue_id, &fep->work_rx);
+
+ pkt_received += ret;
}
return pkt_received;
}
@@ -3160,6 +3166,7 @@
/* setup board info structure */
fep = netdev_priv(ndev);
+ fep->netdev = ndev;
fep->num_rx_queues = num_rx_qs;
fep->num_tx_queues = num_tx_qs;
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index 48b74a5..feb6184 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -983,7 +983,7 @@
u16 max_snoop, max_nosnoop;
u16 max_ltr_enc; /* max LTR latency encoded */
s64 lat_ns; /* latency (ns) */
- s64 value;
+ u64 value;
u32 rxa;
if (!hw->adapter->max_frame_size) {
@@ -1010,12 +1010,13 @@
*/
lat_ns = ((s64)rxa * 1024 -
(2 * (s64)hw->adapter->max_frame_size)) * 8 * 1000;
- if (lat_ns < 0)
- lat_ns = 0;
- else
- do_div(lat_ns, speed);
+ if (lat_ns < 0) {
+ value = 0;
+ } else {
+ value = lat_ns;
+ do_div(value, speed);
+ }
- value = lat_ns;
while (value > PCI_LTR_VALUE_MASK) {
scale++;
value = DIV_ROUND_UP(value, (1 << 5));
diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
index 7067f4b..2a42ae0 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c
@@ -773,7 +773,7 @@
{
struct i40e_tx_desc *txd;
union i40e_rx_desc *rxd;
- struct i40e_ring ring;
+ struct i40e_ring *ring;
struct i40e_vsi *vsi;
int i;
@@ -792,29 +792,32 @@
vsi_seid);
return;
}
- if (is_rx_ring)
- ring = *vsi->rx_rings[ring_id];
- else
- ring = *vsi->tx_rings[ring_id];
+
+ ring = kmemdup(is_rx_ring
+ ? vsi->rx_rings[ring_id] : vsi->tx_rings[ring_id],
+ sizeof(*ring), GFP_KERNEL);
+ if (!ring)
+ return;
+
if (cnt == 2) {
dev_info(&pf->pdev->dev, "vsi = %02i %s ring = %02i\n",
vsi_seid, is_rx_ring ? "rx" : "tx", ring_id);
- for (i = 0; i < ring.count; i++) {
+ for (i = 0; i < ring->count; i++) {
if (!is_rx_ring) {
- txd = I40E_TX_DESC(&ring, i);
+ txd = I40E_TX_DESC(ring, i);
dev_info(&pf->pdev->dev,
" d[%03i] = 0x%016llx 0x%016llx\n",
i, txd->buffer_addr,
txd->cmd_type_offset_bsz);
} else if (sizeof(union i40e_rx_desc) ==
sizeof(union i40e_16byte_rx_desc)) {
- rxd = I40E_RX_DESC(&ring, i);
+ rxd = I40E_RX_DESC(ring, i);
dev_info(&pf->pdev->dev,
" d[%03i] = 0x%016llx 0x%016llx\n",
i, rxd->read.pkt_addr,
rxd->read.hdr_addr);
} else {
- rxd = I40E_RX_DESC(&ring, i);
+ rxd = I40E_RX_DESC(ring, i);
dev_info(&pf->pdev->dev,
" d[%03i] = 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n",
i, rxd->read.pkt_addr,
@@ -823,26 +826,26 @@
}
}
} else if (cnt == 3) {
- if (desc_n >= ring.count || desc_n < 0) {
+ if (desc_n >= ring->count || desc_n < 0) {
dev_info(&pf->pdev->dev,
"descriptor %d not found\n", desc_n);
return;
}
if (!is_rx_ring) {
- txd = I40E_TX_DESC(&ring, desc_n);
+ txd = I40E_TX_DESC(ring, desc_n);
dev_info(&pf->pdev->dev,
"vsi = %02i tx ring = %02i d[%03i] = 0x%016llx 0x%016llx\n",
vsi_seid, ring_id, desc_n,
txd->buffer_addr, txd->cmd_type_offset_bsz);
} else if (sizeof(union i40e_rx_desc) ==
sizeof(union i40e_16byte_rx_desc)) {
- rxd = I40E_RX_DESC(&ring, desc_n);
+ rxd = I40E_RX_DESC(ring, desc_n);
dev_info(&pf->pdev->dev,
"vsi = %02i rx ring = %02i d[%03i] = 0x%016llx 0x%016llx\n",
vsi_seid, ring_id, desc_n,
rxd->read.pkt_addr, rxd->read.hdr_addr);
} else {
- rxd = I40E_RX_DESC(&ring, desc_n);
+ rxd = I40E_RX_DESC(ring, desc_n);
dev_info(&pf->pdev->dev,
"vsi = %02i rx ring = %02i d[%03i] = 0x%016llx 0x%016llx 0x%016llx 0x%016llx\n",
vsi_seid, ring_id, desc_n,
@@ -852,6 +855,7 @@
} else {
dev_info(&pf->pdev->dev, "dump desc rx/tx <vsi_seid> <ring_id> [<desc_n>]\n");
}
+ kfree(ring);
}
/**
diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c
index c1bb64d..62e3664 100644
--- a/drivers/net/ethernet/intel/igb/e1000_phy.c
+++ b/drivers/net/ethernet/intel/igb/e1000_phy.c
@@ -83,6 +83,10 @@
s32 ret_val = 0;
u16 phy_id;
+ /* ensure PHY page selection to fix misconfigured i210 */
+ if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211))
+ phy->ops.write_reg(hw, I347AT4_PAGE_SELECT, 0);
+
ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id);
if (ret_val)
goto out;
diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c
index 4a1be34..70039af 100644
--- a/drivers/net/ethernet/jme.c
+++ b/drivers/net/ethernet/jme.c
@@ -270,11 +270,17 @@
}
static inline void
-jme_clear_pm(struct jme_adapter *jme)
+jme_clear_pm_enable_wol(struct jme_adapter *jme)
{
jwrite32(jme, JME_PMCS, PMCS_STMASK | jme->reg_pmcs);
}
+static inline void
+jme_clear_pm_disable_wol(struct jme_adapter *jme)
+{
+ jwrite32(jme, JME_PMCS, PMCS_STMASK);
+}
+
static int
jme_reload_eeprom(struct jme_adapter *jme)
{
@@ -1857,7 +1863,7 @@
struct jme_adapter *jme = netdev_priv(netdev);
int rc;
- jme_clear_pm(jme);
+ jme_clear_pm_disable_wol(jme);
JME_NAPI_ENABLE(jme);
tasklet_init(&jme->linkch_task, jme_link_change_tasklet,
@@ -1929,11 +1935,11 @@
static void
jme_powersave_phy(struct jme_adapter *jme)
{
- if (jme->reg_pmcs) {
+ if (jme->reg_pmcs && device_may_wakeup(&jme->pdev->dev)) {
jme_set_100m_half(jme);
if (jme->reg_pmcs & (PMCS_LFEN | PMCS_LREN))
jme_wait_link(jme);
- jme_clear_pm(jme);
+ jme_clear_pm_enable_wol(jme);
} else {
jme_phy_off(jme);
}
@@ -2650,9 +2656,6 @@
if (wol->wolopts & WAKE_MAGIC)
jme->reg_pmcs |= PMCS_MFEN;
- jwrite32(jme, JME_PMCS, jme->reg_pmcs);
- device_set_wakeup_enable(&jme->pdev->dev, !!(jme->reg_pmcs));
-
return 0;
}
@@ -3176,8 +3179,8 @@
jme->mii_if.mdio_read = jme_mdio_read;
jme->mii_if.mdio_write = jme_mdio_write;
- jme_clear_pm(jme);
- device_set_wakeup_enable(&pdev->dev, true);
+ jme_clear_pm_disable_wol(jme);
+ device_init_wakeup(&pdev->dev, true);
jme_set_phyfifo_5level(jme);
jme->pcirev = pdev->revision;
@@ -3308,7 +3311,7 @@
if (!netif_running(netdev))
return 0;
- jme_clear_pm(jme);
+ jme_clear_pm_disable_wol(jme);
jme_phy_on(jme);
if (test_bit(JME_FLAG_SSET, &jme->flags))
jme_set_settings(netdev, &jme->old_ecmd);
@@ -3316,13 +3319,14 @@
jme_reset_phy_processor(jme);
jme_phy_calibration(jme);
jme_phy_setEA(jme);
- jme_start_irq(jme);
netif_device_attach(netdev);
atomic_inc(&jme->link_changing);
jme_reset_link(jme);
+ jme_start_irq(jme);
+
return 0;
}
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 3bd7e3d8..e6a7b50 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -216,7 +216,7 @@
/* Various constants */
/* Coalescing */
-#define MVNETA_TXDONE_COAL_PKTS 1
+#define MVNETA_TXDONE_COAL_PKTS 0 /* interrupt per packet */
#define MVNETA_RX_COAL_PKTS 32
#define MVNETA_RX_COAL_USEC 100
diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c
index 56022d6..f0cab2d 100644
--- a/drivers/net/ethernet/mellanox/mlx4/cq.c
+++ b/drivers/net/ethernet/mellanox/mlx4/cq.c
@@ -56,13 +56,19 @@
{
struct mlx4_cq *cq;
+ rcu_read_lock();
cq = radix_tree_lookup(&mlx4_priv(dev)->cq_table.tree,
cqn & (dev->caps.num_cqs - 1));
+ rcu_read_unlock();
+
if (!cq) {
mlx4_dbg(dev, "Completion event for bogus CQ %08x\n", cqn);
return;
}
+ /* Acessing the CQ outside of rcu_read_lock is safe, because
+ * the CQ is freed only after interrupt handling is completed.
+ */
++cq->arm_sn;
cq->comp(cq);
@@ -73,23 +79,19 @@
struct mlx4_cq_table *cq_table = &mlx4_priv(dev)->cq_table;
struct mlx4_cq *cq;
- spin_lock(&cq_table->lock);
-
+ rcu_read_lock();
cq = radix_tree_lookup(&cq_table->tree, cqn & (dev->caps.num_cqs - 1));
- if (cq)
- atomic_inc(&cq->refcount);
-
- spin_unlock(&cq_table->lock);
+ rcu_read_unlock();
if (!cq) {
- mlx4_warn(dev, "Async event for bogus CQ %08x\n", cqn);
+ mlx4_dbg(dev, "Async event for bogus CQ %08x\n", cqn);
return;
}
+ /* Acessing the CQ outside of rcu_read_lock is safe, because
+ * the CQ is freed only after interrupt handling is completed.
+ */
cq->event(cq, event_type);
-
- if (atomic_dec_and_test(&cq->refcount))
- complete(&cq->free);
}
static int mlx4_SW2HW_CQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox,
@@ -256,9 +258,9 @@
if (err)
return err;
- spin_lock_irq(&cq_table->lock);
+ spin_lock(&cq_table->lock);
err = radix_tree_insert(&cq_table->tree, cq->cqn, cq);
- spin_unlock_irq(&cq_table->lock);
+ spin_unlock(&cq_table->lock);
if (err)
goto err_icm;
@@ -297,9 +299,9 @@
return 0;
err_radix:
- spin_lock_irq(&cq_table->lock);
+ spin_lock(&cq_table->lock);
radix_tree_delete(&cq_table->tree, cq->cqn);
- spin_unlock_irq(&cq_table->lock);
+ spin_unlock(&cq_table->lock);
err_icm:
mlx4_cq_free_icm(dev, cq->cqn);
@@ -314,16 +316,16 @@
struct mlx4_cq_table *cq_table = &priv->cq_table;
int err;
+ spin_lock(&cq_table->lock);
+ radix_tree_delete(&cq_table->tree, cq->cqn);
+ spin_unlock(&cq_table->lock);
+
err = mlx4_HW2SW_CQ(dev, NULL, cq->cqn);
if (err)
mlx4_warn(dev, "HW2SW_CQ failed (%d) for CQN %06x\n", err, cq->cqn);
synchronize_irq(priv->eq_table.eq[cq->vector].irq);
- spin_lock_irq(&cq_table->lock);
- radix_tree_delete(&cq_table->tree, cq->cqn);
- spin_unlock_irq(&cq_table->lock);
-
if (atomic_dec_and_test(&cq->refcount))
complete(&cq->free);
wait_for_completion(&cq->free);
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 68fef11..f318142 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -1500,17 +1500,13 @@
{
struct mlx4_en_rx_ring *ring = priv->rx_ring[ring_idx];
int numa_node = priv->mdev->dev->numa_node;
- int ret = 0;
if (!zalloc_cpumask_var(&ring->affinity_mask, GFP_KERNEL))
return -ENOMEM;
- ret = cpumask_set_cpu_local_first(ring_idx, numa_node,
- ring->affinity_mask);
- if (ret)
- free_cpumask_var(ring->affinity_mask);
-
- return ret;
+ cpumask_set_cpu(cpumask_local_spread(ring_idx, numa_node),
+ ring->affinity_mask);
+ return 0;
}
static void mlx4_en_free_affinity_hint(struct mlx4_en_priv *priv, int ring_idx)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index 5bbb59d..c175938 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -438,8 +438,14 @@
ring->cqn = priv->rx_cq[ring_ind]->mcq.cqn;
ring->stride = stride;
- if (ring->stride <= TXBB_SIZE)
+ if (ring->stride <= TXBB_SIZE) {
+ /* Stamp first unused send wqe */
+ __be32 *ptr = (__be32 *)ring->buf;
+ __be32 stamp = cpu_to_be32(1 << STAMP_SHIFT);
+ *ptr = stamp;
+ /* Move pointer to start of rx section */
ring->buf += TXBB_SIZE;
+ }
ring->log_stride = ffs(ring->stride) - 1;
ring->buf_size = ring->size * ring->stride;
@@ -501,8 +507,11 @@
return;
for (ring = 0; ring < priv->rx_ring_num; ring++) {
- if (mlx4_en_is_ring_empty(priv->rx_ring[ring]))
+ if (mlx4_en_is_ring_empty(priv->rx_ring[ring])) {
+ local_bh_disable();
napi_reschedule(&priv->rx_cq[ring]->napi);
+ local_bh_enable();
+ }
}
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index 5980d3f..9fc1dd7 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -139,9 +139,9 @@
ring->queue_index = queue_index;
if (queue_index < priv->num_tx_rings_p_up)
- cpumask_set_cpu_local_first(queue_index,
- priv->mdev->dev->numa_node,
- &ring->affinity_mask);
+ cpumask_set_cpu(cpumask_local_spread(queue_index,
+ priv->mdev->dev->numa_node),
+ &ring->affinity_mask);
*pring = ring;
return 0;
@@ -391,7 +391,6 @@
u32 packets = 0;
u32 bytes = 0;
int factor = priv->cqe_factor;
- u64 timestamp = 0;
int done = 0;
int budget = priv->tx_work_limit;
u32 last_nr_txbb;
@@ -431,9 +430,12 @@
new_index = be16_to_cpu(cqe->wqe_index) & size_mask;
do {
+ u64 timestamp = 0;
+
txbbs_skipped += last_nr_txbb;
ring_index = (ring_index + last_nr_txbb) & size_mask;
- if (ring->tx_info[ring_index].ts_requested)
+
+ if (unlikely(ring->tx_info[ring_index].ts_requested))
timestamp = mlx4_en_get_cqe_ts(cqe);
/* free next descriptor */
diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
index cd5cf6d..fbb0c02 100644
--- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
+++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c
@@ -2769,6 +2769,9 @@
put_res(dev, slave, srqn, RES_SRQ);
qp->srq = srq;
}
+
+ /* Save param3 for dynamic changes from VST back to VGT */
+ qp->param3 = qpc->param3;
put_res(dev, slave, rcqn, RES_CQ);
put_res(dev, slave, mtt_base, RES_MTT);
res_end_move(dev, slave, RES_QP, qpn);
@@ -2922,7 +2925,7 @@
case QP_TRANS_RTS2RTS:
case QP_TRANS_SQD2SQD:
case QP_TRANS_SQD2RTS:
- if (slave != mlx4_master_func_num(dev))
+ if (slave != mlx4_master_func_num(dev)) {
if (optpar & MLX4_QP_OPTPAR_PRIMARY_ADDR_PATH) {
port = (qp_ctx->pri_path.sched_queue >> 6 & 1) + 1;
if (dev->caps.port_mask[port] != MLX4_PORT_TYPE_IB)
@@ -2941,6 +2944,7 @@
if (qp_ctx->alt_path.mgid_index >= num_gids)
return -EINVAL;
}
+ }
break;
default:
break;
@@ -3530,7 +3534,6 @@
int qpn = vhcr->in_modifier & 0x7fffff;
struct res_qp *qp;
u8 orig_sched_queue;
- __be32 orig_param3 = qpc->param3;
u8 orig_vlan_control = qpc->pri_path.vlan_control;
u8 orig_fvl_rx = qpc->pri_path.fvl_rx;
u8 orig_pri_path_fl = qpc->pri_path.fl;
@@ -3571,7 +3574,6 @@
*/
if (!err) {
qp->sched_queue = orig_sched_queue;
- qp->param3 = orig_param3;
qp->vlan_control = orig_vlan_control;
qp->fvl_rx = orig_fvl_rx;
qp->pri_path_fl = orig_pri_path_fl;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
index 10e1f1a..4878025 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
@@ -300,11 +300,11 @@
param = qp->pid;
break;
case QP_STATE:
- param = (u64)mlx5_qp_state_str(be32_to_cpu(ctx->flags) >> 28);
+ param = (unsigned long)mlx5_qp_state_str(be32_to_cpu(ctx->flags) >> 28);
*is_str = 1;
break;
case QP_XPORT:
- param = (u64)mlx5_qp_type_str((be32_to_cpu(ctx->flags) >> 16) & 0xff);
+ param = (unsigned long)mlx5_qp_type_str((be32_to_cpu(ctx->flags) >> 16) & 0xff);
*is_str = 1;
break;
case QP_MTU:
@@ -464,7 +464,7 @@
if (is_str)
- ret = snprintf(tbuf, sizeof(tbuf), "%s\n", (const char *)field);
+ ret = snprintf(tbuf, sizeof(tbuf), "%s\n", (const char *)(unsigned long)field);
else
ret = snprintf(tbuf, sizeof(tbuf), "0x%llx\n", field);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 71b10b2..efffe94 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -86,7 +86,7 @@
[2] = {
.mask = MLX5_PROF_MASK_QP_SIZE |
MLX5_PROF_MASK_MR_CACHE,
- .log_max_qp = 17,
+ .log_max_qp = 18,
.mr_cache[0] = {
.size = 500,
.limit = 250
diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c
index cc0485e..038b9c4 100644
--- a/drivers/net/ethernet/neterion/vxge/vxge-main.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c
@@ -2223,8 +2223,6 @@
return IRQ_NONE;
}
-#ifdef CONFIG_PCI_MSI
-
static irqreturn_t vxge_tx_msix_handle(int irq, void *dev_id)
{
struct vxge_fifo *fifo = (struct vxge_fifo *)dev_id;
@@ -2442,16 +2440,13 @@
if (vdev->config.intr_type == MSI_X)
pci_disable_msix(vdev->pdev);
}
-#endif
static void vxge_rem_isr(struct vxgedev *vdev)
{
-#ifdef CONFIG_PCI_MSI
- if (vdev->config.intr_type == MSI_X) {
+ if (IS_ENABLED(CONFIG_PCI_MSI) &&
+ vdev->config.intr_type == MSI_X) {
vxge_rem_msix_isr(vdev);
- } else
-#endif
- if (vdev->config.intr_type == INTA) {
+ } else if (vdev->config.intr_type == INTA) {
synchronize_irq(vdev->pdev->irq);
free_irq(vdev->pdev->irq, vdev);
}
@@ -2460,11 +2455,10 @@
static int vxge_add_isr(struct vxgedev *vdev)
{
int ret = 0;
-#ifdef CONFIG_PCI_MSI
int vp_idx = 0, intr_idx = 0, intr_cnt = 0, msix_idx = 0, irq_req = 0;
int pci_fun = PCI_FUNC(vdev->pdev->devfn);
- if (vdev->config.intr_type == MSI_X)
+ if (IS_ENABLED(CONFIG_PCI_MSI) && vdev->config.intr_type == MSI_X)
ret = vxge_enable_msix(vdev);
if (ret) {
@@ -2475,7 +2469,7 @@
vdev->config.intr_type = INTA;
}
- if (vdev->config.intr_type == MSI_X) {
+ if (IS_ENABLED(CONFIG_PCI_MSI) && vdev->config.intr_type == MSI_X) {
for (intr_idx = 0;
intr_idx < (vdev->no_of_vpath *
VXGE_HW_VPATH_MSIX_ACTIVE); intr_idx++) {
@@ -2576,9 +2570,8 @@
vdev->vxge_entries[intr_cnt].in_use = 1;
vdev->vxge_entries[intr_cnt].arg = &vdev->vpaths[0];
}
-INTA_MODE:
-#endif
+INTA_MODE:
if (vdev->config.intr_type == INTA) {
snprintf(vdev->desc[0], VXGE_INTR_STRLEN,
"%s:vxge:INTA", vdev->ndev->name);
@@ -3889,12 +3882,12 @@
if (max_mac_vpath > VXGE_MAX_MAC_ADDR_COUNT)
max_mac_vpath = VXGE_MAX_MAC_ADDR_COUNT;
-#ifndef CONFIG_PCI_MSI
- vxge_debug_init(VXGE_ERR,
- "%s: This Kernel does not support "
- "MSI-X. Defaulting to INTA", VXGE_DRIVER_NAME);
- *intr_type = INTA;
-#endif
+ if (!IS_ENABLED(CONFIG_PCI_MSI)) {
+ vxge_debug_init(VXGE_ERR,
+ "%s: This Kernel does not support "
+ "MSI-X. Defaulting to INTA", VXGE_DRIVER_NAME);
+ *intr_type = INTA;
+ }
/* Configure whether MSI-X or IRQL. */
switch (*intr_type) {
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index e56c1bb..76b2cfe 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -567,6 +567,7 @@
u64 tx_dma_map_error;
u64 spurious_intr;
u64 mac_filter_limit_overrun;
+ u64 mbx_spurious_intr;
};
/*
@@ -1092,7 +1093,7 @@
unsigned long status;
spinlock_t queue_lock; /* Mailbox queue lock */
spinlock_t aen_lock; /* Mailbox response/AEN lock */
- atomic_t rsp_status;
+ u32 rsp_status;
u32 num_cmds;
};
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index 840bf36..dd618d7 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -489,7 +489,7 @@
static inline void qlcnic_83xx_notify_mbx_response(struct qlcnic_mailbox *mbx)
{
- atomic_set(&mbx->rsp_status, QLC_83XX_MBX_RESPONSE_ARRIVED);
+ mbx->rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED;
complete(&mbx->completion);
}
@@ -508,7 +508,7 @@
if (event & QLCNIC_MBX_ASYNC_EVENT) {
__qlcnic_83xx_process_aen(adapter);
} else {
- if (atomic_read(&mbx->rsp_status) != rsp_status)
+ if (mbx->rsp_status != rsp_status)
qlcnic_83xx_notify_mbx_response(mbx);
}
out:
@@ -1023,7 +1023,7 @@
if (event & QLCNIC_MBX_ASYNC_EVENT) {
__qlcnic_83xx_process_aen(adapter);
} else {
- if (atomic_read(&mbx->rsp_status) != rsp_status)
+ if (mbx->rsp_status != rsp_status)
qlcnic_83xx_notify_mbx_response(mbx);
}
}
@@ -2338,9 +2338,9 @@
static irqreturn_t qlcnic_83xx_handle_aen(int irq, void *data)
{
+ u32 mask, resp, event, rsp_status = QLC_83XX_MBX_RESPONSE_ARRIVED;
struct qlcnic_adapter *adapter = data;
struct qlcnic_mailbox *mbx;
- u32 mask, resp, event;
unsigned long flags;
mbx = adapter->ahw->mailbox;
@@ -2350,10 +2350,14 @@
goto out;
event = readl(QLCNIC_MBX_FW(adapter->ahw, 0));
- if (event & QLCNIC_MBX_ASYNC_EVENT)
+ if (event & QLCNIC_MBX_ASYNC_EVENT) {
__qlcnic_83xx_process_aen(adapter);
- else
- qlcnic_83xx_notify_mbx_response(mbx);
+ } else {
+ if (mbx->rsp_status != rsp_status)
+ qlcnic_83xx_notify_mbx_response(mbx);
+ else
+ adapter->stats.mbx_spurious_intr++;
+ }
out:
mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK);
@@ -4025,10 +4029,10 @@
struct qlcnic_adapter *adapter = mbx->adapter;
struct qlcnic_mbx_ops *mbx_ops = mbx->ops;
struct device *dev = &adapter->pdev->dev;
- atomic_t *rsp_status = &mbx->rsp_status;
struct list_head *head = &mbx->cmd_q;
struct qlcnic_hardware_context *ahw;
struct qlcnic_cmd_args *cmd = NULL;
+ unsigned long flags;
ahw = adapter->ahw;
@@ -4038,7 +4042,9 @@
return;
}
- atomic_set(rsp_status, QLC_83XX_MBX_RESPONSE_WAIT);
+ spin_lock_irqsave(&mbx->aen_lock, flags);
+ mbx->rsp_status = QLC_83XX_MBX_RESPONSE_WAIT;
+ spin_unlock_irqrestore(&mbx->aen_lock, flags);
spin_lock(&mbx->queue_lock);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
index 494e810..0a2318c 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c
@@ -59,7 +59,8 @@
QLC_OFF(stats.mac_filter_limit_overrun)},
{"spurious intr", QLC_SIZEOF(stats.spurious_intr),
QLC_OFF(stats.spurious_intr)},
-
+ {"mbx spurious intr", QLC_SIZEOF(stats.mbx_spurious_intr),
+ QLC_OFF(stats.mbx_spurious_intr)},
};
static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = {
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
index 6c904a6..7bbb041 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
@@ -1648,7 +1648,18 @@
return;
}
skb_reserve(new_skb, NET_IP_ALIGN);
+
+ pci_dma_sync_single_for_cpu(qdev->pdev,
+ dma_unmap_addr(sbq_desc, mapaddr),
+ dma_unmap_len(sbq_desc, maplen),
+ PCI_DMA_FROMDEVICE);
+
memcpy(skb_put(new_skb, length), skb->data, length);
+
+ pci_dma_sync_single_for_device(qdev->pdev,
+ dma_unmap_addr(sbq_desc, mapaddr),
+ dma_unmap_len(sbq_desc, maplen),
+ PCI_DMA_FROMDEVICE);
skb = new_skb;
/* Frame error, so drop the packet. */
diff --git a/drivers/net/ethernet/qualcomm/qca_spi.c b/drivers/net/ethernet/qualcomm/qca_spi.c
index f77b589..f55be6d 100644
--- a/drivers/net/ethernet/qualcomm/qca_spi.c
+++ b/drivers/net/ethernet/qualcomm/qca_spi.c
@@ -812,7 +812,7 @@
dev->netdev_ops = &qcaspi_netdev_ops;
qcaspi_set_ethtool_ops(dev);
dev->watchdog_timeo = QCASPI_TX_TIMEOUT;
- dev->flags = IFF_MULTICAST;
+ dev->priv_flags &= ~IFF_TX_SKB_SHARING;
dev->tx_queue_len = 100;
qca = netdev_priv(dev);
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index b474dbf..c44bae4 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -1112,6 +1112,7 @@
int rx_ringsize = sizeof(*rxdesc) * mdp->num_rx_ring;
int tx_ringsize = sizeof(*txdesc) * mdp->num_tx_ring;
int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1;
+ dma_addr_t dma_addr;
mdp->cur_rx = 0;
mdp->cur_tx = 0;
@@ -1125,18 +1126,23 @@
/* skb */
mdp->rx_skbuff[i] = NULL;
skb = netdev_alloc_skb(ndev, skbuff_size);
- mdp->rx_skbuff[i] = skb;
if (skb == NULL)
break;
sh_eth_set_receive_align(skb);
/* RX descriptor */
rxdesc = &mdp->rx_ring[i];
- /* The size of the buffer is a multiple of 16 bytes. */
- rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16);
- dma_map_single(&ndev->dev, skb->data, rxdesc->buffer_length,
- DMA_FROM_DEVICE);
- rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4));
+ /* The size of the buffer is a multiple of 32 bytes. */
+ rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 32);
+ dma_addr = dma_map_single(&ndev->dev, skb->data,
+ rxdesc->buffer_length,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&ndev->dev, dma_addr)) {
+ kfree_skb(skb);
+ break;
+ }
+ mdp->rx_skbuff[i] = skb;
+ rxdesc->addr = dma_addr;
rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP);
/* Rx descriptor address set */
@@ -1151,7 +1157,8 @@
mdp->dirty_rx = (u32) (i - mdp->num_rx_ring);
/* Mark the last entry as wrapping the ring. */
- rxdesc->status |= cpu_to_edmac(mdp, RD_RDEL);
+ if (rxdesc)
+ rxdesc->status |= cpu_to_edmac(mdp, RD_RDEL);
memset(mdp->tx_ring, 0, tx_ringsize);
@@ -1391,6 +1398,7 @@
u16 pkt_len = 0;
u32 desc_status;
int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1;
+ dma_addr_t dma_addr;
rxdesc = &mdp->rx_ring[entry];
while (!(rxdesc->status & cpu_to_edmac(mdp, RD_RACT))) {
@@ -1441,9 +1449,9 @@
mdp->rx_skbuff[entry] = NULL;
if (mdp->cd->rpadir)
skb_reserve(skb, NET_IP_ALIGN);
- dma_sync_single_for_cpu(&ndev->dev, rxdesc->addr,
- ALIGN(mdp->rx_buf_sz, 16),
- DMA_FROM_DEVICE);
+ dma_unmap_single(&ndev->dev, rxdesc->addr,
+ ALIGN(mdp->rx_buf_sz, 32),
+ DMA_FROM_DEVICE);
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, ndev);
netif_receive_skb(skb);
@@ -1458,20 +1466,25 @@
for (; mdp->cur_rx - mdp->dirty_rx > 0; mdp->dirty_rx++) {
entry = mdp->dirty_rx % mdp->num_rx_ring;
rxdesc = &mdp->rx_ring[entry];
- /* The size of the buffer is 16 byte boundary. */
- rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16);
+ /* The size of the buffer is 32 byte boundary. */
+ rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 32);
if (mdp->rx_skbuff[entry] == NULL) {
skb = netdev_alloc_skb(ndev, skbuff_size);
- mdp->rx_skbuff[entry] = skb;
if (skb == NULL)
break; /* Better luck next round. */
sh_eth_set_receive_align(skb);
- dma_map_single(&ndev->dev, skb->data,
- rxdesc->buffer_length, DMA_FROM_DEVICE);
+ dma_addr = dma_map_single(&ndev->dev, skb->data,
+ rxdesc->buffer_length,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&ndev->dev, dma_addr)) {
+ kfree_skb(skb);
+ break;
+ }
+ mdp->rx_skbuff[entry] = skb;
skb_checksum_none_assert(skb);
- rxdesc->addr = virt_to_phys(PTR_ALIGN(skb->data, 4));
+ rxdesc->addr = dma_addr;
}
if (entry >= mdp->num_rx_ring - 1)
rxdesc->status |=
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index 63ec209..010009d 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -452,6 +452,17 @@
return rc;
}
+static void efx_ef10_forget_old_piobufs(struct efx_nic *efx)
+{
+ struct efx_channel *channel;
+ struct efx_tx_queue *tx_queue;
+
+ /* All our existing PIO buffers went away */
+ efx_for_each_channel(channel, efx)
+ efx_for_each_channel_tx_queue(tx_queue, channel)
+ tx_queue->piobuf = NULL;
+}
+
#else /* !EFX_USE_PIO */
static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n)
@@ -468,6 +479,10 @@
{
}
+static void efx_ef10_forget_old_piobufs(struct efx_nic *efx)
+{
+}
+
#endif /* EFX_USE_PIO */
static void efx_ef10_remove(struct efx_nic *efx)
@@ -699,6 +714,7 @@
nic_data->must_realloc_vis = true;
nic_data->must_restore_filters = true;
nic_data->must_restore_piobufs = true;
+ efx_ef10_forget_old_piobufs(efx);
nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID;
}
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
index 6cc3cf6..9a289e79 100644
--- a/drivers/net/ethernet/smsc/smc91x.c
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -535,7 +535,7 @@
#define smc_special_lock(lock, flags) spin_lock_irqsave(lock, flags)
#define smc_special_unlock(lock, flags) spin_unlock_irqrestore(lock, flags)
#else
-#define smc_special_trylock(lock, flags) (flags == flags)
+#define smc_special_trylock(lock, flags) ((void)flags, true)
#define smc_special_lock(lock, flags) do { flags = 0; } while (0)
#define smc_special_unlock(lock, flags) do { flags = 0; } while (0)
#endif
diff --git a/drivers/net/ethernet/ti/cpmac.c b/drivers/net/ethernet/ti/cpmac.c
index dd94300..a555c13 100644
--- a/drivers/net/ethernet/ti/cpmac.c
+++ b/drivers/net/ethernet/ti/cpmac.c
@@ -547,7 +547,8 @@
static int cpmac_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- int queue, len;
+ int queue;
+ unsigned int len;
struct cpmac_desc *desc;
struct cpmac_priv *priv = netdev_priv(dev);
@@ -557,7 +558,7 @@
if (unlikely(skb_padto(skb, ETH_ZLEN)))
return NETDEV_TX_OK;
- len = max(skb->len, ETH_ZLEN);
+ len = max_t(unsigned int, skb->len, ETH_ZLEN);
queue = skb_get_queue_mapping(skb);
netif_stop_subqueue(dev, queue);
@@ -1235,7 +1236,7 @@
goto fail_alloc;
}
-#warning FIXME: unhardcode gpio&reset bits
+ /* FIXME: unhardcode gpio&reset bits */
ar7_gpio_disable(26);
ar7_gpio_disable(27);
ar7_device_reset(AR7_RESET_BIT_CPMAC_LO);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 91d6d03..7ae0627 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -506,7 +506,7 @@
cancel_delayed_work_sync(&phydev->state_queue);
mutex_lock(&phydev->lock);
- if (phydev->state > PHY_UP)
+ if (phydev->state > PHY_UP && phydev->state != PHY_HALTED)
phydev->state = PHY_UP;
mutex_unlock(&phydev->lock);
}
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 7efc451..e3fbbbb 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -561,7 +561,7 @@
static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct ppp_file *pf = file->private_data;
+ struct ppp_file *pf;
struct ppp *ppp;
int err = -EFAULT, val, val2, i;
struct ppp_idle idle;
@@ -571,9 +571,14 @@
void __user *argp = (void __user *)arg;
int __user *p = argp;
- if (!pf)
- return ppp_unattached_ioctl(current->nsproxy->net_ns,
- pf, file, cmd, arg);
+ mutex_lock(&ppp_mutex);
+
+ pf = file->private_data;
+ if (!pf) {
+ err = ppp_unattached_ioctl(current->nsproxy->net_ns,
+ pf, file, cmd, arg);
+ goto out;
+ }
if (cmd == PPPIOCDETACH) {
/*
@@ -588,7 +593,6 @@
* this fd and reopening /dev/ppp.
*/
err = -EINVAL;
- mutex_lock(&ppp_mutex);
if (pf->kind == INTERFACE) {
ppp = PF_TO_PPP(pf);
if (file == ppp->owner)
@@ -600,15 +604,13 @@
} else
pr_warn("PPPIOCDETACH file->f_count=%ld\n",
atomic_long_read(&file->f_count));
- mutex_unlock(&ppp_mutex);
- return err;
+ goto out;
}
if (pf->kind == CHANNEL) {
struct channel *pch;
struct ppp_channel *chan;
- mutex_lock(&ppp_mutex);
pch = PF_TO_CHANNEL(pf);
switch (cmd) {
@@ -630,17 +632,16 @@
err = chan->ops->ioctl(chan, cmd, arg);
up_read(&pch->chan_sem);
}
- mutex_unlock(&ppp_mutex);
- return err;
+ goto out;
}
if (pf->kind != INTERFACE) {
/* can't happen */
pr_err("PPP: not interface or channel??\n");
- return -EINVAL;
+ err = -EINVAL;
+ goto out;
}
- mutex_lock(&ppp_mutex);
ppp = PF_TO_PPP(pf);
switch (cmd) {
case PPPIOCSMRU:
@@ -815,7 +816,10 @@
default:
err = -ENOTTY;
}
+
+out:
mutex_unlock(&ppp_mutex);
+
return err;
}
@@ -828,7 +832,6 @@
struct ppp_net *pn;
int __user *p = (int __user *)arg;
- mutex_lock(&ppp_mutex);
switch (cmd) {
case PPPIOCNEWUNIT:
/* Create a new ppp unit */
@@ -879,7 +882,7 @@
default:
err = -ENOTTY;
}
- mutex_unlock(&ppp_mutex);
+
return err;
}
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index a201042..5219412 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -499,11 +499,13 @@
for (i = 0; i < n; i++) {
tfile = rtnl_dereference(tun->tfiles[i]);
BUG_ON(!tfile);
+ tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN;
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
RCU_INIT_POINTER(tfile->tun, NULL);
--tun->numqueues;
}
list_for_each_entry(tfile, &tun->disabled, next) {
+ tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN;
tfile->socket.sk->sk_data_ready(tfile->socket.sk);
RCU_INIT_POINTER(tfile->tun, NULL);
}
@@ -558,6 +560,7 @@
goto out;
}
tfile->queue_index = tun->numqueues;
+ tfile->socket.sk->sk_shutdown &= ~RCV_SHUTDOWN;
rcu_assign_pointer(tfile->tun, tun);
rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile);
tun->numqueues++;
@@ -1360,9 +1363,6 @@
if (!len)
return ret;
- if (tun->dev->reg_state != NETREG_REGISTERED)
- return -EIO;
-
/* Read frames from queue */
skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0,
&peeked, &off, &err);
diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c
index 8cfc3bb..436da0c 100644
--- a/drivers/net/usb/catc.c
+++ b/drivers/net/usb/catc.c
@@ -777,7 +777,7 @@
struct net_device *netdev;
struct catc *catc;
u8 broadcast[ETH_ALEN];
- int i, pktsz;
+ int pktsz, ret;
if (usb_set_interface(usbdev,
intf->altsetting->desc.bInterfaceNumber, 1)) {
@@ -812,12 +812,8 @@
if ((!catc->ctrl_urb) || (!catc->tx_urb) ||
(!catc->rx_urb) || (!catc->irq_urb)) {
dev_err(&intf->dev, "No free urbs available.\n");
- usb_free_urb(catc->ctrl_urb);
- usb_free_urb(catc->tx_urb);
- usb_free_urb(catc->rx_urb);
- usb_free_urb(catc->irq_urb);
- free_netdev(netdev);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto fail_free;
}
/* The F5U011 has the same vendor/product as the netmate but a device version of 0x130 */
@@ -845,15 +841,24 @@
catc->irq_buf, 2, catc_irq_done, catc, 1);
if (!catc->is_f5u011) {
+ u32 *buf;
+ int i;
+
dev_dbg(dev, "Checking memory size\n");
- i = 0x12345678;
- catc_write_mem(catc, 0x7a80, &i, 4);
- i = 0x87654321;
- catc_write_mem(catc, 0xfa80, &i, 4);
- catc_read_mem(catc, 0x7a80, &i, 4);
+ buf = kmalloc(4, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto fail_free;
+ }
+
+ *buf = 0x12345678;
+ catc_write_mem(catc, 0x7a80, buf, 4);
+ *buf = 0x87654321;
+ catc_write_mem(catc, 0xfa80, buf, 4);
+ catc_read_mem(catc, 0x7a80, buf, 4);
- switch (i) {
+ switch (*buf) {
case 0x12345678:
catc_set_reg(catc, TxBufCount, 8);
catc_set_reg(catc, RxBufCount, 32);
@@ -868,6 +873,8 @@
dev_dbg(dev, "32k Memory\n");
break;
}
+
+ kfree(buf);
dev_dbg(dev, "Getting MAC from SEEROM.\n");
@@ -914,16 +921,21 @@
usb_set_intfdata(intf, catc);
SET_NETDEV_DEV(netdev, &intf->dev);
- if (register_netdev(netdev) != 0) {
- usb_set_intfdata(intf, NULL);
- usb_free_urb(catc->ctrl_urb);
- usb_free_urb(catc->tx_urb);
- usb_free_urb(catc->rx_urb);
- usb_free_urb(catc->irq_urb);
- free_netdev(netdev);
- return -EIO;
- }
+ ret = register_netdev(netdev);
+ if (ret)
+ goto fail_clear_intfdata;
+
return 0;
+
+fail_clear_intfdata:
+ usb_set_intfdata(intf, NULL);
+fail_free:
+ usb_free_urb(catc->ctrl_urb);
+ usb_free_urb(catc->tx_urb);
+ usb_free_urb(catc->rx_urb);
+ usb_free_urb(catc->irq_urb);
+ free_netdev(netdev);
+ return ret;
}
static void catc_disconnect(struct usb_interface *intf)
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 0338135..02e7b9e 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -815,7 +815,11 @@
iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber;
- /* reset data interface */
+ /* Reset data interface. Some devices will not reset properly
+ * unless they are configured first. Toggle the altsetting to
+ * force a reset
+ */
+ usb_set_interface(dev->udev, iface_no, data_altsetting);
temp = usb_set_interface(dev->udev, iface_no, 0);
if (temp) {
dev_dbg(&intf->dev, "set interface failed\n");
diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c
index f840802..17fac01 100644
--- a/drivers/net/usb/pegasus.c
+++ b/drivers/net/usb/pegasus.c
@@ -126,40 +126,61 @@
static int get_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
{
+ u8 *buf;
int ret;
+ buf = kmalloc(size, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(pegasus->usb, usb_rcvctrlpipe(pegasus->usb, 0),
PEGASUS_REQ_GET_REGS, PEGASUS_REQT_READ, 0,
- indx, data, size, 1000);
+ indx, buf, size, 1000);
if (ret < 0)
netif_dbg(pegasus, drv, pegasus->net,
"%s returned %d\n", __func__, ret);
+ else if (ret <= size)
+ memcpy(data, buf, ret);
+ kfree(buf);
return ret;
}
-static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size, void *data)
+static int set_registers(pegasus_t *pegasus, __u16 indx, __u16 size,
+ const void *data)
{
+ u8 *buf;
int ret;
+ buf = kmemdup(data, size, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0),
PEGASUS_REQ_SET_REGS, PEGASUS_REQT_WRITE, 0,
- indx, data, size, 100);
+ indx, buf, size, 100);
if (ret < 0)
netif_dbg(pegasus, drv, pegasus->net,
"%s returned %d\n", __func__, ret);
+ kfree(buf);
return ret;
}
static int set_register(pegasus_t *pegasus, __u16 indx, __u8 data)
{
+ u8 *buf;
int ret;
+ buf = kmemdup(&data, 1, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
ret = usb_control_msg(pegasus->usb, usb_sndctrlpipe(pegasus->usb, 0),
PEGASUS_REQ_SET_REG, PEGASUS_REQT_WRITE, data,
- indx, &data, 1, 1000);
+ indx, buf, 1, 1000);
if (ret < 0)
netif_dbg(pegasus, drv, pegasus->net,
"%s returned %d\n", __func__, ret);
+ kfree(buf);
return ret;
}
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index a577151..73f55a9 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -748,6 +748,7 @@
{QMI_FIXED_INTF(0x19d2, 0x1426, 2)}, /* ZTE MF91 */
{QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */
{QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */
+ {QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */
{QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */
{QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */
{QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */
@@ -766,8 +767,10 @@
{QMI_FIXED_INTF(0x1199, 0x9061, 8)}, /* Sierra Wireless Modem */
{QMI_FIXED_INTF(0x1199, 0x9070, 8)}, /* Sierra Wireless MC74xx/EM74xx */
{QMI_FIXED_INTF(0x1199, 0x9070, 10)}, /* Sierra Wireless MC74xx/EM74xx */
- {QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx/EM74xx */
- {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx/EM74xx */
+ {QMI_FIXED_INTF(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx */
+ {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx */
+ {QMI_FIXED_INTF(0x1199, 0x9079, 8)}, /* Sierra Wireless EM74xx */
+ {QMI_FIXED_INTF(0x1199, 0x9079, 10)}, /* Sierra Wireless EM74xx */
{QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
{QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */
{QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */
diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c
index 6e87e57..eab8fba 100644
--- a/drivers/net/usb/rtl8150.c
+++ b/drivers/net/usb/rtl8150.c
@@ -155,16 +155,36 @@
*/
static int get_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
{
- return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
- RTL8150_REQ_GET_REGS, RTL8150_REQT_READ,
- indx, 0, data, size, 500);
+ void *buf;
+ int ret;
+
+ buf = kmalloc(size, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ RTL8150_REQ_GET_REGS, RTL8150_REQT_READ,
+ indx, 0, buf, size, 500);
+ if (ret > 0 && ret <= size)
+ memcpy(data, buf, ret);
+ kfree(buf);
+ return ret;
}
-static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
+static int set_registers(rtl8150_t * dev, u16 indx, u16 size, const void *data)
{
- return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
- RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE,
- indx, 0, data, size, 500);
+ void *buf;
+ int ret;
+
+ buf = kmemdup(data, size, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE,
+ indx, 0, buf, size, 500);
+ kfree(buf);
+ return ret;
}
static void async_set_reg_cb(struct urb *urb)
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 59282dd..2dc8958 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -2260,7 +2260,7 @@
if (data[IFLA_VXLAN_ID]) {
__u32 id = nla_get_u32(data[IFLA_VXLAN_ID]);
- if (id >= VXLAN_VID_MASK)
+ if (id >= VXLAN_N_VID)
return -ERANGE;
}
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index 44541db..69b994f 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -2516,7 +2516,7 @@
dev->mem_start = card->phys_mem
+ BUF_OFFSET ( txBuffer[i][0][0]);
dev->mem_end = card->phys_mem
- + BUF_OFFSET ( txBuffer[i][NUM_TX_BUFFER][0]);
+ + BUF_OFFSET ( txBuffer[i][NUM_TX_BUFFER - 1][LEN_RX_BUFFER - 1]);
dev->base_addr = card->pci_conf;
dev->irq = card->irq;
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 6c86b55..1ec3533 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -344,5 +344,6 @@
source "drivers/net/wireless/cw1200/Kconfig"
source "drivers/net/wireless/rsi/Kconfig"
source "drivers/net/wireless/cnss/Kconfig"
+source "drivers/net/wireless/cnss_genl/Kconfig"
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 585dec3..8c1ea59 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -66,3 +66,4 @@
obj-$(CONFIG_CNSS) += cnss/
obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/
obj-$(CONFIG_CNSS_CRYPTO) += cnss_crypto/
+obj-$(CONFIG_CNSS_GENL) += cnss_genl/
diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c
index 0beb7e7..7349b05 100644
--- a/drivers/net/wireless/ath/ath5k/led.c
+++ b/drivers/net/wireless/ath/ath5k/led.c
@@ -77,7 +77,7 @@
/* HP Compaq CQ60-206US (ddreggors@jumptv.com) */
{ ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137a), ATH_LED(3, 1) },
/* HP Compaq C700 (nitrousnrg@gmail.com) */
- { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) },
+ { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 0) },
/* LiteOn AR5BXB63 (magooz@salug.it) */
{ ATH_SDEVICE(PCI_VENDOR_ID_ATHEROS, 0x3067), ATH_LED(3, 0) },
/* IBM-specific AR5212 (all others) */
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 43c30f0d..c3c1871 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -889,7 +889,7 @@
GFP_KERNEL);
} else if (vif->sme_state == SME_CONNECTED) {
cfg80211_disconnected(vif->ndev, proto_reason,
- NULL, 0, GFP_KERNEL);
+ NULL, 0, false, GFP_KERNEL);
}
vif->sme_state = SME_DISCONNECTED;
@@ -3310,7 +3310,7 @@
}
/* fw uses seconds, also make sure that it's >0 */
- interval = max_t(u16, 1, request->interval / 1000);
+ interval = max_t(u16, 1, request->scan_plans[0].interval);
ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx,
interval, interval,
@@ -3465,7 +3465,7 @@
GFP_KERNEL);
break;
case SME_CONNECTED:
- cfg80211_disconnected(vif->ndev, 0, NULL, 0, GFP_KERNEL);
+ cfg80211_disconnected(vif->ndev, 0, NULL, 0, true, GFP_KERNEL);
break;
}
diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c
index 971d770..2ac0548 100644
--- a/drivers/net/wireless/ath/ath9k/eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/eeprom.c
@@ -408,10 +408,9 @@
if (match) {
if (AR_SREV_9287(ah)) {
- /* FIXME: array overrun? */
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_9287[idxL].pwrPdg[i][0];
- maxPwrT4[i] = data_9287[idxL].pwrPdg[i][4];
+ maxPwrT4[i] = data_9287[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_9287[idxL].pwrPdg[i],
data_9287[idxL].vpdPdg[i],
@@ -421,7 +420,7 @@
} else if (eeprom_4k) {
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_4k[idxL].pwrPdg[i][0];
- maxPwrT4[i] = data_4k[idxL].pwrPdg[i][4];
+ maxPwrT4[i] = data_4k[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_4k[idxL].pwrPdg[i],
data_4k[idxL].vpdPdg[i],
@@ -431,7 +430,7 @@
} else {
for (i = 0; i < numXpdGains; i++) {
minPwrT4[i] = data_def[idxL].pwrPdg[i][0];
- maxPwrT4[i] = data_def[idxL].pwrPdg[i][4];
+ maxPwrT4[i] = data_def[idxL].pwrPdg[i][intercepts - 1];
ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i],
data_def[idxL].pwrPdg[i],
data_def[idxL].vpdPdg[i],
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index c018dea..5ca08ed 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -28,6 +28,16 @@
{ PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */
{ PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
+
+#ifdef CONFIG_ATH9K_PCOEM
+ /* Mini PCI AR9220 MB92 cards: Compex WLM200NX, Wistron DNMA-92 */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0029,
+ PCI_VENDOR_ID_ATHEROS,
+ 0x2096),
+ .driver_data = ATH9K_PCI_LED_ACT_HI },
+#endif
+
{ PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index d2725c8..47d634a 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -270,7 +270,7 @@
if (test_bit(wil_status_fwconnected, wil->status)) {
clear_bit(wil_status_fwconnected, wil->status);
cfg80211_disconnected(ndev, reason_code,
- NULL, 0, GFP_KERNEL);
+ NULL, 0, false, GFP_KERNEL);
} else if (test_bit(wil_status_fwconnecting, wil->status)) {
cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
index 183f08d..121e93f 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
@@ -635,7 +635,7 @@
return 0;
}
-static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
+static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h,
u32 slot_id, struct sk_buff **pktout,
bool remove_item)
{
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index faa106b..b471fa37 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -1150,7 +1150,8 @@
brcmf_err("WLC_DISASSOC failed (%d)\n", err);
}
clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
- cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL);
+ cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0,
+ true, GFP_KERNEL);
}
clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
@@ -1815,7 +1816,7 @@
return -EIO;
clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
- cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL);
+ cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
scbval.val = cpu_to_le32(reason_code);
diff --git a/drivers/net/wireless/cnss/Kconfig b/drivers/net/wireless/cnss/Kconfig
index 7094ac09..93c172c 100644
--- a/drivers/net/wireless/cnss/Kconfig
+++ b/drivers/net/wireless/cnss/Kconfig
@@ -19,6 +19,13 @@
to an address for which PCIe root complex would honor the read
without any errors.
+config CNSS_SDIO
+ bool "Enable/disable sdio driver for wifi module"
+ depends on CNSS
+ depends on MMC
+ ---help---
+ If enabled, sdio will be supported as I/O interface.
+
config CLD_DEBUG
bool "Enable/disable CLD debug features"
help
diff --git a/drivers/net/wireless/cnss/Makefile b/drivers/net/wireless/cnss/Makefile
index 79fb229..1e37b05 100644
--- a/drivers/net/wireless/cnss/Makefile
+++ b/drivers/net/wireless/cnss/Makefile
@@ -1,7 +1,7 @@
# Makefile for CNSS platform driver
obj-$(CONFIG_CNSS) += cnss_pci.o
-obj-$(CONFIG_CNSS) += cnss_sdio.o
+obj-$(CONFIG_CNSS_SDIO) += cnss_sdio.o
obj-$(CONFIG_CNSS_LOGGER) += logger/
obj-$(CONFIG_CNSS) += cnss_common.o
diff --git a/drivers/net/wireless/cnss/cnss_common.c b/drivers/net/wireless/cnss/cnss_common.c
index f9cf250..df3d792 100644
--- a/drivers/net/wireless/cnss/cnss_common.c
+++ b/drivers/net/wireless/cnss/cnss_common.c
@@ -259,9 +259,11 @@
int ret;
switch (cnss_get_dev_bus_type(dev)) {
+#if defined(CONFIG_CNSS_SDIO)
case CNSS_BUS_SDIO:
ret = cnss_sdio_request_bus_bandwidth(bandwidth);
break;
+#endif
case CNSS_BUS_PCI:
ret = cnss_pci_request_bus_bandwidth(bandwidth);
break;
@@ -278,8 +280,10 @@
void *cnss_common_get_virt_ramdump_mem(struct device *dev, unsigned long *size)
{
switch (cnss_get_dev_bus_type(dev)) {
+#if defined(CONFIG_CNSS_SDIO)
case CNSS_BUS_SDIO:
return cnss_sdio_get_virt_ramdump_mem(size);
+#endif
case CNSS_BUS_PCI:
return cnss_pci_get_virt_ramdump_mem(size);
default:
@@ -292,9 +296,11 @@
void cnss_common_device_self_recovery(struct device *dev)
{
switch (cnss_get_dev_bus_type(dev)) {
+#if defined(CONFIG_CNSS_SDIO)
case CNSS_BUS_SDIO:
cnss_sdio_device_self_recovery();
break;
+#endif
case CNSS_BUS_PCI:
cnss_pci_device_self_recovery();
break;
@@ -308,9 +314,11 @@
void cnss_common_schedule_recovery_work(struct device *dev)
{
switch (cnss_get_dev_bus_type(dev)) {
+#if defined(CONFIG_CNSS_SDIO)
case CNSS_BUS_SDIO:
cnss_sdio_schedule_recovery_work();
break;
+#endif
case CNSS_BUS_PCI:
cnss_pci_schedule_recovery_work();
break;
@@ -324,9 +332,11 @@
void cnss_common_device_crashed(struct device *dev)
{
switch (cnss_get_dev_bus_type(dev)) {
+#if defined(CONFIG_CNSS_SDIO)
case CNSS_BUS_SDIO:
cnss_sdio_device_crashed();
break;
+#endif
case CNSS_BUS_PCI:
cnss_pci_device_crashed();
break;
@@ -342,9 +352,11 @@
u8 *ret;
switch (cnss_get_dev_bus_type(dev)) {
+#if defined(CONFIG_CNSS_SDIO)
case CNSS_BUS_SDIO:
ret = cnss_sdio_get_wlan_mac_address(num);
break;
+#endif
case CNSS_BUS_PCI:
ret = cnss_pci_get_wlan_mac_address(num);
break;
@@ -363,9 +375,11 @@
int ret;
switch (cnss_get_dev_bus_type(dev)) {
+#if defined(CONFIG_CNSS_SDIO)
case CNSS_BUS_SDIO:
ret = cnss_sdio_set_wlan_mac_address(in, len);
break;
+#endif
case CNSS_BUS_PCI:
ret = cnss_pcie_set_wlan_mac_address(in, len);
break;
@@ -387,9 +401,11 @@
case CNSS_BUS_PCI:
ret = cnss_pcie_power_up(dev);
break;
+#if defined(CONFIG_CNSS_SDIO)
case CNSS_BUS_SDIO:
ret = cnss_sdio_power_up(dev);
break;
+#endif
default:
pr_err("%s: Invalid Bus Type\n", __func__);
ret = -EINVAL;
@@ -408,9 +424,11 @@
case CNSS_BUS_PCI:
ret = cnss_pcie_power_down(dev);
break;
+#if defined(CONFIG_CNSS_SDIO)
case CNSS_BUS_SDIO:
ret = cnss_sdio_power_down(dev);
break;
+#endif
default:
pr_err("%s: Invalid Bus Type\n", __func__);
ret = -EINVAL;
diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c
index 292a250..67addaa 100644
--- a/drivers/net/wireless/cnss/cnss_pci.c
+++ b/drivers/net/wireless/cnss/cnss_pci.c
@@ -1465,7 +1465,7 @@
struct dma_iommu_mapping *mapping;
int disable_htw = 1;
int atomic_ctx = 1;
- int ret;
+ int ret = 0;
mapping = arm_iommu_create_mapping(&platform_bus_type,
penv->smmu_iova_start,
diff --git a/drivers/net/wireless/cnss_genl/Kconfig b/drivers/net/wireless/cnss_genl/Kconfig
new file mode 100644
index 0000000..f1b8a58
--- /dev/null
+++ b/drivers/net/wireless/cnss_genl/Kconfig
@@ -0,0 +1,7 @@
+config CNSS_GENL
+ tristate "CNSS Generic Netlink Socket Driver"
+ ---help---
+ This module creates generic netlink family "CLD80211". This can be
+ used by cld driver and userspace utilities to communicate over
+ netlink sockets. This module creates different multicast groups to
+ facilitate the same.
diff --git a/drivers/net/wireless/cnss_genl/Makefile b/drivers/net/wireless/cnss_genl/Makefile
new file mode 100644
index 0000000..9431c9e
--- /dev/null
+++ b/drivers/net/wireless/cnss_genl/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CNSS_GENL) := cnss_nl.o
diff --git a/drivers/net/wireless/cnss_genl/cnss_nl.c b/drivers/net/wireless/cnss_genl/cnss_nl.c
new file mode 100644
index 0000000..cbac48cc
--- /dev/null
+++ b/drivers/net/wireless/cnss_genl/cnss_nl.c
@@ -0,0 +1,203 @@
+/* Copyright (c) 2017, The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <net/genetlink.h>
+#include <net/cnss_nl.h>
+#include <linux/module.h>
+
+#define CLD80211_GENL_NAME "cld80211"
+
+#define CLD80211_MULTICAST_GROUP_SVC_MSGS "svc_msgs"
+#define CLD80211_MULTICAST_GROUP_HOST_LOGS "host_logs"
+#define CLD80211_MULTICAST_GROUP_FW_LOGS "fw_logs"
+#define CLD80211_MULTICAST_GROUP_PER_PKT_STATS "per_pkt_stats"
+#define CLD80211_MULTICAST_GROUP_DIAG_EVENTS "diag_events"
+#define CLD80211_MULTICAST_GROUP_FATAL_EVENTS "fatal_events"
+#define CLD80211_MULTICAST_GROUP_OEM_MSGS "oem_msgs"
+
+static const struct genl_multicast_group nl_mcgrps[] = {
+ [CLD80211_MCGRP_SVC_MSGS] = { .name =
+ CLD80211_MULTICAST_GROUP_SVC_MSGS},
+ [CLD80211_MCGRP_HOST_LOGS] = { .name =
+ CLD80211_MULTICAST_GROUP_HOST_LOGS},
+ [CLD80211_MCGRP_FW_LOGS] = { .name =
+ CLD80211_MULTICAST_GROUP_FW_LOGS},
+ [CLD80211_MCGRP_PER_PKT_STATS] = { .name =
+ CLD80211_MULTICAST_GROUP_PER_PKT_STATS},
+ [CLD80211_MCGRP_DIAG_EVENTS] = { .name =
+ CLD80211_MULTICAST_GROUP_DIAG_EVENTS},
+ [CLD80211_MCGRP_FATAL_EVENTS] = { .name =
+ CLD80211_MULTICAST_GROUP_FATAL_EVENTS},
+ [CLD80211_MCGRP_OEM_MSGS] = { .name =
+ CLD80211_MULTICAST_GROUP_OEM_MSGS},
+};
+
+struct cld_ops {
+ cld80211_cb cb;
+ void *cb_ctx;
+};
+
+struct cld80211_nl_data {
+ struct cld_ops cld_ops[CLD80211_MAX_COMMANDS];
+};
+
+static struct cld80211_nl_data nl_data;
+
+static inline struct cld80211_nl_data *get_local_ctx(void)
+{
+ return &nl_data;
+}
+
+static struct genl_ops nl_ops[CLD80211_MAX_COMMANDS];
+
+/* policy for the attributes */
+static const struct nla_policy cld80211_policy[CLD80211_ATTR_MAX+1] = {
+ [CLD80211_ATTR_VENDOR_DATA] = { .type = NLA_NESTED },
+ [CLD80211_ATTR_DATA] = { .type = NLA_BINARY,
+ .len = CLD80211_MAX_NL_DATA },
+};
+
+static int cld80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ u8 cmd_id = ops->cmd;
+ struct cld80211_nl_data *nl = get_local_ctx();
+
+ if (cmd_id < 1 || cmd_id > CLD80211_MAX_COMMANDS) {
+ pr_err("CLD80211: Command Not supported: %u\n", cmd_id);
+ return -EOPNOTSUPP;
+ }
+ info->user_ptr[0] = nl->cld_ops[cmd_id - 1].cb;
+ info->user_ptr[1] = nl->cld_ops[cmd_id - 1].cb_ctx;
+
+ return 0;
+}
+
+/* The netlink family */
+static struct genl_family cld80211_fam = {
+ .id = GENL_ID_GENERATE,
+ .name = CLD80211_GENL_NAME,
+ .hdrsize = 0, /* no private header */
+ .version = 1, /* no particular meaning now */
+ .maxattr = CLD80211_ATTR_MAX,
+ .netnsok = true,
+ .pre_doit = cld80211_pre_doit,
+ .post_doit = NULL,
+};
+
+int register_cld_cmd_cb(u8 cmd_id, cld80211_cb func, void *cb_ctx)
+{
+ struct cld80211_nl_data *nl = get_local_ctx();
+
+ pr_debug("CLD80211: Registering command: %d\n", cmd_id);
+ if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) {
+ pr_debug("CLD80211: invalid command: %d\n", cmd_id);
+ return -EINVAL;
+ }
+
+ nl->cld_ops[cmd_id - 1].cb = func;
+ nl->cld_ops[cmd_id - 1].cb_ctx = cb_ctx;
+
+ return 0;
+}
+EXPORT_SYMBOL(register_cld_cmd_cb);
+
+int deregister_cld_cmd_cb(u8 cmd_id)
+{
+ struct cld80211_nl_data *nl = get_local_ctx();
+
+ pr_debug("CLD80211: De-registering command: %d\n", cmd_id);
+ if (!cmd_id || cmd_id > CLD80211_MAX_COMMANDS) {
+ pr_debug("CLD80211: invalid command: %d\n", cmd_id);
+ return -EINVAL;
+ }
+
+ nl->cld_ops[cmd_id - 1].cb = NULL;
+ nl->cld_ops[cmd_id - 1].cb_ctx = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL(deregister_cld_cmd_cb);
+
+struct genl_family *cld80211_get_genl_family(void)
+{
+ return &cld80211_fam;
+}
+EXPORT_SYMBOL(cld80211_get_genl_family);
+
+static int cld80211_doit(struct sk_buff *skb, struct genl_info *info)
+{
+ cld80211_cb cld_cb;
+ void *cld_ctx;
+
+ cld_cb = info->user_ptr[0];
+
+ if (cld_cb == NULL) {
+ pr_err("CLD80211: Not supported\n");
+ return -EOPNOTSUPP;
+ }
+ cld_ctx = info->user_ptr[1];
+
+ if (info->attrs[CLD80211_ATTR_VENDOR_DATA]) {
+ cld_cb(nla_data(info->attrs[CLD80211_ATTR_VENDOR_DATA]),
+ nla_len(info->attrs[CLD80211_ATTR_VENDOR_DATA]),
+ cld_ctx, info->snd_portid);
+ } else {
+ pr_err("CLD80211: No CLD80211_ATTR_VENDOR_DATA\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int __cld80211_init(void)
+{
+ int err, i;
+
+ memset(&nl_ops[0], 0, sizeof(nl_ops));
+
+ pr_info("CLD80211: Initializing\n");
+ for (i = 0; i < CLD80211_MAX_COMMANDS; i++) {
+ nl_ops[i].cmd = i + 1;
+ nl_ops[i].doit = cld80211_doit;
+ nl_ops[i].policy = cld80211_policy;
+ }
+
+ err = genl_register_family_with_ops_groups(&cld80211_fam, nl_ops,
+ nl_mcgrps);
+ if (err) {
+ pr_err("CLD80211: Failed to register cld80211 family: %d\n",
+ err);
+ }
+
+ return err;
+}
+
+static void __cld80211_exit(void)
+{
+ genl_unregister_family(&cld80211_fam);
+}
+
+static int __init cld80211_init(void)
+{
+ return __cld80211_init();
+}
+
+static void __exit cld80211_exit(void)
+{
+ __cld80211_exit();
+}
+
+module_init(cld80211_init);
+module_exit(cld80211_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CNSS generic netlink module");
diff --git a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
index 3d1a957..9bacdeb 100644
--- a/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
+++ b/drivers/net/wireless/cnss_prealloc/cnss_prealloc.c
@@ -234,7 +234,6 @@
pr_err("wcnss: %s: prealloc not available for size: %d\n",
__func__, size);
- WARN_ON(1);
return NULL;
}
diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c
index 6df3ee5..515aa3f 100644
--- a/drivers/net/wireless/hostap/hostap_hw.c
+++ b/drivers/net/wireless/hostap/hostap_hw.c
@@ -836,25 +836,30 @@
spin_lock_bh(&local->baplock);
res = hfa384x_setup_bap(dev, BAP0, rid, 0);
- if (!res)
- res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec));
+ if (res)
+ goto unlock;
+
+ res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec));
+ if (res)
+ goto unlock;
if (le16_to_cpu(rec.len) == 0) {
/* RID not available */
res = -ENODATA;
+ goto unlock;
}
rlen = (le16_to_cpu(rec.len) - 1) * 2;
- if (!res && exact_len && rlen != len) {
+ if (exact_len && rlen != len) {
printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: "
"rid=0x%04x, len=%d (expected %d)\n",
dev->name, rid, rlen, len);
res = -ENODATA;
}
- if (!res)
- res = hfa384x_from_bap(dev, BAP0, buf, len);
+ res = hfa384x_from_bap(dev, BAP0, buf, len);
+unlock:
spin_unlock_bh(&local->baplock);
mutex_unlock(&local->rid_bap_mtx);
diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c
index 93bdf68..ae047ab 100644
--- a/drivers/net/wireless/iwlegacy/3945.c
+++ b/drivers/net/wireless/iwlegacy/3945.c
@@ -1019,12 +1019,13 @@
int txq_id;
/* Tx queues */
- if (il->txq)
+ if (il->txq) {
for (txq_id = 0; txq_id < il->hw_params.max_txq_num; txq_id++)
if (txq_id == IL39_CMD_QUEUE_NUM)
il_cmd_queue_free(il);
else
il_tx_queue_free(il, txq_id);
+ }
/* free tx queue structure */
il_free_txq_mem(il);
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 380720c..111673e 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -835,14 +835,13 @@
* Events
*/
-void lbs_send_disconnect_notification(struct lbs_private *priv)
+void lbs_send_disconnect_notification(struct lbs_private *priv,
+ bool locally_generated)
{
lbs_deb_enter(LBS_DEB_CFG80211);
- cfg80211_disconnected(priv->dev,
- 0,
- NULL, 0,
- GFP_KERNEL);
+ cfg80211_disconnected(priv->dev, 0, NULL, 0, locally_generated,
+ GFP_KERNEL);
lbs_deb_leave(LBS_DEB_CFG80211);
}
@@ -1458,7 +1457,7 @@
cfg80211_disconnected(priv->dev,
reason,
- NULL, 0,
+ NULL, 0, true,
GFP_KERNEL);
priv->connect_status = LBS_DISCONNECTED;
@@ -2031,7 +2030,7 @@
ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd);
/* TODO: consider doing this at MACREG_INT_CODE_ADHOC_BCN_LOST time */
- lbs_mac_event_disconnected(priv);
+ lbs_mac_event_disconnected(priv, true);
lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
return ret;
diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h
index 10995f5..acccc29 100644
--- a/drivers/net/wireless/libertas/cfg.h
+++ b/drivers/net/wireless/libertas/cfg.h
@@ -10,7 +10,8 @@
int lbs_cfg_register(struct lbs_private *priv);
void lbs_cfg_free(struct lbs_private *priv);
-void lbs_send_disconnect_notification(struct lbs_private *priv);
+void lbs_send_disconnect_notification(struct lbs_private *priv,
+ bool locally_generated);
void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event);
void lbs_scan_done(struct lbs_private *priv);
diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h
index 4279e8a..0c5444b 100644
--- a/drivers/net/wireless/libertas/cmd.h
+++ b/drivers/net/wireless/libertas/cmd.h
@@ -68,7 +68,8 @@
/* From cmdresp.c */
-void lbs_mac_event_disconnected(struct lbs_private *priv);
+void lbs_mac_event_disconnected(struct lbs_private *priv,
+ bool locally_generated);
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
index 65f18f1..e5442e8 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -19,10 +19,13 @@
* reset link state etc.
*
* @priv: A pointer to struct lbs_private structure
+ * @locally_generated: indicates disconnect was requested locally
+ * (usually by userspace)
*
* returns: n/a
*/
-void lbs_mac_event_disconnected(struct lbs_private *priv)
+void lbs_mac_event_disconnected(struct lbs_private *priv,
+ bool locally_generated)
{
if (priv->connect_status != LBS_CONNECTED)
return;
@@ -36,7 +39,7 @@
msleep_interruptible(1000);
if (priv->wdev->iftype == NL80211_IFTYPE_STATION)
- lbs_send_disconnect_notification(priv);
+ lbs_send_disconnect_notification(priv, locally_generated);
/* report disconnect to upper layer */
netif_stop_queue(priv->dev);
@@ -229,17 +232,17 @@
case MACREG_INT_CODE_DEAUTHENTICATED:
lbs_deb_cmd("EVENT: deauthenticated\n");
- lbs_mac_event_disconnected(priv);
+ lbs_mac_event_disconnected(priv, false);
break;
case MACREG_INT_CODE_DISASSOCIATED:
lbs_deb_cmd("EVENT: disassociated\n");
- lbs_mac_event_disconnected(priv);
+ lbs_mac_event_disconnected(priv, false);
break;
case MACREG_INT_CODE_LINK_LOST_NO_SCAN:
lbs_deb_cmd("EVENT: link lost\n");
- lbs_mac_event_disconnected(priv);
+ lbs_mac_event_disconnected(priv, true);
break;
case MACREG_INT_CODE_PS_SLEEP:
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index c9ad4cf..c30cd5d 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -2277,6 +2277,7 @@
if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] ||
!info->attrs[HWSIM_ATTR_FLAGS] ||
!info->attrs[HWSIM_ATTR_COOKIE] ||
+ !info->attrs[HWSIM_ATTR_SIGNAL] ||
!info->attrs[HWSIM_ATTR_TX_INFO])
goto out;
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 8d6c259..6822ae9 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -1423,7 +1423,7 @@
ret = mwifiex_deauthenticate_infra(priv, mac);
if (ret)
cfg80211_disconnected(priv->netdev, 0, NULL, 0,
- GFP_KERNEL);
+ true, GFP_KERNEL);
break;
case NL80211_IFTYPE_ADHOC:
return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_STOP,
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index f1c240e..0028fce 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -125,7 +125,7 @@
if (priv->bss_mode == NL80211_IFTYPE_STATION ||
priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) {
cfg80211_disconnected(priv->netdev, reason_code, NULL, 0,
- GFP_KERNEL);
+ false, GFP_KERNEL);
}
memset(priv->cfg_bssid, 0, ETH_ALEN);
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 1a4facd..700d6bf 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -2861,7 +2861,7 @@
deauthenticate(usbdev);
- cfg80211_disconnected(usbdev->net, 0, NULL, 0, GFP_KERNEL);
+ cfg80211_disconnected(usbdev->net, 0, NULL, 0, true, GFP_KERNEL);
}
netif_carrier_off(usbdev->net);
diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c
index af24869..339f94e 100644
--- a/drivers/net/wireless/rtlwifi/base.c
+++ b/drivers/net/wireless/rtlwifi/base.c
@@ -1587,9 +1587,9 @@
if (((rtlpriv->link_info.num_rx_inperiod +
rtlpriv->link_info.num_tx_inperiod) > 8) ||
(rtlpriv->link_info.num_rx_inperiod > 2))
- rtl_lps_enter(hw);
- else
rtl_lps_leave(hw);
+ else
+ rtl_lps_enter(hw);
}
rtlpriv->link_info.num_rx_inperiod = 0;
diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c
index 348d5aec..2afe4cc 100644
--- a/drivers/net/wireless/rtlwifi/pci.c
+++ b/drivers/net/wireless/rtlwifi/pci.c
@@ -1573,7 +1573,7 @@
true,
HW_DESC_TXBUFF_ADDR),
skb->len, PCI_DMA_TODEVICE);
- kfree_skb(skb);
+ dev_kfree_skb_irq(skb);
ring->idx = (ring->idx + 1) % ring->entries;
}
ring->idx = 0;
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c
index 775e7bc..24a8552 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c
@@ -93,7 +93,6 @@
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
rtl8723be_bt_reg_init(hw);
- rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer();
rtlpriv->dm.dm_initialgain_enable = 1;
@@ -151,6 +150,10 @@
rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps;
rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps;
rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support;
+ rtlpriv->cfg->mod_params->sw_crypto =
+ rtlpriv->cfg->mod_params->sw_crypto;
+ rtlpriv->cfg->mod_params->disable_watchdog =
+ rtlpriv->cfg->mod_params->disable_watchdog;
if (rtlpriv->cfg->mod_params->disable_watchdog)
pr_info("watchdog disabled\n");
rtlpriv->psc.reg_fwctrl_lps = 3;
@@ -267,6 +270,9 @@
.inactiveps = true,
.swctrl_lps = false,
.fwctrl_lps = true,
+ .msi_support = false,
+ .disable_watchdog = false,
+ .debug = DBG_EMERG,
};
static struct rtl_hal_cfg rtl8723be_hal_cfg = {
diff --git a/drivers/of/base.c b/drivers/of/base.c
index df96267..08e2439 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -112,6 +112,7 @@
return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length);
}
+/* always return newly allocated name, caller must free after use */
static const char *safe_name(struct kobject *kobj, const char *orig_name)
{
const char *name = orig_name;
@@ -126,9 +127,12 @@
name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i);
}
- if (name != orig_name)
+ if (name == orig_name) {
+ name = kstrdup(orig_name, GFP_KERNEL);
+ } else {
pr_warn("device-tree: Duplicate name in %s, renamed to \"%s\"\n",
kobject_name(kobj), name);
+ }
return name;
}
@@ -159,6 +163,7 @@
int __of_attach_node_sysfs(struct device_node *np)
{
const char *name;
+ struct kobject *parent;
struct property *pp;
int rc;
@@ -171,15 +176,16 @@
np->kobj.kset = of_kset;
if (!np->parent) {
/* Nodes without parents are new top level trees */
- rc = kobject_add(&np->kobj, NULL, "%s",
- safe_name(&of_kset->kobj, "base"));
+ name = safe_name(&of_kset->kobj, "base");
+ parent = NULL;
} else {
name = safe_name(&np->parent->kobj, kbasename(np->full_name));
- if (!name || !name[0])
- return -EINVAL;
-
- rc = kobject_add(&np->kobj, &np->parent->kobj, "%s", name);
+ parent = &np->parent->kobj;
}
+ if (!name)
+ return -ENOMEM;
+ rc = kobject_add(&np->kobj, parent, "%s", name);
+ kfree(name);
if (rc)
return rc;
@@ -1734,6 +1740,12 @@
return 0;
}
+void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop)
+{
+ sysfs_remove_bin_file(&np->kobj, &prop->attr);
+ kfree(prop->attr.attr.name);
+}
+
void __of_remove_property_sysfs(struct device_node *np, struct property *prop)
{
if (!IS_ENABLED(CONFIG_SYSFS))
@@ -1741,7 +1753,7 @@
/* at early boot, bail here and defer setup to of_init() */
if (of_kset && of_node_is_attached(np))
- sysfs_remove_bin_file(&np->kobj, &prop->attr);
+ __of_sysfs_remove_bin_file(np, prop);
}
/**
@@ -1811,7 +1823,7 @@
return;
if (oldprop)
- sysfs_remove_bin_file(&np->kobj, &oldprop->attr);
+ __of_sysfs_remove_bin_file(np, oldprop);
__of_add_property_sysfs(np, newprop);
}
diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c
index 3351ef4..57e25f6 100644
--- a/drivers/of/dynamic.c
+++ b/drivers/of/dynamic.c
@@ -55,7 +55,7 @@
/* only remove properties if on sysfs */
if (of_node_is_attached(np)) {
for_each_property_of_node(np, pp)
- sysfs_remove_bin_file(&np->kobj, &pp->attr);
+ __of_sysfs_remove_bin_file(np, pp);
kobject_del(&np->kobj);
}
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index c21e364..10145d3 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -386,13 +386,13 @@
EXPORT_SYMBOL_GPL(of_irq_to_resource);
/**
- * of_irq_get - Decode a node's IRQ and return it as a Linux irq number
+ * of_irq_get - Decode a node's IRQ and return it as a Linux IRQ number
* @dev: pointer to device tree node
- * @index: zero-based index of the irq
+ * @index: zero-based index of the IRQ
*
- * Returns Linux irq number on success, or -EPROBE_DEFER if the irq domain
- * is not yet created.
- *
+ * Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
+ * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
+ * of any other failure.
*/
int of_irq_get(struct device_node *dev, int index)
{
@@ -413,12 +413,13 @@
EXPORT_SYMBOL_GPL(of_irq_get);
/**
- * of_irq_get_byname - Decode a node's IRQ and return it as a Linux irq number
+ * of_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
* @dev: pointer to device tree node
- * @name: irq name
+ * @name: IRQ name
*
- * Returns Linux irq number on success, or -EPROBE_DEFER if the irq domain
- * is not yet created, or error code in case of any other failure.
+ * Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
+ * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
+ * of any other failure.
*/
int of_irq_get_byname(struct device_node *dev, const char *name)
{
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 8e882e7..46ddbee 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -81,6 +81,9 @@
extern void __of_detach_node(struct device_node *np);
extern void __of_detach_node_sysfs(struct device_node *np);
+extern void __of_sysfs_remove_bin_file(struct device_node *np,
+ struct property *prop);
+
/* iterators for transactions, used for overlays */
/* forward iterator */
#define for_each_transaction_entry(_oft, _te) \
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index bf9af365..1033764 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -2394,6 +2394,8 @@
return -ENODEV;
}
+ pdev = PCIE_BUS_PRIV_DATA(dev->bus);
+
if (option == 12 || option == 13) {
if (!base || base > 5) {
PCIE_DBG_FS(pdev, "Invalid base_sel: 0x%x\n", base);
@@ -2420,7 +2422,6 @@
}
}
- pdev = PCIE_BUS_PRIV_DATA(dev->bus);
rc_sel = 1 << pdev->rc_idx;
msm_pcie_sel_debug_testcase(pdev, option);
diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c
index c81d0ce..c177977 100644
--- a/drivers/pci/host/pcie-xilinx.c
+++ b/drivers/pci/host/pcie-xilinx.c
@@ -148,10 +148,10 @@
*/
static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port)
{
- u32 val = pcie_read(port, XILINX_PCIE_REG_RPEFR);
+ unsigned long val = pcie_read(port, XILINX_PCIE_REG_RPEFR);
if (val & XILINX_PCIE_RPEFR_ERR_VALID) {
- dev_dbg(port->dev, "Requester ID %d\n",
+ dev_dbg(port->dev, "Requester ID %lu\n",
val & XILINX_PCIE_RPEFR_REQ_ID);
pcie_write(port, XILINX_PCIE_RPEFR_ALL_MASK,
XILINX_PCIE_REG_RPEFR);
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index eb15438..795d800 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -1363,10 +1363,10 @@
if (!sysfs_initialized)
return -EACCES;
- if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
- retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
- else
+ if (pdev->cfg_size > PCI_CFG_SPACE_SIZE)
retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+ else
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
if (retval)
goto err;
@@ -1418,10 +1418,10 @@
err_resource_files:
pci_remove_resource_files(pdev);
err_config_file:
- if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
- sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
- else
+ if (pdev->cfg_size > PCI_CFG_SPACE_SIZE)
sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+ else
+ sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
err:
return retval;
}
@@ -1455,10 +1455,10 @@
pci_remove_capabilities_sysfs(pdev);
- if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
- sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
- else
+ if (pdev->cfg_size > PCI_CFG_SPACE_SIZE)
sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+ else
+ sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
pci_remove_resource_files(pdev);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 597629e..14c3cbd 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -10,6 +10,8 @@
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pm.h>
#include <linux/slab.h>
@@ -1986,6 +1988,10 @@
if (!dev->pme_support)
return false;
+ /* PME-capable in principle, but not from the intended sleep state */
+ if (!pci_pme_capable(dev, pci_target_state(dev)))
+ return false;
+
while (bus->parent) {
struct pci_dev *bridge = bus->self;
@@ -4491,6 +4497,55 @@
{
return atomic_inc_return(&__domain_nr);
}
+
+#ifdef CONFIG_PCI_DOMAINS_GENERIC
+void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent)
+{
+ static int use_dt_domains = -1;
+ int domain = -1;
+
+ if (parent)
+ domain = of_get_pci_domain_nr(parent->of_node);
+ /*
+ * Check DT domain and use_dt_domains values.
+ *
+ * If DT domain property is valid (domain >= 0) and
+ * use_dt_domains != 0, the DT assignment is valid since this means
+ * we have not previously allocated a domain number by using
+ * pci_get_new_domain_nr(); we should also update use_dt_domains to
+ * 1, to indicate that we have just assigned a domain number from
+ * DT.
+ *
+ * If DT domain property value is not valid (ie domain < 0), and we
+ * have not previously assigned a domain number from DT
+ * (use_dt_domains != 1) we should assign a domain number by
+ * using the:
+ *
+ * pci_get_new_domain_nr()
+ *
+ * API and update the use_dt_domains value to keep track of method we
+ * are using to assign domain numbers (use_dt_domains = 0).
+ *
+ * All other combinations imply we have a platform that is trying
+ * to mix domain numbers obtained from DT and pci_get_new_domain_nr(),
+ * which is a recipe for domain mishandling and it is prevented by
+ * invalidating the domain value (domain = -1) and printing a
+ * corresponding error.
+ */
+ if (domain >= 0 && use_dt_domains) {
+ use_dt_domains = 1;
+ } else if (domain < 0 && use_dt_domains != 1) {
+ use_dt_domains = 0;
+ domain = pci_get_new_domain_nr();
+ } else {
+ dev_err(parent, "Node %s has inconsistent \"linux,pci-domain\" property in DT\n",
+ parent->of_node->full_name);
+ domain = -1;
+ }
+
+ bus->domain_nr = domain;
+}
+#endif
#endif
/**
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 08a8f32..bc31b11 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -178,9 +178,6 @@
struct pci_bus_region region, inverted_region;
bool bar_too_big = false, bar_too_high = false, bar_invalid = false;
- if (dev->non_compliant_bars)
- return 0;
-
mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
/* No printks while decoding is disabled! */
@@ -332,6 +329,9 @@
{
unsigned int pos, reg;
+ if (dev->non_compliant_bars)
+ return;
+
for (pos = 0; pos < howmany; pos++) {
struct resource *res = &dev->resource[pos];
reg = PCI_BASE_ADDRESS_0 + (pos << 2);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index b6d646a..b3e63f5 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -288,6 +288,18 @@
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, quirk_citrine);
+/*
+ * This chip can cause bus lockups if config addresses above 0x600
+ * are read or written.
+ */
+static void quirk_nfp6000(struct pci_dev *dev)
+{
+ dev->cfg_size = 0x600;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP4000, quirk_nfp6000);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP6000, quirk_nfp6000);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP6000_VF, quirk_nfp6000);
+
/* On IBM Crocodile ipr SAS adapters, expand BAR to system page size */
static void quirk_extend_bar_to_page(struct pci_dev *dev)
{
@@ -3060,13 +3072,15 @@
}
/*
- * Atheros AR93xx chips do not behave after a bus reset. The device will
- * throw a Link Down error on AER-capable systems and regardless of AER,
- * config space of the device is never accessible again and typically
- * causes the system to hang or reset when access is attempted.
+ * Some Atheros AR9xxx and QCA988x chips do not behave after a bus reset.
+ * The device will throw a Link Down error on AER-capable systems and
+ * regardless of AER, config space of the device is never accessible again
+ * and typically causes the system to hang or reset when access is attempted.
* http://www.spinics.net/lists/linux-pci/msg34797.html
*/
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0030, quirk_no_bus_reset);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0032, quirk_no_bus_reset);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset);
#ifdef CONFIG_ACPI
/*
diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c
index 6f806f9..857f29b 100644
--- a/drivers/pinctrl/freescale/pinctrl-imx.c
+++ b/drivers/pinctrl/freescale/pinctrl-imx.c
@@ -205,9 +205,9 @@
pin_reg = &info->pin_regs[pin_id];
if (pin_reg->mux_reg == -1) {
- dev_err(ipctl->dev, "Pin(%s) does not support mux function\n",
+ dev_dbg(ipctl->dev, "Pin(%s) does not support mux function\n",
info->pins[pin_id].name);
- return -EINVAL;
+ continue;
}
if (info->flags & SHARE_MUX_CONF_REG) {
diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.c b/drivers/pinctrl/nomadik/pinctrl-nomadik.c
index 746db6a..25d5a21 100644
--- a/drivers/pinctrl/nomadik/pinctrl-nomadik.c
+++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.c
@@ -1025,7 +1025,7 @@
int pullidx = 0;
if (pull)
- pullidx = data_out ? 1 : 2;
+ pullidx = data_out ? 2 : 1;
seq_printf(s, " gpio-%-3d (%-20.20s) in %s %s",
gpio,
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index fb94b77..f94d46c 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -1273,9 +1273,9 @@
/* Parse pins in each row from LSB */
while (mask) {
- bit_pos = ffs(mask);
+ bit_pos = __ffs(mask);
pin_num_from_lsb = bit_pos / pcs->bits_per_pin;
- mask_pos = ((pcs->fmask) << (bit_pos - 1));
+ mask_pos = ((pcs->fmask) << bit_pos);
val_pos = val & mask_pos;
submask = mask & mask_pos;
@@ -1576,6 +1576,9 @@
else
mask &= ~soc_mask;
pcs->write(mask, pcswi->reg);
+
+ /* flush posted write */
+ mask = pcs->read(pcswi->reg);
raw_spin_unlock(&pcs->lock);
}
@@ -1851,7 +1854,7 @@
ret = of_property_read_u32(np, "pinctrl-single,function-mask",
&pcs->fmask);
if (!ret) {
- pcs->fshift = ffs(pcs->fmask) - 1;
+ pcs->fshift = __ffs(pcs->fmask);
pcs->fmax = pcs->fmask >> pcs->fshift;
} else {
/* If mask property doesn't exist, function mux is invalid. */
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index bc812e7..73178cd 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm.c
@@ -940,7 +940,8 @@
val = readl(pctrl->regs + g->intr_status_reg);
if (val & BIT(g->intr_status_bit)) {
irq_pin = irq_find_mapping(gc->irqdomain, i);
- handled += generic_handle_irq(irq_pin);
+ generic_handle_irq(irq_pin);
+ handled++;
}
}
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos5440.c b/drivers/pinctrl/samsung/pinctrl-exynos5440.c
index 88acfc0..9a77a2a 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos5440.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos5440.c
@@ -109,6 +109,7 @@
* @nr_groups: number of pin groups available.
* @pmx_functions: list of pin functions parsed from device tree.
* @nr_functions: number of pin functions available.
+ * @range: gpio range to register with pinctrl
*/
struct exynos5440_pinctrl_priv_data {
void __iomem *reg_base;
@@ -119,6 +120,7 @@
unsigned int nr_groups;
const struct exynos5440_pmx_func *pmx_functions;
unsigned int nr_functions;
+ struct pinctrl_gpio_range range;
};
/**
@@ -769,7 +771,6 @@
struct pinctrl_desc *ctrldesc;
struct pinctrl_dev *pctl_dev;
struct pinctrl_pin_desc *pindesc, *pdesc;
- struct pinctrl_gpio_range grange;
char *pin_names;
int pin, ret;
@@ -827,12 +828,12 @@
return -EINVAL;
}
- grange.name = "exynos5440-pctrl-gpio-range";
- grange.id = 0;
- grange.base = 0;
- grange.npins = EXYNOS5440_MAX_PINS;
- grange.gc = priv->gc;
- pinctrl_add_gpio_range(pctl_dev, &grange);
+ priv->range.name = "exynos5440-pctrl-gpio-range";
+ priv->range.id = 0;
+ priv->range.base = 0;
+ priv->range.npins = EXYNOS5440_MAX_PINS;
+ priv->range.gc = priv->gc;
+ pinctrl_add_gpio_range(pctl_dev, &priv->range);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index 3819a57..f9f9e3d 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -1834,7 +1834,7 @@
u32 max_cmds = ipa_get_max_flt_rt_cmds(ipa_ctx->ipa_num_pipes);
mem.base = dma_alloc_coherent(ipa_ctx->pdev, 4, &mem.phys_base,
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!mem.base) {
IPAERR("failed to alloc DMA buff of size 4\n");
return -ENOMEM;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c
index 7c10c4c..3e2d150 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_mhi.c
@@ -276,8 +276,6 @@
*/
int ipa2_disconnect_mhi_pipe(u32 clnt_hdl)
{
- struct ipa_ep_context *ep;
-
IPA_MHI_FUNC_ENTRY();
if (clnt_hdl >= ipa_ctx->ipa_num_pipes) {
@@ -290,7 +288,7 @@
return -EINVAL;
}
- ep->valid = 0;
+ ipa_ctx->ep[clnt_hdl].valid = 0;
ipa_delete_dflt_flt_rules(clnt_hdl);
IPA_MHI_DBG("client (ep: %d) disconnected\n", clnt_hdl);
@@ -302,14 +300,13 @@
bool LPTransitionRejected, bool brstmode_enabled,
union __packed gsi_channel_scratch ch_scratch, u8 index)
{
- int i;
int res;
IPA_MHI_FUNC_ENTRY();
res = ipa_uc_mhi_resume_channel(index, LPTransitionRejected);
if (res) {
IPA_MHI_ERR("failed to suspend channel %d error %d\n",
- i, res);
+ index, res);
return res;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 25aebf5..b53d9aa 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -2063,44 +2063,43 @@
if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v4, IPA_RULE_HASHABLE, &mem)) {
IPAERR("failed to clean q6 flt tbls (v4/hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v6, IPA_RULE_HASHABLE, &mem)) {
IPAERR("failed to clean q6 flt tbls (v6/hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v4, IPA_RULE_NON_HASHABLE, &mem)) {
IPAERR("failed to clean q6 flt tbls (v4/non-hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_flt_tbls(IPA_IP_v6, IPA_RULE_NON_HASHABLE, &mem)) {
IPAERR("failed to clean q6 flt tbls (v6/non-hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v4, IPA_RULE_HASHABLE, &mem)) {
IPAERR("failed to clean q6 rt tbls (v4/hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v6, IPA_RULE_HASHABLE, &mem)) {
IPAERR("failed to clean q6 rt tbls (v6/hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v4, IPA_RULE_NON_HASHABLE, &mem)) {
IPAERR("failed to clean q6 rt tbls (v4/non-hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
if (ipa3_q6_clean_q6_rt_tbls(IPA_IP_v6, IPA_RULE_NON_HASHABLE, &mem)) {
IPAERR("failed to clean q6 rt tbls (v6/non-hashable)\n");
- goto bail_desc;
+ return -EFAULT;
}
/* Flush rules cache */
desc = kzalloc(sizeof(struct ipa3_desc), GFP_KERNEL);
if (!desc) {
IPAERR("failed to allocate memory\n");
- retval = -ENOMEM;
- goto bail_dma;
+ return -ENOMEM;
}
flush.v4_flt = true;
@@ -2117,6 +2116,7 @@
®_write_cmd, false);
if (!cmd_pyld) {
IPAERR("fail construct register_write imm cmd\n");
+ retval = -EFAULT;
goto bail_desc;
}
desc->opcode =
@@ -2133,9 +2133,9 @@
}
ipahal_destroy_imm_cmd(cmd_pyld);
+
bail_desc:
kfree(desc);
-bail_dma:
dma_free_coherent(ipa3_ctx->pdev, mem.size, mem.base, mem.phys_base);
IPADBG("Done - retval = %d\n", retval);
return retval;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
index 8e4661a..83d74d5 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
@@ -549,11 +549,6 @@
return res;
}
}
- if (res) {
- IPA_MHI_ERR("failed to resume channel error %d\n",
- res);
- return res;
- }
res = gsi_start_channel(ep->gsi_chan_hdl);
if (res) {
diff --git a/drivers/platform/msm/msm_bus/msm_bus_dbg.c b/drivers/platform/msm/msm_bus/msm_bus_dbg.c
index 8db3a62..444d79fa 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_dbg.c
+++ b/drivers/platform/msm/msm_bus/msm_bus_dbg.c
@@ -577,7 +577,6 @@
list_for_each_entry(cldata, &cl_list, list) {
if (strnstr(chid, cldata->pdata->name, cnt)) {
found = 1;
- cldata = cldata;
strsep(&chid, " ");
if (chid) {
ret = kstrtoul(chid, 10, &index);
diff --git a/drivers/platform/msm/msm_bus/msm_bus_rules.c b/drivers/platform/msm/msm_bus/msm_bus_rules.c
index 297ba9f..2f91149 100644
--- a/drivers/platform/msm/msm_bus/msm_bus_rules.c
+++ b/drivers/platform/msm/msm_bus/msm_bus_rules.c
@@ -593,7 +593,7 @@
static bool __rule_unregister(int num_rules, struct bus_rule_type *rule,
struct notifier_block *nb)
{
- int i;
+ int i = 0;
struct rule_node_info *node = NULL;
struct rule_node_info *node_tmp = NULL;
struct rules_def *node_rule;
@@ -610,18 +610,20 @@
goto exit_unregister_rule;
}
match_found = true;
- list_for_each_entry_safe(node_rule, node_rule_tmp,
+ for (i = 0; i < num_rules; i++) {
+ list_for_each_entry_safe(node_rule, node_rule_tmp,
&node->node_rules, link) {
- if (comp_rules(&node_rule->rule_ops,
+ if (comp_rules(&node_rule->rule_ops,
&rule[i]) == 0) {
- list_del(&node_rule->link);
- kfree(node_rule);
- match_found = true;
- node->num_rules--;
- list_sort(NULL,
- &node->node_rules,
- node_rules_compare);
- break;
+ list_del(&node_rule->link);
+ kfree(node_rule);
+ match_found = true;
+ node->num_rules--;
+ list_sort(NULL,
+ &node->node_rules,
+ node_rules_compare);
+ break;
+ }
}
}
if (!node->num_rules)
@@ -728,4 +730,3 @@
return ret;
}
-
diff --git a/drivers/platform/msm/qpnp-haptic.c b/drivers/platform/msm/qpnp-haptic.c
index e9ea721..39123be 100755
--- a/drivers/platform/msm/qpnp-haptic.c
+++ b/drivers/platform/msm/qpnp-haptic.c
@@ -1840,7 +1840,7 @@
struct qpnp_hap *hap = container_of(work, struct qpnp_hap,
work);
u8 val = 0x00;
- int rc, reg_en;
+ int rc, reg_en = 0;
if (hap->vcc_pon) {
reg_en = regulator_enable(hap->vcc_pon);
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index 7d01b04..560a4dd 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -298,16 +298,6 @@
static int warm_boot;
module_param(warm_boot, int, 0);
-static int debug_flag = 0;
-static int __init htcramdump_set(char *str)
-{
- if (!strncmp(str, "1", 1))
- debug_flag = 1;
-
- return 0;
-}
-__setup("htcramdump=", htcramdump_set);
-
static int
qpnp_pon_masked_write(struct qpnp_pon *pon, u16 addr, u8 mask, u8 val)
{
@@ -1483,8 +1473,6 @@
"Unable to read s2-type\n");
return rc;
}
- if (debug_flag)
- cfg->s2_type = 0x1;
if (cfg->s2_type > QPNP_PON_RESET_TYPE_MAX) {
dev_err(&pon->spmi->dev,
"Incorrect reset type specified\n");
@@ -1729,7 +1717,7 @@
static int qpnp_pon_smpl_en_get(char *buf, const struct kernel_param *kp)
{
- bool enabled;
+ bool enabled = false;
int rc;
rc = qpnp_pon_get_trigger_config(PON_SMPL, &enabled);
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 43188c9..261bd6b 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -2761,18 +2761,18 @@
log_event_dbg("%s: received SPS_CALLBACK_BAM_TIMER_IRQ\n",
__func__);
-
- spin_lock(&ctx->usb_bam_lock);
-
bam = get_bam_type_from_core_name((char *)user);
+
if (bam < 0 || bam >= MAX_BAMS) {
log_event_err("%s: Invalid bam, type=%d ,name=%s\n",
__func__, bam, (char *)user);
- spin_unlock(&ctx->usb_bam_lock);
return;
}
+
ctx = &msm_usb_bam[bam];
+ spin_lock(&ctx->usb_bam_lock);
+
ctx->is_bam_inactivity = true;
log_event_dbg("%s: Inactivity happened on bam=%s,%d\n",
__func__, (char *)user, bam);
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 26c4fd1..e89f88c 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -1867,11 +1867,24 @@
return status;
}
+#define ACER_WMID_ACCEL_HID "BST0001"
+
static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level,
void *ctx, void **retval)
{
+ struct acpi_device *dev;
+
+ if (!strcmp(ctx, "SENR")) {
+ if (acpi_bus_get_device(ah, &dev))
+ return AE_OK;
+ if (!strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev)))
+ return AE_OK;
+ } else
+ return AE_OK;
+
*(acpi_handle *)retval = ah;
- return AE_OK;
+
+ return AE_CTRL_TERMINATE;
}
static int __init acer_wmi_get_handle(const char *name, const char *prop,
@@ -1898,7 +1911,7 @@
{
int err;
- err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle);
+ err = acer_wmi_get_handle("SENR", ACER_WMID_ACCEL_HID, &gsensor_handle);
if (err)
return err;
@@ -2274,10 +2287,11 @@
err = acer_wmi_input_setup();
if (err)
return err;
+ err = acer_wmi_accel_setup();
+ if (err)
+ return err;
}
- acer_wmi_accel_setup();
-
err = platform_driver_register(&acer_platform_driver);
if (err) {
pr_err("Unable to register platform driver\n");
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index 301386c..450d790 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -723,6 +723,11 @@
if (err)
return err;
+ err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1, &wireless,
+ sizeof(wireless), 0);
+ if (err)
+ return err;
+
if (wireless & 0x1) {
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
RFKILL_TYPE_WLAN,
@@ -910,7 +915,7 @@
gps_rfkill = NULL;
rfkill2_count = 0;
- if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))
+ if (hp_wmi_rfkill_setup(device))
hp_wmi_rfkill2_setup(device);
err = device_create_file(&device->dev, &dev_attr_display);
diff --git a/drivers/power/htc_battery.c b/drivers/power/htc_battery.c
index bef4c21..11e45cf 100644
--- a/drivers/power/htc_battery.c
+++ b/drivers/power/htc_battery.c
@@ -1994,6 +1994,7 @@
{
case HTC_STATS_CATEGORY_ALL: category_ptr = &g_htc_stats_category_all; break;
case HTC_STATS_CATEGORY_FULL_LOW: category_ptr = &g_htc_stats_category_full_low; break;
+ default: return;
}
category_ptr->sample_count += 1;
@@ -2003,11 +2004,12 @@
const char* htc_stats_category2str(int category)
{
- const char* ret;
+ const char* ret = NULL;
switch (category)
{
case HTC_STATS_CATEGORY_ALL: ret = "all"; break;
case HTC_STATS_CATEGORY_FULL_LOW: ret = "full_low"; break;
+ default: return ret;
}
return ret;
}
@@ -2021,6 +2023,7 @@
switch (category) {
case HTC_STATS_CATEGORY_ALL: category_ptr = &g_htc_stats_category_all; break;
case HTC_STATS_CATEGORY_FULL_LOW: category_ptr = &g_htc_stats_category_full_low; break;
+ default: return;
}
if (htc_stats_is_valid(category, chg_time, dischg_time, unplug_level, plug_level))
diff --git a/drivers/power/pmic-voter.c b/drivers/power/pmic-voter.c
index 4ee97a6..4e05363 100644
--- a/drivers/power/pmic-voter.c
+++ b/drivers/power/pmic-voter.c
@@ -183,6 +183,8 @@
effective_result, client_id,
state, client_id);
}
+ // fall through
+ default:
goto out;
}
diff --git a/drivers/power/qpnp-fg.c b/drivers/power/qpnp-fg.c
index 1ac5fb7..2354d93 100644
--- a/drivers/power/qpnp-fg.c
+++ b/drivers/power/qpnp-fg.c
@@ -795,6 +795,9 @@
int rc;
u8 reg;
+ if (WARN_ONCE(len > 1, "Length invalid %d\n", len))
+ return -EINVAL;
+
rc = fg_read(chip, ®, addr, len);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc);
@@ -1141,8 +1144,9 @@
if (offset > 3)
return -EINVAL;
- address = ((address + offset) / 4) * 4;
- offset = (address + offset) % 4;
+ address += offset;
+ offset = address % 4;
+ address = (address / 4) * 4;
user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
@@ -1510,20 +1514,20 @@
len, address, offset);
while (len > 0) {
- num_bytes = (offset + len) > BUF_LEN ?
- (BUF_LEN - offset) : len;
- /* write to byte_enable */
- for (i = offset; i < (offset + num_bytes); i++)
- byte_enable |= BIT(i);
+ num_bytes = (offset + len) > BUF_LEN ?
+ (BUF_LEN - offset) : len;
+ /* write to byte_enable */
+ for (i = offset; i < (offset + num_bytes); i++)
+ byte_enable |= BIT(i);
- rc = fg_write(chip, &byte_enable,
- chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1);
- if (rc) {
- pr_err("Unable to write to byte_en_reg rc=%d\n",
- rc);
- return rc;
- }
- /* write data */
+ rc = fg_write(chip, &byte_enable,
+ chip->mem_base + MEM_INTF_IMA_BYTE_EN, 1);
+ if (rc) {
+ pr_err("Unable to write to byte_en_reg rc=%d\n",
+ rc);
+ return rc;
+ }
+ /* write data */
rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip) + offset,
num_bytes);
if (rc) {
@@ -1837,7 +1841,7 @@
retry:
rc = fg_interleaved_mem_config(chip, val, address, offset, len, 1);
if (rc) {
- pr_err("failed to xonfigure SRAM for IMA rc = %d\n", rc);
+ pr_err("failed to configure SRAM for IMA rc = %d\n", rc);
goto out;
}
@@ -1900,6 +1904,9 @@
u8 reg[4];
char str[DEBUG_PRINT_BUFFER_SIZE];
+ if (WARN_ONCE(offset >= sizeof(reg), "offset too large %d\n", offset))
+ return -EINVAL;
+
rc = fg_mem_read(chip, reg, addr, 4, 0, 1);
if (rc) {
pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc);
@@ -1937,6 +1944,7 @@
address = fg_backup_regs[i].address;
offset = fg_backup_regs[i].offset;
len = fg_backup_regs[i].len;
+ BUG_ON(ptr + len > sram_backup_buffer + sizeof(sram_backup_buffer));
if (save)
rc = fg_interleaved_mem_read(chip, ptr, address,
len, offset);
@@ -1959,7 +1967,7 @@
#define SOC_LOW_PWR_CFG 0xF5
static int fg_reset(struct fg_chip *chip, bool reset)
{
- int rc;
+ int rc;
u8 soc_low_pwr_cfg;
rc = fg_sec_masked_write(chip, chip->soc_base + SOC_FG_RESET,
@@ -2199,7 +2207,7 @@
#define SYSTEM_SOC_SIZE 2
static int get_system_soc(struct fg_chip *chip)
{
- u8 cap[2];
+ u8 cap[SYSTEM_SOC_SIZE];
int soc;
int rc;
@@ -3278,9 +3286,7 @@
}
battery_soc = get_battery_soc_raw(chip) * 100 / FULL_PERCENT_3B;
- if (rc) {
- goto error_done;
- } else if (battery_soc < 25 || battery_soc > 75) {
+ if (battery_soc < 25 || battery_soc > 75) {
if (fg_debug_mask & FG_AGING)
pr_info("Battery SoC (%d) out of range, aborting\n",
(int)battery_soc);
@@ -6316,9 +6322,9 @@
#ifdef CONFIG_HTC_BATT
static int __init batt_enable_bms_charger_log_set(char *str)
{
- g_fg_flag_enable_batt_debug_log = true;
- pr_info("flag_enable_batt_debug_log is set\n");
- return 0;
+ g_fg_flag_enable_batt_debug_log = true;
+ pr_info("flag_enable_batt_debug_log is set\n");
+ return 0;
}
__setup("batt_enable_bms_charger_log=1", batt_enable_bms_charger_log_set);
#endif /* CONFIG_HTC_BATT */
@@ -7083,7 +7089,8 @@
#define DEFAULT_EVALUATION_CURRENT_MA 1000
static int fg_of_init(struct fg_chip *chip)
{
- int rc = 0, sense_type, len = 0;
+ int rc = 0, len = 0;
+ bool sense_type;
const char *data;
struct device_node *node = chip->spmi->dev.of_node;
u32 temp[2] = {0};
@@ -7194,7 +7201,8 @@
"qcom,ext-sense-type");
if (rc == 0) {
if (fg_sense_type < 0)
- fg_sense_type = sense_type;
+ fg_sense_type = sense_type ?
+ EXTERNAL_CURRENT_SENSE : INTERNAL_CURRENT_SENSE;
if (fg_debug_mask & FG_STATUS) {
if (fg_sense_type == INTERNAL_CURRENT_SENSE)
@@ -8531,7 +8539,7 @@
goto out;
}
- pr_info("resetting FG\n");
+ pr_info("resetting FG\n");
/* Assert FG reset */
rc = fg_reset(chip, true);
@@ -8543,7 +8551,7 @@
/* Wait for a small time before deasserting FG reset */
msleep(100);
- pr_info("clearing FG from reset\n");
+ pr_info("clearing FG from reset\n");
/* Deassert FG reset */
rc = fg_reset(chip, false);
@@ -8562,7 +8570,7 @@
goto wait;
}
- pr_info("Calling hw_init\n");
+ pr_info("Calling hw_init\n");
/*
* Once FG is reset, everything in SRAM will be wiped out. Redo
@@ -8585,7 +8593,7 @@
goto out;
}
- pr_info("loading battery profile\n");
+ pr_info("loading battery profile\n");
if (!chip->use_otp_profile) {
chip->battery_missing = true;
chip->profile_loaded = false;
@@ -8636,7 +8644,6 @@
static int fg_setup_memif_offset(struct fg_chip *chip)
{
int rc;
- u8 dig_major;
rc = fg_read(chip, chip->revision, chip->mem_base + DIG_MINOR, 4);
if (rc) {
@@ -8654,7 +8661,7 @@
chip->ima_supported = true;
break;
default:
- pr_err("Digital Major rev=%d not supported\n", dig_major);
+ pr_err("Digital Major rev=%d not supported\n", chip->revision[DIG_MAJOR]);
return -EINVAL;
}
diff --git a/drivers/power/qpnp-smbcharger.c b/drivers/power/qpnp-smbcharger.c
index 14642fc..64c27c29f 100644
--- a/drivers/power/qpnp-smbcharger.c
+++ b/drivers/power/qpnp-smbcharger.c
@@ -1020,19 +1020,6 @@
type = get_type(reg);
bc12_power_supply_type = get_usb_supply_type(type);
-#ifdef CONFIG_HTC_BATT
- /* Precedence: PD > Type-C (3A or 1.5A) -> SDP, CDP, DCP from APSD
- * Origin table matches with APSD detection(SMBCHGL_MISC_IDEV_STS[7:4])
- * Now we extended it with below two types
- * - Type 5: Type C Port
- * - Type 6: Power Delivery Port
- */
- if (htc_battery_is_pd_detected())
- type = 6;
- else if (chip->utc.sink_current &&
- chip->utc.sink_current != utccDefault)
- type = 5;
-#endif /* CONFIG_HTC_BATT */
*usb_type_name = get_usb_type_name(type);
*usb_supply_type = get_usb_supply_type(type);
}
@@ -1320,6 +1307,20 @@
return uv;
}
+#define DEFAULT_BATT_CHARGE_FULL 0
+static int get_prop_batt_charge_full(struct smbchg_chip *chip)
+{
+ int uah, rc;
+
+ rc = get_property_from_fg(chip,
+ POWER_SUPPLY_PROP_CHARGE_FULL, &uah);
+ if (rc) {
+ pr_smb(PR_STATUS, "Couldn't get charge full rc = %d\n", rc);
+ uah = DEFAULT_BATT_CHARGE_FULL;
+ }
+ return uah;
+}
+
#define DEFAULT_CHARGE_COUNTER 0
static int get_prop_batt_charge_counter(struct smbchg_chip *chip)
{
@@ -2044,6 +2045,7 @@
int current_ma)
{
int rc = 0;
+ enum power_supply_type type;
/*
* if the battery is not present, do not allow the usb ICL to lower in
@@ -2076,7 +2078,16 @@
rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, false, 0);
}
- switch (chip->usb_supply_type) {
+ type = chip->usb_supply_type;
+#ifdef CONFIG_HTC_BATT
+ if (htc_battery_is_pd_detected())
+ type = POWER_SUPPLY_TYPE_USB_PD;
+ else if (chip->utc.sink_current &&
+ chip->utc.sink_current != utccDefault)
+ type = POWER_SUPPLY_TYPE_USB_TYPE_C;
+#endif /* CONFIG_HTC_BATT */
+
+ switch (type) {
#ifdef CONFIG_HTC_BATT
case POWER_SUPPLY_TYPE_USB_PD_DRP:
case POWER_SUPPLY_TYPE_USB_PD:
@@ -4168,8 +4179,10 @@
struct smbchg_chip, batt_psy);
union power_supply_propval prop = {0,};
int rc, current_limit = 0, soc;
+#ifndef CONFIG_HTC_BATT
enum power_supply_type usb_supply_type;
char *usb_type_name = "null";
+#endif /* CONFIG_HTC_BATT */
if (chip->bms_psy_name)
chip->bms_psy =
@@ -4203,23 +4216,16 @@
current_limit = prop.intval / 1000;
#ifdef CONFIG_HTC_BATT
- rc = chip->usb_psy->get_property(chip->usb_psy,
- POWER_SUPPLY_PROP_TYPE, &prop);
- if (rc == 0)
- usb_supply_type = prop.intval;
- else
- read_usb_type(chip, &usb_type_name, &usb_supply_type);
+ pr_smb(PR_STATUS, "usb type = %d, current_limit = %d, rc = %d\n",
+ chip->usb_supply_type, current_limit, rc);
+
+ if (chip->usb_supply_type != POWER_SUPPLY_TYPE_USB)
+ goto skip_current_for_non_sdp;
#else
read_usb_type(chip, &usb_type_name, &usb_supply_type);
-#endif /* CONFIG_HTC_BATT */
-
if (usb_supply_type != POWER_SUPPLY_TYPE_USB)
goto skip_current_for_non_sdp;
-#ifdef CONFIG_HTC_BATT
- pr_smb(PR_MISC, "CHG_TYPE is %d, current_limit = %d, rc = %d\n",
- usb_supply_type, current_limit, rc);
-#else
pr_smb(PR_MISC, "usb type = %s current_limit = %d\n",
usb_type_name, current_limit);
#endif /* CONFIG_HTC_BATT */
@@ -5011,7 +5017,10 @@
static int smbchg_change_usb_supply_type(struct smbchg_chip *chip,
enum power_supply_type type)
{
- int rc, current_limit_ma;
+ int rc;
+ // default to DEFAULT_SDP_MA
+ int current_limit_ma = DEFAULT_SDP_MA;
+ enum power_supply_type saved_type = type;
/*
* if the type is not unknown, set the type before changing ICL vote
@@ -5027,6 +5036,14 @@
* Note: for SDP supporting current based on USB notifications.
*/
#ifdef CONFIG_HTC_BATT
+ if (type != POWER_SUPPLY_TYPE_UNKNOWN) {
+ if (htc_battery_is_pd_detected())
+ type = POWER_SUPPLY_TYPE_USB_PD;
+ else if (chip->utc.sink_current &&
+ chip->utc.sink_current != utccDefault)
+ type = POWER_SUPPLY_TYPE_USB_TYPE_C;
+ }
+
/*
* Precedence: PD > Type-C (3A or 1.5A) -> SDP, CDP, DCP from APSD
*/
@@ -5075,7 +5092,7 @@
}
if (!chip->skip_usb_notification)
- power_supply_set_supply_type(chip->usb_psy, type);
+ power_supply_set_supply_type(chip->usb_psy, saved_type);
/*
* otherwise if it is unknown, remove vote
@@ -5466,6 +5483,8 @@
static void smbchg_sink_current_change_worker(struct work_struct *work)
{
+ enum power_supply_type type;
+
if (!the_chip) {
pr_err("called before init\n");
return;
@@ -5476,11 +5495,16 @@
return;
}
- if ((the_chip->usb_supply_type != POWER_SUPPLY_TYPE_USB_TYPE_C) &&
- (the_chip->utc.sink_current != utcc3p0A) &&
- (the_chip->utc.sink_current != utcc1p5A)) {
+ type = the_chip->usb_supply_type;
+ if (the_chip->utc.sink_current &&
+ the_chip->utc.sink_current != utccDefault)
+ type = POWER_SUPPLY_TYPE_USB_TYPE_C;
+
+ if ((type != POWER_SUPPLY_TYPE_USB_TYPE_C) &&
+ (the_chip->utc.sink_current != utcc3p0A) &&
+ (the_chip->utc.sink_current != utcc1p5A)) {
pr_smb(PR_STATUS, "skip change, chg_type: %d, sink_curr: %d\n",
- the_chip->usb_supply_type,
+ type,
(int)the_chip->utc.sink_current);
return;
}
@@ -6775,6 +6799,7 @@
POWER_SUPPLY_PROP_RESTRICTED_CHARGING,
POWER_SUPPLY_PROP_ALLOW_HVDCP3,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
};
static int smbchg_battery_set_property(struct power_supply *psy,
@@ -6981,6 +7006,9 @@
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = get_prop_batt_voltage_max_design(chip);
break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = get_prop_batt_charge_full(chip);
+ break;
case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
val->intval = chip->safety_timer_en;
break;
@@ -7314,23 +7342,12 @@
pr_err("called before init\n");
return;
}
- /* Disable AICL */
- smbchg_sec_masked_write(the_chip, the_chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, 0);
- pr_smb(PR_INTERRUPT, "AICL disabled\n");
rc = vote(the_chip->usb_icl_votable, PSY_ICL_VOTER, true, USB_MA_1000);
if (rc < 0)
pr_err("Couldn't set usb current rc = %d\n", rc);
else
pr_smb(PR_INTERRUPT, "USB max current changed to 1A\n");
-
-
- /* Add a delay so that AICL successfully clears */
- msleep(50);
- smbchg_sec_masked_write(the_chip, the_chip->usb_chgpth_base + USB_AICL_CFG,
- AICL_EN_BIT, AICL_EN_BIT);
- pr_smb(PR_INTERRUPT, "AICL re-enabled\n");
}
#endif /* CONFIG_HTC_BATT */
@@ -7849,6 +7866,12 @@
rc = the_chip->usb_psy->get_property(the_chip->usb_psy,
POWER_SUPPLY_PROP_TYPE, &prop);
usb_supply_type = prop.intval;
+ if (htc_battery_is_pd_detected())
+ usb_supply_type = POWER_SUPPLY_TYPE_USB_PD;
+ else if (the_chip->utc.sink_current &&
+ the_chip->utc.sink_current != utccDefault)
+ usb_supply_type = POWER_SUPPLY_TYPE_USB_TYPE_C;
+
pr_smb(PR_STATUS, "CHG_TYPE = %d, AICL = %d, level = %d, vbat_mv = %d, hard_limit = %d\n",
usb_supply_type, aicl_level, level, vbat_mv, is_smbchg_hard_limit(the_chip));
diff --git a/drivers/power/smb1351-charger.c b/drivers/power/smb1351-charger.c
index 5e863c0..e942a1c 100644
--- a/drivers/power/smb1351-charger.c
+++ b/drivers/power/smb1351-charger.c
@@ -2762,7 +2762,7 @@
static void smb1351_chg_adc_notification(enum qpnp_tm_state state, void *ctx)
{
struct smb1351_charger *chip = ctx;
- struct battery_status *cur;
+ struct battery_status *cur = NULL;
int temp;
if (state >= ADC_TM_STATE_NUM) {
@@ -2862,6 +2862,11 @@
}
}
+ if (!cur) {
+ pr_err("invalid transaction: state %d, temp %d\n", state, temp);
+ return;
+ }
+
if (cur->batt_present)
chip->battery_missing = false;
else
diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c
index 38a8bbe..83797d8 100644
--- a/drivers/pps/clients/pps_parport.c
+++ b/drivers/pps/clients/pps_parport.c
@@ -195,7 +195,7 @@
struct pps_client_pp *device;
/* FIXME: oooh, this is ugly! */
- if (strcmp(pardev->name, KBUILD_MODNAME))
+ if (!pardev || strcmp(pardev->name, KBUILD_MODNAME))
/* not our port */
return;
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 966497d..f3c64b3 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -293,6 +293,8 @@
unsigned int i;
int ret = 0;
+ pwmchip_sysfs_unexport_children(chip);
+
mutex_lock(&pwm_lock);
for (i = 0; i < chip->npwm; i++) {
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 4bd0c63..6c88e1a 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -340,6 +340,24 @@
}
}
+void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
+{
+ struct device *parent;
+ unsigned int i;
+
+ parent = class_find_device(&pwm_class, NULL, chip,
+ pwmchip_sysfs_match);
+ if (!parent)
+ return;
+
+ for (i = 0; i < chip->npwm; i++) {
+ struct pwm_device *pwm = &chip->pwms[i];
+
+ if (test_bit(PWMF_EXPORTED, &pwm->flags))
+ pwm_unexport_child(parent, pwm);
+ }
+}
+
static int __init pwm_sysfs_init(void)
{
return class_register(&pwm_class);
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index d158715..0f2f693 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -112,6 +112,11 @@
struct device *dev,
const char *supply_name);
+static struct regulator_dev *dev_to_rdev(struct device *dev)
+{
+ return container_of(dev, struct regulator_dev, dev);
+}
+
static const char *rdev_get_name(struct regulator_dev *rdev)
{
if (rdev->constraints && rdev->constraints->name)
@@ -4525,13 +4530,57 @@
/* init early to allow our consumers to complete system booting */
core_initcall(regulator_init);
-static int __init regulator_init_complete(void)
+static int __init regulator_late_cleanup(struct device *dev, void *data)
{
- struct regulator_dev *rdev;
- const struct regulator_ops *ops;
- struct regulation_constraints *c;
+ struct regulator_dev *rdev = dev_to_rdev(dev);
+ const struct regulator_ops *ops = rdev->desc->ops;
+ struct regulation_constraints *c = rdev->constraints;
int enabled, ret;
+ if (c && c->always_on)
+ return 0;
+
+ if (c && !(c->valid_ops_mask & REGULATOR_CHANGE_STATUS))
+ return 0;
+
+ mutex_lock(&rdev->mutex);
+
+ if (rdev->use_count)
+ goto unlock;
+
+ /* If we can't read the status assume it's on. */
+ if (ops->is_enabled)
+ enabled = ops->is_enabled(rdev);
+ else
+ enabled = 1;
+
+ if (!enabled)
+ goto unlock;
+
+ if (have_full_constraints()) {
+ /* We log since this may kill the system if it goes
+ * wrong. */
+ rdev_info(rdev, "disabling\n");
+ ret = _regulator_do_disable(rdev);
+ if (ret != 0)
+ rdev_err(rdev, "couldn't disable: %d\n", ret);
+ } else {
+ /* The intention is that in future we will
+ * assume that full constraints are provided
+ * so warn even if we aren't going to do
+ * anything here.
+ */
+ rdev_warn(rdev, "incomplete constraints, leaving on\n");
+ }
+
+unlock:
+ mutex_unlock(&rdev->mutex);
+
+ return 0;
+}
+
+static int __init regulator_init_complete(void)
+{
/*
* Since DT doesn't provide an idiomatic mechanism for
* enabling full constraints and since it's much more natural
@@ -4541,58 +4590,13 @@
if (of_have_populated_dt())
has_full_constraints = true;
- mutex_lock(®ulator_list_mutex);
-
/* If we have a full configuration then disable any regulators
* we have permission to change the status for and which are
* not in use or always_on. This is effectively the default
* for DT and ACPI as they have full constraints.
*/
- list_for_each_entry(rdev, ®ulator_list, list) {
- ops = rdev->desc->ops;
- c = rdev->constraints;
-
- if (c && c->always_on)
- continue;
-
- if (c && !(c->valid_ops_mask & REGULATOR_CHANGE_STATUS))
- continue;
-
- mutex_lock(&rdev->mutex);
-
- if (rdev->use_count)
- goto unlock;
-
- /* If we can't read the status assume it's on. */
- if (ops->is_enabled)
- enabled = ops->is_enabled(rdev);
- else
- enabled = 1;
-
- if (!enabled)
- goto unlock;
-
- if (have_full_constraints()) {
- /* We log since this may kill the system if it
- * goes wrong. */
- rdev_info(rdev, "disabling\n");
- ret = _regulator_do_disable(rdev);
- if (ret != 0)
- rdev_err(rdev, "couldn't disable: %d\n", ret);
- } else {
- /* The intention is that in future we will
- * assume that full constraints are provided
- * so warn even if we aren't going to do
- * anything here.
- */
- rdev_warn(rdev, "incomplete constraints, leaving on\n");
- }
-
-unlock:
- mutex_unlock(&rdev->mutex);
- }
-
- mutex_unlock(®ulator_list_mutex);
+ class_for_each_device(®ulator_class, NULL, NULL,
+ regulator_late_cleanup);
return 0;
}
diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c
index 7a6a2910..37dfc5c 100644
--- a/drivers/regulator/cpr3-hmss-regulator.c
+++ b/drivers/regulator/cpr3-hmss-regulator.c
@@ -1504,7 +1504,7 @@
static int cpr3_hmss_init_aging(struct cpr3_controller *ctrl)
{
struct cpr3_msm8996_hmss_fuses *fuse = NULL;
- struct cpr3_regulator *vreg;
+ struct cpr3_regulator *vreg = NULL;
u32 aging_ro_scale;
int i, j, rc;
@@ -1519,7 +1519,7 @@
}
}
- if (!ctrl->aging_required || !fuse)
+ if (!vreg || !ctrl->aging_required || !fuse)
return 0;
rc = cpr3_parse_array_property(vreg, "qcom,cpr-aging-ro-scaling-factor",
@@ -1704,31 +1704,6 @@
{}
};
-u64* htc_target_quot[2] = {NULL};
-int htc_target_quot_len = 0;
-static void bak_htc_target_quot(struct cpr3_controller *ctrl)
-{
- struct cpr3_msm8996_hmss_fuses *fuse;
- struct cpr3_thread *thread;
- struct cpr3_regulator *vreg;
- int i, size;
-
- /* The number of target_quot array should be same with cpr thread counter */
- size = sizeof(htc_target_quot)/sizeof(u64);
- if (size != ctrl->thread_count) {
- WARN_ON(1);
- return;
- }
-
- htc_target_quot_len = MSM8996_HMSS_FUSE_CORNERS;
- for (i = 0; i < ctrl->thread_count; i++) {
- thread = &ctrl->thread[i];
- vreg = thread->vreg;
- fuse = vreg->platform_fuses;
- htc_target_quot[i] = fuse->target_quot;
- }
-}
-
static int cpr3_hmss_regulator_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1819,7 +1794,6 @@
}
platform_set_drvdata(pdev, ctrl);
- bak_htc_target_quot(ctrl);
return cpr3_regulator_register(pdev, ctrl);
}
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
index 6b2c58b..8a97671 100644
--- a/drivers/regulator/cpr3-regulator.c
+++ b/drivers/regulator/cpr3-regulator.c
@@ -2920,7 +2920,7 @@
struct cpr4_sdelta *sdelta;
bool valid = false;
bool thread_valid;
- int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt;
+ int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt = 0;
u32 reg_last_measurement = 0, sdelta_size;
int *sdelta_table, *boost_table;
@@ -3323,8 +3323,8 @@
{
u32 mask, reg, result, quot_min, quot_max, sel_min, sel_max;
u32 quot_min_scaled, quot_max_scaled;
- u32 gcnt, gcnt_ref, gcnt0_restore, gcnt1_restore, irq_restore;
- u32 cont_dly_restore, up_down_dly_restore;
+ u32 gcnt, gcnt_ref, gcnt0_restore = 0, gcnt1_restore = 0, irq_restore = 0;
+ u32 cont_dly_restore = 0, up_down_dly_restore = 0;
int quot_delta, quot_delta_scaled, quot_delta_scaled_sum;
int *quot_delta_results;
int rc, rc2, i, aging_measurement_count, filtered_count;
@@ -3340,7 +3340,8 @@
if (rc) {
cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
rc);
- goto cleanup;
+ kfree(quot_delta_results);
+ return -EIO;
}
}
@@ -3681,7 +3682,7 @@
struct cpr3_corner *corner;
int *restore_current_corner;
bool *restore_vreg_enabled;
- int i, j, id, rc, rc2, vreg_count, aging_volt, max_aging_volt;
+ int i, j, id, rc, rc2, vreg_count, aging_volt, max_aging_volt = 0;
u32 reg;
if (!ctrl->aging_required || !ctrl->cpr_enabled
diff --git a/drivers/regulator/kryo-regulator.c b/drivers/regulator/kryo-regulator.c
index fe2a62d..8060e33 100644
--- a/drivers/regulator/kryo-regulator.c
+++ b/drivers/regulator/kryo-regulator.c
@@ -846,7 +846,7 @@
return rc;
}
- return rc;
+ return 0;
}
static int kryo_regulator_lpm_prepare(struct kryo_regulator *kvreg)
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index 697be11..ad9920c 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -302,7 +302,7 @@
.enable_mask = S2MPS11_ENABLE_MASK \
}
-#define regulator_desc_s2mps11_buck6_10(num, min, step) { \
+#define regulator_desc_s2mps11_buck67810(num, min, step) { \
.name = "BUCK"#num, \
.id = S2MPS11_BUCK##num, \
.ops = &s2mps11_buck_ops, \
@@ -318,6 +318,22 @@
.enable_mask = S2MPS11_ENABLE_MASK \
}
+#define regulator_desc_s2mps11_buck9 { \
+ .name = "BUCK9", \
+ .id = S2MPS11_BUCK9, \
+ .ops = &s2mps11_buck_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = MIN_3000_MV, \
+ .uV_step = STEP_25_MV, \
+ .n_voltages = S2MPS11_BUCK9_N_VOLTAGES, \
+ .ramp_delay = S2MPS11_RAMP_DELAY, \
+ .vsel_reg = S2MPS11_REG_B9CTRL2, \
+ .vsel_mask = S2MPS11_BUCK9_VSEL_MASK, \
+ .enable_reg = S2MPS11_REG_B9CTRL1, \
+ .enable_mask = S2MPS11_ENABLE_MASK \
+}
+
static const struct regulator_desc s2mps11_regulators[] = {
regulator_desc_s2mps11_ldo(1, STEP_25_MV),
regulator_desc_s2mps11_ldo(2, STEP_50_MV),
@@ -362,11 +378,11 @@
regulator_desc_s2mps11_buck1_4(3),
regulator_desc_s2mps11_buck1_4(4),
regulator_desc_s2mps11_buck5,
- regulator_desc_s2mps11_buck6_10(6, MIN_600_MV, STEP_6_25_MV),
- regulator_desc_s2mps11_buck6_10(7, MIN_600_MV, STEP_6_25_MV),
- regulator_desc_s2mps11_buck6_10(8, MIN_600_MV, STEP_6_25_MV),
- regulator_desc_s2mps11_buck6_10(9, MIN_3000_MV, STEP_25_MV),
- regulator_desc_s2mps11_buck6_10(10, MIN_750_MV, STEP_12_5_MV),
+ regulator_desc_s2mps11_buck67810(6, MIN_600_MV, STEP_6_25_MV),
+ regulator_desc_s2mps11_buck67810(7, MIN_600_MV, STEP_6_25_MV),
+ regulator_desc_s2mps11_buck67810(8, MIN_600_MV, STEP_6_25_MV),
+ regulator_desc_s2mps11_buck9,
+ regulator_desc_s2mps11_buck67810(10, MIN_750_MV, STEP_12_5_MV),
};
static int s2mps14_regulator_enable(struct regulator_dev *rdev)
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c
index 0ab5cbe..c53fe26 100644
--- a/drivers/regulator/s5m8767.c
+++ b/drivers/regulator/s5m8767.c
@@ -202,9 +202,10 @@
}
}
- if (i < s5m8767->num_regulators)
- *enable_ctrl =
- s5m8767_opmode_reg[reg_id][mode] << S5M8767_ENCTRL_SHIFT;
+ if (i >= s5m8767->num_regulators)
+ return -EINVAL;
+
+ *enable_ctrl = s5m8767_opmode_reg[reg_id][mode] << S5M8767_ENCTRL_SHIFT;
return 0;
}
@@ -936,8 +937,12 @@
else
regulators[id].vsel_mask = 0xff;
- s5m8767_get_register(s5m8767, id, &enable_reg,
+ ret = s5m8767_get_register(s5m8767, id, &enable_reg,
&enable_val);
+ if (ret) {
+ dev_err(s5m8767->dev, "error reading registers\n");
+ return ret;
+ }
regulators[id].enable_reg = enable_reg;
regulators[id].enable_mask = S5M8767_ENCTRL_MASK;
regulators[id].enable_val = enable_val;
diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c
index a7e1526..f140b42 100644
--- a/drivers/regulator/stw481x-vmmc.c
+++ b/drivers/regulator/stw481x-vmmc.c
@@ -47,7 +47,8 @@
.volt_table = stw481x_vmmc_voltages,
.enable_time = 200, /* FIXME: look this up */
.enable_reg = STW_CONF1,
- .enable_mask = STW_CONF1_PDN_VMMC,
+ .enable_mask = STW_CONF1_PDN_VMMC | STW_CONF1_MMC_LS_STATUS,
+ .enable_val = STW_CONF1_PDN_VMMC,
.vsel_reg = STW_CONF1,
.vsel_mask = STW_CONF1_VMMC_MASK,
};
diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c
index b936bb4..280584b2 100644
--- a/drivers/rtc/rtc-hym8563.c
+++ b/drivers/rtc/rtc-hym8563.c
@@ -144,7 +144,7 @@
* it does not seem to carry it over a subsequent write/read.
* So we'll limit ourself to 100 years, starting at 2000 for now.
*/
- buf[6] = tm->tm_year - 100;
+ buf[6] = bin2bcd(tm->tm_year - 100);
/*
* CTL1 only contains TEST-mode bits apart from stop,
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
index cf73e96..b69d409c 100644
--- a/drivers/rtc/rtc-max77686.c
+++ b/drivers/rtc/rtc-max77686.c
@@ -463,7 +463,7 @@
info->virq = regmap_irq_get_virq(max77686->rtc_irq_data,
MAX77686_RTCIRQ_RTCA1);
- if (!info->virq) {
+ if (info->virq <= 0) {
ret = -ENXIO;
goto err_rtc;
}
diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c
index f40afdd0..00662dd 100644
--- a/drivers/rtc/rtc-s35390a.c
+++ b/drivers/rtc/rtc-s35390a.c
@@ -15,6 +15,7 @@
#include <linux/bitrev.h>
#include <linux/bcd.h>
#include <linux/slab.h>
+#include <linux/delay.h>
#define S35390A_CMD_STATUS1 0
#define S35390A_CMD_STATUS2 1
@@ -34,10 +35,14 @@
#define S35390A_ALRM_BYTE_HOURS 1
#define S35390A_ALRM_BYTE_MINS 2
+/* flags for STATUS1 */
#define S35390A_FLAG_POC 0x01
#define S35390A_FLAG_BLD 0x02
+#define S35390A_FLAG_INT2 0x04
#define S35390A_FLAG_24H 0x40
#define S35390A_FLAG_RESET 0x80
+
+/* flag for STATUS2 */
#define S35390A_FLAG_TEST 0x01
#define S35390A_INT2_MODE_MASK 0xF0
@@ -94,19 +99,63 @@
return 0;
}
-static int s35390a_reset(struct s35390a *s35390a)
+/*
+ * Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset.
+ * To keep the information if an irq is pending, pass the value read from
+ * STATUS1 to the caller.
+ */
+static int s35390a_reset(struct s35390a *s35390a, char *status1)
{
- char buf[1];
+ char buf;
+ int ret;
+ unsigned initcount = 0;
- if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)) < 0)
- return -EIO;
+ ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1);
+ if (ret < 0)
+ return ret;
- if (!(buf[0] & (S35390A_FLAG_POC | S35390A_FLAG_BLD)))
+ if (*status1 & S35390A_FLAG_POC)
+ /*
+ * Do not communicate for 0.5 seconds since the power-on
+ * detection circuit is in operation.
+ */
+ msleep(500);
+ else if (!(*status1 & S35390A_FLAG_BLD))
+ /*
+ * If both POC and BLD are unset everything is fine.
+ */
return 0;
- buf[0] |= (S35390A_FLAG_RESET | S35390A_FLAG_24H);
- buf[0] &= 0xf0;
- return s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
+ /*
+ * At least one of POC and BLD are set, so reinitialise chip. Keeping
+ * this information in the hardware to know later that the time isn't
+ * valid is unfortunately not possible because POC and BLD are cleared
+ * on read. So the reset is best done now.
+ *
+ * The 24H bit is kept over reset, so set it already here.
+ */
+initialize:
+ *status1 = S35390A_FLAG_24H;
+ buf = S35390A_FLAG_RESET | S35390A_FLAG_24H;
+ ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1);
+
+ if (ret < 0)
+ return ret;
+
+ ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1);
+ if (ret < 0)
+ return ret;
+
+ if (buf & (S35390A_FLAG_POC | S35390A_FLAG_BLD)) {
+ /* Try up to five times to reset the chip */
+ if (initcount < 5) {
+ ++initcount;
+ goto initialize;
+ } else
+ return -EIO;
+ }
+
+ return 1;
}
static int s35390a_disable_test_mode(struct s35390a *s35390a)
@@ -242,6 +291,8 @@
if (alm->time.tm_wday != -1)
buf[S35390A_ALRM_BYTE_WDAY] = bin2bcd(alm->time.tm_wday) | 0x80;
+ else
+ buf[S35390A_ALRM_BYTE_WDAY] = 0;
buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a,
alm->time.tm_hour) | 0x80;
@@ -265,27 +316,61 @@
char buf[3], sts;
int i, err;
+ /*
+ * initialize all members to -1 to signal the core that they are not
+ * defined by the hardware.
+ */
+ alm->time.tm_sec = -1;
+ alm->time.tm_min = -1;
+ alm->time.tm_hour = -1;
+ alm->time.tm_mday = -1;
+ alm->time.tm_mon = -1;
+ alm->time.tm_year = -1;
+ alm->time.tm_wday = -1;
+ alm->time.tm_yday = -1;
+ alm->time.tm_isdst = -1;
+
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
if (err < 0)
return err;
- if (bitrev8(sts) != S35390A_INT2_MODE_ALARM)
- return -EINVAL;
+ if ((bitrev8(sts) & S35390A_INT2_MODE_MASK) != S35390A_INT2_MODE_ALARM) {
+ /*
+ * When the alarm isn't enabled, the register to configure
+ * the alarm time isn't accessible.
+ */
+ alm->enabled = 0;
+ return 0;
+ } else {
+ alm->enabled = 1;
+ }
err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf));
if (err < 0)
return err;
/* This chip returns the bits of each byte in reverse order */
- for (i = 0; i < 3; ++i) {
+ for (i = 0; i < 3; ++i)
buf[i] = bitrev8(buf[i]);
- buf[i] &= ~0x80;
- }
- alm->time.tm_wday = bcd2bin(buf[S35390A_ALRM_BYTE_WDAY]);
- alm->time.tm_hour = s35390a_reg2hr(s35390a,
- buf[S35390A_ALRM_BYTE_HOURS]);
- alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS]);
+ /*
+ * B0 of the three matching registers is an enable flag. Iff it is set
+ * the configured value is used for matching.
+ */
+ if (buf[S35390A_ALRM_BYTE_WDAY] & 0x80)
+ alm->time.tm_wday =
+ bcd2bin(buf[S35390A_ALRM_BYTE_WDAY] & ~0x80);
+
+ if (buf[S35390A_ALRM_BYTE_HOURS] & 0x80)
+ alm->time.tm_hour =
+ s35390a_reg2hr(s35390a,
+ buf[S35390A_ALRM_BYTE_HOURS] & ~0x80);
+
+ if (buf[S35390A_ALRM_BYTE_MINS] & 0x80)
+ alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS] & ~0x80);
+
+ /* alarm triggers always at s=0 */
+ alm->time.tm_sec = 0;
dev_dbg(&client->dev, "%s: alm is mins=%d, hours=%d, wday=%d\n",
__func__, alm->time.tm_min, alm->time.tm_hour,
@@ -327,11 +412,11 @@
static int s35390a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- int err;
+ int err, err_reset;
unsigned int i;
struct s35390a *s35390a;
struct rtc_time tm;
- char buf[1];
+ char buf, status1;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
@@ -360,29 +445,35 @@
}
}
- err = s35390a_reset(s35390a);
- if (err < 0) {
+ err_reset = s35390a_reset(s35390a, &status1);
+ if (err_reset < 0) {
+ err = err_reset;
dev_err(&client->dev, "error resetting chip\n");
goto exit_dummy;
}
- err = s35390a_disable_test_mode(s35390a);
- if (err < 0) {
- dev_err(&client->dev, "error disabling test mode\n");
- goto exit_dummy;
- }
-
- err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
- if (err < 0) {
- dev_err(&client->dev, "error checking 12/24 hour mode\n");
- goto exit_dummy;
- }
- if (buf[0] & S35390A_FLAG_24H)
+ if (status1 & S35390A_FLAG_24H)
s35390a->twentyfourhour = 1;
else
s35390a->twentyfourhour = 0;
- if (s35390a_get_datetime(client, &tm) < 0)
+ if (status1 & S35390A_FLAG_INT2) {
+ /* disable alarm (and maybe test mode) */
+ buf = 0;
+ err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &buf, 1);
+ if (err < 0) {
+ dev_err(&client->dev, "error disabling alarm");
+ goto exit_dummy;
+ }
+ } else {
+ err = s35390a_disable_test_mode(s35390a);
+ if (err < 0) {
+ dev_err(&client->dev, "error disabling test mode\n");
+ goto exit_dummy;
+ }
+ }
+
+ if (err_reset > 0 || s35390a_get_datetime(client, &tm) < 0)
dev_warn(&client->dev, "clock needs to be set\n");
device_set_wakeup_capable(&client->dev, 1);
@@ -395,6 +486,10 @@
err = PTR_ERR(s35390a->rtc);
goto exit_dummy;
}
+
+ if (status1 & S35390A_FLAG_INT2)
+ rtc_update_irq(s35390a->rtc, 1, RTC_AF);
+
return 0;
exit_dummy:
diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c
index 76af92a..1a79a5f 100644
--- a/drivers/rtc/rtc-tegra.c
+++ b/drivers/rtc/rtc-tegra.c
@@ -18,6 +18,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/kernel.h>
+#include <linux/clk.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -59,6 +60,7 @@
struct platform_device *pdev;
struct rtc_device *rtc_dev;
void __iomem *rtc_base; /* NULL if not initialized. */
+ struct clk *clk;
int tegra_rtc_irq; /* alarm and periodic irq */
spinlock_t tegra_rtc_lock;
};
@@ -330,6 +332,14 @@
if (info->tegra_rtc_irq <= 0)
return -EBUSY;
+ info->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->clk))
+ return PTR_ERR(info->clk);
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret < 0)
+ return ret;
+
/* set context info. */
info->pdev = pdev;
spin_lock_init(&info->tegra_rtc_lock);
@@ -350,7 +360,7 @@
ret = PTR_ERR(info->rtc_dev);
dev_err(&pdev->dev, "Unable to register device (err=%d).\n",
ret);
- return ret;
+ goto disable_clk;
}
ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq,
@@ -360,12 +370,25 @@
dev_err(&pdev->dev,
"Unable to request interrupt for device (err=%d).\n",
ret);
- return ret;
+ goto disable_clk;
}
dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
return 0;
+
+disable_clk:
+ clk_disable_unprepare(info->clk);
+ return ret;
+}
+
+static int tegra_rtc_remove(struct platform_device *pdev)
+{
+ struct tegra_rtc_info *info = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(info->clk);
+
+ return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -417,6 +440,7 @@
MODULE_ALIAS("platform:tegra_rtc");
static struct platform_driver tegra_rtc_driver = {
+ .remove = tegra_rtc_remove,
.shutdown = tegra_rtc_shutdown,
.driver = {
.name = "tegra_rtc",
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index 88c9c92..4b0966e 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -272,12 +272,13 @@
}
static const struct rtc_class_ops vr41xx_rtc_ops = {
- .release = vr41xx_rtc_release,
- .ioctl = vr41xx_rtc_ioctl,
- .read_time = vr41xx_rtc_read_time,
- .set_time = vr41xx_rtc_set_time,
- .read_alarm = vr41xx_rtc_read_alarm,
- .set_alarm = vr41xx_rtc_set_alarm,
+ .release = vr41xx_rtc_release,
+ .ioctl = vr41xx_rtc_ioctl,
+ .read_time = vr41xx_rtc_read_time,
+ .set_time = vr41xx_rtc_set_time,
+ .read_alarm = vr41xx_rtc_read_alarm,
+ .set_alarm = vr41xx_rtc_set_alarm,
+ .alarm_irq_enable = vr41xx_rtc_alarm_irq_enable,
};
static int rtc_probe(struct platform_device *pdev)
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 329db99..35672b0 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1621,9 +1621,18 @@
unsigned long long now;
int expires;
+ cqr = (struct dasd_ccw_req *) intparm;
if (IS_ERR(irb)) {
switch (PTR_ERR(irb)) {
case -EIO:
+ if (cqr && cqr->status == DASD_CQR_CLEAR_PENDING) {
+ device = (struct dasd_device *) cqr->startdev;
+ cqr->status = DASD_CQR_CLEARED;
+ dasd_device_clear_timer(device);
+ wake_up(&dasd_flush_wq);
+ dasd_schedule_device_bh(device);
+ return;
+ }
break;
case -ETIMEDOUT:
DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: "
@@ -1639,7 +1648,6 @@
}
now = get_tod_clock();
- cqr = (struct dasd_ccw_req *) intparm;
/* check for conditions that should be handled immediately */
if (!cqr ||
!(scsw_dstat(&irb->scsw) == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 9bb48d7..4d20f72 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -872,7 +872,7 @@
goto cleanup;
for (i=0; i < MAXMINOR; ++i ) {
- sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL);
+ sys_ser[i].buffer = (char *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!sys_ser[i].buffer) {
rc = -ENOMEM;
break;
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c
index 5d06253c..30e9fbbf 100644
--- a/drivers/s390/cio/qdio_thinint.c
+++ b/drivers/s390/cio/qdio_thinint.c
@@ -147,11 +147,11 @@
struct qdio_q *q;
int i;
- for_each_input_queue(irq, q, i) {
- if (!references_shared_dsci(irq) &&
- has_multiple_inq_on_dsci(irq))
- xchg(q->irq_ptr->dsci, 0);
+ if (!references_shared_dsci(irq) &&
+ has_multiple_inq_on_dsci(irq))
+ xchg(irq->dsci, 0);
+ for_each_input_queue(irq, q, i) {
if (q->u.in.queue_start_poll) {
/* skip if polling is enabled or already in work */
if (test_and_set_bit(QDIO_QUEUE_IRQS_DISABLED,
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index c2679bf..d23138b 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -911,6 +911,7 @@
qeth_l2_set_offline(cgdev);
if (card->dev) {
+ netif_napi_del(&card->napi);
unregister_netdev(card->dev);
card->dev = NULL;
}
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index afebb97..0513d11 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -3337,6 +3337,7 @@
qeth_l3_set_offline(cgdev);
if (card->dev) {
+ netif_napi_del(&card->napi);
unregister_netdev(card->dev);
card->dev = NULL;
}
diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c
index fbcd48d..16b2db3 100644
--- a/drivers/scsi/aacraid/commctrl.c
+++ b/drivers/scsi/aacraid/commctrl.c
@@ -63,7 +63,7 @@
struct fib *fibptr;
struct hw_fib * hw_fib = (struct hw_fib *)0;
dma_addr_t hw_fib_pa = (dma_addr_t)0LL;
- unsigned size;
+ unsigned int size, osize;
int retval;
if (dev->in_reset) {
@@ -87,7 +87,8 @@
* will not overrun the buffer when we copy the memory. Return
* an error if we would.
*/
- size = le16_to_cpu(kfib->header.Size) + sizeof(struct aac_fibhdr);
+ osize = size = le16_to_cpu(kfib->header.Size) +
+ sizeof(struct aac_fibhdr);
if (size < le16_to_cpu(kfib->header.SenderSize))
size = le16_to_cpu(kfib->header.SenderSize);
if (size > dev->max_fib_size) {
@@ -118,6 +119,14 @@
goto cleanup;
}
+ /* Sanity check the second copy */
+ if ((osize != le16_to_cpu(kfib->header.Size) +
+ sizeof(struct aac_fibhdr))
+ || (size < le16_to_cpu(kfib->header.SenderSize))) {
+ retval = -EINVAL;
+ goto cleanup;
+ }
+
if (kfib->header.Command == cpu_to_le16(TakeABreakPt)) {
aac_adapter_interrupt(dev);
/*
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index 6b32ddc..ce177a5 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -590,10 +590,10 @@
}
return -EFAULT;
}
- /* We used to udelay() here but that absorbed
- * a CPU when a timeout occured. Not very
- * useful. */
- cpu_relax();
+ /*
+ * Allow other processes / CPUS to use core
+ */
+ schedule();
}
} else if (down_interruptible(&fibptr->event_wait)) {
/* Do nothing ... satisfy
@@ -1921,6 +1921,10 @@
if (difference <= 0)
difference = 1;
set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ break;
+
schedule_timeout(difference);
if (kthread_should_stop())
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index 43761c1..620b04b 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -49,7 +49,7 @@
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
-/* FIXME:
+/* Fixed in linux-4.2, not backported to 3.18:
*
* 1. Although all of the necessary command mapping places have the
* appropriate dma_map.. APIs, the driver still processes its internal
@@ -68,7 +68,6 @@
* 7. advansys_info is not safe against multiple simultaneous callers
* 8. Add module_param to override ISA/VLB ioport array
*/
-#warning this driver is still not properly converted to the DMA API
/* Enable driver /proc statistics. */
#define ADVANSYS_STATS
diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c
index edb43fd..c831e30 100644
--- a/drivers/scsi/aic94xx/aic94xx_sds.c
+++ b/drivers/scsi/aic94xx/aic94xx_sds.c
@@ -983,7 +983,7 @@
{
int err, i;
u32 offs, size;
- struct asd_ll_el *el;
+ struct asd_ll_el *el = NULL;
struct asd_ctrla_phy_settings *ps;
struct asd_ctrla_phy_settings dflt_ps;
@@ -1004,6 +1004,7 @@
size = sizeof(struct asd_ctrla_phy_settings);
ps = &dflt_ps;
+ goto out_process;
}
if (size == 0)
@@ -1028,7 +1029,7 @@
ASD_DPRINTK("couldn't find ctrla phy settings struct\n");
goto out2;
}
-
+out_process:
err = asd_process_ctrla_phy_settings(asd_ha, ps);
if (err) {
ASD_DPRINTK("couldn't process ctrla phy settings\n");
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 0b44fb5..ce6b169 100644
--- a/drivers/scsi/arcmsr/arcmsr_hba.c
+++ b/drivers/scsi/arcmsr/arcmsr_hba.c
@@ -2305,7 +2305,8 @@
}
case ARCMSR_MESSAGE_WRITE_WQBUFFER: {
unsigned char *ver_addr;
- int32_t user_len, cnt2end;
+ uint32_t user_len;
+ int32_t cnt2end;
uint8_t *pQbuffer, *ptmpuserbuffer;
ver_addr = kmalloc(ARCMSR_API_DATA_BUFLEN, GFP_ATOMIC);
if (!ver_addr) {
@@ -2314,6 +2315,11 @@
}
ptmpuserbuffer = ver_addr;
user_len = pcmdmessagefld->cmdmessage.Length;
+ if (user_len > ARCMSR_API_DATA_BUFLEN) {
+ retvalue = ARCMSR_MESSAGE_FAIL;
+ kfree(ver_addr);
+ goto message_out;
+ }
memcpy(ptmpuserbuffer,
pcmdmessagefld->messagedatabuffer, user_len);
spin_lock_irqsave(&acb->wqbuffer_lock, flags);
@@ -2545,18 +2551,9 @@
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
struct CommandControlBlock *ccb;
int target = cmd->device->id;
- int lun = cmd->device->lun;
- uint8_t scsicmd = cmd->cmnd[0];
cmd->scsi_done = done;
cmd->host_scribble = NULL;
cmd->result = 0;
- if ((scsicmd == SYNCHRONIZE_CACHE) ||(scsicmd == SEND_DIAGNOSTIC)){
- if(acb->devstate[target][lun] == ARECA_RAID_GONE) {
- cmd->result = (DID_NO_CONNECT << 16);
- }
- cmd->scsi_done(cmd);
- return 0;
- }
if (target == 16) {
/* virtual device for iop message transfer */
arcmsr_handle_virtual_command(acb, cmd);
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index 543b7d5..3e6c36c 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -3165,7 +3165,7 @@
{
WARN_ON(!virtual_address);
WARN_ON(!physical_address);
- WARN_ON(!length > 0);
+ WARN_ON(!length);
WARN_ON(!sgl);
sgl->va = virtual_address;
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index 315d6d6..4e71044 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -3665,19 +3665,19 @@
if (sfp->state_query_cbfn)
sfp->state_query_cbfn(sfp->state_query_cbarg,
sfp->status);
- sfp->media = NULL;
- }
+ sfp->media = NULL;
+ }
- if (sfp->portspeed) {
- sfp->status = bfa_sfp_speed_valid(sfp, sfp->portspeed);
- if (sfp->state_query_cbfn)
- sfp->state_query_cbfn(sfp->state_query_cbarg,
- sfp->status);
- sfp->portspeed = BFA_PORT_SPEED_UNKNOWN;
- }
+ if (sfp->portspeed) {
+ sfp->status = bfa_sfp_speed_valid(sfp, sfp->portspeed);
+ if (sfp->state_query_cbfn)
+ sfp->state_query_cbfn(sfp->state_query_cbarg,
+ sfp->status);
+ sfp->portspeed = BFA_PORT_SPEED_UNKNOWN;
+ }
- sfp->state_query_lock = 0;
- sfp->state_query_cbfn = NULL;
+ sfp->state_query_lock = 0;
+ sfp->state_query_cbfn = NULL;
}
/*
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index f2e1b92..105adb4 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -9759,6 +9759,7 @@
ioa_cfg->intr_flag = IPR_USE_MSI;
else {
ioa_cfg->intr_flag = IPR_USE_LSI;
+ ioa_cfg->clear_isr = 1;
ioa_cfg->nvectors = 1;
dev_info(&pdev->dev, "Cannot enable MSI.\n");
}
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
index e5afc38..b4ee611 100644
--- a/drivers/scsi/ips.c
+++ b/drivers/scsi/ips.c
@@ -206,10 +206,6 @@
#define IPS_VERSION_HIGH IPS_VER_MAJOR_STRING "." IPS_VER_MINOR_STRING
#define IPS_VERSION_LOW "." IPS_VER_BUILD_STRING " "
-#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
-#warning "This driver has only been tested on the x86/ia64/x86_64 platforms"
-#endif
-
#define IPS_DMA_DIR(scb) ((!scb->scsi_cmd || ips_is_passthru(scb->scsi_cmd) || \
DMA_NONE == scb->scsi_cmd->sc_data_direction) ? \
PCI_DMA_BIDIRECTIONAL : \
@@ -6789,6 +6785,11 @@
static int __init
ips_module_init(void)
{
+#if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__)
+ printk(KERN_ERR "ips: This driver has only been tested on the x86/ia64/x86_64 platforms\n");
+ add_taint(TAINT_CPU_OUT_OF_SPEC, LOCKDEP_STILL_OK);
+#endif
+
if (pci_register_driver(&ips_pci_driver) < 0)
return -ENODEV;
ips_driver_template.module = THIS_MODULE;
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 7854584..4c74cf9 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -560,8 +560,12 @@
WARN_ON_ONCE(task->state == ISCSI_TASK_FREE);
task->state = state;
- if (!list_empty(&task->running))
+ spin_lock_bh(&conn->taskqueuelock);
+ if (!list_empty(&task->running)) {
+ pr_debug_once("%s while task on list", __func__);
list_del_init(&task->running);
+ }
+ spin_unlock_bh(&conn->taskqueuelock);
if (conn->task == task)
conn->task = NULL;
@@ -783,7 +787,9 @@
if (session->tt->xmit_task(task))
goto free_task;
} else {
+ spin_lock_bh(&conn->taskqueuelock);
list_add_tail(&task->running, &conn->mgmtqueue);
+ spin_unlock_bh(&conn->taskqueuelock);
iscsi_conn_queue_work(conn);
}
@@ -1474,8 +1480,10 @@
* this may be on the requeue list already if the xmit_task callout
* is handling the r2ts while we are adding new ones
*/
+ spin_lock_bh(&conn->taskqueuelock);
if (list_empty(&task->running))
list_add_tail(&task->running, &conn->requeue);
+ spin_unlock_bh(&conn->taskqueuelock);
iscsi_conn_queue_work(conn);
}
EXPORT_SYMBOL_GPL(iscsi_requeue_task);
@@ -1512,22 +1520,26 @@
* only have one nop-out as a ping from us and targets should not
* overflow us with nop-ins
*/
+ spin_lock_bh(&conn->taskqueuelock);
check_mgmt:
while (!list_empty(&conn->mgmtqueue)) {
conn->task = list_entry(conn->mgmtqueue.next,
struct iscsi_task, running);
list_del_init(&conn->task->running);
+ spin_unlock_bh(&conn->taskqueuelock);
if (iscsi_prep_mgmt_task(conn, conn->task)) {
/* regular RX path uses back_lock */
spin_lock_bh(&conn->session->back_lock);
__iscsi_put_task(conn->task);
spin_unlock_bh(&conn->session->back_lock);
conn->task = NULL;
+ spin_lock_bh(&conn->taskqueuelock);
continue;
}
rc = iscsi_xmit_task(conn);
if (rc)
goto done;
+ spin_lock_bh(&conn->taskqueuelock);
}
/* process pending command queue */
@@ -1535,19 +1547,24 @@
conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task,
running);
list_del_init(&conn->task->running);
+ spin_unlock_bh(&conn->taskqueuelock);
if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
fail_scsi_task(conn->task, DID_IMM_RETRY);
+ spin_lock_bh(&conn->taskqueuelock);
continue;
}
rc = iscsi_prep_scsi_cmd_pdu(conn->task);
if (rc) {
if (rc == -ENOMEM || rc == -EACCES) {
+ spin_lock_bh(&conn->taskqueuelock);
list_add_tail(&conn->task->running,
&conn->cmdqueue);
conn->task = NULL;
+ spin_unlock_bh(&conn->taskqueuelock);
goto done;
} else
fail_scsi_task(conn->task, DID_ABORT);
+ spin_lock_bh(&conn->taskqueuelock);
continue;
}
rc = iscsi_xmit_task(conn);
@@ -1558,6 +1575,7 @@
* we need to check the mgmt queue for nops that need to
* be sent to aviod starvation
*/
+ spin_lock_bh(&conn->taskqueuelock);
if (!list_empty(&conn->mgmtqueue))
goto check_mgmt;
}
@@ -1577,12 +1595,15 @@
conn->task = task;
list_del_init(&conn->task->running);
conn->task->state = ISCSI_TASK_RUNNING;
+ spin_unlock_bh(&conn->taskqueuelock);
rc = iscsi_xmit_task(conn);
if (rc)
goto done;
+ spin_lock_bh(&conn->taskqueuelock);
if (!list_empty(&conn->mgmtqueue))
goto check_mgmt;
}
+ spin_unlock_bh(&conn->taskqueuelock);
spin_unlock_bh(&conn->session->frwd_lock);
return -ENODATA;
@@ -1738,7 +1759,9 @@
goto prepd_reject;
}
} else {
+ spin_lock_bh(&conn->taskqueuelock);
list_add_tail(&task->running, &conn->cmdqueue);
+ spin_unlock_bh(&conn->taskqueuelock);
iscsi_conn_queue_work(conn);
}
@@ -2917,6 +2940,7 @@
INIT_LIST_HEAD(&conn->mgmtqueue);
INIT_LIST_HEAD(&conn->cmdqueue);
INIT_LIST_HEAD(&conn->requeue);
+ spin_lock_init(&conn->taskqueuelock);
INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
/* allocate login_task used for the login/text sequences */
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index 766098a..3f0c3e0 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -219,7 +219,7 @@
task->num_scatter = qc->n_elem;
} else {
for_each_sg(qc->sg, sg, qc->n_elem, si)
- xfer += sg->length;
+ xfer += sg_dma_len(sg);
task->total_xfer_len = xfer;
task->num_scatter = si;
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 0b2c53a..1c80ae1 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -2822,7 +2822,7 @@
}
vports = lpfc_create_vport_work_array(phba);
- if (vports != NULL)
+ if (vports != NULL) {
for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
struct Scsi_Host *shost;
shost = lpfc_shost_from_vport(vports[i]);
@@ -2839,7 +2839,8 @@
}
spin_unlock_irq(shost->host_lock);
}
- lpfc_destroy_vport_work_array(phba, vports);
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
lpfc_unblock_mgmt_io(phba);
return 0;
@@ -11248,6 +11249,7 @@
.id_table = lpfc_id_table,
.probe = lpfc_pci_probe_one,
.remove = lpfc_pci_remove_one,
+ .shutdown = lpfc_pci_remove_one,
.suspend = lpfc_pci_suspend_one,
.resume = lpfc_pci_resume_one,
.err_handler = &lpfc_err_handler,
diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
index a49914d..4cf75e0 100644
--- a/drivers/scsi/megaraid/megaraid_sas.h
+++ b/drivers/scsi/megaraid/megaraid_sas.h
@@ -1823,7 +1823,7 @@
};
#define MEGASAS_IS_LOGICAL(scp) \
- (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1
+ ((scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1)
#define MEGASAS_DEV_INDEX(inst, scp) \
((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index 6e50380..1ff0ece 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -1614,16 +1614,13 @@
goto out_done;
}
- switch (scmd->cmnd[0]) {
- case SYNCHRONIZE_CACHE:
- /*
- * FW takes care of flush cache on its own
- * No need to send it down
- */
+ /*
+ * FW takes care of flush cache on its own for Virtual Disk.
+ * No need to send it down for VD. For JBOD send SYNCHRONIZE_CACHE to FW.
+ */
+ if ((scmd->cmnd[0] == SYNCHRONIZE_CACHE) && MEGASAS_IS_LOGICAL(scmd)) {
scmd->result = DID_OK << 16;
goto out_done;
- default:
- break;
}
if (instance->instancet->build_and_issue_cmd(instance, scmd)) {
@@ -6096,12 +6093,13 @@
}
for (i = 0; i < ioc->sge_count; i++) {
- if (kbuff_arr[i])
+ if (kbuff_arr[i]) {
dma_free_coherent(&instance->pdev->dev,
le32_to_cpu(kern_sge32[i].length),
kbuff_arr[i],
le32_to_cpu(kern_sge32[i].phys_addr));
kbuff_arr[i] = NULL;
+ }
}
if (instance->ctrl_context && cmd->mpt_pthr_cmd_blocked)
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index 5543956..e37b5e8 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -2508,6 +2508,7 @@
printk("megaraid_sas: pending commands remain after waiting, "
"will reset adapter scsi%d.\n",
instance->host->host_no);
+ *convert = 1;
retval = 1;
}
out:
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
index 89215d4..f054d8d 100644
--- a/drivers/scsi/mvsas/mv_sas.c
+++ b/drivers/scsi/mvsas/mv_sas.c
@@ -737,8 +737,8 @@
mv_dprintk("device %016llx not ready.\n",
SAS_ADDR(dev->sas_addr));
- rc = SAS_PHY_DOWN;
- return rc;
+ rc = SAS_PHY_DOWN;
+ return rc;
}
tei.port = dev->port->lldd_port;
if (tei.port && !tei.port->port_attached && !tmf) {
diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c
index 9f296df..1c965e0 100644
--- a/drivers/scsi/qla2xxx/qla_target.c
+++ b/drivers/scsi/qla2xxx/qla_target.c
@@ -3075,11 +3075,10 @@
{
struct qla_tgt *tgt = cmd->tgt;
struct scsi_qla_host *vha = tgt->vha;
- struct se_cmd *se_cmd = &cmd->se_cmd;
ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014,
"qla_target(%d): terminating exchange for aborted cmd=%p "
- "(se_cmd=%p, tag=%llu)", vha->vp_idx, cmd, &cmd->se_cmd,
+ "(se_cmd=%p, tag=%d)", vha->vp_idx, cmd, &cmd->se_cmd,
cmd->tag);
cmd->state = QLA_TGT_STATE_ABORTED;
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 238e06f..dda3a32 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -3998,6 +3998,7 @@
if (dif_storep)
vfree(dif_storep);
+ vfree(map_storep);
vfree(fake_storep);
}
diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c
index 6e2256f..7439304 100644
--- a/drivers/scsi/scsi_devinfo.c
+++ b/drivers/scsi/scsi_devinfo.c
@@ -227,6 +227,7 @@
{"PIONEER", "CD-ROM DRM-624X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN},
{"Promise", "VTrak E610f", NULL, BLIST_SPARSELUN | BLIST_NO_RSOC},
{"Promise", "", NULL, BLIST_SPARSELUN},
+ {"QEMU", "QEMU CD-ROM", NULL, BLIST_SKIP_VPD_PAGES},
{"QNAP", "iSCSI Storage", NULL, BLIST_MAX_1024},
{"QUANTUM", "XP34301", "1071", BLIST_NOTQ},
{"REGAL", "CDC-4X", NULL, BLIST_MAX5LUN | BLIST_SINGLELUN},
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 3d12c52..29a67a8 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -1115,7 +1115,6 @@
*/
void scsi_eh_finish_cmd(struct scsi_cmnd *scmd, struct list_head *done_q)
{
- scmd->device->host->host_failed--;
scmd->eh_eflags = 0;
list_move_tail(&scmd->eh_entry, done_q);
}
@@ -2213,6 +2212,9 @@
else
scsi_unjam_host(shost);
+ /* All scmds have been handled */
+ shost->host_failed = 0;
+
/*
* Note - if the above fails completely, the action is to take
* individual devices offline and flush the queue of any
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index b1ab509..a118370 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -919,9 +919,12 @@
}
/*
- * If we finished all bytes in the request we are done now.
+ * special case: failed zero length commands always need to
+ * drop down into the retry code. Otherwise, if we finished
+ * all bytes in the request we are done now.
*/
- if (!scsi_end_request(req, error, good_bytes, 0))
+ if (!(blk_rq_bytes(req) == 0 && error) &&
+ !scsi_end_request(req, error, good_bytes, 0))
return;
/*
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 3c0d98e..caf3bc1 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -1530,12 +1530,12 @@
out_err:
kfree(lun_data);
out:
- scsi_device_put(sdev);
if (scsi_device_created(sdev))
/*
* the sdev we used didn't appear in the report luns scan
*/
__scsi_remove_device(sdev);
+ scsi_device_put(sdev);
return ret;
}
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 9f973bb..46b9f39 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1027,10 +1027,6 @@
struct request_queue *rq = sdev->request_queue;
struct scsi_target *starget = sdev->sdev_target;
- error = scsi_device_set_state(sdev, SDEV_RUNNING);
- if (error)
- return error;
-
error = scsi_target_add(starget);
if (error)
return error;
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index b7f62aa..af186d7 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1302,18 +1302,19 @@
struct scsi_disk *sdkp = scsi_disk(bdev->bd_disk);
struct scsi_device *sdp = sdkp->device;
struct Scsi_Host *host = sdp->host;
+ sector_t capacity = logical_to_sectors(sdp, sdkp->capacity);
int diskinfo[4];
/* default to most commonly used values */
- diskinfo[0] = 0x40; /* 1 << 6 */
- diskinfo[1] = 0x20; /* 1 << 5 */
- diskinfo[2] = sdkp->capacity >> 11;
-
+ diskinfo[0] = 0x40; /* 1 << 6 */
+ diskinfo[1] = 0x20; /* 1 << 5 */
+ diskinfo[2] = capacity >> 11;
+
/* override with calculated, extended default, or driver values */
if (host->hostt->bios_param)
- host->hostt->bios_param(sdp, bdev, sdkp->capacity, diskinfo);
+ host->hostt->bios_param(sdp, bdev, capacity, diskinfo);
else
- scsicam_bios_param(bdev, sdkp->capacity, diskinfo);
+ scsicam_bios_param(bdev, capacity, diskinfo);
geo->heads = diskinfo[0];
geo->sectors = diskinfo[1];
@@ -1925,6 +1926,22 @@
#define READ_CAPACITY_RETRIES_ON_RESET 10
+/*
+ * Ensure that we don't overflow sector_t when CONFIG_LBDAF is not set
+ * and the reported logical block size is bigger than 512 bytes. Note
+ * that last_sector is a u64 and therefore logical_to_sectors() is not
+ * applicable.
+ */
+static bool sd_addressable_capacity(u64 lba, unsigned int sector_size)
+{
+ u64 last_sector = (lba + 1ULL) << (ilog2(sector_size) - 9);
+
+ if (sizeof(sector_t) == 4 && last_sector > U32_MAX)
+ return false;
+
+ return true;
+}
+
static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp,
unsigned char *buffer)
{
@@ -1990,7 +2007,7 @@
return -ENODEV;
}
- if ((sizeof(sdkp->capacity) == 4) && (lba >= 0xffffffffULL)) {
+ if (!sd_addressable_capacity(lba, sector_size)) {
sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
"kernel compiled with support for large block "
"devices.\n");
@@ -2076,7 +2093,7 @@
return sector_size;
}
- if ((sizeof(sdkp->capacity) == 4) && (lba == 0xffffffff)) {
+ if (!sd_addressable_capacity(lba, sector_size)) {
sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use a "
"kernel compiled with support for large block "
"devices.\n");
@@ -2215,14 +2232,6 @@
} else
sdkp->max_xfer_blocks = SD_DEF_XFER_BLOCKS;
- /* Rescale capacity to 512-byte units */
- if (sector_size == 4096)
- sdkp->capacity <<= 3;
- else if (sector_size == 2048)
- sdkp->capacity <<= 2;
- else if (sector_size == 1024)
- sdkp->capacity <<= 1;
-
blk_queue_physical_block_size(sdp->request_queue,
sdkp->physical_block_size);
sdkp->device->sector_size = sector_size;
@@ -2740,7 +2749,7 @@
sdkp->disk->queue->limits.max_sectors =
min_not_zero(queue_max_hw_sectors(sdkp->disk->queue), max_xfer);
- set_capacity(disk, sdkp->capacity);
+ set_capacity(disk, logical_to_sectors(sdp, sdkp->capacity));
sd_config_write_same(sdkp);
kfree(buffer);
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 55c6d2d..568337b 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -66,7 +66,7 @@
struct device dev;
struct gendisk *disk;
atomic_t openers;
- sector_t capacity; /* size in 512-byte sectors */
+ sector_t capacity; /* size in logical blocks */
u32 max_xfer_blocks;
u32 max_ws_blocks;
u32 max_unmap_blocks;
@@ -146,6 +146,11 @@
return 0;
}
+static inline sector_t logical_to_sectors(struct scsi_device *sdev, sector_t blocks)
+{
+ return blocks << (ilog2(sdev->sector_size) - 9);
+}
+
/*
* A DIF-capable target device can be formatted with different
* protection schemes. Currently 0 through 3 are defined:
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 64a088d..f917294 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -592,6 +592,9 @@
sg_io_hdr_t *hp;
unsigned char cmnd[SG_MAX_CDB_SIZE];
+ if (unlikely(segment_eq(get_fs(), KERNEL_DS)))
+ return -EINVAL;
+
if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)))
return -ENXIO;
SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp,
@@ -1026,6 +1029,8 @@
result = get_user(val, ip);
if (result)
return result;
+ if (val > SG_MAX_CDB_SIZE)
+ return -ENOMEM;
sfp->next_cmd_len = (val > 0) ? val : 0;
return 0;
case SG_GET_VERSION_NUM:
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index b8475b8..288bd5f 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -839,6 +839,7 @@
unsigned char *buffer;
struct scsi_mode_data data;
struct scsi_sense_hdr sshdr;
+ unsigned int ms_len = 128;
int rc, n;
static const char *loadmech[] =
@@ -865,10 +866,11 @@
scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr);
/* ask for mode page 0x2a */
- rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128,
+ rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, ms_len,
SR_TIMEOUT, 3, &data, NULL);
- if (!scsi_status_is_good(rc)) {
+ if (!scsi_status_is_good(rc) || data.length > ms_len ||
+ data.header_length + data.block_descriptor_length > data.length) {
/* failed, drive doesn't have capabilities mode page */
cd->cdi.speed = 1;
cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R |
diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c
index c48a096..e5d9077 100644
--- a/drivers/scsi/ufs/ufs-debugfs.c
+++ b/drivers/scsi/ufs/ufs-debugfs.c
@@ -22,17 +22,6 @@
#include "unipro.h"
#include "ufshci.h"
-enum field_width {
- BYTE = 1,
- WORD = 2,
-};
-
-struct desc_field_offset {
- char *name;
- int offset;
- enum field_width width_byte;
-};
-
#define UFS_ERR_STATS_PRINT(file, error_index, string, error_seen) \
do { \
if (err_stats[error_index]) { \
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 8d3984b..1a5e7ab 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1414,7 +1414,7 @@
static void ufs_qcom_pm_qos_req_end(struct ufs_hba *hba, struct request *req,
bool should_lock)
{
- unsigned long flags;
+ unsigned long flags = 0;
if (!hba || !req)
return;
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index e4b2c953..e65a500 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -156,6 +156,7 @@
QUERY_DESC_STRING_MAX_SIZE = 0xFE,
QUERY_DESC_GEOMETRY_MAZ_SIZE = 0x44,
QUERY_DESC_POWER_MAX_SIZE = 0x62,
+ QUERY_DESC_HEALTH_MAX_SIZE = 0x25,
QUERY_DESC_RFU_MAX_SIZE = 0x00,
};
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 564cc80..29d846d 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -242,7 +242,7 @@
QUERY_DESC_RFU_MAX_SIZE,
QUERY_DESC_GEOMETRY_MAZ_SIZE,
QUERY_DESC_POWER_MAX_SIZE,
- QUERY_DESC_RFU_MAX_SIZE,
+ QUERY_DESC_HEALTH_MAX_SIZE,
};
enum {
@@ -3318,6 +3318,11 @@
return ufshcd_read_desc(hba, QUERY_DESC_IDN_DEVICE, 0, buf, size);
}
+int ufshcd_read_health_desc(struct ufs_hba *hba, u8 *buf, u32 size)
+{
+ return ufshcd_read_desc(hba, QUERY_DESC_IDN_HEALTH, 0, buf, size);
+}
+
/**
* ufshcd_read_string_desc - read string descriptor
* @hba: pointer to adapter instance
@@ -8408,10 +8413,59 @@
dev_err(hba->dev, "Failed to create sysfs for spm_lvl\n");
}
+static ssize_t ufshcd_health_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ int curr_len = 0, i, err;
+ int buff_len = QUERY_DESC_HEALTH_MAX_SIZE;
+ struct desc_field_offset *tmp;
+ u8 desc_buf[QUERY_DESC_HEALTH_MAX_SIZE];
+
+ struct desc_field_offset health_desc_field_name[] = {
+ {"bLength", 0x00, BYTE},
+ {"bDescriptorType", 0x01, BYTE},
+ {"bPreEOLInfo", 0x02, BYTE},
+ {"bDeviceLifeTimeEstA", 0x03, BYTE},
+ {"bDeviceLifeTimeEstB", 0x04, BYTE}
+ };
+
+ pm_runtime_get_sync(hba->dev);
+ err = ufshcd_read_health_desc(hba, desc_buf, buff_len);
+ pm_runtime_put_sync(hba->dev);
+
+ if (err)
+ return err;
+
+ curr_len = snprintf(buf, PAGE_SIZE, "ufs version: 0x%x\n",
+ hba->ufs_version);
+ for (i = 0; i < ARRAY_SIZE(health_desc_field_name); ++i) {
+ tmp = &health_desc_field_name[i];
+ curr_len += snprintf(buf + curr_len, PAGE_SIZE - curr_len,
+ "Health Descriptor[Byte offset 0x%x]: %s = 0x%x\n",
+ tmp->offset,
+ tmp->name,
+ (u8)desc_buf[tmp->offset]);
+ }
+
+ return curr_len;
+}
+
+static void ufshcd_add_health_sysfs_nodes(struct ufs_hba *hba)
+{
+ hba->health_attr.show = ufshcd_health_show;
+ sysfs_attr_init(&hba->health_attr.attr);
+ hba->health_attr.attr.name = "health";
+ hba->health_attr.attr.mode = S_IRUGO;
+ if (device_create_file(hba->dev, &hba->health_attr))
+ dev_err(hba->dev, "Failed to create sysfs for health\n");
+}
+
static inline void ufshcd_add_sysfs_nodes(struct ufs_hba *hba)
{
ufshcd_add_rpm_lvl_sysfs_nodes(hba);
ufshcd_add_spm_lvl_sysfs_nodes(hba);
+ ufshcd_add_health_sysfs_nodes(hba);
}
/**
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 5c50f99..aefb2c7 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -82,6 +82,17 @@
DEV_CMD_TYPE_QUERY = 0x1,
};
+enum field_width {
+ BYTE = 1,
+ WORD = 2,
+};
+
+struct desc_field_offset {
+ char *name;
+ int offset;
+ enum field_width width_byte;
+};
+
/**
* struct uic_command - UIC command structure
* @command: UIC command
@@ -714,6 +725,7 @@
int spm_lvl;
struct device_attribute rpm_lvl_attr;
struct device_attribute spm_lvl_attr;
+ struct device_attribute health_attr;
int pm_op_in_progress;
struct ufshcd_lrb *lrb;
@@ -1043,6 +1055,7 @@
}
int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size);
+int ufshcd_read_health_desc(struct ufs_hba *hba, u8 *buf, u32 size);
static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info)
{
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 8e8c897..6d852cb 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -1,4 +1,5 @@
KASAN_SANITIZE_scm.o := n
+KCOV_INSTRUMENT_scm.o := n
CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 277ab97..0ae4798 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -1991,6 +1991,7 @@
bool first = true;
ret = (struct glink_core_xprt_ctx *)ERR_PTR(-ENODEV);
+ best_xprt = (struct glink_core_xprt_ctx *)ERR_PTR(-ENODEV);
*best_id = USHRT_MAX;
mutex_lock(&transport_list_lock_lha0);
@@ -2663,7 +2664,7 @@
{
struct glink_core_xprt_ctx *xprt_ctx = NULL;
struct channel_ctx *ctx = (struct channel_ctx *)handle;
- int ret;
+ int ret = 0;
unsigned long flags;
bool is_empty = false;
@@ -5228,7 +5229,7 @@
size_t txd_len = 0;
size_t tx_len = 0;
uint32_t num_pkts = 0;
- int ret;
+ int ret = 0;
spin_lock_irqsave(&ctx->tx_lists_lock_lhc3, flags);
while (txd_len < xprt_ctx->mtu &&
@@ -5318,7 +5319,7 @@
{
struct channel_ctx *ch_ptr;
uint32_t prio;
- uint32_t tx_ready_head_prio;
+ uint32_t tx_ready_head_prio = 0;
int ret;
struct channel_ctx *tx_ready_head = NULL;
bool transmitted_successfully = true;
diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c
index 4952e12..f25e8e8 100644
--- a/drivers/soc/qcom/glink_ssr.c
+++ b/drivers/soc/qcom/glink_ssr.c
@@ -304,8 +304,6 @@
else
ss_info->link_state_handle = link_state_handle;
- BUG_ON(!ss_info->cb_data);
- kfree(ss_info->cb_data);
kfree(close_work);
}
@@ -769,7 +767,7 @@
struct device_node *phandle_node;
struct restart_notifier_block *nb;
struct subsys_info *ss_info;
- struct subsys_info_leaf *ss_info_leaf;
+ struct subsys_info_leaf *ss_info_leaf = NULL;
struct glink_link_info *link_info;
char *key;
const char *edge;
diff --git a/drivers/soc/qcom/perf_event_kryo.c b/drivers/soc/qcom/perf_event_kryo.c
index 67c5582..fd84819 100644
--- a/drivers/soc/qcom/perf_event_kryo.c
+++ b/drivers/soc/qcom/perf_event_kryo.c
@@ -118,7 +118,7 @@
static u32 kryo_read_pmresr(int reg, int l_h)
{
- u32 val;
+ u32 val = 0;
if (reg > KRYO_MAX_L1_REG) {
pr_err("Invalid read of RESR reg %d\n", reg);
@@ -417,4 +417,3 @@
return 0;
}
-
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index 5babe8b..eb25733 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -812,15 +812,17 @@
}
if (desc->subsys_vmid > 0) {
- /* Make sure the memory is actually assigned to Linux. In the
- * case where the shutdown sequence is not able to immediately
- * assign the memory back to Linux, we need to do this here. */
- ret = pil_assign_mem_to_linux(desc, priv->region_start,
+ /* In case of modem ssr, we need to assign memory back to linux.
+ * This is not true after cold boot since linux already owns it.
+ * Also for secure boot devices, modem memory has to be released
+ * after MBA is booted. */
+ if (desc->modem_ssr) {
+ ret = pil_assign_mem_to_linux(desc, priv->region_start,
(priv->region_end - priv->region_start));
- if (ret)
- pil_err(desc, "Failed to assign to linux, ret - %d\n",
+ if (ret)
+ pil_err(desc, "Failed to assign to linux, ret- %d\n",
ret);
-
+ }
ret = pil_assign_mem_to_subsys_and_linux(desc,
priv->region_start,
(priv->region_end - priv->region_start));
@@ -856,6 +858,7 @@
goto err_auth_and_reset;
}
pil_info(desc, "Brought out of reset\n");
+ desc->modem_ssr = false;
err_auth_and_reset:
if (ret && desc->subsys_vmid > 0) {
pil_assign_mem_to_linux(desc, priv->region_start,
@@ -916,6 +919,7 @@
pil_proxy_unvote(desc, 1);
else
flush_delayed_work(&priv->proxy);
+ desc->modem_ssr = true;
}
EXPORT_SYMBOL(pil_shutdown);
diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h
index c3689fa..802abe2 100644
--- a/drivers/soc/qcom/peripheral-loader.h
+++ b/drivers/soc/qcom/peripheral-loader.h
@@ -36,6 +36,7 @@
* @unmap_fw_mem: Custom function used to undo mapping by map_fw_mem.
* This defaults to iounmap if not specified.
* @shutdown_fail: Set if PIL op for shutting down subsystem fails.
+ * @modem_ssr: true if modem is restarting, false if booting for first time.
* @subsys_vmid: memprot id for the subsystem.
*/
struct pil_desc {
@@ -54,6 +55,7 @@
void (*unmap_fw_mem)(void *virt, size_t size, void *data);
void *map_data;
bool shutdown_fail;
+ bool modem_ssr;
u32 subsys_vmid;
};
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 9cef023..d77351b 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -215,6 +215,7 @@
drv->subsys_desc.stop_ack_handler = modem_stop_ack_intr_handler;
drv->subsys_desc.wdog_bite_handler = modem_wdog_bite_intr_handler;
+ drv->q6->desc.modem_ssr = false;
drv->subsys = subsys_register(&drv->subsys_desc);
if (IS_ERR(drv->subsys)) {
ret = PTR_ERR(drv->subsys);
diff --git a/drivers/soc/qcom/rpm_rail_stats.c b/drivers/soc/qcom/rpm_rail_stats.c
index c4d3437..6aefd53 100644
--- a/drivers/soc/qcom/rpm_rail_stats.c
+++ b/drivers/soc/qcom/rpm_rail_stats.c
@@ -220,11 +220,11 @@
static int msm_rpm_rail_stats_probe(struct platform_device *pdev)
{
struct dentry *dent;
- struct msm_rpm_rail_stats_platform_data *pdata;
+ struct msm_rpm_rail_stats_platform_data *pdata = NULL;
struct resource *res;
struct resource *offset;
struct device_node *node;
- uint32_t offset_addr;
+ uint32_t offset_addr = 0;
void __iomem *phys_ptr;
if (!pdev)
@@ -242,8 +242,8 @@
phys_ptr = ioremap_nocache(offset->start, SZ_4);
if (!phys_ptr) {
- pr_err("%s: Failed to ioremap address: %x\n",
- __func__, offset_addr);
+ pr_err("%s: Failed to ioremap address: %pa\n",
+ __func__, &offset->start);
return -ENODEV;
}
offset_addr = readl_relaxed(phys_ptr);
diff --git a/drivers/soc/qcom/scm-errata.c b/drivers/soc/qcom/scm-errata.c
index f68d888..1c4f99a 100644
--- a/drivers/soc/qcom/scm-errata.c
+++ b/drivers/soc/qcom/scm-errata.c
@@ -128,7 +128,7 @@
static int __init scm_errata_init(void)
{
- int ret;
+ int ret = 0;
debugfs_base = debugfs_create_dir("scm_errata", NULL);
if (!debugfs_base)
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 124ec65..b14dccb 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -245,7 +245,7 @@
static int service_locator_send_msg(struct pd_qmi_client_data *pd)
{
struct msg_desc req_desc, resp_desc;
- struct qmi_servreg_loc_get_domain_list_resp_msg_v01 *resp;
+ struct qmi_servreg_loc_get_domain_list_resp_msg_v01 *resp = NULL;
struct qmi_servreg_loc_get_domain_list_req_msg_v01 *req;
int rc;
int db_rev_count = 0, domains_read = 0;
diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c
index 8327e560..1c93c09 100644
--- a/drivers/soundwire/swr-wcd-ctrl.c
+++ b/drivers/soundwire/swr-wcd-ctrl.c
@@ -484,11 +484,17 @@
return -EINVAL;
}
- if (dev_num)
+ if (dev_num) {
ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr,
len);
- else
+ if (ret < 0) {
+ dev_err(&master->dev, "%s: failed, err:%d\n",
+ __func__, ret);
+ return ret;
+ }
+ } else {
val = swrm->read(swrm->handle, reg_addr);
+ }
*reg_val = (u8)val;
pm_runtime_mark_last_busy(&swrm->pdev->dev);
diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c
index 1417f96..fc37844 100644
--- a/drivers/spi/spi-dw-mid.c
+++ b/drivers/spi/spi-dw-mid.c
@@ -111,28 +111,11 @@
dw_spi_xfer_done(dws);
}
-static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
+static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws)
{
- struct dma_async_tx_descriptor *txdesc, *rxdesc;
- struct dma_slave_config txconf, rxconf;
- u16 dma_ctrl = 0;
+ struct dma_slave_config txconf;
+ struct dma_async_tx_descriptor *txdesc;
- /* 1. setup DMA related registers */
- if (cs_change) {
- spi_enable_chip(dws, 0);
- dw_writew(dws, DW_SPI_DMARDLR, 0xf);
- dw_writew(dws, DW_SPI_DMATDLR, 0x10);
- if (dws->tx_dma)
- dma_ctrl |= SPI_DMA_TDMAE;
- if (dws->rx_dma)
- dma_ctrl |= SPI_DMA_RDMAE;
- dw_writew(dws, DW_SPI_DMACR, dma_ctrl);
- spi_enable_chip(dws, 1);
- }
-
- dws->dma_chan_done = 0;
-
- /* 2. Prepare the TX dma transfer */
txconf.direction = DMA_MEM_TO_DEV;
txconf.dst_addr = dws->dma_addr;
txconf.dst_maxburst = LNW_DMA_MSIZE_16;
@@ -157,7 +140,14 @@
txdesc->callback = dw_spi_dma_done;
txdesc->callback_param = dws;
- /* 3. Prepare the RX dma transfer */
+ return txdesc;
+}
+
+static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws)
+{
+ struct dma_slave_config rxconf;
+ struct dma_async_tx_descriptor *rxdesc;
+
rxconf.direction = DMA_DEV_TO_MEM;
rxconf.src_addr = dws->dma_addr;
rxconf.src_maxburst = LNW_DMA_MSIZE_16;
@@ -182,6 +172,43 @@
rxdesc->callback = dw_spi_dma_done;
rxdesc->callback_param = dws;
+ return rxdesc;
+}
+
+static void dw_spi_dma_setup(struct dw_spi *dws)
+{
+ u16 dma_ctrl = 0;
+
+ spi_enable_chip(dws, 0);
+
+ dw_writew(dws, DW_SPI_DMARDLR, 0xf);
+ dw_writew(dws, DW_SPI_DMATDLR, 0x10);
+
+ if (dws->tx_dma)
+ dma_ctrl |= SPI_DMA_TDMAE;
+ if (dws->rx_dma)
+ dma_ctrl |= SPI_DMA_RDMAE;
+ dw_writew(dws, DW_SPI_DMACR, dma_ctrl);
+
+ spi_enable_chip(dws, 1);
+}
+
+static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
+{
+ struct dma_async_tx_descriptor *txdesc, *rxdesc;
+
+ /* 1. setup DMA related registers */
+ if (cs_change)
+ dw_spi_dma_setup(dws);
+
+ dws->dma_chan_done = 0;
+
+ /* 2. Prepare the TX dma transfer */
+ txdesc = dw_spi_dma_prepare_tx(dws);
+
+ /* 3. Prepare the RX dma transfer */
+ rxdesc = dw_spi_dma_prepare_rx(dws);
+
/* rx must be started before tx due to spi instinct */
dmaengine_submit(rxdesc);
dma_async_issue_pending(dws->rxchan);
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 87bc16f..4703aeb 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -264,7 +264,10 @@
static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
{
u32 ser;
- struct rockchip_spi *rs = spi_master_get_devdata(spi->master);
+ struct spi_master *master = spi->master;
+ struct rockchip_spi *rs = spi_master_get_devdata(master);
+
+ pm_runtime_get_sync(rs->dev);
ser = readl_relaxed(rs->regs + ROCKCHIP_SPI_SER) & SER_MASK;
@@ -289,6 +292,8 @@
ser &= ~(1 << spi->chip_select);
writel_relaxed(ser, rs->regs + ROCKCHIP_SPI_SER);
+
+ pm_runtime_put_sync(rs->dev);
}
static int rockchip_spi_prepare_message(struct spi_master *master,
diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c
index 85204c9..19169bf 100644
--- a/drivers/spi/spi-sun4i.c
+++ b/drivers/spi/spi-sun4i.c
@@ -170,13 +170,17 @@
{
struct sun4i_spi *sspi = spi_master_get_devdata(master);
unsigned int mclk_rate, div, timeout;
+ unsigned int start, end, tx_time;
unsigned int tx_len = 0;
int ret = 0;
u32 reg;
/* We don't support transfer larger than the FIFO */
if (tfr->len > SUN4I_FIFO_DEPTH)
- return -EINVAL;
+ return -EMSGSIZE;
+
+ if (tfr->tx_buf && tfr->len >= SUN4I_FIFO_DEPTH)
+ return -EMSGSIZE;
reinit_completion(&sspi->done);
sspi->tx_buf = tfr->tx_buf;
@@ -229,8 +233,8 @@
/* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk);
- if (mclk_rate < (2 * spi->max_speed_hz)) {
- clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz);
+ if (mclk_rate < (2 * tfr->speed_hz)) {
+ clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
mclk_rate = clk_get_rate(sspi->mclk);
}
@@ -248,14 +252,14 @@
* First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1.
*/
- div = mclk_rate / (2 * spi->max_speed_hz);
+ div = mclk_rate / (2 * tfr->speed_hz);
if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
if (div > 0)
div--;
reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS;
} else {
- div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz);
+ div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
reg = SUN4I_CLK_CTL_CDR1(div);
}
@@ -269,8 +273,12 @@
sun4i_spi_write(sspi, SUN4I_BURST_CNT_REG, SUN4I_BURST_CNT(tfr->len));
sun4i_spi_write(sspi, SUN4I_XMIT_CNT_REG, SUN4I_XMIT_CNT(tx_len));
- /* Fill the TX FIFO */
- sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH);
+ /*
+ * Fill the TX FIFO
+ * Filling the FIFO fully causes timeout for some reason
+ * at least on spi2 on A10s
+ */
+ sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1);
/* Enable the interrupts */
sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC);
@@ -279,9 +287,16 @@
reg = sun4i_spi_read(sspi, SUN4I_CTL_REG);
sun4i_spi_write(sspi, SUN4I_CTL_REG, reg | SUN4I_CTL_XCH);
+ tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U);
+ start = jiffies;
timeout = wait_for_completion_timeout(&sspi->done,
- msecs_to_jiffies(1000));
+ msecs_to_jiffies(tx_time));
+ end = jiffies;
if (!timeout) {
+ dev_warn(&master->dev,
+ "%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
+ dev_name(&spi->dev), tfr->len, tfr->speed_hz,
+ jiffies_to_msecs(end - start), tx_time);
ret = -ETIMEDOUT;
goto out;
}
diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
index bd24093..04e9085 100644
--- a/drivers/spi/spi-sun6i.c
+++ b/drivers/spi/spi-sun6i.c
@@ -160,6 +160,7 @@
{
struct sun6i_spi *sspi = spi_master_get_devdata(master);
unsigned int mclk_rate, div, timeout;
+ unsigned int start, end, tx_time;
unsigned int tx_len = 0;
int ret = 0;
u32 reg;
@@ -217,8 +218,8 @@
/* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk);
- if (mclk_rate < (2 * spi->max_speed_hz)) {
- clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz);
+ if (mclk_rate < (2 * tfr->speed_hz)) {
+ clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
mclk_rate = clk_get_rate(sspi->mclk);
}
@@ -236,14 +237,14 @@
* First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1.
*/
- div = mclk_rate / (2 * spi->max_speed_hz);
+ div = mclk_rate / (2 * tfr->speed_hz);
if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
if (div > 0)
div--;
reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS;
} else {
- div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz);
+ div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
reg = SUN6I_CLK_CTL_CDR1(div);
}
@@ -269,9 +270,16 @@
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH);
+ tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U);
+ start = jiffies;
timeout = wait_for_completion_timeout(&sspi->done,
- msecs_to_jiffies(1000));
+ msecs_to_jiffies(tx_time));
+ end = jiffies;
if (!timeout) {
+ dev_warn(&master->dev,
+ "%s: timeout transferring %u bytes@%iHz for %i(%i)ms",
+ dev_name(&spi->dev), tfr->len, tfr->speed_hz,
+ jiffies_to_msecs(end - start), tx_time);
ret = -ETIMEDOUT;
goto out;
}
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
index 0f28c08..77b551d 100644
--- a/drivers/ssb/pci.c
+++ b/drivers/ssb/pci.c
@@ -909,6 +909,7 @@
if (err) {
ssb_warn("WARNING: Using fallback SPROM failed (err %d)\n",
err);
+ goto out_free;
} else {
ssb_dbg("Using SPROM revision %d provided by platform\n",
sprom->revision);
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index b14adec..aaf103b 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -32,6 +32,18 @@
Note that enabling this will break newer Android user-space.
+config ANDROID_BINDER_DEVICES
+ string "Android Binder devices"
+ depends on ANDROID_BINDER_IPC
+ default "binder,hwbinder,vndbinder"
+ ---help---
+ Default value for the binder.devices parameter.
+
+ The binder.devices parameter is a comma-separated list of strings
+ that specifies the names of the binder device nodes that will be
+ created. Each binder device has its own context manager, and is
+ therefore logically separated from the other devices.
+
config ASHMEM
bool "Enable the Anonymous Shared Memory Subsystem"
default n
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile
index a6cf480..29642b9 100644
--- a/drivers/staging/android/Makefile
+++ b/drivers/staging/android/Makefile
@@ -3,7 +3,7 @@
obj-y += ion/
obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger/
-obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
+obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o
obj-$(CONFIG_ASHMEM) += ashmem.o
obj-$(CONFIG_ANDROID_LOGGER) += logger.o
obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 8cb5d4e..63f43c2 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -396,6 +396,7 @@
ret = PTR_ERR(vmfile);
goto out;
}
+ vmfile->f_mode |= FMODE_LSEEK;
asma->file = vmfile;
}
get_file(asma->file);
diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c
index 56f9713..4e1b64a 100644
--- a/drivers/staging/android/binder.c
+++ b/drivers/staging/android/binder.c
@@ -24,9 +24,9 @@
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
-#include <linux/mm.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/spinlock.h>
#include <linux/nsproxy.h>
#include <linux/poll.h>
#include <linux/debugfs.h>
@@ -34,26 +34,26 @@
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
-#include <linux/vmalloc.h>
-#include <linux/slab.h>
#include <linux/pid_namespace.h>
#include <linux/security.h>
+#include <linux/spinlock.h>
#include "binder.h"
+#include "binder_alloc.h"
#include "binder_trace.h"
-static DEFINE_MUTEX(binder_main_lock);
-static DEFINE_MUTEX(binder_deferred_lock);
-static DEFINE_MUTEX(binder_mmap_lock);
-
-static HLIST_HEAD(binder_procs);
static HLIST_HEAD(binder_deferred_list);
-static HLIST_HEAD(binder_dead_nodes);
+static DEFINE_MUTEX(binder_deferred_lock);
+
+static HLIST_HEAD(binder_devices);
+static HLIST_HEAD(binder_procs);
+static DEFINE_MUTEX(binder_procs_lock);
+
+static DEFINE_MUTEX(binder_context_mgr_node_lock);
static struct dentry *binder_debugfs_dir_entry_root;
static struct dentry *binder_debugfs_dir_entry_proc;
-static struct binder_node *binder_context_mgr_node;
-static kuid_t binder_context_mgr_uid = INVALID_UID;
+
static int binder_last_id;
static struct workqueue_struct *binder_deferred_workqueue;
@@ -87,6 +87,9 @@
#define BINDER_SMALL_BUF_SIZE (PAGE_SIZE * 64)
+#define BINDER_NODE_INC 1
+#define BINDER_NODE_DEC 2
+
enum {
BINDER_DEBUG_USER_ERROR = 1U << 0,
BINDER_DEBUG_FAILED_TRANSACTION = 1U << 1,
@@ -104,12 +107,15 @@
BINDER_DEBUG_BUFFER_ALLOC = 1U << 13,
BINDER_DEBUG_PRIORITY_CAP = 1U << 14,
BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 15,
+ BINDER_DEBUG_SPINLOCKS = 1U << 16,
+ BINDER_DEBUG_TODO_LISTS = 1U << 17,
};
-static uint32_t binder_debug_mask = 0;
+static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR |
+ BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION;
module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO);
-static bool binder_debug_no_lock;
-module_param_named(proc_no_lock, binder_debug_no_lock, bool, S_IWUSR | S_IRUGO);
+static char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
+module_param_named(devices, binder_devices_param, charp, S_IRUGO);
static DECLARE_WAIT_QUEUE_HEAD(binder_user_error_wait);
static int binder_stop_on_user_error;
@@ -141,6 +147,17 @@
binder_stop_on_user_error = 2; \
} while (0)
+#define to_flat_binder_object(hdr) \
+ container_of(hdr, struct flat_binder_object, hdr)
+
+#define to_binder_fd_object(hdr) container_of(hdr, struct binder_fd_object, hdr)
+
+#define to_binder_buffer_object(hdr) \
+ container_of(hdr, struct binder_buffer_object, hdr)
+
+#define to_binder_fd_array_object(hdr) \
+ container_of(hdr, struct binder_fd_array_object, hdr)
+
enum binder_stat_types {
BINDER_STAT_PROC,
BINDER_STAT_THREAD,
@@ -153,22 +170,34 @@
};
struct binder_stats {
- int br[_IOC_NR(BR_FAILED_REPLY) + 1];
- int bc[_IOC_NR(BC_DEAD_BINDER_DONE) + 1];
- int obj_created[BINDER_STAT_COUNT];
- int obj_deleted[BINDER_STAT_COUNT];
+ atomic_t br[_IOC_NR(BR_FAILED_REPLY) + 1];
+ atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1];
+ atomic_t obj_created[BINDER_STAT_COUNT];
+ atomic_t obj_deleted[BINDER_STAT_COUNT];
+ atomic_t obj_zombie[BINDER_STAT_COUNT];
};
static struct binder_stats binder_stats;
static inline void binder_stats_deleted(enum binder_stat_types type)
{
- binder_stats.obj_deleted[type]++;
+ atomic_inc(&binder_stats.obj_deleted[type]);
}
static inline void binder_stats_created(enum binder_stat_types type)
{
- binder_stats.obj_created[type]++;
+ atomic_inc(&binder_stats.obj_created[type]);
+}
+
+static inline void binder_stats_zombie(enum binder_stat_types type)
+{
+ atomic_inc(&binder_stats.obj_zombie[type]);
+}
+
+static inline void binder_stats_delete_zombie(enum binder_stat_types type)
+{
+ atomic_dec(&binder_stats.obj_zombie[type]);
+ binder_stats_deleted(type);
}
struct binder_transaction_log_entry {
@@ -182,10 +211,13 @@
int to_node;
int data_size;
int offsets_size;
+ int return_error_line;
+ uint32_t return_error;
+ uint32_t return_error_param;
+ const char *context_name;
};
struct binder_transaction_log {
- int next;
- int full;
+ atomic64_t cur;
struct binder_transaction_log_entry entry[32];
};
static struct binder_transaction_log binder_transaction_log;
@@ -195,22 +227,41 @@
struct binder_transaction_log *log)
{
struct binder_transaction_log_entry *e;
+ uint64_t cur = atomic64_inc_return(&log->cur);
- e = &log->entry[log->next];
+ e = &log->entry[cur % ARRAY_SIZE(log->entry)];
memset(e, 0, sizeof(*e));
- log->next++;
- if (log->next == ARRAY_SIZE(log->entry)) {
- log->next = 0;
- log->full = 1;
- }
return e;
}
+struct binder_context {
+ struct binder_node *binder_context_mgr_node;
+ kuid_t binder_context_mgr_uid;
+ const char *name;
+ bool inherit_fifo_prio;
+};
+
+struct binder_device {
+ struct hlist_node hlist;
+ struct miscdevice miscdev;
+ struct binder_context context;
+};
+
+struct binder_worklist {
+ spinlock_t lock;
+ struct list_head list;
+ bool freeze;
+};
+
struct binder_work {
struct list_head entry;
+ struct binder_worklist *wlist;
+ int last_line;
+
enum {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
+ BINDER_WORK_RETURN_ERROR,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
@@ -218,8 +269,15 @@
} type;
};
+struct binder_error {
+ struct binder_work work;
+ uint32_t cmd;
+};
+
struct binder_node {
int debug_id;
+ int last_op;
+ int last_line;
struct binder_work work;
union {
struct rb_node rb_node;
@@ -239,7 +297,9 @@
unsigned has_async_transaction:1;
unsigned accept_fds:1;
unsigned min_priority:8;
- struct list_head async_todo;
+ unsigned sched_policy:2;
+ bool is_zombie;
+ struct binder_worklist async_todo;
};
struct binder_ref_death {
@@ -259,32 +319,48 @@
struct binder_proc *proc;
struct binder_node *node;
uint32_t desc;
- int strong;
- int weak;
+ atomic_t strong;
+ atomic_t weak;
+ bool is_zombie;
+ bool node_is_zombie;
+ struct hlist_node zombie_ref;
struct binder_ref_death *death;
};
-struct binder_buffer {
- struct list_head entry; /* free and allocated entries by address */
- struct rb_node rb_node; /* free entry by size or allocated entry */
- /* by address */
- unsigned free:1;
- unsigned allow_user_free:1;
- unsigned async_transaction:1;
- unsigned debug_id:29;
-
- struct binder_transaction *transaction;
-
- struct binder_node *target_node;
- size_t data_size;
- size_t offsets_size;
- uint8_t data[0];
-};
-
enum binder_deferred_state {
BINDER_DEFERRED_PUT_FILES = 0x01,
BINDER_DEFERRED_FLUSH = 0x02,
BINDER_DEFERRED_RELEASE = 0x04,
+ BINDER_ZOMBIE_CLEANUP = 0x08,
+};
+
+struct binder_seq_head {
+ int active_count;
+ int max_active_count;
+ spinlock_t lock;
+ struct list_head active_threads;
+ u64 lowest_seq;
+};
+
+#define SEQ_BUCKETS 16
+struct binder_seq_head binder_active_threads[SEQ_BUCKETS];
+struct binder_seq_head zombie_procs;
+
+static inline int binder_seq_hash(struct binder_thread *thread)
+{
+ u64 tp = (u64)thread;
+
+ return ((tp>>8) ^ (tp>>12)) % SEQ_BUCKETS;
+}
+
+struct binder_seq_node {
+ struct list_head list_node;
+ u64 active_seq;
+};
+
+struct binder_priority {
+ unsigned int sched_policy;
+ int prio; /* [100..139] for SCHED_NORMAL, [0..99] for FIFO/RT */
};
struct binder_proc {
@@ -293,34 +369,32 @@
struct rb_root nodes;
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
+ struct list_head waiting_threads;
int pid;
- struct vm_area_struct *vma;
- struct mm_struct *vma_vm_mm;
+ int active_thread_count;
struct task_struct *tsk;
struct files_struct *files;
+ struct files_struct *zombie_files;
struct hlist_node deferred_work_node;
int deferred_work;
- void *buffer;
- ptrdiff_t user_buffer_offset;
-
- struct list_head buffers;
- struct rb_root free_buffers;
- struct rb_root allocated_buffers;
- size_t free_async_space;
-
- struct page **pages;
- size_t buffer_size;
- uint32_t buffer_free;
- struct list_head todo;
- wait_queue_head_t wait;
+ spinlock_t proc_lock;
+ spinlock_t todo_lock;
+ struct binder_worklist todo;
struct binder_stats stats;
- struct list_head delivered_death;
+ struct binder_worklist delivered_death;
int max_threads;
int requested_threads;
int requested_threads_started;
- int ready_threads;
- long default_priority;
+ atomic_t ready_threads;
+ struct binder_priority default_priority;
struct dentry *debugfs_entry;
+ struct binder_seq_node zombie_proc;
+ bool is_zombie;
+ struct hlist_head zombie_nodes;
+ struct hlist_head zombie_refs;
+ struct hlist_head zombie_threads;
+ struct binder_alloc alloc;
+ struct binder_context *context;
};
enum {
@@ -329,24 +403,129 @@
BINDER_LOOPER_STATE_EXITED = 0x04,
BINDER_LOOPER_STATE_INVALID = 0x08,
BINDER_LOOPER_STATE_WAITING = 0x10,
- BINDER_LOOPER_STATE_NEED_RETURN = 0x20
+ BINDER_LOOPER_STATE_POLL = 0x20,
};
struct binder_thread {
struct binder_proc *proc;
- struct rb_node rb_node;
+ union {
+ struct rb_node rb_node;
+ struct hlist_node zombie_thread;
+ };
+ struct binder_seq_node active_node;
+ struct list_head waiting_thread_node;
+
int pid;
- int looper;
+ int looper; /* only modified by this thread */
+ bool looper_need_return; /* can be written by other thread */
struct binder_transaction *transaction_stack;
- struct list_head todo;
- uint32_t return_error; /* Write failed, return error code in read buf */
- uint32_t return_error2; /* Write failed, return error code in read */
- /* buffer. Used when sending a reply to a dead process that */
- /* we are also waiting on */
+ struct binder_worklist todo;
+ struct binder_error return_error;
+ struct binder_error reply_error;
wait_queue_head_t wait;
+ bool is_zombie;
struct binder_stats stats;
+ struct task_struct *task;
};
+static void binder_init_worklist(struct binder_worklist *wlist)
+{
+ spin_lock_init(&wlist->lock);
+ INIT_LIST_HEAD(&wlist->list);
+ wlist->freeze = false;
+}
+
+static void binder_freeze_worklist(struct binder_worklist *wlist)
+{
+ wlist->freeze = true;
+}
+
+static void binder_unfreeze_worklist(struct binder_worklist *wlist)
+{
+ wlist->freeze = false;
+}
+
+static inline bool _binder_worklist_empty(struct binder_worklist *wlist)
+{
+ BUG_ON(!spin_is_locked(&wlist->lock));
+ return wlist->freeze || list_empty(&wlist->list);
+}
+
+static inline bool binder_worklist_empty(struct binder_worklist *wlist)
+{
+ bool ret;
+
+ spin_lock(&wlist->lock);
+ ret = _binder_worklist_empty(wlist);
+ spin_unlock(&wlist->lock);
+ return ret;
+}
+
+static void
+binder_proc_lock(struct binder_proc *proc, int line)
+{
+ binder_debug(BINDER_DEBUG_SPINLOCKS,
+ "%s: line=%d\n", __func__, line);
+ spin_lock(&proc->proc_lock);
+}
+
+static void
+binder_proc_unlock(struct binder_proc *proc, int line)
+{
+ binder_debug(BINDER_DEBUG_SPINLOCKS,
+ "%s: line=%d\n", __func__, line);
+ spin_unlock(&proc->proc_lock);
+}
+
+static inline void
+binder_enqueue_work(struct binder_work *work,
+ struct binder_worklist *target_wlist,
+ int line)
+{
+ binder_debug(BINDER_DEBUG_TODO_LISTS,
+ "%s: line=%d last_line=%d\n", __func__,
+ line, work->last_line);
+ spin_lock(&target_wlist->lock);
+ BUG_ON(work->wlist != NULL);
+ BUG_ON(target_wlist == NULL);
+ work->wlist = target_wlist;
+ list_add_tail(&work->entry, &target_wlist->list);
+ work->last_line = line;
+ spin_unlock(&target_wlist->lock);
+}
+
+static inline void
+_binder_dequeue_work(struct binder_work *work, int line)
+{
+ binder_debug(BINDER_DEBUG_TODO_LISTS,
+ "%s: line=%d last_line=%d\n", __func__,
+ line, work->last_line);
+ list_del_init(&work->entry);
+ /* Add barrier to ensure list delete is seen */
+ smp_mb();
+ work->wlist = NULL;
+ work->last_line = -line;
+}
+
+static inline void
+binder_dequeue_work(struct binder_work *work, int line)
+{
+ struct binder_worklist *wlist = work->wlist;
+
+ while (wlist) {
+ spin_lock(&wlist->lock);
+ if (wlist == work->wlist) {
+ _binder_dequeue_work(work, line);
+ spin_unlock(&wlist->lock);
+ return;
+ }
+ spin_unlock(&wlist->lock);
+ wlist = work->wlist;
+ }
+ /* Add barrier to ensure list delete is visible */
+ smp_mb();
+}
+
struct binder_transaction {
int debug_id;
struct binder_work work;
@@ -361,20 +540,24 @@
struct binder_buffer *buffer;
unsigned int code;
unsigned int flags;
- long priority;
- long saved_priority;
+ struct binder_priority priority;
+ struct binder_priority saved_priority;
+ bool set_priority_called;
kuid_t sender_euid;
};
static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer);
+static inline void binder_queue_for_zombie_cleanup(struct binder_proc *proc);
+
+static void binder_put_thread(struct binder_thread *thread);
+static struct binder_thread *binder_get_thread(struct binder_proc *proc);
static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
{
struct files_struct *files = proc->files;
unsigned long rlim_cur;
unsigned long irqs;
- int ret;
if (files == NULL)
return -ESRCH;
@@ -385,11 +568,7 @@
rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE);
unlock_task_sighand(proc->tsk, &irqs);
- preempt_enable_no_resched();
- ret = __alloc_fd(files, 0, rlim_cur, flags);
- preempt_disable();
-
- return ret;
+ return __alloc_fd(files, 0, rlim_cur, flags);
}
/*
@@ -398,11 +577,8 @@
static void task_fd_install(
struct binder_proc *proc, unsigned int fd, struct file *file)
{
- if (proc->files) {
- preempt_enable_no_resched();
+ if (proc->files)
__fd_install(proc->files, fd, file);
- preempt_disable();
- }
}
/*
@@ -426,516 +602,249 @@
return retval;
}
-static inline void binder_lock(const char *tag)
+static inline bool binder_has_work(struct binder_thread *thread,
+ bool do_proc_work)
{
- trace_binder_lock(tag);
- mutex_lock(&binder_main_lock);
- preempt_disable();
- trace_binder_locked(tag);
+ return !binder_worklist_empty(&thread->todo) ||
+ READ_ONCE(thread->looper_need_return) ||
+ (do_proc_work && !binder_worklist_empty(&thread->proc->todo));
}
-static inline void binder_unlock(const char *tag)
+static bool binder_available_for_proc_work(struct binder_thread *thread)
{
- trace_binder_unlock(tag);
- mutex_unlock(&binder_main_lock);
- preempt_enable();
+ return !thread->transaction_stack &&
+ binder_worklist_empty(&thread->todo) &&
+ (thread->looper & (BINDER_LOOPER_STATE_ENTERED |
+ BINDER_LOOPER_STATE_REGISTERED));
}
-static inline void *kzalloc_preempt_disabled(size_t size)
+static void binder_wakeup_poll_threads(struct binder_proc *proc, bool sync)
{
- void *ptr;
+ struct rb_node *n;
+ struct binder_thread *thread;
- ptr = kzalloc(size, GFP_NOWAIT);
- if (ptr)
- return ptr;
-
- preempt_enable_no_resched();
- ptr = kzalloc(size, GFP_KERNEL);
- preempt_disable();
-
- return ptr;
+ for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) {
+ thread = rb_entry(n, struct binder_thread, rb_node);
+ if (thread->looper & BINDER_LOOPER_STATE_POLL &&
+ binder_available_for_proc_work(thread)) {
+ if (sync)
+ wake_up_interruptible_sync(&thread->wait);
+ else
+ wake_up_interruptible(&thread->wait);
+ }
+ }
}
-static inline long copy_to_user_preempt_disabled(void __user *to, const void *from, long n)
+/**
+ * binder_select_thread() - selects a thread for doing proc work.
+ * @proc: process to select a thread from
+ *
+ * Note that calling this function moves the thread off the waiting_threads
+ * list, so it can only be woken up by the caller of this function, or a
+ * signal. Therefore, callers *should* always wake up the thread this function
+ * returns.
+ *
+ * Return: If there's a thread currently waiting for process work,
+ * returns that thread. Otherwise returns NULL.
+ */
+static struct binder_thread *binder_select_thread(struct binder_proc *proc)
{
- long ret;
+ struct binder_thread *thread;
- preempt_enable_no_resched();
- ret = copy_to_user(to, from, n);
- preempt_disable();
- return ret;
+ BUG_ON(!spin_is_locked(&proc->proc_lock));
+ thread = list_first_entry_or_null(&proc->waiting_threads,
+ struct binder_thread,
+ waiting_thread_node);
+
+ if (thread)
+ list_del_init(&thread->waiting_thread_node);
+
+ return thread;
}
-static inline long copy_from_user_preempt_disabled(void *to, const void __user *from, long n)
+/**
+ * binder_wakeup_thread() - wakes up a thread for doing proc work.
+ * @proc: process to wake up a thread in
+ * @thread: specific thread to wake-up (may be NULL)
+ * @sync: whether to do a synchronous wake-up
+ *
+ * This function wakes up a thread in the @proc process.
+ * The caller may provide a specific thread to wake-up in
+ * the @thread parameter. If @thread is NULL, this function
+ * will wake up threads that have called poll().
+ *
+ * Note that for this function to work as expected, callers
+ * should first call binder_select_thread() to find a thread
+ * to handle the work (if they don't have a thread already),
+ * and pass the result into the @thread parameter.
+ *
+ * The caller must hold the proc lock when calling this function.
+ */
+static void binder_wakeup_thread(struct binder_proc *proc,
+ struct binder_thread *thread,
+ bool sync)
{
- long ret;
+ BUG_ON(!spin_is_locked(&proc->proc_lock));
- preempt_enable_no_resched();
- ret = copy_from_user(to, from, n);
- preempt_disable();
- return ret;
-}
-
-#define get_user_preempt_disabled(x, ptr) \
-({ \
- int __ret; \
- preempt_enable_no_resched(); \
- __ret = get_user(x, ptr); \
- preempt_disable(); \
- __ret; \
-})
-
-#define put_user_preempt_disabled(x, ptr) \
-({ \
- int __ret; \
- preempt_enable_no_resched(); \
- __ret = put_user(x, ptr); \
- preempt_disable(); \
- __ret; \
-})
-
-static void binder_set_nice(long nice)
-{
- long min_nice;
-
- if (can_nice(current, nice)) {
- set_user_nice(current, nice);
+ if (thread) {
+ if (sync)
+ wake_up_interruptible_sync(&thread->wait);
+ else
+ wake_up_interruptible(&thread->wait);
return;
}
- min_nice = rlimit_to_nice(current->signal->rlim[RLIMIT_NICE].rlim_cur);
- binder_debug(BINDER_DEBUG_PRIORITY_CAP,
- "%d: nice value %ld not allowed use %ld instead\n",
- current->pid, nice, min_nice);
- set_user_nice(current, min_nice);
- if (min_nice <= MAX_NICE)
- return;
- binder_user_error("%d RLIMIT_NICE not set\n", current->pid);
+
+ /* Didn't find a thread waiting for proc work; this can happen
+ * in two scenarios:
+ * 1. All threads are busy handling transactions
+ * In that case, one of those threads should call back into
+ * the kernel driver soon and pick up this work.
+ * 2. Threads are using the (e)poll interface, in which case
+ * they may be blocked on the waitqueue without having been
+ * added to waiting_threads. For this case, we just iterate
+ * over all threads not handling transaction work, and
+ * wake them all up. We wake all because we don't know whether
+ * a thread that called into (e)poll is handling non-binder
+ * work currently.
+ */
+ binder_wakeup_poll_threads(proc, sync);
}
-static size_t binder_buffer_size(struct binder_proc *proc,
- struct binder_buffer *buffer)
+static void binder_wakeup_proc(struct binder_proc *proc)
{
- if (list_is_last(&buffer->entry, &proc->buffers))
- return proc->buffer + proc->buffer_size - (void *)buffer->data;
- return (size_t)list_entry(buffer->entry.next,
- struct binder_buffer, entry) - (size_t)buffer->data;
+ struct binder_thread *thread = binder_select_thread(proc);
+
+ binder_wakeup_thread(proc, thread, false /* sync */);
}
-static void binder_insert_free_buffer(struct binder_proc *proc,
- struct binder_buffer *new_buffer)
-{
- struct rb_node **p = &proc->free_buffers.rb_node;
- struct rb_node *parent = NULL;
- struct binder_buffer *buffer;
- size_t buffer_size;
- size_t new_buffer_size;
-
- BUG_ON(!new_buffer->free);
-
- new_buffer_size = binder_buffer_size(proc, new_buffer);
-
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: add free buffer, size %zd, at %pK\n",
- proc->pid, new_buffer_size, new_buffer);
-
- while (*p) {
- parent = *p;
- buffer = rb_entry(parent, struct binder_buffer, rb_node);
- BUG_ON(!buffer->free);
-
- buffer_size = binder_buffer_size(proc, buffer);
-
- if (new_buffer_size < buffer_size)
- p = &parent->rb_left;
- else
- p = &parent->rb_right;
- }
- rb_link_node(&new_buffer->rb_node, parent, p);
- rb_insert_color(&new_buffer->rb_node, &proc->free_buffers);
+static bool is_rt_policy(int policy) {
+ return policy == SCHED_FIFO || policy == SCHED_RR;
}
-static void binder_insert_allocated_buffer(struct binder_proc *proc,
- struct binder_buffer *new_buffer)
-{
- struct rb_node **p = &proc->allocated_buffers.rb_node;
- struct rb_node *parent = NULL;
- struct binder_buffer *buffer;
-
- BUG_ON(new_buffer->free);
-
- while (*p) {
- parent = *p;
- buffer = rb_entry(parent, struct binder_buffer, rb_node);
- BUG_ON(buffer->free);
-
- if (new_buffer < buffer)
- p = &parent->rb_left;
- else if (new_buffer > buffer)
- p = &parent->rb_right;
- else
- BUG();
- }
- rb_link_node(&new_buffer->rb_node, parent, p);
- rb_insert_color(&new_buffer->rb_node, &proc->allocated_buffers);
+static bool is_fair_policy(int policy) {
+ return policy == SCHED_NORMAL || policy == SCHED_BATCH;
}
-static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc,
- uintptr_t user_ptr)
-{
- struct rb_node *n = proc->allocated_buffers.rb_node;
- struct binder_buffer *buffer;
- struct binder_buffer *kern_ptr;
-
- kern_ptr = (struct binder_buffer *)(user_ptr - proc->user_buffer_offset
- - offsetof(struct binder_buffer, data));
-
- while (n) {
- buffer = rb_entry(n, struct binder_buffer, rb_node);
- BUG_ON(buffer->free);
-
- if (kern_ptr < buffer)
- n = n->rb_left;
- else if (kern_ptr > buffer)
- n = n->rb_right;
- else
- return buffer;
- }
- return NULL;
+static bool binder_supported_policy(int policy) {
+ return is_fair_policy(policy) || is_rt_policy(policy);
}
-static int binder_update_page_range(struct binder_proc *proc, int allocate,
- void *start, void *end,
- struct vm_area_struct *vma)
-{
- void *page_addr;
- unsigned long user_page_addr;
- struct page **page;
- struct mm_struct *mm;
-
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: %s pages %pK-%pK\n", proc->pid,
- allocate ? "allocate" : "free", start, end);
-
- if (end <= start)
- return 0;
-
- trace_binder_update_page_range(proc, allocate, start, end);
-
- if (vma)
- mm = NULL;
+static int to_userspace_prio(int policy, int kernel_priority) {
+ if (is_fair_policy(policy))
+ return PRIO_TO_NICE(kernel_priority);
else
- mm = get_task_mm(proc->tsk);
-
- preempt_enable_no_resched();
-
- if (mm) {
- down_write(&mm->mmap_sem);
- vma = proc->vma;
- if (vma && mm != proc->vma_vm_mm) {
- pr_err("%d: vma mm and task mm mismatch\n",
- proc->pid);
- vma = NULL;
- }
- }
-
- if (allocate == 0)
- goto free_range;
-
- if (vma == NULL) {
- pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n",
- proc->pid);
- goto err_no_vma;
- }
-
- for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
- int ret;
-
- page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
-
- BUG_ON(*page);
- *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
- if (*page == NULL) {
- pr_err("%d: binder_alloc_buf failed for page at %pK\n",
- proc->pid, page_addr);
- goto err_alloc_page_failed;
- }
- ret = map_kernel_range_noflush((unsigned long)page_addr,
- PAGE_SIZE, PAGE_KERNEL, page);
- flush_cache_vmap((unsigned long)page_addr,
- (unsigned long)page_addr + PAGE_SIZE);
- if (ret != 1) {
- pr_err("%d: binder_alloc_buf failed to map page at %pK in kernel\n",
- proc->pid, page_addr);
- goto err_map_kernel_failed;
- }
- user_page_addr =
- (uintptr_t)page_addr + proc->user_buffer_offset;
- ret = vm_insert_page(vma, user_page_addr, page[0]);
- if (ret) {
- pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
- proc->pid, user_page_addr);
- goto err_vm_insert_page_failed;
- }
- /* vm_insert_page does not seem to increment the refcount */
- }
- if (mm) {
- up_write(&mm->mmap_sem);
- mmput(mm);
- }
-
- preempt_disable();
-
- return 0;
-
-free_range:
- for (page_addr = end - PAGE_SIZE; page_addr >= start;
- page_addr -= PAGE_SIZE) {
- page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
- if (vma)
- zap_page_range(vma, (uintptr_t)page_addr +
- proc->user_buffer_offset, PAGE_SIZE, NULL);
-err_vm_insert_page_failed:
- unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
-err_map_kernel_failed:
- __free_page(*page);
- *page = NULL;
-err_alloc_page_failed:
- ;
- }
-err_no_vma:
- if (mm) {
- up_write(&mm->mmap_sem);
- mmput(mm);
- }
-
- preempt_disable();
-
- return -ENOMEM;
+ return MAX_USER_RT_PRIO - 1 - kernel_priority;
}
-static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc,
- size_t data_size,
- size_t offsets_size, int is_async)
-{
- struct rb_node *n = proc->free_buffers.rb_node;
- struct binder_buffer *buffer;
- size_t buffer_size;
- struct rb_node *best_fit = NULL;
- void *has_page_addr;
- void *end_page_addr;
- size_t size;
-
- if (proc->vma == NULL) {
- pr_err("%d: binder_alloc_buf, no vma\n",
- proc->pid);
- return NULL;
- }
-
- size = ALIGN(data_size, sizeof(void *)) +
- ALIGN(offsets_size, sizeof(void *));
-
- if (size < data_size || size < offsets_size) {
- binder_user_error("%d: got transaction with invalid size %zd-%zd\n",
- proc->pid, data_size, offsets_size);
- return NULL;
- }
-
- if (is_async &&
- proc->free_async_space < size + sizeof(struct binder_buffer)) {
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_alloc_buf size %zd failed, no async space left\n",
- proc->pid, size);
- return NULL;
- }
-
- while (n) {
- buffer = rb_entry(n, struct binder_buffer, rb_node);
- BUG_ON(!buffer->free);
- buffer_size = binder_buffer_size(proc, buffer);
-
- if (size < buffer_size) {
- best_fit = n;
- n = n->rb_left;
- } else if (size > buffer_size)
- n = n->rb_right;
- else {
- best_fit = n;
- break;
- }
- }
- if (best_fit == NULL) {
- pr_err("%d: binder_alloc_buf size %zd failed, no address space\n",
- proc->pid, size);
- return NULL;
- }
- if (n == NULL) {
- buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
- buffer_size = binder_buffer_size(proc, buffer);
- }
-
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_alloc_buf size %zd got buffer %pK size %zd\n",
- proc->pid, size, buffer, buffer_size);
-
- has_page_addr =
- (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK);
- if (n == NULL) {
- if (size + sizeof(struct binder_buffer) + 4 >= buffer_size)
- buffer_size = size; /* no room for other buffers */
- else
- buffer_size = size + sizeof(struct binder_buffer);
- }
- end_page_addr =
- (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size);
- if (end_page_addr > has_page_addr)
- end_page_addr = has_page_addr;
- if (binder_update_page_range(proc, 1,
- (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL))
- return NULL;
-
- rb_erase(best_fit, &proc->free_buffers);
- buffer->free = 0;
- binder_insert_allocated_buffer(proc, buffer);
- if (buffer_size != size) {
- struct binder_buffer *new_buffer = (void *)buffer->data + size;
-
- list_add(&new_buffer->entry, &buffer->entry);
- new_buffer->free = 1;
- binder_insert_free_buffer(proc, new_buffer);
- }
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_alloc_buf size %zd got %pK\n",
- proc->pid, size, buffer);
- buffer->data_size = data_size;
- buffer->offsets_size = offsets_size;
- buffer->async_transaction = is_async;
- if (is_async) {
- proc->free_async_space -= size + sizeof(struct binder_buffer);
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
- "%d: binder_alloc_buf size %zd async free %zd\n",
- proc->pid, size, proc->free_async_space);
- }
-
- return buffer;
+static int to_kernel_prio(int policy, int user_priority) {
+ if (is_fair_policy(policy))
+ return NICE_TO_PRIO(user_priority);
+ else
+ return MAX_USER_RT_PRIO - 1 - user_priority;
}
-static void *buffer_start_page(struct binder_buffer *buffer)
+static void binder_set_priority(struct task_struct *task,
+ struct binder_priority desired,
+ bool restore)
{
- return (void *)((uintptr_t)buffer & PAGE_MASK);
-}
+ int priority; /* user-space prio value */
+ bool has_cap_nice;
+ unsigned int policy = desired.sched_policy;
-static void *buffer_end_page(struct binder_buffer *buffer)
-{
- return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK);
-}
+ if (task->policy == policy && task->normal_prio == desired.prio)
+ return;
-static void binder_delete_free_buffer(struct binder_proc *proc,
- struct binder_buffer *buffer)
-{
- struct binder_buffer *prev, *next = NULL;
- int free_page_end = 1;
- int free_page_start = 1;
+ has_cap_nice = has_capability_noaudit(task, CAP_SYS_NICE);
- BUG_ON(proc->buffers.next == &buffer->entry);
- prev = list_entry(buffer->entry.prev, struct binder_buffer, entry);
- BUG_ON(!prev->free);
- if (buffer_end_page(prev) == buffer_start_page(buffer)) {
- free_page_start = 0;
- if (buffer_end_page(prev) == buffer_end_page(buffer))
- free_page_end = 0;
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer %pK share page with %pK\n",
- proc->pid, buffer, prev);
- }
+ priority = to_userspace_prio(policy, desired.prio);
- if (!list_is_last(&buffer->entry, &proc->buffers)) {
- next = list_entry(buffer->entry.next,
- struct binder_buffer, entry);
- if (buffer_start_page(next) == buffer_end_page(buffer)) {
- free_page_end = 0;
- if (buffer_start_page(next) ==
- buffer_start_page(buffer))
- free_page_start = 0;
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer %pK share page with %pK\n",
- proc->pid, buffer, prev);
+ if (!restore && is_rt_policy(policy) && !has_cap_nice) {
+ long max_rtprio = task_rlimit(task, RLIMIT_RTPRIO);
+ if (max_rtprio == 0) {
+ policy = SCHED_NORMAL;
+ priority = MIN_NICE;
+ } else if (priority > max_rtprio) {
+ priority = max_rtprio;
}
}
- list_del(&buffer->entry);
- if (free_page_start || free_page_end) {
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: merge free, buffer %pK do not share page%s%s with %pK or %pK\n",
- proc->pid, buffer, free_page_start ? "" : " end",
- free_page_end ? "" : " start", prev, next);
- binder_update_page_range(proc, 0, free_page_start ?
- buffer_start_page(buffer) : buffer_end_page(buffer),
- (free_page_end ? buffer_end_page(buffer) :
- buffer_start_page(buffer)) + PAGE_SIZE, NULL);
+
+ if (!restore && is_fair_policy(policy) && !has_cap_nice) {
+ long min_nice = rlimit_to_nice(task_rlimit(task, RLIMIT_NICE));
+ if (min_nice > MAX_NICE) {
+ binder_user_error("%d RLIMIT_NICE not set\n",
+ task->pid);
+ return;
+ } else if (priority < min_nice) {
+ priority = min_nice;
+ }
}
+
+ if (policy != desired.sched_policy ||
+ to_kernel_prio(policy, priority) != desired.prio)
+ binder_debug(BINDER_DEBUG_PRIORITY_CAP,
+ "%d: priority %d not allowed, using %d instead\n",
+ task->pid, desired.prio,
+ to_kernel_prio(policy, priority));
+
+ trace_binder_set_priority(task->tgid, task->pid, task->normal_prio,
+ to_kernel_prio(policy, priority),
+ desired.prio);
+
+ /* Set the actual priority */
+ if (task->policy != policy || is_rt_policy(policy)) {
+ struct sched_param params;
+
+ params.sched_priority = is_rt_policy(policy) ? priority : 0;
+
+ sched_setscheduler_nocheck(task,
+ policy | SCHED_RESET_ON_FORK,
+ ¶ms);
+ }
+ if (is_fair_policy(policy))
+ set_user_nice(task, priority);
}
-static void binder_free_buf(struct binder_proc *proc,
- struct binder_buffer *buffer)
+static void binder_transaction_priority(struct task_struct *task,
+ struct binder_transaction *t,
+ struct binder_node *target_node)
{
- size_t size, buffer_size;
+ bool inherit_fifo = target_node->proc->context->inherit_fifo_prio;
+ struct binder_priority desired_prio;
- buffer_size = binder_buffer_size(proc, buffer);
+ desired_prio.prio = t->priority.prio;
+ desired_prio.sched_policy = t->priority.sched_policy;
- size = ALIGN(buffer->data_size, sizeof(void *)) +
- ALIGN(buffer->offsets_size, sizeof(void *));
+ t->saved_priority.sched_policy = task->policy;
+ t->saved_priority.prio = task->normal_prio;
+ t->set_priority_called = true;
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%d: binder_free_buf %pK size %zd buffer_size %zd\n",
- proc->pid, buffer, size, buffer_size);
+ if (!binder_supported_policy(desired_prio.sched_policy))
+ return;
- BUG_ON(buffer->free);
- BUG_ON(size > buffer_size);
- BUG_ON(buffer->transaction != NULL);
- BUG_ON((void *)buffer < proc->buffer);
- BUG_ON((void *)buffer > proc->buffer + proc->buffer_size);
-
- if (buffer->async_transaction) {
- proc->free_async_space += size + sizeof(struct binder_buffer);
-
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
- "%d: binder_free_buf size %zd async free %zd\n",
- proc->pid, size, proc->free_async_space);
+ if (target_node->min_priority < t->priority.prio ||
+ (target_node->min_priority == t->priority.prio &&
+ target_node->sched_policy == SCHED_FIFO)) {
+ desired_prio.sched_policy = target_node->sched_policy;
+ desired_prio.prio = target_node->min_priority;
}
- binder_update_page_range(proc, 0,
- (void *)PAGE_ALIGN((uintptr_t)buffer->data),
- (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK),
- NULL);
- rb_erase(&buffer->rb_node, &proc->allocated_buffers);
- buffer->free = 1;
- if (!list_is_last(&buffer->entry, &proc->buffers)) {
- struct binder_buffer *next = list_entry(buffer->entry.next,
- struct binder_buffer, entry);
-
- if (next->free) {
- rb_erase(&next->rb_node, &proc->free_buffers);
- binder_delete_free_buffer(proc, next);
- }
+ if (is_rt_policy(desired_prio.sched_policy) && !inherit_fifo) {
+ /* Can't inherit real-time, fall-back to min nice instead */
+ desired_prio.sched_policy = SCHED_NORMAL;
+ desired_prio.prio = NICE_TO_PRIO(MIN_NICE);
}
- if (proc->buffers.next != &buffer->entry) {
- struct binder_buffer *prev = list_entry(buffer->entry.prev,
- struct binder_buffer, entry);
- if (prev->free) {
- binder_delete_free_buffer(proc, buffer);
- rb_erase(&prev->rb_node, &proc->free_buffers);
- buffer = prev;
- }
- }
- binder_insert_free_buffer(proc, buffer);
+ binder_set_priority(task, desired_prio, false /* restore */);
}
static struct binder_node *binder_get_node(struct binder_proc *proc,
binder_uintptr_t ptr)
{
- struct rb_node *n = proc->nodes.rb_node;
+ struct rb_node *n;
struct binder_node *node;
+ binder_proc_lock(proc, __LINE__);
+ n = proc->nodes.rb_node;
while (n) {
node = rb_entry(n, struct binder_node, rb_node);
@@ -943,20 +852,42 @@
n = n->rb_left;
else if (ptr > node->ptr)
n = n->rb_right;
- else
+ else {
+ node->local_weak_refs++;
+ binder_proc_unlock(proc, __LINE__);
return node;
+ }
}
+ binder_proc_unlock(proc, __LINE__);
+
return NULL;
}
+static void _binder_make_node_zombie(struct binder_node *node)
+{
+ struct binder_proc *proc = node->proc;
+
+ BUG_ON(node->is_zombie);
+ BUG_ON(!spin_is_locked(&proc->proc_lock));
+ rb_erase(&node->rb_node, &proc->nodes);
+ INIT_HLIST_NODE(&node->dead_node);
+ node->is_zombie = true;
+ hlist_add_head(&node->dead_node, &proc->zombie_nodes);
+ binder_queue_for_zombie_cleanup(proc);
+ binder_stats_zombie(BINDER_STAT_NODE);
+}
+
static struct binder_node *binder_new_node(struct binder_proc *proc,
binder_uintptr_t ptr,
binder_uintptr_t cookie)
{
struct rb_node **p = &proc->nodes.rb_node;
struct rb_node *parent = NULL;
- struct binder_node *node;
+ struct binder_node *node, *temp_node;
+ temp_node = kzalloc(sizeof(*node), GFP_KERNEL);
+
+ binder_proc_lock(proc, __LINE__);
while (*p) {
parent = *p;
node = rb_entry(parent, struct binder_node, rb_node);
@@ -965,50 +896,77 @@
p = &(*p)->rb_left;
else if (ptr > node->ptr)
p = &(*p)->rb_right;
- else
- return NULL;
+ else {
+ node->local_weak_refs++;
+ binder_proc_unlock(proc, __LINE__);
+ kfree(temp_node);
+ return node;
+ }
}
- node = kzalloc_preempt_disabled(sizeof(*node));
- if (node == NULL)
+ node = temp_node;
+ if (node == NULL) {
+ binder_proc_unlock(proc, __LINE__);
return NULL;
+ }
binder_stats_created(BINDER_STAT_NODE);
- rb_link_node(&node->rb_node, parent, p);
- rb_insert_color(&node->rb_node, &proc->nodes);
+
node->debug_id = ++binder_last_id;
node->proc = proc;
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE;
INIT_LIST_HEAD(&node->work.entry);
- INIT_LIST_HEAD(&node->async_todo);
+ INIT_HLIST_HEAD(&node->refs);
+ binder_init_worklist(&node->async_todo);
+
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d:%d node %d u%016llx c%016llx created\n",
proc->pid, current->pid, node->debug_id,
(u64)node->ptr, (u64)node->cookie);
+
+ rb_link_node(&node->rb_node, parent, p);
+ rb_insert_color(&node->rb_node, &proc->nodes);
+ node->local_weak_refs++;
+ binder_proc_unlock(proc, __LINE__);
+
return node;
}
static int binder_inc_node(struct binder_node *node, int strong, int internal,
- struct list_head *target_list)
+ struct binder_worklist *target_list, int line)
{
+ int ret = 0;
+ struct binder_proc *proc = node->proc;
+
+ binder_proc_lock(proc, __LINE__);
+
+ node->last_line = line;
+ node->last_op = BINDER_NODE_INC;
+
if (strong) {
if (internal) {
if (target_list == NULL &&
node->internal_strong_refs == 0 &&
- !(node == binder_context_mgr_node &&
- node->has_strong_ref)) {
+ !(node->proc &&
+ node == node->proc->context->
+ binder_context_mgr_node &&
+ node->has_strong_ref)) {
pr_err("invalid inc strong node for %d\n",
node->debug_id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto done;
}
node->internal_strong_refs++;
} else
node->local_strong_refs++;
+
if (!node->has_strong_ref && target_list) {
- list_del_init(&node->work.entry);
- list_add_tail(&node->work.entry, target_list);
+ binder_dequeue_work(&node->work, __LINE__);
+ binder_enqueue_work(&node->work, target_list,
+ __LINE__);
}
+
} else {
if (!internal)
node->local_weak_refs++;
@@ -1016,64 +974,89 @@
if (target_list == NULL) {
pr_err("invalid inc weak node for %d\n",
node->debug_id);
- return -EINVAL;
+ ret = -EINVAL;
+ goto done;
}
- list_add_tail(&node->work.entry, target_list);
+
+ binder_enqueue_work(&node->work, target_list,
+ __LINE__);
}
}
- return 0;
+done:
+ binder_proc_unlock(proc, __LINE__);
+ return ret;
}
-static int binder_dec_node(struct binder_node *node, int strong, int internal)
+static int binder_dec_node(struct binder_node *node, int strong, int internal,
+ int line)
{
+ bool do_wakeup = false;
+ struct binder_proc *proc = node->proc;
+
+ binder_proc_lock(proc, __LINE__);
+
+ node->last_line = line;
+ node->last_op = BINDER_NODE_DEC;
+
if (strong) {
if (internal)
node->internal_strong_refs--;
else
node->local_strong_refs--;
if (node->local_strong_refs || node->internal_strong_refs)
- return 0;
+ goto done;
} else {
if (!internal)
node->local_weak_refs--;
if (node->local_weak_refs || !hlist_empty(&node->refs))
- return 0;
+ goto done;
}
- if (node->proc && (node->has_strong_ref || node->has_weak_ref)) {
+
+ if (node->has_strong_ref || node->has_weak_ref) {
if (list_empty(&node->work.entry)) {
- list_add_tail(&node->work.entry, &node->proc->todo);
- wake_up_interruptible(&node->proc->wait);
+ binder_enqueue_work(&node->work,
+ &proc->todo, __LINE__);
+ do_wakeup = true;
}
+
} else {
if (hlist_empty(&node->refs) && !node->local_strong_refs &&
!node->local_weak_refs) {
- list_del_init(&node->work.entry);
- if (node->proc) {
- rb_erase(&node->rb_node, &node->proc->nodes);
+ binder_dequeue_work(&node->work, __LINE__);
+ if (!node->is_zombie) {
+ _binder_make_node_zombie(node);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"refless node %d deleted\n",
node->debug_id);
} else {
- hlist_del(&node->dead_node);
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"dead node %d deleted\n",
node->debug_id);
}
- kfree(node);
- binder_stats_deleted(BINDER_STAT_NODE);
}
}
+done:
+ if (do_wakeup)
+ binder_wakeup_proc(proc);
+
+ binder_proc_unlock(proc, __LINE__);
return 0;
}
+static inline void binder_put_node(struct binder_node *node)
+{
+ binder_dec_node(node, 0, 0, __LINE__);
+}
static struct binder_ref *binder_get_ref(struct binder_proc *proc,
uint32_t desc, bool need_strong_ref)
{
- struct rb_node *n = proc->refs_by_desc.rb_node;
+ struct rb_node *n;
struct binder_ref *ref;
+ binder_proc_lock(proc, __LINE__);
+ n = proc->refs_by_desc.rb_node;
while (n) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
@@ -1081,24 +1064,43 @@
n = n->rb_left;
} else if (desc > ref->desc) {
n = n->rb_right;
- } else if (need_strong_ref && !ref->strong) {
+ } else if (need_strong_ref && !atomic_read(&ref->strong)) {
binder_user_error("tried to use weak ref as strong ref\n");
+ binder_proc_unlock(proc, __LINE__);
return NULL;
} else {
+ /*
+ * We take an implicit weak reference to ensure
+ * that the ref is not used and deref'd before the
+ * caller has a chance to add a reference. The caller
+ * must use binder_put_ref to indicate completion
+ * of the operation on the ref
+ *
+ * This ref is orthogonal to any existing weak/strong
+ * reference taken by the callers (no relation to the
+ * passed-in need_strong_ref).
+ */
+ atomic_inc(&ref->weak);
+ binder_proc_unlock(proc, __LINE__);
return ref;
}
}
+ binder_proc_unlock(proc, __LINE__);
return NULL;
}
static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
- struct binder_node *node)
+ struct binder_node *node,
+ struct binder_worklist *target_list)
{
struct rb_node *n;
struct rb_node **p = &proc->refs_by_node.rb_node;
struct rb_node *parent = NULL;
struct binder_ref *ref, *new_ref;
+ struct binder_context *context = proc->context;
+ struct binder_proc *node_proc = node->proc;
+ binder_proc_lock(proc, __LINE__);
while (*p) {
parent = *p;
ref = rb_entry(parent, struct binder_ref, rb_node_node);
@@ -1107,20 +1109,95 @@
p = &(*p)->rb_left;
else if (node > ref->node)
p = &(*p)->rb_right;
- else
+ else {
+ atomic_inc(&ref->weak);
+ binder_proc_unlock(proc, __LINE__);
return ref;
+ }
}
- new_ref = kzalloc_preempt_disabled(sizeof(*ref));
+ binder_proc_unlock(proc, __LINE__);
+
+ /* Need to allocate a new ref */
+ new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
if (new_ref == NULL)
return NULL;
+
binder_stats_created(BINDER_STAT_REF);
new_ref->debug_id = ++binder_last_id;
new_ref->proc = proc;
new_ref->node = node;
+ atomic_set(&new_ref->strong, 0);
+ /*
+ * We take an implicit weak reference to ensure
+ * that the ref is not used and deref'd before the
+ * caller has a chance to add a reference. The caller
+ * must use binder_put_ref to indicate completion
+ * of the operation on the ref
+ */
+ atomic_set(&new_ref->weak, 1);
+
+ /*
+ * Attach the new ref to the node before
+ * making it visible for lookups (needed
+ * to insure a weak ref on the node)
+ */
+ binder_proc_lock(node_proc, __LINE__);
+ if (node->is_zombie && hlist_empty(&node->refs)) {
+ /*
+ * Do not allow new refs to unreferenced zombie nodes
+ */
+ binder_proc_unlock(node_proc, __LINE__);
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "%d attempt to take new ref %d desc %d on unreferenced zombie node\n",
+ proc->pid, new_ref->debug_id, new_ref->desc);
+ kfree(new_ref);
+ binder_stats_deleted(BINDER_STAT_REF);
+ return NULL;
+ }
+ INIT_HLIST_NODE(&new_ref->node_entry);
+ hlist_add_head(&new_ref->node_entry, &node->refs);
+
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "%d new ref %d desc %d for node\n",
+ proc->pid, new_ref->debug_id, new_ref->desc);
+ binder_proc_unlock(node_proc, __LINE__);
+
+ binder_proc_lock(proc, __LINE__);
+ /*
+ * Since we dropped the proc lock, we need to
+ * recompute the insertion point
+ */
+ p = &proc->refs_by_node.rb_node;
+ while (*p) {
+ parent = *p;
+ ref = rb_entry(parent, struct binder_ref, rb_node_node);
+
+ if (node < ref->node)
+ p = &(*p)->rb_left;
+ else if (node > ref->node)
+ p = &(*p)->rb_right;
+ else {
+ /*
+ * ref already created by another thread
+ * disconnect and free the new ref
+ */
+ if (!ref->is_zombie)
+ atomic_inc(&ref->weak);
+ else
+ ref = NULL;
+ binder_proc_unlock(proc, __LINE__);
+ binder_proc_lock(node_proc, __LINE__);
+ hlist_del(&new_ref->node_entry);
+ binder_proc_unlock(node_proc, __LINE__);
+ kfree(new_ref);
+ binder_stats_deleted(BINDER_STAT_REF);
+ return ref;
+ }
+ }
rb_link_node(&new_ref->rb_node_node, parent, p);
rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
- new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;
+ new_ref->desc = (node == context->binder_context_mgr_node) ? 0 : 1;
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
if (ref->desc > new_ref->desc)
@@ -1142,111 +1219,151 @@
}
rb_link_node(&new_ref->rb_node_desc, parent, p);
rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);
- if (node) {
- hlist_add_head(&new_ref->node_entry, &node->refs);
-
- binder_debug(BINDER_DEBUG_INTERNAL_REFS,
- "%d new ref %d desc %d for node %d\n",
- proc->pid, new_ref->debug_id, new_ref->desc,
- node->debug_id);
- } else {
- binder_debug(BINDER_DEBUG_INTERNAL_REFS,
- "%d new ref %d desc %d for dead node\n",
- proc->pid, new_ref->debug_id, new_ref->desc);
- }
+ binder_proc_unlock(proc, __LINE__);
+ smp_mb();
+ /*
+ * complete the implicit weak inc_ref by incrementing
+ * the node.
+ */
+ binder_inc_node(new_ref->node, 0, 1, target_list, __LINE__);
return new_ref;
}
-static void binder_delete_ref(struct binder_ref *ref)
+static void binder_delete_ref(struct binder_ref *ref, bool force, int line)
{
+ struct binder_proc *node_proc = ref->node->proc;
+
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d delete ref %d desc %d for node %d\n",
ref->proc->pid, ref->debug_id, ref->desc,
ref->node->debug_id);
+ binder_proc_lock(ref->proc, __LINE__);
+ if (ref->is_zombie ||
+ (!force && ((atomic_read(&ref->strong) != 0) ||
+ (atomic_read(&ref->weak) != 0)))) {
+ /*
+ * Multiple threads could observe the ref counts
+ * going to 0. The first one to get the lock will
+ * make it a zombie. Subsequent callers bail out
+ * here.
+ */
+ binder_proc_unlock(ref->proc, __LINE__);
+ return;
+ }
+
+ ref->is_zombie = true;
rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
- if (ref->strong)
- binder_dec_node(ref->node, 1, 1);
- hlist_del(&ref->node_entry);
- binder_dec_node(ref->node, 0, 1);
if (ref->death) {
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"%d delete ref %d desc %d has death notification\n",
ref->proc->pid, ref->debug_id, ref->desc);
- list_del(&ref->death->work.entry);
- kfree(ref->death);
- binder_stats_deleted(BINDER_STAT_DEATH);
+ if (ref->death->work.wlist)
+ binder_dequeue_work(&ref->death->work, __LINE__);
+ binder_stats_zombie(BINDER_STAT_DEATH);
}
- kfree(ref);
- binder_stats_deleted(BINDER_STAT_REF);
+ INIT_HLIST_NODE(&ref->zombie_ref);
+ hlist_add_head(&ref->zombie_ref, &ref->proc->zombie_refs);
+ binder_queue_for_zombie_cleanup(ref->proc);
+ binder_proc_unlock(ref->proc, __LINE__);
+
+ binder_proc_lock(node_proc, __LINE__);
+ if (ref->node->is_zombie)
+ binder_queue_for_zombie_cleanup(node_proc);
+ hlist_del(&ref->node_entry);
+ binder_proc_unlock(node_proc, __LINE__);
+
+ if (atomic_read(&ref->strong))
+ binder_dec_node(ref->node, 1, 1, line);
+ binder_dec_node(ref->node, 0, 1, line);
+
+ binder_stats_zombie(BINDER_STAT_REF);
}
static int binder_inc_ref(struct binder_ref *ref, int strong,
- struct list_head *target_list)
+ struct binder_worklist *target_list, int line)
{
- int ret;
+ int ret = 0;
- if (strong) {
- if (ref->strong == 0) {
- ret = binder_inc_node(ref->node, 1, 1, target_list);
- if (ret)
- return ret;
+ /* atomic_inc_return does not require explicit barrier */
+ if ((strong && atomic_inc_return(&ref->strong) == 1) ||
+ (!strong && atomic_inc_return(&ref->weak) == 1)) {
+ ret = binder_inc_node(ref->node, strong, 1, target_list, line);
+ if (ret) {
+ atomic_dec(strong ? &ref->strong : &ref->weak);
+ smp_mb__after_atomic();
}
- ref->strong++;
- } else {
- if (ref->weak == 0) {
- ret = binder_inc_node(ref->node, 0, 1, target_list);
- if (ret)
- return ret;
- }
- ref->weak++;
}
+
+ return ret;
+}
+
+static int binder_dec_ref(struct binder_ref *ref, int strong, int line)
+{
+ if (strong) {
+ /* atomic_dec_if_positive does not require explicit barrier */
+ int newval = atomic_dec_if_positive(&ref->strong);
+
+ if (newval < 0) {
+ binder_user_error("%d invalid dec strong, ref %d desc %d s %d w %d\n",
+ ref->proc->pid, ref->debug_id,
+ ref->desc, atomic_read(&ref->strong),
+ atomic_read(&ref->weak));
+ return -EINVAL;
+ }
+ if (newval == 0) {
+ int ret = binder_dec_node(ref->node, strong, 1, line);
+ if (ret)
+ return ret;
+ }
+ } else {
+ int newval = atomic_dec_if_positive(&ref->weak);
+
+ if (newval < 0) {
+ binder_user_error("%d invalid dec weak, ref %d desc %d s %d w %d\n",
+ ref->proc->pid, ref->debug_id,
+ ref->desc, atomic_read(&ref->strong),
+ atomic_read(&ref->weak));
+ return -EINVAL;
+ }
+ }
+
+ if (atomic_read(&ref->strong) == 0 && atomic_read(&ref->weak) == 0)
+ /* it is possible that multiple threads could observe the
+ * strong/weak references going to 0 at the same time.
+ * This case is handled when the proc lock is acquired
+ * in binder_delete_ref()
+ */
+ binder_delete_ref(ref, false, line);
return 0;
}
-
-static int binder_dec_ref(struct binder_ref *ref, int strong)
+static inline void binder_put_ref(struct binder_ref *ref)
{
- if (strong) {
- if (ref->strong == 0) {
- binder_user_error("%d invalid dec strong, ref %d desc %d s %d w %d\n",
- ref->proc->pid, ref->debug_id,
- ref->desc, ref->strong, ref->weak);
- return -EINVAL;
- }
- ref->strong--;
- if (ref->strong == 0) {
- int ret;
-
- ret = binder_dec_node(ref->node, strong, 1);
- if (ret)
- return ret;
- }
- } else {
- if (ref->weak == 0) {
- binder_user_error("%d invalid dec weak, ref %d desc %d s %d w %d\n",
- ref->proc->pid, ref->debug_id,
- ref->desc, ref->strong, ref->weak);
- return -EINVAL;
- }
- ref->weak--;
- }
- if (ref->strong == 0 && ref->weak == 0)
- binder_delete_ref(ref);
- return 0;
+ binder_dec_ref(ref, 0, __LINE__);
}
static void binder_pop_transaction(struct binder_thread *target_thread,
struct binder_transaction *t)
{
- if (target_thread) {
- BUG_ON(target_thread->transaction_stack != t);
- BUG_ON(target_thread->transaction_stack->from != target_thread);
- target_thread->transaction_stack =
- target_thread->transaction_stack->from_parent;
- t->from = NULL;
- }
+ BUG_ON(!target_thread);
+ BUG_ON(!spin_is_locked(&target_thread->proc->proc_lock));
+
+ BUG_ON(target_thread->transaction_stack != t);
+ /*
+ * It is possible that the target_thread has died so
+ * transaction_stack->from could already be NULL
+ */
+ BUG_ON(target_thread->transaction_stack->from &&
+ target_thread->transaction_stack->from != target_thread);
+ target_thread->transaction_stack =
+ target_thread->transaction_stack->from_parent;
+ t->from = NULL;
+}
+
+static void binder_free_transaction(struct binder_transaction *t)
+{
t->need_reply = 0;
if (t->buffer)
t->buffer->transaction = NULL;
@@ -1264,28 +1381,27 @@
while (1) {
target_thread = t->from;
if (target_thread) {
- if (target_thread->return_error != BR_OK &&
- target_thread->return_error2 == BR_OK) {
- target_thread->return_error2 =
- target_thread->return_error;
- target_thread->return_error = BR_OK;
- }
- if (target_thread->return_error == BR_OK) {
- binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
- "send failed reply for transaction %d to %d:%d\n",
- t->debug_id,
- target_thread->proc->pid,
- target_thread->pid);
+ binder_proc_lock(target_thread->proc, __LINE__);
- binder_pop_transaction(target_thread, t);
- target_thread->return_error = error_code;
- wake_up_interruptible(&target_thread->wait);
+ binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
+ "send failed reply for transaction %d to %d:%d\n",
+ t->debug_id,
+ target_thread->proc->pid,
+ target_thread->pid);
+
+ binder_pop_transaction(target_thread, t);
+ if (target_thread->reply_error.cmd == BR_OK) {
+ target_thread->reply_error.cmd = error_code;
+ binder_enqueue_work(
+ &target_thread->reply_error.work,
+ &target_thread->todo, __LINE__);
} else {
- pr_err("reply failed, target thread, %d:%d, has error code %d already\n",
- target_thread->proc->pid,
- target_thread->pid,
- target_thread->return_error);
+ WARN(1, "Unexpected reply error: %u\n",
+ target_thread->reply_error.cmd);
}
+ binder_proc_unlock(target_thread->proc, __LINE__);
+ wake_up_interruptible(&target_thread->wait);
+ binder_free_transaction(t);
return;
}
next = t->from_parent;
@@ -1294,7 +1410,7 @@
"send failed reply for transaction %d, target dead\n",
t->debug_id);
- binder_pop_transaction(target_thread, t);
+ binder_free_transaction(t);
if (next == NULL) {
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"reply failed, no target thread at root\n");
@@ -1307,11 +1423,158 @@
}
}
+/**
+ * binder_validate_object() - checks for a valid metadata object in a buffer.
+ * @buffer: binder_buffer that we're parsing.
+ * @offset: offset in the buffer at which to validate an object.
+ *
+ * Return: If there's a valid metadata object at @offset in @buffer, the
+ * size of that object. Otherwise, it returns zero.
+ */
+static size_t binder_validate_object(struct binder_buffer *buffer, u64 offset)
+{
+ /* Check if we can read a header first */
+ struct binder_object_header *hdr;
+ size_t object_size = 0;
+
+ if (offset > buffer->data_size - sizeof(*hdr) ||
+ buffer->data_size < sizeof(*hdr) ||
+ !IS_ALIGNED(offset, sizeof(u32)))
+ return 0;
+
+ /* Ok, now see if we can read a complete object. */
+ hdr = (struct binder_object_header *)(buffer->data + offset);
+ switch (hdr->type) {
+ case BINDER_TYPE_BINDER:
+ case BINDER_TYPE_WEAK_BINDER:
+ case BINDER_TYPE_HANDLE:
+ case BINDER_TYPE_WEAK_HANDLE:
+ object_size = sizeof(struct flat_binder_object);
+ break;
+ case BINDER_TYPE_FD:
+ object_size = sizeof(struct binder_fd_object);
+ break;
+ case BINDER_TYPE_PTR:
+ object_size = sizeof(struct binder_buffer_object);
+ break;
+ case BINDER_TYPE_FDA:
+ object_size = sizeof(struct binder_fd_array_object);
+ break;
+ default:
+ return 0;
+ }
+ if (offset <= buffer->data_size - object_size &&
+ buffer->data_size >= object_size)
+ return object_size;
+ else
+ return 0;
+}
+
+/**
+ * binder_validate_ptr() - validates binder_buffer_object in a binder_buffer.
+ * @b: binder_buffer containing the object
+ * @index: index in offset array at which the binder_buffer_object is
+ * located
+ * @start: points to the start of the offset array
+ * @num_valid: the number of valid offsets in the offset array
+ *
+ * Return: If @index is within the valid range of the offset array
+ * described by @start and @num_valid, and if there's a valid
+ * binder_buffer_object at the offset found in index @index
+ * of the offset array, that object is returned. Otherwise,
+ * %NULL is returned.
+ * Note that the offset found in index @index itself is not
+ * verified; this function assumes that @num_valid elements
+ * from @start were previously verified to have valid offsets.
+ */
+static struct binder_buffer_object *binder_validate_ptr(struct binder_buffer *b,
+ binder_size_t index,
+ binder_size_t *start,
+ binder_size_t num_valid)
+{
+ struct binder_buffer_object *buffer_obj;
+ binder_size_t *offp;
+
+ if (index >= num_valid)
+ return NULL;
+
+ offp = start + index;
+ buffer_obj = (struct binder_buffer_object *)(b->data + *offp);
+ if (buffer_obj->hdr.type != BINDER_TYPE_PTR)
+ return NULL;
+
+ return buffer_obj;
+}
+
+/**
+ * binder_validate_fixup() - validates pointer/fd fixups happen in order.
+ * @b: transaction buffer
+ * @objects_start start of objects buffer
+ * @buffer: binder_buffer_object in which to fix up
+ * @offset: start offset in @buffer to fix up
+ * @last_obj: last binder_buffer_object that we fixed up in
+ * @last_min_offset: minimum fixup offset in @last_obj
+ *
+ * Return: %true if a fixup in buffer @buffer at offset @offset is
+ * allowed.
+ *
+ * For safety reasons, we only allow fixups inside a buffer to happen
+ * at increasing offsets; additionally, we only allow fixup on the last
+ * buffer object that was verified, or one of its parents.
+ *
+ * Example of what is allowed:
+ *
+ * A
+ * B (parent = A, offset = 0)
+ * C (parent = A, offset = 16)
+ * D (parent = C, offset = 0)
+ * E (parent = A, offset = 32) // min_offset is 16 (C.parent_offset)
+ *
+ * Examples of what is not allowed:
+ *
+ * Decreasing offsets within the same parent:
+ * A
+ * C (parent = A, offset = 16)
+ * B (parent = A, offset = 0) // decreasing offset within A
+ *
+ * Referring to a parent that wasn't the last object or any of its parents:
+ * A
+ * B (parent = A, offset = 0)
+ * C (parent = A, offset = 0)
+ * C (parent = A, offset = 16)
+ * D (parent = B, offset = 0) // B is not A or any of A's parents
+ */
+static bool binder_validate_fixup(struct binder_buffer *b,
+ binder_size_t *objects_start,
+ struct binder_buffer_object *buffer,
+ binder_size_t fixup_offset,
+ struct binder_buffer_object *last_obj,
+ binder_size_t last_min_offset)
+{
+ if (!last_obj) {
+ /* Nothing to fix up in */
+ return false;
+ }
+
+ while (last_obj != buffer) {
+ /*
+ * Safe to retrieve the parent of last_obj, since it
+ * was already previously verified by the driver.
+ */
+ if ((last_obj->flags & BINDER_BUFFER_FLAG_HAS_PARENT) == 0)
+ return false;
+ last_min_offset = last_obj->parent_offset + sizeof(uintptr_t);
+ last_obj = (struct binder_buffer_object *)
+ (b->data + *(objects_start + last_obj->parent));
+ }
+ return (fixup_offset >= last_min_offset);
+}
+
static void binder_transaction_buffer_release(struct binder_proc *proc,
struct binder_buffer *buffer,
binder_size_t *failed_at)
{
- binder_size_t *offp, *off_end;
+ binder_size_t *offp, *off_start, *off_end;
int debug_id = buffer->debug_id;
binder_debug(BINDER_DEBUG_TRANSACTION,
@@ -1320,87 +1583,487 @@
buffer->data_size, buffer->offsets_size, failed_at);
if (buffer->target_node)
- binder_dec_node(buffer->target_node, 1, 0);
+ binder_dec_node(buffer->target_node, 1, 0, __LINE__);
- offp = (binder_size_t *)(buffer->data +
- ALIGN(buffer->data_size, sizeof(void *)));
+ off_start = (binder_size_t *)(buffer->data +
+ ALIGN(buffer->data_size, sizeof(void *)));
if (failed_at)
off_end = failed_at;
else
- off_end = (void *)offp + buffer->offsets_size;
- for (; offp < off_end; offp++) {
- struct flat_binder_object *fp;
+ off_end = (void *)off_start + buffer->offsets_size;
+ for (offp = off_start; offp < off_end; offp++) {
+ struct binder_object_header *hdr;
+ size_t object_size = binder_validate_object(buffer, *offp);
- if (*offp > buffer->data_size - sizeof(*fp) ||
- buffer->data_size < sizeof(*fp) ||
- !IS_ALIGNED(*offp, sizeof(u32))) {
- pr_err("transaction release %d bad offset %lld, size %zd\n",
+ if (object_size == 0) {
+ pr_err("transaction release %d bad object at offset %lld, size %zd\n",
debug_id, (u64)*offp, buffer->data_size);
continue;
}
- fp = (struct flat_binder_object *)(buffer->data + *offp);
- switch (fp->type) {
+ hdr = (struct binder_object_header *)(buffer->data + *offp);
+ switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
- struct binder_node *node = binder_get_node(proc, fp->binder);
+ struct flat_binder_object *fp;
+ struct binder_node *node;
+ fp = to_flat_binder_object(hdr);
+ node = binder_get_node(proc, fp->binder);
if (node == NULL) {
- pr_err("transaction release %d bad node %016llx\n",
+ pr_err("transaction release %d bad node %016llx, target died\n",
debug_id, (u64)fp->binder);
break;
}
binder_debug(BINDER_DEBUG_TRANSACTION,
" node %d u%016llx\n",
node->debug_id, (u64)node->ptr);
- binder_dec_node(node, fp->type == BINDER_TYPE_BINDER, 0);
+ binder_dec_node(node, hdr->type == BINDER_TYPE_BINDER,
+ 0, __LINE__);
+ binder_put_node(node);
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
- struct binder_ref *ref = binder_get_ref(proc, fp->handle,
- fp->type == BINDER_TYPE_HANDLE);
+ struct flat_binder_object *fp;
+ struct binder_ref *ref;
+ fp = to_flat_binder_object(hdr);
+ ref = binder_get_ref(proc, fp->handle,
+ hdr->type == BINDER_TYPE_HANDLE);
if (ref == NULL) {
- pr_err("transaction release %d bad handle %d\n",
+ pr_err("transaction release %d bad handle %d, target died\n",
debug_id, fp->handle);
break;
}
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d (node %d)\n",
ref->debug_id, ref->desc, ref->node->debug_id);
- binder_dec_ref(ref, fp->type == BINDER_TYPE_HANDLE);
+ binder_dec_ref(ref, hdr->type == BINDER_TYPE_HANDLE,
+ __LINE__);
+ binder_put_ref(ref);
} break;
- case BINDER_TYPE_FD:
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " fd %d\n", fp->handle);
- if (failed_at)
- task_close_fd(proc, fp->handle);
- break;
+ case BINDER_TYPE_FD: {
+ struct binder_fd_object *fp = to_binder_fd_object(hdr);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " fd %d\n", fp->fd);
+ if (failed_at)
+ task_close_fd(proc, fp->fd);
+ } break;
+ case BINDER_TYPE_PTR:
+ /*
+ * Nothing to do here, this will get cleaned up when the
+ * transaction buffer gets freed
+ */
+ break;
+ case BINDER_TYPE_FDA: {
+ struct binder_fd_array_object *fda;
+ struct binder_buffer_object *parent;
+ uintptr_t parent_buffer;
+ u32 *fd_array;
+ size_t fd_index;
+ binder_size_t fd_buf_size;
+
+ fda = to_binder_fd_array_object(hdr);
+ parent = binder_validate_ptr(buffer, fda->parent,
+ off_start,
+ offp - off_start);
+ if (!parent) {
+ pr_err("transaction release %d bad parent offset",
+ debug_id);
+ continue;
+ }
+ /*
+ * Since the parent was already fixed up, convert it
+ * back to kernel address space to access it
+ */
+ parent_buffer = parent->buffer -
+ binder_alloc_get_user_buffer_offset(
+ &proc->alloc);
+
+ fd_buf_size = sizeof(u32) * fda->num_fds;
+ if (fda->num_fds >= SIZE_MAX / sizeof(u32)) {
+ pr_err("transaction release %d invalid number of fds (%lld)\n",
+ debug_id, (u64)fda->num_fds);
+ continue;
+ }
+ if (fd_buf_size > parent->length ||
+ fda->parent_offset > parent->length - fd_buf_size) {
+ /* No space for all file descriptors here. */
+ pr_err("transaction release %d not enough space for %lld fds in buffer\n",
+ debug_id, (u64)fda->num_fds);
+ continue;
+ }
+ fd_array = (u32 *)(parent_buffer + fda->parent_offset);
+ for (fd_index = 0; fd_index < fda->num_fds; fd_index++)
+ task_close_fd(proc, fd_array[fd_index]);
+ } break;
default:
pr_err("transaction release %d bad object type %x\n",
- debug_id, fp->type);
+ debug_id, hdr->type);
break;
}
}
}
+static int binder_translate_binder(struct flat_binder_object *fp,
+ struct binder_transaction *t,
+ struct binder_thread *thread)
+{
+ struct binder_node *node;
+ struct binder_ref *ref;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ node = binder_get_node(proc, fp->binder);
+ if (!node) {
+ s8 priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
+ int sched_policy =
+ (fp->flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >>
+ FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
+ node = binder_new_node(proc, fp->binder, fp->cookie);
+ if (!node)
+ return -ENOMEM;
+
+ binder_proc_lock(node->proc, __LINE__);
+ node->sched_policy = sched_policy;
+ node->min_priority = to_kernel_prio(sched_policy, priority);
+ node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
+ binder_proc_unlock(node->proc, __LINE__);
+ }
+ if (fp->cookie != node->cookie) {
+ binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
+ proc->pid, thread->pid, (u64)fp->binder,
+ node->debug_id, (u64)fp->cookie,
+ (u64)node->cookie);
+ binder_put_node(node);
+ return -EINVAL;
+ }
+ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
+ binder_put_node(node);
+ return -EPERM;
+ }
+
+ ref = binder_get_ref_for_node(target_proc, node, &thread->todo);
+ if (!ref) {
+ binder_put_node(node);
+ return -EINVAL;
+ }
+
+ if (fp->hdr.type == BINDER_TYPE_BINDER)
+ fp->hdr.type = BINDER_TYPE_HANDLE;
+ else
+ fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
+ fp->binder = 0;
+ fp->handle = ref->desc;
+ fp->cookie = 0;
+ binder_inc_ref(ref, fp->hdr.type == BINDER_TYPE_HANDLE, &thread->todo,
+ __LINE__);
+
+ trace_binder_transaction_node_to_ref(t, node, ref);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " node %d u%016llx -> ref %d desc %d\n",
+ node->debug_id, (u64)node->ptr,
+ ref->debug_id, ref->desc);
+ binder_put_ref(ref);
+ binder_put_node(node);
+ return 0;
+}
+
+static int binder_translate_handle(struct flat_binder_object *fp,
+ struct binder_transaction *t,
+ struct binder_thread *thread)
+{
+ struct binder_ref *ref;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ ref = binder_get_ref(proc, fp->handle,
+ fp->hdr.type == BINDER_TYPE_HANDLE);
+ if (!ref) {
+ binder_user_error("%d:%d got transaction with invalid handle, %d\n",
+ proc->pid, thread->pid, fp->handle);
+ return -EINVAL;
+ }
+ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
+ binder_put_ref(ref);
+ return -EPERM;
+ }
+
+ if (ref->node->proc == target_proc) {
+ if (fp->hdr.type == BINDER_TYPE_HANDLE)
+ fp->hdr.type = BINDER_TYPE_BINDER;
+ else
+ fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
+ fp->binder = ref->node->ptr;
+ fp->cookie = ref->node->cookie;
+ binder_inc_node(ref->node, fp->hdr.type == BINDER_TYPE_BINDER,
+ 0, NULL, __LINE__);
+ trace_binder_transaction_ref_to_node(t, ref);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " ref %d desc %d -> node %d u%016llx\n",
+ ref->debug_id, ref->desc, ref->node->debug_id,
+ (u64)ref->node->ptr);
+ } else {
+ struct binder_ref *new_ref;
+
+ new_ref = binder_get_ref_for_node(target_proc, ref->node, NULL);
+ if (!new_ref) {
+ binder_put_ref(ref);
+ return -EINVAL;
+ }
+
+ fp->binder = 0;
+ fp->handle = new_ref->desc;
+ fp->cookie = 0;
+ binder_inc_ref(new_ref, fp->hdr.type == BINDER_TYPE_HANDLE,
+ NULL, __LINE__);
+ trace_binder_transaction_ref_to_ref(t, ref, new_ref);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " ref %d desc %d -> ref %d desc %d (node %d)\n",
+ ref->debug_id, ref->desc, new_ref->debug_id,
+ new_ref->desc, ref->node->debug_id);
+ binder_put_ref(new_ref);
+ }
+ binder_put_ref(ref);
+ return 0;
+}
+
+static int binder_translate_fd(int fd,
+ struct binder_transaction *t,
+ struct binder_thread *thread,
+ struct binder_transaction *in_reply_to)
+{
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+ int target_fd;
+ struct file *file;
+ int ret;
+ bool target_allows_fd;
+
+ if (in_reply_to)
+ target_allows_fd = !!(in_reply_to->flags & TF_ACCEPT_FDS);
+ else
+ target_allows_fd = t->buffer->target_node->accept_fds;
+ if (!target_allows_fd) {
+ binder_user_error("%d:%d got %s with fd, %d, but target does not allow fds\n",
+ proc->pid, thread->pid,
+ in_reply_to ? "reply" : "transaction",
+ fd);
+ ret = -EPERM;
+ goto err_fd_not_accepted;
+ }
+
+ file = fget(fd);
+ if (!file) {
+ binder_user_error("%d:%d got transaction with invalid fd, %d\n",
+ proc->pid, thread->pid, fd);
+ ret = -EBADF;
+ goto err_fget;
+ }
+ ret = security_binder_transfer_file(proc->tsk, target_proc->tsk, file);
+ if (ret < 0) {
+ ret = -EPERM;
+ goto err_security;
+ }
+
+ target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
+ if (target_fd < 0) {
+ ret = -ENOMEM;
+ goto err_get_unused_fd;
+ }
+ task_fd_install(target_proc, target_fd, file);
+ trace_binder_transaction_fd(t, fd, target_fd);
+ binder_debug(BINDER_DEBUG_TRANSACTION, " fd %d -> %d\n",
+ fd, target_fd);
+
+ return target_fd;
+
+err_get_unused_fd:
+err_security:
+ fput(file);
+err_fget:
+err_fd_not_accepted:
+ return ret;
+}
+
+static int binder_translate_fd_array(struct binder_fd_array_object *fda,
+ struct binder_buffer_object *parent,
+ struct binder_transaction *t,
+ struct binder_thread *thread,
+ struct binder_transaction *in_reply_to)
+{
+ binder_size_t fdi, fd_buf_size, num_installed_fds;
+ int target_fd;
+ uintptr_t parent_buffer;
+ u32 *fd_array;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ fd_buf_size = sizeof(u32) * fda->num_fds;
+ if (fda->num_fds >= SIZE_MAX / sizeof(u32)) {
+ binder_user_error("%d:%d got transaction with invalid number of fds (%lld)\n",
+ proc->pid, thread->pid, (u64)fda->num_fds);
+ return -EINVAL;
+ }
+ if (fd_buf_size > parent->length ||
+ fda->parent_offset > parent->length - fd_buf_size) {
+ /* No space for all file descriptors here. */
+ binder_user_error("%d:%d not enough space to store %lld fds in buffer\n",
+ proc->pid, thread->pid, (u64)fda->num_fds);
+ return -EINVAL;
+ }
+ /*
+ * Since the parent was already fixed up, convert it
+ * back to the kernel address space to access it
+ */
+ parent_buffer = parent->buffer -
+ binder_alloc_get_user_buffer_offset(&target_proc->alloc);
+ fd_array = (u32 *)(parent_buffer + fda->parent_offset);
+ if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) {
+ binder_user_error("%d:%d parent offset not aligned correctly.\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+ for (fdi = 0; fdi < fda->num_fds; fdi++) {
+ target_fd = binder_translate_fd(fd_array[fdi], t, thread,
+ in_reply_to);
+ if (target_fd < 0)
+ goto err_translate_fd_failed;
+ fd_array[fdi] = target_fd;
+ }
+ return 0;
+
+err_translate_fd_failed:
+ /*
+ * Failed to allocate fd or security error, free fds
+ * installed so far.
+ */
+ num_installed_fds = fdi;
+ for (fdi = 0; fdi < num_installed_fds; fdi++)
+ task_close_fd(target_proc, fd_array[fdi]);
+ return target_fd;
+}
+
+static int binder_fixup_parent(struct binder_transaction *t,
+ struct binder_thread *thread,
+ struct binder_buffer_object *bp,
+ binder_size_t *off_start,
+ binder_size_t num_valid,
+ struct binder_buffer_object *last_fixup_obj,
+ binder_size_t last_fixup_min_off)
+{
+ struct binder_buffer_object *parent;
+ u8 *parent_buffer;
+ struct binder_buffer *b = t->buffer;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ if (!(bp->flags & BINDER_BUFFER_FLAG_HAS_PARENT))
+ return 0;
+
+ parent = binder_validate_ptr(b, bp->parent, off_start, num_valid);
+ if (!parent) {
+ binder_user_error("%d:%d got transaction with invalid parent offset or type\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+
+ if (!binder_validate_fixup(b, off_start,
+ parent, bp->parent_offset,
+ last_fixup_obj,
+ last_fixup_min_off)) {
+ binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+
+ if (parent->length < sizeof(binder_uintptr_t) ||
+ bp->parent_offset > parent->length - sizeof(binder_uintptr_t)) {
+ /* No space for a pointer here! */
+ binder_user_error("%d:%d got transaction with invalid parent offset\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+ parent_buffer = (u8 *)(parent->buffer -
+ binder_alloc_get_user_buffer_offset(
+ &target_proc->alloc));
+ *(binder_uintptr_t *)(parent_buffer + bp->parent_offset) = bp->buffer;
+
+ return 0;
+}
+/**
+ * binder_proc_transaction() - sends a transaction to a process and wakes it up
+ * @t: transaction to send
+ * @proc: process to send the transaction to
+ * @thread: thread in @proc to send the transaction to (may be NULL)
+ *
+ * This function queues a transaction to the specified process. In order
+ * to do proper priority inheritance, it will try to find a thread in the
+ * target process to handle the transaction, raise its priority, and
+ * then wake that thread up.
+ *
+ * If the @thread parameter is not NULL, the transaction is always queued
+ * to the waitlist of that specific thread.
+ *
+ * The caller must hold the proc lock when calling this function.
+ *
+ */
+static void binder_proc_transaction(struct binder_transaction *t,
+ struct binder_proc *proc,
+ struct binder_thread *thread)
+{
+ struct binder_worklist *target_list = NULL;
+ wait_queue_head_t *target_wait = NULL;
+ bool oneway = !!(t->flags & TF_ONE_WAY);
+
+ if (!thread) {
+ /*
+ * See if we can find a thread to take this, so we can
+ * do priority inheritance.
+ */
+ thread = binder_select_thread(proc);
+ }
+
+ if (thread) {
+ target_list = &thread->todo;
+ target_wait = &thread->wait;
+ binder_transaction_priority(thread->task, t,
+ t->buffer->target_node);
+ } else {
+ target_list = &proc->todo;
+ }
+
+ binder_enqueue_work(&t->work, target_list, __LINE__);
+
+ binder_wakeup_thread(proc, thread, !oneway /* sync */);
+}
+
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
- struct binder_transaction_data *tr, int reply)
+ struct binder_transaction_data *tr, int reply,
+ binder_size_t extra_buffers_size)
{
+ int ret;
struct binder_transaction *t;
struct binder_work *tcomplete;
- binder_size_t *offp, *off_end;
+ binder_size_t *offp, *off_end, *off_start;
binder_size_t off_min;
+ u8 *sg_bufp, *sg_buf_end;
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
- struct list_head *target_list;
- wait_queue_head_t *target_wait;
+ struct binder_ref *target_ref = NULL;
struct binder_transaction *in_reply_to = NULL;
struct binder_transaction_log_entry *e;
- uint32_t return_error;
+ uint32_t return_error = 0;
+ uint32_t return_error_param = 0;
+ uint32_t return_error_line = 0;
+ struct binder_buffer_object *last_fixup_obj = NULL;
+ binder_size_t last_fixup_min_off = 0;
+ struct binder_context *context = proc->context;
+ bool oneway;
+ struct binder_priority saved_priority;
e = binder_transaction_log_add(&binder_transaction_log);
e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
@@ -1409,16 +2072,19 @@
e->target_handle = tr->target.handle;
e->data_size = tr->data_size;
e->offsets_size = tr->offsets_size;
+ e->context_name = proc->context->name;
if (reply) {
+ binder_proc_lock(thread->proc, __LINE__);
in_reply_to = thread->transaction_stack;
if (in_reply_to == NULL) {
+ binder_proc_unlock(thread->proc, __LINE__);
binder_user_error("%d:%d got reply transaction with no transaction stack\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_empty_call_stack;
}
- binder_set_nice(in_reply_to->saved_priority);
if (in_reply_to->to_thread != thread) {
binder_user_error("%d:%d got reply transaction with bad transaction stack, transaction %d has target %d:%d\n",
proc->pid, thread->pid, in_reply_to->debug_id,
@@ -1426,57 +2092,70 @@
in_reply_to->to_proc->pid : 0,
in_reply_to->to_thread ?
in_reply_to->to_thread->pid : 0);
+ binder_proc_unlock(thread->proc, __LINE__);
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
in_reply_to = NULL;
goto err_bad_call_stack;
}
thread->transaction_stack = in_reply_to->to_parent;
+ binder_proc_unlock(thread->proc, __LINE__);
+ saved_priority = in_reply_to->saved_priority;
target_thread = in_reply_to->from;
if (target_thread == NULL) {
return_error = BR_DEAD_REPLY;
+ return_error_line = __LINE__;
goto err_dead_binder;
}
+ binder_proc_lock(target_thread->proc, __LINE__);
if (target_thread->transaction_stack != in_reply_to) {
binder_user_error("%d:%d got reply transaction with bad target transaction stack %d, expected %d\n",
proc->pid, thread->pid,
target_thread->transaction_stack ?
target_thread->transaction_stack->debug_id : 0,
in_reply_to->debug_id);
+ binder_proc_unlock(target_thread->proc, __LINE__);
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
in_reply_to = NULL;
target_thread = NULL;
goto err_dead_binder;
}
+ binder_proc_unlock(target_thread->proc, __LINE__);
target_proc = target_thread->proc;
} else {
if (tr->target.handle) {
- struct binder_ref *ref;
-
- ref = binder_get_ref(proc, tr->target.handle, true);
- if (ref == NULL) {
+ target_ref = binder_get_ref(proc, tr->target.handle,
+ true);
+ if (target_ref == NULL) {
binder_user_error("%d:%d got transaction to invalid handle\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_invalid_target_handle;
}
- target_node = ref->node;
+ target_node = target_ref->node;
} else {
- target_node = binder_context_mgr_node;
+ target_node = context->binder_context_mgr_node;
if (target_node == NULL) {
return_error = BR_DEAD_REPLY;
+ return_error_line = __LINE__;
goto err_no_context_mgr_node;
}
}
e->to_node = target_node->debug_id;
target_proc = target_node->proc;
- if (target_proc == NULL) {
+ if (target_node->is_zombie) {
return_error = BR_DEAD_REPLY;
+ return_error_line = __LINE__;
goto err_dead_binder;
}
if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) {
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_invalid_target_handle;
}
+ binder_proc_lock(thread->proc, __LINE__);
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;
@@ -1487,7 +2166,9 @@
tmp->to_proc ? tmp->to_proc->pid : 0,
tmp->to_thread ?
tmp->to_thread->pid : 0);
+ binder_proc_unlock(thread->proc, __LINE__);
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_bad_call_stack;
}
while (tmp) {
@@ -1496,28 +2177,26 @@
tmp = tmp->from_parent;
}
}
+ binder_proc_unlock(thread->proc, __LINE__);
}
if (target_thread) {
e->to_thread = target_thread->pid;
- target_list = &target_thread->todo;
- target_wait = &target_thread->wait;
- } else {
- target_list = &target_proc->todo;
- target_wait = &target_proc->wait;
}
e->to_proc = target_proc->pid;
/* TODO: reuse incoming transaction for reply */
- t = kzalloc_preempt_disabled(sizeof(*t));
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) {
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_alloc_t_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION);
- tcomplete = kzalloc_preempt_disabled(sizeof(*tcomplete));
+ tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
if (tcomplete == NULL) {
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_alloc_tcomplete_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
@@ -1527,20 +2206,22 @@
if (reply)
binder_debug(BINDER_DEBUG_TRANSACTION,
- "%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld\n",
+ "%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_thread->pid,
(u64)tr->data.ptr.buffer,
(u64)tr->data.ptr.offsets,
- (u64)tr->data_size, (u64)tr->offsets_size);
+ (u64)tr->data_size, (u64)tr->offsets_size,
+ (u64)extra_buffers_size);
else
binder_debug(BINDER_DEBUG_TRANSACTION,
- "%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld\n",
+ "%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_node->debug_id,
(u64)tr->data.ptr.buffer,
(u64)tr->data.ptr.offsets,
- (u64)tr->data_size, (u64)tr->offsets_size);
+ (u64)tr->data_size, (u64)tr->offsets_size,
+ (u64)extra_buffers_size);
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
@@ -1551,14 +2232,25 @@
t->to_thread = target_thread;
t->code = tr->code;
t->flags = tr->flags;
- t->priority = task_nice(current);
+ if (!(t->flags & TF_ONE_WAY)) {
+ t->priority.sched_policy = current->policy;
+ t->priority.prio = current->normal_prio;
+ } else {
+ /* Oneway transactions run at default priority of the target */
+ t->priority = target_proc->default_priority;
+ }
trace_binder_transaction(reply, t, target_node);
- t->buffer = binder_alloc_buf(target_proc, tr->data_size,
- tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
- if (t->buffer == NULL) {
- return_error = BR_FAILED_REPLY;
+ t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
+ tr->offsets_size, extra_buffers_size,
+ !reply && (t->flags & TF_ONE_WAY));
+ if (IS_ERR(t->buffer)) {
+ return_error = !(target_proc->tsk->flags & PF_EXITING) ?
+ BR_FAILED_REPLY : BR_DEAD_REPLY;
+ return_error_param = PTR_ERR(t->buffer);
+ return_error_line = __LINE__;
+ t->buffer = NULL;
goto err_binder_alloc_buf_failed;
}
t->buffer->allow_user_free = 0;
@@ -1566,245 +2258,250 @@
t->buffer->transaction = t;
t->buffer->target_node = target_node;
trace_binder_transaction_alloc_buf(t->buffer);
- if (target_node)
- binder_inc_node(target_node, 1, 0, NULL);
+ if (target_node) {
+ binder_inc_node(target_node, 1, 0, NULL, __LINE__);
+ if (target_ref)
+ binder_put_ref(target_ref);
+ target_ref = NULL;
+ }
- offp = (binder_size_t *)(t->buffer->data +
- ALIGN(tr->data_size, sizeof(void *)));
+ off_start = (binder_size_t *)(t->buffer->data +
+ ALIGN(tr->data_size, sizeof(void *)));
+ offp = off_start;
- if (copy_from_user_preempt_disabled(t->buffer->data, (const void __user *)(uintptr_t)
+ if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
binder_user_error("%d:%d got transaction with invalid data ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_copy_data_failed;
}
- if (copy_from_user_preempt_disabled(offp, (const void __user *)(uintptr_t)
+ if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_copy_data_failed;
}
if (!IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))) {
binder_user_error("%d:%d got transaction with invalid offsets size, %lld\n",
proc->pid, thread->pid, (u64)tr->offsets_size);
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_bad_offset;
}
- off_end = (void *)offp + tr->offsets_size;
+ if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) {
+ binder_user_error("%d:%d got transaction with unaligned buffers size, %lld\n",
+ proc->pid, thread->pid,
+ extra_buffers_size);
+ return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
+ goto err_bad_offset;
+ }
+ off_end = (void *)off_start + tr->offsets_size;
+ sg_bufp = (u8 *)(PTR_ALIGN(off_end, sizeof(void *)));
+ sg_buf_end = sg_bufp + extra_buffers_size;
off_min = 0;
for (; offp < off_end; offp++) {
- struct flat_binder_object *fp;
+ struct binder_object_header *hdr;
+ size_t object_size = binder_validate_object(t->buffer, *offp);
- if (*offp > t->buffer->data_size - sizeof(*fp) ||
- *offp < off_min ||
- t->buffer->data_size < sizeof(*fp) ||
- !IS_ALIGNED(*offp, sizeof(u32))) {
- binder_user_error("%d:%d got transaction with invalid offset, %lld (min %lld, max %lld)\n",
+ if (object_size == 0 || *offp < off_min) {
+ binder_user_error("%d:%d got transaction with invalid offset (%lld, min %lld max %lld) or object.\n",
proc->pid, thread->pid, (u64)*offp,
(u64)off_min,
- (u64)(t->buffer->data_size -
- sizeof(*fp)));
+ (u64)t->buffer->data_size);
return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
goto err_bad_offset;
}
- fp = (struct flat_binder_object *)(t->buffer->data + *offp);
- off_min = *offp + sizeof(struct flat_binder_object);
- switch (fp->type) {
+
+ hdr = (struct binder_object_header *)(t->buffer->data + *offp);
+ off_min = *offp + object_size;
+ switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
- struct binder_ref *ref;
- struct binder_node *node = binder_get_node(proc, fp->binder);
+ struct flat_binder_object *fp;
- if (node == NULL) {
- node = binder_new_node(proc, fp->binder, fp->cookie);
- if (node == NULL) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_new_node_failed;
- }
- node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
- node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
- }
- if (fp->cookie != node->cookie) {
- binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
- proc->pid, thread->pid,
- (u64)fp->binder, node->debug_id,
- (u64)fp->cookie, (u64)node->cookie);
+ fp = to_flat_binder_object(hdr);
+ ret = binder_translate_binder(fp, t, thread);
+ if (ret < 0) {
return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
+ return_error_param = ret;
+ return_error_line = __LINE__;
+ goto err_translate_failed;
}
- if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
- }
- ref = binder_get_ref_for_node(target_proc, node);
- if (ref == NULL) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
- }
- if (fp->type == BINDER_TYPE_BINDER)
- fp->type = BINDER_TYPE_HANDLE;
- else
- fp->type = BINDER_TYPE_WEAK_HANDLE;
- fp->binder = 0;
- fp->handle = ref->desc;
- fp->cookie = 0;
- binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
- &thread->todo);
-
- trace_binder_transaction_node_to_ref(t, node, ref);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " node %d u%016llx -> ref %d desc %d\n",
- node->debug_id, (u64)node->ptr,
- ref->debug_id, ref->desc);
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
- struct binder_ref *ref = binder_get_ref(proc, fp->handle,
- fp->type == BINDER_TYPE_HANDLE);
+ struct flat_binder_object *fp;
- if (ref == NULL) {
- binder_user_error("%d:%d got transaction with invalid handle, %d\n",
- proc->pid,
- thread->pid, fp->handle);
+ fp = to_flat_binder_object(hdr);
+ ret = binder_translate_handle(fp, t, thread);
+ if (ret < 0) {
return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_failed;
- }
- if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_failed;
- }
- if (ref->node->proc == target_proc) {
- if (fp->type == BINDER_TYPE_HANDLE)
- fp->type = BINDER_TYPE_BINDER;
- else
- fp->type = BINDER_TYPE_WEAK_BINDER;
- fp->binder = ref->node->ptr;
- fp->cookie = ref->node->cookie;
- binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
- trace_binder_transaction_ref_to_node(t, ref);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " ref %d desc %d -> node %d u%016llx\n",
- ref->debug_id, ref->desc, ref->node->debug_id,
- (u64)ref->node->ptr);
- } else {
- struct binder_ref *new_ref;
-
- new_ref = binder_get_ref_for_node(target_proc, ref->node);
- if (new_ref == NULL) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
- }
- fp->binder = 0;
- fp->handle = new_ref->desc;
- fp->cookie = 0;
- binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
- trace_binder_transaction_ref_to_ref(t, ref,
- new_ref);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " ref %d desc %d -> ref %d desc %d (node %d)\n",
- ref->debug_id, ref->desc, new_ref->debug_id,
- new_ref->desc, ref->node->debug_id);
+ return_error_param = ret;
+ return_error_line = __LINE__;
+ goto err_translate_failed;
}
} break;
case BINDER_TYPE_FD: {
- int target_fd;
- struct file *file;
+ struct binder_fd_object *fp = to_binder_fd_object(hdr);
+ int target_fd = binder_translate_fd(fp->fd, t, thread,
+ in_reply_to);
- if (reply) {
- if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {
- binder_user_error("%d:%d got reply with fd, %d, but target does not allow fds\n",
- proc->pid, thread->pid, fp->handle);
- return_error = BR_FAILED_REPLY;
- goto err_fd_not_allowed;
- }
- } else if (!target_node->accept_fds) {
- binder_user_error("%d:%d got transaction with fd, %d, but target does not allow fds\n",
- proc->pid, thread->pid, fp->handle);
- return_error = BR_FAILED_REPLY;
- goto err_fd_not_allowed;
- }
-
- file = fget(fp->handle);
- if (file == NULL) {
- binder_user_error("%d:%d got transaction with invalid fd, %d\n",
- proc->pid, thread->pid, fp->handle);
- return_error = BR_FAILED_REPLY;
- goto err_fget_failed;
- }
- if (security_binder_transfer_file(proc->tsk, target_proc->tsk, file) < 0) {
- fput(file);
- return_error = BR_FAILED_REPLY;
- goto err_get_unused_fd_failed;
- }
- target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
if (target_fd < 0) {
- fput(file);
return_error = BR_FAILED_REPLY;
- goto err_get_unused_fd_failed;
+ return_error_param = target_fd;
+ return_error_line = __LINE__;
+ goto err_translate_failed;
}
- task_fd_install(target_proc, target_fd, file);
- trace_binder_transaction_fd(t, fp->handle, target_fd);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " fd %d -> %d\n", fp->handle, target_fd);
- /* TODO: fput? */
- fp->binder = 0;
- fp->handle = target_fd;
+ fp->pad_binder = 0;
+ fp->fd = target_fd;
} break;
+ case BINDER_TYPE_FDA: {
+ struct binder_fd_array_object *fda =
+ to_binder_fd_array_object(hdr);
+ struct binder_buffer_object *parent =
+ binder_validate_ptr(t->buffer, fda->parent,
+ off_start,
+ offp - off_start);
+ if (!parent) {
+ binder_user_error("%d:%d got transaction with invalid parent offset or type\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
+ goto err_bad_parent;
+ }
+ if (!binder_validate_fixup(t->buffer, off_start,
+ parent, fda->parent_offset,
+ last_fixup_obj,
+ last_fixup_min_off)) {
+ binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
+ goto err_bad_parent;
+ }
+ ret = binder_translate_fd_array(fda, parent, t, thread,
+ in_reply_to);
+ if (ret < 0) {
+ return_error = BR_FAILED_REPLY;
+ return_error_param = ret;
+ return_error_line = __LINE__;
+ goto err_translate_failed;
+ }
+ last_fixup_obj = parent;
+ last_fixup_min_off =
+ fda->parent_offset + sizeof(u32) * fda->num_fds;
+ } break;
+ case BINDER_TYPE_PTR: {
+ struct binder_buffer_object *bp =
+ to_binder_buffer_object(hdr);
+ size_t buf_left = sg_buf_end - sg_bufp;
+ if (bp->length > buf_left) {
+ binder_user_error("%d:%d got transaction with too large buffer\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
+ goto err_bad_offset;
+ }
+ if (copy_from_user(sg_bufp,
+ (const void __user *)(uintptr_t)
+ bp->buffer, bp->length)) {
+ binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ return_error_line = __LINE__;
+ goto err_copy_data_failed;
+ }
+ /* Fixup buffer pointer to target proc address space */
+ bp->buffer = (uintptr_t)sg_bufp +
+ binder_alloc_get_user_buffer_offset(
+ &target_proc->alloc);
+ sg_bufp += ALIGN(bp->length, sizeof(u64));
+
+ ret = binder_fixup_parent(t, thread, bp, off_start,
+ offp - off_start,
+ last_fixup_obj,
+ last_fixup_min_off);
+ if (ret < 0) {
+ return_error = BR_FAILED_REPLY;
+ return_error_param = ret;
+ return_error_line = __LINE__;
+ goto err_translate_failed;
+ }
+ last_fixup_obj = bp;
+ last_fixup_min_off = 0;
+ } break;
default:
binder_user_error("%d:%d got transaction with invalid object type, %x\n",
- proc->pid, thread->pid, fp->type);
+ proc->pid, thread->pid, hdr->type);
return_error = BR_FAILED_REPLY;
+ return_error_param = hdr->type;
+ return_error_line = __LINE__;
goto err_bad_object_type;
}
}
+
+ t->work.type = BINDER_WORK_TRANSACTION;
+ tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
+ binder_enqueue_work(tcomplete, &thread->todo, __LINE__);
+ oneway = !!(t->flags & TF_ONE_WAY);
+
if (reply) {
BUG_ON(t->buffer->async_transaction != 0);
+ binder_proc_lock(target_thread->proc, __LINE__);
binder_pop_transaction(target_thread, in_reply_to);
+ binder_enqueue_work(&t->work, &target_thread->todo, __LINE__);
+ binder_proc_unlock(target_thread->proc, __LINE__);
+ binder_free_transaction(in_reply_to);
+ wake_up_interruptible_sync(&target_thread->wait);
+ binder_set_priority(current, saved_priority, true /*restore*/);
} else if (!(t->flags & TF_ONE_WAY)) {
BUG_ON(t->buffer->async_transaction != 0);
+ binder_proc_lock(thread->proc, __LINE__);
t->need_reply = 1;
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
+ binder_proc_unlock(thread->proc, __LINE__);
+ binder_proc_lock(target_proc, __LINE__);
+ binder_proc_transaction(t, target_proc, target_thread);
+ binder_proc_unlock(target_proc, __LINE__);
} else {
BUG_ON(target_node == NULL);
BUG_ON(t->buffer->async_transaction != 1);
+
+ binder_proc_lock(target_node->proc, __LINE__);
+ /*
+ * Test/set of has_async_transaction
+ * must be atomic with enqueue on
+ * async_todo
+ */
if (target_node->has_async_transaction) {
- target_list = &target_node->async_todo;
- target_wait = NULL;
- } else
+ binder_enqueue_work(&t->work, &target_node->async_todo,
+ __LINE__);
+ } else {
target_node->has_async_transaction = 1;
- }
- t->work.type = BINDER_WORK_TRANSACTION;
- list_add_tail(&t->work.entry, target_list);
- tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
- list_add_tail(&tcomplete->entry, &thread->todo);
- if (target_wait) {
- if (reply || !(t->flags & TF_ONE_WAY)) {
- wake_up_interruptible_sync(target_wait);
+ binder_proc_transaction(t, target_proc, NULL);
}
- else {
- wake_up_interruptible(target_wait);
- }
+ binder_proc_unlock(target_node->proc, __LINE__);
}
return;
-err_get_unused_fd_failed:
-err_fget_failed:
-err_fd_not_allowed:
-err_binder_get_ref_for_node_failed:
-err_binder_get_ref_failed:
-err_binder_new_node_failed:
+err_translate_failed:
err_bad_object_type:
err_bad_offset:
+err_bad_parent:
err_copy_data_failed:
trace_binder_transaction_failed_buffer_release(t->buffer);
binder_transaction_buffer_release(target_proc, t->buffer, offp);
t->buffer->transaction = NULL;
- binder_free_buf(target_proc, t->buffer);
+ binder_alloc_free_buf(&target_proc->alloc, t->buffer);
err_binder_alloc_buf_failed:
kfree(tcomplete);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
@@ -1817,24 +2514,40 @@
err_dead_binder:
err_invalid_target_handle:
err_no_context_mgr_node:
+ if (target_ref)
+ binder_put_ref(target_ref);
+
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
- "%d:%d transaction failed %d, size %lld-%lld\n",
- proc->pid, thread->pid, return_error,
- (u64)tr->data_size, (u64)tr->offsets_size);
+ "%d:%d transaction failed %d/%d, size %lld-%lld line %d\n",
+ proc->pid, thread->pid, return_error, return_error_param,
+ (u64)tr->data_size, (u64)tr->offsets_size,
+ return_error_line);
{
struct binder_transaction_log_entry *fe;
+ e->return_error = return_error;
+ e->return_error_param = return_error_param;
+ e->return_error_line = return_error_line;
fe = binder_transaction_log_add(&binder_transaction_log_failed);
*fe = *e;
}
- BUG_ON(thread->return_error != BR_OK);
+ binder_proc_lock(thread->proc, __LINE__);
+ BUG_ON(thread->return_error.cmd != BR_OK);
if (in_reply_to) {
- thread->return_error = BR_TRANSACTION_COMPLETE;
+ thread->return_error.cmd = BR_TRANSACTION_COMPLETE;
+ binder_enqueue_work(&thread->return_error.work,
+ &thread->todo, __LINE__);
+ binder_proc_unlock(thread->proc, __LINE__);
+ binder_set_priority(current, saved_priority, true /*restore*/);
binder_send_failed_reply(in_reply_to, return_error);
- } else
- thread->return_error = return_error;
+ } else {
+ thread->return_error.cmd = return_error;
+ binder_enqueue_work(&thread->return_error.work,
+ &thread->todo, __LINE__);
+ binder_proc_unlock(thread->proc, __LINE__);
+ }
}
static int binder_thread_write(struct binder_proc *proc,
@@ -1843,19 +2556,20 @@
binder_size_t *consumed)
{
uint32_t cmd;
+ struct binder_context *context = proc->context;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
- while (ptr < end && thread->return_error == BR_OK) {
- if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
+ while (ptr < end && thread->return_error.cmd == BR_OK) {
+ if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
trace_binder_command(cmd);
if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
- binder_stats.bc[_IOC_NR(cmd)]++;
- proc->stats.bc[_IOC_NR(cmd)]++;
- thread->stats.bc[_IOC_NR(cmd)]++;
+ atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]);
+ atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]);
+ atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]);
}
switch (cmd) {
case BC_INCREFS:
@@ -1865,15 +2579,18 @@
uint32_t target;
struct binder_ref *ref;
const char *debug_string;
+ struct binder_node *ctx_mgr_node =
+ context->binder_context_mgr_node;
- if (get_user_preempt_disabled(target, (uint32_t __user *)ptr))
+ if (get_user(target, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
- if (target == 0 && binder_context_mgr_node &&
+ if (target == 0 && ctx_mgr_node &&
(cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
ref = binder_get_ref_for_node(proc,
- binder_context_mgr_node);
- if (ref->desc != target) {
+ ctx_mgr_node,
+ NULL);
+ if (ref && ref->desc != target) {
binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n",
proc->pid, thread->pid,
ref->desc);
@@ -1890,26 +2607,30 @@
switch (cmd) {
case BC_INCREFS:
debug_string = "IncRefs";
- binder_inc_ref(ref, 0, NULL);
+ binder_inc_ref(ref, 0, NULL, __LINE__);
break;
case BC_ACQUIRE:
debug_string = "Acquire";
- binder_inc_ref(ref, 1, NULL);
+ binder_inc_ref(ref, 1, NULL, __LINE__);
break;
case BC_RELEASE:
debug_string = "Release";
- binder_dec_ref(ref, 1);
+ binder_dec_ref(ref, 1, __LINE__);
break;
case BC_DECREFS:
default:
debug_string = "DecRefs";
- binder_dec_ref(ref, 0);
+ binder_dec_ref(ref, 0, __LINE__);
break;
}
binder_debug(BINDER_DEBUG_USER_REFS,
"%d:%d %s ref %d desc %d s %d w %d for node %d\n",
- proc->pid, thread->pid, debug_string, ref->debug_id,
- ref->desc, ref->strong, ref->weak, ref->node->debug_id);
+ proc->pid, thread->pid, debug_string,
+ ref->debug_id, ref->desc,
+ atomic_read(&ref->strong),
+ atomic_read(&ref->weak),
+ ref->node->debug_id);
+ binder_put_ref(ref);
break;
}
case BC_INCREFS_DONE:
@@ -1918,10 +2639,10 @@
binder_uintptr_t cookie;
struct binder_node *node;
- if (get_user_preempt_disabled(node_ptr, (binder_uintptr_t __user *)ptr))
+ if (get_user(node_ptr, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
- if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr))
+ if (get_user(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
node = binder_get_node(proc, node_ptr);
@@ -1941,13 +2662,17 @@
"BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
(u64)node_ptr, node->debug_id,
(u64)cookie, (u64)node->cookie);
+ binder_put_node(node);
break;
}
+ binder_proc_lock(node->proc, __LINE__);
if (cmd == BC_ACQUIRE_DONE) {
if (node->pending_strong_ref == 0) {
binder_user_error("%d:%d BC_ACQUIRE_DONE node %d has no pending acquire request\n",
proc->pid, thread->pid,
node->debug_id);
+ binder_proc_unlock(node->proc, __LINE__);
+ binder_put_node(node);
break;
}
node->pending_strong_ref = 0;
@@ -1956,16 +2681,21 @@
binder_user_error("%d:%d BC_INCREFS_DONE node %d has no pending increfs request\n",
proc->pid, thread->pid,
node->debug_id);
+ binder_proc_unlock(node->proc, __LINE__);
+ binder_put_node(node);
break;
}
node->pending_weak_ref = 0;
}
- binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0);
+ binder_proc_unlock(node->proc, __LINE__);
+ binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0,
+ __LINE__);
binder_debug(BINDER_DEBUG_USER_REFS,
"%d:%d %s node %d ls %d lw %d\n",
proc->pid, thread->pid,
cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
node->debug_id, node->local_strong_refs, node->local_weak_refs);
+ binder_put_node(node);
break;
}
case BC_ATTEMPT_ACQUIRE:
@@ -1979,11 +2709,12 @@
binder_uintptr_t data_ptr;
struct binder_buffer *buffer;
- if (get_user_preempt_disabled(data_ptr, (binder_uintptr_t __user *)ptr))
+ if (get_user(data_ptr, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
- buffer = binder_buffer_lookup(proc, data_ptr);
+ buffer = binder_alloc_prepare_to_free(&proc->alloc,
+ data_ptr);
if (buffer == NULL) {
binder_user_error("%d:%d BC_FREE_BUFFER u%016llx no match\n",
proc->pid, thread->pid, (u64)data_ptr);
@@ -2005,26 +2736,53 @@
buffer->transaction = NULL;
}
if (buffer->async_transaction && buffer->target_node) {
- BUG_ON(!buffer->target_node->has_async_transaction);
- if (list_empty(&buffer->target_node->async_todo))
- buffer->target_node->has_async_transaction = 0;
- else
- list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
+ struct binder_node *buf_node;
+
+ buf_node = buffer->target_node;
+ binder_proc_lock(buf_node->proc, __LINE__);
+ BUG_ON(!buf_node->has_async_transaction);
+ if (binder_worklist_empty(
+ &buf_node->async_todo))
+ buf_node->has_async_transaction = 0;
+ else {
+ struct binder_work *w;
+
+ w = container_of(
+ buf_node->async_todo.list.next,
+ struct binder_work, entry);
+ binder_dequeue_work(w, __LINE__);
+ binder_enqueue_work(w, &thread->todo,
+ __LINE__);
+ }
+ binder_proc_unlock(buffer->target_node->proc,
+ __LINE__);
}
trace_binder_transaction_buffer_release(buffer);
binder_transaction_buffer_release(proc, buffer, NULL);
- binder_free_buf(proc, buffer);
+ binder_alloc_free_buf(&proc->alloc, buffer);
break;
}
+ case BC_TRANSACTION_SG:
+ case BC_REPLY_SG: {
+ struct binder_transaction_data_sg tr;
+
+ if (copy_from_user(&tr, ptr, sizeof(tr)))
+ return -EFAULT;
+ ptr += sizeof(tr);
+ binder_transaction(proc, thread, &tr.transaction_data,
+ cmd == BC_REPLY_SG, tr.buffers_size);
+ break;
+ }
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
- if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr)))
+ if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
- binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
+ binder_transaction(proc, thread, &tr,
+ cmd == BC_REPLY, 0);
break;
}
@@ -2032,6 +2790,7 @@
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BC_REGISTER_LOOPER\n",
proc->pid, thread->pid);
+ binder_proc_lock(proc, __LINE__);
if (thread->looper & BINDER_LOOPER_STATE_ENTERED) {
thread->looper |= BINDER_LOOPER_STATE_INVALID;
binder_user_error("%d:%d ERROR: BC_REGISTER_LOOPER called after BC_ENTER_LOOPER\n",
@@ -2045,6 +2804,7 @@
proc->requested_threads_started++;
}
thread->looper |= BINDER_LOOPER_STATE_REGISTERED;
+ binder_proc_unlock(proc, __LINE__);
break;
case BC_ENTER_LOOPER:
binder_debug(BINDER_DEBUG_THREADS,
@@ -2071,10 +2831,10 @@
struct binder_ref *ref;
struct binder_ref_death *death;
- if (get_user_preempt_disabled(target, (uint32_t __user *)ptr))
+ if (get_user(target, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
- if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr))
+ if (get_user(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
ref = binder_get_ref(proc, target, false);
@@ -2095,39 +2855,66 @@
"BC_REQUEST_DEATH_NOTIFICATION" :
"BC_CLEAR_DEATH_NOTIFICATION",
(u64)cookie, ref->debug_id, ref->desc,
- ref->strong, ref->weak, ref->node->debug_id);
+ atomic_read(&ref->strong),
+ atomic_read(&ref->weak),
+ ref->node->debug_id);
if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
- if (ref->death) {
- binder_user_error("%d:%d BC_REQUEST_DEATH_NOTIFICATION death notification already set\n",
- proc->pid, thread->pid);
- break;
- }
- death = kzalloc_preempt_disabled(sizeof(*death));
+ death = kzalloc(sizeof(*death), GFP_KERNEL);
if (death == NULL) {
- thread->return_error = BR_ERROR;
+ binder_proc_lock(thread->proc,
+ __LINE__);
+ WARN_ON(thread->return_error.cmd != BR_OK);
+ thread->return_error.cmd = BR_ERROR;
+ binder_enqueue_work(
+ &thread->return_error.work,
+ &thread->todo, __LINE__);
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
"%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n",
proc->pid, thread->pid);
+ binder_proc_unlock(thread->proc,
+ __LINE__);
+ binder_put_ref(ref);
break;
}
binder_stats_created(BINDER_STAT_DEATH);
INIT_LIST_HEAD(&death->work.entry);
death->cookie = cookie;
+ binder_proc_lock(proc, __LINE__);
+ if (ref->death) {
+ binder_user_error("%d:%d BC_REQUEST_DEATH_NOTIFICATION death notification already set\n",
+ proc->pid, thread->pid);
+ binder_proc_unlock(proc, __LINE__);
+ kfree(death);
+ binder_put_ref(ref);
+ break;
+ }
ref->death = death;
- if (ref->node->proc == NULL) {
+ if (ref->node_is_zombie) {
ref->death->work.type = BINDER_WORK_DEAD_BINDER;
- if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
- list_add_tail(&ref->death->work.entry, &thread->todo);
- } else {
- list_add_tail(&ref->death->work.entry, &proc->todo);
- wake_up_interruptible(&proc->wait);
+ if (thread->looper &
+ (BINDER_LOOPER_STATE_REGISTERED |
+ BINDER_LOOPER_STATE_ENTERED))
+ binder_enqueue_work(
+ &ref->death->work,
+ &thread->todo,
+ __LINE__);
+ else {
+ binder_enqueue_work(
+ &ref->death->work,
+ &proc->todo,
+ __LINE__);
+ binder_wakeup_proc(proc);
}
}
+ binder_proc_unlock(proc, __LINE__);
} else {
+ binder_proc_lock(proc, __LINE__);
if (ref->death == NULL) {
binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification not active\n",
proc->pid, thread->pid);
+ binder_proc_unlock(proc, __LINE__);
+ binder_put_ref(ref);
break;
}
death = ref->death;
@@ -2136,39 +2923,58 @@
proc->pid, thread->pid,
(u64)death->cookie,
(u64)cookie);
+ binder_proc_unlock(proc, __LINE__);
+ binder_put_ref(ref);
break;
}
ref->death = NULL;
if (list_empty(&death->work.entry)) {
death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
- if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
- list_add_tail(&death->work.entry, &thread->todo);
- } else {
- list_add_tail(&death->work.entry, &proc->todo);
- wake_up_interruptible(&proc->wait);
+ if (thread->looper &
+ (BINDER_LOOPER_STATE_REGISTERED |
+ BINDER_LOOPER_STATE_ENTERED))
+ binder_enqueue_work(
+ &death->work,
+ &thread->todo,
+ __LINE__);
+ else {
+ binder_enqueue_work(
+ &death->work,
+ &proc->todo,
+ __LINE__);
+ binder_wakeup_proc(proc);
}
} else {
BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER);
death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;
}
+ binder_proc_unlock(proc, __LINE__);
}
+ binder_put_ref(ref);
} break;
case BC_DEAD_BINDER_DONE: {
struct binder_work *w;
binder_uintptr_t cookie;
struct binder_ref_death *death = NULL;
- if (get_user_preempt_disabled(cookie, (binder_uintptr_t __user *)ptr))
+
+ if (get_user(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(void *);
- list_for_each_entry(w, &proc->delivered_death, entry) {
- struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work);
+ spin_lock(&proc->delivered_death.lock);
+ list_for_each_entry(w, &proc->delivered_death.list,
+ entry) {
+ struct binder_ref_death *tmp_death =
+ container_of(w,
+ struct binder_ref_death,
+ work);
if (tmp_death->cookie == cookie) {
death = tmp_death;
break;
}
}
+ spin_unlock(&proc->delivered_death.lock);
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"%d:%d BC_DEAD_BINDER_DONE %016llx found %pK\n",
proc->pid, thread->pid, (u64)cookie,
@@ -2178,19 +2984,25 @@
proc->pid, thread->pid, (u64)cookie);
break;
}
-
- list_del_init(&death->work.entry);
+ binder_proc_lock(proc, __LINE__);
+ binder_dequeue_work(&death->work, __LINE__);
if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) {
death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
- if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
- list_add_tail(&death->work.entry, &thread->todo);
- } else {
- list_add_tail(&death->work.entry, &proc->todo);
- wake_up_interruptible(&proc->wait);
+ if (thread->looper &
+ (BINDER_LOOPER_STATE_REGISTERED |
+ BINDER_LOOPER_STATE_ENTERED))
+ binder_enqueue_work(&death->work,
+ &thread->todo,
+ __LINE__);
+ else {
+ binder_enqueue_work(&death->work,
+ &proc->todo,
+ __LINE__);
+ binder_wakeup_proc(proc);
}
}
- }
- break;
+ binder_proc_unlock(proc, __LINE__);
+ } break;
default:
pr_err("%d:%d unknown command %d\n",
@@ -2207,215 +3019,266 @@
{
trace_binder_return(cmd);
if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) {
- binder_stats.br[_IOC_NR(cmd)]++;
- proc->stats.br[_IOC_NR(cmd)]++;
- thread->stats.br[_IOC_NR(cmd)]++;
+ atomic_inc(&binder_stats.br[_IOC_NR(cmd)]);
+ atomic_inc(&proc->stats.br[_IOC_NR(cmd)]);
+ atomic_inc(&thread->stats.br[_IOC_NR(cmd)]);
}
}
-static int binder_has_proc_work(struct binder_proc *proc,
- struct binder_thread *thread)
+static int binder_wait_for_work(struct binder_thread *thread,
+ bool do_proc_work)
{
- return !list_empty(&proc->todo) ||
- (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
-}
+ DEFINE_WAIT(wait);
+ struct binder_proc *proc = thread->proc;
+ struct binder_thread *get_thread;
+ int ret = 0;
-static int binder_has_thread_work(struct binder_thread *thread)
-{
- return !list_empty(&thread->todo) || thread->return_error != BR_OK ||
- (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
+ binder_put_thread(thread);
+ freezer_do_not_count();
+ binder_proc_lock(proc, __LINE__);
+ for (;;) {
+ prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);
+ if (binder_has_work(thread, do_proc_work))
+ break;
+ if (do_proc_work)
+ list_add(&thread->waiting_thread_node,
+ &proc->waiting_threads);
+ binder_proc_unlock(proc, __LINE__);
+ schedule();
+ binder_proc_lock(proc, __LINE__);
+ list_del_init(&thread->waiting_thread_node);
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ }
+ finish_wait(&thread->wait, &wait);
+ binder_proc_unlock(proc, __LINE__);
+ freezer_count();
+ get_thread = binder_get_thread(proc);
+ BUG_ON(get_thread != thread);
+
+ return ret;
}
static int binder_thread_read(struct binder_proc *proc,
- struct binder_thread *thread,
+ struct binder_thread **threadp,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
+ struct binder_thread *thread = *threadp;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
-
+ struct binder_worklist *wlist = NULL;
int ret = 0;
- int wait_for_proc_work;
+ bool wait_for_proc_work;
if (*consumed == 0) {
- if (put_user_preempt_disabled(BR_NOOP, (uint32_t __user *)ptr))
+ if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
}
retry:
- wait_for_proc_work = thread->transaction_stack == NULL &&
- list_empty(&thread->todo);
+ binder_proc_lock(proc, __LINE__);
+ wait_for_proc_work = binder_available_for_proc_work(thread);
+ binder_proc_unlock(proc, __LINE__);
- if (thread->return_error != BR_OK && ptr < end) {
- if (thread->return_error2 != BR_OK) {
- if (put_user_preempt_disabled(thread->return_error2, (uint32_t __user *)ptr))
- return -EFAULT;
- ptr += sizeof(uint32_t);
- binder_stat_br(proc, thread, thread->return_error2);
- if (ptr == end)
- goto done;
- thread->return_error2 = BR_OK;
- }
- if (put_user_preempt_disabled(thread->return_error, (uint32_t __user *)ptr))
- return -EFAULT;
- ptr += sizeof(uint32_t);
- binder_stat_br(proc, thread, thread->return_error);
- thread->return_error = BR_OK;
- goto done;
- }
-
-
- thread->looper |= BINDER_LOOPER_STATE_WAITING;
if (wait_for_proc_work)
- proc->ready_threads++;
-
- binder_unlock(__func__);
+ atomic_inc(&proc->ready_threads);
trace_binder_wait_for_work(wait_for_proc_work,
!!thread->transaction_stack,
- !list_empty(&thread->todo));
+ !binder_worklist_empty(&thread->todo));
+
+ thread->looper |= BINDER_LOOPER_STATE_WAITING;
+
if (wait_for_proc_work) {
- if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
- BINDER_LOOPER_STATE_ENTERED))) {
- binder_user_error("%d:%d ERROR: Thread waiting for process work before calling BC_REGISTER_LOOPER or BC_ENTER_LOOPER (state %x)\n",
- proc->pid, thread->pid, thread->looper);
- wait_event_interruptible(binder_user_error_wait,
- binder_stop_on_user_error < 2);
- }
- binder_set_nice(proc->default_priority);
- if (non_block) {
- if (!binder_has_proc_work(proc, thread))
- ret = -EAGAIN;
- } else
- ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
- } else {
- if (non_block) {
- if (!binder_has_thread_work(thread))
- ret = -EAGAIN;
- } else
- ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
+ BUG_ON(!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
+ BINDER_LOOPER_STATE_ENTERED)));
+ binder_set_priority(current, proc->default_priority,
+ true /* restore */);
}
- binder_lock(__func__);
+ if (non_block) {
+ if (!binder_has_work(thread, wait_for_proc_work))
+ ret = -EAGAIN;
+ } else {
+ ret = binder_wait_for_work(thread, wait_for_proc_work);
+ }
if (wait_for_proc_work)
- proc->ready_threads--;
- thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
+ atomic_dec(&proc->ready_threads);
if (ret)
return ret;
+ thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
+
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
- struct binder_work *w;
struct binder_transaction *t = NULL;
+ struct binder_work *w = NULL;
+ wlist = NULL;
- if (!list_empty(&thread->todo)) {
- w = list_first_entry(&thread->todo, struct binder_work,
+ binder_proc_lock(thread->proc, __LINE__);
+ spin_lock(&thread->todo.lock);
+ if (!_binder_worklist_empty(&thread->todo)) {
+ w = list_first_entry(&thread->todo.list,
+ struct binder_work,
entry);
- } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
- w = list_first_entry(&proc->todo, struct binder_work,
- entry);
- } else {
- /* no data added */
- if (ptr - buffer == 4 &&
- !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
- goto retry;
+ wlist = &thread->todo;
+ binder_freeze_worklist(wlist);
+ }
+ spin_unlock(&thread->todo.lock);
+ if (!w) {
+ spin_lock(&proc->todo.lock);
+ if (!_binder_worklist_empty(&proc->todo) &&
+ wait_for_proc_work) {
+ w = list_first_entry(&proc->todo.list,
+ struct binder_work,
+ entry);
+ wlist = &proc->todo;
+ binder_freeze_worklist(wlist);
+ }
+ spin_unlock(&proc->todo.lock);
+ if (!w) {
+ binder_proc_unlock(thread->proc, __LINE__);
+ /* no data added */
+ if (ptr - buffer == 4 &&
+ !READ_ONCE(thread->looper_need_return))
+ goto retry;
+ break;
+ }
+ }
+ binder_proc_unlock(thread->proc, __LINE__);
+ if (end - ptr < sizeof(tr) + 4) {
+ if (wlist)
+ binder_unfreeze_worklist(wlist);
break;
}
- if (end - ptr < sizeof(tr) + 4)
- break;
-
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
t = container_of(w, struct binder_transaction, work);
} break;
+ case BINDER_WORK_RETURN_ERROR: {
+ struct binder_error *e = container_of(
+ w, struct binder_error, work);
+
+ WARN_ON(e->cmd == BR_OK);
+ if (put_user(e->cmd, (uint32_t __user *)ptr)) {
+ binder_unfreeze_worklist(wlist);
+ return -EFAULT;
+ }
+ e->cmd = BR_OK;
+ ptr += sizeof(uint32_t);
+
+ binder_stat_br(proc, thread, cmd);
+ binder_dequeue_work(w, __LINE__);
+ binder_unfreeze_worklist(wlist);
+ } break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
cmd = BR_TRANSACTION_COMPLETE;
- if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
+ if (put_user(cmd, (uint32_t __user *)ptr)) {
+ binder_unfreeze_worklist(wlist);
return -EFAULT;
+ }
ptr += sizeof(uint32_t);
binder_stat_br(proc, thread, cmd);
binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE,
"%d:%d BR_TRANSACTION_COMPLETE\n",
proc->pid, thread->pid);
-
- list_del(&w->entry);
+ binder_dequeue_work(w, __LINE__);
+ binder_unfreeze_worklist(wlist);
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
case BINDER_WORK_NODE: {
struct binder_node *node = container_of(w, struct binder_node, work);
- uint32_t cmd = BR_NOOP;
- const char *cmd_name;
- int strong = node->internal_strong_refs || node->local_strong_refs;
- int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong;
+ uint32_t cmd[2];
+ const char *cmd_name[2];
+ int cmd_count = 0;
+ int strong, weak;
+ struct binder_proc *proc = node->proc;
+ int i;
+
+ binder_proc_lock(proc, __LINE__);
+ strong = node->internal_strong_refs ||
+ node->local_strong_refs;
+ weak = !hlist_empty(&node->refs) ||
+ node->local_weak_refs || strong;
if (weak && !node->has_weak_ref) {
- cmd = BR_INCREFS;
- cmd_name = "BR_INCREFS";
+ cmd[cmd_count] = BR_INCREFS;
+ cmd_name[cmd_count++] = "BR_INCREFS";
node->has_weak_ref = 1;
node->pending_weak_ref = 1;
node->local_weak_refs++;
- } else if (strong && !node->has_strong_ref) {
- cmd = BR_ACQUIRE;
- cmd_name = "BR_ACQUIRE";
+ }
+ if (strong && !node->has_strong_ref) {
+ cmd[cmd_count] = BR_ACQUIRE;
+ cmd_name[cmd_count++] = "BR_ACQUIRE";
node->has_strong_ref = 1;
node->pending_strong_ref = 1;
node->local_strong_refs++;
- } else if (!strong && node->has_strong_ref) {
- cmd = BR_RELEASE;
- cmd_name = "BR_RELEASE";
+ }
+ if (!strong && node->has_strong_ref) {
+ cmd[cmd_count] = BR_RELEASE;
+ cmd_name[cmd_count++] = "BR_RELEASE";
node->has_strong_ref = 0;
- } else if (!weak && node->has_weak_ref) {
- cmd = BR_DECREFS;
- cmd_name = "BR_DECREFS";
+ }
+ if (!weak && node->has_weak_ref) {
+ cmd[cmd_count] = BR_DECREFS;
+ cmd_name[cmd_count++] = "BR_DECREFS";
node->has_weak_ref = 0;
}
- if (cmd != BR_NOOP) {
- if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
+ BUG_ON(cmd_count > 2);
+
+ if (!weak && !strong && !node->is_zombie) {
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "%d:%d node %d u%016llx c%016llx deleted\n",
+ proc->pid, thread->pid,
+ node->debug_id,
+ (u64)node->ptr,
+ (u64)node->cookie);
+ _binder_make_node_zombie(node);
+ } else {
+ binder_debug(BINDER_DEBUG_INTERNAL_REFS,
+ "%d:%d node %d u%016llx c%016llx state unchanged\n",
+ proc->pid, thread->pid,
+ node->debug_id,
+ (u64)node->ptr,
+ (u64)node->cookie);
+ }
+ binder_dequeue_work(w, __LINE__);
+ binder_unfreeze_worklist(wlist);
+ binder_proc_unlock(proc, __LINE__);
+
+ for (i = 0; i < cmd_count; i++) {
+ if (put_user(cmd[i], (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
- if (put_user_preempt_disabled(node->ptr, (binder_uintptr_t __user *)
- (binder_uintptr_t __user *)ptr))
- return -EFAULT;
- ptr += sizeof(binder_uintptr_t);
- if (put_user_preempt_disabled(node->cookie, (binder_uintptr_t __user *)
+
+ if (put_user(node->ptr,
(binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
- binder_stat_br(proc, thread, cmd);
+ if (put_user(node->cookie,
+ (binder_uintptr_t __user *)ptr))
+ return -EFAULT;
+ ptr += sizeof(binder_uintptr_t);
+
+ binder_stat_br(proc, thread, cmd[i]);
binder_debug(BINDER_DEBUG_USER_REFS,
"%d:%d %s %d u%016llx c%016llx\n",
- proc->pid, thread->pid, cmd_name,
- node->debug_id,
+ proc->pid, thread->pid,
+ cmd_name[i], node->debug_id,
(u64)node->ptr, (u64)node->cookie);
- } else {
- list_del_init(&w->entry);
- if (!weak && !strong) {
- binder_debug(BINDER_DEBUG_INTERNAL_REFS,
- "%d:%d node %d u%016llx c%016llx deleted\n",
- proc->pid, thread->pid,
- node->debug_id,
- (u64)node->ptr,
- (u64)node->cookie);
- rb_erase(&node->rb_node, &proc->nodes);
- kfree(node);
- binder_stats_deleted(BINDER_STAT_NODE);
- } else {
- binder_debug(BINDER_DEBUG_INTERNAL_REFS,
- "%d:%d node %d u%016llx c%016llx state unchanged\n",
- proc->pid, thread->pid,
- node->debug_id,
- (u64)node->ptr,
- (u64)node->cookie);
- }
}
} break;
case BINDER_WORK_DEAD_BINDER:
@@ -2423,19 +3286,15 @@
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
struct binder_ref_death *death;
uint32_t cmd;
+ binder_uintptr_t cookie;
+ binder_proc_lock(proc, __LINE__);
death = container_of(w, struct binder_ref_death, work);
if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
else
cmd = BR_DEAD_BINDER;
- if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
- return -EFAULT;
- ptr += sizeof(uint32_t);
- if (put_user_preempt_disabled(death->cookie, (binder_uintptr_t __user *) ptr))
- return -EFAULT;
- ptr += sizeof(binder_uintptr_t);
- binder_stat_br(proc, thread, cmd);
+ cookie = death->cookie;
binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
"%d:%d %s %016llx\n",
proc->pid, thread->pid,
@@ -2445,32 +3304,53 @@
(u64)death->cookie);
if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
- list_del(&w->entry);
+ binder_dequeue_work(w, __LINE__);
+ binder_proc_unlock(proc, __LINE__);
kfree(death);
binder_stats_deleted(BINDER_STAT_DEATH);
- } else
- list_move(&w->entry, &proc->delivered_death);
+ } else {
+ binder_dequeue_work(w, __LINE__);
+ binder_enqueue_work(w, &proc->delivered_death,
+ __LINE__);
+ binder_proc_unlock(proc, __LINE__);
+
+ }
+ binder_unfreeze_worklist(wlist);
+ if (put_user(cmd, (uint32_t __user *)ptr)) {
+ return -EFAULT;
+ }
+ ptr += sizeof(uint32_t);
+ if (put_user(cookie, (binder_uintptr_t __user *)ptr)) {
+ return -EFAULT;
+ }
+ ptr += sizeof(binder_uintptr_t);
+ binder_stat_br(proc, thread, cmd);
if (cmd == BR_DEAD_BINDER)
goto done; /* DEAD_BINDER notifications can cause transactions */
} break;
+ default:
+ pr_err("%s: Unknown work type: %d\n",
+ __func__, w->type);
+ BUG();
}
if (!t)
continue;
+ BUG_ON(!wlist || !wlist->freeze);
BUG_ON(t->buffer == NULL);
if (t->buffer->target_node) {
struct binder_node *target_node = t->buffer->target_node;
tr.target.ptr = target_node->ptr;
tr.cookie = target_node->cookie;
- t->saved_priority = task_nice(current);
- if (t->priority < target_node->min_priority &&
- !(t->flags & TF_ONE_WAY))
- binder_set_nice(t->priority);
- else if (!(t->flags & TF_ONE_WAY) ||
- t->saved_priority > target_node->min_priority)
- binder_set_nice(target_node->min_priority);
+ /* Don't need a lock to check set_priority_called, since
+ * the lock was held when pulling t of the workqueue,
+ * and it hasn't changed since then
+ */
+ if (!t->set_priority_called)
+ binder_transaction_priority(current, t,
+ target_node);
cmd = BR_TRANSACTION;
} else {
tr.target.ptr = 0;
@@ -2492,18 +3372,22 @@
tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
- tr.data.ptr.buffer = (binder_uintptr_t)(
- (uintptr_t)t->buffer->data +
- proc->user_buffer_offset);
+ tr.data.ptr.buffer = (binder_uintptr_t)
+ ((uintptr_t)t->buffer->data +
+ binder_alloc_get_user_buffer_offset(&proc->alloc));
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
- if (put_user_preempt_disabled(cmd, (uint32_t __user *) ptr))
+ if (put_user(cmd, (uint32_t __user *)ptr)) {
+ binder_unfreeze_worklist(wlist);
return -EFAULT;
+ }
ptr += sizeof(uint32_t);
- if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr)))
+ if (copy_to_user(ptr, &tr, sizeof(tr))) {
+ binder_unfreeze_worklist(wlist);
return -EFAULT;
+ }
ptr += sizeof(tr);
trace_binder_transaction_received(t);
@@ -2518,46 +3402,67 @@
t->buffer->data_size, t->buffer->offsets_size,
(u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets);
- list_del(&t->work.entry);
+ binder_dequeue_work(&t->work, __LINE__);
+ binder_unfreeze_worklist(wlist);
t->buffer->allow_user_free = 1;
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
+ binder_proc_lock(thread->proc, __LINE__);
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
thread->transaction_stack = t;
+ binder_proc_unlock(thread->proc, __LINE__);
} else {
- t->buffer->transaction = NULL;
- kfree(t);
- binder_stats_deleted(BINDER_STAT_TRANSACTION);
+ binder_free_transaction(t);
}
break;
}
done:
-
*consumed = ptr - buffer;
- if (proc->requested_threads + proc->ready_threads == 0 &&
- proc->requested_threads_started < proc->max_threads &&
- (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
- BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
- /*spawn a new thread if we leave this out */) {
+ binder_proc_lock(thread->proc, __LINE__);
+ if (proc->requested_threads +
+ atomic_read(&proc->ready_threads) == 0 &&
+ proc->requested_threads_started < proc->max_threads &&
+ (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
+ BINDER_LOOPER_STATE_ENTERED))
+ /* the user-space code fails to */
+ /* spawn a new thread if we leave this out */) {
proc->requested_threads++;
+ binder_proc_unlock(thread->proc, __LINE__);
+
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BR_SPAWN_LOOPER\n",
proc->pid, thread->pid);
- if (put_user_preempt_disabled(BR_SPAWN_LOOPER, (uint32_t __user *) buffer))
+ if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
- }
+ } else
+ binder_proc_unlock(thread->proc, __LINE__);
+
return 0;
}
-static void binder_release_work(struct list_head *list)
+static void binder_release_work(struct binder_worklist *wlist)
{
struct binder_work *w;
- while (!list_empty(list)) {
- w = list_first_entry(list, struct binder_work, entry);
- list_del_init(&w->entry);
+ spin_lock(&wlist->lock);
+ while (!list_empty(&wlist->list)) {
+
+ if (wlist->freeze) {
+ /* Very rare race. We can't release work now
+ * since the list is in use by a worker thread.
+ * This can be safely done when the zombie object
+ * is being reaped.
+ */
+ spin_unlock(&wlist->lock);
+ return;
+ }
+ w = list_first_entry(&wlist->list, struct binder_work, entry);
+ _binder_dequeue_work(w, __LINE__);
+
+ spin_unlock(&wlist->lock);
+
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
struct binder_transaction *t;
@@ -2575,12 +3480,21 @@
binder_stats_deleted(BINDER_STAT_TRANSACTION);
}
} break;
+ case BINDER_WORK_RETURN_ERROR: {
+ struct binder_error *e = container_of(
+ w, struct binder_error, work);
+
+ binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
+ "undelivered TRANSACTION_ERROR: %u\n",
+ e->cmd);
+ } break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"undelivered TRANSACTION_COMPLETE\n");
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
+ case BINDER_WORK_DEAD_BINDER:
case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
struct binder_ref_death *death;
@@ -2589,16 +3503,89 @@
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"undelivered death notification, %016llx\n",
(u64)death->cookie);
+ /*
+ * For the non-CLEAR cases, kfree ref->death freeing
+ * done in zombie cleanup path so avoid doing it here
+ */
+ if (w->type == BINDER_WORK_DEAD_BINDER)
+ break;
kfree(death);
binder_stats_deleted(BINDER_STAT_DEATH);
} break;
+ case BINDER_WORK_NODE:
+ pr_info("unfinished BINDER_WORK_NODE, proc has died\n");
+ break;
default:
pr_err("unexpected work type, %d, not freed\n",
w->type);
+ BUG();
break;
}
+ spin_lock(&wlist->lock);
}
+ spin_unlock(&wlist->lock);
+}
+static u64 binder_get_seq(struct binder_seq_head *tracker)
+{
+ u64 seq;
+ /*
+ * No lock needed, worst case we return an overly conservative
+ * value.
+ */
+ seq = READ_ONCE(tracker->lowest_seq);
+ return seq;
+}
+
+atomic64_t binder_seq_count;
+
+static inline u64 binder_get_next_seq(void)
+{
+ return atomic64_inc_return(&binder_seq_count);
+}
+
+static void binder_add_seq(struct binder_seq_node *node,
+ struct binder_seq_head *tracker)
+{
+ spin_lock(&tracker->lock);
+ /*
+ * Was the node previously added?
+ * - binder_get_thread/put_thread should never be nested
+ * - binder_queue_for_zombie_cleanup should first delete and then
+ * enqueue, so this shouldn't happen.
+ */
+ BUG_ON(!list_empty(&node->list_node));
+
+ node->active_seq = binder_get_next_seq();
+ list_add_tail(&node->list_node, &tracker->active_threads);
+ if (node->active_seq < READ_ONCE(tracker->lowest_seq))
+ WRITE_ONCE(tracker->lowest_seq, node->active_seq);
+
+ tracker->active_count++;
+ if (tracker->active_count > tracker->max_active_count)
+ tracker->max_active_count = tracker->active_count;
+ spin_unlock(&tracker->lock);
+}
+
+static void binder_del_seq(struct binder_seq_node *node,
+ struct binder_seq_head *tracker)
+{
+ spin_lock(&tracker->lock);
+ /*
+ * No need to track leftmost node, the queue tracks it already
+ */
+ list_del_init(&node->list_node);
+
+ if (!list_empty(&tracker->active_threads)) {
+ struct binder_seq_node *tmp;
+
+ tmp = list_first_entry(&tracker->active_threads, typeof(*tmp),
+ list_node);
+ WRITE_ONCE(tracker->lowest_seq, tmp->active_seq);
+ } else {
+ WRITE_ONCE(tracker->lowest_seq, ~0ULL);
+ }
+ spin_unlock(&tracker->lock);
}
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
@@ -2606,7 +3593,9 @@
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
struct rb_node **p = &proc->threads.rb_node;
+ bool need_alloc;
+ binder_proc_lock(proc, __LINE__);
while (*p) {
parent = *p;
thread = rb_entry(parent, struct binder_thread, rb_node);
@@ -2618,24 +3607,80 @@
else
break;
}
- if (*p == NULL) {
- thread = kzalloc_preempt_disabled(sizeof(*thread));
- if (thread == NULL)
+ need_alloc = *p == NULL;
+ binder_proc_unlock(proc, __LINE__);
+
+ if (need_alloc) {
+ struct binder_thread *new_thread =
+ kzalloc(sizeof(*thread), GFP_KERNEL);
+ if (new_thread == NULL)
return NULL;
binder_stats_created(BINDER_STAT_THREAD);
- thread->proc = proc;
- thread->pid = current->pid;
- init_waitqueue_head(&thread->wait);
- INIT_LIST_HEAD(&thread->todo);
- rb_link_node(&thread->rb_node, parent, p);
- rb_insert_color(&thread->rb_node, &proc->threads);
- thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
- thread->return_error = BR_OK;
- thread->return_error2 = BR_OK;
+ new_thread->proc = proc;
+ new_thread->pid = current->pid;
+ init_waitqueue_head(&new_thread->wait);
+ binder_init_worklist(&new_thread->todo);
+ WRITE_ONCE(new_thread->looper_need_return, true);
+ INIT_LIST_HEAD(&new_thread->active_node.list_node);
+ INIT_LIST_HEAD(&new_thread->waiting_thread_node);
+ get_task_struct(current);
+ new_thread->task = current;
+ new_thread->return_error.work.type = BINDER_WORK_RETURN_ERROR;
+ new_thread->return_error.cmd = BR_OK;
+ new_thread->reply_error.work.type = BINDER_WORK_RETURN_ERROR;
+ new_thread->reply_error.cmd = BR_OK;
+
+ binder_proc_lock(proc, __LINE__);
+ /*
+ * Since we gave up the proc lock, we need
+ * to recalc the insertion point in the rb tree.
+ */
+ p = &proc->threads.rb_node;
+ while (*p) {
+ parent = *p;
+ thread = rb_entry(parent,
+ struct binder_thread, rb_node);
+
+ if (current->pid < thread->pid)
+ p = &(*p)->rb_left;
+ else if (current->pid > thread->pid)
+ p = &(*p)->rb_right;
+ else
+ break;
+ }
+ /* This thread can't have been added */
+ BUG_ON(*p != NULL);
+
+ rb_link_node(&new_thread->rb_node, parent, p);
+ rb_insert_color(&new_thread->rb_node, &proc->threads);
+ thread = new_thread;
+ binder_proc_unlock(proc, __LINE__);
}
+ /*
+ * Add to active threads
+ */
+ binder_add_seq(&thread->active_node,
+ &binder_active_threads[binder_seq_hash(thread)]);
+ proc->active_thread_count++;
return thread;
}
+static inline void binder_queue_for_zombie_cleanup(struct binder_proc *proc)
+{
+ binder_del_seq(&proc->zombie_proc, &zombie_procs);
+ binder_add_seq(&proc->zombie_proc, &zombie_procs);
+}
+static inline void binder_dequeue_for_zombie_cleanup(struct binder_proc *proc)
+{
+ binder_del_seq(&proc->zombie_proc, &zombie_procs);
+}
+
+static void binder_put_thread(struct binder_thread *thread)
+{
+ binder_del_seq(&thread->active_node,
+ &binder_active_threads[binder_seq_hash(thread)]);
+}
+
static int binder_free_thread(struct binder_proc *proc,
struct binder_thread *thread)
{
@@ -2643,6 +3688,17 @@
struct binder_transaction *send_reply = NULL;
int active_transactions = 0;
+ binder_proc_lock(thread->proc, __LINE__);
+ if (thread->is_zombie) {
+ /*
+ * Can be called twice: by binder_deferred_release
+ * and binder_ioctl(BINDER_THREAD_EXIT). Only process
+ * it the first time.
+ */
+ binder_proc_unlock(thread->proc, __LINE__);
+ return 0;
+ }
+ thread->is_zombie = true;
rb_erase(&thread->rb_node, &proc->threads);
t = thread->transaction_stack;
if (t && t->to_thread == thread)
@@ -2669,11 +3725,15 @@
} else
BUG();
}
+ binder_release_work(&thread->todo);
+ INIT_HLIST_NODE(&thread->zombie_thread);
+ hlist_add_head(&thread->zombie_thread, &proc->zombie_threads);
+ binder_queue_for_zombie_cleanup(proc);
+ binder_proc_unlock(thread->proc, __LINE__);
+
if (send_reply)
binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
- binder_release_work(&thread->todo);
- kfree(thread);
- binder_stats_deleted(BINDER_STAT_THREAD);
+ binder_stats_zombie(BINDER_STAT_THREAD);
return active_transactions;
}
@@ -2682,38 +3742,39 @@
{
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread = NULL;
- int wait_for_proc_work;
-
- binder_lock(__func__);
+ bool wait_for_proc_work;
thread = binder_get_thread(proc);
- wait_for_proc_work = thread->transaction_stack == NULL &&
- list_empty(&thread->todo) && thread->return_error == BR_OK;
+ binder_proc_lock(thread->proc, __LINE__);
+ thread->looper |= BINDER_LOOPER_STATE_POLL;
+ wait_for_proc_work = binder_available_for_proc_work(thread);
+ binder_proc_unlock(thread->proc, __LINE__);
- binder_unlock(__func__);
-
- if (wait_for_proc_work) {
- if (binder_has_proc_work(proc, thread))
- return POLLIN;
- poll_wait(filp, &proc->wait, wait);
- if (binder_has_proc_work(proc, thread))
- return POLLIN;
- } else {
- if (binder_has_thread_work(thread))
- return POLLIN;
- poll_wait(filp, &thread->wait, wait);
- if (binder_has_thread_work(thread))
- return POLLIN;
- }
+ if (binder_has_work(thread, wait_for_proc_work))
+ goto ret_pollin;
+ binder_put_thread(thread);
+ poll_wait(filp, &thread->wait, wait);
+ thread = binder_get_thread(proc);
+ if (!thread)
+ return -ENOENT;
+ if (binder_has_work(thread, wait_for_proc_work))
+ goto ret_pollin;
+ binder_put_thread(thread);
return 0;
+
+ret_pollin:
+ binder_put_thread(thread);
+ return POLLIN;
+
}
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
- struct binder_thread *thread)
+ struct binder_thread **threadp)
{
int ret = 0;
+ int thread_pid = (*threadp)->pid;
struct binder_proc *proc = filp->private_data;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
@@ -2723,49 +3784,52 @@
ret = -EINVAL;
goto out;
}
- if (copy_from_user_preempt_disabled(&bwr, ubuf, sizeof(bwr))) {
+ if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d write %lld at %016llx, read %lld at %016llx\n",
- proc->pid, thread->pid,
+ proc->pid, thread_pid,
(u64)bwr.write_size, (u64)bwr.write_buffer,
(u64)bwr.read_size, (u64)bwr.read_buffer);
if (bwr.write_size > 0) {
- ret = binder_thread_write(proc, thread,
+ ret = binder_thread_write(proc, *threadp,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 0;
- if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr)))
+ if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
if (bwr.read_size > 0) {
- ret = binder_thread_read(proc, thread, bwr.read_buffer,
+ ret = binder_thread_read(proc, threadp, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
- if (!list_empty(&proc->todo))
- wake_up_interruptible(&proc->wait);
+ if (!binder_worklist_empty(&proc->todo)) {
+ binder_proc_lock(proc, __LINE__);
+ binder_wakeup_proc(proc);
+ binder_proc_unlock(proc, __LINE__);
+ }
if (ret < 0) {
- if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr)))
+ if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d wrote %lld of %lld, read return %lld of %lld\n",
- proc->pid, thread->pid,
+ proc->pid, thread_pid,
(u64)bwr.write_consumed, (u64)bwr.write_size,
(u64)bwr.read_consumed, (u64)bwr.read_size);
- if (copy_to_user_preempt_disabled(ubuf, &bwr, sizeof(bwr))) {
+ if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
@@ -2773,13 +3837,45 @@
return ret;
}
+static int binder_ioctl_set_inherit_fifo_prio(struct file *filp)
+{
+ int ret = 0;
+ struct binder_proc *proc = filp->private_data;
+ struct binder_context *context = proc->context;
+
+ kuid_t curr_euid = current_euid();
+ mutex_lock(&binder_context_mgr_node_lock);
+
+ if (uid_valid(context->binder_context_mgr_uid)) {
+ if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
+ pr_err("BINDER_SET_INHERIT_FIFO_PRIO bad uid %d != %d\n",
+ from_kuid(&init_user_ns, curr_euid),
+ from_kuid(&init_user_ns,
+ context->binder_context_mgr_uid));
+ ret = -EPERM;
+ goto out;
+ }
+ }
+
+ context->inherit_fifo_prio = true;
+
+ out:
+ mutex_unlock(&binder_context_mgr_node_lock);
+ return ret;
+}
+
+
static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
- kuid_t curr_euid = current_euid();
+ struct binder_context *context = proc->context;
- if (binder_context_mgr_node != NULL) {
+ kuid_t curr_euid = current_euid();
+ struct binder_node *temp;
+
+ mutex_lock(&binder_context_mgr_node_lock);
+ if (context->binder_context_mgr_node) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
@@ -2787,31 +3883,58 @@
ret = security_binder_set_context_mgr(proc->tsk);
if (ret < 0)
goto out;
- if (uid_valid(binder_context_mgr_uid)) {
- if (!uid_eq(binder_context_mgr_uid, curr_euid)) {
+ if (uid_valid(context->binder_context_mgr_uid)) {
+ if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
from_kuid(&init_user_ns, curr_euid),
from_kuid(&init_user_ns,
- binder_context_mgr_uid));
+ context->binder_context_mgr_uid));
ret = -EPERM;
goto out;
}
} else {
- binder_context_mgr_uid = curr_euid;
+ context->binder_context_mgr_uid = curr_euid;
}
- binder_context_mgr_node = binder_new_node(proc, 0, 0);
- if (binder_context_mgr_node == NULL) {
+
+ temp = binder_new_node(proc, 0, 0);
+ if (temp == NULL) {
+ context->binder_context_mgr_uid = INVALID_UID;
ret = -ENOMEM;
goto out;
}
- binder_context_mgr_node->local_weak_refs++;
- binder_context_mgr_node->local_strong_refs++;
- binder_context_mgr_node->has_strong_ref = 1;
- binder_context_mgr_node->has_weak_ref = 1;
+ temp->local_weak_refs++;
+ temp->local_strong_refs++;
+ temp->has_strong_ref = 1;
+ temp->has_weak_ref = 1;
+ context->binder_context_mgr_node = temp;
+ binder_put_node(temp);
out:
+ mutex_unlock(&binder_context_mgr_node_lock);
return ret;
}
+static inline u64 binder_get_thread_seq(void)
+{
+ u64 thread_seq = ~0ULL;
+ int i;
+
+ for (i = 0; i < SEQ_BUCKETS; i++) {
+ u64 ts = binder_get_seq(&binder_active_threads[i]);
+
+ thread_seq = min(ts, thread_seq);
+ }
+ return thread_seq;
+}
+
+static void zombie_cleanup_check(struct binder_proc *proc)
+{
+ u64 thread_seq = binder_get_thread_seq();
+ u64 zombie_seq = binder_get_seq(&zombie_procs);
+
+ if (thread_seq > zombie_seq)
+ binder_defer_work(proc, BINDER_ZOMBIE_CLEANUP);
+}
+
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
@@ -2820,16 +3943,12 @@
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
- /*pr_info("binder_ioctl: %d:%d %x %lx\n",
- proc->pid, current->pid, cmd, arg);*/
-
trace_binder_ioctl(cmd, arg);
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
- goto err_unlocked;
+ goto err_wait_event;
- binder_lock(__func__);
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
@@ -2838,25 +3957,39 @@
switch (cmd) {
case BINDER_WRITE_READ:
- ret = binder_ioctl_write_read(filp, cmd, arg, thread);
+ ret = binder_ioctl_write_read(filp, cmd, arg, &thread);
if (ret)
goto err;
break;
- case BINDER_SET_MAX_THREADS:
- if (copy_from_user_preempt_disabled(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
+ case BINDER_SET_MAX_THREADS: {
+ int max_threads;
+
+ if (copy_from_user(&max_threads, ubuf,
+ sizeof(max_threads))) {
ret = -EINVAL;
goto err;
}
+ binder_proc_lock(proc, __LINE__);
+ proc->max_threads = max_threads;
+ binder_proc_unlock(proc, __LINE__);
break;
+ }
case BINDER_SET_CONTEXT_MGR:
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
+ case BINDER_SET_INHERIT_FIFO_PRIO:
+ ret = binder_ioctl_set_inherit_fifo_prio(filp);
+ if (ret)
+ goto err;
+ break;
+
case BINDER_THREAD_EXIT:
binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
proc->pid, thread->pid);
binder_free_thread(proc, thread);
+ binder_put_thread(thread);
thread = NULL;
break;
case BINDER_VERSION: {
@@ -2866,7 +3999,9 @@
ret = -EINVAL;
goto err;
}
- if (put_user_preempt_disabled(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)) {
+
+ if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
+ &ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
@@ -2878,13 +4013,17 @@
}
ret = 0;
err:
- if (thread)
- thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
- binder_unlock(__func__);
+ if (thread) {
+ binder_proc_lock(thread->proc, __LINE__);
+ WRITE_ONCE(thread->looper_need_return, false);
+ binder_proc_unlock(thread->proc, __LINE__);
+ binder_put_thread(thread);
+ zombie_cleanup_check(proc);
+ }
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret && ret != -ERESTARTSYS)
pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
-err_unlocked:
+err_wait_event:
trace_binder_ioctl_done(ret);
return ret;
}
@@ -2909,8 +4048,7 @@
proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
- proc->vma = NULL;
- proc->vma_vm_mm = NULL;
+ binder_alloc_vma_close(&proc->alloc);
binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES);
}
@@ -2928,13 +4066,10 @@
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
-
- struct vm_struct *area;
struct binder_proc *proc = filp->private_data;
const char *failure_string;
- struct binder_buffer *buffer;
- if (proc->tsk != current)
+ if (proc->tsk != current->group_leader)
return -EINVAL;
if ((vma->vm_end - vma->vm_start) > SZ_4M)
@@ -2952,77 +4087,16 @@
goto err_bad_arg;
}
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
-
- mutex_lock(&binder_mmap_lock);
- if (proc->buffer) {
- ret = -EBUSY;
- failure_string = "already mapped";
- goto err_already_mapped;
- }
-
- area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
- if (area == NULL) {
- ret = -ENOMEM;
- failure_string = "get_vm_area";
- goto err_get_vm_area_failed;
- }
- proc->buffer = area->addr;
- proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
- mutex_unlock(&binder_mmap_lock);
-
-#ifdef CONFIG_CPU_CACHE_VIPT
- if (cache_is_vipt_aliasing()) {
- while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
- pr_info("binder_mmap: %d %lx-%lx maps %pK bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
- vma->vm_start += PAGE_SIZE;
- }
- }
-#endif
- proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
- if (proc->pages == NULL) {
- ret = -ENOMEM;
- failure_string = "alloc page array";
- goto err_alloc_pages_failed;
- }
- proc->buffer_size = vma->vm_end - vma->vm_start;
-
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
- /* binder_update_page_range assumes preemption is disabled */
- preempt_disable();
- ret = binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma);
- preempt_enable_no_resched();
- if (ret) {
- ret = -ENOMEM;
- failure_string = "alloc small buf";
- goto err_alloc_small_buf_failed;
+ ret = binder_alloc_mmap_handler(&proc->alloc, vma);
+
+ if (!ret) {
+ proc->files = get_files_struct(current);
+ return 0;
}
- buffer = proc->buffer;
- INIT_LIST_HEAD(&proc->buffers);
- list_add(&buffer->entry, &proc->buffers);
- buffer->free = 1;
- binder_insert_free_buffer(proc, buffer);
- proc->free_async_space = proc->buffer_size / 2;
- barrier();
- proc->files = get_files_struct(current);
- proc->vma = vma;
- proc->vma_vm_mm = vma->vm_mm;
- /*pr_info("binder_mmap: %d %lx-%lx maps %pK\n",
- proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
- return 0;
-
-err_alloc_small_buf_failed:
- kfree(proc->pages);
- proc->pages = NULL;
-err_alloc_pages_failed:
- mutex_lock(&binder_mmap_lock);
- vfree(proc->buffer);
- proc->buffer = NULL;
-err_get_vm_area_failed:
-err_already_mapped:
- mutex_unlock(&binder_mmap_lock);
err_bad_arg:
pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
@@ -3032,6 +4106,7 @@
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
+ struct binder_device *binder_dev;
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
@@ -3039,28 +4114,57 @@
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
- get_task_struct(current);
- proc->tsk = current;
- INIT_LIST_HEAD(&proc->todo);
- init_waitqueue_head(&proc->wait);
- proc->default_priority = task_nice(current);
- binder_lock(__func__);
+ get_task_struct(current->group_leader);
+ proc->tsk = current->group_leader;
+ binder_init_worklist(&proc->todo);
+ if (binder_supported_policy(current->policy)) {
+ proc->default_priority.sched_policy = current->policy;
+ proc->default_priority.prio = current->normal_prio;
+ } else {
+ proc->default_priority.sched_policy = SCHED_NORMAL;
+ proc->default_priority.prio = NICE_TO_PRIO(0);
+ }
+ binder_dev = container_of(filp->private_data, struct binder_device,
+ miscdev);
+ proc->context = &binder_dev->context;
+ binder_alloc_init(&proc->alloc);
+
+ mutex_lock(&binder_procs_lock);
binder_stats_created(BINDER_STAT_PROC);
hlist_add_head(&proc->proc_node, &binder_procs);
proc->pid = current->group_leader->pid;
- INIT_LIST_HEAD(&proc->delivered_death);
+ spin_lock_init(&proc->proc_lock);
+ binder_init_worklist(&proc->delivered_death);
+ atomic_set(&proc->ready_threads, 0);
+ proc->max_threads = 0;
+ proc->requested_threads = 0;
+ proc->requested_threads_started = 0;
+ INIT_LIST_HEAD(&proc->zombie_proc.list_node);
+ INIT_HLIST_HEAD(&proc->zombie_refs);
+ INIT_HLIST_HEAD(&proc->zombie_nodes);
+ INIT_HLIST_HEAD(&proc->zombie_threads);
+ INIT_LIST_HEAD(&proc->waiting_threads);
filp->private_data = proc;
- binder_unlock(__func__);
+ mutex_unlock(&binder_procs_lock);
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
+ /*
+ * proc debug entries are shared between contexts, so
+ * this will fail if the process tries to open the driver
+ * again with a different context. The priting code will
+ * anyway print all contexts that a given PID has, so this
+ * is not a problem.
+ */
proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
- binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
+ binder_debugfs_dir_entry_proc,
+ (void *)(unsigned long)proc->pid,
+ &binder_proc_fops);
}
return 0;
@@ -3079,17 +4183,51 @@
{
struct rb_node *n;
int wake_count = 0;
+ int count, i;
- for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) {
- struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node);
+ do {
+ wait_queue_head_t **waits;
- thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
- if (thread->looper & BINDER_LOOPER_STATE_WAITING) {
- wake_up_interruptible(&thread->wait);
- wake_count++;
+ count = 0;
+ binder_proc_lock(proc, __LINE__);
+ for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) {
+ struct binder_thread *thread;
+
+ thread = rb_entry(n, struct binder_thread, rb_node);
+ if (thread->looper & BINDER_LOOPER_STATE_WAITING)
+ count++;
}
- }
- wake_up_interruptible_all(&proc->wait);
+ binder_proc_unlock(proc, __LINE__);
+
+ waits = kcalloc(count, sizeof(*waits), GFP_KERNEL);
+ if (waits == NULL)
+ break;
+
+ binder_proc_lock(proc, __LINE__);
+ for (i = 0, n = rb_first(&proc->threads);
+ n != NULL; n = rb_next(n)) {
+ struct binder_thread *thread;
+
+ thread = rb_entry(n, struct binder_thread, rb_node);
+ WRITE_ONCE(thread->looper_need_return, true);
+ if (thread->looper & BINDER_LOOPER_STATE_WAITING) {
+ if (i < count)
+ waits[i] = &thread->wait;
+ i++;
+ }
+ }
+ binder_proc_unlock(proc, __LINE__);
+
+ if (i <= count) {
+ while (--i >= 0) {
+ wake_up_interruptible(waits[i]);
+ wake_count++;
+ }
+ }
+ kfree(waits);
+
+ /* if another thread grew the tree, try again */
+ } while (i > count);
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"binder_flush: %d woke %d threads\n", proc->pid,
@@ -3110,39 +4248,85 @@
{
struct binder_ref *ref;
int death = 0;
+ struct binder_proc *proc = node->proc;
+ struct binder_ref dummy_ref;
+ struct hlist_head unvisited_refs;
- list_del_init(&node->work.entry);
+ BUG_ON(!proc);
+
+ INIT_HLIST_HEAD(&unvisited_refs);
+
+ /* node->refs entries can have their proc->pid printed */
+ dummy_ref.proc = proc;
+ INIT_HLIST_NODE(&dummy_ref.node_entry);
+
+ binder_proc_lock(proc, __LINE__);
+
+ binder_dequeue_work(&node->work, __LINE__);
binder_release_work(&node->async_todo);
- if (hlist_empty(&node->refs)) {
- kfree(node);
- binder_stats_deleted(BINDER_STAT_NODE);
+ _binder_make_node_zombie(node);
+ if (hlist_empty(&node->refs)) {
+ binder_proc_unlock(proc, __LINE__);
return refs;
}
- node->proc = NULL;
node->local_strong_refs = 0;
node->local_weak_refs = 0;
- hlist_add_head(&node->dead_node, &binder_dead_nodes);
- hlist_for_each_entry(ref, &node->refs, node_entry) {
+ /*
+ * We move all the references to an 'unvisited' list, to avoid
+ * iterating node->refs without the lock held.
+ */
+ hlist_move_list(&node->refs, &unvisited_refs);
+
+ /*
+ * Add a dummy reference to prevent the node from getting freed,
+ * which can happen if a ref goes away while we don't hold the
+ * proc lock for the node below.
+ */
+ hlist_add_head(&dummy_ref.node_entry, &node->refs);
+
+ while (!hlist_empty(&unvisited_refs)) {
+ ref = hlist_entry(unvisited_refs.first, struct binder_ref,
+ node_entry);
+
+ /* First, move it back */
+ hlist_del_init(&ref->node_entry);
+ hlist_add_head(&ref->node_entry, &node->refs);
+
+ /* Then, see if we need to do any work on it */
+ binder_proc_unlock(proc, __LINE__);
+
refs++;
- if (!ref->death)
+ binder_proc_lock(ref->proc, __LINE__);
+
+ ref->node_is_zombie = true;
+
+ if (!ref->death || ref->is_zombie) {
+ binder_proc_unlock(ref->proc, __LINE__);
+ binder_proc_lock(proc, __LINE__);
continue;
+ }
death++;
- if (list_empty(&ref->death->work.entry)) {
- ref->death->work.type = BINDER_WORK_DEAD_BINDER;
- list_add_tail(&ref->death->work.entry,
- &ref->proc->todo);
- wake_up_interruptible(&ref->proc->wait);
- } else
- BUG();
+ BUG_ON(!list_empty(&ref->death->work.entry));
+ ref->death->work.type = BINDER_WORK_DEAD_BINDER;
+ binder_enqueue_work(&ref->death->work, &ref->proc->todo,
+ __LINE__);
+ binder_wakeup_proc(ref->proc);
+
+ binder_proc_unlock(ref->proc, __LINE__);
+ binder_proc_lock(proc, __LINE__);
}
+ hlist_del(&dummy_ref.node_entry);
+
+ binder_proc_unlock(proc, __LINE__);
+
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"node %d now dead, refs %d, death %d\n",
node->debug_id, refs, death);
@@ -3152,31 +4336,42 @@
static void binder_deferred_release(struct binder_proc *proc)
{
- struct binder_transaction *t;
+ struct binder_context *context = proc->context;
struct rb_node *n;
- int threads, nodes, incoming_refs, outgoing_refs, buffers,
- active_transactions, page_count;
+ int threads, nodes, incoming_refs, outgoing_refs, active_transactions;
- BUG_ON(proc->vma);
BUG_ON(proc->files);
- hlist_del(&proc->proc_node);
+ binder_proc_lock(proc, __LINE__);
+ binder_queue_for_zombie_cleanup(proc);
+ binder_proc_unlock(proc, __LINE__);
- if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) {
+ mutex_lock(&binder_procs_lock);
+ hlist_del_init(&proc->proc_node);
+ proc->is_zombie = true;
+ mutex_unlock(&binder_procs_lock);
+
+ mutex_lock(&binder_context_mgr_node_lock);
+ if (context->binder_context_mgr_node &&
+ context->binder_context_mgr_node->proc == proc) {
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"%s: %d context_mgr_node gone\n",
__func__, proc->pid);
- binder_context_mgr_node = NULL;
+ context->binder_context_mgr_node = NULL;
}
+ mutex_unlock(&binder_context_mgr_node_lock);
threads = 0;
active_transactions = 0;
+ binder_proc_lock(proc, __LINE__);
while ((n = rb_first(&proc->threads))) {
struct binder_thread *thread;
thread = rb_entry(n, struct binder_thread, rb_node);
+ binder_proc_unlock(proc, __LINE__);
threads++;
active_transactions += binder_free_thread(proc, thread);
+ binder_proc_lock(proc, __LINE__);
}
nodes = 0;
@@ -3186,8 +4381,9 @@
node = rb_entry(n, struct binder_node, rb_node);
nodes++;
- rb_erase(&node->rb_node, &proc->nodes);
+ binder_proc_unlock(proc, __LINE__);
incoming_refs = binder_node_release(node, incoming_refs);
+ binder_proc_lock(proc, __LINE__);
}
outgoing_refs = 0;
@@ -3196,79 +4392,174 @@
ref = rb_entry(n, struct binder_ref, rb_node_desc);
outgoing_refs++;
- binder_delete_ref(ref);
+ binder_proc_unlock(proc, __LINE__);
+ binder_delete_ref(ref, true, __LINE__);
+ binder_proc_lock(proc, __LINE__);
}
+ binder_proc_unlock(proc, __LINE__);
binder_release_work(&proc->todo);
binder_release_work(&proc->delivered_death);
-
- buffers = 0;
- while ((n = rb_first(&proc->allocated_buffers))) {
- struct binder_buffer *buffer;
-
- buffer = rb_entry(n, struct binder_buffer, rb_node);
-
- t = buffer->transaction;
- if (t) {
- t->buffer = NULL;
- buffer->transaction = NULL;
- pr_err("release proc %d, transaction %d, not freed\n",
- proc->pid, t->debug_id);
- /*BUG();*/
- }
-
- binder_free_buf(proc, buffer);
- buffers++;
- }
-
- binder_stats_deleted(BINDER_STAT_PROC);
-
- page_count = 0;
- if (proc->pages) {
- int i;
-
- for (i = 0; i < proc->buffer_size / PAGE_SIZE; i++) {
- void *page_addr;
-
- if (!proc->pages[i])
- continue;
-
- page_addr = proc->buffer + i * PAGE_SIZE;
- binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
- "%s: %d: page %d at %pK not freed\n",
- __func__, proc->pid, i, page_addr);
- unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
- __free_page(proc->pages[i]);
- page_count++;
- }
- kfree(proc->pages);
- vfree(proc->buffer);
- }
-
- put_task_struct(proc->tsk);
+ binder_stats_zombie(BINDER_STAT_PROC);
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
- "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d, buffers %d, pages %d\n",
+ "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d\n",
__func__, proc->pid, threads, nodes, incoming_refs,
- outgoing_refs, active_transactions, buffers, page_count);
-
- kfree(proc);
+ outgoing_refs, active_transactions);
}
+static int cleared_nodes;
+static int cleared_threads;
+static int cleared_procs;
+
+static bool binder_proc_clear_zombies(struct binder_proc *proc)
+{
+ struct binder_node *node;
+ struct hlist_node *tmp;
+ struct binder_thread *thread;
+ struct binder_ref *ref;
+ struct hlist_head nodes_to_free;
+ struct hlist_head threads_to_free;
+ struct hlist_head refs_to_free;
+ bool needs_requeue = false;
+ struct files_struct *files;
+
+ INIT_HLIST_HEAD(&nodes_to_free);
+ INIT_HLIST_HEAD(&threads_to_free);
+ INIT_HLIST_HEAD(&refs_to_free);
+
+ binder_proc_lock(proc, __LINE__);
+ if (!list_empty(&proc->zombie_proc.list_node)) {
+ /* The proc has been re-queued with new zombie objects */
+ binder_proc_unlock(proc, __LINE__);
+ return 0;
+ }
+ hlist_for_each_entry_safe(ref, tmp, &proc->zombie_refs, zombie_ref) {
+ hlist_del_init(&ref->zombie_ref);
+ hlist_add_head(&ref->zombie_ref, &refs_to_free);
+ }
+ if (!RB_EMPTY_ROOT(&proc->refs_by_desc))
+ needs_requeue = true;
+
+ hlist_for_each_entry_safe(node, tmp, &proc->zombie_nodes, dead_node)
+ if (hlist_empty(&node->refs)) {
+ hlist_del_init(&node->dead_node);
+ hlist_add_head(&node->dead_node, &nodes_to_free);
+ }
+ if (!hlist_empty(&proc->zombie_nodes) || !RB_EMPTY_ROOT(&proc->nodes))
+ needs_requeue = true;
+
+ hlist_for_each_entry_safe(thread, tmp, &proc->zombie_threads,
+ zombie_thread) {
+ hlist_del_init(&thread->zombie_thread);
+ hlist_add_head(&thread->zombie_thread, &threads_to_free);
+ }
+ if (!RB_EMPTY_ROOT(&proc->threads))
+ needs_requeue = true;
+
+ files = proc->zombie_files;
+ proc->zombie_files = NULL;
+
+ binder_proc_unlock(proc, __LINE__);
+
+ if (files)
+ put_files_struct(files);
+
+ hlist_for_each_entry_safe(node, tmp, &nodes_to_free, dead_node) {
+ int work_line = node->work.last_line;
+ hlist_del_init(&node->dead_node);
+ binder_dequeue_work(&node->work, __LINE__);
+ if (!list_empty(&node->work.entry)) {
+ pr_err("binder work node reinserted. last node op: %d, line; %d",
+ node->last_op, node->last_line);
+ pr_err("last work insertion at line %d\n", work_line);
+ BUG();
+ }
+ BUG_ON(node->async_todo.freeze);
+ binder_release_work(&node->async_todo);
+ BUG_ON(!binder_worklist_empty(&node->async_todo));
+ kfree(node);
+ binder_stats_delete_zombie(BINDER_STAT_NODE);
+ cleared_nodes++;
+ }
+ hlist_for_each_entry_safe(thread, tmp, &threads_to_free,
+ zombie_thread) {
+ hlist_del_init(&thread->zombie_thread);
+ BUG_ON(thread->todo.freeze);
+ binder_release_work(&thread->todo);
+ BUG_ON(!binder_worklist_empty(&thread->todo));
+ put_task_struct(thread->task);
+ kfree(thread);
+ binder_stats_delete_zombie(BINDER_STAT_THREAD);
+ cleared_threads++;
+ }
+ hlist_for_each_entry_safe(ref, tmp, &refs_to_free, zombie_ref) {
+ hlist_del_init(&ref->zombie_ref);
+ if (ref->death) {
+ binder_dequeue_work(&ref->death->work, __LINE__);
+ kfree(ref->death);
+ binder_stats_delete_zombie(BINDER_STAT_DEATH);
+ }
+ kfree(ref);
+ binder_stats_delete_zombie(BINDER_STAT_REF);
+ }
+
+ return proc->is_zombie && !needs_requeue;
+}
+
+static void binder_clear_zombies(void)
+{
+ struct binder_proc *proc;
+ struct binder_seq_node *z;
+
+ spin_lock(&zombie_procs.lock);
+ if (list_empty(&zombie_procs.active_threads)) {
+ spin_unlock(&zombie_procs.lock);
+ return;
+ }
+
+ while ((z = list_first_entry_or_null(&zombie_procs.active_threads,
+ typeof(*z), list_node)) != NULL) {
+ if (binder_get_thread_seq() < z->active_seq)
+ break;
+ list_del_init(&z->list_node);
+
+ if (!list_empty(&zombie_procs.active_threads)) {
+ struct binder_seq_node *tmp;
+
+ tmp = list_first_entry(&zombie_procs.active_threads,
+ typeof(*tmp), list_node);
+ WRITE_ONCE(zombie_procs.lowest_seq, tmp->active_seq);
+ } else {
+ WRITE_ONCE(zombie_procs.lowest_seq, ~0ULL);
+ }
+
+ spin_unlock(&zombie_procs.lock);
+
+ proc = container_of(z, struct binder_proc, zombie_proc);
+ if (binder_proc_clear_zombies(proc)) {
+ BUG_ON(proc->todo.freeze);
+ BUG_ON(!list_empty(&proc->zombie_proc.list_node));
+ binder_release_work(&proc->todo);
+ binder_alloc_deferred_release(&proc->alloc);
+ put_task_struct(proc->tsk);
+ kfree(proc);
+ cleared_procs++;
+ binder_stats_delete_zombie(BINDER_STAT_PROC);
+ }
+ spin_lock(&zombie_procs.lock);
+ }
+ spin_unlock(&zombie_procs.lock);
+}
+
+
static void binder_deferred_func(struct work_struct *work)
{
struct binder_proc *proc;
- struct files_struct *files;
-
int defer;
do {
- trace_binder_lock(__func__);
- mutex_lock(&binder_main_lock);
- trace_binder_locked(__func__);
-
mutex_lock(&binder_deferred_lock);
- preempt_disable();
if (!hlist_empty(&binder_deferred_list)) {
proc = hlist_entry(binder_deferred_list.first,
struct binder_proc, deferred_work_node);
@@ -3281,11 +4572,16 @@
}
mutex_unlock(&binder_deferred_lock);
- files = NULL;
if (defer & BINDER_DEFERRED_PUT_FILES) {
- files = proc->files;
- if (files)
+ binder_proc_lock(proc, __LINE__);
+ if (proc->files) {
+ BUG_ON(proc->zombie_files);
+ proc->zombie_files = proc->files;
proc->files = NULL;
+ binder_queue_for_zombie_cleanup(proc);
+ defer |= BINDER_ZOMBIE_CLEANUP;
+ }
+ binder_proc_unlock(proc, __LINE__);
}
if (defer & BINDER_DEFERRED_FLUSH)
@@ -3294,12 +4590,10 @@
if (defer & BINDER_DEFERRED_RELEASE)
binder_deferred_release(proc); /* frees proc */
- trace_binder_unlock(__func__);
- mutex_unlock(&binder_main_lock);
- preempt_enable_no_resched();
- if (files)
- put_files_struct(files);
+ if (defer & BINDER_ZOMBIE_CLEANUP)
+ binder_clear_zombies();
} while (proc);
+
}
static DECLARE_WORK(binder_deferred_work, binder_deferred_func);
@@ -3316,50 +4610,68 @@
mutex_unlock(&binder_deferred_lock);
}
-static void print_binder_transaction(struct seq_file *m, const char *prefix,
- struct binder_transaction *t)
+static void _print_binder_transaction(struct seq_file *m,
+ struct binder_proc *proc,
+ const char *prefix,
+ struct binder_transaction *t)
{
+ struct binder_thread *from = t->from;
+ struct binder_thread *to_thread = t->to_thread;
+ struct binder_proc *to_proc = t->to_proc;
+ struct binder_buffer *buffer = t->buffer;
+
seq_printf(m,
- "%s %d: %pK from %d:%d to %d:%d code %x flags %x pri %ld r%d",
+ "%s %d: %pK from %d:%d to %d:%d code %x flags %x pri %d:%d r%d",
prefix, t->debug_id, t,
- t->from ? t->from->proc->pid : 0,
- t->from ? t->from->pid : 0,
- t->to_proc ? t->to_proc->pid : 0,
- t->to_thread ? t->to_thread->pid : 0,
- t->code, t->flags, t->priority, t->need_reply);
- if (t->buffer == NULL) {
+ from ? from->proc->pid : 0,
+ from ? from->pid : 0,
+ to_proc ? to_proc->pid : 0,
+ to_thread ? to_thread->pid : 0,
+ t->code, t->flags, t->priority.sched_policy,
+ t->priority.prio, t->need_reply);
+
+ if (proc != t->to_proc) {
+ /*
+ * Can only safely deref buffer if we are holding the
+ * correct proc lock for this node
+ */
+ seq_puts(m, "\n");
+ return;
+ }
+ if (buffer == NULL) {
seq_puts(m, " buffer free\n");
return;
}
- if (t->buffer->target_node)
- seq_printf(m, " node %d",
- t->buffer->target_node->debug_id);
+ if (buffer->target_node)
+ seq_printf(m, " node %d", buffer->target_node->debug_id);
seq_printf(m, " size %zd:%zd data %pK\n",
- t->buffer->data_size, t->buffer->offsets_size,
- t->buffer->data);
-}
-
-static void print_binder_buffer(struct seq_file *m, const char *prefix,
- struct binder_buffer *buffer)
-{
- seq_printf(m, "%s %d: %pK size %zd:%zd %s\n",
- prefix, buffer->debug_id, buffer->data,
buffer->data_size, buffer->offsets_size,
- buffer->transaction ? "active" : "delivered");
+ buffer->data);
}
-static void print_binder_work(struct seq_file *m, const char *prefix,
- const char *transaction_prefix,
- struct binder_work *w)
+static void _print_binder_work(struct seq_file *m,
+ struct binder_proc *proc,
+ const char *prefix,
+ const char *transaction_prefix,
+ struct binder_work *w)
{
struct binder_node *node;
struct binder_transaction *t;
+ BUG_ON(!spin_is_locked(&w->wlist->lock));
+
switch (w->type) {
case BINDER_WORK_TRANSACTION:
t = container_of(w, struct binder_transaction, work);
- print_binder_transaction(m, transaction_prefix, t);
+ _print_binder_transaction(m, proc, transaction_prefix, t);
break;
+ case BINDER_WORK_RETURN_ERROR: {
+ struct binder_error *e = container_of(
+ w, struct binder_error, work);
+
+ seq_printf(m, "%stransaction error: %u\n",
+ prefix, e->cmd);
+ } break;
case BINDER_WORK_TRANSACTION_COMPLETE:
seq_printf(m, "%stransaction complete\n", prefix);
break;
@@ -3384,51 +4696,64 @@
}
}
-static void print_binder_thread(struct seq_file *m,
- struct binder_thread *thread,
- int print_always)
+static void _print_binder_thread(struct seq_file *m,
+ struct binder_thread *thread,
+ int print_always)
{
struct binder_transaction *t;
struct binder_work *w;
size_t start_pos = m->count;
size_t header_pos;
- seq_printf(m, " thread %d: l %02x\n", thread->pid, thread->looper);
+ BUG_ON(!spin_is_locked(&thread->proc->proc_lock));
+
+ seq_printf(m, " thread %d: l %02x need_return %d\n",
+ thread->pid, thread->looper,
+ READ_ONCE(thread->looper_need_return));
header_pos = m->count;
t = thread->transaction_stack;
while (t) {
if (t->from == thread) {
- print_binder_transaction(m,
- " outgoing transaction", t);
+ _print_binder_transaction(m, thread->proc,
+ " outgoing transaction", t);
t = t->from_parent;
} else if (t->to_thread == thread) {
- print_binder_transaction(m,
- " incoming transaction", t);
+ _print_binder_transaction(m, thread->proc,
+ " incoming transaction", t);
t = t->to_parent;
} else {
- print_binder_transaction(m, " bad transaction", t);
+ _print_binder_transaction(m, thread->proc,
+ " bad transaction", t);
t = NULL;
}
}
- list_for_each_entry(w, &thread->todo, entry) {
- print_binder_work(m, " ", " pending transaction", w);
+ spin_lock(&thread->todo.lock);
+ list_for_each_entry(w, &thread->todo.list, entry) {
+ _print_binder_work(m, thread->proc, " ",
+ " pending transaction", w);
}
+ spin_unlock(&thread->todo.lock);
if (!print_always && m->count == header_pos)
m->count = start_pos;
}
-static void print_binder_node(struct seq_file *m, struct binder_node *node)
+static void _print_binder_node(struct seq_file *m,
+ struct binder_node *node)
{
struct binder_ref *ref;
struct binder_work *w;
int count;
+ struct binder_proc *proc = node->proc;
+
+ BUG_ON(!spin_is_locked(&proc->proc_lock));
count = 0;
hlist_for_each_entry(ref, &node->refs, node_entry)
count++;
- seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d",
+ seq_printf(m, " node %d: u%016llx c%016llx pri %d:%d hs %d hw %d ls %d lw %d is %d iw %d",
node->debug_id, (u64)node->ptr, (u64)node->cookie,
+ node->sched_policy, node->min_priority,
node->has_strong_ref, node->has_weak_ref,
node->local_strong_refs, node->local_weak_refs,
node->internal_strong_refs, count);
@@ -3438,16 +4763,21 @@
seq_printf(m, " %d", ref->proc->pid);
}
seq_puts(m, "\n");
- list_for_each_entry(w, &node->async_todo, entry)
- print_binder_work(m, " ",
- " pending async transaction", w);
+ spin_lock(&node->async_todo.lock);
+ list_for_each_entry(w, &node->async_todo.list, entry)
+ _print_binder_work(m, node->proc, " ",
+ " pending async transaction", w);
+ spin_unlock(&node->async_todo.lock);
}
-static void print_binder_ref(struct seq_file *m, struct binder_ref *ref)
+static void _print_binder_ref(struct seq_file *m,
+ struct binder_ref *ref)
{
+ BUG_ON(!spin_is_locked(&ref->proc->proc_lock));
seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n",
ref->debug_id, ref->desc, ref->node->proc ? "" : "dead ",
- ref->node->debug_id, ref->strong, ref->weak, ref->death);
+ ref->node->debug_id, atomic_read(&ref->strong),
+ atomic_read(&ref->weak), ref->death);
}
static void print_binder_proc(struct seq_file *m,
@@ -3459,33 +4789,39 @@
size_t header_pos;
seq_printf(m, "proc %d\n", proc->pid);
+ seq_printf(m, "context %s\n", proc->context->name);
header_pos = m->count;
+ binder_proc_lock(proc, __LINE__);
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
- print_binder_thread(m, rb_entry(n, struct binder_thread,
- rb_node), print_all);
+ _print_binder_thread(m, rb_entry(n, struct binder_thread,
+ rb_node), print_all);
for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) {
struct binder_node *node = rb_entry(n, struct binder_node,
rb_node);
if (print_all || node->has_async_transaction)
- print_binder_node(m, node);
+ _print_binder_node(m, node);
}
if (print_all) {
for (n = rb_first(&proc->refs_by_desc);
n != NULL;
n = rb_next(n))
- print_binder_ref(m, rb_entry(n, struct binder_ref,
- rb_node_desc));
+ _print_binder_ref(m, rb_entry(n, struct binder_ref,
+ rb_node_desc));
}
- for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n))
- print_binder_buffer(m, " buffer",
- rb_entry(n, struct binder_buffer, rb_node));
- list_for_each_entry(w, &proc->todo, entry)
- print_binder_work(m, " ", " pending transaction", w);
- list_for_each_entry(w, &proc->delivered_death, entry) {
+ binder_proc_unlock(proc, __LINE__);
+
+ binder_alloc_print_allocated(m, &proc->alloc);
+ spin_lock(&proc->todo.lock);
+ list_for_each_entry(w, &proc->todo.list, entry)
+ _print_binder_work(m, proc, " ", " pending transaction", w);
+ spin_unlock(&proc->todo.lock);
+ spin_lock(&proc->delivered_death.lock);
+ list_for_each_entry(w, &proc->delivered_death.list, entry) {
seq_puts(m, " has delivered dead binder\n");
break;
}
+ spin_unlock(&proc->delivered_death.lock);
if (!print_all && m->count == header_pos)
m->count = start_pos;
}
@@ -3528,7 +4864,9 @@
"BC_EXIT_LOOPER",
"BC_REQUEST_DEATH_NOTIFICATION",
"BC_CLEAR_DEATH_NOTIFICATION",
- "BC_DEAD_BINDER_DONE"
+ "BC_DEAD_BINDER_DONE",
+ "BC_TRANSACTION_SG",
+ "BC_REPLY_SG",
};
static const char * const binder_objstat_strings[] = {
@@ -3548,30 +4886,43 @@
BUILD_BUG_ON(ARRAY_SIZE(stats->bc) !=
ARRAY_SIZE(binder_command_strings));
+
for (i = 0; i < ARRAY_SIZE(stats->bc); i++) {
- if (stats->bc[i])
+ int temp = atomic_read(&stats->bc[i]);
+
+ if (temp)
seq_printf(m, "%s%s: %d\n", prefix,
- binder_command_strings[i], stats->bc[i]);
+ binder_command_strings[i], temp);
}
BUILD_BUG_ON(ARRAY_SIZE(stats->br) !=
ARRAY_SIZE(binder_return_strings));
+
for (i = 0; i < ARRAY_SIZE(stats->br); i++) {
- if (stats->br[i])
+ int temp = atomic_read(&stats->br[i]);
+
+ if (temp)
seq_printf(m, "%s%s: %d\n", prefix,
- binder_return_strings[i], stats->br[i]);
+ binder_return_strings[i], temp);
}
BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) !=
ARRAY_SIZE(binder_objstat_strings));
BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) !=
ARRAY_SIZE(stats->obj_deleted));
+
for (i = 0; i < ARRAY_SIZE(stats->obj_created); i++) {
- if (stats->obj_created[i] || stats->obj_deleted[i])
- seq_printf(m, "%s%s: active %d total %d\n", prefix,
+ int created = atomic_read(&stats->obj_created[i]);
+ int deleted = atomic_read(&stats->obj_deleted[i]);
+ int zombie = atomic_read(&stats->obj_zombie[i]);
+
+ if (created || deleted || zombie)
+ seq_printf(m, "%s%s: active %d zombie %d total %d\n",
+ prefix,
binder_objstat_strings[i],
- stats->obj_created[i] - stats->obj_deleted[i],
- stats->obj_created[i]);
+ created - deleted - zombie,
+ zombie,
+ created);
}
}
@@ -3580,49 +4931,79 @@
{
struct binder_work *w;
struct rb_node *n;
+ struct binder_node *node;
+ struct binder_thread *thread;
+ struct binder_ref *ref;
int count, strong, weak;
+ int zombie_threads;
+ int zombie_nodes;
+ int zombie_refs;
- seq_printf(m, "proc %d\n", proc->pid);
- count = 0;
+ seq_printf(m, "proc %d%s\n", proc->pid,
+ proc->is_zombie ? " (ZOMBIE)" : "");
+ seq_printf(m, "context %s\n", proc->context->name);
+ seq_printf(m, "context FIFO: %d\n", proc->context->inherit_fifo_prio);
+ seq_printf(m, " cleared: procs=%d nodes=%d threads=%d\n",
+ cleared_procs, cleared_nodes, cleared_threads);
+ zombie_threads = zombie_nodes = zombie_refs = count = 0;
+
+ binder_proc_lock(proc, __LINE__);
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
count++;
+ binder_proc_unlock(proc, __LINE__);
seq_printf(m, " threads: %d\n", count);
seq_printf(m, " requested threads: %d+%d/%d\n"
" ready threads %d\n"
- " free async space %zd\n", proc->requested_threads,
- proc->requested_threads_started, proc->max_threads,
- proc->ready_threads, proc->free_async_space);
+ " free async space %zd\n",
+ proc->requested_threads,
+ proc->requested_threads_started,
+ proc->max_threads,
+ atomic_read(&proc->ready_threads),
+ binder_alloc_get_free_async_space(&proc->alloc));
count = 0;
- for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n))
- count++;
+ binder_proc_lock(proc, __LINE__);
+ if (!proc->is_zombie)
+ for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n))
+ count++;
+ else {
+ hlist_for_each_entry(node, &proc->zombie_nodes, dead_node)
+ zombie_nodes++;
+ hlist_for_each_entry(thread, &proc->zombie_threads,
+ zombie_thread)
+ zombie_threads++;
+ hlist_for_each_entry(ref, &proc->zombie_refs, zombie_ref)
+ zombie_refs++;
+ }
+ binder_proc_unlock(proc, __LINE__);
+ seq_printf(m, " active threads: %d\n", proc->active_thread_count);
seq_printf(m, " nodes: %d\n", count);
+ seq_printf(m, " zombie nodes: %d\n", zombie_nodes);
+ seq_printf(m, " zombie threads: %d\n", zombie_threads);
+ seq_printf(m, " zombie refs: %d\n", zombie_refs);
count = 0;
strong = 0;
weak = 0;
+ binder_proc_lock(proc, __LINE__);
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
struct binder_ref *ref = rb_entry(n, struct binder_ref,
rb_node_desc);
count++;
- strong += ref->strong;
- weak += ref->weak;
+ strong += atomic_read(&ref->strong);
+ weak += atomic_read(&ref->weak);
}
+ binder_proc_unlock(proc, __LINE__);
seq_printf(m, " refs: %d s %d w %d\n", count, strong, weak);
- count = 0;
- for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n))
- count++;
+ count = binder_alloc_get_allocated_count(&proc->alloc);
seq_printf(m, " buffers: %d\n", count);
count = 0;
- list_for_each_entry(w, &proc->todo, entry) {
- switch (w->type) {
- case BINDER_WORK_TRANSACTION:
+ spin_lock(&proc->todo.lock);
+ list_for_each_entry(w, &proc->todo.list, entry) {
+ if (w->type == BINDER_WORK_TRANSACTION)
count++;
- break;
- default:
- break;
- }
}
+ spin_unlock(&proc->todo.lock);
seq_printf(m, " pending transactions: %d\n", count);
print_binder_stats(m, " ", &proc->stats);
@@ -3632,83 +5013,76 @@
static int binder_state_show(struct seq_file *m, void *unused)
{
struct binder_proc *proc;
- struct binder_node *node;
- int do_lock = !binder_debug_no_lock;
-
- if (do_lock)
- binder_lock(__func__);
seq_puts(m, "binder state:\n");
- if (!hlist_empty(&binder_dead_nodes))
- seq_puts(m, "dead nodes:\n");
- hlist_for_each_entry(node, &binder_dead_nodes, dead_node)
- print_binder_node(m, node);
-
+ mutex_lock(&binder_procs_lock);
hlist_for_each_entry(proc, &binder_procs, proc_node)
print_binder_proc(m, proc, 1);
- if (do_lock)
- binder_unlock(__func__);
+ mutex_unlock(&binder_procs_lock);
return 0;
}
static int binder_stats_show(struct seq_file *m, void *unused)
{
struct binder_proc *proc;
- int do_lock = !binder_debug_no_lock;
-
- if (do_lock)
- binder_lock(__func__);
+ int proc_count = 0;
+ int i, sum = 0, maxactive = 0;
seq_puts(m, "binder stats:\n");
print_binder_stats(m, "", &binder_stats);
- hlist_for_each_entry(proc, &binder_procs, proc_node)
+ mutex_lock(&binder_procs_lock);
+ hlist_for_each_entry(proc, &binder_procs, proc_node) {
+ proc_count++;
print_binder_proc_stats(m, proc);
- if (do_lock)
- binder_unlock(__func__);
+ }
+ mutex_unlock(&binder_procs_lock);
+
+ for (i = 0; i < SEQ_BUCKETS; i++) {
+ sum += binder_active_threads[i].active_count;
+ maxactive = max(maxactive,
+ binder_active_threads[i].max_active_count);
+ seq_printf(m, " activeThread[%d]: %d/%d\n", i,
+ binder_active_threads[i].active_count,
+ binder_active_threads[i].max_active_count);
+ }
+
+ seq_printf(m, "procs=%d active_threads=%d/%d zombie_procs=%d/%d\n",
+ proc_count,
+ sum, maxactive,
+ zombie_procs.active_count,
+ zombie_procs.max_active_count);
return 0;
}
static int binder_transactions_show(struct seq_file *m, void *unused)
{
struct binder_proc *proc;
- int do_lock = !binder_debug_no_lock;
-
- if (do_lock)
- binder_lock(__func__);
seq_puts(m, "binder transactions:\n");
+ mutex_lock(&binder_procs_lock);
hlist_for_each_entry(proc, &binder_procs, proc_node)
print_binder_proc(m, proc, 0);
- if (do_lock)
- binder_unlock(__func__);
+ mutex_unlock(&binder_procs_lock);
return 0;
}
static int binder_proc_show(struct seq_file *m, void *unused)
{
struct binder_proc *itr;
- struct binder_proc *proc = m->private;
- int do_lock = !binder_debug_no_lock;
- bool valid_proc = false;
+ int pid = (unsigned long)m->private;
- if (do_lock)
- binder_lock(__func__);
-
+ mutex_lock(&binder_procs_lock);
hlist_for_each_entry(itr, &binder_procs, proc_node) {
- if (itr == proc) {
- valid_proc = true;
- break;
+ if (itr->pid == pid) {
+ seq_puts(m, "binder proc state:\n");
+ print_binder_proc(m, itr, 1);
}
}
- if (valid_proc) {
- seq_puts(m, "binder proc state:\n");
- print_binder_proc(m, proc, 1);
- }
- if (do_lock)
- binder_unlock(__func__);
+ mutex_unlock(&binder_procs_lock);
+
return 0;
}
@@ -3716,24 +5090,26 @@
struct binder_transaction_log_entry *e)
{
seq_printf(m,
- "%d: %s from %d:%d to %d:%d node %d handle %d size %d:%d\n",
+ "%d: %s from %d:%d to %d:%d context %s node %d handle %d size %d:%d ret %d/%d l=%d\n",
e->debug_id, (e->call_type == 2) ? "reply" :
((e->call_type == 1) ? "async" : "call "), e->from_proc,
- e->from_thread, e->to_proc, e->to_thread, e->to_node,
- e->target_handle, e->data_size, e->offsets_size);
+ e->from_thread, e->to_proc, e->to_thread, e->context_name,
+ e->to_node, e->target_handle, e->data_size, e->offsets_size,
+ e->return_error, e->return_error_param,
+ e->return_error_line);
}
static int binder_transaction_log_show(struct seq_file *m, void *unused)
{
struct binder_transaction_log *log = m->private;
+ uint64_t count = atomic64_read(&log->cur);
+ uint64_t cur = count % ARRAY_SIZE(log->entry);
int i;
- if (log->full) {
- for (i = log->next; i < ARRAY_SIZE(log->entry); i++)
- print_binder_transaction_log_entry(m, &log->entry[i]);
+ for (i = 0; i < ARRAY_SIZE(log->entry) && i < count; i++) {
+ uint64_t index = cur++ % ARRAY_SIZE(log->entry);
+ print_binder_transaction_log_entry(m, &log->entry[index]);
}
- for (i = 0; i < log->next; i++)
- print_binder_transaction_log_entry(m, &log->entry[i]);
return 0;
}
@@ -3748,20 +5124,48 @@
.release = binder_release,
};
-static struct miscdevice binder_miscdev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "binder",
- .fops = &binder_fops
-};
-
BINDER_DEBUG_ENTRY(state);
BINDER_DEBUG_ENTRY(stats);
BINDER_DEBUG_ENTRY(transactions);
BINDER_DEBUG_ENTRY(transaction_log);
-static int __init binder_init(void)
+static int __init init_binder_device(const char *name)
{
int ret;
+ struct binder_device *binder_device;
+
+ binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
+ if (!binder_device)
+ return -ENOMEM;
+
+ binder_device->miscdev.fops = &binder_fops;
+ binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
+ binder_device->miscdev.name = name;
+
+ binder_device->context.binder_context_mgr_uid = INVALID_UID;
+ binder_device->context.name = name;
+
+ ret = misc_register(&binder_device->miscdev);
+ if (ret < 0) {
+ kfree(binder_device);
+ return ret;
+ }
+
+ hlist_add_head(&binder_device->hlist, &binder_devices);
+
+ return ret;
+}
+
+static int __init binder_init(void)
+{
+ char *device_name, *device_names;
+ struct binder_device *device;
+ struct hlist_node *tmp;
+ int ret, i;
+
+ atomic_set(&binder_seq_count, 0);
+ atomic64_set(&binder_transaction_log.cur, ~0ULL);
+ atomic64_set(&binder_transaction_log_failed.cur, ~0ULL);
binder_deferred_workqueue = create_singlethread_workqueue("binder");
if (!binder_deferred_workqueue)
@@ -3771,7 +5175,7 @@
if (binder_debugfs_dir_entry_root)
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
binder_debugfs_dir_entry_root);
- ret = misc_register(&binder_miscdev);
+
if (binder_debugfs_dir_entry_root) {
debugfs_create_file("state",
S_IRUGO,
@@ -3799,6 +5203,47 @@
&binder_transaction_log_failed,
&binder_transaction_log_fops);
}
+
+ /*
+ * Copy the module_parameter string, because we don't want to
+ * tokenize it in-place.
+ */
+ device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);
+ if (!device_names) {
+ ret = -ENOMEM;
+ goto err_alloc_device_names_failed;
+ }
+ strcpy(device_names, binder_devices_param);
+
+ while ((device_name = strsep(&device_names, ","))) {
+ ret = init_binder_device(device_name);
+ if (ret)
+ goto err_init_binder_device_failed;
+ }
+
+ for (i = 0; i < SEQ_BUCKETS; i++) {
+ spin_lock_init(&binder_active_threads[i].lock);
+ INIT_LIST_HEAD(&binder_active_threads[i].active_threads);
+ WRITE_ONCE(binder_active_threads[i].lowest_seq, ~0ULL);
+ }
+
+ INIT_LIST_HEAD(&zombie_procs.active_threads);
+ spin_lock_init(&zombie_procs.lock);
+ WRITE_ONCE(zombie_procs.lowest_seq, ~0ULL);
+
+ return ret;
+
+err_init_binder_device_failed:
+ hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
+ misc_deregister(&device->miscdev);
+ hlist_del(&device->hlist);
+ kfree(device);
+ }
+err_alloc_device_names_failed:
+ debugfs_remove_recursive(binder_debugfs_dir_entry_root);
+
+ destroy_workqueue(binder_deferred_workqueue);
+
return ret;
}
diff --git a/drivers/staging/android/binder_alloc.c b/drivers/staging/android/binder_alloc.c
new file mode 100644
index 0000000..e2d392d
--- /dev/null
+++ b/drivers/staging/android/binder_alloc.c
@@ -0,0 +1,739 @@
+/* binder_alloc.c
+ *
+ * Android IPC Subsystem
+ *
+ * Copyright (C) 2007-2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <asm/cacheflush.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/rtmutex.h>
+#include <linux/rbtree.h>
+#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include "binder.h"
+#include "binder_alloc.h"
+#include "binder_trace.h"
+
+#define BINDER_MIN_ALLOC (6 * PAGE_SIZE)
+
+static DEFINE_MUTEX(binder_alloc_mmap_lock);
+
+enum {
+ BINDER_DEBUG_OPEN_CLOSE = 1U << 1,
+ BINDER_DEBUG_BUFFER_ALLOC = 1U << 2,
+ BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 3,
+};
+static uint32_t binder_alloc_debug_mask;
+
+module_param_named(debug_mask, binder_alloc_debug_mask,
+ uint, S_IWUSR | S_IRUGO);
+
+#define binder_alloc_debug(mask, x...) \
+ do { \
+ if (binder_alloc_debug_mask & mask) \
+ pr_info(x); \
+ } while (0)
+
+static size_t binder_buffer_size(struct binder_alloc *alloc,
+ struct binder_buffer *buffer)
+{
+ if (list_is_last(&buffer->entry, &alloc->buffers))
+ return alloc->buffer +
+ alloc->buffer_size - (void *)buffer->data;
+ return (size_t)list_entry(buffer->entry.next,
+ struct binder_buffer, entry) - (size_t)buffer->data;
+}
+
+static void binder_insert_free_buffer(struct binder_alloc *alloc,
+ struct binder_buffer *new_buffer)
+{
+ struct rb_node **p = &alloc->free_buffers.rb_node;
+ struct rb_node *parent = NULL;
+ struct binder_buffer *buffer;
+ size_t buffer_size;
+ size_t new_buffer_size;
+
+ BUG_ON(!new_buffer->free);
+
+ new_buffer_size = binder_buffer_size(alloc, new_buffer);
+
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: add free buffer, size %zd, at %pK\n",
+ alloc->pid, new_buffer_size, new_buffer);
+
+ while (*p) {
+ parent = *p;
+ buffer = rb_entry(parent, struct binder_buffer, rb_node);
+ BUG_ON(!buffer->free);
+
+ buffer_size = binder_buffer_size(alloc, buffer);
+
+ if (new_buffer_size < buffer_size)
+ p = &parent->rb_left;
+ else
+ p = &parent->rb_right;
+ }
+ rb_link_node(&new_buffer->rb_node, parent, p);
+ rb_insert_color(&new_buffer->rb_node, &alloc->free_buffers);
+}
+
+static void binder_insert_allocated_buffer(struct binder_alloc *alloc,
+ struct binder_buffer *new_buffer)
+{
+ struct rb_node **p = &alloc->allocated_buffers.rb_node;
+ struct rb_node *parent = NULL;
+ struct binder_buffer *buffer;
+
+ BUG_ON(new_buffer->free);
+
+ while (*p) {
+ parent = *p;
+ buffer = rb_entry(parent, struct binder_buffer, rb_node);
+ BUG_ON(buffer->free);
+
+ if (new_buffer < buffer)
+ p = &parent->rb_left;
+ else if (new_buffer > buffer)
+ p = &parent->rb_right;
+ else
+ BUG();
+ }
+ rb_link_node(&new_buffer->rb_node, parent, p);
+ rb_insert_color(&new_buffer->rb_node, &alloc->allocated_buffers);
+}
+
+struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc,
+ uintptr_t user_ptr)
+{
+ struct rb_node *n;
+ struct binder_buffer *buffer;
+ struct binder_buffer *kern_ptr;
+
+ mutex_lock(&alloc->mutex);
+ kern_ptr = (struct binder_buffer *)(user_ptr - alloc->user_buffer_offset
+ - offsetof(struct binder_buffer, data));
+
+ n = alloc->allocated_buffers.rb_node;
+ while (n) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ BUG_ON(buffer->free);
+
+ if (kern_ptr < buffer)
+ n = n->rb_left;
+ else if (kern_ptr > buffer)
+ n = n->rb_right;
+ else {
+ /*
+ * Guard against user threads attempting to
+ * free the buffer twice
+ */
+ if (!buffer->free_in_progress) {
+ buffer->free_in_progress = 1;
+ } else {
+ pr_err("%d:%d FREE_BUFFER u%016llx user freed buffer twice\n",
+ alloc->pid, current->pid, (u64)user_ptr);
+ buffer = NULL;
+ }
+ mutex_unlock(&alloc->mutex);
+ return buffer;
+ }
+ }
+ mutex_unlock(&alloc->mutex);
+ return NULL;
+}
+
+static int __binder_update_page_range(struct binder_alloc *alloc, int allocate,
+ void *start, void *end,
+ struct vm_area_struct *vma)
+{
+ void *page_addr;
+ unsigned long user_page_addr;
+ struct page **page;
+ struct mm_struct *mm;
+
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: %s pages %pK-%pK\n", alloc->pid,
+ allocate ? "allocate" : "free", start, end);
+
+ if (end <= start)
+ return 0;
+
+ trace_binder_update_page_range(alloc, allocate, start, end);
+
+ if (vma)
+ mm = NULL;
+ else
+ mm = get_task_mm(alloc->tsk);
+
+ if (mm) {
+ down_write(&mm->mmap_sem);
+ vma = alloc->vma;
+ if (vma && mm != alloc->vma_vm_mm) {
+ pr_err("%d: vma mm and task mm mismatch\n",
+ alloc->pid);
+ vma = NULL;
+ }
+ }
+
+ if (allocate == 0)
+ goto free_range;
+
+ if (vma == NULL) {
+ pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n",
+ alloc->pid);
+ goto err_no_vma;
+ }
+
+ for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
+ int ret;
+
+ page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
+
+ BUG_ON(*page);
+ *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
+ if (*page == NULL) {
+ pr_err("%d: binder_alloc_buf failed for page at %pK\n",
+ alloc->pid, page_addr);
+ goto err_alloc_page_failed;
+ }
+ ret = map_kernel_range_noflush((unsigned long)page_addr,
+ PAGE_SIZE, PAGE_KERNEL, page);
+ flush_cache_vmap((unsigned long)page_addr,
+ (unsigned long)page_addr + PAGE_SIZE);
+ if (ret != 1) {
+ pr_err("%d: binder_alloc_buf failed to map page at %pK in kernel\n",
+ alloc->pid, page_addr);
+ goto err_map_kernel_failed;
+ }
+ user_page_addr =
+ (uintptr_t)page_addr + alloc->user_buffer_offset;
+ ret = vm_insert_page(vma, user_page_addr, page[0]);
+ if (ret) {
+ pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
+ alloc->pid, user_page_addr);
+ goto err_vm_insert_page_failed;
+ }
+ /* vm_insert_page does not seem to increment the refcount */
+ }
+ if (mm) {
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ }
+ return 0;
+
+free_range:
+ for (page_addr = end - PAGE_SIZE; page_addr >= start;
+ page_addr -= PAGE_SIZE) {
+ page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
+ if (vma)
+ zap_page_range(vma, (uintptr_t)page_addr +
+ alloc->user_buffer_offset, PAGE_SIZE, NULL);
+err_vm_insert_page_failed:
+ unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
+err_map_kernel_failed:
+ __free_page(*page);
+ *page = NULL;
+err_alloc_page_failed:
+ ;
+ }
+err_no_vma:
+ if (mm) {
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ }
+ return vma ? -ENOMEM : -ESRCH;
+}
+
+static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
+ void *start, void *end,
+ struct vm_area_struct *vma)
+{
+ /*
+ * For regular updates, move up start if needed since MIN_ALLOC pages
+ * are always mapped
+ */
+ if (start - alloc->buffer < BINDER_MIN_ALLOC)
+ start = alloc->buffer + BINDER_MIN_ALLOC;
+
+ return __binder_update_page_range(alloc, allocate, start, end, vma);
+}
+
+struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
+ size_t data_size,
+ size_t offsets_size,
+ size_t extra_buffers_size,
+ int is_async)
+{
+ struct rb_node *n;
+ struct binder_buffer *buffer;
+ size_t buffer_size;
+ struct rb_node *best_fit = NULL;
+ void *has_page_addr;
+ void *end_page_addr;
+ size_t size, data_offsets_size;
+ struct binder_buffer *eret;
+ int ret;
+
+ mutex_lock(&alloc->mutex);
+ if (alloc->vma == NULL) {
+ pr_err("%d: binder_alloc_buf, no vma\n",
+ alloc->pid);
+ eret = ERR_PTR(-ESRCH);
+ goto error_unlock;
+ }
+
+ data_offsets_size = ALIGN(data_size, sizeof(void *)) +
+ ALIGN(offsets_size, sizeof(void *));
+
+ if (data_offsets_size < data_size || data_offsets_size < offsets_size) {
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: got transaction with invalid size %zd-%zd\n",
+ alloc->pid, data_size, offsets_size);
+ eret = ERR_PTR(-EINVAL);
+ goto error_unlock;
+ }
+ size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
+ if (size < data_offsets_size || size < extra_buffers_size) {
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: got transaction with invalid extra_buffers_size %zd\n",
+ alloc->pid, extra_buffers_size);
+ eret = ERR_PTR(-EINVAL);
+ goto error_unlock;
+ }
+ if (is_async &&
+ alloc->free_async_space < size + sizeof(struct binder_buffer)) {
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: binder_alloc_buf size %zd failed, no async space left\n",
+ alloc->pid, size);
+ eret = ERR_PTR(-ENOSPC);
+ goto error_unlock;
+ }
+
+ n = alloc->free_buffers.rb_node;
+ while (n) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ BUG_ON(!buffer->free);
+ buffer_size = binder_buffer_size(alloc, buffer);
+
+ if (size < buffer_size) {
+ best_fit = n;
+ n = n->rb_left;
+ } else if (size > buffer_size)
+ n = n->rb_right;
+ else {
+ best_fit = n;
+ break;
+ }
+ }
+ if (best_fit == NULL) {
+ size_t allocated_buffers = 0;
+ size_t largest_alloc_size = 0;
+ size_t total_alloc_size = 0;
+ size_t free_buffers = 0;
+ size_t largest_free_size = 0;
+ size_t total_free_size = 0;
+
+ for (n = rb_first(&alloc->allocated_buffers); n != NULL;
+ n = rb_next(n)) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ buffer_size = binder_buffer_size(alloc, buffer);
+ allocated_buffers++;
+ total_alloc_size += buffer_size;
+ if (buffer_size > largest_alloc_size)
+ largest_alloc_size = buffer_size;
+ }
+ for (n = rb_first(&alloc->free_buffers); n != NULL;
+ n = rb_next(n)) {
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+ buffer_size = binder_buffer_size(alloc, buffer);
+ free_buffers++;
+ total_free_size += buffer_size;
+ if (buffer_size > largest_free_size)
+ largest_free_size = buffer_size;
+ }
+ pr_err("%d: binder_alloc_buf size %zd failed, no address space\n",
+ alloc->pid, size);
+ pr_err("allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n",
+ total_alloc_size, allocated_buffers, largest_alloc_size,
+ total_free_size, free_buffers, largest_free_size);
+ eret = ERR_PTR(-ENOSPC);
+ goto error_unlock;
+ }
+ if (n == NULL) {
+ buffer = rb_entry(best_fit, struct binder_buffer, rb_node);
+ buffer_size = binder_buffer_size(alloc, buffer);
+ }
+
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: binder_alloc_buf size %zd got buffer %pK size %zd\n",
+ alloc->pid, size, buffer, buffer_size);
+
+ has_page_addr =
+ (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK);
+ if (n == NULL) {
+ if (size + sizeof(struct binder_buffer) + 4 >= buffer_size)
+ buffer_size = size; /* no room for other buffers */
+ else
+ buffer_size = size + sizeof(struct binder_buffer);
+ }
+ end_page_addr =
+ (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size);
+ if (end_page_addr > has_page_addr)
+ end_page_addr = has_page_addr;
+ ret = binder_update_page_range(alloc, 1,
+ (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL);
+ if (ret) {
+ eret = ERR_PTR(ret);
+ goto error_unlock;
+ }
+
+ rb_erase(best_fit, &alloc->free_buffers);
+ buffer->free = 0;
+ buffer->free_in_progress = 0;
+ binder_insert_allocated_buffer(alloc, buffer);
+ if (buffer_size != size) {
+ struct binder_buffer *new_buffer = (void *)buffer->data + size;
+
+ list_add(&new_buffer->entry, &buffer->entry);
+ new_buffer->free = 1;
+ binder_insert_free_buffer(alloc, new_buffer);
+ }
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: binder_alloc_buf size %zd got %pK\n",
+ alloc->pid, size, buffer);
+ buffer->data_size = data_size;
+ buffer->offsets_size = offsets_size;
+ buffer->async_transaction = is_async;
+ buffer->extra_buffers_size = extra_buffers_size;
+ if (is_async) {
+ alloc->free_async_space -= size + sizeof(struct binder_buffer);
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
+ "%d: binder_alloc_buf size %zd async free %zd\n",
+ alloc->pid, size, alloc->free_async_space);
+ }
+ mutex_unlock(&alloc->mutex);
+
+ return buffer;
+
+error_unlock:
+ mutex_unlock(&alloc->mutex);
+ return eret;
+}
+
+static void *buffer_start_page(struct binder_buffer *buffer)
+{
+ return (void *)((uintptr_t)buffer & PAGE_MASK);
+}
+
+static void *buffer_end_page(struct binder_buffer *buffer)
+{
+ return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK);
+}
+
+static void binder_delete_free_buffer(struct binder_alloc *alloc,
+ struct binder_buffer *buffer)
+{
+ struct binder_buffer *prev, *next = NULL;
+ int free_page_end = 1;
+ int free_page_start = 1;
+
+ BUG_ON(alloc->buffers.next == &buffer->entry);
+ prev = list_entry(buffer->entry.prev, struct binder_buffer, entry);
+ BUG_ON(!prev->free);
+ if (buffer_end_page(prev) == buffer_start_page(buffer)) {
+ free_page_start = 0;
+ if (buffer_end_page(prev) == buffer_end_page(buffer))
+ free_page_end = 0;
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: merge free, buffer %pK share page with %pK\n",
+ alloc->pid, buffer, prev);
+ }
+
+ if (!list_is_last(&buffer->entry, &alloc->buffers)) {
+ next = list_entry(buffer->entry.next,
+ struct binder_buffer, entry);
+ if (buffer_start_page(next) == buffer_end_page(buffer)) {
+ free_page_end = 0;
+ if (buffer_start_page(next) ==
+ buffer_start_page(buffer))
+ free_page_start = 0;
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: merge free, buffer %pK share page with %pK\n",
+ alloc->pid, buffer, prev);
+ }
+ }
+ list_del(&buffer->entry);
+ if (free_page_start || free_page_end) {
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: merge free, buffer %pK do not share page%s%s with %pK or %pK\n",
+ alloc->pid, buffer, free_page_start ? "" : " end",
+ free_page_end ? "" : " start", prev, next);
+ binder_update_page_range(alloc, 0, free_page_start ?
+ buffer_start_page(buffer) : buffer_end_page(buffer),
+ (free_page_end ? buffer_end_page(buffer) :
+ buffer_start_page(buffer)) + PAGE_SIZE, NULL);
+ }
+}
+
+static void binder_free_buf_locked(struct binder_alloc *alloc,
+ struct binder_buffer *buffer)
+{
+ size_t size, buffer_size;
+
+ buffer_size = binder_buffer_size(alloc, buffer);
+
+ size = ALIGN(buffer->data_size, sizeof(void *)) +
+ ALIGN(buffer->offsets_size, sizeof(void *)) +
+ ALIGN(buffer->extra_buffers_size, sizeof(void *));
+
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%d: binder_free_buf %pK size %zd buffer_size %zd\n",
+ alloc->pid, buffer, size, buffer_size);
+
+ BUG_ON(buffer->free);
+ BUG_ON(size > buffer_size);
+ BUG_ON(buffer->transaction != NULL);
+ BUG_ON((void *)buffer < alloc->buffer);
+ BUG_ON((void *)buffer > alloc->buffer + alloc->buffer_size);
+
+ if (buffer->async_transaction) {
+ alloc->free_async_space += size + sizeof(struct binder_buffer);
+
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
+ "%d: binder_free_buf size %zd async free %zd\n",
+ alloc->pid, size, alloc->free_async_space);
+ }
+
+ binder_update_page_range(alloc, 0,
+ (void *)PAGE_ALIGN((uintptr_t)buffer->data),
+ (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK),
+ NULL);
+
+ rb_erase(&buffer->rb_node, &alloc->allocated_buffers);
+ buffer->free = 1;
+ if (!list_is_last(&buffer->entry, &alloc->buffers)) {
+ struct binder_buffer *next = list_entry(buffer->entry.next,
+ struct binder_buffer, entry);
+
+ if (next->free) {
+ rb_erase(&next->rb_node, &alloc->free_buffers);
+ binder_delete_free_buffer(alloc, next);
+ }
+ }
+ if (alloc->buffers.next != &buffer->entry) {
+ struct binder_buffer *prev = list_entry(buffer->entry.prev,
+ struct binder_buffer, entry);
+
+ if (prev->free) {
+ binder_delete_free_buffer(alloc, buffer);
+ rb_erase(&prev->rb_node, &alloc->free_buffers);
+ buffer = prev;
+ }
+ }
+ binder_insert_free_buffer(alloc, buffer);
+}
+
+void binder_alloc_free_buf(struct binder_alloc *alloc,
+ struct binder_buffer *buffer)
+{
+ mutex_lock(&alloc->mutex);
+ binder_free_buf_locked(alloc, buffer);
+ mutex_unlock(&alloc->mutex);
+}
+
+int binder_alloc_mmap_handler(struct binder_alloc *alloc,
+ struct vm_area_struct *vma)
+{
+ int ret;
+ struct vm_struct *area;
+ const char *failure_string;
+ struct binder_buffer *buffer;
+
+ mutex_lock(&binder_alloc_mmap_lock);
+ if (alloc->buffer) {
+ ret = -EBUSY;
+ failure_string = "already mapped";
+ goto err_already_mapped;
+ }
+
+ area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
+ if (area == NULL) {
+ ret = -ENOMEM;
+ failure_string = "get_vm_area";
+ goto err_get_vm_area_failed;
+ }
+ alloc->buffer = area->addr;
+ WRITE_ONCE(alloc->user_buffer_offset,
+ vma->vm_start - (uintptr_t)alloc->buffer);
+ mutex_unlock(&binder_alloc_mmap_lock);
+
+#ifdef CONFIG_CPU_CACHE_VIPT
+ if (cache_is_vipt_aliasing()) {
+ while (CACHE_COLOUR(
+ (vma->vm_start ^ (uint32_t)alloc->buffer))) {
+ pr_info("binder_mmap: %d %lx-%lx maps %pK bad alignment\n",
+ alloc->pid, vma->vm_start, vma->vm_end,
+ alloc->buffer);
+ vma->vm_start += PAGE_SIZE;
+ }
+ }
+#endif
+ alloc->pages = kzalloc(sizeof(alloc->pages[0]) *
+ ((vma->vm_end - vma->vm_start) / PAGE_SIZE),
+ GFP_KERNEL);
+ if (alloc->pages == NULL) {
+ ret = -ENOMEM;
+ failure_string = "alloc page array";
+ goto err_alloc_pages_failed;
+ }
+ alloc->buffer_size = vma->vm_end - vma->vm_start;
+
+ if (__binder_update_page_range(alloc, 1, alloc->buffer,
+ alloc->buffer + BINDER_MIN_ALLOC, vma)) {
+ ret = -ENOMEM;
+ failure_string = "alloc small buf";
+ goto err_alloc_small_buf_failed;
+ }
+ buffer = alloc->buffer;
+ INIT_LIST_HEAD(&alloc->buffers);
+ list_add(&buffer->entry, &alloc->buffers);
+ buffer->free = 1;
+ binder_insert_free_buffer(alloc, buffer);
+ alloc->free_async_space = alloc->buffer_size / 2;
+
+ barrier();
+ alloc->vma = vma;
+ alloc->vma_vm_mm = vma->vm_mm;
+
+ return 0;
+
+err_alloc_small_buf_failed:
+ kfree(alloc->pages);
+ alloc->pages = NULL;
+err_alloc_pages_failed:
+ mutex_lock(&binder_alloc_mmap_lock);
+ vfree(alloc->buffer);
+ alloc->buffer = NULL;
+err_get_vm_area_failed:
+err_already_mapped:
+ mutex_unlock(&binder_alloc_mmap_lock);
+ pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
+ alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
+ return ret;
+}
+
+
+void binder_alloc_deferred_release(struct binder_alloc *alloc)
+{
+ struct rb_node *n;
+ int buffers, page_count;
+
+ BUG_ON(alloc->vma);
+
+ buffers = 0;
+ mutex_lock(&alloc->mutex);
+ while ((n = rb_first(&alloc->allocated_buffers))) {
+ struct binder_buffer *buffer;
+
+ buffer = rb_entry(n, struct binder_buffer, rb_node);
+
+ /* Transactiopn should already have been freed */
+ BUG_ON(buffer->transaction);
+
+ binder_free_buf_locked(alloc, buffer);
+ buffers++;
+ }
+
+ page_count = 0;
+ if (alloc->pages) {
+ int i;
+
+ for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
+ void *page_addr;
+
+ if (!alloc->pages[i])
+ continue;
+
+ page_addr = alloc->buffer + i * PAGE_SIZE;
+ binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+ "%s: %d: page %d at %pK not freed\n",
+ __func__, alloc->pid, i, page_addr);
+ unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
+ __free_page(alloc->pages[i]);
+ page_count++;
+ }
+ kfree(alloc->pages);
+ vfree(alloc->buffer);
+ }
+ mutex_unlock(&alloc->mutex);
+
+ binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE,
+ "%s: %d buffers %d, pages %d\n",
+ __func__, alloc->pid, buffers, page_count);
+}
+
+static void print_binder_buffer(struct seq_file *m, const char *prefix,
+ struct binder_buffer *buffer)
+{
+ seq_printf(m, "%s %d: %pK size %zd:%zd:%zd %s\n",
+ prefix, buffer->debug_id, buffer->data,
+ buffer->data_size, buffer->offsets_size,
+ buffer->extra_buffers_size,
+ buffer->transaction ? "active" : "delivered");
+}
+
+void binder_alloc_print_allocated(struct seq_file *m,
+ struct binder_alloc *alloc)
+{
+ struct rb_node *n;
+
+ mutex_lock(&alloc->mutex);
+ for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
+ print_binder_buffer(m, " buffer",
+ rb_entry(n, struct binder_buffer, rb_node));
+ mutex_unlock(&alloc->mutex);
+}
+
+int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
+{
+ struct rb_node *n;
+ int count = 0;
+
+ mutex_lock(&alloc->mutex);
+ for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
+ count++;
+ mutex_unlock(&alloc->mutex);
+ return count;
+}
+
+
+void binder_alloc_vma_close(struct binder_alloc *alloc)
+{
+ WRITE_ONCE(alloc->vma, NULL);
+ WRITE_ONCE(alloc->vma_vm_mm, NULL);
+ barrier();
+}
+
+void binder_alloc_init(struct binder_alloc *alloc)
+{
+ alloc->tsk = current->group_leader;
+ alloc->pid = current->group_leader->pid;
+ mutex_init(&alloc->mutex);
+}
+
diff --git a/drivers/staging/android/binder_alloc.h b/drivers/staging/android/binder_alloc.h
new file mode 100644
index 0000000..96f7c7f2
--- /dev/null
+++ b/drivers/staging/android/binder_alloc.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * Based on, but no longer compatible with, the original
+ * OpenBinder.org binder driver interface, which is:
+ *
+ * Copyright (c) 2005 Palmsource, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _LINUX_BINDER_ALLOC_H
+#define _LINUX_BINDER_ALLOC_H
+
+#include <linux/rbtree.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/rtmutex.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+
+struct binder_transaction;
+
+struct binder_buffer {
+ struct list_head entry; /* free and allocated entries by address */
+ struct rb_node rb_node; /* free entry by size or allocated entry */
+ /* by address */
+ unsigned free:1;
+ unsigned allow_user_free:1;
+ unsigned async_transaction:1;
+ unsigned free_in_progress:1;
+ unsigned debug_id:28;
+
+ struct binder_transaction *transaction;
+
+ struct binder_node *target_node;
+ size_t data_size;
+ size_t offsets_size;
+ size_t extra_buffers_size;
+ uint8_t data[0];
+};
+
+struct binder_alloc {
+ struct mutex mutex;
+ struct task_struct *tsk;
+ struct vm_area_struct *vma;
+ struct mm_struct *vma_vm_mm;
+ void *buffer;
+ ptrdiff_t user_buffer_offset;
+ struct list_head buffers;
+ struct rb_root free_buffers;
+ struct rb_root allocated_buffers;
+ size_t free_async_space;
+ struct page **pages;
+ size_t buffer_size;
+ uint32_t buffer_free;
+ int pid;
+};
+
+extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
+ size_t data_size,
+ size_t offsets_size,
+ size_t extra_buffers_size,
+ int is_async);
+extern void binder_alloc_init(struct binder_alloc *alloc);
+extern void binder_alloc_vma_close(struct binder_alloc *alloc);
+extern struct binder_buffer *
+binder_alloc_prepare_to_free(struct binder_alloc *alloc,
+ uintptr_t user_ptr);
+extern void binder_alloc_free_buf(struct binder_alloc *alloc,
+ struct binder_buffer *buffer);
+extern int binder_alloc_mmap_handler(struct binder_alloc *alloc,
+ struct vm_area_struct *vma);
+extern void binder_alloc_deferred_release(struct binder_alloc *alloc);
+extern int binder_alloc_get_allocated_count(struct binder_alloc *alloc);
+extern void binder_alloc_print_allocated(struct seq_file *m,
+ struct binder_alloc *alloc);
+
+static inline size_t
+binder_alloc_get_free_async_space(struct binder_alloc *alloc)
+{
+ size_t free_async_space;
+
+ mutex_lock(&alloc->mutex);
+ free_async_space = alloc->free_async_space;
+ mutex_unlock(&alloc->mutex);
+ return free_async_space;
+}
+
+static inline ptrdiff_t
+binder_alloc_get_user_buffer_offset(struct binder_alloc *alloc)
+{
+ /*
+ * user_buffer_offset is constant if vma is set and
+ * undefined if vma is not set. It is possible to
+ * get here with !alloc->vma if the target process
+ * is dying while a transaction is being initiated.
+ * Returning the old value is ok in this case and
+ * the transaction will fail.
+ */
+ return READ_ONCE(alloc->user_buffer_offset);
+}
+
+#endif /* _LINUX_BINDER_ALLOC_H */
+
diff --git a/drivers/staging/android/binder_trace.h b/drivers/staging/android/binder_trace.h
index 7f20f3d..1e7311b 100644
--- a/drivers/staging/android/binder_trace.h
+++ b/drivers/staging/android/binder_trace.h
@@ -42,27 +42,6 @@
TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg)
);
-DECLARE_EVENT_CLASS(binder_lock_class,
- TP_PROTO(const char *tag),
- TP_ARGS(tag),
- TP_STRUCT__entry(
- __field(const char *, tag)
- ),
- TP_fast_assign(
- __entry->tag = tag;
- ),
- TP_printk("tag=%s", __entry->tag)
-);
-
-#define DEFINE_BINDER_LOCK_EVENT(name) \
-DEFINE_EVENT(binder_lock_class, name, \
- TP_PROTO(const char *func), \
- TP_ARGS(func))
-
-DEFINE_BINDER_LOCK_EVENT(binder_lock);
-DEFINE_BINDER_LOCK_EVENT(binder_locked);
-DEFINE_BINDER_LOCK_EVENT(binder_unlock);
-
DECLARE_EVENT_CLASS(binder_function_return_class,
TP_PROTO(int ret),
TP_ARGS(ret),
@@ -84,6 +63,30 @@
DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_write_done);
DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_read_done);
+TRACE_EVENT(binder_set_priority,
+ TP_PROTO(int proc, int thread, unsigned int old_prio,
+ unsigned int desired_prio, unsigned int new_prio),
+ TP_ARGS(proc, thread, old_prio, new_prio, desired_prio),
+
+ TP_STRUCT__entry(
+ __field(int, proc)
+ __field(int, thread)
+ __field(unsigned int, old_prio)
+ __field(unsigned int, new_prio)
+ __field(unsigned int, desired_prio)
+ ),
+ TP_fast_assign(
+ __entry->proc = proc;
+ __entry->thread = thread;
+ __entry->old_prio = old_prio;
+ __entry->new_prio = new_prio;
+ __entry->desired_prio = desired_prio;
+ ),
+ TP_printk("proc=%d thread=%d old=%d => new=%d desired=%d",
+ __entry->proc, __entry->thread, __entry->old_prio,
+ __entry->new_prio, __entry->desired_prio)
+);
+
TRACE_EVENT(binder_wait_for_work,
TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo),
TP_ARGS(proc_work, transaction_stack, thread_todo),
@@ -268,9 +271,9 @@
TP_ARGS(buffer));
TRACE_EVENT(binder_update_page_range,
- TP_PROTO(struct binder_proc *proc, bool allocate,
+ TP_PROTO(struct binder_alloc *alloc, bool allocate,
void *start, void *end),
- TP_ARGS(proc, allocate, start, end),
+ TP_ARGS(alloc, allocate, start, end),
TP_STRUCT__entry(
__field(int, proc)
__field(bool, allocate)
@@ -278,9 +281,9 @@
__field(size_t, size)
),
TP_fast_assign(
- __entry->proc = proc->pid;
+ __entry->proc = alloc->pid;
__entry->allocate = allocate;
- __entry->offset = start - proc->buffer;
+ __entry->offset = start - alloc->buffer;
__entry->size = end - start;
),
TP_printk("proc=%d allocate=%d offset=%zu size=%zu",
diff --git a/drivers/staging/android/ion/ion_system_secure_heap.c b/drivers/staging/android/ion/ion_system_secure_heap.c
index 10b63ee..251763a8 100644
--- a/drivers/staging/android/ion/ion_system_secure_heap.c
+++ b/drivers/staging/android/ion/ion_system_secure_heap.c
@@ -39,10 +39,10 @@
static bool is_cp_flag_present(unsigned long flags)
{
- return flags && (ION_FLAG_CP_TOUCH ||
- ION_FLAG_CP_BITSTREAM ||
- ION_FLAG_CP_PIXEL ||
- ION_FLAG_CP_NON_PIXEL ||
+ return flags & (ION_FLAG_CP_TOUCH |
+ ION_FLAG_CP_BITSTREAM |
+ ION_FLAG_CP_PIXEL |
+ ION_FLAG_CP_NON_PIXEL |
ION_FLAG_CP_CAMERA);
}
diff --git a/drivers/staging/android/uapi/binder.h b/drivers/staging/android/uapi/binder.h
index dba4cef..d51eab3 100644
--- a/drivers/staging/android/uapi/binder.h
+++ b/drivers/staging/android/uapi/binder.h
@@ -32,11 +32,51 @@
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
+ BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),
+ BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
};
-enum {
+/**
+ * enum flat_binder_object_flags - flags for use in flat_binder_object.flags
+ */
+enum flat_binder_object_flags {
+ /**
+ * @FLAT_BINDER_FLAG_PRIORITY_MASK: bit-mask for min scheduler priority
+ *
+ * These bits can be used to set the minimum scheduler priority
+ * at which transactions into this node should run. Valid values
+ * in these bits depend on the scheduler policy encoded in
+ * @FLAT_BINDER_FLAG_SCHED_POLICY_MASK.
+ *
+ * For SCHED_NORMAL, the valid range is between [-20..19]
+ * For SCHED_FIFO/SCHED_RR, the value can run between [1..99]
+ */
FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff,
+ /**
+ * @FLAT_BINDER_FLAG_ACCEPTS_FDS: whether the node accepts fds.
+ */
FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100,
+ /**
+ * @FLAT_BINDER_FLAG_SCHED_POLICY_MASK: bit-mask for scheduling policy
+ *
+ * These two bits can be used to set the min scheduling policy at which
+ * transactions on this node should run. These match the UAPI
+ * scheduler policy values, eg:
+ * 00b: SCHED_NORMAL
+ * 01b: SCHED_FIFO
+ * 10b: SCHED_RR
+ * 11b: SCHED_BATCH
+ */
+ FLAT_BINDER_FLAG_SCHED_POLICY_MASK = 0x600,
+};
+
+/**
+ * enum flat_binder_object_shifts: shift values for flat_binder_object_flags
+ * @FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT: shift for getting scheduler policy.
+ *
+ */
+enum flat_binder_object_shifts {
+ FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT = 9,
};
#ifdef BINDER_IPC_32BIT
@@ -47,6 +87,14 @@
typedef __u64 binder_uintptr_t;
#endif
+/**
+ * struct binder_object_header - header shared by all binder metadata objects.
+ * @type: type of the object
+ */
+struct binder_object_header {
+ __u32 type;
+};
+
/*
* This is the flattened representation of a Binder object for transfer
* between processes. The 'offsets' supplied as part of a binder transaction
@@ -55,9 +103,8 @@
* between processes.
*/
struct flat_binder_object {
- /* 8 bytes for large_flat_header. */
- __u32 type;
- __u32 flags;
+ struct binder_object_header hdr;
+ __u32 flags;
/* 8 bytes of data. */
union {
@@ -69,6 +116,84 @@
binder_uintptr_t cookie;
};
+/**
+ * struct binder_fd_object - describes a filedescriptor to be fixed up.
+ * @hdr: common header structure
+ * @pad_flags: padding to remain compatible with old userspace code
+ * @pad_binder: padding to remain compatible with old userspace code
+ * @fd: file descriptor
+ * @cookie: opaque data, used by user-space
+ */
+struct binder_fd_object {
+ struct binder_object_header hdr;
+ __u32 pad_flags;
+ union {
+ binder_uintptr_t pad_binder;
+ __u32 fd;
+ };
+
+ binder_uintptr_t cookie;
+};
+
+/* struct binder_buffer_object - object describing a userspace buffer
+ * @hdr: common header structure
+ * @flags: one or more BINDER_BUFFER_* flags
+ * @buffer: address of the buffer
+ * @length: length of the buffer
+ * @parent: index in offset array pointing to parent buffer
+ * @parent_offset: offset in @parent pointing to this buffer
+ *
+ * A binder_buffer object represents an object that the
+ * binder kernel driver can copy verbatim to the target
+ * address space. A buffer itself may be pointed to from
+ * within another buffer, meaning that the pointer inside
+ * that other buffer needs to be fixed up as well. This
+ * can be done by setting the BINDER_BUFFER_FLAG_HAS_PARENT
+ * flag in @flags, by setting @parent buffer to the index
+ * in the offset array pointing to the parent binder_buffer_object,
+ * and by setting @parent_offset to the offset in the parent buffer
+ * at which the pointer to this buffer is located.
+ */
+struct binder_buffer_object {
+ struct binder_object_header hdr;
+ __u32 flags;
+ binder_uintptr_t buffer;
+ binder_size_t length;
+ binder_size_t parent;
+ binder_size_t parent_offset;
+};
+
+enum {
+ BINDER_BUFFER_FLAG_HAS_PARENT = 0x01,
+};
+
+/* struct binder_fd_array_object - object describing an array of fds in a buffer
+ * @hdr: common header structure
+ * @num_fds: number of file descriptors in the buffer
+ * @parent: index in offset array to buffer holding the fd array
+ * @parent_offset: start offset of fd array in the buffer
+ *
+ * A binder_fd_array object represents an array of file
+ * descriptors embedded in a binder_buffer_object. It is
+ * different from a regular binder_buffer_object because it
+ * describes a list of file descriptors to fix up, not an opaque
+ * blob of memory, and hence the kernel needs to treat it differently.
+ *
+ * An example of how this would be used is with Android's
+ * native_handle_t object, which is a struct with a list of integers
+ * and a list of file descriptors. The native_handle_t struct itself
+ * will be represented by a struct binder_buffer_objct, whereas the
+ * embedded list of file descriptors is represented by a
+ * struct binder_fd_array_object with that binder_buffer_object as
+ * a parent.
+ */
+struct binder_fd_array_object {
+ struct binder_object_header hdr;
+ binder_size_t num_fds;
+ binder_size_t parent;
+ binder_size_t parent_offset;
+};
+
/*
* On 64-bit platforms where user code may run in 32-bits the driver must
* translate the buffer (and local binder) addresses appropriately.
@@ -103,6 +228,7 @@
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32)
#define BINDER_THREAD_EXIT _IOW('b', 8, __s32)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
+#define BINDER_SET_INHERIT_FIFO_PRIO _IO('b', 10)
/*
* NOTE: Two special error codes you should check for when calling
@@ -161,6 +287,11 @@
} data;
};
+struct binder_transaction_data_sg {
+ struct binder_transaction_data transaction_data;
+ binder_size_t buffers_size;
+};
+
struct binder_ptr_cookie {
binder_uintptr_t ptr;
binder_uintptr_t cookie;
@@ -345,6 +476,12 @@
/*
* void *: cookie
*/
+
+ BC_TRANSACTION_SG = _IOW('c', 17, struct binder_transaction_data_sg),
+ BC_REPLY_SG = _IOW('c', 18, struct binder_transaction_data_sg),
+ /*
+ * binder_transaction_data_sg: the sent command.
+ */
};
#endif /* _UAPI_LINUX_BINDER_H */
diff --git a/drivers/staging/bcm/Kconfig b/drivers/staging/bcm/Kconfig
index 8acf4b2..94e0470 100644
--- a/drivers/staging/bcm/Kconfig
+++ b/drivers/staging/bcm/Kconfig
@@ -1,6 +1,7 @@
config BCM_WIMAX
tristate "Beceem BCS200/BCS220-3 and BCSM250 wimax support"
depends on USB && NET
+ depends on !64BIT
help
This is an experimental driver for the Beceem WIMAX chipset used
by Sprint 4G.
diff --git a/drivers/staging/comedi/drivers/daqboard2000.c b/drivers/staging/comedi/drivers/daqboard2000.c
index e5b5a81..55cfc34 100644
--- a/drivers/staging/comedi/drivers/daqboard2000.c
+++ b/drivers/staging/comedi/drivers/daqboard2000.c
@@ -637,7 +637,7 @@
const struct daq200_boardtype *board;
int i;
- if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
+ if (pcidev->subsystem_vendor != PCI_VENDOR_ID_IOTECH)
return NULL;
for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
index b7588e4..04f7a3b 100644
--- a/drivers/staging/comedi/drivers/ni_mio_common.c
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -2105,7 +2105,7 @@
unsigned int *data)
{
struct ni_private *devpriv = dev->private;
- unsigned int mask = (s->maxdata + 1) >> 1;
+ unsigned int mask = s->maxdata;
int i, n;
unsigned signbits;
unsigned int d;
@@ -3015,7 +3015,15 @@
int i;
static const int timeout = 1000;
- if (trig_num != cmd->start_arg)
+ /*
+ * Require trig_num == cmd->start_arg when cmd->start_src == TRIG_INT.
+ * For backwards compatibility, also allow trig_num == 0 when
+ * cmd->start_src != TRIG_INT (i.e. when cmd->start_src == TRIG_EXT);
+ * in that case, the internal trigger is being used as a pre-trigger
+ * before the external trigger.
+ */
+ if (!(trig_num == cmd->start_arg ||
+ (trig_num == 0 && cmd->start_src != TRIG_INT)))
return -EINVAL;
/* Null trig at beginning prevent ao start trigger from executing more than
@@ -5674,7 +5682,7 @@
s->maxdata = (devpriv->is_m_series) ? 0xffffffff
: 0x00ffffff;
s->insn_read = ni_tio_insn_read;
- s->insn_write = ni_tio_insn_read;
+ s->insn_write = ni_tio_insn_write;
s->insn_config = ni_tio_insn_config;
#ifdef PCIDMA
if (dev->irq && devpriv->mite) {
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index c110a25..8343a78 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -236,7 +236,7 @@
st->mclk = pdata->ext_clk_Hz;
else
st->mclk = AD7192_INT_FREQ_MHz;
- break;
+ break;
default:
ret = -EINVAL;
goto out;
diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
index b6bd609..3559e1d 100644
--- a/drivers/staging/iio/impedance-analyzer/ad5933.c
+++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
@@ -647,6 +647,7 @@
struct iio_dev *indio_dev = i2c_get_clientdata(st->client);
signed short buf[2];
unsigned char status;
+ int ret;
mutex_lock(&indio_dev->mlock);
if (st->state == AD5933_CTRL_INIT_START_FREQ) {
@@ -654,19 +655,22 @@
ad5933_cmd(st, AD5933_CTRL_START_SWEEP);
st->state = AD5933_CTRL_START_SWEEP;
schedule_delayed_work(&st->work, st->poll_time_jiffies);
- mutex_unlock(&indio_dev->mlock);
- return;
+ goto out;
}
- ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status);
+ ret = ad5933_i2c_read(st->client, AD5933_REG_STATUS, 1, &status);
+ if (ret)
+ goto out;
if (status & AD5933_STAT_DATA_VALID) {
int scan_count = bitmap_weight(indio_dev->active_scan_mask,
indio_dev->masklength);
- ad5933_i2c_read(st->client,
+ ret = ad5933_i2c_read(st->client,
test_bit(1, indio_dev->active_scan_mask) ?
AD5933_REG_REAL_DATA : AD5933_REG_IMAG_DATA,
scan_count * 2, (u8 *)buf);
+ if (ret)
+ goto out;
if (scan_count == 2) {
buf[0] = be16_to_cpu(buf[0]);
@@ -678,8 +682,7 @@
} else {
/* no data available - try again later */
schedule_delayed_work(&st->work, st->poll_time_jiffies);
- mutex_unlock(&indio_dev->mlock);
- return;
+ goto out;
}
if (status & AD5933_STAT_SWEEP_DONE) {
@@ -691,7 +694,7 @@
ad5933_cmd(st, AD5933_CTRL_INC_FREQ);
schedule_delayed_work(&st->work, st->poll_time_jiffies);
}
-
+out:
mutex_unlock(&indio_dev->mlock);
}
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index aaec6b2..f1475ac 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -967,7 +967,7 @@
else
pix_fmt = HDMI_FC_AVICONF0_PIX_FMT_RGB;
- under_scan = HDMI_FC_AVICONF0_SCAN_INFO_NODATA;
+ under_scan = HDMI_FC_AVICONF0_SCAN_INFO_NODATA;
/*
* Active format identification data is present in the AVI InfoFrame.
diff --git a/drivers/staging/lustre/lustre/llite/llite_lib.c b/drivers/staging/lustre/lustre/llite/llite_lib.c
index f4ca7b7..500af35 100644
--- a/drivers/staging/lustre/lustre/llite/llite_lib.c
+++ b/drivers/staging/lustre/lustre/llite/llite_lib.c
@@ -1504,7 +1504,7 @@
if (attr->ia_valid & (ATTR_SIZE |
ATTR_ATIME | ATTR_ATIME_SET |
- ATTR_MTIME | ATTR_MTIME_SET))
+ ATTR_MTIME | ATTR_MTIME_SET)) {
/* For truncate and utimes sending attributes to OSTs, setting
* mtime/atime to the past will be performed under PW [0:EOF]
* extent lock (new_size:EOF for truncate). It may seem
@@ -1516,6 +1516,7 @@
rc = ll_setattr_ost(inode, attr);
if (attr->ia_valid & ATTR_SIZE)
up_write(&lli->lli_trunc_sem);
+ }
out:
if (op_data) {
if (op_data->op_ioepoch) {
diff --git a/drivers/staging/nanohub/main.c b/drivers/staging/nanohub/main.c
index 8078c44..2d3daac 100644
--- a/drivers/staging/nanohub/main.c
+++ b/drivers/staging/nanohub/main.c
@@ -1259,7 +1259,8 @@
&& data->kthread_err_cnt > KTHREAD_ERR_CNT) {
dev_info(sensor_dev,
"kthread: hard reset due to consistent error\n");
- if (nanohub_hw_reset(data)) {
+ ret = nanohub_hw_reset(data);
+ if (ret) {
dev_info(sensor_dev,
"%s: failed to reset nanohub: ret=%d\n",
__func__, ret);
diff --git a/drivers/staging/nanohub/spi.c b/drivers/staging/nanohub/spi.c
index 632f4d4..a315327 100644
--- a/drivers/staging/nanohub/spi.c
+++ b/drivers/staging/nanohub/spi.c
@@ -321,7 +321,11 @@
if (ret == 0) {
if (offset > 0) {
packet = (struct nanohub_packet *)rx;
- memcpy(&rx[offset], comms->rx_buffer, xfer.len);
+ if (offset + xfer.len > max_length)
+ memcpy(&rx[offset], comms->rx_buffer,
+ max_length - offset);
+ else
+ memcpy(&rx[offset], comms->rx_buffer, xfer.len);
spi_data->rx_length = xfer.len;
spi_data->rx_offset = min_size + packet->len - offset;
} else {
diff --git a/drivers/staging/nvec/nvec_ps2.c b/drivers/staging/nvec/nvec_ps2.c
index f56f1db..3f631c0 100644
--- a/drivers/staging/nvec/nvec_ps2.c
+++ b/drivers/staging/nvec/nvec_ps2.c
@@ -106,13 +106,12 @@
{
struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
struct serio *ser_dev;
- char mouse_reset[] = { NVEC_PS2, SEND_COMMAND, PSMOUSE_RST, 3 };
ser_dev = devm_kzalloc(&pdev->dev, sizeof(struct serio), GFP_KERNEL);
if (ser_dev == NULL)
return -ENOMEM;
- ser_dev->id.type = SERIO_PS_PSTHRU;
+ ser_dev->id.type = SERIO_8042;
ser_dev->write = ps2_sendcommand;
ser_dev->start = ps2_startstreaming;
ser_dev->stop = ps2_stopstreaming;
@@ -127,9 +126,6 @@
serio_register_port(ser_dev);
- /* mouse reset */
- nvec_write_async(nvec, mouse_reset, sizeof(mouse_reset));
-
return 0;
}
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt.h b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt.h
index aa96850..c608cf5 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt.h
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -135,9 +135,13 @@
* 3.27 Add a new interface for flow-control. The following t2h messages have
* been included: HTT_T2H_MSG_TYPE_FLOW_POOL_MAP and
* HTT_T2H_MSG_TYPE_FLOW_POOL_UNMAP
+ * 3.28 Add a new interface for ring interface change. The following two h2t
+ * and one t2h messages have been included:
+ * HTT_H2T_MSG_TYPE_SRING_SETUP, HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG,
+ * and HTT_T2H_MSG_TYPE_SRING_SETUP_DONE
*/
#define HTT_CURRENT_VERSION_MAJOR 3
-#define HTT_CURRENT_VERSION_MINOR 27
+#define HTT_CURRENT_VERSION_MINOR 28
#define HTT_NUM_TX_FRAG_DESC 1024
@@ -458,17 +462,19 @@
/*=== host -> target messages ===============================================*/
enum htt_h2t_msg_type {
- HTT_H2T_MSG_TYPE_VERSION_REQ = 0x0,
- HTT_H2T_MSG_TYPE_TX_FRM = 0x1,
- HTT_H2T_MSG_TYPE_RX_RING_CFG = 0x2,
- HTT_H2T_MSG_TYPE_STATS_REQ = 0x3,
- HTT_H2T_MSG_TYPE_SYNC = 0x4,
- HTT_H2T_MSG_TYPE_AGGR_CFG = 0x5,
- HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 0x6,
- DEPRECATED_HTT_H2T_MSG_TYPE_MGMT_TX = 0x7, /* no longer used */
- HTT_H2T_MSG_TYPE_WDI_IPA_CFG = 0x8,
- HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ = 0x9,
- HTT_H2T_MSG_TYPE_AGGR_CFG_EX = 0xa, /* per vdev amsdu subfrm limit */
+ HTT_H2T_MSG_TYPE_VERSION_REQ = 0x0,
+ HTT_H2T_MSG_TYPE_TX_FRM = 0x1,
+ HTT_H2T_MSG_TYPE_RX_RING_CFG = 0x2,
+ HTT_H2T_MSG_TYPE_STATS_REQ = 0x3,
+ HTT_H2T_MSG_TYPE_SYNC = 0x4,
+ HTT_H2T_MSG_TYPE_AGGR_CFG = 0x5,
+ HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 0x6,
+ DEPRECATED_HTT_H2T_MSG_TYPE_MGMT_TX = 0x7, /* no longer used */
+ HTT_H2T_MSG_TYPE_WDI_IPA_CFG = 0x8,
+ HTT_H2T_MSG_TYPE_WDI_IPA_OP_REQ = 0x9,
+ HTT_H2T_MSG_TYPE_AGGR_CFG_EX = 0xa, /* per vdev amsdu subfrm limit */
+ HTT_H2T_MSG_TYPE_SRING_SETUP = 0xb,
+ HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG = 0xc,
/* keep this last */
HTT_H2T_NUM_MSGS
};
@@ -2908,6 +2914,1125 @@
((_var) |= ((_val) << HTT_WDI_IPA_OP_REQUEST_OP_CODE_S)); \
} while (0)
+/*
+ * @brief host -> target HTT_SRING_SETUP message
+ *
+ * @details
+ * After target is booted up, Host can send SRING setup message for
+ * each host facing LMAC SRING. Target setups up HW registers based
+ * on setup message and confirms back to Host if response_required is set.
+ * Host should wait for confirmation message before sending new SRING
+ * setup message
+ *
+ * The message would appear as follows:
+ * |31 24|23 20|19|18 16|15|14 8|7 0|
+ * |--------------- +-----------------+----------------+------------------|
+ * | ring_type | ring_id | pdev_id | msg_type |
+ * |----------------------------------------------------------------------|
+ * | ring_base_addr_lo |
+ * |----------------------------------------------------------------------|
+ * | ring_base_addr_hi |
+ * |----------------------------------------------------------------------|
+ * |ring_misc_cfg_flag|ring_entry_size| ring_size |
+ * |----------------------------------------------------------------------|
+ * | ring_head_offset32_remote_addr_lo |
+ * |----------------------------------------------------------------------|
+ * | ring_head_offset32_remote_addr_hi |
+ * |----------------------------------------------------------------------|
+ * | ring_tail_offset32_remote_addr_lo |
+ * |----------------------------------------------------------------------|
+ * | ring_tail_offset32_remote_addr_hi |
+ * |----------------------------------------------------------------------|
+ * | ring_msi_addr_lo |
+ * |----------------------------------------------------------------------|
+ * | ring_msi_addr_hi |
+ * |----------------------------------------------------------------------|
+ * | ring_msi_data |
+ * |----------------------------------------------------------------------|
+ * | intr_timer_th |IM| intr_batch_counter_th |
+ * |----------------------------------------------------------------------|
+ * | reserved |RR|PTCF| intr_low_threshold |
+ * |----------------------------------------------------------------------|
+ * Where
+ * IM = sw_intr_mode
+ * RR = response_required
+ * PTCF = prefetch_timer_cfg
+ *
+ * The message is interpreted as follows:
+ * dword0 - b'0:7 - msg_type: This will be set to
+ * HTT_H2T_MSG_TYPE_SRING_SETUP
+ * b'8:15 - pdev_id:
+ * 0 (for rings at SOC/UMAC level),
+ * 1/2/3 mac id (for rings at LMAC level)
+ * b'16:23 - ring_id: identify which ring is to setup,
+ * more details can be got from enum htt_srng_ring_id
+ * b'24:31 - ring_type: identify type of host rings,
+ * more details can be got from enum htt_srng_ring_type
+ * dword1 - b'0:31 - ring_base_addr_lo: Lower 32bits of ring base address
+ * dword2 - b'0:31 - ring_base_addr_hi: Upper 32bits of ring base address
+ * dword3 - b'0:15 - ring_size: size of the ring in unit of 4-bytes words
+ * b'16:23 - ring_entry_size: Size of each entry in 4-byte word units
+ * b'24:31 - ring_misc_cfg_flag: Valid only for HW_TO_SW_RING and
+ * SW_TO_HW_RING.
+ * Refer to HTT_SRING_SETUP_RING_MISC_CFG_RING defs.
+ * dword4 - b'0:31 - ring_head_offset32_remote_addr_lo:
+ * Lower 32 bits of memory address of the remote variable
+ * storing the 4-byte word offset that identifies the head
+ * element within the ring.
+ * (The head offset variable has type A_UINT32.)
+ * Valid for HW_TO_SW and SW_TO_SW rings.
+ * dword5 - b'0:31 - ring_head_offset32_remote_addr_hi:
+ * Upper 32 bits of memory address of the remote variable
+ * storing the 4-byte word offset that identifies the head
+ * element within the ring.
+ * (The head offset variable has type A_UINT32.)
+ * Valid for HW_TO_SW and SW_TO_SW rings.
+ * dword6 - b'0:31 - ring_tail_offset32_remote_addr_lo:
+ * Lower 32 bits of memory address of the remote variable
+ * storing the 4-byte word offset that identifies the tail
+ * element within the ring.
+ * (The tail offset variable has type A_UINT32.)
+ * Valid for HW_TO_SW and SW_TO_SW rings.
+ * dword7 - b'0:31 - ring_tail_offset32_remote_addr_hi:
+ * Upper 32 bits of memory address of the remote variable
+ * storing the 4-byte word offset that identifies the tail
+ * element within the ring.
+ * (The tail offset variable has type A_UINT32.)
+ * Valid for HW_TO_SW and SW_TO_SW rings.
+ * dword8 - b'0:31 - ring_msi_addr_lo: Lower 32bits of MSI cfg address
+ * valid only for HW_TO_SW_RING and SW_TO_HW_RING
+ * dword9 - b'0:31 - ring_msi_addr_hi: Upper 32bits of MSI cfg address
+ * valid only for HW_TO_SW_RING and SW_TO_HW_RING
+ * dword10 - b'0:31 - ring_msi_data: MSI data
+ * Refer to HTT_SRING_SETUP_RING_MSC_CFG_xxx defs
+ * valid only for HW_TO_SW_RING and SW_TO_HW_RING
+ * dword11 - b'0:14 - intr_batch_counter_th:
+ * batch counter threshold is in units of 4-byte words.
+ * HW internally maintains and increments batch count.
+ * (see SRING spec for detail description).
+ * When batch count reaches threshold value, an interrupt
+ * is generated by HW.
+ * b'15 - sw_intr_mode:
+ * This configuration shall be static.
+ * Only programmed at power up.
+ * 0: generate pulse style sw interrupts
+ * 1: generate level style sw interrupts
+ * b'16:31 - intr_timer_th:
+ * The timer init value when timer is idle or is
+ * initialized to start downcounting.
+ * In 8us units (to cover a range of 0 to 524 ms)
+ * dword12 - b'0:15 - intr_low_threshold:
+ * Used only by Consumer ring to generate ring_sw_int_p.
+ * Ring entries low threshold water mark, that is used
+ * in combination with the interrupt timer as well as
+ * the the clearing of the level interrupt.
+ * b'16:18 - prefetch_timer_cfg:
+ * Used only by Consumer ring to set timer mode to
+ * support Application prefetch handling.
+ * The external tail offset/pointer will be updated
+ * at following intervals:
+ * 3'b000: (Prefetch feature disabled; used only for debug)
+ * 3'b001: 1 usec
+ * 3'b010: 4 usec
+ * 3'b011: 8 usec (default)
+ * 3'b100: 16 usec
+ * Others: Reserverd
+ * b'19 - response_required:
+ * Host needs HTT_T2H_MSG_TYPE_SRING_SETUP_DONE as response
+ * b'20:31 - reserved: reserved for future use
+ */
+PREPACK struct htt_sring_setup_t {
+ A_UINT32 msg_type: 8,
+ pdev_id: 8,
+ ring_id: 8,
+ ring_type: 8;
+ A_UINT32 ring_base_addr_lo;
+ A_UINT32 ring_base_addr_hi;
+ A_UINT32 ring_size: 16,
+ ring_entry_size: 8,
+ ring_misc_cfg_flag: 8;
+ A_UINT32 ring_head_offset32_remote_addr_lo;
+ A_UINT32 ring_head_offset32_remote_addr_hi;
+ A_UINT32 ring_tail_offset32_remote_addr_lo;
+ A_UINT32 ring_tail_offset32_remote_addr_hi;
+ A_UINT32 ring_msi_addr_lo;
+ A_UINT32 ring_msi_addr_hi;
+ A_UINT32 ring_msi_data;
+ A_UINT32 intr_batch_counter_th: 15,
+ sw_intr_mode: 1,
+ intr_timer_th: 16;
+ A_UINT32 intr_low_threshold: 16,
+ prefetch_timer_cfg: 3,
+ response_required: 1,
+ reserved1: 12;
+} POSTPACK;
+
+enum htt_srng_ring_type {
+ HTT_HW_TO_SW_RING = 0,
+ HTT_SW_TO_HW_RING,
+ HTT_SW_TO_SW_RING,
+ /* Insert new ring types above this line */
+};
+
+enum htt_srng_ring_id {
+ HTT_RXDMA_HOST_BUF_RING = 0, /* Used by FW to feed remote buffers and update remote packets */
+ HTT_RXDMA_MONITOR_STATUS_RING, /* For getting all PPDU/MPDU/MSDU status deescriptors on host for monitor VAP or packet log purposes */
+ HTT_RXDMA_MONITOR_BUF_RING, /* For feeding free host buffers to RxDMA for monitor traffic upload */
+ HTT_RXDMA_MONITOR_DESC_RING, /* For providing free LINK_DESC to RXDMA for monitor traffic upload */
+ HTT_RXDMA_MONITOR_DEST_RING, /* Per MPDU indication to host for monitor traffic upload */
+ HTT_HOST1_TO_FW_RXBUF_RING, /* (mobile only) used by host to provide remote RX buffers */
+ HTT_HOST2_TO_FW_RXBUF_RING, /* (mobile only) second ring used by host to provide remote RX buffers */
+ /* Add Other SRING which can't be directly configured by host software above this line */
+};
+
+#define HTT_SRING_SETUP_SZ (sizeof(struct htt_sring_setup_t))
+
+#define HTT_SRING_SETUP_PDEV_ID_M 0x0000ff00
+#define HTT_SRING_SETUP_PDEV_ID_S 8
+#define HTT_SRING_SETUP_PDEV_ID_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_PDEV_ID_M) >> \
+ HTT_SRING_SETUP_PDEV_ID_S)
+#define HTT_SRING_SETUP_PDEV_ID_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_PDEV_ID, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_PDEV_ID_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_RING_ID_M 0x00ff0000
+#define HTT_SRING_SETUP_RING_ID_S 16
+#define HTT_SRING_SETUP_RING_ID_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_ID_M) >> \
+ HTT_SRING_SETUP_RING_ID_S)
+#define HTT_SRING_SETUP_RING_ID_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_ID, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_ID_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_RING_TYPE_M 0xff000000
+#define HTT_SRING_SETUP_RING_TYPE_S 24
+#define HTT_SRING_SETUP_RING_TYPE_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_TYPE_M) >> \
+ HTT_SRING_SETUP_RING_TYPE_S)
+#define HTT_SRING_SETUP_RING_TYPE_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_TYPE, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_TYPE_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_RING_BASE_ADDR_LO_M 0xffffffff
+#define HTT_SRING_SETUP_RING_BASE_ADDR_LO_S 0
+#define HTT_SRING_SETUP_RING_BASE_ADDR_LO_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_BASE_ADDR_LO_M) >> \
+ HTT_SRING_SETUP_RING_BASE_ADDR_LO_S)
+#define HTT_SRING_SETUP_RING_BASE_ADDR_LO_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_BASE_ADDR_LO, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_BASE_ADDR_LO_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_RING_BASE_ADDR_HI_M 0xffffffff
+#define HTT_SRING_SETUP_RING_BASE_ADDR_HI_S 0
+#define HTT_SRING_SETUP_RING_BASE_ADDR_HI_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_BASE_ADDR_HI_M) >> \
+ HTT_SRING_SETUP_RING_BASE_ADDR_HI_S)
+#define HTT_SRING_SETUP_RING_BASE_ADDR_HI_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_BASE_ADDR_HI, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_BASE_ADDR_HI_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_RING_SIZE_M 0x0000ffff
+#define HTT_SRING_SETUP_RING_SIZE_S 0
+#define HTT_SRING_SETUP_RING_SIZE_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_SIZE_M) >> \
+ HTT_SRING_SETUP_RING_SIZE_S)
+#define HTT_SRING_SETUP_RING_SIZE_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_SIZE, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_SIZE_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_ENTRY_SIZE_M 0x00ff00000
+#define HTT_SRING_SETUP_ENTRY_SIZE_S 16
+#define HTT_SRING_SETUP_ENTRY_SIZE_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_ENTRY_SIZE_M) >> \
+ HTT_SRING_SETUP_ENTRY_SIZE_S)
+#define HTT_SRING_SETUP_ENTRY_SIZE_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_ENTRY_SIZE, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_ENTRY_SIZE_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_MISC_CFG_FLAG_M 0xff0000000
+#define HTT_SRING_SETUP_MISC_CFG_FLAG_S 24
+#define HTT_SRING_SETUP_MISC_CFG_FLAG_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_MISC_CFG_FLAG_M) >> \
+ HTT_SRING_SETUP_MISC_CFG_FLAG_S)
+#define HTT_SRING_SETUP_MISC_CFG_FLAG_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_MISC_CFG_FLAG, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_MISC_CFG_FLAG_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_LO_M 0xffffffff
+#define HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_LO_S 0
+#define HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_LO_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_LO_M) >> \
+ HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_LO_S)
+#define HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_LO_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_LO, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_LO_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_HI_M 0xffffffff
+#define HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_HI_S 0
+#define HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_HI_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_HI_M) >> \
+ HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_HI_S)
+#define HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_HI_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_HI, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_HEAD_OFFSET32_REMOTE_BASE_ADDR_HI_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_LO_M 0xffffffff
+#define HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_LO_S 0
+#define HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_LO_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_LO_M) >> \
+ HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_LO_S)
+#define HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_LO_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_LO, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_LO_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_HI_M 0xffffffff
+#define HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_HI_S 0
+#define HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_HI_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_HI_M) >> \
+ HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_HI_S)
+#define HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_HI_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_HI, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_TAIL_OFFSET32_REMOTE_BASE_ADDR_HI_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_RING_MISC_ADDR_LO_M 0xffffffff
+#define HTT_SRING_SETUP_RING_MISC_ADDR_LO_S 0
+#define HTT_SRING_SETUP_RING_MISC_ADDR_LO_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_MISC_ADDR_LO_M) >> \
+ HTT_SRING_SETUP_RING_MISC_ADDR_LO_S)
+#define HTT_SRING_SETUP_RING_MISC_ADDR_LO_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_MISC_ADDR_LO, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_MISC_ADDR_LO_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_RING_MISC_ADDR_HI_M 0xffffffff
+#define HTT_SRING_SETUP_RING_MISC_ADDR_HI_S 0
+#define HTT_SRING_SETUP_RING_MISC_ADDR_HI_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_MISC_ADDR_HI_M) >> \
+ HTT_SRING_SETUP_RING_MISC_ADDR_HI_S)
+#define HTT_SRING_SETUP_RING_MISC_ADDR_HI_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_MISC_ADDR_HI, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_MISC_ADDR_HI_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_RING_MISC_DATA_M 0xffffffff
+#define HTT_SRING_SETUP_RING_MISC_DATA_S 0
+#define HTT_SRING_SETUP_RING_MISC_DATA_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_MISC_DATA_M) >> \
+ HTT_SRING_SETUP_RING_MISC_DATA_S)
+#define HTT_SRING_SETUP_RING_MISC_DATA_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_MISC_DATA, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_MISC_DATA_S)); \
+ } while (0)
+
+/* This control bit is applicable to only Producer, which updates Ring ID field
+ * of each descriptor before pushing into the ring.
+ * 0: updates ring_id(default)
+ * 1: ring_id updating disabled */
+#define HTT_SRING_SETUP_RING_MISC_DATA_RING_ID_DISABLE_M 0x01
+#define HTT_SRING_SETUP_RING_MISC_DATA_RING_ID_DISABLE_S 0
+#define HTT_SRING_SETUP_RING_MISC_DATA_RING_ID_DISABLE_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_MISC_DATA_RING_ID_DISABLE_M) >> \
+ HTT_SRING_SETUP_RING_MISC_DATA_RING_ID_DISABLE_S)
+#define HTT_SRING_SETUP_RING_MISC_DATA_RING_ID_DISABLE_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_MISC_DATA_RING_ID_DISABLE, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_MISC_DATA_RING_ID_DISABLE_S)); \
+ } while (0)
+
+/* This control bit is applicable to only Producer, which updates Loopcnt field
+ * of each descriptor before pushing into the ring.
+ * 0: updates Loopcnt(default)
+ * 1: Loopcnt updating disabled */
+#define HTT_SRING_SETUP_RING_MISC_DATA_LOOPCOUNT_DISABLE_M 0x02
+#define HTT_SRING_SETUP_RING_MISC_DATA_LOOPCOUNT_DISABLE_S 1
+#define HTT_SRING_SETUP_RING_MISC_DATA_LOOPCOUNT_DISABLE_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_MISC_DATA_LOOPCOUNT_DISABLE_M) >> \
+ HTT_SRING_SETUP_RING_MISC_DATA_LOOPCOUNT_DISABLE_S)
+#define HTT_SRING_SETUP_RING_MISC_DATA_LOOPCOUNT_DISABLE_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_MISC_DATA_LOOPCOUNT_DISABLE, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_MISC_DATA_LOOPCOUNT_DISABLE_S)); \
+ } while (0)
+
+/* Secured access enable/disable bit. SRNG drives value of this register bit
+ * into security_id port of GXI/AXI. */
+#define HTT_SRING_SETUP_RING_MISC_DATA_SECURITY_M 0x04
+#define HTT_SRING_SETUP_RING_MISC_DATA_SECURITY_S 2
+#define HTT_SRING_SETUP_RING_MISC_DATA_SECURITY_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_MISC_DATA_SECURITY_M) >> \
+ HTT_SRING_SETUP_RING_MISC_DATA_SECURITY_S)
+#define HTT_SRING_SETUP_RING_MISC_DATA_SECURITY_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_MISC_DATA_SECURITY, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_MISC_DATA_SECURITY_S)); \
+ } while (0)
+
+/* During MSI write operation, SRNG drives value of this register bit into
+ * swap bit of GXI/AXI. */
+#define HTT_SRING_SETUP_RING_MISC_DATA_MSI_SWAP_M 0x08
+#define HTT_SRING_SETUP_RING_MISC_DATA_MSI_SWAP_S 3
+#define HTT_SRING_SETUP_RING_MISC_DATA_MSI_SWAP_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_MISC_DATA_MSI_SWAP_M) >> \
+ HTT_SRING_SETUP_RING_MISC_DATA_MSI_SWAP_S)
+#define HTT_SRING_SETUP_RING_MISC_DATA_MSI_SWAP_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_MISC_DATA_MSI_SWAP, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_MISC_DATA_MSI_SWAP_S)); \
+ } while (0)
+
+/* During Pointer write operation, SRNG drives value of this register bit into
+ * swap bit of GXI/AXI. */
+#define HTT_SRING_SETUP_RING_MISC_DATA_HOST_FW_SWAP_M 0x10
+#define HTT_SRING_SETUP_RING_MISC_DATA_HOST_FW_SWAP_S 4
+#define HTT_SRING_SETUP_RING_MISC_DATA_HOST_FW_SWAP_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_MISC_DATA_HOST_FW_SWAP_M) >> \
+ HTT_SRING_SETUP_RING_MISC_DATA_HOST_FW_SWAP_S)
+#define HTT_SRING_SETUP_RING_MISC_DATA_HOST_FW_SWAP_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_MISC_DATA_HOST_FW_SWAP, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_MISC_DATA_HOST_FW_SWAP_S)); \
+ } while (0)
+
+/* During any data or TLV write operation, SRNG drives value of this register
+ * bit into swap bit of GXI/AXI. */
+#define HTT_SRING_SETUP_RING_MISC_DATA_TLV_SWAP_M 0x20
+#define HTT_SRING_SETUP_RING_MISC_DATA_TLV_SWAP_S 5
+#define HTT_SRING_SETUP_RING_MISC_DATA_TLV_SWAP_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RING_MISC_DATA_TLV_SWAP_M) >> \
+ HTT_SRING_SETUP_RING_MISC_DATA_TLV_SWAP_S)
+#define HTT_SRING_SETUP_RING_MISC_DATA_TLV_SWAP_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RING_MISC_DATA_TLV_SWAP, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RING_MISC_DATA_TLV_SWAP_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_RING_MISC_CFG_RESERVED1 0x40
+#define HTT_SRING_SETUP_RING_MISC_CFG_RESERVED2 0x80
+
+#define HTT_SRING_SETUP_INTR_BATCH_COUNTER_TH_M 0x00007fff
+#define HTT_SRING_SETUP_INTR_BATCH_COUNTER_TH_S 0
+#define HTT_SRING_SETUP_INTR_BATCH_COUNTER_TH_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_INTR_BATCH_COUNTER_TH_M) >> \
+ HTT_SRING_SETUP_INTR_BATCH_COUNTER_TH_S)
+#define HTT_SRING_SETUP_INTR_BATCH_COUNTER_TH_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_INTR_BATCH_COUNTER_TH, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_INTR_BATCH_COUNTER_TH_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_SW_INTR_MODE_M 0x00008000
+#define HTT_SRING_SETUP_SW_INTR_MODE_S 15
+#define HTT_SRING_SETUP_SW_INTR_MODE_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_SW_INTR_MODE_M) >> \
+ HTT_SRING_SETUP_SW_INTR_MODE_S)
+#define HTT_SRING_SETUP_SW_INTR_MODE_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_SW_INTR_MODE, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_SW_INTR_MODE_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_INTR_TIMER_TH_M 0xffff0000
+#define HTT_SRING_SETUP_INTR_TIMER_TH_S 16
+#define HTT_SRING_SETUP_INTR_TIMER_TH_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_INTR_TIMER_TH_M) >> \
+ HTT_SRING_SETUP_INTR_TIMER_TH_S)
+#define HTT_SRING_SETUP_INTR_TIMER_TH_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_INTR_TIMER_TH, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_INTR_TIMER_TH_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_INTR_LOW_TH_M 0x0000ffff
+#define HTT_SRING_SETUP_INTR_LOW_TH_S 0
+#define HTT_SRING_SETUP_INTR_LOW_TH_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_INTR_LOW_TH_M) >> \
+ HTT_SRING_SETUP_INTR_LOW_TH_S)
+#define HTT_SRING_SETUP_INTR_LOW_TH_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_INTR_LOW_TH, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_INTR_LOW_TH_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_PREFETCH_TIMER_CFG_M 0x00070000
+#define HTT_SRING_SETUP_PREFETCH_TIMER_CFG_S 16
+#define HTT_SRING_SETUP_PREFETCH_TIMER_CFG_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_PREFETCH_TIMER_CFG_M) >> \
+ HTT_SRING_SETUP_PREFETCH_TIMER_CFG_S)
+#define HTT_SRING_SETUP_PREFETCH_TIMER_CFG_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_PREFETCH_TIMER_CFG, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_PREFETCH_TIMER_CFG_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_RESPONSE_REQUIRED_M 0x00080000
+#define HTT_SRING_SETUP_RESPONSE_REQUIRED_S 19
+#define HTT_SRING_SETUP_RESPONSE_REQUIRED_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_RESPONSE_REQUIRED_M) >> \
+ HTT_SRING_SETUP_RESPONSE_REQUIRED_S)
+#define HTT_SRING_SETUP_RESPONSE_REQUIRED_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_RESPONSE_REQUIRED, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_RESPONSE_REQUIRED_S)); \
+ } while (0)
+
+
+/**
+ * @brief HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG Message
+ *
+ * @details
+ * HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG message is sent by host to
+ * configure RXDMA rings.
+ * The configuration is per ring based and includes both packet subtypes
+ * and PPDU/MPDU TLVs.
+ *
+ * The message would appear as follows:
+ *
+ * |31 26|25|24|23 16|15 8|7 0|
+ * |-----------------+----------------+----------------+---------------|
+ * | rsvd1 |PS|SS| ring_id | pdev_id | msg_type |
+ * |-------------------------------------------------------------------|
+ * | rsvd2 | ring_buffer_size |
+ * |-------------------------------------------------------------------|
+ * | packet_type_enable_flags_0 |
+ * |-------------------------------------------------------------------|
+ * | packet_type_enable_flags_1 |
+ * |-------------------------------------------------------------------|
+ * | packet_type_enable_flags_2 |
+ * |-------------------------------------------------------------------|
+ * | packet_type_enable_flags_3 |
+ * |-------------------------------------------------------------------|
+ * | tlv_filter_in_flags |
+ * |-------------------------------------------------------------------|
+ * Where:
+ * PS = pkt_swap
+ * SS = status_swap
+ * The message is interpreted as follows:
+ * dword0 - b'0:7 - msg_type: This will be set to
+ * HTT_H2T_MSG_TYPE_RX_RING_SELECTION_CFG
+ * b'8:15 - pdev_id:
+ * 0 (for rings at SOC/UMAC level),
+ * 1/2/3 mac id (for rings at LMAC level)
+ * b'16:23 - ring_id : Identify the ring to configure.
+ * More details can be got from enum htt_srng_ring_id
+ * b'24 - status_swap: 1 is to swap status TLV
+ * b'25 - pkt_swap: 1 is to swap packet TLV
+ * b'26:31 - rsvd1: reserved for future use
+ * dword1 - b'0:16 - ring_buffer_size: size of bufferes referenced by rx ring,
+ * in byte units.
+ * Valid only for HW_TO_SW_RING and SW_TO_HW_RING
+ * - b'16:31 - rsvd2: Reserved for future use
+ * dword2 - b'0:31 - packet_type_enable_flags_0:
+ * Enable MGMT packet from 0b0000 to 0b1001
+ * bits from low to high: FP, MD, MO - 3 bits
+ * FP: Filter_Pass
+ * MD: Monitor_Direct
+ * MO: Monitor_Other
+ * 10 mgmt subtypes * 3 bits -> 30 bits
+ * Refer to PKT_TYPE_ENABLE_FLAG0_xxx_MGMT_xxx defs
+ * dword3 - b'0:31 - packet_type_enable_flags_1:
+ * Enable MGMT packet from 0b1010 to 0b1111
+ * bits from low to high: FP, MD, MO - 3 bits
+ * Refer to PKT_TYPE_ENABLE_FLAG1_xxx_MGMT_xxx defs
+ * dword4 - b'0:31 - packet_type_enable_flags_2:
+ * Enable CTRL packet from 0b0000 to 0b1001
+ * bits from low to high: FP, MD, MO - 3 bits
+ * Refer to PKT_TYPE_ENABLE_FLAG2_xxx_CTRL_xxx defs
+ * dword5 - b'0:31 - packet_type_enable_flags_3:
+ * Enable CTRL packet from 0b1010 to 0b1111,
+ * MCAST_DATA, UCAST_DATA, NULL_DATA
+ * bits from low to high: FP, MD, MO - 3 bits
+ * Refer to PKT_TYPE_ENABLE_FLAG3_xxx_CTRL_xxx defs
+ * dword6 - b'0:31 - tlv_filter_in_flags:
+ * Filter in Attention/MPDU/PPDU/Header/User tlvs
+ * Refer to CFG_TLV_FILTER_IN_FLAG defs
+ */
+PREPACK struct htt_rx_ring_selection_cfg_t {
+ A_UINT32 msg_type: 8,
+ pdev_id: 8,
+ ring_id: 8,
+ status_swap: 1,
+ pkt_swap: 1,
+ rsvd1: 6;
+ A_UINT32 ring_buffer_size: 16,
+ rsvd2: 16;
+ A_UINT32 packet_type_enable_flags_0;
+ A_UINT32 packet_type_enable_flags_1;
+ A_UINT32 packet_type_enable_flags_2;
+ A_UINT32 packet_type_enable_flags_3;
+ A_UINT32 tlv_filter_in_flags;
+} POSTPACK;
+
+#define HTT_RX_RING_SELECTION_CFG_SZ (sizeof(struct htt_rx_ring_selection_cfg_t))
+
+#define HTT_RX_RING_SELECTION_CFG_PDEV_ID_M 0x0000ff00
+#define HTT_RX_RING_SELECTION_CFG_PDEV_ID_S 8
+#define HTT_RX_RING_SELECTION_CFG_PDEV_ID_GET(_var) \
+ (((_var) & HTT_RX_RING_SELECTION_CFG_PDEV_ID_M) >> \
+ HTT_RX_RING_SELECTION_CFG_PDEV_ID_S)
+#define HTT_RX_RING_SELECTION_CFG_PDEV_ID_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_RX_RING_SELECTION_CFG_PDEV_ID, _val); \
+ ((_var) |= ((_val) << HTT_RX_RING_SELECTION_CFG_PDEV_ID_S)); \
+ } while (0)
+
+#define HTT_RX_RING_SELECTION_CFG_RING_ID_M 0x00ff0000
+#define HTT_RX_RING_SELECTION_CFG_RING_ID_S 16
+#define HTT_RX_RING_SELECTION_CFG_RING_ID_GET(_var) \
+ (((_var) & HTT_RX_RING_SELECTION_CFG_RING_ID_M) >> \
+ HTT_RX_RING_SELECTION_CFG_RING_ID_S)
+#define HTT_RX_RING_SELECTION_CFG_RING_ID_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_RX_RING_SELECTION_CFG_RING_ID, _val); \
+ ((_var) |= ((_val) << HTT_RX_RING_SELECTION_CFG_RING_ID_S)); \
+ } while (0)
+
+#define HTT_RX_RING_SELECTION_CFG_STATUS_TLV_SWAP_M 0x01000000
+#define HTT_RX_RING_SELECTION_CFG_STATUS_TLV_SWAP_S 24
+#define HTT_RX_RING_SELECTION_CFG_STATUS_TLV_GET(_var) \
+ (((_var) & HTT_RX_RING_SELECTION_CFG_STATUS_TLV_SWAP_M) >> \
+ HTT_RX_RING_SELECTION_CFG_STATUS_TLV_SWAP_S)
+#define HTT_RX_RING_SELECTION_CFG_STATUS_TLV_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_RX_RING_SELECTION_CFG_STATUS_TLV_SWAP, _val); \
+ ((_var) |= ((_val) << HTT_RX_RING_SELECTION_CFG_STATUS_TLV_SWAP_S)); \
+ } while (0)
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TLV_SWAP_M 0x02000000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TLV_SWAP_S 25
+#define HTT_RX_RING_SELECTION_CFG_PKT_TLV_GET(_var) \
+ (((_var) & HTT_RX_RING_SELECTION_CFG_PKT_TLV_SWAP_M) >> \
+ HTT_RX_RING_SELECTION_CFG_PKT_TLV_SWAP_S)
+#define HTT_RX_RING_SELECTION_CFG_PKT_TLV_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_RX_RING_SELECTION_CFG_PKT_TLV_SWAP, _val); \
+ ((_var) |= ((_val) << HTT_RX_RING_SELECTION_CFG_PKT_TLV_SWAP_S)); \
+ } while (0)
+
+#define HTT_RX_RING_SELECTION_CFG_RING_BUFFER_SIZE_M 0x0000ffff
+#define HTT_RX_RING_SELECTION_CFG_RING_BUFFER_SIZE_S 0
+#define HTT_RX_RING_SELECTION_CFG_RING_BUFFER_SIZE_GET(_var) \
+ (((_var) & HTT_RX_RING_SELECTION_CFG_RING_BUFFER_SIZE_M) >> \
+ HTT_RX_RING_SELECTION_CFG_RING_BUFFER_SIZE_S)
+#define HTT_RX_RING_SELECTION_CFG_RING_BUFFER_SIZE_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_RX_RING_SELECTION_CFG_RING_BUFFER_SIZE, _val); \
+ ((_var) |= ((_val) << HTT_RX_RING_SELECTION_CFG_RING_BUFFER_SIZE_S)); \
+ } while (0)
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_0_M 0xffffffff
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_0_S 0
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_0_GET(_var) \
+ (((_var) & HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_0_M) >> \
+ HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_0_S)
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_0_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_0, _val); \
+ ((_var) |= ((_val) << HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_0_S)); \
+ } while (0)
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_1_M 0xffffffff
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_1_S 0
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_1_GET(_var) \
+ (((_var) & HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_1_M) >> \
+ HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_1_S)
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_1_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_1, _val); \
+ ((_var) |= ((_val) << HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_1_S)); \
+ } while (0)
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_2_M 0xffffffff
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_2_S 0
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_2_GET(_var) \
+ (((_var) & HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_2_M) >> \
+ HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_2_S)
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_2_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_2, _val); \
+ ((_var) |= ((_val) << HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_2_S)); \
+ } while (0)
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_3_M 0xffffffff
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_3_S 0
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_3_GET(_var) \
+ (((_var) & HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_3_M) >> \
+ HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_3_S)
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_3_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_3, _val); \
+ ((_var) |= ((_val) << HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG_3_S)); \
+ } while (0)
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_M 0xffffffff
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_S 0
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_GET(_var) \
+ (((_var) & HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_M) >> \
+ HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_S)
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG, _val); \
+ ((_var) |= ((_val) << HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_S)); \
+ } while (0)
+
+/*
+ * Subtype based MGMT frames enable bits.
+ * FP: Filter_Pass, MD: Monitor_Direct MO: Monitor_Other
+ */
+/* association request */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0000_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0000_S 0
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0000_M 0x00000002
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0000_S 1
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0000_M 0x00000004
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0000_S 2
+
+/* association response */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0001_M 0x00000008
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0001_S 3
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0001_M 0x00000010
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0001_S 4
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0001_M 0x00000020
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0001_S 5
+
+/* Reassociation request */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0010_M 0x00000040
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0010_S 6
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0010_M 0x00000080
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0010_S 7
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0010_M 0x00000100
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0010_S 8
+
+/* Reassociation response */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0011_M 0x00000200
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0011_S 9
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0011_M 0x00000400
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0011_S 10
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0011_M 0x00000800
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0011_S 11
+
+/* Probe request */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0100_M 0x00001000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0100_S 12
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0100_M 0x00002000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0100_S 13
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0100_M 0x00004000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0100_S 14
+
+/* Probe response */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0101_M 0x00008000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0101_S 15
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0101_M 0x00010000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0101_S 16
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0101_M 0x00020000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0101_S 17
+
+/* Timing Advertisement */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0110_M 0x00040000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0110_S 18
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0110_M 0x00080000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0110_S 19
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0110_M 0x00100000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0110_S 20
+
+/* Reserved */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0111_M 0x00200000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_0111_S 21
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0111_M 0x00400000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_0111_S 22
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0111_M 0x00800000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_0111_S 23
+
+/* Beacon */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_1000_M 0x01000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_1000_S 24
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_1000_M 0x02000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_1000_S 25
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_1000_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_1000_S 26
+
+/* ATIM */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_1001_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_FP_MGMT_1001_S 27
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_1001_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MD_MGMT_1001_S 28
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_1001_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG0_MO_MGMT_1001_S 29
+
+/* Disassociation */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1010_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1010_S 0
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1010_M 0x00000002
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1010_S 1
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1010_M 0x00000004
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1010_S 2
+
+/* Authentication */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1011_M 0x00000008
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1011_S 3
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1011_M 0x00000010
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1011_S 4
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1011_M 0x00000020
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1011_S 5
+
+/* Deauthentication */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1100_M 0x00000040
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1100_S 6
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1100_M 0x00000080
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1100_S 7
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1100_M 0x00000100
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1100_S 8
+
+/* Action */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1101_M 0x00000200
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1101_S 9
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1101_M 0x00000400
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1101_S 10
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1101_M 0x00000800
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1101_S 11
+
+/* Action No Ack */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1110_M 0x00001000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1110_S 12
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1110_M 0x00002000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1110_S 13
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1110_M 0x00004000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1110_S 14
+
+/* Reserved */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1111_M 0x00008000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_FP_MGMT_1111_S 15
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1111_M 0x00010000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MD_MGMT_1111_S 16
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1111_M 0x00020000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG1_MO_MGMT_1111_S 17
+
+/*
+ * Subtype based CTRL frames enable bits.
+ * FP: Filter_Pass, MD: Monitor_Direct, MO: Monitor_Other
+ */
+/* Reserved */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0000_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0000_S 0
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0000_M 0x00000002
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0000_S 1
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0000_M 0x00000004
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0000_S 2
+
+/* Reserved */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0001_M 0x00000008
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0001_S 3
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0001_M 0x00000010
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0001_S 4
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0001_M 0x00000020
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0001_S 5
+
+/* Reserved */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0010_M 0x00000040
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0010_S 6
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0010_M 0x00000080
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0010_S 7
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0010_M 0x00000100
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0010_S 8
+
+/* Reserved */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0011_M 0x00000200
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0011_S 9
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0011_M 0x00000400
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0011_S 10
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0011_M 0x00000800
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0011_S 11
+
+/* Reserved */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0100_M 0x00001000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0100_S 12
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0100_M 0x00002000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0100_S 13
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0100_M 0x00004000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0100_S 14
+
+/* Reserved */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0101_M 0x00008000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0101_S 15
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0101_M 0x00010000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0101_S 16
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0101_M 0x00020000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0101_S 17
+
+/* Reserved */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0110_M 0x00040000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0110_S 18
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0110_M 0x00080000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0110_S 19
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0110_M 0x00100000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0110_S 20
+
+/* Control Wrapper */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0111_M 0x00200000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_0111_S 21
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0111_M 0x00400000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_0111_S 22
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0111_M 0x00800000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_0111_S 23
+
+/* Block Ack Request */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_1000_M 0x01000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_1000_S 24
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_1000_M 0x02000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_1000_S 25
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_1000_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_1000_S 26
+
+/* Block Ack*/
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_1001_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_FP_CTRL_1001_S 27
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_1001_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MD_CTRL_1001_S 28
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_1001_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG2_MO_CTRL_1001_S 29
+
+/* PS-POLL */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1010_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1010_S 0
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1010_M 0x00000002
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1010_S 1
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1010_M 0x00000004
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1010_S 2
+
+/* RTS */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1011_M 0x00000008
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1011_S 3
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1011_M 0x00000010
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1011_S 4
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1011_M 0x00000020
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1011_S 5
+
+/* CTS */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1100_M 0x00000040
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1100_S 6
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1100_M 0x00000080
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1100_S 7
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1100_M 0x00000100
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1100_S 8
+
+/* ACK */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1101_M 0x00000200
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1101_S 9
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1101_M 0x00000400
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1101_S 10
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1101_M 0x00000800
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1101_S 11
+
+/* CF-END */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1110_M 0x00001000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1110_S 12
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1110_M 0x00002000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1110_S 13
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1110_M 0x00004000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1110_S 14
+
+/* CF-END + CF-ACK */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1111_M 0x00008000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_CTRL_1111_S 15
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1111_M 0x00010000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_CTRL_1111_S 16
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1111_M 0x00020000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_CTRL_1111_S 17
+
+/* Multicast data */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_DATA_MCAST_M 0x00040000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_DATA_MCAST_S 18
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_DATA_MCAST_M 0x00080000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_DATA_MCAST_S 19
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_DATA_MCAST_M 0x00100000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_DATA_MCAST_S 20
+
+/* Unicast data */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_DATA_UCAST_M 0x00200000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_DATA_UCAST_S 21
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_DATA_UCAST_M 0x00400000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_DATA_UCAST_S 22
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_DATA_UCAST_M 0x00800000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_DATA_UCAST_S 23
+
+/* NULL data */
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_DATA_NULL_M 0x01000000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_FP_DATA_NULL_S 24
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_DATA_NULL_M 0x02000000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MD_DATA_NULL_S 25
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_DATA_NULL_M 0x04000000
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_FLAG3_MO_DATA_NULL_S 26
+
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_SET(word, httsym, value) \
+ do { \
+ HTT_CHECK_SET_VAL(httsym, value); \
+ (word) |= (value) << httsym##_S; \
+ } while (0)
+#define HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_GET(word, httsym) \
+ (((word) & httsym##_M) >> httsym##_S)
+
+#define htt_rx_ring_pkt_enable_subtype_set( \
+ word, flag, mode, type, subtype, val) \
+ HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_SET( \
+ word, HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_##flag##_##mode##_##type##_##subtype, val)
+
+#define htt_rx_ring_pkt_enable_subtype_get( \
+ word, flag, mode, type, subtype) \
+ HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_GET( \
+ word, HTT_RX_RING_SELECTION_CFG_PKT_TYPE_ENABLE_##flag##_##mode##_##type##_##subtype)
+
+/* Definition to filter in TLVs */
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_MPDU_START_M 0x00000001
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_MPDU_START_S 0
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_MSDU_START_M 0x00000002
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_MSDU_START_S 1
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PACKET_M 0x00000004
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PACKET_S 2
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_MSDU_END_M 0x00000008
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_MSDU_END_S 3
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_MPDU_END_M 0x00000010
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_MPDU_END_S 4
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PACKET_HEADER_M 0x00000020
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PACKET_HEADER_S 5
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_RESERVED_M 0x00000040
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_RESERVED_S 6
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_ATTENTION_M 0x00000080
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_ATTENTION_S 7
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PPDU_START_M 0x00000100
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PPDU_START_S 8
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PPDU_END_M 0x00000200
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PPDU_END_S 9
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PPDU_END_USER_STATS_M 0x00000400
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PPDU_END_USER_STATS_S 10
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PPDU_END_USER_STATS_EXT_M 0x00000800
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PPDU_END_USER_STATS_EXT_S 11
+
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PPDU_END_STATUS_DONE_M 0x00001000
+#define HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_PPDU_END_STATUS_DONE_S 12
+
+#define HTT_RX_RING_TLV_ENABLE_SET(word, httsym, enable) \
+ do { \
+ HTT_CHECK_SET_VAL(httsym, enable); \
+ (word) |= (enable) << httsym##_S; \
+ } while (0)
+#define HTT_RX_RING_TLV_ENABLE_GET(word, httsym) \
+ (((word) & httsym##_M) >> httsym##_S)
+
+#define htt_rx_ring_tlv_filter_in_enable_set(word, tlv, enable) \
+ HTT_RX_RING_TLV_ENABLE_SET( \
+ word, HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_##tlv, enable)
+
+#define htt_rx_ring_tlv_filter_in_enable_get(word, tlv) \
+ HTT_RX_RING_TLV_ENABLE_GET( \
+ word, HTT_RX_RING_SELECTION_CFG_TLV_FILTER_IN_FLAG_RX_##tlv)
@@ -2942,6 +4067,7 @@
HTT_T2H_MSG_TYPE_RATE_REPORT = 0x17,
HTT_T2H_MSG_TYPE_FLOW_POOL_MAP = 0x18,
HTT_T2H_MSG_TYPE_FLOW_POOL_UNMAP = 0x19,
+ HTT_T2H_MSG_TYPE_SRING_SETUP_DONE = 0x1a,
HTT_T2H_MSG_TYPE_TEST,
/* keep this last */
@@ -6921,4 +8047,80 @@
((_var) |= ((_val) << HTT_FLOW_POOL_UNMAP_FLOW_POOL_ID_S)); \
} while (0)
+/**
+ * @brief HTT_T2H_MSG_TYPE_SRING_SETUP_DONE Message
+ *
+ * @details
+ * HTT_T2H_MSG_TYPE_SRING_SETUP_DONE message is sent by the target when
+ * SRNG ring setup is done
+ *
+ * This message indicates whether the last setup operation is successful.
+ * It will be sent to host when host set respose_required bit in
+ * HTT_H2T_MSG_TYPE_SRING_SETUP.
+ * The message would appear as follows:
+ *
+ * |31 24|23 16|15 8|7 0|
+ * |--------------- +----------------+----------------+----------------|
+ * | setup_status | ring_id | pdev_id | msg_type |
+ * |-------------------------------------------------------------------|
+ *
+ * The message is interpreted as follows:
+ * dword0 - b'0:7 - msg_type: This will be set to
+ * HTT_T2H_MSG_TYPE_SRING_SETUP_DONE
+ * b'8:15 - pdev_id:
+ * 0 (for rings at SOC/UMAC level),
+ * 1/2/3 mac id (for rings at LMAC level)
+ * b'16:23 - ring_id: Identify the ring which is set up
+ * More details can be got from enum htt_srng_ring_id
+ * b'24:31 - setup_status: Indicate status of setup operation
+ * Refer to htt_ring_setup_status
+ */
+
+PREPACK struct htt_sring_setup_done_t {
+ A_UINT32 msg_type: 8,
+ pdev_id: 8,
+ ring_id: 8,
+ setup_status: 8;
+} POSTPACK;
+
+enum htt_ring_setup_status {
+ htt_ring_setup_status_ok = 0,
+ htt_ring_setup_status_error,
+};
+
+#define HTT_SRING_SETUP_DONE_SZ (sizeof(struct htt_sring_setup_done_t))
+
+#define HTT_SRING_SETUP_DONE_PDEV_ID_M 0x0000ff00
+#define HTT_SRING_SETUP_DONE_PDEV_ID_S 8
+#define HTT_SRING_SETUP_DONE_PDEV_ID_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_DONE_PDEV_ID_M) >> \
+ HTT_SRING_SETUP_DONE_PDEV_ID_S)
+#define HTT_SRING_SETUP_DONE_PDEV_ID_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_DONE_PDEV_ID, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_DONE_PDEV_ID_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_DONE_RING_ID_M 0x00ff0000
+#define HTT_SRING_SETUP_DONE_RING_ID_S 16
+#define HTT_SRING_SETUP_DONE_RING_ID_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_DONE_RING_ID_M) >> \
+ HTT_SRING_SETUP_DONE_RING_ID_S)
+#define HTT_SRING_SETUP_DONE_RING_ID_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_DONE_RING_ID, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_DONE_RING_ID_S)); \
+ } while (0)
+
+#define HTT_SRING_SETUP_DONE_STATUS_M 0xff000000
+#define HTT_SRING_SETUP_DONE_STATUS_S 24
+#define HTT_SRING_SETUP_DONE_STATUS_GET(_var) \
+ (((_var) & HTT_SRING_SETUP_DONE_STATUS_M) >> \
+ HTT_SRING_SETUP_DONE_STATUS_S)
+#define HTT_SRING_SETUP_DONE_STATUS_SET(_var, _val) \
+ do { \
+ HTT_CHECK_SET_VAL(HTT_SRING_SETUP_DONE_STATUS, _val); \
+ ((_var) |= ((_val) << HTT_SRING_SETUP_DONE_STATUS_S)); \
+ } while (0)
+
#endif
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_rx.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_rx.c
index da171d7..065fbb8 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_rx.c
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_rx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -50,6 +50,7 @@
#include <ol_htt_rx_api.h>
#include <htt_internal.h> /* HTT_ASSERT, htt_pdev_t, HTT_RX_BUF_SIZE */
#include "regtable.h"
+#include "adf_trace.h"
#include <ieee80211_common.h> /* ieee80211_frame, ieee80211_qoscntl */
#include <ieee80211_defines.h> /* ieee80211_rx_status */
@@ -1867,6 +1868,8 @@
/* Get the total number of MSDUs */
msdu_count = HTT_RX_IN_ORD_PADDR_IND_MSDU_CNT_GET(*(msg_word + 1));
HTT_RX_CHECK_MSDU_COUNT(msdu_count);
+ peer_id = HTT_RX_IN_ORD_PADDR_IND_PEER_ID_GET(
+ *(u_int32_t *)rx_ind_data);
msg_word = (u_int32_t *)(rx_ind_data + HTT_RX_IN_ORD_PADDR_IND_HDR_BYTES);
if (offload_ind) {
@@ -1908,6 +1911,14 @@
*/
adf_nbuf_pull_head(msdu, HTT_RX_STD_DESC_RESERVATION);
+ adf_dp_trace_set_track(msdu, ADF_RX);
+ NBUF_SET_PACKET_TRACK(msdu, NBUF_TX_PKT_DATA_TRACK);
+ ol_rx_log_packet(pdev, peer_id, msdu);
+ DPTRACE(adf_dp_trace(msdu,
+ ADF_DP_TRACE_RX_HTT_PACKET_PTR_RECORD,
+ adf_nbuf_data_addr(msdu),
+ sizeof(adf_nbuf_data(msdu)), ADF_RX));
+
adf_nbuf_trim_tail(
msdu, HTT_RX_BUF_SIZE - (RX_STD_DESC_SIZE +
HTT_RX_IN_ORD_PADDR_IND_MSDU_LEN_GET(*(msg_word + 1))));
@@ -1918,8 +1929,7 @@
msdu_count--;
/* calling callback function for packet logging */
- peer_id = HTT_RX_IN_ORD_PADDR_IND_PEER_ID_GET(
- *(u_int32_t *)rx_ind_data);
+
if (adf_os_unlikely((*((u_int8_t *) &rx_desc->fw_desc.u.val)) &
FW_RX_DESC_MIC_ERR_M))
status = RX_PKT_FATE_FW_DROP_INVALID;
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_tx.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_tx.c
index 3a2fa5e..6073be6 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_tx.c
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/HTT/htt_tx.c
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2011, 2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011, 2014, 2016-2017 The Linux Foundation.
+ * All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -475,7 +476,7 @@
NBUF_UPDATE_TX_PKT_COUNT(msdu, NBUF_TX_PKT_HTT);
DPTRACE(adf_dp_trace(msdu, ADF_DP_TRACE_HTT_PACKET_PTR_RECORD,
adf_nbuf_data_addr(msdu),
- sizeof(adf_nbuf_data(msdu))));
+ sizeof(adf_nbuf_data(msdu)), ADF_TX));
if (adf_nbuf_queue_len(&pdev->txnbufq) > 0) {
HTT_TX_NBUF_QUEUE_ADD(pdev, msdu);
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx.c
index 769e72b..c0780c91 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx.c
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -60,6 +60,7 @@
#include <ol_vowext_dbg_defs.h>
#include <wma.h>
#include "pktlog_ac_fmt.h"
+#include "adf_trace.h"
#ifdef HTT_RX_RESTORE
#include "vos_cnss.h"
@@ -1317,6 +1318,11 @@
while (msdu) {
adf_nbuf_t next = adf_nbuf_next(msdu);
+ DPTRACE(adf_dp_trace(msdu,
+ ADF_DP_TRACE_RX_TXRX_PACKET_PTR_RECORD,
+ adf_nbuf_data_addr(msdu),
+ sizeof(adf_nbuf_data(msdu)), ADF_RX));
+
OL_RX_PEER_STATS_UPDATE(peer, msdu);
OL_RX_ERR_STATISTICS_1(vdev->pdev, vdev, peer, rx_desc, OL_RX_ERR_NONE);
TXRX_STATS_MSDU_INCR(vdev->pdev, rx.delivered, msdu);
@@ -1333,6 +1339,24 @@
OL_RX_OSIF_DELIVER(vdev, peer, msdu_list);
}
+/**
+ * ol_rx_log_packet() - log rx packet
+ * @htt_pdev: htt pdev
+ * @peer_id: peer_id
+ * @msdu: skb
+ *
+ * Return: none
+ */
+void ol_rx_log_packet(htt_pdev_handle htt_pdev,
+ uint8_t peer_id, adf_nbuf_t msdu)
+{
+ struct ol_txrx_peer_t *peer;
+
+ peer = ol_txrx_peer_find_by_id(htt_pdev->txrx_pdev, peer_id);
+ if (peer)
+ adf_dp_trace_log_pkt(peer->vdev->vdev_id, msdu, ADF_RX);
+}
+
void
ol_rx_offload_paddr_deliver_ind_handler(
htt_pdev_handle htt_pdev,
@@ -1353,6 +1377,14 @@
peer = ol_txrx_peer_find_by_id(htt_pdev->txrx_pdev, peer_id);
if (peer && peer->vdev) {
+ adf_dp_trace_set_track(head_buf, ADF_RX);
+ NBUF_SET_PACKET_TRACK(head_buf, NBUF_TX_PKT_DATA_TRACK);
+ adf_dp_trace_log_pkt(peer->vdev->vdev_id,
+ head_buf, ADF_RX);
+ DPTRACE(adf_dp_trace(head_buf,
+ ADF_DP_TRACE_RX_OFFLOAD_HTT_PACKET_PTR_RECORD,
+ adf_nbuf_data_addr(head_buf),
+ sizeof(adf_nbuf_data(head_buf)), ADF_RX));
vdev = peer->vdev;
OL_RX_OSIF_DELIVER(vdev, peer, head_buf);
} else {
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx.h b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx.h
index b189002..87e409a 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx.h
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_rx.h
@@ -1,5 +1,6 @@
/*
- * Copyright (c) 2011, 2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011, 2014, 2016-2017 The Linux Foundation.
+ * All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -63,6 +64,9 @@
unsigned tid,
adf_nbuf_t head_msdu);
+void ol_rx_log_packet(htt_pdev_handle htt_pdev,
+ uint8_t peer_id, adf_nbuf_t msdu);
+
void
ol_rx_offload_paddr_deliver_ind_handler(
htt_pdev_handle htt_pdev,
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx.c
index cc3c356..f7b95aa 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx.c
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, 2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -111,7 +111,7 @@
* tx_send call.
*/
next = adf_nbuf_next(msdu);
- ol_tx_send(vdev->pdev, tx_desc, msdu);
+ ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id);
msdu = next;
}
return NULL; /* all MSDUs were accepted */
@@ -242,7 +242,7 @@
DPTRACE(adf_dp_trace(msdu_list,
ADF_DP_TRACE_TXRX_QUEUE_PACKET_PTR_RECORD,
adf_nbuf_data_addr(msdu_list),
- sizeof(adf_nbuf_data(msdu_list))));
+ sizeof(adf_nbuf_data(msdu_list)), ADF_TX));
vdev->ll_pause.txq.depth++;
if (!vdev->ll_pause.txq.head) {
@@ -514,7 +514,7 @@
* downloaded to the target via the HTT tx descriptor.
*/
htt_tx_desc_display(tx_desc->htt_tx_desc);
- ol_tx_send(vdev->pdev, tx_desc, msdu);
+ ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id);
msdu = next;
}
return NULL; /* all MSDUs were accepted */
@@ -665,8 +665,7 @@
if (adf_os_atomic_read(&pdev->tx_queue.rsrc_cnt) >
TXRX_HL_TX_DESC_HI_PRIO_RESERVED) {
tx_desc = ol_tx_desc_hl(pdev, vdev, msdu, &tx_msdu_info);
- } else if ((adf_nbuf_is_dhcp_pkt(msdu) == A_STATUS_OK)
- || (adf_nbuf_is_eapol_pkt(msdu) == A_STATUS_OK)) {
+ } else if (ADF_NBUF_GET_IS_DHCP(msdu) || ADF_NBUF_GET_IS_EAPOL(msdu)) {
tx_desc = ol_tx_desc_hl(pdev, vdev, msdu, &tx_msdu_info);
TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
"Provided tx descriptor from reserve pool for DHCP/EAPOL\n");
@@ -1275,7 +1274,7 @@
htt_tx_desc_set_peer_id(tx_desc->htt_tx_desc, peer_id);
- ol_tx_send(vdev->pdev, tx_desc, msdu);
+ ol_tx_send(vdev->pdev, tx_desc, msdu, vdev->vdev_id);
return NULL;
}
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_classify.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_classify.c
index 49bd5b0..e3856d5 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_classify.c
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_classify.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -376,7 +376,7 @@
return NULL; /* error */
} else if ((peer->security[OL_TXRX_PEER_SECURITY_MULTICAST].sec_type
!= htt_sec_type_wapi) &&
- (A_STATUS_OK == adf_nbuf_is_dhcp_pkt(tx_nbuf))) {
+ adf_nbuf_is_dhcp_pkt(tx_nbuf)) {
/* DHCP frame to go with voice priority */
txq = &peer->txqs[TX_DHCP_TID];
tx_msdu_info->htt.info.ext_tid = TX_DHCP_TID;
@@ -502,7 +502,7 @@
tid = OL_TX_NON_QOS_TID;
} else if ((peer->security[OL_TXRX_PEER_SECURITY_UNICAST].sec_type
!= htt_sec_type_wapi) &&
- (A_STATUS_OK == adf_nbuf_is_dhcp_pkt(tx_nbuf))) {
+ adf_nbuf_is_dhcp_pkt(tx_nbuf)) {
/* DHCP frame to go with voice priority */
tid = TX_DHCP_TID;
}
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_desc.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_desc.c
index 3ac64c4..61fac64 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_desc.c
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_desc.c
@@ -68,6 +68,7 @@
pdev->tx_desc.num_free--;
tx_desc = pdev->tx_desc.freelist->tx_desc;
pdev->tx_desc.freelist = pdev->tx_desc.freelist->next;
+ ol_tx_desc_dup_detect_set(pdev, tx_desc);
#ifdef QCA_SUPPORT_TXDESC_SANITY_CHECKS
if (tx_desc->pkt_type != 0xff
#ifdef QCA_COMPUTE_TX_DELAY
@@ -131,6 +132,7 @@
ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc)
{
adf_os_spin_lock_bh(&pdev->tx_mutex);
+ ol_tx_desc_dup_detect_reset(pdev, tx_desc);
#ifdef QCA_SUPPORT_TXDESC_SANITY_CHECKS
tx_desc->pkt_type = 0xff;
#ifdef QCA_COMPUTE_TX_DELAY
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_desc.h b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_desc.h
index 8e7ebb7..e6d2a01 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_desc.h
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_desc.h
@@ -37,6 +37,11 @@
#include <ol_txrx_types.h> /* ol_tx_desc_t */
#include <ol_txrx_internal.h> /*TXRX_ASSERT2 */
+#define DIV_BY_8 3
+#define DIV_BY_32 5
+#define MOD_BY_8 0x7
+#define MOD_BY_32 0x1F
+
/**
* @brief Allocate and initialize a tx descriptor for a LL system.
* @details
@@ -174,4 +179,130 @@
void
ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc);
+#ifdef DESC_DUP_DETECT_DEBUG
+/**
+ * ol_tx_desc_dup_detect_init() - initialize descriptor duplication logic
+ * @pdev: pdev handle
+ * @pool_size: global pool size
+ *
+ * Return: none
+ */
+static inline
+void ol_tx_desc_dup_detect_init(struct ol_txrx_pdev_t *pdev, uint16_t pool_size)
+{
+ uint16_t size = (pool_size >> DIV_BY_8) +
+ sizeof(*pdev->tx_desc.free_list_bitmap);
+ pdev->tx_desc.free_list_bitmap = vos_mem_malloc(size);
+ if (!pdev->tx_desc.free_list_bitmap)
+ adf_os_print("%s: malloc failed", __func__);
+ vos_mem_set(pdev->tx_desc.free_list_bitmap, size, 0);
+}
+
+/**
+ * ol_tx_desc_dup_detect_deinit() - deinit descriptor duplication logic
+ * @pdev: pdev handle
+ *
+ * Return: none
+ */
+static inline
+void ol_tx_desc_dup_detect_deinit(struct ol_txrx_pdev_t *pdev)
+{
+ adf_os_print("%s: pool_size %d num_free %d\n", __func__,
+ pdev->tx_desc.pool_size, pdev->tx_desc.num_free);
+ if (pdev->tx_desc.free_list_bitmap)
+ vos_mem_free(pdev->tx_desc.free_list_bitmap);
+}
+
+/**
+ * ol_tx_desc_dup_detect_set() - set bit for msdu_id
+ * @pdev: pdev handle
+ * @tx_desc: tx descriptor
+ *
+ * Return: none
+ */
+static inline
+void ol_tx_desc_dup_detect_set(struct ol_txrx_pdev_t *pdev,
+ struct ol_tx_desc_t *tx_desc)
+{
+ uint16_t msdu_id = ol_tx_desc_id(pdev, tx_desc);
+ bool test;
+
+ if (!pdev->tx_desc.free_list_bitmap)
+ return;
+
+ if (adf_os_unlikely(msdu_id > pdev->tx_desc.pool_size)) {
+ adf_os_print("%s: msdu_id %d > pool_size %d",
+ __func__, msdu_id, pdev->tx_desc.pool_size);
+ VOS_BUG(0);
+ }
+
+ test = test_and_set_bit(msdu_id, pdev->tx_desc.free_list_bitmap);
+ if (adf_os_unlikely(test)) {
+ uint16_t size = (pdev->tx_desc.pool_size >> DIV_BY_8) +
+ ((pdev->tx_desc.pool_size & MOD_BY_8) ? 1 : 0);
+ adf_os_print("duplicate msdu_id %d detected !!\n", msdu_id);
+ vos_trace_hex_dump(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_ERROR,
+ (void *)pdev->tx_desc.free_list_bitmap, size);
+ VOS_BUG(0);
+ }
+}
+
+/**
+ * ol_tx_desc_dup_detect_reset() - reset bit for msdu_id
+ * @pdev: pdev handle
+ * @tx_desc: tx descriptor
+ *
+ * Return: none
+ */
+static inline
+void ol_tx_desc_dup_detect_reset(struct ol_txrx_pdev_t *pdev,
+ struct ol_tx_desc_t *tx_desc)
+{
+ uint16_t msdu_id = ol_tx_desc_id(pdev, tx_desc);
+ bool test;
+
+ if (!pdev->tx_desc.free_list_bitmap)
+ return;
+
+ if (adf_os_unlikely(msdu_id > pdev->tx_desc.pool_size)) {
+ adf_os_print("%s: msdu_id %d > pool_size %d",
+ __func__, msdu_id, pdev->tx_desc.pool_size);
+ VOS_BUG(0);
+ }
+
+ test = !test_and_clear_bit(msdu_id, pdev->tx_desc.free_list_bitmap);
+ if (adf_os_unlikely(test)) {
+ uint16_t size = (pdev->tx_desc.pool_size >> DIV_BY_8) +
+ ((pdev->tx_desc.pool_size & MOD_BY_8) ? 1 : 0);
+ adf_os_print("duplicate free msg received for msdu_id %d!!\n",
+ msdu_id);
+ vos_trace_hex_dump(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_ERROR,
+ (void *)pdev->tx_desc.free_list_bitmap, size);
+ VOS_BUG(0);
+ }
+}
+#else
+static inline
+void ol_tx_desc_dup_detect_init(struct ol_txrx_pdev_t *pdev, uint16_t size)
+{
+}
+
+static inline
+void ol_tx_desc_dup_detect_deinit(struct ol_txrx_pdev_t *pdev)
+{
+}
+
+static inline
+void ol_tx_desc_dup_detect_set(struct ol_txrx_pdev_t *pdev,
+ struct ol_tx_desc_t *tx_desc)
+{
+}
+
+static inline
+void ol_tx_desc_dup_detect_reset(struct ol_txrx_pdev_t *pdev,
+ struct ol_tx_desc_t *tx_desc)
+{
+}
+#endif
+
#endif /* _OL_TX_DESC__H_ */
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_send.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_send.c
index b35dc99a..0734b79e 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_send.c
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_send.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -211,7 +211,7 @@
ol_tx_send(
struct ol_txrx_pdev_t *pdev,
struct ol_tx_desc_t *tx_desc,
- adf_nbuf_t msdu)
+ adf_nbuf_t msdu, uint8_t vdev_id)
{
int msdu_credit_consumed;
u_int16_t id;
@@ -222,7 +222,7 @@
NBUF_UPDATE_TX_PKT_COUNT(msdu, NBUF_TX_PKT_TXRX);
DPTRACE(adf_dp_trace_ptr(msdu, ADF_DP_TRACE_TXRX_PACKET_PTR_RECORD,
adf_nbuf_data_addr(msdu),
- sizeof(adf_nbuf_data(msdu)), tx_desc->id, 0));
+ sizeof(adf_nbuf_data(msdu)), tx_desc->id, vdev_id));
failed = htt_tx_send_std(pdev->htt_pdev, msdu, id);
if (adf_os_unlikely(failed)) {
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_send.h b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_send.h
index 2d20da9..a19c14c 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_send.h
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_tx_send.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011, 2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -60,7 +60,7 @@
ol_tx_send(
struct ol_txrx_pdev_t *pdev,
struct ol_tx_desc_t *tx_desc,
- adf_nbuf_t msdu);
+ adf_nbuf_t msdu, uint8_t vdev_id);
/**
* @brief Send a tx batch download to the target.
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c
index ea161b5..587a142 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -434,6 +434,8 @@
ol_tx_target_credit_init(pdev, desc_pool_size);
}
+ ol_tx_desc_dup_detect_init(pdev, desc_pool_size);
+
pdev->htt_pdev = htt_attach(
pdev, ctrl_pdev, htc_pdev, osdev, desc_pool_size);
if (!pdev->htt_pdev) {
@@ -989,6 +991,8 @@
htt_detach(pdev->htt_pdev);
+ ol_tx_desc_dup_detect_deinit(pdev);
+
ol_txrx_peer_find_detach(pdev);
adf_os_spinlock_destroy(&pdev->tx_mutex);
@@ -1723,7 +1727,7 @@
if (adf_os_atomic_dec_and_test(&peer->ref_cnt)) {
u_int16_t peer_id;
- TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
+ TXRX_PRINT(TXRX_PRINT_LEVEL_INFO1,
"Deleting peer %p (%02x:%02x:%02x:%02x:%02x:%02x)\n",
peer,
peer->mac_addr.raw[0], peer->mac_addr.raw[1],
@@ -1836,7 +1840,7 @@
/* debug print to dump rx reorder state */
//htt_rx_reorder_log_print(vdev->pdev->htt_pdev);
- TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
+ TXRX_PRINT(TXRX_PRINT_LEVEL_INFO1,
"%s:peer %p (%02x:%02x:%02x:%02x:%02x:%02x)\n",
__func__, peer,
peer->mac_addr.raw[0], peer->mac_addr.raw[1],
@@ -1897,12 +1901,61 @@
total = ol_cfg_target_tx_credit(pdev->ctrl_pdev);
TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
- "Total tx credits %d free_credits %d",
+ "Total tx credits %d free_credits %d\n",
total, pdev->tx_desc.num_free);
return;
}
+static void ol_txrx_dump_tx_queue_paused_reason(ol_txrx_pdev_handle pdev_handle)
+{
+ struct ol_txrx_pdev_t *pdev = (ol_txrx_pdev_handle)pdev_handle;
+ struct ol_txrx_vdev_t *vdev;
+
+ TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) {
+ if (vdev->ll_pause.paused_reason & OL_TXQ_PAUSE_REASON_FW)
+ TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
+ "%s vdev_id %d vdev tx queue paused reason %d",
+ __func__, vdev->vdev_id, vdev->ll_pause.paused_reason);
+ }
+ return;
+}
+
+static void ol_txrx_dump_tx_queue_length(ol_txrx_pdev_handle pdev_handle)
+{
+ struct ol_txrx_pdev_t *pdev = (ol_txrx_pdev_handle)pdev_handle;
+ struct ol_txrx_vdev_t *vdev;
+
+ TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) {
+ if (vdev->ll_pause.txq.depth)
+ TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
+ "%s vdev_id %d vdev tx queue depth %d",
+ __func__, vdev->vdev_id, vdev->ll_pause.txq.depth);
+ }
+ return;
+}
+
+void ol_txrx_dump_tx_queue_stats(ol_txrx_pdev_handle pdev_handle)
+{
+ uint16_t size = (pdev_handle->tx_desc.pool_size >> 3) +
+ ((pdev_handle->tx_desc.pool_size & 0x7) ? 1 : 0);
+
+ TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
+ "%s: Dump TX LL Queue Stats:\n",
+ __func__);
+ TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
+ "%s vdev tx ll queues status: %d num of frames pending: %d\n",
+ __func__, ol_txrx_get_queue_status(pdev_handle), ol_txrx_get_tx_pending(pdev_handle));
+ ol_txrx_dump_tx_queue_paused_reason(pdev_handle);
+ ol_txrx_dump_tx_queue_length(pdev_handle);
+ TXRX_PRINT(TXRX_PRINT_LEVEL_ERR, "%s: dump freelist bitmap\n",
+ __func__);
+ /* Dump the tx descriptor dup-detection bitmap array (33 DWORDS)*/
+ vos_trace_hex_dump(VOS_MODULE_ID_TXRX, VOS_TRACE_LEVEL_ERROR,
+ (void *)pdev_handle->tx_desc.free_list_bitmap, size);
+ return;
+}
+
int
ol_txrx_get_tx_pending(ol_txrx_pdev_handle pdev_handle)
{
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_peer_find.c b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_peer_find.c
index 5196bc3..f70056a 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_peer_find.c
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_peer_find.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -487,7 +487,7 @@
* Remove a reference to the peer.
* If there are no more references, delete the peer object.
*/
- TXRX_PRINT(TXRX_PRINT_LEVEL_ERR,
+ TXRX_PRINT(TXRX_PRINT_LEVEL_INFO1,
"%s: Remove the ID %d reference to peer %p\n",
__func__, peer_id, peer);
ol_txrx_peer_unref_delete(peer);
diff --git a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_types.h b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_types.h
index bc80e551..42f1c25 100644
--- a/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_types.h
+++ b/drivers/staging/qcacld-2.0/CORE/CLD_TXRX/TXRX/ol_txrx_types.h
@@ -598,6 +598,9 @@
u_int16_t num_free;
struct ol_tx_desc_list_elem_t *array;
struct ol_tx_desc_list_elem_t *freelist;
+#ifdef DESC_DUP_DETECT_DEBUG
+ unsigned long *free_list_bitmap;
+#endif
} tx_desc;
struct {
diff --git a/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_helper.c b/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_helper.c
index 59ab798..52e14f2 100644
--- a/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_helper.c
+++ b/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_helper.c
@@ -46,7 +46,6 @@
#include <wlan_hdd_tx_rx.h>
#include <wniApi.h>
#include <wlan_nlink_srv.h>
-#include <wlan_btc_svc.h>
#include <wlan_hdd_cfg.h>
#include <wlan_ptt_sock_svc.h>
#include <wlan_hdd_wowl.h>
diff --git a/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_main.c b/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_main.c
index 26d05f7..f7b649d 100644
--- a/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_main.c
+++ b/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_main.c
@@ -45,7 +45,6 @@
#include <wlan_hdd_tx_rx.h>
#include <wniApi.h>
#include <wlan_nlink_srv.h>
-#include <wlan_btc_svc.h>
#include <wlan_hdd_cfg.h>
#include <wlan_ptt_sock_svc.h>
#include <wlan_hdd_wowl.h>
diff --git a/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_rx.c b/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_rx.c
index 679cf25..c6f2d1e 100644
--- a/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_rx.c
+++ b/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_rx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -45,7 +45,6 @@
#include <wlan_hdd_tx_rx.h>
#include <wniApi.h>
#include <wlan_nlink_srv.h>
-#include <wlan_btc_svc.h>
#include <wlan_hdd_cfg.h>
#include <wlan_ptt_sock_svc.h>
#include <wlan_hdd_wowl.h>
diff --git a/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_tx.c b/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_tx.c
index 7427714..ab842fe 100644
--- a/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_tx.c
+++ b/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_tx.c
@@ -45,7 +45,6 @@
#include <wlan_hdd_tx_rx.h>
#include <wniApi.h>
#include <wlan_nlink_srv.h>
-#include <wlan_btc_svc.h>
#include <wlan_hdd_cfg.h>
#include <wlan_ptt_sock_svc.h>
#include <wlan_hdd_wowl.h>
diff --git a/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_txrx.c b/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_txrx.c
index bceba3d..b2f9039 100644
--- a/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_txrx.c
+++ b/drivers/staging/qcacld-2.0/CORE/EPPING/src/epping_txrx.c
@@ -45,7 +45,6 @@
#include <wlan_hdd_tx_rx.h>
#include <wniApi.h>
#include <wlan_nlink_srv.h>
-#include <wlan_btc_svc.h>
#include <wlan_hdd_cfg.h>
#include <wlan_ptt_sock_svc.h>
#include <wlan_hdd_wowl.h>
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_assoc.h b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_assoc.h
index 4376a69..3f91faf 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_assoc.h
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_assoc.h
@@ -49,6 +49,8 @@
/* Timeout in ms for peer info request completion */
#define IBSS_PEER_INFO_REQ_TIMOEUT 1000
+#define INVALID_PEER_IDX -1
+
/**
* enum eConnectionState - connection state values at HDD
* @eConnectionState_NotConnected: Not associated in Infra or participating in
@@ -174,4 +176,9 @@
bool hdd_save_peer(hdd_station_ctx_t *sta_ctx, uint8_t sta_id,
v_MACADDR_t *peer_mac_addr);
+void hdd_delete_peer(hdd_station_ctx_t *sta_ctx, uint8_t sta_id);
+
+int hdd_get_peer_idx(hdd_station_ctx_t *sta_ctx, v_MACADDR_t *addr);
+VOS_STATUS hdd_roamDeregisterSTA(hdd_adapter_t *adapter, uint8_t sta_id);
+
#endif
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_cfg.h b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_cfg.h
index 4df2657..20f44d2 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_cfg.h
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_cfg.h
@@ -56,6 +56,8 @@
//Number of items that can be configured
#define MAX_CFG_INI_ITEMS 1024
+#define MAX_PRB_REQ_VENDOR_OUI_INI_LEN 160
+#define VENDOR_SPECIFIC_IE_BITMAP 0x20000000
#ifdef SAP_AUTH_OFFLOAD
/* 802.11 pre-share key length */
@@ -549,16 +551,6 @@
#define CFG_ACTIVE_MIN_CHANNEL_TIME_MAX ( 10000 )
#define CFG_ACTIVE_MIN_CHANNEL_TIME_DEFAULT ( 20 )
-#define CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_NAME "gActiveMaxChannelTimeBtc"
-#define CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_MIN ( 0 )
-#define CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_MAX ( 10000 )
-#define CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_DEFAULT ( 120 )
-
-#define CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_NAME "gActiveMinChannelTimeBtc"
-#define CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_MIN ( 0 )
-#define CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_MAX ( 10000 )
-#define CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_DEFAULT ( 60 )
-
#define CFG_RETRY_LIMIT_ZERO_NAME "gRetryLimitZero"
#define CFG_RETRY_LIMIT_ZERO_MIN ( 0 )
#define CFG_RETRY_LIMIT_ZERO_MAX ( 15 )
@@ -2361,6 +2353,16 @@
#define CFG_ENABLE_VHT_FOR_24GHZ_MAX (1)
#define CFG_ENABLE_VHT_FOR_24GHZ_DEFAULT (0)
+/*
+ * Parameter to control VHT support based on vendor ie in 2.4 GHz band
+ * This parameter will enable SAP to read VHT capability in vendor ie in Assoc
+ * Req and send VHT caps in Resp to establish connection in VHT Mode.
+ */
+#define CFG_ENABLE_VENDOR_VHT_FOR_24GHZ_NAME "gEnableVendorVhtFor24GHzBand"
+#define CFG_ENABLE_VENDOR_VHT_FOR_24GHZ_MIN (0)
+#define CFG_ENABLE_VENDOR_VHT_FOR_24GHZ_MAX (1)
+#define CFG_ENABLE_VENDOR_VHT_FOR_24GHZ_DEFAULT (1)
+
#define CFG_MAX_MEDIUM_TIME "gMaxMediumTime"
#define CFG_MAX_MEDIUM_TIME_STAMIN WNI_CFG_MAX_MEDIUM_TIME_STAMIN
@@ -3860,6 +3862,160 @@
#define CFG_ACTIVE_MODE_OFFLOAD_MAX (1)
#define CFG_ACTIVE_MODE_OFFLOAD_DEFAULT (0)
+/*
+ * maximum interval (in seconds) for a
+ * single scan plan supported by the device.
+ */
+#define CFG_MAX_SCHED_SCAN_PLAN_INT_NAME "g_max_sched_scan_plan_int"
+#define CFG_MAX_SCHED_SCAN_PLAN_INT_MIN (1)
+#define CFG_MAX_SCHED_SCAN_PLAN_INT_MAX (7200)
+#define CFG_MAX_SCHED_SCAN_PLAN_INT_DEFAULT (3600)
+
+/*
+ * maximum number of iterations for a single
+ * scan plan supported by the device.
+ */
+#define CFG_MAX_SCHED_SCAN_PLAN_ITRNS_NAME "g_max_sched_scan_plan_itrns"
+#define CFG_MAX_SCHED_SCAN_PLAN_ITRNS_MIN (1)
+#define CFG_MAX_SCHED_SCAN_PLAN_ITRNS_MAX (100)
+#define CFG_MAX_SCHED_SCAN_PLAN_ITRNS_DEFAULT (10)
+
+/*
+ * 5G preference parameters for boosting RSSI
+ * enable_band_specific_pref: Enable preference for 5G from INI.
+ * raise_rssi_thresh_5g: A_band_boost_threshold above which 5 GHz is favored.
+ * raise_factor_5g : Factor by which 5GHz RSSI is boosted.
+ * max_raise_rssi_5g: Maximum boost that can be applied to 5GHz RSSI.
+ */
+
+#define CFG_ENABLE_5G_BAND_PREF_NAME "enable_5g_band_pref"
+#define CFG_ENABLE_5G_BAND_PREF_MIN (0)
+#define CFG_ENABLE_5G_BAND_PREF_MAX (1)
+#define CFG_ENABLE_5G_BAND_PREF_DEFAULT (0)
+
+#define CFG_5G_RSSI_BOOST_THRESHOLD_NAME "5g_rssi_boost_threshold"
+#define CFG_5G_RSSI_BOOST_THRESHOLD_MIN (-55)
+#define CFG_5G_RSSI_BOOST_THRESHOLD_MAX (-70)
+#define CFG_5G_RSSI_BOOST_THRESHOLD_DEFAULT (-60)
+
+#define CFG_5G_RSSI_BOOST_FACTOR_NAME "5g_rssi_boost_factor"
+#define CFG_5G_RSSI_BOOST_FACTOR_MIN (0)
+#define CFG_5G_RSSI_BOOST_FACTOR_MAX (2)
+#define CFG_5G_RSSI_BOOST_FACTOR_DEFAULT (1)
+
+#define CFG_5G_MAX_RSSI_BOOST_NAME "5g_max_rssi_boost"
+#define CFG_5G_MAX_RSSI_BOOST_MIN (0)
+#define CFG_5G_MAX_RSSI_BOOST_MAX (20)
+#define CFG_5G_MAX_RSSI_BOOST_DEFAULT (10)
+
+/*
+ * 5G preference parameters for penalizing RSSI
+ * drop_rssi_thresh_5g: threshold below which 5 GHz is not favored.
+ * drop_factor_5g : Factor by which a weak 5GHz RSSI is penalized.
+ * max_drop_rssi_5g: Maximum penalty that can be applied to 5GHz RSSI.
+ */
+
+#define CFG_5G_RSSI_PENALIZE_THRESHOLD_NAME "5g_rssi_penalize_threshold"
+#define CFG_5G_RSSI_PENALIZE_THRESHOLD_MIN (-65)
+#define CFG_5G_RSSI_PENALIZE_THRESHOLD_MAX (-80)
+#define CFG_5G_RSSI_PENALIZE_THRESHOLD_DEFAULT (-70)
+
+#define CFG_5G_RSSI_PENALIZE_FACTOR_NAME "5g_rssi_penalize_factor"
+#define CFG_5G_RSSI_PENALIZE_FACTOR_MIN (0)
+#define CFG_5G_RSSI_PENALIZE_FACTOR_MAX (2)
+#define CFG_5G_RSSI_PENALIZE_FACTOR_DEFAULT (1)
+
+#define CFG_5G_MAX_RSSI_PENALIZE_NAME "5g_max_rssi_penalize"
+#define CFG_5G_MAX_RSSI_PENALIZE_MIN (0)
+#define CFG_5G_MAX_RSSI_PENALIZE_MAX (20)
+#define CFG_5G_MAX_RSSI_PENALIZE_DEFAULT (10)
+
+/* enable/disable probe request whiltelist IE feature */
+#define CFG_PRB_REQ_IE_WHITELIST_NAME "g_enable_probereq_whitelist_ies"
+#define CFG_PRB_REQ_IE_WHITELIST_MIN (0)
+#define CFG_PRB_REQ_IE_WHITELIST_MAX (1)
+#define CFG_PRB_REQ_IE_WHITELIST_DEFAULT (0)
+/*
+ * For IE white listing in Probe Req, following ini parameters from
+ * g_probe_req_ie_bitmap_0 to g_probe_req_ie_bitmap_7 are used. User needs to
+ * input this values in hexa decimal format, when bit is set, corresponding ie
+ * needs to be included in probe request.
+ */
+#define CFG_PRB_REQ_IE_BIT_MAP0_NAME "g_probe_req_ie_bitmap_0"
+#define CFG_PRB_REQ_IE_BIT_MAP0_MIN (0x00000000)
+#define CFG_PRB_REQ_IE_BIT_MAP0_MAX (0xFFFFFFFF)
+#define CFG_PRB_REQ_IE_BIT_MAP0_DEFAULT (0x00000000)
+
+#define CFG_PRB_REQ_IE_BIT_MAP1_NAME "g_probe_req_ie_bitmap_1"
+#define CFG_PRB_REQ_IE_BIT_MAP1_MIN (0x00000000)
+#define CFG_PRB_REQ_IE_BIT_MAP1_MAX (0xFFFFFFFF)
+#define CFG_PRB_REQ_IE_BIT_MAP1_DEFAULT (0x00000000)
+
+#define CFG_PRB_REQ_IE_BIT_MAP2_NAME "g_probe_req_ie_bitmap_2"
+#define CFG_PRB_REQ_IE_BIT_MAP2_MIN (0x00000000)
+#define CFG_PRB_REQ_IE_BIT_MAP2_MAX (0xFFFFFFFF)
+#define CFG_PRB_REQ_IE_BIT_MAP2_DEFAULT (0x00000000)
+
+#define CFG_PRB_REQ_IE_BIT_MAP3_NAME "g_probe_req_ie_bitmap_3"
+#define CFG_PRB_REQ_IE_BIT_MAP3_MIN (0x00000000)
+#define CFG_PRB_REQ_IE_BIT_MAP3_MAX (0xFFFFFFFF)
+#define CFG_PRB_REQ_IE_BIT_MAP3_DEFAULT (0x00000000)
+
+#define CFG_PRB_REQ_IE_BIT_MAP4_NAME "g_probe_req_ie_bitmap_4"
+#define CFG_PRB_REQ_IE_BIT_MAP4_MIN (0x00000000)
+#define CFG_PRB_REQ_IE_BIT_MAP4_MAX (0xFFFFFFFF)
+#define CFG_PRB_REQ_IE_BIT_MAP4_DEFAULT (0x00000000)
+
+#define CFG_PRB_REQ_IE_BIT_MAP5_NAME "g_probe_req_ie_bitmap_5"
+#define CFG_PRB_REQ_IE_BIT_MAP5_MIN (0x00000000)
+#define CFG_PRB_REQ_IE_BIT_MAP5_MAX (0xFFFFFFFF)
+#define CFG_PRB_REQ_IE_BIT_MAP5_DEFAULT (0x00000000)
+
+#define CFG_PRB_REQ_IE_BIT_MAP6_NAME "g_probe_req_ie_bitmap_6"
+#define CFG_PRB_REQ_IE_BIT_MAP6_MIN (0x00000000)
+#define CFG_PRB_REQ_IE_BIT_MAP6_MAX (0xFFFFFFFF)
+#define CFG_PRB_REQ_IE_BIT_MAP6_DEFAULT (0x00000000)
+
+#define CFG_PRB_REQ_IE_BIT_MAP7_NAME "g_probe_req_ie_bitmap_7"
+#define CFG_PRB_REQ_IE_BIT_MAP7_MIN (0x00000000)
+#define CFG_PRB_REQ_IE_BIT_MAP7_MAX (0xFFFFFFFF)
+#define CFG_PRB_REQ_IE_BIT_MAP7_DEFAULT (0x00000000)
+
+/*
+ * For vendor specific IE, Probe Req OUI types and sub types which are
+ * to be white listed are specifed in gProbeReqOUIs in the following
+ * example format - gProbeReqOUIs=AABBCCDD EEFF1122
+ */
+#define CFG_PROBE_REQ_OUI_NAME "gProbeReqOUIs"
+#define CFG_PROBE_REQ_OUI_DEFAULT ""
+
+/*
+ * <ini>
+ * arp_ac_category - ARP access category
+ * @Min: 0
+ * @Max: 3
+ * @Default: 3
+ *
+ * Firmware by default categorizes ARP packets with VOICE TID.
+ * This ini shall be used to override the default configuration.
+ * Access category enums are referenced in ieee80211_common.h
+ * WME_AC_BE = 0 (Best effort)
+ * WME_AC_BK = 1 (Background)
+ * WME_AC_VI = 2 (Video)
+ * WME_AC_VO = 3 (Voice)
+ *
+ * Related: none
+ *
+ * Usage: Internal/External
+ *
+ * </ini>
+ */
+#define CFG_ARP_AC_CATEGORY "arp_ac_category"
+#define CFG_ARP_AC_CATEGORY_MIN (0)
+#define CFG_ARP_AC_CATEGORY_MAX (3)
+#define CFG_ARP_AC_CATEGORY_DEFAULT (3)
+
+
/*---------------------------------------------------------------------------
Type declarations
-------------------------------------------------------------------------*/
@@ -3986,8 +4142,6 @@
v_U32_t nInitialDwellTime; //in units of milliseconds
bool initial_scan_no_dfs_chnl;
- v_U32_t nActiveMinChnTimeBtc; //in units of milliseconds
- v_U32_t nActiveMaxChnTimeBtc; //in units of milliseconds
#ifdef WLAN_AP_STA_CONCURRENCY
v_U32_t nPassiveMinChnTimeConc; //in units of milliseconds
v_U32_t nPassiveMaxChnTimeConc; //in units of milliseconds
@@ -4286,7 +4440,6 @@
char listOfNonDfsCountryCode[128];
v_BOOL_t enableSSR;
v_U32_t cfgMaxMediumTime;
- v_BOOL_t enableVhtFor24GHzBand;
#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
/* Flag indicating whether legacy fast roam during concurrency is enabled in cfg.ini or not */
v_BOOL_t bFastRoamInConIniFeatureEnabled;
@@ -4326,6 +4479,8 @@
#ifdef WLAN_FEATURE_11AC
v_U8_t fVhtAmpduLenExponent;
v_U32_t vhtMpduLen;
+ bool enableVhtFor24GHzBand;
+ bool enable_vendor_vht_for_24ghz_band;
#endif
#ifdef IPA_OFFLOAD
v_U32_t IpaConfig;
@@ -4623,6 +4778,32 @@
bool sap_restrt_ch_avoid;
uint32_t rx_wakelock_timeout;
bool active_mode_offload;
+ uint32_t max_sched_scan_plan_interval;
+ uint32_t max_sched_scan_plan_iterations;
+ /* 5G preference parameters for boosting RSSI */
+ bool enable_5g_band_pref;
+ int8_t rssi_boost_threshold_5g;
+ uint8_t rssi_boost_factor_5g;
+ uint8_t max_rssi_boost_5g;
+ /* 5G preference parameters for dropping RSSI*/
+ int8_t rssi_penalize_threshold_5g;
+ uint8_t rssi_penalize_factor_5g;
+ uint8_t max_rssi_penalize_5g;
+
+ bool probe_req_ie_whitelist;
+ /* probe request bit map ies */
+ uint32_t probe_req_ie_bitmap_0;
+ uint32_t probe_req_ie_bitmap_1;
+ uint32_t probe_req_ie_bitmap_2;
+ uint32_t probe_req_ie_bitmap_3;
+ uint32_t probe_req_ie_bitmap_4;
+ uint32_t probe_req_ie_bitmap_5;
+ uint32_t probe_req_ie_bitmap_6;
+ uint32_t probe_req_ie_bitmap_7;
+
+ /* Probe Request multiple vendor OUIs */
+ uint8_t probe_req_ouis[MAX_PRB_REQ_VENDOR_OUI_INI_LEN];
+ uint32_t arp_ac_category;
};
typedef struct hdd_config hdd_config_t;
@@ -4740,6 +4921,9 @@
Function declarations and documentation
-------------------------------------------------------------------------*/
VOS_STATUS hdd_parse_config_ini(hdd_context_t *pHddCtx);
+uint32_t hdd_validate_prb_req_ie_bitmap(hdd_context_t* pHddCtx);
+VOS_STATUS hdd_parse_probe_req_ouis(hdd_context_t* pHddCtx);
+void hdd_free_probe_req_ouis(hdd_context_t* pHddCtx);
VOS_STATUS hdd_update_mac_config(hdd_context_t *pHddCtx);
VOS_STATUS hdd_set_sme_config( hdd_context_t *pHddCtx );
VOS_STATUS hdd_set_sme_chan_list(hdd_context_t *hdd_ctx);
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_cfg80211.h b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_cfg80211.h
index a29ad12..505a54a4 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_cfg80211.h
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_cfg80211.h
@@ -788,6 +788,11 @@
*/
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_BUCKETS_SCANNED,
+ /*
+ * Unsigned 32bit value; a EXTSCAN Capabilities attribute to send maximum
+ * number of blacklist bssid's that firmware can support.
+ */
+ QCA_WLAN_VENDOR_ATTR_EXTSCAN_MAX_NUM_BLACKLISTED_BSSID,
/* keep last */
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_AFTER_LAST,
@@ -1472,6 +1477,10 @@
#define WIFI_FEATURE_MKEEP_ALIVE 0x100000 /* WiFi mkeep_alive */
#define WIFI_FEATURE_CONFIG_NDO 0x200000 /* ND offload configure */
#define WIFI_FEATURE_TX_TRANSMIT_POWER 0x400000 /* Tx transmit power levels */
+#define WIFI_FEATURE_CONTROL_ROAMING 0x800000 /* Enable/Disable roaming */
+#define WIFI_FEATURE_IE_WHITELIST 0x1000000 /* Support Probe IE white listing */
+#define WIFI_FEATURE_SCAN_RAND 0x2000000 /* Support MAC & Probe Sequence Number randomization */
+
/* Add more features here */
#define WIFI_TDLS_SUPPORT BIT(0)
@@ -1774,6 +1783,16 @@
* Rx wake packet count due to ipv6 multicast
* @QCA_WLAN_VENDOR_ATTR_OTHER_RX_MULTICAST_CNT:
* Rx wake packet count due to non-ipv4 and non-ipv6 packets
+ * @QCA_WLAN_VENDOR_ATTR_RSSI_BREACH_CNT:
+ * wake rssi breach packet count
+ * @QCA_WLAN_VENDOR_ATTR_LOW_RSSI_CNT:
+ * wake low rssi packet count
+ * @QCA_WLAN_VENDOR_ATTR_GSCAN_CNT:
+ * wake gscan packet count
+ * @QCA_WLAN_VENDOR_ATTR_PNO_COMPLETE_CNT:
+ * wake pno complete packet count
+ * @QCA_WLAN_VENDOR_ATTR_PNO_MATCH_CNT:
+ * wake pno match packet count
*/
enum qca_wlan_vendor_attr_wake_stats {
QCA_WLAN_VENDOR_ATTR_GET_WAKE_STATS_INVALID = 0,
@@ -1795,6 +1814,11 @@
QCA_WLAN_VENDOR_ATTR_ICMP4_RX_MULTICAST_CNT,
QCA_WLAN_VENDOR_ATTR_ICMP6_RX_MULTICAST_CNT,
QCA_WLAN_VENDOR_ATTR_OTHER_RX_MULTICAST_CNT,
+ QCA_WLAN_VENDOR_ATTR_RSSI_BREACH_CNT,
+ QCA_WLAN_VENDOR_ATTR_LOW_RSSI_CNT,
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CNT,
+ QCA_WLAN_VENDOR_ATTR_PNO_COMPLETE_CNT,
+ QCA_WLAN_VENDOR_ATTR_PNO_MATCH_CNT,
/* keep last */
QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST,
QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX =
@@ -1994,4 +2018,18 @@
GFP_KERNEL);
}
#endif
+
+/**
+ * enum wlan_hdd_scan_type - type of scan
+ * @WLAN_HDD_HOST_SCAN: refers to scan request from cfg80211_ops "scan"
+ * @WLAN_HDD_PNO_SCAN: refers to scan request is from "sched_scan_start"
+ *
+ * driver uses this enum to identify source of scan
+ *
+ */
+enum wlan_hdd_scan_type {
+ WLAN_HDD_HOST_SCAN,
+ WLAN_HDD_PNO_SCAN,
+};
+
#endif
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_main.h b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_main.h
index 36054df..1412304 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_main.h
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_main.h
@@ -126,6 +126,7 @@
#define WLAN_WAIT_TIME_ABORTSCAN 2000
#define WLAN_WAIT_TIME_EXTSCAN 1000
#define WLAN_WAIT_TIME_LL_STATS 800
+#define WLAN_WAIT_TIME_POWER_STATS 800
#define WLAN_WAIT_SMPS_FORCE_MODE 500
@@ -156,6 +157,8 @@
#define WLAN_WAIT_TIME_BPF 1000
+#define WLAN_WAIT_TIME_SET_RND 100
+
#define MAX_NUMBER_OF_ADAPTERS 4
#define MAX_CFG_STRING_LEN 255
@@ -248,6 +251,31 @@
typedef v_U8_t tWlanHddMacAddr[HDD_MAC_ADDR_LEN];
+#define MAX_PROBE_REQ_OUIS 16
+#define SCAN_REJECT_THRESHOLD_TIME 300000 /* Time is in msec, equal to 5 mins */
+
+/*
+ * @eHDD_SCAN_REJECT_DEFAULT: default value
+ * @eHDD_CONNECTION_IN_PROGRESS: connection is in progress
+ * @eHDD_REASSOC_IN_PROGRESS: reassociation is in progress
+ * @eHDD_EAPOL_IN_PROGRESS: STA/P2P-CLI is in middle of EAPOL/WPS exchange
+ * @eHDD_SAP_EAPOL_IN_PROGRESS: SAP/P2P-GO is in middle of EAPOL/WPS exchange
+ */
+typedef enum
+{
+ eHDD_SCAN_REJECT_DEFAULT = 0,
+ eHDD_CONNECTION_IN_PROGRESS,
+ eHDD_REASSOC_IN_PROGRESS,
+ eHDD_EAPOL_IN_PROGRESS,
+ eHDD_SAP_EAPOL_IN_PROGRESS,
+} scan_reject_states;
+
+/*
+ * Maximum no.of random mac addresses supported by firmware
+ * for transmitting management action frames
+ */
+#define MAX_RANDOM_MAC_ADDRS 16
+
/*
* Generic asynchronous request/response support
*
@@ -296,6 +324,20 @@
unsigned int magic;
};
+/**
+ * struct random_mac_context - Context used with hdd_random_mac_callback
+ * @random_mac_completion: Event on which hdd_set_random_mac will wait
+ * @adapter: Pointer to adapter
+ * @magic: For valid context this is set to ACTION_FRAME_RANDOM_CONTEXT_MAGIC
+ * @set_random_addr: Status of random filter set
+ */
+struct random_mac_context {
+ struct completion random_mac_completion;
+ hdd_adapter_t *adapter;
+ unsigned int magic;
+ bool set_random_addr;
+};
+
extern spinlock_t hdd_context_lock;
#define STATS_CONTEXT_MAGIC 0x53544154 //STAT
@@ -306,7 +348,9 @@
#define LINK_STATUS_MAGIC 0x4C4B5354 //LINKSTATUS(LNST)
#define TEMP_CONTEXT_MAGIC 0x74656d70 // TEMP (temperature)
#define FW_STATUS_MAGIC 0x46575354 /* FWSTATUS(FWST) */
+#define POWER_STATS_MAGIC 0x14111990
#define BPF_CONTEXT_MAGIC 0x4575354 /* BPF */
+#define ACTION_FRAME_RANDOM_CONTEXT_MAGIC 0x87878787
#ifdef QCA_LL_TX_FLOW_CT
/* MAX OS Q block time value in msec
@@ -699,6 +743,28 @@
eCsrPhyMode phy_mode;
};
+/**
+ * struct action_frame_cookie - Action frame cookie item in cookie list
+ * @cookie_node: List item
+ * @cookie: Cookie value
+ */
+struct action_frame_cookie {
+ struct list_head cookie_node;
+ uint64_t cookie;
+};
+
+/**
+ * struct action_frame_random_mac - Action Frame random mac addr & related attrs
+ * @in_use: Checks whether random mac is in use
+ * @addr: Contains random mac addr
+ * @cookie_list: List of cookies tied with random mac
+ */
+struct action_frame_random_mac {
+ bool in_use;
+ uint8_t addr[VOS_MAC_ADDR_SIZE];
+ struct list_head cookie_list;
+};
+
struct hdd_station_ctx
{
/** Handle to the Wireless Extension State */
@@ -740,6 +806,8 @@
#ifdef WLAN_FEATURE_NAN_DATAPATH
struct nan_datapath_ctx ndp_ctx;
#endif
+
+ uint8_t broadcast_staid;
};
#define BSS_STOP 0
@@ -1219,6 +1287,11 @@
struct hdd_netif_queue_history
queue_oper_history[WLAN_HDD_MAX_HISTORY_ENTRY];
struct hdd_netif_queue_stats queue_oper_stats[WLAN_REASON_TYPE_MAX];
+
+ /* random address management for management action frames */
+ spinlock_t random_mac_lock;
+ struct action_frame_random_mac random_mac[MAX_RANDOM_MAC_ADDRS];
+ struct power_stats_response *chip_power_stats;
};
#define WLAN_HDD_GET_STATION_CTX_PTR(pAdapter) (&(pAdapter)->sessionCtx.station)
@@ -1805,6 +1878,13 @@
unsigned int last_scan_bug_report_timestamp;
bool driver_being_stopped; /* Track if DRIVER STOP cmd is sent */
uint8_t max_mc_addr_list;
+
+ uint32_t no_of_probe_req_ouis;
+ struct vendor_oui *probe_req_voui;
+
+ v_U8_t last_scan_reject_session_id;
+ scan_reject_states last_scan_reject_reason;
+ v_TIME_t last_scan_reject_timestamp;
};
/*---------------------------------------------------------------------------
@@ -1849,6 +1929,20 @@
hdd_adapter_t * hdd_get_adapter_by_vdev( hdd_context_t *pHddCtx,
tANI_U32 vdev_id );
hdd_adapter_t * hdd_get_adapter_by_macaddr( hdd_context_t *pHddCtx, tSirMacAddr macAddr );
+
+/**
+ * hdd_get_adapter_by_rand_macaddr() - Get adapter using random DA of MA frms
+ * @hdd_ctx: Pointer to hdd context
+ * @mac_addr: random mac address
+ *
+ * This function is used to get adapter from randomized destination mac
+ * address present in management action frames
+ *
+ * Return: If randomized addr is present then return pointer to adapter
+ * else NULL
+ */
+hdd_adapter_t * hdd_get_adapter_by_rand_macaddr(hdd_context_t *hdd_ctx, tSirMacAddr mac_addr);
+
VOS_STATUS hdd_init_station_mode( hdd_adapter_t *pAdapter );
hdd_adapter_t * hdd_get_adapter( hdd_context_t *pHddCtx, device_mode_t mode );
void hdd_deinit_adapter(hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter,
@@ -1899,7 +1993,8 @@
v_BOOL_t hdd_is_valid_mac_address(const tANI_U8* pMacAddr);
VOS_STATUS hdd_issta_p2p_clientconnected(hdd_context_t *pHddCtx);
void hdd_ipv4_notifier_work_queue(struct work_struct *work);
-bool hdd_isConnectionInProgress(hdd_context_t *pHddCtx);
+bool hdd_isConnectionInProgress(hdd_context_t *pHddCtx, v_U8_t *session_id,
+ scan_reject_states *reason);
#ifdef WLAN_FEATURE_PACKET_FILTERING
int wlan_hdd_setIPv6Filter(hdd_context_t *pHddCtx, tANI_U8 filterType, tANI_U8 sessionId);
#endif
@@ -1984,10 +2079,6 @@
void wlan_hdd_cfg80211_stats_ext_init(hdd_context_t *pHddCtx);
#endif
-#ifdef WLAN_FEATURE_LINK_LAYER_STATS
-void wlan_hdd_cfg80211_link_layer_stats_init(hdd_context_t *pHddCtx);
-#endif
-
void hdd_update_macaddr(hdd_config_t *cfg_ini, v_MACADDR_t hw_macaddr);
#if defined(FEATURE_WLAN_LFR) && defined(WLAN_FEATURE_ROAM_SCAN_OFFLOAD)
void wlan_hdd_disable_roaming(hdd_adapter_t *pAdapter);
@@ -2034,17 +2125,78 @@
return;
}
+
+/**
+ * wlan_hdd_cfg80211_link_layer_stats_init() - Initialize llstats callbacks
+ * @pHddCtx: HDD context
+ *
+ * Return: none
+ */
+void wlan_hdd_cfg80211_link_layer_stats_init(hdd_context_t *pHddCtx);
+
#else
static inline bool hdd_link_layer_stats_supported(void)
{
return false;
}
+
static inline void hdd_init_ll_stats_ctx(hdd_context_t *hdd_ctx)
{
return;
}
+
+void wlan_hdd_cfg80211_link_layer_stats_init(hdd_context_t *pHddCtx)
+{
+ return;
+}
#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
+#ifdef FEATURE_WLAN_LFR
+static inline bool hdd_driver_roaming_supported(hdd_context_t *hdd_ctx)
+{
+ return hdd_ctx->cfg_ini->isFastRoamIniFeatureEnabled;
+}
+#else
+static inline bool hdd_driver_roaming_supported(hdd_context_t *hdd_ctx)
+{
+ return false;
+}
+#endif
+
+#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
+static inline bool hdd_firmware_roaming_supported(hdd_context_t *hdd_ctx)
+{
+ return hdd_ctx->cfg_ini->isRoamOffloadScanEnabled;
+}
+#else
+static inline bool hdd_firmware_roaming_supported(hdd_context_t *hdd_ctx)
+{
+ return false;
+}
+#endif
+
+static inline bool hdd_roaming_supported(hdd_context_t *hdd_ctx)
+{
+ bool val;
+
+ val = hdd_driver_roaming_supported(hdd_ctx) ||
+ hdd_firmware_roaming_supported(hdd_ctx);
+
+ return val;
+}
+
+#ifdef CFG80211_SCAN_RANDOM_MAC_ADDR
+static inline bool hdd_scan_random_mac_addr_supported(void)
+{
+ return true;
+}
+#else
+static inline bool hdd_scan_random_mac_addr_supported(void)
+{
+ return false;
+}
+#endif
+
void hdd_get_fw_version(hdd_context_t *hdd_ctx,
uint32_t *major_spid, uint32_t *minor_spid,
uint32_t *siid, uint32_t *crmid);
@@ -2093,7 +2245,8 @@
void hdd_connect_result(struct net_device *dev, const u8 *bssid,
tCsrRoamInfo *roam_info, const u8 *req_ie,
size_t req_ie_len, const u8 * resp_ie,
- size_t resp_ie_len, u16 status, gfp_t gfp);
+ size_t resp_ie_len, u16 status, gfp_t gfp,
+ bool connect_timeout);
int wlan_hdd_init_tx_rx_histogram(hdd_context_t *pHddCtx);
void wlan_hdd_deinit_tx_rx_histogram(hdd_context_t *pHddCtx);
@@ -2163,4 +2316,5 @@
void hdd_sap_restart_handle(struct work_struct *work);
+void wlan_hdd_stop_enter_lowpower(hdd_context_t *hdd_ctx);
#endif // end #if !defined( WLAN_HDD_MAIN_H )
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_oemdata.h b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_oemdata.h
index 296b0d1..df43191 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_oemdata.h
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_oemdata.h
@@ -42,7 +42,7 @@
#define __WLAN_HDD_OEM_DATA_H__
#ifndef OEM_DATA_REQ_SIZE
-#define OEM_DATA_REQ_SIZE 280
+#define OEM_DATA_REQ_SIZE 500
#endif
#ifndef OEM_DATA_RSP_SIZE
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_p2p.h b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_p2p.h
index 158b695..1d62d14 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_p2p.h
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_p2p.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -47,12 +47,16 @@
#define WLAN_HDD_GET_TYPE_FRM_FC(__fc__) (((__fc__) & 0x0F) >> 2)
#define WLAN_HDD_GET_SUBTYPE_FRM_FC(__fc__) (((__fc__) & 0xF0) >> 4)
#define WLAN_HDD_80211_FRM_DA_OFFSET 4
+#define WLAN_HDD_80211_FRM_SA_OFFSET 10
#define P2P_WILDCARD_SSID_LEN 7
#define P2P_WILDCARD_SSID "DIRECT-"
#define P2P_ROC_DURATION_MULTIPLIER_GO_PRESENT 2
#define P2P_ROC_DURATION_MULTIPLIER_GO_ABSENT 5
+#define ACTION_FRAME_RSP_WAIT 500
+#define ACTION_FRAME_ACK_WAIT 300
+
#ifdef WLAN_FEATURE_11W
#define WLAN_HDD_SET_WEP_FRM_FC(__fc__) ( (__fc__) = ((__fc__) | 0x40))
#endif //WLAN_FEATURE_11W
@@ -198,4 +202,17 @@
#define MAX_ROC_REQ_QUEUE_ENTRY 10
void wlan_hdd_roc_request_dequeue(struct work_struct *work);
+
+/**
+ * hdd_check_random_mac() - Whether addr is present in random_mac array
+ * @adapter: Pointer to adapter
+ * @mac_addr: random mac address
+ *
+ * This function is used to check whether given mac addr is present in list of
+ * random_mac structures in specified adapter
+ *
+ * Return: If random addr is present return true else false
+ */
+bool hdd_check_random_mac(hdd_adapter_t *adapter, uint8_t *random_mac_addr);
+
#endif // __P2P_H
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_tx_rx.h b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_tx_rx.h
index 90d836a..93a4b90f 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_tx_rx.h
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/inc/wlan_hdd_tx_rx.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -196,6 +196,8 @@
VOS_STATUS hdd_get_peer_sta_id(hdd_station_ctx_t *sta_ctx,
v_MACADDR_t *peer_mac_addr, uint8_t *sta_id);
+int hdd_get_peer_idx(hdd_station_ctx_t *sta_ctx, v_MACADDR_t *addr);
+
/**============================================================================
@brief hdd_flush_ibss_tx_queues() -
Flush tx queues in IBSS mode
@@ -272,6 +274,7 @@
const char *hdd_action_type_to_string(enum netif_action_type action);
void wlan_hdd_netif_queue_control(hdd_adapter_t *adapter,
enum netif_action_type action, enum netif_reason_type reason);
+void wlan_hdd_classify_pkt(struct sk_buff *skb);
#ifdef QCA_PKT_PROTO_TRACE
void hdd_dhcp_pkt_trace_buf_update(struct sk_buff *skb, int is_transmission,
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_assoc.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_assoc.c
index 92c9a84..51d1c36 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_assoc.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_assoc.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -51,7 +51,6 @@
#include <aniGlobal.h>
#include "dot11f.h"
#include "wlan_nlink_common.h"
-#include "wlan_btc_svc.h"
#include "wlan_hdd_power.h"
#include "wlan_hdd_trace.h"
#include <linux/ieee80211.h>
@@ -72,6 +71,7 @@
#include <wlan_logging_sock_svc.h>
#include "tl_shim.h"
#include "wlan_hdd_oemdata.h"
+#include "adf_trace.h"
struct ether_addr
{
@@ -874,7 +874,6 @@
}
#endif
}
- send_btc_nlink_msg(type, 0);
}
static void hdd_connRemoveConnectInfo(hdd_station_ctx_t *pHddStaCtx)
@@ -898,8 +897,15 @@
vos_mem_zero( &pHddStaCtx->conn_info.SSID, sizeof( tCsrSSIDInfo ) );
}
-/* TODO Revisit this function. and data path */
-static VOS_STATUS hdd_roamDeregisterSTA( hdd_adapter_t *pAdapter, tANI_U8 staId )
+
+/**
+ * hdd_roamDeregisterSTA() - Deregister STA from data path
+ * @pAdapter - HDD context
+ * @staId - Station ID
+ *
+ * Return: 0 or VOS_STATUS error code
+ */
+VOS_STATUS hdd_roamDeregisterSTA(hdd_adapter_t *pAdapter, tANI_U8 staId)
{
VOS_STATUS vosStatus;
hdd_station_ctx_t *pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
@@ -979,6 +985,10 @@
}
#endif /* QCA_PKT_PROTO_TRACE */
+ DPTRACE(adf_dp_trace_mgmt_pkt(ADF_DP_TRACE_MGMT_PACKET_RECORD,
+ pAdapter->sessionId,
+ ADF_PROTO_TYPE_MGMT, ADF_PROTO_MGMT_DISASSOC));
+
/* HDD has initiated disconnect, do not send disconnect indication
* to kernel. Sending disconnected event to kernel for userspace
* initiated disconnect will be handled by diconnect handler call
@@ -1812,6 +1822,11 @@
vos_pkt_trace_buf_update("ST:ASSOC");
}
#endif /* QCA_PKT_PROTO_TRACE */
+
+ DPTRACE(adf_dp_trace_mgmt_pkt(ADF_DP_TRACE_MGMT_PACKET_RECORD,
+ pAdapter->sessionId,
+ ADF_PROTO_TYPE_MGMT, ADF_PROTO_MGMT_ASSOC));
+
//For reassoc, the station is already registered, all we need is to change the state
//of the STA in TL.
//If authentication is required (WPA/WPA2/DWEP), change TL to CONNECTED instead of AUTHENTICATED
@@ -1935,7 +1950,7 @@
pFTAssocReq, assocReqlen,
pFTAssocRsp, assocRsplen,
WLAN_STATUS_SUCCESS,
- GFP_KERNEL);
+ GFP_KERNEL, false);
}
}
else
@@ -1970,7 +1985,7 @@
reqRsnIe, reqRsnLength,
rspRsnIe, rspRsnLength,
WLAN_STATUS_SUCCESS,
- GFP_KERNEL);
+ GFP_KERNEL, false);
}
}
}
@@ -2074,6 +2089,7 @@
else
{
hdd_context_t* pHddCtx = (hdd_context_t*)pAdapter->pHddCtx;
+ bool connect_timeout = false;
hdd_wext_state_t *pWextState = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
if (pRoamInfo)
@@ -2148,6 +2164,7 @@
pRoamInfo ? pRoamInfo->bssid : pWextState->req_bssId);
sme_remove_bssid_from_scan_list(hHal,
pRoamInfo ? pRoamInfo->bssid : pWextState->req_bssId);
+ connect_timeout = true;
}
/* CR465478: Only send up a connection failure result when CSR has
@@ -2175,12 +2192,12 @@
hdd_connect_result(dev, pRoamInfo->bssid, NULL,
NULL, 0, NULL, 0,
WLAN_STATUS_ASSOC_DENIED_UNSPEC,
- GFP_KERNEL);
+ GFP_KERNEL, connect_timeout);
else
hdd_connect_result(dev, pWextState->req_bssId, NULL,
NULL, 0, NULL, 0,
WLAN_STATUS_ASSOC_DENIED_UNSPEC,
- GFP_KERNEL);
+ GFP_KERNEL, connect_timeout);
}
else
{
@@ -2190,12 +2207,12 @@
pRoamInfo->reasonCode ?
pRoamInfo->reasonCode :
WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_KERNEL);
+ GFP_KERNEL, connect_timeout);
else
hdd_connect_result(dev, pWextState->req_bssId, NULL,
NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE,
- GFP_KERNEL);
+ GFP_KERNEL, connect_timeout);
}
/* Clear the roam profile */
hdd_clearRoamProfileIe(pAdapter);
@@ -2438,6 +2455,9 @@
for (idx = 0; idx < HDD_MAX_NUM_IBSS_STA; idx++) {
if (0 == sta_ctx->conn_info.staId[idx]) {
+ hddLog(VOS_TRACE_LEVEL_DEBUG,
+ FL("adding peer: %pM, sta_id: %d, at idx: %d"),
+ peer_mac_addr, sta_id, idx);
sta_ctx->conn_info.staId[idx] = sta_id;
vos_copy_macaddr(
&sta_ctx->conn_info.peerMacAddress[idx],
@@ -2448,6 +2468,27 @@
return false;
}
+/**
+ * hdd_delete_peer() - removes peer from hdd station context peer table
+ * @sta_ctx: pointer to hdd station context
+ * @sta_id: station ID
+ *
+ * Return: none
+ */
+void hdd_delete_peer(hdd_station_ctx_t *sta_ctx, uint8_t sta_id)
+{
+ int i;
+
+ for (i = 0; i < HDD_MAX_NUM_IBSS_STA; i++) {
+ if (sta_id == sta_ctx->conn_info.staId[i]) {
+ sta_ctx->conn_info.staId[i] = 0;
+ return;
+ }
+ }
+
+ hddLog(LOGE, FL("sta_id %d is not present in peer table"), sta_id);
+}
+
/**============================================================================
*
@brief roamRemoveIbssStation() - Remove the IBSS peer MAC address in the adapter.
@@ -5450,4 +5491,3 @@
return ret;
}
-
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_cfg.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_cfg.c
index 95bfec9..87104c4 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_cfg.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_cfg.c
@@ -873,20 +873,6 @@
CFG_ACTIVE_MIN_CHANNEL_TIME_MIN,
CFG_ACTIVE_MIN_CHANNEL_TIME_MAX ),
- REG_VARIABLE( CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_NAME, WLAN_PARAM_Integer,
- hdd_config_t, nActiveMaxChnTimeBtc,
- VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
- CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_DEFAULT,
- CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_MIN,
- CFG_ACTIVE_MAX_CHANNEL_TIME_BTC_MAX ),
-
- REG_VARIABLE( CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_NAME, WLAN_PARAM_Integer,
- hdd_config_t, nActiveMinChnTimeBtc,
- VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
- CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_DEFAULT,
- CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_MIN,
- CFG_ACTIVE_MIN_CHANNEL_TIME_BTC_MAX ),
-
REG_VARIABLE( CFG_RETRY_LIMIT_ZERO_NAME, WLAN_PARAM_Integer,
hdd_config_t, retryLimitZero,
VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
@@ -2781,6 +2767,13 @@
CFG_ENABLE_VHT_FOR_24GHZ_DEFAULT,
CFG_ENABLE_VHT_FOR_24GHZ_MIN,
CFG_ENABLE_VHT_FOR_24GHZ_MAX),
+
+ REG_VARIABLE( CFG_ENABLE_VENDOR_VHT_FOR_24GHZ_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, enable_vendor_vht_for_24ghz_band,
+ VAR_FLAGS_OPTIONAL,
+ CFG_ENABLE_VENDOR_VHT_FOR_24GHZ_DEFAULT,
+ CFG_ENABLE_VENDOR_VHT_FOR_24GHZ_MIN,
+ CFG_ENABLE_VENDOR_VHT_FOR_24GHZ_MAX),
#endif
#ifdef WLAN_FEATURE_ROAM_SCAN_OFFLOAD
@@ -4603,6 +4596,145 @@
CFG_ACTIVE_MODE_OFFLOAD_DEFAULT,
CFG_ACTIVE_MODE_OFFLOAD_MIN,
CFG_ACTIVE_MODE_OFFLOAD_MAX),
+
+ REG_VARIABLE(CFG_MAX_SCHED_SCAN_PLAN_INT_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, max_sched_scan_plan_interval,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_MAX_SCHED_SCAN_PLAN_INT_DEFAULT,
+ CFG_MAX_SCHED_SCAN_PLAN_INT_MIN,
+ CFG_MAX_SCHED_SCAN_PLAN_INT_MAX),
+
+ REG_VARIABLE(CFG_MAX_SCHED_SCAN_PLAN_ITRNS_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, max_sched_scan_plan_iterations,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_MAX_SCHED_SCAN_PLAN_ITRNS_DEFAULT,
+ CFG_MAX_SCHED_SCAN_PLAN_ITRNS_MIN,
+ CFG_MAX_SCHED_SCAN_PLAN_ITRNS_MAX),
+
+ REG_VARIABLE(CFG_ENABLE_5G_BAND_PREF_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, enable_5g_band_pref,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_ENABLE_5G_BAND_PREF_DEFAULT,
+ CFG_ENABLE_5G_BAND_PREF_MIN,
+ CFG_ENABLE_5G_BAND_PREF_MAX),
+
+ REG_VARIABLE(CFG_5G_RSSI_BOOST_THRESHOLD_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, rssi_boost_threshold_5g,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_5G_RSSI_BOOST_THRESHOLD_DEFAULT,
+ CFG_5G_RSSI_BOOST_THRESHOLD_MIN,
+ CFG_5G_RSSI_BOOST_THRESHOLD_MAX),
+
+ REG_VARIABLE(CFG_5G_RSSI_BOOST_FACTOR_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, rssi_boost_factor_5g,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_5G_RSSI_BOOST_FACTOR_DEFAULT,
+ CFG_5G_RSSI_BOOST_FACTOR_MIN,
+ CFG_5G_RSSI_BOOST_FACTOR_MAX),
+
+ REG_VARIABLE(CFG_5G_MAX_RSSI_BOOST_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, max_rssi_boost_5g,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_5G_MAX_RSSI_BOOST_DEFAULT,
+ CFG_5G_MAX_RSSI_BOOST_MIN,
+ CFG_5G_MAX_RSSI_BOOST_MAX),
+
+ REG_VARIABLE(CFG_5G_RSSI_PENALIZE_THRESHOLD_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, rssi_penalize_threshold_5g,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_5G_RSSI_PENALIZE_THRESHOLD_DEFAULT,
+ CFG_5G_RSSI_PENALIZE_THRESHOLD_MIN,
+ CFG_5G_RSSI_PENALIZE_THRESHOLD_MAX),
+
+ REG_VARIABLE(CFG_5G_RSSI_PENALIZE_FACTOR_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, rssi_penalize_factor_5g,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_5G_RSSI_PENALIZE_FACTOR_DEFAULT,
+ CFG_5G_RSSI_PENALIZE_FACTOR_MIN,
+ CFG_5G_RSSI_PENALIZE_FACTOR_MAX),
+
+ REG_VARIABLE(CFG_5G_MAX_RSSI_PENALIZE_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, max_rssi_penalize_5g,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_5G_MAX_RSSI_PENALIZE_DEFAULT,
+ CFG_5G_MAX_RSSI_PENALIZE_MIN,
+ CFG_5G_MAX_RSSI_PENALIZE_MAX),
+
+ REG_VARIABLE(CFG_PRB_REQ_IE_WHITELIST_NAME, WLAN_PARAM_Integer,
+ hdd_config_t, probe_req_ie_whitelist,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_PRB_REQ_IE_WHITELIST_DEFAULT,
+ CFG_PRB_REQ_IE_WHITELIST_MIN,
+ CFG_PRB_REQ_IE_WHITELIST_MAX),
+
+ REG_VARIABLE(CFG_PRB_REQ_IE_BIT_MAP0_NAME, WLAN_PARAM_HexInteger,
+ hdd_config_t, probe_req_ie_bitmap_0,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP0_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP0_MIN,
+ CFG_PRB_REQ_IE_BIT_MAP0_MAX),
+
+ REG_VARIABLE(CFG_PRB_REQ_IE_BIT_MAP1_NAME, WLAN_PARAM_HexInteger,
+ hdd_config_t, probe_req_ie_bitmap_1,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP1_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP1_MIN,
+ CFG_PRB_REQ_IE_BIT_MAP1_MAX),
+
+ REG_VARIABLE(CFG_PRB_REQ_IE_BIT_MAP2_NAME, WLAN_PARAM_HexInteger,
+ hdd_config_t, probe_req_ie_bitmap_2,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP2_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP2_MIN,
+ CFG_PRB_REQ_IE_BIT_MAP2_MAX),
+
+ REG_VARIABLE(CFG_PRB_REQ_IE_BIT_MAP3_NAME, WLAN_PARAM_HexInteger,
+ hdd_config_t, probe_req_ie_bitmap_3,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP3_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP3_MIN,
+ CFG_PRB_REQ_IE_BIT_MAP3_MAX),
+
+ REG_VARIABLE(CFG_PRB_REQ_IE_BIT_MAP4_NAME, WLAN_PARAM_HexInteger,
+ hdd_config_t, probe_req_ie_bitmap_4,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP4_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP4_MIN,
+ CFG_PRB_REQ_IE_BIT_MAP4_MAX),
+
+ REG_VARIABLE(CFG_PRB_REQ_IE_BIT_MAP5_NAME, WLAN_PARAM_HexInteger,
+ hdd_config_t, probe_req_ie_bitmap_5,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP5_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP5_MIN,
+ CFG_PRB_REQ_IE_BIT_MAP5_MAX),
+
+ REG_VARIABLE(CFG_PRB_REQ_IE_BIT_MAP6_NAME, WLAN_PARAM_HexInteger,
+ hdd_config_t, probe_req_ie_bitmap_6,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP6_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP6_MIN,
+ CFG_PRB_REQ_IE_BIT_MAP6_MAX),
+
+ REG_VARIABLE(CFG_PRB_REQ_IE_BIT_MAP7_NAME, WLAN_PARAM_HexInteger,
+ hdd_config_t, probe_req_ie_bitmap_7,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP7_DEFAULT,
+ CFG_PRB_REQ_IE_BIT_MAP7_MIN,
+ CFG_PRB_REQ_IE_BIT_MAP7_MAX),
+
+ REG_VARIABLE_STRING(CFG_PROBE_REQ_OUI_NAME, WLAN_PARAM_String,
+ hdd_config_t, probe_req_ouis,
+ VAR_FLAGS_OPTIONAL,
+ (void *)CFG_PROBE_REQ_OUI_DEFAULT),
+
+ REG_VARIABLE(CFG_ARP_AC_CATEGORY, WLAN_PARAM_Integer,
+ hdd_config_t, arp_ac_category,
+ VAR_FLAGS_OPTIONAL | VAR_FLAGS_RANGE_CHECK_ASSUME_DEFAULT,
+ CFG_ARP_AC_CATEGORY_DEFAULT,
+ CFG_ARP_AC_CATEGORY_MIN,
+ CFG_ARP_AC_CATEGORY_MAX),
+
};
@@ -5106,6 +5238,9 @@
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [gEnableLpwrImgTransition] Value = [%u] ",pHddCtx->cfg_ini->enableLpwrImgTransition);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [gEnableSSR] Value = [%u] ",pHddCtx->cfg_ini->enableSSR);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [gEnableVhtFor24GHzBand] Value = [%u] ",pHddCtx->cfg_ini->enableVhtFor24GHzBand);
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH,
+ "Name = [gEnableVendorVhtFor24GHzBand] Value = [%u] ",
+ pHddCtx->cfg_ini->enable_vendor_vht_for_24ghz_band);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [gFlexConnectPowerFactor] Value = [%u] ", pHddCtx->cfg_ini->flexConnectPowerFactor);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_HIGH, "Name = [gEnableIbssHeartBeatOffload] Value = [%u] ", pHddCtx->cfg_ini->enableIbssHeartBeatOffload);
@@ -5427,6 +5562,49 @@
CFG_ACTIVE_MODE_OFFLOAD,
pHddCtx->cfg_ini->active_mode_offload);
hdd_ndp_print_ini_config(pHddCtx);
+
+ hddLog(LOG2, "Name = [%s] Value = [%x] ",
+ CFG_PRB_REQ_IE_WHITELIST_NAME,
+ pHddCtx->cfg_ini->probe_req_ie_whitelist);
+
+ hddLog(LOG2, "Name = [%s] Value = [%x] ",
+ CFG_PRB_REQ_IE_BIT_MAP0_NAME,
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_0);
+
+ hddLog(LOG2, "Name = [%s] Value = [%x] ",
+ CFG_PRB_REQ_IE_BIT_MAP1_NAME,
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_1);
+
+ hddLog(LOG2, "Name = [%s] Value = [%x] ",
+ CFG_PRB_REQ_IE_BIT_MAP2_NAME,
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_2);
+
+ hddLog(LOG2, "Name = [%s] Value = [%x] ",
+ CFG_PRB_REQ_IE_BIT_MAP3_NAME,
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_3);
+
+ hddLog(LOG2, "Name = [%s] Value = [%x] ",
+ CFG_PRB_REQ_IE_BIT_MAP4_NAME,
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_4);
+
+ hddLog(LOG2, "Name = [%s] Value = [%x] ",
+ CFG_PRB_REQ_IE_BIT_MAP5_NAME,
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_5);
+
+ hddLog(LOG2, "Name = [%s] Value = [%x] ",
+ CFG_PRB_REQ_IE_BIT_MAP6_NAME,
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_6);
+
+ hddLog(LOG2, "Name = [%s] Value = [%x] ",
+ CFG_PRB_REQ_IE_BIT_MAP7_NAME,
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_7);
+
+ hddLog(LOG2, "Name = [%s] Value =[%s]",
+ CFG_PROBE_REQ_OUI_NAME,
+ pHddCtx->cfg_ini->probe_req_ouis);
+ hddLog(LOG2, "Name = [%s] Value = [%u]",
+ CFG_ARP_AC_CATEGORY,
+ pHddCtx->cfg_ini->arp_ac_category);
}
#define CFG_VALUE_MAX_LEN 256
@@ -5991,23 +6169,6 @@
}
-static void hdd_set_btc_config(hdd_context_t *pHddCtx)
-{
- hdd_config_t *pConfig = pHddCtx->cfg_ini;
- tSmeBtcConfig btcParams;
- int i;
-
- sme_BtcGetConfig(pHddCtx->hHal, &btcParams);
-
- btcParams.btcExecutionMode = pConfig->btcExecutionMode;
-
- for (i = 0; i < 6; i++) {
- btcParams.mwsCoexConfig[i] = pConfig->mwsCoexConfig[i];
- }
-
- sme_BtcSetConfig(pHddCtx->hHal, &btcParams);
-}
-
static void hdd_set_power_save_config(hdd_context_t *pHddCtx, tSmeConfigParams *smeConfig)
{
hdd_config_t *pConfig = pHddCtx->cfg_ini;
@@ -7076,8 +7237,6 @@
smeConfig->csrConfig.nActiveMinChnTime = pConfig->nActiveMinChnTime;
smeConfig->csrConfig.nPassiveMaxChnTime = pConfig->nPassiveMaxChnTime;
smeConfig->csrConfig.nPassiveMinChnTime = pConfig->nPassiveMinChnTime;
- smeConfig->csrConfig.nActiveMaxChnTimeBtc = pConfig->nActiveMaxChnTimeBtc;
- smeConfig->csrConfig.nActiveMinChnTimeBtc = pConfig->nActiveMinChnTimeBtc;
smeConfig->csrConfig.disableAggWithBtc = pConfig->disableAggWithBtc;
#ifdef WLAN_AP_STA_CONCURRENCY
smeConfig->csrConfig.nActiveMaxChnTimeConc = pConfig->nActiveMaxChnTimeConc;
@@ -7127,6 +7286,8 @@
smeConfig->csrConfig.txBFCsnValue = pConfig->txBFCsnValue;
smeConfig->csrConfig.enable2x2 = pConfig->enable2x2;
smeConfig->csrConfig.enableVhtFor24GHz = pConfig->enableVhtFor24GHzBand;
+ smeConfig->csrConfig.vendor_vht_for_24ghz_sap =
+ pConfig->enable_vendor_vht_for_24ghz_band;
smeConfig->csrConfig.enableMuBformee = pConfig->enableMuBformee;
smeConfig->csrConfig.enableVhtpAid = pConfig->enableVhtpAid;
smeConfig->csrConfig.enableVhtGid = pConfig->enableVhtGid;
@@ -7190,8 +7351,6 @@
hdd_set_power_save_offload_config(pHddCtx);
}
- hdd_set_btc_config(pHddCtx);
-
#ifdef WLAN_FEATURE_VOWIFI_11R
smeConfig->csrConfig.csr11rConfig.IsFTResourceReqSupported = pConfig->fFTResourceReqSupported;
#endif
@@ -7878,3 +8037,214 @@
hddLog(LOGE, "Fail to set coex page sap bt interval parameters");
}
}
+
+/**
+ * hdd_validate_prb_req_ie_bitmap - validates user input for ie bit map
+ * @hdd_ctx: the pointer to hdd context
+ *
+ * This function checks whether user have entered valid probe request
+ * ie bitmap and also verifies vendor ouis if vendor specific ie is set
+ *
+ * Return: status of verification
+ * 1 - valid input
+ * 0 - invalid input
+ */
+uint32_t hdd_validate_prb_req_ie_bitmap(hdd_context_t* pHddCtx)
+{
+ if (!(pHddCtx->cfg_ini->probe_req_ie_bitmap_0 ||
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_1 ||
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_2 ||
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_3 ||
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_4 ||
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_5 ||
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_6 ||
+ pHddCtx->cfg_ini->probe_req_ie_bitmap_7))
+ return 0;
+
+ /**
+ * check whether vendor oui IE is set and OUIs are present, each OUI
+ * is eneterd in the form of string of 8 characters from ini, therefore,
+ * for atleast one OUI, minimum length is 8 and hence this string length
+ * is checked for minimum of 8
+ */
+ if ((pHddCtx->cfg_ini->probe_req_ie_bitmap_6 &
+ VENDOR_SPECIFIC_IE_BITMAP) &&
+ (strlen(pHddCtx->cfg_ini->probe_req_ouis) < 8))
+ return 0;
+
+ /* check whether vendor oui IE is not set but OUIs are present */
+ if (!(pHddCtx->cfg_ini->probe_req_ie_bitmap_6 &
+ VENDOR_SPECIFIC_IE_BITMAP) &&
+ (strlen(pHddCtx->cfg_ini->probe_req_ouis) > 0))
+ return 0;
+
+ return 1;
+}
+
+/**
+ * probe_req_voui_convert_to_hex - converts str of 8 chars into two hex values
+ * @temp: string to be converted
+ * @voui: contains the type and subtype values
+ *
+ * This function converts the string length of 8 characters into two
+ * hexa-decimal values, oui_type and oui_subtype, where oui_type is the
+ * hexa decimal value converted from first 6 characters and oui_subtype is
+ * hexa decimal value converted from last 2 characters.
+ * strings which doesn't match with the specified pattern are ignored.
+ *
+ * Return: status of conversion
+ * 1 - if conversion is successful
+ * 0 - if conversion is failed
+ */
+static uint32_t hdd_probe_req_voui_convert_to_hex(uint8_t *temp,
+ struct vendor_oui *voui)
+{
+ uint32_t hex_value[4];
+ uint32_t i = 0;
+ uint32_t indx = 0;
+
+ memset(hex_value, 0x00, sizeof(hex_value));
+ memset(voui, 0x00, sizeof(*voui));
+
+ /* convert string to hex */
+ for (i = 0; i < 8; i++) {
+ if (temp[i] >= '0' && temp[i] <= '9') {
+ hex_value[indx] = (temp[i] - '0') << 4;
+ } else if (temp[i] >= 'A' && temp[i] <= 'F') {
+ hex_value[indx] = (temp[i] - 'A') + 0xA;
+ hex_value[indx] = hex_value[indx] << 4;
+ } else {
+ /* invalid character in oui */
+ return 0;
+ }
+
+ if (temp[i + 1] >= '0' && temp[i + 1] <= '9') {
+ hex_value[indx] |= (temp[i + 1] - '0');
+ i = i + 1;
+ indx = indx + 1;
+ } else if (temp[i + 1] >= 'A' && temp[i + 1] <= 'F') {
+ hex_value[indx] |= ((temp[i + 1] - 'A') + 0xA);
+ i = i + 1;
+ indx = indx + 1;
+ } else {
+ /* invalid character in oui */
+ return 0;
+ }
+ }
+
+ voui->oui_type = (hex_value[0] | (hex_value[1] << 8) |
+ (hex_value[2] << 16));
+ voui->oui_subtype = hex_value[3];
+
+ hddLog(LOG1, FL("OUI_type = %x and OUI_subtype = %x"), voui->oui_type,
+ voui->oui_subtype);
+ return 1;
+}
+
+/**
+ * hdd_parse_probe_req_ouis - form ouis from ini gProbeReqOUIs
+ * @hdd_ctx: the pointer to hdd context
+ *
+ * This function parses the ini string gProbeReqOUIs which needs to in the
+ * following format:
+ * "<8 characters of [0-9] or [A-F]>space<8 characters from [0-9] etc.,"
+ * example: "AABBCCDD 1122EEFF"
+ * and the logic counts the number of OUIS and allocates the memory
+ * for every valid OUI and is stored in hdd_context_t
+ *
+ * Return: status of parsing
+ */
+VOS_STATUS hdd_parse_probe_req_ouis(hdd_context_t* pHddCtx)
+{
+ struct vendor_oui voui[MAX_PROBE_REQ_OUIS];
+ uint8_t *str;
+ uint8_t temp[9];
+ uint32_t start = 0, end = 0;
+ uint32_t oui_indx = 0;
+ uint32_t i = 0;
+
+ pHddCtx->cfg_ini->probe_req_ouis[MAX_PRB_REQ_VENDOR_OUI_INI_LEN - 1] =
+ '\0';
+ if (!strlen(pHddCtx->cfg_ini->probe_req_ouis)) {
+ pHddCtx->no_of_probe_req_ouis = 0;
+ pHddCtx->probe_req_voui = NULL;
+ hddLog(LOG1, FL("NO OUIS to parse"));
+ return VOS_STATUS_SUCCESS;
+ }
+
+ str = (uint8_t *)(pHddCtx->cfg_ini->probe_req_ouis);
+
+ while(str[i] != '\0') {
+ if (str[i] == ' ') {
+ if ((end - start) != 8)
+ {
+ end = start = 0;
+ i++;
+ continue;
+ } else {
+ memcpy(temp, &str[i - 8], 8);
+ i++;
+ temp[8] = '\0';
+ if (hdd_probe_req_voui_convert_to_hex(temp,
+ &voui[oui_indx]) == 0) {
+ continue;
+ }
+ oui_indx++;
+ if (oui_indx > MAX_PROBE_REQ_OUIS) {
+ hddLog(LOGE, "Max no.of OUIS supported "
+ "is 16. ignoring the rest");
+ return VOS_STATUS_SUCCESS;
+ }
+ }
+ start = end = 0;
+ } else {
+ i++;
+ end++;
+ }
+ }
+
+ if ((end - start) == 8) {
+ memcpy(temp, &str[i - 8], 8);
+ temp[8] = '\0';
+ if (hdd_probe_req_voui_convert_to_hex(temp,
+ &voui[oui_indx]) == 1)
+ oui_indx++;
+ }
+
+ if (!oui_indx)
+ return VOS_STATUS_SUCCESS;
+
+ pHddCtx->probe_req_voui = (struct vendor_oui *)vos_mem_malloc(oui_indx *
+ sizeof(struct vendor_oui));
+ if (pHddCtx->probe_req_voui == NULL) {
+ hddLog(LOGE,"Not Enough memory for OUI");
+ pHddCtx->no_of_probe_req_ouis = 0;
+ return VOS_STATUS_E_FAILURE;
+ }
+ vos_mem_zero(pHddCtx->probe_req_voui,
+ oui_indx * sizeof(struct vendor_oui));
+ pHddCtx->no_of_probe_req_ouis = oui_indx;
+ vos_mem_copy(pHddCtx->probe_req_voui, voui,
+ oui_indx * sizeof(struct vendor_oui));
+
+ return VOS_STATUS_SUCCESS;
+}
+
+/**
+ * hdd_free_probe_req_ouis - de-allocates the probe req ouis
+ * @hdd_ctx: the pointer to hdd context
+ *
+ * This function de-alloactes the probe req ouis which are
+ * allocated while parsing of ini string gProbeReqOUIs
+ *
+ * Return: None
+ */
+void hdd_free_probe_req_ouis(hdd_context_t* pHddCtx)
+{
+ if (pHddCtx->probe_req_voui) {
+ vos_mem_free(pHddCtx->probe_req_voui);
+ pHddCtx->probe_req_voui = NULL;
+ }
+
+ pHddCtx->no_of_probe_req_ouis = 0;
+}
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_cfg80211.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_cfg80211.c
index cb5e069..8cf5cb4 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -129,6 +129,13 @@
#define WLAN_HDD_TGT_NOISE_FLOOR_DBM (-96)
+/*
+ * max_sched_scan_plans defined to 2 for
+ * (1)fast scan
+ * (2)slow scan
+ */
+#define MAX_SCHED_SCAN_PLANS 2
+
/* For IBSS, enable obss, fromllb, overlapOBSS & overlapFromllb protection
check. The bit map is defined in:
@@ -1552,6 +1559,15 @@
if (hdd_link_layer_stats_supported())
fset |= WIFI_FEATURE_LINK_LAYER_STATS;
+ if (hdd_roaming_supported(pHddCtx))
+ fset |= WIFI_FEATURE_CONTROL_ROAMING;
+
+ if (pHddCtx->cfg_ini->probe_req_ie_whitelist)
+ fset |= WIFI_FEATURE_IE_WHITELIST;
+
+ if (hdd_scan_random_mac_addr_supported())
+ fset |= WIFI_FEATURE_SCAN_RAND;
+
skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(fset) +
NLMSG_HDRLEN);
@@ -1599,6 +1615,49 @@
}
/**
+ * wlan_hdd_fill_whitelist_ie_attrs - fill the white list members
+ * @ie_whitelist: enables whitelist
+ * @probe_req_ie_bitmap: bitmap to be filled
+ * @num_vendor_oui: pointer to no of ouis
+ * @voui: pointer to ouis to be filled
+ * @pHddCtx: pointer to hdd ctx
+ *
+ * This function fills the ie bitmap and vendor oui fields with the
+ * corresponding values present in cfg_ini and PHddCtx
+ *
+ * Return: Return none
+ */
+static void wlan_hdd_fill_whitelist_ie_attrs(bool *ie_whitelist,
+ uint32_t *probe_req_ie_bitmap,
+ uint32_t *num_vendor_oui,
+ struct vendor_oui *voui,
+ hdd_context_t *pHddCtx)
+{
+ uint32_t i = 0;
+
+ *ie_whitelist = true;
+ probe_req_ie_bitmap[0] = pHddCtx->cfg_ini->probe_req_ie_bitmap_0;
+ probe_req_ie_bitmap[1] = pHddCtx->cfg_ini->probe_req_ie_bitmap_1;
+ probe_req_ie_bitmap[2] = pHddCtx->cfg_ini->probe_req_ie_bitmap_2;
+ probe_req_ie_bitmap[3] = pHddCtx->cfg_ini->probe_req_ie_bitmap_3;
+ probe_req_ie_bitmap[4] = pHddCtx->cfg_ini->probe_req_ie_bitmap_4;
+ probe_req_ie_bitmap[5] = pHddCtx->cfg_ini->probe_req_ie_bitmap_5;
+ probe_req_ie_bitmap[6] = pHddCtx->cfg_ini->probe_req_ie_bitmap_6;
+ probe_req_ie_bitmap[7] = pHddCtx->cfg_ini->probe_req_ie_bitmap_7;
+
+ *num_vendor_oui = 0;
+
+ if ((pHddCtx->no_of_probe_req_ouis != 0) && (voui != NULL)) {
+ *num_vendor_oui = pHddCtx->no_of_probe_req_ouis;
+ for (i = 0; i < pHddCtx->no_of_probe_req_ouis; i++) {
+ voui[i].oui_type = pHddCtx->probe_req_voui[i].oui_type;
+ voui[i].oui_subtype =
+ pHddCtx->probe_req_voui[i].oui_subtype;
+ }
+ }
+}
+
+/**
* __wlan_hdd_cfg80211_set_scanning_mac_oui() - set scan MAC
* @wiphy: pointer to wireless wiphy structure.
* @wdev: pointer to wireless_dev structure.
@@ -1620,6 +1679,8 @@
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI_MAX + 1];
eHalStatus status;
int ret;
+ struct net_device *ndev = wdev->netdev;
+ hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR(ndev);
ENTER();
@@ -1644,11 +1705,16 @@
return -EINVAL;
}
- pReqMsg = vos_mem_malloc(sizeof(*pReqMsg));
+ pReqMsg = vos_mem_malloc(sizeof(*pReqMsg) +
+ (pHddCtx->no_of_probe_req_ouis) *
+ (sizeof(struct vendor_oui)));
if (!pReqMsg) {
hddLog(LOGE, FL("vos_mem_malloc failed"));
return -ENOMEM;
}
+ vos_mem_zero(pReqMsg, sizeof(*pReqMsg) +
+ (pHddCtx->no_of_probe_req_ouis) *
+ (sizeof(struct vendor_oui)));
/* Parse and fetch oui */
if (!tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI]) {
@@ -1660,8 +1726,20 @@
tb[QCA_WLAN_VENDOR_ATTR_SET_SCANNING_MAC_OUI],
sizeof(pReqMsg->oui));
- hddLog(LOG1, FL("Oui (%02x:%02x:%02x)"), pReqMsg->oui[0], pReqMsg->oui[1],
- pReqMsg->oui[2]);
+ /* populate pReqMsg for mac addr randomization */
+ pReqMsg->vdev_id = pAdapter->sessionId;
+ pReqMsg->enb_probe_req_sno_randomization = true;
+
+ hddLog(LOG1, FL("Oui (%02x:%02x:%02x), vdev_id = %d"), pReqMsg->oui[0],
+ pReqMsg->oui[1], pReqMsg->oui[2], pReqMsg->vdev_id);
+
+ if (pHddCtx->cfg_ini->probe_req_ie_whitelist)
+ wlan_hdd_fill_whitelist_ie_attrs(&pReqMsg->ie_whitelist,
+ pReqMsg->probe_req_ie_bitmap,
+ &pReqMsg->num_vendor_oui,
+ (struct vendor_oui *)((uint8_t *)pReqMsg +
+ sizeof(*pReqMsg)),
+ pHddCtx);
status = sme_SetScanningMacOui(pHddCtx->hHal, pReqMsg);
if (!HAL_STATUS_SUCCESS(status)) {
@@ -1915,46 +1993,63 @@
switch(cmd_type) {
case QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST:
i = 0;
- nla_for_each_nested(curr_attr,
- tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST],
- rem) {
- if (nla_parse(tb2,
- QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX,
- nla_data(curr_attr), nla_len(curr_attr),
- wlan_hdd_set_roam_param_policy)) {
- hddLog(LOGE, FL("nla_parse failed"));
- goto fail;
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS]) {
+ count = nla_get_u32(
+ tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS]);
+ } else {
+ hddLog(LOGE, FL("Number of networks is not provided"));
+ goto fail;
+ }
+
+ if (count &&
+ tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST]) {
+ nla_for_each_nested(curr_attr,
+ tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST],
+ rem) {
+ if (nla_parse(tb2,
+ QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX,
+ nla_data(curr_attr), nla_len(curr_attr),
+ wlan_hdd_set_roam_param_policy)) {
+ hddLog(LOGE, FL("nla_parse failed"));
+ goto fail;
+ }
+ /* Parse and Fetch allowed SSID list*/
+ if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID]) {
+ hddLog(LOGE, FL("attr allowed ssid failed"));
+ goto fail;
+ }
+ buf_len = nla_len(tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID]);
+ /*
+ * Upper Layers include a null termination character.
+ * Check for the actual permissible length of SSID and
+ * also ensure not to copy the NULL termination
+ * character to the driver buffer.
+ */
+ if (buf_len && (i < MAX_SSID_ALLOWED_LIST) &&
+ ((buf_len - 1) <= SIR_MAC_MAX_SSID_LENGTH)) {
+ nla_memcpy(roam_params.ssid_allowed_list[i].ssId,
+ tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID],
+ buf_len - 1);
+ roam_params.ssid_allowed_list[i].length =
+ buf_len - 1;
+ hddLog(VOS_TRACE_LEVEL_DEBUG,
+ FL("SSID[%d]: %.*s,length = %d"), i,
+ roam_params.ssid_allowed_list[i].length,
+ roam_params.ssid_allowed_list[i].ssId,
+ roam_params.ssid_allowed_list[i].length);
+ i++;
+ }
+ else {
+ hddLog(LOGE, FL("Invalid SSID len %d,idx %d"),
+ buf_len, i);
+ }
}
- /* Parse and Fetch allowed SSID list*/
- if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID]) {
- hddLog(LOGE, FL("attr allowed ssid failed"));
- goto fail;
- }
- buf_len = nla_len(tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID]);
- /*
- * Upper Layers include a null termination character.
- * Check for the actual permissible length of SSID and
- * also ensure not to copy the NULL termination
- * character to the driver buffer.
- */
- if (buf_len && (i < MAX_SSID_ALLOWED_LIST) &&
- ((buf_len - 1) <= SIR_MAC_MAX_SSID_LENGTH)) {
- nla_memcpy(roam_params.ssid_allowed_list[i].ssId,
- tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID],
- buf_len - 1);
- roam_params.ssid_allowed_list[i].length =
- buf_len - 1;
- hddLog(VOS_TRACE_LEVEL_DEBUG,
- FL("SSID[%d]: %.*s,length = %d"), i,
- roam_params.ssid_allowed_list[i].length,
- roam_params.ssid_allowed_list[i].ssId,
- roam_params.ssid_allowed_list[i].length);
- i++;
- }
- else {
- hddLog(LOGE, FL("Invalid SSID len %d,idx %d"),
- buf_len, i);
- }
+ }
+ if (i != count) {
+ hddLog(LOGE, FL("Invalid number of SSIDs i = %d, count = %d"),
+ i, count);
+ goto fail;
}
roam_params.num_ssid_allowed_list = i;
hddLog(VOS_TRACE_LEVEL_DEBUG, FL("Num of Allowed SSID %d"),
@@ -2126,34 +2221,41 @@
hddLog(VOS_TRACE_LEVEL_DEBUG,
FL("Num of blacklist BSSID: %d"), count);
i = 0;
- nla_for_each_nested(curr_attr,
- tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS],
- rem) {
+ if (count &&
+ tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS]) {
+ nla_for_each_nested(curr_attr,
+ tb[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS],
+ rem) {
- if (i == count) {
- hddLog(LOGW, FL("Ignoring excess Blacklist BSSID"));
- break;
- }
+ if (i == count) {
+ hddLog(LOGW, FL("Ignoring excess Blacklist BSSID"));
+ break;
+ }
+ if (curr_attr == NULL) {
+ hddLog(LOGW, FL("Blacklist BSSID, curr_attr is null"));
+ continue;
+ }
- if (nla_parse(tb2,
- QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX,
- nla_data(curr_attr), nla_len(curr_attr),
- wlan_hdd_set_roam_param_policy)) {
- hddLog(LOGE, FL("nla_parse failed"));
- goto fail;
+ if (nla_parse(tb2,
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX,
+ nla_data(curr_attr), nla_len(curr_attr),
+ wlan_hdd_set_roam_param_policy)) {
+ hddLog(LOGE, FL("nla_parse failed"));
+ goto fail;
+ }
+ /* Parse and fetch MAC address */
+ if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID]) {
+ hddLog(LOGE, FL("attr blacklist addr failed"));
+ goto fail;
+ }
+ nla_memcpy(roam_params.bssid_avoid_list[i],
+ tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID],
+ sizeof(tSirMacAddr));
+ hddLog(VOS_TRACE_LEVEL_DEBUG, MAC_ADDRESS_STR,
+ MAC_ADDR_ARRAY(
+ roam_params.bssid_avoid_list[i]));
+ i++;
}
- /* Parse and fetch MAC address */
- if (!tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID]) {
- hddLog(LOGE, FL("attr blacklist addr failed"));
- goto fail;
- }
- nla_memcpy(roam_params.bssid_avoid_list[i],
- tb2[QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID],
- sizeof(tSirMacAddr));
- hddLog(VOS_TRACE_LEVEL_DEBUG, MAC_ADDRESS_STR,
- MAC_ADDR_ARRAY(
- roam_params.bssid_avoid_list[i]));
- i++;
}
if (i < count)
hddLog(LOGW,
@@ -2374,6 +2476,8 @@
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID
#define MAX_NUM_WHITELISTED_SSID \
QCA_WLAN_VENDOR_ATTR_EXTSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID
+#define MAX_NUM_BLACKLISTED_BSSID \
+ QCA_WLAN_VENDOR_ATTR_EXTSCAN_MAX_NUM_BLACKLISTED_BSSID
/**
* wlan_hdd_send_ext_scan_capability - send ext scan capability to user space
@@ -2408,7 +2512,8 @@
(sizeof(data->max_hotlist_ssids) + NLA_HDRLEN) +
(sizeof(data->max_number_epno_networks) + NLA_HDRLEN) +
(sizeof(data->max_number_epno_networks_by_ssid) + NLA_HDRLEN) +
- (sizeof(data->max_number_of_white_listed_ssid) + NLA_HDRLEN);
+ (sizeof(data->max_number_of_white_listed_ssid) + NLA_HDRLEN) +
+ (sizeof(data->max_number_of_black_listed_bssid) + NLA_HDRLEN);
skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len);
@@ -2438,6 +2543,8 @@
data->max_number_epno_networks_by_ssid);
hddLog(LOG1, "max_number_of_white_listed_ssid (%u)",
data->max_number_of_white_listed_ssid);
+ hddLog(LOG1, "max_number_of_black_listed_bssid (%u)",
+ data->max_number_of_black_listed_bssid);
if (nla_put_u32(skb, PARAM_REQUEST_ID, data->requestId) ||
nla_put_u32(skb, PARAM_STATUS, data->status) ||
@@ -2460,7 +2567,9 @@
nla_put_u32(skb, MAX_NUM_EPNO_NETS_BY_SSID,
data->max_number_epno_networks_by_ssid) ||
nla_put_u32(skb, MAX_NUM_WHITELISTED_SSID,
- data->max_number_of_white_listed_ssid)) {
+ data->max_number_of_white_listed_ssid) ||
+ nla_put_u32(skb, MAX_NUM_BLACKLISTED_BSSID,
+ data->max_number_of_black_listed_bssid)) {
hddLog(LOGE, FL("nla put fail"));
goto nla_put_failure;
}
@@ -2490,6 +2599,7 @@
#undef MAX_NUM_EPNO_NETS
#undef MAX_NUM_EPNO_NETS_BY_SSID
#undef MAX_NUM_WHITELISTED_SSID
+#undef MAX_NUM_BLACKLISTED_BSSID
static int __wlan_hdd_cfg80211_extscan_get_capabilities(struct wiphy *wiphy,
struct wireless_dev *wdev,
@@ -5876,8 +5986,7 @@
* after receiving Link Layer indications from FW.This callback converts the
* firmware data to the NL data and send the same to the kernel/upper layers.
*/
-static void wlan_hdd_cfg80211_link_layer_stats_callback(void *ctx,
- int indType,
+static void wlan_hdd_cfg80211_link_layer_stats_callback(void *ctx, int indType,
void *pRsp)
{
hdd_adapter_t *pAdapter = NULL;
@@ -10194,6 +10303,16 @@
QCA_WLAN_VENDOR_ATTR_ICMP6_RX_MULTICAST_CNT
#define PARAM_OTHER_RX_MULTICAST_CNT \
QCA_WLAN_VENDOR_ATTR_OTHER_RX_MULTICAST_CNT
+#define PARAM_RSSI_BREACH_CNT \
+ QCA_WLAN_VENDOR_ATTR_RSSI_BREACH_CNT
+#define PARAM_LOW_RSSI_CNT \
+ QCA_WLAN_VENDOR_ATTR_LOW_RSSI_CNT
+#define PARAM_GSCAN_CNT \
+ QCA_WLAN_VENDOR_ATTR_GSCAN_CNT
+#define PARAM_PNO_COMPLETE_CNT \
+ QCA_WLAN_VENDOR_ATTR_PNO_COMPLETE_CNT
+#define PARAM_PNO_MATCH_CNT \
+ QCA_WLAN_VENDOR_ATTR_PNO_MATCH_CNT
/**
@@ -10244,6 +10363,16 @@
data->wow_ipv6_mcast_na_stats);
hddLog(LOG1, "wow_icmpv4_count %d", data->wow_icmpv4_count);
hddLog(LOG1, "wow_icmpv6_count %d", data->wow_icmpv6_count);
+ hddLog(LOG1, "wow_rssi_breach_wake_up_count %d",
+ data->wow_rssi_breach_wake_up_count);
+ hddLog(LOG1, "wow_low_rssi_wake_up_count %d",
+ data->wow_low_rssi_wake_up_count);
+ hddLog(LOG1, "wow_gscan_wake_up_count %d",
+ data->wow_gscan_wake_up_count);
+ hddLog(LOG1, "wow_pno_complete_wake_up_count %d",
+ data->wow_pno_complete_wake_up_count);
+ hddLog(LOG1, "wow_pno_match_wake_up_count %d",
+ data->wow_pno_match_wake_up_count);
ipv6_rx_multicast_addr_cnt =
data->wow_ipv6_mcast_wake_up_count;
@@ -10288,7 +10417,17 @@
data->wow_ipv4_mcast_wake_up_count) ||
nla_put_u32(skb, PARAM_ICMP6_RX_MULTICAST_CNT,
ipv6_rx_multicast_addr_cnt) ||
- nla_put_u32(skb, PARAM_OTHER_RX_MULTICAST_CNT, 0)) {
+ nla_put_u32(skb, PARAM_OTHER_RX_MULTICAST_CNT, 0) ||
+ nla_put_u32(skb, PARAM_RSSI_BREACH_CNT,
+ data->wow_rssi_breach_wake_up_count) ||
+ nla_put_u32(skb, PARAM_LOW_RSSI_CNT,
+ data->wow_low_rssi_wake_up_count) ||
+ nla_put_u32(skb, PARAM_GSCAN_CNT,
+ data->wow_gscan_wake_up_count) ||
+ nla_put_u32(skb, PARAM_PNO_COMPLETE_CNT,
+ data->wow_pno_complete_wake_up_count) ||
+ nla_put_u32(skb, PARAM_PNO_MATCH_CNT,
+ data->wow_pno_match_wake_up_count)) {
hddLog(LOGE, FL("nla put fail"));
goto nla_put_failure;
}
@@ -10412,6 +10551,7 @@
uint32_t is_fast_roam_enabled;
eHalStatus status;
int ret;
+ hdd_station_ctx_t *hddstactx;
ENTER();
@@ -10441,6 +10581,26 @@
tb[QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY]);
hddLog(LOG1, FL("isFastRoamEnabled %d"), is_fast_roam_enabled);
+ /*
+ * If framework sends pause_roam, host to send WAIT indication to
+ * framework if roaming is in progress. This can help framework to
+ * defer out-network roaming. EBUSY is used to convey wait indication.
+ */
+ if (!is_fast_roam_enabled) {
+ if (sme_staInMiddleOfRoaming(hdd_ctx->hHal,
+ adapter->sessionId)) {
+ hddLog(LOG1, FL("Roaming in progress, do not allow disable"));
+ return -EBUSY;
+ }
+
+ hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+ if (hddstactx->hdd_ReassocScenario) {
+ hddLog(LOG1,
+ FL("Roaming in progress, so unable to disable roaming"));
+ return -EBUSY;
+ }
+ }
+
/* Update roaming */
status = sme_config_fast_roaming(hdd_ctx->hHal, adapter->sessionId,
is_fast_roam_enabled);
@@ -10449,6 +10609,7 @@
FL("sme_config_fast_roaming (err=%d)"), status);
return -EINVAL;
}
+
EXIT();
return 0;
}
@@ -10726,8 +10887,7 @@
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_EXTSCAN_GET_CAPABILITIES,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
- WIPHY_VENDOR_CMD_NEED_NETDEV |
- WIPHY_VENDOR_CMD_NEED_RUNNING,
+ WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_extscan_get_capabilities
},
{
@@ -11101,8 +11261,7 @@
.info.vendor_id = QCA_NL80211_VENDOR_ID,
.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE,
.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
- WIPHY_VENDOR_CMD_NEED_NETDEV |
- WIPHY_VENDOR_CMD_NEED_RUNNING,
+ WIPHY_VENDOR_CMD_NEED_NETDEV,
.doit = wlan_hdd_cfg80211_get_bus_size
},
{
@@ -11178,6 +11337,90 @@
return wiphy;
}
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(4,4,0)) || \
+ defined (CFG80211_MULTI_SCAN_PLAN_BACKPORT)
+/**
+ * hdd_config_sched_scan_plans_to_wiphy() - configure sched scan plans to wiphy
+ * @wiphy: pointer to wiphy
+ * @config: pointer to config
+ *
+ * Return: None
+ */
+static void hdd_config_sched_scan_plans_to_wiphy(struct wiphy *wiphy,
+ hdd_config_t *config)
+{
+ wiphy->max_sched_scan_plans = MAX_SCHED_SCAN_PLANS;
+ if (config->max_sched_scan_plan_interval)
+ wiphy->max_sched_scan_plan_interval =
+ config->max_sched_scan_plan_interval;
+ if (config->max_sched_scan_plan_iterations)
+ wiphy->max_sched_scan_plan_iterations =
+ config->max_sched_scan_plan_iterations;
+}
+#else
+static void hdd_config_sched_scan_plans_to_wiphy(struct wiphy *wiphy,
+ hdd_config_t *config)
+{
+}
+#endif
+
+#ifdef CFG80211_SCAN_RANDOM_MAC_ADDR
+static void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy)
+{
+ wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+ wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+}
+#else
+static void wlan_hdd_cfg80211_scan_randomization_init(struct wiphy *wiphy)
+{
+ return;
+}
+#endif
+
+#ifdef CFG80211_RAND_TA_FOR_PUBLIC_ACTION_FRAME
+/**
+ * wlan_hdd_cfg80211_action_frame_randomization_init() - Randomize SA of MA frms
+ * @wiphy: Pointer to wiphy
+ *
+ * This function is used to indicate the support of source mac address
+ * randomization of management action frames
+ *
+ * Return: None
+ */
+static void
+wlan_hdd_cfg80211_action_frame_randomization_init(struct wiphy *wiphy)
+{
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA);
+}
+#else
+static void
+wlan_hdd_cfg80211_action_frame_randomization_init(struct wiphy *wiphy)
+{
+ return;
+}
+#endif
+
+/**
+ * wlan_hdd_cfg80211_add_connected_pno_support() - Set connected PNO support
+ * @wiphy: Pointer to wireless phy
+ *
+ * This function is used to set connected PNO support to kernel
+ *
+ * Return: None
+ */
+#if defined(CFG80211_REPORT_BETTER_BSS_IN_SCHED_SCAN)
+static void wlan_hdd_cfg80211_add_connected_pno_support(struct wiphy *wiphy)
+{
+ wiphy_ext_feature_set(wiphy,
+ NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI);
+}
+#else
+static void wlan_hdd_cfg80211_add_connected_pno_support(struct wiphy *wiphy)
+{
+ return;
+}
+#endif
+
/*
* FUNCTION: wlan_hdd_cfg80211_init
* This function is called by hdd_wlan_startup()
@@ -11261,6 +11504,7 @@
wiphy->max_match_sets = SIR_PNO_MAX_SUPP_NETWORKS;
wiphy->max_sched_scan_ie_len = SIR_MAC_MAX_IE_LENGTH;
}
+ wlan_hdd_cfg80211_add_connected_pno_support(wiphy);
#endif/*FEATURE_WLAN_SCAN_PNO*/
#if defined QCA_WIFI_FTM
@@ -11423,6 +11667,10 @@
wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
#endif
+ hdd_config_sched_scan_plans_to_wiphy(wiphy, pCfg);
+ wlan_hdd_cfg80211_scan_randomization_init(wiphy);
+ wlan_hdd_cfg80211_action_frame_randomization_init(wiphy);
+
EXIT();
return 0;
}
@@ -15877,6 +16125,43 @@
return bss;
}
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4 , 3, 0)) || \
+ defined (CFG80211_INFORM_BSS_FRAME_DATA)
+static struct cfg80211_bss *
+wlan_hdd_cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ struct ieee80211_mgmt *mgmt,
+ size_t frame_len,
+ int rssi, gfp_t gfp,
+ uint64_t boottime_ns)
+{
+ struct cfg80211_bss *bss_status = NULL;
+ struct cfg80211_inform_bss data = {0};
+
+ data.chan = chan;
+ data.boottime_ns = boottime_ns;
+ data.signal = rssi;
+ bss_status = cfg80211_inform_bss_frame_data(wiphy, &data, mgmt,
+ frame_len, gfp);
+ return bss_status;
+}
+#else
+static struct cfg80211_bss *
+wlan_hdd_cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
+ struct ieee80211_channel *chan,
+ struct ieee80211_mgmt *mgmt,
+ size_t frame_len,
+ int rssi, gfp_t gfp,
+ uint64_t boottime_ns)
+{
+ struct cfg80211_bss *bss_status = NULL;
+
+ bss_status = cfg80211_inform_bss_frame(wiphy, chan, mgmt, frame_len,
+ rssi, gfp);
+ return bss_status;
+}
+#endif
+
/*
* FUNCTION: wlan_hdd_cfg80211_inform_bss_frame
* This function is used to inform the BSS details to nl80211 interface.
@@ -16029,8 +16314,10 @@
MAC_ADDR_ARRAY(mgmt->bssid),
vos_freq_to_chan(chan->center_freq),(int)(rssi/100));
- bss_status = cfg80211_inform_bss_frame(wiphy, chan, mgmt, frame_len, rssi,
- GFP_KERNEL);
+ bss_status = wlan_hdd_cfg80211_inform_bss_frame_data(wiphy, chan, mgmt,
+ frame_len, rssi,
+ GFP_KERNEL,
+ bss_desc->scansystimensec);
kfree(mgmt);
return bss_status;
}
@@ -16498,23 +16785,144 @@
return 0;
}
+/**
+ * hdd_is_sta_in_middle_of_eapol() - to check STA connection Status
+ * @adapter: Pointer to Global MAC Structure
+ * @session_id: session id
+ * @reason: scan reject reason
+ *
+ * This function is used to check the connection status of STA/P2P Client
+ *
+ * Return: true or false
+ */
+static bool hdd_is_sta_in_middle_of_eapol(hdd_adapter_t *adapter,
+ v_U8_t *session_id, scan_reject_states *reason)
+{
+ hdd_station_ctx_t *hdd_sta_ctx = NULL;
+ v_U8_t *sta_mac = NULL;
+
+ hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+ if ((eConnectionState_Associated == hdd_sta_ctx->conn_info.connState) &&
+ (VOS_FALSE == hdd_sta_ctx->conn_info.uIsAuthenticated)) {
+ sta_mac = (v_U8_t *) &(adapter->macAddressCurrent.bytes[0]);
+ hddLog(LOGE, FL("client " MAC_ADDRESS_STR " is in the middle of WPS/EAPOL exchange."),
+ MAC_ADDR_ARRAY(sta_mac));
+ if (session_id && reason) {
+ *session_id = adapter->sessionId;
+ *reason = eHDD_EAPOL_IN_PROGRESS;
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+ * hdd_is_sap_in_middle_of_eapol() - to check SAP connection Status
+ * @adapter: Pointer to Global MAC Structure
+ * @session_id: session id
+ * @reason: scan reject reason
+ *
+ * This function is used to check the connection status of SAP/P2P GO
+ *
+ * Return: true or false
+ */
+static bool hdd_is_sap_in_middle_of_eapol(hdd_adapter_t *adapter,
+ v_U8_t *session_id, scan_reject_states *reason)
+{
+ v_U8_t sta_id = 0;
+ v_U8_t *sta_mac = NULL;
+
+ for (sta_id = 0; sta_id < WLAN_MAX_STA_COUNT; sta_id++) {
+ if ((adapter->aStaInfo[sta_id].isUsed) &&
+ (WLANTL_STA_CONNECTED ==
+ adapter->aStaInfo[sta_id].tlSTAState)) {
+ sta_mac = (v_U8_t *) &(adapter->aStaInfo[sta_id].
+ macAddrSTA.bytes[0]);
+
+ hddLog(LOGE, FL("client " MAC_ADDRESS_STR " of SoftAP/P2P-GO is in the middle of WPS/EAPOL exchange."),
+ MAC_ADDR_ARRAY(sta_mac));
+ if (session_id && reason) {
+ *session_id = adapter->sessionId;
+ *reason = eHDD_SAP_EAPOL_IN_PROGRESS;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * hdd_check_connection_status() - to check connection Status
+ * @adapter: Pointer to Global MAC Structure
+ * @session_id: session id
+ * @reason: scan reject reason
+ *
+ * This function is used to check the connection status
+ *
+ * Return: true or false
+ */
+static bool hdd_check_connection_status(hdd_adapter_t *adapter,
+ v_U8_t *session_id, scan_reject_states *reason)
+{
+ hddLog(LOG1, FL("Adapter with device mode %s(%d) exists"),
+ hdd_device_mode_to_string(adapter->device_mode),
+ adapter->device_mode);
+ if (((WLAN_HDD_INFRA_STATION == adapter->device_mode) ||
+ (WLAN_HDD_P2P_CLIENT == adapter->device_mode) ||
+ (WLAN_HDD_P2P_DEVICE == adapter->device_mode)) &&
+ (eConnectionState_Connecting ==
+ (WLAN_HDD_GET_STATION_CTX_PTR(adapter))->conn_info.connState)) {
+ hddLog(LOGE, FL("%p(%d) Connection is in progress"),
+ WLAN_HDD_GET_STATION_CTX_PTR(adapter),
+ adapter->sessionId);
+ if (session_id && reason) {
+ *session_id = adapter->sessionId;
+ *reason = eHDD_CONNECTION_IN_PROGRESS;
+ }
+ return true;
+ }
+ if ((WLAN_HDD_INFRA_STATION == adapter->device_mode) &&
+ smeNeighborMiddleOfRoaming(WLAN_HDD_GET_HAL_CTX(adapter),
+ adapter->sessionId)) {
+ hddLog(LOGE, FL("%p(%d) Reassociation is in progress"),
+ WLAN_HDD_GET_STATION_CTX_PTR(adapter),
+ adapter->sessionId);
+ if (session_id && reason) {
+ *session_id = adapter->sessionId;
+ *reason = eHDD_REASSOC_IN_PROGRESS;
+ }
+ return true;
+ }
+ if ((WLAN_HDD_INFRA_STATION == adapter->device_mode) ||
+ (WLAN_HDD_P2P_CLIENT == adapter->device_mode) ||
+ (WLAN_HDD_P2P_DEVICE == adapter->device_mode)) {
+ if(hdd_is_sta_in_middle_of_eapol(adapter, session_id, reason))
+ return true;
+ } else if ((WLAN_HDD_SOFTAP == adapter->device_mode) ||
+ (WLAN_HDD_P2P_GO == adapter->device_mode)) {
+ if(hdd_is_sap_in_middle_of_eapol(adapter, session_id, reason))
+ return true;
+ }
+ return false;
+}
+
+
/*
* hdd_isConnectionInProgress() - HDD function to check connection in progress
* @pHddCtx - HDD context
- * @is_roc - roc
+ * @session_id: session id
+ * @reason: scan reject reason
*
* Go through each adapter and check if Connection is in progress
*
* Return: true if connection in progress; false otherwise.
*/
-bool hdd_isConnectionInProgress(hdd_context_t *pHddCtx)
+bool hdd_isConnectionInProgress(hdd_context_t *pHddCtx, v_U8_t *session_id,
+ scan_reject_states *reason)
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
- hdd_station_ctx_t *pHddStaCtx = NULL;
hdd_adapter_t *pAdapter = NULL;
VOS_STATUS status = 0;
- v_U8_t staId = 0;
- v_U8_t *staMac = NULL;
if (TRUE == pHddCtx->btCoexModeSet) {
hddLog(LOG1, FL("BTCoex Mode operation in progress"));
@@ -16526,76 +16934,9 @@
while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) {
pAdapter = pAdapterNode->pAdapter;
- if (pAdapter) {
- hddLog(LOG1, FL("Adapter with device mode %s(%d) exists"),
- hdd_device_mode_to_string(pAdapter->device_mode),
- pAdapter->device_mode);
- if (((WLAN_HDD_INFRA_STATION ==
- pAdapter->device_mode) ||
- (WLAN_HDD_P2P_CLIENT ==
- pAdapter->device_mode) ||
- (WLAN_HDD_P2P_DEVICE ==
- pAdapter->device_mode)) &&
- (eConnectionState_Connecting ==
- (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->
- conn_info.connState)) {
- hddLog(LOGE,
- FL("%p(%d) Connection is in progress"),
- WLAN_HDD_GET_STATION_CTX_PTR(pAdapter),
- pAdapter->sessionId);
- return true;
- }
- if ((WLAN_HDD_INFRA_STATION == pAdapter->device_mode) &&
- smeNeighborMiddleOfRoaming(
- WLAN_HDD_GET_HAL_CTX(pAdapter), pAdapter->sessionId))
- {
- hddLog(VOS_TRACE_LEVEL_ERROR,
- "%s: %p(%d) Reassociation is in progress", __func__,
- WLAN_HDD_GET_STATION_CTX_PTR(pAdapter), pAdapter->sessionId);
- return VOS_TRUE;
- }
- if ((WLAN_HDD_INFRA_STATION ==
- pAdapter->device_mode) ||
- (WLAN_HDD_P2P_CLIENT ==
- pAdapter->device_mode) ||
- (WLAN_HDD_P2P_DEVICE ==
- pAdapter->device_mode)) {
- pHddStaCtx =
- WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
- if ((eConnectionState_Associated ==
- pHddStaCtx->conn_info.connState) &&
- (VOS_FALSE ==
- pHddStaCtx->conn_info.
- uIsAuthenticated)) {
- staMac = (v_U8_t *) &(pAdapter->
- macAddressCurrent.bytes[0]);
- hddLog(LOGE,
- FL("client " MAC_ADDRESS_STR " is in the middle of WPS/EAPOL exchange."),
- MAC_ADDR_ARRAY(staMac));
- return true;
- }
- } else if ((WLAN_HDD_SOFTAP == pAdapter->device_mode) ||
- (WLAN_HDD_P2P_GO == pAdapter->device_mode)) {
- for (staId = 0; staId < WLAN_MAX_STA_COUNT;
- staId++) {
- if ((pAdapter->aStaInfo[staId].
- isUsed) &&
- (WLANTL_STA_CONNECTED ==
- pAdapter->aStaInfo[staId].
- tlSTAState)) {
- staMac = (v_U8_t *) &(pAdapter->
- aStaInfo[staId].
- macAddrSTA.bytes[0]);
-
- hddLog(LOGE,
- FL("client " MAC_ADDRESS_STR " of SoftAP/P2P-GO is in the "
- "middle of WPS/EAPOL exchange."),
- MAC_ADDR_ARRAY(staMac));
- return true;
- }
- }
- }
- }
+ if (pAdapter)
+ hdd_check_connection_status(pAdapter, session_id,
+ reason);
status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
pAdapterNode = pNext;
}
@@ -16626,6 +16967,89 @@
}
}
+#ifdef CFG80211_SCAN_RANDOM_MAC_ADDR
+/**
+ * wlan_hdd_update_scan_rand_attrs - fill the host/pno scan rand attrs
+ * @scan_req: pointer for destination mac addr and mac mask
+ * @cfg_scan_req: pointer for source mac addr and mac mask
+ * @scan_type: type of scan from enum wlan_hdd_scan_type
+ *
+ * If scan randomize flag is set in cfg scan request flags, this function
+ * copies mac addr and mac mask in cfg80211 scan/sched scan request to
+ * randomization attributes in tCsrScanRequest (normal scan) or
+ * tpSirPNOScanReq (sched scan). Based on the type of scan, scan_req and
+ * cfg_scan_req are type casted accordingly.
+ *
+ * Return: Return none
+ */
+static void wlan_hdd_update_scan_rand_attrs(void *scan_req,
+ void *cfg_scan_req,
+ uint32_t scan_type)
+{
+ uint32_t flags = 0;
+ uint8_t *cfg_mac_addr = NULL;
+ uint8_t *cfg_mac_addr_mask = NULL;
+ uint32_t *scan_randomization = NULL;
+ uint8_t *scan_mac_addr = NULL;
+ uint8_t *scan_mac_addr_mask = NULL;
+
+ if (scan_type == WLAN_HDD_HOST_SCAN) {
+ tCsrScanRequest *csr_scan_req = NULL;
+ struct cfg80211_scan_request *request = NULL;
+
+ csr_scan_req = (tCsrScanRequest *)scan_req;
+ request = (struct cfg80211_scan_request *)cfg_scan_req;
+
+ flags = request->flags;
+ if (!(flags & NL80211_SCAN_FLAG_RANDOM_ADDR))
+ return;
+
+ cfg_mac_addr = request->mac_addr;
+ cfg_mac_addr_mask = request->mac_addr_mask;
+ scan_randomization = &csr_scan_req->enable_scan_randomization;
+ scan_mac_addr = csr_scan_req->mac_addr;
+ scan_mac_addr_mask = csr_scan_req->mac_addr_mask;
+ } else if (scan_type == WLAN_HDD_PNO_SCAN) {
+ tpSirPNOScanReq pno_scan_req = NULL;
+ struct cfg80211_sched_scan_request *request = NULL;
+
+ pno_scan_req = (tpSirPNOScanReq)scan_req;
+ request = (struct cfg80211_sched_scan_request *)cfg_scan_req;
+
+ flags = request->flags;
+ if (!(flags & NL80211_SCAN_FLAG_RANDOM_ADDR))
+ return;
+
+ cfg_mac_addr = request->mac_addr;
+ cfg_mac_addr_mask = request->mac_addr_mask;
+ scan_randomization =
+ &pno_scan_req->enable_pno_scan_randomization;
+ scan_mac_addr = pno_scan_req->mac_addr;
+ scan_mac_addr_mask = pno_scan_req->mac_addr_mask;
+ } else {
+ hddLog(LOGE, FL("invalid scan type for randomization"));
+ return;
+ }
+
+ /* enable mac randomization */
+ *scan_randomization = 1;
+ memcpy(scan_mac_addr, cfg_mac_addr, VOS_MAC_ADDR_SIZE);
+ memcpy(scan_mac_addr_mask, cfg_mac_addr_mask, VOS_MAC_ADDR_SIZE);
+
+ hddLog(LOG1, FL("Mac Addr: "MAC_ADDRESS_STR
+ " and Mac Mask: " MAC_ADDRESS_STR),
+ MAC_ADDR_ARRAY(scan_mac_addr),
+ MAC_ADDR_ARRAY(scan_mac_addr_mask));
+}
+#else
+static void wlan_hdd_update_scan_rand_attrs(void *scan_req,
+ void *cfg_scan_req,
+ uint32_t scan_type)
+{
+ return;
+}
+#endif
+
/*
* FUNCTION: __wlan_hdd_cfg80211_scan
* this scan respond to scan trigger and update cfg80211 scan database
@@ -16643,6 +17067,7 @@
hdd_adapter_t *pAdapter = WLAN_HDD_GET_PRIV_PTR( dev );
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX( pAdapter );
hdd_wext_state_t *pwextBuf = WLAN_HDD_GET_WEXT_STATE_PTR(pAdapter);
+ hdd_station_ctx_t *station_ctx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
hdd_config_t *cfg_param = NULL;
tCsrScanRequest scanRequest;
tANI_U8 *channelList = NULL, i;
@@ -16654,6 +17079,9 @@
uint16_t con_dfs_ch;
bool is_p2p_scan = false;
uint8_t num_chan = 0;
+ v_U8_t curr_session_id;
+ scan_reject_states curr_reason;
+ static uint32_t scan_ebusy_cnt;
ENTER();
@@ -16703,11 +17131,12 @@
}
}
- if (TRUE == pScanInfo->mScanPending)
- {
- if ( MAX_PENDING_LOG > pScanInfo->mScanPendingCounter++ )
- {
- hddLog(VOS_TRACE_LEVEL_ERROR, "%s: mScanPending is TRUE", __func__);
+ if (TRUE == pScanInfo->mScanPending) {
+ scan_ebusy_cnt++;
+
+ if (MAX_PENDING_LOG > pScanInfo->mScanPendingCounter++) {
+ hddLog(LOGE, "%s: mScanPending is TRUE scan_ebusy_cnt: %u",
+ __func__, scan_ebusy_cnt);
}
return -EBUSY;
}
@@ -16716,9 +17145,11 @@
//Channel and action frame is pending
//Otherwise Cancel Remain On Channel and allow Scan
//If no action frame pending
- if (0 != wlan_hdd_check_remain_on_channel(pAdapter))
- {
- hddLog(VOS_TRACE_LEVEL_ERROR, "%s: Remain On Channel Pending", __func__);
+ if (0 != wlan_hdd_check_remain_on_channel(pAdapter)) {
+ scan_ebusy_cnt++;
+ hddLog(LOGE, "%s: Remain On Channel Pending. scan_ebusy_cnt: %u",
+ __func__, scan_ebusy_cnt);
+
return -EBUSY;
}
#ifdef FEATURE_WLAN_TDLS
@@ -16754,19 +17185,54 @@
if (TRUE == pHddCtx->tmInfo.tmAction.enterImps)
{
mutex_unlock(&pHddCtx->tmInfo.tmOperationLock);
- hddLog(VOS_TRACE_LEVEL_ERROR,
- "%s: MAX TM Level Scan not allowed", __func__);
+ scan_ebusy_cnt++;
+ hddLog(LOGE, "%s: MAX TM Level Scan not allowed. scan_ebusy_cnt: %u",
+ __func__, scan_ebusy_cnt);
+
return -EBUSY;
}
mutex_unlock(&pHddCtx->tmInfo.tmOperationLock);
/* Check if scan is allowed at this point of time.
*/
- if (hdd_isConnectionInProgress(pHddCtx)) {
- hddLog(LOGE, FL("Scan not allowed"));
+ if (hdd_isConnectionInProgress(pHddCtx, &curr_session_id, &curr_reason)) {
+ scan_ebusy_cnt++;
+ hddLog(LOGE, FL("Scan not allowed, scan_ebusy_cnt: %u"),
+ scan_ebusy_cnt);
+
+ if (pHddCtx->last_scan_reject_session_id != curr_session_id ||
+ pHddCtx->last_scan_reject_reason != curr_reason ||
+ !pHddCtx->last_scan_reject_timestamp) {
+ pHddCtx->last_scan_reject_session_id = curr_session_id;
+ pHddCtx->last_scan_reject_reason = curr_reason;
+ pHddCtx->last_scan_reject_timestamp = jiffies_to_msecs(jiffies);
+ } else {
+ hddLog(LOGE, FL("curr_session id %d curr_reason %d time delta %lu"),
+ curr_session_id, curr_reason,
+ (jiffies_to_msecs(jiffies) -
+ pHddCtx->last_scan_reject_timestamp));
+ if ((jiffies_to_msecs(jiffies) -
+ pHddCtx->last_scan_reject_timestamp) >=
+ SCAN_REJECT_THRESHOLD_TIME) {
+ pHddCtx->last_scan_reject_timestamp = 0;
+ if (pHddCtx->cfg_ini->enable_fatal_event) {
+ vos_flush_logs(WLAN_LOG_TYPE_FATAL,
+ WLAN_LOG_INDICATOR_HOST_DRIVER,
+ WLAN_LOG_REASON_SCAN_NOT_ALLOWED,
+ DUMP_NO_TRACE);
+ } else {
+ hddLog(LOGE, FL("Triggering SSR due to scan stuck"));
+ vos_wlanRestart();
+ }
+ }
+ }
return -EBUSY;
}
+ pHddCtx->last_scan_reject_timestamp = 0;
+ pHddCtx->last_scan_reject_session_id = 0xFF;
+ pHddCtx->last_scan_reject_reason = 0;
+
vos_mem_zero( &scanRequest, sizeof(scanRequest));
/* Even though supplicant doesn't provide any SSIDs, n_ssids is
@@ -17002,6 +17468,32 @@
pAdapter->sessionId);
#endif
+ wlan_hdd_update_scan_rand_attrs((void *)&scanRequest, (void *)request,
+ WLAN_HDD_HOST_SCAN);
+
+ if (pAdapter->device_mode == WLAN_HDD_INFRA_STATION &&
+ !is_p2p_scan &&
+ !hdd_connIsConnected(station_ctx) &&
+ (pHddCtx->cfg_ini->probe_req_ie_whitelist)) {
+ if (pHddCtx->no_of_probe_req_ouis != 0) {
+ scanRequest.voui = (struct vendor_oui *)vos_mem_malloc(
+ pHddCtx->no_of_probe_req_ouis *
+ sizeof(struct vendor_oui));
+ if (!scanRequest.voui) {
+ hddLog(LOGE, FL("Not enough memory for voui"));
+ scanRequest.num_vendor_oui = 0;
+ status = -ENOMEM;
+ goto free_mem;
+ }
+ }
+
+ wlan_hdd_fill_whitelist_ie_attrs(&scanRequest.ie_whitelist,
+ scanRequest.probe_req_ie_bitmap,
+ &scanRequest.num_vendor_oui,
+ scanRequest.voui,
+ pHddCtx);
+ }
+
vos_runtime_pm_prevent_suspend(pHddCtx->runtime_context.scan);
status = sme_ScanRequest( WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId, &scanRequest, &scanId,
@@ -17012,14 +17504,13 @@
hddLog(VOS_TRACE_LEVEL_ERROR,
"%s: sme_ScanRequest returned error %d", __func__, status);
complete(&pScanInfo->scan_req_completion_event);
- if(eHAL_STATUS_RESOURCES == status)
- {
- hddLog(VOS_TRACE_LEVEL_ERROR, "%s: HO is in progress.So defer the scan by informing busy",
- __func__);
+ if (eHAL_STATUS_RESOURCES == status) {
+ scan_ebusy_cnt++;
+ hddLog(LOGE, FL("HO is in progress. Defer scan by informing busy scan_ebusy_cnt: %u"),
+ scan_ebusy_cnt);
+
status = -EBUSY;
- }
- else
- {
+ } else {
status = -EIO;
}
@@ -17043,6 +17534,12 @@
if( channelList )
vos_mem_free( channelList );
+ if(scanRequest.voui)
+ vos_mem_free(scanRequest.voui);
+
+ if (status == 0)
+ scan_ebusy_cnt = 0;
+
EXIT();
return status;
}
@@ -17347,7 +17844,6 @@
vos_mem_copy((void *)(pRoamProfile->SSIDs.SSIDList->SSID.ssId),
ssid, ssid_len);
- pRoamProfile->do_not_roam = false;
if (bssid)
{
pRoamProfile->BSSIDs.numOfBSSIDs = 1;
@@ -20780,31 +21276,88 @@
"%s: cfg80211 scan result database updated", __func__);
}
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0)) || \
+ defined (CFG80211_MULTI_SCAN_PLAN_BACKPORT)
/**
- * wlan_hdd_is_pno_allowed() - Check if PNO is allowed
- * @adapter: HDD Device Adapter
+ * hdd_config_sched_scan_plan() - configures the sched scan plans
+ * from the framework.
+ * @pno_req: pointer to PNO scan request
+ * @request: pointer to scan request from framework
*
- * The PNO Start request is coming from upper layers.
- * It is to be allowed only for Infra STA device type
- * and the link should be in a disconnected state.
- *
- * Return: Success if PNO is allowed, Failure otherwise.
+ * Return: None
*/
-static eHalStatus wlan_hdd_is_pno_allowed(hdd_adapter_t *adapter)
+static void hdd_config_sched_scan_plan(tpSirPNOScanReq pno_req,
+ struct cfg80211_sched_scan_request *request,
+ hdd_context_t *hdd_ctx)
{
- hddLog(LOG1,
- FL("dev_mode=%d, conn_state=%d, session ID=%d"),
- adapter->device_mode,
- adapter->sessionCtx.station.conn_info.connState,
- adapter->sessionId);
- if ((adapter->device_mode == WLAN_HDD_INFRA_STATION) &&
- (eConnectionState_NotConnected ==
- adapter->sessionCtx.station.conn_info.connState))
- return eHAL_STATUS_SUCCESS;
- else
- return eHAL_STATUS_FAILURE;
-
+ if (request->n_scan_plans == 2) {
+ pno_req->fast_scan_period =
+ request->scan_plans[0].interval * MSEC_PER_SEC;
+ pno_req->fast_scan_max_cycles =
+ request->scan_plans[0].iterations;
+ pno_req->slow_scan_period =
+ request->scan_plans[1].interval * MSEC_PER_SEC;
+ hddLog(LOGE, "Base scan interval: %d sec, scan cycles: %d, slow scan interval %d",
+ request->scan_plans[0].interval,
+ request->scan_plans[0].iterations,
+ request->scan_plans[1].interval);
+ } else if (request->n_scan_plans == 1) {
+ pno_req->fast_scan_period = request->scan_plans[0].interval *
+ MSEC_PER_SEC;
+ pno_req->fast_scan_max_cycles = 1;
+ pno_req->slow_scan_period = request->scan_plans[0].interval *
+ MSEC_PER_SEC;
+ } else {
+ hddLog(LOGE, "Invalid number of scan plans %d !!",
+ request->n_scan_plans);
+ }
}
+#else
+static void hdd_config_sched_scan_plan(tpSirPNOScanReq pno_req,
+ struct cfg80211_sched_scan_request *request,
+ hdd_context_t *hdd_ctx)
+{
+ pno_req->fast_scan_period = request->interval;
+ pno_req->fast_scan_max_cycles =
+ hdd_ctx->cfg_ini->configPNOScanTimerRepeatValue;
+ pno_req->slow_scan_period =
+ hdd_ctx->cfg_ini->pno_slow_scan_multiplier *
+ pno_req->fast_scan_period;
+ hddLog(LOGE, "Base scan interval: %d sec PNOScanTimerRepeatValue: %d",
+ (request->interval / 1000),
+ hdd_ctx->cfg_ini->configPNOScanTimerRepeatValue);
+}
+#endif
+
+/**
+ * wlan_hdd_sched_scan_update_relative_rssi() - update CPNO params
+ * @pno_request: pointer to PNO scan request
+ * @request: Pointer to cfg80211 scheduled scan start request
+ *
+ * This function is used to update Connected PNO params sent by kernel
+ *
+ * Return: None
+ */
+#if defined(CFG80211_REPORT_BETTER_BSS_IN_SCHED_SCAN)
+static inline void wlan_hdd_sched_scan_update_relative_rssi(
+ tpSirPNOScanReq pno_request,
+ struct cfg80211_sched_scan_request *request)
+{
+ pno_request->relative_rssi_set = request->relative_rssi_set;
+ pno_request->relative_rssi = request->relative_rssi;
+ if (NL80211_BAND_2GHZ == request->rssi_adjust.band)
+ pno_request->band_rssi_pref.band = SIR_BAND_2_4_GHZ;
+ else if (NL80211_BAND_5GHZ == request->rssi_adjust.band)
+ pno_request->band_rssi_pref.band = SIR_BAND_5_GHZ;
+ pno_request->band_rssi_pref.rssi = request->rssi_adjust.delta;
+}
+#else
+static inline void wlan_hdd_sched_scan_update_relative_rssi(
+ tpSirPNOScanReq pno_request,
+ struct cfg80211_sched_scan_request *request)
+{
+}
+#endif
/*
* FUNCTION: __wlan_hdd_cfg80211_sched_scan_start
@@ -20826,6 +21379,7 @@
hdd_scaninfo_t *pScanInfo = &pAdapter->scan_info;
hdd_config_t *config = NULL;
v_U32_t num_ignore_dfs_ch = 0;
+ hdd_station_ctx_t *station_ctx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
ENTER();
@@ -20880,14 +21434,14 @@
}
}
- if (eHAL_STATUS_FAILURE == wlan_hdd_is_pno_allowed(pAdapter))
- {
- VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
- "%s: pno is not allowed", __func__);
- return -ENOTSUPP;
- }
+ if (!hdd_connIsConnected(station_ctx) &&
+ pHddCtx->cfg_ini->probe_req_ie_whitelist)
+ pPnoRequest = (tpSirPNOScanReq) vos_mem_malloc(sizeof(tSirPNOScanReq) +
+ (pHddCtx->no_of_probe_req_ouis) *
+ (sizeof(struct vendor_oui)));
+ else
+ pPnoRequest = (tpSirPNOScanReq) vos_mem_malloc(sizeof(tSirPNOScanReq));
- pPnoRequest = (tpSirPNOScanReq) vos_mem_malloc(sizeof (tSirPNOScanReq));
if (NULL == pPnoRequest)
{
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_FATAL,
@@ -20895,7 +21449,14 @@
return -ENOMEM;
}
- memset(pPnoRequest, 0, sizeof (tSirPNOScanReq));
+ if (!hdd_connIsConnected(station_ctx) &&
+ pHddCtx->cfg_ini->probe_req_ie_whitelist)
+ memset(pPnoRequest, 0, sizeof (tSirPNOScanReq) +
+ (pHddCtx->no_of_probe_req_ouis) *
+ (sizeof(struct vendor_oui)));
+ else
+ memset(pPnoRequest, 0, sizeof (tSirPNOScanReq));
+
pPnoRequest->enable = 1; /*Enable PNO */
pPnoRequest->ucNetworksCount = request->n_match_sets;
if ((!pPnoRequest->ucNetworksCount ) ||
@@ -21031,8 +21592,8 @@
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"request->ie_len = %zu", request->ie_len);
- if ((0 < request->ie_len) && (NULL != request->ie))
- {
+ if ((request->ie_len > 0 && request->ie_len <= SIR_PNO_MAX_PB_REQ_SIZE) &&
+ (NULL != request->ie)) {
pPnoRequest->us24GProbeTemplateLen = request->ie_len;
memcpy(&pPnoRequest->p24GProbeTemplate, request->ie,
pPnoRequest->us24GProbeTemplateLen);
@@ -21042,22 +21603,8 @@
pPnoRequest->us5GProbeTemplateLen);
}
- /*
- * Driver gets only one time interval which is hard coded in
- * supplicant for 10000ms. Taking power consumption into account
- * firmware after gPNOScanTimerRepeatValue times fast_scan_period switches
- * slow_scan_period. This is less frequent scans and firmware shall be
- * in slow_scan_period mode until next PNO Start.
- */
- pPnoRequest->fast_scan_period = request->interval;
- pPnoRequest->fast_scan_max_cycles =
- pHddCtx->cfg_ini->configPNOScanTimerRepeatValue;
- pPnoRequest->slow_scan_period = pHddCtx->cfg_ini->pno_slow_scan_multiplier *
- pPnoRequest->fast_scan_period;
-
- hddLog(LOG1, "Base scan interval: %d sec PNOScanTimerRepeatValue: %d",
- (request->interval / 1000),
- pHddCtx->cfg_ini->configPNOScanTimerRepeatValue);
+ hdd_config_sched_scan_plan(pPnoRequest, request, pHddCtx);
+ wlan_hdd_sched_scan_update_relative_rssi(pPnoRequest, request);
pPnoRequest->modePNO = SIR_PNO_MODE_IMMEDIATE;
@@ -21065,6 +21612,19 @@
"SessionId %d, enable %d, modePNO %d",
pAdapter->sessionId, pPnoRequest->enable, pPnoRequest->modePNO);
+ wlan_hdd_update_scan_rand_attrs((void *)pPnoRequest, (void *)request,
+ WLAN_HDD_PNO_SCAN);
+
+ if (pHddCtx->cfg_ini->probe_req_ie_whitelist &&
+ !hdd_connIsConnected(station_ctx))
+ wlan_hdd_fill_whitelist_ie_attrs(&pPnoRequest->ie_whitelist,
+ pPnoRequest->probe_req_ie_bitmap,
+ &pPnoRequest->num_vendor_oui,
+ (struct vendor_oui *)(
+ (uint8_t *)pPnoRequest +
+ sizeof(*pPnoRequest)),
+ pHddCtx);
+
status = sme_SetPreferredNetworkList(WLAN_HDD_GET_HAL_CTX(pAdapter),
pPnoRequest, pAdapter->sessionId,
hdd_cfg80211_sched_scan_done_callback, pAdapter);
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_debugfs.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_debugfs.c
index 5871ae6..55b83ae 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_debugfs.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_debugfs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -29,11 +29,22 @@
#include <wlan_hdd_includes.h>
#include <wlan_hdd_wowl.h>
#include <vos_sched.h>
+#include "wlan_hdd_debugfs.h"
#define MAX_USER_COMMAND_SIZE_WOWL_ENABLE 8
#define MAX_USER_COMMAND_SIZE_WOWL_PATTERN 512
#define MAX_USER_COMMAND_SIZE_FRAME 4096
+#ifdef MULTI_IF_NAME
+#define HDD_DEBUGFS_DIRNAME "wlan_wcnss" MULTI_IF_NAME
+#else
+#define HDD_DEBUGFS_DIRNAME "wlan_wcnss"
+#endif
+
+#ifdef WLAN_POWER_DEBUGFS
+#define POWER_DEBUGFS_BUFFER_MAX_LEN 4096
+#endif
+
/**
* __wcnss_wowenable_write() - write wow enable
* @file: file pointer
@@ -587,6 +598,249 @@
return ret;
}
+#ifdef WLAN_POWER_DEBUGFS
+/**
+ * hdd_power_debugstats_cb() - callback routine for Power stats debugs
+ * @response: Pointer to Power stats response
+ * @context: Pointer to statsContext
+ *
+ * Return: None
+ */
+static void hdd_power_debugstats_cb(struct power_stats_response *response,
+ void *context)
+{
+ struct statsContext *stats_context;
+ struct power_stats_response *power_stats;
+ hdd_adapter_t *adapter;
+ uint32_t power_stats_len;
+ uint32_t stats_registers_len;
+
+ ENTER();
+ if (NULL == context) {
+ hddLog(LOGE, FL("context is NULL"));
+ return;
+ }
+
+ stats_context = (struct statsContext *)context;
+
+ spin_lock(&hdd_context_lock);
+ adapter = stats_context->pAdapter;
+ if ((POWER_STATS_MAGIC != stats_context->magic) ||
+ (NULL == adapter) ||
+ (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
+ spin_unlock(&hdd_context_lock);
+ hddLog(LOGE, FL("Invalid context, adapter [%p] magic [%08x]"),
+ adapter, stats_context->magic);
+ return;
+ }
+
+ /* Invalidate the Stats context magic */
+ stats_context->magic = 0;
+
+ stats_registers_len = (sizeof(response->debug_registers[0]) *
+ response->num_debug_register);
+ power_stats_len = stats_registers_len + sizeof(*power_stats);
+ adapter->chip_power_stats = vos_mem_malloc(power_stats_len);
+ if (!adapter->chip_power_stats) {
+ hddLog(LOGE, FL("Power stats memory alloc fails!"));
+ goto exit_stats_cb;
+ }
+
+ power_stats = adapter->chip_power_stats;
+ vos_mem_zero(power_stats, power_stats_len);
+
+ power_stats->cumulative_sleep_time_ms
+ = response->cumulative_sleep_time_ms;
+ power_stats->cumulative_total_on_time_ms
+ = response->cumulative_total_on_time_ms;
+ power_stats->deep_sleep_enter_counter
+ = response->deep_sleep_enter_counter;
+ power_stats->last_deep_sleep_enter_tstamp_ms
+ = response->last_deep_sleep_enter_tstamp_ms;
+ power_stats->debug_register_fmt
+ = response->debug_register_fmt;
+ power_stats->num_debug_register
+ = response->num_debug_register;
+
+ power_stats->debug_registers = (uint32_t *)(power_stats + 1);
+
+ vos_mem_copy(power_stats->debug_registers,
+ response->debug_registers,
+ stats_registers_len);
+
+exit_stats_cb:
+ complete(&stats_context->completion);
+ spin_unlock(&hdd_context_lock);
+ EXIT();
+}
+
+/**
+ * __wlan_hdd_read_power_debugfs() - API to collect Chip power stats from FW
+ * @file: file pointer
+ * @buf: buffer
+ * @count: count
+ * @pos: position pointer
+ *
+ * Return: Number of bytes read on success, error number otherwise
+ */
+static ssize_t __wlan_hdd_read_power_debugfs(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ hdd_adapter_t *adapter;
+ hdd_context_t *hdd_ctx;
+ struct statsContext context;
+ struct power_stats_response *chip_power_stats;
+ ssize_t ret_cnt = 0;
+ int rc = 0, j;
+ unsigned int len = 0;
+ char *power_debugfs_buf;
+
+ ENTER();
+ adapter = (hdd_adapter_t *)file->private_data;
+ if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
+ hddLog(LOGE,
+ FL("Invalid adapter or adapter has invalid magic"));
+ return -EINVAL;
+ }
+
+ hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+ ret_cnt = wlan_hdd_validate_context(hdd_ctx);
+ if (0 != ret_cnt)
+ return ret_cnt;
+
+ if (adapter->chip_power_stats)
+ vos_mem_free(adapter->chip_power_stats);
+
+ adapter->chip_power_stats = NULL;
+ context.pAdapter = adapter;
+ context.magic = POWER_STATS_MAGIC;
+
+ init_completion(&context.completion);
+
+ if (eHAL_STATUS_SUCCESS !=
+ sme_power_debug_stats_req(hdd_ctx->hHal,
+ hdd_power_debugstats_cb,
+ &context)) {
+ hddLog(LOGE, FL("chip power stats request failed"));
+ return -EINVAL;
+ }
+
+ rc = wait_for_completion_timeout(&context.completion,
+ msecs_to_jiffies(WLAN_WAIT_TIME_POWER_STATS));
+ if (!rc) {
+ hddLog(LOGE, FL("Target response timed out Power stats"));
+ /* Invalidate the Stats context magic */
+ spin_lock(&hdd_context_lock);
+ context.magic = 0;
+ spin_unlock(&hdd_context_lock);
+ return -ETIMEDOUT;
+ }
+
+ chip_power_stats = adapter->chip_power_stats;
+ if (!chip_power_stats) {
+ hddLog(LOGE, FL("Power stats retrieval fails!"));
+ return -EINVAL;
+ }
+
+ power_debugfs_buf = vos_mem_malloc(POWER_DEBUGFS_BUFFER_MAX_LEN);
+ if (!power_debugfs_buf) {
+ hddLog(LOGE, FL("Power stats buffer alloc fails!"));
+ vos_mem_free(chip_power_stats);
+ adapter->chip_power_stats = NULL;
+ return -EINVAL;
+ }
+
+ len += scnprintf(power_debugfs_buf, POWER_DEBUGFS_BUFFER_MAX_LEN,
+ "POWER DEBUG STATS\n=================\n"
+ "cumulative_sleep_time_ms: %d\n"
+ "cumulative_total_on_time_ms: %d\n"
+ "deep_sleep_enter_counter: %d\n"
+ "last_deep_sleep_enter_tstamp_ms: %d\n"
+ "debug_register_fmt: %d\n"
+ "num_debug_register: %d\n",
+ chip_power_stats->cumulative_sleep_time_ms,
+ chip_power_stats->cumulative_total_on_time_ms,
+ chip_power_stats->deep_sleep_enter_counter,
+ chip_power_stats->last_deep_sleep_enter_tstamp_ms,
+ chip_power_stats->debug_register_fmt,
+ chip_power_stats->num_debug_register);
+
+ for (j = 0; j < chip_power_stats->num_debug_register; j++) {
+ if ((POWER_DEBUGFS_BUFFER_MAX_LEN - len) > 0)
+ len += scnprintf(power_debugfs_buf + len,
+ POWER_DEBUGFS_BUFFER_MAX_LEN - len,
+ "debug_registers[%d]: 0x%x\n", j,
+ chip_power_stats->debug_registers[j]);
+ else
+ j = chip_power_stats->num_debug_register;
+ }
+
+ vos_mem_free(chip_power_stats);
+ adapter->chip_power_stats = NULL;
+
+ ret_cnt = simple_read_from_buffer(buf, count, pos,
+ power_debugfs_buf, len);
+ vos_mem_free(power_debugfs_buf);
+ return ret_cnt;
+}
+
+/**
+ * wlan_hdd_read_power_debugfs() - SSR wrapper function to read power debugfs
+ * @file: file pointer
+ * @buf: buffer
+ * @count: count
+ * @pos: position pointer
+ *
+ * Return: Number of bytes read on success, error number otherwise
+ */
+static ssize_t wlan_hdd_read_power_debugfs(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ int ret;
+
+ vos_ssr_protect(__func__);
+ ret = __wlan_hdd_read_power_debugfs(file, buf, count, pos);
+ vos_ssr_unprotect(__func__);
+
+ return ret;
+}
+
+/**
+ * __wlan_hdd_open_power_debugfs() - Function to save private on open
+ * @inode: Pointer to inode structure
+ * @file: file pointer
+ *
+ * Return: zero
+ */
+static int __wlan_hdd_open_power_debugfs(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/**
+ * wlan_hdd_open_power_debugfs() - SSR wrapper function to save private on open
+ * @inode: Pointer to inode structure
+ * @file: file pointer
+ *
+ * Return: zero
+ */
+static int wlan_hdd_open_power_debugfs(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ vos_ssr_protect(__func__);
+ ret = __wlan_hdd_open_power_debugfs(inode, file);
+ vos_ssr_unprotect(__func__);
+
+ return ret;
+}
+
+#endif
+
+
static const struct file_operations fops_wowenable = {
.write = wcnss_wowenable_write,
.open = wcnss_debugfs_open,
@@ -608,10 +862,43 @@
.llseek = default_llseek,
};
+#ifdef WLAN_POWER_DEBUGFS
+static const struct file_operations fops_powerdebugs = {
+ .read = wlan_hdd_read_power_debugfs,
+ .open = wlan_hdd_open_power_debugfs,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+/**
+ * wlan_hdd_create_power_stats_file() - API to create power stats file
+ *
+ * Return: VOS_STATUS
+ */
+static VOS_STATUS wlan_hdd_create_power_stats_file(hdd_adapter_t *adapter,
+ hdd_context_t *hdd_ctx)
+{
+ if (NULL == debugfs_create_file("power_stats",
+ S_IRUSR | S_IRGRP | S_IROTH,
+ hdd_ctx->debugfs_phy, adapter,
+ &fops_powerdebugs))
+ return VOS_STATUS_E_FAILURE;
+
+ return VOS_STATUS_SUCCESS;
+}
+
+#else
+static VOS_STATUS wlan_hdd_create_power_stats_file(hdd_adapter_t *adapter,
+ hdd_context_t *hdd_ctx)
+{
+ return VOS_STATUS_SUCCESS;
+}
+#endif
+
VOS_STATUS hdd_debugfs_init(hdd_adapter_t *pAdapter)
{
hdd_context_t *pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
- pHddCtx->debugfs_phy = debugfs_create_dir("wlan_wcnss", 0);
+ pHddCtx->debugfs_phy = debugfs_create_dir(HDD_DEBUGFS_DIRNAME, 0);
if (NULL == pHddCtx->debugfs_phy)
return VOS_STATUS_E_FAILURE;
@@ -628,6 +915,10 @@
pHddCtx->debugfs_phy, pAdapter, &fops_patterngen))
return VOS_STATUS_E_FAILURE;
+ if (VOS_STATUS_SUCCESS != wlan_hdd_create_power_stats_file(pAdapter,
+ pHddCtx))
+ return VOS_STATUS_E_FAILURE;
+
return VOS_STATUS_SUCCESS;
}
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_early_suspend.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_early_suspend.c
index 844eca9..5598d36 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_early_suspend.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_early_suspend.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -52,7 +52,6 @@
#include <vos_sched.h>
#include <macInitApi.h>
#include <wlan_qct_sys.h>
-#include <wlan_btc_svc.h>
#include <wlan_nlink_common.h>
#include <wlan_hdd_main.h>
#include <wlan_hdd_assoc.h>
@@ -2328,6 +2327,10 @@
/* Register TM level change handler function to the platform */
hddDevTmRegisterNotifyCallback(pHddCtx);
+ pHddCtx->last_scan_reject_session_id = 0xFF;
+ pHddCtx->last_scan_reject_reason = 0;
+ pHddCtx->last_scan_reject_timestamp = 0;
+
pHddCtx->hdd_mcastbcast_filter_set = FALSE;
pHddCtx->btCoexModeSet = false;
hdd_register_mcast_bcast_filter(pHddCtx);
@@ -2355,6 +2358,8 @@
wlan_hdd_cfg80211_extscan_callback);
#endif /* FEATURE_WLAN_EXTSCAN */
sme_set_rssi_threshold_breached_cb(pHddCtx->hHal, hdd_rssi_threshold_breached);
+ wlan_hdd_cfg80211_link_layer_stats_init(pHddCtx);
+ sme_bpf_offload_register_callback(pHddCtx->hHal, hdd_get_bpf_offload_cb);
#ifdef WLAN_FEATURE_LPSS
wlan_hdd_send_all_scan_intf_info(pHddCtx);
@@ -2382,7 +2387,6 @@
/* Unregister the Net Device Notifier */
unregister_netdevice_notifier(&hdd_netdev_notifier);
/* Clean up HDD Nlink Service */
- send_btc_nlink_msg(WLAN_MODULE_DOWN_IND, 0);
#ifdef WLAN_KD_READY_NOTIFIER
nl_srv_exit(pHddCtx->ptt_pid);
#else
@@ -2410,8 +2414,6 @@
return -EPERM;
success:
- /* Trigger replay of BTC events */
- send_btc_nlink_msg(WLAN_MODULE_DOWN_IND, 0);
pHddCtx->isLogpInProgress = FALSE;
hdd_ssr_timer_del();
return VOS_STATUS_SUCCESS;
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_hostapd.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_hostapd.c
index 27b7451..2154791 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_hostapd.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_hostapd.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -71,7 +71,6 @@
#include <linux/netdevice.h>
#include <linux/mmc/sdio_func.h>
#include "wlan_nlink_common.h"
-#include "wlan_btc_svc.h"
#include "wlan_hdd_p2p.h"
#ifdef IPA_OFFLOAD
#include <wlan_hdd_ipa.h>
@@ -120,7 +119,6 @@
/* EID byte + length byte + four byte WiFi OUI */
#define DOT11F_EID_HEADER_LEN (6)
-#define DUMP_DP_TRACE 0
/*---------------------------------------------------------------------------
* Function definitions
@@ -569,10 +567,9 @@
goto exit;
}
- if ((!ifr) || (!ifr->ifr_data))
- {
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
- FL("ifr or ifr->ifr_data is NULL"));
+ if ((!ifr) || (!ifr->ifr_data)) {
+ hddLog(LOGE,
+ FL("ifr or ifr->ifr_data is NULL cmd: %d"), cmd);
ret = -EINVAL;
goto exit;
}
@@ -1452,9 +1449,6 @@
hdd_wlan_green_ap_start_bss(pHddCtx);
- // Send current operating channel of SoftAP to BTC-ES
- send_btc_nlink_msg(WLAN_BTC_SOFTAP_BSS_START, 0);
-
/* Set default key index */
VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
"%s: default key index %hu", __func__,
@@ -1795,6 +1789,10 @@
}
#endif /* QCA_PKT_PROTO_TRACE */
+ DPTRACE(adf_dp_trace_mgmt_pkt(ADF_DP_TRACE_MGMT_PACKET_RECORD,
+ pHostapdAdapter->sessionId,
+ ADF_PROTO_TYPE_MGMT, ADF_PROTO_MGMT_ASSOC));
+
#ifdef FEATURE_BUS_BANDWIDTH
/* start timer in sap/p2p_go */
if (pHddApCtx->bApActive == VOS_FALSE)
@@ -1917,6 +1915,11 @@
vos_pkt_trace_buf_update("HA:DISASC");
}
#endif /* QCA_PKT_PROTO_TRACE */
+
+ DPTRACE(adf_dp_trace_mgmt_pkt(ADF_DP_TRACE_MGMT_PACKET_RECORD,
+ pHostapdAdapter->sessionId,
+ ADF_PROTO_TYPE_MGMT, ADF_PROTO_MGMT_DISASSOC));
+
hdd_softap_DeregisterSTA(pHostapdAdapter, staId);
pHddApCtx->bApActive = VOS_FALSE;
@@ -2587,6 +2590,10 @@
value[1], value[2]);
if (value[1] == DUMP_DP_TRACE)
adf_dp_trace_dump_all(value[2]);
+ else if (value[1] == ENABLE_DP_TRACE_LIVE_MODE)
+ adf_dp_trace_enable_live_mode();
+ else if (value[1] == CLEAR_DP_TRACE_BUFFER)
+ adf_dp_trace_clear_buffer();
else
hddLog(LOGE, "unexpected value for dump_dp_trace");
break;
@@ -5428,68 +5435,69 @@
char *extra)
{
- hdd_adapter_t *pHostapdAdapter = (netdev_priv(dev));
+ hdd_adapter_t *pHostapdAdapter = (netdev_priv(dev));
#ifndef WLAN_FEATURE_MBSSID
- v_CONTEXT_t pVosContext;
+ v_CONTEXT_t pVosContext;
#endif
- eHalStatus halStatus= eHAL_STATUS_SUCCESS;
- u_int8_t *genie = (u_int8_t *)extra;
- hdd_context_t *hdd_ctx;
- int ret;
+ eHalStatus halStatus = eHAL_STATUS_SUCCESS;
+ u_int8_t *genie = (u_int8_t *)extra;
+ hdd_context_t *hdd_ctx;
+ int ret;
- ENTER();
+ ENTER();
- hdd_ctx = WLAN_HDD_GET_CTX(pHostapdAdapter);
- ret = wlan_hdd_validate_context(hdd_ctx);
- if (0 != ret)
- return ret;
+ hdd_ctx = WLAN_HDD_GET_CTX(pHostapdAdapter);
+ ret = wlan_hdd_validate_context(hdd_ctx);
+ if (0 != ret)
+ return ret;
#ifndef WLAN_FEATURE_MBSSID
- pVosContext = hdd_ctx->pvosContext;
- if (NULL == pVosContext) {
- VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
- "%s: VOS Context is NULL", __func__);
- return -EINVAL;
- }
+ pVosContext = hdd_ctx->pvosContext;
+ if (NULL == pVosContext) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+ "%s: VOS Context is NULL", __func__);
+ return -EINVAL;
+ }
#endif
- if(!wrqu->data.length)
- {
- EXIT();
- return 0;
- }
+ if (!wrqu->data.length) {
+ EXIT();
+ return 0;
+ }
- if (wrqu->data.length > DOT11F_IE_RSN_MAX_LEN) {
- VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
- "%s: WPARSN Ie input length is more than max[%d]", __func__,
- wrqu->data.length);
- return -EINVAL;
- }
+ if (wrqu->data.length > DOT11F_IE_RSN_MAX_LEN) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+ "%s: WPARSN Ie input length is more than max[%d]", __func__,
+ wrqu->data.length);
+ return -EINVAL;
+ }
- switch (genie[0])
- {
- case DOT11F_EID_WPA:
- case DOT11F_EID_RSN:
- if((WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->uPrivacy == 0)
- {
- hdd_softap_Deregister_BC_STA(pHostapdAdapter);
- hdd_softap_Register_BC_STA(pHostapdAdapter, 1);
- }
- (WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->uPrivacy = 1;
+ switch (genie[0]) {
+ case DOT11F_EID_WPA:
+ case DOT11F_EID_RSN:
+ if ((WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->uPrivacy == 0) {
+ hdd_softap_Deregister_BC_STA(pHostapdAdapter);
+ hdd_softap_Register_BC_STA(pHostapdAdapter, 1);
+ }
+ (WLAN_HDD_GET_AP_CTX_PTR(pHostapdAdapter))->uPrivacy = 1;
#ifdef WLAN_FEATURE_MBSSID
- halStatus = WLANSAP_Set_WPARSNIes(WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter), genie, wrqu->data.length);
+ halStatus = WLANSAP_Set_WPARSNIes(
+ WLAN_HDD_GET_SAP_CTX_PTR(pHostapdAdapter),
+ genie,
+ wrqu->data.length);
#else
- halStatus = WLANSAP_Set_WPARSNIes(pVosContext, genie, wrqu->data.length);
+ halStatus = WLANSAP_Set_WPARSNIes(pVosContext,
+ genie, wrqu->data.length);
#endif
- break;
+ break;
- default:
- hddLog (LOGE, "%s Set UNKNOWN IE %X",__func__, genie[0]);
- halStatus = 0;
- }
+ default:
+ hddLog(LOGE, "%s Set UNKNOWN IE %X", __func__, genie[0]);
+ halStatus = 0;
+ }
- EXIT();
- return halStatus;
+ EXIT();
+ return halStatus;
}
/**
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_main.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_main.c
index c50adaf..7137753 100755
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_main.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_main.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -69,7 +69,6 @@
#include <wlan_hdd_tx_rx.h>
#include <wniApi.h>
#include <wlan_nlink_srv.h>
-#include <wlan_btc_svc.h>
#include <wlan_hdd_cfg.h>
#include <wlan_ptt_sock_svc.h>
#include <dbglog_host.h>
@@ -135,6 +134,10 @@
#include "tl_shim.h"
#include "wlan_hdd_oemdata.h"
+#ifdef CNSS_GENL
+#include <net/cnss_nl.h>
+#endif
+
#if defined(LINUX_QCMBR)
#define SIOCIOCTLTX99 (SIOCDEVPRIVATE+13)
#endif
@@ -5233,6 +5236,7 @@
return -EFAULT;
}
+ hddLog(LOG1, FL("Get antenna mode ret: 0 mode: %s"), extra);
return 0;
}
@@ -6345,7 +6349,7 @@
pAdapter->sessionId,
nOpportunisticThresholdDiff);
}
- else if (strncmp(priv_data.buf, "GETOPPORTUNISTICRSSIDIFF", 24) == 0)
+ else if (strncmp(command, "GETOPPORTUNISTICRSSIDIFF", 24) == 0)
{
tANI_S8 val = sme_GetRoamOpportunisticScanThresholdDiff(
pHddCtx->hHal);
@@ -6388,7 +6392,7 @@
pAdapter->sessionId,
nRoamRescanRssiDiff);
}
- else if (strncmp(priv_data.buf, "GETROAMRESCANRSSIDIFF", 21) == 0)
+ else if (strncmp(command, "GETROAMRESCANRSSIDIFF", 21) == 0)
{
tANI_U8 val = sme_GetRoamRescanRssiDiff(pHddCtx->hHal);
char extra[32];
@@ -7153,9 +7157,10 @@
ret = -EFAULT;
goto exit;
}
- priv_data.buf[numOfBytestoPrint] = '\0';
+ /* This overwrites the last space, which we already copied */
+ extra[numOfBytestoPrint - 1] = '\0';
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
- "%s", priv_data.buf);
+ "%s", extra);
if (length > numOfBytestoPrint)
{
@@ -7169,7 +7174,7 @@
goto exit;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
- "%s", &priv_data.buf[numOfBytestoPrint]);
+ "%s", &extra[numOfBytestoPrint]);
}
/* Free temporary buffer */
@@ -7275,7 +7280,7 @@
/* Success ! */
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_MED,
- "%s", priv_data.buf);
+ "%s", extra);
ret = 0;
}
else if (strncmp(command, "SETRMCTXRATE", 12) == 0)
@@ -7918,8 +7923,6 @@
ret = drv_cmd_get_antenna_mode(pAdapter, pHddCtx, command,
14, &priv_data);
- hddLog(LOG1, FL("Get antenna mode ret: %d mode: %s"),
- ret, priv_data.buf);
} else if (strncmp(command, "STOP", 4) == 0) {
hddLog(LOG1, FL("STOP command"));
pHddCtx->driver_being_stopped = true;
@@ -9006,6 +9009,14 @@
WLAN_CONTROL_PATH);
}
+ /* Enable carrier and transmit queues for NDI */
+ if (WLAN_HDD_IS_NDI(pAdapter)) {
+ hddLog(LOG1, FL("Enabling Tx Queues"));
+ wlan_hdd_netif_queue_control(pAdapter,
+ WLAN_START_ALL_NETIF_QUEUE_N_CARRIER,
+ WLAN_CONTROL_PATH);
+ }
+
return 0;
}
@@ -9098,7 +9109,7 @@
*
* For module, when all the interfaces are down, enter low power mode.
*/
-static inline void wlan_hdd_stop_enter_lowpower(hdd_context_t *hdd_ctx)
+void wlan_hdd_stop_enter_lowpower(hdd_context_t *hdd_ctx)
{
hddLog(VOS_TRACE_LEVEL_INFO,
"%s: All Interfaces are Down entering standby",
@@ -9217,7 +9228,7 @@
* For static driver, when all the interfaces are down, enter low power mode by
* bringing down WLAN hardware.
*/
-static inline void wlan_hdd_stop_enter_lowpower(hdd_context_t *hdd_ctx)
+void wlan_hdd_stop_enter_lowpower(hdd_context_t *hdd_ctx)
{
bool ready;
@@ -9313,6 +9324,17 @@
wlan_hdd_netif_queue_control(pAdapter, WLAN_NETIF_TX_DISABLE_N_CARRIER,
WLAN_CONTROL_PATH);
+ /*
+ * NAN data interface is different in some sense. The traffic on NDI is
+ * bursty in nature and depends on the need to transfer. The service layer
+ * may down the interface after the usage and up again when required.
+ * In some sense, the NDI is expected to be available (like SAP) iface
+ * until NDI delete request is issued by the service layer.
+ * Skip BSS termination and adapter deletion for NAN Data interface (NDI).
+ */
+ if (WLAN_HDD_IS_NDI(pAdapter))
+ return 0;
+
/* The interface is marked as down for outside world (aka kernel)
* But the driver is pretty much alive inside. The driver needs to
* tear down the existing connection on the netdev (session)
@@ -10081,6 +10103,19 @@
hdd_adapter_runtime_suspend_denit(hdd_adapter_t *adapter) { }
#endif
+/**
+ * hdd_adapter_init_action_frame_random_mac() - Initialze attributes needed for
+ * randomization of SA in management action frames
+ * @adapter: Pointer to adapter
+ *
+ * Return: None
+ */
+static void hdd_adapter_init_action_frame_random_mac(hdd_adapter_t *adapter)
+{
+ spin_lock_init(&adapter->random_mac_lock);
+ vos_mem_zero(adapter->random_mac, sizeof(adapter->random_mac));
+}
+
static hdd_adapter_t* hdd_alloc_station_adapter( hdd_context_t *pHddCtx, tSirMacAddr macAddr, const char* name )
{
struct net_device *pWlanDev = NULL;
@@ -10913,6 +10948,9 @@
if( VOS_STATUS_SUCCESS != status )
goto err_free_netdev;
+ /* initialize action frame random mac info */
+ hdd_adapter_init_action_frame_random_mac(pAdapter);
+
// Workqueue which gets scheduled in IPv4 notification callback
vos_init_work(&pAdapter->ipv4NotifierWorkQueue,
hdd_ipv4_notifier_work_queue);
@@ -11665,6 +11703,48 @@
return VOS_STATUS_SUCCESS;
}
+#if defined CFG80211_CONNECT_BSS
+/**
+ * hdd_connect_bss() - API to send connection status to supplicant
+ * @dev: network device
+ * @bssid: bssid to which we want to associate
+ * @req_ie: Request Information Element
+ * @req_ie_len: len of the req IE
+ * @resp_ie: Response IE
+ * @resp_ie_len: len of ht response IE
+ * @status: status
+ * @gfp: Kernel Flag
+ * @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp
+ *
+ * The API is a wrapper to send connection status to supplicant
+ *
+ * Return: Void
+ */
+#if defined CFG80211_CONNECT_TIMEOUT
+static void hdd_connect_bss(struct net_device *dev, const u8 *bssid,
+ struct cfg80211_bss *bss, const u8 *req_ie,
+ size_t req_ie_len, const u8 *resp_ie,
+ size_t resp_ie_len, int status, gfp_t gfp,
+ bool connect_timeout)
+{
+ if (connect_timeout)
+ cfg80211_connect_timeout(dev, bssid, NULL, 0, GFP_KERNEL);
+ else
+ cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len,
+ resp_ie, resp_ie_len, status, gfp);
+}
+#else
+static void hdd_connect_bss(struct net_device *dev, const u8 *bssid,
+ struct cfg80211_bss *bss, const u8 *req_ie,
+ size_t req_ie_len, const u8 *resp_ie,
+ size_t resp_ie_len, int status, gfp_t gfp,
+ bool connect_timeout)
+{
+ cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len,
+ resp_ie, resp_ie_len, status, gfp);
+}
+#endif
+
/**
* hdd_connect_result() - API to send connection status to supplicant
* @dev: network device
@@ -11676,13 +11756,13 @@
* @resp_ie_len: len of ht response IE
* @status: status
* @gfp: Kernel Flag
+ * @connect_timeout: If timed out waiting for Auth/Assoc/Probe resp
*
* The API is a wrapper to send connection status to supplicant
* and allow runtime suspend
*
* Return: Void
*/
-#if defined CFG80211_CONNECT_BSS
void hdd_connect_result(struct net_device *dev,
const u8 *bssid,
tCsrRoamInfo *roam_info,
@@ -11691,7 +11771,8 @@
const u8 *resp_ie,
size_t resp_ie_len,
u16 status,
- gfp_t gfp)
+ gfp_t gfp,
+ bool connect_timeout)
{
hdd_adapter_t *padapter = (hdd_adapter_t *) netdev_priv(dev);
struct cfg80211_bss *bss = NULL;
@@ -11722,9 +11803,9 @@
#endif
}
- cfg80211_connect_bss(dev, bssid, bss, req_ie, req_ie_len,
- resp_ie, resp_ie_len, status, gfp);
-
+ hdd_connect_bss(dev, bssid, bss, req_ie,
+ req_ie_len, resp_ie, resp_ie_len,
+ status, gfp, connect_timeout);
vos_runtime_pm_allow_suspend(padapter->runtime_context.connect);
}
#else
@@ -11736,7 +11817,8 @@
const u8 * resp_ie,
size_t resp_ie_len,
u16 status,
- gfp_t gfp)
+ gfp_t gfp,
+ bool connect_timeout)
{
hdd_adapter_t *padapter = (hdd_adapter_t *) netdev_priv(dev);
@@ -11808,7 +11890,7 @@
hdd_connect_result(pAdapter->dev, NULL, NULL,
NULL, 0, NULL, 0,
WLAN_STATUS_ASSOC_DENIED_UNSPEC,
- GFP_KERNEL);
+ GFP_KERNEL, false);
}
#ifdef QCA_LL_TX_FLOW_CT
@@ -12315,6 +12397,25 @@
}
+hdd_adapter_t * hdd_get_adapter_by_rand_macaddr(hdd_context_t *hdd_ctx,
+ tSirMacAddr mac_addr)
+{
+ hdd_adapter_list_node_t *adapter_node = NULL, *next = NULL;
+ hdd_adapter_t *adapter;
+ VOS_STATUS status;
+
+ status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
+ while (adapter_node && status == VOS_STATUS_SUCCESS) {
+ adapter = adapter_node->pAdapter;
+ if(adapter && hdd_check_random_mac(adapter, mac_addr))
+ return adapter;
+ status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
+ adapter_node = next;
+ }
+
+ return NULL;
+}
+
hdd_adapter_t * hdd_get_adapter_by_name( hdd_context_t *pHddCtx, tANI_U8 *name )
{
hdd_adapter_list_node_t *pAdapterNode = NULL, *pNext = NULL;
@@ -13041,9 +13142,6 @@
if (pConfig && pConfig->fIsLogpEnabled)
vos_watchdog_close(pVosContext);
- //Clean up HDD Nlink Service
- send_btc_nlink_msg(WLAN_MODULE_DOWN_IND, 0);
-
if (VOS_FTM_MODE != hdd_get_conparam())
wlan_hdd_logging_sock_deactivate_svc(pHddCtx);
@@ -13086,6 +13184,7 @@
}
wlan_hdd_deinit_tx_rx_histogram(pHddCtx);
+ hdd_free_probe_req_ouis(pHddCtx);
wiphy_unregister(wiphy) ;
wlan_hdd_cfg80211_deinit(wiphy);
wiphy_free(wiphy) ;
@@ -14436,6 +14535,7 @@
#ifdef QCA_ARP_SPOOFING_WAR
adf_os_device_t adf_ctx;
#endif
+ struct sme_5g_band_pref_params band_pref_params;
ENTER();
@@ -14493,6 +14593,9 @@
((VosContextType*)(pVosContext))->pHDDContext = (v_VOID_t*)pHddCtx;
pHddCtx->parent_dev = dev;
+ pHddCtx->last_scan_reject_session_id = 0xFF;
+ pHddCtx->last_scan_reject_reason = 0;
+ pHddCtx->last_scan_reject_timestamp = 0;
init_completion(&pHddCtx->full_pwr_comp_var);
init_completion(&pHddCtx->standby_comp_var);
@@ -14544,6 +14647,27 @@
goto err_config;
}
+ if (pHddCtx->cfg_ini->probe_req_ie_whitelist)
+ {
+ if (hdd_validate_prb_req_ie_bitmap(pHddCtx))
+ {
+ /* parse ini string probe req oui */
+ status = hdd_parse_probe_req_ouis(pHddCtx);
+ if (VOS_STATUS_SUCCESS != status)
+ {
+ hddLog(LOGE, FL("Error parsing probe req ouis - Ignoring them"
+ " disabling white list"));
+ pHddCtx->cfg_ini->probe_req_ie_whitelist = false;
+ }
+ }
+ else
+ {
+ hddLog(LOGE, FL("invalid probe req ie bitmap and ouis,"
+ " disabling white list"));
+ pHddCtx->cfg_ini->probe_req_ie_whitelist = false;
+ }
+ }
+
((VosContextType*)pVosContext)->pHIFContext = hif_sc;
/* store target type and target version info in hdd ctx */
@@ -14789,6 +14913,14 @@
__func__, ret);
}
+ ret = process_wma_set_command(0, WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
+ pHddCtx->cfg_ini->arp_ac_category, PDEV_CMD);
+ if (0 != ret) {
+ hddLog(LOGE,
+ "%s: WMI_PDEV_PARAM_ARP_AC_OVERRIDE failed AC: %d ret: %d",
+ __func__, pHddCtx->cfg_ini->arp_ac_category, ret);
+ }
+
status = hdd_set_sme_chan_list(pHddCtx);
if (status != VOS_STATUS_SUCCESS) {
hddLog(VOS_TRACE_LEVEL_FATAL,
@@ -15130,13 +15262,6 @@
pHddCtx->kd_nl_init = 1;
#endif /* WLAN_KD_READY_NOTIFIER */
- //Initialize the BTC service
- if(btc_activate_service(pHddCtx) != 0)
- {
- hddLog(VOS_TRACE_LEVEL_FATAL,"%s: btc_activate_service failed",__func__);
- goto err_reg_netdev;
- }
-
#ifdef FEATURE_OEM_DATA_SUPPORT
//Initialize the OEM service
if (oem_activate_service(pHddCtx) != 0)
@@ -15324,9 +15449,7 @@
wlan_hdd_cfg80211_extscan_callback);
#endif /* FEATURE_WLAN_EXTSCAN */
sme_set_rssi_threshold_breached_cb(pHddCtx->hHal, hdd_rssi_threshold_breached);
-#ifdef WLAN_FEATURE_LINK_LAYER_STATS
- wlan_hdd_cfg80211_link_layer_stats_init(pHddCtx);
-#endif
+ wlan_hdd_cfg80211_link_layer_stats_init(pHddCtx);
wlan_hdd_tsf_init(pHddCtx);
@@ -15398,6 +15521,22 @@
if (eHAL_STATUS_SUCCESS != hal_status)
hddLog(LOGE, FL("Failed to disable Chan Avoidance Indcation"));
}
+ if (pHddCtx->cfg_ini->enable_5g_band_pref) {
+ band_pref_params.rssi_boost_threshold_5g =
+ pHddCtx->cfg_ini->rssi_boost_threshold_5g;
+ band_pref_params.rssi_boost_factor_5g =
+ pHddCtx->cfg_ini->rssi_boost_factor_5g;
+ band_pref_params.max_rssi_boost_5g =
+ pHddCtx->cfg_ini->max_rssi_boost_5g;
+ band_pref_params.rssi_penalize_threshold_5g =
+ pHddCtx->cfg_ini->rssi_penalize_threshold_5g;
+ band_pref_params.rssi_penalize_factor_5g =
+ pHddCtx->cfg_ini->rssi_penalize_factor_5g;
+ band_pref_params.max_rssi_penalize_5g =
+ pHddCtx->cfg_ini->max_rssi_penalize_5g;
+ sme_set_5g_band_pref(pHddCtx->hHal, &band_pref_params);
+ }
+
wlan_comp.status = 0;
complete(&wlan_comp.wlan_start_comp);
goto success;
@@ -15497,8 +15636,10 @@
err_free_hdd_context:
/* wiphy_free() will free the HDD context so remove global reference */
- if (pVosContext)
+ if (pVosContext) {
+ hdd_free_probe_req_ouis(pHddCtx);
((VosContextType*)(pVosContext))->pHDDContext = NULL;
+ }
wiphy_free(wiphy) ;
//kfree(wdev) ;
@@ -16948,6 +17089,23 @@
}
#endif
+/**
+ * nl_srv_bcast_svc() - Wrapper function to send bcast msgs to SVC mcast group
+ * @skb: sk buffer pointer
+ *
+ * Sends the bcast message to SVC multicast group with generic nl socket
+ * if CNSS_GENL is enabled. Else, use the legacy netlink socket to send.
+ *
+ * Return: None
+ */
+static void nl_srv_bcast_svc(struct sk_buff *skb)
+{
+#ifdef CNSS_GENL
+ nl_srv_bcast(skb, CLD80211_MCGRP_SVC_MSGS, WLAN_NL_MSG_SVC);
+#else
+ nl_srv_bcast(skb);
+#endif
+}
void wlan_hdd_send_svc_nlink_msg(int type, void *data, int len)
{
@@ -17010,7 +17168,7 @@
return;
}
- nl_srv_bcast(skb);
+ nl_srv_bcast_svc(skb);
return;
}
@@ -17248,7 +17406,6 @@
}
#endif
-
#ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH
void wlan_hdd_check_sta_ap_concurrent_ch_intf(void *data)
{
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_nan_datapath.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_nan_datapath.c
index 4360de6..55826d0 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_nan_datapath.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_nan_datapath.c
@@ -42,7 +42,7 @@
[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID] = { .type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR] = { .type = NLA_NUL_STRING,
.len = IFNAMSIZ - 1 },
- [QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID] = { .type = NLA_U16 },
+ [QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID] = { .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL] = { .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR] = {
.type = NLA_BINARY,
@@ -50,14 +50,23 @@
[QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY] = { .type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS] = { .type = NLA_BINARY,
.len = NDP_QOS_INFO_LEN },
- [QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO_LEN] = { .type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO] = { .type = NLA_BINARY,
.len = NDP_APP_INFO_LEN },
[QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID] = { .type = NLA_U32 },
[QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE] = { .type = NLA_U16 },
- [QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_STATUS_CODE] = { .type = NLA_U16 },
[QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR] = { .type = NLA_BINARY,
.len = VOS_MAC_ADDR_SIZE },
+ [QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY] = { .type = NLA_BINARY,
+ .len = NDP_NUM_INSTANCE_ID },
+ [QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE] = { .type =
+ NLA_U32 },
+ [QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE] = { .type = NLA_U32 },
+ [QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG] = { .type = NLA_U32 },
+ [QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE] = { .type = NLA_U32 },
+ [QCA_WLAN_VENDOR_ATTR_NDP_PMK] = { .type = NLA_BINARY,
+ .len = NDP_PMK_LEN },
+ [QCA_WLAN_VENDOR_ATTR_NDP_SCID] = { .type = NLA_BINARY,
+ .len = NDP_SCID_BUF_LEN },
};
/**
@@ -383,6 +392,7 @@
uint16_t transaction_id;
struct nan_datapath_ctx *ndp_ctx;
int ret;
+ hdd_station_ctx_t *sta_ctx;
ENTER();
@@ -422,13 +432,25 @@
return -EINVAL;
}
- /* check if there are active NDP sessions on the adapter */
- if (ndp_ctx->active_ndp_sessions > 0) {
- hddLog(LOGE, FL("NDP sessions active %d, cannot delete NDI"),
- ndp_ctx->active_ndp_sessions);
+ sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+ if (!sta_ctx) {
+ hddLog(LOGE, FL("sta_ctx is NULL"));
return -EINVAL;
}
+ /* check if there are active peers on the adapter */
+ if (ndp_ctx->active_ndp_peers > 0) {
+ hddLog(LOGE, FL("NDP peers active: %d, cannot delete NDI"),
+ ndp_ctx->active_ndp_peers);
+ return -EINVAL;
+ }
+
+ /*
+ * Since, the interface is being deleted, remove the
+ */
+ hdd_ctx->sta_to_adapter[sta_ctx->broadcast_staid] = 0;
+ sta_ctx->broadcast_staid = HDD_WLAN_INVALID_STA_ID;
+
ndp_ctx->ndp_delete_transaction_id = transaction_id;
ndp_ctx->state = NAN_DATA_NDI_DELETING_STATE;
@@ -447,6 +469,18 @@
* @hdd_ctx: hdd context
* @tb: parsed NL attribute list
*
+ * tb will contain following vendor attributes:
+ * QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR
+ * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID
+ * QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL - optional
+ * QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG
+ * QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID
+ * QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR
+ * QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO - optional
+ * QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS - optional
+ * QCA_WLAN_VENDOR_ATTR_NDP_PMK - optional
+ * QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE - optional
+ *
* Return: 0 on success or error code on failure
*/
static int hdd_ndp_initiator_req_handler(hdd_context_t *hdd_ctx,
@@ -502,19 +536,20 @@
req.transaction_id =
nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]);
- if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL]) {
- hddLog(LOGE, FL("NDP channel is unavailable"));
- return -EINVAL;
- }
- req.channel =
- nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL]);
+ if (tb[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL])
+ req.channel =
+ nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL]);
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG])
+ req.channel_cfg =
+ nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG]);
if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID]) {
hddLog(LOGE, FL("NDP service instance ID is unavailable"));
return -EINVAL;
}
req.service_instance_id =
- nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID]);
+ nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID]);
vos_mem_copy(req.self_ndi_mac_addr.bytes,
adapter->macAddressCurrent.bytes, VOS_MAC_ADDR_SIZE);
@@ -527,19 +562,12 @@
nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR]),
VOS_MAC_ADDR_SIZE);
- if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO_LEN]) {
- hddLog(LOGE, FL("NDP app info len is unavailable"));
- return -EINVAL;
+ if (tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]) {
+ req.ndp_info.ndp_app_info_len =
+ nla_len(tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]);
+ req.ndp_info.ndp_app_info =
+ nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]);
}
- req.ndp_info.ndp_app_info_len =
- nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO_LEN]);
-
- if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]) {
- hddLog(LOGE, FL("NDP app info is unavailable"));
- return -EINVAL;
- }
- req.ndp_info.ndp_app_info =
- nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO]);
if (tb[QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS]) {
/* at present ndp config stores 4 bytes QOS info only */
@@ -549,10 +577,31 @@
nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS]);
}
- hddLog(LOG1, FL("vdev_id: %d, transaction_id: %d, channel: %d, service_instance_id: %d, ndp_app_info_len: %d, peer_discovery_mac_addr: %pM"),
- req.vdev_id, req.transaction_id, req.channel,
+ if (tb[QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE] &&
+ !tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]) {
+ hddLog(LOGE, FL("PMK cannot be absent when CSID is present."));
+ return -EINVAL;
+ }
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]) {
+ req.pmk.pmk = nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]);
+ req.pmk.pmk_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]);
+ VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD,
+ VOS_TRACE_LEVEL_DEBUG,
+ req.pmk.pmk, req.pmk.pmk_len);
+ }
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE]) {
+ req.ncs_sk_type =
+ nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE]);
+
+ }
+
+ hddLog(LOG1,
+ FL("vdev_id: %d, transaction_id: %d, channel: %d, channel_cfg: %d, service_instance_id: %d, ndp_app_info_len: %d, csid: %d, peer_discovery_mac_addr: %pM"),
+ req.vdev_id, req.transaction_id, req.channel, req.channel_cfg,
req.service_instance_id, req.ndp_info.ndp_app_info_len,
- req.peer_discovery_mac_addr.bytes);
+ req.ncs_sk_type, req.peer_discovery_mac_addr.bytes);
status = sme_ndp_initiator_req_handler(hal, &req);
if (status != VOS_STATUS_SUCCESS) {
hddLog(LOGE,
@@ -569,6 +618,16 @@
* @hdd_ctx: hdd context
* @tb: parsed NL attribute list
*
+ * tb includes following vendor attributes:
+ * QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR
+ * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID
+ * QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID
+ * QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE
+ * QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO - optional
+ * QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS - optional
+ * QCA_WLAN_VENDOR_ATTR_NDP_PMK - optional
+ * QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE - optional
+ *
* Return: 0 on success or error code on failure
*/
static int hdd_ndp_responder_req_handler(hdd_context_t *hdd_ctx,
@@ -663,10 +722,31 @@
hddLog(LOG1, FL("NDP config data is unavailable"));
}
+ if (tb[QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE] &&
+ !tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]) {
+ hddLog(LOGE, FL("PMK cannot be absent when CSID is present."));
+ return -EINVAL;
+ }
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]) {
+ req.pmk.pmk = nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]);
+ req.pmk.pmk_len = nla_len(tb[QCA_WLAN_VENDOR_ATTR_NDP_PMK]);
+ VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD,
+ VOS_TRACE_LEVEL_DEBUG,
+ req.pmk.pmk, req.pmk.pmk_len);
+ }
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE]) {
+ req.ncs_sk_type =
+ nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE]);
+
+ }
+
hddLog(LOG1,
- FL("vdev_id: %d, transaction_id: %d, ndp_rsp %d, ndp_instance_id: %d, ndp_app_info_len: %d"),
+ FL("vdev_id: %d, transaction_id: %d, ndp_rsp %d, ndp_instance_id: %d, ndp_app_info_len: %d, csid: %d"),
req.vdev_id, req.transaction_id, req.ndp_rsp,
- req.ndp_instance_id, req.ndp_info.ndp_app_info_len);
+ req.ndp_instance_id, req.ndp_info.ndp_app_info_len,
+ req.ncs_sk_type);
status = sme_ndp_responder_req_handler(hdd_ctx->hHal, &req);
if (status != eHAL_STATUS_SUCCESS) {
@@ -687,27 +767,55 @@
*
* Return: 0 on success or error code on failure
*/
-static int hdd_ndp_end_req_handler(hdd_context_t *hdd_ctx,
- struct nlattr **tb)
+static int hdd_ndp_end_req_handler(hdd_context_t *hdd_ctx, struct nlattr **tb)
{
+ struct ndp_end_req req = {0};
+ VOS_STATUS status;
+ tHalHandle hal = hdd_ctx->hHal;
+
+ ENTER();
+
+ /* NAN data path coexists only with STA interface */
+ if (!hdd_is_ndp_allowed(hdd_ctx)) {
+ hddLog(LOGE, FL("Unsupported concurrency for NAN datapath"));
+ return -EINVAL;
+ }
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]) {
+ hddLog(LOGE, FL("Transaction ID is unavailable"));
+ return -EINVAL;
+ }
+ req.transaction_id =
+ nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]);
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY]) {
+ hddLog(LOGE, FL("NDP instance ID array is unavailable"));
+ return -EINVAL;
+ }
+
+ req.num_ndp_instances =
+ nla_len(tb[QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY]) /
+ sizeof(uint32_t);
+ if (0 >= req.num_ndp_instances) {
+ hddLog(LOGE, FL("Num NDP instances is 0"));
+ return -EINVAL;
+ }
+ req.ndp_ids = nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY]);
+
+ hddLog(LOG1, FL("sending ndp_end_req to SME, transaction_id: %d"),
+ req.transaction_id);
+
+ status = sme_ndp_end_req_handler(hal, &req);
+ if (status != VOS_STATUS_SUCCESS) {
+ hddLog(LOGE, FL("sme_ndp_end_req_handler failed, status: %d"),
+ status);
+ return -ECOMM;
+ }
+ EXIT();
return 0;
}
/**
- * hdd_ndp_schedule_req_handler() - NDP schedule request handler
- * @hdd_ctx: hdd context
- * @tb: parsed NL attribute list
- *
- * Return: 0 on success or error code on failure
- */
-static int hdd_ndp_schedule_req_handler(hdd_context_t *hdd_ctx,
- struct nlattr **tb)
-{
- return 0;
-}
-
-
-/**
* hdd_ndp_iface_create_rsp_handler() - NDP iface create response handler
* @adapter: pointer to adapter context
* @rsp_params: response parameters
@@ -715,6 +823,13 @@
* The function is expected to send a response back to the user space
* even if the creation of BSS has failed
*
+ * Following vendor event is sent to cfg80211:
+ * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD =
+ * QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID (2 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE (4 bytes)
+ *
* Return: none
*/
static void hdd_ndp_iface_create_rsp_handler(hdd_adapter_t *adapter,
@@ -723,12 +838,17 @@
struct sk_buff *vendor_event;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct ndi_create_rsp *ndi_rsp = (struct ndi_create_rsp *)rsp_params;
- uint32_t data_len = (2 * sizeof(uint32_t)) + sizeof(uint16_t) +
- NLMSG_HDRLEN + (3 * NLA_HDRLEN);
+ uint32_t data_len = (3 * sizeof(uint32_t)) + sizeof(uint16_t) +
+ NLMSG_HDRLEN + (4 * NLA_HDRLEN);
struct nan_datapath_ctx *ndp_ctx = WLAN_HDD_GET_NDP_CTX_PTR(adapter);
bool create_fail = false;
uint8_t create_transaction_id = 0;
- uint8_t create_status = 0;
+ uint32_t create_status = NDP_RSP_STATUS_ERROR;
+ uint32_t create_reason = NDP_NAN_DATA_IFACE_CREATE_FAILED;
+ hdd_station_ctx_t *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+ v_MACADDR_t bc_mac_addr = VOS_MAC_ADDR_BROADCAST_INITIALIZER;
+ tCsrRoamInfo roam_info = {0};
+ tSirBssDescription tmp_bss_descp = {0};
ENTER();
@@ -738,6 +858,7 @@
if (ndi_rsp) {
create_status = ndi_rsp->status;
+ create_reason = ndi_rsp->reason;
} else {
hddLog(LOGE, FL("Invalid ndi create response"));
create_fail = true;
@@ -750,6 +871,11 @@
create_fail = true;
}
+ if (!sta_ctx) {
+ hddLog(LOGE, FL("ndp_ctx is NULL"));
+ create_fail = true;
+ }
+
/* notify response to the upper layer */
vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
NULL,
@@ -778,7 +904,8 @@
}
/* Status code */
- if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE,
+ if (nla_put_u32(vendor_event,
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
create_status)) {
hddLog(LOGE, FL("VENDOR_ATTR_NDP_DRV_RETURN_TYPE put fail"));
goto nla_put_failure;
@@ -786,7 +913,8 @@
/* Status return value */
if (nla_put_u32(vendor_event,
- QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, 0xA5)) {
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
+ create_reason)) {
hddLog(LOGE, FL("VENDOR_ATTR_NDP_DRV_RETURN_VALUE put fail"));
goto nla_put_failure;
}
@@ -798,9 +926,11 @@
QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID,
create_transaction_id);
hddLog(LOG2, FL("status code: %d, value: %d"),
- QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE, create_status);
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
+ create_status);
hddLog(LOG2, FL("Return value: %d, value: %d"),
- QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, 0xA5);
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
+ create_reason);
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
@@ -814,13 +944,20 @@
} else {
hddLog(LOGE,
FL("NDI interface creation failed with reason %d"),
- ndi_rsp->reason);
+ create_reason);
}
/* Something went wrong while starting the BSS */
if (create_fail)
goto close_ndi;
+ sta_ctx->broadcast_staid = ndi_rsp->sta_id;
+ hdd_save_peer(sta_ctx, sta_ctx->broadcast_staid, &bc_mac_addr);
+ hdd_roamRegisterSTA(adapter, &roam_info,
+ sta_ctx->broadcast_staid,
+ &bc_mac_addr, &tmp_bss_descp);
+ hdd_ctx->sta_to_adapter[sta_ctx->broadcast_staid] = adapter;
+
EXIT();
return;
@@ -843,6 +980,7 @@
{
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct ndi_delete_rsp *ndi_rsp = rsp_params;
+ struct nan_datapath_ctx *ndp_ctx;
if (wlan_hdd_validate_context(hdd_ctx))
return;
@@ -852,13 +990,17 @@
return;
}
- if (ndi_rsp->status == VOS_STATUS_SUCCESS)
+ if (ndi_rsp->status == NDP_RSP_STATUS_SUCCESS)
hddLog(LOGE, FL("NDI BSS successfully stopped"));
else
hddLog(LOGE,
FL("NDI BSS stop failed with reason %d"),
ndi_rsp->reason);
+ ndp_ctx = WLAN_HDD_GET_NDP_CTX_PTR(adapter);
+ ndp_ctx->ndi_delete_rsp_reason = ndi_rsp->reason;
+ ndp_ctx->ndi_delete_rsp_status = ndi_rsp->status;
+
wlan_hdd_netif_queue_control(adapter,
WLAN_STOP_ALL_NETIF_QUEUE_N_CARRIER,
WLAN_CONTROL_PATH);
@@ -870,6 +1012,13 @@
* hdd_ndp_session_end_handler() - NDI session termination handler
* @adapter: pointer to adapter context
*
+ * Following vendor event is sent to cfg80211:
+ * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD =
+ * QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID (2 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE (4 bytes)
+ *
* Return: none
*/
void hdd_ndp_session_end_handler(hdd_adapter_t *adapter)
@@ -877,8 +1026,11 @@
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct sk_buff *vendor_event;
struct nan_datapath_ctx *ndp_ctx;
- uint32_t data_len = sizeof(uint32_t) * 2 + sizeof(uint16_t) +
- NLA_HDRLEN * 3 + NLMSG_HDRLEN;
+ uint32_t data_len = sizeof(uint32_t) * 3 + sizeof(uint16_t) +
+ NLA_HDRLEN * 4 + NLMSG_HDRLEN;
+ VOS_STATUS status;
+ bool can_enter_standby = true;
+ hdd_adapter_list_node_t *adapter_node, *next;
ENTER();
@@ -936,14 +1088,16 @@
/* Status code */
if (nla_put_u32(vendor_event,
- QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE, 0x0)) {
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
+ ndp_ctx->ndi_delete_rsp_status)) {
hddLog(LOGE, FL("VENDOR_ATTR_NDP_DRV_RETURN_TYPE put fail"));
goto failure;
}
/* Status return value */
if (nla_put_u32(vendor_event,
- QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, 0x0)) {
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
+ ndp_ctx->ndi_delete_rsp_reason)) {
hddLog(LOGE, FL("VENDOR_ATTR_NDP_DRV_RETURN_VALUE put fail"));
goto failure;
}
@@ -955,16 +1109,35 @@
QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID,
ndp_ctx->ndp_delete_transaction_id);
hddLog(LOG2, FL("status code: %d, value: %d"),
- QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE,
- true);
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
+ ndp_ctx->ndi_delete_rsp_status);
hddLog(LOG2, FL("Return value: %d, value: %d"),
- QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, 0x5A);
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
+ ndp_ctx->ndi_delete_rsp_reason);
ndp_ctx->ndp_delete_transaction_id = 0;
ndp_ctx->state = NAN_DATA_NDI_DELETED_STATE;
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+ /*
+ * It is possible that deleted NDI was the last active interface.
+ * We should let the device enter lower power mode
+ */
+ status = hdd_get_front_adapter(hdd_ctx, &adapter_node);
+ while ((NULL != adapter_node) && (VOS_STATUS_SUCCESS == status)) {
+ if (test_bit(DEVICE_IFACE_OPENED,
+ &adapter_node->pAdapter->event_flags)) {
+ can_enter_standby = false;
+ break;
+ }
+ status = hdd_get_next_adapter(hdd_ctx, adapter_node, &next);
+ adapter_node = next;
+ }
+
+ if (can_enter_standby)
+ wlan_hdd_stop_enter_lowpower(hdd_ctx);
+
EXIT();
return;
@@ -978,6 +1151,14 @@
* @adapter: pointer to adapter context
* @rsp_params: response parameters
*
+ * Following vendor event is sent to cfg80211:
+ * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD =
+ * QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_RESPONSE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID (2 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE (4 bytes)
+ *
* Return: none
*/
static void hdd_ndp_initiator_rsp_handler(hdd_adapter_t *adapter,
@@ -986,7 +1167,7 @@
struct sk_buff *vendor_event;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct ndp_initiator_rsp *rsp = rsp_params;
- uint32_t data_len = (3 * sizeof(uint32_t)) + (2 * sizeof(uint16_t)) +
+ uint32_t data_len = (4 * sizeof(uint32_t)) + (1 * sizeof(uint16_t)) +
NLMSG_HDRLEN + (5 * NLA_HDRLEN);
ENTER();
@@ -1015,22 +1196,23 @@
rsp->transaction_id))
goto ndp_initiator_rsp_nla_failed;
- if (nla_put_u16(vendor_event,
- QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID,
+ if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID,
rsp->ndp_instance_id))
goto ndp_initiator_rsp_nla_failed;
- if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE,
- rsp->status))
+ if (nla_put_u32(vendor_event,
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
+ rsp->status))
goto ndp_initiator_rsp_nla_failed;
if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
- 0))
+ rsp->reason))
goto ndp_initiator_rsp_nla_failed;
hddLog(LOG1,
- FL("NDP Initiator rsp sent, tid:%d, instance id:%d, status:%d"),
- rsp->transaction_id, rsp->ndp_instance_id, rsp->status);
+ FL("NDP Initiator rsp sent, tid:%d, instance id:%d, status:%d, reason: %d"),
+ rsp->transaction_id, rsp->ndp_instance_id, rsp->status,
+ rsp->reason);
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
EXIT();
return;
@@ -1056,7 +1238,6 @@
tCsrRoamInfo roam_info = {0};
struct nan_datapath_ctx *ndp_ctx = WLAN_HDD_GET_NDP_CTX_PTR(adapter);
hdd_station_ctx_t *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
- v_MACADDR_t bc_mac_addr = VOS_MAC_ADDR_BROADCAST_INITIALIZER;
ENTER();
@@ -1064,6 +1245,9 @@
hddLog(LOGE, FL("Invalid new NDP peer params"));
return;
}
+ hddLog(LOG1, FL("session_id: %d, peer_mac: %pM, sta_id: %d"),
+ new_peer_ind->session_id, new_peer_ind->peer_mac_addr.bytes,
+ new_peer_ind->sta_id);
/* save peer in ndp ctx */
if (false == hdd_save_peer(sta_ctx, new_peer_ind->sta_id,
@@ -1074,16 +1258,13 @@
/* this function is called for each new peer */
ndp_ctx->active_ndp_peers++;
-
+ hddLog(LOG1, FL("vdev_id: %d, num_peers: %d"),
+ adapter->sessionId, ndp_ctx->active_ndp_peers);
hdd_roamRegisterSTA(adapter, &roam_info, new_peer_ind->sta_id,
&new_peer_ind->peer_mac_addr, &tmp_bss_descp);
hdd_ctx->sta_to_adapter[new_peer_ind->sta_id] = adapter;
/* perform following steps for first new peer ind */
if (ndp_ctx->active_ndp_peers == 1) {
- hdd_ctx->sta_to_adapter[NDP_BROADCAST_STAID] = adapter;
- hdd_save_peer(sta_ctx, new_peer_ind->sta_id, &bc_mac_addr);
- hdd_roamRegisterSTA(adapter, &roam_info, NDP_BROADCAST_STAID,
- &bc_mac_addr, &tmp_bss_descp);
hddLog(LOG1, FL("Set ctx connection state to connected"));
sta_ctx->conn_info.connState = eConnectionState_NdiConnected;
hdd_wmm_connect(adapter, &roam_info, eCSR_BSS_TYPE_NDI);
@@ -1091,19 +1272,36 @@
WLAN_WAKE_ALL_NETIF_QUEUE,
WLAN_CONTROL_PATH);
}
+
EXIT();
}
/**
- * hdd_ndp_peer_departed_ind_handler() - NDP peer departed indication handler
+ * hdd_ndp_peer_departed_ind_handler() - Handle NDP peer departed indication
* @adapter: pointer to adapter context
* @ind_params: indication parameters
*
* Return: none
*/
-static void hdd_ndp_peer_departed_ind_handler(
- hdd_adapter_t *adapter, void *ind_params)
+static void hdd_ndp_peer_departed_ind_handler(hdd_adapter_t *adapter,
+ void *ind_params)
{
- return;
+ hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+ struct sme_ndp_peer_ind *peer_ind = ind_params;
+ struct nan_datapath_ctx *ndp_ctx = WLAN_HDD_GET_NDP_CTX_PTR(adapter);
+ hdd_station_ctx_t *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+
+ hdd_roamDeregisterSTA(adapter, peer_ind->sta_id);
+ hdd_delete_peer(sta_ctx, peer_ind->sta_id);
+ hdd_ctx->sta_to_adapter[peer_ind->sta_id] = 0;
+
+ if (--ndp_ctx->active_ndp_peers == 0) {
+ hddLog(LOG1, FL("No more ndp peers."));
+ sta_ctx->conn_info.connState = eConnectionState_NdiDisconnected;
+ hdd_connSetConnectionState(adapter,
+ eConnectionState_NdiDisconnected);
+ hddLog(LOG1, FL("Stop netif tx queues."));
+ netif_tx_stop_all_queues(adapter->dev);
+ }
}
/**
@@ -1111,16 +1309,28 @@
* @adapter: pointer to adapter context
* @ind_params: indication parameters
*
+ * Following vendor event is sent to cfg80211:
+ * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD =
+ * QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR (6 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR (IFNAMSIZ)
+ * QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO (ndp_app_info_len size)
+ * QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_RETURN_VALUE (4 bytes)
+ *
* Return: none
*/
static void hdd_ndp_confirm_ind_handler(hdd_adapter_t *adapter,
void *ind_params)
{
+ int idx;
uint32_t ndp_qos_config = 0;
struct ndp_confirm_event *ndp_confirm = ind_params;
struct sk_buff *vendor_event;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
struct nan_datapath_ctx *ndp_ctx = WLAN_HDD_GET_NDP_CTX_PTR(adapter);
+ hdd_station_ctx_t *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
uint32_t data_len;
ENTER();
@@ -1132,12 +1342,20 @@
if (0 != wlan_hdd_validate_context(hdd_ctx))
return;
- /* ndp_confirm is called each time user generated npd req succeeds */
- ndp_ctx->active_ndp_sessions++;
+ /* ndp_confirm is called each time user generated ndp req succeeds */
+ idx = hdd_get_peer_idx(sta_ctx, &ndp_confirm->peer_ndi_mac_addr);
+ if (idx == INVALID_PEER_IDX)
+ hddLog(LOGE,
+ FL("can't find addr: %pM in vdev_id: %d, peer table."),
+ &ndp_confirm->peer_ndi_mac_addr, adapter->sessionId);
+ else if (ndp_confirm->rsp_code == NDP_RESPONSE_ACCEPT)
+ ndp_ctx->active_ndp_sessions[idx]++;
data_len = (4 * sizeof(uint32_t)) + VOS_MAC_ADDR_SIZE + IFNAMSIZ +
- sizeof(uint16_t) + NLMSG_HDRLEN + (8 * NLA_HDRLEN) +
- ndp_confirm->ndp_info.ndp_app_info_len;
+ NLMSG_HDRLEN + (6 * NLA_HDRLEN);
+
+ if (ndp_confirm->ndp_info.ndp_app_info_len)
+ data_len += NLA_HDRLEN + ndp_confirm->ndp_info.ndp_app_info_len;
vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL,
data_len, QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX,
@@ -1163,35 +1381,28 @@
IFNAMSIZ, adapter->dev->name))
goto ndp_confirm_nla_failed;
- if (nla_put_u16(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO_LEN,
- ndp_confirm->ndp_info.ndp_app_info_len))
- goto ndp_confirm_nla_failed;
-
if (ndp_confirm->ndp_info.ndp_app_info_len && nla_put(vendor_event,
- QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO,
- ndp_confirm->ndp_info.ndp_app_info_len,
- ndp_confirm->ndp_info.ndp_app_info))
+ QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO,
+ ndp_confirm->ndp_info.ndp_app_info_len,
+ ndp_confirm->ndp_info.ndp_app_info))
goto ndp_confirm_nla_failed;
- if (ndp_confirm->ndp_config.ndp_cfg_len) {
- ndp_qos_config = *((uint32_t *)ndp_confirm->ndp_config.ndp_cfg);
- /* at present ndp config stores 4 bytes QOS info only */
- if (nla_put_u32(vendor_event,
- QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS,
- ndp_qos_config))
- goto ndp_confirm_nla_failed;
- }
-
if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE,
ndp_confirm->rsp_code))
goto ndp_confirm_nla_failed;
+ if (nla_put_u32(vendor_event,
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
+ ndp_confirm->reason_code))
+ goto ndp_confirm_nla_failed;
+
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
- hddLog(LOG1, FL("NDP confim sent, ndp instance id: %d, peer addr: %pM, ndp_cfg: %d, rsp_code: %d"),
+ hddLog(LOG1, FL("NDP confim sent, ndp instance id: %d, peer addr: %pM, ndp_cfg: %d, rsp_code: %d, reason_code: %d"),
ndp_confirm->ndp_instance_id,
ndp_confirm->peer_ndi_mac_addr.bytes,
- ndp_qos_config, ndp_confirm->rsp_code);
+ ndp_qos_config, ndp_confirm->rsp_code,
+ ndp_confirm->reason_code);
hddLog(LOG1, FL("NDP confim, ndp app info dump"));
VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
@@ -1212,14 +1423,16 @@
*
* Following vendor event is sent to cfg80211:
* QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD =
- * QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND (4 bytes)
* QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR (IFNAMSIZ)
- * QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID (2 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID (4 bytes)
* QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR (6 bytes)
* QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR (6 bytes)
* QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID (4 bytes)
* QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO (ndp_app_info_len size)
* QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE(4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_SCID(scid_len in size)
*
* Return: none
*/
@@ -1275,10 +1488,9 @@
return;
}
- data_len = 3 * sizeof(uint32_t) + 2 * sizeof(uint16_t) +
- 2 * VOS_MAC_ADDR_SIZE + IFNAMSIZ +
- event->ndp_info.ndp_app_info_len + 9 * NLA_HDRLEN +
- NLMSG_HDRLEN;
+ data_len = (5 * sizeof(uint32_t)) + (2 * VOS_MAC_ADDR_SIZE) + IFNAMSIZ +
+ event->ndp_info.ndp_app_info_len + event->scid.scid_len +
+ (10 * NLA_HDRLEN) + NLMSG_HDRLEN;
/* notify response to the upper layer */
vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
@@ -1298,7 +1510,7 @@
IFNAMSIZ, adapter->dev->name))
goto ndp_indication_nla_failed;
- if (nla_put_u16(vendor_event,
+ if (nla_put_u32(vendor_event,
QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID,
event->service_instance_id))
goto ndp_indication_nla_failed;
@@ -1332,6 +1544,24 @@
goto ndp_indication_nla_failed;
}
+ if (event->scid.scid_len) {
+ if (nla_put_u32(vendor_event,
+ QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE,
+ event->ncs_sk_type))
+ goto ndp_indication_nla_failed;
+
+ if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SCID,
+ event->scid.scid_len,
+ event->scid.scid))
+ goto ndp_indication_nla_failed;
+
+ hddLog(LOG1, FL("csid: %d, scid_len: %d"),
+ event->ncs_sk_type, event->scid.scid_len);
+
+ VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
+ event->scid.scid, event->scid.scid_len);
+ }
+
cfg80211_vendor_event(vendor_event, GFP_KERNEL);
EXIT();
return;
@@ -1348,10 +1578,10 @@
*
* Following vendor event is sent to cfg80211:
* QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD =
- * QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE (4 bytes)
* QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID (2 bytes)
- * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE (4 bytes)
- * QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE (4 bytes)
*
* Return: none
*/
@@ -1397,12 +1627,13 @@
rsp->transaction_id))
goto ndp_responder_rsp_nla_failed;
- if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE,
+ if (nla_put_u32(vendor_event,
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
rsp->status))
goto ndp_responder_rsp_nla_failed;
if (nla_put_u32(vendor_event,
- QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE,
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
rsp->reason))
goto ndp_responder_rsp_nla_failed;
@@ -1420,12 +1651,70 @@
* @adapter: pointer to adapter context
* @rsp_params: response parameters
*
+ * Following vendor event is sent to cfg80211:
+ * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD =
+ * QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID (2 bytes)
+ *
* Return: none
*/
-static void hdd_ndp_end_rsp_handler(hdd_adapter_t *adapter,
- void *rsp_params)
+static void hdd_ndp_end_rsp_handler(hdd_adapter_t *adapter, void *rsp_params)
{
+ struct sk_buff *vendor_event;
+ hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+ struct ndp_end_rsp_event *rsp = rsp_params;
+ uint32_t data_len;
+
+ ENTER();
+
+ if (!rsp) {
+ hddLog(LOGE, FL("Invalid ndp end response"));
+ return;
+ }
+
+ if (0 != wlan_hdd_validate_context(hdd_ctx))
+ return;
+
+ data_len = NLMSG_HDRLEN + (4 * NLA_HDRLEN) + (3 * sizeof(uint32_t)) +
+ sizeof(uint16_t);
+
+ vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL,
+ data_len, QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX,
+ GFP_KERNEL);
+ if (!vendor_event) {
+ hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
+ return;
+ }
+
+ if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD,
+ QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE))
+ goto ndp_end_rsp_nla_failed;
+
+ if (nla_put_u32(vendor_event,
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
+ rsp->status))
+ goto ndp_end_rsp_nla_failed;
+
+ if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
+ rsp->reason))
+ goto ndp_end_rsp_nla_failed;
+
+ if (nla_put_u16(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID,
+ rsp->transaction_id))
+ goto ndp_end_rsp_nla_failed;
+
+ hddLog(LOG1, FL("NDP End rsp sent, transaction id: %d, status: %d, reason: %d"),
+ rsp->transaction_id, rsp->status, rsp->reason);
+ cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+ EXIT();
return;
+
+ndp_end_rsp_nla_failed:
+ hddLog(LOGE, FL("nla_put api failed"));
+ kfree_skb(vendor_event);
+ EXIT();
}
/**
@@ -1433,25 +1722,96 @@
* @adapter: pointer to adapter context
* @ind_params: indication parameters
*
+ * Following vendor event is sent to cfg80211:
+ * QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD =
+ * QCA_WLAN_VENDOR_ATTR_NDP_END_IND (4 bytes)
+ * QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY (4 * no. of NDP instances)
+ *
* Return: none
*/
static void hdd_ndp_end_ind_handler(hdd_adapter_t *adapter,
void *ind_params)
{
- return;
-}
+ struct sk_buff *vendor_event;
+ hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+ struct ndp_end_indication_event *end_ind = ind_params;
+ uint32_t data_len, i;
+ struct nan_datapath_ctx *ndp_ctx = WLAN_HDD_GET_NDP_CTX_PTR(adapter);
+ hdd_station_ctx_t *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+ uint32_t *ndp_instance_array;
+ hdd_adapter_t *ndi_adapter;
-/**
- * hdd_ndp_schedule_update_rsp_handler() - NDP schedule update response handler
- * @adapter: pointer to adapter context
- * @rsp_params: response parameters
- *
- * Return: none
- */
-static void hdd_ndp_schedule_update_rsp_handler(
- hdd_adapter_t *adapter, void *rsp_params)
-{
+ ENTER();
+
+ if (!end_ind) {
+ hddLog(LOGE, FL("Invalid ndp end indication"));
+ return;
+ }
+
+ if (0 != wlan_hdd_validate_context(hdd_ctx))
+ return;
+
+ ndp_instance_array = vos_mem_malloc(end_ind->num_ndp_ids *
+ sizeof(*ndp_instance_array));
+ if (!ndp_instance_array) {
+ hddLog(LOGE, "Failed to allocate ndp_instance_array");
+ return;
+ }
+ for (i = 0; i < end_ind->num_ndp_ids; i++) {
+ int idx;
+
+ ndp_instance_array[i] = end_ind->ndp_map[i].ndp_instance_id;
+ ndi_adapter = hdd_get_adapter_by_vdev(hdd_ctx,
+ end_ind->ndp_map[i].vdev_id);
+ if (ndi_adapter == NULL) {
+ hddLog(LOGE, FL("Adapter not found for vdev_id: %d"),
+ end_ind->ndp_map[i].vdev_id);
+ continue;
+ }
+ ndp_ctx = WLAN_HDD_GET_NDP_CTX_PTR(ndi_adapter);
+ idx = hdd_get_peer_idx(sta_ctx,
+ &end_ind->ndp_map[i].peer_ndi_mac_addr);
+ if (idx == INVALID_PEER_IDX) {
+ hddLog(LOGE,
+ FL("can't find addr: %pM in sta_ctx."),
+ &end_ind->ndp_map[i].peer_ndi_mac_addr);
+ continue;
+ }
+ /* save the value of active sessions on each peer */
+ ndp_ctx->active_ndp_sessions[idx] =
+ end_ind->ndp_map[i].num_active_ndp_sessions;
+ }
+
+ data_len = NLMSG_HDRLEN + (2 * NLA_HDRLEN) +
+ end_ind->num_ndp_ids * sizeof(*ndp_instance_array);
+
+ vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL,
+ data_len, QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX,
+ GFP_KERNEL);
+ if (!vendor_event) {
+ hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed"));
+ return;
+ }
+
+ if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD,
+ QCA_WLAN_VENDOR_ATTR_NDP_END_IND))
+ goto ndp_end_ind_nla_failed;
+
+ if (nla_put(vendor_event, QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY,
+ end_ind->num_ndp_ids * sizeof(*ndp_instance_array),
+ ndp_instance_array))
+ goto ndp_end_ind_nla_failed;
+
+ cfg80211_vendor_event(vendor_event, GFP_KERNEL);
+ vos_mem_free(ndp_instance_array);
+ EXIT();
return;
+
+ndp_end_ind_nla_failed:
+ hddLog(LOGE, FL("nla_put api failed"));
+ kfree_skb(vendor_event);
+ vos_mem_free(ndp_instance_array);
+ EXIT();
}
/**
@@ -1470,11 +1830,11 @@
{
if (roam_status == eCSR_ROAM_NDP_STATUS_UPDATE) {
switch (roam_result) {
- case eCSR_ROAM_RESULT_NDP_CREATE_RSP:
+ case eCSR_ROAM_RESULT_NDI_CREATE_RSP:
hdd_ndp_iface_create_rsp_handler(adapter,
&roam_info->ndp.ndi_create_params);
break;
- case eCSR_ROAM_RESULT_NDP_DELETE_RSP:
+ case eCSR_ROAM_RESULT_NDI_DELETE_RSP:
hdd_ndp_iface_delete_rsp_handler(adapter,
&roam_info->ndp.ndi_delete_params);
break;
@@ -1494,17 +1854,13 @@
hdd_ndp_indication_handler(adapter,
&roam_info->ndp.ndp_indication_params);
break;
- case eCSR_ROAM_RESULT_NDP_SCHED_UPDATE_RSP:
- hdd_ndp_schedule_update_rsp_handler(adapter,
- &roam_info->ndp.ndp_sched_upd_rsp_params);
- break;
case eCSR_ROAM_RESULT_NDP_RESPONDER_RSP:
hdd_ndp_responder_rsp_handler(adapter,
&roam_info->ndp.ndp_responder_rsp_params);
break;
case eCSR_ROAM_RESULT_NDP_END_RSP:
hdd_ndp_end_rsp_handler(adapter,
- &roam_info->ndp.ndp_end_rsp_params);
+ roam_info->ndp.ndp_end_rsp_params);
break;
case eCSR_ROAM_RESULT_NDP_PEER_DEPARTED_IND:
hdd_ndp_peer_departed_ind_handler(adapter,
@@ -1512,7 +1868,7 @@
break;
case eCSR_ROAM_RESULT_NDP_END_IND:
hdd_ndp_end_ind_handler(adapter,
- &roam_info->ndp.ndp_end_ind_params);
+ roam_info->ndp.ndp_end_ind_params);
break;
default:
hddLog(LOGE,
@@ -1579,14 +1935,15 @@
transaction_id = nla_get_u16(
tb[QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID]);
- if (!tb[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR]) {
- hddLog(LOGE, FL("Interface name string is unavailable"));
- return -EINVAL;
+ if (tb[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR]) {
+ iface_name = nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR]);
+ hddLog(LOG2, FL("Transaction Id: %d NDP Cmd: %d iface_name: %s"),
+ transaction_id, ndp_cmd_type, iface_name);
+ } else {
+ hddLog(LOG2,
+ FL("Transaction Id: %d NDP Cmd: %d iface_name: unspecified"),
+ transaction_id, ndp_cmd_type);
}
- iface_name = nla_data(tb[QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR]);
-
- hddLog(LOG2, FL("Transaction Id: %d NDP Cmd: %d iface_name: %s"),
- transaction_id, ndp_cmd_type, iface_name);
switch (ndp_cmd_type) {
case QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE:
@@ -1604,9 +1961,6 @@
case QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST:
ret_val = hdd_ndp_end_req_handler(hdd_ctx, tb);
break;
- case QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REQUEST:
- ret_val = hdd_ndp_schedule_req_handler(hdd_ctx, tb);
- break;
default:
hddLog(LOGE, FL("Unrecognized NDP vendor cmd %d"),
ndp_cmd_type);
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_nan_datapath.h b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_nan_datapath.h
index 3a36427..3af78bd 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_nan_datapath.h
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_nan_datapath.h
@@ -39,6 +39,9 @@
#define NDP_APP_INFO_LEN 255
#define NDP_QOS_INFO_LEN 255
+#define NDP_PMK_LEN 32
+#define NDP_SCID_BUF_LEN 256
+#define NDP_NUM_INSTANCE_ID 255
#define HDD_MAX_NUM_NDP_STA (HDD_MAX_NUM_IBSS_STA)
@@ -66,16 +69,14 @@
* @QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR: Iface name
* @QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY: Security configuration
* @QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS: Qos configuration
- * @QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO_LEN: Application info length
* @QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO: Application info
* @QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID: NDP instance id
- * @QCA_WLAN_VENDOR_ATTR_NDP_NUM_INSTANCE_ID: Number of NDP instance ids
* @QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY: NDP instance id array
* @QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE: Schedule response
- * @QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_STATUS_CODE: schedule status
* @QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR: NDI mac address
- * @QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE: Driver return status
+ * @QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE: Driver return status
* @QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE: Driver return value
+ * @QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG: Channel config request type
*/
enum qca_wlan_vendor_attr_ndp_params {
QCA_WLAN_VENDOR_ATTR_NDP_PARAM_INVALID = 0,
@@ -85,18 +86,20 @@
QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL,
QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR,
QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR,
+ /* CONFIG_SECURITY is deprecated, use NCS_SK_TYPE/PMK/SCID instead */
QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY,
QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS,
- QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO_LEN,
QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO,
QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID,
- QCA_WLAN_VENDOR_ATTR_NDP_NUM_INSTANCE_ID,
QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY,
QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE,
- QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_STATUS_CODE,
QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR,
- QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_TYPE,
+ QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
+ QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG,
+ QCA_WLAN_VENDOR_ATTR_NDP_NCS_SK_TYPE,
+ QCA_WLAN_VENDOR_ATTR_NDP_PMK,
+ QCA_WLAN_VENDOR_ATTR_NDP_SCID,
QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX =
@@ -130,11 +133,8 @@
* @QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE: NDP responder response
* @QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST: NDP end request
* @QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE: NDP end response
- * @QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REQUEST: NDP update request
- * @QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_RESPONSE: NDP update response
* @QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND: NDP request indication
* @QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND: NDP confirm indication
- * @QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND: NDP sched update indication
* @QCA_WLAN_VENDOR_ATTR_NDP_END_IND: NDP End indication
*/
enum qca_wlan_vendor_attr_ndp_sub_cmd_value {
@@ -147,12 +147,9 @@
QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE = 6,
QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST = 7,
QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE = 8,
- QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REQUEST = 9,
- QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_RESPONSE = 10,
- QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND = 11,
- QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND = 12,
- QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 13,
- QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 14
+ QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND = 9,
+ QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND = 10,
+ QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 11
};
/** enum nan_datapath_state - NAN datapath states
@@ -187,23 +184,24 @@
* @active_ndp_peers: number of active ndp peers
* @ndp_create_transaction_id: transaction id for create req
* @ndp_delete_transaction_id: transaction id for delete req
- * @wext_state: Wext state variable
- * @conn_info: NDP connection info
- * @roam_info: NDP roam info
- * @gtk_offload_req_params: GTK offload request params
* @ndp_key_installed: NDP security key installed
* @ndp_enc_key: NDP encryption key info
* @ndp_debug_state: debug state info
+ * @ndi_delete_rsp_reason: reason code for ndi_delete rsp
+ * @ndi_delete_rsp_status: status for ndi_delete rsp
*/
struct nan_datapath_ctx {
enum nan_datapath_state state;
- uint32_t active_ndp_sessions;
+ /* idx in following array should follow conn_info.peerMacAddress */
+ uint32_t active_ndp_sessions[HDD_MAX_NUM_NDP_STA];
uint32_t active_ndp_peers;
uint16_t ndp_create_transaction_id;
uint16_t ndp_delete_transaction_id;
bool ndp_key_installed;
tCsrRoamSetKey ndp_enc_key;
uint32_t ndp_debug_state;
+ uint32_t ndi_delete_rsp_reason;
+ uint32_t ndi_delete_rsp_status;
};
#ifdef WLAN_FEATURE_NAN_DATAPATH
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_oemdata.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_oemdata.c
index c8b3996..c80a5a1 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_oemdata.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_oemdata.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -48,6 +48,9 @@
#include "vos_utils.h"
#include "wma.h"
#include "wlan_hdd_oemdata.h"
+#ifdef CNSS_GENL
+#include <net/cnss_nl.h>
+#endif
static struct hdd_context_s *pHddCtx;
@@ -178,6 +181,26 @@
return 0;
}
+/**
+ * nl_srv_ucast_oem() - Wrapper function to send ucast msgs to OEM
+ * @skb: sk buffer pointer
+ * @dst_pid: Destination PID
+ * @flag: flags
+ *
+ * Sends the ucast message to OEM with generic nl socket if CNSS_GENL
+ * is enabled. Else, use the legacy netlink socket to send.
+ *
+ * Return: None
+ */
+static void nl_srv_ucast_oem(struct sk_buff *skb, int dst_pid, int flag)
+{
+#ifdef CNSS_GENL
+ nl_srv_ucast(skb, dst_pid, flag, WLAN_NL_MSG_OEM,
+ CLD80211_MCGRP_OEM_MSGS);
+#else
+ nl_srv_ucast(skb, dst_pid, flag);
+#endif
+}
/**---------------------------------------------------------------------------
\brief send_oem_reg_rsp_nlink_msg() - send oem registration response
@@ -267,7 +290,7 @@
"%s: sending App Reg Response length (%d) to process pid (%d)",
__func__, aniHdr->length, pHddCtx->oem_pid);
- (void)nl_srv_ucast(skb, pHddCtx->oem_pid, MSG_DONTWAIT);
+ (void)nl_srv_ucast_oem(skb, pHddCtx->oem_pid, MSG_DONTWAIT);
return;
}
@@ -319,7 +342,7 @@
"%s: sending oem error response to process pid (%d)",
__func__, app_pid);
- (void)nl_srv_ucast(skb, app_pid, MSG_DONTWAIT);
+ (void)nl_srv_ucast_oem(skb, app_pid, MSG_DONTWAIT);
return;
}
@@ -387,7 +410,7 @@
"%s: sending Oem Data Response of len (%d) to process pid (%d)",
__func__, length, pHddCtx->oem_pid);
- (void)nl_srv_ucast(skb, pHddCtx->oem_pid, MSG_DONTWAIT);
+ (void)nl_srv_ucast_oem(skb, pHddCtx->oem_pid, MSG_DONTWAIT);
return;
}
@@ -624,7 +647,7 @@
"%s: sending channel info resp for num channels (%d) to pid (%d)",
__func__, numOfChannels, pHddCtx->oem_pid);
- (void)nl_srv_ucast(skb, pHddCtx->oem_pid, MSG_DONTWAIT);
+ (void)nl_srv_ucast_oem(skb, pHddCtx->oem_pid, MSG_DONTWAIT);
return 0;
}
@@ -686,7 +709,7 @@
hddLog(LOG1, FL("sending oem response to process pid %d"), app_pid);
- (void)nl_srv_ucast(skb, app_pid, MSG_DONTWAIT);
+ (void)nl_srv_ucast_oem(skb, app_pid, MSG_DONTWAIT);
return error_code;
}
@@ -752,7 +775,7 @@
hddLog(LOG1, FL("send rsp to oem-pid:%d for get_capability"),
pHddCtx->oem_pid);
- (void)nl_srv_ucast(skb, pHddCtx->oem_pid, MSG_DONTWAIT);
+ (void)nl_srv_ucast_oem(skb, pHddCtx->oem_pid, MSG_DONTWAIT);
return 0;
}
@@ -870,7 +893,7 @@
pPeerInfo->peer_chan_info.reg_info_2,
pPeerInfo->reserved0);
- (void)nl_srv_ucast(skb, pHddCtx->oem_pid, MSG_DONTWAIT);
+ (void)nl_srv_ucast_oem(skb, pHddCtx->oem_pid, MSG_DONTWAIT);
return;
}
@@ -881,18 +904,19 @@
* @hdd_ctx: handle to HDD context
* @msg_hdr: pointer to ANI message header
* @nlh: pointer to NL message header
+ * @pid: Process ID
*
* Return: 0 if success, error code otherwise
*/
static int oem_app_reg_req_handler(struct hdd_context_s *hdd_ctx,
- tAniMsgHdr *msg_hdr, struct nlmsghdr *nlh)
+ tAniMsgHdr *msg_hdr, int pid)
{
char *sign_str = NULL;
/* Registration request is only allowed for Qualcomm Application */
hddLog(LOG1,
FL("Received App Reg Req from App process pid(%d), len(%d)"),
- nlh->nlmsg_pid, msg_hdr->length);
+ pid, msg_hdr->length);
sign_str = (char *)((char *)msg_hdr + sizeof(tAniMsgHdr));
if ((OEM_APP_SIGNATURE_LEN == msg_hdr->length) &&
@@ -900,17 +924,16 @@
OEM_APP_SIGNATURE_LEN))) {
hddLog(LOG1,
FL("Valid App Reg Req from oem app process pid(%d)"),
- nlh->nlmsg_pid);
+ pid);
hdd_ctx->oem_app_registered = TRUE;
- hdd_ctx->oem_pid = nlh->nlmsg_pid;
+ hdd_ctx->oem_pid = pid;
send_oem_reg_rsp_nlink_msg();
} else {
hddLog(LOGE,
FL("Invalid signature in App Reg Req from pid(%d)"),
- nlh->nlmsg_pid);
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
- OEM_ERR_INVALID_SIGNATURE);
+ pid);
+ send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_SIGNATURE);
return -EPERM;
}
@@ -922,30 +945,29 @@
* @hdd_ctx: handle to HDD context
* @msg_hdr: pointer to ANI message header
* @nlh: pointer to NL message header
+ * @pid: Process ID
*
* Return: 0 if success, error code otherwise
*/
static int oem_data_req_handler(struct hdd_context_s *hdd_ctx,
- tAniMsgHdr *msg_hdr, struct nlmsghdr *nlh)
+ tAniMsgHdr *msg_hdr, int pid)
{
hddLog(LOG1, FL("Received Oem Data Request length(%d) from pid: %d"),
- msg_hdr->length, nlh->nlmsg_pid);
+ msg_hdr->length, pid);
if ((!hdd_ctx->oem_app_registered) ||
- (nlh->nlmsg_pid != hdd_ctx->oem_pid)) {
+ (pid != hdd_ctx->oem_pid)) {
/* either oem app is not registered yet or pid is different */
hddLog(LOGE, FL("OEM DataReq: app not registered(%d) or incorrect pid(%d)"),
- hdd_ctx->oem_app_registered, nlh->nlmsg_pid);
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
- OEM_ERR_APP_NOT_REGISTERED);
+ hdd_ctx->oem_app_registered, pid);
+ send_oem_err_rsp_nlink_msg(pid, OEM_ERR_APP_NOT_REGISTERED);
return -EPERM;
}
if ((!msg_hdr->length) || (OEM_DATA_REQ_SIZE < msg_hdr->length)) {
hddLog(LOGE, FL("Invalid length (%d) in Oem Data Request"),
msg_hdr->length);
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
- OEM_ERR_INVALID_MESSAGE_LENGTH);
+ send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_MESSAGE_LENGTH);
return -EPERM;
}
@@ -961,24 +983,24 @@
* @hdd_ctx: handle to HDD context
* @msg_hdr: pointer to ANI message header
* @nlh: pointer to NL message header
+ * @pid: Process ID
*
* Return: 0 if success, error code otherwise
*/
static int oem_chan_info_req_handler(struct hdd_context_s *hdd_ctx,
- tAniMsgHdr *msg_hdr, struct nlmsghdr *nlh)
+ tAniMsgHdr *msg_hdr, int pid)
{
hddLog(LOG1,
FL("Received channel info request, num channel(%d) from pid: %d"),
- msg_hdr->length, nlh->nlmsg_pid);
+ msg_hdr->length, pid);
if ((!hdd_ctx->oem_app_registered) ||
- (nlh->nlmsg_pid != hdd_ctx->oem_pid)) {
+ (pid != hdd_ctx->oem_pid)) {
/* either oem app is not registered yet or pid is different */
hddLog(LOGE,
FL("Chan InfoReq: app not registered(%d) or incorrect pid(%d)"),
- hdd_ctx->oem_app_registered, nlh->nlmsg_pid);
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
- OEM_ERR_APP_NOT_REGISTERED);
+ hdd_ctx->oem_app_registered, pid);
+ send_oem_err_rsp_nlink_msg(pid, OEM_ERR_APP_NOT_REGISTERED);
return -EPERM;
}
@@ -988,8 +1010,7 @@
hddLog(LOGE,
FL("Invalid length (%d) in channel info request"),
msg_hdr->length);
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
- OEM_ERR_INVALID_MESSAGE_LENGTH);
+ send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_MESSAGE_LENGTH);
return -EPERM;
}
oem_process_channel_info_req_msg(msg_hdr->length,
@@ -1003,22 +1024,22 @@
* @hdd_ctx: handle to HDD context
* @msg_hdr: pointer to ANI message header
* @nlh: pointer to NL message header
+ * @pid: Process ID
*
* Return: 0 if success, error code otherwise
*/
static int oem_set_cap_req_handler(struct hdd_context_s *hdd_ctx,
- tAniMsgHdr *msg_hdr, struct nlmsghdr *nlh)
+ tAniMsgHdr *msg_hdr, int pid)
{
hddLog(LOG1, FL("Received set oem cap req of length:%d from pid: %d"),
- msg_hdr->length, nlh->nlmsg_pid);
+ msg_hdr->length, pid);
if ((!hdd_ctx->oem_app_registered) ||
- (nlh->nlmsg_pid != hdd_ctx->oem_pid)) {
+ (pid != hdd_ctx->oem_pid)) {
/* oem app is not registered yet or pid is different */
hddLog(LOGE, FL("set_oem_capability : app not registered(%d) or incorrect pid(%d)"),
- hdd_ctx->oem_app_registered, nlh->nlmsg_pid);
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
- OEM_ERR_APP_NOT_REGISTERED);
+ hdd_ctx->oem_app_registered, pid);
+ send_oem_err_rsp_nlink_msg(pid, OEM_ERR_APP_NOT_REGISTERED);
return -EPERM;
}
@@ -1026,14 +1047,13 @@
(sizeof(struct sme_oem_capability) < msg_hdr->length)) {
hddLog(LOGE, FL("Invalid length (%d) in set_oem_capability"),
msg_hdr->length);
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
- OEM_ERR_INVALID_MESSAGE_LENGTH);
+ send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_MESSAGE_LENGTH);
return -EPERM;
}
oem_process_set_cap_req_msg(msg_hdr->length, (char *)
((char *)msg_hdr + sizeof(tAniMsgHdr)),
- nlh->nlmsg_pid);
+ pid);
return 0;
}
@@ -1042,22 +1062,22 @@
* @hdd_ctx: handle to HDD context
* @msg_hdr: pointer to ANI message header
* @nlh: pointer to NL message header
+ * @pid: Process ID
*
* Return: 0 if success, error code otherwise
*/
static int oem_get_cap_req_handler(struct hdd_context_s *hdd_ctx,
- tAniMsgHdr *msg_hdr, struct nlmsghdr *nlh)
+ tAniMsgHdr *msg_hdr, int pid)
{
hddLog(LOG1, FL("Rcvd get oem capability req - length:%d from pid: %d"),
- msg_hdr->length, nlh->nlmsg_pid);
+ msg_hdr->length, pid);
if ((!hdd_ctx->oem_app_registered) ||
- (nlh->nlmsg_pid != hdd_ctx->oem_pid)) {
+ (pid != hdd_ctx->oem_pid)) {
/* oem app is not registered yet or pid is different */
hddLog(LOGE, FL("get_oem_capability : app not registered(%d) or incorrect pid(%d)"),
- hdd_ctx->oem_app_registered, nlh->nlmsg_pid);
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
- OEM_ERR_APP_NOT_REGISTERED);
+ hdd_ctx->oem_app_registered, pid);
+ send_oem_err_rsp_nlink_msg(pid, OEM_ERR_APP_NOT_REGISTERED);
return -EPERM;
}
@@ -1065,6 +1085,108 @@
return 0;
}
+/**
+ * oem_request_dispatcher() - OEM command dispatcher API
+ * @msg_hdr: ANI Message Header
+ * @pid: process id
+ *
+ * This API is used to dispatch the command from OEM depending
+ * on the type of the message received.
+ *
+ * Return: None
+ */
+static void oem_request_dispatcher(tAniMsgHdr *msg_hdr, int pid)
+{
+ switch (msg_hdr->type) {
+ case ANI_MSG_APP_REG_REQ:
+ oem_app_reg_req_handler(pHddCtx, msg_hdr, pid);
+ break;
+
+ case ANI_MSG_OEM_DATA_REQ:
+ oem_data_req_handler(pHddCtx, msg_hdr, pid);
+ break;
+
+ case ANI_MSG_CHANNEL_INFO_REQ:
+ oem_chan_info_req_handler(pHddCtx, msg_hdr, pid);
+ break;
+
+ case ANI_MSG_SET_OEM_CAP_REQ:
+ oem_set_cap_req_handler(pHddCtx, msg_hdr, pid);
+ break;
+
+ case ANI_MSG_GET_OEM_CAP_REQ:
+ oem_get_cap_req_handler(pHddCtx, msg_hdr, pid);
+ break;
+
+ default:
+ hddLog(LOGE, FL("Received Invalid message type (%d), length (%d)"),
+ msg_hdr->type, msg_hdr->length);
+ send_oem_err_rsp_nlink_msg(pid, OEM_ERR_INVALID_MESSAGE_TYPE);
+ }
+}
+
+#ifdef CNSS_GENL
+/**
+ * oem_cmd_handler() - API to handle OEM commands
+ * @data: Pointer to data
+ * @data_len: length of the received data
+ * @ctx: Pointer to the context
+ * @pid: Process id
+ *
+ * This API handles the command from OEM application from user space and
+ * send back event to user space if necessary.
+ *
+ * Return: None
+ */
+static void oem_cmd_handler(const void *data, int data_len, void *ctx, int pid)
+{
+ tAniMsgHdr *msg_hdr;
+ int ret;
+ struct nlattr *tb[CLD80211_ATTR_MAX + 1];
+
+ ret = wlan_hdd_validate_context(pHddCtx);
+ if (ret) {
+ hddLog(LOGE, FL("hdd ctx validate fails"));
+ return;
+ }
+
+ if (nla_parse(tb, CLD80211_ATTR_MAX, data, data_len, NULL)) {
+ hddLog(LOGE, FL("Invalid ATTR"));
+ return;
+ }
+
+ if (!tb[CLD80211_ATTR_DATA]) {
+ hddLog(LOGE, FL("attr ATTR_DATA failed"));
+ return;
+ }
+
+ msg_hdr = (tAniMsgHdr *)nla_data(tb[CLD80211_ATTR_DATA]);
+ if (!msg_hdr) {
+ hddLog(LOGE, FL("msg_hdr null"));
+ send_oem_err_rsp_nlink_msg(pid, OEM_ERR_NULL_MESSAGE_HEADER);
+ return;
+ }
+ oem_request_dispatcher(msg_hdr, pid);
+
+ return;
+}
+
+/**
+ * oem_activate_service() - API to register the oem command handler
+ * @hdd_ctx: Pointer to HDD Context
+ *
+ * This API is used to register the oem app command handler. Argument
+ * @pAdapter is given for prototype compatibility with legacy code.
+ *
+ * Return: 0
+ */
+int oem_activate_service(void *hdd_ctx)
+{
+ pHddCtx = (struct hdd_context_s *) hdd_ctx;
+ register_cld_cmd_cb(WLAN_NL_MSG_OEM, oem_cmd_handler, NULL);
+ return 0;
+}
+#else
/*
* Callback function invoked by Netlink service for all netlink
* messages (from user space) addressed to WLAN_NL_MSG_OEM
@@ -1082,67 +1204,39 @@
*/
static int oem_msg_callback(struct sk_buff *skb)
{
- struct nlmsghdr *nlh;
- tAniMsgHdr *msg_hdr;
- int ret;
+ struct nlmsghdr *nlh;
+ tAniMsgHdr *msg_hdr;
+ int ret;
- nlh = (struct nlmsghdr *)skb->data;
+ nlh = (struct nlmsghdr *)skb->data;
- if (!nlh) {
- hddLog(LOGE, FL("Netlink header null"));
- return -EPERM;
- }
+ if (!nlh) {
+ hddLog(LOGE, FL("Netlink header null"));
+ return -EPERM;
+ }
- ret = wlan_hdd_validate_context(pHddCtx);
- if (0 != ret)
- return ret;
+ ret = wlan_hdd_validate_context(pHddCtx);
+ if (0 != ret)
+ return ret;
- msg_hdr = NLMSG_DATA(nlh);
+ msg_hdr = NLMSG_DATA(nlh);
+ if (!msg_hdr) {
+ hddLog(LOGE, FL("Message header null"));
+ send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
+ OEM_ERR_NULL_MESSAGE_HEADER);
+ return -EPERM;
+ }
- if (!msg_hdr) {
- hddLog(LOGE, FL("Message header null"));
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid, OEM_ERR_NULL_MESSAGE_HEADER);
- return -EPERM;
- }
+ if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(tAniMsgHdr) + msg_hdr->length)) {
+ hddLog(LOGE, FL("Invalid nl msg len, nlh->nlmsg_len (%d), msg_hdr->len (%d)"),
+ nlh->nlmsg_len, msg_hdr->length);
+ send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
+ OEM_ERR_INVALID_MESSAGE_LENGTH);
+ return -EPERM;
+ }
- if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(tAniMsgHdr) + msg_hdr->length)) {
- hddLog(LOGE, FL("Invalid nl msg len, nlh->nlmsg_len (%d), msg_hdr->len (%d)"),
- nlh->nlmsg_len, msg_hdr->length);
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
- OEM_ERR_INVALID_MESSAGE_LENGTH);
- return -EPERM;
- }
-
- switch (msg_hdr->type) {
- case ANI_MSG_APP_REG_REQ:
- ret = oem_app_reg_req_handler(pHddCtx, msg_hdr, nlh);
- break;
-
- case ANI_MSG_OEM_DATA_REQ:
- ret = oem_data_req_handler(pHddCtx, msg_hdr, nlh);
- break;
-
- case ANI_MSG_CHANNEL_INFO_REQ:
- ret = oem_chan_info_req_handler(pHddCtx, msg_hdr, nlh);
- break;
-
- case ANI_MSG_SET_OEM_CAP_REQ:
- ret = oem_set_cap_req_handler(pHddCtx, msg_hdr, nlh);
- break;
-
- case ANI_MSG_GET_OEM_CAP_REQ:
- ret = oem_get_cap_req_handler(pHddCtx, msg_hdr, nlh);
- break;
-
- default:
- hddLog(LOGE,
- FL("Received Invalid message type (%d), length (%d)"),
- msg_hdr->type, msg_hdr->length);
- send_oem_err_rsp_nlink_msg(nlh->nlmsg_pid,
- OEM_ERR_INVALID_MESSAGE_TYPE);
- return -EPERM;
- }
- return ret;
+ oem_request_dispatcher(msg_hdr, nlh->nlmsg_pid);
+ return ret;
}
static int __oem_msg_callback(struct sk_buff *skb)
@@ -1164,20 +1258,18 @@
an OEM application process.
\param -
- - pAdapter - pointer to HDD adapter
+ - hdd_ctx: Pointer to HDD context
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
-int oem_activate_service(void *pAdapter)
+int oem_activate_service(void *hdd_ctx)
{
- pHddCtx = (struct hdd_context_s*) pAdapter;
+ pHddCtx = (struct hdd_context_s *) hdd_ctx;
/* Register the msg handler for msgs addressed to WLAN_NL_MSG_OEM */
nl_srv_register(WLAN_NL_MSG_OEM, __oem_msg_callback);
return 0;
}
-
-
-
+#endif
#endif
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_p2p.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_p2p.c
index 028dc39..7ce7eec 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_p2p.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_p2p.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -162,6 +162,500 @@
return FALSE;
}
+/**
+ * hdd_random_mac_callback() - Callback invoked from wmi layer
+ * @set_random_addr: Status of random mac filter set operation
+ * @context: Context passed while registring callback
+ *
+ * This function is invoked from wmi layer to give the status of
+ * random mac filter set operation by firmware.
+ *
+ * Return: None
+ */
+static void hdd_random_mac_callback(bool set_random_addr, void *context)
+{
+ struct random_mac_context *rnd_ctx;
+ hdd_adapter_t *adapter;
+
+ if (!context) {
+ hddLog(LOGE, FL("Bad param, pContext"));
+ return;
+ }
+
+ rnd_ctx = context;
+ adapter = rnd_ctx->adapter;
+
+ spin_lock(&hdd_context_lock);
+ if ((!adapter) ||
+ (rnd_ctx->magic != ACTION_FRAME_RANDOM_CONTEXT_MAGIC)) {
+ spin_unlock(&hdd_context_lock);
+ hddLog(VOS_TRACE_LEVEL_WARN,
+ FL("Invalid context, magic [%08x]"), rnd_ctx->magic);
+ return;
+ }
+
+ rnd_ctx->magic = 0;
+ if (set_random_addr)
+ rnd_ctx->set_random_addr = true;
+
+ complete(&rnd_ctx->random_mac_completion);
+ spin_unlock(&hdd_context_lock);
+}
+
+/**
+ * hdd_set_random_mac() - Invoke sme api to set random mac filter
+ * @adapter: Pointer to adapter
+ * @random_mac_addr: Mac addr filter to be set
+ *
+ * Return: If set is successful return true else return false
+ */
+static bool hdd_set_random_mac(hdd_adapter_t *adapter, uint8_t *random_mac_addr)
+{
+ struct random_mac_context context;
+ hdd_context_t *hdd_ctx;
+ eHalStatus sme_status;
+ unsigned long rc;
+ bool status = false;
+
+ ENTER();
+ hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+ if (wlan_hdd_validate_context(hdd_ctx)) {
+ hddLog(LOGE,FL("Invalid hdd ctx"));
+ return false;
+ }
+
+ init_completion(&context.random_mac_completion);
+ context.adapter = adapter;
+ context.magic = ACTION_FRAME_RANDOM_CONTEXT_MAGIC;
+ context.set_random_addr = false;
+
+ sme_status = sme_set_random_mac(hdd_ctx->hHal, hdd_random_mac_callback,
+ adapter->sessionId, random_mac_addr,
+ &context);
+
+ if (sme_status != eHAL_STATUS_SUCCESS) {
+ hddLog(LOGE,FL("Unable to set random mac"));
+ } else {
+ rc = wait_for_completion_timeout(&context.random_mac_completion,
+ msecs_to_jiffies(WLAN_WAIT_TIME_SET_RND));
+ if (!rc) {
+ hddLog(LOGE,
+ FL("SME timed out while setting random mac"));
+ }
+ }
+
+ spin_lock(&hdd_context_lock);
+ context.magic = 0;
+ status = context.set_random_addr;
+ spin_unlock(&hdd_context_lock);
+
+ EXIT();
+ return status;
+}
+
+/**
+ * hdd_clear_random_mac() - Invoke sme api to clear random mac filter
+ * @adapter: Pointer to adapter
+ * @random_mac_addr: Mac addr filter to be cleared
+ *
+ * Return: If clear is successful return true else return false
+ */
+static bool hdd_clear_random_mac(hdd_adapter_t *adapter,
+ uint8_t *random_mac_addr)
+{
+ hdd_context_t *hdd_ctx;
+ eHalStatus status;
+
+ ENTER();
+ hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+ if (wlan_hdd_validate_context(hdd_ctx)) {
+ hddLog(LOGE,FL("Invalid hdd ctx"));
+ return false;
+ }
+
+ status = sme_clear_random_mac(hdd_ctx->hHal, adapter->sessionId,
+ random_mac_addr);
+
+ if (status != eHAL_STATUS_SUCCESS) {
+ hddLog(LOGE,FL("Unable to clear random mac"));
+ return false;
+ }
+
+ EXIT();
+ return true;
+}
+
+bool hdd_check_random_mac(hdd_adapter_t *adapter, uint8_t *random_mac_addr)
+{
+ uint32_t i = 0;
+
+ spin_lock(&adapter->random_mac_lock);
+ for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
+ if ((adapter->random_mac[i].in_use) &&
+ (!memcmp(adapter->random_mac[i].addr, random_mac_addr,
+ VOS_MAC_ADDR_SIZE))) {
+ spin_unlock(&adapter->random_mac_lock);
+ return true;
+ }
+ }
+ spin_unlock(&adapter->random_mac_lock);
+ return false;
+}
+
+/**
+ * find_action_frame_cookie() - Checks for action cookie in cookie list
+ * @cookie_list: List of cookies
+ * @cookie: Cookie to be searched
+ *
+ * Return: If search is successful return pointer to action_frame_cookie
+ * object in which cookie item is encapsulated.
+ */
+static struct action_frame_cookie * find_action_frame_cookie(
+ struct list_head *cookie_list,
+ uint64_t cookie)
+{
+ struct action_frame_cookie *action_cookie = NULL;
+ struct list_head *temp = NULL;
+
+ list_for_each(temp, cookie_list) {
+ action_cookie = list_entry(temp, struct action_frame_cookie,
+ cookie_node);
+ if (action_cookie->cookie == cookie)
+ return action_cookie;
+ }
+
+ return NULL;
+}
+
+/**
+ * allocate_action_frame_cookie() - Allocate and add action cookie to given list
+ * @cookie_list: List of cookies
+ * @cookie: Cookie to be added
+ *
+ * Return: If allocation and addition is successful return pointer to
+ * action_frame_cookie object in which cookie item is encapsulated.
+ */
+static struct action_frame_cookie * allocate_action_frame_cookie(
+ struct list_head *cookie_list,
+ uint64_t cookie)
+{
+ struct action_frame_cookie *action_cookie = NULL;
+
+ action_cookie = vos_mem_malloc(sizeof(*action_cookie));
+ if(!action_cookie)
+ return NULL;
+
+ action_cookie->cookie = cookie;
+ list_add(&action_cookie->cookie_node, cookie_list);
+
+ return action_cookie;
+}
+
+/**
+ * delete_action_frame_cookie() - Delete the cookie from given list
+ * @cookie_list: List of cookies
+ * @cookie: Cookie to be deleted
+ *
+ * This function deletes the cookie item from given list and corresponding
+ * object in which it is encapsulated.
+ *
+ * Return: None
+ */
+static void delete_action_frame_cookie(
+ struct action_frame_cookie *action_cookie)
+{
+ list_del(&action_cookie->cookie_node);
+ vos_mem_free(action_cookie);
+}
+
+/**
+ * append_action_frame_cookie() - Append action cookie to given list
+ * @cookie_list: List of cookies
+ * @cookie: Cookie to be append
+ *
+ * This is a wrapper function which invokes allocate_action_frame_cookie
+ * if the cookie to be added is not duplicate
+ *
+ * Return: 0 - for successfull case
+ * -EALREADY - if cookie is duplicate
+ * -ENOMEM - if allocation is failed
+ */
+static int32_t append_action_frame_cookie(struct list_head *cookie_list,
+ uint64_t cookie)
+{
+ struct action_frame_cookie *action_cookie = NULL;
+
+ /*
+ * There should be no mac entry with empty cookie list,
+ * check and ignore if duplicate
+ */
+ action_cookie = find_action_frame_cookie(cookie_list, cookie);
+ if (action_cookie)
+ /* random mac address is already programmed */
+ return -EALREADY;
+
+ /* insert new cookie in cookie list */
+ action_cookie = allocate_action_frame_cookie(cookie_list, cookie);
+ if (!action_cookie)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * hdd_set_action_frame_random_mac() - Store action frame cookie
+ * @adapter: Pointer to adapter
+ * @random_mac_addr: Mac addr for cookie
+ * @cookie: Cookie to be stored
+ *
+ * This function is used to create cookie list and append the cookies
+ * to same for corresponding random mac addr. If this cookie is the first
+ * item in the list then random mac filter is set.
+ *
+ * Return: 0 - for success else negative value
+ */
+static int32_t hdd_set_action_frame_random_mac(hdd_adapter_t *adapter,
+ uint8_t *random_mac_addr,
+ uint64_t cookie)
+{
+ uint32_t i = 0;
+ uint32_t in_use_cnt = 0;
+ struct action_frame_cookie *action_cookie = NULL;
+ int32_t append_ret = 0;
+
+ if (!cookie) {
+ hddLog(LOGE, FL("Invalid cookie"));
+ return -EINVAL;
+ }
+
+ hddLog(LOG1, FL("mac_addr: " MAC_ADDRESS_STR " && cookie = %llu"),
+ MAC_ADDR_ARRAY(random_mac_addr), cookie);
+
+ spin_lock(&adapter->random_mac_lock);
+ for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
+ if (adapter->random_mac[i].in_use) {
+ in_use_cnt++;
+ if (!memcmp(adapter->random_mac[i].addr,
+ random_mac_addr, VOS_MAC_ADDR_SIZE))
+ break;
+ }
+ }
+
+ if (i != MAX_RANDOM_MAC_ADDRS) {
+ append_ret = append_action_frame_cookie(
+ &adapter->random_mac[i].cookie_list,
+ cookie);
+ spin_unlock(&adapter->random_mac_lock);
+
+ if(append_ret == -ENOMEM) {
+ hddLog(LOGE, FL("No Sufficient memory for cookie"));
+ return append_ret;
+ }
+
+ return 0;
+ }
+
+ if (in_use_cnt == MAX_RANDOM_MAC_ADDRS) {
+ spin_unlock(&adapter->random_mac_lock);
+ hddLog(LOGE, FL("Reached the limit of Max random addresses"));
+ return -EBUSY;
+ }
+
+ /* get the first unused buf and store new random mac */
+ for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
+ if (!adapter->random_mac[i].in_use)
+ break;
+ }
+
+ INIT_LIST_HEAD(&adapter->random_mac[i].cookie_list);
+ action_cookie = allocate_action_frame_cookie(&adapter->random_mac[i].cookie_list,
+ cookie);
+ if(!action_cookie) {
+ spin_unlock(&adapter->random_mac_lock);
+ hddLog(LOGE, FL("No Sufficient memory for cookie"));
+ return -ENOMEM;
+ }
+ vos_mem_copy(adapter->random_mac[i].addr, random_mac_addr,
+ VOS_MAC_ADDR_SIZE);
+ adapter->random_mac[i].in_use = true;
+ spin_unlock(&adapter->random_mac_lock);
+ /* Program random mac_addr */
+ if (!hdd_set_random_mac(adapter, adapter->random_mac[i].addr)) {
+ spin_lock(&adapter->random_mac_lock);
+ /* clear the cookie */
+ delete_action_frame_cookie(action_cookie);
+ adapter->random_mac[i].in_use = false;
+ spin_unlock(&adapter->random_mac_lock);
+ hddLog(LOGE, FL("random mac filter set failed for: "
+ MAC_ADDRESS_STR),
+ MAC_ADDR_ARRAY(adapter->random_mac[i].addr));
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/**
+ * hdd_reset_action_frame_random_mac() - Delete action frame cookie with
+ * given random mac addr
+ * @adapter: Pointer to adapter
+ * @random_mac_addr: Mac addr for cookie
+ * @cookie: Cookie to be deleted
+ *
+ * This function is used to delete the cookie from the cookie list corresponding
+ * to given random mac addr.If cookie list is empty after deleting,
+ * it will clear random mac filter.
+ *
+ * Return: 0 - for success else negative value
+ */
+static int32_t hdd_reset_action_frame_random_mac(hdd_adapter_t *adapter,
+ uint8_t *random_mac_addr,
+ uint64_t cookie)
+{
+ uint32_t i = 0;
+ struct action_frame_cookie *action_cookie = NULL;
+
+ if (!cookie) {
+ hddLog(LOGE, FL("Invalid cookie"));
+ return -EINVAL;
+ }
+
+ hddLog(LOG1, FL("mac_addr: " MAC_ADDRESS_STR " && cookie = %llu"),
+ MAC_ADDR_ARRAY(random_mac_addr), cookie);
+
+ spin_lock(&adapter->random_mac_lock);
+ for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
+ if ((adapter->random_mac[i].in_use) &&
+ (!memcmp(adapter->random_mac[i].addr,
+ random_mac_addr, VOS_MAC_ADDR_SIZE)))
+ break;
+ }
+
+ if (i == MAX_RANDOM_MAC_ADDRS) {
+ spin_unlock(&adapter->random_mac_lock);
+ hddLog(LOGE, FL("trying to delete cookie of random mac-addr"
+ " for which entry is not present"));
+ return -EINVAL;
+ }
+
+ action_cookie = find_action_frame_cookie(&adapter->random_mac[i].cookie_list,
+ cookie);
+
+ if (!action_cookie) {
+ spin_unlock(&adapter->random_mac_lock);
+ hddLog(LOG1, FL("No cookie matches"));
+ return 0;
+ }
+
+ delete_action_frame_cookie(action_cookie);
+ if (list_empty(&adapter->random_mac[i].cookie_list)) {
+ adapter->random_mac[i].in_use = false;
+ spin_unlock(&adapter->random_mac_lock);
+ hdd_clear_random_mac(adapter, random_mac_addr);
+ hddLog(LOG1, FL("Deleted random mac_addr:"
+ MAC_ADDRESS_STR),
+ MAC_ADDR_ARRAY(random_mac_addr));
+ return 0;
+ }
+
+ spin_unlock(&adapter->random_mac_lock);
+ return 0;
+}
+
+/**
+ * hdd_delete_action_frame_cookie() - Delete action frame cookie
+ * @adapter: Pointer to adapter
+ * @cookie: Cookie to be deleted
+ *
+ * This function parses the cookie list of each random mac addr until the
+ * specified cookie is found and then deletes it. If cookie list is empty
+ * after deleting, it will clear random mac filter.
+ *
+ * Return: 0 - for success else negative value
+ */
+static int32_t hdd_delete_action_frame_cookie(hdd_adapter_t *adapter,
+ uint64_t cookie)
+{
+ uint32_t i = 0;
+ struct action_frame_cookie *action_cookie = NULL;
+
+ hddLog(LOG1, FL("Delete cookie = %llu"), cookie);
+
+ spin_lock(&adapter->random_mac_lock);
+ for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
+ if (!adapter->random_mac[i].in_use)
+ continue;
+
+ action_cookie = find_action_frame_cookie(&adapter->random_mac[i].cookie_list,
+ cookie);
+
+ if (!action_cookie)
+ continue;
+
+ delete_action_frame_cookie(action_cookie);
+
+ if (list_empty(&adapter->random_mac[i].cookie_list)) {
+ adapter->random_mac[i].in_use = false;
+ spin_unlock(&adapter->random_mac_lock);
+ hdd_clear_random_mac(adapter,
+ adapter->random_mac[i].addr);
+ hddLog(LOG1, FL("Deleted random addr "MAC_ADDRESS_STR),
+ MAC_ADDR_ARRAY(adapter->random_mac[i].addr));
+ return 0;
+ }
+ spin_unlock(&adapter->random_mac_lock);
+ return 0;
+ }
+
+ spin_unlock(&adapter->random_mac_lock);
+ hddLog(LOG1, FL("Invalid cookie"));
+ return -EINVAL;
+}
+
+/**
+ * hdd_delete_all_action_frame_cookies() - Delete all action frame cookies
+ * @adapter: Pointer to adapter
+ *
+ * This function deletes all the cookie lists of each random mac addr and clears
+ * the corresponding random mac filters.
+ *
+ * Return: 0 - for success else negative value
+ */
+static void hdd_delete_all_action_frame_cookies(hdd_adapter_t *adapter)
+{
+ uint32_t i = 0;
+ struct action_frame_cookie *action_cookie = NULL;
+ struct list_head *n;
+ struct list_head *temp;
+
+ spin_lock(&adapter->random_mac_lock);
+
+ for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) {
+
+ if (!adapter->random_mac[i].in_use)
+ continue;
+
+ /* empty the list and clear random addr */
+ list_for_each_safe(temp, n,
+ &adapter->random_mac[i].cookie_list) {
+ action_cookie = list_entry(temp,
+ struct action_frame_cookie,
+ cookie_node);
+ list_del(temp);
+ vos_mem_free(action_cookie);
+ }
+
+ adapter->random_mac[i].in_use = false;
+ spin_unlock(&adapter->random_mac_lock);
+ hdd_clear_random_mac(adapter, adapter->random_mac[i].addr);
+ hddLog(LOG1, FL("Deleted random addr " MAC_ADDRESS_STR),
+ MAC_ADDR_ARRAY(adapter->random_mac[i].addr));
+ spin_lock(&adapter->random_mac_lock);
+ }
+
+ spin_unlock(&adapter->random_mac_lock);
+}
+
static eHalStatus
wlan_hdd_remain_on_channel_callback(tHalHandle hHal, void* pCtx,
eHalStatus status)
@@ -356,6 +850,7 @@
( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode )
)
{
+ hdd_delete_all_action_frame_cookies(pAdapter);
sme_CancelRemainOnChannel( WLAN_HDD_GET_HAL_CTX( pAdapter ),
pAdapter->sessionId );
}
@@ -459,6 +954,7 @@
} else if (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT ||
pAdapter->device_mode == WLAN_HDD_P2P_DEVICE)
{
+ hdd_delete_all_action_frame_cookies(pAdapter);
sme_CancelRemainOnChannel(WLAN_HDD_GET_HAL_CTX(pAdapter),
pAdapter->sessionId);
}
@@ -539,6 +1035,7 @@
( WLAN_HDD_P2P_DEVICE == pAdapter->device_mode )
)
{
+ hdd_delete_all_action_frame_cookies(pAdapter);
sme_CancelRemainOnChannel( WLAN_HDD_GET_HAL_CTX( pAdapter ),
pAdapter->sessionId );
}
@@ -890,7 +1387,8 @@
if (0 != ret)
return ret;
- if (hdd_isConnectionInProgress((hdd_context_t *)pAdapter->pHddCtx)) {
+ if (hdd_isConnectionInProgress((hdd_context_t *)pAdapter->pHddCtx, NULL,
+ NULL)) {
hddLog(LOGE, FL("Connection is in progress"));
isBusy = VOS_TRUE;
}
@@ -1221,14 +1719,48 @@
*/
mutex_lock(&cfgState->remain_on_chan_ctx_lock);
pRemainChanCtx = cfgState->remain_on_chan_ctx;
- if( (cfgState->remain_on_chan_ctx == NULL) ||
- (cfgState->remain_on_chan_ctx->cookie != cookie) )
- {
- mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
- hddLog( LOGE,
- "%s: No Remain on channel pending with specified cookie value",
- __func__);
- return -EINVAL;
+ if (pRemainChanCtx) {
+ hddLog(LOGE,
+ "action_cookie = %08llx, roc cookie = %08llx, cookie = %08llx",
+ cfgState->action_cookie, pRemainChanCtx->cookie, cookie);
+
+ if (pRemainChanCtx->cookie == cookie) {
+ /* request to cancel on-going roc */
+ if (cfgState->buf) {
+ /* Tx frame pending */
+ if (cfgState->action_cookie != cookie) {
+ hddLog( LOGE,
+ FL("Cookie matched with RoC cookie but not with tx cookie, indicate expired event for roc"));
+ /* RoC was extended to accomodate the tx frame */
+ if (REMAIN_ON_CHANNEL_REQUEST ==
+ pRemainChanCtx->rem_on_chan_request) {
+ cfg80211_remain_on_channel_expired(pRemainChanCtx->dev->
+ ieee80211_ptr,
+ pRemainChanCtx->cookie,
+ &pRemainChanCtx->chan,
+ GFP_KERNEL);
+ }
+ pRemainChanCtx->rem_on_chan_request = OFF_CHANNEL_ACTION_TX;
+ pRemainChanCtx->cookie = cfgState->action_cookie;
+ return 0;
+ }
+ }
+ } else if (cfgState->buf && cfgState->action_cookie == cookie) {
+ mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
+ hddLog( LOGE,
+ FL("Cookie not matched with RoC cookie but matched with tx cookie, cleanup action frame"));
+ /*free the buf and return 0*/
+ hdd_cleanup_actionframe(pHddCtx, pAdapter);
+ return 0;
+ } else {
+ mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
+ hddLog( LOGE, FL("No matching cookie"));
+ return -EINVAL;
+ }
+ } else {
+ mutex_unlock(&cfgState->remain_on_chan_ctx_lock);
+ hddLog( LOGE, FL("RoC context is NULL, return success"));
+ return 0;
}
if (NULL != cfgState->remain_on_chan_ctx)
@@ -1284,6 +1816,7 @@
(WLAN_HDD_P2P_CLIENT == pAdapter->device_mode) ||
(WLAN_HDD_P2P_DEVICE == pAdapter->device_mode)) {
tANI_U8 sessionId = pAdapter->sessionId;
+ hdd_delete_all_action_frame_cookies(pAdapter);
sme_CancelRemainOnChannel( WLAN_HDD_GET_HAL_CTX( pAdapter ),
sessionId );
} else if ((WLAN_HDD_SOFTAP== pAdapter->device_mode) ||
@@ -1378,6 +1911,7 @@
unsigned long rc;
hdd_adapter_t *goAdapter;
uint8_t home_ch = 0;
+ bool enb_random_mac = false;
ENTER();
@@ -1705,6 +2239,19 @@
}
}
+ if (!vos_mem_compare((uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET]),
+ &pAdapter->macAddressCurrent, VOS_MAC_ADDR_SIZE)) {
+ hddLog(LOG1, "%s: sa of action frame is randomized with mac-addr: "
+ MAC_ADDRESS_STR, __func__,
+ MAC_ADDR_ARRAY((uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET])));
+ enb_random_mac = true;
+ }
+
+ if (enb_random_mac && !noack)
+ hdd_set_action_frame_random_mac(pAdapter,
+ (uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET]),
+ *cookie);
+
if (eHAL_STATUS_SUCCESS !=
sme_sendAction( WLAN_HDD_GET_HAL_CTX(pAdapter),
sessionId, buf, len, extendedWait, noack))
@@ -1737,6 +2284,13 @@
err:
if(!noack)
{
+ if (enb_random_mac &&
+ ((pAdapter->device_mode == WLAN_HDD_INFRA_STATION) ||
+ (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) ||
+ (pAdapter->device_mode == WLAN_HDD_P2P_DEVICE)))
+ hdd_reset_action_frame_random_mac(pAdapter,
+ (uint8_t *)(&buf[WLAN_HDD_80211_FRM_SA_OFFSET]),
+ *cookie);
hdd_sendActionCnf( pAdapter, FALSE );
}
return 0;
@@ -1810,6 +2364,31 @@
return ret;
}
+/**
+ * hdd_wlan_delete_mgmt_tx_cookie() - Wrapper to delete action frame cookie
+ * @wdev: Pointer to wireless device
+ * @cookie: Cookie to be deleted
+ *
+ * This is a wrapper function which actually invokes the hdd api to delete
+ * cookie based on the device mode of adapter.
+ *
+ * Return: 0 - for success else negative value
+ */
+static int hdd_wlan_delete_mgmt_tx_cookie(struct wireless_dev *wdev,
+ u64 cookie)
+{
+ struct net_device *dev = wdev->netdev;
+ hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+
+ if ((adapter->device_mode == WLAN_HDD_INFRA_STATION) ||
+ (adapter->device_mode == WLAN_HDD_P2P_CLIENT) ||
+ (adapter->device_mode == WLAN_HDD_P2P_DEVICE)) {
+ hdd_delete_action_frame_cookie(adapter, cookie);
+ }
+
+ return 0;
+}
+
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,6,0)) || defined(WITH_BACKPORTS)
int __wlan_hdd_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev,
@@ -1820,6 +2399,8 @@
return -EINVAL;
}
+ hdd_wlan_delete_mgmt_tx_cookie(wdev, cookie);
+
return wlan_hdd_cfg80211_cancel_remain_on_channel(wiphy, wdev, cookie);
}
#else
@@ -2458,6 +3039,12 @@
&pbFrames[WLAN_HDD_80211_FRM_DA_OFFSET]);
if (NULL == pAdapter)
{
+ pAdapter = hdd_get_adapter_by_rand_macaddr(pHddCtx,
+ &pbFrames[WLAN_HDD_80211_FRM_DA_OFFSET]);
+ }
+
+ if (NULL == pAdapter)
+ {
/* We will receive broadcast management frames in OCB mode */
pAdapter = hdd_get_adapter(pHddCtx, WLAN_HDD_OCB);
if (NULL == pAdapter || !vos_is_macaddr_broadcast(
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_softap_tx_rx.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
index e927073..e932303 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_softap_tx_rx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -270,6 +270,10 @@
while (skb) {
skb_next = skb->next;
+ /* memset skb control block */
+ vos_mem_zero(skb->cb, sizeof(skb->cb));
+ wlan_hdd_classify_pkt(skb);
+
pDestMacAddress = (v_MACADDR_t*)skb->data;
if (vos_is_macaddr_broadcast( pDestMacAddress ) ||
@@ -355,9 +359,6 @@
ac = hdd_QdiscAcToTlAC[skb->queue_mapping];
++pAdapter->hdd_stats.hddTxRxStats.txXmitClassifiedAC[ac];
- adf_dp_trace_log_pkt(pAdapter->sessionId, skb,
- WIFI_EVENT_DRIVER_EAPOL_FRAME_TRANSMIT_REQUESTED);
-
#ifdef QCA_PKT_PROTO_TRACE
if ((hddCtxt->cfg_ini->gEnableDebugLog & VOS_PKT_TRAC_TYPE_EAPOL) ||
(hddCtxt->cfg_ini->gEnableDebugLog & VOS_PKT_TRAC_TYPE_DHCP))
@@ -397,19 +398,22 @@
list_tail->next = skb;
list_tail = list_tail->next;
}
- vos_mem_zero(skb->cb, sizeof(skb->cb));
+
+ adf_dp_trace_log_pkt(pAdapter->sessionId, skb, ADF_TX);
NBUF_SET_PACKET_TRACK(skb, NBUF_TX_PKT_DATA_TRACK);
NBUF_UPDATE_TX_PKT_COUNT(skb, NBUF_TX_PKT_HDD);
- adf_dp_trace_set_track(skb);
- DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_PACKET_PTR_RECORD,
- (uint8_t *)&skb->data, sizeof(skb->data)));
- DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_PACKET_RECORD,
- (uint8_t *)skb->data, skb->len));
- if (skb->len > ADF_DP_TRACE_RECORD_SIZE)
- DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_PACKET_RECORD,
- (uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE],
- (skb->len - ADF_DP_TRACE_RECORD_SIZE)));
+ adf_dp_trace_set_track(skb, ADF_TX);
+ DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_TX_PACKET_PTR_RECORD,
+ (uint8_t *)&skb->data, sizeof(skb->data), ADF_TX));
+ DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_TX_PACKET_RECORD,
+ (uint8_t *)skb->data, adf_nbuf_len(skb), ADF_TX));
+
+ if (adf_nbuf_len(skb) > ADF_DP_TRACE_RECORD_SIZE)
+ DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_TX_PACKET_RECORD,
+ (uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE],
+ (adf_nbuf_len(skb) - ADF_DP_TRACE_RECORD_SIZE),
+ ADF_TX));
skb = skb_next;
continue;
@@ -479,7 +483,7 @@
int i = 0;
DPTRACE(adf_dp_trace(NULL, ADF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT,
- NULL, 0));
+ NULL, 0, ADF_TX));
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
if (hdd_ctx->isLogpInProgress) {
@@ -846,8 +850,21 @@
++pAdapter->stats.rx_packets;
pAdapter->stats.rx_bytes += skb->len;
- adf_dp_trace_log_pkt(pAdapter->sessionId, skb,
- WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED);
+ DPTRACE(adf_dp_trace(skb,
+ ADF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD,
+ adf_nbuf_data_addr(skb),
+ sizeof(adf_nbuf_data(skb)), ADF_RX));
+ DPTRACE(adf_dp_trace(skb,
+ ADF_DP_TRACE_HDD_RX_PACKET_RECORD,
+ (uint8_t *)skb->data, adf_nbuf_len(skb), ADF_RX));
+
+ if (adf_nbuf_len(skb) > ADF_DP_TRACE_RECORD_SIZE)
+ DPTRACE(adf_dp_trace(skb,
+ ADF_DP_TRACE_HDD_RX_PACKET_RECORD,
+ (uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE],
+ (adf_nbuf_len(skb) - ADF_DP_TRACE_RECORD_SIZE),
+ ADF_RX));
+
#ifdef QCA_PKT_PROTO_TRACE
if ((pHddCtx->cfg_ini->gEnableDebugLog & VOS_PKT_TRAC_TYPE_EAPOL) ||
(pHddCtx->cfg_ini->gEnableDebugLog & VOS_PKT_TRAC_TYPE_DHCP)) {
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_trace.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_trace.c
index 37a7af2..2e9b34c 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_trace.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_trace.c
@@ -128,11 +128,11 @@
void hddTraceDump(void *pMac, tpvosTraceRecord pRecord, tANI_U16 recIndex)
{
if (TRACE_CODE_HDD_RX_SME_MSG == pRecord->code)
- hddLog(LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ hddLog(LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session, "RX SME MSG:",
get_eRoamCmdStatus_str(pRecord->data), pRecord->data);
else
- hddLog(LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ hddLog(LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session, "HDD Event:",
hddTraceGetEventString(pRecord->code), pRecord->data);
}
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_tx_rx.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_tx_rx.c
index 507170a..05bf457 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_tx_rx.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_tx_rx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -91,38 +91,6 @@
Function definitions and documentation
-------------------------------------------------------------------------*/
-/**
- * wlan_hdd_is_wai() - Check if frame is EAPOL or WAPI
- * @skb: skb data
- *
- * This function checks if the frame is EAPOL or WAPI.
- * single routine call will check for both types, thus avoiding
- * data path performance penalty.
- *
- * Return: true (1) if packet is EAPOL or WAPI
- *
- */
-static bool wlan_hdd_is_eapol_or_wai(struct sk_buff *skb)
-{
- uint16_t ether_type;
-
- if (!skb) {
- hddLog(VOS_TRACE_LEVEL_ERROR, FL("skb is NULL"));
- return false;
- }
-
- ether_type = (uint16_t)(*(uint16_t *)
- (skb->data + HDD_ETHERTYPE_802_1_X_FRAME_OFFSET));
-
- if (ether_type == VOS_SWAP_U16(HDD_ETHERTYPE_802_1_X) ||
- ether_type == VOS_SWAP_U16(HDD_ETHERTYPE_WAI))
- return true;
-
- /* No error msg handled since this will happen often */
- return false;
-}
-
-
/**============================================================================
@brief hdd_flush_tx_queues() - Utility function to flush the TX queues
@@ -302,11 +270,6 @@
{
vos_timer_stop(&pAdapter->tx_flow_control_timer);
}
- if (adf_os_unlikely(hdd_sta_ctx->hdd_ReassocScenario)) {
- hddLog(LOGW,
- FL("flow control, tx queues un-pause avoided as we are in REASSOCIATING state"));
- return;
- }
hddLog(LOG1, FL("Enabling queues"));
wlan_hdd_netif_queue_control(pAdapter,
WLAN_WAKE_ALL_NETIF_QUEUE,
@@ -358,11 +321,11 @@
void hdd_drop_skb(hdd_adapter_t *adapter, struct sk_buff *skb)
{
DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_DROP_PACKET_RECORD,
- (uint8_t *)skb->data, skb->len));
+ (uint8_t *)skb->data, skb->len, ADF_TX));
if (skb->len > ADF_DP_TRACE_RECORD_SIZE)
DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_DROP_PACKET_RECORD,
(uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE],
- (skb->len - ADF_DP_TRACE_RECORD_SIZE)));
+ (skb->len - ADF_DP_TRACE_RECORD_SIZE), ADF_TX));
++adapter->stats.tx_dropped;
++adapter->hdd_stats.hddTxRxStats.txXmitDropped;
@@ -385,12 +348,12 @@
while (skb) {
DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_DROP_PACKET_RECORD,
- (uint8_t *)skb->data, skb->len));
+ (uint8_t *)skb->data, skb->len, ADF_TX));
if (skb->len > ADF_DP_TRACE_RECORD_SIZE)
DPTRACE(adf_dp_trace(skb,
ADF_DP_TRACE_DROP_PACKET_RECORD,
(uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE],
- (skb->len - ADF_DP_TRACE_RECORD_SIZE)));
+ (skb->len - ADF_DP_TRACE_RECORD_SIZE), ADF_TX));
++adapter->stats.tx_dropped;
++adapter->hdd_stats.hddTxRxStats.txXmitDropped;
@@ -404,6 +367,81 @@
}
}
+/**
+ * hdd_get_transmit_sta_id() - function to retrieve station id to be used for
+ * sending traffic towards a particular destination address. The destination
+ * address can be unicast, multicast or broadcast
+ *
+ * @adapter: Handle to adapter context
+ * @dst_addr: Destination address
+ * @station_id: station id
+ *
+ * Returns: None
+ */
+static void hdd_get_transmit_sta_id(hdd_adapter_t *adapter,
+ v_MACADDR_t *dst_addr, uint8_t *station_id)
+{
+ bool mcbc_addr = false;
+ hdd_station_ctx_t *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+
+ hdd_get_peer_sta_id(sta_ctx, dst_addr, station_id);
+ if (*station_id == HDD_WLAN_INVALID_STA_ID) {
+ if (vos_is_macaddr_broadcast(dst_addr) ||
+ vos_is_macaddr_group(dst_addr)) {
+ hddLog(LOG1,
+ "Received MC/BC packet for transmission");
+ mcbc_addr = true;
+ }
+ }
+
+ if (adapter->device_mode == WLAN_HDD_IBSS) {
+ /*
+ * This check is necessary to make sure station id is not
+ * overwritten for UC traffic in IBSS mode
+ */
+ if (mcbc_addr)
+ *station_id = IBSS_BROADCAST_STAID;
+ } else if (adapter->device_mode == WLAN_HDD_NDI) {
+ /*
+ * This check is necessary to make sure station id is not
+ * overwritten for UC traffic in NAN data mode
+ */
+ if (mcbc_addr)
+ *station_id = sta_ctx->broadcast_staid;
+ } else {
+ /* For the rest, traffic is directed to AP/P2P GO */
+ if (eConnectionState_Associated == sta_ctx->conn_info.connState)
+ *station_id = sta_ctx->conn_info.staId[0];
+ }
+}
+
+/**
+ * wlan_hdd_classify_pkt() - classify skb packet type.
+ * @data: Pointer to skb
+ *
+ * This function classifies skb packet type.
+ *
+ * Return: none
+ */
+void wlan_hdd_classify_pkt(struct sk_buff *skb)
+{
+ /* classify broadcast/multicast packet */
+ if (adf_nbuf_is_bcast_pkt(skb))
+ ADF_NBUF_SET_BCAST(skb);
+ else if (adf_nbuf_is_multicast_pkt(skb))
+ ADF_NBUF_SET_MCAST(skb);
+
+ /* classify eapol/arp/dhcp/wai packet */
+ if (adf_nbuf_is_eapol_pkt(skb))
+ ADF_NBUF_SET_EAPOL(skb);
+ else if (adf_nbuf_is_ipv4_arp_pkt(skb))
+ ADF_NBUF_SET_ARP(skb);
+ else if (adf_nbuf_is_dhcp_pkt(skb))
+ ADF_NBUF_SET_DHCP(skb);
+ else if (adf_nbuf_is_wai_pkt(skb))
+ ADF_NBUF_SET_WAPI(skb);
+}
+
/**============================================================================
@brief hdd_hard_start_xmit() - Function registered with the Linux OS for
transmitting packets. This version of the function directly passes the packet
@@ -427,6 +465,7 @@
struct sk_buff *skb_next, *list_head = NULL, *list_tail = NULL;
void *vdev_handle = NULL, *vdev_temp;
bool is_update_ac_stats = FALSE;
+ v_MACADDR_t *pDestMacAddress = NULL;
#ifdef QCA_PKT_PROTO_TRACE
hdd_context_t *hddCtxt = WLAN_HDD_GET_CTX(pAdapter);
v_U8_t proto_type = 0;
@@ -453,51 +492,17 @@
while (skb) {
skb_next = skb->next;
- if (WLAN_HDD_IBSS == pAdapter->device_mode)
- {
- v_MACADDR_t *pDestMacAddress = (v_MACADDR_t*)skb->data;
+ /* memset skb control block */
+ vos_mem_zero(skb->cb, sizeof(skb->cb));
+ wlan_hdd_classify_pkt(skb);
- if ( VOS_STATUS_SUCCESS !=
- hdd_get_peer_sta_id(&pAdapter->sessionCtx.station,
- pDestMacAddress, &STAId))
- {
- STAId = HDD_WLAN_INVALID_STA_ID;
- }
+ pDestMacAddress = (v_MACADDR_t*)skb->data;
+ STAId = HDD_WLAN_INVALID_STA_ID;
- if ((STAId == HDD_WLAN_INVALID_STA_ID) &&
- (vos_is_macaddr_broadcast( pDestMacAddress ) ||
- vos_is_macaddr_group(pDestMacAddress)))
- {
- STAId = IBSS_BROADCAST_STAID;
- VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO_LOW,
- "%s: BC/MC packet", __func__);
- }
- else if (STAId == HDD_WLAN_INVALID_STA_ID)
- {
- VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,
- "%s: Received Unicast frame with invalid staID", __func__);
- goto drop_pkt;
- }
- } else if (WLAN_HDD_NDI == pAdapter->device_mode) {
- v_MACADDR_t *pDestMacAddress = (v_MACADDR_t *)skb->data;
- if (hdd_get_peer_sta_id(&pAdapter->sessionCtx.station,
- pDestMacAddress, &STAId)
- != VOS_STATUS_SUCCESS) {
- VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_WARN,
- FL("Can't find peer: %pM, dropping packet"),
- pDestMacAddress);
- goto drop_pkt;
- }
- } else {
- if (WLAN_HDD_OCB != pAdapter->device_mode
- && eConnectionState_Associated !=
- pHddStaCtx->conn_info.connState) {
- VOS_TRACE(VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_INFO,
- FL("Tx frame in not associated state in %d context"),
- pAdapter->device_mode);
- goto drop_pkt;
- }
- STAId = pHddStaCtx->conn_info.staId[0];
+ hdd_get_transmit_sta_id(pAdapter, pDestMacAddress, &STAId);
+ if (STAId == HDD_WLAN_INVALID_STA_ID) {
+ hddLog(LOGE, "Invalid station id, transmit operation suspended");
+ goto drop_pkt;
}
vdev_temp = tlshim_peer_validity(
@@ -529,22 +534,25 @@
}
#endif /* QCA_LL_TX_FLOW_CT */
- //Get TL AC corresponding to Qdisc queue index/AC.
+ /* Get TL AC corresponding to Qdisc queue index/AC */
ac = hdd_QdiscAcToTlAC[skb->queue_mapping];
- //user priority from IP header, which is already extracted and set from
- //select_queue call back function
+ /*
+ * user priority from IP header, which is already extracted and set from
+ * select_queue call back function
+ */
up = skb->priority;
++pAdapter->hdd_stats.hddTxRxStats.txXmitClassifiedAC[ac];
#ifdef HDD_WMM_DEBUG
VOS_TRACE( VOS_MODULE_ID_HDD_DATA, VOS_TRACE_LEVEL_FATAL,
"%s: Classified as ac %d up %d", __func__, ac, up);
-#endif // HDD_WMM_DEBUG
+#endif /* HDD_WMM_DEBUG */
if (HDD_PSB_CHANGED == pAdapter->psbChanged)
{
- /* Function which will determine acquire admittance for a
+ /*
+ * Function which will determine acquire admittance for a
* WMM AC is required or not based on psb configuration done
* in the framework
*/
@@ -561,7 +569,7 @@
likely(
pAdapter->hddWmmStatus.wmmAcStatus[ac].wmmAcAccessAllowed)) ||
((pHddStaCtx->conn_info.uIsAuthenticated == VOS_FALSE) &&
- wlan_hdd_is_eapol_or_wai(skb)))
+ (ADF_NBUF_GET_IS_EAPOL(skb) || ADF_NBUF_GET_IS_WAPI(skb))))
{
granted = VOS_TRUE;
}
@@ -573,7 +581,8 @@
if (!granted) {
bool isDefaultAc = VOS_FALSE;
- /* ADDTS request for this AC is sent, for now
+ /*
+ * ADDTS request for this AC is sent, for now
* send this packet through next available lower
* Access category until ADDTS negotiation completes.
*/
@@ -643,21 +652,23 @@
list_tail->next = skb;
list_tail = list_tail->next;
}
- vos_mem_zero(skb->cb, sizeof(skb->cb));
- adf_dp_trace_log_pkt(pAdapter->sessionId, skb,
- WIFI_EVENT_DRIVER_EAPOL_FRAME_TRANSMIT_REQUESTED);
+
+ adf_dp_trace_log_pkt(pAdapter->sessionId, skb, ADF_TX);
NBUF_SET_PACKET_TRACK(skb, NBUF_TX_PKT_DATA_TRACK);
NBUF_UPDATE_TX_PKT_COUNT(skb, NBUF_TX_PKT_HDD);
- adf_dp_trace_set_track(skb);
- DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_PACKET_PTR_RECORD,
- (uint8_t *)&skb->data, sizeof(skb->data)));
- DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_PACKET_RECORD,
- (uint8_t *)skb->data, skb->len));
- if (skb->len > ADF_DP_TRACE_RECORD_SIZE)
- DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_PACKET_RECORD,
- (uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE],
- (skb->len - ADF_DP_TRACE_RECORD_SIZE)));
+ adf_dp_trace_set_track(skb, ADF_TX);
+ DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_TX_PACKET_PTR_RECORD,
+ (uint8_t *)&skb->data, sizeof(skb->data), ADF_TX));
+ DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_TX_PACKET_RECORD,
+ (uint8_t *)skb->data, adf_nbuf_len(skb), ADF_TX));
+
+ if (adf_nbuf_len(skb) > ADF_DP_TRACE_RECORD_SIZE)
+ DPTRACE(adf_dp_trace(skb, ADF_DP_TRACE_HDD_TX_PACKET_RECORD,
+ (uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE],
+ (adf_nbuf_len(skb) - ADF_DP_TRACE_RECORD_SIZE),
+ ADF_TX));
+
skb = skb_next;
continue;
@@ -736,6 +747,29 @@
}
/**
+ * hdd_get_peer_idx() - Get the idx for given address in peer table
+ * @sta_ctx: pointer to HDD Station Context
+ * @addr: pointer to Peer Mac address
+ *
+ * Return: index when success else INVALID_PEER_IDX
+ */
+int hdd_get_peer_idx(hdd_station_ctx_t *sta_ctx, v_MACADDR_t *addr)
+{
+ uint8_t idx;
+
+ for (idx = 0; idx < HDD_MAX_NUM_IBSS_STA; idx++) {
+ if (sta_ctx->conn_info.staId[idx] == 0)
+ continue;
+ if (!vos_mem_compare(&sta_ctx->conn_info.peerMacAddress[idx],
+ addr, sizeof(v_MACADDR_t)))
+ continue;
+ return idx;
+ }
+
+ return INVALID_PEER_IDX;
+}
+
+/**
* wlan_display_tx_timeout_stats() - HDD tx timeout stats display handler
* @adapter: hdd adapter
*
@@ -812,7 +846,7 @@
hddLog(LOGE, FL("Transmission timeout occurred jiffies %lu trans_start %lu"),
jiffies, dev->trans_start);
DPTRACE(adf_dp_trace(NULL, ADF_DP_TRACE_HDD_TX_TIMEOUT,
- NULL, 0));
+ NULL, 0, ADF_TX));
/*
* Getting here implies we disabled the TX queues for too long. Queues are
* disabled either because of disassociation or low resource scenarios. In
@@ -1217,8 +1251,20 @@
continue;
}
- adf_dp_trace_log_pkt(pAdapter->sessionId, skb,
- WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED);
+ DPTRACE(adf_dp_trace(skb,
+ ADF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD,
+ adf_nbuf_data_addr(skb),
+ sizeof(adf_nbuf_data(skb)), ADF_RX));
+ DPTRACE(adf_dp_trace(skb,
+ ADF_DP_TRACE_HDD_RX_PACKET_RECORD,
+ (uint8_t *)skb->data, adf_nbuf_len(skb), ADF_RX));
+
+ if (adf_nbuf_len(skb) > ADF_DP_TRACE_RECORD_SIZE)
+ DPTRACE(adf_dp_trace(skb,
+ ADF_DP_TRACE_HDD_RX_PACKET_RECORD,
+ (uint8_t *)&skb->data[ADF_DP_TRACE_RECORD_SIZE],
+ (adf_nbuf_len(skb) - ADF_DP_TRACE_RECORD_SIZE),
+ ADF_RX));
#ifdef QCA_PKT_PROTO_TRACE
if ((pHddCtx->cfg_ini->gEnableDebugLog & VOS_PKT_TRAC_TYPE_EAPOL) ||
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_wext.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_wext.c
index 3ff758106..56fed65 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_wext.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_wext.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -48,7 +48,6 @@
#include <linux/wireless.h>
#include <macTrace.h>
#include <wlan_hdd_includes.h>
-#include <wlan_btc_svc.h>
#include <wlan_nlink_common.h>
#include <vos_api.h>
#include <net/arp.h>
@@ -458,7 +457,6 @@
#endif
#define WE_SET_MON_MODE_CHAN 3
#define WE_DUMP_DP_TRACE_LEVEL 4
-#define DUMP_DP_TRACE 0
#define WLAN_STATS_INVALID 0
#define WLAN_STATS_RETRY_CNT 1
@@ -9807,55 +9805,59 @@
return 0;
}
+/**
+ * wlan_hdd_set_mc_addr_list() - Set multicast address list
+ * @pAdapter: Adapter context
+ * @set: flag to notify set/clear action on the multicast addr
+ *
+ * Returns: None
+ */
void wlan_hdd_set_mc_addr_list(hdd_adapter_t *pAdapter, v_U8_t set)
{
v_U8_t i;
tpSirRcvFltMcAddrList pMulticastAddrs = NULL;
- tHalHandle hHal = NULL;
+ tHalHandle hHal;
hdd_context_t* pHddCtx = (hdd_context_t*)pAdapter->pHddCtx;
+ hdd_station_ctx_t *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
- if (NULL == pHddCtx)
- {
- hddLog(VOS_TRACE_LEVEL_ERROR, FL("HDD CTX is NULL"));
+ ENTER();
+
+ if (wlan_hdd_validate_context(pHddCtx))
return;
- }
hHal = pHddCtx->hHal;
- if (NULL == hHal)
- {
+ if (NULL == hHal) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("HAL Handle is NULL"));
return;
}
- /* Check if INI is enabled or not, other wise just return
- */
- if (pHddCtx->cfg_ini->fEnableMCAddrList)
- {
+ if (!sta_ctx) {
+ hddLog(LOGE, "sta_ctx is NULL");
+ return;
+ }
+
+ if (pHddCtx->cfg_ini->fEnableMCAddrList) {
pMulticastAddrs = vos_mem_malloc(sizeof(tSirRcvFltMcAddrList));
- if (NULL == pMulticastAddrs)
- {
+ if (NULL == pMulticastAddrs) {
hddLog(VOS_TRACE_LEVEL_ERROR, FL("Could not allocate Memory"));
return;
}
vos_mem_zero(pMulticastAddrs, sizeof(tSirRcvFltMcAddrList));
pMulticastAddrs->action = set;
- if (set)
- {
- /* Following pre-conditions should be satisfied before we
- * configure the MC address list.
- */
- if (((pAdapter->device_mode == WLAN_HDD_INFRA_STATION) ||
- (pAdapter->device_mode == WLAN_HDD_P2P_CLIENT))
- && pAdapter->mc_addr_list.mc_cnt
- && (eConnectionState_Associated ==
- (WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))->conn_info.connState))
- {
+ if (set) {
+ if (pAdapter->mc_addr_list.mc_cnt &&
+ (((pAdapter->device_mode == WLAN_HDD_INFRA_STATION ||
+ pAdapter->device_mode == WLAN_HDD_P2P_CLIENT) &&
+ hdd_connIsConnected(sta_ctx)) ||
+ (WLAN_HDD_IS_NDI(pAdapter) &&
+ WLAN_HDD_IS_NDI_CONNECTED(pAdapter)))) {
+
pMulticastAddrs->ulMulticastAddrCnt =
pAdapter->mc_addr_list.mc_cnt;
- for (i = 0; i < pAdapter->mc_addr_list.mc_cnt; i++)
- {
+
+ for (i = 0; i < pAdapter->mc_addr_list.mc_cnt; i++) {
memcpy(pMulticastAddrs->multicastAddr[i],
&pAdapter->mc_addr_list.addr[i * ETH_ALEN],
ETH_ALEN);
@@ -9868,19 +9870,15 @@
/* Set multicast filter */
sme_8023MulticastList(hHal, pAdapter->sessionId,
pMulticastAddrs);
- }
- else {
+ } else {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("MC address list not sent to FW, cnt: %d"),
pAdapter->mc_addr_list.mc_cnt);
}
- }
- else
- {
+ } else {
/* Need to clear only if it was previously configured
*/
- if (pAdapter->mc_addr_list.isFilterApplied)
- {
+ if (pAdapter->mc_addr_list.isFilterApplied) {
pMulticastAddrs->ulMulticastAddrCnt =
pAdapter->mc_addr_list.mc_cnt;
i = 0;
@@ -9907,12 +9905,12 @@
pAdapter->mc_addr_list.isFilterApplied = set ? TRUE : FALSE;
vos_mem_free(pMulticastAddrs);
- }
- else
- {
+ } else {
hddLog(VOS_TRACE_LEVEL_INFO,
FL("gMCAddrListEnable is not enabled in INI"));
}
+
+ EXIT();
return;
}
@@ -10936,6 +10934,10 @@
value[1], value[2]);
if (value[1] == DUMP_DP_TRACE)
adf_dp_trace_dump_all(value[2]);
+ else if (value[1] == ENABLE_DP_TRACE_LIVE_MODE)
+ adf_dp_trace_enable_live_mode();
+ else if (value[1] == CLEAR_DP_TRACE_BUFFER)
+ adf_dp_trace_clear_buffer();
else
hddLog(LOGE, "unexpected value for dump_dp_trace");
break;
diff --git a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_wmm.c b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_wmm.c
index ff92971..5e286cbe 100644
--- a/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_wmm.c
+++ b/drivers/staging/qcacld-2.0/CORE/HDD/src/wlan_hdd_wmm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -1634,23 +1634,6 @@
}
/**============================================================================
- @brief is_dhcp_packet() - Function which will check OS packet for
- DHCP packet
-
- @param skb : [in] pointer to OS packet (sk_buff)
- @return : VOS_TRUE if the OS packet is DHCP packet
- : otherwise VOS_FALSE
- ===========================================================================*/
-v_BOOL_t is_dhcp_packet(struct sk_buff *skb)
-{
- if (*((u16*)((u8*)skb->data+34)) == DHCP_SOURCE_PORT ||
- *((u16*)((u8*)skb->data+34)) == DHCP_DESTINATION_PORT)
- return VOS_TRUE;
-
- return VOS_FALSE;
-}
-
-/**============================================================================
@brief hdd_wmm_classify_pkt() - Function which will classify an OS packet
into a WMM AC based on either 802.1Q or DSCP
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/inc/aniGlobal.h b/drivers/staging/qcacld-2.0/CORE/MAC/inc/aniGlobal.h
index 02731a6..114e720 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/inc/aniGlobal.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/inc/aniGlobal.h
@@ -74,7 +74,6 @@
#include "smeInternal.h"
#include "sapApi.h"
#include "ccmApi.h"
-#include "btcApi.h"
#include "csrInternal.h"
#ifdef FEATURE_OEM_DATA_SUPPORT
@@ -229,6 +228,7 @@
* @WLAN_LOG_REASON_SME_OUT_OF_CMD_BUFL sme out of cmd buffer
* @WLAN_LOG_REASON_NO_SCAN_RESULTS: no scan results to report from HDD
* This enum contains the different reason codes for bug report
+ * @WLAN_LOG_REASON_SCAN_NOT_ALLOWED: scan not allowed due to connection states
*/
enum log_event_host_reason_code {
WLAN_LOG_REASON_CODE_UNUSED,
@@ -249,6 +249,7 @@
WLAN_LOG_REASON_SME_OUT_OF_CMD_BUF,
WLAN_LOG_REASON_NO_SCAN_RESULTS,
WLAN_LOG_REASON_STALE_SESSION_FOUND,
+ WLAN_LOG_REASON_SCAN_NOT_ALLOWED,
};
@@ -1213,7 +1214,6 @@
tOemDataStruct oemData;
#endif
tPmcInfo pmc;
- tSmeBtcInfo btc;
tCcm ccm;
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/inc/qwlan_version.h b/drivers/staging/qcacld-2.0/CORE/MAC/inc/qwlan_version.h
index 350ea13..ef71632 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/inc/qwlan_version.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/inc/qwlan_version.h
@@ -42,9 +42,9 @@
#define QWLAN_VERSION_MINOR 4
#define QWLAN_VERSION_PATCH 25
#define QWLAN_VERSION_EXTRA ""
-#define QWLAN_VERSION_BUILD 29
+#define QWLAN_VERSION_BUILD 41
-#define QWLAN_VERSIONSTR "4.4.25.029"
+#define QWLAN_VERSIONSTR "4.4.25.041"
#define AR6320_REV1_VERSION 0x5000000
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/inc/sirApi.h b/drivers/staging/qcacld-2.0/CORE/MAC/inc/sirApi.h
index 1042f82..aaf37d6 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/inc/sirApi.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/inc/sirApi.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -89,6 +89,7 @@
#define MAXNUM_PERIODIC_TX_PTRNS 6
#define WIFI_SCANNING_MAC_OUI_LENGTH 3
+#define PROBE_REQ_BITMAP_LEN 8
#define MAX_LEN_UDP_RESP_OFFLOAD 128
@@ -691,6 +692,7 @@
tANI_BOOLEAN obssEnabled;
uint8_t sap_dot11mc;
+ bool vendor_vht_for_24ghz_sap;
} tSirSmeStartBssReq, *tpSirSmeStartBssReq;
@@ -704,7 +706,7 @@
//offset of the ieFields from bssId.
tANI_U16 length;
tSirMacAddr bssId;
- v_TIME_t scanSysTimeMsec;
+ v_TIME_t scansystimensec;
tANI_U32 timeStamp[2];
tANI_U16 beaconInterval;
tANI_U16 capabilityInfo;
@@ -931,6 +933,15 @@
tANI_U16 uIEFieldLen;
tANI_U16 uIEFieldOffset;
+ uint32_t enable_scan_randomization;
+ uint8_t mac_addr[VOS_MAC_ADDR_SIZE];
+ uint8_t mac_addr_mask[VOS_MAC_ADDR_SIZE];
+ bool ie_whitelist;
+ uint32_t probe_req_ie_bitmap[PROBE_REQ_BITMAP_LEN];
+ uint32_t num_vendor_oui;
+ uint32_t oui_field_len;
+ uint32_t oui_field_offset;
+
//channelList MUST be the last field of this structure
tSirChannelList channelList;
/*-----------------------------
@@ -949,7 +960,10 @@
----------------------------- <--+
... variable size uIEFiled
up to uIEFieldLen (can be 0)
- -----------------------------*/
+ -----------------------------
+ ... variable size upto num_vendor_oui
+ struct vendor_oui voui;
+ */
} tSirSmeScanReq, *tpSirSmeScanReq;
typedef struct sSirSmeScanAbortReq
@@ -970,8 +984,9 @@
#ifdef FEATURE_OEM_DATA_SUPPORT
#ifndef OEM_DATA_REQ_SIZE
-#define OEM_DATA_REQ_SIZE 280
+#define OEM_DATA_REQ_SIZE 500
#endif
+
#ifndef OEM_DATA_RSP_SIZE
#define OEM_DATA_RSP_SIZE 1724
#endif
@@ -981,7 +996,7 @@
tANI_U16 messageType; /* eWNI_SME_OEM_DATA_REQ */
tANI_U16 messageLen;
tSirMacAddr selfMacAddr;
- uint8_t data_len;
+ uint32_t data_len;
uint8_t *data;
} tSirOemDataReq, *tpSirOemDataReq;
@@ -3704,6 +3719,18 @@
} tSirNetworkType;
/**
+ * struct connected_pno_band_rssi_pref - BSS preference based on band
+ * and RSSI
+ * @band: band preference
+ * @rssi_pref: RSSI preference
+ */
+struct connected_pno_band_rssi_pref
+{
+ tSirRFBand band;
+ int8_t rssi;
+};
+
+/**
* struct sSirPNOScanReq - PNO Scan request structure
* @enable: flag to enable or disable
* @modePNO: PNO Mode
@@ -3722,6 +3749,10 @@
* @p24GProbeTemplate: 2.4G probe template
* @us5GProbeTemplateLen: 5G probe template length
* @p5GProbeTemplate: 5G probe template
+ * @relative_rssi_set: Flag to check whether realtive_rssi is set or not
+ * @relative_rssi: Relative rssi threshold, used for connected pno
+ * @band_rssi_pref: Band and RSSI preference that can be given to one BSS
+ * over the other BSS
*/
typedef struct sSirPNOScanReq {
uint8_t enable;
@@ -3743,6 +3774,19 @@
uint8_t p24GProbeTemplate[SIR_PNO_MAX_PB_REQ_SIZE];
uint16_t us5GProbeTemplateLen;
uint8_t p5GProbeTemplate[SIR_PNO_MAX_PB_REQ_SIZE];
+
+ bool relative_rssi_set;
+ int8_t relative_rssi;
+ struct connected_pno_band_rssi_pref band_rssi_pref;
+
+ /* mac address randomization attributes */
+ uint32_t enable_pno_scan_randomization;
+ uint8_t mac_addr[VOS_MAC_ADDR_SIZE];
+ uint8_t mac_addr_mask[VOS_MAC_ADDR_SIZE];
+ bool ie_whitelist;
+ uint32_t probe_req_ie_bitmap[PROBE_REQ_BITMAP_LEN];
+ uint32_t num_vendor_oui;
+ /* followed by one or more struct vendor_oui */
} tSirPNOScanReq, *tpSirPNOScanReq;
typedef struct sSirSetRSSIFilterReq
@@ -4467,6 +4511,16 @@
tSirP2pScanType p2pScanType;
tANI_U16 uIEFieldLen;
tANI_U16 uIEFieldOffset;
+
+ uint32_t enable_scan_randomization;
+ uint8_t mac_addr[VOS_MAC_ADDR_SIZE];
+ uint8_t mac_addr_mask[VOS_MAC_ADDR_SIZE];
+ bool ie_whitelist;
+ uint32_t probe_req_ie_bitmap[PROBE_REQ_BITMAP_LEN];
+ uint32_t num_vendor_oui;
+ uint32_t oui_field_len;
+ uint32_t oui_field_offset;
+
tSirChannelList channelList;
/*-----------------------------
sSirScanOffloadReq....
@@ -4484,7 +4538,10 @@
----------------------------- <--+
... variable size uIEField
up to uIEFieldLen (can be 0)
- -----------------------------*/
+ -----------------------------
+ ... variable size upto num_vendor_oui
+ struct vendor_oui voui;
+ ------------------------*/
} tSirScanOffloadReq, *tpSirScanOffloadReq;
/**
@@ -5213,6 +5270,7 @@
uint32_t max_number_epno_networks;
uint32_t max_number_epno_networks_by_ssid;
uint32_t max_number_of_white_listed_ssid;
+ uint32_t max_number_of_black_listed_bssid;
};
@@ -5344,6 +5402,7 @@
* @moreData: 0 - for last fragment
* 1 - still more fragment(s) coming
* @ap: bssid info
+ * @bss_description: BSS description
*
* Reported when each probe response is received, if reportEvents
* enabled in tSirWifiScanCmdReqParams
@@ -5353,6 +5412,7 @@
uint32_t requestId;
bool moreData;
tSirWifiScanResult ap;
+ tSirBssDescription bss_description;
} tSirWifiFullScanResultEvent, *tpSirWifiFullScanResultEvent;
/**
@@ -5711,9 +5771,47 @@
tANI_U8 stopReq;
} tSirLLStatsClearReq, *tpSirLLStatsClearReq;
+/**
+ * struct vendor_oui - probe request ie vendor oui information
+ * @oui_type: type of the vendor oui (3 valid octets)
+ * @oui_subtype: subtype of the vendor oui (1 valid octet)
+ */
+struct vendor_oui {
+ uint32_t oui_type;
+ uint32_t oui_subtype;
+};
+
+#ifdef WLAN_POWER_DEBUGFS
+/**
+ * struct power_stats_response - Power stats response
+ * @cumulative_sleep_time_ms: cumulative sleep time in ms
+ * @cumulative_total_on_time_ms: total awake time in ms
+ * @deep_sleep_enter_counter: deep sleep enter counter
+ * @last_deep_sleep_enter_tstamp_ms: last deep sleep enter timestamp
+ * @debug_register_fmt: debug registers format
+ * @num_debug_register: number of debug registers
+ * @debug_registers: Pointer to the debug registers buffer
+ */
+struct power_stats_response {
+ uint32_t cumulative_sleep_time_ms;
+ uint32_t cumulative_total_on_time_ms;
+ uint32_t deep_sleep_enter_counter;
+ uint32_t last_deep_sleep_enter_tstamp_ms;
+ uint32_t debug_register_fmt;
+ uint32_t num_debug_register;
+ uint32_t *debug_registers;
+};
+#endif
+
typedef struct
{
tANI_U8 oui[WIFI_SCANNING_MAC_OUI_LENGTH];
+ uint32_t vdev_id;
+ bool enb_probe_req_sno_randomization;
+ bool ie_whitelist;
+ uint32_t probe_req_ie_bitmap[PROBE_REQ_BITMAP_LEN];
+ uint32_t num_vendor_oui;
+ /* Followed by 0 or more struct vendor_oui */
} tSirScanMacOui, *tpSirScanMacOui;
enum {
@@ -6820,6 +6918,11 @@
* @wow_ipv6_mcast_na_stats: ipv6 multicast na stats
* @wow_icmpv4_count: ipv4 icmp packet count
* @wow_icmpv6_count: ipv6 icmp packet count
+ * @wow_rssi_breach_wake_up_count: rssi breach wakeup count
+ * @wow_low_rssi_wake_up_count: low rssi wakeup count
+ * @wow_gscan_wake_up_count: gscan wakeup count
+ * @wow_pno_complete_wake_up_count: pno complete wakeup count
+ * @wow_pno_match_wake_up_count: pno match wakeup count
*/
struct sir_wake_lock_stats {
uint32_t wow_ucast_wake_up_count;
@@ -6831,6 +6934,11 @@
uint32_t wow_ipv6_mcast_na_stats;
uint32_t wow_icmpv4_count;
uint32_t wow_icmpv6_count;
+ uint32_t wow_rssi_breach_wake_up_count;
+ uint32_t wow_low_rssi_wake_up_count;
+ uint32_t wow_gscan_wake_up_count;
+ uint32_t wow_pno_complete_wake_up_count;
+ uint32_t wow_pno_match_wake_up_count;
};
/**
@@ -7050,6 +7158,79 @@
};
/**
+ * enum ndp_end_type - NDP end type
+ * @NDP_END_TYPE_UNSPECIFIED: type is unspecified
+ * @NDP_END_TYPE_PEER_UNAVAILABLE: type is peer unavailable
+ * @NDP_END_TYPE_OTA_FRAME: type OTA frame
+ *
+ */
+enum ndp_end_type {
+ NDP_END_TYPE_UNSPECIFIED = 0x00,
+ NDP_END_TYPE_PEER_UNAVAILABLE = 0x01,
+ NDP_END_TYPE_OTA_FRAME = 0x02,
+};
+
+/**
+ * enum ndp_end_reason_code - NDP end reason code
+ * @NDP_END_TYPE_UNSPECIFIED: reason is unspecified
+ * @NDP_END_TYPE_PEER_UNAVAILABLE: reason is peer inactivity
+ * @NDP_END_TYPE_OTA_FRAME: reason data end
+ *
+ */
+enum ndp_end_reason_code {
+ NDP_END_REASON_UNSPECIFIED = 0x00,
+ NDP_END_REASON_INACTIVITY = 0x01,
+ NDP_END_REASON_PEER_DATA_END = 0x02,
+};
+
+/**
+ * enum nan_status_type - NDP status type
+ * @NDP_RSP_STATUS_SUCCESS: request was successful
+ * @NDP_RSP_STATUS_ERROR: request failed
+ */
+enum nan_status_type {
+ NDP_RSP_STATUS_SUCCESS = 0x00,
+ NDP_RSP_STATUS_ERROR = 0x01,
+};
+
+/**
+ * enum nan_reason_code - NDP command rsp reason code value
+ * @NDP_UNSUPPORTED_CONCURRENCY: Will be used in unsupported concurrency cases
+ * @NDP_NAN_DATA_IFACE_CREATE_FAILED: ndi create failed
+ * @NDP_NAN_DATA_IFACE_DELETE_FAILED: ndi delete failed
+ * @NDP_DATA_INITIATOR_REQ_FAILED: data initiator request failed
+ * @NDP_DATA_RESPONDER_REQ_FAILED: data responder request failed
+ * @NDP_INVALID_SERVICE_INSTANCE_ID: invalid service instance id
+ * @NDP_INVALID_NDP_INSTANCE_ID: invalid ndp instance id
+ * @NDP_INVALID_RSP_CODE: invalid response code in ndp responder request
+ * @NDP_INVALID_APP_INFO_LEN: invalid app info length
+ * @NDP_NMF_REQ_FAIL: OTA nan mgmt frame failure for data request
+ * @NDP_NMF_RSP_FAIL: OTA nan mgmt frame failure for data response
+ * @NDP_NMF_CNF_FAIL: OTA nan mgmt frame failure for confirm
+ * @NDP_END_FAILED: ndp end failed
+ * @NDP_NMF_END_REQ_FAIL: OTA nan mgmt frame failure for data end
+ * @NDP_VENDOR_SPECIFIC_ERROR: other vendor specific failures
+ */
+enum nan_reason_code {
+ NDP_UNSUPPORTED_CONCURRENCY = 9000,
+ NDP_NAN_DATA_IFACE_CREATE_FAILED = 9001,
+ NDP_NAN_DATA_IFACE_DELETE_FAILED = 9002,
+ NDP_DATA_INITIATOR_REQ_FAILED = 9003,
+ NDP_DATA_RESPONDER_REQ_FAILED = 9004,
+ NDP_INVALID_SERVICE_INSTANCE_ID = 9005,
+ NDP_INVALID_NDP_INSTANCE_ID = 9006,
+ NDP_INVALID_RSP_CODE = 9007,
+ NDP_INVALID_APP_INFO_LEN = 9008,
+ NDP_NMF_REQ_FAIL = 9009,
+ NDP_NMF_RSP_FAIL = 9010,
+ NDP_NMF_CNF_FAIL = 9011,
+ NDP_END_FAILED = 9012,
+ NDP_NMF_END_REQ_FAIL = 9013,
+ /* 9500 onwards vendor specific error codes */
+ NDP_VENDOR_SPECIFIC_ERROR = 9500,
+};
+
+/**
* struct ndp_cfg - ndp configuration
* @tag: unique identifier
* @ndp_cfg_len: ndp configuration length
@@ -7089,6 +7270,28 @@
};
/**
+ * struct ndp_scid - structure to hold sceurity context identifier
+ * @scid_len: length of scid
+ * @scid: scid
+ *
+ */
+struct ndp_scid {
+ uint32_t scid_len;
+ uint8_t *scid;
+};
+
+/**
+ * struct ndp_pmk - structure to hold pairwise master key
+ * @pmk_len: length of pairwise master key
+ * @pmk: buffer containing pairwise master key
+ *
+ */
+struct ndp_pmk {
+ uint32_t pmk_len;
+ uint8_t *pmk;
+};
+
+/**
* struct ndi_create_req - ndi create request params
* @transaction_id: unique identifier
* @iface_name: interface name
@@ -7101,37 +7304,23 @@
/**
* struct ndi_create_rsp - ndi create response params
- * @transaction_id: unique identifier
* @status: request status
* @reason: reason if any
*
*/
struct ndi_create_rsp {
- uint32_t transaction_id;
uint32_t status;
uint32_t reason;
-};
-
-/**
- * struct ndi_delete_req - ndi delete request params
- * @transaction_id: unique identifier
- * @iface_name: interface name
- *
- */
- struct ndi_delete_req {
- uint32_t transaction_id;
- char iface_name[IFACE_NAME_SIZE];
+ uint8_t sta_id;
};
/**
* struct ndi_delete_rsp - ndi delete response params
- * @transaction_id: unique identifier
* @status: request status
* @reason: reason if any
*
*/
struct ndi_delete_rsp {
- uint32_t transaction_id;
uint32_t status;
uint32_t reason;
};
@@ -7141,22 +7330,28 @@
* @transaction_id: unique identifier
* @vdev_id: session id of the interface over which ndp is being created
* @channel: suggested channel for ndp creation
+ * @channel_cfg: channel config, 0=no channel, 1=optional, 2=mandatory
* @service_instance_id: Service identifier
* @peer_discovery_mac_addr: Peer's discovery mac address
* @self_ndi_mac_addr: self NDI mac address
* @ndp_config: ndp configuration params
* @ndp_info: ndp application info
+ * @ncs_sk_type: indicates NCS_SK_128 or NCS_SK_256
+ * @pmk: pairwise master key
*
*/
struct ndp_initiator_req {
uint32_t transaction_id;
uint32_t vdev_id;
uint32_t channel;
+ uint32_t channel_cfg;
uint32_t service_instance_id;
v_MACADDR_t peer_discovery_mac_addr;
v_MACADDR_t self_ndi_mac_addr;
struct ndp_cfg ndp_config;
struct ndp_app_info ndp_info;
+ uint32_t ncs_sk_type;
+ struct ndp_pmk pmk;
};
/**
@@ -7173,6 +7368,7 @@
uint32_t vdev_id;
uint32_t ndp_instance_id;
uint32_t status;
+ uint32_t reason;
};
/**
@@ -7187,6 +7383,8 @@
* @ndp_accept_policy: accept policy configured by the upper layer
* @ndp_config: ndp configuration params
* @ndp_info: ndp application info
+ * @ncs_sk_type: indicates NCS_SK_128 or NCS_SK_256
+ * @scid: security context identifier
*
*/
struct ndp_indication_event {
@@ -7199,6 +7397,8 @@
enum ndp_accept_policy policy;
struct ndp_cfg ndp_config;
struct ndp_app_info ndp_info;
+ uint32_t ncs_sk_type;
+ struct ndp_scid scid;
};
/**
@@ -7209,6 +7409,8 @@
* @ndp_rsp: response to the ndp create request
* @ndp_config: ndp configuration params
* @ndp_info: ndp application info
+ * @pmk: pairwise master key
+ * @ncs_sk_type: indicates NCS_SK_128 or NCS_SK_256
*
*/
struct ndp_responder_req {
@@ -7218,6 +7420,8 @@
enum ndp_response_code ndp_rsp;
struct ndp_cfg ndp_config;
struct ndp_app_info ndp_info;
+ struct ndp_pmk pmk;
+ uint32_t ncs_sk_type;
};
/**
@@ -7235,40 +7439,41 @@
uint32_t status;
uint32_t reason;
v_MACADDR_t peer_mac_addr;
+ bool create_peer;
};
/**
* struct ndp_confirm_event - ndp confirmation event from FW
* @vdev_id: session id of the interface over which ndp is being created
* @ndp_instance_id: ndp instance id for which confirm is being generated
+ * @reason_code : reason code(opaque to driver)
+ * @num_active_ndps_on_peer: number of ndp instances on peer
* @peer_ndi_mac_addr: peer NDI mac address
* @rsp_code: ndp response code
- * @ndp_config: ndp configuration
* @ndp_info: ndp application info
*
*/
struct ndp_confirm_event {
uint32_t vdev_id;
uint32_t ndp_instance_id;
+ uint32_t reason_code;
+ uint32_t num_active_ndps_on_peer;
v_MACADDR_t peer_ndi_mac_addr;
enum ndp_response_code rsp_code;
- struct ndp_cfg ndp_config;
struct ndp_app_info ndp_info;
};
/**
* struct ndp_end_req - ndp end request
* @transaction_id: unique transaction identifier
- * @vdev_id: session id of the interface over which ndp is being created
* @num_ndp_instances: number of ndp instances to be terminated
- * @ndp_instances: list of ndp instances to be terminated
+ * @ndp_ids: pointer to array of ndp_instance_id to be terminated
*
*/
struct ndp_end_req {
uint32_t transaction_id;
- uint32_t vdev_id;
uint32_t num_ndp_instances;
- uint32_t ndp_instances[];
+ uint32_t *ndp_ids;
};
/**
@@ -7276,39 +7481,41 @@
* @vdev_id: session id of the interface over which ndp is being created
* @peer_ndi_mac_addr: peer NDI mac address
* @num_active_ndp_sessions: number of active NDP sessions on the peer
+ * @type: NDP end indication type
+ * @reason_code: NDP end indication reason code
+ * @ndp_instance_id: NDP instance ID
*
*/
struct peer_ndp_map {
uint32_t vdev_id;
v_MACADDR_t peer_ndi_mac_addr;
uint32_t num_active_ndp_sessions;
+ enum ndp_end_type type;
+ enum ndp_end_reason_code reason_code;
+ uint32_t ndp_instance_id;
};
/**
* struct ndp_end_rsp_event - firmware response to ndp end request
* @transaction_id: unique identifier for the request
- * @vdev_id: session id of the interface over which ndp is being created
- * @ndp_map: mapping of NDP instances to peer to VDEV
+ * @status: status of operation
+ * @reason: reason(opaque to host driver)
*
*/
struct ndp_end_rsp_event {
uint32_t transaction_id;
- uint32_t vdev_id;
- struct peer_ndp_map ndp_map[];
+ uint32_t status;
+ uint32_t reason;
};
/**
* struct ndp_end_indication_event - ndp termination notification from FW
- * @vdev_id: session id of the interface over which ndp is being created
- * @reason: reason code for failure if any
- * @status: status of the request
- * @ndp_map: mapping of NDP instances to peer to VDEV
+ * @num_ndp_ids: number of NDP ids
+ * @ndp_map: mapping of NDP instances to peer and vdev
*
*/
struct ndp_end_indication_event {
- uint32_t vdev_id;
- uint32_t status;
- uint32_t reason;
+ uint32_t num_ndp_ids;
struct peer_ndp_map ndp_map[];
};
@@ -7373,4 +7580,32 @@
uint32_t rx_aggregation_size;
};
+/**
+ * enum action_filter_type - Type of action frame filter
+ * @SME_ACTION_FRAME_RANDOM_MAC_SET: Set filter
+ * @SME_ACTION_FRAME_RANDOM_MAC_CLEAR: Clear filter
+ */
+enum action_filter_type {
+ SME_ACTION_FRAME_RANDOM_MAC_SET,
+ SME_ACTION_FRAME_RANDOM_MAC_CLEAR,
+};
+
+typedef void (*action_frame_random_filter_callback)(bool set_random_addr,
+ void *context);
+/**
+ * struct action_frame_random_filter - Random mac filter attrs for set/clear
+ * @session_id: Session interface
+ * @filter_type: Type of filter from action_filter_type
+ * @callback: Invoked from wmi
+ * @context: Parameter to be used with callback
+ * @mac_addr: Random mac addr for which filter is to be set
+ */
+struct action_frame_random_filter {
+ uint32_t session_id;
+ enum action_filter_type filter_type;
+ action_frame_random_filter_callback callback;
+ void *context;
+ uint8_t mac_addr[VOS_MAC_ADDR_SIZE];
+};
+
#endif /* __SIR_API_H */
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/inc/wniApi.h b/drivers/staging/qcacld-2.0/CORE/MAC/inc/wniApi.h
index 5d1f80c..b82d757 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/inc/wniApi.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/inc/wniApi.h
@@ -277,8 +277,6 @@
eWNI_SME_REGISTER_MGMT_FRAME_REQ,
- eWNI_SME_COEX_IND,
-
#ifdef FEATURE_WLAN_SCAN_PNO
eWNI_SME_PREF_NETWORK_FOUND_IND,
#endif // FEATURE_WLAN_SCAN_PNO
@@ -413,6 +411,7 @@
eWNI_SME_NDP_INDICATION,
eWNI_SME_NDP_RESPONDER_REQ,
eWNI_SME_NDP_RESPONDER_RSP,
+ eWNI_SME_NDP_END_REQ,
eWNI_SME_NDP_END_RSP,
eWNI_SME_NDP_PEER_DEPARTED_IND,
eWNI_SME_NDP_END_IND,
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgApi.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgApi.c
index f5760c3..1f3cc58 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgApi.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgApi.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -1079,8 +1079,6 @@
mmhMsg.bodyval = (tANI_U32)cfgId;
mmhMsg.bodyptr = NULL;
- MTRACE(macTraceMsgTx(pMac, NO_SESSION, mmhMsg.type));
-
if ((ntfMask & CFG_CTL_NTF_SCH) != 0)
schPostMessage(pMac, &mmhMsg);
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgUtil/dot11f.frms b/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgUtil/dot11f.frms
index 06d6a8a..93ee08f 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgUtil/dot11f.frms
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/cfg/cfgUtil/dot11f.frms
@@ -2044,10 +2044,6 @@
{
}
-IE Vendor2IE (EID_VENDOR_SPECIFIC) OUI (0x00, 0x90, 0x4C)
-{
-}
-
IE Vendor3IE (EID_VENDOR_SPECIFIC) OUI (0x00, 0x16, 0x32)
{
}
@@ -3014,6 +3010,14 @@
MANDATORYTLV NoticeOfAbsence;
}
+IE vendor2_ie (EID_VENDOR_SPECIFIC) OUI (0x00, 0x90, 0x4c)
+{
+ type, 1;
+ sub_type, 1;
+ OPTIE IE VHTCaps;
+ OPTIE IE VHTOperation;
+}
+
/////////////////////////////////////////////////////////////////////////////
// Frames
@@ -3070,7 +3074,7 @@
OPTIE WiderBWChanSwitchAnn;
OPTIE OBSSScanParameters;
OPTIE Vendor1IE;
- OPTIE Vendor2IE;
+ OPTIE vendor2_ie;
OPTIE Vendor3IE;
OPTIE ChannelSwitchWrapper;
OPTIE QComVendorIE;
@@ -3157,7 +3161,7 @@
OPTIE WiderBWChanSwitchAnn;
OPTIE OBSSScanParameters;
OPTIE Vendor1IE;
- OPTIE Vendor2IE;
+ OPTIE vendor2_ie;
OPTIE Vendor3IE;
OPTIE ChannelSwitchWrapper;
OPTIE QComVendorIE;
@@ -3221,7 +3225,7 @@
OPTIE WiderBWChanSwitchAnn;
OPTIE OBSSScanParameters;
OPTIE Vendor1IE;
- OPTIE Vendor2IE;
+ OPTIE vendor2_ie;
OPTIE Vendor3IE;
OPTIE ChannelSwitchWrapper;
OPTIE QComVendorIE;
@@ -3262,6 +3266,7 @@
OPTIE ExtCap;
OPTIE OperatingMode;
OPTIE QosMapSet;
+ OPTIE vendor2_ie;
} // End frame AssocRequest.
FRAME AssocResponse // 7.2.3.5
@@ -3295,6 +3300,7 @@
OPTIE ExtCap;
OPTIE OBSSScanParameters;
OPTIE QosMapSet;
+ OPTIE vendor2_ie;
} // End frame AssocResponse.
FRAME ReAssocRequest // 7.2.3.6
@@ -3331,6 +3337,7 @@
OPTIE ExtCap;
OPTIE OperatingMode;
OPTIE QosMapSet;
+ OPTIE vendor2_ie;
} // End frame ReAssocRequest.
FRAME ReAssocResponse // 7.2.3.7
@@ -3365,6 +3372,7 @@
OPTIE ExtCap;
OPTIE OBSSScanParameters;
OPTIE QosMapSet;
+ OPTIE vendor2_ie;
} // End frame ReAssocResponse.
FRAME ProbeRequest // 7.2.3.8
@@ -3431,7 +3439,7 @@
OPTIE ExtCap;
OPTIE OBSSScanParameters;
OPTIE Vendor1IE;
- OPTIE Vendor2IE;
+ OPTIE vendor2_ie;
OPTIE Vendor3IE;
OPTIE ChannelSwitchWrapper;
OPTIE QComVendorIE;
@@ -3995,7 +4003,7 @@
OPTIE TimeAdvertisement;
OPTIE ExtCap;
OPTIE Vendor1IE;
- OPTIE Vendor2IE;
+ OPTIE vendor2_ie;
OPTIE Vendor3IE;
}
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/include/dot11f.h b/drivers/staging/qcacld-2.0/CORE/MAC/src/include/dot11f.h
index 10f1872..686786f 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/include/dot11f.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/include/dot11f.h
@@ -2421,6 +2421,84 @@
#ifdef __cplusplus
}; /* End extern "C". */
#endif /* C++ */
+// EID 191 (0xbf)
+typedef struct sDot11fIEVHTCaps {
+ tANI_U8 present;
+ tANI_U32 maxMPDULen: 2;
+ tANI_U32 supportedChannelWidthSet: 2;
+ tANI_U32 ldpcCodingCap: 1;
+ tANI_U32 shortGI80MHz: 1;
+ tANI_U32 shortGI160and80plus80MHz: 1;
+ tANI_U32 txSTBC: 1;
+ tANI_U32 rxSTBC: 3;
+ tANI_U32 suBeamFormerCap: 1;
+ tANI_U32 suBeamformeeCap: 1;
+ tANI_U32 csnofBeamformerAntSup: 3;
+ tANI_U32 numSoundingDim: 3;
+ tANI_U32 muBeamformerCap: 1;
+ tANI_U32 muBeamformeeCap: 1;
+ tANI_U32 vhtTXOPPS: 1;
+ tANI_U32 htcVHTCap: 1;
+ tANI_U32 maxAMPDULenExp: 3;
+ tANI_U32 vhtLinkAdaptCap: 2;
+ tANI_U32 rxAntPattern: 1;
+ tANI_U32 txAntPattern: 1;
+ tANI_U32 reserved1: 2;
+ tANI_U16 rxMCSMap;
+ tANI_U16 rxHighSupDataRate: 13;
+ tANI_U16 reserved2: 3;
+ tANI_U16 txMCSMap;
+ tANI_U16 txSupDataRate: 13;
+ tANI_U16 reserved3: 3;
+} tDot11fIEVHTCaps;
+
+#define DOT11F_EID_VHTCAPS ( 191 )
+
+// N.B. These #defines do *not* include the EID & length
+#define DOT11F_IE_VHTCAPS_MIN_LEN ( 12 )
+
+#define DOT11F_IE_VHTCAPS_MAX_LEN ( 12 )
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* C++ */
+tANI_U32 dot11fUnpackIeVHTCaps(tpAniSirGlobal, tANI_U8*,tANI_U8, tDot11fIEVHTCaps*);
+
+tANI_U32 dot11fPackIeVHTCaps(tpAniSirGlobal, tDot11fIEVHTCaps*, tANI_U8*, tANI_U32, tANI_U32*);
+
+tANI_U32 dot11fGetPackedIEVHTCaps(tpAniSirGlobal, tDot11fIEVHTCaps*, tANI_U32*);
+
+#ifdef __cplusplus
+}; /* End extern "C". */
+#endif /* C++ */
+// EID 192 (0xc0)
+typedef struct sDot11fIEVHTOperation {
+ tANI_U8 present;
+ tANI_U8 chanWidth;
+ tANI_U8 chanCenterFreqSeg1;
+ tANI_U8 chanCenterFreqSeg2;
+ tANI_U16 basicMCSSet;
+} tDot11fIEVHTOperation;
+
+#define DOT11F_EID_VHTOPERATION ( 192 )
+
+// N.B. These #defines do *not* include the EID & length
+#define DOT11F_IE_VHTOPERATION_MIN_LEN ( 5 )
+
+#define DOT11F_IE_VHTOPERATION_MAX_LEN ( 5 )
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* C++ */
+tANI_U32 dot11fUnpackIeVHTOperation(tpAniSirGlobal, tANI_U8*,tANI_U8, tDot11fIEVHTOperation*);
+
+tANI_U32 dot11fPackIeVHTOperation(tpAniSirGlobal, tDot11fIEVHTOperation*, tANI_U8*, tANI_U32, tANI_U32*);
+
+tANI_U32 dot11fGetPackedIEVHTOperation(tpAniSirGlobal, tDot11fIEVHTOperation*, tANI_U32*);
+
+#ifdef __cplusplus
+}; /* End extern "C". */
+#endif /* C++ */
// EID 221 (0xdd) {OUI 0x00, 0x50, 0xf2, 0x02, 0x09}
typedef struct sDot11fIEWMMSchedule {
tANI_U8 present;
@@ -4973,56 +5051,6 @@
#ifdef __cplusplus
}; /* End extern "C". */
#endif /* C++ */
-// EID 191 (0xbf)
-typedef struct sDot11fIEVHTCaps {
- tANI_U8 present;
- tANI_U32 maxMPDULen: 2;
- tANI_U32 supportedChannelWidthSet: 2;
- tANI_U32 ldpcCodingCap: 1;
- tANI_U32 shortGI80MHz: 1;
- tANI_U32 shortGI160and80plus80MHz: 1;
- tANI_U32 txSTBC: 1;
- tANI_U32 rxSTBC: 3;
- tANI_U32 suBeamFormerCap: 1;
- tANI_U32 suBeamformeeCap: 1;
- tANI_U32 csnofBeamformerAntSup: 3;
- tANI_U32 numSoundingDim: 3;
- tANI_U32 muBeamformerCap: 1;
- tANI_U32 muBeamformeeCap: 1;
- tANI_U32 vhtTXOPPS: 1;
- tANI_U32 htcVHTCap: 1;
- tANI_U32 maxAMPDULenExp: 3;
- tANI_U32 vhtLinkAdaptCap: 2;
- tANI_U32 rxAntPattern: 1;
- tANI_U32 txAntPattern: 1;
- tANI_U32 reserved1: 2;
- tANI_U16 rxMCSMap;
- tANI_U16 rxHighSupDataRate: 13;
- tANI_U16 reserved2: 3;
- tANI_U16 txMCSMap;
- tANI_U16 txSupDataRate: 13;
- tANI_U16 reserved3: 3;
-} tDot11fIEVHTCaps;
-
-#define DOT11F_EID_VHTCAPS ( 191 )
-
-// N.B. These #defines do *not* include the EID & length
-#define DOT11F_IE_VHTCAPS_MIN_LEN ( 12 )
-
-#define DOT11F_IE_VHTCAPS_MAX_LEN ( 12 )
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* C++ */
-tANI_U32 dot11fUnpackIeVHTCaps(tpAniSirGlobal, tANI_U8*,tANI_U8, tDot11fIEVHTCaps*);
-
-tANI_U32 dot11fPackIeVHTCaps(tpAniSirGlobal, tDot11fIEVHTCaps*, tANI_U8*, tANI_U32, tANI_U32*);
-
-tANI_U32 dot11fGetPackedIEVHTCaps(tpAniSirGlobal, tDot11fIEVHTCaps*, tANI_U32*);
-
-#ifdef __cplusplus
-}; /* End extern "C". */
-#endif /* C++ */
// EID 193 (0xc1)
typedef struct sDot11fIEVHTExtBssLoad {
tANI_U8 present;
@@ -5052,34 +5080,6 @@
#ifdef __cplusplus
}; /* End extern "C". */
#endif /* C++ */
-// EID 192 (0xc0)
-typedef struct sDot11fIEVHTOperation {
- tANI_U8 present;
- tANI_U8 chanWidth;
- tANI_U8 chanCenterFreqSeg1;
- tANI_U8 chanCenterFreqSeg2;
- tANI_U16 basicMCSSet;
-} tDot11fIEVHTOperation;
-
-#define DOT11F_EID_VHTOPERATION ( 192 )
-
-// N.B. These #defines do *not* include the EID & length
-#define DOT11F_IE_VHTOPERATION_MIN_LEN ( 5 )
-
-#define DOT11F_IE_VHTOPERATION_MAX_LEN ( 5 )
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* C++ */
-tANI_U32 dot11fUnpackIeVHTOperation(tpAniSirGlobal, tANI_U8*,tANI_U8, tDot11fIEVHTOperation*);
-
-tANI_U32 dot11fPackIeVHTOperation(tpAniSirGlobal, tDot11fIEVHTOperation*, tANI_U8*, tANI_U32, tANI_U32*);
-
-tANI_U32 dot11fGetPackedIEVHTOperation(tpAniSirGlobal, tDot11fIEVHTOperation*, tANI_U32*);
-
-#ifdef __cplusplus
-}; /* End extern "C". */
-#endif /* C++ */
// EID 221 (0xdd) {OUI 0x00, 0x10, 0x18}
typedef struct sDot11fIEVendor1IE {
tANI_U8 present;
@@ -5104,30 +5104,6 @@
#ifdef __cplusplus
}; /* End extern "C". */
#endif /* C++ */
-// EID 221 (0xdd) {OUI 0x00, 0x90, 0x4c}
-typedef struct sDot11fIEVendor2IE {
- tANI_U8 present;
-} tDot11fIEVendor2IE;
-
-#define DOT11F_EID_VENDOR2IE ( 221 )
-
-// N.B. These #defines do *not* include the EID & length
-#define DOT11F_IE_VENDOR2IE_MIN_LEN ( 3 )
-
-#define DOT11F_IE_VENDOR2IE_MAX_LEN ( 3 )
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* C++ */
-tANI_U32 dot11fUnpackIeVendor2IE(tpAniSirGlobal, tANI_U8*,tANI_U8, tDot11fIEVendor2IE*);
-
-tANI_U32 dot11fPackIeVendor2IE(tpAniSirGlobal, tDot11fIEVendor2IE*, tANI_U8*, tANI_U32, tANI_U32*);
-
-tANI_U32 dot11fGetPackedIEVendor2IE(tpAniSirGlobal, tDot11fIEVendor2IE*, tANI_U32*);
-
-#ifdef __cplusplus
-}; /* End extern "C". */
-#endif /* C++ */
// EID 221 (0xdd) {OUI 0x00, 0x16, 0x32}
typedef struct sDot11fIEVendor3IE {
tANI_U8 present;
@@ -5827,6 +5803,34 @@
#ifdef __cplusplus
}; /* End extern "C". */
#endif /* C++ */
+// EID 221 (0xdd) {OUI 0x00, 0x90, 0x4c}
+typedef struct sDot11fIEvendor2_ie {
+ tANI_U8 present;
+ tANI_U8 type;
+ tANI_U8 sub_type;
+ tDot11fIEVHTCaps VHTCaps;
+ tDot11fIEVHTOperation VHTOperation;
+} tDot11fIEvendor2_ie;
+
+#define DOT11F_EID_VENDOR2_IE ( 221 )
+
+// N.B. These #defines do *not* include the EID & length
+#define DOT11F_IE_VENDOR2_IE_MIN_LEN ( 5 )
+
+#define DOT11F_IE_VENDOR2_IE_MAX_LEN ( 26 )
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* C++ */
+tANI_U32 dot11fUnpackIevendor2_ie(tpAniSirGlobal, tANI_U8*,tANI_U8, tDot11fIEvendor2_ie*);
+
+tANI_U32 dot11fPackIevendor2_ie(tpAniSirGlobal, tDot11fIEvendor2_ie*, tANI_U8*, tANI_U32, tANI_U32*);
+
+tANI_U32 dot11fGetPackedIEvendor2_ie(tpAniSirGlobal, tDot11fIEvendor2_ie*, tANI_U32*);
+
+#ifdef __cplusplus
+}; /* End extern "C". */
+#endif /* C++ */
/************************************************************************
* Frames
**********************************************************************/
@@ -5967,6 +5971,7 @@
tDot11fIEExtCap ExtCap;
tDot11fIEOperatingMode OperatingMode;
tDot11fIEQosMapSet QosMapSet;
+ tDot11fIEvendor2_ie vendor2_ie;
} tDot11fAssocRequest;
#define DOT11F_ASSOCREQUEST ( 5 )
@@ -6015,6 +6020,7 @@
tDot11fIEExtCap ExtCap;
tDot11fIEOBSSScanParameters OBSSScanParameters;
tDot11fIEQosMapSet QosMapSet;
+ tDot11fIEvendor2_ie vendor2_ie;
} tDot11fAssocResponse;
#define DOT11F_ASSOCRESPONSE ( 6 )
@@ -6108,7 +6114,7 @@
tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn;
tDot11fIEOBSSScanParameters OBSSScanParameters;
tDot11fIEVendor1IE Vendor1IE;
- tDot11fIEVendor2IE Vendor2IE;
+ tDot11fIEvendor2_ie vendor2_ie;
tDot11fIEVendor3IE Vendor3IE;
tDot11fIEChannelSwitchWrapper ChannelSwitchWrapper;
tDot11fIEQComVendorIE QComVendorIE;
@@ -6189,7 +6195,7 @@
tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn;
tDot11fIEOBSSScanParameters OBSSScanParameters;
tDot11fIEVendor1IE Vendor1IE;
- tDot11fIEVendor2IE Vendor2IE;
+ tDot11fIEvendor2_ie vendor2_ie;
tDot11fIEVendor3IE Vendor3IE;
tDot11fIEChannelSwitchWrapper ChannelSwitchWrapper;
tDot11fIEQComVendorIE QComVendorIE;
@@ -6258,7 +6264,7 @@
tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn;
tDot11fIEOBSSScanParameters OBSSScanParameters;
tDot11fIEVendor1IE Vendor1IE;
- tDot11fIEVendor2IE Vendor2IE;
+ tDot11fIEvendor2_ie vendor2_ie;
tDot11fIEVendor3IE Vendor3IE;
tDot11fIEChannelSwitchWrapper ChannelSwitchWrapper;
tDot11fIEQComVendorIE QComVendorIE;
@@ -6881,7 +6887,7 @@
tDot11fIEExtCap ExtCap;
tDot11fIEOBSSScanParameters OBSSScanParameters;
tDot11fIEVendor1IE Vendor1IE;
- tDot11fIEVendor2IE Vendor2IE;
+ tDot11fIEvendor2_ie vendor2_ie;
tDot11fIEVendor3IE Vendor3IE;
tDot11fIEChannelSwitchWrapper ChannelSwitchWrapper;
tDot11fIEQComVendorIE QComVendorIE;
@@ -7048,6 +7054,7 @@
tDot11fIEExtCap ExtCap;
tDot11fIEOperatingMode OperatingMode;
tDot11fIEQosMapSet QosMapSet;
+ tDot11fIEvendor2_ie vendor2_ie;
} tDot11fReAssocRequest;
#define DOT11F_REASSOCREQUEST ( 43 )
@@ -7097,6 +7104,7 @@
tDot11fIEExtCap ExtCap;
tDot11fIEOBSSScanParameters OBSSScanParameters;
tDot11fIEQosMapSet QosMapSet;
+ tDot11fIEvendor2_ie vendor2_ie;
} tDot11fReAssocResponse;
#define DOT11F_REASSOCRESPONSE ( 44 )
@@ -7452,7 +7460,7 @@
tDot11fIETimeAdvertisement TimeAdvertisement;
tDot11fIEExtCap ExtCap;
tDot11fIEVendor1IE Vendor1IE;
- tDot11fIEVendor2IE Vendor2IE;
+ tDot11fIEvendor2_ie vendor2_ie;
tDot11fIEVendor3IE Vendor3IE;
} tDot11fTimingAdvertisementFrame;
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/include/parserApi.h b/drivers/staging/qcacld-2.0/CORE/MAC/src/include/parserApi.h
index 6184e10..6da39f5 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/include/parserApi.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/include/parserApi.h
@@ -171,7 +171,7 @@
tDot11fIEWiderBWChanSwitchAnn WiderBWChanSwitchAnn;
#endif
tANI_U8 Vendor1IEPresent;
- tANI_U8 Vendor2IEPresent;
+ tDot11fIEvendor2_ie vendor2_ie;
tANI_U8 Vendor3IEPresent;
tDot11fIEIBSSParams IBSSParams;
@@ -255,6 +255,7 @@
tDot11fIEOperatingMode operMode;
#endif
tDot11fIEExtCap ExtCap;
+ tDot11fIEvendor2_ie vendor2_ie;
} tSirAssocReq, *tpSirAssocReq;
@@ -311,6 +312,7 @@
#ifdef WLAN_FEATURE_11W
tDot11fIETimeoutInterval TimeoutInterval;
#endif
+ tDot11fIEvendor2_ie vendor2_ie;
} tSirAssocRsp, *tpSirAssocRsp;
#if defined(FEATURE_WLAN_ESE_UPLOAD)
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/include/sirParams.h b/drivers/staging/qcacld-2.0/CORE/MAC/src/include/sirParams.h
index 60f6006..f327fde 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/include/sirParams.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/include/sirParams.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -773,10 +773,11 @@
#define SIR_HAL_NDP_INDICATION (SIR_HAL_ITC_MSG_TYPES_BEGIN + 356)
#define SIR_HAL_NDP_CONFIRM (SIR_HAL_ITC_MSG_TYPES_BEGIN + 357)
#define SIR_HAL_NDP_END_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 358)
-#define SIR_HAL_SEND_FREQ_RANGE_CONTROL_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 359)
-
+#define SIR_HAL_SEND_FREQ_RANGE_CONTROL_IND (SIR_HAL_ITC_MSG_TYPES_BEGIN + 359)
#define SIR_BTC_BT_WLAN_INTERVAL_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 360)
#define SIR_HAL_SET_ALLOWED_ACTION_FRAMES (SIR_HAL_ITC_MSG_TYPES_BEGIN + 361)
+#define SIR_HAL_POWER_DEBUG_STATS_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 371)
+#define SIR_HAL_ACTION_FRAME_RANDOM_MAC (SIR_HAL_ITC_MSG_TYPES_BEGIN + 374)
#define SIR_HAL_MSG_TYPES_END (SIR_HAL_MSG_TYPES_BEGIN + 0x1FF)
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/include/limGlobal.h b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/include/limGlobal.h
index a7b4ca8..be13f29b 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/include/limGlobal.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/include/limGlobal.h
@@ -313,18 +313,11 @@
#ifdef FEATURE_OEM_DATA_SUPPORT
-#ifndef OEM_DATA_REQ_SIZE
-#define OEM_DATA_REQ_SIZE 280
-#endif
-#ifndef OEM_DATA_RSP_SIZE
-#define OEM_DATA_RSP_SIZE 1724
-#endif
-
// OEM Data related structure definitions
typedef struct sLimMlmOemDataReq
{
tSirMacAddr selfMacAddr;
- uint8_t data_len;
+ uint32_t data_len;
uint8_t *data;
} tLimMlmOemDataReq, *tpLimMlmOemDataReq;
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/include/limSession.h b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/include/limSession.h
index d533ad5..c0d087f 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/include/limSession.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/include/limSession.h
@@ -501,6 +501,11 @@
/* Supported NSS is intersection of self and peer NSS */
bool supported_nss_1x1;
bool is_ext_caps_present;
+ bool is_vendor_specific_vhtcaps;
+ uint8_t vendor_specific_vht_ie_type;
+ uint8_t vendor_specific_vht_ie_sub_type;
+ bool vendor_vht_for_24ghz_sap;
+
} tPESession, *tpPESession;
/*-------------------------------------------------------------------------
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limAssocUtils.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limAssocUtils.c
index d5277d5..515a6c9 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limAssocUtils.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limAssocUtils.c
@@ -3501,6 +3501,17 @@
mlmJoinCnf.sessionId = psessionEntry->peSessionId;
limPostSmeMessage(pMac, LIM_MLM_JOIN_CNF, (tANI_U32 *) &mlmJoinCnf);
} // if ((pMac->lim.gLimSystemRole == IBSS....
+
+ if (pBPR->vendor2_ie.VHTCaps.present) {
+ psessionEntry->is_vendor_specific_vhtcaps = true;
+ psessionEntry->vendor_specific_vht_ie_type =
+ pBPR->vendor2_ie.type;
+ psessionEntry->vendor_specific_vht_ie_sub_type =
+ pBPR->vendor2_ie.sub_type;
+ limLog(pMac, LOG1, FL(
+ "VHT caps are present in vendor specific IE"));
+ }
+
}
/**
@@ -3638,6 +3649,86 @@
return retCode;
}
+ /* lim_update_vhtcaps_assoc_resp : Update VHT caps in assoc response.
+ * @pMac Pointer to Global MAC structure
+ * @add_bss_params: parameters required for add bss params.
+ * @vht_caps: VHT capabilities.
+ * @session_entry : session entry.
+ *
+ * Return : void
+ */
+void lim_update_vhtcaps_assoc_resp(tpAniSirGlobal pMac,
+ tpAddBssParams add_bss_params,
+ tDot11fIEVHTCaps *vht_caps, tpPESession session_entry)
+{
+ add_bss_params->currentExtChannel = limGet11ACPhyCBState(pMac,
+ add_bss_params->currentOperChannel,
+ add_bss_params->currentExtChannel,
+ session_entry->apCenterChan,
+ session_entry);
+
+ add_bss_params->staContext.vht_caps =
+ ((vht_caps->maxMPDULen << SIR_MAC_VHT_CAP_MAX_MPDU_LEN) |
+ (vht_caps->supportedChannelWidthSet <<
+ SIR_MAC_VHT_CAP_SUPP_CH_WIDTH_SET) |
+ (vht_caps->ldpcCodingCap <<
+ SIR_MAC_VHT_CAP_LDPC_CODING_CAP) |
+ (vht_caps->shortGI80MHz <<
+ SIR_MAC_VHT_CAP_SHORTGI_80MHZ) |
+ (vht_caps->shortGI160and80plus80MHz <<
+ SIR_MAC_VHT_CAP_SHORTGI_160_80_80MHZ) |
+ (vht_caps->txSTBC << SIR_MAC_VHT_CAP_TXSTBC) |
+ (vht_caps->rxSTBC << SIR_MAC_VHT_CAP_RXSTBC) |
+ (vht_caps->suBeamFormerCap <<
+ SIR_MAC_VHT_CAP_SU_BEAMFORMER_CAP) |
+ (vht_caps->suBeamformeeCap <<
+ SIR_MAC_VHT_CAP_SU_BEAMFORMEE_CAP) |
+ (vht_caps->csnofBeamformerAntSup <<
+ SIR_MAC_VHT_CAP_CSN_BEAMORMER_ANT_SUP) |
+ (vht_caps->numSoundingDim <<
+ SIR_MAC_VHT_CAP_NUM_SOUNDING_DIM) |
+ (vht_caps->muBeamformerCap <<
+ SIR_MAC_VHT_CAP_NUM_BEAM_FORMER_CAP)|
+ (vht_caps->muBeamformeeCap <<
+ SIR_MAC_VHT_CAP_NUM_BEAM_FORMEE_CAP) |
+ (vht_caps->vhtTXOPPS << SIR_MAC_VHT_CAP_TXOPPS) |
+ (vht_caps->htcVHTCap << SIR_MAC_VHT_CAP_HTC_CAP) |
+ (vht_caps->maxAMPDULenExp <<
+ SIR_MAC_VHT_CAP_MAX_AMDU_LEN_EXPO) |
+ (vht_caps->vhtLinkAdaptCap <<
+ SIR_MAC_VHT_CAP_LINK_ADAPT_CAP) |
+ (vht_caps->rxAntPattern <<
+ SIR_MAC_VHT_CAP_RX_ANTENNA_PATTERN) |
+ (vht_caps->txAntPattern <<
+ SIR_MAC_VHT_CAP_TX_ANTENNA_PATTERN) |
+ (vht_caps->reserved1 << SIR_MAC_VHT_CAP_RESERVED2));
+
+ add_bss_params->staContext.maxAmpduSize =
+ SIR_MAC_GET_VHT_MAX_AMPDU_EXPO(
+ add_bss_params->staContext.vht_caps);
+
+ limLog(pMac, LOG1,
+ FL("Updating VHT Caps in assoc Response"));
+}
+
+/**
+ * lim_update_vht_oper_assoc_resp : Update VHT Operations in assoc response.
+ * @pMac Pointer to Global MAC structure
+ * @add_bss_params: parameters required for add bss params.
+ * @vht_oper: VHT Operations to update.
+ * @session_entry : session entry.
+ *
+ * Return : void
+ */
+void lim_update_vht_oper_assoc_resp(tpAniSirGlobal pMac,
+ tpAddBssParams add_bss_params,
+ tDot11fIEVHTOperation *vht_oper, tpPESession session_entry)
+{
+ if (vht_oper->chanWidth)
+ add_bss_params->vhtTxChannelWidthSet = vht_oper->chanWidth;
+ limLog(pMac, LOG1,
+ FL("Updating VHT Operation in assoc Response"));
+}
/**
@@ -3681,6 +3772,10 @@
tANI_U32 shortGi20MhzSupport;
tANI_U32 shortGi40MhzSupport;
tANI_U32 enableTxBF20MHz;
+ tDot11fIEVHTCaps *vht_caps = NULL;
+ tDot11fIEVHTOperation *vht_oper = NULL;
+
+
// Package SIR_HAL_ADD_BSS_REQ message parameters
pAddBssParams = vos_mem_malloc(sizeof( tAddBssParams ));
if (NULL == pAddBssParams)
@@ -3815,57 +3910,34 @@
if (psessionEntry->vhtCapability && ( pAssocRsp->VHTCaps.present ))
{
pAddBssParams->vhtCapable = pAssocRsp->VHTCaps.present;
- pAddBssParams->vhtTxChannelWidthSet = pAssocRsp->VHTOperation.chanWidth;
pAddBssParams->currentExtChannel = limGet11ACPhyCBState ( pMac,
pAddBssParams->currentOperChannel,
pAddBssParams->currentExtChannel,
psessionEntry->apCenterChan,
psessionEntry);
-
- pAddBssParams->staContext.vht_caps =
- ((pAssocRsp->VHTCaps.maxMPDULen << SIR_MAC_VHT_CAP_MAX_MPDU_LEN) |
- (pAssocRsp->VHTCaps.supportedChannelWidthSet <<
- SIR_MAC_VHT_CAP_SUPP_CH_WIDTH_SET) |
- (pAssocRsp->VHTCaps.ldpcCodingCap <<
- SIR_MAC_VHT_CAP_LDPC_CODING_CAP) |
- (pAssocRsp->VHTCaps.shortGI80MHz <<
- SIR_MAC_VHT_CAP_SHORTGI_80MHZ) |
- (pAssocRsp->VHTCaps.shortGI160and80plus80MHz <<
- SIR_MAC_VHT_CAP_SHORTGI_160_80_80MHZ) |
- (pAssocRsp->VHTCaps.txSTBC << SIR_MAC_VHT_CAP_TXSTBC) |
- (pAssocRsp->VHTCaps.rxSTBC << SIR_MAC_VHT_CAP_RXSTBC) |
- (pAssocRsp->VHTCaps.suBeamFormerCap <<
- SIR_MAC_VHT_CAP_SU_BEAMFORMER_CAP) |
- (pAssocRsp->VHTCaps.suBeamformeeCap <<
- SIR_MAC_VHT_CAP_SU_BEAMFORMEE_CAP) |
- (pAssocRsp->VHTCaps.csnofBeamformerAntSup <<
- SIR_MAC_VHT_CAP_CSN_BEAMORMER_ANT_SUP) |
- (pAssocRsp->VHTCaps.numSoundingDim <<
- SIR_MAC_VHT_CAP_NUM_SOUNDING_DIM) |
- (pAssocRsp->VHTCaps.muBeamformerCap <<
- SIR_MAC_VHT_CAP_NUM_BEAM_FORMER_CAP)|
- (pAssocRsp->VHTCaps.muBeamformeeCap <<
- SIR_MAC_VHT_CAP_NUM_BEAM_FORMEE_CAP) |
- (pAssocRsp->VHTCaps.vhtTXOPPS << SIR_MAC_VHT_CAP_TXOPPS) |
- (pAssocRsp->VHTCaps.htcVHTCap << SIR_MAC_VHT_CAP_HTC_CAP) |
- (pAssocRsp->VHTCaps.maxAMPDULenExp <<
- SIR_MAC_VHT_CAP_MAX_AMDU_LEN_EXPO) |
- (pAssocRsp->VHTCaps.vhtLinkAdaptCap <<
- SIR_MAC_VHT_CAP_LINK_ADAPT_CAP) |
- (pAssocRsp->VHTCaps.rxAntPattern <<
- SIR_MAC_VHT_CAP_RX_ANTENNA_PATTERN) |
- (pAssocRsp->VHTCaps.txAntPattern <<
- SIR_MAC_VHT_CAP_TX_ANTENNA_PATTERN) |
- (pAssocRsp->VHTCaps.reserved1 << SIR_MAC_VHT_CAP_RESERVED2));
-
- pAddBssParams->staContext.maxAmpduSize =
- SIR_MAC_GET_VHT_MAX_AMPDU_EXPO(
- pAddBssParams->staContext.vht_caps);
+ vht_caps = &pAssocRsp->VHTCaps;
+ vht_oper = &pAssocRsp->VHTOperation;
+ } else if (psessionEntry->vhtCapability &&
+ pAssocRsp->vendor2_ie.VHTCaps.present) {
+ pAddBssParams->vhtCapable =
+ pAssocRsp->vendor2_ie.VHTCaps.present;
+ limLog(pMac, LOG1,
+ FL("VHT Caps and Operation are present in vendor Specfic IE"));
+ vht_caps = &pAssocRsp->vendor2_ie.VHTCaps;
+ vht_oper = &pAssocRsp->vendor2_ie.VHTOperation;
}
else
- {
pAddBssParams->vhtCapable = 0;
+
+ if (pAddBssParams->vhtCapable) {
+ if (vht_oper != NULL)
+ lim_update_vht_oper_assoc_resp(pMac, pAddBssParams,
+ vht_oper, psessionEntry);
+ if (vht_caps != NULL)
+ lim_update_vhtcaps_assoc_resp(pMac, pAddBssParams,
+ vht_caps, psessionEntry);
}
+
limLog(pMac, LOG2, FL("vhtCapable %d vhtTxChannelWidthSet %d "
"currentExtChannel %d"),pAddBssParams->vhtCapable,
pAddBssParams->vhtTxChannelWidthSet,
@@ -3927,33 +3999,48 @@
pAddBssParams->staContext.greenFieldCapable,
pAddBssParams->staContext.lsigTxopProtection);
#ifdef WLAN_FEATURE_11AC
- if (psessionEntry->vhtCapability && IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps))
- {
+ if (psessionEntry->vhtCapability &&
+ (IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps) ||
+ IS_BSS_VHT_CAPABLE(
+ pBeaconStruct->vendor2_ie.VHTCaps))) {
pAddBssParams->staContext.vhtCapable = 1;
pAddBssParams->staContext.vhtSupportedRxNss = pStaDs->vhtSupportedRxNss;
- if ((pAssocRsp->VHTCaps.suBeamFormerCap ||
- pAssocRsp->VHTCaps.muBeamformerCap) &&
- psessionEntry->txBFIniFeatureEnabled)
- {
- pAddBssParams->staContext.vhtTxBFCapable = 1;
- }
+ if (pAssocRsp->VHTCaps.present)
+ vht_caps = &pAssocRsp->VHTCaps;
+ else if (pAssocRsp->vendor2_ie.VHTCaps.present) {
+ vht_caps =
+ &pAssocRsp->vendor2_ie.VHTCaps;
+ limLog(pMac, LOG1,
+ FL("VHT Caps is present in vendor Specfic IE"));
- if (pAssocRsp->VHTCaps.muBeamformerCap &&
- psessionEntry->txMuBformee )
- {
+ }
+ if ((vht_caps != NULL) && (vht_caps->suBeamFormerCap ||
+ vht_caps->muBeamformerCap) &&
+ psessionEntry->txBFIniFeatureEnabled)
+ pAddBssParams->staContext.vhtTxBFCapable = 1;
+ if ((vht_caps != NULL) &&
+ vht_caps->muBeamformerCap &&
+ psessionEntry->txMuBformee)
pAddBssParams->staContext.vhtTxMUBformeeCapable = 1;
}
- }
#endif
if( (pAssocRsp->HTCaps.supportedChannelWidthSet) &&
(chanWidthSupp) )
{
pAddBssParams->staContext.txChannelWidthSet = ( tANI_U8 )pAssocRsp->HTInfo.recommendedTxWidthSet;
#ifdef WLAN_FEATURE_11AC
- if (pAddBssParams->staContext.vhtCapable)
- {
- pAddBssParams->staContext.vhtTxChannelWidthSet = pAssocRsp->VHTOperation.chanWidth; //pMac->lim.apChanWidth;
+ if (pAssocRsp->VHTCaps.present)
+ vht_oper = &pAssocRsp->VHTOperation;
+ else if (pAssocRsp->vendor2_ie.VHTCaps.present) {
+ vht_oper = &pAssocRsp->vendor2_ie.VHTOperation;
+ limLog(pMac, LOG1, FL("VHT Operation is present in vendor Specfic IE"));
}
+
+ if ((vht_oper != NULL) &&
+ pAddBssParams->staContext.vhtCapable)
+ pAddBssParams->staContext.vhtTxChannelWidthSet = vht_oper->chanWidth; //pMac->lim.apChanWidth;
+
+
limLog(pMac, LOG2,FL("StaContext vhtCapable %d "
"vhtTxChannelWidthSet: %d vhtTxBFCapable: %d"),
pAddBssParams->staContext.vhtCapable,
@@ -4232,6 +4319,7 @@
tANI_U32 shortGi20MhzSupport;
tANI_U32 shortGi40MhzSupport;
tpSirBssDescription bssDescription = &psessionEntry->pLimJoinReq->bssDescription;
+ tDot11fIEVHTCaps *vht_caps = NULL;
pBeaconStruct = vos_mem_malloc(sizeof(tSchBeaconStruct));
if (NULL == pBeaconStruct)
@@ -4364,16 +4452,18 @@
limLog(pMac, LOG2, FL("currentOperChannel %d"),
pAddBssParams->currentOperChannel);
#ifdef WLAN_FEATURE_11AC
- if (psessionEntry->vhtCapability && IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps))
- {
- pAddBssParams->vhtCapable = pBeaconStruct->VHTCaps.present;
+ if (psessionEntry->vhtCapability &&
+ (IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps) ||
+ IS_BSS_VHT_CAPABLE(pBeaconStruct->vendor2_ie.VHTCaps))) {
+ pAddBssParams->vhtCapable = 1;
/*
* in limExtractApCapability function intersection of FW advertised
* channel width and AP advertised channel width has been taken into
* account for calculating psessionEntry->apChanWidth
*/
pAddBssParams->vhtTxChannelWidthSet = psessionEntry->apChanWidth;
- pAddBssParams->currentExtChannel = limGet11ACPhyCBState (pMac,
+
+ pAddBssParams->currentExtChannel = limGet11ACPhyCBState ( pMac,
pAddBssParams->currentOperChannel,
pAddBssParams->currentExtChannel,
psessionEntry->apCenterChan,
@@ -4428,21 +4518,26 @@
pAddBssParams->staContext.greenFieldCapable,
pAddBssParams->staContext.lsigTxopProtection);
#ifdef WLAN_FEATURE_11AC
- if (psessionEntry->vhtCapability && IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps))
- {
+ if (psessionEntry->vhtCapability &&
+ (IS_BSS_VHT_CAPABLE(pBeaconStruct->VHTCaps) ||
+ IS_BSS_VHT_CAPABLE(pBeaconStruct->vendor2_ie.VHTCaps))) {
pAddBssParams->staContext.vhtCapable = 1;
- if ((pBeaconStruct->VHTCaps.suBeamFormerCap ||
- pBeaconStruct->VHTCaps.muBeamformerCap) &&
- psessionEntry->txBFIniFeatureEnabled )
- {
- pAddBssParams->staContext.vhtTxBFCapable = 1;
- }
+ if (pBeaconStruct->VHTCaps.present)
+ vht_caps = &pBeaconStruct->VHTCaps;
+ else if (
+ pBeaconStruct->vendor2_ie.VHTCaps.present)
+ vht_caps =
+ &pBeaconStruct->vendor2_ie.VHTCaps;
- if ( pBeaconStruct->VHTCaps.muBeamformerCap &&
- psessionEntry->txMuBformee )
- {
+ if ((vht_caps != NULL) &&
+ (vht_caps->suBeamFormerCap ||
+ vht_caps->muBeamformerCap) &&
+ psessionEntry->txBFIniFeatureEnabled)
+ pAddBssParams->staContext.vhtTxBFCapable = 1;
+ if ((vht_caps != NULL) && vht_caps->muBeamformerCap &&
+ psessionEntry->txMuBformee)
pAddBssParams->staContext.vhtTxMUBformeeCapable = 1;
- }
+
}
#endif
if( (pBeaconStruct->HTCaps.supportedChannelWidthSet) &&
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAssocReqFrame.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAssocReqFrame.c
index ea8bcf1d..28c1f62 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAssocReqFrame.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAssocReqFrame.c
@@ -252,6 +252,7 @@
#endif
tANI_U16 assocId = 0;
bool assoc_req_copied = false;
+ tDot11fIEVHTCaps *vht_caps;
limGetPhyMode(pMac, &phyMode, psessionEntry);
@@ -401,6 +402,14 @@
(tANI_U8 *) pBody, framelen);
pAssocReq->assocReqFrameLength = framelen;
+ if (pAssocReq->VHTCaps.present)
+ vht_caps = &pAssocReq->VHTCaps;
+ else if (pAssocReq->vendor2_ie.VHTCaps.present &&
+ psessionEntry->vendor_vht_for_24ghz_sap)
+ vht_caps = &pAssocReq->vendor2_ie.VHTCaps;
+ else
+ vht_caps = NULL;
+
if (cfgGetCapabilityInfo(pMac, &temp,psessionEntry) != eSIR_SUCCESS)
{
limLog(pMac, LOGP, FL("could not retrieve Capabilities"));
@@ -520,7 +529,7 @@
if (LIM_IS_AP_ROLE(psessionEntry) &&
(psessionEntry->dot11mode == WNI_CFG_DOT11_MODE_11AC_ONLY) &&
- (!pAssocReq->VHTCaps.present)) {
+ (vht_caps != NULL) && (!vht_caps->present)) {
limSendAssocRspMgmtFrame( pMac, eSIR_MAC_CAPABILITIES_NOT_SUPPORTED_STATUS,
1, pHdr->sa, subType, 0, psessionEntry );
limLog(pMac, LOGE, FL("SOFTAP was in 11AC only mode, reject"));
@@ -1167,7 +1176,10 @@
pStaDs->mlmStaContext.htCapability = pAssocReq->HTCaps.present;
#ifdef WLAN_FEATURE_11AC
- pStaDs->mlmStaContext.vhtCapability = pAssocReq->VHTCaps.present;
+ if ((vht_caps != NULL) && vht_caps->present)
+ pStaDs->mlmStaContext.vhtCapability = vht_caps->present;
+ else
+ pStaDs->mlmStaContext.vhtCapability = false;
#endif
pStaDs->qos.addtsPresent = (pAssocReq->addtsPresent==0) ? false : true;
pStaDs->qos.addts = pAssocReq->addtsReq;
@@ -1252,7 +1264,7 @@
pStaDs->vhtSupportedChannelWidthSet = (tANI_U8)((pAssocReq->operMode.chanWidth == eHT_CHANNEL_WIDTH_80MHZ) ? WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ : WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ);
pStaDs->htSupportedChannelWidthSet = (tANI_U8)(pAssocReq->operMode.chanWidth ? eHT_CHANNEL_WIDTH_40MHZ : eHT_CHANNEL_WIDTH_20MHZ);
}
- else if (pAssocReq->VHTCaps.present)
+ else if ((vht_caps != NULL) && vht_caps->present)
{
// Check if STA has enabled it's channel bonding mode.
// If channel bonding mode is enabled, we decide based on SAP's current configuration.
@@ -1260,7 +1272,7 @@
pStaDs->vhtSupportedChannelWidthSet = (tANI_U8)((pStaDs->htSupportedChannelWidthSet == eHT_CHANNEL_WIDTH_20MHZ) ?
WNI_CFG_VHT_CHANNEL_WIDTH_20_40MHZ :
psessionEntry->vhtTxChannelWidthSet );
- pStaDs->htMaxRxAMpduFactor = pAssocReq->VHTCaps.maxAMPDULenExp;
+ pStaDs->htMaxRxAMpduFactor = vht_caps->maxAMPDULenExp;
}
// Lesser among the AP and STA bandwidth of operation.
@@ -1273,9 +1285,9 @@
pStaDs->htLdpcCapable = (tANI_U8)pAssocReq->HTCaps.advCodingCap;
}
- if(pAssocReq->VHTCaps.present && pAssocReq->wmeInfoPresent)
+ if((vht_caps != NULL) && vht_caps->present && pAssocReq->wmeInfoPresent)
{
- pStaDs->vhtLdpcCapable = (tANI_U8)pAssocReq->VHTCaps.ldpcCodingCap;
+ pStaDs->vhtLdpcCapable = (tANI_U8)vht_caps->ldpcCodingCap;
}
if (!pAssocReq->wmeInfoPresent) {
@@ -1290,7 +1302,7 @@
&(pAssocReq->supportedRates),
&(pAssocReq->extendedRates),
pAssocReq->HTCaps.supportedMCSSet,
- psessionEntry, &pAssocReq->VHTCaps)
+ psessionEntry, vht_caps)
!= eSIR_SUCCESS)
#else
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAssocRspFrame.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAssocRspFrame.c
index b53a055..ed61b05 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAssocRspFrame.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessAssocRspFrame.c
@@ -84,6 +84,8 @@
tANI_U16 rxHighestRate = 0;
uint32_t shortgi_20mhz_support;
uint32_t shortgi_40mhz_support;
+ tDot11fIEVHTCaps *vht_caps = NULL;
+ tDot11fIEVHTOperation *vht_oper = NULL;
limGetPhyMode(pMac, &phyMode, psessionEntry);
@@ -163,33 +165,48 @@
}
#ifdef WLAN_FEATURE_11AC
- if(IS_DOT11_MODE_VHT(psessionEntry->dot11mode))
- {
- pStaDs->mlmStaContext.vhtCapability = pAssocRsp->VHTCaps.present;
- if (pAssocRsp->VHTCaps.present &&
- psessionEntry->htSupportedChannelWidthSet)
- pStaDs->vhtSupportedChannelWidthSet =
- pAssocRsp->VHTOperation.chanWidth;
- }
-
- // If 11ac is supported and if the peer is sending VHT capabilities,
- // then htMaxRxAMpduFactor should be overloaded with VHT maxAMPDULenExp
if (pAssocRsp->VHTCaps.present)
- {
- pStaDs->htMaxRxAMpduFactor = pAssocRsp->VHTCaps.maxAMPDULenExp;
- }
+ vht_caps = &pAssocRsp->VHTCaps;
+ else if (pAssocRsp->vendor2_ie.VHTCaps.present)
+ vht_caps = &pAssocRsp->vendor2_ie.VHTCaps;
+ if (pAssocRsp->VHTOperation.present)
+ vht_oper = &pAssocRsp->VHTOperation;
+ else if (pAssocRsp->vendor2_ie.VHTCaps.present)
+ vht_oper = &pAssocRsp->vendor2_ie.VHTOperation;
+ if ((vht_oper != NULL) &&
+ (IS_DOT11_MODE_VHT(psessionEntry->dot11mode)) &&
+ vht_oper->present &&
+ psessionEntry->htSupportedChannelWidthSet)
+ pStaDs->vhtSupportedChannelWidthSet = vht_oper->chanWidth;
+ if ((vht_caps != NULL) && vht_caps->present) {
+ if (IS_DOT11_MODE_VHT(psessionEntry->dot11mode))
+ pStaDs->mlmStaContext.vhtCapability =
+ vht_caps->present;
+ /*
+ * If 11ac is supported and if the peer is
+ * sending VHT capabilities,
+ * then htMaxRxAMpduFactor should be
+ * overloaded with VHT maxAMPDULenExp
+ */
+ pStaDs->htMaxRxAMpduFactor = vht_caps->maxAMPDULenExp;
+ }
if (limPopulatePeerRateSet(pMac, &pStaDs->supportedRates,
pAssocRsp->HTCaps.supportedMCSSet,
- false,psessionEntry , &pAssocRsp->VHTCaps) != eSIR_SUCCESS)
+ false, psessionEntry,
+ vht_caps) != eSIR_SUCCESS) {
+ limLog(pMac, LOGP,
+ FL("could not get rateset and extended rate set"));
+ return;
+ }
#else
if (limPopulatePeerRateSet(pMac, &pStaDs->supportedRates, pAssocRsp->HTCaps.supportedMCSSet, false,psessionEntry) != eSIR_SUCCESS)
-#endif
{
limLog(pMac, LOGP, FL("could not get rateset and extended rate set"));
return;
}
+#endif
#ifdef WLAN_FEATURE_11AC
pStaDs->vhtSupportedRxNss = ((pStaDs->supportedRates.vhtRxMCSMap & MCSMAPMASK2x2)
== MCSMAPMASK2x2) ? 1 : 2;
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessCfgUpdates.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessCfgUpdates.c
index 37e45a8e..488b60a 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessCfgUpdates.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessCfgUpdates.c
@@ -552,28 +552,6 @@
pMac->lim.gLimAssocStaLimit = (tANI_U16)val1;
break;
- case WNI_CFG_DEL_ALL_RX_BA_SESSIONS_2_4_G_BTC:
- if (wlan_cfgGetInt
- (pMac, WNI_CFG_DEL_ALL_RX_BA_SESSIONS_2_4_G_BTC, &val1) !=
- eSIR_SUCCESS)
- {
- limLog(pMac, LOGE,
- FL( "Unable to get WNI_CFG_DEL_ALL_RX_BA_SESSIONS_2_4_G_BTC"));
- break;
- }
- if (val1)
- {
- limLog(pMac, LOGW,
- FL("BTC requested to disable all RX BA sessions"));
- limDelPerBssBASessionsBtc(pMac);
- }
- else
- {
- limLog(pMac, LOGW,
- FL("Resetting the WNI_CFG_DEL_ALL_RX_BA_SESSIONS_2_4_G_BTC"));
- }
- break;
-
default:
break;
}
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMessageQueue.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
index 6792ba7..3a2b513 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMessageQueue.c
@@ -244,14 +244,14 @@
tSirMsgQ mmh_msg;
tpSirMacMgmtHdr hdr;
- result = vos_mem_malloc(sizeof(*result) + ie_len);
+ result = vos_mem_malloc(sizeof(*result) + ie_len + ie_len);
if (NULL == result) {
limLog(pmac, LOGE, FL("Memory allocation failed"));
return;
}
hdr = WDA_GET_RX_MAC_HEADER(rx_pkt_info);
body = WDA_GET_RX_MPDU_DATA(rx_pkt_info);
- vos_mem_zero(result, sizeof(*result) + ie_len);
+ vos_mem_zero(result, sizeof(*result) + ie_len + ie_len);
/* Received frame does not have request id, hence set 0 */
result->requestId = 0;
@@ -275,6 +275,15 @@
vos_mem_copy((uint8_t *) &result->ap.ieData,
body + SIR_MAC_B_PR_SSID_OFFSET, ie_len);
+ /* bss_description points to ap.ieData as ap.ieData is flexible arrary
+ * member.
+ * Fill result->bss_description by leaving ie_len bytes after ap.ieData
+ * manually.
+ */
+ limCollectBssDescription(pmac,
+ (tSirBssDescription *)((uint8_t *)&result->bss_description +
+ ie_len), frame, rx_pkt_info, eANI_BOOLEAN_FALSE);
+
mmh_msg.type = msg_type;
mmh_msg.bodyptr = result;
mmh_msg.bodyval = 0;
@@ -1393,12 +1402,10 @@
break;
case eWNI_SME_SCAN_ABORT_IND:
{
- tSirMbMsg *pMsg = limMsg->bodyptr;
- tANI_U8 sessionId;
+ tSirSmeScanAbortReq *pMsg = (tSirSmeScanAbortReq *) limMsg->bodyptr;
if (pMsg)
{
- sessionId = (tANI_U8) pMsg->data[0];
- limProcessAbortScanInd(pMac, sessionId);
+ limProcessAbortScanInd(pMac, pMsg->sessionId);
vos_mem_free((v_VOID_t *)limMsg->bodyptr);
limMsg->bodyptr = NULL;
}
@@ -1465,6 +1472,7 @@
case eWNI_SME_NDP_INITIATOR_REQ:
case eWNI_SME_NDP_RESPONDER_REQ:
case eWNI_SME_REGISTER_P2P_ACK_CB:
+ case eWNI_SME_NDP_END_REQ:
// These messages are from HDD
limProcessNormalHddMsg(pMac, limMsg, false); //no need to response to hdd
break;
@@ -2215,6 +2223,8 @@
case SIR_HAL_NDP_INDICATION:
case SIR_HAL_NDP_CONFIRM:
case SIR_HAL_NDP_RESPONDER_RSP:
+ case SIR_HAL_NDP_END_RSP:
+ case SIR_HAL_NDP_END_IND:
lim_handle_ndp_event_message(pMac, limMsg);
break;
default:
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMlmReqMessages.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMlmReqMessages.c
index ed1eb79..7b8394c 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMlmReqMessages.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMlmReqMessages.c
@@ -2823,10 +2823,12 @@
mlmDisassocCnf.resultCode = eSIR_SME_INVALID_PARAMETERS;
goto end;
}
- limLog(pMac, LOG1,FL("Process DisAssoc Req on sessionID %d Systemrole %d "
- "mlmstate %d from: "MAC_ADDRESS_STR), pMlmDisassocReq->sessionId,
- GET_LIM_SYSTEM_ROLE(psessionEntry), psessionEntry->limMlmState,
- MAC_ADDR_ARRAY(pMlmDisassocReq->peerMacAddr));
+ limLog(pMac, LOGE, FL("Process DisAssoc Req on sessionID %d Systemrole %d "
+ "reason code: %d mlmstate %d from: "MAC_ADDRESS_STR),
+ pMlmDisassocReq->sessionId,
+ GET_LIM_SYSTEM_ROLE(psessionEntry), pMlmDisassocReq->reasonCode,
+ psessionEntry->limMlmState,
+ MAC_ADDR_ARRAY(pMlmDisassocReq->peerMacAddr));
sirCopyMacAddr(currentBssId,psessionEntry->bssId);
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMlmRspMessages.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMlmRspMessages.c
index 18421cb..6df6229 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMlmRspMessages.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessMlmRspMessages.c
@@ -2316,15 +2316,26 @@
}
}
+/**
+ * limProcessMlmDelStaRsp() - Process WDA_DELETE_STA_RSP
+ * @pMac: Global MAC context
+ * @limMsgQ: LIM Message pointer
+ *
+ * Return: None
+ */
void limProcessMlmDelStaRsp( tpAniSirGlobal pMac, tpSirMsgQ limMsgQ )
{
- //we need to process the deferred message since the initiating req. there might be nested request.
- //in the case of nested request the new request initiated from the response will take care of resetting
- //the deffered flag.
-
tpPESession psessionEntry;
tpDeleteStaParams pDeleteStaParams;
pDeleteStaParams = (tpDeleteStaParams)limMsgQ->bodyptr;
+
+ /*
+ * we need to process the message deferred since the initiating req.
+ * There might be nested request. In the case of nested request,
+ * the new request initiated from the response will take care of resetting
+ * the deferred flag.
+ */
+
SET_LIM_PROCESS_DEFD_MESGS(pMac, true);
if(NULL == pDeleteStaParams ||
@@ -2344,6 +2355,11 @@
limProcessBtAmpApMlmDelStaRsp(pMac,limMsgQ,psessionEntry);
return;
}
+
+ if (LIM_IS_NDI_ROLE(psessionEntry)) {
+ lim_process_ndi_del_sta_rsp(pMac, limMsgQ, psessionEntry);
+ return;
+ }
limProcessStaMlmDelStaRsp(pMac, limMsgQ,psessionEntry);
}
@@ -2354,8 +2370,7 @@
tSirResultCodes statusCode = eSIR_SME_SUCCESS;
if(limMsgQ->bodyptr == NULL)
{
- limLog( pMac, LOGE,
- FL( "limMsgQ->bodyptry NULL"));
+ limLog(pMac, LOGE, FL("limMsgQ->bodyptr NULL"));
return;
}
pStaDs = dphGetHashEntry(pMac, pDelStaParams->assocId, &psessionEntry->dph.dphHashTable);
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessSmeReqMessages.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessSmeReqMessages.c
index 3f95ad5..39f0112 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessSmeReqMessages.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limProcessSmeReqMessages.c
@@ -157,72 +157,101 @@
#endif // BACKGROUND_SCAN_ENABLED
-// determine if a fresh scan request must be issued or not
-/*
-* PE will do fresh scan, if all of the active sessions are in good state (Link Est or BSS Started)
-* If one of the sessions is not in one of the above states, then PE does not do fresh scan
-* If no session exists (scanning very first time), then PE will always do fresh scan if SME
-* asks it to do that.
-*/
-static tANI_U8
-__limFreshScanReqd(tpAniSirGlobal pMac, tANI_U8 returnFreshResults)
+/**
+ * __limFreshScanReqd() - determine if fresh scan request must be issued or not
+ * @mac_ctx - mac global context
+ * @return_fresh_results - fresh scan requested
+ *
+ * PE will do fresh scan, if all of the active sessions are in good state
+ * (Link Est or BSS Started). If one of the sessions is not in one of the these
+ * states, then PE does not do fresh scan. If no session exists (scanning very
+ * first time), then PE will always do fresh scan if SME asks it to do that.
+ */
+static uint8_t __limFreshScanReqd(tpAniSirGlobal mac_ctx,
+ uint8_t return_fresh_results)
{
+ int i;
+ uint8_t valid_state = true;
- tANI_U8 validState = TRUE;
- int i;
+ limLog(mac_ctx, LOG1, FL("gLimSmeState: %d, return_fresh_results 0x%x"),
+ mac_ctx->lim.gLimSmeState, return_fresh_results);
- limLog(pMac, LOG1, FL("gLimSmeState: %d, returnFreshResults 0x%x"),
- pMac->lim.gLimSmeState, returnFreshResults);
- if(pMac->lim.gLimSmeState != eLIM_SME_IDLE_STATE)
- {
- limLog(pMac, LOG1, FL("return FALSE"));
- return FALSE;
- }
- for(i =0; i < pMac->lim.maxBssId; i++)
- {
+ if (mac_ctx->lim.gLimSmeState != eLIM_SME_IDLE_STATE) {
+ limLog(mac_ctx, LOG1, FL("return false, global_sme_state: %d"),
+ mac_ctx->lim.gLimSmeState);
+ return false;
+ }
- if(pMac->lim.gpSession[i].valid == TRUE)
- {
- limLog(pMac, LOG1,
- FL("session %d, bsstype %d, limSystemRole %d, limSmeState %d"),
- i,
- pMac->lim.gpSession[i].bssType,
- pMac->lim.gpSession[i].limSystemRole,
- pMac->lim.gpSession[i].limSmeState);
- if(!( ( ( (pMac->lim.gpSession[i].bssType == eSIR_INFRASTRUCTURE_MODE) ||
- (pMac->lim.gpSession[i].limSystemRole == eLIM_BT_AMP_STA_ROLE))&&
- (pMac->lim.gpSession[i].limSmeState == eLIM_SME_LINK_EST_STATE) )||
+ for (i = 0; i < mac_ctx->lim.maxBssId; i++) {
+ if (mac_ctx->lim.gpSession[i].valid == false)
+ continue;
- ( ( (pMac->lim.gpSession[i].bssType == eSIR_IBSS_MODE)||
- (pMac->lim.gpSession[i].limSystemRole == eLIM_BT_AMP_AP_ROLE)||
- (pMac->lim.gpSession[i].limSystemRole == eLIM_BT_AMP_STA_ROLE) )&&
- (pMac->lim.gpSession[i].limSmeState == eLIM_SME_NORMAL_STATE) )
- || ( ( ( (pMac->lim.gpSession[i].bssType == eSIR_INFRA_AP_MODE)
- && ( pMac->lim.gpSession[i].pePersona == VOS_P2P_GO_MODE) )
- || (pMac->lim.gpSession[i].limSystemRole == eLIM_AP_ROLE) )
- && (pMac->lim.gpSession[i].limSmeState == eLIM_SME_NORMAL_STATE) )
- ))
- {
- validState = FALSE;
- break;
- }
+ limLog(mac_ctx, LOG1,
+ FL("session %d, bsstype %d, limSystemRole %d, limSmeState %d"),
+ i, mac_ctx->lim.gpSession[i].bssType,
+ mac_ctx->lim.gpSession[i].limSystemRole,
+ mac_ctx->lim.gpSession[i].limSmeState);
- }
- }
+ if (mac_ctx->lim.gpSession[i].bssType == eSIR_NDI_MODE)
+ continue;
+ if (mac_ctx->lim.gpSession[i].bssType ==
+ eSIR_INFRASTRUCTURE_MODE
+ && mac_ctx->lim.gpSession[i].limSmeState ==
+ eLIM_SME_LINK_EST_STATE)
+ continue;
- if((validState) &&
- (returnFreshResults & SIR_BG_SCAN_RETURN_FRESH_RESULTS)) {
- limLog(pMac, LOG1, FL("validState: %d, return TRUE"), validState);
- return TRUE;
- } else {
- limLog(pMac, LOG1, FL("validState: %d, return FALSE"), validState);
- return FALSE;
- }
+ if (mac_ctx->lim.gpSession[i].limSystemRole ==
+ eLIM_BT_AMP_STA_ROLE
+ && mac_ctx->lim.gpSession[i].limSmeState ==
+ eLIM_SME_LINK_EST_STATE)
+ continue;
+
+ if (mac_ctx->lim.gpSession[i].bssType == eSIR_IBSS_MODE
+ && mac_ctx->lim.gpSession[i].limSmeState ==
+ eLIM_SME_NORMAL_STATE)
+ continue;
+
+ if (mac_ctx->lim.gpSession[i].limSystemRole ==
+ eLIM_BT_AMP_AP_ROLE
+ && mac_ctx->lim.gpSession[i].limSmeState ==
+ eLIM_SME_NORMAL_STATE)
+ continue;
+
+ if (mac_ctx->lim.gpSession[i].limSystemRole ==
+ eLIM_BT_AMP_STA_ROLE
+ && mac_ctx->lim.gpSession[i].limSmeState ==
+ eLIM_SME_NORMAL_STATE)
+ continue;
+
+ if (mac_ctx->lim.gpSession[i].bssType == eSIR_INFRA_AP_MODE
+ && mac_ctx->lim.gpSession[i].pePersona ==
+ VOS_P2P_GO_MODE
+ && mac_ctx->lim.gpSession[i].limSmeState ==
+ eLIM_SME_NORMAL_STATE)
+ continue;
+
+ if (mac_ctx->lim.gpSession[i].limSystemRole == eLIM_AP_ROLE
+ && mac_ctx->lim.gpSession[i].limSmeState ==
+ eLIM_SME_NORMAL_STATE)
+ continue;
+
+ valid_state = false;
+ break;
+ }
+
+ if (valid_state && (return_fresh_results
+ & SIR_BG_SCAN_RETURN_FRESH_RESULTS)) {
+ limLog(mac_ctx, LOG1, FL("valid_state: %d, return true"),
+ valid_state);
+ return true;
+ } else {
+ limLog(mac_ctx, LOG1, FL("valid_state: %d, return false"),
+ valid_state);
+ return false;
+ }
}
-
-
/**
* __limIsSmeAssocCnfValid()
*
@@ -743,6 +772,8 @@
psessionEntry->ssidHidden = pSmeStartBssReq->ssidHidden;
psessionEntry->wps_state = pSmeStartBssReq->wps_state;
psessionEntry->sap_dot11mc = pSmeStartBssReq->sap_dot11mc;
+ psessionEntry->vendor_vht_for_24ghz_sap =
+ pSmeStartBssReq->vendor_vht_for_24ghz_sap;
limGetShortSlotFromPhyMode(pMac, psessionEntry,
psessionEntry->gLimPhyMode,
&psessionEntry->shortSlotTimeSupported);
@@ -1193,7 +1224,7 @@
/* The tSirScanOffloadReq will reserve the space for first channel,
so allocate the memory for (numChannels - 1) and uIEFieldLen */
len = sizeof(tSirScanOffloadReq) + (pScanReq->channelList.numChannels - 1) +
- pScanReq->uIEFieldLen;
+ pScanReq->uIEFieldLen + pScanReq->oui_field_len;
if (!pMac->per_band_chainmask_supp) {
if (IS_DOT11_MODE_HT(pScanReq->dot11mode)) {
@@ -1287,7 +1318,8 @@
pScanOffloadReq->uIEFieldLen = pScanReq->uIEFieldLen;
pScanOffloadReq->uIEFieldOffset = len - addn_ie_len -
- pScanOffloadReq->uIEFieldLen;
+ pScanOffloadReq->uIEFieldLen -
+ pScanReq->oui_field_len;
vos_mem_copy(
(tANI_U8 *) pScanOffloadReq + pScanOffloadReq->uIEFieldOffset,
(tANI_U8 *) pScanReq + pScanReq->uIEFieldOffset,
@@ -1323,6 +1355,32 @@
#endif /* WLAN_FEATURE_11AC */
}
+ pScanOffloadReq->enable_scan_randomization =
+ pScanReq->enable_scan_randomization;
+ if (pScanOffloadReq->enable_scan_randomization) {
+ vos_mem_copy(pScanOffloadReq->mac_addr, pScanReq->mac_addr,
+ VOS_MAC_ADDR_SIZE);
+ vos_mem_copy(pScanOffloadReq->mac_addr_mask, pScanReq->mac_addr_mask,
+ VOS_MAC_ADDR_SIZE);
+ }
+
+ pScanOffloadReq->oui_field_len = pScanReq->oui_field_len;
+ pScanOffloadReq->num_vendor_oui = pScanReq->num_vendor_oui;
+ pScanOffloadReq->ie_whitelist = pScanReq->ie_whitelist;
+ if (pScanOffloadReq->ie_whitelist)
+ vos_mem_copy(pScanOffloadReq->probe_req_ie_bitmap,
+ pScanReq->probe_req_ie_bitmap,
+ PROBE_REQ_BITMAP_LEN * sizeof(uint32_t));
+ pScanOffloadReq->oui_field_offset = sizeof(tSirScanOffloadReq) +
+ (pScanOffloadReq->channelList.numChannels - 1) +
+ pScanOffloadReq->uIEFieldLen;
+ if (pScanOffloadReq->num_vendor_oui != 0) {
+ vos_mem_copy(
+ (tANI_U8 *) pScanOffloadReq + pScanOffloadReq->oui_field_offset,
+ (uint8_t *) pScanReq + pScanReq->oui_field_offset,
+ pScanReq->oui_field_len);
+ }
+
rc = wdaPostCtrlMsg(pMac, &msg);
if (rc != eSIR_SUCCESS)
{
@@ -6283,6 +6341,7 @@
break;
case eWNI_SME_NDP_INITIATOR_REQ:
case eWNI_SME_NDP_RESPONDER_REQ:
+ case eWNI_SME_NDP_END_REQ:
lim_handle_ndp_request_message(pMac, pMsg);
break;
case eWNI_SME_REGISTER_P2P_ACK_CB:
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limPropExtsUtils.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limPropExtsUtils.c
index bf7e910..752c1fc 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limPropExtsUtils.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limPropExtsUtils.c
@@ -153,7 +153,7 @@
psessionEntry->vhtTxChannelWidthSet = vht_ch_wd;
if (pBeaconStruct->Vendor1IEPresent &&
- pBeaconStruct->Vendor2IEPresent &&
+ pBeaconStruct->vendor2_ie.present &&
pBeaconStruct->Vendor3IEPresent)
{
if (((pBeaconStruct->VHTCaps.txMCSMap & VHT_MCS_3x3_MASK) ==
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limScanResultUtils.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limScanResultUtils.c
index 5ddb392..6028f09 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limScanResultUtils.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limScanResultUtils.c
@@ -179,7 +179,7 @@
sizeof(tSirMacAddr));
// Copy Timestamp, Beacon Interval and Capability Info
- pBssDescr->scanSysTimeMsec = vos_timer_get_system_time();
+ pBssDescr->scansystimensec = vos_get_monotonic_boottime_ns();
pBssDescr->timeStamp[0] = pBPR->timeStamp[0];
pBssDescr->timeStamp[1] = pBPR->timeStamp[1];
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSendManagementFrames.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSendManagementFrames.c
index 8f9167c..f0ee58b 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSendManagementFrames.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSendManagementFrames.c
@@ -1342,6 +1342,23 @@
PopulateDot11fVHTOperation( pMac, psessionEntry, &frm.VHTOperation);
isVHTEnabled = eANI_BOOLEAN_TRUE;
}
+
+ if (psessionEntry->vhtCapability &&
+ psessionEntry->vendor_vht_for_24ghz_sap &&
+ (pAssocReq != NULL) && pAssocReq->vendor2_ie.VHTCaps.present) {
+ limLog(pMac, LOG1,
+ FL("Populate Vendor VHT IEs in Assoc Response"));
+ frm.vendor2_ie.present = 1;
+ frm.vendor2_ie.type =
+ psessionEntry->vendor_specific_vht_ie_type;
+ frm.vendor2_ie.sub_type =
+ psessionEntry->vendor_specific_vht_ie_sub_type;
+
+ frm.vendor2_ie.VHTCaps.present = 1;
+ PopulateDot11fVHTCaps(pMac, psessionEntry,
+ &frm.vendor2_ie.VHTCaps);
+ isVHTEnabled = true;
+ }
#endif
PopulateDot11fExtCap(pMac, isVHTEnabled, &frm.ExtCap, psessionEntry);
@@ -2227,6 +2244,23 @@
PopulateDot11fVHTCaps( pMac, psessionEntry, &pFrm->VHTCaps );
isVHTEnabled = eANI_BOOLEAN_TRUE;
}
+ if (!isVHTEnabled &&
+ psessionEntry->is_vendor_specific_vhtcaps) {
+ limLog(pMac, LOG1,
+ FL("Populate Vendor VHT IEs in Assoc Request"));
+ pFrm->vendor2_ie.present = 1;
+ pFrm->vendor2_ie.type =
+ psessionEntry->vendor_specific_vht_ie_type;
+ pFrm->vendor2_ie.sub_type =
+ psessionEntry->vendor_specific_vht_ie_sub_type;
+
+ pFrm->vendor2_ie.VHTCaps.present = 1;
+ PopulateDot11fVHTCaps(pMac, psessionEntry,
+ &pFrm->vendor2_ie.VHTCaps);
+ isVHTEnabled = true;
+ }
+
+
#endif
if (psessionEntry->is_ext_caps_present)
PopulateDot11fExtCap( pMac, isVHTEnabled, &pFrm->ExtCap, psessionEntry);
@@ -2702,6 +2736,20 @@
}
if (psessionEntry->is_ext_caps_present)
PopulateDot11fExtCap(pMac, isVHTEnabled, &frm.ExtCap, psessionEntry);
+
+ if (!isVHTEnabled && psessionEntry->is_vendor_specific_vhtcaps) {
+ limLog(pMac, LOG1,
+ FL("Populate Vendor VHT IEs in Re-Assoc Request"));
+ frm.vendor2_ie.present = 1;
+ frm.vendor2_ie.type =
+ psessionEntry->vendor_specific_vht_ie_type;
+ frm.vendor2_ie.sub_type =
+ psessionEntry->vendor_specific_vht_ie_sub_type;
+ frm.vendor2_ie.VHTCaps.present = 1;
+ PopulateDot11fVHTCaps(pMac, psessionEntry,
+ &frm.vendor2_ie.VHTCaps);
+ isVHTEnabled = true;
+ }
#endif
nStatus = dot11fGetPackedReAssocRequestSize( pMac, &frm, &nPayload );
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSerDesUtils.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSerDesUtils.c
index 56b43e2..5ad6c00 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSerDesUtils.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limSerDesUtils.c
@@ -126,8 +126,8 @@
return eSIR_FAILURE;
// Extract timer
- vos_mem_copy( (tANI_U8 *) (&pBssDescription->scanSysTimeMsec),
- pBuf, sizeof(v_TIME_t));
+ vos_mem_copy( (tANI_U8 *) (&pBssDescription->scansystimensec),
+ pBuf, sizeof(v_TIME_t));
pBuf += sizeof(v_TIME_t);
len -= sizeof(v_TIME_t);
if (limCheckRemainingLength(pMac, len) == eSIR_FAILURE)
@@ -694,6 +694,10 @@
pStartBssReq->sap_dot11mc = *pBuf++;
len--;
+ /* extract vendor_vht_for_24ghz_sap */
+ pStartBssReq->vendor_vht_for_24ghz_sap = *pBuf++;
+ len--;
+
if (len)
{
limLog(pMac, LOGW, FL("Extra bytes left in SME_START_BSS_REQ, len=%d"), len);
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limTimerUtils.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limTimerUtils.c
index 8953530..4d86881 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limTimerUtils.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limTimerUtils.c
@@ -1063,17 +1063,6 @@
if (pMac->lim.gpLimMlmScanReq) {
val =
SYS_MS_TO_TICKS(pMac->lim.gpLimMlmScanReq->minChannelTime);
- if (pMac->btc.btcScanCompromise) {
- if (pMac->lim.gpLimMlmScanReq->minChannelTimeBtc) {
- val = SYS_MS_TO_TICKS(
- pMac->lim.gpLimMlmScanReq->minChannelTimeBtc);
- limLog(pMac, LOG1,
- FL("Using BTC Min Active Scan time"));
- } else {
- limLog(pMac, LOGE,
- FL("BTC Active Scan Min Time is Not Set"));
- }
- }
} else {
limLog(pMac, LOGE, FL("gpLimMlmScanReq is NULL"));
break;
@@ -1100,17 +1089,6 @@
}
val = SYS_MS_TO_TICKS(pMac->lim.gpLimMlmScanReq->minChannelTime)/2;
- if (pMac->btc.btcScanCompromise)
- {
- if (pMac->lim.gpLimMlmScanReq->minChannelTimeBtc)
- {
- val = SYS_MS_TO_TICKS(pMac->lim.gpLimMlmScanReq->minChannelTimeBtc)/2;
- }
- else
- {
- limLog(pMac, LOGE, FL("BTC Active Scan Min Time is Not Set"));
- }
- }
if (val)
{
if (tx_timer_change(&pMac->lim.limTimers.gLimPeriodicProbeReqTimer,
@@ -1151,17 +1129,6 @@
if (pMac->lim.gpLimMlmScanReq) {
val = SYS_MS_TO_TICKS(
pMac->lim.gpLimMlmScanReq->maxChannelTime);
- if (pMac->btc.btcScanCompromise) {
- if (pMac->lim.gpLimMlmScanReq->maxChannelTimeBtc) {
- val = SYS_MS_TO_TICKS(
- pMac->lim.gpLimMlmScanReq->maxChannelTimeBtc);
- limLog(pMac, LOG1,
- FL("Using BTC Max Active Scan time"));
- } else {
- limLog(pMac, LOGE,
- FL("BTC Active Scan Max Time is Not Set"));
- }
- }
} else {
limLog(pMac, LOGE, FL("gpLimMlmScanReq is NULL"));
break;
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limTrace.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limTrace.c
index 8ad0e21..f0d1218 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limTrace.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limTrace.c
@@ -151,19 +151,19 @@
switch (pRecord->code) {
case TRACE_CODE_MLM_STATE:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"MLM State:", limTraceGetMlmStateString((tANI_U16)pRecord->data),
pRecord->data );
break;
case TRACE_CODE_SME_STATE:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"SME State:", limTraceGetSmeStateString((tANI_U16)pRecord->data),
pRecord->data );
break;
case TRACE_CODE_TX_MGMT:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"TX Mgmt:", frameSubtypeStr[pRecord->data], pRecord->data);
break;
@@ -176,7 +176,7 @@
}
else
{
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(%d) SN: %d",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(%d) SN: %d",
recIndex, pRecord->time, pRecord->session,
"RX Mgmt:",
frameSubtypeStr[LIM_TRACE_GET_SUBTYPE(pRecord->data)],
@@ -185,7 +185,7 @@
}
break;
case TRACE_CODE_RX_MGMT_DROP:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(%d)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(%d)",
recIndex, pRecord->time, pRecord->session,
"Drop RX Mgmt:",
__limTraceGetMgmtDropReasonString((tANI_U16)pRecord->data),
@@ -194,26 +194,26 @@
case TRACE_CODE_RX_MGMT_TSF:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s0x%x(%d)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s0x%x(%d)",
recIndex, pRecord->time, pRecord->session,
"RX Mgmt TSF:", " ", pRecord->data, pRecord->data );
break;
case TRACE_CODE_TX_COMPLETE:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %d" ,
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %d" ,
recIndex, pRecord->time, pRecord->session,
"TX Complete", pRecord->data );
break;
case TRACE_CODE_TX_SME_MSG:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"TX SME Msg:",
macTraceGetSmeMsgString((tANI_U16)pRecord->data),
pRecord->data );
break;
case TRACE_CODE_RX_SME_MSG:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
LIM_TRACE_GET_DEFRD_OR_DROPPED(pRecord->data) ?
"Def/Drp LIM Msg:": "RX Sme Msg:",
@@ -222,7 +222,7 @@
break;
case TRACE_CODE_TX_WDA_MSG:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"TX WDA Msg:",
macTraceGetWdaMsgString((tANI_U16)pRecord->data),
@@ -230,7 +230,7 @@
break;
case TRACE_CODE_RX_WDA_MSG:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
LIM_TRACE_GET_DEFRD_OR_DROPPED(pRecord->data) ?
"Def/Drp LIM Msg:": "RX WDA Msg:",
@@ -239,14 +239,14 @@
break;
case TRACE_CODE_TX_LIM_MSG:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"TX LIM Msg:",
macTraceGetLimMsgString((tANI_U16)pRecord->data),
pRecord->data );
break;
case TRACE_CODE_RX_LIM_MSG:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
LIM_TRACE_GET_DEFRD_OR_DROPPED(pRecord->data) ?
"Def/Drp LIM Msg:": "RX LIM Msg",
@@ -254,14 +254,14 @@
pRecord->data );
break;
case TRACE_CODE_TX_CFG_MSG:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"TX CFG Msg:",
macTraceGetCfgMsgString((tANI_U16)pRecord->data),
pRecord->data );
break;
case TRACE_CODE_RX_CFG_MSG:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
LIM_TRACE_GET_DEFRD_OR_DROPPED(pRecord->data) ?
"Def/Drp LIM Msg:": "RX CFG Msg:",
@@ -271,14 +271,14 @@
break;
case TRACE_CODE_TIMER_ACTIVATE:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"Timer Actvtd",
__limTraceGetTimerString((tANI_U16)pRecord->data),
pRecord->data );
break;
case TRACE_CODE_TIMER_DEACTIVATE:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"Timer DeActvtd",
__limTraceGetTimerString((tANI_U16)pRecord->data),
@@ -286,14 +286,14 @@
break;
case TRACE_CODE_INFO_LOG:
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"INFORMATION_LOG",
macTraceGetInfoLogString((tANI_U16)pRecord->data),
pRecord->data );
break;
default :
- limLog(pMac, LOG1, "%04d %012llu S%d %-14s(%d) (0x%x)",
+ limLog(pMac, LOG1, "%04d %s S%d %-14s(%d) (0x%x)",
recIndex, pRecord->time, pRecord->session,
"Unknown Code", pRecord->code, pRecord->data );
break;
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limUtils.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limUtils.c
index 5296278..3db4e90 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limUtils.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limUtils.c
@@ -5246,28 +5246,6 @@
}
/** -------------------------------------------------------------
-\fn limDelAllBASessionsBtc
-\brief Deletes all the existing BA recipient sessions in 2.4GHz
- band.
-\param tpAniSirGlobal pMac
-\return None
--------------------------------------------------------------*/
-
-void limDelPerBssBASessionsBtc(tpAniSirGlobal pMac)
-{
- tANI_U8 sessionId;
- tpPESession pSessionEntry;
- pSessionEntry = peFindSessionByBssid(pMac,pMac->btc.btcBssfordisableaggr,
- &sessionId);
- if (pSessionEntry)
- {
- PELOGW(limLog(pMac, LOGW,
- "Deleting the BA for session %d as host got BTC event", sessionId);)
- limDeleteBASessions(pMac, pSessionEntry, BA_RECIPIENT);
- }
-}
-
-/** -------------------------------------------------------------
\fn limProcessDelTsInd
\brief Handles the DeleteTS indication coming from HAL or generated by
PE itself in some error cases. Validates the request, sends the
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limUtils.h b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limUtils.h
index d252ba4..d81c759 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limUtils.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/lim/limUtils.h
@@ -398,7 +398,6 @@
void limDeleteStaContext(tpAniSirGlobal pMac, tpSirMsgQ limMsg);
void limProcessAddBaInd(tpAniSirGlobal pMac, tpSirMsgQ limMsg);
void limDeleteBASessions(tpAniSirGlobal pMac, tpPESession pSessionEntry, tANI_U32 baDirection);
-void limDelPerBssBASessionsBtc(tpAniSirGlobal pMac);
void limDelAllBASessions(tpAniSirGlobal pMac);
void limDeleteDialogueTokenList(tpAniSirGlobal pMac);
tSirRetStatus limSearchAndDeleteDialogueToken(tpAniSirGlobal pMac, tANI_U8 token, tANI_U16 assocId, tANI_U16 tid);
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/nan/nan_datapath.c b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/nan/nan_datapath.c
index 91afbe5..7d320cc 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/nan/nan_datapath.c
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/nan/nan_datapath.c
@@ -131,8 +131,9 @@
VOS_STATUS status = VOS_STATUS_SUCCESS;
limLog(mac_ctx, LOG1,
- FL("role: %d, vdev: %d, peer_mac_addr "MAC_ADDRESS_STR),
- ndp_ind->role, ndp_ind->vdev_id,
+ FL("role: %d, vdev: %d, csid: %d, peer_mac_addr "
+ MAC_ADDRESS_STR),
+ ndp_ind->role, ndp_ind->vdev_id, ndp_ind->ncs_sk_type,
MAC_ADDR_ARRAY(ndp_ind->peer_mac_addr.bytes));
if ((ndp_ind->role == NDP_ROLE_INITIATOR) ||
@@ -197,7 +198,8 @@
goto responder_rsp;
}
- if (VOS_STATUS_SUCCESS == rsp_ind->status) {
+ if (VOS_STATUS_SUCCESS == rsp_ind->status &&
+ rsp_ind->create_peer == true) {
ret_val = lim_add_ndi_peer(mac_ctx, rsp_ind->vdev_id,
rsp_ind->peer_mac_addr);
if (VOS_STATUS_SUCCESS != ret_val) {
@@ -216,6 +218,244 @@
}
/**
+ * lim_ndp_delete_peer_by_addr() - Delete NAN data peer, given addr and vdev_id
+ * @mac_ctx: handle to mac context
+ * @vdev_id: vdev_id on which peer was added
+ * @peer_ndi_mac_addr: mac addr of peer
+ * This function deletes a peer if there was NDP_Confirm with REJECT
+ *
+ * Return: None
+ */
+void lim_ndp_delete_peer_by_addr(tpAniSirGlobal mac_ctx, uint8_t vdev_id,
+ v_MACADDR_t peer_ndi_mac_addr)
+{
+ tpPESession session;
+ tpDphHashNode sta_ds;
+ uint16_t peer_idx;
+
+ limLog(mac_ctx, LOG1,
+ FL("deleting peer: "MAC_ADDRESS_STR" confirm rejected"),
+ MAC_ADDR_ARRAY(peer_ndi_mac_addr.bytes));
+
+ session = pe_find_session_by_sme_session_id(mac_ctx, vdev_id);
+ if (!session || (session->bssType != eSIR_NDI_MODE)) {
+ limLog(mac_ctx, LOGE,
+ FL("PE session is NULL or non-NDI for sme session %d"),
+ vdev_id);
+ return;
+ }
+
+ sta_ds = dphLookupHashEntry(mac_ctx, peer_ndi_mac_addr.bytes,
+ &peer_idx, &session->dph.dphHashTable);
+ if (!sta_ds) {
+ limLog(mac_ctx, LOGE, FL("Unknown NDI Peer"));
+ return;
+ }
+ if (sta_ds->staType != STA_ENTRY_NDI_PEER) {
+ limLog(mac_ctx, LOGE, FL("Non-NDI Peer ignored"));
+ return;
+ }
+ /*
+ * Call limDelSta() with response required set true. Hence DphHashEntry
+ * will be deleted after receiving that response.
+ */
+
+ limDelSta(mac_ctx, sta_ds, true, session);
+}
+
+/**
+ * lim_ndp_delete_peers() - Delete NAN data peers
+ * @mac_ctx: handle to mac context
+ * @ndp_map: NDP instance/peer map
+ * @num_peers: number of peers entries in peer_map
+ * This function deletes a peer if there are no active NDPs left with that peer
+ *
+ * Return: None
+ */
+static void lim_ndp_delete_peers(tpAniSirGlobal mac_ctx,
+ struct peer_ndp_map *ndp_map, uint8_t num_peers)
+{
+ tpDphHashNode sta_ds = NULL;
+ uint16_t deleted_num = 0;
+ int i, j;
+ tpPESession session;
+ v_MACADDR_t *deleted_peers;
+ uint16_t peer_idx;
+ bool found;
+
+ deleted_peers = vos_mem_malloc(num_peers * sizeof(*deleted_peers));
+ if (!deleted_peers) {
+ limLog(mac_ctx, LOGE, FL("Memory allocation failed"));
+ return;
+ }
+
+ vos_mem_zero(deleted_peers, num_peers * sizeof(*deleted_peers));
+ for (i = 0; i < num_peers; i++) {
+ limLog(mac_ctx, LOG1,
+ FL("ndp_map[%d]: MAC: " MAC_ADDRESS_STR "num_active %d"),
+ i,
+ MAC_ADDR_ARRAY(ndp_map[i].peer_ndi_mac_addr.bytes),
+ ndp_map[i].num_active_ndp_sessions);
+
+ /* Do not delete a peer with active NDPs */
+ if (ndp_map[i].num_active_ndp_sessions > 0)
+ continue;
+
+ session = pe_find_session_by_sme_session_id(mac_ctx,
+ ndp_map[i].vdev_id);
+ if (!session || (session->bssType != eSIR_NDI_MODE)) {
+ limLog(mac_ctx, LOGE,
+ FL("PE session is NULL or non-NDI for sme session %d"),
+ ndp_map[i].vdev_id);
+ continue;
+ }
+
+ /* Check if this peer is already in the deleted list */
+ found = false;
+ for (j = 0; j < deleted_num && !found; j++) {
+ if (vos_mem_compare(
+ &deleted_peers[j].bytes,
+ &ndp_map[i].peer_ndi_mac_addr.bytes,
+ VOS_MAC_ADDR_SIZE)) {
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ continue;
+
+ sta_ds = dphLookupHashEntry(mac_ctx,
+ ndp_map[i].peer_ndi_mac_addr.bytes,
+ &peer_idx, &session->dph.dphHashTable);
+ if (!sta_ds) {
+ limLog(mac_ctx, LOGE, FL("Unknown NDI Peer"));
+ continue;
+ }
+ if (sta_ds->staType != STA_ENTRY_NDI_PEER) {
+ limLog(mac_ctx, LOGE,
+ FL("Non-NDI Peer ignored"));
+ continue;
+ }
+ /*
+ * Call limDelSta() with response required set true.
+ * Hence DphHashEntry will be deleted after receiving
+ * that response.
+ */
+ limDelSta(mac_ctx, sta_ds, true, session);
+ vos_copy_macaddr(&deleted_peers[deleted_num++],
+ &ndp_map[i].peer_ndi_mac_addr);
+ }
+ vos_mem_free(deleted_peers);
+}
+
+/**
+ * lim_ndp_end_indication_handler() - Handler for NDP end indication
+ * @mac_ctx: handle to mac context
+ * @ind_buf: pointer to indication buffer
+ *
+ * It deletes peers from ndp_map. Response of that operation goes
+ * to LIM and HDD. But peer information does not go to service layer.
+ * ndp_id_list is sent to service layer; it is not interpreted by the
+ * driver.
+ *
+ * Return: VOS_STATUS_SUCCESS on success; error number otherwise
+ */
+static VOS_STATUS lim_ndp_end_indication_handler(tpAniSirGlobal mac_ctx,
+ uint32_t *ind_buf)
+{
+
+ struct ndp_end_indication_event *ndp_event_buf =
+ (struct ndp_end_indication_event *)ind_buf;
+ int buf_size;
+
+ if (!ind_buf) {
+ limLog(mac_ctx, LOGE, FL("NDP end indication buffer is NULL"));
+ return VOS_STATUS_E_INVAL;
+ }
+ lim_ndp_delete_peers(mac_ctx, ndp_event_buf->ndp_map,
+ ndp_event_buf->num_ndp_ids);
+
+ buf_size = sizeof(*ndp_event_buf) + ndp_event_buf->num_ndp_ids *
+ sizeof(ndp_event_buf->ndp_map[0]);
+ lim_send_ndp_event_to_sme(mac_ctx, eWNI_SME_NDP_END_IND,
+ ndp_event_buf, buf_size, false);
+
+ return VOS_STATUS_SUCCESS;
+}
+
+/**
+ * lim_process_ndi_del_sta_rsp() - Handle WDA_DELETE_STA_RSP in eLIM_NDI_ROLE
+ * @mac_ctx: Global MAC context
+ * @lim_msg: LIM message
+ * @pe_session: PE session
+ *
+ * Return: None
+ */
+void lim_process_ndi_del_sta_rsp(tpAniSirGlobal mac_ctx, tpSirMsgQ lim_msg,
+ tpPESession pe_session)
+{
+ tpDeleteStaParams del_sta_params = (tpDeleteStaParams) lim_msg->bodyptr;
+ tpDphHashNode sta_ds;
+ tSirResultCodes status = eSIR_SME_SUCCESS;
+ struct sme_ndp_peer_ind peer_ind;
+
+ if (!del_sta_params) {
+ limLog(mac_ctx, LOGE,
+ FL("del_sta_params is NULL"));
+ return;
+ }
+ if (!LIM_IS_NDI_ROLE(pe_session)) {
+ limLog(mac_ctx, LOGE,
+ FL("Session %d is not NDI role"), del_sta_params->assocId);
+ status = eSIR_SME_REFUSED;
+ goto skip_event;
+ }
+
+ sta_ds = dphGetHashEntry(mac_ctx, del_sta_params->assocId,
+ &pe_session->dph.dphHashTable);
+ if (!sta_ds) {
+ limLog(mac_ctx, LOGE,
+ FL("DPH Entry for STA %X is missing."),
+ del_sta_params->assocId);
+ status = eSIR_SME_REFUSED;
+ goto skip_event;
+ }
+
+ if (eHAL_STATUS_SUCCESS != del_sta_params->status) {
+ limLog(mac_ctx, LOGE, FL("DEL STA failed!"));
+ status = eSIR_SME_REFUSED;
+ goto skip_event;
+ }
+ limLog(mac_ctx, LOG1,
+ FL("Deleted STA AssocID %d staId %d MAC " MAC_ADDRESS_STR),
+ sta_ds->assocId, sta_ds->staIndex,
+ MAC_ADDR_ARRAY(sta_ds->staAddr));
+
+ /*
+ * Copy peer info in del peer indication before
+ * limDeleteDphHashEntry is called as this will be lost.
+ */
+ peer_ind.msg_len = sizeof(peer_ind);
+ peer_ind.msg_type = eWNI_SME_NDP_PEER_DEPARTED_IND;
+ peer_ind.session_id = pe_session->smeSessionId;
+ peer_ind.sta_id = sta_ds->staIndex;
+ vos_mem_copy(&peer_ind.peer_mac_addr.bytes,
+ sta_ds->staAddr, sizeof(tSirMacAddr));
+
+ limReleasePeerIdx(mac_ctx, sta_ds->assocId, pe_session);
+ limDeleteDphHashEntry(mac_ctx, sta_ds->staAddr, sta_ds->assocId,
+ pe_session);
+ pe_session->limMlmState = eLIM_MLM_IDLE_STATE;
+
+ lim_send_ndp_event_to_sme(mac_ctx, eWNI_SME_NDP_PEER_DEPARTED_IND,
+ &peer_ind, sizeof(peer_ind), false);
+
+skip_event:
+ vos_mem_free(del_sta_params);
+ lim_msg->bodyptr = NULL;
+}
+
+/**
* lim_handle_ndp_event_message() - Handler for NDP events/RSP from WMA
* @mac_ctx: handle to mac structure
* @msg: pointer to message
@@ -227,25 +467,50 @@
VOS_STATUS status = VOS_STATUS_SUCCESS;
switch (msg->type) {
- case SIR_HAL_NDP_CONFIRM:
+ case SIR_HAL_NDP_CONFIRM: {
+ struct ndp_confirm_event *ndp_confirm = msg->bodyptr;
+ if (ndp_confirm->rsp_code != NDP_RESPONSE_ACCEPT &&
+ ndp_confirm->num_active_ndps_on_peer == 0) {
+ /*
+ * This peer was created at ndp_indication but
+ * ndp_confirm failed, so it needs to be deleted
+ */
+ limLog(mac_ctx, LOGE,
+ FL("NDP confirm with reject and no active ndp sessions. deleting peer: "MAC_ADDRESS_STR" on vdev_id: %d"),
+ MAC_ADDR_ARRAY(
+ ndp_confirm->peer_ndi_mac_addr.bytes),
+ ndp_confirm->vdev_id);
+ lim_ndp_delete_peer_by_addr(mac_ctx,
+ ndp_confirm->vdev_id,
+ ndp_confirm->peer_ndi_mac_addr);
+ }
lim_send_ndp_event_to_sme(mac_ctx, eWNI_SME_NDP_CONFIRM_IND,
- msg->bodyptr, sizeof(struct ndp_confirm_event),
+ msg->bodyptr, sizeof(*ndp_confirm),
msg->bodyval);
break;
+ }
case SIR_HAL_NDP_INITIATOR_RSP:
lim_send_ndp_event_to_sme(mac_ctx, eWNI_SME_NDP_INITIATOR_RSP,
msg->bodyptr, sizeof(struct ndp_initiator_rsp),
msg->bodyval);
break;
- case SIR_HAL_NDP_INDICATION: {
- struct ndp_indication_event *ndp_ind = msg->bodyptr;
- status = lim_handle_ndp_indication_event(mac_ctx, ndp_ind);
+ case SIR_HAL_NDP_END_RSP: {
+ lim_send_ndp_event_to_sme(mac_ctx, eWNI_SME_NDP_END_RSP,
+ msg->bodyptr,
+ sizeof(struct ndp_end_rsp_event),
+ msg->bodyval);
break;
}
+ case SIR_HAL_NDP_INDICATION:
+ status = lim_handle_ndp_indication_event(mac_ctx, msg->bodyptr);
+ break;
case SIR_HAL_NDP_RESPONDER_RSP:
status = lim_ndp_responder_rsp_handler(mac_ctx, msg->bodyptr,
msg->bodyval);
break;
+ case SIR_HAL_NDP_END_IND:
+ status = lim_ndp_end_indication_handler(mac_ctx, msg->bodyptr);
+ break;
default:
limLog(mac_ctx, LOGE, FL("Unhandled NDP event: %d"), msg->type);
status = VOS_STATUS_E_NOSUPPORT;
@@ -355,6 +620,55 @@
}
/**
+ * lim_process_sme_ndp_data_end_req() - Handler for eWNI_SME_NDP_END_REQ
+ * from SME.
+ * @mac_ctx: handle to mac context
+ * @sme_msg: ndp data end request msg
+ *
+ * Return: Status of operation
+ */
+VOS_STATUS lim_process_sme_ndp_data_end_req(tpAniSirGlobal mac_ctx,
+ struct sir_sme_ndp_end_req *sme_msg)
+{
+ tSirMsgQ msg;
+ uint32_t len;
+ VOS_STATUS status = VOS_STATUS_SUCCESS;
+
+ if (NULL == sme_msg) {
+ limLog(mac_ctx, LOGE, FL("invalid ndp_req"));
+ /* msg to unblock SME, but not send rsp to HDD */
+ lim_send_ndp_event_to_sme(mac_ctx, eWNI_SME_NDP_END_RSP, NULL,
+ 0, true);
+ return VOS_STATUS_E_INVAL;
+ }
+
+ msg.type = SIR_HAL_NDP_END_REQ;
+ msg.reserved = 0;
+ len = sizeof(*sme_msg->req) + (sme_msg->req->num_ndp_instances *
+ sizeof(uint32_t));
+ msg.bodyptr = vos_mem_malloc(len);
+ if (NULL == msg.bodyptr) {
+ /* msg to unblock SME, but not send rsp to HDD */
+ lim_send_ndp_event_to_sme(mac_ctx, eWNI_SME_NDP_END_RSP, NULL,
+ 0, true);
+ return VOS_STATUS_E_NOMEM;
+ }
+ vos_mem_copy(msg.bodyptr, sme_msg->req, len);
+ msg.bodyval = 0;
+
+ limLog(mac_ctx, LOG1, FL("sending SIR_HAL_NDP_END_REQ to WMA"));
+ MTRACE(macTraceMsgTx(mac_ctx, NO_SESSION, msg.type));
+
+ if (eSIR_SUCCESS != wdaPostCtrlMsg(mac_ctx, &msg)) {
+ limLog(mac_ctx, LOGE,
+ FL("Post msg failed for SIR_HAL_NDP_END_REQ"));
+ status = VOS_STATUS_E_FAILURE;
+ }
+
+ return status;
+}
+
+/**
* lim_handle_ndp_request_message() - Handler for NDP req from SME
* @mac_ctx: handle to mac structure
* @msg: pointer to message
@@ -367,6 +681,10 @@
VOS_STATUS status;
switch (msg->type) {
+ case eWNI_SME_NDP_END_REQ:
+ status = lim_process_sme_ndp_data_end_req(mac_ctx,
+ msg->bodyptr);
+ break;
case eWNI_SME_NDP_INITIATOR_REQ:
status = lim_process_sme_ndp_initiator_req(mac_ctx,
msg->bodyptr);
@@ -413,6 +731,7 @@
session_entry->bssIdx = (uint8_t) add_bss_params->bssIdx;
session_entry->limSystemRole = eLIM_NDI_ROLE;
session_entry->statypeForBss = STA_ENTRY_SELF;
+ session_entry->staId = add_bss_params->staContext.staIdx;
/* Apply previously set configuration at HW */
limApplyConfiguration(mac_ctx, session_entry);
mlm_start_cnf.resultCode = eSIR_SME_SUCCESS;
diff --git a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/nan/nan_datapath.h b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/nan/nan_datapath.h
index 8c72126..4f247ee 100644
--- a/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/nan/nan_datapath.h
+++ b/drivers/staging/qcacld-2.0/CORE/MAC/src/pe/nan/nan_datapath.h
@@ -118,6 +118,9 @@
void lim_ndp_add_sta_rsp(tpAniSirGlobal mac_ctx, tpPESession session_entry,
tAddStaParams *add_sta_rsp);
+void lim_process_ndi_del_sta_rsp(tpAniSirGlobal mac_ctx, tpSirMsgQ lim_msg,
+ tpPESession pe_session);
+
#else
/* Function to process NDP requests */
@@ -144,6 +147,10 @@
void *msg, tpPESession session_entry)
{
}
+static inline void lim_process_ndi_del_sta_rsp(tpAniSirGlobal mac_ctx,
+ tpSirMsgQ lim_msg, tpPESession pe_session)
+{
+}
static inline void lim_ndp_add_sta_rsp(tpAniSirGlobal mac_ctx,
tpPESession session_entry,
@@ -154,4 +161,3 @@
#endif /* WLAN_FEATURE_NAN_DATAPATH */
#endif /* __MAC_NAN_DATAPATH_H */
-
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_nbuf.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_nbuf.c
index dc0ef31..87b6f5d 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_nbuf.c
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_nbuf.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -708,6 +708,9 @@
subtype = (uint8_t)(*(uint8_t *)
(data + ICMP_SUBTYPE_OFFSET));
+ VOS_TRACE(VOS_MODULE_ID_ADF, VOS_TRACE_LEVEL_DEBUG,
+ "ICMP proto type: 0x%02x", subtype);
+
switch (subtype) {
case ICMP_REQUEST:
proto_subtype = ADF_PROTO_ICMP_REQ;
@@ -740,6 +743,9 @@
subtype = (uint8_t)(*(uint8_t *)
(data + ICMPV6_SUBTYPE_OFFSET));
+ VOS_TRACE(VOS_MODULE_ID_ADF, VOS_TRACE_LEVEL_DEBUG,
+ "ICMPv6 proto type: 0x%02x", subtype);
+
switch (subtype) {
case ICMPV6_REQUEST:
proto_subtype = ADF_PROTO_ICMPV6_REQ;
@@ -747,6 +753,12 @@
case ICMPV6_RESPONSE:
proto_subtype = ADF_PROTO_ICMPV6_RES;
break;
+ case ICMPV6_RS:
+ proto_subtype = ADF_PROTO_ICMPV6_RS;
+ break;
+ case ICMPV6_RA:
+ proto_subtype = ADF_PROTO_ICMPV6_RA;
+ break;
case ICMPV6_NS:
proto_subtype = ADF_PROTO_ICMPV6_NS;
break;
@@ -804,11 +816,10 @@
*
* This func. checks whether it is a DHCP packet or not.
*
- * Return: A_STATUS_OK if it is a DHCP packet
- * A_STATUS_FAILED if not
+ * Return: TRUE if it is a DHCP packet
+ * FALSE if not
*/
-a_status_t
-__adf_nbuf_data_is_dhcp_pkt(uint8_t *data)
+bool __adf_nbuf_data_is_dhcp_pkt(uint8_t *data)
{
a_uint16_t SPort;
a_uint16_t DPort;
@@ -823,11 +834,11 @@
((ADF_NBUF_TRAC_DHCP_CLI_PORT == adf_os_cpu_to_be16(SPort)) &&
(ADF_NBUF_TRAC_DHCP_SRV_PORT == adf_os_cpu_to_be16(DPort))))
{
- return A_STATUS_OK;
+ return true;
}
else
{
- return A_STATUS_FAILED;
+ return false;
}
}
@@ -837,11 +848,10 @@
*
* This func. checks whether it is a EAPOL packet or not.
*
- * Return: A_STATUS_OK if it is a EAPOL packet
- * A_STATUS_FAILED if not
+ * Return: TRUE if it is a EAPOL packet
+ * FALSE if not
*/
-a_status_t
-__adf_nbuf_data_is_eapol_pkt(uint8_t *data)
+bool __adf_nbuf_data_is_eapol_pkt(uint8_t *data)
{
a_uint16_t ether_type;
@@ -849,11 +859,11 @@
ADF_NBUF_TRAC_ETH_TYPE_OFFSET));
if (ADF_NBUF_TRAC_EAPOL_ETH_TYPE == adf_os_cpu_to_be16(ether_type))
{
- return A_STATUS_OK;
+ return true;
}
else
{
- return A_STATUS_FAILED;
+ return false;
}
}
@@ -1250,3 +1260,65 @@
rtap_hdr_len, rtap_len - rtap_hdr_len);
return rtap_len;
}
+
+/**
+ * __adf_nbuf_is_wai_pkt() - Check if frame is WAI
+ * @data: pointer to skb data buffer
+ *
+ * This function checks if the frame is WAI.
+ *
+ * Return: true (1) if WAI
+ *
+ */
+bool __adf_nbuf_is_wai_pkt(uint8_t *data)
+{
+ uint16_t ether_type;
+
+ ether_type = (uint16_t)(*(uint16_t *)
+ (data + ADF_NBUF_TRAC_ETH_TYPE_OFFSET));
+
+ if (ether_type == VOS_SWAP_U16(ADF_NBUF_TRAC_WAI_ETH_TYPE))
+ return true;
+
+ return false;
+}
+
+/**
+ * __adf_nbuf_is_group_pkt() - Check if frame is multicast packet
+ * @data: pointer to skb data buffer
+ *
+ * This function checks if the frame is multicast packet.
+ *
+ * Return: true (1) if multicast
+ *
+ */
+bool __adf_nbuf_is_multicast_pkt(uint8_t *data)
+{
+ struct adf_mac_addr *mac_addr = (struct adf_mac_addr*)data;
+
+ if ( mac_addr->bytes[0] & 0x01 )
+ return true;
+
+ return false;
+}
+
+/**
+ * __adf_nbuf_is_bcast_pkt() - Check if frame is broadcast packet
+ * @data: pointer to skb data buffer
+ *
+ * This function checks if the frame is broadcast packet.
+ *
+ * Return: true (1) if broadcast
+ *
+ */
+bool __adf_nbuf_is_bcast_pkt(uint8_t *data)
+{
+ struct adf_mac_addr *mac_addr = (struct adf_mac_addr*)data;
+ struct adf_mac_addr bcast_addr = VOS_MAC_ADDR_BROADCAST_INITIALIZER;
+
+ if (!memcmp( mac_addr, &bcast_addr, VOS_MAC_ADDR_SIZE))
+ return true;
+
+ return false;
+}
+
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_nbuf.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_nbuf.h
index db80902..1616244 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_nbuf.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_nbuf.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -79,6 +79,7 @@
#define ADF_NBUF_TRAC_TCP_TYPE 6
#define ADF_NBUF_TRAC_UDP_TYPE 17
#define ADF_NBUF_TRAC_ICMPV6_TYPE 0x3a
+#define ADF_NBUF_TRAC_WAI_ETH_TYPE 0x88b4
/* EAPOL Related MASK */
#define EAPOL_PACKET_TYPE_OFFSET 15
@@ -176,17 +177,65 @@
#define ICMPV6_SUBTYPE_OFFSET 54
#define ICMPV6_REQUEST 0x80
#define ICMPV6_RESPONSE 0x81
+#define ICMPV6_RS 0x85
+#define ICMPV6_RA 0x86
#define ICMPV6_NS 0x87
#define ICMPV6_NA 0x88
#define ADF_NBUF_IPA_CHECK_MASK 0x80000000
+/**
+ * adf_proto_type - protocol type
+ * @ADF_PROTO_TYPE_DHCP - DHCP
+ * @ADF_PROTO_TYPE_EAPOL - EAPOL
+ * @ADF_PROTO_TYPE_ARP - ARP
+ * @ADF_PROTO_TYPE_MGMT - MGMT
+ * @ADF_PROTO_TYPE_EVENT - EVENT
+ */
enum adf_proto_type {
ADF_PROTO_TYPE_DHCP = 0,
ADF_PROTO_TYPE_EAPOL,
ADF_PROTO_TYPE_ARP,
+ ADF_PROTO_TYPE_MGMT,
+ ADF_PROTO_TYPE_EVENT,
ADF_PROTO_TYPE_MAX
};
+/**
+ * adf_proto_subtype - subtype of packet
+ * @ADF_PROTO_EAPOL_M1 - EAPOL 1/4
+ * @ADF_PROTO_EAPOL_M2 - EAPOL 2/4
+ * @ADF_PROTO_EAPOL_M3 - EAPOL 3/4
+ * @ADF_PROTO_EAPOL_M4 - EAPOL 4/4
+ * @ADF_PROTO_DHCP_DISCOVER - discover
+ * @ADF_PROTO_DHCP_REQUEST - request
+ * @ADF_PROTO_DHCP_OFFER - offer
+ * @ADF_PROTO_DHCP_ACK - ACK
+ * @ADF_PROTO_DHCP_NACK - NACK
+ * @ADF_PROTO_DHCP_RELEASE - release
+ * @ADF_PROTO_DHCP_INFORM - inform
+ * @ADF_PROTO_DHCP_DECLINE - decline
+ * @ADF_PROTO_ARP_REQ - arp request
+ * @ADF_PROTO_ARP_RES - arp response
+ * @ADF_PROTO_ICMP_REQ - icmp request
+ * @ADF_PROTO_ICMP_RES - icmp response
+ * @ADF_PROTO_ICMPV6_REQ - icmpv6 request
+ * @ADF_PROTO_ICMPV6_RES - icmpv6 response
+ * @ADF_PROTO_ICMPV6_RS - icmpv6 rs packet
+ * @ADF_PROTO_ICMPV6_RA - icmpv6 ra packet
+ * @ADF_PROTO_ICMPV6_NS - icmpv6 ns packet
+ * @ADF_PROTO_ICMPV6_NA - icmpv6 na packet
+ * @ADF_PROTO_IPV4_UDP - ipv4 udp
+ * @ADF_PROTO_IPV4_TCP - ipv4 tcp
+ * @ADF_PROTO_IPV6_UDP - ipv6 udp
+ * @ADF_PROTO_IPV6_TCP - ipv6 tcp
+ * @ADF_PROTO_MGMT_ASSOC -assoc
+ * @ADF_PROTO_MGMT_DISASSOC - disassoc
+ * @ADF_PROTO_MGMT_AUTH - auth
+ * @ADF_PROTO_MGMT_DEAUTH - deauth
+ * @ADF_ROAM_SYNCH - roam synch indication from fw
+ * @ADF_ROAM_COMPLETE - roam complete cmd to fw
+ * @ADF_ROAM_EVENTID - roam eventid from fw
+ */
enum adf_proto_subtype {
ADF_PROTO_INVALID = 0,
ADF_PROTO_EAPOL_M1,
@@ -203,17 +252,25 @@
ADF_PROTO_DHCP_DECLINE,
ADF_PROTO_ARP_REQ,
ADF_PROTO_ARP_RES,
- ADF_PROTO_ARP_SUBTYPE,
ADF_PROTO_ICMP_REQ,
ADF_PROTO_ICMP_RES,
ADF_PROTO_ICMPV6_REQ,
ADF_PROTO_ICMPV6_RES,
+ ADF_PROTO_ICMPV6_RS,
+ ADF_PROTO_ICMPV6_RA,
ADF_PROTO_ICMPV6_NS,
ADF_PROTO_ICMPV6_NA,
ADF_PROTO_IPV4_UDP,
ADF_PROTO_IPV4_TCP,
ADF_PROTO_IPV6_UDP,
ADF_PROTO_IPV6_TCP,
+ ADF_PROTO_MGMT_ASSOC,
+ ADF_PROTO_MGMT_DISASSOC,
+ ADF_PROTO_MGMT_AUTH,
+ ADF_PROTO_MGMT_DEAUTH,
+ ADF_ROAM_SYNCH,
+ ADF_ROAM_COMPLETE,
+ ADF_ROAM_EVENTID,
ADF_PROTO_SUBTYPE_MAX
};
@@ -1522,11 +1579,11 @@
*
* This func. checks whether it is a DHCP packet or not.
*
- * Return: A_STATUS_OK if it is a DHCP packet
- * A_STATUS_FAILED if not
+ * Return: TRUE if it is a DHCP packet
+ * FALSE if not
*/
-static inline a_status_t
-adf_nbuf_is_dhcp_pkt(adf_nbuf_t buf)
+static inline
+bool adf_nbuf_is_dhcp_pkt(adf_nbuf_t buf)
{
return __adf_nbuf_data_is_dhcp_pkt(adf_nbuf_data(buf));
}
@@ -1537,11 +1594,11 @@
*
* This func. checks whether it is a DHCP packet or not.
*
- * Return: A_STATUS_OK if it is a DHCP packet
- * A_STATUS_FAILED if not
+ * Return: TRUE if it is a DHCP packet
+ * FALSE if not
*/
-static inline a_status_t
-adf_nbuf_data_is_dhcp_pkt(uint8_t *data)
+static inline
+bool adf_nbuf_data_is_dhcp_pkt(uint8_t *data)
{
return __adf_nbuf_data_is_dhcp_pkt(data);
}
@@ -1552,11 +1609,11 @@
*
* This func. checks whether it is a EAPOL packet or not.
*
- * Return: A_STATUS_OK if it is a EAPOL packet
- * A_STATUS_FAILED if not
+ * Return: TRUE if it is a EAPOL packet
+ * FALSE if not
*/
-static inline a_status_t
-adf_nbuf_is_eapol_pkt(adf_nbuf_t buf)
+static inline
+bool adf_nbuf_is_eapol_pkt(adf_nbuf_t buf)
{
return __adf_nbuf_data_is_eapol_pkt(adf_nbuf_data(buf));
}
@@ -1567,11 +1624,11 @@
*
* This func. checks whether it is a EAPOL packet or not.
*
- * Return: A_STATUS_OK if it is a EAPOL packet
- * A_STATUS_FAILED if not
+ * Return: TRUE if it is a EAPOL packet
+ * FALSE if not
*/
-static inline a_status_t
-adf_nbuf_data_is_eapol_pkt(uint8_t *data)
+static inline
+bool adf_nbuf_data_is_eapol_pkt(uint8_t *data)
{
return __adf_nbuf_data_is_eapol_pkt(data);
}
@@ -1900,6 +1957,51 @@
__adf_nbuf_update_skb_mark(skb, mask);
}
+/**
+ * adf_nbuf_is_wai_pkt() - Check if frame is WAI
+ * @skb: Pointer to skb
+ *
+ * This function checks if the frame is WAI.
+ *
+ * Return: true (1) if WAI
+ *
+ */
+static inline bool adf_nbuf_is_wai_pkt(struct sk_buff *skb)
+{
+ return __adf_nbuf_is_wai_pkt(skb->data);
+}
+
+/**
+ * adf_nbuf_is_multicast_pkt() - Check if frame is multicast packet
+ * @skb: Pointer to skb
+ *
+ * This function checks if the frame is multicast packet.
+ *
+ * Return: true (1) if multicast
+ *
+ */
+static inline bool adf_nbuf_is_multicast_pkt(struct sk_buff *skb)
+{
+ return __adf_nbuf_is_multicast_pkt(skb->data);
+}
+
+/**
+ * adf_nbuf_is_bcast_pkt() - Check if frame is broadcast packet
+ * @skb: Pointer to skb
+ *
+ * This function checks if the frame is broadcast packet.
+ *
+ * Return: true (1) if broadcast
+ *
+ */
+static inline bool adf_nbuf_is_bcast_pkt(struct sk_buff *skb)
+{
+ return __adf_nbuf_is_bcast_pkt(skb->data);
+}
+
+
+
+
void adf_nbuf_set_state(adf_nbuf_t nbuf, uint8_t current_state);
void adf_nbuf_tx_desc_count_display(void);
void adf_nbuf_tx_desc_count_clear(void);
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_trace.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_trace.c
index 8311147..0cd8d88 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_trace.c
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_trace.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -43,6 +43,7 @@
#include "sirDebug.h"
#include "debug_linux.h"
#include "adf_os_io.h"
+#include "vos_timer.h"
/* Static and Global variables */
static spinlock_t l_dp_trace_lock;
@@ -75,22 +76,31 @@
g_adf_dp_trace_data.head = INVALID_ADF_DP_TRACE_ADDR;
g_adf_dp_trace_data.tail = INVALID_ADF_DP_TRACE_ADDR;
g_adf_dp_trace_data.num = 0;
- g_adf_dp_trace_data.proto_bitmap = 0;
+ g_adf_dp_trace_data.proto_bitmap = NBUF_PKT_TRAC_TYPE_EAPOL |
+ NBUF_PKT_TRAC_TYPE_DHCP |
+ NBUF_PKT_TRAC_TYPE_MGMT_ACTION |
+ NBUF_PKT_TRAC_TYPE_ARP;
g_adf_dp_trace_data.no_of_record = 0;
- g_adf_dp_trace_data.verbosity = ADF_DP_TRACE_VERBOSITY_DEFAULT;
+ g_adf_dp_trace_data.verbosity = ADF_DP_TRACE_VERBOSITY_HIGH;
g_adf_dp_trace_data.enable = true;
+ g_adf_dp_trace_data.tx_count = 0;
+ g_adf_dp_trace_data.rx_count = 0;
+ g_adf_dp_trace_data.live_mode = 0;
for (i = 0; i < ADF_DP_TRACE_MAX; i++)
adf_dp_trace_cb_table[i] = adf_dp_display_record;
adf_dp_trace_cb_table[ADF_DP_TRACE_TXRX_PACKET_PTR_RECORD] =
- adf_dp_trace_cb_table[ADF_DP_TRACE_TXRX_FAST_PACKET_PTR_RECORD] =
adf_dp_trace_cb_table[ADF_DP_TRACE_FREE_PACKET_PTR_RECORD] =
adf_dp_display_ptr_record;
adf_dp_trace_cb_table[ADF_DP_TRACE_EAPOL_PACKET_RECORD] =
adf_dp_trace_cb_table[ADF_DP_TRACE_DHCP_PACKET_RECORD] =
adf_dp_trace_cb_table[ADF_DP_TRACE_ARP_PACKET_RECORD] =
adf_dp_display_proto_pkt;
+ adf_dp_trace_cb_table[ADF_DP_TRACE_MGMT_PACKET_RECORD] =
+ adf_dp_display_mgmt_pkt;
+ adf_dp_trace_cb_table[ADF_DP_TRACE_EVENT_RECORD] =
+ adf_dp_display_event_record;
}
/**
@@ -155,21 +165,33 @@
/**
* adf_dp_trace_set_track() - Marks whether the packet needs to be traced
* @nbuf : defines the netbuf
+ * @dir: direction
*
* Return: None
*/
-void adf_dp_trace_set_track(adf_nbuf_t nbuf)
+void adf_dp_trace_set_track(adf_nbuf_t nbuf, enum adf_proto_dir dir)
{
+ uint32_t count = 0;
+
spin_lock_bh(&l_dp_trace_lock);
- g_adf_dp_trace_data.count++;
+ if (ADF_TX == dir)
+ count = ++g_adf_dp_trace_data.tx_count;
+ else if (ADF_RX == dir)
+ count = ++g_adf_dp_trace_data.rx_count;
+
if ((g_adf_dp_trace_data.no_of_record != 0) &&
- (g_adf_dp_trace_data.count %
- g_adf_dp_trace_data.no_of_record == 0)) {
- ADF_NBUF_SET_DP_TRACE(nbuf, 1);
+ (count % g_adf_dp_trace_data.no_of_record == 0)) {
+ if (ADF_TX == dir)
+ ADF_NBUF_CB_TX_DP_TRACE(nbuf) = 1;
+ else if (ADF_RX == dir)
+ ADF_NBUF_CB_RX_DP_TRACE(nbuf) = 1;
}
spin_unlock_bh(&l_dp_trace_lock);
}
+#define DPTRACE_PRINT(args...) \
+ VOS_TRACE(VOS_MODULE_ID_ADF, VOS_TRACE_LEVEL_INFO, ## args)
+
/**
* dump_hex_trace() - Display the data in buffer
* @str: string to print
@@ -180,13 +202,20 @@
*/
static void dump_hex_trace(char *str, uint8_t *buf, uint8_t buf_len)
{
- uint8_t i;
+ unsigned char linebuf[BUFFER_SIZE];
+ const u8 *ptr = buf;
+ int i, linelen, remaining = buf_len;
/* Dump the bytes in the last line */
- printk("%s: ", str);
- for (i = 0; i < buf_len; i++)
- printk("%02x ", buf[i]);
- printk("\n");
+ for (i = 0; i < buf_len; i += ROW_SIZE) {
+ linelen = min(remaining, ROW_SIZE);
+ remaining -= ROW_SIZE;
+
+ hex_dump_to_buffer(ptr + i, linelen, ROW_SIZE, 1,
+ linebuf, sizeof(linebuf), false);
+
+ DPTRACE_PRINT("DPT: %s: %s", str, linebuf);
+ }
}
/**
@@ -206,32 +235,42 @@
return "DHCP:";
case ADF_DP_TRACE_ARP_PACKET_RECORD:
return "ARP:";
- case ADF_DP_TRACE_HDD_PACKET_PTR_RECORD:
- return "HDD: PTR:";
- case ADF_DP_TRACE_HDD_PACKET_RECORD:
- return "HDD: DATA:";
+ case ADF_DP_TRACE_MGMT_PACKET_RECORD:
+ return "MGMT:";
+ case ADF_DP_TRACE_EVENT_RECORD:
+ return "EVENT:";
+ case ADF_DP_TRACE_HDD_TX_PACKET_PTR_RECORD:
+ return "HDD: TX: PTR:";
+ case ADF_DP_TRACE_HDD_TX_PACKET_RECORD:
+ return "HDD: TX: DATA:";
case ADF_DP_TRACE_CE_PACKET_PTR_RECORD:
- return "CE: PTR:";
- case ADF_DP_TRACE_CE_FAST_PACKET_PTR_RECORD:
- return "CE:F: PTR:";
+ return "CE: TX: PTR:";
case ADF_DP_TRACE_FREE_PACKET_PTR_RECORD:
- return "FREE: PTR:";
+ return "FREE: TX: PTR:";
+ case ADF_DP_TRACE_RX_HTT_PACKET_PTR_RECORD:
+ return "HTT: RX: PTR:";
+ case ADF_DP_TRACE_RX_OFFLOAD_HTT_PACKET_PTR_RECORD:
+ return "HTT: RX: OF: PTR:";
+ case ADF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD:
+ return "HDD: RX: PTR:";
+ case ADF_DP_TRACE_HDD_RX_PACKET_RECORD:
+ return "HDD: RX: DATA:";
case ADF_DP_TRACE_TXRX_QUEUE_PACKET_PTR_RECORD:
- return "TX:Q: PTR:";
+ return "TXRX: TX: Q: PTR:";
case ADF_DP_TRACE_TXRX_PACKET_PTR_RECORD:
- return "TX: PTR:";
- case ADF_DP_TRACE_TXRX_FAST_PACKET_PTR_RECORD:
- return "TX:F: PTR:";
+ return "TXRX: TX: PTR:";
case ADF_DP_TRACE_HTT_PACKET_PTR_RECORD:
- return "HTT: PTR:";
+ return "HTT: TX: PTR:";
case ADF_DP_TRACE_HTC_PACKET_PTR_RECORD:
- return "HTC: PTR:";
+ return "HTC: TX: PTR:";
case ADF_DP_TRACE_HIF_PACKET_PTR_RECORD:
- return "HIF: PTR:";
+ return "HIF: TX: PTR:";
+ case ADF_DP_TRACE_RX_TXRX_PACKET_PTR_RECORD:
+ return "TXRX: RX: PTR:";
case ADF_DP_TRACE_HDD_TX_TIMEOUT:
- return "STA: TO:";
+ return "HDD: STA: TO:";
case ADF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT:
- return "SAP: TO:";
+ return "HDD: SAP: TO:";
default:
return "Invalid";
}
@@ -247,9 +286,9 @@
{
switch (dir) {
case ADF_TX:
- return "->";
+ return " --> ";
case ADF_RX:
- return "<-";
+ return " <-- ";
default:
return "invalid";
}
@@ -270,6 +309,10 @@
return "EAPOL";
case ADF_PROTO_TYPE_ARP:
return "ARP";
+ case ADF_PROTO_TYPE_MGMT:
+ return "MGMT";
+ case ADF_PROTO_TYPE_EVENT:
+ return "EVENT";
default:
return "invalid";
}
@@ -312,6 +355,20 @@
return "REQUEST";
case ADF_PROTO_ARP_RES:
return "RESPONSE";
+ case ADF_PROTO_MGMT_ASSOC:
+ return "ASSOC";
+ case ADF_PROTO_MGMT_DISASSOC:
+ return "DISASSOC";
+ case ADF_PROTO_MGMT_AUTH:
+ return "AUTH";
+ case ADF_PROTO_MGMT_DEAUTH:
+ return "DEAUTH";
+ case ADF_ROAM_SYNCH:
+ return "ROAM SYNCH";
+ case ADF_ROAM_COMPLETE:
+ return "ROAM COMPLETE";
+ case ADF_ROAM_EVENTID:
+ return "ROAM EVENTID";
default:
return "invalid";
}
@@ -324,7 +381,8 @@
*
* Return: true/false
*/
-bool adf_dp_enable_check(adf_nbuf_t nbuf, enum ADF_DP_TRACE_ID code)
+bool adf_dp_enable_check(adf_nbuf_t nbuf, enum ADF_DP_TRACE_ID code,
+ enum adf_proto_dir dir)
{
/* Return when Dp trace is not enabled */
if (!g_adf_dp_trace_data.enable)
@@ -335,7 +393,8 @@
if ((nbuf) && ((NBUF_GET_PACKET_TRACK(nbuf) !=
NBUF_TX_PKT_DATA_TRACK) ||
- (!ADF_NBUF_GET_DP_TRACE(nbuf))))
+ ((dir == ADF_TX) && (ADF_NBUF_CB_TX_DP_TRACE(nbuf) == 0)) ||
+ ((dir == ADF_RX) && (ADF_NBUF_CB_RX_DP_TRACE(nbuf) == 0))))
return false;
return true;
@@ -346,13 +405,15 @@
* @code: dptrace code
* @data: data pointer
* @size: size of buffer
+ * @print: print it in kmsg
*
* Return: none
*/
void adf_dp_add_record(enum ADF_DP_TRACE_ID code,
- uint8_t *data, uint8_t size)
+ uint8_t *data, uint8_t size, bool print)
{
struct adf_dp_trace_record_s *rec = NULL;
+ int index;
spin_lock_bh(&l_dp_trace_lock);
@@ -381,6 +442,7 @@
}
rec = &g_adf_dp_trace_tbl[g_adf_dp_trace_data.tail];
+ index = g_adf_dp_trace_data.tail;
rec->code = code;
rec->size = 0;
if (data != NULL && size > 0) {
@@ -391,35 +453,44 @@
adf_os_mem_copy(rec->data, data, size);
}
- rec->time = adf_os_gettimestamp();
+ vos_get_time_of_the_day_in_hr_min_sec_usec(rec->time,
+ sizeof(rec->time));
rec->pid = (in_interrupt() ? 0 : current->pid);
spin_unlock_bh(&l_dp_trace_lock);
+
+ if (g_adf_dp_trace_data.live_mode || print == true)
+ adf_dp_trace_cb_table[rec->code] (rec, index);
}
/**
* adf_log_eapol_pkt() - log EAPOL packet
* @session_id: vdev_id
* @skb: skb pointer
- * @event_type: event_type
+ * @dir: direction
*
* Return: true/false
*/
bool adf_log_eapol_pkt(uint8_t session_id, struct sk_buff *skb,
- uint8_t event_type)
+ enum adf_proto_dir dir)
{
enum adf_proto_subtype subtype;
if ((adf_dp_get_proto_bitmap() & NBUF_PKT_TRAC_TYPE_EAPOL) &&
- adf_nbuf_is_eapol_pkt(skb) == A_STATUS_OK) {
+ ((dir == ADF_TX && ADF_NBUF_GET_IS_EAPOL(skb)) ||
+ (dir == ADF_RX && adf_nbuf_is_eapol_pkt(skb)))) {
subtype = adf_nbuf_get_eapol_subtype(skb);
DPTRACE(adf_dp_trace_proto_pkt(ADF_DP_TRACE_EAPOL_PACKET_RECORD,
session_id, (skb->data + ADF_NBUF_SRC_MAC_OFFSET),
(skb->data + ADF_NBUF_DEST_MAC_OFFSET),
ADF_PROTO_TYPE_EAPOL, subtype,
- event_type == ADF_RX ?
- ADF_RX : ADF_TX));
- ADF_NBUF_SET_DP_TRACE(skb, 1);
+ dir));
+ if (ADF_TX == dir)
+ ADF_NBUF_CB_TX_DP_TRACE(skb) = 1;
+ else if (ADF_RX == dir)
+ ADF_NBUF_CB_RX_DP_TRACE(skb) = 1;
+
+ ADF_NBUF_CB_DP_TRACE_PRINT(skb) = true;
return true;
}
return false;
@@ -429,26 +500,31 @@
* adf_log_dhcp_pkt() - log DHCP packet
* @session_id: vdev_id
* @skb: skb pointer
- * @event_type: event_type
+ * @dir: direction
*
* Return: true/false
*/
bool adf_log_dhcp_pkt(uint8_t session_id, struct sk_buff *skb,
- uint8_t event_type)
+ enum adf_proto_dir dir)
{
enum adf_proto_subtype subtype = ADF_PROTO_INVALID;
if ((adf_dp_get_proto_bitmap() & NBUF_PKT_TRAC_TYPE_DHCP) &&
- adf_nbuf_is_dhcp_pkt(skb) == A_STATUS_OK) {
+ ((dir == ADF_TX && ADF_NBUF_GET_IS_DHCP(skb)) ||
+ (dir == ADF_RX && adf_nbuf_is_dhcp_pkt(skb)))) {
subtype = adf_nbuf_get_dhcp_subtype(skb);
DPTRACE(adf_dp_trace_proto_pkt(ADF_DP_TRACE_DHCP_PACKET_RECORD,
session_id, (skb->data + ADF_NBUF_SRC_MAC_OFFSET),
(skb->data + ADF_NBUF_DEST_MAC_OFFSET),
ADF_PROTO_TYPE_DHCP, subtype,
- event_type == ADF_RX ?
- ADF_RX : ADF_TX));
- ADF_NBUF_SET_DP_TRACE(skb, 1);
+ dir));
+ if (ADF_TX == dir)
+ ADF_NBUF_CB_TX_DP_TRACE(skb) = 1;
+ else if (ADF_RX == dir)
+ ADF_NBUF_CB_RX_DP_TRACE(skb) = 1;
+
+ ADF_NBUF_CB_DP_TRACE_PRINT(skb) = true;
return true;
}
return false;
@@ -458,17 +534,18 @@
* adf_log_arp_pkt() - log ARP packet
* @session_id: vdev_id
* @skb: skb pointer
- * @event_type: event_type
+ * @dir: direction
*
* Return: true/false
*/
bool adf_log_arp_pkt(uint8_t session_id, struct sk_buff *skb,
- uint8_t event_type)
+ enum adf_proto_dir dir)
{
enum adf_proto_subtype proto_subtype;
if ((adf_dp_get_proto_bitmap() & NBUF_PKT_TRAC_TYPE_ARP) &&
- adf_nbuf_is_ipv4_arp_pkt(skb) == true) {
+ ((dir == ADF_TX && ADF_NBUF_GET_IS_ARP(skb)) ||
+ (dir == ADF_RX && adf_nbuf_is_ipv4_arp_pkt(skb)))){
proto_subtype = adf_nbuf_get_arp_subtype(skb);
@@ -476,9 +553,13 @@
session_id, (skb->data + ADF_NBUF_SRC_MAC_OFFSET),
(skb->data + ADF_NBUF_DEST_MAC_OFFSET),
ADF_PROTO_TYPE_ARP, proto_subtype,
- event_type == ADF_RX ?
- ADF_RX : ADF_TX));
- ADF_NBUF_SET_DP_TRACE(skb, 1);
+ dir));
+ if (ADF_TX == dir)
+ ADF_NBUF_CB_TX_DP_TRACE(skb) = 1;
+ else if (ADF_RX == dir)
+ ADF_NBUF_CB_RX_DP_TRACE(skb) = 1;
+
+ ADF_NBUF_CB_DP_TRACE_PRINT(skb) = true;
return true;
}
return false;
@@ -488,20 +569,20 @@
* adf_dp_trace_log_pkt() - log packet type enabled through iwpriv
* @session_id: vdev_id
* @skb: skb pointer
- * @event_type: event type
+ * @dir: direction
*
* Return: none
*/
void adf_dp_trace_log_pkt(uint8_t session_id, struct sk_buff *skb,
- uint8_t event_type)
+ enum adf_proto_dir dir)
{
if (adf_dp_get_proto_bitmap()) {
if (adf_log_arp_pkt(session_id,
- skb, event_type) == false) {
+ skb, dir) == false) {
if (adf_log_dhcp_pkt(session_id,
- skb, event_type) == false) {
+ skb, dir) == false) {
if (adf_log_eapol_pkt(session_id,
- skb, event_type) == false) {
+ skb, dir) == false) {
return;
}
}
@@ -522,10 +603,10 @@
struct adf_dp_trace_proto_buf *buf =
(struct adf_dp_trace_proto_buf *)record->data;
- adf_os_print("%04d: %012llu: %s vdev_id %d\n", index,
+ DPTRACE_PRINT("DPT: %04d: %s: %s vdev_id %d\n", index,
record->time, adf_dp_code_to_string(record->code),
buf->vdev_id);
- adf_os_print("SA: " MAC_ADDRESS_STR " %s DA: " MAC_ADDRESS_STR
+ DPTRACE_PRINT("DPT: SA: " MAC_ADDRESS_STR " %s DA: " MAC_ADDRESS_STR
" Type %s Subtype %s\n",
MAC_ADDR_ARRAY(buf->sa.bytes), adf_dp_dir_to_str(buf->dir),
MAC_ADDR_ARRAY(buf->da.bytes), adf_dp_type_to_str(buf->type),
@@ -551,7 +632,7 @@
struct adf_dp_trace_proto_buf buf;
int buf_size = sizeof(struct adf_dp_trace_ptr_buf);
- if (adf_dp_enable_check(NULL, code) == false)
+ if (adf_dp_enable_check(NULL, code, dir) == false)
return;
if (buf_size > ADF_DP_TRACE_RECORD_SIZE)
@@ -563,7 +644,69 @@
buf.type = type;
buf.subtype = subtype;
buf.vdev_id = vdev_id;
- adf_dp_add_record(code, (uint8_t *)&buf, buf_size);
+ adf_dp_add_record(code, (uint8_t *)&buf, buf_size, true);
+}
+
+void adf_dp_display_mgmt_pkt(struct adf_dp_trace_record_s *record,
+ uint16_t index)
+{
+ struct adf_dp_trace_mgmt_buf *buf =
+ (struct adf_dp_trace_mgmt_buf *)record->data;
+
+ DPTRACE_PRINT("DPT: %04d: %s: %s vdev_id %d", index,
+ record->time, adf_dp_code_to_string(record->code),
+ buf->vdev_id);
+ DPTRACE_PRINT("DPT: Type %s Subtype %s", adf_dp_type_to_str(buf->type),
+ adf_dp_subtype_to_str(buf->subtype));
+}
+
+void adf_dp_trace_mgmt_pkt(enum ADF_DP_TRACE_ID code, uint8_t vdev_id,
+ enum adf_proto_type type, enum adf_proto_subtype subtype)
+{
+ struct adf_dp_trace_mgmt_buf buf;
+ int buf_size = sizeof(struct adf_dp_trace_mgmt_buf);
+
+ if (adf_dp_enable_check(NULL, code, ADF_NA) == false)
+ return;
+
+ if (buf_size > ADF_DP_TRACE_RECORD_SIZE)
+ ADF_BUG(0);
+
+ buf.type = type;
+ buf.subtype = subtype;
+ buf.vdev_id = vdev_id;
+ adf_dp_add_record(code, (uint8_t *)&buf, buf_size, true);
+}
+
+void adf_dp_display_event_record(struct adf_dp_trace_record_s *record,
+ uint16_t index)
+{
+ struct adf_dp_trace_event_buf *buf =
+ (struct adf_dp_trace_event_buf *)record->data;
+
+ DPTRACE_PRINT("DPT: %04d: %s: %s vdev_id %d", index,
+ record->time, adf_dp_code_to_string(record->code),
+ buf->vdev_id);
+ DPTRACE_PRINT("DPT: Type %s Subtype %s", adf_dp_type_to_str(buf->type),
+ adf_dp_subtype_to_str(buf->subtype));
+}
+
+void adf_dp_trace_record_event(enum ADF_DP_TRACE_ID code, uint8_t vdev_id,
+ enum adf_proto_type type, enum adf_proto_subtype subtype)
+{
+ struct adf_dp_trace_event_buf buf;
+ int buf_size = sizeof(struct adf_dp_trace_event_buf);
+
+ if (adf_dp_enable_check(NULL, code, ADF_NA) == false)
+ return;
+
+ if (buf_size > ADF_DP_TRACE_RECORD_SIZE)
+ ADF_BUG(0);
+
+ buf.type = type;
+ buf.subtype = subtype;
+ buf.vdev_id = vdev_id;
+ adf_dp_add_record(code, (uint8_t *)&buf, buf_size, true);
}
/**
@@ -579,9 +722,15 @@
struct adf_dp_trace_ptr_buf *buf =
(struct adf_dp_trace_ptr_buf *)record->data;
- adf_os_print("%04d: %012llu: %s msdu_id: %d, status: %d", index,
- record->time, adf_dp_code_to_string(record->code),
- buf->msdu_id, buf->status);
+ if (record->code == ADF_DP_TRACE_FREE_PACKET_PTR_RECORD)
+ DPTRACE_PRINT("DPT: %04d: %s: %s msdu_id: %d, status: %d\n", index,
+ record->time, adf_dp_code_to_string(record->code),
+ buf->msdu_id, buf->status);
+ else
+ DPTRACE_PRINT("DPT: %04d: %s: %s msdu_id: %d, vdev_id: %d\n", index,
+ record->time, adf_dp_code_to_string(record->code),
+ buf->msdu_id, buf->status);
+
dump_hex_trace("cookie", (uint8_t *)&buf->cookie, sizeof(buf->cookie));
}
@@ -601,7 +750,7 @@
struct adf_dp_trace_ptr_buf buf;
int buf_size = sizeof(struct adf_dp_trace_ptr_buf);
- if (adf_dp_enable_check(nbuf, code) == false)
+ if (adf_dp_enable_check(nbuf, code, ADF_TX) == false)
return;
if (buf_size > ADF_DP_TRACE_RECORD_SIZE)
@@ -610,7 +759,8 @@
adf_os_mem_copy(&buf.cookie, data, size);
buf.msdu_id = msdu_id;
buf.status = status;
- adf_dp_add_record(code, (uint8_t *)&buf, buf_size);
+ adf_dp_add_record(code, (uint8_t *)&buf, buf_size,
+ ADF_NBUF_CB_DP_TRACE_PRINT(nbuf));
}
/**
@@ -623,18 +773,17 @@
void adf_dp_display_record(struct adf_dp_trace_record_s *pRecord,
uint16_t recIndex)
{
- adf_os_print("%04d: %012llu: %s", recIndex,
+ DPTRACE_PRINT("DPT: %04d: %s: %s\n", recIndex,
pRecord->time, adf_dp_code_to_string(pRecord->code));
switch (pRecord->code) {
case ADF_DP_TRACE_HDD_TX_TIMEOUT:
- VOS_TRACE(VOS_MODULE_ID_ADF, VOS_TRACE_LEVEL_ERROR,
- "HDD TX Timeout\n");
+ DPTRACE_PRINT("DPT: HDD TX Timeout\n");
break;
case ADF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT:
- VOS_TRACE(VOS_MODULE_ID_ADF, VOS_TRACE_LEVEL_ERROR,
- "HDD SoftAP TX Timeout\n");
+ DPTRACE_PRINT("DPT: HDD SoftAP TX Timeout\n");
break;
- case ADF_DP_TRACE_HDD_PACKET_RECORD:
+ case ADF_DP_TRACE_HDD_TX_PACKET_RECORD:
+ case ADF_DP_TRACE_HDD_RX_PACKET_RECORD:
dump_hex_trace("DATA", pRecord->data, pRecord->size);
break;
default:
@@ -652,12 +801,45 @@
* Return: None
*/
void adf_dp_trace(adf_nbuf_t nbuf, enum ADF_DP_TRACE_ID code,
- uint8_t *data, uint8_t size)
+ uint8_t *data, uint8_t size, enum adf_proto_dir dir)
{
- if (adf_dp_enable_check(nbuf, code) == false)
+
+ if (adf_dp_enable_check(nbuf, code, dir) == false)
return;
- adf_dp_add_record(code, data, size);
+ adf_dp_add_record(code, data, size,
+ ADF_NBUF_CB_DP_TRACE_PRINT(nbuf));
+}
+
+/**
+ * adf_dp_trace_enable_live_mode() - enable live mode
+ *
+ * Return: none
+ */
+void adf_dp_trace_enable_live_mode(void)
+{
+ g_adf_dp_trace_data.live_mode = 1;
+}
+
+void adf_dp_trace_clear_buffer(void)
+{
+ g_adf_dp_trace_data.head = INVALID_ADF_DP_TRACE_ADDR;
+ g_adf_dp_trace_data.tail = INVALID_ADF_DP_TRACE_ADDR;
+ g_adf_dp_trace_data.num = 0;
+ g_adf_dp_trace_data.proto_bitmap = NBUF_PKT_TRAC_TYPE_EAPOL |
+ NBUF_PKT_TRAC_TYPE_DHCP |
+ NBUF_PKT_TRAC_TYPE_MGMT_ACTION |
+ NBUF_PKT_TRAC_TYPE_ARP;
+ g_adf_dp_trace_data.no_of_record = 0;
+ g_adf_dp_trace_data.verbosity = ADF_DP_TRACE_VERBOSITY_HIGH;
+ g_adf_dp_trace_data.enable = true;
+ g_adf_dp_trace_data.tx_count = 0;
+ g_adf_dp_trace_data.rx_count = 0;
+ g_adf_dp_trace_data.live_mode = 0;
+
+ memset(g_adf_dp_trace_tbl, 0,
+ MAX_ADF_DP_TRACE_RECORDS * sizeof(struct adf_dp_trace_record_s));
+
}
/**
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_trace.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_trace.h
index b426edd..7d6d19e 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_trace.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/adf_trace.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -56,6 +56,11 @@
#define ADF_DP_TRACE_VERBOSITY_LOW 1
#define ADF_DP_TRACE_VERBOSITY_DEFAULT 0
+#define DUMP_DP_TRACE 0
+#define ENABLE_DP_TRACE_LIVE_MODE 1
+#define CLEAR_DP_TRACE_BUFFER 2
+
+
/**
* struct adf_mac_addr - mac address array
* @bytes: MAC address bytes
@@ -66,59 +71,98 @@
/**
* enum ADF_DP_TRACE_ID - Generic ID to identify various events in data path
- * @ADF_DP_TRACE_INVALID: Invalid ID
- * @ADF_DP_TRACE_DROP_PACKET_RECORD: Dropped packet stored with this id
- * @ADF_DP_TRACE_HDD_PACKET_PTR_RECORD: nbuf->data ptr of HDD
- * @ADF_DP_TRACE_HDD_PACKET_RECORD: nbuf->data stored with this id
- * @ADF_DP_TRACE_CE_PACKET_PTR_RECORD: nbuf->data ptr of CE
- * @ADF_DP_TRACE_CE_PACKET_RECORD: nbuf->data stored with this id
- * @ADF_DP_TRACE_TXRX_QUEUE_PACKET_PTR_RECORD: nbuf->data ptr of txrx queue
- * @ADF_DP_TRACE_TXRX_PACKET_PTR_RECORD: nbuf->data ptr of txrx
- * @ADF_DP_TRACE_HTT_PACKET_PTR_RECORD: nbuf->data ptr of htt
- * @ADF_DP_TRACE_HTC_PACKET_PTR_RECORD: nbuf->data ptr of htc
- * @ADF_DP_TRACE_HIF_PACKET_PTR_RECORD: nbuf->data ptr of hif
- * @ADF_DP_TRACE_HDD_TX_TIMEOUT: hdd tx timeout event
- * @ADF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT: hdd tx softap timeout event
- *
+ * @ADF_DP_TRACE_INVALID - invalid
+ * @ADF_DP_TRACE_DROP_PACKET_RECORD - record drop packet
+ * @ADF_DP_TRACE_EAPOL_PACKET_RECORD - record EAPOL packet
+ * @ADF_DP_TRACE_DHCP_PACKET_RECORD - record DHCP packet
+ * @ADF_DP_TRACE_ARP_PACKET_RECORD - record ARP packet
+ * @ADF_DP_TRACE_MGMT_PACKET_RECORD - record MGMT pacekt
+ * @ADF_DP_TRACE_EVENT_RECORD - record events
+ * @ADF_DP_TRACE_DEFAULT_VERBOSITY - below this are part of default verbosity
+ * @ADF_DP_TRACE_HDD_TX_TIMEOUT - HDD tx timeout
+ * @ADF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT- SOFTAP HDD tx timeout
+ * @ADF_DP_TRACE_HDD_TX_PACKET_PTR_RECORD - HDD layer ptr record
+ * @ADF_DP_TRACE_CE_PACKET_PTR_RECORD - CE layer ptr record
+ * @ADF_DP_TRACE_FREE_PACKET_PTR_RECORD - tx completion ptr record
+ * @ADF_DP_TRACE_RX_HTT_PACKET_PTR_RECORD - HTT RX record
+ * @ADF_DP_TRACE_RX_OFFLOAD_HTT_PACKET_PTR_RECORD- HTT RX offload record
+ * @ADF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD - HDD RX record
+ * @ADF_DP_TRACE_LOW_VERBOSITY - below this are part of low verbosity
+ * @ADF_DP_TRACE_TXRX_QUEUE_PACKET_PTR_RECORD -tx queue ptr record
+ * @ADF_DP_TRACE_TXRX_PACKET_PTR_RECORD - txrx packet ptr record
+ * @ADF_DP_TRACE_HTT_PACKET_PTR_RECORD - htt packet ptr record
+ * @ADF_DP_TRACE_HTC_PACKET_PTR_RECORD - htc packet ptr record
+ * @ADF_DP_TRACE_HIF_PACKET_PTR_RECORD - hif packet ptr record
+ * @ADF_DP_TRACE_RX_TXRX_PACKET_PTR_RECORD - txrx packet ptr record
+ * @ADF_DP_TRACE_MED_VERBOSITY - below this are part of med verbosity
+ * @ADF_DP_TRACE_HDD_TX_PACKET_RECORD - record 32 bytes at HDD
+ * @ADF_DP_TRACE_HDD_RX_PACKET_RECORD - record 32 bytes at HDD
+ * @ADF_DP_TRACE_HIGH_VERBOSITY - below this are part of high verbosity
*/
-
enum ADF_DP_TRACE_ID {
ADF_DP_TRACE_INVALID = 0,
ADF_DP_TRACE_DROP_PACKET_RECORD,
ADF_DP_TRACE_EAPOL_PACKET_RECORD,
ADF_DP_TRACE_DHCP_PACKET_RECORD,
ADF_DP_TRACE_ARP_PACKET_RECORD,
+ ADF_DP_TRACE_MGMT_PACKET_RECORD,
+ ADF_DP_TRACE_EVENT_RECORD,
ADF_DP_TRACE_DEFAULT_VERBOSITY,
ADF_DP_TRACE_HDD_TX_TIMEOUT,
ADF_DP_TRACE_HDD_SOFTAP_TX_TIMEOUT,
- ADF_DP_TRACE_HDD_PACKET_PTR_RECORD,
+ ADF_DP_TRACE_HDD_TX_PACKET_PTR_RECORD,
ADF_DP_TRACE_CE_PACKET_PTR_RECORD,
- ADF_DP_TRACE_CE_FAST_PACKET_PTR_RECORD,
ADF_DP_TRACE_FREE_PACKET_PTR_RECORD,
+ ADF_DP_TRACE_RX_HTT_PACKET_PTR_RECORD,
+ ADF_DP_TRACE_RX_OFFLOAD_HTT_PACKET_PTR_RECORD,
+ ADF_DP_TRACE_RX_HDD_PACKET_PTR_RECORD,
ADF_DP_TRACE_LOW_VERBOSITY,
ADF_DP_TRACE_TXRX_QUEUE_PACKET_PTR_RECORD,
ADF_DP_TRACE_TXRX_PACKET_PTR_RECORD,
- ADF_DP_TRACE_TXRX_FAST_PACKET_PTR_RECORD,
ADF_DP_TRACE_HTT_PACKET_PTR_RECORD,
ADF_DP_TRACE_HTC_PACKET_PTR_RECORD,
ADF_DP_TRACE_HIF_PACKET_PTR_RECORD,
+ ADF_DP_TRACE_RX_TXRX_PACKET_PTR_RECORD,
ADF_DP_TRACE_MED_VERBOSITY,
- ADF_DP_TRACE_HDD_PACKET_RECORD,
+ ADF_DP_TRACE_HDD_TX_PACKET_RECORD,
+ ADF_DP_TRACE_HDD_RX_PACKET_RECORD,
ADF_DP_TRACE_HIGH_VERBOSITY,
ADF_DP_TRACE_MAX
};
+/**
+ * adf_proto_dir - direction
+ * @ADF_TX: TX direction
+ * @ADF_RX: RX direction
+ * @ADF_NA: not applicable
+ */
enum adf_proto_dir {
ADF_TX,
- ADF_RX
+ ADF_RX,
+ ADF_NA
};
+/**
+ * struct adf_dp_trace_ptr_buf - pointer record buffer
+ * @cookie: cookie value
+ * @msdu_id: msdu_id
+ * @status: completion status
+ */
struct adf_dp_trace_ptr_buf {
uint64_t cookie;
uint16_t msdu_id;
uint16_t status;
};
+/**
+ * struct adf_dp_trace_proto_buf - proto packet buffer
+ * @sa: source address
+ * @da: destination address
+ * @vdev_id : vdev id
+ * @type: packet type
+ * @subtype: packet subtype
+ * @dir: direction
+ */
struct adf_dp_trace_proto_buf {
struct adf_mac_addr sa;
struct adf_mac_addr da;
@@ -128,6 +172,29 @@
uint8_t dir;
};
+/**
+ * struct adf_dp_trace_mgmt_buf - mgmt packet buffer
+ * @vdev_id : vdev id
+ * @type: packet type
+ * @subtype: packet subtype
+ */
+struct adf_dp_trace_mgmt_buf {
+ uint8_t vdev_id;
+ uint8_t type;
+ uint8_t subtype;
+};
+
+/**
+ * struct adf_dp_trace_event_buf - event buffer
+ * @vdev_id : vdev id
+ * @type: packet type
+ * @subtype: packet subtype
+ */
+struct adf_dp_trace_event_buf {
+ uint8_t vdev_id;
+ uint8_t type;
+ uint8_t subtype;
+};
/**
* struct adf_dp_trace_record_s - Describes a record in DP trace
@@ -138,7 +205,7 @@
* @pid : process id which stored the data in this record
*/
struct adf_dp_trace_record_s {
- uint64_t time;
+ char time[20];
uint8_t code;
uint8_t data[ADF_DP_TRACE_RECORD_SIZE];
uint8_t size;
@@ -166,7 +233,9 @@
uint8_t no_of_record;
uint8_t verbosity;
bool enable;
- uint32_t count;
+ uint32_t tx_count;
+ uint32_t rx_count;
+ bool live_mode;
};
/* Function declarations and documenation */
@@ -174,9 +243,9 @@
void adf_dp_trace_init(void);
void adf_dp_trace_set_value(uint8_t proto_bitmap, uint8_t no_of_records,
uint8_t verbosity);
-void adf_dp_trace_set_track(adf_nbuf_t nbuf);
+void adf_dp_trace_set_track(adf_nbuf_t nbuf, enum adf_proto_dir dir);
void adf_dp_trace(adf_nbuf_t nbuf, enum ADF_DP_TRACE_ID code,
- uint8_t *data, uint8_t size);
+ uint8_t *data, uint8_t size, enum adf_proto_dir dir);
void adf_dp_trace_dump_all(uint32_t count);
typedef void (*tp_adf_dp_trace_cb)(struct adf_dp_trace_record_s* , uint16_t);
void adf_dp_display_record(struct adf_dp_trace_record_s *record,
@@ -194,8 +263,53 @@
void adf_dp_display_proto_pkt(struct adf_dp_trace_record_s *record,
uint16_t index);
void adf_dp_trace_log_pkt(uint8_t session_id, struct sk_buff *skb,
- uint8_t event_type);
+ enum adf_proto_dir dir);
+void adf_dp_trace_enable_live_mode(void);
+void adf_dp_trace_clear_buffer(void);
+/**
+ * adf_dp_trace_mgmt_pkt() - record mgmt packet
+ * @code: dptrace code
+ * @vdev_id: vdev id
+ * @type: proto type
+ * @subtype: proto subtype
+ *
+ * Return: none
+ */
+void adf_dp_trace_mgmt_pkt(enum ADF_DP_TRACE_ID code, uint8_t vdev_id,
+ enum adf_proto_type type, enum adf_proto_subtype subtype);
+
+/**
+ * adf_dp_display_mgmt_pkt() - display proto packet
+ * @record: dptrace record
+ * @index: index
+ *
+ * Return: none
+ */
+void adf_dp_display_mgmt_pkt(struct adf_dp_trace_record_s *record,
+ uint16_t index);
+
+/**
+ * adf_dp_display_event_record() - display event records
+ * @record: dptrace record
+ * @index: index
+ *
+ * Return: none
+ */
+void adf_dp_display_event_record(struct adf_dp_trace_record_s *record,
+ uint16_t index);
+
+/**
+ * adf_dp_trace_record_event() - record events
+ * @code: dptrace code
+ * @vdev_id: vdev id
+ * @type: proto type
+ * @subtype: proto subtype
+ *
+ * Return: none
+ */
+void adf_dp_trace_record_event(enum ADF_DP_TRACE_ID code, uint8_t vdev_id,
+ enum adf_proto_type type, enum adf_proto_subtype subtype);
#else
static inline void adf_dp_trace_init(void)
{
@@ -206,12 +320,14 @@
{
}
-static inline void adf_dp_trace_set_track(adf_nbuf_t nbuf)
+static inline void adf_dp_trace_set_track(adf_nbuf_t nbuf,
+ enum adf_proto_dir dir)
{
}
static inline void adf_dp_trace(adf_nbuf_t nbuf,
- enum ADF_DP_TRACE_ID code, uint8_t *data, uint8_t size)
+ enum ADF_DP_TRACE_ID code, uint8_t *data, uint8_t size,
+ enum adf_proto_dir dir)
{
}
@@ -254,10 +370,19 @@
}
static inline void adf_dp_trace_log_pkt(uint8_t session_id, struct sk_buff *skb,
- uint8_t event_type)
+ enum adf_proto_dir dir)
{
}
+static inline
+void adf_dp_trace_enable_live_mode(void)
+{
+}
+
+static inline
+void adf_dp_trace_clear_buffer(void)
+{
+}
#endif
#endif /* __ADF_TRACE_H */
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/linux/adf_nbuf_pvt.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/linux/adf_nbuf_pvt.h
index 4f492c5..7b3fe52 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/linux/adf_nbuf_pvt.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/adf/linux/adf_nbuf_pvt.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014,2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -88,9 +88,10 @@
* Store info for data path tracing
*/
struct {
- uint8_t packet_state;
- uint8_t packet_track;
- uint8_t dp_trace;
+ uint8_t packet_state:4;
+ uint8_t packet_track:2;
+ uint8_t dp_trace_tx:1;
+ uint8_t dp_trace_rx:1;
} trace;
/*
@@ -141,6 +142,16 @@
unsigned char tx_htt2_frm: 1;
unsigned char tx_htt2_reserved: 7;
#endif /* QCA_TX_HTT2_SUPPORT */
+ struct {
+ uint8_t is_eapol:1;
+ uint8_t is_arp:1;
+ uint8_t is_dhcp:1;
+ uint8_t is_wapi:1;
+ uint8_t is_mcast:1;
+ uint8_t is_bcast:1;
+ uint8_t reserved:1;
+ uint8_t print:1;
+ } packet_type;
} __packed;
#ifdef QCA_ARP_SPOOFING_WAR
@@ -212,17 +223,59 @@
#define NBUF_SET_PACKET_TRACK(skb, pkt_track) \
(((struct cvg_nbuf_cb *)((skb)->cb))->trace.packet_track = \
pkt_track)
+#define ADF_NBUF_SET_EAPOL(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_eapol = \
+ true)
+#define ADF_NBUF_SET_ARP(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_arp = \
+ true)
+#define ADF_NBUF_SET_DHCP(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_dhcp = \
+ true)
+#define ADF_NBUF_SET_WAPI(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_wapi = \
+ true)
+#define ADF_NBUF_SET_MCAST(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_mcast = \
+ true)
+#define ADF_NBUF_SET_BCAST(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_bcast = \
+ true)
+
#define NBUF_GET_PACKET_TRACK(skb) \
(((struct cvg_nbuf_cb *)((skb)->cb))->trace.packet_track)
#define NBUF_UPDATE_TX_PKT_COUNT(skb, PACKET_STATE) \
adf_nbuf_set_state(skb, PACKET_STATE)
-#define ADF_NBUF_SET_DP_TRACE(skb, enable) \
- (((struct cvg_nbuf_cb *)((skb)->cb))->trace.dp_trace \
- = enable)
-#define ADF_NBUF_GET_DP_TRACE(skb) \
- (((struct cvg_nbuf_cb *)((skb)->cb))->trace.dp_trace)
+#define ADF_NBUF_CB_TX_DP_TRACE(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->trace.dp_trace_tx)
+
+#define ADF_NBUF_CB_DP_TRACE_PRINT(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.print)
+
+#define ADF_NBUF_CB_RX_DP_TRACE(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->trace.dp_trace_rx)
+
+#define ADF_NBUF_GET_IS_EAPOL(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_eapol)
+
+#define ADF_NBUF_GET_IS_ARP(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_arp)
+
+#define ADF_NBUF_GET_IS_DHCP(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_dhcp)
+
+#define ADF_NBUF_GET_IS_WAPI(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_wapi)
+
+#define ADF_NBUF_GET_IS_BCAST(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_bcast)
+
+#define ADF_NBUF_GET_IS_MCAST(skb) \
+ (((struct cvg_nbuf_cb *)((skb)->cb))->packet_type.is_mcast)
+
+
#define __adf_nbuf_get_num_frags(skb) \
/* assume the OS provides a single fragment */ \
@@ -341,8 +394,8 @@
bool __adf_nbuf_data_is_ipv4_tcp_pkt(uint8_t *data);
bool __adf_nbuf_data_is_ipv6_udp_pkt(uint8_t *data);
bool __adf_nbuf_data_is_ipv6_tcp_pkt(uint8_t *data);
-a_status_t __adf_nbuf_data_is_dhcp_pkt(uint8_t *data);
-a_status_t __adf_nbuf_data_is_eapol_pkt(uint8_t *data);
+bool __adf_nbuf_data_is_dhcp_pkt(uint8_t *data);
+bool __adf_nbuf_data_is_eapol_pkt(uint8_t *data);
bool __adf_nbuf_data_is_ipv4_arp_pkt(uint8_t *data);
enum adf_proto_subtype __adf_nbuf_data_get_dhcp_subtype(uint8_t *data);
enum adf_proto_subtype __adf_nbuf_data_get_eapol_subtype(uint8_t *data);
@@ -351,6 +404,9 @@
enum adf_proto_subtype __adf_nbuf_data_get_icmpv6_subtype(uint8_t *data);
uint8_t __adf_nbuf_data_get_ipv4_proto(uint8_t *data);
uint8_t __adf_nbuf_data_get_ipv6_proto(uint8_t *data);
+bool __adf_nbuf_is_bcast_pkt(uint8_t *data);
+bool __adf_nbuf_is_multicast_pkt(uint8_t *data);
+bool __adf_nbuf_is_wai_pkt(uint8_t *data);
#ifdef QCA_PKT_PROTO_TRACE
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/dbglog_id.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/dbglog_id.h
index 66eafe6..6922d6f 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/dbglog_id.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/dbglog_id.h
@@ -33,16 +33,6 @@
#endif
/*
- * The target state machine framework will send dbglog messages on behalf on
- * other modules. We do this do avoid each target module adding identical
- * dbglog code for state transitions and event processing. We also don't want
- * to force each module to define the the same XXX_DBGID_SM_MSG with the same
- * value below. Instead we use a special ID that the host dbglog code
- * recognizes as a message sent by the SM on behalf on another module.
- */
-#define DBGLOG_DBGID_SM_FRAMEWORK_PROXY_DBGLOG_MSG 1000
-
-/*
* The nomenclature for the debug identifiers is MODULE_DESCRIPTION.
* Please ensure that the definition of any new debugid introduced is captured
* between the <MODULE>_DBGID_DEFINITION_START and
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_ctrl_api.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_ctrl_api.h
index d260abc..d5011d2 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_ctrl_api.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/ol_txrx_ctrl_api.h
@@ -779,6 +779,8 @@
ol_txrx_get_queue_status(
ol_txrx_pdev_handle pdev);
+void ol_txrx_dump_tx_queue_stats(ol_txrx_pdev_handle pdev_handle);
+
void ol_txrx_dump_tx_desc(ol_txrx_pdev_handle pdev);
/**
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wal_rx_desc.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wal_rx_desc.h
index 82ad0bf..168f35c 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wal_rx_desc.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wal_rx_desc.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2012, 2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2012, 2014, 2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -89,6 +89,9 @@
}u;
};
+#define FW_MSDU_INFO_FIRST_WAKEUP_M 0x40
+#define FW_MSDU_INFO_FIRST_WAKEUP_S 6
+
#define FW_RX_DESC_DISCARD_M 0x1
#define FW_RX_DESC_DISCARD_S 0
#define FW_RX_DESC_FORWARD_M 0x2
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wlan_defs.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wlan_defs.h
index 19c49f3..6a24728 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wlan_defs.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wlan_defs.h
@@ -60,6 +60,16 @@
#define SUPPORT_11AX 0 /* 11ax not supported by default */
#endif
+/* defines to set Packet extension values which can be 0 us, 8 us or 16 us */
+/* NOTE: Below values cannot be changed without breaking WMI Compatibility */
+#define MAX_HE_NSS 8
+#define MAX_HE_MODULATION 8
+#define MAX_HE_RU 4
+#define HE_MODULATION_NONE 7
+#define HE_PET_0_USEC 0
+#define HE_PET_8_USEC 1
+#define HE_PET_16_USEC 2
+
typedef enum {
MODE_11A = 0, /* 11a Mode */
MODE_11G = 1, /* 11b/g Mode */
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wlan_module_ids.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wlan_module_ids.h
index 716a4f6..da99e3c 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wlan_module_ids.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wlan_module_ids.h
@@ -100,6 +100,7 @@
WLAN_MODULE_QBOOST, /* 0x41 */
WLAN_MODULE_P2P_LISTEN_OFFLOAD, /* 0x42 */
WLAN_MODULE_HALPHY, /* 0x43 */
+ WAL_MODULE_ENQ, /* 0x44 */
WLAN_MODULE_ID_MAX,
WLAN_MODULE_ID_INVALID = WLAN_MODULE_ID_MAX,
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi.h
index ae7500c..7d31ff2 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi.h
@@ -91,9 +91,16 @@
#define WMI_SET_FIELD(_msg_buf, _msg_type, _f, _val) \
SET_FIELD(_msg_buf, _msg_type ## _ ## _f, _val)
-#define WMI_EP_APASS 0x0
+/* TYPO: leave incorrect name as an alias for the correct name */
+#define WMI_EP_APASS WMI_EP_APSS
+/* WLAN driver running on apps processor sub-system */
+#define WMI_EP_APSS 0x0
#define WMI_EP_LPASS 0x1
#define WMI_EP_SENSOR 0x2
+/* WLAN driver running on NANO Hub */
+#define WMI_EP_NANOHUB 0x3
+#define WMI_EP_MODEM 0x4
+#define WMI_EP_LOCATION 0x5
/*
* Control Path
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_services.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_services.h
index 37dc6f9..4eff350 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_services.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_services.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -42,136 +42,276 @@
-typedef enum {
- WMI_SERVICE_BEACON_OFFLOAD=0, /* beacon offload */
- WMI_SERVICE_SCAN_OFFLOAD, /* scan offload */
- WMI_SERVICE_ROAM_SCAN_OFFLOAD, /* roam scan offload */
- WMI_SERVICE_BCN_MISS_OFFLOAD, /* beacon miss offload */
- WMI_SERVICE_STA_PWRSAVE, /* fake sleep + basic power save */
- WMI_SERVICE_STA_ADVANCED_PWRSAVE, /* uapsd, pspoll, force sleep */
- WMI_SERVICE_AP_UAPSD, /* uapsd on AP */
- WMI_SERVICE_AP_DFS, /* DFS on AP */
- WMI_SERVICE_11AC, /* supports 11ac */
- WMI_SERVICE_BLOCKACK, /* Supports triggering ADDBA/DELBA from host*/
- WMI_SERVICE_PHYERR, /* PHY error */
- WMI_SERVICE_BCN_FILTER, /* Beacon filter support */
- WMI_SERVICE_RTT, /* RTT (round trip time) support */
- WMI_SERVICE_WOW, /* WOW Support */
- WMI_SERVICE_RATECTRL_CACHE, /* Rate-control caching */
- WMI_SERVICE_IRAM_TIDS, /* TIDs in IRAM */
- WMI_SERVICE_ARPNS_OFFLOAD, /* ARP NS Offload support for STA vdev */
- WMI_SERVICE_NLO, /* Network list offload service */
- WMI_SERVICE_GTK_OFFLOAD, /* GTK offload */
- WMI_SERVICE_SCAN_SCH, /* Scan Scheduler Service */
- WMI_SERVICE_CSA_OFFLOAD, /* CSA offload service */
- WMI_SERVICE_CHATTER, /* Chatter service */
- WMI_SERVICE_COEX_FREQAVOID, /* FW report freq range to avoid */
- WMI_SERVICE_PACKET_POWER_SAVE, /* packet power save service */
- WMI_SERVICE_FORCE_FW_HANG, /* Service to test the firmware recovery mechanism */
- WMI_SERVICE_GPIO, /* GPIO service */
- WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, /* Modulated DTIM support */
- WMI_STA_UAPSD_BASIC_AUTO_TRIG, /* Basic version of station UAPSD AC Trigger Generation Method with
- * variable tigger periods (service, delay, and suspend intervals) */
- WMI_STA_UAPSD_VAR_AUTO_TRIG, /* Station UAPSD AC Trigger Generation Method with variable
- * trigger periods (service, delay, and suspend intervals) */
- WMI_SERVICE_STA_KEEP_ALIVE, /* Serivce to support the STA KEEP ALIVE mechanism */
- WMI_SERVICE_TX_ENCAP, /* Packet type for TX encapsulation */
- WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, /* detect out-of-sync sleeping stations */
- WMI_SERVICE_EARLY_RX, /* adaptive early-rx feature */
- WMI_SERVICE_STA_SMPS, /* STA MIMO-PS */
- WMI_SERVICE_FWTEST, /* Firmware test service */
- WMI_SERVICE_STA_WMMAC, /* STA WMMAC */
- WMI_SERVICE_TDLS, /* TDLS support */
- WMI_SERVICE_BURST, /* SIFS spaced burst support */
- WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE, /* Dynamic beaocn interval change for SAP/P2p GO in MCC scenario */
- WMI_SERVICE_ADAPTIVE_OCS, /* Service to support adaptive off-channel scheduler */
- WMI_SERVICE_BA_SSN_SUPPORT, /* target will provide Sequence number for the peer/tid combo */
- WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE,
- WMI_SERVICE_WLAN_HB, /* wlan HB service */
- WMI_SERVICE_LTE_ANT_SHARE_SUPPORT, /* support LTE/WLAN antenna sharing */
- WMI_SERVICE_BATCH_SCAN, /*Service to support batch scan*/
- WMI_SERVICE_QPOWER, /* QPower service */
- WMI_SERVICE_PLMREQ,
- WMI_SERVICE_THERMAL_MGMT, /* thermal throttling support */
- WMI_SERVICE_RMC, /* RMC support */
- WMI_SERVICE_MHF_OFFLOAD, /* multi-hop forwarding offload */
- WMI_SERVICE_COEX_SAR, /* target support SAR tx limit from WMI_PDEV_PARAM_TXPOWER_LIMITxG */
- WMI_SERVICE_BCN_TXRATE_OVERRIDE, /* Will support the bcn/prb rsp rate override */
- WMI_SERVICE_NAN, /* Neighbor Awareness Network */
- WMI_SERVICE_L1SS_STAT, /* L1SS statistics counter report */
- WMI_SERVICE_ESTIMATE_LINKSPEED, /* Linkspeed Estimation per peer */
- WMI_SERVICE_OBSS_SCAN, /* Service to support OBSS scan */
- WMI_SERVICE_TDLS_OFFCHAN, /* TDLS off channel support */
- WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, /* TDLS UAPSD Buffer STA support */
- WMI_SERVICE_TDLS_UAPSD_SLEEP_STA, /* TDLS UAPSD Sleep STA support */
- WMI_SERVICE_IBSS_PWRSAVE, /* IBSS power save support */
- WMI_SERVICE_LPASS, /*Service to support LPASS*/
- WMI_SERVICE_EXTSCAN, /* Extended Scans */
- WMI_SERVICE_D0WOW, /* D0-WOW Support */
- WMI_SERVICE_HSOFFLOAD, /* Hotspot offload feature Support */
- WMI_SERVICE_ROAM_HO_OFFLOAD, /* roam handover offload */
- WMI_SERVICE_RX_FULL_REORDER, /* target-based Rx full reorder */
- WMI_SERVICE_DHCP_OFFLOAD, /* DHCP offload support */
- WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, /* STA RX DATA offload to IPA support */
- WMI_SERVICE_MDNS_OFFLOAD, /* mDNS responder offload support */
- WMI_SERVICE_SAP_AUTH_OFFLOAD, /* softap auth offload */
- WMI_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT, /* Dual Band Simultaneous support */
- WMI_SERVICE_OCB, /* OCB mode support */
- WMI_SERVICE_AP_ARPNS_OFFLOAD, /* arp offload support for ap mode vdev */
- WMI_SERVICE_PER_BAND_CHAINMASK_SUPPORT, /* Per band chainmask support */
- WMI_SERVICE_PACKET_FILTER_OFFLOAD, /* Per vdev packet filters */
- WMI_SERVICE_MGMT_TX_HTT, /* Mgmt Tx via HTT interface */
- WMI_SERVICE_MGMT_TX_WMI, /* Mgmt Tx via WMI interface */
- WMI_SERVICE_EXT_MSG, /* WMI_SERVICE_READY_EXT msg follows */
- WMI_SERVICE_MAWC, /* Motion Aided WiFi Connectivity (MAWC)*/
- WMI_SERVICE_PEER_ASSOC_CONF, /* target will send ASSOC_CONF after ASSOC_CMD is processed */
- WMI_SERVICE_EGAP, /* enhanced green ap support */
- WMI_SERVICE_STA_PMF_OFFLOAD, /* FW supports 11W PMF Offload for STA */
- WMI_SERVICE_UNIFIED_WOW_CAPABILITY, /* FW supports unified D0 and D3 wow */
- WMI_SERVICE_ENHANCED_PROXY_STA, /* Enhanced ProxySTA mode support */
- WMI_SERVICE_ATF, /* Air Time Fairness support */
- WMI_SERVICE_COEX_GPIO, /* BTCOEX GPIO support */
- WMI_SERVICE_AUX_SPECTRAL_INTF, /* Aux Radio enhancement support for ignoring spectral scan intf from main radios */
- WMI_SERVICE_AUX_CHAN_LOAD_INTF, /* Aux Radio enhancement support for ignoring chan load intf from main radios*/
- WMI_SERVICE_BSS_CHANNEL_INFO_64, /* BSS channel info (freq, noise floor, 64-bit counters) event support */
- WMI_SERVICE_ENTERPRISE_MESH, /* Enterprise MESH Service Support */
- WMI_SERVICE_RESTRT_CHNL_SUPPORT, /* Restricted Channel Support */
- WMI_SERVICE_BPF_OFFLOAD, /* FW supports bpf offload */
- WMI_SERVICE_SYNC_DELETE_CMDS, /* FW sends response event for Peer, Vdev delete commands */
- WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT,
- WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT,
- WMI_SERVICE_RATECTRL_LIMIT_MAX_MIN_RATES, /* allow per-peer tx MCS min/max limits by host */
- WMI_SERVICE_NAN_DATA, /* FW supports NAN data */
- WMI_SERVICE_NAN_RTT, /* FW supports NAN RTT */
- WMI_SERVICE_11AX, /* FW supports 802.11ax */
+typedef enum {
+ WMI_SERVICE_BEACON_OFFLOAD = 0, /* beacon offload */
+ WMI_SERVICE_SCAN_OFFLOAD = 1, /* scan offload */
+ WMI_SERVICE_ROAM_SCAN_OFFLOAD = 2, /* roam scan offload */
+ WMI_SERVICE_BCN_MISS_OFFLOAD = 3, /* beacon miss offload */
+ /* fake sleep + basic power save */
+ WMI_SERVICE_STA_PWRSAVE = 4,
+ WMI_SERVICE_STA_ADVANCED_PWRSAVE = 5, /* uapsd, pspoll, force sleep */
+ WMI_SERVICE_AP_UAPSD = 6, /* uapsd on AP */
+ WMI_SERVICE_AP_DFS = 7, /* DFS on AP */
+ WMI_SERVICE_11AC = 8, /* supports 11ac */
+ /* Supports triggering ADDBA/DELBA from host*/
+ WMI_SERVICE_BLOCKACK = 9,
+ WMI_SERVICE_PHYERR = 10, /* PHY error */
+ WMI_SERVICE_BCN_FILTER = 11, /* Beacon filter support */
+ /* RTT (round trip time) support */
+ WMI_SERVICE_RTT = 12,
+ WMI_SERVICE_WOW = 13, /* WOW Support */
+ WMI_SERVICE_RATECTRL_CACHE = 14, /* Rate-control caching */
+ WMI_SERVICE_IRAM_TIDS = 15, /* TIDs in IRAM */
+ /* ARP NS Offload support for STA vdev */
+ WMI_SERVICE_ARPNS_OFFLOAD = 16,
+ /* Network list offload service */
+ WMI_SERVICE_NLO = 17,
+ WMI_SERVICE_GTK_OFFLOAD = 18, /* GTK offload */
+ WMI_SERVICE_SCAN_SCH = 19, /* Scan Scheduler Service */
+ WMI_SERVICE_CSA_OFFLOAD = 20, /* CSA offload service */
+ WMI_SERVICE_CHATTER = 21, /* Chatter service */
+ /* FW report freq range to avoid */
+ WMI_SERVICE_COEX_FREQAVOID = 22,
+ WMI_SERVICE_PACKET_POWER_SAVE = 23, /* packet power save service */
+ /* Service to test the firmware recovery mechanism */
+ WMI_SERVICE_FORCE_FW_HANG = 24,
+ WMI_SERVICE_GPIO = 25, /* GPIO service */
+ /* Modulated DTIM support */
+ WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM = 26,
+ /**
+ * Basic version of station UAPSD AC Trigger Generation Method with
+ * variable tigger periods (service, delay, and suspend intervals)
+ */
+ WMI_STA_UAPSD_BASIC_AUTO_TRIG = 27,
+ /**
+ * Station UAPSD AC Trigger Generation Method with variable
+ * trigger periods (service, delay, and suspend intervals)
+ */
+ WMI_STA_UAPSD_VAR_AUTO_TRIG = 28,
+ /* Serivce to support the STA KEEP ALIVE mechanism */
+ WMI_SERVICE_STA_KEEP_ALIVE = 29,
+ /* Packet type for TX encapsulation */
+ WMI_SERVICE_TX_ENCAP = 30,
+ /* detect out-of-sync sleeping stations */
+ WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC = 31,
+ WMI_SERVICE_EARLY_RX = 32, /* adaptive early-rx feature */
+ WMI_SERVICE_STA_SMPS = 33, /* STA MIMO-PS */
+ WMI_SERVICE_FWTEST = 34, /* Firmware test service */
+ WMI_SERVICE_STA_WMMAC = 35, /* STA WMMAC */
+ WMI_SERVICE_TDLS = 36, /* TDLS support */
+ WMI_SERVICE_BURST = 37, /* SIFS spaced burst support */
+ /* Dynamic beaocn interval change for SAP/P2p GO in MCC scenario */
+ WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE = 38,
+ /* Service to support adaptive off-channel scheduler */
+ WMI_SERVICE_ADAPTIVE_OCS = 39,
+ /* target will provide Sequence number for the peer/tid combo */
+ WMI_SERVICE_BA_SSN_SUPPORT = 40,
+ WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE = 41,
+ WMI_SERVICE_WLAN_HB = 42, /* wlan HB service */
+ /* support LTE/WLAN antenna sharing */
+ WMI_SERVICE_LTE_ANT_SHARE_SUPPORT = 43,
+ WMI_SERVICE_BATCH_SCAN = 44, /*Service to support batch scan*/
+ WMI_SERVICE_QPOWER = 45, /* QPower service */
+ WMI_SERVICE_PLMREQ = 46,
+ WMI_SERVICE_THERMAL_MGMT = 47, /* thermal throttling support */
+ WMI_SERVICE_RMC = 48, /* RMC support */
+ /* multi-hop forwarding offload */
+ WMI_SERVICE_MHF_OFFLOAD = 49,
+ /* target support SAR tx limit from WMI_PDEV_PARAM_TXPOWER_LIMITxG */
+ WMI_SERVICE_COEX_SAR = 50,
+ /* Will support the bcn/prb rsp rate override */
+ WMI_SERVICE_BCN_TXRATE_OVERRIDE = 51,
+ WMI_SERVICE_NAN = 52, /* Neighbor Awareness Network */
+ /* L1SS statistics counter report */
+ WMI_SERVICE_L1SS_STAT = 53,
+ /* Linkspeed Estimation per peer */
+ WMI_SERVICE_ESTIMATE_LINKSPEED = 54,
+ /* Service to support OBSS scan */
+ WMI_SERVICE_OBSS_SCAN = 55,
+ WMI_SERVICE_TDLS_OFFCHAN = 56, /* TDLS off channel support */
+ /* TDLS UAPSD Buffer STA support */
+ WMI_SERVICE_TDLS_UAPSD_BUFFER_STA = 57,
+ /* TDLS UAPSD Sleep STA support */
+ WMI_SERVICE_TDLS_UAPSD_SLEEP_STA = 58,
+ WMI_SERVICE_IBSS_PWRSAVE = 59, /* IBSS power save support */
+ WMI_SERVICE_LPASS = 60, /*Service to support LPASS*/
+ WMI_SERVICE_EXTSCAN = 61, /* Extended Scans */
+ WMI_SERVICE_D0WOW = 62, /* D0-WOW Support */
+ /* Hotspot offload feature Support */
+ WMI_SERVICE_HSOFFLOAD = 63,
+ WMI_SERVICE_ROAM_HO_OFFLOAD = 64, /* roam handover offload */
+ /* target-based Rx full reorder */
+ WMI_SERVICE_RX_FULL_REORDER = 65,
+ WMI_SERVICE_DHCP_OFFLOAD = 66, /* DHCP offload support */
+ /* STA RX DATA offload to IPA support */
+ WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT = 67,
+ /* mDNS responder offload support */
+ WMI_SERVICE_MDNS_OFFLOAD = 68,
+ WMI_SERVICE_SAP_AUTH_OFFLOAD = 69, /* softap auth offload */
+ /* Dual Band Simultaneous support */
+ WMI_SERVICE_DUAL_BAND_SIMULTANEOUS_SUPPORT = 70,
+ WMI_SERVICE_OCB = 71, /* OCB mode support */
+ /* arp offload support for ap mode vdev */
+ WMI_SERVICE_AP_ARPNS_OFFLOAD = 72,
+ /* Per band chainmask support */
+ WMI_SERVICE_PER_BAND_CHAINMASK_SUPPORT = 73,
+ WMI_SERVICE_PACKET_FILTER_OFFLOAD = 74, /* Per vdev packet filters */
+ WMI_SERVICE_MGMT_TX_HTT = 75, /* Mgmt Tx via HTT interface */
+ WMI_SERVICE_MGMT_TX_WMI = 76, /* Mgmt Tx via WMI interface */
+ /* WMI_SERVICE_READY_EXT msg follows */
+ WMI_SERVICE_EXT_MSG = 77,
+ /* Motion Aided WiFi Connectivity (MAWC)*/
+ WMI_SERVICE_MAWC = 78,
+ /* target will send ASSOC_CONF after ASSOC_CMD is processed */
+ WMI_SERVICE_PEER_ASSOC_CONF = 79,
+ WMI_SERVICE_EGAP = 80, /* enhanced green ap support */
+ /* FW supports 11W PMF Offload for STA */
+ WMI_SERVICE_STA_PMF_OFFLOAD = 81,
+ /* FW supports unified D0 and D3 wow */
+ WMI_SERVICE_UNIFIED_WOW_CAPABILITY = 82,
+ /* Enhanced ProxySTA mode support */
+ WMI_SERVICE_ENHANCED_PROXY_STA = 83,
+ WMI_SERVICE_ATF = 84, /* Air Time Fairness support */
+ WMI_SERVICE_COEX_GPIO = 85, /* BTCOEX GPIO support */
+ /**
+ * Aux Radio enhancement support for ignoring spectral scan intf
+ * from main radios
+ */
+ WMI_SERVICE_AUX_SPECTRAL_INTF = 86,
+ /**
+ * Aux Radio enhancement support for ignoring chan load intf
+ * from main radios
+ */
+ WMI_SERVICE_AUX_CHAN_LOAD_INTF = 87,
+ /**
+ * BSS channel info (freq, noise floor, 64-bit counters)
+ * event support
+ */
+ WMI_SERVICE_BSS_CHANNEL_INFO_64 = 88,
+ /* Enterprise MESH Service Support */
+ WMI_SERVICE_ENTERPRISE_MESH = 89,
+ WMI_SERVICE_RESTRT_CHNL_SUPPORT = 90, /* Restricted Channel Support */
+ WMI_SERVICE_BPF_OFFLOAD = 91, /* FW supports bpf offload */
+ /* FW sends response event for Peer, Vdev delete commands */
+ WMI_SERVICE_SYNC_DELETE_CMDS = 92,
+ WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT = 93,
+ WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT = 94,
+ /* allow per-peer tx MCS min/max limits by host */
+ WMI_SERVICE_RATECTRL_LIMIT_MAX_MIN_RATES = 95,
+ WMI_SERVICE_NAN_DATA = 96, /* FW supports NAN data */
+ WMI_SERVICE_NAN_RTT = 97, /* FW supports NAN RTT */
+ WMI_SERVICE_11AX = 98, /* FW supports 802.11ax */
+
/* WMI_SERVICE_DEPRECATED_REPLACE
* FW supports these new WMI commands, to be used rather than
* deprecated matching commands:
* - WMI_PDEV_SET_PCL_CMDID (vs. WMI_SOC_SET_PCL_CMDID)
- * - WMI_PDEV_SET_HW_MODE_CMDID (vs. WMI_SOC_SET_HW_MODE_CMDID)
- * - WMI_PDEV_SET_MAC_CONFIG_CMDID (vs. WMI_SOC_SET_DUAL_MAC_CONFIG_CMDID)
- * - WMI_PDEV_SET_ANTENNA_MODE_CMDID (vs. WMI_SOC_SET_ANTENNA_MODE_CMDID)
- * - WMI_VDEV_SET_DSCP_TID_MAP_CMDID (vs. WMI_VDEV_SET_WMM_PARAMS_CMDID)
+ * - WMI_PDEV_SET_HW_MODE_CMDID
+ * (vs. WMI_SOC_SET_HW_MODE_CMDID)
+ * - WMI_PDEV_SET_MAC_CONFIG_CMDID
+ * (vs. WMI_SOC_SET_DUAL_MAC_CONFIG_CMDID)
+ * - WMI_PDEV_SET_ANTENNA_MODE_CMDID
+ * (vs. WMI_SOC_SET_ANTENNA_MODE_CMDID)
+ * - WMI_VDEV_SET_DSCP_TID_MAP_CMDID
+ * (vs. WMI_VDEV_SET_WMM_PARAMS_CMDID)
*/
- WMI_SERVICE_DEPRECATED_REPLACE,
- WMI_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE, /* FW supports a new mode that allows to run connection tracker in host */
- WMI_SERVICE_ENHANCED_MCAST_FILTER,/* FW supports enhanced multicast filtering (of mcast IP inside ucast WLAN) */
- WMI_SERVICE_PERIODIC_CHAN_STAT_SUPPORT, /* periodic channel stats service */
- WMI_SERVICE_MESH_11S,
- WMI_SERVICE_HALF_RATE_QUARTER_RATE_SUPPORT, /* FW+HW supports 10 MHz (half rate) and 5 MHz (quarter rate) channel bandwidth */
- WMI_SERVICE_VDEV_RX_FILTER, /* Support per-vdev specs of which rx frames to filter out */
- WMI_SERVICE_P2P_LISTEN_OFFLOAD_SUPPORT,
+ WMI_SERVICE_DEPRECATED_REPLACE = 99,
+ /**
+ * FW supports a new mode that allows to run connection tracker
+ * in host
+ */
+ WMI_SERVICE_TDLS_CONN_TRACKER_IN_HOST_MODE = 100,
+ /**
+ * FW supports enhanced multicast filtering (of mcast IP inside
+ * ucast WLAN)
+ */
+ WMI_SERVICE_ENHANCED_MCAST_FILTER = 101,
+ /* periodic channel stats service */
+ WMI_SERVICE_PERIODIC_CHAN_STAT_SUPPORT = 102,
+ WMI_SERVICE_MESH_11S = 103,
+ /**
+ * FW+HW supports 10 MHz (half rate) and 5 MHz (quarter rate)
+ * channel bandwidth
+ */
+ WMI_SERVICE_HALF_RATE_QUARTER_RATE_SUPPORT = 104,
+ /* Support per-vdev specs of which rx frames to filter out */
+ WMI_SERVICE_VDEV_RX_FILTER = 105,
+ WMI_SERVICE_P2P_LISTEN_OFFLOAD_SUPPORT = 106,
/*
* FW supports marking the first data packet which wakes
* the host from suspend
*/
- WMI_SERVICE_MARK_FIRST_WAKEUP_PACKET,
- WMI_MAX_SERVICE=128 /* max service */
+ WMI_SERVICE_MARK_FIRST_WAKEUP_PACKET = 107,
+ /* FW supports command that can add/delete multiple mcast filters */
+ WMI_SERVICE_MULTIPLE_MCAST_FILTER_SET = 108,
+ /* WMI_SERVICE_HOST_MANAGED_RX_REORDER -
+ * FW supports host-managed RX reorder.
+ * Host managed RX reorder involves RX BA state machine handling
+ * on peer/TID basis, REO configuration for HW based reordering/PN
+ * check and processing reorder exceptions generated by HW.
+ */
+ WMI_SERVICE_HOST_MANAGED_RX_REORDER = 109,
+ /* Specify whether the target supports the following WMI messages
+ * for reading / writing its flash memory:
+ * WMI_READ_DATA_FROM_FLASH_CMDID,
+ * WMI_READ_DATA_FROM_FLASH_EVENTID,
+ * WMI_TRANSFER_DATA_TO_FLASH_CMDID,
+ * WMI_TRANSFER_DATA_TO_FLASH_COMPLETE_EVENTID,
+ */
+ WMI_SERVICE_FLASH_RDWR_SUPPORT = 110,
+
+ WMI_SERVICE_WLAN_STATS_REPORT=111, /* support WLAN stats report */
+
+ /* WMI_SERVICE_TX_MSDU_ID_NEW_PARTITION_SUPPORT -
+ * FW supports bigger MSDU ID partition which is defined as
+ * HTT_TX_IPA_NEW_MSDU_ID_SPACE_BEGIN. When both host and FW support
+ * new partition, FW uses HTT_TX_IPA_NEW_MSDU_ID_SPACE_BEGIN. If host
+ * doesn't support, FW falls back to HTT_TX_IPA_MSDU_ID_SPACE_BEGIN
+ * Handshaking is done through WMI_INIT and WMI service ready
+ *
+ * support bigger MSDU ID partition
+ */
+ WMI_SERVICE_TX_MSDU_ID_NEW_PARTITION_SUPPORT = 112,
+ WMI_SERVICE_DFS_PHYERR_OFFLOAD=113,
+ WMI_SERVICE_RCPI_SUPPORT=114,
+ WMI_SERVICE_FW_MEM_DUMP_SUPPORT = 115, /* Support FW Memory dump */
+ WMI_SERVICE_PEER_STATS_INFO = 116, /* support per peer stats info */
+ WMI_SERVICE_REGULATORY_DB = 117, /* support regulatory database in FW */
+ WMI_SERVICE_11D_OFFLOAD = 118, /* support 11D scan offload in FW */
+ WMI_SERVICE_HW_DATA_FILTERING = 119,
+
+ /***** ADD NEW SERVICES HERE UNTIL ALL VALUES UP TO 128 ARE USED *****/
+
+ WMI_MAX_SERVICE = 128, /* max service */
+
+ /**
+ * NOTE:
+ * The above service flags are delivered in the wmi_service_bitmap
+ * field of the WMI_SERVICE_READY_EVENT message.
+ * The below service flags are delivered in a
+ * WMI_SERVICE_AVAILABLE_EVENT message rather than in the
+ * WMI_SERVICE_READY_EVENT message's wmi_service_bitmap field.
+ * The WMI_SERVICE_AVAILABLE_EVENT message immediately precedes the
+ * WMI_SERVICE_READY_EVENT message.
+ */
+
+ /*PUT 1ST EXT SERVICE HERE:*//*WMI_SERVICE_xxxxxxxx=128,*/
+ /*PUT 2ND EXT SERVICE HERE:*//*WMI_SERVICE_yyyyyyyy=129,*/
+
+ WMI_MAX_EXT_SERVICE
+
} WMI_SERVICE;
#define WMI_SERVICE_BM_SIZE ((WMI_MAX_SERVICE + sizeof(A_UINT32)- 1)/sizeof(A_UINT32))
-#define WMI_SERVICE_ROAM_OFFLOAD WMI_SERVICE_ROAM_SCAN_OFFLOAD /* depreciated the name WMI_SERVICE_ROAM_OFFLOAD, but here to help compiling with old host driver */
+#define WMI_NUM_EXT_SERVICES (WMI_MAX_EXT_SERVICE - WMI_MAX_SERVICE)
+#define WMI_SERVICE_EXT_BM_SIZE32 ((WMI_NUM_EXT_SERVICES + 31) / 32)
+
+/**
+ * depreciated the name WMI_SERVICE_ROAM_OFFLOAD, but here to help
+ * compiling with old host driver
+ */
+#define WMI_SERVICE_ROAM_OFFLOAD WMI_SERVICE_ROAM_SCAN_OFFLOAD
/*
* turn on the WMI service bit corresponding to the WMI service.
@@ -188,6 +328,39 @@
( ((pwmi_svc_bmap)[(svc_id)/(sizeof(A_UINT32))] & \
(1 << ((svc_id)%(sizeof(A_UINT32)))) ) != 0)
+#define WMI_SERVICE_EXT_ENABLE(pwmi_svc_bmap, pwmi_svc_ext_bmap, svc_id) \
+ do { \
+ if (svc_id < WMI_MAX_SERVICE) { \
+ WMI_SERVICE_ENABLE(pwmi_svc_bmap, svc_id); \
+ } else { \
+ int word = ((svc_id) - WMI_MAX_SERVICE) / 32; \
+ int bit = (svc_id) & 0x1f; /* svc_id mod 32 */ \
+ (pwmi_svc_ext_bmap)[word] |= (1 << bit); \
+ } \
+ } while (0)
+
+#define WMI_SERVICE_EXT_DISABLE(pwmi_svc_bmap, pwmi_svc_ext_bmap, svc_id) \
+ do { \
+ if (svc_id < WMI_MAX_SERVICE) { \
+ WMI_SERVICE_DISABLE(pwmi_svc_bmap, svc_id); \
+ } else { \
+ int word = ((svc_id) - WMI_MAX_SERVICE) / 32; \
+ int bit = (svc_id) & 0x1f; /* svc_id mod 32 */ \
+ (pwmi_svc_ext_bmap)[word] &= ~(1 << bit); \
+ } \
+ } while (0)
+
+#define WMI_SERVICE_EXT_IS_ENABLED(pwmi_svc_bmap, pwmi_svc_ext_bmap, svc_id) \
+ /* If the service ID is beyond the known limit, treat it as disabled */ \
+ ((svc_id) >= WMI_MAX_EXT_SERVICE ? 0 : \
+ /* If service ID is in the non-extension range, use the old check */ \
+ (svc_id) < WMI_MAX_SERVICE ? \
+ WMI_SERVICE_IS_ENABLED(pwmi_svc_bmap, svc_id) : \
+ /* If service ID is in the extended range, check ext_bmap */ \
+ (pwmi_svc_ext_bmap)[((svc_id) - WMI_MAX_SERVICE) / 32] >> \
+ ((svc_id) & 0x1f))
+
+
#ifdef __cplusplus
}
#endif
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_tlv_defs.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_tlv_defs.h
index ad06601..a0277a4 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_tlv_defs.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_tlv_defs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -714,6 +714,79 @@
WMITLV_TAG_STRUC_wmi_p2p_lo_start_cmd_fixed_param,
WMITLV_TAG_STRUC_wmi_p2p_lo_stop_cmd_fixed_param,
WMITLV_TAG_STRUC_wmi_p2p_lo_stopped_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_peer_reorder_queue_setup_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_peer_reorder_queue_remove_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_set_multiple_mcast_filter_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_mgmt_tx_compl_bundle_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_read_data_from_flash_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_read_data_from_flash_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_pdev_set_reorder_timeout_val_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_peer_set_rx_blocksize_cmd_fixed_param,
+ WMITLV_TAG_STRUC_WMI_PDEV_SET_WAKEUP_CONFIG_CMDID_fixed_param,
+ WMITLV_TAG_STRUC_wmi_tlv_buf_len_param,
+ WMITLV_TAG_STRUC_wmi_service_available_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_peer_antdiv_info_req_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_peer_antdiv_info_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_peer_antdiv_info,
+ WMITLV_TAG_STRUC_wmi_pdev_get_antdiv_status_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_pdev_antdiv_status_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_mnt_filter_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_get_chip_power_stats_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_pdev_chip_power_stats_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_coex_get_antenna_isolation_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_coex_report_isolation_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_chan_cca_stats,
+ WMITLV_TAG_STRUC_wmi_peer_signal_stats,
+ WMITLV_TAG_STRUC_wmi_tx_stats,
+ WMITLV_TAG_STRUC_wmi_peer_ac_tx_stats,
+ WMITLV_TAG_STRUC_wmi_rx_stats,
+ WMITLV_TAG_STRUC_wmi_peer_ac_rx_stats,
+ WMITLV_TAG_STRUC_wmi_report_stats_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_chan_cca_stats_thresh,
+ WMITLV_TAG_STRUC_wmi_peer_signal_stats_thresh,
+ WMITLV_TAG_STRUC_wmi_tx_stats_thresh,
+ WMITLV_TAG_STRUC_wmi_rx_stats_thresh,
+ WMITLV_TAG_STRUC_wmi_pdev_set_stats_threshold_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_request_wlan_stats_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_rx_aggr_failure_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_rx_aggr_failure_info,
+ WMITLV_TAG_STRUC_wmi_vdev_encrypt_decrypt_data_req_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_vdev_encrypt_decrypt_data_resp_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_pdev_band_to_mac,
+ WMITLV_TAG_STRUC_wmi_tbtt_offset_info,
+ WMITLV_TAG_STRUC_wmi_tbtt_offset_ext_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_sar_limits_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_sar_limit_cmd_row,
+ WMITLV_TAG_STRUC_wmi_pdev_dfs_phyerr_offload_enable_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_pdev_dfs_phyerr_offload_disable_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_vdev_adfs_ch_cfg_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_vdev_adfs_ocac_abort_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_pdev_dfs_radar_detection_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_vdev_adfs_ocac_complete_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_vdev_dfs_cac_complete_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_vendor_oui,
+ WMITLV_TAG_STRUC_wmi_request_rcpi_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_update_rcpi_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_request_peer_stats_info_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_peer_stats_info,
+ WMITLV_TAG_STRUC_wmi_peer_stats_info_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_pkgid_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_connected_nlo_rssi_params,
+ WMITLV_TAG_STRUC_wmi_set_current_country_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_regulatory_rule_struct,
+ WMITLV_TAG_STRUC_wmi_reg_chan_list_cc_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_11d_scan_start_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_11d_scan_stop_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_11d_new_country_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_request_radio_chan_stats_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_radio_chan_stats,
+ WMITLV_TAG_STRUC_wmi_radio_chan_stats_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_roam_per_config_fixed_param,
+ WMITLV_TAG_STRUC_wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_vdev_add_mac_addr_to_rx_filter_status_event_fixed_param,
+ WMITLV_TAG_STRUC_wmi_bpf_set_vdev_active_mode_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_hw_data_filter_cmd_fixed_param,
+ WMITLV_TAG_STRUC_wmi_connected_nlo_bss_band_rssi_pref,
} WMITLV_TAG_ID;
/*
@@ -1003,6 +1076,36 @@
OP(WMI_DBGLOG_TIME_STAMP_SYNC_CMDID) \
OP(WMI_P2P_LISTEN_OFFLOAD_START_CMDID) \
OP(WMI_P2P_LISTEN_OFFLOAD_STOP_CMDID) \
+ OP(WMI_PEER_REORDER_QUEUE_SETUP_CMDID) \
+ OP(WMI_PEER_REORDER_QUEUE_REMOVE_CMDID) \
+ OP(WMI_SET_MULTIPLE_MCAST_FILTER_CMDID) \
+ OP(WMI_READ_DATA_FROM_FLASH_CMDID) \
+ OP(WMI_PDEV_SET_REORDER_TIMEOUT_VAL_CMDID) \
+ OP(WMI_PEER_SET_RX_BLOCKSIZE_CMDID) \
+ OP(WMI_PDEV_SET_WAKEUP_CONFIG_CMDID) \
+ OP(WMI_PEER_ANTDIV_INFO_REQ_CMDID) \
+ OP(WMI_PDEV_GET_ANTDIV_STATUS_CMDID) \
+ OP(WMI_MNT_FILTER_CMDID) \
+ OP(WMI_PDEV_GET_CHIP_POWER_STATS_CMDID) \
+ OP(WMI_COEX_GET_ANTENNA_ISOLATION_CMDID) \
+ OP(WMI_PDEV_SET_STATS_THRESHOLD_CMDID) \
+ OP(WMI_REQUEST_WLAN_STATS_CMDID) \
+ OP(WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID) \
+ OP(WMI_SAR_LIMITS_CMDID) \
+ OP(WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID) \
+ OP(WMI_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMDID) \
+ OP(WMI_VDEV_ADFS_CH_CFG_CMDID) \
+ OP(WMI_VDEV_ADFS_OCAC_ABORT_CMDID) \
+ OP(WMI_REQUEST_RCPI_CMDID) \
+ OP(WMI_REQUEST_PEER_STATS_INFO_CMDID) \
+ OP(WMI_SET_CURRENT_COUNTRY_CMDID) \
+ OP(WMI_11D_SCAN_START_CMDID) \
+ OP(WMI_11D_SCAN_STOP_CMDID) \
+ OP(WMI_REQUEST_RADIO_CHAN_STATS_CMDID) \
+ OP(WMI_ROAM_PER_CONFIG_CMDID) \
+ OP(WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID) \
+ OP(WMI_BPF_SET_VDEV_ACTIVE_MODE_CMDID) \
+ OP(WMI_HW_DATA_FILTER_CMDID) \
/* add new CMD_LIST elements above this line */
/*
@@ -1154,6 +1257,26 @@
OP(WMI_PDEV_SET_MAC_CONFIG_RESP_EVENTID) \
OP(WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID) \
OP(WMI_P2P_LISTEN_OFFLOAD_STOPPED_EVENTID) \
+ OP(WMI_MGMT_TX_BUNDLE_COMPLETION_EVENTID) \
+ OP(WMI_READ_DATA_FROM_FLASH_EVENTID) \
+ OP(WMI_SERVICE_AVAILABLE_EVENTID) \
+ OP(WMI_PEER_ANTDIV_INFO_EVENTID) \
+ OP(WMI_PDEV_ANTDIV_STATUS_EVENTID) \
+ OP(WMI_PDEV_CHIP_POWER_STATS_EVENTID) \
+ OP(WMI_COEX_REPORT_ANTENNA_ISOLATION_EVENTID) \
+ OP(WMI_REPORT_STATS_EVENTID) \
+ OP(WMI_REPORT_RX_AGGR_FAILURE_EVENTID) \
+ OP(WMI_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENTID) \
+ OP(WMI_PDEV_DFS_RADAR_DETECTION_EVENTID) \
+ OP(WMI_VDEV_DFS_CAC_COMPLETE_EVENTID) \
+ OP(WMI_VDEV_ADFS_OCAC_COMPLETE_EVENTID) \
+ OP(WMI_UPDATE_RCPI_EVENTID) \
+ OP(WMI_PEER_STATS_INFO_EVENTID) \
+ OP(WMI_PKGID_EVENTID) \
+ OP(WMI_REG_CHAN_LIST_CC_EVENTID) \
+ OP(WMI_11D_NEW_COUNTRY_EVENTID) \
+ OP(WMI_RADIO_CHAN_STATS_EVENTID) \
+ OP(WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID) \
/* add new EVT_LIST elements above this line */
@@ -1163,7 +1286,8 @@
#define WMITLV_TABLE_WMI_INIT_CMDID(id,op,buf,len)\
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_init_cmd_fixed_param, wmi_init_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)\
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_resource_config, wmi_resource_config, resource_config, WMITLV_SIZE_FIX)\
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wlan_host_memory_chunk, host_mem_chunks, WMITLV_SIZE_VAR)
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wlan_host_memory_chunk, host_mem_chunks, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_set_hw_mode_cmd_fixed_param, wmi_pdev_set_hw_mode_cmd_fixed_param, hw_mode, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_INIT_CMDID);
@@ -1273,8 +1397,9 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_roam_offload_tlv_param, offload_param, WMITLV_SIZE_VAR) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_roam_11i_offload_tlv_param, offload_11i_param, WMITLV_SIZE_VAR) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_roam_11r_offload_tlv_param, offload_11r_param, WMITLV_SIZE_VAR) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_roam_ese_offload_tlv_param, offload_ese_param, WMITLV_SIZE_VAR)
-
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_STRUC, wmi_roam_ese_offload_tlv_param, offload_ese_param, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_STRUC, wmi_tlv_buf_len_param, assoc_ie_len_param, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, assoc_ie_buf, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_ROAM_SCAN_MODE);
/* Roam scan Rssi Threshold Cmd */
@@ -1320,6 +1445,11 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, non_prefer_ch_attr, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_ROAM_SET_MBO_PARAM_CMDID);
+/* Roam PER configure cmd */
+#define WMITLV_TABLE_WMI_ROAM_PER_CONFIG_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_roam_per_config_fixed_param, wmi_roam_per_config_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+
+WMITLV_CREATE_PARAM_STRUC(WMI_ROAM_PER_CONFIG_CMDID);
#define WMITLV_TABLE_WMI_VDEV_PLMREQ_START_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_plmreq_start_cmd_fixed_param, wmi_vdev_plmreq_start_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
@@ -1338,7 +1468,8 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, channel_list, WMITLV_SIZE_VAR) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_FIXED_STRUC, wmi_ssid, ssid_list, WMITLV_SIZE_VAR) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_FIXED_STRUC, wmi_mac_addr, bssid_list, WMITLV_SIZE_VAR) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ie_data, WMITLV_SIZE_VAR)
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ie_data, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vendor_oui, vendor_oui, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_START_SCAN_CMDID);
@@ -1651,13 +1782,13 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, data, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_RTT_TSF_CMDID);
-/*RTT OEM req Cmd - DEPRECATED */
+/* RTT OEM req Cmd */
#define WMITLV_TABLE_WMI_OEM_REQ_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, data, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_OEM_REQ_CMDID);
-/* RTT OEM request Cmd */
+/* RTT OEM request Cmd - DEPRECATED */
#define WMITLV_TABLE_WMI_OEM_REQUEST_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, data, WMITLV_SIZE_VAR)
@@ -1726,7 +1857,10 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, nlo_configured_parameters, nlo_list, WMITLV_SIZE_VAR) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, channel_list, WMITLV_SIZE_VAR)\
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, nlo_channel_prediction_cfg, channel_prediction_param, WMITLV_SIZE_VAR) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_enlo_candidate_score_param, enlo_candidate_score_params, candidate_score_params, WMITLV_SIZE_FIX)
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_enlo_candidate_score_param, enlo_candidate_score_params, candidate_score_params, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vendor_oui, vendor_oui, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_connected_nlo_rssi_params, connected_nlo_rssi_params, cnlo_rssi_params, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, connected_nlo_bss_band_rssi_pref, cnlo_bss_band_rssi_pref, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
@@ -1766,9 +1900,13 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, data, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_QVIT_CMDID);
+#define WMITLV_TABLE_WMI_PDEV_SET_WAKEUP_CONFIG_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_WMI_PDEV_SET_WAKEUP_CONFIG_CMDID_fixed_param, WMI_PDEV_SET_WAKEUP_CONFIG_CMDID_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_SET_WAKEUP_CONFIG_CMDID);
+
/* Vdev Set keep alive Cmd */
-#define WMITLV_TABLE_WMI_VDEV_SET_KEEPALIVE_CMDID(id,op,buf,len) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_set_keepalive_cmd_fixed_param, wmi_vdev_set_keepalive_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+#define WMITLV_TABLE_WMI_VDEV_SET_KEEPALIVE_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_vdev_set_keepalive_cmd_fixed_param, wmi_vdev_set_keepalive_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_SET_KEEPALIVE_CMDID);
/* Vdev Get keep alive Cmd */
@@ -1801,6 +1939,12 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_WMI_SET_MCASTBCAST_FILTER_CMD_fixed_param, WMI_SET_MCASTBCAST_FILTER_CMD_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_SET_MCASTBCAST_FILTER_CMDID);
+/* Enhanced Mcast add/delete filter list cmd */
+#define WMITLV_TABLE_WMI_SET_MULTIPLE_MCAST_FILTER_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_set_multiple_mcast_filter_cmd_fixed_param, WMI_SET_MULTIPLE_MCAST_FILTER_CMD_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_FIXED_STRUC, wmi_mac_addr, mcast_list, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_SET_MULTIPLE_MCAST_FILTER_CMDID);
+
/* Set dbglog time stamp sync cmd */
#define WMITLV_TABLE_WMI_DBGLOG_TIME_STAMP_SYNC_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_dbglog_time_stamp_sync_cmd_fixed_param, WMI_DBGLOG_TIME_STAMP_SYNC_CMD_fixed_param, fixed_param, WMITLV_SIZE_FIX)
@@ -1993,6 +2137,30 @@
WMITLV_CREATE_PARAM_STRUC(WMI_DFS_PHYERR_FILTER_DIS_CMDID);
+/* DFS phyerr processing offload enable cmd */
+#define WMITLV_TABLE_WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_dfs_phyerr_offload_enable_cmd_fixed_param, wmi_pdev_dfs_phyerr_offload_enable_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+
+WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID);
+
+/* DFS phyerr processing offload disble cmd */
+#define WMITLV_TABLE_WMI_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_dfs_phyerr_offload_disable_cmd_fixed_param, wmi_pdev_dfs_phyerr_offload_disable_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+
+WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMDID);
+
+/* set ADFS channel config cmd */
+#define WMITLV_TABLE_WMI_VDEV_ADFS_CH_CFG_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_adfs_ch_cfg_cmd_fixed_param, wmi_vdev_adfs_ch_cfg_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+
+WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_ADFS_CH_CFG_CMDID);
+
+/* DFS abort ADFS ocac currently in progress */
+#define WMITLV_TABLE_WMI_VDEV_ADFS_OCAC_ABORT_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_adfs_ocac_abort_cmd_fixed_param, wmi_vdev_adfs_ocac_abort_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+
+WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_ADFS_OCAC_ABORT_CMDID);
+
/* WOW Add Wake Pattern Cmd */
#define WMITLV_TABLE_WMI_WOW_ADD_WAKE_PATTERN_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_WMI_WOW_ADD_PATTERN_CMD_fixed_param, WMI_WOW_ADD_PATTERN_CMD_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
@@ -2135,7 +2303,8 @@
WMITLV_CREATE_PARAM_STRUC(WMI_SCAN_UPDATE_REQUEST_CMDID);
#define WMITLV_TABLE_WMI_SCAN_PROB_REQ_OUI_CMDID(id,op,buf,len) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_scan_prob_req_oui_cmd_fixed_param, wmi_scan_prob_req_oui_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_scan_prob_req_oui_cmd_fixed_param, wmi_scan_prob_req_oui_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vendor_oui, vendor_oui, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_SCAN_PROB_REQ_OUI_CMDID);
@@ -2297,6 +2466,12 @@
WMITLV_CREATE_PARAM_STRUC(WMI_PEER_INFO_REQ_CMDID);
+#define WMITLV_TABLE_WMI_PEER_ANTDIV_INFO_REQ_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_peer_antdiv_info_req_cmd_fixed_param, \
+ wmi_peer_antdiv_info_req_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+
+WMITLV_CREATE_PARAM_STRUC(WMI_PEER_ANTDIV_INFO_REQ_CMDID);
+
#define WMITLV_TABLE_WMI_RMC_SET_MODE_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_rmc_set_mode_cmd_fixed_param, \
wmi_rmc_set_mode_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
@@ -2413,7 +2588,8 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_ndp_initiator_req_fixed_param, wmi_ndp_initiator_req_fixed_param_PROTOTYPE, fixed_param, WMITLV_SIZE_FIX) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_channel, wmi_channel, channel, WMITLV_SIZE_FIX) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_cfg, WMITLV_SIZE_VAR) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_app_info, WMITLV_SIZE_VAR)
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_app_info, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_pmk, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_NDP_INITIATOR_REQ_CMDID);
/** NAN Data Responder Request Cmd
@@ -2425,7 +2601,8 @@
#define WMITLV_TABLE_WMI_NDP_RESPONDER_REQ_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_ndp_responder_req_fixed_param, wmi_ndp_responder_req_fixed_param_PROTOTYPE, fixed_param, WMITLV_SIZE_FIX) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_cfg, WMITLV_SIZE_VAR) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_app_info, WMITLV_SIZE_VAR)
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_app_info, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_pmk, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_NDP_RESPONDER_REQ_CMDID);
/** NAN Data End Request Cmd
@@ -2439,11 +2616,22 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_ndp_end_req_PROTOTYPE, ndp_end_req_list, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_NDP_END_REQ_CMDID);
+/* RCPI Info Request Cmd */
+#define WMITLV_TABLE_WMI_REQUEST_RCPI_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_request_rcpi_cmd_fixed_param, wmi_request_rcpi_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_REQUEST_RCPI_CMDID);
+
/* Modem power state cmd */
#define WMITLV_TABLE_WMI_MODEM_POWER_STATE_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_modem_power_state_cmd_param, wmi_modem_power_state_cmd_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_MODEM_POWER_STATE_CMDID);
+/* SAR limit update cmd */
+#define WMITLV_TABLE_WMI_SAR_LIMITS_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_sar_limits_cmd_fixed_param, wmi_sar_limits_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_STRUC, wmi_sar_limit_cmd_row, sar_limits, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_SAR_LIMITS_CMDID);
+
/* get estimated link speed cmd */
#define WMITLV_TABLE_WMI_PEER_GET_ESTIMATED_LINKSPEED_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_peer_get_estimated_linkspeed_cmd_fixed_param, wmi_peer_get_estimated_linkspeed_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
@@ -2503,6 +2691,17 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_get_temperature_cmd_fixed_param, wmi_pdev_get_temperature_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_GET_TEMPERATURE_CMDID);
+/* Pdev get ANT DIV feature status Cmd */
+#define WMITLV_TABLE_WMI_PDEV_GET_ANTDIV_STATUS_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_pdev_get_antdiv_status_cmd_fixed_param, wmi_pdev_get_antdiv_status_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_GET_ANTDIV_STATUS_CMDID);
+
+/* DISA feature : vdev encrypt decrypt request */
+#define WMITLV_TABLE_WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_encrypt_decrypt_data_req_cmd_fixed_param, wmi_vdev_encrypt_decrypt_data_req_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, data, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID);
+
/* Set antenna diversity Cmd */
#define WMITLV_TABLE_WMI_SET_ANTENNA_DIVERSITY_CMDID(id,op,buf,len) \
WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_pdev_set_antenna_diversity_cmd_fixed_param, wmi_pdev_set_antenna_diversity_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
@@ -2551,7 +2750,9 @@
#define WMITLV_TABLE_WMI_ROAM_INVOKE_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_roam_invoke_cmd_fixed_param, wmi_roam_invoke_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, channel_list, WMITLV_SIZE_VAR) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_FIXED_STRUC, wmi_mac_addr, bssid_list, WMITLV_SIZE_VAR)
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_FIXED_STRUC, wmi_mac_addr, bssid_list, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_FIXED_STRUC, wmi_tlv_buf_len_param, bcn_prb_buf_list, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, bcn_prb_frm, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_ROAM_INVOKE_CMDID);
/* SAP Authentication offload param Cmd */
@@ -2698,6 +2899,10 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, data, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_TRANSFER_DATA_TO_FLASH_CMDID);
+#define WMITLV_TABLE_WMI_READ_DATA_FROM_FLASH_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_read_data_from_flash_cmd_fixed_param, wmi_read_data_from_flash_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_READ_DATA_FROM_FLASH_CMDID);
+
#define WMITLV_TABLE_WMI_CONFIG_ENHANCED_MCAST_FILTER_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_config_enhanced_mcast_filter_fixed_param, wmi_config_enhanced_mcast_filter_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_CONFIG_ENHANCED_MCAST_FILTER_CMDID);
@@ -2731,6 +2936,11 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_WMI_COEX_CONFIG_CMD_fixed_param, WMI_COEX_CONFIG_CMD_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_COEX_CONFIG_CMDID);
+/* Coex get antenna isolation cmd */
+#define WMITLV_TABLE_WMI_COEX_GET_ANTENNA_ISOLATION_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_coex_get_antenna_isolation_cmd_fixed_param, wmi_coex_get_antenna_isolation_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_COEX_GET_ANTENNA_ISOLATION_CMDID);
+
/* bpf offload capability get cmd */
#define WMITLV_TABLE_WMI_BPF_GET_CAPABILITY_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_bpf_get_capability_cmd_fixed_param, wmi_bpf_get_capability_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
@@ -2752,6 +2962,10 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_bpf_del_vdev_instructions_cmd_fixed_param, wmi_bpf_del_vdev_instructions_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_BPF_DEL_VDEV_INSTRUCTIONS_CMDID);
+#define WMITLV_TABLE_WMI_BPF_SET_VDEV_ACTIVE_MODE_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_bpf_set_vdev_active_mode_cmd_fixed_param, wmi_bpf_set_vdev_active_mode_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_BPF_SET_VDEV_ACTIVE_MODE_CMDID);
+
/* Enable/Disable Smart Antenna */
#define WMITLV_TABLE_WMI_PDEV_SMART_ANT_ENABLE_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_smart_ant_enable_cmd_fixed_param, wmi_pdev_smart_ant_enable_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
@@ -2876,12 +3090,89 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, args, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_WAL_POWER_DEBUG_CMDID);
+/* pdev set reorder timeout val */
+#define WMITLV_TABLE_WMI_PDEV_SET_REORDER_TIMEOUT_VAL_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_pdev_set_reorder_timeout_val_cmd_fixed_param, wmi_pdev_set_reorder_timeout_val_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_SET_REORDER_TIMEOUT_VAL_CMDID);
+
+/* peer set rx blocksize cmd */
+#define WMITLV_TABLE_WMI_PEER_SET_RX_BLOCKSIZE_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_peer_set_rx_blocksize_cmd_fixed_param, wmi_peer_set_rx_blocksize_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PEER_SET_RX_BLOCKSIZE_CMDID);
+
/* Bandwidth Fairness (BWF) peer configure commands */
#define WMITLV_TABLE_WMI_PEER_BWF_REQUEST_CMDID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_peer_bwf_request_fixed_param, wmi_peer_bwf_request_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_bwf_peer_info, peer_info, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_PEER_BWF_REQUEST_CMDID);
+/* peer reorder queue setup cmd */
+#define WMITLV_TABLE_WMI_PEER_REORDER_QUEUE_SETUP_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_peer_reorder_queue_setup_cmd_fixed_param, wmi_peer_reorder_queue_setup_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PEER_REORDER_QUEUE_SETUP_CMDID);
+
+/* peer reorder queue remove cmd */
+#define WMITLV_TABLE_WMI_PEER_REORDER_QUEUE_REMOVE_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_peer_reorder_queue_remove_cmd_fixed_param, wmi_peer_reorder_queue_remove_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PEER_REORDER_QUEUE_REMOVE_CMDID);
+
+/* Filter in monitor mode paramters Cmd */
+#define WMITLV_TABLE_WMI_MNT_FILTER_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_mnt_filter_cmd_fixed_param, wmi_mnt_filter_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_MNT_FILTER_CMDID);
+
+/* WLAN GET Chip power Stats*/
+#define WMITLV_TABLE_WMI_PDEV_GET_CHIP_POWER_STATS_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_get_chip_power_stats_cmd_fixed_param, wmi_pdev_get_chip_power_stats_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_GET_CHIP_POWER_STATS_CMDID);
+
+/* pdev set stats threshold cmd*/
+#define WMITLV_TABLE_WMI_PDEV_SET_STATS_THRESHOLD_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_set_stats_threshold_cmd_fixed_param, wmi_pdev_set_stats_threshold_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_chan_cca_stats_thresh, wmi_chan_cca_stats_thresh, cca_thresh, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_peer_signal_stats_thresh, wmi_peer_signal_stats_thresh, signal_thresh, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_tx_stats_thresh, wmi_tx_stats_thresh, tx_thresh, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_rx_stats_thresh, wmi_rx_stats_thresh, rx_thresh, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_SET_STATS_THRESHOLD_CMDID);
+
+/* Request wlan stats cmd */
+#define WMITLV_TABLE_WMI_REQUEST_WLAN_STATS_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_request_wlan_stats_cmd_fixed_param, wmi_request_wlan_stats_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_REQUEST_WLAN_STATS_CMDID);
+
+/* Request peer stats info cmd */
+#define WMITLV_TABLE_WMI_REQUEST_PEER_STATS_INFO_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_request_peer_stats_info_cmd_fixed_param, wmi_request_peer_stats_info_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_REQUEST_PEER_STATS_INFO_CMDID);
+
+/* Host sets the current country code */
+#define WMITLV_TABLE_WMI_SET_CURRENT_COUNTRY_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_set_current_country_cmd_fixed_param, wmi_set_current_country_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_SET_CURRENT_COUNTRY_CMDID);
+
+/* Start 11d scan in FW */
+#define WMITLV_TABLE_WMI_11D_SCAN_START_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_11d_scan_start_cmd_fixed_param, wmi_11d_scan_start_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_11D_SCAN_START_CMDID);
+
+/* Stop 11d scan in FW */
+#define WMITLV_TABLE_WMI_11D_SCAN_STOP_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_11d_scan_stop_cmd_fixed_param, wmi_11d_scan_stop_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_11D_SCAN_STOP_CMDID);
+
+/* Request radio channel stats cmd */
+#define WMITLV_TABLE_WMI_REQUEST_RADIO_CHAN_STATS_CMDID(id,op,buf,len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_request_radio_chan_stats_cmd_fixed_param, wmi_request_radio_chan_stats_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_REQUEST_RADIO_CHAN_STATS_CMDID);
+/* mac randomization cmd */
+#define WMITLV_TABLE_WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param, wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID);
+
+#define WMITLV_TABLE_WMI_HW_DATA_FILTER_CMDID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_hw_data_filter_cmd_fixed_param, wmi_hw_data_filter_cmd_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_HW_DATA_FILTER_CMDID);
+
/************************** TLV definitions of WMI events *******************************/
/* Service Ready event */
@@ -2893,6 +3184,11 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, wlan_dbs_hw_mode_list, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_SERVICE_READY_EVENTID);
+/* service available event */
+#define WMITLV_TABLE_WMI_SERVICE_AVAILABLE_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_service_available_event_fixed_param, wmi_service_available_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_SERVICE_AVAILABLE_EVENTID);
+
/* Service Ready Extension event */
#define WMITLV_TABLE_WMI_SERVICE_READY_EXT_EVENTID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_service_ready_ext_event_fixed_param, wmi_service_ready_ext_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
@@ -3004,6 +3300,13 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_mgmt_tx_compl_event_fixed_param, wmi_mgmt_tx_compl_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_MGMT_TX_COMPLETION_EVENTID);
+/* Bundled Mgmt TX completion event */
+#define WMITLV_TABLE_WMI_MGMT_TX_BUNDLE_COMPLETION_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_mgmt_tx_compl_bundle_event_fixed_param, wmi_mgmt_tx_compl_bundle_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, desc_ids, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, status, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_MGMT_TX_BUNDLE_COMPLETION_EVENTID);
+
/* VDEV Start response Event */
#define WMITLV_TABLE_WMI_VDEV_START_RESP_EVENTID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_start_response_event_fixed_param, wmi_vdev_start_response_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
@@ -3076,7 +3379,9 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_channel, wmi_channel, chan, WMITLV_SIZE_FIX) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_key_material, key, WMITLV_SIZE_VAR) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, status, WMITLV_SIZE_VAR) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, reassoc_req_frame, WMITLV_SIZE_VAR)
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, reassoc_req_frame, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_STRUC, wmi_pdev_hw_mode_transition_event_fixed_param, hw_mode_transition_fixed_param, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_STRUC, wmi_pdev_set_hw_mode_response_vdev_mac_entry, wmi_pdev_set_hw_mode_response_vdev_mac_mapping, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_ROAM_SYNCH_EVENTID);
/* WOW Wakeup Host Event */
@@ -3147,6 +3452,11 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_transfer_data_to_flash_complete_event_fixed_param, wmi_transfer_data_to_flash_complete_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_TRANSFER_DATA_TO_FLASH_COMPLETE_EVENTID);
+#define WMITLV_TABLE_WMI_READ_DATA_FROM_FLASH_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_read_data_from_flash_event_fixed_param, wmi_read_data_from_flash_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, data, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_READ_DATA_FROM_FLASH_EVENTID);
+
/* Diagnostics Event */
#define WMITLV_TABLE_WMI_DIAG_EVENTID(id,op,buf,len)\
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, bufp, WMITLV_SIZE_VAR)
@@ -3234,6 +3544,11 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_vdev_rate_ht_info, ht_info, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_UPDATE_VDEV_RATE_STATS_EVENTID);
+/* report rx aggregation failure information */
+#define WMITLV_TABLE_WMI_REPORT_RX_AGGR_FAILURE_EVENTID(id,op,buf,len)\
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_rx_aggr_failure_event_fixed_param, wmi_rx_aggr_failure_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_rx_aggr_failure_info, failure_info, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_REPORT_RX_AGGR_FAILURE_EVENTID);
/* Update memory dump complete Event */
#define WMITLV_TABLE_WMI_UPDATE_FW_MEM_DUMP_EVENTID(id,op,buf,len)\
@@ -3367,6 +3682,11 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_info, peer_info, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_PEER_INFO_EVENTID);
+#define WMITLV_TABLE_WMI_PEER_ANTDIV_INFO_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_peer_antdiv_info_event_fixed_param, wmi_peer_antdiv_info_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_antdiv_info, peer_info, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_PEER_ANTDIV_INFO_EVENTID);
+
#define WMITLV_TABLE_WMI_PEER_TX_FAIL_CNT_THR_EVENTID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_peer_tx_fail_cnt_thr_event_fixed_param, wmi_peer_tx_fail_cnt_thr_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_PEER_TX_FAIL_CNT_THR_EVENTID);
@@ -3376,6 +3696,18 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_dfs_radar_event_fixed_param, wmi_dfs_radar_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_DFS_RADAR_EVENTID);
+#define WMITLV_TABLE_WMI_PDEV_DFS_RADAR_DETECTION_EVENTID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_dfs_radar_detection_event_fixed_param, wmi_pdev_dfs_radar_detection_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_DFS_RADAR_DETECTION_EVENTID);
+
+#define WMITLV_TABLE_WMI_VDEV_ADFS_OCAC_COMPLETE_EVENTID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_adfs_ocac_complete_event_fixed_param, wmi_vdev_adfs_ocac_complete_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_ADFS_OCAC_COMPLETE_EVENTID);
+
+#define WMITLV_TABLE_WMI_VDEV_DFS_CAC_COMPLETE_EVENTID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_vdev_dfs_cac_complete_event_fixed_param, wmi_vdev_dfs_cac_complete_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_DFS_CAC_COMPLETE_EVENTID);
+
/* Thermal Event */
#define WMITLV_TABLE_WMI_THERMAL_MGMT_EVENTID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_thermal_mgmt_event_fixed_param, wmi_thermal_mgmt_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
@@ -3407,6 +3739,11 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_nan_joined_cluster_event_fixed_param, wmi_nan_joined_cluster_event_fixed_param_PROTOTYPE, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_NAN_JOINED_CLUSTER_EVENTID);
+/* Coex report antenna isolation event */
+#define WMITLV_TABLE_WMI_COEX_REPORT_ANTENNA_ISOLATION_EVENTID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_coex_report_isolation_event_fixed_param, wmi_coex_report_isolation_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_COEX_REPORT_ANTENNA_ISOLATION_EVENTID);
+
/* NDP capabilities response event */
#define WMITLV_TABLE_WMI_NDI_CAP_RSP_EVENTID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_ndi_cap_rsp_event_fixed_param, wmi_ndi_cap_rsp_event_fixed_param_PROTOTYPE, fixed_param, WMITLV_SIZE_FIX)
@@ -3445,7 +3782,8 @@
#define WMITLV_TABLE_WMI_NDP_INDICATION_EVENTID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_ndp_indication_event_fixed_param, wmi_ndp_indication_event_fixed_param_PROTOTYPE, fixed_param, WMITLV_SIZE_FIX) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_cfg, WMITLV_SIZE_VAR) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_app_info, WMITLV_SIZE_VAR)
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_app_info, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, ndp_scid, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_NDP_INDICATION_EVENTID);
/** NDP confirm event
@@ -3470,6 +3808,11 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_ndp_end_indication_PROTOTYPE, ndp_end_indication_list, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_NDP_END_INDICATION_EVENTID);
+/* Update RCPI Info Event */
+#define WMITLV_TABLE_WMI_UPDATE_RCPI_EVENTID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_update_rcpi_event_fixed_param, wmi_update_rcpi_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_UPDATE_RCPI_EVENTID);
+
/* L1SS track Event */
#define WMITLV_TABLE_WMI_PDEV_L1SS_TRACK_EVENTID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_l1ss_track_event_fixed_param, wmi_pdev_l1ss_track_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
@@ -3524,6 +3867,11 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_temperature_event_fixed_param, wmi_pdev_temperature_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_TEMPERATURE_EVENTID);
+/* Pdev get ANT DIV feature status event */
+#define WMITLV_TABLE_WMI_PDEV_ANTDIV_STATUS_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_pdev_antdiv_status_event_fixed_param, wmi_pdev_antdiv_status_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_ANTDIV_STATUS_EVENTID);
+
/* mDNS offload stats event */
#define WMITLV_TABLE_WMI_MDNS_STATS_EVENTID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_mdns_stats_event_fixed_param, wmi_mdns_stats_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
@@ -3651,10 +3999,21 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_bpf_vdev_stats_info_evt_fixed_param, wmi_bpf_vdev_stats_info_evt_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_BPF_VDEV_STATS_INFO_EVENTID);
+/* Indicate new country code to host from 11d scan */
+#define WMITLV_TABLE_WMI_11D_NEW_COUNTRY_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_11d_new_country_event_fixed_param, wmi_11d_new_country_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_11D_NEW_COUNTRY_EVENTID);
+
+/* Regulatory channel list of current country code */
+#define WMITLV_TABLE_WMI_REG_CHAN_LIST_CC_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_reg_chan_list_cc_event_fixed_param, wmi_reg_chan_list_cc_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_STRUC, wmi_regulatory_rule_struct, reg_rule_array, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_REG_CHAN_LIST_CC_EVENTID);
+
/* FIPS event */
#define WMITLV_TABLE_WMI_PDEV_FIPS_EVENTID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_fips_event_fixed_param, wmi_pdev_fips_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
- WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, data, WMITLV_SIZE_VAR)
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, data, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_FIPS_EVENTID);
#define WMITLV_TABLE_WMI_PDEV_CHANNEL_HOPPING_EVENTID(id,op,buf,len) \
@@ -3665,6 +4024,11 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_ani_cck_event_fixed_param, wmi_ani_cck_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_ANI_CCK_LEVEL_EVENTID);
+#define WMITLV_TABLE_WMI_PDEV_CHIP_POWER_STATS_EVENTID(id,op,buf,len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_pdev_chip_power_stats_event_fixed_param, wmi_pdev_chip_power_stats_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, debug_registers, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_CHIP_POWER_STATS_EVENTID);
+
#define WMITLV_TABLE_WMI_PDEV_ANI_OFDM_LEVEL_EVENTID(id,op,buf,len) \
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_ani_ofdm_event_fixed_param, wmi_ani_ofdm_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
WMITLV_CREATE_PARAM_STRUC(WMI_PDEV_ANI_OFDM_LEVEL_EVENTID);
@@ -3704,6 +4068,158 @@
WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, tx_time_per_power_level, WMITLV_SIZE_VAR)
WMITLV_CREATE_PARAM_STRUC(WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID);
+/* Layout of WMI_REPORT_STATS_EVENTID message:
+ * fixed_param;
+ * wmi_chan_cca_stats chan_cca_stats[]; Array length is specified by num_chan_cca_stats
+ * wmi_peer_signal_stats peer_signal_stats[]; Array length is specified by num_peer_signal_stats
+ * wmi_peer_ac_tx_stats peer_ac_tx_stats[]; Array length is specified by num_peer_ac_tx_stats
+ * wmi_tx_stats tx_stats[][]; Array length is num_peer_ac_tx_stats * WLAN_MAX_AC, array index is (peer_index * WLAN_MAX_AC + ac_index)
+ * A_UINT32 tx_mpdu_aggr[][][]; Array length is num_peer_ac_tx_stats * WLAN_MAX_AC * tx_mpdu_aggr_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * tx_mpdu_aggr_array_len + A-MPDU size index
+ * Contains a histogram of how many A-MPDUs of a given size (i.e. number of MPDUs) have been transmitted.
+ * Element 0 contains the number of PPDUs with a single-MPDU A-MPDU.
+ * Element 1 contains the number of PPDUs with 2 MPDUs.
+ * Element 2 contains the number of PPDUs with 3 MPDUs.
+ * Element tx_mpdu_aggr_array_len-1 contains the number of PPDUs with >= tx_mpdu_aggr_array_len MPDUs.
+ * A_UINT32 tx_succ_mcs[][][]; Array length is num_peer_ac_tx_stats * WLAN_MAX_AC * tx_succ_mcs_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * tx_succ_mcs_array_len + MCS index
+ * Contains a count of how many tx PPDUs have been acked for each MCS of each AC of each peer.
+ * A_UINT32 tx_fail_mcs[][][]; Array length is num_peer_ac_tx_stats * WLAN_MAX_AC * tx_fail_mcs_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * tx_fail_mcs_array_len + MCS index
+ * Contains a count of how many PPDUs failed tx due to no ack for each MCS of each AC of each peer.
+ * A_UINT32 tx_ppdu_delay[][][]; Array length is num_peer_ac_tx_stats * WLAN_MAX_AC * tx_ppdu_delay_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * tx_ppdu_delay_array_len + delay index
+ * Contains a histogram of how many PPDUs encountered each level of delay due to retries or air interface contention.
+ * The time represented by each array element (i.e. histogram bin) is specified by tx_ppdu_delay_bin_size_ms.
+ * Element 0 contains the count of PPDUs delayed by less than tx_ppdu_delay_bin_size_ms.
+ * Element 1 contains the count of PPDUs delayed by more than 1x tx_ppdu_delay_bin_size_ms but less than 2x.
+ * Element tx_delay_array_len-1 contains the count of PPDUs delayed by
+ * >= tx_ppdu_delay_bin_size_ms * (tx_ppdu_delay_array_len-1)
+ * wmi_peer_ac_rx_stats peer_ac_rx_stats[]; Array length is specified by num_peer_ac_rx_stats
+ * wmi_rx_stats rx_stats[][]; Array length is num_peer_ac_rx_stats * WLAN_MAX_AC, array index is (peer_index * WLAN_MAX_AC + ac_index)
+ * A_UINT32 rx_mpdu_aggr[][][]; Array length is num_peer_ac_rx_stats * WLAN_MAX_AC * rx_mpdu_aggr_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * rx_mpdu_aggr_array_len + A-MPDU size index
+ * Contains a histogram of how many A-MPDUs of a given size (i.e. number of MPDUs) have been received.
+ * Element 0 contains the number of PPDUs with a single MPDU.
+ * Element 1 contains the number of PPDUs with 2 MPDUs.
+ * Element 2 contains the number of PPDUs with 3 MPDUs.
+ * Element rx_mpdu_aggr_array_len-1 contains the number of PPDUs with >= rx_mpdu_aggr_array_len MPDUs.
+ * A_UINT32 rx_mcs[][][]; Array length is (num_peer_ac_rx_stats * WLAN_MAX_AC) * rx_mcs_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * rx_mcs_array_len + MCS index
+ * Contains a count of rx PPDUs for each MCS of each AC of each peer.
+ * For example, if there were 2 peers (X and Y) whose stats were being reported,
+ * the message and its TLV arrays would look like this:
+ * 1. fixed_param
+ * 2. chan_cca_stats[0]
+ * 3. peer_signal_stats[0] for peer X
+ * 4. peer_signal_stats[1] for peer Y
+ * 5. peer_ac_tx_stats[0] for X
+ * 6. peer_ac_tx_stats[1] for Y
+ * 7. tx_stats[0][0] for peer X, AC 0
+ * 8. tx_stats[0][1] for peer X, AC 1
+ * 9. tx_stats[0][2] for peer X, AC 2
+ * 10. tx_stats[0][3] for peer X, AC 3
+ * 11. tx_stats[1][0] for peer Y, AC 0
+ * 12 tx_stats[1][1] for peer Y, AC 1
+ * 13. tx_stats[1][2] for peer Y, AC 2
+ * 14. tx_stats[1][3] for peer Y, AC 3
+ * 15. tx_mpdu_aggr[0][0][] for peer X, AC 0
+ * 16. tx_mpdu_aggr[0][1][] for peer X, AC 1
+ * 17. tx_mpdu_aggr[0][2][] for peer X, AC 2
+ * 18. tx_mpdu_aggr[0][3][] for peer X, AC 3
+ * 19. tx_mpdu_aggr[1][0][] for peer Y, AC 0
+ * 20. tx_mpdu_aggr[1][1][] for peer Y, AC 1
+ * 21. tx_mpdu_aggr[1][2][] for peer Y, AC 2
+ * 22. tx_mpdu_aggr[1][3][] for peer Y, AC 3
+ * 23. tx_succ_mcs[0][0][] for peer X, AC 0
+ * 24. tx_succ_mcs[0][1][] for peer X, AC 1
+ * 25. tx_succ_mcs[0][2][] for peer X, AC 2
+ * 26. tx_succ_mcs[0][3][] for peer X, AC 3
+ * 27. tx_succ_mcs[1][0][] for peer Y, AC 0
+ * 28. tx_succ_mcs[1][1][] for peer Y, AC 1
+ * 29. tx_succ_mcs[1][2][] for peer Y, AC 2
+ * 30. tx_succ_mcs[1][3][] for peer Y, AC 3
+ * 31. tx_fail_mcs[0][0][] for peer X, AC 0
+ * 32. tx_fail_mcs[0][1][] for peer X, AC 1
+ * 33. tx_fail_mcs[0][2][] for peer X, AC 2
+ * 34. tx_fail_mcs[0][3][] for peer X, AC 3
+ * 35. tx_fail_mcs[1][0][] for peer Y, AC 0
+ * 36. tx_fail_mcs[1][1][] for peer Y, AC 1
+ * 37. tx_fail_mcs[1][2][] for peer Y, AC 2
+ * 38. tx_fail_mcs[1][3][] for peer Y, AC 3
+ * 39. tx_ppdu_delay[0][0][] for peer X, AC 0
+ * 40. tx_ppdu_delay[0][1][] for peer X, AC 1
+ * 41. tx_ppdu_delay[0][2][] for peer X, AC 2
+ * 42. tx_ppdu_delay[0][3][] for peer X, AC 3
+ * 43. tx_ppdu_delay[1][0][] for peer Y, AC 0
+ * 44. tx_ppdu_delay[1][1][] for peer Y, AC 1
+ * 45. tx_ppdu_delay[1][2][] for peer Y, AC 2
+ * 46. tx_ppdu_delay[1][3][] for peer Y, AC 3
+ * 47. peer_ac_rx_stats[0] for X
+ * 48. peer_ac_rx_stats[1] for Y
+ * 49. rx_stats[0][0] for peer X, AC 0
+ * 50. rx_stats[0][1] for peer X, AC 1
+ * 51. rx_stats[0][2] for peer X, AC 2
+ * 52. rx_stats[0][3] for peer X, AC 3
+ * 53. rx_stats[1][0] for peer Y, AC 0
+ * 54. rx_stats[1][1] for peer Y, AC 1
+ * 55. rx_stats[1][2] for peer Y, AC 2
+ * 56. rx_stats[1][3] for peer Y, AC 3
+ * 57. rx_mpdu_aggr[0][0][] for peer X, AC 0
+ * 58. rx_mpdu_aggr[0][1][] for peer X, AC 1
+ * 59. rx_mpdu_aggr[0][2][] for peer X, AC 2
+ * 60. rx_mpdu_aggr[0][3][] for peer X, AC 3
+ * 61. rx_mpdu_aggr[1][0][] for peer Y, AC 0
+ * 62. rx_mpdu_aggr[1][1][] for peer Y, AC 1
+ * 63. rx_mpdu_aggr[1][2][] for peer Y, AC 2
+ * 64. rx_mpdu_aggr[1][3][] for peer Y, AC 3
+ * 65. rx_mcs[0][0][] for peer X, AC 0
+ * 66. rx_mcs[0][1][] for peer X, AC 1
+ * 67. rx_mcs[0][2][] for peer X, AC 2
+ * 68. rx_mcs[0][3][] for peer X, AC 3
+ * 69. rx_mcs[1][0][] for peer Y, AC 0
+ * 70. rx_mcs[1][1][] for peer Y, AC 1
+ * 71. rx_mcs[1][2][] for peer Y, AC 2
+ * 72. rx_mcs[1][3][] for peer Y, AC 3
+ **/
+#define WMITLV_TABLE_WMI_REPORT_STATS_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_STRUC_wmi_report_stats_event_fixed_param, wmi_report_stats_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_chan_cca_stats, chan_cca_stats, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_signal_stats, peer_signal_stats, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_ac_tx_stats, peer_ac_tx_stats, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_tx_stats, tx_stats, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, tx_mpdu_aggr, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, tx_succ_mcs, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, tx_fail_mcs, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, tx_ppdu_delay, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_ac_rx_stats, peer_ac_rx_stats, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_STRUC, wmi_rx_stats, rx_stats, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, rx_mpdu_aggr, WMITLV_SIZE_VAR) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_UINT32, A_UINT32, rx_mcs, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_REPORT_STATS_EVENTID);
+
+#define WMITLV_TABLE_WMI_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_vdev_encrypt_decrypt_data_resp_event_fixed_param, wmi_vdev_encrypt_decrypt_data_resp_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id,op,buf,len, WMITLV_TAG_ARRAY_BYTE, A_UINT8, enc80211_frame, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENTID);
+
+#define WMITLV_TABLE_WMI_PEER_STATS_INFO_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_peer_stats_info_event_fixed_param, wmi_peer_stats_info_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_STRUC, wmi_peer_stats_info, peer_stats_info, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_PEER_STATS_INFO_EVENTID);
+
+#define WMITLV_TABLE_WMI_RADIO_CHAN_STATS_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_radio_chan_stats_event_fixed_param,wmi_radio_chan_stats_event_fixed_param, fixed_param, WMITLV_SIZE_FIX) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_ARRAY_STRUC, wmi_radio_chan_stats, radio_chan_stats, WMITLV_SIZE_VAR)
+WMITLV_CREATE_PARAM_STRUC(WMI_RADIO_CHAN_STATS_EVENTID);
+
+#define WMITLV_TABLE_WMI_PKGID_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_pkgid_event_fixed_param, wmi_pkgid_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_PKGID_EVENTID);
+/* mac randomization event */
+#define WMITLV_TABLE_WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID(id, op, buf, len) \
+ WMITLV_ELEM(id, op, buf, len, WMITLV_TAG_STRUC_wmi_vdev_add_mac_addr_to_rx_filter_status_event_fixed_param, wmi_vdev_add_mac_addr_to_rx_filter_status_event_fixed_param, fixed_param, WMITLV_SIZE_FIX)
+WMITLV_CREATE_PARAM_STRUC(WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID);
#ifdef __cplusplus
}
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_unified.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_unified.h
index db86c4c..3629137 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_unified.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_unified.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -122,6 +122,8 @@
(((num_entries) / (32 / (bits_per_entry))) + \
(((num_entries) % (32 / (bits_per_entry))) ? 1 : 0))
+#define WMI_RETURN_STRING(str) case ((str)): return (uint8_t *)(# str);
+
static INLINE A_UINT32 wmi_packed_arr_get_bits(A_UINT32 *arr,
A_UINT32 entry_index, A_UINT32 bits_per_entry)
{
@@ -236,6 +238,9 @@
WMI_GRP_BPF_OFFLOAD, /* 0x36 Berkeley Packet Filter */
WMI_GRP_NAN_DATA, /* 0x37 */
WMI_GRP_PROTOTYPE, /* 0x38 */
+ WMI_GRP_MONITOR, /* 0x39 */
+ WMI_GRP_REGULATORY, /* 0x3a */
+ WMI_GRP_HW_DATA_FILTER, /* 0x3b */
} WMI_GRP_ID;
#define WMI_CMD_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1)
@@ -343,8 +348,18 @@
WMI_SET_PERIODIC_CHANNEL_STATS_CONFIG_CMDID,
/** WMI command for power debug framework */
WMI_PDEV_WAL_POWER_DEBUG_CMDID,
+ /** set per-AC rx reorder timeouts */
+ WMI_PDEV_SET_REORDER_TIMEOUT_VAL_CMDID,
+ /** WMI command for WOW gpio and type */
+ WMI_PDEV_SET_WAKEUP_CONFIG_CMDID,
+ /* Get current ANT's per chain's RSSI info */
+ WMI_PDEV_GET_ANTDIV_STATUS_CMDID,
+ /** WMI command for getting Chip Power Stats */
+ WMI_PDEV_GET_CHIP_POWER_STATS_CMDID,
+ /** set stats reporting thresholds - see WMI_REPORT_STATS_EVENTID */
+ WMI_PDEV_SET_STATS_THRESHOLD_CMDID,
- /* VDEV(virtual device) specific commands */
+ /* VDEV (virtual device) specific commands */
/** vdev create */
WMI_VDEV_CREATE_CMDID=WMI_CMD_GRP_START_ID(WMI_GRP_VDEV),
/** vdev delete */
@@ -392,6 +407,11 @@
/** To set custom aggregation size for per vdev */
WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID,
+ /* DISA feature: Encrypt-decrypt data request */
+ WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID,
+ /** Command to enable mac randomizaton **/
+ WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID,
+
/* peer specific commands */
/** create a peer */
@@ -436,6 +456,18 @@
WMI_PEER_ATF_REQUEST_CMDID,
/** bandwidth fairness (BWF) peer configuration request command */
WMI_PEER_BWF_REQUEST_CMDID,
+ /** rx reorder queue setup for peer/tid */
+ WMI_PEER_REORDER_QUEUE_SETUP_CMDID,
+ /** rx reorder queue remove for peer/tid */
+ WMI_PEER_REORDER_QUEUE_REMOVE_CMDID,
+ /** specify a limit for rx A-MPDU block size */
+ WMI_PEER_SET_RX_BLOCKSIZE_CMDID,
+ /**
+ * request peer antdiv info from FW. FW shall respond with
+ * PEER_ANTDIV_INFO_EVENTID
+ */
+ WMI_PEER_ANTDIV_INFO_REQ_CMDID,
+
/* beacon/management specific commands */
@@ -492,6 +524,14 @@
WMI_DFS_PHYERR_FILTER_ENA_CMDID,
/** enable DFS phyerr/parse filter offload */
WMI_DFS_PHYERR_FILTER_DIS_CMDID,
+ /** enable DFS phyerr processing offload */
+ WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID,
+ /** disable DFS phyerr processing offload */
+ WMI_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMDID,
+ /** set ADFS channel config */
+ WMI_VDEV_ADFS_CH_CFG_CMDID,
+ /** abort ADFS off-channel-availability-check currently in progress */
+ WMI_VDEV_ADFS_OCAC_ABORT_CMDID,
/* Roaming specific commands */
/** set roam scan mode */
@@ -521,7 +561,9 @@
/** configure thresholds for MAWC */
WMI_ROAM_CONFIGURE_MAWC_CMDID,
/** configure MultiBand Operation(refer WFA MBO spec) parameter */
- WMI_ROAM_SET_MBO_PARAM_CMDID,
+ WMI_ROAM_SET_MBO_PARAM_CMDID, /* DEPRECATED */
+ /** configure packet error rate threshold for triggering roaming */
+ WMI_ROAM_PER_CONFIG_CMDID,
/** offload scan specific commands */
/** set offload scan AP profile */
@@ -667,6 +709,18 @@
/** Cmd to configure the verbose level */
WMI_DIAG_EVENT_LOG_CONFIG_CMDID,
+ /** One time request for wlan stats */
+ WMI_REQUEST_WLAN_STATS_CMDID,
+
+ /** Request for getting RCPI of peer */
+ WMI_REQUEST_RCPI_CMDID,
+
+ /** One time request for peer stats info */
+ WMI_REQUEST_PEER_STATS_INFO_CMDID,
+
+ /** One time request for radio channel stats */
+ WMI_REQUEST_RADIO_CHAN_STATS_CMDID,
+
/** ARP OFFLOAD REQUEST*/
WMI_SET_ARP_NS_OFFLOAD_CMDID=WMI_CMD_GRP_START_ID(WMI_GRP_ARP_NS_OFL),
@@ -770,6 +824,10 @@
WMI_VDEV_WISA_CMDID,
/** set debug log time stamp sync up with host */
WMI_DBGLOG_TIME_STAMP_SYNC_CMDID,
+ /** Command for host to set/delete multiple mcast filters */
+ WMI_SET_MULTIPLE_MCAST_FILTER_CMDID,
+ /** upload a requested section of data from firmware flash to host */
+ WMI_READ_DATA_FROM_FLASH_CMDID,
/* GPIO Configuration */
WMI_GPIO_CONFIG_CMDID=WMI_CMD_GRP_START_ID(WMI_GRP_GPIO),
@@ -859,6 +917,8 @@
WMI_CHAN_AVOID_UPDATE_CMDID,
WMI_COEX_CONFIG_CMDID,
WMI_CHAN_AVOID_RPT_ALLOW_CMDID,
+ WMI_COEX_GET_ANTENNA_ISOLATION_CMDID,
+ WMI_SAR_LIMITS_CMDID,
/**
* OBSS scan offload enable/disable commands
@@ -938,6 +998,16 @@
WMI_BPF_GET_VDEV_STATS_CMDID,
WMI_BPF_SET_VDEV_INSTRUCTIONS_CMDID,
WMI_BPF_DEL_VDEV_INSTRUCTIONS_CMDID,
+ WMI_BPF_SET_VDEV_ACTIVE_MODE_CMDID,
+
+ /** WMI commands related to monitor mode. */
+ WMI_MNT_FILTER_CMDID = WMI_CMD_GRP_START_ID(WMI_GRP_MONITOR),
+
+ /** WMI commands related to regulatory offload */
+ WMI_SET_CURRENT_COUNTRY_CMDID = WMI_CMD_GRP_START_ID(WMI_GRP_REGULATORY),
+ WMI_11D_SCAN_START_CMDID,
+ WMI_11D_SCAN_STOP_CMDID,
+
/**
* Nan Data commands
* NDI - NAN Data Interface
@@ -948,6 +1018,10 @@
WMI_NDP_INITIATOR_REQ_CMDID,
WMI_NDP_RESPONDER_REQ_CMDID,
WMI_NDP_END_REQ_CMDID,
+
+ /** WMI commands related to HW data filtering **/
+ WMI_HW_DATA_FILTER_CMDID = WMI_CMD_GRP_START_ID(WMI_GRP_HW_DATA_FILTER),
+
} WMI_CMD_ID;
typedef enum {
@@ -956,6 +1030,13 @@
/** WMI is ready; after this event the wlan subsystem is initialized and can process commands. */
WMI_READY_EVENTID,
+ /**
+ * Specify what WMI services the target supports
+ * (for services beyond what fits in the WMI_SERVICE_READY_EVENT
+ * message's wmi_service_bitmap)
+ */
+ WMI_SERVICE_AVAILABLE_EVENTID,
+
/** Scan specific events */
WMI_SCAN_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_SCAN) ,
@@ -1008,6 +1089,10 @@
WMI_PDEV_SET_HW_MODE_RESP_EVENTID,
WMI_PDEV_HW_MODE_TRANSITION_EVENTID,
WMI_PDEV_SET_MAC_CONFIG_RESP_EVENTID,
+ /** Report ANT DIV feature's status */
+ WMI_PDEV_ANTDIV_STATUS_EVENTID,
+ /** Chip level Power stats */
+ WMI_PDEV_CHIP_POWER_STATS_EVENTID,
/* VDEV specific events */
/** VDEV started event in response to VDEV_START request */
@@ -1030,7 +1115,15 @@
/* FW response to Host for vdev delete cmdid */
WMI_VDEV_DELETE_RESP_EVENTID,
- /* peer specific events */
+ /**
+ * DISA feature: FW response to Host with encrypted/decrypted
+ * 802.11 DISA frame
+ */
+ WMI_VDEV_ENCRYPT_DECRYPT_DATA_RESP_EVENTID,
+ /** event to report mac randomization success **/
+ WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID,
+
+ /* peer specific events */
/** FW reauet to kick out the station for reasons like inactivity,lack of response ..etc */
WMI_PEER_STA_KICKOUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PEER),
@@ -1061,6 +1154,8 @@
WMI_PEER_RATECODE_LIST_EVENTID,
WMI_WDS_PEER_EVENTID,
WMI_PEER_STA_PS_STATECHG_EVENTID,
+ /** Peer Ant Div Info Event with rssi per chain, etc */
+ WMI_PEER_ANTDIV_INFO_EVENTID,
/* beacon/mgmt specific events */
/** RX management frame. the entire frame is carried along with the event. */
@@ -1081,6 +1176,12 @@
WMI_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID,
/** Event for Mgmt TX completion event */
WMI_MGMT_TX_COMPLETION_EVENTID,
+ /** Event for Mgmt TX bundle completion event */
+ WMI_MGMT_TX_BUNDLE_COMPLETION_EVENTID,
+ /** vdev_map used in WMI_TBTTOFFSET_UPDATE_EVENTID supports max 32 vdevs
+ * Use this event if number of vdevs > 32.
+ */
+ WMI_TBTTOFFSET_EXT_UPDATE_EVENTID,
/*ADDBA Related WMI Events*/
/** Indication the completion of the prior
@@ -1151,6 +1252,33 @@
/** FW update tx power levels event */
WMI_RADIO_TX_POWER_LEVEL_STATS_EVENTID,
+ /** This event is used to report wlan stats to host.
+ * It is triggered under 3 conditions:
+ * (a) Periodic timer timed out, based on the period specified
+ * by WMI_PDEV_PARAM_STATS_OBSERVATION_PERIOD
+ * (b) Whenever any of the (enabled) stats thresholds specified
+ * in the WMI_PDEV_SET_STATS_THRESHOLD_CMD message is exceeded
+ * within the current stats period.
+ * (c) In response to the one-time wlan stats request of
+ * WMI_REQUEST_WLAN_STATS_CMDID from host.
+ *
+ * If this event is triggered by condition a or b,
+ * the stats counters are cleared at the start of each period.
+ * But if it is triggered by condition c, stats counters won't be cleared.
+ */
+ WMI_REPORT_STATS_EVENTID,
+
+ /** Event indicating RCPI of the peer requested by host in the
+ * WMI_REQUEST_RCPI_CMDID */
+ WMI_UPDATE_RCPI_EVENTID,
+
+ /** This event is used to respond to WMI_REQUEST_PEER_STATS_INFO_CMDID
+ * and report peer stats info to host */
+ WMI_PEER_STATS_INFO_EVENTID,
+
+ /** This event is used to respond to WMI_REQUEST_RADIO_CHAN_STATS_CMDID
+ * and report radio channel stats to host */
+ WMI_RADIO_CHAN_STATS_EVENTID,
/* NLO specific events */
/** NLO match event after the first match */
@@ -1176,6 +1304,12 @@
/*chatter query reply event*/
WMI_CHATTER_PC_QUERY_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_CHATTER),
+ /** DFS related events */
+ WMI_PDEV_DFS_RADAR_DETECTION_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_DFS),
+ /** Indicate channel-availability-check completion event to host */
+ WMI_VDEV_DFS_CAC_COMPLETE_EVENTID,
+ /** Indicate off-channel-availability-check completion event to host */
+ WMI_VDEV_ADFS_OCAC_COMPLETE_EVENTID,
/** echo event in response to echo command */
WMI_ECHO_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_MISC),
@@ -1241,6 +1375,15 @@
/** event to report SCPC calibrated data to host */
WMI_PDEV_UTF_SCPC_EVENTID,
+ /** event to provide requested data from the target's flash memory */
+ WMI_READ_DATA_FROM_FLASH_EVENTID,
+
+ /** event to report rx aggregation failure frame information */
+ WMI_REPORT_RX_AGGR_FAILURE_EVENTID,
+
+ /** event to upload a PKGID to host to identify chip for various products */
+ WMI_PKGID_EVENTID,
+
/* GPIO Event */
WMI_GPIO_INPUT_EVENTID=WMI_EVT_GRP_START_ID(WMI_GRP_GPIO),
/** upload H_CV info WMI event
@@ -1280,6 +1423,10 @@
WMI_NAN_STARTED_CLUSTER_EVENTID,
WMI_NAN_JOINED_CLUSTER_EVENTID,
+ /* Coex Event */
+ WMI_COEX_REPORT_ANTENNA_ISOLATION_EVENTID =
+ WMI_EVT_GRP_START_ID(WMI_GRP_COEX),
+
/* LPI Event */
WMI_LPI_RESULT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_LPI),
WMI_LPI_STATUS_EVENTID,
@@ -1321,6 +1468,12 @@
WMI_BPF_CAPABILIY_INFO_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_BPF_OFFLOAD),
WMI_BPF_VDEV_STATS_INFO_EVENTID,
+ /** WMI events related to regulatory offload */
+ WMI_REG_CHAN_LIST_CC_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_REGULATORY),
+ WMI_11D_NEW_COUNTRY_EVENTID,
+
+ _place_holder_evt_1 = WMI_EVT_GRP_START_ID(WMI_GRP_RMC),
+
/** Events in Prototyping phase */
WMI_NDI_CAP_RSP_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_PROTOTYPE),
WMI_NDP_INITIATOR_RSP_EVENTID,
@@ -1558,8 +1711,8 @@
};
/** NOTE: This defs cannot be changed in the future without breaking WMI compatibility */
-#define WMI_MAX_NUM_SS 8
-#define WMI_MAX_NUM_RU 4
+#define WMI_MAX_NUM_SS MAX_HE_NSS
+#define WMI_MAX_NUM_RU MAX_HE_RU
/*
* Figure 8 554ae: -PPE Threshold Info field format
@@ -1582,27 +1735,30 @@
* Note that in these macros, "ru" is one-based, not zero-based, while
* nssm1 is zero-based.
*/
-#define WMI_SET_PPET16(ppet16_ppet8_ru3_ru0, ppet, ru, nssm1) \
+#define WMI_SET_PPET16(ppet16_ppet8_ru3_ru0, ru, nssm1, ppet) \
do { \
- ppet16_ppet8_ru3_ru0[nssm1] &= ~(7 << (((ru-1)%4)*6)); \
- ppet16_ppet8_ru3_ru0[nssm1] |= ((ppet&7) << (((ru-1)%4)*6)); \
+ ppet16_ppet8_ru3_ru0[nssm1] &= ~(7 << (((ru-1)&3)*6)); \
+ ppet16_ppet8_ru3_ru0[nssm1] |= ((ppet&7) << (((ru-1)&3)*6)); \
} while (0)
#define WMI_GET_PPET16(ppet16_ppet8_ru3_ru0, ru, nssm1) \
- ((ppet16_ppet8_ru3_ru0[nssm1] >> (((ru-1)%4)*6))&7)
+ ((ppet16_ppet8_ru3_ru0[nssm1] >> (((ru-1)&3)*6))&7)
-#define WMI_SET_PPET8(ppet16_ppet8_ru3_ru0, ppet, ru, nssm1) \
+#define WMI_SET_PPET8(ppet16_ppet8_ru3_ru0, ru, nssm1, ppet) \
do { \
- ppet16_ppet8_ru3_ru0[nssm1] &= ~(7 << (((ru-1)%4)*6+3)); \
- ppet16_ppet8_ru3_ru0[nssm1] |= ((ppet&7) << (((ru-1)%4)*6+3)); \
+ ppet16_ppet8_ru3_ru0[nssm1] &= ~(7 << (((ru-1)&3)*6+3)); \
+ ppet16_ppet8_ru3_ru0[nssm1] |= ((ppet&7) << (((ru-1)&3)*6+3)); \
} while (0)
#define WMI_GET_PPET8(ppet16_ppet8_ru3_ru0, ru, nssm1) \
- ((ppet16_ppet8_ru3_ru0[nssm1] >> (((ru-1)%4)*6+3))&7)
+ ((ppet16_ppet8_ru3_ru0[nssm1] >> (((ru-1)&3)*6+3))&7)
typedef struct _wmi_ppe_threshold {
A_UINT32 numss_m1; /** NSS - 1*/
- A_UINT32 ru_count; /** Max RU count */
+ union {
+ A_UINT32 ru_count; /** RU COUNT OBSOLETE to be removed after few versions */
+ A_UINT32 ru_mask; /** RU index mask */
+ };
A_UINT32 ppet16_ppet8_ru3_ru0[WMI_MAX_NUM_SS]; /** ppet8 and ppet16 for max num ss */
} wmi_ppe_threshold;
@@ -1821,6 +1977,25 @@
*/
} wmi_service_ready_event_fixed_param;
+#define WMI_SERVICE_SEGMENT_BM_SIZE32 4 /* 4x A_UINT32 = 128 bits */
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_service_available_event_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /**
+ * The wmi_service_segment offset field specifies the position within
+ * the logical bitmap of WMI service flags at which the WMI service
+ * flags specified within this message begin.
+ * Since the first 128 WMI service flags are specified within the
+ * wmi_service_bitmap field of the WMI_SERVICE_READY_EVENT message,
+ * the wmi_service_segment_offset value is expected to be 128 or more.
+ */
+ A_UINT32 wmi_service_segment_offset;
+ A_UINT32 wmi_service_segment_bitmap[WMI_SERVICE_SEGMENT_BM_SIZE32];
+} wmi_service_available_event_fixed_param;
+
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_WMI_SERVICE_EXT_READY_EVENT */
/* which WMI_DBS_CONC_SCAN_CFG setting the FW is initialized with */
@@ -1843,6 +2018,12 @@
* Value 0 means FW hasn't given any limit to host.
*/
A_UINT32 max_bssid_rx_filters;
+ /*
+ * Extended FW build version information:
+ * bits 27:0 -> reserved
+ * bits 31:28 -> CRM sub ID
+ */
+ A_UINT32 fw_build_vers_ext;
} wmi_service_ready_ext_event_fixed_param;
typedef enum {
@@ -2236,6 +2417,13 @@
#define WMI_RSRC_CFG_FLAG_QWRAP_MODE_ENABLE_S 8
#define WMI_RSRC_CFG_FLAG_QWRAP_MODE_ENABLE_M 0x100
+ #define WMI_RSRC_CFG_FLAG_MGMT_COMP_EVT_BUNDLE_SUPPORT_S 9
+ #define WMI_RSRC_CFG_FLAG_MGMT_COMP_EVT_BUNDLE_SUPPORT_M 0x200
+
+ #define WMI_RSRC_CFG_FLAG_TX_MSDU_ID_NEW_PARTITION_SUPPORT_S 10
+ #define WMI_RSRC_CFG_FLAG_TX_MSDU_ID_NEW_PARTITION_SUPPORT_M 0x400
+
+
A_UINT32 flag1;
/** @brief smart_ant_cap - Smart Antenna capabilities information
@@ -2340,6 +2528,16 @@
#define WMI_RSRC_CFG_FLAG_QWRAP_MODE_ENABLE_GET(word32) \
WMI_RSRC_CFG_FLAG_GET((word32), QWRAP_MODE_ENABLE)
+#define WMI_RSRC_CFG_FLAG_MGMT_COMP_EVT_BUNDLE_SUPPORT_SET(word32, value) \
+ WMI_RSRC_CFG_FLAG_SET((word32), MGMT_COMP_EVT_BUNDLE_SUPPORT, (value))
+#define WMI_RSRC_CFG_FLAG_MGMT_COMP_EVT_BUNDLE_SUPPORT_GET(word32) \
+ WMI_RSRC_CFG_FLAG_GET((word32), MGMT_COMP_EVT_BUNDLE_SUPPORT)
+
+#define WMI_RSRC_CFG_FLAG_TX_MSDU_ID_NEW_PARTITION_SUPPORT_SET(word32, value) \
+ WMI_RSRC_CFG_FLAG_SET((word32), TX_MSDU_ID_NEW_PARTITION_SUPPORT, (value))
+#define WMI_RSRC_CFG_FLAG_TX_MSDU_ID_NEW_PARTITION_SUPPORT_GET(word32) \
+ WMI_RSRC_CFG_FLAG_GET((word32), TX_MSDU_ID_NEW_PARTITION_SUPPORT)
+
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_init_cmd_fixed_param */
@@ -2350,9 +2548,17 @@
wmi_abi_version host_abi_vers;
A_UINT32 num_host_mem_chunks; /** size of array host_mem_chunks[] */
- /* The TLVs for resource_config and host_mem_chunks[] will follow.
+
+ /* The TLVs for resource_config, host_mem_chunks[], and hw_mode_config
+ * will follow.
* wmi_resource_config resource_config;
* wlan_host_memory_chunk host_mem_chunks[];
+ * wmi_pdev_set_hw_mode_cmd_fixed_param hw_mode_config;
+ * Note that the hw_mode_config, in spite of its "pdev" name,
+ * applies to the entire target rather than for a single pdev
+ * within the target.
+ * To avoid specifying a HW mode for the target, the host should
+ * fill hw_mode_config's fields with 0x0.
*/
} wmi_init_cmd_fixed_param;
@@ -2393,6 +2599,23 @@
A_UINT32 ie_data[1];
} wmi_ie_data;
+/**
+ * TLV used for length/buffer
+ */
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_tlv_buf_len_param
+ */
+ A_UINT32 tlv_header;
+ A_UINT32 buf_len; /** Length of buf */
+ /**
+ * Following this structure is the TLV byte stream of buf
+ * of length buf_len:
+ * A_UINT8 buf[];
+ *
+ */
+} wmi_tlv_buf_len_param;
typedef struct {
/** Len of the SSID */
@@ -2418,6 +2641,9 @@
#define WLAN_SCAN_PARAMS_MAX_BSSID 4
#define WLAN_SCAN_PARAMS_MAX_IE_LEN 512
+/* NOTE: This constant cannot be changed without breaking WMI compatibility */
+#define WMI_IE_BITMAP_SIZE 8
+
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_start_scan_cmd_fixed_param */
/** Scan ID */
@@ -2472,7 +2698,14 @@
A_UINT32 ie_len;
/** Max number of probes to be sent */
A_UINT32 n_probes;
-
+ /** MAC Address to use in Probe Req as SA **/
+ wmi_mac_addr mac_addr;
+ /** Mask on which MAC has to be randomized **/
+ wmi_mac_addr mac_mask;
+ /** ie bitmap to use in probe req **/
+ A_UINT32 ie_bitmap[WMI_IE_BITMAP_SIZE];
+ /** Number of vendor OUIs. In the TLV vendor_oui[] **/
+ A_UINT32 num_vendor_oui;
/**
* TLV (tag length value ) parameters follow the scan_cmd
@@ -2481,6 +2714,7 @@
* wmi_ssid ssid_list[];
* wmi_mac_addr bssid_list[];
* A_UINT8 ie_data[];
+ * wmi_vendor_oui vendor_oui[];
*/
} wmi_start_scan_cmd_fixed_param;
@@ -2526,6 +2760,12 @@
/** always do passive scan on passive channels */
#define WMI_SCAN_FLAG_STRICT_PASSIVE_ON_PCHN 0x10000
+/** set HALF (10MHz) rate support */
+#define WMI_SCAN_FLAG_HALF_RATE_SUPPORT 0x20000
+/** set Quarter (5MHz) rate support */
+#define WMI_SCAN_FLAG_QUARTER_RATE_SUPPORT 0x40000
+#define WMI_SCAN_RANDOM_SEQ_NO_IN_PROBE_REQ 0x80000
+#define WMI_SCAN_ENABLE_IE_WHITELIST_IN_PROBE_REQ 0x100000
/** for adaptive scan mode using 3 bits (21 - 23 bits) */
#define WMI_SCAN_DWELL_MODE_MASK 0x00E00000
@@ -2572,7 +2812,8 @@
* Req Type
* req_type should be WMI_SCAN_STOP_ONE, WMI_SCN_STOP_VAP_ALL or WMI_SCAN_STOP_ALL
* WMI_SCAN_STOP_ONE indicates to stop a specific scan with scan_id
- * WMI_SCN_STOP_VAP_ALL indicates to stop all scan requests on a specific vDev with vdev_id
+ * WMI_SCN_STOP_VAP_ALL indicates to stop all scan requests on a specific
+ * vDev with vdev_id and pdev with pdev_id
* WMI_SCAN_STOP_ALL indicates to stop all scan requests in both Scheduler's queue and Scan Engine
*/
A_UINT32 req_type;
@@ -2581,6 +2822,11 @@
* used when req_type equals to WMI_SCN_STOP_VAP_ALL, it indexed the vDev on which to stop the scan
*/
A_UINT32 vdev_id;
+ /** pdev_id for identifying the MAC
+ * See macros starting with WMI_PDEV_ID_ for values.
+ * In non-DBDC case host should set it to 0
+ */
+ A_UINT32 pdev_id;
} wmi_stop_scan_cmd_fixed_param;
#define MAX_NUM_CHAN_PER_WMI_CMD 58 // each WMI cmd can hold 58 channel entries at most
@@ -2590,6 +2836,7 @@
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_scan_chan_list_cmd_fixed_param */
A_UINT32 num_scan_chans; /** no of elements in chan_info[] */
A_UINT32 flags; /* Flags used to control the behavior of channel list update on target side */
+ A_UINT32 pdev_id; /* pdev_id for identifying the MAC. See macros starting with WMI_PDEV_ID_ for values */
/** Followed by the variable length TLV chan_info:
* wmi_channel chan_info[] */
} wmi_scan_chan_list_cmd_fixed_param;
@@ -2629,6 +2876,12 @@
* for WLAN_M_STA type, there are 3 entries in the table (refer to default_scan_priority_mapping_table definition)
*/
A_UINT32 number_rows;
+ /**
+ * pdev_id for identifying the MAC. See macros starting with
+ * WMI_PDEV_ID_ for values.In non-DBDC case host should
+ * set it to 0.
+ */
+ A_UINT32 pdev_id;
/** mapping_table for a specific vdev follows this TLV
* WLAN_PRIORITY_MAPPING mapping_table[]; */
}wmi_scan_sch_priority_table_cmd_fixed_param;
@@ -2652,8 +2905,22 @@
A_UINT32 min_rest_time;
/** min rest time. Only valid if WMI_SCAN_UPDATE_MAX_REST_TIME flag is set in scan_update_flag */
A_UINT32 max_rest_time;
+ /**
+ * pdev_id for identifying the MAC. See macros starting with
+ * WMI_PDEV_ID_ for values. In non-DBDC case host should set it to 0
+ */
+ A_UINT32 pdev_id;
} wmi_scan_update_request_cmd_fixed_param;
+#define WMI_SCAN_PROBE_OUI_SPOOFED_MAC_IN_PROBE_REQ 0x1
+#define WMI_SCAN_PROBE_OUI_RANDOM_SEQ_NO_IN_PROBE_REQ 0x2
+#define WMI_SCAN_PROBE_OUI_ENABLE_IE_WHITELIST_IN_PROBE_REQ 0x4
+
+typedef struct _wmi_vendor_oui {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_vendor_oui */
+ A_UINT32 oui_type_subtype; /** Vendor OUI type and subtype, lower 3 bytes is type and highest byte is subtype**/
+}wmi_vendor_oui;
+
typedef struct {
A_UINT32 tlv_header;
/** oui to be used in probe request frame when random mac addresss is
@@ -2661,6 +2928,20 @@
* host initated scans. host can request for random mac address with
* WMI_SCAN_ADD_SPOOFED_MAC_IN_PROBE_REQ flag. */
A_UINT32 prob_req_oui;
+ A_UINT32 vdev_id;
+ /** Control Flags **/
+ A_UINT32 flags;
+ /** ie bitmap to use in probe req **/
+ A_UINT32 ie_bitmap[WMI_IE_BITMAP_SIZE];
+ /** Number of vendor OUIs. In the TLV vendor_oui[] **/
+ A_UINT32 num_vendor_oui;
+ /**
+ * pdev_id for identifying the MAC. See macros starting with
+ * WMI_PDEV_ID_ for values. In non-DBDC case host should set it to 0
+ */
+ A_UINT32 pdev_id;
+ /* Following this tlv, there comes an array of structure of type wmi_vendor_ouiwmi_vendor_oui vendor_oui[];*/
+
} wmi_scan_prob_req_oui_cmd_fixed_param;
enum wmi_scan_event_type {
@@ -3253,11 +3534,40 @@
A_UINT32 enable_cmd;
} wmi_vdev_spectral_enable_cmd_fixed_param;
+/* information sub element id for QSBW, expected value is 0x02 */
+#define WMI_CSA_EVENT_QSBW_ISE_ID_MASK 0x000000FF
+/* length of QSBW ISE data, expected value is 0x02 */
+#define WMI_CSA_EVENT_QSBW_ISE_LEN_MASK 0x0000FF00
+/* capabilities, 0x01 for 5MHz, 0x02 for 10MHz, 0x01|0x2 for both
+ * (see WMI_CSA_EVENT_QSBW_ISE bitmask defs)
+ */
+#define WMI_CSA_EVENT_QSBW_ISE_CAP_MASK 0x00FF0000
+/* notification from AP, 0x01 for 5MHz, 0x02 for 10MHz
+ * (see WMI_CSA_EVENT_QSBW_ISE bitmask defs)
+ */
+#define WMI_CSA_EVENT_QSBW_ISE_NOTIF_MASK 0xFF000000
+
+#define WMI_CSA_EVENT_QSBW_ISE_ID 0x02
+#define WMI_CSA_EVENT_QSBW_ISE_LEN 0x02
+
+#define WMI_CSA_EVENT_QSBW_ISE_5M_BITMASK 0x01
+#define WMI_CSA_EVENT_QSBW_ISE_10M_BITMASK 0x02
+
+#define WMI_CSA_EVENT_QSBW_ISE_CAP_5M(qsbw_ise) \
+ (((qsbw_ise) >> 16) & WMI_CSA_EVENT_QSBW_ISE_5M_BITMASK)
+#define WMI_CSA_EVENT_QSBW_ISE_CAP_10M(qsbw_ise) \
+ (((qsbw_ise) >> 16) & WMI_CSA_EVENT_QSBW_ISE_10M_BITMASK)
+#define WMI_CSA_EVENT_QSBW_ISE_NOTIF_5M(qsbw_ise) \
+ (((qsbw_ise) >> 24) & WMI_CSA_EVENT_QSBW_ISE_5M_BITMASK)
+#define WMI_CSA_EVENT_QSBW_ISE_NOTIF_10M(qsbw_ise) \
+ (((qsbw_ise) >> 24) & WMI_CSA_EVENT_QSBW_ISE_10M_BITMASK)
+
typedef enum {
WMI_CSA_IE_PRESENT = 0x00000001,
WMI_XCSA_IE_PRESENT = 0x00000002,
WMI_WBW_IE_PRESENT = 0x00000004,
WMI_CSWARP_IE_PRESENT = 0x00000008,
+WMI_QSBW_ISE_PRESENT = 0x00000010,
}WMI_CSA_EVENT_IES_PRESENT_FLAG;
/* wmi CSA receive event from beacon frame */
@@ -3275,6 +3585,7 @@
A_UINT32 wb_ie[2];
A_UINT32 cswarp_ie;
A_UINT32 ies_present_flag; //WMI_CSA_EVENT_IES_PRESENT_FLAG
+ A_UINT32 qsbw_ise;
}wmi_csa_event_fixed_param;
typedef enum {
@@ -3548,6 +3859,88 @@
WMI_PDEV_PARAM_CUST_TXPOWER_SCALE,
/** ATF enabe/disabe dynamically */
WMI_PDEV_PARAM_ATF_DYNAMIC_ENABLE,
+ /** Set tx retry limit for control frames. 0 = disable, 31 = max */
+ WMI_PDEV_PARAM_CTRL_RETRY_LIMIT,
+ /** Set propagation delay for 2G / 5G band.
+ * The propagation delay is fundamentally a per-peer property, but
+ * the target may not support per-peer settings for ack timeouts.
+ * This pdev parameter allows the MAC-level ack timeout to be set to
+ * a value suitable for the worst-case propagation delay of any peer
+ * within that pdev.
+ * Units are microseconds.
+ */
+ WMI_PDEV_PARAM_PROPAGATION_DELAY,
+ /**
+ * Host can enable/disable ANT DIV feature
+ * if it's been enabled in BDF
+ */
+ WMI_PDEV_PARAM_ENA_ANT_DIV,
+ /** Host can force one chain to select a specific ANT */
+ WMI_PDEV_PARAM_FORCE_CHAIN_ANT,
+ /**
+ * Start a cycle ANT self test periodically.
+ * In the test, the FW would select each ANT pair
+ * one by one, the cycle time could be configured
+ * via WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL
+ */
+ WMI_PDEV_PARAM_ANT_DIV_SELFTEST,
+ /**
+ * Configure the cycle time of ANT self test,
+ * the unit is micro second. Per the timer
+ * limitation, too small value could be not so
+ * accurate.
+ */
+ WMI_PDEV_PARAM_ANT_DIV_SELFTEST_INTVL,
+
+ /**
+ * wlan stats observation period, the unit is millisecond.
+ * The value of 0 is used to turn off periodic stats report.
+ */
+ WMI_PDEV_PARAM_STATS_OBSERVATION_PERIOD,
+ /**
+ * Set tx_ppdu_delay[] bin size to specify how many
+ * milliseconds each bin of the wmi_tx_stats.tx_ppdu_delay[]
+ * histogram represents.
+ */
+ WMI_PDEV_PARAM_TX_PPDU_DELAY_BIN_SIZE_MS,
+ /** set wmi_tx_stats.tx_ppdu_delay[] array length */
+ WMI_PDEV_PARAM_TX_PPDU_DELAY_ARRAY_LEN,
+ /** set wmi_tx_stats.tx_mpdu_aggr[] array length */
+ WMI_PDEV_PARAM_TX_MPDU_AGGR_ARRAY_LEN,
+ /** set wmi_rx_stats.rx_mpdu_aggr[] array length */
+ WMI_PDEV_PARAM_RX_MPDU_AGGR_ARRAY_LEN,
+ /** Set TX delay value in TX sch module, unit is microseconds */
+ WMI_PDEV_PARAM_TX_SCH_DELAY,
+ /** Set RTS enable for SIFS bursting */
+ WMI_PDEV_PARAM_ENABLE_RTS_SIFS_BURSTING,
+ /** Set Maximum number of MPDUs in an AMPDU*/
+ WMI_PDEV_PARAM_MAX_MPDUS_IN_AMPDU,
+ /** Enable/disable peer stats info mechanism
+ * A zero value disables; a non-zero value enables.
+ */
+ WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE,
+
+ /** Configure Fast PWR Transition mode
+ * 0x0 -> inidcates Fast PWR transition disabled
+ * 0x1 -> indicates Static mode enabled
+ * 0x2 -> indicates Dynamic mode enabled
+ */
+ WMI_PDEV_PARAM_FAST_PWR_TRANSITION,
+
+ /** Enable/disable radio channel stats mechanism
+ * A zero value disables; a non-zero value enables.
+ */
+ WMI_PDEV_PARAM_RADIO_CHAN_STATS_ENABLE,
+ /** Enable/disable radio diagnosis feature
+ * which allows retrieving the status of radio.
+ * A zero value disables; a non-zero value enables.
+ */
+ WMI_PDEV_PARAM_RADIO_DIAGNOSIS_ENABLE,
+ /** Enable/Disable mesh mcast traffic
+ * 1 - Allow mesh mcast traffic
+ * 0 - Disallow mesh mcast traffic
+ */
+ WMI_PDEV_PARAM_MESH_MCAST_ENABLE,
} WMI_PDEV_PARAM;
@@ -3715,6 +4108,15 @@
A_UINT32 status; /* WMI_MGMT_TX_COMP_STATUS_TYPE */
} wmi_mgmt_tx_compl_event_fixed_param;
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 num_reports;
+ /* tlv for completion
+ * A_UINT32 desc_ids[num_reports]; <- from tx_send_cmd
+ * A_UINT32 status[num_reports]; <- WMI_MGMT_TX_COMP_STATUS_TYPE
+ */
+} wmi_mgmt_tx_compl_bundle_event_fixed_param;
+
#define WMI_TPC_RATE_MAX 160
/* WMI_TPC_TX_NUM_CHAIN macro can't be changed without breaking the WMI compatibility */
#define WMI_TPC_TX_NUM_CHAIN 4
@@ -3817,6 +4219,7 @@
WMI_PKTLOG_EVENT_RCU = 0x8, /* Rate Control Update */
/* 0x10 used by deprecated DBG_PRINT */
WMI_PKTLOG_EVENT_SMART_ANTENNA = 0x20, /* To support Smart Antenna */
+ WMI_PKTLOG_EVENT_SW = 0x40, /* To support SW defined events */
} WMI_PKTLOG_EVENT;
typedef enum {
@@ -3914,6 +4317,32 @@
A_UINT32 enable_override;
} wmi_vdev_set_dscp_tid_map_cmd_fixed_param;
+enum WMI_WAKE_GPIO_TYPE {
+ WMI_WAKE_GPIO_LOW = 1,
+ WMI_WAKE_GPIO_HIGH = 2,
+ WMI_WAKE_GPIO_RISING_EDGE = 3,
+ WMI_WAKE_GPIO_FALLING_EDGE = 4,
+};
+
+/**
+ * Set GPIO numbers used to wakeup host and wakeup target.
+ */
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_WMI_PDEV_SET_WAKEUP_CONFIG_CMDID_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /* gpio num used to wakeup host, 0xff disable wakeup gpio */
+ A_UINT32 host_wakeup_gpio;
+ /* refer to WMI_WAKE_GPIO_TYPE */
+ A_UINT32 host_wakeup_type;
+ /* gpio num used to wakeup target, 0xff disable wakeup gpio */
+ A_UINT32 target_wakeup_gpio;
+ /* refer to WMI_WAKE_GPIO_TYPE */
+ A_UINT32 target_wakeup_type;
+} WMI_PDEV_SET_WAKEUP_CONFIG_CMDID_fixed_param;
+
/** Fixed rate (rate-code) for broadcast/ multicast data frames */
/* @brief bcast_mcast_data_rate - set the rates for the bcast/ mcast frames
* @details
@@ -4275,6 +4704,8 @@
* per power level stats.
*/
A_UINT32 power_level_offset;
+ /* radio id for this tx time per power level statistics (if multiple radio supported) */
+ A_UINT32 radio_id;
/*
* This TLV will be followed by a TLV containing a variable-length array of
* A_UINT32 with tx time per power level data
@@ -4457,6 +4888,10 @@
time driver waits before shutting down the radio or switching the channel and after receiving an ACK for
a data frame with PM bit set) */
A_UINT32 rx_leak_window;
+ A_UINT32 tx_rts_succ_cnt;
+ A_UINT32 tx_rts_fail_cnt;
+ A_UINT32 tx_ppdu_succ_cnt;
+ A_UINT32 tx_ppdu_fail_cnt;
} wmi_iface_link_stats;
/** Interface statistics (once started) reset and start afresh after each connection */
@@ -4506,6 +4941,25 @@
} wmi_vdev_rate_ht_info;
typedef struct {
+ /**
+ * TLV tag and len, tag equals
+ * WMITLV_TAG_STRUC_wmi_rx_aggr_failure_event_fixed_param
+ */
+ A_UINT32 tlv_header;
+ A_UINT32 num_failure_info; /* How many holes on rx aggregation */
+} wmi_rx_aggr_failure_event_fixed_param;
+
+typedef struct {
+ /**
+ * TLV tag and len, tag equals
+ * WMITLV_wmi_rx_aggr_failure_info
+ */
+ A_UINT32 tlv_header;
+ A_UINT32 start_seq; /* start sequence number of the hole */
+ A_UINT32 end_seq; /* end sequence number of the hole */
+} wmi_rx_aggr_failure_info;
+
+typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_stats_event_fixed_param */
wmi_stats_id stats_id;
/** number of pdev stats event structures (wmi_pdev_stats) 0 or 1 */
@@ -4532,6 +4986,547 @@
*/
} wmi_stats_event_fixed_param;
+/* WLAN channel CCA stats bitmap */
+#define WLAN_STATS_IDLE_TIME_SHIFT 0
+#define WLAN_STATS_IDLE_TIME_TIME 0x00000001
+
+#define WLAN_STATS_TX_TIME_SHIFT 1
+#define WLAN_STATS_TX_TIME_MASK 0x00000002
+
+#define WLAN_STATS_RX_IN_BSS_TIME_SHIFT 2
+#define WLAN_STATS_RX_IN_BSS_TIME_MASK 0x00000004
+
+#define WLAN_STATS_RX_OUT_BSS_TIME_SHIFT 3
+#define WLAN_STATS_RX_OUT_BSS_TIME_MASK 0x00000008
+
+#define WLAN_STATS_RX_BUSY_TIME_SHIFT 4
+#define WLAN_STATS_RX_BUSY_TIME_MASK 0x00000010
+
+#define WLAN_STATS_RX_IN_BAD_COND_TIME_SHIFT 5
+#define WLAN_STATS_RX_IN_BAD_COND_TIME_MASK 0x00000020
+
+#define WLAN_STATS_TX_IN_BAD_COND_TIME_SHIFT 6
+#define WLAN_STATS_TX_IN_BAD_COND_TIME_MASK 0x00000040
+
+#define WLAN_STATS_WLAN_NOT_AVAIL_TIME_SHIFT 7
+#define WLAN_STATS_WLAN_NOT_AVAIL_TIME_MASK 0x00000080
+
+/* WLAN peer signal stats bitmap */
+#define WLAN_STATS_PER_CHAIN_SNR_SHIFT 0
+#define WLAN_STATS_PER_CHAIN_SNR_MASK 0x00000001
+
+#define WLAN_STATS_PER_CHAIN_NF_SHIFT 1
+#define WLAN_STATS_PER_CHAIN_NF_MASK 0x00000002
+
+/* WLAN TX stats bitmap */
+#define WLAN_STATS_TX_MSDU_CNT_SHIFT 0
+#define WLAN_STATS_TX_MSDU_CNT_MASK 0x00000001
+
+#define WLAN_STATS_TX_MPDU_CNT_SHIFT 1
+#define WLAN_STATS_TX_MPDU_CNT_MASK 0x00000002
+
+#define WLAN_STATS_TX_PPDU_CNT_SHIFT 2
+#define WLAN_STATS_TX_PPDU_CNT_MASK 0x00000004
+
+#define WLAN_STATS_TX_BYTES_SHIFT 3
+#define WLAN_STATS_TX_BYTES_MASK 0x00000008
+
+#define WLAN_STATS_TX_MSDU_DROP_CNT_SHIFT 4
+#define WLAN_STATS_TX_MSDU_DROP_CNT_MASK 0x00000010
+
+#define WLAN_STATS_TX_DROP_BYTES_SHIFT 5
+#define WLAN_STATS_TX_DROP_BYTES_MASK 0x00000020
+
+#define WLAN_STATS_TX_MPDU_RETRY_CNT_SHIFT 6
+#define WLAN_STATS_TX_MPDU_RETRY_CNT_MASK 0x00000040
+
+#define WLAN_STATS_TX_MPDU_FAIL_CNT_SHIFT 7
+#define WLAN_STATS_TX_MPDU_FAIL_CNT_MASK 0x00000080
+
+#define WLAN_STATS_TX_PPDU_FAIL_CNT_SHIFT 8
+#define WLAN_STATS_TX_PPDU_FAIL_CNT_MASK 0x00000100
+
+#define WLAN_STATS_TX_MPDU_AGGR_SHIFT 9
+#define WLAN_STATS_TX_MPDU_AGGR_MASK 0x00000200
+
+#define WLAN_STATS_TX_SUCC_MCS_SHIFT 10
+#define WLAN_STATS_TX_SUCC_MCS_MASK 0x00000400
+
+#define WLAN_STATS_TX_FAIL_MCS_SHIFT 11
+#define WLAN_STATS_TX_FAIL_MCS_MASK 0x00000800
+
+#define WLAN_STATS_TX_PPDU_DELAY_SHIFT 12
+#define WLAN_STATS_TX_PPDU_DELAY_MASK 0x00001000
+
+/* WLAN RX stats bitmap */
+#define WLAN_STATS_MAC_RX_MPDU_CNT_SHIFT 0
+#define WLAN_STATS_MAC_RX_MPDU_CNT_MASK 0x00000001
+
+#define WLAN_STATS_MAC_RX_BYTES_SHIFT 1
+#define WLAN_STATS_MAC_RX_BYTES_MASK 0x00000002
+
+#define WLAN_STATS_PHY_RX_PPDU_CNT_SHIFT 2
+#define WLAN_STATS_PHY_RX_PPDU_CNT_MASK 0x00000004
+
+#define WLAN_STATS_PHY_RX_BYTES_SHIFT 3
+#define WLAN_STATS_PHY_RX_BYTES_MASK 0x00000008
+
+#define WLAN_STATS_RX_DISORDER_CNT_SHIFT 4
+#define WLAN_STATS_RX_DISORDER_CNT_MASK 0x00000010
+
+#define WLAN_STATS_RX_RETRY_CNT_SHIFT 5
+#define WLAN_STATS_RX_RETRY_CNT_MASK 0x00000020
+
+#define WLAN_STATS_RX_DUP_CNT_SHIFT 6
+#define WLAN_STATS_RX_DUP_CNT_MASK 0x00000040
+
+#define WLAN_STATS_RX_DISCARD_CNT_SHIFT 7
+#define WLAN_STATS_RX_DISCARD_CNT_MASK 0x00000080
+
+#define WLAN_STATS_RX_MPDU_AGGR_SHIFT 8
+#define WLAN_STATS_RX_MPDU_AGGR_MASK 0x00000100
+
+#define WLAN_STATS_RX_MCS_SHIFT 9
+#define WLAN_STATS_RX_MCS_MASK 0x00000200
+
+#define WLAN_STATS_STA_PS_INDS_SHIFT 10
+#define WLAN_STATS_STA_PS_INDS_MASK 0x00000400
+
+#define WLAN_STATS_STA_PS_DURS_SHIFT 11
+#define WLAN_STATS_STA_PS_DURS_MASK 0x00000800
+
+#define WLAN_STATS_RX_PROBE_REQS_SHIFT 12
+#define WLAN_STATS_RX_PROBE_REQS_MASK 0x00001000
+
+#define WLAN_STATS_RX_OTH_MGMTS_SHIFT 13
+#define WLAN_STATS_RX_OTH_MGMTS_MASK 0x00002000
+
+typedef struct
+{
+ A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_chan_cca_stats */
+ A_UINT32 vdev_id;
+ /** Percentage of idle time, no TX, no RX, no interference */
+ A_UINT32 idle_time;
+ /** Percentage of time transmitting packets */
+ A_UINT32 tx_time;
+ /** Percentage of time receiving packets in current BSSs */
+ A_UINT32 rx_in_bss_time;
+ /** Percentage of time receiving packets not in current BSSs */
+ A_UINT32 rx_out_bss_time;
+ /** Percentage of time interference detected. */
+ A_UINT32 rx_busy_time;
+ /** Percentage of time receiving packets with errors
+ * or packets flagged as retransmission or seqnum discontinued. */
+ A_UINT32 rx_in_bad_cond_time;
+ /** Percentage of time the device transmitted packets that haven't been ACKed. */
+ A_UINT32 tx_in_bad_cond_time;
+ /** Percentage of time the chip is unable to work in normal conditions. */
+ A_UINT32 wlan_not_avail_time;
+} wmi_chan_cca_stats;
+
+/** Thresholds of cca stats, stands for percentages of stats variation.
+ * Check wmi_chan_cca_stats for each stats's meaning.
+ */
+typedef struct
+{
+ A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_chan_cca_stats_thresh */
+ A_UINT32 idle_time; /* units = percent */
+ A_UINT32 tx_time; /* units = percent */
+ A_UINT32 rx_in_bss_time; /* units = percent */
+ A_UINT32 rx_out_bss_time; /* units = percent */
+ A_UINT32 rx_busy_time; /* units = percent */
+ A_UINT32 rx_in_bad_cond_time; /* units = percent */
+ A_UINT32 tx_in_bad_cond_time; /* units = percent */
+ A_UINT32 wlan_not_avail_time; /* units = percent */
+} wmi_chan_cca_stats_thresh;
+
+typedef struct
+{
+ A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_peer_signal_stats */
+ A_UINT32 vdev_id;
+ A_UINT32 peer_id;
+ /** per chain SNR in current bss, units are dB */
+ A_INT32 per_chain_snr[WMI_MAX_CHAINS];
+ /** per chain background noise, units are dBm */
+ A_INT32 per_chain_nf[WMI_MAX_CHAINS];
+} wmi_peer_signal_stats;
+
+/** Thresholds of signal stats, stand for percentage of stats variation.
+ * Check wmi_peer_signal_stats for each stats's meaning.
+ */
+typedef struct
+{
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_peer_signal_stats_thresh
+ */
+ A_UINT32 tlv_header;
+ A_UINT32 per_chain_snr; /* units = dB */
+ A_UINT32 per_chain_nf; /* units = dBm */
+} wmi_peer_signal_stats_thresh;
+
+typedef struct
+{
+ A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_tx_stats */
+ /** Number of total TX MSDUs on MAC layer in the period */
+ A_UINT32 tx_msdu_cnt;
+ /** Number of total TX MPDUs on MAC layer in the period */
+ A_UINT32 tx_mpdu_cnt;
+ /** Number of total TX PPDUs on MAC layer in the period */
+ A_UINT32 tx_ppdu_cnt;
+ /** Bytes of tx data on MAC layer in the period */
+ A_UINT32 tx_bytes;
+ /** Number of TX MSDUs cancelled due to any reason in the period,
+ * such as WMM limitation/bandwidth limitation/radio congestion */
+ A_UINT32 tx_msdu_drop_cnt;
+ /** Bytes of dropped TX packets in the period */
+ A_UINT32 tx_drop_bytes;
+ /** Number of unacked transmissions of MPDUs */
+ A_UINT32 tx_mpdu_retry_cnt;
+ /** Number of MPDUs have not been ACKed despite retried */
+ A_UINT32 tx_mpdu_fail_cnt;
+ /** Number of PPDUs which received no block ack */
+ A_UINT32 tx_ppdu_fail_cnt;
+ /* This TLV is followed by TLVs below: :
+ * A_UINT32 tx_mpdu_aggr[tx_mpdu_aggr_array_len];
+ * A_UINT32 tx_succ_mcs[tx_succ_mcs_array_len];
+ * A_UINT32 tx_fail_mcs[tx_fail_mcs_array_len];
+ * A_UINT32 tx_delay[tx_ppdu_delay_array_len];
+ */
+} wmi_tx_stats;
+
+/** Thresholds of tx stats, stand for percentage of stats variation.
+ * Check wmi_tx_stats for each stats's meaning.
+ */
+typedef struct
+{
+ A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_tx_stats_thresh */
+ A_UINT32 tx_msdu_cnt;
+ A_UINT32 tx_mpdu_cnt;
+ A_UINT32 tx_ppdu_cnt;
+ A_UINT32 tx_bytes;
+ A_UINT32 tx_msdu_drop_cnt;
+ A_UINT32 tx_drop_bytes;
+ A_UINT32 tx_mpdu_retry_cnt;
+ A_UINT32 tx_mpdu_fail_cnt;
+ A_UINT32 tx_ppdu_fail_cnt;
+ A_UINT32 tx_mpdu_aggr;
+ A_UINT32 tx_succ_mcs;
+ A_UINT32 tx_fail_mcs;
+ A_UINT32 tx_ppdu_delay;
+} wmi_tx_stats_thresh;
+
+typedef struct
+{
+ A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_peer_ac_tx_stats */
+ A_UINT32 vdev_id;
+ A_UINT32 peer_id;
+ /* The TLVs for the 4 AC follows:
+ * wmi_tx_stats tx_stats[]; wmi_tx_stats for BE/BK/VI/VO
+ */
+} wmi_peer_ac_tx_stats;
+
+typedef struct
+{
+ A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_rx_stats */
+ /** Number of RX MPDUs on MAC layer */
+ A_UINT32 mac_rx_mpdu_cnt;
+ /** Bytes of RX packets on MAC layer */
+ A_UINT32 mac_rx_bytes;
+ /** Number of RX PPDU on PHY layer */
+ A_UINT32 phy_rx_ppdu_cnt;
+ /** Bytes of RX packets on PHY layer */
+ A_UINT32 phy_rx_bytes;
+ /** Number of discontinuity in seqnum */
+ A_UINT32 rx_disorder_cnt;
+ /** Number of RX MPDUs flagged as retransmissions */
+ A_UINT32 rx_mpdu_retry_cnt;
+ /** Number of RX MPDUs identified as duplicates */
+ A_UINT32 rx_mpdu_dup_cnt;
+ /** Number of RX MPDUs discarded */
+ A_UINT32 rx_mpdu_discard_cnt;
+ /* This TLV is followed by TLVs below:
+ * A_UINT32 rx_mpdu_aggr[rx_mpdu_aggr_array_len];
+ * A_UINT32 rx_mcs[rx_msdu_mcs_array_len];
+ */
+} wmi_rx_stats;
+
+/** Thresholds of rx stats, stands for percentage of stats variation.
+ * Check wmi_rx_stats for each stats's meaning.
+ */
+typedef struct
+{
+ A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_rx_stats_thresh */
+ A_UINT32 mac_rx_mpdu_cnt;
+ A_UINT32 mac_rx_bytes;
+ A_UINT32 phy_rx_ppdu_cnt;
+ A_UINT32 phy_rx_bytes;
+ A_UINT32 rx_disorder_cnt;
+ A_UINT32 rx_mpdu_retry_cnt;
+ A_UINT32 rx_mpdu_dup_cnt;
+ A_UINT32 rx_mpdu_discard_cnt;
+ A_UINT32 rx_mpdu_aggr;
+ A_UINT32 rx_mcs;
+ A_UINT32 sta_ps_inds;
+ A_UINT32 sta_ps_durs;
+ A_UINT32 rx_probe_reqs;
+ A_UINT32 rx_oth_mgmts;
+} wmi_rx_stats_thresh;
+
+typedef struct
+{
+ A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_peer_ac_rx_stats */
+ A_UINT32 vdev_id;
+ A_UINT32 peer_id;
+ /** How many times STAs go to sleep */
+ A_UINT32 sta_ps_inds;
+ /** Total sleep time of STAs, milliseconds units */
+ A_UINT32 sta_ps_durs;
+ /** Number of probe requests received */
+ A_UINT32 rx_probe_reqs;
+ /** Number of other management frames received, not including probe requests */
+ A_UINT32 rx_oth_mgmts;
+ /* The TLVs for the 4 AC follows:
+ * wmi_rx_stats rx_stats[]; wmi_rx_stats for BE/BK/VI/VO
+ */
+} wmi_peer_ac_rx_stats;
+
+typedef enum {
+ /** Periodic timer timed out, based on the period specified
+ * by WMI_PDEV_PARAM_STATS_OBSERVATION_PERIOD
+ */
+ TRIGGER_COND_ID_TIMER_TIMED_OUT = 0x1,
+ /** Any of the (enabled) stats thresholds specified
+ * in the WMI_PDEV_SET_STATS_THRESHOLD_CMD message is exceeded
+ * within the current stats period.
+ */
+ TRIGGER_COND_ID_THRESH_EXCEEDED = 0x2,
+ /** In Response to the one-time wlan stats request of
+ * WMI_REQUEST_WLAN_STATS_CMDID from host.
+ */
+ TRIGGER_COND_ID_ONE_TIME_REQUEST = 0x3,
+} wmi_report_stats_event_trigger_cond_id;
+
+typedef struct {
+ A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_report_stats_event_fixed_param */
+ /** Indicate what triggered this event, check wmi_report_stats_event_trigger_cond_id for details */
+ A_UINT32 trigger_cond_id;
+ /** Bitmap to indicate changed channel CCA stats which exceeded the thresholds */
+ A_UINT32 cca_chgd_bitmap;
+ /** Bitmap to indicate changed peer signal stats which exceeded the thresholds */
+ A_UINT32 sig_chgd_bitmap;
+ /** Bitmap to indicate changed TX counters which exceeded the thresholds */
+ A_UINT32 tx_chgd_bitmap;
+ /** Bitmap to indicate changed RX counters which exceeded the thresholds */
+ A_UINT32 rx_chgd_bitmap;
+ /** number of per channel CCA stats structures (wmi_chan_cca_stats), 0 to max vdevs*/
+ A_UINT32 num_chan_cca_stats;
+ /** number of per peer signal stats structures (wmi_peer_signal_stats), 0 to max peers*/
+ A_UINT32 num_peer_signal_stats;
+ /** number of per peer ac TX stats structures (wmi_peer_ac_tx_stats), 0 to max peers*/
+ A_UINT32 num_peer_ac_tx_stats;
+ /** Array length of tx_mpdu_aggr[] which is histogram of MPDU aggregation size(1 to 7 and 8+).
+ * The array indicates number of MPDUs sent on specified aggregation size
+ * (per number of MPDUs per AMPDUs / 1 to 7 and 8+).
+ * Array length can be set per WMI_PDEV_PARAM_TX_MPDU_AGGR_ARRAY_LEN */
+ A_UINT32 tx_mpdu_aggr_array_len;
+ /** Array length of tx_succ_mcs[] which is histogram of encoding rate.
+ * The array indicates number of acked PPDUs sent at a specific rate */
+ A_UINT32 tx_succ_mcs_array_len;
+ /** Array length of tx_fail_mcs[] which is histogram of encoding rate.
+ * The array indicates number of unacked PPDUs sent at a specific rate */
+ A_UINT32 tx_fail_mcs_array_len;
+ /** tx_ppdu_delay[]is a histogram of delays on MAC layer.
+ * The array counts numbers of PPDUs encountering different TX time delays.
+ * TX delay here means time interval between the time a PPDU is queued
+ * to the MAC HW for transmission and the time the lower layers of
+ * tx FW return a tx status.
+ *
+ * The bin size tx_ppdu_delay_bin_size_ms specifies how many
+ * milliseconds. Each bin of the tx_ppdu_delay histogram represents.
+ * By default the bin size is 10ms.
+ * tx_ppdu_delay[0] -> delays between 0-9 ms
+ * tx_ppdu_delay[1] -> delays between 10-19 ms
+ * ...
+ * tx_ppdu_delay[9] -> delays between 90-99 ms
+ * tx_ppdu_delay[10] -> delays >= 100 ms
+ * Bin size can be set per WMI_PDEV_PARAM_TX_PPDU_DELAY_BIN_SIZE_MS.
+ */
+ A_UINT32 tx_ppdu_delay_bin_size_ms;
+ /** Array length of tx_ppdu_delay[]. It can be set per WMI_PDEV_PARAM_TX_PPDU_DELAY_ARRAY_LEN */
+ A_UINT32 tx_ppdu_delay_array_len;
+ /** number of per peer ac RX stats structures (wmi_peer_ac_rx_stats), 0 to max peers*/
+ A_UINT32 num_peer_ac_rx_stats;
+ /** Array length of rx_mpdu_aggr[] which is histogram of MPDU aggregation size(1 to 7 and 8+).
+ * It can be set per WMI_PDEV_PARAM_RX_MPDU_AGGR_ARRAY_LEN */
+ A_UINT32 rx_mpdu_aggr_array_len;
+ /** Array size of rx_mcs[] which is histogram of encoding rate.
+ * The array indicates number of PPDUs received at a specific rate */
+ A_UINT32 rx_mcs_array_len;
+
+ /**
+ * This TLV is followed by TLVs below:
+ * wmi_chan_cca_stats chan_cca_stats[]; Array length is specified by num_chan_cca_stats
+ * wmi_peer_signal_stats peer_signal_stats[]; Array length is specified by num_peer_signal_stats
+ * wmi_peer_ac_tx_stats peer_ac_tx_stats[]; Array length is specified by num_peer_ac_tx_stats
+ * wmi_tx_stats tx_stats[][]; Array length is num_peer_ac_tx_stats * WLAN_MAX_AC, array index is (peer_index * WLAN_MAX_AC + ac_index)
+ * A_UINT32 tx_mpdu_aggr[][][]; Array length is num_peer_ac_tx_stats * WLAN_MAX_AC * tx_mpdu_aggr_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * tx_mpdu_aggr_array_len + A-MPDU aggregation index
+ * A_UINT32 tx_succ_mcs[][][]; Array length is num_peer_ac_tx_stats * WLAN_MAX_AC * tx_succ_mcs_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * tx_succ_mcs_array_len + MCS index
+ * A_UINT32 tx_fail_mcs[][][]; Array length is num_peer_ac_tx_stats * WLAN_MAX_AC * tx_fail_mcs_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * tx_fail_mcs_array_len + MCS index
+ * A_UINT32 tx_ppdu_delay[][][]; Array length is num_peer_ac_tx_stats * WLAN_MAX_AC * tx_ppdu_delay_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * tx_ppdu_delay_array_len + tx delay index
+ * wmi_peer_ac_rx_stats peer_ac_rx_stats[]; Array length is specified by num_peer_ac_rx_stats
+ * wmi_rx_stats rx_stats[][]; Array length is num_peer_ac_rx_stats * WLAN_MAX_AC, array index is (peer_index * WLAN_MAX_AC + ac_index)
+ * A_UINT32 rx_mpdu_aggr[][][]; Array length is num_peer_ac_rx_stats * WLAN_MAX_AC * rx_mpdu_aggr_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * rx_mpdu_aggr_array_len + A-MPDU aggregation index
+ * A_UINT32 rx_mcs[][][]; Array length is (num_peer_ac_rx_stats * WLAN_MAX_AC) * rx_mcs_array_len,
+ * array index is (peer_index * WLAN_MAX_AC + ac_index) * rx_mcs_array_len + MCS index
+ **/
+} wmi_report_stats_event_fixed_param;
+
+typedef struct {
+ /** TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_peer_stats_info */
+ A_UINT32 tlv_header;
+ /** peer MAC address */
+ wmi_mac_addr peer_macaddr;
+ /** bytes (size of MPDUs) transmitted to this peer */
+ struct {
+ /* lower 32 bits of the tx_bytes value */
+ A_UINT32 low_32;
+ /* upper 32 bits of the tx_bytes value */
+ A_UINT32 high_32;
+ } tx_bytes;
+ /** packets (MSDUs) transmitted to this peer */
+ struct {
+ /* lower 32 bits of the tx_packets value */
+ A_UINT32 low_32;
+ /* upper 32 bits of the tx_packets value */
+ A_UINT32 high_32;
+ } tx_packets;
+ /** bytes (size of MPDUs) received from this peer */
+ struct {
+ /* lower 32 bits of the rx_bytes value */
+ A_UINT32 low_32;
+ /* upper 32 bits of the rx_bytes value */
+ A_UINT32 high_32;
+ } rx_bytes;
+ /** packets (MSDUs) received from this peer */
+ struct {
+ /* lower 32 bits of the rx_packets value */
+ A_UINT32 low_32;
+ /* upper 32 bits of the rx_packets value */
+ A_UINT32 high_32;
+ } rx_packets;
+ /** cumulative retry counts (MPDUs) */
+ A_UINT32 tx_retries;
+ /** number of failed transmissions (MPDUs) (retries exceeded, no ACK) */
+ A_UINT32 tx_failed;
+ /** rate information, it is output of WMI_ASSEMBLE_RATECODE_V1
+ * (in format of 0x1000RRRR)
+ * The rate-code is a 4-bytes field in which,
+ * for given rate, nss and preamble
+ *
+ * b'31-b'29 unused / reserved
+ * b'28 indicate the version of rate-code (1 = RATECODE_V1)
+ * b'27-b'11 unused / reserved
+ * b'10-b'8 indicate the preamble (0 OFDM, 1 CCK, 2 HT, 3 VHT)
+ * b'7-b'5 indicate the NSS (0 - 1x1, 1 - 2x2, 2 - 3x3, 3 - 4x4)
+ * b'4-b'0 indicate the rate, which is indicated as follows:
+ * OFDM : 0: OFDM 48 Mbps
+ * 1: OFDM 24 Mbps
+ * 2: OFDM 12 Mbps
+ * 3: OFDM 6 Mbps
+ * 4: OFDM 54 Mbps
+ * 5: OFDM 36 Mbps
+ * 6: OFDM 18 Mbps
+ * 7: OFDM 9 Mbps
+ * CCK (pream == 1)
+ * 0: CCK 11 Mbps Long
+ * 1: CCK 5.5 Mbps Long
+ * 2: CCK 2 Mbps Long
+ * 3: CCK 1 Mbps Long
+ * 4: CCK 11 Mbps Short
+ * 5: CCK 5.5 Mbps Short
+ * 6: CCK 2 Mbps Short
+ * HT/VHT (pream == 2/3)
+ * 0..7: MCS0..MCS7 (HT)
+ * 0..9: MCS0..MCS9 (11AC VHT)
+ * 0..11: MCS0..MCS11 (11AX VHT)
+ */
+ /** rate-code of the last transmission */
+ A_UINT32 last_tx_rate_code;
+ /** rate-code of the last received PPDU */
+ A_UINT32 last_rx_rate_code;
+ /** bitrate of the last transmission, in units of kbps */
+ A_UINT32 last_tx_bitrate_kbps;
+ /** bitrate of the last received PPDU, in units of kbps */
+ A_UINT32 last_rx_bitrate_kbps;
+ /** combined RSSI of the last received PPDU, in unit of dBm */
+ A_INT32 peer_rssi;
+} wmi_peer_stats_info;
+
+typedef struct {
+ /** TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_peer_stats_info_event_fixed_param */
+ A_UINT32 tlv_header;
+ /** VDEV to which the peers belong to */
+ A_UINT32 vdev_id;
+ /** number of peers in peer_stats_info[] */
+ A_UINT32 num_peers;
+ /** flag to indicate if there are more peers which will
+ * be sent a following seperate peer_stats_info event */
+ A_UINT32 more_data;
+ /* This TLV is followed by another TLV of array of structs
+ * wmi_peer_stats_info peer_stats_info[];
+ */
+} wmi_peer_stats_info_event_fixed_param;
+
+typedef struct {
+ /** TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_radio_chan_stats */
+ A_UINT32 tlv_header;
+ /** primary channel freq of the channel whose stats is sent */
+ A_UINT32 chan_mhz;
+ /** accumulation of time the radio is tuned to this channel,
+ * in units of microseconds */
+ A_UINT32 on_chan_us;
+ /** accumulation of the TX PPDU duration over the measurement period,
+ * in units of microseconds */
+ A_UINT32 tx_duration_us;
+ /** accumulation of the RX PPDU duration over the measurement period,
+ * in units of microseconds */
+ A_UINT32 rx_duration_us;
+ /** ratio of channel busy time to on_chan_us, in units of percent */
+ A_UINT32 chan_busy_ratio;
+ /** ratio of on_chan_us to the measurement period, in units of percent */
+ A_UINT32 on_chan_ratio;
+ /** measurement period, in units of microseconds */
+ A_UINT32 measurement_period_us;
+ /** MPDUs transmitted on this channel */
+ A_UINT32 tx_mpdus;
+ /** MSDUs transmitted on this channel */
+ A_UINT32 tx_msdus;
+ /** MPDUS successfully received on this channel */
+ A_UINT32 rx_succ_mpdus;
+ /** Failed MPDUs (CRC failures) received on this channel */
+ A_UINT32 rx_fail_mpdus;
+} wmi_radio_chan_stats;
+
+typedef struct {
+ /** TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_radio_chan_stats_event_fixed_param */
+ A_UINT32 tlv_header;
+ /** number of channel stats in radio_chan_stats[] */
+ A_UINT32 num_chans;
+ /* This TLV is followed by another TLV of array of structs
+ * wmi_radio_chan_stats radio_chan_stats[];
+ */
+} wmi_radio_chan_stats_event_fixed_param;
+
/**
* PDEV statistics
* @todo
@@ -4748,6 +5743,16 @@
/* NAN Data Interface */
#define WMI_VDEV_TYPE_NDI 0x7
+/*
+ * Param values to be sent for WMI_VDEV_PARAM_SGI command
+ * which are used in 11ax systems
+ */
+#define WMI_SGI_LEGACY 0x1 /* for HT and VHT */
+#define WMI_SGI_HE_400_NS 0x2 /* for HE 400 nsec */
+#define WMI_SGI_HE_800_NS 0x4 /* for HE 800 nsec */
+#define WMI_SGI_HE_1600_NS 0x8 /* for HE 1600 nsec */
+#define WMI_SGI_HE_3200_NS 0x10 /* for HE 3200 nsec */
+
/** values for vdev_subtype */
#define WMI_UNIFIED_VDEV_SUBTYPE_P2P_DEVICE 0x1
#define WMI_UNIFIED_VDEV_SUBTYPE_P2P_CLIENT 0x2
@@ -4778,6 +5783,163 @@
*/
#define WMI_UNIFIED_VDEV_START_BCN_TX_RATE_PRESENT (1<<2)
+#define WMI_HEOPS_COLOR_GET(he_ops) WMI_GET_BITS(he_ops, 0, 6)
+#define WMI_HEOPS_COLOR_SET(he_ops, value) WMI_SET_BITS(he_ops, 0, 6, value)
+
+#define WMI_HEOPS_DEFPE_GET(he_ops) WMI_GET_BITS(he_ops, 6, 3)
+#define WMI_HEOPS_DEFPE_SET(he_ops, value) WMI_SET_BITS(he_ops, 6, 3, value)
+
+#define WMI_HEOPS_TWT_GET(he_ops) WMI_GET_BITS(he_ops, 9, 1)
+#define WMI_HEOPS_TWT_SET(he_ops, value) WMI_SET_BITS(he_ops, 9, 1, value)
+
+#define WMI_HEOPS_RTSTHLD_GET(he_ops) WMI_GET_BITS(he_ops, 10, 10)
+#define WMI_HEOPS_RTSTHLD_SET(he_ops, value) WMI_SET_BITS(he_ops, 10, 10, value)
+
+#define WMI_HEOPS_PDMIN_GET(he_ops) WMI_GET_BITS(he_ops, 20, 5)
+#define WMI_HEOPS_PDMIN_SET(he_ops, value) WMI_SET_BITS(he_ops, 20, 5, value)
+
+#define WMI_HEOPS_PDMAX_GET(he_ops) WMI_GET_BITS(he_ops, 25, 5)
+#define WMI_HEOPS_PDMAX_SET(he_ops, value) WMI_SET_BITS(he_ops, 25, 5, value)
+
+#define WMI_MAX_HECAP_PHY_SIZE (3)
+#define WMI_HECAP_PHY_COD_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 0, 1)
+#define WMI_HECAP_PHY_COD_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 0, 1, value)
+
+#define WMI_HECAP_PHY_TXLDPC_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 1, 1)
+#define WMI_HECAP_PHY_TXLDPC_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 1, 1, value)
+
+#define WMI_HECAP_PHY_RXLDPC_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 2, 1)
+#define WMI_HECAP_PHY_RXLDPC_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 2, 1, value)
+
+#define WMI_HECAP_PHY_DCM_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 3, 3)
+#define WMI_HECAP_PHY_DCM_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 3, 3, value)
+
+#define WMI_HECAP_PHY_OLTF_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 6, 1)
+#define WMI_HECAP_PHY_OLTF_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 6, 1, value)
+
+#define WMI_HECAP_PHY_CBW_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 7, 3)
+#define WMI_HECAP_PHY_CBW_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 7, 3, value)
+
+#define WMI_HECAP_PHY_TXSTBC_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 10, 1)
+#define WMI_HECAP_PHY_TXSTBC_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 10, 1, value)
+
+#define WMI_HECAP_PHY_RXSTBC_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 11, 1)
+#define WMI_HECAP_PHY_RXSTBC_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 11, 1, value)
+
+#define WMI_HECAP_PHY_DLOFMAMUMIMO_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 12, 1)
+#define WMI_HECAP_PHY_DLOFDMAMUMIO_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 12, 1, value)
+
+#define WMI_HECAP_PHY_UL_MU_MIMO_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 13, 1)
+#define WMI_HECAP_PHY_UL_MU_MIMO_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 13, 1, value)
+
+#define WMI_HECAP_PHY_ULOFDMA_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 14, 1)
+#define WMI_HECAP_PHY_ULOFDMA_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 14, 1, value)
+
+#define WMI_HECAP_PHY_TXDOPPLER_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 15, 1)
+#define WMI_HECAP_PHY_TXDOPPLER_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 15, 1, value)
+
+#define WMI_HECAP_PHY_RXDOPPLER_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 16, 1)
+#define WMI_HECAP_PHY_RXDOPPLER_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 16, 1, value)
+
+#define WMI_HECAP_PHY_CBMODE_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 17, 8)
+#define WMI_HECAP_PHY_CBMODE_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 17, 8, value)
+
+#define WMI_HECAP_PHY_PADDING_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[0], 25, 2)
+#define WMI_HECAP_PHY_PADDING_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[0], 25, 2, value)
+
+#define WMI_HECAP_PHY_32GI_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[1], 0, 26)
+#define WMI_HECAP_PHY_32GI_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[1], 0, 26, value)
+
+#define WMI_HECAP_PHY_SUBFMR_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[1], 26, 1)
+#define WMI_HECAP_PHY_SUBFMR_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[1], 26, 1, value)
+
+#define WMI_HECAP_PHY_SUBFME_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[1], 27, 1)
+#define WMI_HECAP_PHY_SUBFME_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[1], 27, 1, value)
+
+#define WMI_HECAP_PHY_SUBFMESTS_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[1], 28, 3)
+#define WMI_HECAP_PHY_SUBFMESTS_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[1], 28, 3, value)
+
+#define WMI_HECAP_PHY_NOSUNDIMENS_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[2], 0, 3)
+#define WMI_HECAP_PHY_NOSUNDIMENS_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[2], 0, 3, value)
+
+#define WMI_HECAP_PHY_MUBFMR_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[2], 3, 1)
+#define WMI_HECAP_PHY_MUBFMR_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[2], 3, 1, value)
+
+#define WMI_HECAP_PHY_40MHZNSS_GET(he_cap_phy) WMI_GET_BITS(he_cap_phy[2], 4, 18)
+#define WMI_HECAP_PHY_40MHZNSS_SET(he_cap_phy, value) WMI_SET_BITS(he_cap_phy[2], 4, 18, value)
+
+#define WMI_HECAP_MAC_MTID_GET(he_cap) WMI_GET_BITS(he_cap, 0, 3)
+#define WMI_HECAP_MAC_MTID_SET(he_cap, value) WMI_SET_BITS(he_cap, 0, 3, value)
+
+#define WMI_HECAP_MAC_AACK_GET(he_cap) WMI_GET_BITS(he_cap, 3, 1)
+#define WMI_HECAP_MAC_AACK_SET(he_cap, value) WMI_SET_BITS(he_cap, 3, 1, value)
+
+#define WMI_HECAP_MAC_MINFRAGSZ_GET(he_cap) WMI_GET_BITS(he_cap, 4, 2)
+#define WMI_HECAP_MAC_MINFRAGSZ_SET(he_cap, value) WMI_SET_BITS(he_cap, 4, 2, value)
+
+#define WMI_HECAP_MAC_HEFRAG_GET(he_cap) WMI_GET_BITS(he_cap, 6, 2)
+#define WMI_HECAP_MAC_HEFRAG_SET(he_cap, value) WMI_SET_BITS(he_cap, 6, 2, value)
+
+#define WMI_HECAP_MAC_MURTS_GET(he_cap) WMI_GET_BITS(he_cap, 8, 1)
+#define WMI_HECAP_MAC_MURTS_SET(he_cap, value) WMI_SET_BITS(he_cap, 8, 1, value)
+
+#define WMI_HECAP_MAC_OMI_GET(he_cap) WMI_GET_BITS(he_cap, 9, 1)
+#define WMI_HECAP_MAC_OMI_SET(he_cap, value) WMI_SET_BITS(he_cap, 9, 1, value)
+
+#define WMI_HECAP_MAC_HECTRL_GET(he_cap) WMI_GET_BITS(he_cap, 10, 1)
+#define WMI_HECAP_MAC_HECTRL_SET(he_cap, value) WMI_SET_BITS(he_cap, 10, 1, value)
+
+#define WMI_HECAP_MAC_MBAHECTRL_GET(he_cap) WMI_GET_BITS(he_cap, 11, 1)
+#define WMI_HECAP_MAC_MBAHECTRL_SET(he_cap, value) WMI_SET_BITS(he_cap, 11, 1, value)
+
+#define WMI_HECAP_MAC_ULMURSP_GET(he_cap) WMI_GET_BITS(he_cap, 12, 1)
+#define WMI_HECAP_MAC_ULMURSP_SET(he_cap, value) WMI_SET_BITS(he_cap, 12, 1, value)
+
+#define WMI_HECAP_MAC_HELKAD_GET(he_cap) WMI_GET_BITS(he_cap, 13, 2)
+#define WMI_HECAP_MAC_HELKAD_SET(he_cap, value) WMI_SET_BITS(he_cap, 13, 2, value)
+
+#define WMI_HECAP_MAC_BSR_GET(he_cap) WMI_GET_BITS(he_cap, 15, 1)
+#define WMI_HECAP_MAC_BSR_SET(he_cap, value) WMI_SET_BITS(he_cap, 15, 1, value)
+
+#define WMI_HECAP_MAC_TWTREQ_GET(he_cap) WMI_GET_BITS(he_cap, 16, 1)
+#define WMI_HECAP_MAC_TWTREQ_SET(he_cap, value) WMI_SET_BITS(he_cap, 16, 1, value)
+
+#define WMI_HECAP_MAC_TWTRSP_GET(he_cap) WMI_GET_BITS(he_cap, 17, 1)
+#define WMI_HECAP_MAC_TWTRSP_SET(he_cap, value) WMI_SET_BITS(he_cap, 17, 1, value)
+
+#define WMI_HECAP_MAC_BCSTTWT_GET(he_cap) WMI_GET_BITS(he_cap, 18, 1)
+#define WMI_HECAP_MAC_BCSTTWT_SET(he_cap, value) WMI_SET_BITS(he_cap, 18, 1, value)
+
+#define WMI_HECAP_MAC_MBSS_GET(he_cap) WMI_GET_BITS(he_cap, 19, 1)
+#define WMI_HECAP_MAC_MBSS_SET(he_cap, value) WMI_SET_BITS(he_cap, 19, 1, value)
+
+#define WMI_HECAP_MAC_TRIGPADDUR_GET(he_cap) WMI_GET_BITS(he_cap, 20, 2)
+#define WMI_HECAP_MAC_TRIGPADDUR_SET(he_cap, value) WMI_SET_BITS(he_cap, 20, 2, value)
+
+#define WMI_HECAP_MAC_MAXFRAGMSDU_GET(he_cap) WMI_GET_BITS(he_cap, 22, 3)
+#define WMI_HECAP_MAC_MAXFRAGMSDU_SET(he_cap, value) WMI_SET_BITS(he_cap, 22, 3, value)
+
+#define WMI_HECAP_MAC_32BITBA_GET(he_cap) WMI_GET_BITS(he_cap, 25, 1)
+#define WMI_HECAP_MAC_32BITBA_SET(he_cap, value) WMI_SET_BITS(he_cap, 25, 1, value)
+
+#define WMI_HECAP_MAC_MUCASCADE_GET(he_cap) WMI_GET_BITS(he_cap, 26, 1)
+#define WMI_HECAP_MAC_MUCASCADE_SET(he_cap, value) WMI_SET_BITS(he_cap, 26, 1, value)
+
+#define WMI_HECAP_MAC_ACKMTIDAMPDU_GET(he_cap) WMI_GET_BITS(he_cap, 27, 1)
+#define WMI_HECAP_MAC_ACKMTIDAMPDU_SET(he_cap, value) WMI_SET_BITS(he_cap, 27, 1, value)
+
+#define WMI_HECAP_MAC_GROUPMSTABA_GET(he_cap) WMI_GET_BITS(he_cap, 28, 1)
+#define WMI_HECAP_MAC_GROUPMSTABA_SET(he_cap, value) WMI_SET_BITS(he_cap, 28, 1, value)
+
+#define WMI_HECAP_MAC_OFDMARA_GET(he_cap) WMI_GET_BITS(he_cap, 29, 1)
+#define WMI_HECAP_MAC_OFDMARA_SET(he_cap, value) WMI_SET_BITS(he_cap, 29, 1, value)
+
+#define WMI_GET_HW_RATECODE_PREAM_V1(_rcode) (((_rcode) >> 8) & 0x7)
+#define WMI_GET_HW_RATECODE_NSS_V1(_rcode) (((_rcode) >> 5) & 0x7)
+#define WMI_GET_HW_RATECODE_RATE_V1(_rcode) (((_rcode) >> 0) & 0x1F)
+#define WMI_ASSEMBLE_RATECODE_V1(_rate, _nss, _pream) \
+ (((1) << 28) | ((_pream) << 8) | ((_nss) << 5) | (_rate))
+
typedef struct {
A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_vdev_start_request_cmd_fixed_param */
/** unique id identifying the VDEV, generated by the caller */
@@ -4811,7 +5973,9 @@
/** This field will be invalid unless the Dual Band Simultaneous (DBS) feature is enabled. */
/** the DBS policy manager indicates the preferred number of receive streams. */
A_UINT32 preferred_rx_streams;
-
+ A_UINT32 he_ops; /* refer to WMI_HEOPS_xxx macros */
+ A_UINT32 cac_duration_ms; /* in milliseconds */
+ A_UINT32 regdomain;
/* The TLVs follows this structure:
* wmi_channel chan; //WMI channel
* wmi_p2p_noa_descriptor noa_descriptors[]; //actual p2p NOA descriptor from scan entry
@@ -4924,6 +6088,7 @@
WMI_RATE_PREAMBLE_CCK,
WMI_RATE_PREAMBLE_HT,
WMI_RATE_PREAMBLE_VHT,
+ WMI_RATE_PREAMBLE_HE,
} WMI_RATE_PREAMBLE;
/** Value to disable fixed rate setting */
@@ -4983,7 +6148,18 @@
WMI_VDEV_PARAM_MGMT_RATE,
/** Protection Mode */
WMI_VDEV_PARAM_PROTECTION_MODE,
- /** Fixed rate setting */
+ /** Fixed rate setting
+ * The top nibble is used to select which format to use for encoding
+ * the rate specification: 0xVXXXXXXX
+ * If V == 0b0000: format is same as before: 0x000000RR
+ * If V == 0b0001: format is: 0x1000RRRR.
+ * This will be output of WMI_ASSEMBLE_RATECODE_V1
+ * The host shall use the new V1 format (and set V = 0x1) if the target
+ * indicates 802.11ax support via the WMI_SERVICE_11AX flag, or if the
+ * system is configured with Nss > 4 (either at compile time within the
+ * host driver, or through WMI_SERVICE_READY PHY capabilities provided
+ * by the target).
+ */
WMI_VDEV_PARAM_FIXED_RATE,
/** Short GI Enable/Disable */
WMI_VDEV_PARAM_SGI,
@@ -5283,6 +6459,30 @@
*/
WMI_VDEV_PARAM_AGG_SW_RETRY_TH,
+ /** disable dynamic bw RTS **/
+ WMI_VDEV_PARAM_DISABLE_DYN_BW_RTS,
+
+ /**
+ * Per ssid (vdev) based ATF strict/fair scheduling policy
+ * Param values are WMI_ATF_SSID_FAIR_SCHED or
+ * WMI_ATF_SSID_STRICT_SCHED
+ */
+ WMI_VDEV_PARAM_ATF_SSID_SCHED_POLICY,
+
+ /** Enable or disable Dual carrier modulation
+ * valid values: 0-Disable DCM, 1-Enable DCM.
+ */
+ WMI_VDEV_PARAM_HE_DCM,
+ /** Enable or disable Extended range
+ * valid values: 0-Disable ER, 1-Enable ER.
+ */
+ WMI_VDEV_PARAM_HE_RANGE_EXT,
+ /* enable or disable BCAST probe response feature */
+ WMI_VDEV_PARAM_ENABLE_BCAST_PROBE_RESPONSE,
+
+ /* param to specify probe request Tx delay during Fast Initial Link Setup */
+ WMI_VDEV_PARAM_FILS_MAX_CHANNEL_GUARD_TIME, /* units = milliseconds */
+
/*
* === ADD NEW VDEV PARAM TYPES ABOVE THIS LINE ===
* The below vdev param types are used for prototyping, and are
@@ -5291,24 +6491,20 @@
WMI_VDEV_PARAM_PROTOTYPE = 0x8000,
/* 11AX SPECIFIC defines */
WMI_VDEV_PARAM_BSS_COLOR,
- /* In case of AP this will enable / disable MU-MIMO mode */
- WMI_VDEV_PARAM_SET_UL_MU_MIMO,
/*
- * set fragmentation level of the vdev's peers.
- * Values can be WMI_HE_FRAG_SUPPORT_LEVEL0..WMI_HE_FRAG_SUPPORT_LEVEL3
- */
- WMI_VDEV_PARAM_SET_FRAG_LEVEL,
- /*
- * control different features of HEControl:
- * Bit 0:- 1/0-> Enable/Disable transmssion of UL scheduling.
- * Bit 1:- 1/0-> Enable / disable honoring of ROMI from a peer.
- * Applicable in AP mode only.
- */
- WMI_VDEV_PARAM_SET_HECONTROL,
- /*
- * enable / disable trigger access for a AP vdev's peers.
+ * Enable / disable trigger access for a AP vdev's peers.
* For a STA mode vdev this will enable/disable triggered access
* and enable/disable Multi User mode of operation.
+ * 0 - Disable MU OFDMA and MU MIMO
+ * 1 - Disable DL OFDMA
+ * 2 - Disable DL MUMIMO
+ * 3 - Disable UL OFDMA
+ * 4 - Disable UL MUMIMO
+ * 5 - Enable MU OFDMA and MU MIMO
+ * 6 - Enable DL OFDMA
+ * 7 - Enable DL MUMIMO
+ * 8 - Enable UL OFDMA
+ * 9 - Enable UL MUMIMO
*/
WMI_VDEV_PARAM_SET_HEMU_MODE,
/*
@@ -6008,13 +7204,17 @@
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_tim_info */
- /** TIM bitmap len (in bytes)*/
+ /** TIM bitmap len (in bytes) */
A_UINT32 tim_len;
/** TIM Partial Virtual Bitmap */
A_UINT32 tim_mcast;
A_UINT32 tim_bitmap[WMI_TIM_BITMAP_ARRAY_SIZE];
A_UINT32 tim_changed;
A_UINT32 tim_num_ps_pending;
+ /** Use the vdev_id only if vdev_id_valid is set */
+ A_UINT32 vdev_id_valid;
+ /** unique id identifying the VDEV */
+ A_UINT32 vdev_id;
} wmi_tim_info;
typedef struct {
@@ -6044,6 +7244,10 @@
*/
A_UINT32 noa_attributes;
wmi_p2p_noa_descriptor noa_descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS];
+ /** Use the vdev_id only if vdev_id_valid is set */
+ A_UINT32 vdev_id_valid;
+ /** unique id identifying the VDEV */
+ A_UINT32 vdev_id;
}wmi_p2p_noa_info;
#define WMI_UNIFIED_NOA_ATTR_MODIFIED 0x1
@@ -6108,6 +7312,11 @@
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_host_swba_event_fixed_param */
/** bitmap identifying the VDEVs, generated by the caller */
A_UINT32 vdev_map;
+ /** how many vdev's info is included in this message
+ * If this field is zero, then the number of vdevs is specified by
+ * the number of bits set in the vdev_map bitmap.
+ */
+ A_UINT32 num_vdevs;
/* This TLV is followed by tim_info and p2p_noa_info for each vdev in vdevmap :
* wmi_tim_info tim_info[];
* wmi_p2p_noa_info p2p_noa_info[];
@@ -6128,6 +7337,28 @@
*/
} wmi_tbtt_offset_event_fixed_param;
+ typedef struct {
+ /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_tbtt_offset_info */
+ A_UINT32 tlv_header;
+ /** unique id identifying the VDEV */
+ A_UINT32 vdev_id;
+ /** tbttoffset in TUs */
+ A_UINT32 tbttoffset;
+ } wmi_tbtt_offset_info;
+
+ /** Use this event if number of vdevs > 32 */
+ typedef struct {
+ /*
+ * TLV tag and len;
+ * tag equals WMITLV_TAG_STRUC_wmi_tbtt_offset_ext_event_fixed_param
+ */
+ A_UINT32 tlv_header;
+ A_UINT32 num_vdevs;
+ /*
+ * The TLVs for tbttoffset will follow this TLV.
+ * Of size num_vdevs * wmi_tbtt_offset_info
+ */
+ } wmi_tbtt_offset_ext_event_fixed_param;
/* Peer Specific commands and events */
@@ -6204,6 +7435,23 @@
wmi_mac_addr peer_macaddr;
} wmi_peer_delete_cmd_fixed_param;
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_peer_set_rx_blocksize_cmd_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /** unique id identifying the VDEV, generated by the caller */
+ A_UINT32 vdev_id;
+ /** peer MAC address */
+ wmi_mac_addr peer_macaddr;
+ /**
+ * maximum block ack window size to use during a rx block ack negotiation,
+ * i.e. the maximum number of MPDUs per A-MPDU that will be received
+ */
+ A_UINT32 rx_block_ack_win_limit;
+} wmi_peer_set_rx_blocksize_cmd_fixed_param;
+
typedef struct {
A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_peer_flush_tids_cmd_fixed_param */
/** unique id identifying the VDEV, generated by the caller */
@@ -6430,7 +7678,18 @@
#define WMI_PEER_PHYMODE 0xD
/** Use FIXED Pwr */
#define WMI_PEER_USE_FIXED_PWR 0xE
-/** Set peer fixed rate */
+/** Set peer fixed rate
+ * The top nibble is used to select which format to use for encoding
+ * the rate specification: 0xVXXXXXXX
+ * If V == 0b0000: format is same as before: 0x000000RR
+ * If V == 0b0001: format is: 0x1000RRRR.
+ * This will be output of WMI_ASSEMBLE_RATECODE_V1
+ * The host shall use the new V1 format (and set V = 0x1) if the target
+ * indicates 802.11ax support via the WMI_SERVICE_11AX flag, or if the
+ * system is configured with Nss > 4 (either at compile time within the
+ * host driver, or through WMI_SERVICE_READY PHY capabilities provided
+ * by the target).
+ */
#define WMI_PEER_PARAM_FIXED_RATE 0xF
/** Whitelist peer TIDs */
#define WMI_PEER_SET_MU_WHITELIST 0x10
@@ -6438,6 +7697,14 @@
#define WMI_PEER_SET_MAX_TX_RATE 0x11
/** Set peer minimal tx rate (MCS) in adaptive rate ctrl */
#define WMI_PEER_SET_MIN_TX_RATE 0x12
+/**
+ * default ring routing for peer data packets,
+ * param_value = bit 0 for hash based routing enabled or not
+ * (value 1 is enabled, value 0 is disabled)
+ * bits 1:5 are for ring 32 (i.e. ring id value
+ * selected from 0 to 31 values)
+ */
+#define WMI_PEER_SET_DEFAULT_ROUTING 0x13
/** mimo ps values for the parameter WMI_PEER_MIMO_PS_STATE */
#define WMI_PEER_MIMO_PS_NONE 0x0
@@ -6481,26 +7748,42 @@
#define WMI_PEER_MAX_MIN_TX_RATE_SET(value32, tx_mode) WMI_SET_BITS(value32, 16, 16, tx_mode)
/* CCK max/min tx Rate description
- * tx_rate = 0: 1Mbps,
- * tx_rate = 1: 2Mbps
- * tx_rate = 2: 5.5Mbps
- * tx_rate = 3: 11Mbps
- * tx_rate = else : invalid.
+ * tx_rate = 0: 1 Mbps
+ * tx_rate = 1: 2 Mbps
+ * tx_rate = 2: 5.5 Mbps
+ * tx_rate = 3: 11 Mbps
+ * tx_rate = else: invalid
*/
-#define WMI_MAX_CCK_TX_RATE 0x03
+enum {
+ WMI_MAX_CCK_TX_RATE_1M, /* up to 1M CCK Rate avaliable */
+ WMI_MAX_CCK_TX_RATE_2M, /* up to 2M CCK Rate avaliable */
+ WMI_MAX_CCK_TX_RATE_5_5M, /* up to 5.5M CCK Rate avaliable */
+ WMI_MAX_CCK_TX_RATE_11M, /* up to 11M CCK Rate avaliable */
+ WMI_MAX_CCK_TX_RATE = WMI_MAX_CCK_TX_RATE_11M,
+};
/* OFDM max/min tx Rate description
- * tx_rate = 0: 6Mbps,
- * tx_rate = 1: 9Mbps
- * tx_rate = 2: 12Mbps
- * tx_rate = 3: 18Mbps
- * tx_rate = 4: 24Mbps
- * tx_rate = 5: 32Mbps
- * tx_rate = 6: 48Mbps
- * tx_rate = 7: 54Mbps
- * tx_rate = else : invalid.
+ * tx_rate = 0: 6 Mbps
+ * tx_rate = 1: 9 Mbps
+ * tx_rate = 2: 12 Mbps
+ * tx_rate = 3: 18 Mbps
+ * tx_rate = 4: 24 Mbps
+ * tx_rate = 5: 32 Mbps
+ * tx_rate = 6: 48 Mbps
+ * tx_rate = 7: 54 Mbps
+ * tx_rate = else: invalid
*/
-#define WMI_MAX_OFDM_TX_RATE 0x07
+enum {
+ WMI_MAX_OFDM_TX_RATE_6M, /* up to 6M OFDM Rate avaliable */
+ WMI_MAX_OFDM_TX_RATE_9M, /* up to 9M OFDM Rate avaliable */
+ WMI_MAX_OFDM_TX_RATE_12M, /* up to 12M OFDM Rate avaliable */
+ WMI_MAX_OFDM_TX_RATE_18M, /* up to 18M OFDM Rate avaliable */
+ WMI_MAX_OFDM_TX_RATE_24M, /* up to 24M OFDM Rate avaliable */
+ WMI_MAX_OFDM_TX_RATE_36M, /* up to 36M OFDM Rate avaliable */
+ WMI_MAX_OFDM_TX_RATE_48M, /* up to 48M OFDM Rate avaliable */
+ WMI_MAX_OFDM_TX_RATE_54M, /* up to 54M OFDM Rate avaliable */
+ WMI_MAX_OFDM_TX_RATE = WMI_MAX_OFDM_TX_RATE_54M,
+};
/* HT max/min tx rate description
* tx_rate = 0~7 : MCS Rate 0~7
@@ -6549,6 +7832,7 @@
#define WMI_PEER_QOS 0x00000002 /* QoS enabled */
#define WMI_PEER_NEED_PTK_4_WAY 0x00000004 /* Needs PTK 4 way handshake for authorization */
#define WMI_PEER_NEED_GTK_2_WAY 0x00000010 /* Needs GTK 2 way handshake after 4-way handshake */
+#define WMI_PEER_HE 0x00000400 /* HE Enabled */
#define WMI_PEER_APSD 0x00000800 /* U-APSD power save enabled */
#define WMI_PEER_HT 0x00001000 /* HT enabled */
#define WMI_PEER_40MHZ 0x00002000 /* 40MHz enabld */
@@ -6649,8 +7933,10 @@
wmi_ppe_threshold peer_ppet;
A_UINT32 peer_he_cap_info; /* protocol-defined HE / 11ax capability flags */
A_UINT32 peer_he_ops; /* HE operation contains BSS color */
+ A_UINT32 peer_he_cap_phy[WMI_MAX_HECAP_PHY_SIZE];
+ A_UINT32 peer_he_mcs; /* HE MCS/NSS set */
- /* Following this struc are the TLV's:
+ /* Following this struct are the TLV's:
* A_UINT8 peer_legacy_rates[];
* A_UINT8 peer_ht_rates[];
* wmi_vht_rate_set peer_vht_rates; //VHT capabilties of the peer
@@ -6671,6 +7957,9 @@
A_UINT32 vdev_id;
} wmi_peer_add_wds_entry_cmd_fixed_param;
+#define WMI_CHAN_INFO_START_RESP 0
+#define WMI_CHAN_INFO_END_RESP 1
+
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_peer_remove_wds_entry_cmd_fixed_param */
/** wds MAC addr */
@@ -6731,6 +8020,10 @@
A_UINT32 my_bss_rx_cycle_count;
/** b-mode data rx time (units are microseconds) */
A_UINT32 rx_11b_mode_data_duration;
+ /** tx frame count */
+ A_UINT32 tx_frame_cnt;
+ /** mac clock */
+ A_UINT32 mac_clk_mhz;
} wmi_chan_info_event_fixed_param;
/**
@@ -6884,6 +8177,10 @@
* and to look for a matching AP profile from a list of
* configured profiles. */
+/* flags for roam_scan_mode_cmd
+ * indicate the status (success/fail) of wmi_roam_scan_mode cmd through WMI_ROAM_EVENTID */
+#define WMI_ROAM_SCAN_MODE_FLAG_REPORT_STATUS 0x1
+
/**
* WMI_ROAM_SCAN_MODE: Set Roam Scan mode
* the roam scan mode is one of the periodic, rssi change, both, none.
@@ -6896,8 +8193,9 @@
*/
typedef struct {
A_UINT32 tlv_header; /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_roam_scan_mode_fixed_param */
- A_UINT32 roam_scan_mode;
+ A_UINT32 roam_scan_mode;
A_UINT32 vdev_id;
+ A_UINT32 flags; /* see WMI_ROAM_SCAN_MODE_FLAG defs */
} wmi_roam_scan_mode_fixed_param;
#define WMI_ROAM_SCAN_MODE_NONE 0x0
@@ -7084,6 +8382,13 @@
A_UINT32 rsn_mcastcipherset;
/** mcast/group management frames cipher set */
A_UINT32 rsn_mcastmgmtcipherset;
+ /**
+ * rssi_abs_thresold value: the value of the candidate AP should
+ * higher than this absolute RSSI threshold.
+ * Zero means no absolute minimum RSSI is required.
+ * units are the offset from the noise floor in dB.
+ */
+ A_UINT32 rssi_abs_thresh;
} wmi_ap_profile;
/** Support early stop roaming scanning when finding a strong candidate AP
@@ -7328,6 +8633,7 @@
* WMI_ROAM_REASON_HO_FAILED no matter WMI_ROAM_INVOKE_CMDID is called or not.
*/
#define WMI_ROAM_REASON_INVOKE_ROAM_FAIL 0x6
+#define ROAM_REASON_RSO_STATUS 0x7
/* reserved up through 0xF */
/* subnet status: bits 4-5 */
@@ -7352,9 +8658,12 @@
WMI_ROAM_SUBNET_CHANGE_STATUS_SHIFT)
/* roaming notification */
-#define WMI_ROAM_NOTIF_INVALID 0x0 /** invalid notification. Do not interpret notif field */
-#define WMI_ROAM_NOTIF_ROAM_START 0x1 /** indicate that roaming is started. sent only in non WOW state */
-#define WMI_ROAM_NOTIF_ROAM_ABORT 0x2 /** indicate that roaming is aborted. sent only in non WOW state */
+#define WMI_ROAM_NOTIF_INVALID 0x0 /** invalid notification. Do not interpret notif field */
+#define WMI_ROAM_NOTIF_ROAM_START 0x1 /** indicate that roaming is started. sent only in non WOW state */
+#define WMI_ROAM_NOTIF_ROAM_ABORT 0x2 /** indicate that roaming is aborted. sent only in non WOW state */
+#define WMI_ROAM_NOTIF_ROAM_REASSOC 0x3 /** indicate that reassociation is done. sent only in non WOW state */
+#define WMI_ROAM_NOTIF_SCAN_MODE_SUCCESS 0x4 /** indicate that roaming scan mode is successful */
+#define WMI_ROAM_NOTIF_SCAN_MODE_FAIL 0x5 /** indicate that roaming scan mode is failed due to internal roaming state */
/**whenever RIC request information change, host driver should pass all ric related information to firmware (now only support tsepc)
* Once, 11r roaming happens, firmware can generate RIC request in reassoc request based on these informations
@@ -7395,7 +8704,9 @@
#define WMI_ROAM_INVOKE_FLAG_ADD_CH_TO_CACHE 0
/* indicate to host of failure if WMI_ROAM_INVOKE_CMDID. */
#define WMI_ROAM_INVOKE_FLAG_REPORT_FAILURE 1
-/* from bit 2 to bit 31 are reserved */
+/* during host-invoked roaming, don't send null data frame to AP */
+#define WMI_ROAM_INVOKE_FLAG_NO_NULL_FRAME_TO_AP 2
+/* from bit 3 to bit 31 are reserved */
#define WMI_SET_ROAM_INVOKE_ADD_CH_TO_CACHE(flag) do { \
(flag) |= (1 << WMI_SET_ROAM_INVOKE_ADD_CH_TO_CACHE); \
@@ -7412,6 +8723,7 @@
#define WMI_ROAM_INVOKE_SCAN_MODE_FIXED_CH 0 /* scan given channel only */
#define WMI_ROAM_INVOKE_SCAN_MODE_CACHE_LIST 1 /* scan cached channel list */
#define WMI_ROAM_INVOKE_SCAN_MODE_FULL_CH 2 /* scan full channel */
+#define WMI_ROAM_INVOKE_SCAN_MODE_SKIP 3 /* no scan is performed. use beacon/probe resp given by the host */
#define WMI_ROAM_INVOKE_AP_SEL_FIXED_BSSID 0 /* roam to given BSSID only */
#define WMI_ROAM_INVOKE_AP_SEL_ANY_BSSID 1 /* roam to any BSSID */
@@ -7438,11 +8750,14 @@
A_UINT32 roam_delay; /** 0 = immediate roam, 1-2^32 = roam after this delay (msec) */
A_UINT32 num_chan; /** # if channels to scan. In the TLV channel_list[] */
A_UINT32 num_bssid; /** number of bssids. In the TLV bssid_list[] */
+ A_UINT32 num_buf; /** number of buffers In the TLV bcn_prb_buf_list[] */
/**
* TLV (tag length value ) parameters follows roam_invoke_req
* The TLV's are:
* A_UINT32 channel_list[];
* wmi_mac_addr bssid_list[];
+ * wmi_tlv_buf_len_param bcn_prb_buf_list[];
+ * A_UINT8 bcn_prb_frm[];
*/
} wmi_roam_invoke_cmd_fixed_param;
@@ -7749,6 +9064,21 @@
A_UINT32 reason;/* refer to p2p_lo_stopped_reason_e */
} wmi_p2p_lo_stopped_event_fixed_param;
+typedef enum {
+ WMI_MNT_FILTER_CONFIG_MANAGER,
+ WMI_MNT_FILTER_CONFIG_CONTROL,
+ WMI_MNT_FILTER_CONFIG_DATA,
+ WMI_MNT_FILTER_CONFIG_ALL,
+ WMI_MNT_FILTER_CONFIG_UNKNOWN,
+} WMI_MNT_FILTER_CONFIG_TYPE;
+
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 vdev_id;
+ A_UINT32 clear_or_set;
+ A_UINT32 configure_type; /* see WMI_MNT_FILTER_CONFIG_TYPE */
+} wmi_mnt_filter_cmd_fixed_param;
+
typedef struct {
A_UINT32 time32; //upper 32 bits of time stamp
A_UINT32 time0; //lower 32 bits of time stamp
@@ -7950,6 +9280,7 @@
WOW_REASON_TDLS_CONN_TRACKER_EVENT,
WOW_REASON_CRITICAL_LOG,
WOW_REASON_P2P_LISTEN_OFFLOAD,
+ WOW_REASON_NAN_EVENT_WAKE_HOST,
WOW_REASON_DEBUG_TEST = 0xFF,
} WOW_WAKE_REASON_TYPE;
@@ -7967,6 +9298,10 @@
* to request it.
*/
WMI_WOW_FLAG_SEND_PM_PME = 0x00000002,
+ /* Flag to indicate unit test */
+ WMI_WOW_FLAG_UNIT_TEST_ENABLE = 0x00000004,
+ /* Force HTC wakeup */
+ WMI_WOW_FLAG_DO_HTC_WAKEUP = 0x00000008,
};
typedef struct {
@@ -8315,6 +9650,7 @@
A_UINT32 swol_indoor_pattern; /* wakeup pattern */
A_UINT32 swol_indoor_exception; /* wakeup when exception happens */
A_UINT32 swol_indoor_exception_app;
+ A_UINT32 swol_assist_enable; /* whether to enable IoT mode */
} wmi_extwow_set_app_type1_params_cmd_fixed_param;
typedef struct {
@@ -8682,16 +10018,20 @@
#define WMI_NLO_MAX_SSIDS 16
#define WMI_NLO_MAX_CHAN 48
-#define WMI_NLO_CONFIG_STOP (0x1 << 0)
-#define WMI_NLO_CONFIG_START (0x1 << 1)
-#define WMI_NLO_CONFIG_RESET (0x1 << 2)
-#define WMI_NLO_CONFIG_SLOW_SCAN (0x1 << 4)
-#define WMI_NLO_CONFIG_FAST_SCAN (0x1 << 5)
-#define WMI_NLO_CONFIG_SSID_HIDE_EN (0x1 << 6)
+#define WMI_NLO_CONFIG_STOP (0x1 << 0)
+#define WMI_NLO_CONFIG_START (0x1 << 1)
+#define WMI_NLO_CONFIG_RESET (0x1 << 2)
+#define WMI_NLO_CONFIG_SLOW_SCAN (0x1 << 4)
+#define WMI_NLO_CONFIG_FAST_SCAN (0x1 << 5)
+#define WMI_NLO_CONFIG_SSID_HIDE_EN (0x1 << 6)
/* This bit is used to indicate if EPNO or supplicant PNO is enabled. Only one of them can be enabled at a given time */
-#define WMI_NLO_CONFIG_ENLO (0x1 << 7)
-#define WMI_NLO_CONFIG_SCAN_PASSIVE (0x1 << 8)
-#define WMI_NLO_CONFIG_ENLO_RESET (0x1 << 9)
+#define WMI_NLO_CONFIG_ENLO (0x1 << 7)
+#define WMI_NLO_CONFIG_SCAN_PASSIVE (0x1 << 8)
+#define WMI_NLO_CONFIG_ENLO_RESET (0x1 << 9)
+#define WMI_NLO_CONFIG_SPOOFED_MAC_IN_PROBE_REQ (0x1 << 10)
+#define WMI_NLO_CONFIG_RANDOM_SEQ_NO_IN_PROBE_REQ (0x1 << 11)
+#define WMI_NLO_CONFIG_ENABLE_IE_WHITELIST_IN_PROBE_REQ (0x1 << 12)
+#define WMI_NLO_CONFIG_ENABLE_CNLO_RSSI_CONFIG (0x1 << 13)
/* Whether directed scan needs to be performed (for hidden SSIDs) */
#define WMI_ENLO_FLAG_DIRECTED_SCAN 1
@@ -8794,6 +10134,24 @@
A_UINT32 band5GHz_bonus; /* 5GHz RSSI score bonus (applied to all 5GHz networks) */
} enlo_candidate_score_params;
+typedef struct connected_nlo_bss_band_rssi_pref_t {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_connected_nlo_bss_band_rssi_pref */
+ /** band which needs to get preference over other band - see wmi_set_vdev_ie_band enum */
+ A_UINT32 band;
+ /* Amount of RSSI preference (in dB) that can be given to a band */
+ A_INT32 rssi_pref;
+} connected_nlo_bss_band_rssi_pref;
+
+typedef struct connected_nlo_rssi_params_t {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_connected_nlo_rssi_params */
+ /* Relative rssi threshold (in dB) by which new BSS should have better rssi than
+ * the current connected BSS.
+ */
+ A_INT32 relative_rssi;
+ /* The amount of rssi preference (in dB) that can be given to a 5G BSS over 2.4G BSS. */
+ A_INT32 relative_rssi_5g_pref;
+} connected_nlo_rssi_params;
+
typedef struct wmi_nlo_config {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_nlo_config_cmd_fixed_param */
A_UINT32 flags;
@@ -8810,11 +10168,25 @@
A_UINT32 no_of_ssids;
A_UINT32 num_of_channels;
A_UINT32 delay_start_time; /* NLO scan start delay time in milliseconds */
+ /** MAC Address to use in Probe Req as SA **/
+ wmi_mac_addr mac_addr;
+ /** Mask on which MAC has to be randomized **/
+ wmi_mac_addr mac_mask;
+ /** IE bitmap to use in Probe Req **/
+ A_UINT32 ie_bitmap[WMI_IE_BITMAP_SIZE];
+ /** Number of vendor OUIs. In the TLV vendor_oui[] **/
+ A_UINT32 num_vendor_oui;
+ /** Number of connected NLO band preferences **/
+ A_UINT32 num_cnlo_band_pref;
+
/* The TLVs will follow.
* nlo_configured_parameters nlo_list[];
- * A_UINT32 channel_list[];
+ * A_UINT32 channel_list[num_of_channels];
* nlo_channel_prediction_cfg ch_prediction_cfg;
* enlo_candidate_score_params candidate_score_params;
+ * wmi_vendor_oui vendor_oui[num_vendor_oui];
+ * connected_nlo_rssi_params cnlo_rssi_params;
+ * connected_nlo_bss_band_rssi_pref cnlo_bss_band_rssi_pref[num_cnlo_band_pref];
*/
} wmi_nlo_config_cmd_fixed_param;
@@ -9194,8 +10566,12 @@
A_UINT32 type; /*0:unused 1: ASSERT, 2: not respond detect command,3: simulate ep-full(),4:...*/
A_UINT32 delay_time_ms; /*0xffffffff means the simulate will delay for random time (0 ~0xffffffff ms)*/
}WMI_FORCE_FW_HANG_CMD_fixed_param;
-#define WMI_MCAST_FILTER_SET 1
-#define WMI_MCAST_FILTER_DELETE 2
+
+typedef enum {
+ WMI_MCAST_FILTER_SET = 1,
+ WMI_MCAST_FILTER_DELETE
+} WMI_SET_SINGLE_MCAST_FILTER_OP;
+
typedef struct {
A_UINT32 tlv_header;
A_UINT32 vdev_id;
@@ -9204,6 +10580,28 @@
wmi_mac_addr mcastbdcastaddr;
} WMI_SET_MCASTBCAST_FILTER_CMD_fixed_param;
+typedef enum {
+ WMI_MULTIPLE_MCAST_FILTER_CLEAR = 1, /* clear all previous mc list */
+ /* clear all previous mc list, and set new list */
+ WMI_MULTIPLE_MCAST_FILTER_SET,
+ WMI_MULTIPLE_MCAST_FILTER_DELETE, /* delete one/multiple mc list */
+ WMI_MULTIPLE_MCAST_FILTER_ADD /* add one/multiple mc list */
+} WMI_MULTIPLE_MCAST_FILTER_OP;
+
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 vdev_id;
+ A_UINT32 operation; /* refer WMI_MULTIPLE_MCAST_FILTER_OP */
+ /* number of elements in the subsequent mcast addr list */
+ A_UINT32 num_mcastaddrs;
+ /**
+ * TLV (tag length value) parameters follow the
+ * structure. The TLV's are:
+ * wmi_mac_addr mcastaddr_list[num_mcastaddrs];
+ */
+} WMI_SET_MULTIPLE_MCAST_FILTER_CMD_fixed_param;
+
+
/* WMI_DBGLOG_TIME_STAMP_SYNC_CMDID */
typedef enum {
WMI_TIME_STAMP_SYNC_MODE_MS, /* millisecond units */
@@ -9450,6 +10848,74 @@
A_UINT32 pdev_id;
} wmi_dfs_phyerr_filter_dis_cmd_fixed_param;
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 pdev_id;
+} wmi_pdev_dfs_phyerr_offload_enable_cmd_fixed_param;
+
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 pdev_id;
+} wmi_pdev_dfs_phyerr_offload_disable_cmd_fixed_param;
+
+typedef enum {
+ QUICK_OCAC = 0,
+ EXTENSIVE_OCAC,
+} WMI_ADFS_OCAC_MODE;
+
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 vdev_id;
+ A_UINT32 ocac_mode; /* WMI_ADFS_OCAC_MODE */
+ A_UINT32 min_duration_ms; /* in milliseconds */
+ A_UINT32 max_duration_ms; /* in milliseconds */
+ A_UINT32 chan_freq; /* in MHz */
+ A_UINT32 chan_width; /* in MHz */
+ A_UINT32 center_freq; /* in MHz */
+} wmi_vdev_adfs_ch_cfg_cmd_fixed_param;
+
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 vdev_id;
+} wmi_vdev_adfs_ocac_abort_cmd_fixed_param;
+
+typedef enum {
+ IN_SERVICE_MODE = 0,
+ OCAC_MODE,
+} WMI_DFS_RADAR_DETECTION_MODE;
+
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 pdev_id;
+ /* In-service mode or O-CAC mode */
+ A_UINT32 detection_mode; /* WMI_DFS_RADAR_DETECTION_MODE */
+ A_UINT32 chan_freq; /* in MHz */
+ A_UINT32 chan_width; /* in MHz */
+ A_UINT32 detector_id;
+ A_UINT32 segment_id;
+ A_UINT32 timestamp;
+ A_UINT32 is_chirp;
+} wmi_pdev_dfs_radar_detection_event_fixed_param;
+
+typedef enum {
+ OCAC_COMPLETE = 0,
+ OCAC_ABORT,
+} WMI_VDEV_OCAC_COMPLETE_STATUS;
+
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 vdev_id;
+ A_UINT32 chan_freq; /* in MHz */
+ A_UINT32 chan_width; /* in MHz */
+ A_UINT32 center_freq; /* in MHz */
+ A_UINT32 status; /* WMI_VDEV_OCAC_COMPLETE_STATUS */
+} wmi_vdev_adfs_ocac_complete_event_fixed_param;
+
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 vdev_id;
+} wmi_vdev_dfs_cac_complete_event_fixed_param;
+
/** TDLS COMMANDS */
/* WMI_TDLS_SET_STATE_CMDID */
@@ -9739,6 +11205,16 @@
WMI_TDLS_ENTER_BT_BUSY_MODE,
/** BT exited busy mode, TDLS connection tracker needs to handle this */
WMI_TDLS_EXIT_BT_BUSY_MODE,
+ /*
+ * TDLS module received a scan start event, TDLS connection tracker
+ * needs to handle this
+ */
+ WMI_TDLS_SCAN_STARTED_EVENT,
+ /*
+ * TDLS module received a scan complete event, TDLS connection tracker
+ * needs to handle this
+ */
+ WMI_TDLS_SCAN_COMPLETED_EVENT,
};
/* WMI_TDLS_PEER_EVENTID */
@@ -10070,6 +11546,9 @@
#define LPI_IE_BITMAP_FLAGS 0x00200000 // reserved as a bitmap to indicate more scan information; one such use being to indicate if the on-going scan is interrupted or not
#define LPI_IE_BITMAP_CACHING_REQD 0x00400000 // extscan will use this field to indicate if this frame info needs to be cached in LOWI LP or not
#define LPI_IE_BITMAP_REPORT_CONTEXT_HUB 0x00800000 // extscan will use this field to indicate to LOWI LP whether to report result to context hub or not.
+#define LPI_IE_BITMAP_CHRE_ESS 0x010000000 /* ESS capability info for CHRE */
+#define LPI_IE_BITMAP_CHRE_SEC_MODE 0x020000000 /* Security capability info for CHRE */
+#define LPI_IE_BITMAP_CHRE_SUPPORTED_RATE 0x040000000 /* Hightest MCS corresponding NCC for TX and RX */
#define LPI_IE_BITMAP_ALL 0xFFFFFFFF
typedef struct {
@@ -10371,6 +11850,69 @@
*/
} wmi_peer_info_event_fixed_param;
+/**
+ * WMI_PEER_ANTDIV_INFO_REQ_CMDID
+ * Request FW to provide peer info
+ */
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_peer_antdiv_info_req_cmd_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /**
+ * In order to get the peer antdiv info for a single peer, host shall
+ * issue the peer_mac_address of that peer. For getting the
+ * info all peers, the host shall issue 0xFFFFFFFF as the mac
+ * address. The firmware will return the peer info for all the
+ * peers on the specified vdev_id
+ */
+ wmi_mac_addr peer_mac_address;
+ /** vdev id */
+ A_UINT32 vdev_id;
+} wmi_peer_antdiv_info_req_cmd_fixed_param;
+
+/** FW response with the peer antdiv info */
+typedef struct {
+ /** TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_peer_antdiv_info_event_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /** number of peers in peer_info */
+ A_UINT32 num_peers;
+ /** VDEV to which the peer belongs to */
+ A_UINT32 vdev_id;
+ /**
+ * This TLV is followed by another TLV of array of structs
+ * wmi_peer_antdiv_info peer_antdiv_info[];
+ */
+} wmi_peer_antdiv_info_event_fixed_param;
+
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_peer_antdiv_info
+ */
+ A_UINT32 tlv_header;
+ /** mac addr of the peer */
+ wmi_mac_addr peer_mac_address;
+ /**
+ * per chain rssi of the peer, for up to 8 chains.
+ * Each chain's entry reports the RSSI for different bandwidths:
+ * bits 7:0 -> primary 20 MHz
+ * bits 15:8 -> secondary 20 MHz of 40 MHz channel (if applicable)
+ * bits 23:16 -> secondary 40 MHz of 80 MHz channel (if applicable)
+ * bits 31:24 -> secondary 80 MHz of 160 MHz channel (if applicable)
+ * Each of these 8-bit RSSI reports is in dB units, with respect to
+ * the noise floor.
+ * 0x80 means invalid.
+ * All unused bytes within used chain_rssi indices shall be set
+ * to 0x80.
+ * All unused chain_rssi indices shall be set to 0x80808080.
+ */
+ A_INT32 chain_rssi[8];
+} wmi_peer_antdiv_info;
+
/** FW response when tx failure count has reached threshold
* for a peer */
typedef struct {
@@ -10879,6 +12421,17 @@
#define wmi_ndp_rsp_code wmi_ndp_rsp_code_PROTOTYPE
/**
+* NDP Channel configuration type
+*/
+typedef enum {
+ WMI_NDP_CHANNEL_NOT_REQUESTED = 0, /* Channel will not configured */
+ WMI_NDP_REQUEST_CHANNEL_SETUP = 1, /* Channel will be provided and is optional/hint */
+ WMI_NDP_FORCE_CHANNEL_SETUP = 2/* NDP must start on the provided channel */
+} wmi_ndp_channel_cfg_PROTOTYPE;
+
+#define wmi_ndp_channel_cfg wmi_ndp_channel_cfg_PROTOTYPE
+
+/**
* NDP Initiator requesting a data session
*/
typedef struct {
@@ -10896,12 +12449,19 @@
A_UINT32 ndp_cfg_len;
/** Actual number of bytes in TLV ndp_app_info */
A_UINT32 ndp_app_info_len;
+ /** NDP channel configuration type defined in wmi_ndp_channel_cfg */
+ A_UINT32 ndp_channel_cfg;
+ /** NAN Cipher Suite Shared Key */
+ A_UINT32 nan_csid;
+ /** Actual number of bytes in TLV nan_pmk */
+ A_UINT32 nan_pmk_len;
/**
* TLV (tag length value ) parameters follow the ndp_initiator_req
* structure. The TLV's are:
* wmi_channel channel;
* A_UINT8 ndp_cfg[];
* A_UINT8 ndp_app_info[];
+ * A_UINT8 nan_pmk[];
*/
} wmi_ndp_initiator_req_fixed_param_PROTOTYPE;
@@ -10929,11 +12489,16 @@
A_UINT32 ndp_cfg_len;
/** Number of bytes in TLV ndp_app_info */
A_UINT32 ndp_app_info_len;
+ /** NAN Cipher Suite Shared Key */
+ A_UINT32 nan_csid;
+ /** Actual number of bytes in TLV nan_pmk */
+ A_UINT32 nan_pmk_len;
/**
* TLV (tag length value ) parameters follow the ndp_responder_req
* structure. The TLV's are:
* A_UINT8 ndp_cfg[];
* A_UINT8 ndp_app_info[];
+ * A_UINT8 nan_pmk[];
*/
} wmi_ndp_responder_req_fixed_param_PROTOTYPE;
@@ -11006,6 +12571,8 @@
A_UINT32 max_ndp_sessions;
/** Max number of peer's per ndi */
A_UINT32 max_peers_per_ndi;
+ /** which combination of bands is supported - see NAN_DATA_SUPPORTED_BAND enums */
+ A_UINT32 nan_data_supported_bands;
} wmi_ndi_cap_rsp_event_fixed_param_PROTOTYPE;
#define wmi_ndi_cap_rsp_event_fixed_param wmi_ndi_cap_rsp_event_fixed_param_PROTOTYPE
@@ -11062,6 +12629,8 @@
A_UINT32 ndp_instance_id;
/** NDI mac address of the peer */
wmi_mac_addr peer_ndi_mac_addr;
+ /** Host can create peer if this entry is TRUE */
+ A_UINT32 create_peer;
} wmi_ndp_responder_rsp_event_fixed_param_PROTOTYPE;
#define wmi_ndp_responder_rsp_event_fixed_param wmi_ndp_responder_rsp_event_fixed_param_PROTOTYPE
@@ -11167,11 +12736,16 @@
A_UINT32 ndp_cfg_len;
/** Number of bytes in TLV wmi_ndp_app_info */
A_UINT32 ndp_app_info_len;
+ /** Peer NAN Cipher Suite Shared Key */
+ A_UINT32 nan_csid;
+ /** Actual number of bytes in TLV nan_scid */
+ A_UINT32 nan_scid_len;
/**
* TLV (tag length value ) parameters follow the ndp_indication
* structure. The TLV's are:
* A_UINT8 ndp_cfg[];
* A_UINT8 ndp_app_info[];
+ * A_UINT8 nan_scid[];
*/
} wmi_ndp_indication_event_fixed_param_PROTOTYPE;
@@ -11293,6 +12867,115 @@
WMI_MODEM_STATE_ON
};
+/**
+ * This command is sent from WLAN host driver to firmware to
+ * notify the updated Specific Absorption Rate (SAR) limits.
+ * A critical regulation for FCC compliance, OEMs require methods to set
+ * limits on TX power of WLAN/WWAN.
+ * Host would receive instructions on what to set the limits per
+ * band/chain/modulation to, it would then interpret and send the limits
+ * to FW using this WMI message.
+ * Since it is possible to have too many commands to fit into one message,
+ * FW will keep receiving the messages, until it finds one with
+ * commit_limits = 1, at which point it will apply all the received
+ * specifications.
+*/
+
+typedef struct {
+ /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_sar_limits_cmd_param */
+ A_UINT32 tlv_header;
+
+ /** when set to WMI_SAR_FEATURE_ON, enable SAR feature;
+ * with BDF (SET_0 to 4) or WMI
+ * if set to WMI_SAR_FEATURE_OFF, disable feature;
+ * if set to WMI_SAR_FEATURE_NO_CHANGE, do not alter state of feature;
+ */
+
+ A_UINT32 sar_enable;
+
+ /** number of items in sar_limits[] */
+ A_UINT32 num_limit_rows;
+ /** once received and is set to 1, FW will calculate the power limits
+ * and send set_power command to apply them.
+ * Otherwise just update local values stored in FW until a future msg
+ * with commit_limits=1 arrives.
+ */
+
+ A_UINT32 commit_limits;
+
+ /**
+ * TLV (tag length value) parameters follow the sar_limit_cmd_row
+ * structure. The TLV's are:
+ * wmi_sar_limit_cmd_row sar_limits[];
+ */
+} wmi_sar_limits_cmd_fixed_param;
+
+enum wmi_sar_feature_state_flags {
+ WMI_SAR_FEATURE_OFF = 0,
+ WMI_SAR_FEATURE_ON_SET_0,
+ WMI_SAR_FEATURE_ON_SET_1,
+ WMI_SAR_FEATURE_ON_SET_2,
+ WMI_SAR_FEATURE_ON_SET_3,
+ WMI_SAR_FEATURE_ON_SET_4,
+ WMI_SAR_FEATURE_NO_CHANGE,
+ WMI_SAR_FEATURE_ON_USER_DEFINED,
+};
+
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_sar_limit_cmd_row */
+
+ /** Current values: WMI_SAR_2G_ID, WMI_SAR_5G_ID. Can be extended by adding
+ * new band_id values .
+ */
+ A_UINT32 band_id;
+
+ A_UINT32 chain_id;
+
+ /** Current values: WMI_SAR_MOD_CCK, WMI_SAR_MOD_OFDM */
+ A_UINT32 mod_id;
+
+ /** actual power limit value, in steps of 0.5 dBm */
+ A_UINT32 limit_value;
+
+ /** in case the OEM doesn't care about one of the qualifiers from above,
+ * the bit for that qualifier within the validity_bitmap can be set to 0
+ * so that limit is applied to all possible cases of this qualifier
+ * (i.e. if a qualifier's validity_bitmap flag is 0, the qualifier is
+ * treated as a wildcard).
+ * Current masks:
+ * WMI_SAR_BAND_ID_VALID_MASK
+ * WMI_SAR_CHAIN_ID_VALID_MASK
+ * WMI_SAR_MOD_ID_VALID_MASK
+ * Example: if !WMI_IS_SAR_MOD_ID_VALID(bitmap),
+ * it means apply same limit_value to both WMI_SAR_MOD_CCK and
+ * WMI_SAR_MOD_OFDM cases.
+ */
+
+ A_UINT32 validity_bitmap;
+} wmi_sar_limit_cmd_row;
+
+enum wmi_sar_band_id_flags {
+ WMI_SAR_2G_ID = 0,
+ WMI_SAR_5G_ID
+};
+
+enum wmi_sar_mod_id_flags {
+ WMI_SAR_MOD_CCK = 0,
+ WMI_SAR_MOD_OFDM
+};
+
+#define WMI_SAR_BAND_ID_VALID_MASK (0x1)
+#define WMI_SAR_CHAIN_ID_VALID_MASK (0x2)
+#define WMI_SAR_MOD_ID_VALID_MASK (0x4)
+
+#define WMI_SET_SAR_BAND_ID_VALID(bitmap) ((bitmap) |= WMI_SAR_BAND_ID_VALID_MASK)
+#define WMI_SET_SAR_CHAIN_ID_VALID(bitmap) ((bitmap) |= WMI_SAR_CHAIN_ID_VALID_MASK)
+#define WMI_SET_SAR_MOD_ID_VALID(bitmap) ((bitmap) |= WMI_SAR_MOD_ID_VALID_MASK)
+
+#define WMI_IS_SAR_BAND_ID_VALID(bitmap) ((bitmap) & WMI_SAR_BAND_ID_VALID_MASK)
+#define WMI_IS_SAR_CHAIN_ID_VALID(bitmap) ((bitmap) & WMI_SAR_CHAIN_ID_VALID_MASK)
+#define WMI_IS_SAR_MOD_ID_VALID(bitmap) ((bitmap) & WMI_SAR_MOD_ID_VALID_MASK)
+
#define WMI_ROAM_AUTH_STATUS_CONNECTED 0x1 /** connected, but not authenticated */
#define WMI_ROAM_AUTH_STATUS_AUTHENTICATED 0x2 /** connected and authenticated */
@@ -12378,6 +14061,50 @@
A_UINT32 pdev_id;
} wmi_pdev_temperature_event_fixed_param;
+typedef enum {
+ ANTDIV_HW_CFG_STATUS,
+ ANTDIV_SW_CFG_STATUS,
+ ANTDIV_MAX_STATUS_TYPE_NUM
+} ANTDIV_STATUS_TYPE;
+
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_pdev_get_antdiv_status_cmd_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /* Status event ID - see ANTDIV_STATUS_TYPE */
+ A_UINT32 status_event_id;
+ /**
+ * pdev_id for identifying the MAC
+ * See macros starting with WMI_PDEV_ID_ for values.
+ */
+ A_UINT32 pdev_id;
+} wmi_pdev_get_antdiv_status_cmd_fixed_param;
+
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_pdev_antdiv_status_event_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /* ANT DIV feature enabled or not */
+ A_UINT32 support;
+ A_UINT32 chain_num; /* how many chain supported */
+ /* how many ANT supported, 32 max */
+ A_UINT32 ant_num;
+ /**
+ * Each entry is for a tx/rx chain, and contains a bitmap identifying
+ * the antennas attached to that tx/rx chain.
+ */
+ A_UINT32 selectable_ant_mask[8];
+ /**
+ * pdev_id for identifying the MAC
+ * See macros starting with WMI_PDEV_ID_ for values.
+ */
+ A_UINT32 pdev_id;
+} wmi_pdev_antdiv_status_event_fixed_param;
+
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_set_dhcp_server_offload_cmd_fixed_param */
A_UINT32 vdev_id;
@@ -12832,6 +14559,11 @@
A_UINT32 channel_count;
A_UINT32 schedule_size;
A_UINT32 flags;
+ /**
+ * Max duration of continuing multichannel operation without
+ * receiving a TA frame (units = seconds)
+ */
+ A_UINT32 ta_max_duration;
/** This is followed by a TLV array of wmi_channel. */
/** This is followed by a TLV array of wmi_ocb_channel. */
@@ -13457,6 +15189,22 @@
A_UINT32 qtimer_high;
} wmi_vdev_tsf_report_event_fixed_param;
+/**
+ * ie_id values:
+ * 0 to 255 are used for individual IEEE802.11 Information Element types
+ */
+#define WMI_SET_VDEV_IE_ID_SCAN_SET_DEFAULT_IE 256
+
+/* source values: */
+#define WMI_SET_VDEV_IE_SOURCE_HOST 0x0
+
+/* band values: */
+typedef enum {
+ WMI_SET_VDEV_IE_BAND_ALL = 0,
+ WMI_SET_VDEV_IE_BAND_2_4GHZ,
+ WMI_SET_VDEV_IE_BAND_5GHZ,
+} wmi_set_vdev_ie_band;
+
typedef struct {
/** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_vdev_set_ie_cmd_fixed_param */
A_UINT32 tlv_header;
@@ -13465,11 +15213,86 @@
/** unique id to identify the ie_data as defined by ieee 802.11 spec */
A_UINT32 ie_id; /** ie_len corresponds to num of bytes in ie_data[] */
A_UINT32 ie_len;
+ /** source of this command */
+ A_UINT32 ie_source; /* see WMI_SET_VDEV_IE_SOURCE_ defs */
+ /** band for this IE - se wmi_set_vdev_ie_band enum */
+ A_UINT32 band;
/**
* Following this structure is the TLV byte stream of ie data of length ie_buf_len:
* A_UINT8 ie_data[]; */
} wmi_vdev_set_ie_cmd_fixed_param;
+/* DISA feature related data structures */
+#define MAX_MAC_HEADER_LEN 32
+typedef enum {
+ WMI_ENCRYPT_DECRYPT_FLAG_INVALID,
+ WMI_ENCRYPT = 1,
+ WMI_DECRYPT = 2,
+} WMI_ENCRYPT_DECRYPT_FLAG;
+
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_vdev_encrypt_decrypt_data_req_cmd_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /** unique id identifying the VDEV, generated by the caller */
+ A_UINT32 vdev_id;
+ A_UINT32 key_flag; /* WMI_ENCRYPT_DECRYPT_FLAG */
+ A_UINT32 key_idx;
+ A_UINT32 key_cipher;
+ A_UINT32 key_len; /* units = bytes */
+ A_UINT32 key_txmic_len; /* units = bytes */
+ A_UINT32 key_rxmic_len; /* units = bytes */
+ /** Key: This array needs to be provided in little-endian order */
+ A_UINT8 key_data[WMI_MAX_KEY_LEN];
+ /**
+ * Packet number: This array needs to be provided in little-endian
+ * order.
+ * If the PN is less than 8 bytes, the PN data shall be placed into this
+ * pn[] array starting at byte 0, leaving the MSBs empty.
+ */
+ A_UINT8 pn[8];
+ /**
+ * 802.11 MAC header to be typecast to struct ieee80211_qosframe_addr4
+ * This array needs to be provided in little-endian order.
+ */
+ A_UINT8 mac_hdr[MAX_MAC_HEADER_LEN];
+ A_UINT32 data_len; /** Payload length, units = bytes */
+ /**
+ * Following this struct are this TLV:
+ * A_UINT8 data[]; <-- actual data to be encrypted,
+ * needs to be provided in little-endian order
+ */
+} wmi_vdev_encrypt_decrypt_data_req_cmd_fixed_param;
+
+/**
+ * This event is generated in response to
+ * WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID from HOST.
+ * On receiving WMI command WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID from
+ * HOST with DISA test vectors, DISA frame will prepared and submitted to HW,
+ * then on receiving the tx completion for the DISA frame this WMI event
+ * will be delivered to HOST with the encrypted frame.
+ */
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_vdev_encrypt_decrypt_data_resp_event_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /* VDEV identifier */
+ A_UINT32 vdev_id;
+ A_INT32 status; /* 0: success, -1: Failure, */
+ /* 802.11 header length + encrypted payload length (units = bytes) */
+ A_UINT32 data_length;
+ /**
+ * Following this struct is this TLV:
+ * A_UINT8 enc80211_frame[]; <-- Encrypted 802.11 frame;
+ * 802.11 header + encrypted payload,
+ * provided in little-endian order
+ */
+} wmi_vdev_encrypt_decrypt_data_resp_event_fixed_param;
+
/* DEPRECATED - use wmi_pdev_set_pcl_cmd_fixed_param instead */
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_soc_set_pcl_cmd_fixed_param */
@@ -13484,6 +15307,15 @@
**/
} wmi_soc_set_pcl_cmd_fixed_param;
+/* Values for channel_weight */
+typedef enum {
+ WMI_PCL_WEIGHT_DISALLOW = 0,
+ WMI_PCL_WEIGHT_LOW = 1,
+ WMI_PCL_WEIGHT_MEDIUM = 2,
+ WMI_PCL_WEIGHT_HIGH = 3,
+ WMI_PCL_WEIGHT_VERY_HIGH = 4,
+} wmi_pcl_chan_weight;
+
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_pdev_set_pcl_cmd_fixed_param */
/** Set Preferred Channel List **/
@@ -13512,6 +15344,19 @@
} wmi_soc_set_hw_mode_cmd_fixed_param;
typedef struct {
+ /* TLV tag and len tag equals WMITLV_TAG_STRUC_wmi_pdev_band_to_mac */
+ A_UINT32 tlv_header;
+ /** pdev_id for identifying the MACC
+ * See macros starting with WMI_PDEV_ID_ for values..
+ */
+ A_UINT32 pdev_id;
+ /* start frequency in MHz */
+ A_UINT32 start_freq;
+ /* end frequency in MHz */
+ A_UINT32 end_freq;
+} wmi_pdev_band_to_mac;
+
+typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_pdev_set_hw_mode_cmd_fixed_param */
/** Set Hardware Mode **/
@@ -13522,6 +15367,13 @@
/* Hardware Mode Index */
A_UINT32 hw_mode_index;
+ /* Number of band to mac TLVs */
+
+ A_UINT32 num_band_to_mac;
+
+ /* Followed by TLVs of typee
+ * num_band_to_mac * wmi_pdev_band_to_mac.
+ */
} wmi_pdev_set_hw_mode_cmd_fixed_param;
/* DEPRECATED - use wmi_pdev_set_dual_mac_config_cmd_fixed_param instead */
@@ -13731,6 +15583,55 @@
*/
} wmi_pdev_hw_mode_transition_event_fixed_param;
+/**
+ * This command is sent from WLAN host driver to firmware for
+ * plugging in reorder queue desc to lithium hw.
+ *
+ * Example: plug-in queue desc for TID 5
+ * host->target: WMI_PEER_REORDER_QUEUE_SETUP_CMDID,
+ * (vdev_id = PEER vdev id,
+ * peer_macaddr = PEER mac addr,
+ * tid = 5,
+ * queue_ptr_lo = queue desc addr lower 32 bits,
+ * queue_ptr_hi = queue desc addr higher 32 bits,
+ * queue_no = 16-bit number assigned by host for queue,
+ * stored in bits 15:0 of queue_no field)
+ */
+typedef struct {
+ /* TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_peer_reorder_queue_setup_cmd_fixed_param
+ */
+ A_UINT32 tlv_header;
+ A_UINT32 vdev_id;
+ wmi_mac_addr peer_macaddr; /* peer mac address */
+ A_UINT32 tid; /* 0 to 15 = QoS TIDs, 16 = non-qos TID */
+ A_UINT32 queue_ptr_lo; /* lower 32 bits of queue desc adddress */
+ A_UINT32 queue_ptr_hi; /* upper 32 bits of queue desc adddress */
+ A_UINT32 queue_no; /* 16-bit number assigned by host for queue,
+ stored in bits 15:0 of queue_no field */
+} wmi_peer_reorder_queue_setup_cmd_fixed_param;
+
+/**
+ * This command is sent from WLAN host driver to firmware for
+ * removing one or more reorder queue desc to lithium hw.
+ *
+ * Example: remove queue desc for all TIDs
+ * host->target: WMI_PEER_REORDER_REMOVE_CMDID,
+ * (vdev_id = PEER vdev id,
+ * peer_macaddr = PEER mac addr,
+ * tid = 0x1FFFF,
+ */
+typedef struct {
+ /* TLV tag and len;
+ * tag equals WMITLV_TAG_STRUC_wmi_peer_reorder_queue_remove_cmd_fixed_param
+ */
+ A_UINT32 tlv_header;
+ A_UINT32 vdev_id;
+ wmi_mac_addr peer_macaddr; /* peer mac address */
+ A_UINT32 tid_mask; /* bits 0 to 15 = QoS TIDs, bit 16 = non-qos TID */
+} wmi_peer_reorder_queue_remove_cmd_fixed_param;
+
+
/* DEPRECATED - use wmi_pdev_set_mac_config_response_event_fixed_param instead */
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_soc_set_dual_mac_config_response_event_fixed_param */
@@ -13798,7 +15699,7 @@
* 1 - Allow to connect to MBO AP only
* Bit 1-31 : reserved.
*/
-#define WMI_ROAM_MBO_FLAG_MBO_ONLY_MODE (1<<0)
+#define WMI_ROAM_MBO_FLAG_MBO_ONLY_MODE (1<<0) /* DEPRECATED */
typedef struct {
/* TLV tag and len; tag equals
@@ -13810,7 +15711,7 @@
A_UINT32 enable;
/** MBO flags, refer to definition of MBO flags*/
A_UINT32 flags;
-} wmi_roam_set_mbo_fixed_param;
+} wmi_roam_set_mbo_fixed_param; /* DEPRECATED */
typedef struct {
/* TLV tag and len; tag equals
@@ -13869,6 +15770,41 @@
} wmi_extscan_configure_mawc_cmd_fixed_param;
typedef struct {
+ /*
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_roam_per_config_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /* Unique id identifying the VDEV */
+ A_UINT32 vdev_id;
+ /* enable(1) or disable(0) packet error rate trigger for roaming */
+ A_UINT32 enable;
+ /* high_rate_thresh, low_rate_thresh, pkt_err_rate_thresh_pct:
+ * If PER monitoring as a trigger for roaming is enabled,
+ * it is controlled by high_rate_thresh, low_rate_thresh, and
+ * pkt_err_rate_thresh_pct.
+ * PER monitoring is performed only when the time-averaged throughput
+ * is less than high_rate_thresh.
+ * During PER monitoring, the target keeps track of the PHY rate for
+ * each of the first N PPDUs within a time window.
+ * If the number of PPDUs with PHY rate < low_rate_thresh exceeds
+ * N * pkt_err_rate_thresh_pct / 100, roaming will be triggered.
+ *
+ * This PER monitoring as a trigger for roaming is performed
+ * concurrently but independently for rx and tx.
+ */
+ A_UINT32 high_rate_thresh; /* units = Kbps */
+ A_UINT32 low_rate_thresh; /* units = Kbps */
+ A_UINT32 pkt_err_rate_thresh_pct;
+ /*
+ * rest time after associating to a new AP before
+ * starting to monitor PER as a roaming trigger,
+ * (units are seconds)
+ */
+ A_UINT32 per_rest_time;
+} wmi_roam_per_config_fixed_param;
+
+typedef struct {
/* TLV tag and len; tag equals
* WMITLV_TAG_STRUC_wmi_nlo_configure_mawc_cmd_fixed_param */
A_UINT32 tlv_header;
@@ -14153,10 +16089,6 @@
A_UINT32 toeplitz_hash_ipv6_40;
} wmi_lro_info_cmd_fixed_param;
-/*
- * This structure is used to set the pattern for WOW host wakeup pin pulse
- * pattern confirguration.
- */
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_transfer_data_to_flash_cmd_fixed_param */
A_UINT32 offset; /* flash offset to write, starting from 0 */
@@ -14169,6 +16101,25 @@
A_UINT32 status;
} wmi_transfer_data_to_flash_complete_event_fixed_param;
+typedef struct {
+ /* TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_read_data_from_flash_cmd_fixed_param
+ */
+ A_UINT32 tlv_header;
+ A_UINT32 offset; /* flash offset to read, starting from 0 */
+ A_UINT32 length; /* data length to read, unit: byte */
+} wmi_read_data_from_flash_cmd_fixed_param;
+
+typedef struct {
+ /* TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_read_data_from_flash_event_fixed_param
+ */
+ A_UINT32 tlv_header;
+ A_UINT32 status; /* Return status. 0 for success, non-zero otherwise */
+ A_UINT32 offset; /* flash offset reading from, starting from 0 */
+ A_UINT32 length; /* length of data being reported, unit: byte */
+} wmi_read_data_from_flash_event_fixed_param;
+
typedef enum {
ENHANCED_MCAST_FILTER_DISABLED,
ENHANCED_MCAST_FILTER_ENABLED
@@ -14221,6 +16172,12 @@
*/
} wmi_scpc_event_fixed_param;
+typedef enum {
+FW_ACTIVE_BPF_MODE_DISABLE = (1 << 1),
+FW_ACTIVE_BPF_MODE_FORCE_ENABLE = (1 << 2),
+FW_ACTIVE_BPF_MODE_ADAPTIVE_ENABLE = (1 << 3),
+} FW_ACTIVE_BPF_MODE;
+
/* bpf interface structure */
typedef struct wmi_bpf_get_capability_cmd_s {
A_UINT32 tlv_header;
@@ -14232,6 +16189,8 @@
A_UINT32 bpf_version; /* fw's implement version */
A_UINT32 max_bpf_filters; /* max filters that fw supports */
A_UINT32 max_bytes_for_bpf_inst; /* the maximum bytes that can be used as bpf instructions */
+ A_UINT32 fw_active_bpf_support_mcbc_modes; /* multicast/broadcast - refer to FW_ACTIVE_BPF_MODE, it can be 'or' of them */
+ A_UINT32 fw_active_bpf_support_uc_modes; /* unicast - refer to FW_ACTIVE_BPF_MODE, it can be 'or' of them */
} wmi_bpf_capability_info_evt_fixed_param;
/* bit 0 of flags: report counters */
@@ -14272,6 +16231,13 @@
A_UINT32 filter_id; /* BPF_FILTER_ID_ALL means delete all */
} wmi_bpf_del_vdev_instructions_cmd_fixed_param;
+typedef struct wmi_bpf_set_vdev_active_mode_cmd_s {
+ A_UINT32 tlv_header;
+ A_UINT32 vdev_id;
+ A_UINT32 mcbc_mode; /* refer to FW_ACTIVE_BPF_MODE */
+ A_UINT32 uc_mode; /* refer to FW_ACTIVE_BPF_MODE */
+} wmi_bpf_set_vdev_active_mode_cmd_fixed_param;
+
#define AES_BLOCK_LEN 16 /* in bytes */
#define FIPS_KEY_LENGTH_128 16 /* in bytes */
#define FIPS_KEY_LENGTH_256 32 /* in bytes */
@@ -14481,6 +16447,9 @@
#define WMI_ATF_DENOMINATION 1000 /* Expressed in 1 part in 1000 (permille) */
+#define WMI_ATF_SSID_FAIR_SCHED 0 /** Fair ATF scheduling for vdev */
+#define WMI_ATF_SSID_STRICT_SCHED 1 /** Strict ATF scheduling for vdev */
+
typedef struct {
/** TLV tag and len; tag equals
* WMITLV_TAG_STRUC_wmi_atf_peer_info */
@@ -14708,6 +16677,16 @@
} wmi_pdev_get_tpc_cmd_fixed_param;
typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals
+ WMITLV_TAG_STRUC_wmi_pdev_get_chip_power_stats_cmd_fixed_param */
+ /**
+ * pdev_id for identifying the MAC See macros
+ * starting with WMI_PDEV_ID_ for values.
+ */
+ A_UINT32 pdev_id;
+} wmi_pdev_get_chip_power_stats_cmd_fixed_param;
+
+typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_pdev_tpc_event_fixed_param */
A_UINT32 reserved0; /* for future need */
/*
@@ -14750,17 +16729,73 @@
A_UINT32 cck_level;
} wmi_ani_cck_event_fixed_param;
+typedef enum wmi_power_debug_reg_fmt_type {
+ /* WMI_POWER_DEBUG_REG_FMT_TYPE_ROME -> Dumps following 12 Registers
+ * SOC_SYSTEM_SLEEP
+ * WLAN_SYSTEM_SLEEP
+ * RTC_SYNC_FORCE_WAKE
+ * MAC_DMA_ISR
+ * MAC_DMA_TXRX_ISR
+ * MAC_DMA_ISR_S1
+ * MAC_DMA_ISR_S2
+ * MAC_DMA_ISR_S3
+ * MAC_DMA_ISR_S4
+ * MAC_DMA_ISR_S5
+ * MAC_DMA_ISR_S6
+ * MAC_DMA_ISR_S7
+ */
+ WMI_POWER_DEBUG_REG_FMT_TYPE_ROME,
+ WMI_POWER_DEBUG_REG_FMT_TYPE_MAX = 0xf,
+} WMI_POWER_DEBUG_REG_FMT_TYPE;
+
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals
+ WMITLV_TAG_STRUC_wmi_chip_power_stats_event_fixed_param */
+ A_UINT32 cumulative_sleep_time_ms; /* maximum range is 35 hours, due to conversion from internal 0.03215 ms units to ms */
+ A_UINT32 cumulative_total_on_time_ms; /* maximum range is 35 hours, due to conversion from internal 0.03215 ms units to ms */
+ A_UINT32 deep_sleep_enter_counter; /* count of number of times chip enterred deep sleep */
+ A_UINT32 last_deep_sleep_enter_tstamp_ms; /* Last Timestamp when Chip went to deep sleep */
+ A_UINT32 debug_register_fmt; /* WMI_POWER_DEBUG_REG_FMT_TYPE enum, describes debug registers being dumped as part of the event */
+ A_UINT32 num_debug_register; /* number of debug registers being sent to host */
+ /*
+ * Following this structure is the TLV:
+ * A_UINT32 debug_registers[num_debug_registers];
+ */
+} wmi_pdev_chip_power_stats_event_fixed_param;
+
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_ani_ofdm_event_fixed_param */
A_UINT32 ofdm_level;
} wmi_ani_ofdm_event_fixed_param;
typedef enum wmi_coex_config_type {
- WMI_COEX_CONFIG_PAGE_P2P_TDM = 1, /* config interval (arg1 BT, arg2 WLAN) for P2P + PAGE */
- WMI_COEX_CONFIG_PAGE_STA_TDM = 2, /* config interval (arg1 BT, arg2 WLAN) for STA + PAGE */
- WMI_COEX_CONFIG_PAGE_SAP_TDM = 3, /* config interval (arg1 BT, arg2 WLAN) for SAP + PAGE */
- WMI_COEX_CONFIG_DURING_WLAN_CONN = 4, /* config during WLAN connection */
- WMI_COEX_CONFIG_BTC_ENABLE = 5, /* config to enable/disable BTC */
+ /* config interval (arg1 BT, arg2 WLAN) for P2P + PAGE */
+ WMI_COEX_CONFIG_PAGE_P2P_TDM = 1,
+ /* config interval (arg1 BT, arg2 WLAN) for STA + PAGE */
+ WMI_COEX_CONFIG_PAGE_STA_TDM = 2,
+ /* config interval (arg1 BT, arg2 WLAN) for SAP + PAGE */
+ WMI_COEX_CONFIG_PAGE_SAP_TDM = 3,
+ /* config during WLAN connection */
+ WMI_COEX_CONFIG_DURING_WLAN_CONN = 4,
+ /* config to enable/disable BTC */
+ WMI_COEX_CONFIG_BTC_ENABLE = 5,
+ /* config of COEX debug setting */
+ WMI_COEX_CONFIG_COEX_DBG = 6,
+ /* config interval (ms units) (arg1 BT, arg2 WLAN) for P2P + STA + PAGE */
+ WMI_COEX_CONFIG_PAGE_P2P_STA_TDM = 7,
+ /* config interval (ms units) (arg1 BT, arg2 WLAN) for P2P + INQUIRY */
+ WMI_COEX_CONFIG_INQUIRY_P2P_TDM = 8,
+ /* config interval (ms units) (arg1 BT, arg2 WLAN) for STA + INQUIRY */
+ WMI_COEX_CONFIG_INQUIRY_STA_TDM = 9,
+ /* config interval (ms units) (arg1 BT, arg2 WLAN) for SAP + INQUIRY */
+ WMI_COEX_CONFIG_INQUIRY_SAP_TDM = 10,
+ /*
+ * config interval (ms units) (arg1 BT, arg2 WLAN) for P2P + STA +
+ * INQUIRY
+ */
+ WMI_COEX_CONFIG_INQUIRY_P2P_STA_TDM = 11,
+ /* config wlan total tx power when bt coex (arg1 is wlan_tx_power_limit, in 0.5dbm units) */
+ WMI_COEX_CONFIG_TX_POWER = 12,
} WMI_COEX_CONFIG_TYPE;
typedef struct {
@@ -14816,11 +16851,172 @@
**/
} wmi_pdev_wal_power_debug_cmd_fixed_param;
+typedef struct {
+ /**
+ * TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_pdev_set_reorder_timeout_val_cmd_fixed_param
+ */
+ A_UINT32 tlv_header;
+ /**
+ * @brief rx_timeout_pri - what rx reorder timeout (ms) to use for the AC
+ * @details
+ * Each WMM access category (voice, video, best-effort, background)
+ * will have its own timeout value to dictate how long to wait for
+ * missing rx MPDUs to arrive before releasing subsequent MPDUs that
+ * have already been received.
+ * This parameter specifies the timeout in milliseconds for each
+ * access category.
+ * The array elements are indexed by the WMI_AC enum to identify
+ * which array element corresponds to which AC / traffic type.
+ */
+ A_UINT32 rx_timeout_pri[WMI_AC_MAX];
+} wmi_pdev_set_reorder_timeout_val_cmd_fixed_param;
+
+/**
+ * wlan stats shall be understood as per period.
+ * Generally, it is reported periodically based on the period specified by host.
+ * But if the variation of one stats of compared to the
+ * pervious notification exceeds a threshold,
+ * FW will report the wlan stats immediately.
+ * The values of the stats becomes the new reference to compute variations.
+ * This threshold can be a global setting or per category.
+ * Host can enable/disable the mechanism for any stats per bitmap.
+ * TX/RX thresholds (percentage value) are shared across ACs,
+ * and TX/RX stats comprisons are processed per AC of each peer.
+ * For example, if bit 0 (stand for tx_mpdus) of tx_thresh_bitmap is set to 1,
+ * and the detailed tx_mpdus threshold value is set to 10%,
+ * suppose tx_mpdus value of BE of peer 0 is 100 in first period,
+ * and it reaches 110 during the second period,
+ * FW will generate and send out a wlan stats event immediately.
+ */
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_pdev_set_stats_threshold_cmd_fixed_param */
+ /** Indicate if threshold mechnism is enabled or disabled.
+ * It is disabled by default.
+ * Host can enable and disable it dynamically.
+ */
+ A_UINT32 enable_thresh;
+ /** use_thresh_bitmap equals 0 means gbl_thresh is used.
+ * when use_thresh_bitmap equals 1, ignore gbl_thresh and use stats bitmap indicated thresholds.
+ */
+ A_UINT32 use_thresh_bitmap;
+ /** global threshold, valid when use_thresh_bitmap equals 0.
+ * It takes effect for all counters.
+ * If use_thresh_bitmap ==0 && gbl_thresh == 0, disable threshold mechanism.
+ */
+ A_UINT32 gbl_thresh;
+ /** Enable/disable bitmap for threshold mechanism of CCA stats */
+ A_UINT32 cca_thresh_enable_bitmap;
+ /** Enable/disable bitmap for threshold mechanism of signal stats */
+ A_UINT32 signal_thresh_enable_bitmap;
+ /** Enable/disable bitmap for threshold mechanism of TX stats */
+ A_UINT32 tx_thresh_enable_bitmap;
+ /** Enable/disable bitmap for threshold mechanism of RX stats */
+ A_UINT32 rx_thresh_enable_bitmap;
+ /* This TLV is followed by TLVs below:
+ * wmi_chan_cca_stats_thresh cca_thresh;
+ * wmi_peer_signal_stats_thresh signal_thresh;
+ * wmi_tx_stats_thresh tx_thresh;
+ * wmi_rx_stats_thresh rx_thresh;
+ */
+} wmi_pdev_set_stats_threshold_cmd_fixed_param;
+
+typedef enum {
+ WMI_REQUEST_WLAN_TX_STAT = 0x01,
+ WMI_REQUEST_WLAN_RX_STAT = 0x02,
+ WMI_REQUEST_WLAN_CCA_STAT = 0x04,
+ WMI_REQUEST_WLAN_SIGNAL_STAT = 0x08,
+} wmi_wlan_stats_id;
+
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_request_wlan_stats_cmd_fixed_param */
+ wmi_wlan_stats_id stats_id;
+} wmi_request_wlan_stats_cmd_fixed_param;
+
+typedef enum {
+ WMI_REQUEST_ONE_PEER_STATS_INFO = 0x01, /* request stats of one specified peer */
+ WMI_REQUEST_VDEV_ALL_PEER_STATS_INFO = 0x02, /* request stats of all peers belong to specified VDEV */
+} wmi_peer_stats_info_request_type;
+
+/** It is required to issue WMI_PDEV_PARAM_PEER_STATS_INFO_ENABLE
+* (with a non-zero value) before issuing the first REQUEST_PEER_STATS_INFO.
+*/
+typedef struct {
+ /** TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_request_peer_stats_info_cmd_fixed_param */
+ A_UINT32 tlv_header;
+ /** request_type to indicate if only stats of
+ * one peer or all peers of the VDEV are requested,
+ * see wmi_peer_stats_info_request_type.
+ */
+ A_UINT32 request_type;
+ /** VDEV identifier */
+ A_UINT32 vdev_id;
+ /** this peer_macaddr is only used if request_type == ONE_PEER_STATS_INFO */
+ wmi_mac_addr peer_macaddr;
+ /** flag to indicate if FW needs to reset requested peers stats */
+ A_UINT32 reset_after_request;
+} wmi_request_peer_stats_info_cmd_fixed_param;
+
+typedef enum {
+ WMI_REQUEST_ONE_RADIO_CHAN_STATS = 0x01, /* request stats of one specified channel */
+ WMI_REQUEST_ALL_RADIO_CHAN_STATS = 0x02, /* request stats of all channels */
+} wmi_radio_chan_stats_request_type;
+
+/** It is required to issue WMI_PDEV_PARAM_RADIO_CHAN_STATS_ENABLE
+ * (with a non-zero value) before issuing the first REQUEST_RADIO_CHAN_STATS.
+ */
+typedef struct {
+ /** TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_request_radio_chan_stats_cmd_fixed_param */
+ A_UINT32 tlv_header;
+ /** request_type to indicate if only stats of
+ * one channel or all channels are requested,
+ * see wmi_radio_chan_stats_request_type.
+ */
+ A_UINT32 request_type;
+ /** Frequency of channel whose stats is requested,
+ * only used when request_type == WMI_REQUEST_ONE_RADIO_CHAN_STATS
+ */
+ A_UINT32 chan_mhz;
+ /** flag to indicate if FW needs to reset requested stats of specified channel/channels */
+ A_UINT32 reset_after_request;
+} wmi_request_radio_chan_stats_cmd_fixed_param;
+
typedef enum {
WLAN_2G_CAPABILITY = 0x1,
WLAN_5G_CAPABILITY = 0x2,
} WLAN_BAND_CAPABILITY;
+typedef enum wmi_hw_mode_config_type {
+ /* Only one PHY is active. */
+ WMI_HW_MODE_SINGLE = 0,
+ /**
+ * Both PHYs are active in different bands, one in 2G
+ * and another in 5G.
+ */
+ WMI_HW_MODE_DBS = 1,
+ /**
+ * Both PHYs are in passive mode (only rx) in same band;
+ * no tx allowed.
+ */
+ WMI_HW_MODE_SBS_PASSIVE = 2,
+ /**
+ * Both PHYs are active in the same band.
+ * Support for both PHYs within one band is planned for 5G only
+ * (as indicated in WMI_MAC_PHY_CAPABILITIES),
+ * but could be extended to other bands in the future.
+ * The separation of the band between the two PHYs needs to be
+ * communicated separately.
+ */
+ WMI_HW_MODE_SBS = 3,
+ /**
+ * 3 PHYs, with 2 on the same band doing SBS
+ * as in WMI_HW_MODE_SBS, and 3rd on the other band
+ */
+ WMI_HW_MODE_DBS_SBS = 4,
+} WMI_HW_MODE_CONFIG_TYPE;
+
#define WMI_SUPPORT_11B_GET(flags) WMI_GET_BITS(flags, 0, 1)
#define WMI_SUPPORT_11B_SET(flags, value) WMI_SET_BITS(flags, 0, 1, value)
@@ -14839,6 +17035,9 @@
#define WMI_SUPPORT_11AX_GET(flags) WMI_GET_BITS(flags, 5, 1)
#define WMI_SUPPORT_11AX_SET(flags, value) WMI_SET_BITS(flags, 5, 1, value)
+#define WMI_MAX_MUBFEE_GET(flags) WMI_GET_BITS(flags, 28, 4)
+#define WMI_MAX_MUBFEE_SET(flags, value) WMI_SET_BITS(flags, 28, 4, value)
+
typedef struct {
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_WMI_MAC_PHY_CAPABILITIES */
/* hw_mode_id - identify a particular set of HW characteristics, as specified
@@ -14851,7 +17050,7 @@
A_UINT32 pdev_id;
/* phy id. Starts with 0 */
A_UINT32 phy_id;
- /* supported modulations */
+ /* supported modulations and number of MU beamformees */
union {
struct {
A_UINT32 supports_11b:1,
@@ -14859,7 +17058,12 @@
supports_11a:1,
supports_11n:1,
supports_11ac:1,
- supports_11ax:1;
+ supports_11ax:1,
+
+ unused:22,
+
+ /* max MU beamformees supported per MAC */
+ max_mubfee:4;
};
A_UINT32 supported_flags;
};
@@ -14910,6 +17114,11 @@
A_UINT32 tx_chain_mask_5G;
/* Valid Receive chain mask */
A_UINT32 rx_chain_mask_5G;
+ /* HE capability phy field of 802.11ax, WMI_HE_CAP defines */
+ A_UINT32 he_cap_phy_info_2G[WMI_MAX_HECAP_PHY_SIZE];
+ A_UINT32 he_cap_phy_info_5G[WMI_MAX_HECAP_PHY_SIZE];
+ wmi_ppe_threshold he_ppet2G;
+ wmi_ppe_threshold he_ppet5G;
} WMI_MAC_PHY_CAPABILITIES;
typedef struct {
@@ -14917,12 +17126,21 @@
/* hw_mode_id - identify a particular set of HW characteristics,
* as specified by the subsequent fields */
A_UINT32 hw_mode_id;
- /* BIT0 represents phy_id 0, BIT1 represent phy_id 1 and so on */
+ /**
+ * BIT0 represents phy_id 0, BIT1 represent phy_id 1 and so on.
+ * Number of bits set in phy_id_map represents number of
+ * WMI_MAC_PHY_CAPABILITIES TLV's, one for each active PHY for current HW
+ * mode identified by hw_mode_id. For example, for DBS/SBS mode there will
+ * be 2 WMI_MAC_PHY_CAPABILITIES TLVs and for single MAC modes it will be
+ * 1 WMI_MAC_PHY_CAPABILITIES TLVs
+ */
A_UINT32 phy_id_map;
- /* number of bits set in phy_id_map represents number of WMI_MAC_PHY_CAPABILITIES TLV's
- * one for each active PHY for current HW mode identified by hw_mode_id. For example for
- * DBS/SBS mode there will be 2 WMI_MAC_PHY_CAPABILITIES TLVs and for single MAC modes it
- * will be 1 WMI_MAC_PHY_CAPABILITIES TLVs */
+ /**
+ * hw_mode_config_type
+ * Identify a particular type of HW mode such as SBS, DBS etc.
+ * Refer to WMI_HW_MODE_CONFIG_TYPE values.
+ */
+ A_UINT32 hw_mode_config_type;
} WMI_HW_MODE_CAPABILITIES;
typedef struct {
@@ -14975,13 +17193,816 @@
A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_scan_adaptive_dwell_config_fixed_param */
/* globally enable/disable adaptive dwell */
A_UINT32 enable;
-/**
- * followed by TLV (tag length value) parameters array
- * The TLV's are:
- * wmi_scan_adaptive_dwell_parameters_tlv param[]; (0 or 1 elements)
- */
+ /**
+ * pdev_id for identifying the MAC. See macros starting with
+ * WMI_PDEV_ID_ for values. In non-DBDC case host should set it to 0
+ */
+ A_UINT32 pdev_id;
+ /**
+ * followed by TLV (tag length value) parameters array
+ * The TLV's are:
+ * wmi_scan_adaptive_dwell_parameters_tlv param[]; (0 or 1 elements)
+ */
} wmi_scan_adaptive_dwell_config_fixed_param;
+typedef enum {
+ WMI_REG_EXT_FCC_MIDBAND = 0,
+ WMI_REG_EXT_JAPAN_MIDBAND = 1,
+ WMI_REG_EXT_FCC_DFS_HT40 = 2,
+ WMI_REG_EXT_JAPAN_NONDFS_HT40 = 3,
+ WMI_REG_EXT_JAPAN_DFS_HT40 = 4,
+ WMI_REG_EXT_FCC_CH_144 = 5,
+} WMI_REG_EXT_BITMAP;
+
+#ifdef WMI_CMD_STRINGS
+static INLINE A_UINT8 *wmi_id_to_name(A_UINT32 wmi_command)
+{
+ switch (wmi_command) {
+ /* initialize the wlan sub system */
+ WMI_RETURN_STRING(WMI_INIT_CMDID);
+
+ /* Scan specific commands */
+
+ /* start scan request to FW */
+ WMI_RETURN_STRING(WMI_START_SCAN_CMDID);
+ /* stop scan request to FW */
+ WMI_RETURN_STRING(WMI_STOP_SCAN_CMDID);
+ /* full list of channels as defined by the regulatory
+ * that will be used by scanner */
+ WMI_RETURN_STRING(WMI_SCAN_CHAN_LIST_CMDID);
+ /* overwrite default priority table in scan scheduler */
+ WMI_RETURN_STRING(WMI_SCAN_SCH_PRIO_TBL_CMDID);
+ /* This command to adjust the priority and min.max_rest_time
+ * of an on ongoing scan request.
+ */
+ WMI_RETURN_STRING(WMI_SCAN_UPDATE_REQUEST_CMDID);
+
+ /* PDEV(physical device) specific commands */
+ /* set regulatorty ctl id used by FW to determine the exact
+ * ctl power limits */
+ WMI_RETURN_STRING(WMI_PDEV_SET_REGDOMAIN_CMDID);
+ /* set channel. mainly used for supporting monitor mode */
+ WMI_RETURN_STRING(WMI_PDEV_SET_CHANNEL_CMDID);
+ /* set pdev specific parameters */
+ WMI_RETURN_STRING(WMI_PDEV_SET_PARAM_CMDID);
+ /* enable packet log */
+ WMI_RETURN_STRING(WMI_PDEV_PKTLOG_ENABLE_CMDID);
+ /* disable packet log*/
+ WMI_RETURN_STRING(WMI_PDEV_PKTLOG_DISABLE_CMDID);
+ /* set wmm parameters */
+ WMI_RETURN_STRING(WMI_PDEV_SET_WMM_PARAMS_CMDID);
+ /* set HT cap ie that needs to be carried probe requests
+ * HT/VHT channels */
+ WMI_RETURN_STRING(WMI_PDEV_SET_HT_CAP_IE_CMDID);
+ /* set VHT cap ie that needs to be carried on probe
+ * requests on VHT channels */
+ WMI_RETURN_STRING(WMI_PDEV_SET_VHT_CAP_IE_CMDID);
+
+ /* Command to send the DSCP-to-TID map to the target */
+ WMI_RETURN_STRING(WMI_PDEV_SET_DSCP_TID_MAP_CMDID);
+ /* set quiet ie parameters. primarily used in AP mode */
+ WMI_RETURN_STRING(WMI_PDEV_SET_QUIET_MODE_CMDID);
+ /* Enable/Disable Green AP Power Save */
+ WMI_RETURN_STRING(WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID);
+ /* get TPC config for the current operating channel */
+ WMI_RETURN_STRING(WMI_PDEV_GET_TPC_CONFIG_CMDID);
+
+ /* set the base MAC address for the physical device before
+ * a VDEV is created. For firmware that does not support
+ * this feature and this command, the pdev MAC address will
+ * not be changed. */
+ WMI_RETURN_STRING(WMI_PDEV_SET_BASE_MACADDR_CMDID);
+
+ /* eeprom content dump , the same to bdboard data */
+ WMI_RETURN_STRING(WMI_PDEV_DUMP_CMDID);
+
+ /* VDEV(virtual device) specific commands */
+ /* vdev create */
+ WMI_RETURN_STRING(WMI_VDEV_CREATE_CMDID);
+ /* vdev delete */
+ WMI_RETURN_STRING(WMI_VDEV_DELETE_CMDID);
+ /* vdev start request */
+ WMI_RETURN_STRING(WMI_VDEV_START_REQUEST_CMDID);
+ /* vdev restart request (RX only, NO TX, used for CAC period)*/
+ WMI_RETURN_STRING(WMI_VDEV_RESTART_REQUEST_CMDID);
+ /* vdev up request */
+ WMI_RETURN_STRING(WMI_VDEV_UP_CMDID);
+ /* vdev stop request */
+ WMI_RETURN_STRING(WMI_VDEV_STOP_CMDID);
+ /* vdev down request */
+ WMI_RETURN_STRING(WMI_VDEV_DOWN_CMDID);
+ /* set a vdev param */
+ WMI_RETURN_STRING(WMI_VDEV_SET_PARAM_CMDID);
+ /* set a key (used for setting per peer unicast
+ * and per vdev multicast) */
+ WMI_RETURN_STRING(WMI_VDEV_INSTALL_KEY_CMDID);
+
+ /* wnm sleep mode command */
+ WMI_RETURN_STRING(WMI_VDEV_WNM_SLEEPMODE_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_WMM_ADDTS_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_WMM_DELTS_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_SET_WMM_PARAMS_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_SET_GTX_PARAMS_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMDID);
+
+ WMI_RETURN_STRING(WMI_VDEV_PLMREQ_START_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_PLMREQ_STOP_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_TSF_TSTAMP_ACTION_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_SET_IE_CMDID);
+
+ /* peer specific commands */
+
+ /** create a peer */
+ WMI_RETURN_STRING(WMI_PEER_CREATE_CMDID);
+ /** delete a peer */
+ WMI_RETURN_STRING(WMI_PEER_DELETE_CMDID);
+ /** flush specific tid queues of a peer */
+ WMI_RETURN_STRING(WMI_PEER_FLUSH_TIDS_CMDID);
+ /** set a parameter of a peer */
+ WMI_RETURN_STRING(WMI_PEER_SET_PARAM_CMDID);
+ /* set peer to associated state. will cary all parameters
+ * determined during assocication time */
+ WMI_RETURN_STRING(WMI_PEER_ASSOC_CMDID);
+ /* add a wds (4 address ) entry. used only for testing
+ * WDS feature on AP products */
+ WMI_RETURN_STRING(WMI_PEER_ADD_WDS_ENTRY_CMDID);
+ /* remove wds (4 address ) entry. used only for testing WDS
+ * feature on AP products */
+ WMI_RETURN_STRING(WMI_PEER_REMOVE_WDS_ENTRY_CMDID);
+ /* set up mcast info for multicast to unicast conversion */
+ WMI_RETURN_STRING(WMI_PEER_MCAST_GROUP_CMDID);
+ /* request peer info from FW to get PEER_INFO_EVENTID */
+ WMI_RETURN_STRING(WMI_PEER_INFO_REQ_CMDID);
+
+ /* beacon/management specific commands */
+
+ /* transmit beacon by reference. used for transmitting beacon
+ * on low latency interface like pcie */
+ WMI_RETURN_STRING(WMI_BCN_TX_CMDID);
+ /* transmit beacon by value */
+ WMI_RETURN_STRING(WMI_PDEV_SEND_BCN_CMDID);
+ /* set the beacon template. used in beacon offload mode to setup
+ * the common beacon template with the FW to be used by FW to
+ * generate beacons */
+ WMI_RETURN_STRING(WMI_BCN_TMPL_CMDID);
+ /* set beacon filter with FW */
+ WMI_RETURN_STRING(WMI_BCN_FILTER_RX_CMDID);
+ /* enable/disable filtering of probe requests in the firmware */
+ WMI_RETURN_STRING(WMI_PRB_REQ_FILTER_RX_CMDID);
+ /* transmit management frame by value. will be deprecated */
+ WMI_RETURN_STRING(WMI_MGMT_TX_CMDID);
+ /* set the probe response template. used in beacon offload mode
+ * to setup the common probe response template with the FW to
+ * be used by FW to generate probe responses */
+ WMI_RETURN_STRING(WMI_PRB_TMPL_CMDID);
+
+ /* commands to directly control ba negotiation directly from
+ * host. only used in test mode */
+
+ /* turn off FW Auto addba mode and let host control addba */
+ WMI_RETURN_STRING(WMI_ADDBA_CLEAR_RESP_CMDID);
+ /* send add ba request */
+ WMI_RETURN_STRING(WMI_ADDBA_SEND_CMDID);
+ WMI_RETURN_STRING(WMI_ADDBA_STATUS_CMDID);
+ /* send del ba */
+ WMI_RETURN_STRING(WMI_DELBA_SEND_CMDID);
+ /* set add ba response will be used by FW to generate
+ * addba response*/
+ WMI_RETURN_STRING(WMI_ADDBA_SET_RESP_CMDID);
+ /* send single VHT MPDU with AMSDU */
+ WMI_RETURN_STRING(WMI_SEND_SINGLEAMSDU_CMDID);
+
+ /* Station power save specific config */
+ /* enable/disable station powersave */
+ WMI_RETURN_STRING(WMI_STA_POWERSAVE_MODE_CMDID);
+ /* set station power save specific parameter */
+ WMI_RETURN_STRING(WMI_STA_POWERSAVE_PARAM_CMDID);
+ /* set station mimo powersave mode */
+ WMI_RETURN_STRING(WMI_STA_MIMO_PS_MODE_CMDID);
+
+ /* DFS-specific commands */
+ /* enable DFS (radar detection)*/
+ WMI_RETURN_STRING(WMI_PDEV_DFS_ENABLE_CMDID);
+ /* disable DFS (radar detection)*/
+ WMI_RETURN_STRING(WMI_PDEV_DFS_DISABLE_CMDID);
+ /* enable DFS phyerr/parse filter offload */
+ WMI_RETURN_STRING(WMI_DFS_PHYERR_FILTER_ENA_CMDID);
+ /* enable DFS phyerr/parse filter offload */
+ WMI_RETURN_STRING(WMI_DFS_PHYERR_FILTER_DIS_CMDID);
+
+ /* Roaming specific commands */
+ /* set roam scan mode */
+ WMI_RETURN_STRING(WMI_ROAM_SCAN_MODE);
+ /* set roam scan rssi threshold below which roam
+ * scan is enabled */
+ WMI_RETURN_STRING(WMI_ROAM_SCAN_RSSI_THRESHOLD);
+ /* set roam scan period for periodic roam scan mode */
+ WMI_RETURN_STRING(WMI_ROAM_SCAN_PERIOD);
+ /* set roam scan trigger rssi change threshold */
+ WMI_RETURN_STRING(WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD);
+ /* set roam AP profile */
+ WMI_RETURN_STRING(WMI_ROAM_AP_PROFILE);
+ /* set channel list for roam scans */
+ WMI_RETURN_STRING(WMI_ROAM_CHAN_LIST);
+ /* offload scan specific commands */
+ /* set offload scan AP profile */
+ WMI_RETURN_STRING(WMI_OFL_SCAN_ADD_AP_PROFILE);
+ /* remove offload scan AP profile */
+ WMI_RETURN_STRING(WMI_OFL_SCAN_REMOVE_AP_PROFILE);
+ /* set offload scan period */
+ WMI_RETURN_STRING(WMI_OFL_SCAN_PERIOD);
+
+ /* P2P specific commands */
+ /* set P2P device info. FW will used by FW to create P2P IE
+ * to be carried in probe response generated during p2p listen
+ * and for p2p discoverability */
+ WMI_RETURN_STRING(WMI_P2P_DEV_SET_DEVICE_INFO);
+ /* enable/disable p2p discoverability on STA/AP VDEVs */
+ WMI_RETURN_STRING(WMI_P2P_DEV_SET_DISCOVERABILITY);
+ /* set p2p ie to be carried in beacons generated by FW for GO */
+ WMI_RETURN_STRING(WMI_P2P_GO_SET_BEACON_IE);
+ /* set p2p ie to be carried in probe response frames generated
+ * by FW for GO */
+ WMI_RETURN_STRING(WMI_P2P_GO_SET_PROBE_RESP_IE);
+ /* set the vendor specific p2p ie data.
+ * FW will use this to parse the P2P NoA
+ * attribute in the beacons/probe responses received.
+ */
+ WMI_RETURN_STRING(WMI_P2P_SET_VENDOR_IE_DATA_CMDID);
+ /* set the configure of p2p find offload */
+ WMI_RETURN_STRING(WMI_P2P_DISC_OFFLOAD_CONFIG_CMDID);
+ /* set the vendor specific p2p ie data for p2p find offload */
+ WMI_RETURN_STRING(WMI_P2P_DISC_OFFLOAD_APPIE_CMDID);
+ /* set the BSSID/device name pattern of p2p find offload */
+ WMI_RETURN_STRING(WMI_P2P_DISC_OFFLOAD_PATTERN_CMDID);
+ /* set OppPS related parameters **/
+ WMI_RETURN_STRING(WMI_P2P_SET_OPPPS_PARAM_CMDID);
+
+ /* AP power save specific config
+ * set AP power save specific param */
+ WMI_RETURN_STRING(WMI_AP_PS_PEER_PARAM_CMDID);
+ /* set AP UAPSD coex pecific param */
+ WMI_RETURN_STRING(WMI_AP_PS_PEER_UAPSD_COEX_CMDID);
+
+ /* Rate-control specific commands */
+ WMI_RETURN_STRING(WMI_PEER_RATE_RETRY_SCHED_CMDID);
+
+ /* WLAN Profiling commands. */
+ WMI_RETURN_STRING(WMI_WLAN_PROFILE_TRIGGER_CMDID);
+ WMI_RETURN_STRING(WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID);
+ WMI_RETURN_STRING(WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID);
+ WMI_RETURN_STRING(WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID);
+ WMI_RETURN_STRING(WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID);
+
+ /* Suspend resume command Ids */
+ WMI_RETURN_STRING(WMI_PDEV_SUSPEND_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_RESUME_CMDID);
+
+ /* Beacon filter commands */
+ /* add a beacon filter */
+ WMI_RETURN_STRING(WMI_ADD_BCN_FILTER_CMDID);
+ /* remove a beacon filter */
+ WMI_RETURN_STRING(WMI_RMV_BCN_FILTER_CMDID);
+
+ /* WOW Specific WMI commands */
+ /* add pattern for awake */
+ WMI_RETURN_STRING(WMI_WOW_ADD_WAKE_PATTERN_CMDID);
+ /* deleta a wake pattern */
+ WMI_RETURN_STRING(WMI_WOW_DEL_WAKE_PATTERN_CMDID);
+ /* enable/deisable wake event */
+ WMI_RETURN_STRING(WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
+ /* enable WOW */
+ WMI_RETURN_STRING(WMI_WOW_ENABLE_CMDID);
+ /* host woke up from sleep event to FW. Generated in response
+ * to WOW Hardware event */
+ WMI_RETURN_STRING(WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
+
+ /* RTT measurement related cmd */
+ /* reques to make an RTT measurement */
+ WMI_RETURN_STRING(WMI_RTT_MEASREQ_CMDID);
+ /* reques to report a tsf measurement */
+ WMI_RETURN_STRING(WMI_RTT_TSF_CMDID);
+
+ /* spectral scan command */
+ /* configure spectral scan */
+ WMI_RETURN_STRING(WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID);
+ /* enable/disable spectral scan and trigger */
+ WMI_RETURN_STRING(WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID);
+
+ /* F/W stats */
+ /* one time request for stats */
+ WMI_RETURN_STRING(WMI_REQUEST_STATS_CMDID);
+ /* Push MCC Adaptive Scheduler Stats to Firmware */
+ WMI_RETURN_STRING(WMI_MCC_SCHED_TRAFFIC_STATS_CMDID);
+
+ /* ARP OFFLOAD REQUEST*/
+ WMI_RETURN_STRING(WMI_SET_ARP_NS_OFFLOAD_CMDID);
+
+ /* Proactive ARP Response Add Pattern Command*/
+ WMI_RETURN_STRING(WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID);
+
+ /* Proactive ARP Response Del Pattern Command*/
+ WMI_RETURN_STRING(WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID);
+
+ /* NS offload confid*/
+ WMI_RETURN_STRING(WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
+
+ /* GTK offload Specific WMI commands */
+ WMI_RETURN_STRING(WMI_GTK_OFFLOAD_CMDID);
+
+ /* CSA offload Specific WMI commands */
+ /* csa offload enable */
+ WMI_RETURN_STRING(WMI_CSA_OFFLOAD_ENABLE_CMDID);
+ /* chan switch command */
+ WMI_RETURN_STRING(WMI_CSA_OFFLOAD_CHANSWITCH_CMDID);
+
+ /* Chatter commands */
+ /* Change chatter mode of operation */
+ WMI_RETURN_STRING(WMI_CHATTER_SET_MODE_CMDID);
+ /* chatter add coalescing filter command */
+ WMI_RETURN_STRING(WMI_CHATTER_ADD_COALESCING_FILTER_CMDID);
+ /* chatter delete coalescing filter command */
+ WMI_RETURN_STRING(WMI_CHATTER_DELETE_COALESCING_FILTER_CMDID);
+ /* chatter coalecing query command */
+ WMI_RETURN_STRING(WMI_CHATTER_COALESCING_QUERY_CMDID);
+
+ /* addba specific commands */
+ /* start the aggregation on this TID */
+ WMI_RETURN_STRING(WMI_PEER_TID_ADDBA_CMDID);
+ /* stop the aggregation on this TID */
+ WMI_RETURN_STRING(WMI_PEER_TID_DELBA_CMDID);
+
+ /* set station mimo powersave method */
+ WMI_RETURN_STRING(WMI_STA_DTIM_PS_METHOD_CMDID);
+ /* Configure the Station UAPSD AC Auto Trigger Parameters */
+ WMI_RETURN_STRING(WMI_STA_UAPSD_AUTO_TRIG_CMDID);
+ /* Configure the Keep Alive Parameters */
+ WMI_RETURN_STRING(WMI_STA_KEEPALIVE_CMDID);
+
+ /* Request ssn from target for a sta/tid pair */
+ WMI_RETURN_STRING(WMI_BA_REQ_SSN_CMDID);
+ /* misc command group */
+ /* echo command mainly used for testing */
+ WMI_RETURN_STRING(WMI_ECHO_CMDID);
+
+ /* !!IMPORTANT!!
+ * If you need to add a new WMI command to the
+ * WMI_RETURN_STRING(WMI_GRP_MISC) sub-group,
+ * please make sure you add it BEHIND
+ * WMI_RETURN_STRING(WMI_PDEV_UTF_CMDID);
+ * as we MUST have a fixed value here to maintain compatibility between
+ * UTF and the ART2 driver
+ */
+ /* UTF WMI commands */
+ WMI_RETURN_STRING(WMI_PDEV_UTF_CMDID);
+
+ /* set debug log config */
+ WMI_RETURN_STRING(WMI_DBGLOG_CFG_CMDID);
+ /* QVIT specific command id */
+ WMI_RETURN_STRING(WMI_PDEV_QVIT_CMDID);
+ /* Factory Testing Mode request command
+ * used for integrated chipsets */
+ WMI_RETURN_STRING(WMI_PDEV_FTM_INTG_CMDID);
+ /* set and get keepalive parameters command */
+ WMI_RETURN_STRING(WMI_VDEV_SET_KEEPALIVE_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_GET_KEEPALIVE_CMDID);
+ /* For fw recovery test command */
+ WMI_RETURN_STRING(WMI_FORCE_FW_HANG_CMDID);
+ /* Set Mcast/Bdcast filter */
+ WMI_RETURN_STRING(WMI_SET_MCASTBCAST_FILTER_CMDID);
+ /* set thermal management params */
+ WMI_RETURN_STRING(WMI_THERMAL_MGMT_CMDID);
+ WMI_RETURN_STRING(WMI_RSSI_BREACH_MONITOR_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_LRO_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_TRANSFER_DATA_TO_FLASH_CMDID);
+ WMI_RETURN_STRING(WMI_CONFIG_ENHANCED_MCAST_FILTER_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_WISA_CMDID);
+ WMI_RETURN_STRING(WMI_SCAN_ADAPTIVE_DWELL_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_WOW_SET_ACTION_WAKE_UP_CMDID);
+ WMI_RETURN_STRING(WMI_MAWC_SENSOR_REPORT_IND_CMDID);
+ WMI_RETURN_STRING(WMI_ROAM_CONFIGURE_MAWC_CMDID);
+ WMI_RETURN_STRING(WMI_NLO_CONFIGURE_MAWC_CMDID);
+ WMI_RETURN_STRING(WMI_EXTSCAN_CONFIGURE_MAWC_CMDID);
+ /* GPIO Configuration */
+ WMI_RETURN_STRING(WMI_GPIO_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_GPIO_OUTPUT_CMDID);
+
+ /* Txbf configuration command */
+ WMI_RETURN_STRING(WMI_TXBF_CMDID);
+
+ /* FWTEST Commands */
+ WMI_RETURN_STRING(WMI_FWTEST_VDEV_MCC_SET_TBTT_MODE_CMDID);
+ /* set NoA descs */
+ WMI_RETURN_STRING(WMI_FWTEST_P2P_SET_NOA_PARAM_CMDID);
+
+ /* TDLS Configuration */
+ /* enable/disable TDLS */
+ WMI_RETURN_STRING(WMI_TDLS_SET_STATE_CMDID);
+ /* set tdls peer state */
+ WMI_RETURN_STRING(WMI_TDLS_PEER_UPDATE_CMDID);
+
+ /* Resmgr Configuration */
+ /* Adaptive OCS is enabled by default in the FW.
+ * This command is used to disable FW based adaptive OCS.
+ */
+ WMI_RETURN_STRING
+ (WMI_RESMGR_ADAPTIVE_OCS_ENABLE_DISABLE_CMDID);
+ /* set the requested channel time quota for the home channels */
+ WMI_RETURN_STRING(WMI_RESMGR_SET_CHAN_TIME_QUOTA_CMDID);
+ /* set the requested latency for the home channels */
+ WMI_RETURN_STRING(WMI_RESMGR_SET_CHAN_LATENCY_CMDID);
+
+ /* STA SMPS Configuration */
+ /* force SMPS mode */
+ WMI_RETURN_STRING(WMI_STA_SMPS_FORCE_MODE_CMDID);
+ /* set SMPS parameters */
+ WMI_RETURN_STRING(WMI_STA_SMPS_PARAM_CMDID);
+
+ /* Wlan HB commands */
+ /* enalbe/disable wlan HB */
+ WMI_RETURN_STRING(WMI_HB_SET_ENABLE_CMDID);
+ /* set tcp parameters for wlan HB */
+ WMI_RETURN_STRING(WMI_HB_SET_TCP_PARAMS_CMDID);
+ /* set tcp pkt filter for wlan HB */
+ WMI_RETURN_STRING(WMI_HB_SET_TCP_PKT_FILTER_CMDID);
+ /* set udp parameters for wlan HB */
+ WMI_RETURN_STRING(WMI_HB_SET_UDP_PARAMS_CMDID);
+ /* set udp pkt filter for wlan HB */
+ WMI_RETURN_STRING(WMI_HB_SET_UDP_PKT_FILTER_CMDID);
+
+ /* Wlan RMC commands*/
+ /* enable/disable RMC */
+ WMI_RETURN_STRING(WMI_RMC_SET_MODE_CMDID);
+ /* configure action frame period */
+ WMI_RETURN_STRING(WMI_RMC_SET_ACTION_PERIOD_CMDID);
+ /* For debug/future enhancement purposes only,
+ * configures/finetunes RMC algorithms */
+ WMI_RETURN_STRING(WMI_RMC_CONFIG_CMDID);
+
+ /* WLAN MHF offload commands */
+ /* enable/disable MHF offload */
+ WMI_RETURN_STRING(WMI_MHF_OFFLOAD_SET_MODE_CMDID);
+ /* Plumb routing table for MHF offload */
+ WMI_RETURN_STRING(WMI_MHF_OFFLOAD_PLUMB_ROUTING_TBL_CMDID);
+
+ /* location scan commands */
+ /* start batch scan */
+ WMI_RETURN_STRING(WMI_BATCH_SCAN_ENABLE_CMDID);
+ /* stop batch scan */
+ WMI_RETURN_STRING(WMI_BATCH_SCAN_DISABLE_CMDID);
+ /* get batch scan result */
+ WMI_RETURN_STRING(WMI_BATCH_SCAN_TRIGGER_RESULT_CMDID);
+ /* OEM related cmd */
+ WMI_RETURN_STRING(WMI_OEM_REQ_CMDID);
+ WMI_RETURN_STRING(WMI_OEM_REQUEST_CMDID);
+ /* NAN request cmd */
+ WMI_RETURN_STRING(WMI_NAN_CMDID);
+ /* Modem power state cmd */
+ WMI_RETURN_STRING(WMI_MODEM_POWER_STATE_CMDID);
+ WMI_RETURN_STRING(WMI_REQUEST_STATS_EXT_CMDID);
+ WMI_RETURN_STRING(WMI_OBSS_SCAN_ENABLE_CMDID);
+ WMI_RETURN_STRING(WMI_OBSS_SCAN_DISABLE_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_GET_ESTIMATED_LINKSPEED_CMDID);
+ WMI_RETURN_STRING(WMI_ROAM_SCAN_CMD);
+ WMI_RETURN_STRING(WMI_PDEV_SET_LED_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_HOST_AUTO_SHUTDOWN_CFG_CMDID);
+ WMI_RETURN_STRING(WMI_CHAN_AVOID_UPDATE_CMDID);
+ WMI_RETURN_STRING(WMI_COEX_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_WOW_IOAC_ADD_KEEPALIVE_CMDID);
+ WMI_RETURN_STRING(WMI_WOW_IOAC_DEL_KEEPALIVE_CMDID);
+ WMI_RETURN_STRING(WMI_WOW_IOAC_ADD_WAKE_PATTERN_CMDID);
+ WMI_RETURN_STRING(WMI_WOW_IOAC_DEL_WAKE_PATTERN_CMDID);
+ WMI_RETURN_STRING(WMI_REQUEST_LINK_STATS_CMDID);
+ WMI_RETURN_STRING(WMI_START_LINK_STATS_CMDID);
+ WMI_RETURN_STRING(WMI_CLEAR_LINK_STATS_CMDID);
+ WMI_RETURN_STRING(WMI_GET_FW_MEM_DUMP_CMDID);
+ WMI_RETURN_STRING(WMI_LPI_MGMT_SNOOPING_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_LPI_START_SCAN_CMDID);
+ WMI_RETURN_STRING(WMI_LPI_STOP_SCAN_CMDID);
+ WMI_RETURN_STRING(WMI_EXTSCAN_START_CMDID);
+ WMI_RETURN_STRING(WMI_EXTSCAN_STOP_CMDID);
+ WMI_RETURN_STRING(WMI_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMDID);
+ WMI_RETURN_STRING(WMI_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMDID);
+ WMI_RETURN_STRING(WMI_EXTSCAN_GET_CACHED_RESULTS_CMDID);
+ WMI_RETURN_STRING(WMI_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMDID);
+ WMI_RETURN_STRING(WMI_EXTSCAN_SET_CAPABILITIES_CMDID);
+ WMI_RETURN_STRING(WMI_EXTSCAN_GET_CAPABILITIES_CMDID);
+ WMI_RETURN_STRING(WMI_EXTSCAN_CONFIGURE_HOTLIST_SSID_MONITOR_CMDID);
+ WMI_RETURN_STRING(WMI_ROAM_SYNCH_COMPLETE);
+ WMI_RETURN_STRING(WMI_D0_WOW_ENABLE_DISABLE_CMDID);
+ WMI_RETURN_STRING(WMI_EXTWOW_ENABLE_CMDID);
+ WMI_RETURN_STRING(WMI_EXTWOW_SET_APP_TYPE1_PARAMS_CMDID);
+ WMI_RETURN_STRING(WMI_EXTWOW_SET_APP_TYPE2_PARAMS_CMDID);
+ WMI_RETURN_STRING(WMI_UNIT_TEST_CMDID);
+ WMI_RETURN_STRING(WMI_ROAM_SET_RIC_REQUEST_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_GET_TEMPERATURE_CMDID);
+ WMI_RETURN_STRING(WMI_SET_DHCP_SERVER_OFFLOAD_CMDID);
+ WMI_RETURN_STRING(WMI_TPC_CHAINMASK_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_IPA_OFFLOAD_ENABLE_DISABLE_CMDID);
+ WMI_RETURN_STRING(WMI_SCAN_PROB_REQ_OUI_CMDID);
+ WMI_RETURN_STRING(WMI_TDLS_SET_OFFCHAN_MODE_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_LED_FLASHING_CMDID);
+ WMI_RETURN_STRING(WMI_MDNS_OFFLOAD_ENABLE_CMDID);
+ WMI_RETURN_STRING(WMI_MDNS_SET_FQDN_CMDID);
+ WMI_RETURN_STRING(WMI_MDNS_SET_RESPONSE_CMDID);
+ WMI_RETURN_STRING(WMI_MDNS_GET_STATS_CMDID);
+ WMI_RETURN_STRING(WMI_ROAM_INVOKE_CMDID);
+ WMI_RETURN_STRING(WMI_SET_ANTENNA_DIVERSITY_CMDID);
+ WMI_RETURN_STRING(WMI_SAP_OFL_ENABLE_CMDID);
+ WMI_RETURN_STRING(WMI_APFIND_CMDID);
+ WMI_RETURN_STRING(WMI_PASSPOINT_LIST_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_OCB_SET_SCHED_CMDID);
+ WMI_RETURN_STRING(WMI_OCB_SET_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_OCB_SET_UTC_TIME_CMDID);
+ WMI_RETURN_STRING(WMI_OCB_START_TIMING_ADVERT_CMDID);
+ WMI_RETURN_STRING(WMI_OCB_STOP_TIMING_ADVERT_CMDID);
+ WMI_RETURN_STRING(WMI_OCB_GET_TSF_TIMER_CMDID);
+ WMI_RETURN_STRING(WMI_DCC_GET_STATS_CMDID);
+ WMI_RETURN_STRING(WMI_DCC_CLEAR_STATS_CMDID);
+ WMI_RETURN_STRING(WMI_DCC_UPDATE_NDL_CMDID);
+ WMI_RETURN_STRING(WMI_ROAM_FILTER_CMDID);
+ WMI_RETURN_STRING(WMI_ROAM_SUBNET_CHANGE_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_DEBUG_MESG_FLUSH_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_SET_RATE_REPORT_CONDITION_CMDID);
+ WMI_RETURN_STRING(WMI_SOC_SET_PCL_CMDID);
+ WMI_RETURN_STRING(WMI_SOC_SET_HW_MODE_CMDID);
+ WMI_RETURN_STRING(WMI_SOC_SET_DUAL_MAC_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_WOW_ENABLE_ICMPV6_NA_FLT_CMDID);
+ WMI_RETURN_STRING(WMI_DIAG_EVENT_LOG_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_PACKET_FILTER_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_PACKET_FILTER_ENABLE_CMDID);
+ WMI_RETURN_STRING(WMI_SAP_SET_BLACKLIST_PARAM_CMDID);
+ WMI_RETURN_STRING(WMI_WOW_UDP_SVC_OFLD_CMDID);
+ WMI_RETURN_STRING(WMI_MGMT_TX_SEND_CMDID);
+ WMI_RETURN_STRING(WMI_SOC_SET_ANTENNA_MODE_CMDID);
+ WMI_RETURN_STRING(WMI_WOW_HOSTWAKEUP_GPIO_PIN_PATTERN_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_AP_PS_EGAP_PARAM_CMDID);
+ WMI_RETURN_STRING(WMI_PMF_OFFLOAD_SET_SA_QUERY_CMDID);
+ WMI_RETURN_STRING(WMI_BPF_GET_CAPABILITY_CMDID);
+ WMI_RETURN_STRING(WMI_BPF_GET_VDEV_STATS_CMDID);
+ WMI_RETURN_STRING(WMI_BPF_SET_VDEV_INSTRUCTIONS_CMDID);
+ WMI_RETURN_STRING(WMI_BPF_DEL_VDEV_INSTRUCTIONS_CMDID);
+ WMI_RETURN_STRING(WMI_NDI_GET_CAP_REQ_CMDID);
+ WMI_RETURN_STRING(WMI_NDP_INITIATOR_REQ_CMDID);
+ WMI_RETURN_STRING(WMI_NDP_RESPONDER_REQ_CMDID);
+ WMI_RETURN_STRING(WMI_NDP_END_REQ_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_UPDATE_WDS_ENTRY_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_ADD_PROXY_STA_ENTRY_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_FIPS_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SMART_ANT_ENABLE_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SMART_ANT_SET_RX_ANTENNA_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_ANTENNA_SWITCH_TABLE_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_CTL_TABLE_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_MIMOGAIN_TABLE_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_GET_TPC_CMDID);
+ WMI_RETURN_STRING(WMI_MIB_STATS_ENABLE_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_GET_ANI_CCK_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_GET_ANI_OFDM_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_RATEMASK_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_ATF_REQUEST_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_SET_DSCP_TID_MAP_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_FILTER_NEIGHBOR_RX_PACKETS_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_SET_QUIET_MODE_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_SMART_ANT_SET_TX_ANTENNA_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_SMART_ANT_SET_TRAIN_INFO_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_SMART_ANT_SET_NODE_CONFIG_OPS_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_ATF_REQUEST_CMDID);
+ WMI_RETURN_STRING(WMI_FWTEST_CMDID);
+ WMI_RETURN_STRING(WMI_QBOOST_CFG_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_GET_NFCAL_POWER_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_PCL_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_HW_MODE_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_MAC_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_ANTENNA_MODE_CMDID);
+ WMI_RETURN_STRING(WMI_ROAM_SET_MBO_PARAM_CMDID);
+ WMI_RETURN_STRING(WMI_CHAN_AVOID_RPT_ALLOW_CMDID);
+ WMI_RETURN_STRING(WMI_SET_PERIODIC_CHANNEL_STATS_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_WAL_POWER_DEBUG_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_BWF_REQUEST_CMDID);
+ WMI_RETURN_STRING(WMI_DBGLOG_TIME_STAMP_SYNC_CMDID);
+ WMI_RETURN_STRING(WMI_P2P_LISTEN_OFFLOAD_START_CMDID);
+ WMI_RETURN_STRING(WMI_P2P_LISTEN_OFFLOAD_STOP_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_REORDER_QUEUE_SETUP_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_REORDER_QUEUE_REMOVE_CMDID);
+ WMI_RETURN_STRING(WMI_SET_MULTIPLE_MCAST_FILTER_CMDID);
+ WMI_RETURN_STRING(WMI_READ_DATA_FROM_FLASH_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_REORDER_TIMEOUT_VAL_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_SET_RX_BLOCKSIZE_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_WAKEUP_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_GET_ANTDIV_STATUS_CMDID);
+ WMI_RETURN_STRING(WMI_PEER_ANTDIV_INFO_REQ_CMDID);
+ WMI_RETURN_STRING(WMI_MNT_FILTER_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_GET_CHIP_POWER_STATS_CMDID);
+ WMI_RETURN_STRING(WMI_COEX_GET_ANTENNA_ISOLATION_CMDID);
+ WMI_RETURN_STRING(WMI_PDEV_SET_STATS_THRESHOLD_CMDID);
+ WMI_RETURN_STRING(WMI_REQUEST_WLAN_STATS_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID);
+ WMI_RETURN_STRING(WMI_REQUEST_PEER_STATS_INFO_CMDID);
+ WMI_RETURN_STRING(WMI_REQUEST_RADIO_CHAN_STATS_CMDID);
+ WMI_RETURN_STRING(WMI_ROAM_PER_CONFIG_CMDID);
+ WMI_RETURN_STRING(WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID);
+ WMI_RETURN_STRING(WMI_BPF_SET_VDEV_ACTIVE_MODE_CMDID);
+ WMI_RETURN_STRING(WMI_HW_DATA_FILTER_CMDID);
+ }
+
+ return "Invalid WMI cmd";
+}
+#endif /* WMI_CMD_STRINGS */
+
+/** WMI commands/events for the regulatory offload */
+
+/** Host indicating current country code to FW */
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_set_current_country_cmd_fixed_param */
+ A_UINT32 new_alpha2; /** alpha2 characters representing the country code */
+} wmi_set_current_country_cmd_fixed_param;
+
+/* Freq units in MHz */
+#define WMI_REG_RULE_START_FREQ_GET(freq_info) WMI_GET_BITS(freq_info, 0, 16)
+#define WMI_REG_RULE_START_FREQ_SET(freq_info, value) WMI_SET_BITS(freq_info, 0, 16, value)
+#define WMI_REG_RULE_END_FREQ_GET(freq_info) WMI_GET_BITS(freq_info, 16, 16)
+#define WMI_REG_RULE_END_FREQ_SET(freq_info, value) WMI_SET_BITS(freq_info, 16, 16, value)
+
+/* BW in MHz */
+#define WMI_REG_RULE_MAX_BW_GET(bw_info) WMI_GET_BITS(bw_info, 0, 16)
+#define WMI_REG_RULE_MAX_BW_SET(bw_info, value) WMI_SET_BITS(bw_info, 0, 16, value)
+/* regpower in dBm */
+#define WMI_REG_RULE_REG_POWER_GET(bw_info) WMI_GET_BITS(bw_info, 16, 8)
+#define WMI_REG_RULE_REG_POWER_SET(bw_info, value) WMI_SET_BITS(bw_info, 16, 8, value)
+
+typedef enum {
+ WMI_REG_FLAG_CHAN_NO_IR = 0x0001, /* passive channel */
+ WMI_REG_FLAG_CHAN_RADAR = 0x0002, /* dfs channel */
+ WMI_REG_FLAG_CHAN_NO_OFDM = 0x0004, /* no ofdm channel */
+ WMI_REG_FLAG_CHAN_INDOOR_ONLY = 0x0008, /* indoor only channel */
+} WMI_REGULATORY_FLAGS;
+
+#define WMI_REG_RULE_FLAGS_GET(flag_info) WMI_GET_BITS(flag_info, 0, 16)
+#define WMI_REG_RULE_FLAGS_SET(flag_info, value) WMI_SET_BITS(flag_info, 0, 16, value)
+
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_regulatory_rule_struct */
+ A_UINT32 freq_info; /* u16 start_freq, u16 end_freq */
+ A_UINT32 bw_info; /* u16 max_bw, u8 reg_power, u8 reserved */
+ A_UINT32 power_flag_info; /* u16 flags, u16 reserved */
+} wmi_regulatory_rule_struct;
+
+typedef enum {
+ WMI_REG_DFS_UNINIT_REGION = 0,
+ WMI_REG_DFS_FCC_REGION = 1,
+ WMI_REG_DFS_ETSI_REGION = 2,
+ WMI_REG_DFS_MKK_REGION = 3,
+ WMI_REG_DFS_CN_REGION = 4,
+ WMI_REG_DFS_KR_REGION = 5,
+
+ /* Add new items above */
+ WMI_REG_DFS_UNDEF_REGION = 0xFFFF,
+} WMI_REG_DFS_REGION;
+
+typedef enum {
+ WMI_REGULATORY_PHYMODE_NO11A = 0x0001, /* NO 11A */
+ WMI_REGULATORY_PHYMODE_NO11B = 0x0002, /* NO 11B */
+ WMI_REGULATORY_PHYMODE_NO11G = 0x0004, /* NO 11G */
+ WMI_REGULATORY_PHYMODE_NO11N = 0x0008, /* NO 11N */
+ WMI_REGULATORY_PHYMODE_NO11AC = 0x0010, /* NO 11AC */
+ WMI_REGULATORY_PHYMODE_NO11AX = 0x0020, /* NO 11AX */
+} WMI_REGULATORY_PHYBITMAP;
+
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_reg_chan_list_cc_event_fixed_param */
+ A_UINT32 alpha2;
+ A_UINT32 dfs_region; /* WMI_REG_DFS_REGION */
+ A_UINT32 phybitmap; /* WMI_REGULATORY_PHYBITMAP */
+ A_UINT32 min_bw_2g; /* BW in MHz */
+ A_UINT32 max_bw_2g; /* BW in MHz */
+ A_UINT32 min_bw_5g; /* BW in MHz */
+ A_UINT32 max_bw_5g; /* BW in MHz */
+ A_UINT32 num_2g_reg_rules;
+ A_UINT32 num_5g_reg_rules;
+/* followed by wmi_regulatory_rule_struct TLV array. First 2G and then 5G */
+} wmi_reg_chan_list_cc_event_fixed_param;
+
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_11d_scan_start_cmd_fixed_param */
+ A_UINT32 vdev_id;
+ A_UINT32 scan_period_msec; /** scan duration in milli-seconds */
+ A_UINT32 start_interval_msec; /** offset duration to start the scan in milli-seconds */
+} wmi_11d_scan_start_cmd_fixed_param;
+
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_11d_scan_stop_cmd_fixed_param */
+ A_UINT32 vdev_id;
+} wmi_11d_scan_stop_cmd_fixed_param;
+
+/** FW indicating new current country code to Host */
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_11d_new_country_event_fixed_param */
+ A_UINT32 new_alpha2; /** alpha2 characters representing the country code */
+} wmi_11d_new_country_event_fixed_param;
+
+typedef struct {
+ /** TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_coex_get_antenna_isolation_cmd_fixed_param */
+ A_UINT32 tlv_header;
+ /* Currently there are no parameters for this message. */
+} wmi_coex_get_antenna_isolation_cmd_fixed_param;
+
+typedef struct {
+ /** TLV tag and len; tag equals
+ * WMITLV_TAG_STRUC_wmi_coex_report_isolation_event_fixed_param */
+ A_UINT32 tlv_header;
+ /** Antenna isolation value in dB units, none zero value is valid while 0 means failed to do isolation measurement or corresponding chain is not active.
+ * Currently the HW descriptor only supports 4 chains at most.
+ * Further isolation_chainX elements can be added in the future
+ * for additional chains, if needed.
+ */
+ A_UINT32 isolation_chain0:8, /* [7:0], isolation value for chain 0 */
+ isolation_chain1:8, /* [15:8], isolation value for chain 1 */
+ isolation_chain2:8, /* [23:16], isolation value for chain 2 */
+ isolation_chain3:8; /* [31:24], isolation value for chain 3 */
+} wmi_coex_report_isolation_event_fixed_param;
+
+typedef enum {
+ WMI_RCPI_MEASUREMENT_TYPE_AVG_MGMT = 1,
+ WMI_RCPI_MEASUREMENT_TYPE_AVG_DATA = 2,
+ WMI_RCPI_MEASUREMENT_TYPE_LAST_MGMT = 3,
+ WMI_RCPI_MEASUREMENT_TYPE_LAST_DATA = 4,
+} wmi_rcpi_measurement_type;
+
+typedef struct {
+ /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_request_rcpi_cmd_fixed_param */
+ A_UINT32 tlv_header;
+ /* VDEV identifier */
+ A_UINT32 vdev_id;
+ /* peer MAC address */
+ wmi_mac_addr peer_macaddr;
+ /* measurement type - defined in enum wmi_rcpi_measurement_type */
+ A_UINT32 measurement_type;
+} wmi_request_rcpi_cmd_fixed_param;
+
+typedef struct {
+ /** TLV tag and len; tag equals WMITLV_TAG_STRUC_wmi_update_rcpi_event_fixed_param */
+ A_UINT32 tlv_header;
+ /* VDEV identifier */
+ A_UINT32 vdev_id;
+ /* peer MAC address */
+ wmi_mac_addr peer_macaddr;
+ /* measurement type - defined in enum wmi_rcpi_measurement_type */
+ A_UINT32 measurement_type;
+ /* Measured RCPI in dBm of the peer requested by host */
+ A_INT32 rcpi;
+ /** status
+ * 0 - Requested peer RCPI available
+ * 1 - Requested peer RCPI not available
+ */
+ A_UINT32 status;
+} wmi_update_rcpi_event_fixed_param;
+
+/* Definition of mask for various package id */
+#define WMI_PKGID_MASK_AUTO 0x00000080
+
+typedef struct {
+ /** TLV tag and len; tag equals*/
+ A_UINT32 tlv_header;
+ /**
+ * The value field is filled with WMI_PKGID_MASK values.
+ * Currently, the only flag used within values is
+ * WMI_PKGID_MASK_AUTO, where bit7=1 for automotive systems.
+ */
+ A_UINT32 value;
+}wmi_pkgid_event_fixed_param;
+
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals
+ WMITLV_TAG_STRUC_wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_params */
+ A_UINT32 vdev_id; /* vdev id whose mac to be randomized */
+ /* enable is set to 1 if mac randomization to be enabled */
+ A_UINT32 enable;
+ /* randomization mac address if randomization is enabled */
+ wmi_mac_addr mac_addr;
+} wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param;
+
+typedef struct {
+ A_UINT32 tlv_header; /* TLV tag and len; tag equals
+ WMITLV_TAG_STRUC_wmi_vdev_add_mac_addr_to_rx_filter_event_fixed_params */
+ A_UINT32 vdev_id; /* vdev of id whose mac address was randomized */
+ A_UINT32 status; /* status is 1 if success and 0 if failed */
+} wmi_vdev_add_mac_addr_to_rx_filter_status_event_fixed_param;
+
+/* Definition of HW data filtering */
+typedef enum {
+ WMI_HW_DATA_FILTER_DROP_NON_ARP_BC = 0x01,
+ WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC = 0x02,
+} WMI_HW_DATA_FILTER_BITMAP_TYPE;
+
+typedef struct {
+ A_UINT32 tlv_header;
+ A_UINT32 vdev_id;
+ A_UINT32 enable; /* 1 . enable, 0- disable */
+ A_UINT32 hw_filter_bitmap; /* see WMI_HW_DATA_FILTER_BITMAP_TYPE */
+} wmi_hw_data_filter_cmd_fixed_param;
+
/* ADD NEW DEFS HERE */
/*****************************************************************************
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_version.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_version.h
index f2e272c..a480752 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_version.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/COMMON/wmi_version.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -36,7 +36,7 @@
#define __WMI_VER_MINOR_ 0
/** WMI revision number has to be incremented when there is a
* change that may or may not break compatibility. */
-#define __WMI_REVISION_ 260
+#define __WMI_REVISION_ 340
/** The Version Namespace should not be normally changed. Only
* host and firmware of the same WMI namespace will work
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/ar9888def.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/ar9888def.h
index b26b1b7..e3d3f7f 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/ar9888def.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/ar9888def.h
@@ -26,7 +26,7 @@
*/
#ifndef _AR9888DEF_H_
-#define AR9888__AR9888DEF_H_
+#define _AR9888DEF_H_
/* Base Addresses */
#define AR9888_RTC_SOC_BASE_ADDRESS 0x00004000
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/copy_engine.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/copy_engine.c
index 3bfad87..1c8e0e2 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/copy_engine.c
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/copy_engine.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014,2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014,2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -428,7 +428,7 @@
DPTRACE(adf_dp_trace((adf_nbuf_t)per_transfer_context,
ADF_DP_TRACE_CE_PACKET_PTR_RECORD,
(uint8_t *)&(((adf_nbuf_t)per_transfer_context)->data),
- sizeof(((adf_nbuf_t)per_transfer_context)->data)));
+ sizeof(((adf_nbuf_t)per_transfer_context)->data), ADF_TX));
} else {
/*
* Probably not worth the additional complexity to support
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/hif_pci.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/hif_pci.c
index 656649b..f1074ab 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/hif_pci.c
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/hif_pci.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -375,7 +375,7 @@
NBUF_UPDATE_TX_PKT_COUNT(nbuf, NBUF_TX_PKT_HIF);
DPTRACE(adf_dp_trace(nbuf, ADF_DP_TRACE_HIF_PACKET_PTR_RECORD,
adf_nbuf_data_addr(nbuf),
- sizeof(adf_nbuf_data(nbuf))));
+ sizeof(adf_nbuf_data(nbuf)), ADF_TX));
status = CE_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id);
A_ASSERT(status == A_OK);
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/if_pci.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/if_pci.c
index 79f410c..d49addc 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/if_pci.c
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/PCIe/if_pci.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -1182,7 +1182,6 @@
sc->pm_stats.runtime_put);
goto out;
}
-
temp_module = vos_get_context(VOS_MODULE_ID_WDA, vos_context);
if (!temp_module) {
pr_err("%s: WDA module is NULL\n", __func__);
@@ -1335,7 +1334,8 @@
{
if (init)
sc->pm_dentry = debugfs_create_file("cnss_runtime_pm",
- S_IRUSR, NULL, sc,
+ S_IRUSR | S_IRGRP | S_IROTH,
+ NULL, sc,
&hif_pci_autopm_fops);
else
debugfs_remove(sc->pm_dentry);
@@ -2679,8 +2679,23 @@
ol_txrx_get_queue_status(txrx_pdev)) {
msleep(OL_ATH_TX_DRAIN_WAIT_DELAY);
if (++tx_drain_wait_cnt > OL_ATH_TX_DRAIN_WAIT_CNT) {
- printk("%s: tx frames are pending\n", __func__);
+ u_int32_t ctrl_addr_dest = CE_BASE_ADDRESS(1);
+ u_int32_t ctrl_addr_src = CE_BASE_ADDRESS(4);
+
ol_txrx_dump_tx_desc(txrx_pdev);
+ /* Dump vdev tx ll queue stats*/
+ ol_txrx_dump_tx_queue_stats(txrx_pdev);
+ /* increment the pci suspend count, When reach to 10 crash the system*/
+ A_TARGET_ACCESS_BEGIN_RET(targid);
+ printk("%s: Destination ring read idx 0x%x and write idx 0x%x\n",
+ __func__,
+ CE_DEST_RING_READ_IDX_GET(targid, ctrl_addr_dest),
+ CE_DEST_RING_WRITE_IDX_GET(targid, ctrl_addr_dest));
+ printk("%s: Source ring read idx 0x%x and write idx 0x%x\n",
+ __func__,
+ CE_SRC_RING_READ_IDX_GET(targid, ctrl_addr_src),
+ CE_SRC_RING_WRITE_IDX_GET(targid, ctrl_addr_src));
+ A_TARGET_ACCESS_END_RET(targid);
goto out;
}
}
@@ -2886,9 +2901,12 @@
if (retry > MAX_REG_READ_RETRIES) {
pr_err("%s: PCIe link is possible down!\n", __func__);
- print_config_soc_reg(sc);
- VOS_ASSERT(0);
- break;
+ if (vos_is_logp_in_progress(VOS_MODULE_ID_HIF, NULL))
+ return 0;
+ sc->recovery = true;
+ vos_set_logp_in_progress(VOS_MODULE_ID_VOSS, TRUE);
+ vos_wlan_pci_link_down();
+ goto out;
}
A_MDELAY(1);
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/sdio/cepci.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/sdio/cepci.h
index f5ba19b..5619613 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/sdio/cepci.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/HIF/sdio/cepci.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -32,6 +32,7 @@
* Support for Copy Engine over PCI.
* Structures shared between Host software and Target firmware.
*/
+#include <a_types.h> /* A_UINT32 */
/*
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/HTC/htc_send.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/HTC/htc_send.c
index 53f73e5..b3e552b 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/HTC/htc_send.c
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/HTC/htc_send.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -1326,7 +1326,7 @@
NBUF_UPDATE_TX_PKT_COUNT(netbuf, NBUF_TX_PKT_HTC);
DPTRACE(adf_dp_trace(netbuf, ADF_DP_TRACE_HTC_PACKET_PTR_RECORD,
adf_nbuf_data_addr(netbuf),
- sizeof(adf_nbuf_data(netbuf))));
+ sizeof(adf_nbuf_data(netbuf)), ADF_TX));
status = HIFSend_head(target->hif_dev,
pEndpoint->UL_PipeID,
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c
index da89559..438532f 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -111,6 +111,7 @@
#include "wma_ocb.h"
#include "wma_nan_datapath.h"
+#include "adf_trace.h"
/* ################### defines ################### */
/*
@@ -1677,7 +1678,7 @@
__func__, bssid, vdev_id, wma->interfaces[vdev_id].peer_count);
return;
} else {
- WMA_LOGE("%s: Removed peer with addr %pM vdevid %d peer_count %d",
+ WMA_LOGI("%s: Removing peer with addr %pM vdevid %d peer_count %d",
__func__, bssid, vdev_id, wma->interfaces[vdev_id].peer_count);
}
#endif
@@ -3908,6 +3909,8 @@
event->num_epno_networks;
dest_capab->max_number_of_white_listed_ssid =
event->num_roam_ssid_whitelist;
+ dest_capab->max_number_of_black_listed_bssid =
+ event->num_roam_bssid_blacklist;
dest_capab->status = 0;
WMA_LOGD("%s: request_id: %u status: %d",
@@ -3928,11 +3931,13 @@
dest_capab->max_significant_wifi_change_aps);
WMA_LOGD("%s: Capabilities: max_hotlist_ssids: %d,"
"max_number_epno_networks: %d, max_number_epno_networks_by_ssid: %d,"
- "max_number_of_white_listed_ssid: %d",
+ "max_number_of_white_listed_ssid: %d,"
+ "max_number_of_black_listed_bssid: %d ",
__func__, dest_capab->max_hotlist_ssids,
dest_capab->max_number_epno_networks,
dest_capab->max_number_epno_networks_by_ssid,
- dest_capab->max_number_of_white_listed_ssid);
+ dest_capab->max_number_of_white_listed_ssid,
+ dest_capab->max_number_of_black_listed_bssid);
pMac->sme.pExtScanIndCb(pMac->hHdd,
eSIR_EXTSCAN_GET_CAPABILITIES_IND,
@@ -5023,6 +5028,94 @@
#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
/**
+ * wma_unified_power_debug_stats_event_handler() - WMA handler function to
+ * handle Power stats event from firmware
+ * @handle: Pointer to wma handle
+ * @cmd_param_info: Pointer to Power stats event TLV
+ * @len: Length of the cmd_param_info
+ *
+ * Return: 0 on success, error number otherwise
+ */
+#ifdef WLAN_POWER_DEBUGFS
+static int wma_unified_power_debug_stats_event_handler(void *handle,
+ uint8_t *cmd_param_info, uint32_t len)
+{
+ tp_wma_handle wma_handle = (tp_wma_handle) handle;
+ WMI_PDEV_CHIP_POWER_STATS_EVENTID_param_tlvs *param_tlvs;
+ struct power_stats_response *power_stats_results;
+ wmi_pdev_chip_power_stats_event_fixed_param *param_buf;
+ uint32_t power_stats_len, stats_registers_len, *debug_registers;
+
+ tpAniSirGlobal mac = (tpAniSirGlobal)vos_get_context(VOS_MODULE_ID_PE,
+ wma_handle->vos_context);
+ param_tlvs =
+ (WMI_PDEV_CHIP_POWER_STATS_EVENTID_param_tlvs *) cmd_param_info;
+
+ param_buf = (wmi_pdev_chip_power_stats_event_fixed_param *)
+ param_tlvs->fixed_param;
+ if (!mac || !mac->sme.power_stats_resp_callback) {
+ WMA_LOGD("%s: NULL mac ptr or HDD callback is null", __func__);
+ return -EINVAL;
+ }
+
+ if (!param_buf) {
+ WMA_LOGD("%s: NULL power stats event fixed param", __func__);
+ return -EINVAL;
+ }
+
+ debug_registers = param_tlvs->debug_registers;
+ stats_registers_len =
+ (sizeof(uint32_t) * param_buf->num_debug_register);
+ power_stats_len = stats_registers_len + sizeof(*power_stats_results);
+ power_stats_results = vos_mem_malloc(power_stats_len);
+ if (NULL == power_stats_results) {
+ WMA_LOGD("%s: could not allocate mem for power stats results",
+ __func__);
+ return -ENOMEM;
+ }
+
+ vos_mem_zero(power_stats_results, power_stats_len);
+ WMA_LOGD("Cumulative sleep time %d cumulative total on time %d deep sleep enter counter %d last deep sleep enter tstamp ts %d debug registers fmt %d num debug register %d",
+ param_buf->cumulative_sleep_time_ms,
+ param_buf->cumulative_total_on_time_ms,
+ param_buf->deep_sleep_enter_counter,
+ param_buf->last_deep_sleep_enter_tstamp_ms,
+ param_buf->debug_register_fmt,
+ param_buf->num_debug_register);
+
+ power_stats_results->cumulative_sleep_time_ms
+ = param_buf->cumulative_sleep_time_ms;
+ power_stats_results->cumulative_total_on_time_ms
+ = param_buf->cumulative_total_on_time_ms;
+ power_stats_results->deep_sleep_enter_counter
+ = param_buf->deep_sleep_enter_counter;
+ power_stats_results->last_deep_sleep_enter_tstamp_ms
+ = param_buf->last_deep_sleep_enter_tstamp_ms;
+ power_stats_results->debug_register_fmt
+ = param_buf->debug_register_fmt;
+ power_stats_results->num_debug_register
+ = param_buf->num_debug_register;
+
+ power_stats_results->debug_registers
+ = (uint32_t *)(power_stats_results + 1);
+
+ vos_mem_copy(power_stats_results->debug_registers,
+ debug_registers, stats_registers_len);
+
+ mac->sme.power_stats_resp_callback(power_stats_results,
+ mac->sme.power_debug_stats_context);
+ vos_mem_free(power_stats_results);
+ return 0;
+}
+#else
+static int wma_unified_power_debug_stats_event_handler(void *handle,
+ uint8_t *cmd_param_info, uint32_t len)
+{
+ return 0;
+}
+#endif
+
+/**
* wma_fw_mem_dump_event_handler() - handles fw memory dump event
*
* handle - pointer to wma handle.
@@ -6579,6 +6672,9 @@
return -EINVAL;
}
+ DPTRACE(adf_dp_trace_record_event(ADF_DP_TRACE_EVENT_RECORD,
+ synch_event->vdev_id, ADF_PROTO_TYPE_EVENT, ADF_ROAM_SYNCH));
+
if(wma->interfaces[synch_event->vdev_id].roam_synch_in_progress ==
VOS_TRUE) {
WMA_LOGE("%s: Ignoring RSI since one is already in progress",
@@ -7129,6 +7225,9 @@
len += vos_scnprintf(buf + len, *size - len,
"\n wow_ipv6_mcast_na_stats %d",
wma_handle->wow_ipv6_mcast_na_stats);
+ len += vos_scnprintf(buf + len, *size - len,
+ "\n wow_oem_response_wake_up_count %d",
+ wma_handle->wow_oem_response_wake_up_count);
*size -= len;
*buf_ptr += len;
@@ -7143,6 +7242,59 @@
vos_register_debug_callback(VOS_MODULE_ID_WDA, &wma_state_info_dump);
}
+/**
+ * wma_action_frame_filter_mac_event_handler() - action frame filter evt handler
+ * @handle: wma handle
+ * @event_buf: event handler data
+ * @len: length of @event_buf
+ *
+ * this function will handle the
+ * WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID
+ *
+ * Return: int
+ */
+static int
+wma_action_frame_filter_mac_event_handler(void *handle, u_int8_t *event_buf,
+ u_int32_t len)
+{
+ tp_wma_handle wma_handle = (tp_wma_handle)handle;
+ WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID_param_tlvs *param_buf;
+ wmi_vdev_add_mac_addr_to_rx_filter_status_event_fixed_param *event;
+ struct action_frame_random_filter *filter;
+ struct wma_txrx_node *intr;
+ bool status = false;
+
+ WMA_LOGD("%s: Enter", __func__);
+
+ param_buf =
+ (WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID_param_tlvs *)event_buf;
+ if (!param_buf) {
+ WMA_LOGA(FL("Invalid action frame filter mac event"));
+ return -EINVAL;
+ }
+ event = param_buf->fixed_param;
+ if (!event) {
+ WMA_LOGA(FL("Invalid fixed param"));
+ return -EINVAL;
+ }
+
+ intr = &wma_handle->interfaces[event->vdev_id];
+ /* command is in progess */
+ if(!intr->action_frame_filter) {
+ WMA_LOGE(FL("no action frame req is pending - invalid event"));
+ return -1;
+ }
+ filter = intr->action_frame_filter;
+ if (event->status)
+ status = true;
+
+ (filter->callback)(status, filter->context);
+ intr->action_frame_filter = NULL;
+ vos_mem_free(filter);
+
+ return 0;
+}
+
struct wma_version_info g_wmi_version_info;
/*
@@ -7196,7 +7348,20 @@
vos_wake_lock_init(&wma_handle->extscan_wake_lock,
"wlan_extscan_wl");
#endif
- vos_wake_lock_init(&wma_handle->wow_wake_lock, "wlan_wow_wl");
+ vos_wake_lock_init(&wma_handle->wow_wake_lock,
+ "wlan_wow_wl");
+ vos_wake_lock_init(&wma_handle->wow_auth_req_wl,
+ "wlan_auth_req_wl");
+ vos_wake_lock_init(&wma_handle->wow_assoc_req_wl,
+ "wlan_assoc_req_wl");
+ vos_wake_lock_init(&wma_handle->wow_deauth_rec_wl,
+ "wlan_deauth_rec_wl");
+ vos_wake_lock_init(&wma_handle->wow_disassoc_rec_wl,
+ "wlan_disassoc_rec_wl");
+ vos_wake_lock_init(&wma_handle->wow_ap_assoc_lost_wl,
+ "wlan_ap_assoc_lost_wl");
+ vos_wake_lock_init(&wma_handle->wow_auto_shutdown_wl,
+ "wlan_auto_shutdown_wl");
}
/* attach the wmi */
@@ -7545,6 +7710,9 @@
wmi_unified_register_event_handler(wma_handle->wmi_handle,
WMI_BPF_CAPABILIY_INFO_EVENTID,
wma_get_bpf_caps_event_handler);
+ wmi_unified_register_event_handler(wma_handle->wmi_handle,
+ WMI_PDEV_CHIP_POWER_STATS_EVENTID,
+ wma_unified_power_debug_stats_event_handler);
WMA_LOGD("%s: Exit", __func__);
@@ -7558,6 +7726,10 @@
WMI_RSSI_BREACH_EVENTID,
wma_rssi_breached_event_handler);
+ wmi_unified_register_event_handler(wma_handle->wmi_handle,
+ WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_STATUS_EVENTID,
+ wma_action_frame_filter_mac_event_handler);
+
wma_register_debug_callback();
wma_ndp_register_all_event_handlers(wma_handle);
@@ -7593,6 +7765,12 @@
vos_wake_lock_destroy(&wma_handle->extscan_wake_lock);
#endif
vos_wake_lock_destroy(&wma_handle->wow_wake_lock);
+ vos_wake_lock_destroy(&wma_handle->wow_auth_req_wl);
+ vos_wake_lock_destroy(&wma_handle->wow_assoc_req_wl);
+ vos_wake_lock_destroy(&wma_handle->wow_deauth_rec_wl);
+ vos_wake_lock_destroy(&wma_handle->wow_disassoc_rec_wl);
+ vos_wake_lock_destroy(&wma_handle->wow_ap_assoc_lost_wl);
+ vos_wake_lock_destroy(&wma_handle->wow_auto_shutdown_wl);
}
wma_runtime_context_deinit(wma_handle);
@@ -8967,6 +9145,8 @@
u_int8_t SSID_num;
int i;
int len = sizeof(*cmd);
+ wmi_vendor_oui *voui = NULL;
+ struct vendor_oui *pvoui = NULL;
tpAniSirGlobal pMac = (tpAniSirGlobal )vos_get_context(VOS_MODULE_ID_PE,
wma_handle->vos_context);
@@ -8991,6 +9171,10 @@
if (scan_req->uIEFieldLen)
len += roundup(scan_req->uIEFieldLen, sizeof(u_int32_t));
+ len += WMI_TLV_HDR_SIZE; /* Length of TLV for array of wmi_vendor_oui */
+ if (scan_req->num_vendor_oui)
+ len += scan_req->num_vendor_oui * sizeof(wmi_vendor_oui);
+
/* Allocate the memory */
*buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
if (!*buf) {
@@ -9081,6 +9265,15 @@
*/
cmd->burst_duration = 0;
+ /* mac randomization attributes */
+ if (scan_req->enable_scan_randomization) {
+ cmd->scan_ctrl_flags |= WMI_SCAN_ADD_SPOOFED_MAC_IN_PROBE_REQ |
+ WMI_SCAN_RANDOM_SEQ_NO_IN_PROBE_REQ;
+ WMI_CHAR_ARRAY_TO_MAC_ADDR(scan_req->mac_addr, &cmd->mac_addr);
+ WMI_CHAR_ARRAY_TO_MAC_ADDR(scan_req->mac_addr_mask,
+ &cmd->mac_mask);
+ }
+
if (!scan_req->p2pScanType) {
WMA_LOGD("Normal Scan request");
cmd->scan_ctrl_flags |= WMI_SCAN_ADD_CCK_RATES;
@@ -9092,6 +9285,15 @@
cmd->scan_ctrl_flags |= WMI_SCAN_ADD_TPC_IE_IN_PROBE_REQ;
cmd->scan_ctrl_flags |= WMI_SCAN_FILTER_PROBE_REQ;
+ if (scan_req->ie_whitelist) {
+ cmd->scan_ctrl_flags |=
+ WMI_SCAN_ENABLE_IE_WHITELIST_IN_PROBE_REQ;
+ for (i = 0; i < PROBE_REQ_BITMAP_LEN; i++)
+ cmd->ie_bitmap[i] =
+ scan_req->probe_req_ie_bitmap[i];
+ }
+
+ cmd->num_vendor_oui = scan_req->num_vendor_oui;
/*
* Decide burst_duration and dwell_time_active based on
* what type of devices are active.
@@ -9291,6 +9493,29 @@
}
buf_ptr += WMI_TLV_HDR_SIZE + ie_len_with_pad;
+ /* mac randomization */
+ WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
+ scan_req->num_vendor_oui *
+ sizeof(wmi_vendor_oui));
+
+ buf_ptr += WMI_TLV_HDR_SIZE;
+
+ if (cmd->num_vendor_oui != 0) {
+ voui = (wmi_vendor_oui *)buf_ptr;
+ pvoui = (struct vendor_oui *)((u_int8_t *)scan_req +
+ (scan_req->oui_field_offset));
+ for (i = 0; i < cmd->num_vendor_oui; i++) {
+ WMITLV_SET_HDR(&voui[i].tlv_header,
+ WMITLV_TAG_STRUC_wmi_vendor_oui,
+ WMITLV_GET_STRUCT_TLVLEN(
+ wmi_vendor_oui));
+ voui[i].oui_type_subtype = pvoui[i].oui_type |
+ (pvoui[i].oui_subtype << 24);
+ }
+ buf_ptr += cmd->num_vendor_oui *
+ sizeof(wmi_vendor_oui);
+ }
+
*buf_len = len;
return VOS_STATUS_SUCCESS;
error:
@@ -17749,6 +17974,8 @@
WMA_LOGD("%s: to delete sta for IBSS mode", __func__);
}
#endif
+ if (del_sta->staType == STA_ENTRY_NDI_PEER)
+ oper_mode = BSS_OPERATIONAL_MODE_NDI;
switch (oper_mode) {
case BSS_OPERATIONAL_MODE_STA:
@@ -17761,6 +17988,9 @@
case BSS_OPERATIONAL_MODE_AP:
wma_delete_sta_req_ap_mode(wma, del_sta);
break;
+ case BSS_OPERATIONAL_MODE_NDI:
+ wma_delete_sta_req_ndi_mode(wma, del_sta);
+ break;
}
#ifdef QCA_IBSS_SUPPORT
@@ -17865,6 +18095,14 @@
wma->interfaces[params->smesessionId].psnr_req = NULL;
}
+ if (wma->interfaces[params->smesessionId].action_frame_filter) {
+ struct action_frame_random_filter *action_frame_filter =
+ wma->interfaces[params->smesessionId].action_frame_filter;
+ wma->interfaces[params->smesessionId].action_frame_filter =
+ NULL;
+ vos_mem_free(action_frame_filter);
+ }
+
if (wlan_op_mode_ibss == txrx_vdev->opmode) {
wma->ibss_started = 0;
}
@@ -19595,17 +19833,29 @@
u_int8_t *buf_ptr;
u_int8_t i;
int ret;
+ wmi_vendor_oui *voui = NULL;
+ struct vendor_oui *pvoui = NULL;
+ connected_nlo_rssi_params *nlo_relative_rssi;
+ connected_nlo_bss_band_rssi_pref *nlo_band_rssi;
WMA_LOGD("PNO Start");
len = sizeof(*cmd) +
WMI_TLV_HDR_SIZE + /* TLV place holder for array of structures nlo_configured_parameters(nlo_list) */
- WMI_TLV_HDR_SIZE; /* TLV place holder for array of uint32 channel_list */
+ WMI_TLV_HDR_SIZE + /* TLV place holder for array of uint32 channel_list */
+ WMI_TLV_HDR_SIZE + /* TLV of nlo_channel_prediction_cfg */
+ WMI_TLV_HDR_SIZE + /* array of wmi_vendor_oui */
+ WMI_TLV_HDR_SIZE; /* array of connected_nlo_bss_band_rssi_pref */
len += sizeof(u_int32_t) * MIN(pno->aNetworks[0].ucChannelCount,
WMI_NLO_MAX_CHAN);
len += sizeof(nlo_configured_parameters) *
MIN(pno->ucNetworksCount, WMI_NLO_MAX_SSIDS);
+ /* Add the fixed length of enlo_candidate_score_params */
+ len += sizeof(enlo_candidate_score_params);
+ len += sizeof(wmi_vendor_oui) * pno->num_vendor_oui;
+ len += sizeof(connected_nlo_rssi_params);
+ len += sizeof(connected_nlo_bss_band_rssi_pref);
buf = wmi_buf_alloc(wma->wmi_handle, len);
if (!buf) {
@@ -19637,6 +19887,27 @@
cmd->fast_scan_period, cmd->slow_scan_period);
WMA_LOGD("fast_scan_max_cycles: %d", cmd->fast_scan_max_cycles);
+ if (pno->enable_pno_scan_randomization) {
+ cmd->flags |= WMI_NLO_CONFIG_SPOOFED_MAC_IN_PROBE_REQ |
+ WMI_NLO_CONFIG_RANDOM_SEQ_NO_IN_PROBE_REQ;
+ WMI_CHAR_ARRAY_TO_MAC_ADDR(pno->mac_addr, &cmd->mac_addr);
+ WMI_CHAR_ARRAY_TO_MAC_ADDR(pno->mac_addr_mask, &cmd->mac_mask);
+ }
+
+ if (pno->ie_whitelist)
+ cmd->flags |= WMI_NLO_CONFIG_ENABLE_IE_WHITELIST_IN_PROBE_REQ;
+ if(pno->relative_rssi_set)
+ cmd->flags |= WMI_NLO_CONFIG_ENABLE_CNLO_RSSI_CONFIG;
+
+ WMA_LOGI("pno flags = %x", cmd->flags);
+
+ cmd->num_vendor_oui = pno->num_vendor_oui;
+
+ if (pno->ie_whitelist) {
+ for (i = 0; i < PROBE_REQ_BITMAP_LEN; i++)
+ cmd->ie_bitmap[i] = pno->probe_req_ie_bitmap[i];
+ }
+
buf_ptr += sizeof(wmi_nlo_config_cmd_fixed_param);
cmd->no_of_ssids = MIN(pno->ucNetworksCount, WMI_NLO_MAX_SSIDS);
@@ -19697,6 +19968,76 @@
}
buf_ptr += cmd->num_of_channels * sizeof(u_int32_t);
+ /*
+ * For pno start, this is not needed but to get the correct offset of
+ * wmi_vendor_oui, this is needed
+ */
+ WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC, 0);
+ buf_ptr += WMI_TLV_HDR_SIZE; /* zero no.of nlo_channel_prediction_cfg */
+ WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_STRUC_enlo_candidate_score_param,
+ WMITLV_GET_STRUCT_TLVLEN(enlo_candidate_score_params));
+ buf_ptr += sizeof(enlo_candidate_score_params);
+
+ /* ie white list */
+ WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
+ pno->num_vendor_oui *
+ sizeof(wmi_vendor_oui));
+
+ buf_ptr += WMI_TLV_HDR_SIZE;
+
+ if (cmd->num_vendor_oui != 0) {
+ voui = (wmi_vendor_oui *)buf_ptr;
+ pvoui = (struct vendor_oui *)((uint8_t *)pno + sizeof(*pno));
+ for (i = 0; i < cmd->num_vendor_oui; i++) {
+ WMITLV_SET_HDR(&voui[i].tlv_header,
+ WMITLV_TAG_STRUC_wmi_vendor_oui,
+ WMITLV_GET_STRUCT_TLVLEN(
+ wmi_vendor_oui));
+ voui[i].oui_type_subtype = pvoui[i].oui_type |
+ (pvoui[i].oui_subtype << 24);
+ }
+ buf_ptr += cmd->num_vendor_oui * sizeof(wmi_vendor_oui);
+ }
+
+ /*
+ * Firmware calculation using connected PNO params:
+ * New AP's RSSI >= (Connected AP's RSSI + relative_rssi +/- rssi_pref)
+ * deduction of rssi_pref for chosen band_pref and
+ * addition of rssi_pref for remaining bands (other than chosen band).
+ */
+ nlo_relative_rssi = (connected_nlo_rssi_params *) buf_ptr;
+ WMITLV_SET_HDR(&nlo_relative_rssi->tlv_header,
+ WMITLV_TAG_STRUC_wmi_connected_nlo_rssi_params,
+ WMITLV_GET_STRUCT_TLVLEN(connected_nlo_rssi_params));
+ nlo_relative_rssi->relative_rssi = pno->relative_rssi;
+ WMA_LOGD("relative_rssi %d", nlo_relative_rssi->relative_rssi);
+ buf_ptr += sizeof(*nlo_relative_rssi);
+
+ /*
+ * As of now Kernel and Host supports one band and rssi preference.
+ * Firmware supports array of band and rssi preferences
+ */
+ cmd->num_cnlo_band_pref = 1;
+ WMITLV_SET_HDR(buf_ptr,
+ WMITLV_TAG_ARRAY_STRUC,
+ cmd->num_cnlo_band_pref *
+ sizeof(connected_nlo_bss_band_rssi_pref));
+ buf_ptr += WMI_TLV_HDR_SIZE;
+
+ nlo_band_rssi = (connected_nlo_bss_band_rssi_pref *) buf_ptr;
+ for (i = 0; i < cmd->num_cnlo_band_pref; i++) {
+ WMITLV_SET_HDR(&nlo_band_rssi[i].tlv_header,
+ WMITLV_TAG_STRUC_wmi_connected_nlo_bss_band_rssi_pref,
+ WMITLV_GET_STRUCT_TLVLEN(
+ connected_nlo_bss_band_rssi_pref));
+ nlo_band_rssi[i].band = pno->band_rssi_pref.band;
+ nlo_band_rssi[i].rssi_pref = pno->band_rssi_pref.rssi;
+ WMA_LOGD("band_pref %d, rssi_pref %d",
+ nlo_band_rssi[i].band,
+ nlo_band_rssi[i].rssi_pref);
+ }
+ buf_ptr += cmd->num_cnlo_band_pref * sizeof(*nlo_band_rssi);
+
/* TODO: Discrete firmware doesn't have command/option to configure
* App IE which comes from wpa_supplicant as of part PNO start request.
*/
@@ -20156,6 +20497,8 @@
return "REASSOC_RES_RECV";
case WOW_REASON_ACTION_FRAME_RECV:
return "ACTION_FRAME_RECV";
+ case WOW_REASON_OEM_RESPONSE_EVENT:
+ return "WOW_REASON_OEM_RESPONSE_EVENT";
}
return "unknown";
}
@@ -20657,7 +21000,7 @@
*/
static void wma_wow_wake_up_stats_display(tp_wma_handle wma)
{
- WMA_LOGA("uc %d bc %d v4_mc %d v6_mc %d ra %d ns %d na %d pno_match %d pno_complete %d gscan %d low_rssi %d rssi_breach %d icmp %d icmpv6 %d",
+ WMA_LOGA("uc %d bc %d v4_mc %d v6_mc %d ra %d ns %d na %d pno_match %d pno_complete %d gscan %d low_rssi %d rssi_breach %d icmp %d icmpv6 %d oem %d",
wma->wow_ucast_wake_up_count,
wma->wow_bcast_wake_up_count,
wma->wow_ipv4_mcast_wake_up_count,
@@ -20671,7 +21014,8 @@
wma->wow_low_rssi_wake_up_count,
wma->wow_rssi_breach_wake_up_count,
wma->wow_icmpv4_count,
- wma->wow_icmpv6_count);
+ wma->wow_icmpv6_count,
+ wma->wow_oem_response_wake_up_count);
return;
}
@@ -20796,6 +21140,10 @@
wma->wow_rssi_breach_wake_up_count++;
break;
+ case WOW_REASON_OEM_RESPONSE_EVENT:
+ wma->wow_oem_response_wake_up_count++;
+ break;
+
default:
WMA_LOGE("Unknown wake up reason");
break;
@@ -20901,6 +21249,14 @@
return "ICMPV6 REQUEST";
case ADF_PROTO_ICMPV6_RES:
return "ICMPV6 RESPONSE";
+ case ADF_PROTO_ICMPV6_RS:
+ return "ICMPV6 RS";
+ case ADF_PROTO_ICMPV6_RA:
+ return "ICMPV6 RA";
+ case ADF_PROTO_ICMPV6_NS:
+ return "ICMPV6 NS";
+ case ADF_PROTO_ICMPV6_NA:
+ return "ICMPV6 NA";
case ADF_PROTO_IPV4_UDP:
return "IPV4 UDP Packet";
case ADF_PROTO_IPV4_TCP:
@@ -20938,15 +21294,13 @@
adf_os_cpu_to_be16(ether_type)) {
if (len >= WMA_EAPOL_SUBTYPE_GET_MIN_LEN)
return adf_nbuf_data_get_eapol_subtype(data);
- VOS_TRACE(VOS_MODULE_ID_WDA,
- VOS_TRACE_LEVEL_ERROR, "EAPOL Packet");
+ WMA_LOGD("EAPOL Packet");
return ADF_PROTO_INVALID;
} else if (ADF_NBUF_TRAC_ARP_ETH_TYPE ==
adf_os_cpu_to_be16(ether_type)) {
if (len >= WMA_ARP_SUBTYPE_GET_MIN_LEN)
return adf_nbuf_data_get_arp_subtype(data);
- VOS_TRACE(VOS_MODULE_ID_WDA,
- VOS_TRACE_LEVEL_ERROR, "ARP Packet");
+ WMA_LOGD("ARP Packet");
return ADF_PROTO_INVALID;
} else if (ADF_NBUF_TRAC_IPV4_ETH_TYPE ==
adf_os_cpu_to_be16(ether_type)) {
@@ -20959,19 +21313,15 @@
if (len >= WMA_ICMP_SUBTYPE_GET_MIN_LEN)
return adf_nbuf_data_get_icmp_subtype(
data);
- VOS_TRACE(VOS_MODULE_ID_WDA,
- VOS_TRACE_LEVEL_ERROR, "ICMP Packet");
+ WMA_LOGD("ICMP Packet");
return ADF_PROTO_INVALID;
} else if (proto_type == ADF_NBUF_TRAC_UDP_TYPE) {
if (len >= WMA_IS_DHCP_GET_MIN_LEN) {
- if (adf_nbuf_data_is_dhcp_pkt(data) ==
- A_STATUS_OK) {
+ if (adf_nbuf_data_is_dhcp_pkt(data)) {
if (len >=
WMA_DHCP_SUBTYPE_GET_MIN_LEN)
return adf_nbuf_data_get_dhcp_subtype(data);
- VOS_TRACE(VOS_MODULE_ID_WDA,
- VOS_TRACE_LEVEL_ERROR,
- "DHCP Packet");
+ WMA_LOGD("DHCP Packet");
return ADF_PROTO_INVALID;
}
}
@@ -20980,8 +21330,7 @@
return ADF_PROTO_IPV4_TCP;
}
}
- VOS_TRACE(VOS_MODULE_ID_WDA,
- VOS_TRACE_LEVEL_ERROR, "IPV4 Packet");
+ WMA_LOGD("IPV4 Packet");
return ADF_PROTO_INVALID;
} else if (ADF_NBUF_TRAC_IPV6_ETH_TYPE ==
adf_os_cpu_to_be16(ether_type)) {
@@ -20989,13 +21338,12 @@
uint8_t proto_type;
proto_type = adf_nbuf_data_get_ipv6_proto(data);
- WMA_LOGE("IPV6_proto_type: %u", proto_type);
+ WMA_LOGD("IPV6_proto_type: %u", proto_type);
if (proto_type == ADF_NBUF_TRAC_ICMPV6_TYPE) {
if (len >= WMA_ICMPV6_SUBTYPE_GET_MIN_LEN)
return adf_nbuf_data_get_icmpv6_subtype(
data);
- VOS_TRACE(VOS_MODULE_ID_WDA,
- VOS_TRACE_LEVEL_ERROR, "ICMPV6 Packet");
+ WMA_LOGD("ICMPV6 Packet");
return ADF_PROTO_INVALID;
} else if (proto_type == ADF_NBUF_TRAC_UDP_TYPE) {
return ADF_PROTO_IPV6_UDP;
@@ -21003,8 +21351,7 @@
return ADF_PROTO_IPV6_TCP;
}
}
- VOS_TRACE(VOS_MODULE_ID_WDA,
- VOS_TRACE_LEVEL_ERROR, "IPV6 Packet");
+ WMA_LOGD("IPV6 Packet");
return ADF_PROTO_INVALID;
}
@@ -21052,7 +21399,7 @@
EAPOL_PKT_LEN_OFFSET));
key_len = (uint16_t)(*(uint16_t *)(data +
EAPOL_KEY_LEN_OFFSET));
- WMA_LOGE("Pkt_len: %u, Key_len: %u",
+ WMA_LOGD("Pkt_len: %u, Key_len: %u",
adf_os_cpu_to_be16(pkt_len),
adf_os_cpu_to_be16(key_len));
}
@@ -21073,7 +21420,7 @@
DHCP_PKT_LEN_OFFSET));
transaction_id = (uint32_t)(*(uint32_t *)(data +
DHCP_TRANSACTION_ID_OFFSET));
- WMA_LOGE("Pkt_len: %u, Transaction_id: %u",
+ WMA_LOGD("Pkt_len: %u, Transaction_id: %u",
adf_os_cpu_to_be16(pkt_len),
adf_os_cpu_to_be32(transaction_id));
}
@@ -21094,7 +21441,7 @@
IPV4_PKT_LEN_OFFSET));
seq_num = (uint16_t)(*(uint16_t *)(data +
ICMP_SEQ_NUM_OFFSET));
- WMA_LOGE("Pkt_len: %u, Seq_num: %u",
+ WMA_LOGD("Pkt_len: %u, Seq_num: %u",
adf_os_cpu_to_be16(pkt_len),
adf_os_cpu_to_be16(seq_num));
}
@@ -21102,14 +21449,18 @@
case ADF_PROTO_ICMPV6_REQ:
case ADF_PROTO_ICMPV6_RES:
- WMA_LOGE("WOW %s rcvd",
+ case ADF_PROTO_ICMPV6_RS:
+ case ADF_PROTO_ICMPV6_RA:
+ case ADF_PROTO_ICMPV6_NS:
+ case ADF_PROTO_ICMPV6_NA:
+ WMA_LOGD("WOW Wakeup: %s rcvd",
wma_pkt_proto_subtype_to_string(proto_subtype));
if (buf_len >= WMA_IPV6_PKT_INFO_GET_MIN_LEN) {
pkt_len = (uint16_t)(*(uint16_t *)(data +
IPV6_PKT_LEN_OFFSET));
seq_num = (uint16_t)(*(uint16_t *)(data +
ICMPV6_SEQ_NUM_OFFSET));
- WMA_LOGE("len: %u, SN: %u",
+ WMA_LOGD("len: %u, SN: %u",
adf_os_cpu_to_be16(pkt_len),
adf_os_cpu_to_be16(seq_num));
}
@@ -21267,6 +21618,12 @@
case WOW_REASON_DISASSOC_RECVD:
wake_lock_duration = WMA_DISASSOC_RECV_WAKE_LOCK_DURATION;
break;
+ case WOW_REASON_AP_ASSOC_LOST:
+ wake_lock_duration = WMA_BMISS_EVENT_WAKE_LOCK_DURATION;
+ break;
+ case WOW_REASON_HOST_AUTO_SHUTDOWN:
+ wake_lock_duration = WMA_AUTO_SHUTDOWN_WAKE_LOCK_DURATION;
+ break;
default:
break;
}
@@ -21274,6 +21631,40 @@
return wake_lock_duration;
}
+/**
+ * wma_wow_get_wakelock() - return the wakelock
+ * for some mgmt packets received.
+ * @wma_handle: wma handle
+ * @wake_reason: wow wakeup reason
+ *
+ * This function returns the wakelock for some mgmt packets
+ * received while in wow suspend.
+ *
+ * Return: wakelock
+ */
+static vos_wake_lock_t *wma_wow_get_wakelock(tp_wma_handle wma_handle,
+ int wake_reason)
+{
+
+ switch (wake_reason) {
+ case WOW_REASON_AUTH_REQ_RECV:
+ return &wma_handle->wow_auth_req_wl;
+ case WOW_REASON_ASSOC_REQ_RECV:
+ return &wma_handle->wow_assoc_req_wl;
+ case WOW_REASON_DEAUTH_RECVD:
+ return &wma_handle->wow_deauth_rec_wl;
+ case WOW_REASON_DISASSOC_RECVD:
+ return &wma_handle->wow_disassoc_rec_wl;
+ case WOW_REASON_AP_ASSOC_LOST:
+ return &wma_handle->wow_ap_assoc_lost_wl;
+ case WOW_REASON_HOST_AUTO_SHUTDOWN:
+ return &wma_handle->wow_auto_shutdown_wl;
+ default:
+ return NULL;
+ }
+
+}
+
/*
* Handler to catch wow wakeup host event. This event will have
* reason why the firmware has woken the host.
@@ -21319,8 +21710,6 @@
case WOW_REASON_REASSOC_RES_RECV:
case WOW_REASON_BEACON_RECV:
case WOW_REASON_ACTION_FRAME_RECV:
- wake_lock_duration =
- wma_wow_get_wakelock_duration(wake_info->wake_reason);
if (param_buf->wow_packet_buffer) {
/* First 4-bytes of wow_packet_buffer is the length */
vos_mem_copy((uint8_t *) &wow_buf_pkt_len,
@@ -21337,7 +21726,6 @@
break;
case WOW_REASON_AP_ASSOC_LOST:
- wake_lock_duration = WMA_BMISS_EVENT_WAKE_LOCK_DURATION;
wma_wow_ap_lost_helper(wma, param_buf);
break;
#ifdef FEATURE_WLAN_RA_FILTERING
@@ -21347,7 +21735,6 @@
#endif
#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
case WOW_REASON_HOST_AUTO_SHUTDOWN:
- wake_lock_duration = WMA_AUTO_SHUTDOWN_WAKE_LOCK_DURATION;
WMA_LOGA("Received WOW Auto Shutdown trigger in suspend");
if (wma_post_auto_shutdown_msg())
return -EINVAL;
@@ -21557,7 +21944,7 @@
break;
#endif
case WOW_REASON_NAN_DATA:
- WMA_LOGD(FL("Host woken up for NAN data path event from FW"));
+ WMA_LOGD(FL("Host woken up for NAN data event from FW"));
if (param_buf->wow_packet_buffer) {
wow_buf_pkt_len =
*(uint32_t *)param_buf->wow_packet_buffer;
@@ -21587,15 +21974,41 @@
}
break;
#endif
+
+ case WOW_REASON_OEM_RESPONSE_EVENT:
+ wma_wow_wake_up_stats(wma, NULL, 0,
+ WOW_REASON_OEM_RESPONSE_EVENT);
+ if (param_buf->wow_packet_buffer) {
+ WMA_LOGD(FL("Host woken up by OEM Response event"));
+ wow_buf_pkt_len =
+ *(uint32_t *)param_buf->wow_packet_buffer;
+ vos_trace_hex_dump(VOS_MODULE_ID_WDA,
+ VOS_TRACE_LEVEL_DEBUG,
+ param_buf->wow_packet_buffer,
+ wow_buf_pkt_len);
+ wma_oem_data_response_handler(handle,
+ (uint8_t*)(param_buf->wow_packet_buffer),
+ wow_buf_pkt_len);
+ } else {
+ WMA_LOGD(FL("No wow_packet_buffer for OEM response"));
+ }
+ break;
+
default:
break;
}
+ wake_lock_duration =
+ wma_wow_get_wakelock_duration(wake_info->wake_reason);
if (wake_lock_duration) {
- vos_wake_lock_timeout_acquire(&wma->wow_wake_lock,
+ vos_wake_lock_t *wake_lock = wma_wow_get_wakelock(wma,
+ wake_info->wake_reason);
+ if (wake_lock) {
+ vos_wake_lock_timeout_acquire(wake_lock,
wake_lock_duration,
WIFI_POWER_EVENT_WAKELOCK_WOW);
- WMA_LOGA("Holding %d msec wake_lock", wake_lock_duration);
+ WMA_LOGA("Holding %d msec wake_lock", wake_lock_duration);
+ }
}
return 0;
@@ -21676,6 +22089,8 @@
return "WOW_NAN_DATA_EVENT";
case WOW_TDLS_CONN_TRACKER_EVENT:
return "WOW_TDLS_CONN_TRACKER_EVENT";
+ case WOW_OEM_RESPONSE_EVENT:
+ return "WOW_OEM_RESPONSE_EVENT";
default:
return "UNSPECIFIED_EVENT";
}
@@ -23089,7 +23504,15 @@
* suspend indication received on last vdev before
* enabling wow in fw.
*/
- if (wma->no_of_suspend_ind < wma_get_vdev_count(wma)) {
+ /*
+ * While processing suspend indication, there is possibility of
+ * vdev(SAP/P2P-GO) deletion due to stop_ap. This may lead, Host to
+ * send WoW indication twice to FW, as vdev is one but HDD sends
+ * suspend indication twice to WMA. To fix this race condition check
+ * for wow_enable along with vdev count.
+ */
+ if (wma->no_of_suspend_ind < wma_get_vdev_count(wma) ||
+ wma->wow.wow_enable) {
vos_mem_free(info);
return VOS_STATUS_SUCCESS;
}
@@ -23112,6 +23535,9 @@
* 2) Is any one of vdev in connected state (in STA mode) ?
* 3) Is PNO in progress in any one of vdev ?
* 4) Is Extscan in progress in any one of vdev ?
+ * 5) Is any vdev in NAN data mode? BSS is already started at the
+ * the time of device creation. It is ready to accept data
+ * requests.
*/
for (i = 0; i < wma->max_bssid; i++) {
if ((wma_is_vdev_in_ap_mode(wma, i)
@@ -23125,6 +23551,11 @@
i);
enable_wow = true;
}
+ if (WMA_IS_VDEV_IN_NDI_MODE(wma->interfaces, i)) {
+ WMA_LOGD("vdev %d is in NAN data mode, enabling wow",
+ i);
+ enable_wow = true;
+ }
}
for (i = 0; i < wma->max_bssid; i++) {
if (wma->interfaces[i].conn_state)
@@ -27052,7 +27483,6 @@
cmd->scan_ctrl_flags = WMI_SCAN_ADD_BCAST_PROBE_REQ |
WMI_SCAN_ADD_CCK_RATES |
WMI_SCAN_ADD_OFDM_RATES |
- WMI_SCAN_ADD_SPOOFED_MAC_IN_PROBE_REQ |
WMI_SCAN_ADD_DS_IE_IN_PROBE_REQ;
cmd->scan_priority = WMI_SCAN_PRIORITY_HIGH;
cmd->num_ssids = 0;
@@ -28282,13 +28712,17 @@
uint32_t len;
u_int8_t *buf_ptr;
u_int32_t *oui_buf;
+ uint32_t i = 0;
+ wmi_vendor_oui *voui = NULL;
+ struct vendor_oui *pvoui = NULL;
if (!wma || !wma->wmi_handle) {
WMA_LOGE("%s: WMA is closed, can not issue cmd",
__func__);
return VOS_STATUS_E_INVAL;
}
- len = sizeof(*cmd);
+ len = sizeof(*cmd) + WMI_TLV_HDR_SIZE +
+ psetoui->num_vendor_oui * sizeof(wmi_vendor_oui);
wmi_buf = wmi_buf_alloc(wma->wmi_handle, len);
if (!wmi_buf) {
WMA_LOGE("%s: wmi_buf_alloc failed", __func__);
@@ -28308,6 +28742,44 @@
WMA_LOGD("%s: wma:oui received from hdd %08x", __func__,
cmd->prob_req_oui);
+ cmd->vdev_id = psetoui->vdev_id;
+ cmd->flags = WMI_SCAN_PROBE_OUI_SPOOFED_MAC_IN_PROBE_REQ;
+ if (psetoui->enb_probe_req_sno_randomization)
+ cmd->flags |= WMI_SCAN_PROBE_OUI_RANDOM_SEQ_NO_IN_PROBE_REQ;
+
+ if (psetoui->ie_whitelist)
+ cmd->flags |=
+ WMI_SCAN_PROBE_OUI_ENABLE_IE_WHITELIST_IN_PROBE_REQ;
+
+ WMA_LOGI(FL("vdev_id = %d, flags = %x"), cmd->vdev_id, cmd->flags);
+
+ cmd->num_vendor_oui = psetoui->num_vendor_oui;
+
+ if (psetoui->ie_whitelist) {
+ for (i = 0; i < PROBE_REQ_BITMAP_LEN; i++)
+ cmd->ie_bitmap[i] = psetoui->probe_req_ie_bitmap[i];
+ }
+
+ buf_ptr += sizeof(*cmd);
+ WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
+ psetoui->num_vendor_oui *
+ sizeof(wmi_vendor_oui));
+
+ buf_ptr += WMI_TLV_HDR_SIZE;
+ if (cmd->num_vendor_oui != 0) {
+ voui = (wmi_vendor_oui *)buf_ptr;
+ pvoui = (struct vendor_oui *)((u_int8_t *)psetoui +
+ sizeof(*psetoui));
+ for (i = 0; i < cmd->num_vendor_oui; i++) {
+ WMITLV_SET_HDR(&voui[i].tlv_header,
+ WMITLV_TAG_STRUC_wmi_vendor_oui,
+ WMITLV_GET_STRUCT_TLVLEN(
+ wmi_vendor_oui));
+ voui[i].oui_type_subtype = pvoui[i].oui_type |
+ (pvoui[i].oui_subtype << 24);
+ }
+ }
+
if (wmi_unified_cmd_send(wma->wmi_handle, wmi_buf, len,
WMI_SCAN_PROB_REQ_OUI_CMDID)) {
WMA_LOGE("%s: failed to send command", __func__);
@@ -29911,6 +30383,16 @@
wake_lock_stats->wow_icmpv4_count = wma_handle->wow_icmpv4_count;
wake_lock_stats->wow_icmpv6_count =
wma_handle->wow_icmpv6_count;
+ wake_lock_stats->wow_rssi_breach_wake_up_count =
+ wma_handle->wow_rssi_breach_wake_up_count;
+ wake_lock_stats->wow_low_rssi_wake_up_count =
+ wma_handle->wow_low_rssi_wake_up_count;
+ wake_lock_stats->wow_gscan_wake_up_count =
+ wma_handle->wow_gscan_wake_up_count;
+ wake_lock_stats->wow_pno_complete_wake_up_count =
+ wma_handle->wow_pno_complete_wake_up_count;
+ wake_lock_stats->wow_pno_match_wake_up_count =
+ wma_handle->wow_pno_match_wake_up_count;
return VOS_STATUS_SUCCESS;
}
@@ -30054,6 +30536,152 @@
return;
}
+/**
+ * wma_process_action_frame_random_mac() - set/clear action frame random mac
+ * @wma_handle: pointer to wma handle
+ * @filter: pointer to buffer containing random mac, session_id and callback
+ *
+ * Return: VOS_STATUS_SUCCESS for success or error code.
+ */
+static VOS_STATUS
+wma_process_action_frame_random_mac(tp_wma_handle wma_handle,
+ struct action_frame_random_filter *filter)
+{
+ wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param *cmd;
+ uint32_t len;
+ wmi_buf_t buf;
+ int ret;
+ struct action_frame_random_filter *filter_bkup = NULL;
+ struct wma_txrx_node *intr = NULL;
+
+ if (!filter) {
+ WMA_LOGE(FL("invalid pointer"));
+ return VOS_STATUS_E_INVAL;
+ }
+
+ if (!wma_handle) {
+ WMA_LOGE(FL("WMA context is invald!"));
+ return VOS_STATUS_E_INVAL;
+ }
+
+ if (filter->filter_type == SME_ACTION_FRAME_RANDOM_MAC_SET) {
+ intr = &wma_handle->interfaces[filter->session_id];
+ /* command is in progess */
+ if(intr->action_frame_filter != NULL) {
+ WMA_LOGE(FL("previous action frame req is pending"));
+ return VOS_STATUS_SUCCESS;
+ }
+
+ filter_bkup = adf_os_mem_alloc(NULL, sizeof(*filter));
+ if (!filter_bkup) {
+ WMA_LOGE(
+ FL("action frame filter mem alloc failed"));
+ return VOS_STATUS_E_FAILURE;
+ }
+
+ adf_os_mem_set(filter_bkup, 0, sizeof(*filter));
+ filter_bkup->session_id = filter->session_id;
+ filter_bkup->callback = filter->callback;
+ filter_bkup->filter_type = filter->filter_type;
+ filter_bkup->context = filter->context;
+
+ vos_mem_copy(filter_bkup->mac_addr, filter->mac_addr,
+ VOS_MAC_ADDR_SIZE);
+ intr->action_frame_filter = (void *)filter_bkup;
+ }
+
+ len = sizeof(*cmd);
+ buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
+
+ if (!buf) {
+ WMA_LOGE(FL("Failed allocate wmi buffer"));
+ return VOS_STATUS_E_NOMEM;
+ }
+ cmd = (wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param *)
+ wmi_buf_data(buf);
+
+ WMITLV_SET_HDR(&cmd->tlv_header,
+ WMITLV_TAG_STRUC_wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param,
+ WMITLV_GET_STRUCT_TLVLEN(wmi_vdev_add_mac_addr_to_rx_filter_cmd_fixed_param));
+
+ cmd->vdev_id = filter->session_id;
+ WMI_CHAR_ARRAY_TO_MAC_ADDR(filter->mac_addr, &cmd->mac_addr);
+ if (filter->filter_type == SME_ACTION_FRAME_RANDOM_MAC_SET)
+ cmd->enable = 1;
+ else
+ cmd->enable = 0;
+
+ ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
+ WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID);
+ if (ret) {
+ WMA_LOGE(FL("Failed to send action frame random mac cmd"));
+ wmi_buf_free(buf);
+ if (filter->filter_type == SME_ACTION_FRAME_RANDOM_MAC_SET) {
+ adf_os_mem_free(filter_bkup);
+ intr->action_frame_filter = NULL;
+ }
+ return VOS_STATUS_E_FAILURE;
+ }
+
+ return VOS_STATUS_SUCCESS;
+}
+
+/**
+ * wma_process_power_debug_stats_req() - Process the Chip Power stats collect
+ * request and pass the Power stats request to Fw
+ * @wma_handle: WMA handle
+ *
+ * Return: VOS_STATUS
+ */
+#ifdef WLAN_POWER_DEBUGFS
+static VOS_STATUS wma_process_power_debug_stats_req(tp_wma_handle wma_handle)
+{
+ wmi_pdev_get_chip_power_stats_cmd_fixed_param *cmd;
+ int32_t len;
+ wmi_buf_t buf;
+ uint8_t *buf_ptr;
+ int ret;
+
+ if (!wma_handle) {
+ WMA_LOGE("%s: input pointer is NULL", __func__);
+ return VOS_STATUS_E_FAILURE;
+ }
+
+ len = sizeof(*cmd);
+ buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
+
+ if (!buf) {
+ WMA_LOGE("%s: Failed allocate wmi buffer", __func__);
+ return VOS_STATUS_E_NOMEM;
+ }
+
+ buf_ptr = (u_int8_t *) wmi_buf_data(buf);
+ cmd = (wmi_pdev_get_chip_power_stats_cmd_fixed_param *) buf_ptr;
+
+ WMITLV_SET_HDR(&cmd->tlv_header,
+ WMITLV_TAG_STRUC_wmi_get_chip_power_stats_cmd_fixed_param,
+ WMITLV_GET_STRUCT_TLVLEN(
+ wmi_pdev_get_chip_power_stats_cmd_fixed_param));
+ cmd->pdev_id = 0;
+
+ WMA_LOGD("POWER_DEBUG_STATS - Get Request Params; Pdev id - %d",
+ cmd->pdev_id);
+ ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
+ WMI_PDEV_GET_CHIP_POWER_STATS_CMDID);
+ if (ret) {
+ WMA_LOGE("%s: Failed to send power debug stats request",
+ __func__);
+ wmi_buf_free(buf);
+ return VOS_STATUS_E_FAILURE;
+ }
+ return VOS_STATUS_SUCCESS;
+}
+#else
+static VOS_STATUS wma_process_power_debug_stats_req(tp_wma_handle wma_handle)
+{
+ return VOS_STATUS_SUCCESS;
+}
+#endif
/*
* function : wma_mc_process_msg
@@ -30923,8 +31551,19 @@
case SIR_HAL_SET_ALLOWED_ACTION_FRAMES:
wma_process_set_allowed_action_frames_ind(wma_handle,
msg->bodyptr);
+ break;
+ case SIR_HAL_NDP_END_REQ:
+ wma_handle_ndp_end_req(wma_handle, msg->bodyptr);
vos_mem_free(msg->bodyptr);
break;
+ case WDA_ACTION_FRAME_RANDOM_MAC:
+ wma_process_action_frame_random_mac(wma_handle,
+ (struct action_frame_random_filter *)msg->bodyptr);
+ vos_mem_free(msg->bodyptr);
+ break;
+ case SIR_HAL_POWER_DEBUG_STATS_REQ:
+ wma_process_power_debug_stats_req(wma_handle);
+ break;
default:
WMA_LOGD("unknow msg type %x", msg->type);
/* Do Nothing? MSG Body should be freed at here */
@@ -31340,6 +31979,9 @@
WMA_LOGD("%s: Reason %x for vdevid %x, rssi %d",
__func__, wmi_event->reason, wmi_event->vdev_id, wmi_event->rssi);
+ DPTRACE(adf_dp_trace_record_event(ADF_DP_TRACE_EVENT_RECORD,
+ wmi_event->vdev_id, ADF_PROTO_TYPE_EVENT, ADF_ROAM_EVENTID));
+
switch(wmi_event->reason) {
case WMI_ROAM_REASON_BMISS:
WMA_LOGD("Beacon Miss for vdevid %x",
@@ -32917,7 +33559,14 @@
vos_wake_lock_destroy(&wma_handle->extscan_wake_lock);
#endif
vos_wake_lock_destroy(&wma_handle->wow_wake_lock);
+ vos_wake_lock_destroy(&wma_handle->wow_auth_req_wl);
+ vos_wake_lock_destroy(&wma_handle->wow_assoc_req_wl);
+ vos_wake_lock_destroy(&wma_handle->wow_deauth_rec_wl);
+ vos_wake_lock_destroy(&wma_handle->wow_disassoc_rec_wl);
+ vos_wake_lock_destroy(&wma_handle->wow_ap_assoc_lost_wl);
+ vos_wake_lock_destroy(&wma_handle->wow_auto_shutdown_wl);
}
+
vos_mem_zero(&wma_handle->wow, sizeof(struct wma_wow));
wma_runtime_context_deinit(wma_handle);
@@ -36239,7 +36888,7 @@
__func__, iface->handle);
return;
}
- WMA_LOGA("%s: vdevid %d bssid %pM", __func__, vdev_id, iface->bssid);
+ WMA_LOGI("%s: vdevid %d bssid %pM", __func__, vdev_id, iface->bssid);
iface->pause_bitmap |= (1 << PAUSE_TYPE_HOST);
wdi_in_vdev_pause(iface->handle, OL_TXQ_PAUSE_REASON_TX_ABORT);
@@ -36462,6 +37111,10 @@
wmi_buf_free(wmi_buf);
return;
}
+
+ DPTRACE(adf_dp_trace_record_event(ADF_DP_TRACE_EVENT_RECORD,
+ synchcnf->sessionId, ADF_PROTO_TYPE_EVENT, ADF_ROAM_COMPLETE));
+
return;
}
void wma_process_roam_synch_fail(WMA_HANDLE handle,
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.h
index d88e9e0..7ff456d 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -614,7 +614,7 @@
uint8_t wep_default_key_idx;
bool is_vdev_valid;
-
+ struct action_frame_random_filter *action_frame_filter;
};
#if defined(QCA_WIFI_FTM)
@@ -813,6 +813,13 @@
vos_wake_lock_t extscan_wake_lock;
#endif
vos_wake_lock_t wow_wake_lock;
+ vos_wake_lock_t wow_auth_req_wl;
+ vos_wake_lock_t wow_assoc_req_wl;
+ vos_wake_lock_t wow_deauth_rec_wl;
+ vos_wake_lock_t wow_disassoc_rec_wl;
+ vos_wake_lock_t wow_ap_assoc_lost_wl;
+ vos_wake_lock_t wow_auto_shutdown_wl;
+
int wow_nack;
u_int32_t ap_client_cnt;
adf_os_atomic_t is_wow_bus_suspended;
@@ -887,6 +894,7 @@
uint32_t wow_ipv6_mcast_na_stats;
uint32_t wow_icmpv4_count;
uint32_t wow_icmpv6_count;
+ uint32_t wow_oem_response_wake_up_count;
uint32_t wow_wakeup_enable_mask;
uint32_t wow_wakeup_disable_mask;
uint16_t max_mgmt_tx_fail_count;
@@ -1435,6 +1443,8 @@
#define WMA_DISASSOC_RECV_WAKE_LOCK_DURATION (5 * 1000) /* in msec */
#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
#define WMA_AUTO_SHUTDOWN_WAKE_LOCK_DURATION (5 * 1000) /* in msec */
+#else
+#define WMA_AUTO_SHUTDOWN_WAKE_LOCK_DURATION 0
#endif
#define WMA_BMISS_EVENT_WAKE_LOCK_DURATION (4 * 1000) /* in msec */
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.c
index ee418e8..7802220 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.c
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.c
@@ -43,30 +43,30 @@
VOS_STATUS status;
int ret;
uint16_t len;
- uint32_t vdev_id, ndp_cfg_len, ndp_app_info_len;
+ uint32_t vdev_id, ndp_cfg_len, ndp_app_info_len, pmk_len;
struct ndp_initiator_rsp *rsp = NULL;
- uint8_t *cfg_info, *app_info;
ol_txrx_vdev_handle vdev;
wmi_buf_t buf;
wmi_ndp_initiator_req_fixed_param *cmd;
vos_msg_t pe_msg = {0};
struct ndp_initiator_req *ndp_req = req;
wmi_channel *ch_tlv;
+ uint8_t *tlv_ptr;
if (NULL == ndp_req) {
WMA_LOGE(FL("Invalid ndp_req."));
- goto send_ndi_initiator_fail;
+ goto send_ndp_initiator_fail;
}
vdev_id = ndp_req->vdev_id;
vdev = wma_find_vdev_by_id(wma_handle, vdev_id);
if (!vdev) {
WMA_LOGE(FL("vdev not found for vdev id %d."), vdev_id);
- goto send_ndi_initiator_fail;
+ goto send_ndp_initiator_fail;
}
if (!WMA_IS_VDEV_IN_NDI_MODE(wma_handle->interfaces, vdev_id)) {
WMA_LOGE(FL("vdev :%d, not in NDI mode"), vdev_id);
- goto send_ndi_initiator_fail;
+ goto send_ndp_initiator_fail;
}
/*
@@ -75,13 +75,15 @@
*/
ndp_cfg_len = vos_roundup(ndp_req->ndp_config.ndp_cfg_len, 4);
ndp_app_info_len = vos_roundup(ndp_req->ndp_info.ndp_app_info_len, 4);
+ pmk_len = vos_roundup(ndp_req->pmk.pmk_len, 4);
/* allocated memory for fixed params as well as variable size data */
- len = sizeof(*cmd) + ndp_cfg_len + ndp_app_info_len +
- (2 * WMI_TLV_HDR_SIZE) + sizeof(*ch_tlv);
+ len = sizeof(*cmd) + sizeof(*ch_tlv) + (3 * WMI_TLV_HDR_SIZE)
+ + ndp_cfg_len + ndp_app_info_len + pmk_len;
+
buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
if (!buf) {
WMA_LOGE(FL("wmi_buf_alloc failed"));
- goto send_ndi_initiator_fail;
+ goto send_ndp_initiator_fail;
}
cmd = (wmi_ndp_initiator_req_fixed_param *) wmi_buf_data(buf);
WMITLV_SET_HDR(&cmd->tlv_header,
@@ -96,6 +98,9 @@
cmd->ndp_cfg_len = ndp_req->ndp_config.ndp_cfg_len;
cmd->ndp_app_info_len = ndp_req->ndp_info.ndp_app_info_len;
+ cmd->ndp_channel_cfg = ndp_req->channel_cfg;
+ cmd->nan_pmk_len = ndp_req->pmk.pmk_len;
+ cmd->nan_csid = ndp_req->ncs_sk_type;
ch_tlv = (wmi_channel *)&cmd[1];
WMITLV_SET_HDR(ch_tlv, WMITLV_TAG_STRUC_wmi_channel,
@@ -103,21 +108,26 @@
ch_tlv->mhz = ndp_req->channel;
ch_tlv->band_center_freq1 =
vos_chan_to_freq(vos_freq_to_chan(ndp_req->channel));
+ tlv_ptr = (uint8_t *)&ch_tlv[1];
- cfg_info = (uint8_t *)&ch_tlv[1];
- WMITLV_SET_HDR(cfg_info, WMITLV_TAG_ARRAY_BYTE, ndp_cfg_len);
- vos_mem_copy(&cfg_info[WMI_TLV_HDR_SIZE], ndp_req->ndp_config.ndp_cfg,
- cmd->ndp_cfg_len);
+ WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, ndp_cfg_len);
+ vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE],
+ ndp_req->ndp_config.ndp_cfg, cmd->ndp_cfg_len);
+ tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + ndp_cfg_len;
- app_info = &cfg_info[WMI_TLV_HDR_SIZE + ndp_cfg_len];
- WMITLV_SET_HDR(app_info, WMITLV_TAG_ARRAY_BYTE, ndp_app_info_len);
- vos_mem_copy(&app_info[WMI_TLV_HDR_SIZE],
- ndp_req->ndp_info.ndp_app_info,
- cmd->ndp_app_info_len);
+ WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, ndp_app_info_len);
+ vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE],
+ ndp_req->ndp_info.ndp_app_info, cmd->ndp_app_info_len);
+ tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + ndp_app_info_len;
- WMA_LOGE(FL("vdev_id = %d, transaction_id: %d, service_instance_id, %d channel: %d"),
+ WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, pmk_len);
+ vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE], ndp_req->pmk.pmk,
+ cmd->nan_pmk_len);
+ tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + pmk_len;
+
+ WMA_LOGE(FL("vdev_id = %d, transaction_id: %d, service_instance_id: %d, ch: %d, ch_cfg: %d, csid: %d"),
cmd->vdev_id, cmd->transaction_id, cmd->service_instance_id,
- ch_tlv->mhz);
+ ch_tlv->mhz, cmd->ndp_channel_cfg, cmd->nan_csid);
WMA_LOGE(FL("peer mac addr: mac_addr31to0: 0x%x, mac_addr47to32: 0x%x"),
cmd->peer_discovery_mac_addr.mac_addr31to0,
cmd->peer_discovery_mac_addr.mac_addr47to32);
@@ -132,20 +142,24 @@
ndp_req->ndp_info.ndp_app_info,
ndp_req->ndp_info.ndp_app_info_len);
+ WMA_LOGE(FL("pmk len: %d"), cmd->nan_pmk_len);
+ VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
+ ndp_req->pmk.pmk, cmd->nan_pmk_len);
WMA_LOGE(FL("sending WMI_NDP_INITIATOR_REQ_CMDID(0x%X)"),
WMI_NDP_INITIATOR_REQ_CMDID);
+
ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
WMI_NDP_INITIATOR_REQ_CMDID);
if (ret < 0) {
WMA_LOGE(FL("WMI_NDP_INITIATOR_REQ_CMDID failed, ret: %d"),
ret);
wmi_buf_free(buf);
- goto send_ndi_initiator_fail;
+ goto send_ndp_initiator_fail;
}
return VOS_STATUS_SUCCESS;
-send_ndi_initiator_fail:
+send_ndp_initiator_fail:
status = VOS_STATUS_E_FAILURE;
if (ndp_req) {
rsp = vos_mem_malloc(sizeof(*rsp));
@@ -158,7 +172,8 @@
rsp->vdev_id = ndp_req->vdev_id;
rsp->transaction_id = ndp_req->transaction_id;
rsp->ndp_instance_id = ndp_req->service_instance_id;
- rsp->status = NDP_CMD_RSP_STATUS_ERROR;
+ rsp->status = NDP_RSP_STATUS_ERROR;
+ rsp->reason = NDP_DATA_INITIATOR_REQ_FAILED;
}
} else {
/* unblock SME queue, but do not send rsp to HDD */
@@ -228,8 +243,8 @@
{
wmi_buf_t buf;
ol_txrx_vdev_handle vdev;
- uint32_t vdev_id = 0, ndp_cfg_len, ndp_app_info_len;
- uint8_t *cfg_info, *app_info;
+ uint32_t vdev_id = 0, ndp_cfg_len, ndp_app_info_len, pmk_len;
+ uint8_t *tlv_ptr;
int ret;
wmi_ndp_responder_req_fixed_param *cmd;
uint16_t len;
@@ -249,12 +264,12 @@
vdev = wma_find_vdev_by_id(wma_handle, vdev_id);
if (!vdev) {
WMA_LOGE(FL("vdev not found for vdev id %d."), vdev_id);
- goto send_ndi_responder_fail;
+ goto send_ndp_responder_fail;
}
if (!WMA_IS_VDEV_IN_NDI_MODE(wma_handle->interfaces, vdev_id)) {
WMA_LOGE(FL("vdev :$%d, not in NDI mode"), vdev_id);
- goto send_ndi_responder_fail;
+ goto send_ndp_responder_fail;
}
/*
@@ -263,13 +278,15 @@
*/
ndp_cfg_len = vos_roundup(req_params->ndp_config.ndp_cfg_len, 4);
ndp_app_info_len = vos_roundup(req_params->ndp_info.ndp_app_info_len, 4);
+ pmk_len = vos_roundup(req_params->pmk.pmk_len, 4);
/* allocated memory for fixed params as well as variable size data */
- len = sizeof(*cmd) + ndp_cfg_len + ndp_app_info_len +
- (2 * WMI_TLV_HDR_SIZE);
+ len = sizeof(*cmd) + 3*WMI_TLV_HDR_SIZE + ndp_cfg_len + ndp_app_info_len
+ + pmk_len;
+
buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
if (!buf) {
WMA_LOGE(FL("wmi_buf_alloc failed"));
- goto send_ndi_responder_fail;
+ goto send_ndp_responder_fail;
}
cmd = (wmi_ndp_responder_req_fixed_param *) wmi_buf_data(buf);
WMITLV_SET_HDR(&cmd->tlv_header,
@@ -283,19 +300,29 @@
cmd->ndp_cfg_len = req_params->ndp_config.ndp_cfg_len;
cmd->ndp_app_info_len = req_params->ndp_info.ndp_app_info_len;
+ cmd->nan_pmk_len = req_params->pmk.pmk_len;
+ cmd->nan_csid = req_params->ncs_sk_type;
- cfg_info = (uint8_t *)&cmd[1];
- /* WMI command expects 4 byte alligned len */
- WMITLV_SET_HDR(cfg_info, WMITLV_TAG_ARRAY_BYTE, ndp_cfg_len);
- vos_mem_copy(&cfg_info[WMI_TLV_HDR_SIZE],
- req_params->ndp_config.ndp_cfg, cmd->ndp_cfg_len);
+ tlv_ptr = (uint8_t *)&cmd[1];
- app_info = &cfg_info[WMI_TLV_HDR_SIZE + ndp_cfg_len];
- /* WMI command expects 4 byte alligned len */
- WMITLV_SET_HDR(app_info, WMITLV_TAG_ARRAY_BYTE, ndp_app_info_len);
- vos_mem_copy(&app_info[WMI_TLV_HDR_SIZE],
+ WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, ndp_cfg_len);
+ vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE],
+ req_params->ndp_config.ndp_cfg, cmd->ndp_cfg_len);
+ tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + ndp_cfg_len;
+
+ WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, ndp_app_info_len);
+ vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE],
req_params->ndp_info.ndp_app_info,
req_params->ndp_info.ndp_app_info_len);
+ tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + ndp_app_info_len;
+
+ WMITLV_SET_HDR(tlv_ptr, WMITLV_TAG_ARRAY_BYTE, pmk_len);
+ vos_mem_copy(&tlv_ptr[WMI_TLV_HDR_SIZE], req_params->pmk.pmk,
+ cmd->nan_pmk_len);
+ tlv_ptr = tlv_ptr + WMI_TLV_HDR_SIZE + pmk_len;
+
+ WMA_LOGE(FL("vdev_id = %d, transaction_id: %d, csid: %d"),
+ cmd->vdev_id, cmd->transaction_id, cmd->nan_csid);
WMA_LOGD(FL("ndp_config len: %d"),
req_params->ndp_config.ndp_cfg_len);
@@ -309,20 +336,27 @@
req_params->ndp_info.ndp_app_info,
req_params->ndp_info.ndp_app_info_len);
+ WMA_LOGE(FL("pmk len: %d"), cmd->nan_pmk_len);
+ VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
+ req_params->pmk.pmk, cmd->nan_pmk_len);
+
+ WMA_LOGE(FL("sending WMI_NDP_RESPONDER_REQ_CMDID(0x%X)"),
+ WMI_NDP_RESPONDER_REQ_CMDID);
ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
WMI_NDP_RESPONDER_REQ_CMDID);
if (ret < 0) {
WMA_LOGE(FL("WMI_NDP_RESPONDER_REQ_CMDID failed, ret: %d"),
ret);
wmi_buf_free(buf);
- goto send_ndi_responder_fail;
+ goto send_ndp_responder_fail;
}
return VOS_STATUS_SUCCESS;
-send_ndi_responder_fail:
+send_ndp_responder_fail:
vos_mem_zero(&rsp, sizeof(rsp));
rsp.vdev_id = req_params->vdev_id;
rsp.transaction_id = req_params->transaction_id;
- rsp.status = VOS_STATUS_E_FAILURE;
+ rsp.status = NDP_RSP_STATUS_ERROR;
+ rsp.reason = NDP_DATA_RESPONDER_REQ_FAILED;
wma_send_ndp_responder_rsp(&rsp);
return VOS_STATUS_E_FAILURE;
}
@@ -330,14 +364,92 @@
/**
* wma_handle_ndp_end_req() - NDP end request handler
* @wma_handle: wma handle
- * @req_params: request parameters
+ * @ptr: request parameters
*
* Return: VOS_STATUS_SUCCESS on success; error number otherwise
*/
-VOS_STATUS wma_handle_ndp_end_req(tp_wma_handle wma_handle,
- struct ndp_end_req *req_params)
+VOS_STATUS wma_handle_ndp_end_req(tp_wma_handle wma_handle, void *ptr)
{
+ int ret;
+ uint16_t len;
+ uint32_t ndp_end_req_len, i;
+ wmi_ndp_end_req *ndp_end_req_lst;
+ wmi_buf_t buf;
+ vos_msg_t pe_msg = {0};
+ wmi_ndp_end_req_fixed_param *cmd;
+ struct ndp_end_rsp_event *end_rsp = NULL;
+ struct ndp_end_req *req = ptr;
+
+ if (NULL == req) {
+ WMA_LOGE(FL("Invalid ndp_end_req"));
+ goto send_ndp_end_fail;
+ }
+
+ /* len of tlv following fixed param */
+ ndp_end_req_len = sizeof(wmi_ndp_end_req) * req->num_ndp_instances;
+ /* above comes out to 4 byte alligned already, no need of padding */
+ len = sizeof(*cmd) + ndp_end_req_len + WMI_TLV_HDR_SIZE;
+ buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
+ if (!buf) {
+ WMA_LOGE(FL("Malloc failed"));
+ return VOS_STATUS_E_NOMEM;
+ }
+ cmd = (wmi_ndp_end_req_fixed_param *) wmi_buf_data(buf);
+
+ WMITLV_SET_HDR(&cmd->tlv_header,
+ WMITLV_TAG_STRUC_wmi_ndp_end_req_fixed_param,
+ WMITLV_GET_STRUCT_TLVLEN(wmi_ndp_end_req_fixed_param));
+
+ cmd->transaction_id = req->transaction_id;
+
+ /* set tlv pointer to end of fixed param */
+ WMITLV_SET_HDR((uint8_t *)&cmd[1], WMITLV_TAG_ARRAY_STRUC,
+ ndp_end_req_len);
+
+ ndp_end_req_lst = (wmi_ndp_end_req *)((uint8_t *)&cmd[1] +
+ WMI_TLV_HDR_SIZE);
+ for (i = 0; i < req->num_ndp_instances; i++) {
+ WMITLV_SET_HDR(&ndp_end_req_lst[i],
+ WMITLV_TAG_ARRAY_FIXED_STRUC,
+ (sizeof(*ndp_end_req_lst) - WMI_TLV_HDR_SIZE));
+
+ ndp_end_req_lst[i].ndp_instance_id = req->ndp_ids[i];
+ }
+
+ WMA_LOGD(FL("Sending WMI_NDP_END_REQ_CMDID to FW"));
+ ret = wmi_unified_cmd_send(wma_handle->wmi_handle, buf, len,
+ WMI_NDP_END_REQ_CMDID);
+ if (ret < 0) {
+ WMA_LOGE(FL("WMI_NDP_END_REQ_CMDID failed, ret: %d"), ret);
+ wmi_buf_free(buf);
+ goto send_ndp_end_fail;
+ }
return VOS_STATUS_SUCCESS;
+
+send_ndp_end_fail:
+ pe_msg.type = SIR_HAL_NDP_END_RSP;
+ if (req) {
+ end_rsp = vos_mem_malloc(sizeof(*end_rsp));
+ if (NULL == end_rsp) {
+ WMA_LOGE(FL("Malloc failed"));
+ pe_msg.bodyval = true;
+ } else {
+ vos_mem_zero(end_rsp, sizeof(*end_rsp));
+ end_rsp->status = NDP_RSP_STATUS_ERROR;
+ end_rsp->reason = NDP_END_FAILED;
+ end_rsp->transaction_id = req->transaction_id;
+ pe_msg.bodyptr = end_rsp;
+ }
+ } else {
+ pe_msg.bodyval = true;
+ }
+
+ if (VOS_STATUS_SUCCESS !=
+ vos_mq_post_message(VOS_MODULE_ID_PE, &pe_msg)) {
+ WMA_LOGE("NDP_END_RSP to PE failed");
+ vos_mem_free(end_rsp);
+ }
+ return VOS_STATUS_E_FAILURE;
}
/**
@@ -375,20 +487,6 @@
fixed_params =
(wmi_ndp_indication_event_fixed_param *)event->fixed_param;
- WMA_LOGD(FL("WMI_NDP_INDICATION_EVENTID(0x%X) received. vdev %d, service_instance %d, ndp_instance %d, role %d, policy %d"),
- WMI_NDP_INDICATION_EVENTID, fixed_params->vdev_id,
- fixed_params->service_instance_id,
- fixed_params->ndp_instance_id, fixed_params->self_ndp_role,
- fixed_params->accept_policy);
-
- WMA_LOGD(FL("ndp_cfg - %d bytes"), fixed_params->ndp_cfg_len);
- VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
- &event->ndp_cfg, fixed_params->ndp_cfg_len);
-
- WMA_LOGD(FL("ndp_app_info - %d bytes"), fixed_params->ndp_app_info_len);
- VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
- &event->ndp_app_info, fixed_params->ndp_app_info_len);
-
ind_event = vos_mem_malloc(sizeof(*ind_event));
if (!ind_event) {
WMA_LOGP(FL("Failed to allocate memory"));
@@ -406,8 +504,28 @@
WMI_MAC_ADDR_TO_CHAR_ARRAY(&fixed_params->peer_discovery_mac_addr,
ind_event->peer_discovery_mac_addr.bytes);
+ WMA_LOGD(FL("WMI_NDP_INDICATION_EVENTID(0x%X) received. vdev %d, \n"
+ "service_instance %d, ndp_instance %d, role %d, policy %d, \n"
+ "csid: %d, scid_len: %d, peer_mac_addr: %pM, peer_disc_mac_addr: %pM"),
+ WMI_NDP_INDICATION_EVENTID, fixed_params->vdev_id,
+ fixed_params->service_instance_id,
+ fixed_params->ndp_instance_id, fixed_params->self_ndp_role,
+ fixed_params->accept_policy,
+ fixed_params->nan_csid, fixed_params->nan_scid_len,
+ ind_event->peer_mac_addr.bytes,
+ ind_event->peer_discovery_mac_addr.bytes);
+
+ WMA_LOGD(FL("ndp_cfg - %d bytes"), fixed_params->ndp_cfg_len);
+ VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
+ &event->ndp_cfg, fixed_params->ndp_cfg_len);
+
+ WMA_LOGD(FL("ndp_app_info - %d bytes"), fixed_params->ndp_app_info_len);
+ VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
+ &event->ndp_app_info, fixed_params->ndp_app_info_len);
ind_event->ndp_config.ndp_cfg_len = fixed_params->ndp_cfg_len;
ind_event->ndp_info.ndp_app_info_len = fixed_params->ndp_app_info_len;
+ ind_event->ncs_sk_type = fixed_params->nan_csid;
+ ind_event->scid.scid_len = fixed_params->nan_scid_len;
if (ind_event->ndp_config.ndp_cfg_len) {
ind_event->ndp_config.ndp_cfg =
@@ -434,6 +552,24 @@
event->ndp_app_info,
ind_event->ndp_info.ndp_app_info_len);
}
+
+ if (ind_event->scid.scid_len) {
+ ind_event->scid.scid =
+ vos_mem_malloc(ind_event->scid.scid_len);
+ if (NULL == ind_event->scid.scid) {
+ WMA_LOGE(FL("malloc failed"));
+ vos_mem_free(ind_event->ndp_config.ndp_cfg);
+ vos_mem_free(ind_event->ndp_info.ndp_app_info);
+ vos_mem_free(ind_event);
+ return VOS_STATUS_E_NOMEM;
+ }
+ vos_mem_copy(ind_event->scid.scid,
+ event->ndp_scid, ind_event->scid.scid_len);
+ WMA_LOGD(FL("scid hex dump:"));
+ VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
+ ind_event->scid.scid, ind_event->scid.scid_len);
+ }
+
pe_msg.type = SIR_HAL_NDP_INDICATION;
pe_msg.bodyptr = ind_event;
pe_msg.bodyval = 0;
@@ -444,6 +580,7 @@
WMA_LOGE(FL("fail to post SIR_HAL_NDP_INDICATION msg to PE"));
vos_mem_free(ind_event->ndp_config.ndp_cfg);
vos_mem_free(ind_event->ndp_info.ndp_app_info);
+ vos_mem_free(ind_event->scid.scid);
vos_mem_free(ind_event);
}
@@ -473,8 +610,15 @@
rsp.transaction_id = fixed_params->transaction_id;
rsp.reason = fixed_params->reason_code;
rsp.status = fixed_params->rsp_status;
+ rsp.create_peer = fixed_params->create_peer;
WMI_MAC_ADDR_TO_CHAR_ARRAY(&fixed_params->peer_ndi_mac_addr,
rsp.peer_mac_addr.bytes);
+
+ WMA_LOGE(FL("WMI_NDP_RESPONDER_RSP_EVENTID(0x%X) received. vdev_id: %d, peer_mac_addr: %pM, transaction_id: %d, status_code %d, reason_code: %d, create_peer: %d"),
+ WMI_NDP_RESPONDER_RSP_EVENTID, rsp.vdev_id,
+ rsp.peer_mac_addr.bytes, rsp.transaction_id,
+ rsp.status, rsp.reason, rsp.create_peer);
+
return wma_send_ndp_responder_rsp(&rsp);
}
@@ -498,9 +642,11 @@
event = (WMI_NDP_CONFIRM_EVENTID_param_tlvs *) event_info;
fixed_params = (wmi_ndp_confirm_event_fixed_param *)event->fixed_param;
- WMA_LOGE(FL("WMI_NDP_CONFIRM_EVENTID(0x%X) recieved. vdev %d, ndp_instance %d, rsp_code %d"),
+ WMA_LOGE(FL("WMI_NDP_CONFIRM_EVENTID(0x%X) recieved. vdev %d, ndp_instance %d, rsp_code %d, reason_code: %d, num_active_ndps_on_peer: %d"),
WMI_NDP_CONFIRM_EVENTID, fixed_params->vdev_id,
- fixed_params->ndp_instance_id, fixed_params->rsp_code);
+ fixed_params->ndp_instance_id, fixed_params->rsp_code,
+ fixed_params->reason_code,
+ fixed_params->num_active_ndps_on_peer);
WMA_LOGE(FL("ndp_cfg - %d bytes"), fixed_params->ndp_cfg_len);
VOS_TRACE_HEX_DUMP(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_DEBUG,
@@ -520,35 +666,19 @@
ndp_confirm->vdev_id = fixed_params->vdev_id;
ndp_confirm->ndp_instance_id = fixed_params->ndp_instance_id;
ndp_confirm->rsp_code = fixed_params->rsp_code;
+ ndp_confirm->reason_code = fixed_params->reason_code;
+ ndp_confirm->num_active_ndps_on_peer =
+ fixed_params->num_active_ndps_on_peer;
WMI_MAC_ADDR_TO_CHAR_ARRAY(&fixed_params->peer_ndi_mac_addr,
ndp_confirm->peer_ndi_mac_addr.bytes);
- vos_mem_copy(&ndp_confirm->ndp_config, event->ndp_cfg,
- fixed_params->ndp_cfg_len);
-
- ndp_confirm->ndp_config.ndp_cfg_len = fixed_params->ndp_cfg_len;
ndp_confirm->ndp_info.ndp_app_info_len = fixed_params->ndp_app_info_len;
-
- if (ndp_confirm->ndp_config.ndp_cfg_len) {
- ndp_confirm->ndp_config.ndp_cfg =
- vos_mem_malloc(ndp_confirm->ndp_config.ndp_cfg_len);
- if (NULL == ndp_confirm->ndp_config.ndp_cfg) {
- WMA_LOGE(FL("malloc failed"));
- vos_mem_free(ndp_confirm);
- return VOS_STATUS_E_NOMEM;
- }
- vos_mem_copy(ndp_confirm->ndp_config.ndp_cfg,
- event->ndp_cfg,
- ndp_confirm->ndp_config.ndp_cfg_len);
- }
-
if (ndp_confirm->ndp_info.ndp_app_info_len) {
ndp_confirm->ndp_info.ndp_app_info =
vos_mem_malloc(fixed_params->ndp_app_info_len);
if (NULL == ndp_confirm->ndp_info.ndp_app_info) {
WMA_LOGE(FL("malloc failed"));
- vos_mem_free(ndp_confirm->ndp_config.ndp_cfg);
vos_mem_free(ndp_confirm);
return VOS_STATUS_E_NOMEM;
}
@@ -563,7 +693,6 @@
status = vos_mq_post_message(VOS_MODULE_ID_PE, &msg);
if (!VOS_IS_STATUS_SUCCESS(status)) {
WMA_LOGE(FL("fail to post SIR_HAL_NDP_CONFIRM msg to PE"));
- vos_mem_free(ndp_confirm->ndp_config.ndp_cfg);
vos_mem_free(ndp_confirm->ndp_info.ndp_app_info);
vos_mem_free(ndp_confirm);
}
@@ -583,7 +712,44 @@
static int wma_ndp_end_response_event_handler(void *handle,
uint8_t *event_info, uint32_t len)
{
- return 0;
+ int ret = 0;
+ VOS_STATUS status;
+ vos_msg_t pe_msg = {0};
+ struct ndp_end_rsp_event *end_rsp;
+ WMI_NDP_END_RSP_EVENTID_param_tlvs *event;
+ wmi_ndp_end_rsp_event_fixed_param *fixed_params = NULL;
+
+ event = (WMI_NDP_END_RSP_EVENTID_param_tlvs *) event_info;
+ fixed_params = (wmi_ndp_end_rsp_event_fixed_param *)event->fixed_param;
+ WMA_LOGD(FL("WMI_NDP_END_RSP_EVENTID(0x%X) recieved. transaction_id: %d, rsp_status: %d, reason_code: %d"),
+ WMI_NDP_END_RSP_EVENTID, fixed_params->transaction_id,
+ fixed_params->rsp_status, fixed_params->reason_code);
+
+ end_rsp = vos_mem_malloc(sizeof(*end_rsp));
+ if (NULL == end_rsp) {
+ WMA_LOGE("malloc failed");
+ pe_msg.bodyval = true;
+ ret = -ENOMEM;
+ goto send_ndp_end_rsp;
+ }
+ pe_msg.bodyptr = end_rsp;
+ vos_mem_zero(end_rsp, sizeof(*end_rsp));
+
+ end_rsp->transaction_id = fixed_params->transaction_id;
+ end_rsp->reason = fixed_params->reason_code;
+ end_rsp->status = fixed_params->rsp_status;
+
+send_ndp_end_rsp:
+ pe_msg.type = SIR_HAL_NDP_END_RSP;
+ WMA_LOGD(FL("Sending SIR_HAL_NDP_END_RSP msg to PE"));
+ status = vos_mq_post_message(VOS_MODULE_ID_PE, &pe_msg);
+ if (!VOS_IS_STATUS_SUCCESS(status)) {
+ WMA_LOGE("SIR_HAL_NDP_END_RSP to PE failed");
+ vos_mem_free(end_rsp);
+ ret = -EINVAL;
+ }
+
+ return ret;
}
/**
@@ -598,7 +764,74 @@
static int wma_ndp_end_indication_event_handler(void *handle,
uint8_t *event_info, uint32_t len)
{
- return 0;
+ WMI_NDP_END_INDICATION_EVENTID_param_tlvs *event;
+ wmi_ndp_end_indication *ind;
+ vos_msg_t pe_msg;
+ struct ndp_end_indication_event *ndp_event_buf;
+ VOS_STATUS vos_status;
+ int i;
+ v_MACADDR_t peer_addr;
+ int buf_size;
+
+ event = (WMI_NDP_END_INDICATION_EVENTID_param_tlvs *) event_info;
+
+ if (event->num_ndp_end_indication_list == 0) {
+ WMA_LOGE(
+ FL("Error: Event ignored, 0 ndp instances"));
+ return -EINVAL;
+ }
+
+ WMA_LOGD(FL("number of ndp instances = %d"),
+ event->num_ndp_end_indication_list);
+
+ buf_size = sizeof(*ndp_event_buf) + event->num_ndp_end_indication_list *
+ sizeof(ndp_event_buf->ndp_map[0]);
+ ndp_event_buf = vos_mem_malloc(buf_size);
+ if (!ndp_event_buf) {
+ WMA_LOGP(FL("Failed to allocate memory"));
+ return -ENOMEM;
+ }
+ vos_mem_zero(ndp_event_buf, buf_size);
+ ndp_event_buf->num_ndp_ids = event->num_ndp_end_indication_list;
+
+ ind = event->ndp_end_indication_list;
+ for (i = 0; i < ndp_event_buf->num_ndp_ids; i++) {
+ WMI_MAC_ADDR_TO_CHAR_ARRAY(
+ &ind[i].peer_ndi_mac_addr,
+ peer_addr.bytes);
+ WMA_LOGD(
+ FL("ind[%d]: type %d, reason_code %d, instance_id %d num_active %d MAC: " MAC_ADDRESS_STR),
+ i,
+ ind[i].type,
+ ind[i].reason_code,
+ ind[i].ndp_instance_id,
+ ind[i].num_active_ndps_on_peer,
+ MAC_ADDR_ARRAY(peer_addr.bytes));
+
+ /* Add each instance entry to the list */
+ ndp_event_buf->ndp_map[i].ndp_instance_id =
+ ind[i].ndp_instance_id;
+ ndp_event_buf->ndp_map[i].vdev_id = ind[i].vdev_id;
+ WMI_MAC_ADDR_TO_CHAR_ARRAY(&ind[i].peer_ndi_mac_addr,
+ ndp_event_buf->ndp_map[i].peer_ndi_mac_addr.bytes);
+ ndp_event_buf->ndp_map[i].num_active_ndp_sessions =
+ ind[i].num_active_ndps_on_peer;
+ ndp_event_buf->ndp_map[i].type = ind[i].type;
+ ndp_event_buf->ndp_map[i].reason_code =
+ ind[i].reason_code;
+ }
+
+ pe_msg.type = SIR_HAL_NDP_END_IND;
+ pe_msg.bodyptr = ndp_event_buf;
+ pe_msg.bodyval = 0;
+ vos_status = vos_mq_post_message(VOS_MODULE_ID_PE, &pe_msg);
+ if (VOS_IS_STATUS_SUCCESS(vos_status)) {
+ return 0;
+ }
+
+ WMA_LOGE(FL("failed to post msg to PE"));
+ vos_mem_free(ndp_event_buf);
+ return -EINVAL;
}
/**
@@ -632,6 +865,7 @@
rsp->transaction_id = fixed_params->transaction_id;
rsp->ndp_instance_id = fixed_params->ndp_instance_id;
rsp->status = fixed_params->rsp_status;
+ rsp->reason = fixed_params->reason_code;
pe_msg.type = SIR_HAL_NDP_INITIATOR_RSP;
pe_msg.bodyptr = rsp;
@@ -1089,3 +1323,48 @@
add_sta->staMac, add_sta->status);
wma_send_msg(wma, WDA_ADD_STA_RSP, (void *)add_sta, 0);
}
+
+/**
+ * wma_delete_sta_req_ndi_mode() - Process DEL_STA request for NDI data peer
+ * @wma: WMA context
+ * @del_sta: DEL_STA parameters from LIM
+ *
+ * Removes wma/txrx peer entry for the NDI STA
+ *
+ * Return: None
+ */
+void wma_delete_sta_req_ndi_mode(tp_wma_handle wma,
+ tpDeleteStaParams del_sta)
+{
+ ol_txrx_pdev_handle pdev;
+ struct ol_txrx_peer_t *peer;
+
+ pdev = vos_get_context(VOS_MODULE_ID_TXRX, wma->vos_context);
+
+ if (!pdev) {
+ WMA_LOGE(FL("Failed to get pdev"));
+ del_sta->status = VOS_STATUS_E_FAILURE;
+ goto send_del_rsp;
+ }
+
+ peer = ol_txrx_peer_find_by_local_id(pdev, del_sta->staIdx);
+ if (!peer) {
+ WMA_LOGE(FL("Failed to get peer handle using peer id %d"),
+ del_sta->staIdx);
+ del_sta->status = VOS_STATUS_E_FAILURE;
+ goto send_del_rsp;
+ }
+
+ wma_remove_peer(wma, peer->mac_addr.raw, del_sta->smesessionId, peer,
+ false);
+ del_sta->status = VOS_STATUS_SUCCESS;
+
+send_del_rsp:
+ if (del_sta->respReqd) {
+ WMA_LOGD(FL("Sending del rsp to umac (status: %d)"),
+ del_sta->status);
+ wma_send_msg(wma, WDA_DELETE_STA_RSP, del_sta, 0);
+ }
+}
+
+
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.h b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.h
index a639f39..ba78743 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.h
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMA/wma_nan_datapath.h
@@ -58,6 +58,9 @@
void wma_add_bss_ndi_mode(tp_wma_handle wma, tpAddBssParams add_bss);
void wma_add_sta_ndi_mode(tp_wma_handle wma, tpAddStaParams add_sta);
VOS_STATUS wma_handle_ndp_initiator_req(tp_wma_handle wma_handle, void *req);
+VOS_STATUS wma_handle_ndp_end_req(tp_wma_handle wma_handle, void *req);
+void wma_delete_sta_req_ndi_mode(tp_wma_handle wma,
+ tpDeleteStaParams del_sta);
#else
static inline void wma_add_bss_ndi_mode(tp_wma_handle wma,
tpAddBssParams add_bss) {}
@@ -85,6 +88,14 @@
{
return VOS_STATUS_SUCCESS;
}
-
+static inline VOS_STATUS wma_handle_ndp_end_req(tp_wma_handle wma_handle,
+ void *req)
+{
+ return VOS_STATUS_SUCCESS;
+}
+static inline void wma_delete_sta_req_ndi_mode(tp_wma_handle wma,
+ tpDeleteStaParams del_sta)
+{
+}
#endif /* WLAN_FEATURE_NAN_DATAPATH */
#endif /* __WMA_NAN_DATAPATH_H */
diff --git a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMI/wmi_unified.c b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMI/wmi_unified.c
index c987c75..9eae215 100644
--- a/drivers/staging/qcacld-2.0/CORE/SERVICES/WMI/wmi_unified.c
+++ b/drivers/staging/qcacld-2.0/CORE/SERVICES/WMI/wmi_unified.c
@@ -704,6 +704,36 @@
CASE_RETURN_STRING(WMI_DBGLOG_TIME_STAMP_SYNC_CMDID);
CASE_RETURN_STRING(WMI_P2P_LISTEN_OFFLOAD_START_CMDID);
CASE_RETURN_STRING(WMI_P2P_LISTEN_OFFLOAD_STOP_CMDID);
+ CASE_RETURN_STRING(WMI_PEER_REORDER_QUEUE_SETUP_CMDID);
+ CASE_RETURN_STRING(WMI_PEER_REORDER_QUEUE_REMOVE_CMDID);
+ CASE_RETURN_STRING(WMI_SET_MULTIPLE_MCAST_FILTER_CMDID);
+ CASE_RETURN_STRING(WMI_READ_DATA_FROM_FLASH_CMDID);
+ CASE_RETURN_STRING(WMI_PDEV_SET_REORDER_TIMEOUT_VAL_CMDID);
+ CASE_RETURN_STRING(WMI_PEER_SET_RX_BLOCKSIZE_CMDID);
+ CASE_RETURN_STRING(WMI_PDEV_SET_WAKEUP_CONFIG_CMDID);
+ CASE_RETURN_STRING(WMI_PDEV_GET_ANTDIV_STATUS_CMDID);
+ CASE_RETURN_STRING(WMI_PEER_ANTDIV_INFO_REQ_CMDID);
+ CASE_RETURN_STRING(WMI_MNT_FILTER_CMDID);
+ CASE_RETURN_STRING(WMI_PDEV_GET_CHIP_POWER_STATS_CMDID);
+ CASE_RETURN_STRING(WMI_COEX_GET_ANTENNA_ISOLATION_CMDID);
+ CASE_RETURN_STRING(WMI_PDEV_SET_STATS_THRESHOLD_CMDID);
+ CASE_RETURN_STRING(WMI_REQUEST_WLAN_STATS_CMDID);
+ CASE_RETURN_STRING(WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID);
+ CASE_RETURN_STRING(WMI_SAR_LIMITS_CMDID);
+ CASE_RETURN_STRING(WMI_PDEV_DFS_PHYERR_OFFLOAD_ENABLE_CMDID);
+ CASE_RETURN_STRING(WMI_PDEV_DFS_PHYERR_OFFLOAD_DISABLE_CMDID);
+ CASE_RETURN_STRING(WMI_VDEV_ADFS_CH_CFG_CMDID);
+ CASE_RETURN_STRING(WMI_VDEV_ADFS_OCAC_ABORT_CMDID);
+ CASE_RETURN_STRING(WMI_REQUEST_RCPI_CMDID);
+ CASE_RETURN_STRING(WMI_REQUEST_PEER_STATS_INFO_CMDID);
+ CASE_RETURN_STRING(WMI_SET_CURRENT_COUNTRY_CMDID);
+ CASE_RETURN_STRING(WMI_11D_SCAN_START_CMDID);
+ CASE_RETURN_STRING(WMI_11D_SCAN_STOP_CMDID);
+ CASE_RETURN_STRING(WMI_REQUEST_RADIO_CHAN_STATS_CMDID);
+ CASE_RETURN_STRING(WMI_ROAM_PER_CONFIG_CMDID);
+ CASE_RETURN_STRING(WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID);
+ CASE_RETURN_STRING(WMI_BPF_SET_VDEV_ACTIVE_MODE_CMDID);
+ CASE_RETURN_STRING(WMI_HW_DATA_FILTER_CMDID);
}
return "Invalid WMI cmd";
}
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/inc/btcApi.h b/drivers/staging/qcacld-2.0/CORE/SME/inc/btcApi.h
deleted file mode 100644
index 9a06f7c..0000000
--- a/drivers/staging/qcacld-2.0/CORE/SME/inc/btcApi.h
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
- *
- * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
- *
- *
- * Permission to use, copy, modify, and/or distribute this software for
- * any purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all
- * copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-/*
- * This file was originally distributed by Qualcomm Atheros, Inc.
- * under proprietary terms before Copyright ownership was assigned
- * to the Linux Foundation.
- */
-
-/******************************************************************************
-*
-* Name: btcApi.h
-*
-* Description: BTC Events Layer API definitions.
-*
-
-*
-******************************************************************************/
-
-#ifndef __BTC_API_H__
-#define __BTC_API_H__
-
-#include "vos_types.h"
-#include "vos_timer.h"
-#include "vos_nvitem.h"
-
-#define BT_INVALID_CONN_HANDLE (0xFFFF) /**< Invalid connection handle */
-
-/* ACL and Sync connection attempt results */
-#define BT_CONN_STATUS_FAIL (0) /**< Connection failed */
-#define BT_CONN_STATUS_SUCCESS (1) /**< Connection successful */
-#define BT_CONN_STATUS_MAX (2) /**< This and beyond are invalid values */
-
-/** ACL and Sync link types
- These must match the Bluetooth Spec!
-*/
-#define BT_SCO (0) /**< SCO Link */
-#define BT_ACL (1) /**< ACL Link */
-#define BT_eSCO (2) /**< eSCO Link */
-#define BT_LINK_TYPE_MAX (3) /**< This value and higher are invalid */
-
-/** ACL link modes
- These must match the Bluetooth Spec!
-*/
-#define BT_ACL_ACTIVE (0) /**< Active mode */
-#define BT_ACL_HOLD (1) /**< Hold mode */
-#define BT_ACL_SNIFF (2) /**< Sniff mode */
-#define BT_ACL_PARK (3) /**< Park mode */
-#define BT_ACL_MODE_MAX (4) /**< This value and higher are invalid */
-
-/**
- * A2DP BTC max no of BT sub intervals
- *
- * **/
-#define BTC_MAX_NUM_ACL_BT_SUB_INTS (7)
-
-/** BTC Executions Modes allowed to be set by user
-*/
-#define BTC_SMART_COEXISTENCE (0) /** BTC Mapping Layer decides whats best */
-#define BTC_WLAN_ONLY (1) /** WLAN takes all mode */
-#define BTC_PTA_ONLY (2) /** Allow only 3 wire protocol in H/W */
-#define BTC_SMART_MAX_WLAN (3) /** BTC Mapping Layer decides whats best, WLAN weighted */
-#define BTC_SMART_MAX_BT (4) /** BTC Mapping Layer decides whats best, BT weighted */
-#define BTC_SMART_BT_A2DP (5) /** BTC Mapping Layer decides whats best, balanced + BT A2DP weight */
-#define BT_EXEC_MODE_MAX (6) /** This and beyond are invalid values */
-
-/** Bitmaps used for maintaining various BT events that requires
- enough time to complete such that it might require disbling of
- heartbeat monitoring to avoid WLAN link loss with the AP
-*/
-#define BT_INQUIRY_STARTED (1<<0)
-#define BT_PAGE_STARTED (1<<1)
-#define BT_CREATE_ACL_CONNECTION_STARTED (1<<2)
-#define BT_CREATE_SYNC_CONNECTION_STARTED (1<<3)
-
-/** Maximum time duration in milliseconds between a specific BT start event and its
- respective stop event, before it can be declared timed out on receiving the stop event.
-*/
-#define BT_MAX_EVENT_DONE_TIMEOUT 45000
-
-
-/*
- To suppurt multiple SCO connections for BT+UAPSD work
-*/
-#define BT_MAX_SCO_SUPPORT 3
-#define BT_MAX_ACL_SUPPORT 3
-#define BT_MAX_DISCONN_SUPPORT (BT_MAX_SCO_SUPPORT+BT_MAX_ACL_SUPPORT)
-#define BT_MAX_NUM_EVENT_ACL_DEFERRED 4 //We may need to defer these many BT events for ACL
-#define BT_MAX_NUM_EVENT_SCO_DEFERRED 4 //We may need to defer these many BT events for SYNC
-
-/*
- * Number of mws coex configurations
- */
-#define MWS_COEX_MAX_CONFIG 6
-
-/** Enumeration of all the different kinds of BT events
-*/
-typedef enum eSmeBtEventType
-{
- BT_EVENT_DEVICE_SWITCHED_ON,
- BT_EVENT_DEVICE_SWITCHED_OFF,
- BT_EVENT_INQUIRY_STARTED,
- BT_EVENT_INQUIRY_STOPPED,
- BT_EVENT_INQUIRY_SCAN_STARTED,
- BT_EVENT_INQUIRY_SCAN_STOPPED,
- BT_EVENT_PAGE_STARTED,
- BT_EVENT_PAGE_STOPPED,
- BT_EVENT_PAGE_SCAN_STARTED,
- BT_EVENT_PAGE_SCAN_STOPPED,
- BT_EVENT_CREATE_ACL_CONNECTION,
- BT_EVENT_ACL_CONNECTION_COMPLETE,
- BT_EVENT_CREATE_SYNC_CONNECTION,
- BT_EVENT_SYNC_CONNECTION_COMPLETE,
- BT_EVENT_SYNC_CONNECTION_UPDATED,
- BT_EVENT_DISCONNECTION_COMPLETE,
- BT_EVENT_MODE_CHANGED,
- BT_EVENT_A2DP_STREAM_START,
- BT_EVENT_A2DP_STREAM_STOP,
- BT_EVENT_TYPE_MAX, //This and beyond are invalid values
-} tSmeBtEventType;
-
-/**Data structure that specifies the needed event parameters for
- BT_EVENT_CREATE_ACL_CONNECTION and BT_EVENT_ACL_CONNECTION_COMPLETE
-*/
-typedef struct sSmeBtAclConnectionParam
-{
- v_U8_t bdAddr[6];
- v_U16_t connectionHandle;
- v_U8_t status;
-} tSmeBtAclConnectionParam, *tpSmeBtAclConnectionParam;
-
-/** Data structure that specifies the needed event parameters for
- BT_EVENT_CREATE_SYNC_CONNECTION, BT_EVENT_SYNC_CONNECTION_COMPLETE
- and BT_EVENT_SYNC_CONNECTION_UPDATED
-*/
-typedef struct sSmeBtSyncConnectionParam
-{
- v_U8_t bdAddr[6];
- v_U16_t connectionHandle;
- v_U8_t status;
- v_U8_t linkType;
- v_U8_t scoInterval; //units in number of 625us slots
- v_U8_t scoWindow; //units in number of 625us slots
- v_U8_t retransmisisonWindow; //units in number of 625us slots
-} tSmeBtSyncConnectionParam, *tpSmeBtSyncConnectionParam;
-
-typedef struct sSmeBtSyncUpdateHist
-{
- tSmeBtSyncConnectionParam btSyncConnection;
- v_BOOL_t fValid;
-} tSmeBtSyncUpdateHist, *tpSmeBtSyncUpdateHist;
-
-/**Data structure that specifies the needed event parameters for
- BT_EVENT_MODE_CHANGED
-*/
-typedef struct sSmeBtAclModeChangeParam
-{
- v_U16_t connectionHandle;
- v_U8_t mode;
-} tSmeBtAclModeChangeParam, *tpSmeBtAclModeChangeParam;
-
-/*Data structure that specifies the needed event parameters for
- BT_EVENT_DISCONNECTION_COMPLETE
-*/
-typedef struct sSmeBtDisconnectParam
-{
- v_U16_t connectionHandle;
-} tSmeBtDisconnectParam, *tpSmeBtDisconnectParam;
-
-/*Data structure that specifies the needed event parameters for
- BT_EVENT_A2DP_STREAM_START
- BT_EVENT_A2DP_STREAM_STOP
-*/
-typedef struct sSmeBtA2DPParam
-{
- v_U8_t bdAddr[6];
-} tSmeBtA2DPParam, *tpSmeBtA2DPParam;
-
-
-/** Generic Bluetooth Event structure for BTC
-*/
-typedef struct sSmeBtcBtEvent
-{
- tSmeBtEventType btEventType;
- union
- {
- v_U8_t bdAddr[6]; /**< For events with only a BT Addr in event_data */
- tSmeBtAclConnectionParam btAclConnection;
- tSmeBtSyncConnectionParam btSyncConnection;
- tSmeBtDisconnectParam btDisconnect;
- tSmeBtAclModeChangeParam btAclModeChange;
- }uEventParam;
-} tSmeBtEvent, *tpSmeBtEvent;
-
-
-/** Data structure that specifies the BTC Configuration parameters
-*/
-typedef struct sSmeBtcConfig
-{
- v_U8_t btcExecutionMode;
- v_U32_t mwsCoexConfig[MWS_COEX_MAX_CONFIG];
-} tSmeBtcConfig, *tpSmeBtcConfig;
-
-
-typedef struct sSmeBtAclModeChangeEventHist
-{
- tSmeBtAclModeChangeParam btAclModeChange;
- v_BOOL_t fValid;
-} tSmeBtAclModeChangeEventHist, *tpSmeBtAclModeChangeEventHist;
-
-typedef struct sSmeBtAclEventHist
-{
- //At most, cached events are COMPLETION, DISCONNECT, CREATION, COMPLETION
- tSmeBtEventType btEventType[BT_MAX_NUM_EVENT_ACL_DEFERRED];
- tSmeBtAclConnectionParam btAclConnection[BT_MAX_NUM_EVENT_ACL_DEFERRED];
- //bNextEventIdx == 0 meaning no event cached here
- tANI_U8 bNextEventIdx;
-} tSmeBtAclEventHist, *tpSmeBtAclEventHist;
-
-typedef struct sSmeBtSyncEventHist
-{
- //At most, cached events are COMPLETION, DISCONNECT, CREATION, COMPLETION
- tSmeBtEventType btEventType[BT_MAX_NUM_EVENT_SCO_DEFERRED];
- tSmeBtSyncConnectionParam btSyncConnection[BT_MAX_NUM_EVENT_SCO_DEFERRED];
- //bNextEventIdx == 0 meaning no event cached here
- tANI_U8 bNextEventIdx;
-} tSmeBtSyncEventHist, *tpSmeBtSyncEventHist;
-
-typedef struct sSmeBtDisconnectEventHist
-{
- tSmeBtDisconnectParam btDisconnect;
- v_BOOL_t fValid;
-} tSmeBtDisconnectEventHist, *tpSmeBtDisconnectEventHist;
-
-
-/*
- Data structure for the history of BT events
-*/
-typedef struct sSmeBtcEventHist
-{
- tSmeBtSyncEventHist btSyncConnectionEvent[BT_MAX_SCO_SUPPORT];
- tSmeBtAclEventHist btAclConnectionEvent[BT_MAX_ACL_SUPPORT];
- tSmeBtAclModeChangeEventHist btAclModeChangeEvent[BT_MAX_ACL_SUPPORT];
- tSmeBtDisconnectEventHist btDisconnectEvent[BT_MAX_DISCONN_SUPPORT];
- tSmeBtSyncUpdateHist btSyncUpdateEvent[BT_MAX_SCO_SUPPORT];
- int nInquiryEvent; //>0 for # of outstanding inquiriy starts
- //<0 for # of outstanding inquiry stops
- //0 == no inquiry event
- int nPageEvent; //>0 for # of outstanding page starts
- //<0 for # of outstanding page stops
- //0 == no page event
- v_BOOL_t fA2DPStarted;
- v_BOOL_t fA2DPStopped;
-} tSmeBtcEventHist, *tpSmeBtcEventHist;
-
-typedef struct sSmeBtcEventReplay
-{
- tSmeBtcEventHist btcEventHist;
- v_BOOL_t fBTSwitchOn;
- v_BOOL_t fBTSwitchOff;
- //This is not directly tied to BT event so leave it alone when processing BT events
- v_BOOL_t fRestoreHBMonitor;
-} tSmeBtcEventReplay, *tpSmeBtcEventReplay;
-
-typedef struct sSmeBtcInfo
-{
- tSmeBtcConfig btcConfig;
- v_BOOL_t btcReady;
- v_U8_t btcEventState;
- v_U8_t btcHBActive; /* Is HB currently active */
- v_U8_t btcHBCount; /* default HB count */
- vos_timer_t restoreHBTimer; /* Timer to restore heart beat */
- tSmeBtcEventReplay btcEventReplay;
- v_BOOL_t fReplayBTEvents;
- v_BOOL_t btcUapsdOk; /* Indicate whether BTC is ok with UAPSD */
- v_BOOL_t fA2DPTrafStop;/*flag to check A2DP_STOP event has come before MODE_CHANGED*/
- v_U16_t btcScoHandles[BT_MAX_SCO_SUPPORT]; /* Handles for SCO, if any*/
- v_BOOL_t fA2DPUp; /*remember whether A2DP is in session*/
- v_BOOL_t btcScanCompromise;
- v_U8_t btcBssfordisableaggr[VOS_MAC_ADDRESS_LEN];
-} tSmeBtcInfo, *tpSmeBtcInfo;
-
-
-/** Routine definitions
-*/
-
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
-VOS_STATUS btcOpen (tHalHandle hHal);
-VOS_STATUS btcClose (tHalHandle hHal);
-VOS_STATUS btcReady (tHalHandle hHal);
-VOS_STATUS btcSendCfgMsg(tHalHandle hHal, tpSmeBtcConfig pSmeBtcConfig);
-VOS_STATUS btcSignalBTEvent (tHalHandle hHal, tpSmeBtEvent pBtEvent);
-VOS_STATUS btcSetConfig (tHalHandle hHal, tpSmeBtcConfig pSmeBtcConfig);
-VOS_STATUS btcGetConfig (tHalHandle hHal, tpSmeBtcConfig pSmeBtcConfig);
-/*
- Caller can check whether BTC's current event allows UAPSD. This doesn't affect
- BMPS.
- return: VOS_TRUE -- BTC is ready for UAPSD
- VOS_FALSE -- certain BT event is active, cannot enter UAPSD
-*/
-v_BOOL_t btcIsReadyForUapsd( tHalHandle hHal );
-eHalStatus btcHandleCoexInd(tHalHandle hHal, void* pMsg);
-#endif /* End of WLAN_MDM_CODE_REDUCTION_OPT */
-
-#endif
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/inc/csrApi.h b/drivers/staging/qcacld-2.0/CORE/SME/inc/csrApi.h
index 0994996..6683577 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/inc/csrApi.h
+++ b/drivers/staging/qcacld-2.0/CORE/SME/inc/csrApi.h
@@ -310,6 +310,14 @@
eCsrRequestType requestType; //11d scan or full scan
tANI_BOOLEAN p2pSearch;
tANI_BOOLEAN skipDfsChnlInP2pSearch;
+
+ uint32_t enable_scan_randomization;
+ uint8_t mac_addr[VOS_MAC_ADDR_SIZE];
+ uint8_t mac_addr_mask[VOS_MAC_ADDR_SIZE];
+ bool ie_whitelist;
+ uint32_t probe_req_ie_bitmap[PROBE_REQ_BITMAP_LEN];
+ uint32_t num_vendor_oui;
+ struct vendor_oui *voui;
}tCsrScanRequest;
typedef struct tagCsrBGScanRequest
@@ -643,13 +651,12 @@
eCSR_ROAM_RESULT_DFS_CHANSW_UPDATE_FAILURE,
eCSR_ROAM_EXT_CHG_CHNL_UPDATE_IND,
- eCSR_ROAM_RESULT_NDP_CREATE_RSP,
- eCSR_ROAM_RESULT_NDP_DELETE_RSP,
+ eCSR_ROAM_RESULT_NDI_CREATE_RSP,
+ eCSR_ROAM_RESULT_NDI_DELETE_RSP,
eCSR_ROAM_RESULT_NDP_INITIATOR_RSP,
eCSR_ROAM_RESULT_NDP_NEW_PEER_IND,
eCSR_ROAM_RESULT_NDP_CONFIRM_IND,
eCSR_ROAM_RESULT_NDP_INDICATION,
- eCSR_ROAM_RESULT_NDP_SCHED_UPDATE_RSP,
eCSR_ROAM_RESULT_NDP_RESPONDER_RSP,
eCSR_ROAM_RESULT_NDP_END_RSP,
eCSR_ROAM_RESULT_NDP_PEER_DEPARTED_IND,
@@ -1188,8 +1195,6 @@
tANI_U32 nInitialDwellTime; //in units of milliseconds
bool initial_scan_no_dfs_chnl;
- tANI_U32 nActiveMinChnTimeBtc; //in units of milliseconds
- tANI_U32 nActiveMaxChnTimeBtc; //in units of milliseconds
tANI_U32 disableAggWithBtc;
#ifdef WLAN_AP_STA_CONCURRENCY
tANI_U32 nPassiveMinChnTimeConc; //in units of milliseconds
@@ -1266,6 +1271,7 @@
tANI_U8 txBFCsnValue;
tANI_U8 enable2x2;
tANI_BOOLEAN enableVhtFor24GHz;
+ bool vendor_vht_for_24ghz_sap;
tANI_U8 enableMuBformee;
tANI_U8 enableVhtpAid;
tANI_U8 enableVhtGid;
@@ -1453,8 +1459,8 @@
union {
struct sme_ndp_peer_ind ndp_peer_ind_params;
struct ndp_schedule_update_rsp ndp_sched_upd_rsp_params;
- struct ndp_end_indication_event ndp_end_ind_params;
- struct ndp_end_rsp_event ndp_end_rsp_params;
+ struct ndp_end_indication_event *ndp_end_ind_params;
+ struct ndp_end_rsp_event *ndp_end_rsp_params;
struct ndp_confirm_event ndp_confirm_params;
struct ndp_responder_rsp_event ndp_responder_rsp_params;
struct ndp_indication_event ndp_indication_params;
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/inc/csrInternal.h b/drivers/staging/qcacld-2.0/CORE/SME/inc/csrInternal.h
index 18ca45a..57494eb 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/inc/csrInternal.h
+++ b/drivers/staging/qcacld-2.0/CORE/SME/inc/csrInternal.h
@@ -600,8 +600,6 @@
tANI_U32 nInitialDwellTime; //in units of milliseconds
bool initial_scan_no_dfs_chnl;
- tANI_U32 nActiveMinChnTimeBtc; //in units of milliseconds
- tANI_U32 nActiveMaxChnTimeBtc; //in units of milliseconds
tANI_U8 disableAggWithBtc;
#ifdef WLAN_AP_STA_CONCURRENCY
tANI_U32 nPassiveMinChnTimeConc; //in units of milliseconds
@@ -730,6 +728,7 @@
uint32_t edca_vi_aifs;
uint32_t edca_bk_aifs;
uint32_t edca_be_aifs;
+ bool vendor_vht_for_24ghz_sap;
}tCsrConfig;
typedef struct tagCsrChannelPowerInfo
@@ -1293,6 +1292,7 @@
tANI_BOOLEAN csrIsValidMcConcurrentSession(tpAniSirGlobal pMac, tANI_U32 sessionId,
tSirBssDescription *pBssDesc);
tANI_BOOLEAN csrIsConnStateConnectedInfraAp( tpAniSirGlobal pMac, tANI_U32 sessionId );
+bool csr_is_ndi_started(tpAniSirGlobal mac_ctx, uint32_t session_id);
/*----------------------------------------------------------------------------
\fn csrRoamRegisterLinkQualityIndCallback
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/inc/oemDataApi.h b/drivers/staging/qcacld-2.0/CORE/SME/inc/oemDataApi.h
index 57c2cc4..5f5deea 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/inc/oemDataApi.h
+++ b/drivers/staging/qcacld-2.0/CORE/SME/inc/oemDataApi.h
@@ -42,14 +42,6 @@
#include "sirMacProtDef.h"
#include "csrLinkList.h"
-#ifndef OEM_DATA_REQ_SIZE
-#define OEM_DATA_REQ_SIZE 280
-#endif
-
-#ifndef OEM_DATA_RSP_SIZE
-#define OEM_DATA_RSP_SIZE 1724
-#endif
-
/* message subtype for internal purpose */
#define OEM_MESSAGE_SUBTYPE_INTERNAL 0xdeadbeef
#define OEM_MESSAGE_SUBTYPE_LEN 4
@@ -62,7 +54,7 @@
typedef struct tagOemDataReq
{
tANI_U8 sessionId;
- uint8_t data_len;
+ uint32_t data_len;
uint8_t *data;
} tOemDataReq, tOemDataReqConfig;
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/inc/smeInside.h b/drivers/staging/qcacld-2.0/CORE/SME/inc/smeInside.h
index e0de898..e8693b6 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/inc/smeInside.h
+++ b/drivers/staging/qcacld-2.0/CORE/SME/inc/smeInside.h
@@ -217,6 +217,7 @@
#ifdef WLAN_FEATURE_NAN_DATAPATH
struct ndp_initiator_req initiator_req;
struct ndp_responder_req responder_req;
+ struct ndp_end_req *data_end_req;
#endif
}u;
}tSmeCmd;
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/inc/smeInternal.h b/drivers/staging/qcacld-2.0/CORE/SME/inc/smeInternal.h
index 46e4fab..3f8ef67 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/inc/smeInternal.h
+++ b/drivers/staging/qcacld-2.0/CORE/SME/inc/smeInternal.h
@@ -100,6 +100,7 @@
eSmeCommandNoAUpdate,
eSmeCommandNdpInitiatorRequest,
eSmeCommandNdpResponderRequest,
+ eSmeCommandNdpDataEndInitiatorRequest,
} eSmeCommandType;
@@ -178,6 +179,12 @@
void(*pLinkLayerStatsIndCallback)(void *callbackContext,
int indType, void *pRsp);
#endif /* WLAN_FEATURE_LINK_LAYER_STATS */
+
+#ifdef WLAN_POWER_DEBUGFS
+ void *power_debug_stats_context;
+ void(*power_stats_resp_callback)(struct power_stats_response *rsp,
+ void *callback_context);
+#endif
#ifdef FEATURE_WLAN_AUTO_SHUTDOWN
void (*pAutoShutdownNotificationCb) (void);
#endif
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/inc/sme_Api.h b/drivers/staging/qcacld-2.0/CORE/SME/inc/sme_Api.h
index 00bacea..1adf98e 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/inc/sme_Api.h
+++ b/drivers/staging/qcacld-2.0/CORE/SME/inc/sme_Api.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -50,7 +50,6 @@
#include "vos_lock.h"
#include "halTypes.h"
#include "sirApi.h"
-#include "btcApi.h"
#include "vos_nvitem.h"
#include "p2p_Api.h"
#include "smeInternal.h"
@@ -266,6 +265,24 @@
uint32_t reserved2;
};
+/*
+ * struct sme_5g_pref_params : 5G preference params to be read from ini
+ * @rssi_boost_threshold_5g: RSSI threshold above which 5 GHz is favored
+ * @rssi_boost_factor_5g: Factor by which 5GHz RSSI is boosted
+ * @max_rssi_boost_5g: Maximum boost that can be applied to 5GHz RSSI
+ * @rssi_penalize_threshold_5g: RSSI threshold below which 5G is not favored
+ * @rssi_penalize_factor_5g: Factor by which 5GHz RSSI is penalized
+ * @max_rssi_penalize_5g: Maximum penalty that can be applied to 5G RSSI
+ */
+struct sme_5g_band_pref_params {
+ int8_t rssi_boost_threshold_5g;
+ uint8_t rssi_boost_factor_5g;
+ uint8_t max_rssi_boost_5g;
+ int8_t rssi_penalize_threshold_5g;
+ uint8_t rssi_penalize_factor_5g;
+ uint8_t max_rssi_penalize_5g;
+};
+
/*-------------------------------------------------------------------------
Function declarations and documentation
------------------------------------------------------------------------*/
@@ -1843,50 +1860,6 @@
tANI_U8 sessionId );
/* ---------------------------------------------------------------------------
- \fn sme_BtcSignalBtEvent
- \brief API to signal Bluetooth (BT) event to the WLAN driver. Based on the
- BT event type and the current operating mode of Libra (full power,
- BMPS, UAPSD etc), appropriate Bluetooth Coexistence (BTC) strategy
- would be employed.
- \param hHal - The handle returned by macOpen.
- \param pBtcBtEvent - Pointer to a caller allocated object of type tSmeBtEvent
- Caller owns the memory and is responsible for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE BT Event not passed to HAL. This can happen
- if driver has not yet been initialized or if BTC
- Events Layer has been disabled.
- VOS_STATUS_SUCCESS BT Event passed to HAL
- ---------------------------------------------------------------------------*/
-VOS_STATUS sme_BtcSignalBtEvent (tHalHandle hHal, tpSmeBtEvent pBtcBtEvent);
-
-/* ---------------------------------------------------------------------------
- \fn sme_BtcSetConfig
- \brief API to change the current Bluetooth Coexistence (BTC) configuration
- This function should be invoked only after CFG download has completed.
- Calling it after sme_HDDReadyInd is recommended.
- \param hHal - The handle returned by macOpen.
- \param pSmeBtcConfig - Pointer to a caller allocated object of type
- tSmeBtcConfig. Caller owns the memory and is responsible
- for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE Config not passed to HAL.
- VOS_STATUS_SUCCESS Config passed to HAL
- ---------------------------------------------------------------------------*/
-VOS_STATUS sme_BtcSetConfig (tHalHandle hHal, tpSmeBtcConfig pSmeBtcConfig);
-
-/* ---------------------------------------------------------------------------
- \fn sme_BtcGetConfig
- \brief API to retrieve the current Bluetooth Coexistence (BTC) configuration
- \param hHal - The handle returned by macOpen.
- \param pSmeBtcConfig - Pointer to a caller allocated object of type tSmeBtcConfig.
- Caller owns the memory and is responsible for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE - failure
- VOS_STATUS_SUCCESS success
- ---------------------------------------------------------------------------*/
-VOS_STATUS sme_BtcGetConfig (tHalHandle hHal, tpSmeBtcConfig pSmeBtcConfig);
-
-/* ---------------------------------------------------------------------------
\fn sme_SetCfgPrivacy
\brief API to set configure privacy parameters
\param hHal - The handle returned by macOpen.
@@ -4574,4 +4547,45 @@
eHalStatus sme_register_p2p_ack_ind_callback(tHalHandle hal,
sir_p2p_ack_ind_callback callback);
void sme_set_allowed_action_frames(tHalHandle hal, uint32_t bitmap0);
+void sme_set_5g_band_pref(tHalHandle hal_handle,
+ struct sme_5g_band_pref_params *pref_params);
+
+/**
+ * sme_set_random_mac() - Set random mac address filter
+ * @hal: hal handle for getting global mac struct
+ * @callback: callback to be invoked for response from firmware
+ * @session_id: interface id
+ * @random_mac: random mac address to be set
+ * @context: parameter to callback
+ *
+ * This function is used to set random mac address filter for action frames
+ * which are send with the same address, callback is invoked when corresponding
+ * event from firmware has come.
+ *
+ * Return: eHalStatus enumeration.
+ */
+eHalStatus sme_set_random_mac(tHalHandle hal,
+ action_frame_random_filter_callback callback,
+ uint32_t session_id, uint8_t *random_mac,
+ void *context);
+
+/**
+ * sme_clear_random_mac() - clear random mac address filter
+ * @hal: HAL handle
+ * @session_id: interface id
+ * @random_mac: random mac address to be cleared
+ *
+ * This function is used to clear the random mac address filters
+ * which are set with sme_set_random_mac
+ *
+ * Return: eHalStatus enumeration.
+ */
+eHalStatus sme_clear_random_mac(tHalHandle hal, uint32_t session_id,
+ uint8_t *random_mac);
+
+#ifdef WLAN_POWER_DEBUGFS
+eHalStatus sme_power_debug_stats_req(tHalHandle hal, void (*callback_fn)
+ (struct power_stats_response *response,
+ void *context), void *power_stats_context);
+#endif
#endif //#if !defined( __SME_API_H )
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/inc/sme_nan_datapath.h b/drivers/staging/qcacld-2.0/CORE/SME/inc/sme_nan_datapath.h
index bec7ed3..1216789 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/inc/sme_nan_datapath.h
+++ b/drivers/staging/qcacld-2.0/CORE/SME/inc/sme_nan_datapath.h
@@ -63,6 +63,19 @@
struct ndp_responder_req req;
};
+/**
+ * struct sir_sme_ndp_end_req - sme request struct for ndp end req
+ * @msg_type: SME msg type(sir_sme_ndp_initiator_req)
+ * @msg_len: lenght of message
+ * @req: actual ndp initiator request
+ *
+ */
+struct sir_sme_ndp_end_req {
+ uint16_t msg_type;
+ uint16_t msg_len;
+ struct ndp_end_req *req;
+};
+
/* NaN initiator request handler */
eHalStatus sme_ndp_initiator_req_handler(tHalHandle hal,
struct ndp_initiator_req *req_params);
@@ -72,28 +85,12 @@
struct ndp_responder_req *req_params);
/* NaN indication response handler */
-VOS_STATUS sme_ndp_end_req_handler(uint32_t session_id,
- struct ndp_end_req *req_params);
-
-/* NaN schedule update request handler */
-VOS_STATUS sme_ndp_sched_req_handler(uint32_t session_id,
- struct ndp_schedule_update_req *req_params);
-
-/* Function to handle NDP messages from lower layers */
-void sme_ndp_message_processor(tpAniSirGlobal mac_ctx, uint16_t msg_type,
- void *msg);
+VOS_STATUS sme_ndp_end_req_handler(tHalHandle hal, struct ndp_end_req *req);
/* Start NDI BSS */
VOS_STATUS csr_roam_start_ndi(tpAniSirGlobal mac_ctx, uint32_t session_id,
tCsrRoamProfile *profile);
-void csr_roam_fill_roaminfo_ndp(tpAniSirGlobal mac_ctx,
- tCsrRoamInfo *roam_info,
- eCsrRoamResult roam_result,
- tSirResultCodes status_code,
- uint32_t reason_code,
- uint32_t transaction_id);
-
void csr_roam_save_ndi_connected_info(tpAniSirGlobal mac_ctx,
tANI_U32 session_id,
tCsrRoamProfile *roam_profile,
@@ -106,13 +103,38 @@
void *roam_info);
eHalStatus csr_process_ndp_initiator_request(tpAniSirGlobal mac_ctx,
tSmeCmd *cmd);
+eHalStatus csr_process_ndp_data_end_request(tpAniSirGlobal mac_ctx,
+ tSmeCmd *cmd);
void sme_ndp_msg_processor(tpAniSirGlobal mac_ctx, vos_msg_t *msg);
eHalStatus csr_process_ndp_responder_request(tpAniSirGlobal mac_ctx,
tSmeCmd *cmd);
+
+void csr_release_ndp_initiator_req(tpAniSirGlobal mac_ctx, tSmeCmd *cmd);
+void csr_release_ndp_responder_req(tpAniSirGlobal mac_ctx, tSmeCmd *cmd);
+void csr_release_ndp_data_end_req(tpAniSirGlobal mac_ctx, tSmeCmd *cmd);
+
#else
+/* NAN initiator request handler */
+static inline eHalStatus sme_ndp_initiator_req_handler(tHalHandle hal,
+ void *req_params) {
+ return eHAL_STATUS_SUCCESS;
+}
+
+
+/* NAN responder request handler */
+static inline eHalStatus sme_ndp_responder_req_handler(tHalHandle hal,
+ void *req_params) {
+ return eHAL_STATUS_SUCCESS;
+}
+
+/* NAN indication response handler */
+static inline VOS_STATUS sme_ndp_end_req_handler(tHalHandle hal, void *req) {
+ return VOS_STATUS_SUCCESS;
+}
+
/* Start NDI BSS */
static inline VOS_STATUS csr_roam_start_ndi(tpAniSirGlobal mac_ctx,
uint32_t session_id,
@@ -121,16 +143,6 @@
return VOS_STATUS_SUCCESS;
}
-/* Fill in ndp information in roam_info */
-static inline void csr_roam_fill_roaminfo_ndp(tpAniSirGlobal mac_ctx,
- tCsrRoamInfo *roam_info,
- eCsrRoamResult roam_result,
- tSirResultCodes status_code,
- uint32_t reason_code,
- uint32_t transaction_id)
-{
-}
-
static inline void csr_roam_save_ndi_connected_info(tpAniSirGlobal mac_ctx,
tANI_U32 session_id,
tCsrRoamProfile *roam_profile,
@@ -161,6 +173,20 @@
{
return eHAL_STATUS_SUCCESS;
}
+
+static inline eHalStatus csr_process_ndp_data_end_request(
+ tpAniSirGlobal mac_ctx, tSmeCmd *cmd)
+{
+ return eHAL_STATUS_SUCCESS;
+}
+
+static inline void csr_release_ndp_initiator_req(tpAniSirGlobal mac_ctx,
+ tSmeCmd *cmd) {}
+static inline void csr_release_ndp_responder_req(tpAniSirGlobal mac_ctx,
+ tSmeCmd *cmd) {}
+static inline void csr_release_ndp_data_end_req(tpAniSirGlobal mac_ctx,
+ tSmeCmd *cmd) {}
+
#endif /* WLAN_FEATURE_NAN_DATAPATH */
#endif /* __SME_NAN_DATAPATH_H */
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/QoS/sme_Qos.c b/drivers/staging/qcacld-2.0/CORE/SME/src/QoS/sme_Qos.c
index 0d3e783..9546f70 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/QoS/sme_Qos.c
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/QoS/sme_Qos.c
@@ -3386,14 +3386,7 @@
pTspec->suspendInterval = pTspec_Info->suspension_interval;
pTspec->svcStartTime = pTspec_Info->svc_start_time;
pTspec->tsinfo.traffic.direction = pTspec_Info->ts_info.direction;
-
- //Make sure UAPSD is allowed. BTC may want to disable UAPSD while keep QoS setup
- if (pTspec_Info->ts_info.psb && btcIsReadyForUapsd(pMac)) {
- pTspec->tsinfo.traffic.psb = pTspec_Info->ts_info.psb;
- } else {
- pTspec->tsinfo.traffic.psb = 0;
- pTspec_Info->ts_info.psb = 0;
- }
+ pTspec->tsinfo.traffic.psb = pTspec_Info->ts_info.psb;
pTspec->tsinfo.traffic.tsid = pTspec_Info->ts_info.tid;
pTspec->tsinfo.traffic.userPrio = pTspec_Info->ts_info.up;
pTspec->tsinfo.traffic.accessPolicy = SME_QOS_ACCESS_POLICY_EDCA;
@@ -3503,15 +3496,7 @@
ricIE.TSPEC.suspension_int = pTspec_Info->suspension_interval;
ricIE.TSPEC.service_start_time = pTspec_Info->svc_start_time;
ricIE.TSPEC.direction = pTspec_Info->ts_info.direction;
- //Make sure UAPSD is allowed. BTC may want to disable UAPSD while keep QoS setup
- if( pTspec_Info->ts_info.psb && btcIsReadyForUapsd(pMac) )
- {
- ricIE.TSPEC.psb = pTspec_Info->ts_info.psb;
- }
- else
- {
- ricIE.TSPEC.psb = 0;
- }
+ ricIE.TSPEC.psb = pTspec_Info->ts_info.psb;
ricIE.TSPEC.tsid = pTspec_Info->ts_info.tid;
ricIE.TSPEC.user_priority = pTspec_Info->ts_info.up;
ricIE.TSPEC.access_policy = SME_QOS_ACCESS_POLICY_EDCA;
@@ -3547,15 +3532,7 @@
ricIE.WMMTSPEC.suspension_int = pTspec_Info->suspension_interval;
ricIE.WMMTSPEC.service_start_time = pTspec_Info->svc_start_time;
ricIE.WMMTSPEC.direction = pTspec_Info->ts_info.direction;
- //Make sure UAPSD is allowed. BTC may want to disable UAPSD while keep QoS setup
- if( pTspec_Info->ts_info.psb && btcIsReadyForUapsd(pMac) )
- {
- ricIE.WMMTSPEC.psb = pTspec_Info->ts_info.psb;
- }
- else
- {
- ricIE.WMMTSPEC.psb = 0;
- }
+ ricIE.WMMTSPEC.psb = pTspec_Info->ts_info.psb;
ricIE.WMMTSPEC.tsid = pTspec_Info->ts_info.tid;
ricIE.WMMTSPEC.user_priority = pTspec_Info->ts_info.up;
ricIE.WMMTSPEC.access_policy = SME_QOS_ACCESS_POLICY_EDCA;
@@ -4126,9 +4103,7 @@
pMsg->req.tspec.svcStartTime = 0;
pMsg->req.tspec.tsinfo.traffic.direction = pTspec_Info->ts_info.direction;
//Make sure UAPSD is allowed. BTC may want to disable UAPSD while keep QoS setup
- if( pTspec_Info->ts_info.psb
- && btcIsReadyForUapsd(pMac)
- )
+ if( pTspec_Info->ts_info.psb)
{
pMsg->req.tspec.tsinfo.traffic.psb = pTspec_Info->ts_info.psb;
}
@@ -8371,14 +8346,7 @@
tCsrRoamModifyProfileFields modifyProfileFields;
//we need to do a reassoc on these AC
csrGetModifyProfileFields(pMac, sessionId, &modifyProfileFields);
- if( btcIsReadyForUapsd(pMac) )
- {
- modifyProfileFields.uapsd_mask = uapsd_mask;
- }
- else
- {
- modifyProfileFields.uapsd_mask = 0;
- }
+ modifyProfileFields.uapsd_mask = uapsd_mask;
//Do we need to inform HDD?
if(!HAL_STATUS_SUCCESS(sme_QosRequestReassoc(pMac, sessionId, &modifyProfileFields, VOS_TRUE)))
{
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/btc/btcApi.c b/drivers/staging/qcacld-2.0/CORE/SME/src/btc/btcApi.c
deleted file mode 100644
index 2615d71..0000000
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/btc/btcApi.c
+++ /dev/null
@@ -1,2075 +0,0 @@
-/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
- *
- * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
- *
- *
- * Permission to use, copy, modify, and/or distribute this software for
- * any purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all
- * copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-/*
- * This file was originally distributed by Qualcomm Atheros, Inc.
- * under proprietary terms before Copyright ownership was assigned
- * to the Linux Foundation.
- */
-
-/******************************************************************************
-*
-* Name: btcApi.c
-*
-* Description: Routines that make up the BTC API.
-*
-
-*
-******************************************************************************/
-#include "wlan_qct_wda.h"
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
-#include "aniGlobal.h"
-#include "smsDebug.h"
-#include "btcApi.h"
-#include "cfgApi.h"
-#include "pmc.h"
-#include "smeQosInternal.h"
-#include "sme_Trace.h"
-#ifdef FEATURE_WLAN_DIAG_SUPPORT
-#include "vos_diag_core_event.h"
-#include "vos_diag_core_log.h"
-#endif /* FEATURE_WLAN_DIAG_SUPPORT */
-static void btcLogEvent (tHalHandle hHal, tpSmeBtEvent pBtEvent);
-static void btcRestoreHeartBeatMonitoringHandle(void* hHal);
-static void btcUapsdCheck( tpAniSirGlobal pMac, tpSmeBtEvent pBtEvent );
-VOS_STATUS btcCheckHeartBeatMonitoring(tHalHandle hHal, tpSmeBtEvent pBtEvent);
-static void btcPowerStateCB( v_PVOID_t pContext, tPmcState pmcState );
-static void btcPowerOffloadStateCB(v_PVOID_t pContext, tANI_U32 sessionId,
- tPmcState pmcState );
-static VOS_STATUS btcDeferEvent( tpAniSirGlobal pMac, tpSmeBtEvent pEvent );
-static VOS_STATUS btcDeferDisconnEvent( tpAniSirGlobal pMac, tpSmeBtEvent pEvent );
-#ifdef FEATURE_WLAN_DIAG_SUPPORT
-static void btcDiagEventLog (tHalHandle hHal, tpSmeBtEvent pBtEvent);
-#endif /* FEATURE_WLAN_DIAG_SUPPORT */
-/* ---------------------------------------------------------------------------
- \fn btcOpen
- \brief API to init the BTC Events Layer
- \param hHal - The handle returned by macOpen.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE success
- VOS_STATUS_SUCCESS failure
- ---------------------------------------------------------------------------*/
-VOS_STATUS btcOpen (tHalHandle hHal)
-{
- tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
- VOS_STATUS vosStatus;
- int i;
-
- /* Initialize BTC configuration. */
- pMac->btc.btcConfig.btcExecutionMode = BTC_SMART_COEXISTENCE;
-
- pMac->btc.btcReady = VOS_FALSE;
- pMac->btc.btcEventState = 0;
- pMac->btc.btcHBActive = VOS_TRUE;
- pMac->btc.btcScanCompromise = VOS_FALSE;
-
- for (i = 0; i < MWS_COEX_MAX_CONFIG; i++)
- {
- pMac->btc.btcConfig.mwsCoexConfig[i] = 0;
- }
-
- vosStatus = vos_timer_init( &pMac->btc.restoreHBTimer,
- VOS_TIMER_TYPE_SW,
- btcRestoreHeartBeatMonitoringHandle,
- (void*) hHal);
- if (!VOS_IS_STATUS_SUCCESS(vosStatus)) {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "btcOpen: Fail to init timer");
- return VOS_STATUS_E_FAILURE;
- }
-
- if(!pMac->psOffloadEnabled)
- {
- if(!HAL_STATUS_SUCCESS(pmcRegisterDeviceStateUpdateInd(pMac,
- btcPowerStateCB, pMac)))
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
- "btcOpen: Fail to register PMC callback");
- return VOS_STATUS_E_FAILURE;
- }
- }
- else
- {
- tANI_U32 i;
- for(i = 0; i < CSR_ROAM_SESSION_MAX; i++)
- {
- if(!HAL_STATUS_SUCCESS(pmcOffloadRegisterDeviceStateUpdateInd(pMac,
- i, btcPowerOffloadStateCB, pMac)))
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
- "btcOpen: Fail to register PMC callback");
- return VOS_STATUS_E_FAILURE;
- }
- }
- }
- return VOS_STATUS_SUCCESS;
-}
-/* ---------------------------------------------------------------------------
- \fn btcClose
- \brief API to exit the BTC Events Layer
- \param hHal - The handle returned by macOpen.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE success
- VOS_STATUS_SUCCESS failure
- ---------------------------------------------------------------------------*/
-VOS_STATUS btcClose (tHalHandle hHal)
-{
- tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
- VOS_STATUS vosStatus;
- pMac->btc.btcReady = VOS_FALSE;
- pMac->btc.btcUapsdOk = VOS_FALSE;
- vos_timer_stop(&pMac->btc.restoreHBTimer);
- vosStatus = vos_timer_destroy(&pMac->btc.restoreHBTimer);
- if (!VOS_IS_STATUS_SUCCESS(vosStatus)) {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "btcClose: Fail to destroy timer");
- return VOS_STATUS_E_FAILURE;
- }
-
- if(!pMac->psOffloadEnabled)
- {
- if(!HAL_STATUS_SUCCESS(
- pmcDeregisterDeviceStateUpdateInd(pMac, btcPowerStateCB)))
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,
- "%s: %d: cannot deregister pmcDeregisterDeviceStateUpdateInd()",
- __func__, __LINE__);
- }
- }
- else
- {
- tANI_U32 i;
- for(i = 0; i < CSR_ROAM_SESSION_MAX; i++)
- {
- if(!HAL_STATUS_SUCCESS(pmcOffloadDeregisterDeviceStateUpdateInd(pMac,
- i, btcPowerOffloadStateCB)))
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
- "btcOpen: Fail to deregister PMC callback");
- return VOS_STATUS_E_FAILURE;
- }
- }
- }
- return VOS_STATUS_SUCCESS;
-}
-
-/* ---------------------------------------------------------------------------
- \fn btcReady
- \brief fn to inform BTC that eWNI_SME_SYS_READY_IND has been sent to PE.
- This acts as a trigger to send a message to HAL to update the BTC
- related config to FW. Note that if HDD configures any power BTC
- related stuff before this API is invoked, BTC will buffer all the
- configuration.
- \param hHal - The handle returned by macOpen.
- \return VOS_STATUS
- ---------------------------------------------------------------------------*/
-VOS_STATUS btcReady (tHalHandle hHal)
-{
- tpAniSirGlobal pMac = PMAC_STRUCT(hHal);
- v_U32_t cfgVal = 0;
- v_U8_t i;
- pMac->btc.btcReady = VOS_TRUE;
- pMac->btc.btcUapsdOk = VOS_TRUE;
- for(i=0; i < BT_MAX_SCO_SUPPORT; i++)
- {
- pMac->btc.btcScoHandles[i] = BT_INVALID_CONN_HANDLE;
- }
-
- // Read heartbeat threshold CFG and save it.
- ccmCfgGetInt(pMac, WNI_CFG_HEART_BEAT_THRESHOLD, &cfgVal);
- pMac->btc.btcHBCount = (v_U8_t)cfgVal;
- if (btcSendCfgMsg(hHal, &(pMac->btc.btcConfig)) != VOS_STATUS_SUCCESS)
- {
- return VOS_STATUS_E_FAILURE;
- }
- return VOS_STATUS_SUCCESS;
-}
-
-static VOS_STATUS btcSendBTEvent(tpAniSirGlobal pMac, tpSmeBtEvent pBtEvent)
-{
- vos_msg_t msg;
- tpSmeBtEvent ptrSmeBtEvent = NULL;
- switch(pBtEvent->btEventType)
- {
- case BT_EVENT_CREATE_SYNC_CONNECTION:
- case BT_EVENT_SYNC_CONNECTION_UPDATED:
- if(pBtEvent->uEventParam.btSyncConnection.linkType != BT_SCO &&
- pBtEvent->uEventParam.btSyncConnection.linkType != BT_eSCO)
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
- "Invalid link type %d for Sync Connection. BT event will be dropped ",
- __func__, pBtEvent->uEventParam.btSyncConnection.linkType);
- return VOS_STATUS_E_FAILURE;
- }
- break;
- case BT_EVENT_SYNC_CONNECTION_COMPLETE:
- if((pBtEvent->uEventParam.btSyncConnection.status == BT_CONN_STATUS_SUCCESS) &&
- ((pBtEvent->uEventParam.btSyncConnection.linkType != BT_SCO && pBtEvent->uEventParam.btSyncConnection.linkType != BT_eSCO) ||
- (pBtEvent->uEventParam.btSyncConnection.connectionHandle == BT_INVALID_CONN_HANDLE)))
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
- "Invalid connection handle %d or link type %d for Sync Connection. BT event will be dropped ",
- __func__,
- pBtEvent->uEventParam.btSyncConnection.connectionHandle,
- pBtEvent->uEventParam.btSyncConnection.linkType);
- return VOS_STATUS_E_FAILURE;
- }
- break;
- case BT_EVENT_MODE_CHANGED:
- if(pBtEvent->uEventParam.btAclModeChange.mode >= BT_ACL_MODE_MAX)
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
- "Invalid mode %d for ACL Connection. BT event will be dropped ",
- __func__,
- pBtEvent->uEventParam.btAclModeChange.mode);
- return VOS_STATUS_E_FAILURE;
- }
- break;
- case BT_EVENT_DEVICE_SWITCHED_OFF:
- pMac->btc.btcEventState = 0;
- break;
- default:
- break;
- }
- ptrSmeBtEvent = vos_mem_malloc(sizeof(tSmeBtEvent));
- if (NULL == ptrSmeBtEvent)
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
- "Not able to allocate memory for BT event", __func__);
- return VOS_STATUS_E_FAILURE;
- }
- btcLogEvent(pMac, pBtEvent);
-#ifdef FEATURE_WLAN_DIAG_SUPPORT
- btcDiagEventLog(pMac, pBtEvent);
-#endif
- vos_mem_copy(ptrSmeBtEvent, pBtEvent, sizeof(tSmeBtEvent));
- msg.type = WDA_SIGNAL_BT_EVENT;
- msg.reserved = 0;
- msg.bodyptr = ptrSmeBtEvent;
- MTRACE(vos_trace(VOS_MODULE_ID_SME, TRACE_CODE_SME_TX_WDA_MSG, NO_SESSION,
- msg.type));
- if(VOS_STATUS_SUCCESS != vos_mq_post_message(VOS_MODULE_ID_WDA, &msg))
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
- "Not able to post WDA_SIGNAL_BT_EVENT message to WDA", __func__);
- vos_mem_free( ptrSmeBtEvent );
- return VOS_STATUS_E_FAILURE;
- }
- // After successfully posting the message, check if heart beat
- // monitoring needs to be turned off
- (void)btcCheckHeartBeatMonitoring(pMac, pBtEvent);
- //Check whether BTC and UAPSD can co-exist
- btcUapsdCheck( pMac, pBtEvent );
- return VOS_STATUS_SUCCESS;
- }
-
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
-/* ---------------------------------------------------------------------------
- \fn btcSignalBTEvent
- \brief API to signal Bluetooth (BT) event to the WLAN driver. Based on the
- BT event type and the current operating mode of Libra (full power,
- BMPS, UAPSD etc), appropriate Bluetooth Coexistence (BTC) strategy
- would be employed.
- \param hHal - The handle returned by macOpen.
- \param pBtEvent - Pointer to a caller allocated object of type tSmeBtEvent.
- Caller owns the memory and is responsible for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE – BT Event not passed to HAL. This can happen
- if driver has not yet been initialized or if BTC
- Events Layer has been disabled.
- VOS_STATUS_SUCCESS – BT Event passed to HAL
- ---------------------------------------------------------------------------*/
-VOS_STATUS btcSignalBTEvent (tHalHandle hHal, tpSmeBtEvent pBtEvent)
-{
- tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
- VOS_STATUS vosStatus;
- if( NULL == pBtEvent )
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
- "Null pointer for SME BT Event", __func__);
- return VOS_STATUS_E_FAILURE;
- }
- if(( BTC_WLAN_ONLY == pMac->btc.btcConfig.btcExecutionMode ) ||
- ( BTC_PTA_ONLY == pMac->btc.btcConfig.btcExecutionMode ))
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
- "BTC execution mode not set to BTC_SMART_COEXISTENCE. BT event will be dropped", __func__);
- return VOS_STATUS_E_FAILURE;
- }
- if( pBtEvent->btEventType < 0 || pBtEvent->btEventType >= BT_EVENT_TYPE_MAX )
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
- "Invalid BT event %d being passed. BT event will be dropped",
- __func__, pBtEvent->btEventType);
- return VOS_STATUS_E_FAILURE;
- }
- //Check PMC state to make sure whether we need to defer
- //If we already have deferred events, defer the new one as well, in case PMC is in transition state
- if( pMac->btc.fReplayBTEvents || !PMC_IS_CHIP_ACCESSIBLE(pmcGetPmcState( pMac )) )
- {
- //We need to defer the event
- vosStatus = btcDeferEvent(pMac, pBtEvent);
- if( VOS_IS_STATUS_SUCCESS(vosStatus) )
- {
- pMac->btc.fReplayBTEvents = VOS_TRUE;
- return VOS_STATUS_SUCCESS;
- }
- else
- {
- return vosStatus;
- }
- }
- btcSendBTEvent(pMac, pBtEvent);
- return VOS_STATUS_SUCCESS;
-}
-#endif
-/* ---------------------------------------------------------------------------
- \fn btcCheckHeartBeatMonitoring
- \brief API to check whether heartbeat monitoring is required to be disabled
- for specific BT start events which takes significant time to complete
- during which WLAN misses beacons. To avoid WLAN-MAC from disconnecting
- for the not enough beacons received we stop the heartbeat timer during
- this start BT event till the stop of that BT event.
- \param hHal - The handle returned by macOpen.
- \param pBtEvent - Pointer to a caller allocated object of type tSmeBtEvent.
- Caller owns the memory and is responsible for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE Config not passed to HAL.
- VOS_STATUS_SUCCESS Config passed to HAL
- ---------------------------------------------------------------------------*/
-VOS_STATUS btcCheckHeartBeatMonitoring(tHalHandle hHal, tpSmeBtEvent pBtEvent)
-{
- tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
- VOS_STATUS vosStatus;
- switch(pBtEvent->btEventType)
- {
- // Start events which requires heartbeat monitoring be disabled.
- case BT_EVENT_INQUIRY_STARTED:
- pMac->btc.btcEventState |= BT_INQUIRY_STARTED;
- break;
- case BT_EVENT_PAGE_STARTED:
- pMac->btc.btcEventState |= BT_PAGE_STARTED;
- break;
- case BT_EVENT_CREATE_ACL_CONNECTION:
- pMac->btc.btcEventState |= BT_CREATE_ACL_CONNECTION_STARTED;
- break;
- case BT_EVENT_CREATE_SYNC_CONNECTION:
- pMac->btc.btcEventState |= BT_CREATE_SYNC_CONNECTION_STARTED;
- break;
- // Stop/done events which indicates heartbeat monitoring can be enabled
- case BT_EVENT_INQUIRY_STOPPED:
- pMac->btc.btcEventState &= ~(BT_INQUIRY_STARTED);
- break;
- case BT_EVENT_PAGE_STOPPED:
- pMac->btc.btcEventState &= ~(BT_PAGE_STARTED);
- break;
- case BT_EVENT_ACL_CONNECTION_COMPLETE:
- pMac->btc.btcEventState &= ~(BT_CREATE_ACL_CONNECTION_STARTED);
- break;
- case BT_EVENT_SYNC_CONNECTION_COMPLETE:
- pMac->btc.btcEventState &= ~(BT_CREATE_SYNC_CONNECTION_STARTED);
- break;
- default:
- // Ignore other events
- return VOS_STATUS_SUCCESS;
- }
- // Check if any of the BT start events are active
- if (pMac->btc.btcEventState) {
- if (pMac->btc.btcHBActive) {
- // set heartbeat threshold CFG to zero
- ccmCfgSetInt(pMac, WNI_CFG_HEART_BEAT_THRESHOLD, 0, NULL, eANI_BOOLEAN_FALSE);
- pMac->btc.btcHBActive = VOS_FALSE;
- }
- // Deactivate and active the restore HB timer
- vos_timer_stop( &pMac->btc.restoreHBTimer);
- vosStatus= vos_timer_start( &pMac->btc.restoreHBTimer, BT_MAX_EVENT_DONE_TIMEOUT );
- if (!VOS_IS_STATUS_SUCCESS(vosStatus)) {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "btcCheckHeartBeatMonitoring: Fail to start timer");
- return VOS_STATUS_E_FAILURE;
- }
- } else {
- // Restore CFG back to the original value only if it was disabled
- if (!pMac->btc.btcHBActive) {
- ccmCfgSetInt(pMac, WNI_CFG_HEART_BEAT_THRESHOLD, pMac->btc.btcHBCount, NULL, eANI_BOOLEAN_FALSE);
- pMac->btc.btcHBActive = VOS_TRUE;
- }
- // Deactivate the timer
- vosStatus = vos_timer_stop( &pMac->btc.restoreHBTimer);
- if (!VOS_IS_STATUS_SUCCESS(vosStatus)) {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "btcCheckHeartBeatMonitoring: Fail to stop timer");
- return VOS_STATUS_E_FAILURE;
- }
- }
- return VOS_STATUS_SUCCESS;
-}
-/* ---------------------------------------------------------------------------
- \fn btcRestoreHeartBeatMonitoringHandle
- \brief Timer handler to handle the timeout condition when a specific BT
- stop event does not come back, in which case to restore back the
- heartbeat timer.
- \param hHal - The handle returned by macOpen.
- \return VOID
- ---------------------------------------------------------------------------*/
-void btcRestoreHeartBeatMonitoringHandle(tHalHandle hHal)
-{
- tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
- if( !pMac->btc.btcHBActive )
- {
- tPmcState pmcState;
- //Check PMC state to make sure whether we need to defer
- pmcState = pmcGetPmcState( pMac );
- if( PMC_IS_CHIP_ACCESSIBLE(pmcState) )
- {
- // Restore CFG back to the original value
- ccmCfgSetInt(pMac, WNI_CFG_HEART_BEAT_THRESHOLD, pMac->btc.btcHBCount, NULL, eANI_BOOLEAN_FALSE);
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "BT event timeout, restoring back HeartBeat timer");
- }
- else
- {
- //defer it
- pMac->btc.btcEventReplay.fRestoreHBMonitor = VOS_TRUE;
- }
- }
-}
-
-
-/* ---------------------------------------------------------------------------
- \fn btcSetConfig
- \brief API to change the current Bluetooth Coexistence (BTC) configuration
- This function should be invoked only after CFG download has completed.
- \param hHal - The handle returned by macOpen.
- \param pSmeBtcConfig - Pointer to a caller allocated object of type
- tSmeBtcConfig. Caller owns the memory and is responsible
- for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE Config not passed to HAL.
- VOS_STATUS_SUCCESS Config passed to HAL
- ---------------------------------------------------------------------------*/
-VOS_STATUS btcSetConfig (tHalHandle hHal, tpSmeBtcConfig pSmeBtcConfig)
-{
- tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
- //Save a copy in the global BTC config
- vos_mem_copy(&(pMac->btc.btcConfig), pSmeBtcConfig, sizeof(tSmeBtcConfig));
- //Send the config down only if SME_HddReady has been invoked. If not ready,
- //BTC config will plumbed down when btcReady is eventually invoked.
- if(pMac->btc.btcReady)
- {
- if(VOS_STATUS_SUCCESS != btcSendCfgMsg(hHal, pSmeBtcConfig))
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,
- "Failure to send BTC config down");
- return VOS_STATUS_E_FAILURE;
- }
- }
- return VOS_STATUS_SUCCESS;
-}
-/* ---------------------------------------------------------------------------
- \fn btcPostBtcCfgMsg
- \brief Private API to post BTC config message to HAL
- \param hHal - The handle returned by macOpen.
- \param pSmeBtcConfig - Pointer to a caller allocated object of type
- tSmeBtcConfig. Caller owns the memory and is responsible
- for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE Config not passed to HAL.
- VOS_STATUS_SUCCESS Config passed to HAL
- ---------------------------------------------------------------------------*/
-VOS_STATUS btcSendCfgMsg(tHalHandle hHal, tpSmeBtcConfig pSmeBtcConfig)
-{
- tpSmeBtcConfig ptrSmeBtcConfig = NULL;
- vos_msg_t msg;
- if( NULL == pSmeBtcConfig )
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "btcSendCfgMsg: "
- "Null pointer for BTC Config");
- return VOS_STATUS_E_FAILURE;
- }
- if( pSmeBtcConfig->btcExecutionMode >= BT_EXEC_MODE_MAX )
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "btcSendCfgMsg: "
- "Invalid BT execution mode %d being set",
- pSmeBtcConfig->btcExecutionMode);
- return VOS_STATUS_E_FAILURE;
- }
- ptrSmeBtcConfig = vos_mem_malloc(sizeof(tSmeBtcConfig));
- if (NULL == ptrSmeBtcConfig)
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "btcSendCfgMsg: "
- "Not able to allocate memory for SME BTC Config");
- return VOS_STATUS_E_FAILURE;
- }
- vos_mem_copy(ptrSmeBtcConfig, pSmeBtcConfig, sizeof(tSmeBtcConfig));
- msg.type = WDA_BTC_SET_CFG;
- msg.reserved = 0;
- msg.bodyptr = ptrSmeBtcConfig;
- MTRACE(vos_trace(VOS_MODULE_ID_SME, TRACE_CODE_SME_TX_WDA_MSG, NO_SESSION,
- msg.type));
- if(VOS_STATUS_SUCCESS != vos_mq_post_message(VOS_MODULE_ID_WDA, &msg))
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "btcSendCfgMsg: "
- "Not able to post WDA_BTC_SET_CFG message to WDA");
- vos_mem_free( ptrSmeBtcConfig );
- return VOS_STATUS_E_FAILURE;
- }
- return VOS_STATUS_SUCCESS;
-}
-/* ---------------------------------------------------------------------------
- \fn btcGetConfig
- \brief API to retrieve the current Bluetooth Coexistence (BTC) configuration
- \param hHal - The handle returned by macOpen.
- \param pSmeBtcConfig - Pointer to a caller allocated object of type
- tSmeBtcConfig. Caller owns the memory and is responsible
- for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE - failure
- VOS_STATUS_SUCCESS success
- ---------------------------------------------------------------------------*/
-VOS_STATUS btcGetConfig (tHalHandle hHal, tpSmeBtcConfig pSmeBtcConfig)
-{
- tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
- if( NULL == pSmeBtcConfig )
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "btcGetConfig: "
- "Null pointer for BTC Config");
- return VOS_STATUS_E_FAILURE;
- }
- vos_mem_copy(pSmeBtcConfig, &(pMac->btc.btcConfig), sizeof(tSmeBtcConfig));
- return VOS_STATUS_SUCCESS;
-}
-/*
- btcFindAclEventHist find a suited ACL event buffer
- Param: bdAddr - NULL meaning not care.
- pointer to caller allocated buffer containing the BD
- address to find a match
- handle - BT_INVALID_CONN_HANDLE == not care
- otherwise, a handle to match
- NOPTE: Either bdAddr or handle can be valid, if both of them are valid, use bdAddr only. If neither
- bdAddr nor handle is valid, return the next free slot.
-*/
-static tpSmeBtAclEventHist btcFindAclEventHist( tpAniSirGlobal pMac, v_U8_t *bdAddr, v_U16_t handle )
-{
- int i, j;
- tpSmeBtAclEventHist pRet = NULL;
- tSmeBtcEventReplay *pReplay = &pMac->btc.btcEventReplay;
- for( i = 0; (i < BT_MAX_ACL_SUPPORT) && (NULL == pRet); i++ )
- {
- if( NULL != bdAddr )
- {
- //try to match addr
- if( pReplay->btcEventHist.btAclConnectionEvent[i].bNextEventIdx )
- {
- for(j = 0; j < pReplay->btcEventHist.btAclConnectionEvent[i].bNextEventIdx; j++)
- {
- if( vos_mem_compare(pReplay->btcEventHist.btAclConnectionEvent[i].btAclConnection[j].bdAddr,
- bdAddr, 6) )
- {
- //found it
- pRet = &pReplay->btcEventHist.btAclConnectionEvent[i];
- break;
- }
- }
- }
- }
- else if( BT_INVALID_CONN_HANDLE != handle )
- {
- //try to match handle
- if( pReplay->btcEventHist.btAclConnectionEvent[i].bNextEventIdx )
- {
- for(j = 0; j < pReplay->btcEventHist.btAclConnectionEvent[i].bNextEventIdx; j++)
- {
- if( pReplay->btcEventHist.btAclConnectionEvent[i].btAclConnection[j].connectionHandle ==
- handle )
- {
- //found it
- pRet = &pReplay->btcEventHist.btAclConnectionEvent[i];
- break;
- }
- }
- }
- }
- else if( 0 == pReplay->btcEventHist.btAclConnectionEvent[i].bNextEventIdx )
- {
- pRet = &pReplay->btcEventHist.btAclConnectionEvent[i];
- break;
- }
- }
- return (pRet);
-}
-
-/*
- btcFindSyncEventHist find a suited SYNC event buffer
- Param: bdAddr - NULL meaning not care.
- pointer to caller allocated buffer containing the
- BD address to find a match
- handle - BT_INVALID_CONN_HANDLE == not care
- otherwise, a handle to match
- NOPTE: Either bdAddr or handle can be valid, if both of them are valid, use bdAddr only. If neither
- bdAddr nor handle is valid, return the next free slot.
-*/
-static tpSmeBtSyncEventHist btcFindSyncEventHist( tpAniSirGlobal pMac, v_U8_t *bdAddr, v_U16_t handle )
-{
- int i, j;
- tpSmeBtSyncEventHist pRet = NULL;
- tSmeBtcEventReplay *pReplay = &pMac->btc.btcEventReplay;
- for( i = 0; (i < BT_MAX_SCO_SUPPORT) && (NULL == pRet); i++ )
- {
- if( NULL != bdAddr )
- {
- //try to match addr
- if( pReplay->btcEventHist.btSyncConnectionEvent[i].bNextEventIdx )
- {
- for(j = 0; j < pReplay->btcEventHist.btAclConnectionEvent[i].bNextEventIdx; j++)
- {
- if( vos_mem_compare(pReplay->btcEventHist.btSyncConnectionEvent[i].btSyncConnection[j].bdAddr,
- bdAddr, 6) )
- {
- //found it
- pRet = &pReplay->btcEventHist.btSyncConnectionEvent[i];
- break;
- }
- }
- }
- }
- else if( BT_INVALID_CONN_HANDLE != handle )
- {
- //try to match handle
- if( pReplay->btcEventHist.btSyncConnectionEvent[i].bNextEventIdx )
- {
- for(j = 0; j < pReplay->btcEventHist.btAclConnectionEvent[i].bNextEventIdx; j++)
- {
- if( pReplay->btcEventHist.btSyncConnectionEvent[i].btSyncConnection[j].connectionHandle ==
- handle )
- {
- //found it
- pRet = &pReplay->btcEventHist.btSyncConnectionEvent[i];
- break;
- }
- }
- }
- }
- else if( !pReplay->btcEventHist.btSyncConnectionEvent[i].bNextEventIdx )
- {
- pRet = &pReplay->btcEventHist.btSyncConnectionEvent[i];
- break;
- }
- }
- return (pRet);
-}
-
-/*
- btcFindDisconnEventHist find a slot for the deferred disconnect event
- If handle is invalid, it returns a free slot, if any.
- If handle is valid, it tries to find a match first in case same disconnect event comes down again.
-*/
-static tpSmeBtDisconnectEventHist btcFindDisconnEventHist( tpAniSirGlobal pMac, v_U16_t handle )
-{
- tpSmeBtDisconnectEventHist pRet = NULL;
- tSmeBtcEventReplay *pReplay = &pMac->btc.btcEventReplay;
- int i;
- if( BT_INVALID_CONN_HANDLE != handle )
- {
- for(i = 0; i < BT_MAX_DISCONN_SUPPORT; i++)
- {
- if( pReplay->btcEventHist.btDisconnectEvent[i].fValid &&
- (handle == pReplay->btcEventHist.btDisconnectEvent[i].btDisconnect.connectionHandle) )
- {
- pRet = &pReplay->btcEventHist.btDisconnectEvent[i];
- break;
- }
- }
- }
- if( NULL == pRet )
- {
- //Find a free slot
- for(i = 0; i < BT_MAX_DISCONN_SUPPORT; i++)
- {
- if( !pReplay->btcEventHist.btDisconnectEvent[i].fValid )
- {
- pRet = &pReplay->btcEventHist.btDisconnectEvent[i];
- break;
- }
- }
- }
- return (pRet);
-}
-
-/*
- btcFindModeChangeEventHist find a slot for the deferred mode change event
- If handle is invalid, it returns a free slot, if any.
- If handle is valid, it tries to find a match first in case same disconnect event comes down again.
-*/
-tpSmeBtAclModeChangeEventHist btcFindModeChangeEventHist( tpAniSirGlobal pMac, v_U16_t handle )
-{
- tpSmeBtAclModeChangeEventHist pRet = NULL;
- tSmeBtcEventReplay *pReplay = &pMac->btc.btcEventReplay;
- int i;
- if( BT_INVALID_CONN_HANDLE != handle )
- {
- for(i = 0; i < BT_MAX_ACL_SUPPORT; i++)
- {
- if( pReplay->btcEventHist.btAclModeChangeEvent[i].fValid &&
- (handle == pReplay->btcEventHist.btAclModeChangeEvent[i].btAclModeChange.connectionHandle) )
- {
- pRet = &pReplay->btcEventHist.btAclModeChangeEvent[i];
- break;
- }
- }
- }
- if( NULL == pRet )
- {
- //Find a free slot
- for(i = 0; i < BT_MAX_ACL_SUPPORT; i++)
- {
- if( !pReplay->btcEventHist.btAclModeChangeEvent[i].fValid )
- {
- pRet = &pReplay->btcEventHist.btAclModeChangeEvent[i];
- break;
- }
- }
- }
- return (pRet);
-}
-
-/*
- btcFindSyncUpdateEventHist find a slot for the deferred SYNC_UPDATE event
- If handle is invalid, it returns a free slot, if any.
- If handle is valid, it tries to find a match first in case same disconnect event comes down again.
-*/
-tpSmeBtSyncUpdateHist btcFindSyncUpdateEventHist( tpAniSirGlobal pMac, v_U16_t handle )
-{
- tpSmeBtSyncUpdateHist pRet = NULL;
- tSmeBtcEventReplay *pReplay = &pMac->btc.btcEventReplay;
- int i;
- if( BT_INVALID_CONN_HANDLE != handle )
- {
- for(i = 0; i < BT_MAX_SCO_SUPPORT; i++)
- {
- if( pReplay->btcEventHist.btSyncUpdateEvent[i].fValid &&
- (handle == pReplay->btcEventHist.btSyncUpdateEvent[i].btSyncConnection.connectionHandle) )
- {
- pRet = &pReplay->btcEventHist.btSyncUpdateEvent[i];
- break;
- }
- }
- }
- if( NULL == pRet )
- {
- //Find a free slot
- for(i = 0; i < BT_MAX_SCO_SUPPORT; i++)
- {
- if( !pReplay->btcEventHist.btSyncUpdateEvent[i].fValid )
- {
- pRet = &pReplay->btcEventHist.btSyncUpdateEvent[i];
- break;
- }
- }
- }
- return (pRet);
-}
-
-/*
- Call must validate pAclEventHist
-*/
-static void btcReleaseAclEventHist( tpAniSirGlobal pMac, tpSmeBtAclEventHist pAclEventHist )
-{
- vos_mem_zero( pAclEventHist, sizeof(tSmeBtAclEventHist) );
-}
-
-/*
- Call must validate pSyncEventHist
-*/
-static void btcReleaseSyncEventHist( tpAniSirGlobal pMac, tpSmeBtSyncEventHist pSyncEventHist )
-{
- vos_mem_zero( pSyncEventHist, sizeof(tSmeBtSyncEventHist) );
-}
-
-/*To defer a ACL creation event
- We only support one ACL per BD address.
- If the last cached event another ACL create event, replace that event with the new event
- If a completion event with success status code, and the new ACL creation
- on same address, defer a new disconnect event(fake one), then cache this ACL creation event.
- Otherwise, save this create event.
-*/
-static VOS_STATUS btcDeferAclCreate( tpAniSirGlobal pMac, tpSmeBtEvent pEvent )
-{
- VOS_STATUS status = VOS_STATUS_SUCCESS;
- tpSmeBtAclEventHist pAclEventHist;
- tSmeBtAclConnectionParam *pAclEvent = NULL;
- do
- {
- //Find a match
- pAclEventHist = btcFindAclEventHist( pMac, pEvent->uEventParam.btAclConnection.bdAddr,
- BT_INVALID_CONN_HANDLE );
- if( NULL == pAclEventHist )
- {
- //No cached ACL event on this address
- //Find a free slot and save it
- pAclEventHist = btcFindAclEventHist( pMac, NULL, BT_INVALID_CONN_HANDLE );
- if( NULL != pAclEventHist )
- {
- vos_mem_copy(&pAclEventHist->btAclConnection[0], &pEvent->uEventParam.btAclConnection,
- sizeof(tSmeBtAclConnectionParam));
- pAclEventHist->btEventType[0] = BT_EVENT_CREATE_ACL_CONNECTION;
- pAclEventHist->bNextEventIdx = 1;
- }
- else
- {
- smsLog(pMac, LOGE, FL(" failed to find ACL event slot"));
- status = VOS_STATUS_E_RESOURCES;
- }
- //done
- break;
- }
- else
- {
- //There is history on this BD address
- if ((pAclEventHist->bNextEventIdx <= 0) ||
- (pAclEventHist->bNextEventIdx > BT_MAX_NUM_EVENT_ACL_DEFERRED))
- {
- VOS_ASSERT(0);
- status = VOS_STATUS_E_FAILURE;
- break;
- }
- pAclEvent = &pAclEventHist->btAclConnection[pAclEventHist->bNextEventIdx - 1];
- if(BT_EVENT_CREATE_ACL_CONNECTION == pAclEventHist->btEventType[pAclEventHist->bNextEventIdx - 1])
- {
- //The last cached event is creation, replace it with the new one
- if (pAclEvent)
- {
- vos_mem_copy(pAclEvent,
- &pEvent->uEventParam.btAclConnection,
- sizeof(tSmeBtAclConnectionParam));
- }
- //done
- break;
- }
- else if(BT_EVENT_ACL_CONNECTION_COMPLETE ==
- pAclEventHist->btEventType[pAclEventHist->bNextEventIdx - 1])
- {
- //The last cached event is completion, check the status.
- if(BT_CONN_STATUS_SUCCESS == pAclEvent->status)
- {
- tSmeBtEvent btEvent;
- //The last event we have is success completion event.
- //Should not get a creation event before creation.
- smsLog(pMac, LOGE, FL(" Missing disconnect event on handle %d"), pAclEvent->connectionHandle);
- //Fake a disconnect event
- btEvent.btEventType = BT_EVENT_DISCONNECTION_COMPLETE;
- btEvent.uEventParam.btDisconnect.connectionHandle = pAclEvent->connectionHandle;
- btcDeferDisconnEvent(pMac, &btEvent);
- }
- }
- //Need to save the new event
- if(pAclEventHist->bNextEventIdx < BT_MAX_NUM_EVENT_ACL_DEFERRED)
- {
- pAclEventHist->btEventType[pAclEventHist->bNextEventIdx] = BT_EVENT_CREATE_ACL_CONNECTION;
- vos_mem_copy(&pAclEventHist->btAclConnection[pAclEventHist->bNextEventIdx],
- &pEvent->uEventParam.btAclConnection,
- sizeof(tSmeBtAclConnectionParam));
- pAclEventHist->bNextEventIdx++;
- }
- else
- {
- smsLog(pMac, LOGE, FL(" ACL event overflow"));
- VOS_ASSERT(0);
- }
- }
- }while(0);
- return status;
-}
-
-/*Defer a ACL completion event
- If there is cached event on this BD address, check completion status.
- If status is fail and last cached event is creation, remove the creation event and drop
- this completion event. Otherwise, cache this completion event as the latest one.
-*/
-static VOS_STATUS btcDeferAclComplete( tpAniSirGlobal pMac, tpSmeBtEvent pEvent )
-{
- VOS_STATUS status = VOS_STATUS_SUCCESS;
- tpSmeBtAclEventHist pAclEventHist;
- do
- {
- //Find a match
- pAclEventHist = btcFindAclEventHist( pMac, pEvent->uEventParam.btAclConnection.bdAddr,
- BT_INVALID_CONN_HANDLE );
- if(pAclEventHist)
- {
- if (pAclEventHist->bNextEventIdx <= 0)
- {
- VOS_ASSERT(pAclEventHist->bNextEventIdx >0);
- return VOS_STATUS_E_EMPTY;
- }
- //Found one
- if(BT_CONN_STATUS_SUCCESS != pEvent->uEventParam.btAclConnection.status)
- {
- //If completion fails, and the last one is creation, remove the creation event
- if(BT_EVENT_CREATE_ACL_CONNECTION == pAclEventHist->btEventType[pAclEventHist->bNextEventIdx-1])
- {
- vos_mem_zero(&pAclEventHist->btAclConnection[pAclEventHist->bNextEventIdx-1],
- sizeof(tSmeBtAclConnectionParam));
- pAclEventHist->bNextEventIdx--;
- //Done with this event
- break;
- }
- else
- {
- smsLog(pMac, LOGE, FL(" ACL completion fail but last event(%d) not creation"),
- pAclEventHist->btEventType[pAclEventHist->bNextEventIdx-1]);
- }
- }
- }
- if( NULL == pAclEventHist )
- {
- pAclEventHist = btcFindAclEventHist( pMac, NULL, BT_INVALID_CONN_HANDLE );
- }
- if(pAclEventHist)
- {
- if(pAclEventHist->bNextEventIdx < BT_MAX_NUM_EVENT_ACL_DEFERRED)
- {
- //Save this event
- pAclEventHist->btEventType[pAclEventHist->bNextEventIdx] = BT_EVENT_ACL_CONNECTION_COMPLETE;
- vos_mem_copy(&pAclEventHist->btAclConnection[pAclEventHist->bNextEventIdx],
- &pEvent->uEventParam.btAclConnection,
- sizeof(tSmeBtAclConnectionParam));
- pAclEventHist->bNextEventIdx++;
- }
- else
- {
- smsLog(pMac, LOGE, FL(" ACL event overflow"));
- VOS_ASSERT(0);
- }
- }
- else
- {
- smsLog(pMac, LOGE, FL(" cannot find match for failed "
- "BT_EVENT_ACL_CONNECTION_COMPLETE of bdAddr "
- MAC_ADDRESS_STR),
- MAC_ADDR_ARRAY(pEvent->uEventParam.btAclConnection.bdAddr));
- status = VOS_STATUS_E_EMPTY;
- }
- }while(0);
- return (status);
-}
-
-/*To defer a SYNC creation event
- If the last cached event is another SYNC create event, replace
- that event with the new event.
- If there is a completion event with success status code, cache a new
- disconnect event(fake) first, then cache this SYNC creation event.
- Otherwise, cache this create event.
-*/
-static VOS_STATUS btcDeferSyncCreate( tpAniSirGlobal pMac, tpSmeBtEvent pEvent )
-{
- VOS_STATUS status = VOS_STATUS_SUCCESS;
- tpSmeBtSyncEventHist pSyncEventHist;
- tSmeBtSyncConnectionParam *pSyncEvent = NULL;
- do
- {
- //Find a match
- pSyncEventHist = btcFindSyncEventHist( pMac, pEvent->uEventParam.btSyncConnection.bdAddr,
- BT_INVALID_CONN_HANDLE );
- if( NULL == pSyncEventHist )
- {
- //No cached ACL event on this address
- //Find a free slot and save it
- pSyncEventHist = btcFindSyncEventHist( pMac, NULL, BT_INVALID_CONN_HANDLE );
- if( NULL != pSyncEventHist )
- {
- vos_mem_copy(&pSyncEventHist->btSyncConnection[0], &pEvent->uEventParam.btSyncConnection,
- sizeof(tSmeBtSyncConnectionParam));
- pSyncEventHist->btEventType[0] = BT_EVENT_CREATE_SYNC_CONNECTION;
- pSyncEventHist->bNextEventIdx = 1;
- }
- else
- {
- smsLog(pMac, LOGE, FL(" failed to find SYNC event slot"));
- status = VOS_STATUS_E_RESOURCES;
- }
- //done
- break;
- }
- else
- {
- //There is history on this BD address
- if ((pSyncEventHist->bNextEventIdx <= 0) ||
- (pSyncEventHist->bNextEventIdx > BT_MAX_NUM_EVENT_SCO_DEFERRED))
- {
- VOS_ASSERT(0);
- status = VOS_STATUS_E_FAILURE;
- return status;
- }
- pSyncEvent = &pSyncEventHist->btSyncConnection[pSyncEventHist->bNextEventIdx - 1];
- if(BT_EVENT_CREATE_SYNC_CONNECTION ==
- pSyncEventHist->btEventType[pSyncEventHist->bNextEventIdx - 1])
- {
- //The last cached event is creation, replace it with the new one
- if(pSyncEvent)
- {
- vos_mem_copy(pSyncEvent,
- &pEvent->uEventParam.btSyncConnection,
- sizeof(tSmeBtSyncConnectionParam));
- }
- //done
- break;
- }
- else if(BT_EVENT_SYNC_CONNECTION_COMPLETE ==
- pSyncEventHist->btEventType[pSyncEventHist->bNextEventIdx - 1])
- {
- //The last cached event is completion, check the status.
- if(BT_CONN_STATUS_SUCCESS == pSyncEvent->status)
- {
- tSmeBtEvent btEvent;
- //The last event we have is success completion event.
- //Should not get a creation event before creation.
- smsLog(pMac, LOGE, FL(" Missing disconnect event on handle %d"), pSyncEvent->connectionHandle);
- //Fake a disconnect event
- btEvent.btEventType = BT_EVENT_DISCONNECTION_COMPLETE;
- btEvent.uEventParam.btDisconnect.connectionHandle = pSyncEvent->connectionHandle;
- btcDeferDisconnEvent(pMac, &btEvent);
- }
- }
- //Need to save the new event
- if(pSyncEventHist->bNextEventIdx < BT_MAX_NUM_EVENT_SCO_DEFERRED)
- {
- pSyncEventHist->btEventType[pSyncEventHist->bNextEventIdx] = BT_EVENT_CREATE_SYNC_CONNECTION;
- vos_mem_copy(&pSyncEventHist->btSyncConnection[pSyncEventHist->bNextEventIdx],
- &pEvent->uEventParam.btSyncConnection,
- sizeof(tSmeBtSyncConnectionParam));
- pSyncEventHist->bNextEventIdx++;
- }
- else
- {
- smsLog(pMac, LOGE, FL(" SYNC event overflow"));
- }
- }
- }while(0);
- return status;
-}
-
-/*
- * Defer a SYNC completion event
- * If there is cached event on this BD address, check completion status.
- * If status is fail and last cached event is creation, remove the
- * creation event and drop this completion event.
- * Otherwise, cache this completion event as the latest one.
- */
-static VOS_STATUS btcDeferSyncComplete( tpAniSirGlobal pMac, tpSmeBtEvent pEvent )
-{
- VOS_STATUS status = VOS_STATUS_SUCCESS;
- tpSmeBtSyncEventHist pSyncEventHist;
- do
- {
- //Find a match
- pSyncEventHist = btcFindSyncEventHist( pMac, pEvent->uEventParam.btSyncConnection.bdAddr,
- BT_INVALID_CONN_HANDLE );
- if(pSyncEventHist)
- {
- if (pSyncEventHist->bNextEventIdx <= 0)
- {
- VOS_ASSERT(pSyncEventHist->bNextEventIdx >0);
- return VOS_STATUS_E_EMPTY;
- }
- //Found one
- if(BT_CONN_STATUS_SUCCESS != pEvent->uEventParam.btSyncConnection.status)
- {
- //If completion fails, and the last one is creation, remove the creation event
- if(BT_EVENT_CREATE_SYNC_CONNECTION == pSyncEventHist->btEventType[pSyncEventHist->bNextEventIdx-1])
- {
- vos_mem_zero(&pSyncEventHist->btSyncConnection[pSyncEventHist->bNextEventIdx-1],
- sizeof(tSmeBtSyncConnectionParam));
- pSyncEventHist->bNextEventIdx--;
- //Done with this event
- break;
- }
- else
- {
- smsLog(pMac, LOGE, FL(" SYNC completion fail but last event(%d) not creation"),
- pSyncEventHist->btEventType[pSyncEventHist->bNextEventIdx-1]);
- }
- }
- }
- if(NULL == pSyncEventHist)
- {
- //In case we don't defer the creation event
- pSyncEventHist = btcFindSyncEventHist( pMac, NULL, BT_INVALID_CONN_HANDLE );
- }
- if(pSyncEventHist)
- {
- if(pSyncEventHist->bNextEventIdx < BT_MAX_NUM_EVENT_ACL_DEFERRED)
- {
- //Save this event
- pSyncEventHist->btEventType[pSyncEventHist->bNextEventIdx] = BT_EVENT_SYNC_CONNECTION_COMPLETE;
- vos_mem_copy(&pSyncEventHist->btSyncConnection[pSyncEventHist->bNextEventIdx],
- &pEvent->uEventParam.btSyncConnection,
- sizeof(tSmeBtSyncConnectionParam));
- pSyncEventHist->bNextEventIdx++;
- }
- else
- {
- smsLog(pMac, LOGE, FL(" SYNC event overflow"));
- }
- }
- else
- {
- smsLog(pMac, LOGE, FL(" cannot find match for "
- "BT_EVENT_SYNC_CONNECTION_COMPLETE of bdAddr "
- MAC_ADDRESS_STR),
- MAC_ADDR_ARRAY(pEvent->uEventParam.btSyncConnection.bdAddr));
- status = VOS_STATUS_E_EMPTY;
- }
- }while(0);
- return (status);
-}
-
-//return VOS_STATUS_E_EXISTS if the event handle cannot be found
-//VOS_STATUS_SUCCESS if the event is processed
-//Other error status meaning it cannot continue due to other errors
-/*
- Defer a disconnect event for ACL
- Check if any history on this event handle.
- If both ACL_CREATION and ACL_COMPLETION is cached, remove both those events and drop
- this disconnect event.
- Otherwise save disconnect event in this ACL's bin.
- If not ACL match on this handle, not to do anything.
- Either way, remove any cached MODE_CHANGE event matches this disconnect event's handle.
-*/
-static VOS_STATUS btcDeferDisconnectEventForACL( tpAniSirGlobal pMac, tpSmeBtEvent pEvent )
-{
- VOS_STATUS status = VOS_STATUS_SUCCESS;
- tpSmeBtAclEventHist pAclEventHist;
- tpSmeBtAclModeChangeEventHist pModeChangeEventHist;
- v_BOOL_t fDone = VOS_FALSE;
- int i;
- pAclEventHist = btcFindAclEventHist( pMac, NULL,
- pEvent->uEventParam.btDisconnect.connectionHandle );
- if(pAclEventHist)
- {
- if( pAclEventHist->bNextEventIdx > BT_MAX_NUM_EVENT_ACL_DEFERRED)
- {
- smsLog(pMac, LOGE, FL(" ACL event history index:%d overflow, resetting to BT_MAX_NUM_EVENT_ACL_DEFERRED"), pAclEventHist->bNextEventIdx);
- pAclEventHist->bNextEventIdx = BT_MAX_NUM_EVENT_ACL_DEFERRED;
- }
- /* Looking back-words */
- for(i = pAclEventHist->bNextEventIdx - 1; i >= 0; i--)
- {
- if( BT_EVENT_ACL_CONNECTION_COMPLETE == pAclEventHist->btEventType[i] )
- {
- //make sure we can cancel the link
- if( (i > 0) && (BT_EVENT_CREATE_ACL_CONNECTION == pAclEventHist->btEventType[i - 1]) )
- {
- fDone = VOS_TRUE;
- if(i == 1)
- {
- //All events can be wiped off
- btcReleaseAclEventHist(pMac, pAclEventHist);
- break;
- }
- //we have both ACL creation and completion, wipe out all of them
- pAclEventHist->bNextEventIdx = (tANI_U8)(i - 1);
- vos_mem_zero(&pAclEventHist->btAclConnection[i-1], sizeof(tSmeBtAclConnectionParam));
- vos_mem_zero(&pAclEventHist->btAclConnection[i], sizeof(tSmeBtAclConnectionParam));
- break;
- }
- }
- }//for loop
- if(!fDone)
- {
- //Save this disconnect event
- if(pAclEventHist->bNextEventIdx < BT_MAX_NUM_EVENT_ACL_DEFERRED)
- {
- pAclEventHist->btEventType[pAclEventHist->bNextEventIdx] =
- BT_EVENT_DISCONNECTION_COMPLETE;
- pAclEventHist->btAclConnection[pAclEventHist->bNextEventIdx].connectionHandle =
- pEvent->uEventParam.btDisconnect.connectionHandle;
- pAclEventHist->bNextEventIdx++;
- }
- else
- {
- smsLog(pMac, LOGE, FL(" ACL event overflow"));
- status = VOS_STATUS_E_FAILURE;
- }
- }
- }
- else
- {
- status = VOS_STATUS_E_EXISTS;
- }
- //Wipe out the related mode change event if it is there
- pModeChangeEventHist = btcFindModeChangeEventHist( pMac,
- pEvent->uEventParam.btDisconnect.connectionHandle );
- if( pModeChangeEventHist && pModeChangeEventHist->fValid )
- {
- pModeChangeEventHist->fValid = VOS_FALSE;
- }
- return status;
-}
-
-/*
- * This function works the same as btcDeferDisconnectEventForACL except it
- * handles SYNC events return VOS_STATUS_E_EXISTS if the event handle cannot be
- * found VOS_STATUS_SUCCESS if the event is processed.
- * Other error status meaning it cannot continue due to other errors
- */
-/*
- Defer a disconnect event for SYNC
- Check if any SYNC history on this event handle.
- If yes and if both SYNC_CREATION and SYNC_COMPLETION is cached, remove both those events and drop
- this disconnect event.
- Otherwise save disconnect event in this SYNC's bin.
- If no match found, not to save this event here.
- Either way, remove any cached SYNC_UPDATE event matches this disconnect event's handle.
-*/
-static VOS_STATUS btcDeferDisconnectEventForSync( tpAniSirGlobal pMac, tpSmeBtEvent pEvent )
-{
- VOS_STATUS status = VOS_STATUS_SUCCESS;
- tpSmeBtSyncEventHist pSyncEventHist;
- tpSmeBtSyncUpdateHist pSyncUpdateHist;
- v_BOOL_t fDone = VOS_FALSE;
- int i;
- pSyncEventHist = btcFindSyncEventHist( pMac, NULL,
- pEvent->uEventParam.btDisconnect.connectionHandle );
- if(pSyncEventHist)
- {
- if( pSyncEventHist->bNextEventIdx > BT_MAX_NUM_EVENT_SCO_DEFERRED)
- {
- smsLog(pMac, LOGE, FL(" SYNC event history index:%d overflow, resetting to BT_MAX_NUM_EVENT_SCO_DEFERRED"), pSyncEventHist->bNextEventIdx);
- pSyncEventHist->bNextEventIdx = BT_MAX_NUM_EVENT_SCO_DEFERRED;
- }
- /* Looking back-words */
- for(i = pSyncEventHist->bNextEventIdx - 1; i >= 0; i--)
- {
- //if a mode change event exists, drop it
- if( BT_EVENT_SYNC_CONNECTION_COMPLETE == pSyncEventHist->btEventType[i] )
- {
- //make sure we can cancel the link
- if( (i > 0) && (BT_EVENT_CREATE_SYNC_CONNECTION == pSyncEventHist->btEventType[i - 1]) )
- {
- fDone = VOS_TRUE;
- if(i == 1)
- {
- //All events can be wiped off
- btcReleaseSyncEventHist(pMac, pSyncEventHist);
- break;
- }
- //we have both ACL creation and completion, wipe out all of them
- pSyncEventHist->bNextEventIdx = (tANI_U8)(i - 1);
- vos_mem_zero(&pSyncEventHist->btSyncConnection[i-1], sizeof(tSmeBtSyncConnectionParam));
- vos_mem_zero(&pSyncEventHist->btSyncConnection[i], sizeof(tSmeBtSyncConnectionParam));
- break;
- }
- }
- }//for loop
- if(!fDone)
- {
- //Save this disconnect event
- if(pSyncEventHist->bNextEventIdx < BT_MAX_NUM_EVENT_SCO_DEFERRED)
- {
- pSyncEventHist->btEventType[pSyncEventHist->bNextEventIdx] =
- BT_EVENT_DISCONNECTION_COMPLETE;
- pSyncEventHist->btSyncConnection[pSyncEventHist->bNextEventIdx].connectionHandle =
- pEvent->uEventParam.btDisconnect.connectionHandle;
- pSyncEventHist->bNextEventIdx++;
- }
- else
- {
- smsLog(pMac, LOGE, FL(" SYNC event overflow"));
- status = VOS_STATUS_E_FAILURE;
- }
- }
- }
- else
- {
- status = VOS_STATUS_E_EXISTS;
- }
- //Wipe out the related mode change event if it is there
- pSyncUpdateHist = btcFindSyncUpdateEventHist( pMac,
- pEvent->uEventParam.btDisconnect.connectionHandle );
- if( pSyncUpdateHist && pSyncUpdateHist->fValid )
- {
- pSyncUpdateHist->fValid = VOS_FALSE;
- }
- return status;
-}
-
-/*
- Defer a disconnect event.
- Try to defer it as part of the ACL event first.
- If no match is found, try SYNC.
- If still no match found, defer it at DISCONNECT event bin.
-*/
-static VOS_STATUS btcDeferDisconnEvent( tpAniSirGlobal pMac, tpSmeBtEvent pEvent )
-{
- VOS_STATUS status = VOS_STATUS_SUCCESS;
- tpSmeBtDisconnectEventHist pDisconnEventHist;
- if( BT_INVALID_CONN_HANDLE == pEvent->uEventParam.btDisconnect.connectionHandle )
- {
- smsLog( pMac, LOGE, FL(" invalid handle") );
- return (VOS_STATUS_E_INVAL);
- }
- //Check ACL first
- status = btcDeferDisconnectEventForACL(pMac, pEvent);
- if(!VOS_IS_STATUS_SUCCESS(status))
- {
- status = btcDeferDisconnectEventForSync(pMac, pEvent);
- }
- if( !VOS_IS_STATUS_SUCCESS(status) )
- {
- //Save the disconnect event
- pDisconnEventHist = btcFindDisconnEventHist( pMac,
- pEvent->uEventParam.btDisconnect.connectionHandle );
- if( pDisconnEventHist )
- {
- pDisconnEventHist->fValid = VOS_TRUE;
- vos_mem_copy( &pDisconnEventHist->btDisconnect, &pEvent->uEventParam.btDisconnect,
- sizeof(tSmeBtDisconnectParam) );
- status = VOS_STATUS_SUCCESS;
- }
- else
- {
- smsLog( pMac, LOGE, FL(" cannot find match for BT_EVENT_DISCONNECTION_COMPLETE of handle (%d)"),
- pEvent->uEventParam.btDisconnect.connectionHandle);
- status = VOS_STATUS_E_EMPTY;
- }
- }
- return (status);
-}
-
-/*
- btcDeferEvent save the event for possible replay when chip can be accessed
- This function is called only when in IMPS/Standby state
-*/
-static VOS_STATUS btcDeferEvent( tpAniSirGlobal pMac, tpSmeBtEvent pEvent )
-{
- VOS_STATUS status = VOS_STATUS_SUCCESS;
- tpSmeBtSyncUpdateHist pSyncUpdateHist;
- tpSmeBtAclModeChangeEventHist pModeChangeEventHist;
- tSmeBtcEventReplay *pReplay = &pMac->btc.btcEventReplay;
- switch(pEvent->btEventType)
- {
- case BT_EVENT_DEVICE_SWITCHED_ON:
- //Clear all events first
- vos_mem_zero( &pReplay->btcEventHist, sizeof(tSmeBtcEventHist) );
- pReplay->fBTSwitchOn = VOS_TRUE;
- pReplay->fBTSwitchOff = VOS_FALSE;
- break;
- case BT_EVENT_DEVICE_SWITCHED_OFF:
- //Clear all events first
- vos_mem_zero( &pReplay->btcEventHist, sizeof(tSmeBtcEventHist) );
- pReplay->fBTSwitchOff = VOS_TRUE;
- pReplay->fBTSwitchOn = VOS_FALSE;
- break;
- case BT_EVENT_INQUIRY_STARTED:
- pReplay->btcEventHist.nInquiryEvent++;
- break;
- case BT_EVENT_INQUIRY_STOPPED:
- pReplay->btcEventHist.nInquiryEvent--;
- break;
- case BT_EVENT_PAGE_STARTED:
- pReplay->btcEventHist.nPageEvent++;
- break;
- case BT_EVENT_PAGE_STOPPED:
- pReplay->btcEventHist.nPageEvent--;
- break;
- case BT_EVENT_CREATE_ACL_CONNECTION:
- status = btcDeferAclCreate(pMac, pEvent);
- break;
- case BT_EVENT_ACL_CONNECTION_COMPLETE:
- status = btcDeferAclComplete( pMac, pEvent );
- break;
- case BT_EVENT_CREATE_SYNC_CONNECTION:
- status = btcDeferSyncCreate(pMac, pEvent);
- break;
- case BT_EVENT_SYNC_CONNECTION_COMPLETE:
- status = btcDeferSyncComplete( pMac, pEvent );
- break;
- case BT_EVENT_SYNC_CONNECTION_UPDATED:
- if( BT_INVALID_CONN_HANDLE == pEvent->uEventParam.btDisconnect.connectionHandle )
- {
- smsLog( pMac, LOGE, FL(" invalid handle") );
- status = VOS_STATUS_E_INVAL;
- break;
- }
- //Find a match on handle. If not found, get a free slot.
- pSyncUpdateHist = btcFindSyncUpdateEventHist( pMac,
- pEvent->uEventParam.btSyncConnection.connectionHandle );
- if(pSyncUpdateHist)
- {
- pSyncUpdateHist->fValid = VOS_TRUE;
- vos_mem_copy(&pSyncUpdateHist->btSyncConnection, &pEvent->uEventParam.btSyncConnection,
- sizeof(tSmeBtSyncConnectionParam));
- }
- else
- {
- smsLog( pMac, LOGE, FL(" cannot find match for BT_EVENT_SYNC_CONNECTION_UPDATED of handle (%d)"),
- pEvent->uEventParam.btSyncConnection.connectionHandle );
- status = VOS_STATUS_E_EMPTY;
- }
- break;
- case BT_EVENT_DISCONNECTION_COMPLETE:
- status = btcDeferDisconnEvent( pMac, pEvent );
- break;
- case BT_EVENT_MODE_CHANGED:
- if( BT_INVALID_CONN_HANDLE == pEvent->uEventParam.btDisconnect.connectionHandle )
- {
- smsLog( pMac, LOGE, FL(" invalid handle") );
- status = VOS_STATUS_E_INVAL;
- break;
- }
- //Find a match on handle, If not found, return a free slot
- pModeChangeEventHist = btcFindModeChangeEventHist( pMac,
- pEvent->uEventParam.btAclModeChange.connectionHandle );
- if(pModeChangeEventHist)
- {
- pModeChangeEventHist->fValid = VOS_TRUE;
- vos_mem_copy( &pModeChangeEventHist->btAclModeChange,
- &pEvent->uEventParam.btAclModeChange, sizeof(tSmeBtAclModeChangeParam) );
- }
- else
- {
- smsLog( pMac, LOGE, FL(" cannot find match for BT_EVENT_MODE_CHANGED of handle (%d)"),
- pEvent->uEventParam.btAclModeChange.connectionHandle);
- status = VOS_STATUS_E_EMPTY;
- }
- break;
- case BT_EVENT_A2DP_STREAM_START:
- pReplay->btcEventHist.fA2DPStarted = VOS_TRUE;
- pReplay->btcEventHist.fA2DPStopped = VOS_FALSE;
- break;
- case BT_EVENT_A2DP_STREAM_STOP:
- pReplay->btcEventHist.fA2DPStopped = VOS_TRUE;
- pReplay->btcEventHist.fA2DPStarted = VOS_FALSE;
- break;
- default:
- smsLog( pMac, LOGE, FL(" event (%d) is not deferred"), pEvent->btEventType );
- status = VOS_STATUS_E_NOSUPPORT;
- break;
- }
- return (status);
-}
-
-/*
- Replay all cached events in the following order
- 1. If BT_SWITCH_OFF event, send it.
- 2. Send INQUIRY event (START or STOP),if available
- 3. Send PAGE event (START or STOP), if available
- 4. Send DISCONNECT events, these DISCONNECT events are not tied to
- any ACL/SYNC event that we have cached
- 5. Send ACL events (possible events, CREATION, COMPLETION, DISCONNECT)
- 6. Send MODE_CHANGE events, if available
- 7. Send A2DP event(START or STOP), if available
- 8. Send SYNC events (possible events, CREATION, COMPLETION, DISCONNECT)
- 9. Send SYNC_UPDATE events, if available
-*/
-static void btcReplayEvents( tpAniSirGlobal pMac )
-{
- int i, j;
- tSmeBtEvent btEvent;
- tpSmeBtAclEventHist pAclHist;
- tpSmeBtSyncEventHist pSyncHist;
- tSmeBtcEventReplay *pReplay = &pMac->btc.btcEventReplay;
- //Always turn on HB monitor first.
- //It is independent of BT events even though BT event causes this
- if( pReplay->fRestoreHBMonitor )
- {
- pReplay->fRestoreHBMonitor = VOS_FALSE;
- //Only do it when needed
- if( !pMac->btc.btcHBActive )
- {
- ccmCfgSetInt(pMac, WNI_CFG_HEART_BEAT_THRESHOLD, pMac->btc.btcHBCount, NULL, eANI_BOOLEAN_FALSE);
- pMac->btc.btcHBActive = VOS_TRUE;
- }
- }
- if( pMac->btc.fReplayBTEvents )
- {
- /*Set the flag to false here so btcSignalBTEvent won't defer any further.
- This works because SME has it global lock*/
- pMac->btc.fReplayBTEvents = VOS_FALSE;
- if( pReplay->fBTSwitchOff )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_DEVICE_SWITCHED_OFF;
- btcSendBTEvent( pMac, &btEvent );
- pReplay->fBTSwitchOff = VOS_FALSE;
- }
- else if( pReplay->fBTSwitchOn )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_DEVICE_SWITCHED_ON;
- btcSendBTEvent( pMac, &btEvent );
- pReplay->fBTSwitchOn = VOS_FALSE;
- }
- //Do inquire first
- if( pReplay->btcEventHist.nInquiryEvent > 0 )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_INQUIRY_STARTED;
- i = pReplay->btcEventHist.nInquiryEvent;
- while(i--)
- {
- btcSendBTEvent( pMac, &btEvent );
- }
- }
- else if( pReplay->btcEventHist.nInquiryEvent < 0 )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_INQUIRY_STOPPED;
- i = pReplay->btcEventHist.nInquiryEvent;
- while(i++)
- {
- btcSendBTEvent( pMac, &btEvent );
- }
- }
- //Page
- if( pReplay->btcEventHist.nPageEvent > 0 )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_PAGE_STARTED;
- i = pReplay->btcEventHist.nPageEvent;
- while(i--)
- {
- btcSendBTEvent( pMac, &btEvent );
- }
- }
- else if( pReplay->btcEventHist.nPageEvent < 0 )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_PAGE_STOPPED;
- i = pReplay->btcEventHist.nPageEvent;
- while(i++)
- {
- btcSendBTEvent( pMac, &btEvent );
- }
- }
- //Replay non-completion disconnect events first
- //Disconnect
- for( i = 0; i < BT_MAX_DISCONN_SUPPORT; i++ )
- {
- if( pReplay->btcEventHist.btDisconnectEvent[i].fValid )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_DISCONNECTION_COMPLETE;
- vos_mem_copy( &btEvent.uEventParam.btDisconnect,
- &pReplay->btcEventHist.btDisconnectEvent[i].btDisconnect, sizeof(tSmeBtDisconnectParam) );
- btcSendBTEvent( pMac, &btEvent );
- }
- }
- //ACL
- for( i = 0; i < BT_MAX_ACL_SUPPORT; i++ )
- {
- if( pReplay->btcEventHist.btAclConnectionEvent[i].bNextEventIdx )
- {
- pAclHist = &pReplay->btcEventHist.btAclConnectionEvent[i];
- //Replay all ACL events for this BD address/handle
- for(j = 0; j < pAclHist->bNextEventIdx; j++)
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = pAclHist->btEventType[j];
- if(BT_EVENT_DISCONNECTION_COMPLETE != btEvent.btEventType)
- {
- //It must be CREATE or CONNECTION_COMPLETE
- vos_mem_copy( &btEvent.uEventParam.btAclConnection,
- &pAclHist->btAclConnection[j], sizeof(tSmeBtAclConnectionParam) );
- }
- else
- {
- btEvent.uEventParam.btDisconnect.connectionHandle = pAclHist->btAclConnection[j].connectionHandle;
- }
- btcSendBTEvent( pMac, &btEvent );
- }
- }
- }
- //Mode change
- for( i = 0; i < BT_MAX_ACL_SUPPORT; i++ )
- {
- if( pReplay->btcEventHist.btAclModeChangeEvent[i].fValid )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_MODE_CHANGED;
- vos_mem_copy( &btEvent.uEventParam.btAclModeChange,
- &pReplay->btcEventHist.btAclModeChangeEvent[i].btAclModeChange, sizeof(tSmeBtAclModeChangeParam) );
- btcSendBTEvent( pMac, &btEvent );
- }
- }
- //A2DP
- if( pReplay->btcEventHist.fA2DPStarted )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_A2DP_STREAM_START;
- btcSendBTEvent( pMac, &btEvent );
- }
- else if( pReplay->btcEventHist.fA2DPStopped )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_A2DP_STREAM_STOP;
- btcSendBTEvent( pMac, &btEvent );
- }
- //SCO
- for( i = 0; i < BT_MAX_SCO_SUPPORT; i++ )
- {
- if( pReplay->btcEventHist.btSyncConnectionEvent[i].bNextEventIdx )
- {
- pSyncHist = &pReplay->btcEventHist.btSyncConnectionEvent[i];
- //Replay all SYNC events for this BD address/handle
- for(j = 0; j < pSyncHist->bNextEventIdx; j++)
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = pSyncHist->btEventType[j];
- if(BT_EVENT_DISCONNECTION_COMPLETE != btEvent.btEventType)
- {
- //Must be CREATION or CONNECTION_COMPLETE
- vos_mem_copy( &btEvent.uEventParam.btSyncConnection,
- &pSyncHist->btSyncConnection[j], sizeof(tSmeBtSyncConnectionParam) );
- }
- else
- {
- btEvent.uEventParam.btDisconnect.connectionHandle = pSyncHist->btSyncConnection[j].connectionHandle;
- }
- btcSendBTEvent( pMac, &btEvent );
- }
- }
- }
- //SYNC update
- for( i = 0; i < BT_MAX_SCO_SUPPORT; i++ )
- {
- if( pReplay->btcEventHist.btSyncUpdateEvent[i].fValid )
- {
- vos_mem_zero( &btEvent, sizeof(tSmeBtEvent) );
- btEvent.btEventType = BT_EVENT_SYNC_CONNECTION_UPDATED;
- vos_mem_copy( &btEvent.uEventParam.btSyncConnection,
- &pReplay->btcEventHist.btSyncUpdateEvent[i].btSyncConnection,
- sizeof(tSmeBtSyncConnectionParam) );
- btcSendBTEvent( pMac, &btEvent );
- }
- }
- //Clear all events
- vos_mem_zero( &pReplay->btcEventHist, sizeof(tSmeBtcEventHist) );
- }
-}
-
-static void btcPowerStateCB( v_PVOID_t pContext, tPmcState pmcState )
-{
- tpAniSirGlobal pMac = PMAC_STRUCT(pContext);
- if( FULL_POWER == pmcState )
- {
- btcReplayEvents( pMac );
- }
-}
-
-static void btcPowerOffloadStateCB(v_PVOID_t pContext, tANI_U32 sessionId,
- tPmcState pmcState )
-{
- tpAniSirGlobal pMac = PMAC_STRUCT(pContext);
- if(FULL_POWER == pmcState)
- {
- btcReplayEvents(pMac);
- }
-}
-
-
-/* ---------------------------------------------------------------------------
- \fn btcLogEvent
- \brief API to log the the current Bluetooth event
- \param hHal - The handle returned by macOpen.
- \param pSmeBtcConfig - Pointer to a caller allocated object of type
- tSmeBtEvent. Caller owns the memory and is responsible
- for freeing it.
- \return None
- ---------------------------------------------------------------------------*/
-static void btcLogEvent (tHalHandle hHal, tpSmeBtEvent pBtEvent)
-{
- v_U8_t bdAddrRev[6];
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: "
- "Bluetooth Event %d received", __func__, pBtEvent->btEventType);
- switch(pBtEvent->btEventType)
- {
- case BT_EVENT_CREATE_SYNC_CONNECTION:
- case BT_EVENT_SYNC_CONNECTION_COMPLETE:
- case BT_EVENT_SYNC_CONNECTION_UPDATED:
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "SCO Connection: "
- "connectionHandle = %d status = %d linkType %d "
- "scoInterval %d scoWindow %d retransmisisonWindow = %d ",
- pBtEvent->uEventParam.btSyncConnection.connectionHandle,
- pBtEvent->uEventParam.btSyncConnection.status,
- pBtEvent->uEventParam.btSyncConnection.linkType,
- pBtEvent->uEventParam.btSyncConnection.scoInterval,
- pBtEvent->uEventParam.btSyncConnection.scoWindow,
- pBtEvent->uEventParam.btSyncConnection.retransmisisonWindow);
-
- bdAddrRev[0] = pBtEvent->uEventParam.btSyncConnection.bdAddr[5];
- bdAddrRev[1] = pBtEvent->uEventParam.btSyncConnection.bdAddr[4];
- bdAddrRev[2] = pBtEvent->uEventParam.btSyncConnection.bdAddr[3];
- bdAddrRev[3] = pBtEvent->uEventParam.btSyncConnection.bdAddr[2];
- bdAddrRev[4] = pBtEvent->uEventParam.btSyncConnection.bdAddr[1];
- bdAddrRev[5] = pBtEvent->uEventParam.btSyncConnection.bdAddr[0];
-
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "BD ADDR = "
- MAC_ADDRESS_STR, MAC_ADDR_ARRAY(bdAddrRev));
- break;
- case BT_EVENT_CREATE_ACL_CONNECTION:
- case BT_EVENT_ACL_CONNECTION_COMPLETE:
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "ACL Connection: "
- "connectionHandle = %d status = %d ",
- pBtEvent->uEventParam.btAclConnection.connectionHandle,
- pBtEvent->uEventParam.btAclConnection.status);
-
- bdAddrRev[0] = pBtEvent->uEventParam.btAclConnection.bdAddr[5];
- bdAddrRev[1] = pBtEvent->uEventParam.btAclConnection.bdAddr[4];
- bdAddrRev[2] = pBtEvent->uEventParam.btAclConnection.bdAddr[3];
- bdAddrRev[3] = pBtEvent->uEventParam.btAclConnection.bdAddr[2];
- bdAddrRev[4] = pBtEvent->uEventParam.btAclConnection.bdAddr[1];
- bdAddrRev[5] = pBtEvent->uEventParam.btAclConnection.bdAddr[0];
-
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "BD ADDR = "
- MAC_ADDRESS_STR, MAC_ADDR_ARRAY(bdAddrRev));
- break;
- case BT_EVENT_MODE_CHANGED:
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "ACL Mode change : "
- "connectionHandle %d mode %d ",
- pBtEvent->uEventParam.btAclModeChange.connectionHandle,
- pBtEvent->uEventParam.btAclModeChange.mode);
- break;
- case BT_EVENT_DISCONNECTION_COMPLETE:
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "Disconnect Event : "
- "connectionHandle %d ", pBtEvent->uEventParam.btAclModeChange.connectionHandle);
- break;
- default:
- break;
- }
- }
-
-/*
- Caller can check whether BTC's current event allows UAPSD. This doesn't affect
- BMPS.
- return: VOS_TRUE -- BTC is ready for UAPSD
- VOS_FALSE -- certain BT event is active, cannot enter UAPSD
-*/
-v_BOOL_t btcIsReadyForUapsd( tHalHandle hHal )
-{
- tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
- return( pMac->btc.btcUapsdOk );
-}
-
-/*
- Base on the BT event, this function sets the flag on whether to allow UAPSD
- At this time, we are only interested in SCO and A2DP.
- A2DP tracking is through BT_EVENT_A2DP_STREAM_START and BT_EVENT_A2DP_STREAM_STOP
- SCO is through BT_EVENT_SYNC_CONNECTION_COMPLETE and BT_EVENT_DISCONNECTION_COMPLETE
- BT_EVENT_DEVICE_SWITCHED_OFF overwrites them all
-*/
-void btcUapsdCheck( tpAniSirGlobal pMac, tpSmeBtEvent pBtEvent )
-{
- v_U8_t i;
- v_BOOL_t fLastUapsdState = pMac->btc.btcUapsdOk, fMoreSCO = VOS_FALSE;
- switch( pBtEvent->btEventType )
- {
- case BT_EVENT_DISCONNECTION_COMPLETE:
- if( (VOS_FALSE == pMac->btc.btcUapsdOk) &&
- BT_INVALID_CONN_HANDLE != pBtEvent->uEventParam.btDisconnect.connectionHandle )
- {
- //Check whether all SCO connections are gone
- for(i=0; i < BT_MAX_SCO_SUPPORT; i++)
- {
- if( (BT_INVALID_CONN_HANDLE != pMac->btc.btcScoHandles[i]) &&
- (pMac->btc.btcScoHandles[i] != pBtEvent->uEventParam.btDisconnect.connectionHandle) )
- {
- //We still have outstanding SCO connection
- fMoreSCO = VOS_TRUE;
- }
- else if( pMac->btc.btcScoHandles[i] == pBtEvent->uEventParam.btDisconnect.connectionHandle )
- {
- pMac->btc.btcScoHandles[i] = BT_INVALID_CONN_HANDLE;
- }
- }
- if( !fMoreSCO && !pMac->btc.fA2DPUp )
- {
- //All SCO is disconnected
- smsLog( pMac, LOGE, "BT event (DISCONNECTION) happens, UAPSD-allowed flag (%d) change to TRUE",
- pMac->btc.btcUapsdOk );
- pMac->btc.btcUapsdOk = VOS_TRUE;
- }
- }
- break;
- case BT_EVENT_DEVICE_SWITCHED_OFF:
- smsLog( pMac, LOGE, "BT event (DEVICE_OFF) happens, UAPSD-allowed flag (%d) change to TRUE",
- pMac->btc.btcUapsdOk );
- //Clean up SCO
- for(i=0; i < BT_MAX_SCO_SUPPORT; i++)
- {
- pMac->btc.btcScoHandles[i] = BT_INVALID_CONN_HANDLE;
- }
- pMac->btc.fA2DPUp = VOS_FALSE;
- pMac->btc.btcUapsdOk = VOS_TRUE;
- break;
- case BT_EVENT_A2DP_STREAM_STOP:
- smsLog( pMac, LOGE, "BT event (A2DP_STREAM_STOP) happens, UAPSD-allowed flag (%d)",
- pMac->btc.btcUapsdOk );
- pMac->btc.fA2DPUp = VOS_FALSE;
- //Check whether SCO is on
- for(i=0; i < BT_MAX_SCO_SUPPORT; i++)
- {
- if(pMac->btc.btcScoHandles[i] != BT_INVALID_CONN_HANDLE)
- {
- break;
- }
- }
- if( BT_MAX_SCO_SUPPORT == i )
- {
- pMac->btc.fA2DPTrafStop = VOS_TRUE;
- smsLog( pMac, LOGE, "BT_EVENT_A2DP_STREAM_STOP: UAPSD-allowed flag is now %d",
- pMac->btc.btcUapsdOk );
- }
- break;
-
- case BT_EVENT_MODE_CHANGED:
- smsLog( pMac, LOGE, "BT event (BT_EVENT_MODE_CHANGED) happens, Mode (%d) UAPSD-allowed flag (%d)",
- pBtEvent->uEventParam.btAclModeChange.mode, pMac->btc.btcUapsdOk );
- if(pBtEvent->uEventParam.btAclModeChange.mode == BT_ACL_SNIFF)
- {
- //Check whether SCO is on
- for(i=0; i < BT_MAX_SCO_SUPPORT; i++)
- {
- if(pMac->btc.btcScoHandles[i] != BT_INVALID_CONN_HANDLE)
- {
- break;
- }
- }
- if( BT_MAX_SCO_SUPPORT == i )
- {
- if(VOS_TRUE == pMac->btc.fA2DPTrafStop)
- {
- pMac->btc.btcUapsdOk = VOS_TRUE;
- pMac->btc.fA2DPTrafStop = VOS_FALSE;
- }
- smsLog( pMac, LOGE, "BT_EVENT_MODE_CHANGED with Mode:%d UAPSD-allowed flag is now %d",
- pBtEvent->uEventParam.btAclModeChange.mode,pMac->btc.btcUapsdOk );
- }
- }
- break;
- case BT_EVENT_CREATE_SYNC_CONNECTION:
- {
- pMac->btc.btcUapsdOk = VOS_FALSE;
- smsLog( pMac, LOGE, "BT_EVENT_CREATE_SYNC_CONNECTION (%d) happens, UAPSD-allowed flag (%d) change to FALSE",
- pBtEvent->btEventType, pMac->btc.btcUapsdOk );
- }
- break;
- case BT_EVENT_SYNC_CONNECTION_COMPLETE:
- //Make sure it is a success
- if( BT_CONN_STATUS_FAIL != pBtEvent->uEventParam.btSyncConnection.status )
- {
- /* Save the handle for later use */
- for( i = 0; i < BT_MAX_SCO_SUPPORT; i++)
- {
- VOS_ASSERT(BT_INVALID_CONN_HANDLE != pBtEvent->uEventParam.btSyncConnection.connectionHandle);
- if( (BT_INVALID_CONN_HANDLE == pMac->btc.btcScoHandles[i]) &&
- (BT_INVALID_CONN_HANDLE != pBtEvent->uEventParam.btSyncConnection.connectionHandle))
- {
- pMac->btc.btcScoHandles[i] = pBtEvent->uEventParam.btSyncConnection.connectionHandle;
- break;
- }
- }
-
- if( i >= BT_MAX_SCO_SUPPORT )
- {
- smsLog(pMac, LOGE, FL("Too many SCO, ignore this one"));
- }
- }
- else
- {
- //Check whether SCO is on
- for(i=0; i < BT_MAX_SCO_SUPPORT; i++)
- {
- if(pMac->btc.btcScoHandles[i] != BT_INVALID_CONN_HANDLE)
- {
- break;
- }
- }
- /*If No Other Sco/A2DP is ON reenable UAPSD*/
- if( (BT_MAX_SCO_SUPPORT == i) && !pMac->btc.fA2DPUp)
- {
- pMac->btc.btcUapsdOk = VOS_TRUE;
- }
- smsLog(pMac, LOGE, FL("TSYNC complete failed"));
- }
- break;
- case BT_EVENT_A2DP_STREAM_START:
- smsLog( pMac, LOGE, "BT_EVENT_A2DP_STREAM_START (%d) happens, UAPSD-allowed flag (%d) change to FALSE",
- pBtEvent->btEventType, pMac->btc.btcUapsdOk );
- pMac->btc.fA2DPTrafStop = VOS_FALSE;
- pMac->btc.btcUapsdOk = VOS_FALSE;
- pMac->btc.fA2DPUp = VOS_TRUE;
- break;
- default:
- //No change for these events
- smsLog( pMac, LOGE, "BT event (%d) happens, UAPSD-allowed flag (%d) no change",
- pBtEvent->btEventType, pMac->btc.btcUapsdOk );
- break;
- }
- if(fLastUapsdState != pMac->btc.btcUapsdOk)
- {
- sme_QosTriggerUapsdChange( pMac );
- }
-}
-
-/* ---------------------------------------------------------------------------
- \fn btcHandleCoexInd
- \brief API to handle Coex indication from WDI
- \param pMac - The handle returned by macOpen.
- \return eHalStatus
- eHAL_STATUS_FAILURE success
- eHAL_STATUS_SUCCESS failure
- ---------------------------------------------------------------------------*/
-eHalStatus btcHandleCoexInd(tHalHandle hHal, void* pMsg)
-{
- tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
- eHalStatus status = eHAL_STATUS_SUCCESS;
- tSirSmeCoexInd *pSmeCoexInd = (tSirSmeCoexInd *)pMsg;
-
- if (NULL == pMsg)
- {
- smsLog(pMac, LOGE, "in %s msg ptr is NULL", __func__);
- status = eHAL_STATUS_FAILURE;
- }
- else
- {
- // DEBUG
- smsLog(pMac, LOG1, "Coex indication in %s(), type %d",
- __func__, pSmeCoexInd->coexIndType);
-
- // suspend heartbeat monitoring
- if (pSmeCoexInd->coexIndType == SIR_COEX_IND_TYPE_DISABLE_HB_MONITOR)
- {
- // set heartbeat threshold CFG to zero
- ccmCfgSetInt(pMac, WNI_CFG_HEART_BEAT_THRESHOLD, 0, NULL, eANI_BOOLEAN_FALSE);
- pMac->btc.btcHBActive = VOS_FALSE;
- }
-
- // resume heartbeat monitoring
- else if (pSmeCoexInd->coexIndType == SIR_COEX_IND_TYPE_ENABLE_HB_MONITOR)
- {
- if (!pMac->btc.btcHBActive)
- {
- ccmCfgSetInt(pMac, WNI_CFG_HEART_BEAT_THRESHOLD, pMac->btc.btcHBCount, NULL, eANI_BOOLEAN_FALSE);
- pMac->btc.btcHBActive = VOS_TRUE;
- }
- }
- else if (pSmeCoexInd->coexIndType == SIR_COEX_IND_TYPE_SCAN_COMPROMISED)
- {
- pMac->btc.btcScanCompromise = VOS_TRUE;
- smsLog(pMac, LOGW, "Coex indication in %s(), type - SIR_COEX_IND_TYPE_SCAN_COMPROMISED",
- __func__);
- }
- else if (pSmeCoexInd->coexIndType == SIR_COEX_IND_TYPE_SCAN_NOT_COMPROMISED)
- {
- pMac->btc.btcScanCompromise = VOS_FALSE;
- smsLog(pMac, LOGW, "Coex indication in %s(), type - SIR_COEX_IND_TYPE_SCAN_NOT_COMPROMISED",
- __func__);
- }
- else if (pSmeCoexInd->coexIndType == SIR_COEX_IND_TYPE_DISABLE_AGGREGATION_IN_2p4)
- {
- if (pMac->roam.configParam.disableAggWithBtc)
- {
- ccmCfgSetInt(pMac, WNI_CFG_DEL_ALL_RX_BA_SESSIONS_2_4_G_BTC, 1,
- NULL, eANI_BOOLEAN_FALSE);
- pMac->btc.btcBssfordisableaggr[0] = pSmeCoexInd->coexIndData[0] & 0xFF;
- pMac->btc.btcBssfordisableaggr[1] = pSmeCoexInd->coexIndData[0] >> 8;
- pMac->btc.btcBssfordisableaggr[2] = pSmeCoexInd->coexIndData[1] & 0xFF;
- pMac->btc.btcBssfordisableaggr[3] = pSmeCoexInd->coexIndData[1] >> 8;
- pMac->btc.btcBssfordisableaggr[4] = pSmeCoexInd->coexIndData[2] & 0xFF;
- pMac->btc.btcBssfordisableaggr[5] = pSmeCoexInd->coexIndData[2] >> 8;
- smsLog(pMac, LOGW, "Coex indication in %s(), "
- "type - SIR_COEX_IND_TYPE_DISABLE_AGGREGATION_IN_2p4 "
- "for BSSID "MAC_ADDRESS_STR,__func__,
- MAC_ADDR_ARRAY(pMac->btc.btcBssfordisableaggr));
- }
- }
- else if (pSmeCoexInd->coexIndType == SIR_COEX_IND_TYPE_ENABLE_AGGREGATION_IN_2p4)
- {
- if (pMac->roam.configParam.disableAggWithBtc)
- {
- ccmCfgSetInt(pMac, WNI_CFG_DEL_ALL_RX_BA_SESSIONS_2_4_G_BTC, 0,
- NULL, eANI_BOOLEAN_FALSE);
- smsLog(pMac, LOGW,
- "Coex indication in %s(), type - SIR_COEX_IND_TYPE_ENABLE_AGGREGATION_IN_2p4",
- __func__);
- }
- }
- // unknown indication type
- else
- {
- smsLog(pMac, LOGE, "unknown Coex indication type in %s()", __func__);
- }
- }
-
- return(status);
-}
-
-#ifdef FEATURE_WLAN_DIAG_SUPPORT
-/* ---------------------------------------------------------------------------
- \fn btcDiagEventLog
- \brief API to log the the current Bluetooth event
- \param hHal - The handle returned by macOpen.
- \param pSmeBtcConfig - Pointer to a caller allocated object of type
- tSmeBtEvent. Caller owns the memory and is responsible
- for freeing it.
- \return None
- ---------------------------------------------------------------------------*/
-static void btcDiagEventLog (tHalHandle hHal, tpSmeBtEvent pBtEvent)
-{
- //vos_event_wlan_btc_type *log_ptr = NULL;
- WLAN_VOS_DIAG_EVENT_DEF(btDiagEvent, vos_event_wlan_btc_type);
- {
- btDiagEvent.eventId = pBtEvent->btEventType;
- switch(pBtEvent->btEventType)
- {
- case BT_EVENT_CREATE_SYNC_CONNECTION:
- case BT_EVENT_SYNC_CONNECTION_COMPLETE:
- case BT_EVENT_SYNC_CONNECTION_UPDATED:
- btDiagEvent.connHandle = pBtEvent->uEventParam.btSyncConnection.connectionHandle;
- btDiagEvent.connStatus = pBtEvent->uEventParam.btSyncConnection.status;
- btDiagEvent.linkType = pBtEvent->uEventParam.btSyncConnection.linkType;
- btDiagEvent.scoInterval = pBtEvent->uEventParam.btSyncConnection.scoInterval;
- btDiagEvent.scoWindow = pBtEvent->uEventParam.btSyncConnection.scoWindow;
- btDiagEvent.retransWindow = pBtEvent->uEventParam.btSyncConnection.retransmisisonWindow;
- vos_mem_copy(btDiagEvent.btAddr, pBtEvent->uEventParam.btSyncConnection.bdAddr,
- sizeof(btDiagEvent.btAddr));
- break;
- case BT_EVENT_CREATE_ACL_CONNECTION:
- case BT_EVENT_ACL_CONNECTION_COMPLETE:
- btDiagEvent.connHandle = pBtEvent->uEventParam.btAclConnection.connectionHandle;
- btDiagEvent.connStatus = pBtEvent->uEventParam.btAclConnection.status;
- vos_mem_copy(btDiagEvent.btAddr, pBtEvent->uEventParam.btAclConnection.bdAddr,
- sizeof(btDiagEvent.btAddr));
- break;
- case BT_EVENT_MODE_CHANGED:
- btDiagEvent.connHandle = pBtEvent->uEventParam.btAclModeChange.connectionHandle;
- btDiagEvent.mode = pBtEvent->uEventParam.btAclModeChange.mode;
- break;
- case BT_EVENT_DISCONNECTION_COMPLETE:
- btDiagEvent.connHandle = pBtEvent->uEventParam.btAclModeChange.connectionHandle;
- break;
- default:
- break;
- }
- }
- WLAN_VOS_DIAG_EVENT_REPORT(&btDiagEvent, EVENT_WLAN_BTC);
-}
-#endif /* FEATURE_WLAN_DIAG_SUPPORT */
-#endif /* WLAN_MDM_CODE_REDUCTION_OPT*/
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrApiRoam.c b/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrApiRoam.c
index 06aeebd..fffc6b7 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrApiRoam.c
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrApiRoam.c
@@ -1101,6 +1101,18 @@
csrReleaseCommandRemoveKey( pMac, pCommand );
break;
+ case eSmeCommandNdpInitiatorRequest:
+ csr_release_ndp_initiator_req(pMac, pCommand);
+ break;
+
+ case eSmeCommandNdpResponderRequest:
+ csr_release_ndp_responder_req(pMac, pCommand);
+ break;
+
+ case eSmeCommandNdpDataEndInitiatorRequest:
+ csr_release_ndp_data_end_req(pMac, pCommand);
+ break;
+
default:
smsLog( pMac, LOGW, " CSR abort standard command %d", pCommand->command );
csrReleaseCommand( pMac, pCommand );
@@ -1200,8 +1212,6 @@
pMac->roam.configParam.nActiveMinChnTime = CSR_ACTIVE_MIN_CHANNEL_TIME;
pMac->roam.configParam.nPassiveMaxChnTime = CSR_PASSIVE_MAX_CHANNEL_TIME;
pMac->roam.configParam.nPassiveMinChnTime = CSR_PASSIVE_MIN_CHANNEL_TIME;
- pMac->roam.configParam.nActiveMaxChnTimeBtc = CSR_ACTIVE_MAX_CHANNEL_TIME_BTC;
- pMac->roam.configParam.nActiveMinChnTimeBtc = CSR_ACTIVE_MIN_CHANNEL_TIME_BTC;
pMac->roam.configParam.disableAggWithBtc = eANI_BOOLEAN_TRUE;
#ifdef WLAN_AP_STA_CONCURRENCY
pMac->roam.configParam.nActiveMaxChnTimeConc = CSR_ACTIVE_MAX_CHANNEL_TIME_CONC;
@@ -1806,14 +1816,6 @@
cfgSetInt(pMac, WNI_CFG_PASSIVE_MINIMUM_CHANNEL_TIME,
pParam->nPassiveMinChnTime);
}
- if (pParam->nActiveMaxChnTimeBtc)
- {
- pMac->roam.configParam.nActiveMaxChnTimeBtc = pParam->nActiveMaxChnTimeBtc;
- }
- if (pParam->nActiveMinChnTimeBtc)
- {
- pMac->roam.configParam.nActiveMinChnTimeBtc = pParam->nActiveMinChnTimeBtc;
- }
#ifdef WLAN_AP_STA_CONCURRENCY
if (pParam->nActiveMaxChnTimeConc)
{
@@ -2029,6 +2031,8 @@
pMac->roam.configParam.ignore_peer_ht_opmode =
pParam->ignore_peer_ht_opmode;
pMac->roam.configParam.obssEnabled = pParam->obssEnabled;
+ pMac->roam.configParam.vendor_vht_for_24ghz_sap =
+ pParam->vendor_vht_for_24ghz_sap;
pMac->roam.configParam.conc_custom_rule1 =
pParam->conc_custom_rule1;
pMac->roam.configParam.conc_custom_rule2 =
@@ -2097,8 +2101,6 @@
pParam->nActiveMinChnTime = pMac->roam.configParam.nActiveMinChnTime;
pParam->nPassiveMaxChnTime = pMac->roam.configParam.nPassiveMaxChnTime;
pParam->nPassiveMinChnTime = pMac->roam.configParam.nPassiveMinChnTime;
- pParam->nActiveMaxChnTimeBtc = pMac->roam.configParam.nActiveMaxChnTimeBtc;
- pParam->nActiveMinChnTimeBtc = pMac->roam.configParam.nActiveMinChnTimeBtc;
pParam->disableAggWithBtc = pMac->roam.configParam.disableAggWithBtc;
#ifdef WLAN_AP_STA_CONCURRENCY
pParam->nActiveMaxChnTimeConc = pMac->roam.configParam.nActiveMaxChnTimeConc;
@@ -2225,6 +2227,9 @@
pParam->obssEnabled = pMac->roam.configParam.obssEnabled;
+ pParam->vendor_vht_for_24ghz_sap =
+ pMac->roam.configParam.vendor_vht_for_24ghz_sap;
+
pParam->conc_custom_rule1 =
pMac->roam.configParam.conc_custom_rule1;
pParam->conc_custom_rule2 =
@@ -6204,6 +6209,8 @@
else
pSession->connectState = eCSR_ASSOC_STATE_TYPE_WDS_DISCONNECTED;
+ roamInfo.staId = (uint8_t)pSmeStartBssRsp->staId;
+
if (CSR_IS_NDI(pProfile)) {
csrRoamStateChange(pMac, eCSR_ROAMING_STATE_JOINED, sessionId);
pSirBssDesc = &pSmeStartBssRsp->bssDescription;
@@ -6335,14 +6342,8 @@
if (CSR_IS_NDI(pProfile)) {
csr_roam_update_ndp_return_params(pMac, Result,
&roamStatus, &roamResult, &roamInfo);
- csr_roam_fill_roaminfo_ndp(pMac, &roamInfo, roamResult,
- pSmeStartBssRsp->statusCode,
- 0, 0);
}
- //Only tell upper layer is we start the BSS because Vista doesn't like multiple connection
- //indications. If we don't start the BSS ourself, handler of eSIR_SME_JOINED_NEW_BSS will
- //trigger the connection start indication in Vista
roamInfo.statusCode = pSession->joinFailStatusCode.statusCode;
roamInfo.reasonCode = pSession->joinFailStatusCode.reasonCode;
//We start the IBSS (didn't find any matched IBSS out there)
@@ -6428,9 +6429,6 @@
if (CSR_IS_NDI(pProfile)) {
csr_roam_update_ndp_return_params(pMac, Result,
&roamStatus, &roamResult, &roamInfo);
- csr_roam_fill_roaminfo_ndp(pMac, &roamInfo, roamResult,
- (pSmeStartBssRsp) ? pSmeStartBssRsp->statusCode :
- eHAL_STATUS_FAILURE, 0, 0);
}
if(Context)
@@ -13628,20 +13626,8 @@
smsLog(pMac, LOGE, FL("can not find any valid channel"));
*pBuf++ = 0; //tSirSupChnl->numChnl
}
- //Check whether it is ok to enter UAPSD
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- if( btcIsReadyForUapsd(pMac) )
-#endif /* WLAN_MDM_CODE_REDUCTION_OPT*/
- {
- *pBuf++ = uapsdMask;
- }
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- else
- {
- smsLog(pMac, LOGE, FL(" BTC doesn't allow UAPSD for uapsd_mask(0x%X)"), uapsdMask);
- *pBuf++ = 0;
- }
-#endif /* WLAN_MDM_CODE_REDUCTION_OPT*/
+
+ *pBuf++ = uapsdMask;
// move the entire BssDescription into the join request.
vos_mem_copy(pBuf, pBssDescription,
@@ -14269,6 +14255,14 @@
txBFCsnValue = MIN(txBFCsnValue,
pIes->VHTCaps.numSoundingDim);
}
+ else if (IS_BSS_VHT_CAPABLE(pIes->vendor2_ie.VHTCaps) &&
+ pMac->roam.configParam.txBFEnable) {
+ txBFCsnValue = (tANI_U8)pMac->roam.configParam.txBFCsnValue;
+ if (pIes->vendor2_ie.VHTCaps.numSoundingDim)
+ txBFCsnValue = MIN(txBFCsnValue,
+ pIes->vendor2_ie.VHTCaps.numSoundingDim);
+ }
+
*pBuf = txBFCsnValue;
pBuf++;
@@ -15314,6 +15308,7 @@
*pBuf++ = (tANI_U8)pMac->roam.configParam.obssEnabled;
*pBuf++ = (tANI_U8)pParam->sap_dot11mc;
+ *pBuf++ = (tANI_U8)pMac->roam.configParam.vendor_vht_for_24ghz_sap;
msgLen = (tANI_U16)(sizeof(tANI_U32 ) + (pBuf - wTmpBuf)); //msg_header + msg
pMsg->length = pal_cpu_to_be16(msgLen);
@@ -19146,7 +19141,7 @@
tANI_U8 sessionId)
{
tCsrNeighborRoamBSSInfo handoffNode = {{0}};
- tANI_U32 timer_diff = 0;
+ uint64_t timer_diff = 0;
tANI_U32 timeStamp[2];
tpSirBssDescription pBssDescription = NULL;
@@ -19155,10 +19150,11 @@
return eHAL_STATUS_FAILURE;
}
pBssDescription = handoffNode.pBssDescription;
- // Get the time diff in milli seconds
- timer_diff = vos_timer_get_system_time() - pBssDescription->scanSysTimeMsec;
- // Convert msec to micro sec timer
- timer_diff = (tANI_U32)(timer_diff * SYSTEM_TIME_MSEC_TO_USEC);
+ // Get the time diff in nano seconds
+ timer_diff = (vos_get_monotonic_boottime_ns() -
+ pBssDescription->scansystimensec);
+ // Convert nano to micro sec timer
+ timer_diff = (timer_diff / SYSTEM_TIME_NSEC_TO_USEC);
timeStamp[0] = pBssDescription->timeStamp[0];
timeStamp[1] = pBssDescription->timeStamp[1];
UpdateCCKMTSF(&(timeStamp[0]), &(timeStamp[1]), &timer_diff);
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrApiScan.c b/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrApiScan.c
index c057b22..853dff2 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrApiScan.c
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrApiScan.c
@@ -282,8 +282,6 @@
pScanRequest->maxChnTime = pMac->roam.configParam.nPassiveMaxChnTimeConc;
pScanRequest->minChnTime = pMac->roam.configParam.nPassiveMinChnTimeConc;
}
- pScanRequest->maxChnTimeBtc = pMac->roam.configParam.nActiveMaxChnTimeBtc;
- pScanRequest->minChnTimeBtc = pMac->roam.configParam.nActiveMinChnTimeBtc;
pScanRequest->restTime = pMac->roam.configParam.nRestTimeConc;
pScanRequest->min_rest_time = pMac->roam.configParam.min_rest_time_conc;
@@ -311,8 +309,6 @@
pScanRequest->maxChnTime = pMac->roam.configParam.nPassiveMaxChnTime;
pScanRequest->minChnTime = pMac->roam.configParam.nPassiveMinChnTime;
}
- pScanRequest->maxChnTimeBtc = pMac->roam.configParam.nActiveMaxChnTimeBtc;
- pScanRequest->minChnTimeBtc = pMac->roam.configParam.nActiveMinChnTimeBtc;
#ifdef WLAN_AP_STA_CONCURRENCY
/* No rest time/Idle time if no sessions are connected. */
@@ -726,7 +722,6 @@
*/
status = csrRoamGetConnectState(pMac,sessionId,&ConnectState);
if (HAL_STATUS_SUCCESS(status) &&
- pMac->btc.fA2DPUp &&
(eCSR_ASSOC_STATE_TYPE_INFRA_ASSOCIATED != ConnectState) &&
(eCSR_ASSOC_STATE_TYPE_IBSS_CONNECTED != ConnectState))
{
@@ -738,8 +733,6 @@
pScanRequest->minChnTime);
}
- pScanRequest->maxChnTimeBtc = pMac->roam.configParam.nActiveMaxChnTimeBtc;
- pScanRequest->minChnTimeBtc = pMac->roam.configParam.nActiveMinChnTimeBtc;
//Need to make the following atomic
pScanCmd->u.scanCmd.scanID = pMac->scan.nextScanID++; //let it wrap around
@@ -804,9 +797,6 @@
p11dScanCmd->u.scanCmd.reason = eCsrScanIdleScan;
scanReq.maxChnTime = pMac->roam.configParam.nActiveMaxChnTime;
scanReq.minChnTime = pMac->roam.configParam.nActiveMinChnTime;
-
- scanReq.maxChnTimeBtc = pMac->roam.configParam.nActiveMaxChnTimeBtc;
- scanReq.minChnTimeBtc = pMac->roam.configParam.nActiveMinChnTimeBtc;
}
if (pMac->roam.configParam.nInitialDwellTime)
{
@@ -1057,8 +1047,6 @@
scanReq.requestType = reqType;
scanReq.maxChnTime = pMac->roam.configParam.nActiveMaxChnTime;
scanReq.minChnTime = pMac->roam.configParam.nActiveMinChnTime;
- scanReq.maxChnTimeBtc = pMac->roam.configParam.nActiveMaxChnTimeBtc;
- scanReq.minChnTimeBtc = pMac->roam.configParam.nActiveMinChnTimeBtc;
//Scan with invalid sessionId.
//This results in SME using the first available session to scan.
status = csrScanRequest(pMac, CSR_SESSION_ID_INVALID, &scanReq,
@@ -1346,8 +1334,6 @@
pCommand->u.scanCmd.pContext = NULL;
pCommand->u.scanCmd.u.scanRequest.maxChnTime = pMac->roam.configParam.nActiveMaxChnTime;
pCommand->u.scanCmd.u.scanRequest.minChnTime = pMac->roam.configParam.nActiveMinChnTime;
- pCommand->u.scanCmd.u.scanRequest.maxChnTimeBtc = pMac->roam.configParam.nActiveMaxChnTimeBtc;
- pCommand->u.scanCmd.u.scanRequest.minChnTimeBtc = pMac->roam.configParam.nActiveMinChnTimeBtc;
pCommand->u.scanCmd.u.scanRequest.scanType = eSIR_ACTIVE_SCAN;
if(pSession->connectedProfile.SSID.length)
{
@@ -1523,8 +1509,6 @@
pCommand->u.scanCmd.pContext = NULL;
pCommand->u.scanCmd.u.scanRequest.maxChnTime = pMac->roam.configParam.nActiveMaxChnTime;
pCommand->u.scanCmd.u.scanRequest.minChnTime = pMac->roam.configParam.nActiveMinChnTime;
- pCommand->u.scanCmd.u.scanRequest.maxChnTimeBtc = pMac->roam.configParam.nActiveMaxChnTimeBtc;
- pCommand->u.scanCmd.u.scanRequest.minChnTimeBtc = pMac->roam.configParam.nActiveMinChnTimeBtc;
pCommand->u.scanCmd.u.scanRequest.scanType = eSIR_ACTIVE_SCAN;
if(pSession->pCurRoamProfile)
{
@@ -1637,8 +1621,6 @@
pCommand->u.scanCmd.pContext = NULL;
pCommand->u.scanCmd.u.scanRequest.maxChnTime = pMac->roam.configParam.nActiveMaxChnTime;
pCommand->u.scanCmd.u.scanRequest.minChnTime = pMac->roam.configParam.nActiveMinChnTime;
- pCommand->u.scanCmd.u.scanRequest.maxChnTimeBtc = pMac->roam.configParam.nActiveMaxChnTimeBtc;
- pCommand->u.scanCmd.u.scanRequest.minChnTimeBtc = pMac->roam.configParam.nActiveMinChnTimeBtc;
pCommand->u.scanCmd.u.scanRequest.scanType = eSIR_ACTIVE_SCAN;
vos_mem_copy(&pCommand->u.scanCmd.u.scanRequest.bssid, bAddr, sizeof(tCsrBssid));
//Put to the head of pending queue
@@ -5763,7 +5745,8 @@
msgLen = (tANI_U16)(sizeof( tSirSmeScanReq ) - sizeof( pMsg->channelList.channelNumber ) +
( sizeof( pMsg->channelList.channelNumber ) * pScanReq->ChannelInfo.numOfChannels )) +
- ( pScanReq->uIEFieldLen ) ;
+ ( pScanReq->uIEFieldLen ) +
+ pScanReq->num_vendor_oui * sizeof(struct vendor_oui);
pMsg = vos_mem_malloc(msgLen);
if ( NULL == pMsg )
@@ -5886,8 +5869,7 @@
pMsg->minChannelTime = pal_cpu_to_be32(minChnTime);
pMsg->maxChannelTime = pal_cpu_to_be32(maxChnTime);
- pMsg->minChannelTimeBtc = pMac->roam.configParam.nActiveMinChnTimeBtc;
- pMsg->maxChannelTimeBtc = pMac->roam.configParam.nActiveMaxChnTimeBtc;
+
//hidden SSID option
pMsg->hiddenSsid = pScanReqParam->hiddenSsid;
/* maximum rest time */
@@ -5928,6 +5910,36 @@
pMsg->backgroundScanMode = eSIR_ROAMING_SCAN;
}
+ pMsg->enable_scan_randomization =
+ pScanReq->enable_scan_randomization;
+ if (pMsg->enable_scan_randomization) {
+ vos_mem_copy(pMsg->mac_addr, pScanReq->mac_addr,
+ VOS_MAC_ADDR_SIZE);
+ vos_mem_copy(pMsg->mac_addr_mask, pScanReq->mac_addr_mask,
+ VOS_MAC_ADDR_SIZE);
+ }
+
+ pMsg->ie_whitelist = pScanReq->ie_whitelist;
+ if (pMsg->ie_whitelist)
+ vos_mem_copy(pMsg->probe_req_ie_bitmap,
+ pScanReq->probe_req_ie_bitmap,
+ PROBE_REQ_BITMAP_LEN * sizeof(uint32_t));
+ pMsg->num_vendor_oui = pScanReq->num_vendor_oui;
+ pMsg->oui_field_len = pScanReq->num_vendor_oui *
+ sizeof(struct vendor_oui);
+ pMsg->oui_field_offset = (tANI_U16)(sizeof( tSirSmeScanReq ) -
+ sizeof( pMsg->channelList.channelNumber ) +
+ (sizeof( pMsg->channelList.channelNumber ) *
+ pScanReq->ChannelInfo.numOfChannels )) +
+ pScanReq->uIEFieldLen;
+
+ if (pScanReq->num_vendor_oui != 0)
+ {
+ vos_mem_copy((tANI_U8 *)pMsg + pMsg->oui_field_offset,
+ (uint8_t*)(pScanReq->voui),
+ pMsg->oui_field_len);
+ }
+
}while(0);
smsLog(pMac, LOG1, FL("domainIdCurrent %s (%d) scanType %s (%d)"
"bssType %s (%d), requestType %s(%d)"
@@ -6407,6 +6419,7 @@
pDstReq->pIEField = NULL;
pDstReq->ChannelInfo.ChannelList = NULL;
pDstReq->SSIDs.SSIDList = NULL;
+ pDstReq->voui = NULL;
if(pSrcReq->uIEFieldLen == 0)
{
@@ -6595,6 +6608,35 @@
pDstReq->p2pSearch = pSrcReq->p2pSearch;
pDstReq->skipDfsChnlInP2pSearch = pSrcReq->skipDfsChnlInP2pSearch;
+ if (pSrcReq->num_vendor_oui == 0)
+ {
+ pDstReq->num_vendor_oui = 0;
+ pDstReq->voui = NULL;
+ }
+ else
+ {
+ pDstReq->voui = vos_mem_malloc(pSrcReq->num_vendor_oui *
+ sizeof(*pDstReq->voui));
+ if (NULL == pDstReq->voui)
+ status = eHAL_STATUS_FAILURE;
+ else
+ status = eHAL_STATUS_SUCCESS;
+
+ if (HAL_STATUS_SUCCESS(status))
+ {
+ pDstReq->num_vendor_oui = pSrcReq->num_vendor_oui;
+ vos_mem_copy(pDstReq->voui,
+ pSrcReq->voui,
+ pSrcReq->num_vendor_oui *
+ sizeof(*pDstReq->voui));
+ }
+ else
+ {
+ pDstReq->num_vendor_oui = 0;
+ smsLog(pMac, LOGE, FL("No memory for voui"));
+ break;
+ }
+ }
}
}while(0);
@@ -6629,6 +6671,13 @@
}
pReq->SSIDs.numOfSSIDs = 0;
+ if(pReq->voui)
+ {
+ vos_mem_free(pReq->voui);
+ pReq->voui = NULL;
+ }
+ pReq->num_vendor_oui = 0;
+
return eHAL_STATUS_SUCCESS;
}
@@ -7613,10 +7662,6 @@
pScanCmd->u.scanCmd.u.scanRequest.minChnTime =
pMac->roam.configParam.nActiveMinChnTime;
}
- pScanCmd->u.scanCmd.u.scanRequest.maxChnTimeBtc =
- pMac->roam.configParam.nActiveMaxChnTimeBtc;
- pScanCmd->u.scanCmd.u.scanRequest.minChnTimeBtc =
- pMac->roam.configParam.nActiveMinChnTimeBtc;
if(pProfile->BSSIDs.numOfBSSIDs == 1)
{
vos_mem_copy(pScanCmd->u.scanCmd.u.scanRequest.bssid,
@@ -8838,10 +8883,10 @@
#ifdef FEATURE_WLAN_ESE
// Update the TSF with the difference in system time
-void UpdateCCKMTSF(tANI_U32 *timeStamp0, tANI_U32 *timeStamp1, tANI_U32 *incr)
+void UpdateCCKMTSF(tANI_U32 *timeStamp0, tANI_U32 *timeStamp1, uint64_t *incr)
{
tANI_U64 timeStamp64 = ((tANI_U64)*timeStamp1 << 32) | (*timeStamp0);
- timeStamp64 = (tANI_U64)(timeStamp64 + (tANI_U64)*incr);
+ timeStamp64 = (tANI_U64)(timeStamp64 + (*incr));
*timeStamp0 = (tANI_U32)(timeStamp64 & 0xffffffff);
*timeStamp1 = (tANI_U32)((timeStamp64 >> 32) & 0xffffffff);
}
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrInsideApi.h b/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrInsideApi.h
index 1987e39..d0eea7d 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrInsideApi.h
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrInsideApi.h
@@ -73,6 +73,8 @@
#define CSR_MAX_BSS_SUPPORT 512
#define SYSTEM_TIME_MSEC_TO_USEC 1000
+#define SYSTEM_TIME_SEC_TO_MSEC 1000
+#define SYSTEM_TIME_NSEC_TO_USEC 1000
/* This number minus 1 means the number of times a channel is scanned
before a BSS is removed from cache scan result */
@@ -1036,7 +1038,7 @@
#endif
#if defined(FEATURE_WLAN_ESE)
-void UpdateCCKMTSF(tANI_U32 *timeStamp0, tANI_U32 *timeStamp1, tANI_U32 *incr);
+void UpdateCCKMTSF(tANI_U32 *timeStamp0, tANI_U32 *timeStamp1, uint64_t *incr);
#endif
eHalStatus csrGetDefaultCountryCodeFrmNv(tpAniSirGlobal pMac, tANI_U8 *pCountry);
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrLogDump.c b/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrLogDump.c
index 0a64afd..3bdfc0d 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrLogDump.c
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrLogDump.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -31,7 +31,6 @@
============================================================================*/
#include "aniGlobal.h"
#include "csrApi.h"
-#include "btcApi.h"
#include "logDump.h"
#include "smsDebug.h"
#include "smeInside.h"
@@ -55,50 +54,7 @@
}
return p;
}
-static char *dump_btcSetEvent( tpAniSirGlobal pMac, tANI_U32 arg1,
- tANI_U32 arg2, tANI_U32 arg3, tANI_U32 arg4, char *p )
-{
- tSmeBtEvent btEvent;
- if( arg1 < BT_EVENT_TYPE_MAX )
- {
- smsLog(pMac, LOGE, FL(" signal BT event (%d) handle (%d) 3rd param(%d)"), arg1, arg2, arg3);
- vos_mem_zero(&btEvent, sizeof(tSmeBtEvent));
- btEvent.btEventType = arg1;
- switch( arg1 )
- {
- case BT_EVENT_SYNC_CONNECTION_COMPLETE:
- case BT_EVENT_SYNC_CONNECTION_UPDATED:
- btEvent.uEventParam.btSyncConnection.connectionHandle = (v_U16_t)arg2;
- btEvent.uEventParam.btSyncConnection.status = (v_U8_t)arg3;
- break;
- case BT_EVENT_DISCONNECTION_COMPLETE:
- btEvent.uEventParam.btDisconnect.connectionHandle = (v_U16_t)arg2;
- break;
- case BT_EVENT_CREATE_ACL_CONNECTION:
- case BT_EVENT_ACL_CONNECTION_COMPLETE:
- btEvent.uEventParam.btAclConnection.connectionHandle = (v_U16_t)arg2;
- btEvent.uEventParam.btAclConnection.status = (v_U8_t)arg3;
- break;
- case BT_EVENT_MODE_CHANGED:
- btEvent.uEventParam.btAclModeChange.connectionHandle = (v_U16_t)arg2;
- break;
- default:
- break;
- }
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- if(HAL_STATUS_SUCCESS(sme_AcquireGlobalLock( &pMac->sme )))
- {
- btcSignalBTEvent(pMac, &btEvent);
- sme_ReleaseGlobalLock( &pMac->sme );
- }
-#endif
- }
- else
- {
- smsLog(pMac, LOGE, FL(" invalid event (%d)"), arg1);
- }
- return p;
-}
+
static char* dump_csrApConcScanParams( tpAniSirGlobal pMac, tANI_U32 arg1,
tANI_U32 arg2, tANI_U32 arg3, tANI_U32 arg4, char *p )
{
@@ -123,7 +79,6 @@
static tDumpFuncEntry csrMenuDumpTable[] = {
{0, "CSR (850-860)", NULL},
{851, "CSR: CSR testing connection to AniNet", dump_csr},
- {852, "BTC: Fake BT events (event, handle)", dump_btcSetEvent},
{853, "CSR: Split Scan related params", dump_csrApConcScanParams},
};
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrUtil.c b/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrUtil.c
index 4f168db..5b148e5 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrUtil.c
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/csr/csrUtil.c
@@ -636,6 +636,23 @@
return ( fRc );
}
+/**
+ * csr_is_ndi_started() - function to check if NDI is started
+ * @mac_ctx: handle to mac context
+ * @session_id: session identifier
+ *
+ * returns: true if NDI is started, false otherwise
+ */
+bool csr_is_ndi_started(tpAniSirGlobal mac_ctx, uint32_t session_id)
+{
+ tCsrRoamSession *session = CSR_GET_SESSION(mac_ctx, session_id);
+
+ if (!session)
+ return false;
+
+ return (eCSR_CONNECT_STATE_TYPE_NDI_STARTED == session->connectState);
+}
+
tANI_S8 csrGetInfraSessionId( tpAniSirGlobal pMac )
{
tANI_U8 i;
@@ -1677,9 +1694,9 @@
if (pIes->HTCaps.present) {
phyMode = eCSR_DOT11_MODE_11n;
#ifdef WLAN_FEATURE_11AC
- if (IS_BSS_VHT_CAPABLE(pIes->VHTCaps)) {
+ if (IS_BSS_VHT_CAPABLE(pIes->VHTCaps) ||
+ IS_BSS_VHT_CAPABLE(pIes->vendor2_ie.VHTCaps))
phyMode = eCSR_DOT11_MODE_11ac;
- }
#endif
}
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/nan/nan_datapath_api.c b/drivers/staging/qcacld-2.0/CORE/SME/src/nan/nan_datapath_api.c
index 0a183c6..e9eff93e 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/nan/nan_datapath_api.c
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/nan/nan_datapath_api.c
@@ -30,6 +30,46 @@
#include "sme_nan_datapath.h"
/**
+ * csr_free_ndp_initiator_req() - free resouces from sme command for ndp
+ * initiator request
+ * @cmd: sme command msg
+ *
+ * Return: None
+ */
+void csr_free_ndp_initiator_req(tSmeCmd *cmd)
+{
+ vos_mem_free(cmd->u.initiator_req.ndp_config.ndp_cfg);
+ cmd->u.initiator_req.ndp_config.ndp_cfg = NULL;
+ cmd->u.initiator_req.ndp_config.ndp_cfg_len = 0;
+ vos_mem_free(cmd->u.initiator_req.ndp_info.ndp_app_info);
+ cmd->u.initiator_req.ndp_info.ndp_app_info = NULL;
+ cmd->u.initiator_req.ndp_info.ndp_app_info_len = 0;
+ vos_mem_free(cmd->u.initiator_req.pmk.pmk);
+ cmd->u.initiator_req.pmk.pmk = NULL;
+ cmd->u.initiator_req.pmk.pmk_len = 0;
+}
+
+/**
+ * csr_free_ndp_responder_req() - free resouces from sme command for ndp
+ * responder request
+ * @cmd: sme command msg
+ *
+ * Return: None
+ */
+void csr_free_ndp_responder_req(tSmeCmd *cmd)
+{
+ vos_mem_free(cmd->u.responder_req.ndp_config.ndp_cfg);
+ cmd->u.responder_req.ndp_config.ndp_cfg = NULL;
+ cmd->u.responder_req.ndp_config.ndp_cfg_len = 0;
+ vos_mem_free(cmd->u.responder_req.ndp_info.ndp_app_info);
+ cmd->u.responder_req.ndp_info.ndp_app_info = NULL;
+ cmd->u.responder_req.ndp_info.ndp_app_info_len = 0;
+ vos_mem_free(cmd->u.responder_req.pmk.pmk);
+ cmd->u.responder_req.pmk.pmk = NULL;
+ cmd->u.responder_req.pmk.pmk_len = 0;
+}
+
+/**
* sme_ndp_initiator_req_handler() - ndp initiator req handler
* @hal: hal handle
* @req_params: request parameters
@@ -67,11 +107,13 @@
/* pointers copied as part of above operation are to be overwritten */
cmd->u.initiator_req.ndp_info.ndp_app_info = NULL;
cmd->u.initiator_req.ndp_config.ndp_cfg = NULL;
+ cmd->u.initiator_req.pmk.pmk = NULL;
if (req_params->ndp_info.ndp_app_info_len) {
cmd->u.initiator_req.ndp_info.ndp_app_info =
vos_mem_malloc(req_params->ndp_info.ndp_app_info_len);
if (NULL == cmd->u.initiator_req.ndp_info.ndp_app_info) {
+ csr_release_ndp_initiator_req(mac_ctx, cmd);
sme_ReleaseGlobalLock(&mac_ctx->sme);
return eHAL_STATUS_FAILED_ALLOC;
}
@@ -84,10 +126,8 @@
cmd->u.initiator_req.ndp_config.ndp_cfg =
vos_mem_malloc(req_params->ndp_config.ndp_cfg_len);
if (NULL == cmd->u.initiator_req.ndp_config.ndp_cfg) {
+ csr_release_ndp_initiator_req(mac_ctx, cmd);
sme_ReleaseGlobalLock(&mac_ctx->sme);
- vos_mem_free(
- cmd->u.initiator_req.ndp_info.ndp_app_info);
- cmd->u.initiator_req.ndp_info.ndp_app_info_len = 0;
return eHAL_STATUS_FAILED_ALLOC;
}
vos_mem_copy(cmd->u.initiator_req.ndp_config.ndp_cfg,
@@ -95,14 +135,23 @@
req_params->ndp_config.ndp_cfg_len);
}
+ if (req_params->pmk.pmk_len) {
+ cmd->u.initiator_req.pmk.pmk =
+ vos_mem_malloc(req_params->pmk.pmk_len);
+ if (NULL == cmd->u.initiator_req.pmk.pmk) {
+ csr_release_ndp_initiator_req(mac_ctx, cmd);
+ sme_ReleaseGlobalLock(&mac_ctx->sme);
+ return eHAL_STATUS_FAILED_ALLOC;
+ }
+ vos_mem_copy(cmd->u.initiator_req.pmk.pmk,
+ req_params->pmk.pmk, req_params->pmk.pmk_len);
+ }
+
status = csrQueueSmeCommand(mac_ctx, cmd, TRUE);
if (eHAL_STATUS_SUCCESS != status) {
smsLog(mac_ctx, LOGE, FL("SME enqueue failed, status:%d"),
status);
- vos_mem_free(cmd->u.initiator_req.ndp_info.ndp_app_info);
- vos_mem_free(cmd->u.initiator_req.ndp_config.ndp_cfg);
- cmd->u.initiator_req.ndp_info.ndp_app_info_len = 0;
- cmd->u.initiator_req.ndp_config.ndp_cfg_len = 0;
+ csr_release_ndp_initiator_req(mac_ctx, cmd);
}
sme_ReleaseGlobalLock(&mac_ctx->sme);
@@ -151,11 +200,13 @@
*/
cmd->u.responder_req.ndp_info.ndp_app_info = NULL;
cmd->u.responder_req.ndp_config.ndp_cfg = NULL;
+ cmd->u.responder_req.pmk.pmk = NULL;
if (req_params->ndp_info.ndp_app_info_len) {
cmd->u.responder_req.ndp_info.ndp_app_info =
vos_mem_malloc(req_params->ndp_info.ndp_app_info_len);
if (NULL == cmd->u.responder_req.ndp_info.ndp_app_info) {
+ csr_release_ndp_responder_req(mac_ctx, cmd);
sme_ReleaseGlobalLock(&mac_ctx->sme);
return eHAL_STATUS_FAILED_ALLOC;
}
@@ -168,10 +219,8 @@
cmd->u.responder_req.ndp_config.ndp_cfg =
vos_mem_malloc(req_params->ndp_config.ndp_cfg_len);
if (NULL == cmd->u.responder_req.ndp_config.ndp_cfg) {
+ csr_release_ndp_responder_req(mac_ctx, cmd);
sme_ReleaseGlobalLock(&mac_ctx->sme);
- vos_mem_free(
- cmd->u.responder_req.ndp_info.ndp_app_info);
- cmd->u.responder_req.ndp_info.ndp_app_info_len = 0;
return eHAL_STATUS_FAILED_ALLOC;
}
vos_mem_copy(cmd->u.responder_req.ndp_config.ndp_cfg,
@@ -179,14 +228,23 @@
req_params->ndp_config.ndp_cfg_len);
}
+ if (req_params->pmk.pmk_len) {
+ cmd->u.responder_req.pmk.pmk =
+ vos_mem_malloc(req_params->pmk.pmk_len);
+ if (NULL == cmd->u.responder_req.pmk.pmk) {
+ csr_release_ndp_responder_req(mac_ctx, cmd);
+ sme_ReleaseGlobalLock(&mac_ctx->sme);
+ return eHAL_STATUS_FAILED_ALLOC;
+ }
+ vos_mem_copy(cmd->u.responder_req.pmk.pmk,
+ req_params->pmk.pmk, req_params->pmk.pmk_len);
+ }
+
status = csrQueueSmeCommand(mac_ctx, cmd, TRUE);
if (eHAL_STATUS_SUCCESS != status) {
smsLog(mac_ctx, LOGE,
FL("SME enqueue failed, status:%d"), status);
- vos_mem_free(cmd->u.responder_req.ndp_info.ndp_app_info);
- vos_mem_free(cmd->u.responder_req.ndp_config.ndp_cfg);
- cmd->u.responder_req.ndp_info.ndp_app_info_len = 0;
- cmd->u.responder_req.ndp_config.ndp_cfg_len = 0;
+ csr_release_ndp_responder_req(mac_ctx, cmd);
}
sme_ReleaseGlobalLock(&mac_ctx->sme);
return status;
@@ -194,29 +252,60 @@
/**
* sme_ndp_end_req_handler() - ndp end request handler
- * @session_id: session id over which the ndp is being created
- * @req_params: request parameters
+ * @hal: hal handle
+ * @req: ndp end request parameters
*
* Return: VOS_STATUS_SUCCESS on success; error number otherwise
*/
-VOS_STATUS sme_ndp_end_req_handler(uint32_t session_id,
- struct ndp_end_req *req_params)
+VOS_STATUS sme_ndp_end_req_handler(tHalHandle hal, struct ndp_end_req *req)
{
- return VOS_STATUS_SUCCESS;
-}
+ tSmeCmd *cmd;
+ VOS_STATUS ret = VOS_STATUS_SUCCESS;
+ eHalStatus status = eHAL_STATUS_SUCCESS;
+ tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal);
+ if (NULL == req) {
+ smsLog(mac_ctx, LOGE, FL("Invalid ndp end req"));
+ return VOS_STATUS_E_INVAL;
+ }
-/**
- * sme_ndp_sched_req_handler() - ndp schedule request handler
- * @session_id: session id over which the ndp is being created
- * @req_params: request parameters
- *
- * Return: VOS_STATUS_SUCCESS on success; error number otherwise
- */
-VOS_STATUS sme_ndp_sched_req_handler(uint32_t session_id,
- struct ndp_schedule_update_req *req_params)
-{
- return VOS_STATUS_SUCCESS;
+ status = sme_AcquireGlobalLock(&mac_ctx->sme);
+ if (eHAL_STATUS_SUCCESS != status) {
+ smsLog(mac_ctx, LOGE,
+ FL("SME lock failed, status:%d"), status);
+ return VOS_STATUS_E_RESOURCES;
+ }
+ cmd = csrGetCommandBuffer(mac_ctx);
+ if (NULL == cmd) {
+ sme_ReleaseGlobalLock(&mac_ctx->sme);
+ return VOS_STATUS_E_RESOURCES;
+ }
+
+ cmd->command = eSmeCommandNdpDataEndInitiatorRequest;
+ cmd->u.data_end_req = vos_mem_malloc(sizeof(*req) +
+ (req->num_ndp_instances * sizeof(uint32)));
+ if (NULL == cmd->u.data_end_req) {
+ csrReleaseCommandRoam(mac_ctx, cmd);
+ sme_ReleaseGlobalLock(&mac_ctx->sme);
+ return VOS_STATUS_E_NOMEM;
+ }
+
+ vos_mem_copy(cmd->u.data_end_req, req, sizeof(*req));
+ cmd->u.data_end_req->ndp_ids =
+ (uint32_t *)((uint8_t *)&cmd->u.data_end_req[1]);
+ vos_mem_copy(cmd->u.data_end_req->ndp_ids, req->ndp_ids,
+ sizeof(uint32_t) * req->num_ndp_instances);
+
+ status = csrQueueSmeCommand(mac_ctx, cmd, true);
+ if (eHAL_STATUS_SUCCESS != status) {
+ smsLog(mac_ctx, LOGE, FL("SME enqueue failed, status:%d"),
+ status);
+ ret = VOS_STATUS_E_FAILURE;
+ csr_release_ndp_data_end_req(mac_ctx, cmd);
+ }
+
+ sme_ReleaseGlobalLock(&mac_ctx->sme);
+ return ret;
}
/**
@@ -260,36 +349,6 @@
}
/**
- * csr_roam_fill_roaminfo_ndp() - fill the ndi create struct inside roam info
- * @mac_ctx: Global MAC context
- * @roam_info: roam info to be updated with ndi create params
- * @roam_result: roam result to update
- * @status_code: status code to update
- * @reason_code: reason code to update
- * @transaction_id: transcation id to update
- *
- * Return: Nothing
- */
-void csr_roam_fill_roaminfo_ndp(tpAniSirGlobal mac_ctx,
- tCsrRoamInfo *roam_info,
- eCsrRoamResult roam_result,
- tSirResultCodes status_code,
- uint32_t reason_code,
- uint32_t transaction_id)
-{
- struct ndi_create_rsp *rsp_params;
-
- smsLog(mac_ctx, LOG1,
- FL("reason 0x%x, status 0x%x, transaction_id %d"),
- reason_code, status_code, transaction_id);
- rsp_params = (struct ndi_create_rsp *)
- &roam_info->ndp.ndi_create_params;
- rsp_params->reason = reason_code;
- rsp_params->status = status_code;
- rsp_params->transaction_id = transaction_id;
-}
-
-/**
* csr_roam_save_ndi_connected_info() - Save connected profile parameters
* @mac_ctx: Global MAC context
* @session_id: Session ID
@@ -362,19 +421,33 @@
switch (result) {
case eCsrStartBssSuccess:
- case eCsrStartBssFailure:
+ roam_info->ndp.ndi_create_params.reason = 0;
+ roam_info->ndp.ndi_create_params.sta_id = roam_info->staId;
+ roam_info->ndp.ndi_create_params.status =
+ NDP_RSP_STATUS_SUCCESS;
*roam_status = eCSR_ROAM_NDP_STATUS_UPDATE;
- *roam_result = eCSR_ROAM_RESULT_NDP_CREATE_RSP;
+ *roam_result = eCSR_ROAM_RESULT_NDI_CREATE_RSP;
+ break;
+ case eCsrStartBssFailure:
+ roam_info->ndp.ndi_create_params.status = NDP_RSP_STATUS_ERROR;
+ roam_info->ndp.ndi_create_params.reason =
+ NDP_NAN_DATA_IFACE_CREATE_FAILED;
+ *roam_status = eCSR_ROAM_NDP_STATUS_UPDATE;
+ *roam_result = eCSR_ROAM_RESULT_NDI_CREATE_RSP;
break;
case eCsrStopBssSuccess:
+ roam_info->ndp.ndi_delete_params.reason = 0;
+ roam_info->ndp.ndi_delete_params.status =
+ NDP_RSP_STATUS_SUCCESS;
*roam_status = eCSR_ROAM_NDP_STATUS_UPDATE;
- *roam_result = eCSR_ROAM_RESULT_NDP_DELETE_RSP;
- roam_info->ndp.ndi_delete_params.status = VOS_STATUS_SUCCESS;
+ *roam_result = eCSR_ROAM_RESULT_NDI_DELETE_RSP;
break;
case eCsrStopBssFailure:
+ roam_info->ndp.ndi_delete_params.status = NDP_RSP_STATUS_ERROR;
+ roam_info->ndp.ndi_delete_params.reason =
+ NDP_NAN_DATA_IFACE_DELETE_FAILED;
*roam_status = eCSR_ROAM_NDP_STATUS_UPDATE;
- *roam_result = eCSR_ROAM_RESULT_NDP_DELETE_RSP;
- roam_info->ndp.ndi_delete_params.status = VOS_STATUS_E_FAILURE;
+ *roam_result = eCSR_ROAM_RESULT_NDI_DELETE_RSP;
break;
default:
smsLog(mac_ctx, LOGE,
@@ -393,6 +466,7 @@
eHalStatus csr_process_ndp_initiator_request(tpAniSirGlobal mac_ctx,
tSmeCmd *cmd)
{
+ eHalStatus status;
struct sir_sme_ndp_initiator_req *lim_msg;
uint16_t msg_len;
uint8_t *self_mac_addr = NULL;
@@ -400,14 +474,17 @@
if (NULL == cmd) {
smsLog(mac_ctx, LOGE, FL("Invalid req_params"));
- return eHAL_STATUS_INVALID_PARAMETER;
+ status = eHAL_STATUS_INVALID_PARAMETER;
+ goto sme_initiator_req_failed;
}
req = &cmd->u.initiator_req;
msg_len = sizeof(*lim_msg);
lim_msg = vos_mem_malloc(msg_len);
- if (NULL == lim_msg)
- return eHAL_STATUS_FAILED_ALLOC;
+ if (NULL == lim_msg) {
+ status = eHAL_STATUS_FAILED_ALLOC;
+ goto sme_initiator_req_failed;
+ }
vos_mem_set(lim_msg, msg_len, 0);
lim_msg->msg_type =
@@ -425,7 +502,13 @@
smsLog(mac_ctx, LOG1, FL("selfMac = "MAC_ADDRESS_STR),
MAC_ADDR_ARRAY(self_mac_addr));
- return palSendMBMessage(mac_ctx->hHdd, lim_msg);
+ status = palSendMBMessage(mac_ctx->hHdd, lim_msg);
+
+sme_initiator_req_failed:
+ /* If fail, free up resources allocated in sme. */
+ if (!HAL_STATUS_SUCCESS(status))
+ csr_free_ndp_initiator_req(cmd);
+ return status;
}
/**
@@ -460,9 +543,9 @@
pal_cpu_to_be16((uint16_t)eWNI_SME_NDP_RESPONDER_REQ);
lim_msg->msg_len = pal_cpu_to_be16(msg_len);
/*
- * following is being copied from p_cmd->u.initiator_req,
+ * following is being copied from p_cmd->u.responder_req,
* no need to perform deep copy, as we are going to use memory
- * allocated at SME in p_cmd->u.initiator_req and pass it all the way
+ * allocated at SME in p_cmd->u.responder_req and pass it all the way
* to WMA.
*/
vos_mem_copy(&lim_msg->req, &cmd->u.responder_req,
@@ -477,17 +560,48 @@
status = palSendMBMessage(mac_ctx->hHdd, lim_msg);
free_config:
- if (!HAL_STATUS_SUCCESS(status)) {
- /*
- * If fail, free up the ndp_cfg and ndp_app_info
- * allocated in sme.
- */
- vos_mem_free(cmd->u.responder_req.ndp_info.ndp_app_info);
- vos_mem_free(cmd->u.responder_req.ndp_config.ndp_cfg);
- cmd->u.responder_req.ndp_info.ndp_app_info_len = 0;
- cmd->u.responder_req.ndp_config.ndp_cfg_len = 0;
- cmd->u.responder_req.ndp_config.ndp_cfg = NULL;
- cmd->u.responder_req.ndp_info.ndp_app_info = NULL;
+ /* If fail, free up the ndp_cfg and ndp_app_info allocated in sme. */
+ if (!HAL_STATUS_SUCCESS(status))
+ csr_free_ndp_responder_req(cmd);
+ return status;
+}
+
+/**
+ * csr_process_ndp_data_end_request() - process ndp data end request
+ * @mac_ctx: Global MAC context
+ * @cmd: sme command containing ndp initiator request
+ *
+ * Return: status of operation
+ */
+eHalStatus csr_process_ndp_data_end_request(tpAniSirGlobal mac_ctx,
+ tSmeCmd *cmd)
+{
+ eHalStatus status;
+ struct sir_sme_ndp_end_req *lim_msg;
+ uint16_t msg_len;
+
+ if (NULL == cmd) {
+ smsLog(mac_ctx, LOGE, FL("NULL sme cmd"));
+ return eHAL_STATUS_INVALID_PARAMETER;
+ }
+
+ msg_len = sizeof(*lim_msg);
+ lim_msg = vos_mem_malloc(msg_len);
+ if (NULL == lim_msg) {
+ smsLog(mac_ctx, LOGE, FL("Malloc failed"));
+ vos_mem_free(cmd->u.data_end_req);
+ cmd->u.data_end_req = NULL;
+ return eHAL_STATUS_FAILED_ALLOC;
+ }
+
+ lim_msg->msg_type = (uint16_t)eWNI_SME_NDP_END_REQ;
+ lim_msg->msg_len = msg_len;
+ lim_msg->req = cmd->u.data_end_req;
+
+ status = palSendMBMessage(mac_ctx->hHdd, lim_msg);
+ if (status != eHAL_STATUS_SUCCESS) {
+ vos_mem_free(cmd->u.data_end_req);
+ cmd->u.data_end_req = NULL;
}
return status;
}
@@ -514,6 +628,10 @@
eSmeCommandType cmd_to_rel = eSmeNoCommand;
bool send_to_user = true;
+ entry = csrLLPeekHead(&mac_ctx->sme.smeCmdActiveList, LL_ACCESS_LOCK);
+ if (entry != NULL)
+ cmd = GET_BASE_ADDR(entry, tSmeCmd, Link);
+
switch (msg->type) {
case eWNI_SME_NDP_CONFIRM_IND: {
result = eCSR_ROAM_RESULT_NDP_CONFIRM_IND;
@@ -573,6 +691,45 @@
release_active_cmd = true;
cmd_to_rel = eSmeCommandNdpResponderRequest;
break;
+ case eWNI_SME_NDP_END_RSP: {
+ if (true == msg->bodyval) {
+ /* rsp was locally generated, do not send to HDD */
+ send_to_user = false;
+ } else {
+ result = eCSR_ROAM_RESULT_NDP_END_RSP;
+ roam_info.ndp.ndp_end_rsp_params = msg->bodyptr;
+ /*
+ * NDP_END_IND is independent of session, but session_id
+ * is needed for csrRoamCallCallback(). Set it to 0
+ * which is a valid session.
+ */
+ session_id = 0;
+ }
+ release_active_cmd = true;
+ cmd_to_rel = eSmeCommandNdpDataEndInitiatorRequest;
+ break;
+ }
+ case eWNI_SME_NDP_END_IND:
+ result = eCSR_ROAM_RESULT_NDP_END_IND;
+ roam_info.ndp.ndp_end_ind_params = msg->bodyptr;
+ /*
+ * NDP_END_IND is independent of session, but session_id is
+ * needed for csrRoamCallCallback(). Set it to vdev_id of first
+ * entry which is a valid session. vdev_id is likely to be same
+ * for all.
+ */
+ session_id =
+ roam_info.ndp.ndp_end_ind_params->ndp_map[0].vdev_id;
+ break;
+ case eWNI_SME_NDP_PEER_DEPARTED_IND:
+ result = eCSR_ROAM_RESULT_NDP_PEER_DEPARTED_IND;
+ /* copy msg from msg body to roam info passed to callback */
+ vos_mem_copy(&roam_info.ndp.ndp_peer_ind_params,
+ msg->bodyptr,
+ sizeof(roam_info.ndp.ndp_peer_ind_params));
+ session_id =
+ ((struct sme_ndp_peer_ind *)msg->bodyptr)->session_id;
+ break;
default:
smsLog(mac_ctx, LOGE, FL("Unhandled NDP rsp"));
vos_mem_free(msg->bodyptr);
@@ -584,61 +741,46 @@
eCSR_ROAM_NDP_STATUS_UPDATE, result);
}
+ vos_mem_free(msg->bodyptr);
+ msg->bodyptr = NULL;
/* free ndp_cfg and ndp_app_info if required
* For some commands this info may be needed in HDD
* so free them after roam callback.
*/
switch (msg->type) {
case eWNI_SME_NDP_INITIATOR_RSP:
- entry = csrLLPeekHead(&mac_ctx->sme.smeCmdActiveList,
- LL_ACCESS_LOCK);
- if (entry != NULL) {
- cmd = GET_BASE_ADDR(entry, tSmeCmd, Link);
- if (eSmeCommandNdpInitiatorRequest == cmd->command) {
- vos_mem_free(
- cmd->u.initiator_req.
- ndp_config.ndp_cfg);
- vos_mem_free(
- cmd->u.initiator_req.
- ndp_info.ndp_app_info);
- }
+ if (cmd && eSmeCommandNdpInitiatorRequest == cmd->command) {
+ vos_mem_free(cmd->u.initiator_req.ndp_config.ndp_cfg);
+ vos_mem_free(
+ cmd->u.initiator_req.ndp_info.ndp_app_info);
}
break;
case eWNI_SME_NDP_RESPONDER_RSP:
- entry = csrLLPeekHead(&mac_ctx->sme.smeCmdActiveList,
- LL_ACCESS_LOCK);
- if (entry != NULL) {
- cmd = GET_BASE_ADDR(entry, tSmeCmd, Link);
- if (eSmeCommandNdpResponderRequest == cmd->command) {
- vos_mem_free(
- cmd->u.responder_req.
- ndp_config.ndp_cfg);
- vos_mem_free(
- cmd->u.responder_req.
- ndp_info.ndp_app_info);
- }
+ if (cmd && eSmeCommandNdpResponderRequest == cmd->command) {
+ vos_mem_free(cmd->u.responder_req.ndp_config.ndp_cfg);
+ vos_mem_free(
+ cmd->u.responder_req.ndp_info.ndp_app_info);
}
break;
case eWNI_SME_NDP_INDICATION:
+ vos_mem_free(roam_info.ndp.ndp_indication_params.scid.scid);
+ vos_mem_free(roam_info.ndp.ndp_indication_params.ndp_config.ndp_cfg);
vos_mem_free(
- roam_info.ndp.ndp_indication_params.ndp_config.ndp_cfg);
- vos_mem_free(
- roam_info.ndp.ndp_indication_params.
- ndp_info.ndp_app_info);
+ roam_info.ndp.ndp_indication_params.ndp_info.ndp_app_info);
+ break;
+ case eWNI_SME_NDP_END_RSP:
+ if (cmd &&
+ eSmeCommandNdpDataEndInitiatorRequest == cmd->command) {
+ vos_mem_free(cmd->u.data_end_req);
+ cmd->u.data_end_req = NULL;
+ }
+ break;
+ case eWNI_SME_NDP_END_IND:
break;
default:
break;
}
- vos_mem_free(msg->bodyptr);
- if (release_active_cmd == false)
- return;
-
- entry = csrLLPeekHead(&mac_ctx->sme.smeCmdActiveList, LL_ACCESS_LOCK);
- if (entry == NULL)
- return;
-
- cmd = GET_BASE_ADDR(entry, tSmeCmd, Link);
- if (cmd_to_rel == cmd->command) {
+ if (release_active_cmd && cmd && cmd_to_rel == cmd->command) {
/* Now put this cmd back on the avilable command list */
if (csrLLRemoveEntry(&mac_ctx->sme.smeCmdActiveList,
entry, LL_ACCESS_LOCK))
@@ -646,3 +788,47 @@
smeProcessPendingQueue(mac_ctx);
}
}
+
+/**
+ * csr_release_ndp_initiator_req() - free resouces from sme command for ndp
+ * and release the cmd
+ * initiator request
+ * @mac_ctx: Global MAC context
+ * @cmd: sme command msg
+ *
+ * Return: None
+ */
+void csr_release_ndp_initiator_req(tpAniSirGlobal mac_ctx, tSmeCmd *cmd)
+{
+ csr_free_ndp_initiator_req(cmd);
+ smeReleaseCommand(mac_ctx, cmd);
+}
+
+/**
+ * csr_release_ndp_responder_req() - free resouces from sme command for ndp
+ * responder request and release the command
+ * @mac_ctx: Global MAC context
+ * @cmd: sme command msg
+ *
+ * Return: None
+ */
+void csr_release_ndp_responder_req(tpAniSirGlobal mac_ctx, tSmeCmd *cmd)
+{
+ csr_free_ndp_responder_req(cmd);
+ smeReleaseCommand(mac_ctx, cmd);
+}
+
+/**
+ * csr_release_ndp_data_end_req() - free resouces from sme command for ndp
+ * data end request
+ * @mac_ctx: Global MAC context
+ * @cmd: sme command msg
+ *
+ * Return: None
+ */
+void csr_release_ndp_data_end_req(tpAniSirGlobal mac_ctx, tSmeCmd *cmd)
+{
+ vos_mem_free(cmd->u.data_end_req);
+ cmd->u.data_end_req = NULL;
+ smeReleaseCommand(mac_ctx, cmd);
+}
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/pmc/pmc.c b/drivers/staging/qcacld-2.0/CORE/SME/src/pmc/pmc.c
index 4b99b5d..ac04c8e 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/pmc/pmc.c
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/pmc/pmc.c
@@ -1193,77 +1193,36 @@
else
{
pMac->pmc.uapsdSessionRequired = TRUE;
- //Check BTC state
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- if( btcIsReadyForUapsd( pMac ) )
-#endif /* WLAN_MDM_CODE_REDUCTION_OPT*/
+
+ /* Put device in BMPS mode first. This step should NEVER fail.
+ That is why no need to buffer the UAPSD request*/
+ if(pmcEnterRequestBmpsState(hHal) != eHAL_STATUS_SUCCESS)
{
- /* Put device in BMPS mode first. This step should NEVER fail.
- That is why no need to buffer the UAPSD request*/
- if(pmcEnterRequestBmpsState(hHal) != eHAL_STATUS_SUCCESS)
- {
- pmcLog(pMac, LOGE, "PMC: Device in Full Power. Enter Request Bmps failed. "
- "UAPSD request will be dropped ");
- return eHAL_STATUS_FAILURE;
- }
+ pmcLog(pMac, LOGE, "PMC: Device in Full Power. Enter Request Bmps failed. "
+ "UAPSD request will be dropped ");
+ return eHAL_STATUS_FAILURE;
}
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- else
- {
- (void)pmcStartTrafficTimer(hHal, pMac->pmc.bmpsConfig.trafficMeasurePeriod);
- }
-#endif /* WLAN_MDM_CODE_REDUCTION_OPT*/
}
break;
case BMPS:
- //It is already in BMPS mode, check BTC state
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- if( btcIsReadyForUapsd(pMac) )
-#endif /* WLAN_MDM_CODE_REDUCTION_OPT*/
- {
/* Tell MAC to have device enter UAPSD mode. */
- if (pmcIssueCommand(hHal, 0, eSmeCommandEnterUapsd, NULL, 0, FALSE)
- != eHAL_STATUS_SUCCESS)
- {
- pmcLog(pMac, LOGE, "PMC: failure to send message "
- "eWNI_PMC_ENTER_BMPS_REQ");
- return eHAL_STATUS_FAILURE;
- }
- }
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- else
+ if (pmcIssueCommand(hHal, 0, eSmeCommandEnterUapsd, NULL, 0, FALSE)
+ != eHAL_STATUS_SUCCESS)
{
- //Not ready for UAPSD at this time, save it first and wake up the chip
- pmcLog(pMac, LOGE, " PMC state = %d",pMac->pmc.pmcState);
- pMac->pmc.uapsdSessionRequired = TRUE;
- /* While BTC traffic is going on, STA can be in BMPS
- * and need not go to Full Power */
- //fFullPower = VOS_TRUE;
+ pmcLog(pMac, LOGE, "PMC: failure to send message "
+ "eWNI_PMC_ENTER_BMPS_REQ");
+ return eHAL_STATUS_FAILURE;
}
-#endif /* WLAN_MDM_CODE_REDUCTION_OPT*/
break;
case REQUEST_START_UAPSD:
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- if( !btcIsReadyForUapsd(pMac) )
- {
- //BTC rejects UAPSD, bring it back to full power
- fFullPower = VOS_TRUE;
- }
-#endif
+
break;
case REQUEST_BMPS:
/* Buffer request for UAPSD mode. */
pMac->pmc.uapsdSessionRequired = TRUE;
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- if( !btcIsReadyForUapsd(pMac) )
- {
- //BTC rejects UAPSD, bring it back to full power
- fFullPower = VOS_TRUE;
- }
-#endif /* WLAN_MDM_CODE_REDUCTION_OPT*/
break;
default:
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/pmc/pmcApi.c b/drivers/staging/qcacld-2.0/CORE/SME/src/pmc/pmcApi.c
index 552c650..924749a 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/pmc/pmcApi.c
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/pmc/pmcApi.c
@@ -2866,14 +2866,17 @@
return eHAL_STATUS_FAILURE;
}
- pRequestBuf = vos_mem_malloc(sizeof(tSirPNOScanReq));
+ pRequestBuf = vos_mem_malloc(sizeof(tSirPNOScanReq) +
+ (pRequest->num_vendor_oui) *
+ (sizeof(struct vendor_oui)));
if (NULL == pRequestBuf)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: Not able to allocate memory for PNO request", __func__);
return eHAL_STATUS_FAILED_ALLOC;
}
- vos_mem_copy(pRequestBuf, pRequest, sizeof(tSirPNOScanReq));
+ vos_mem_copy(pRequestBuf, pRequest, sizeof(tSirPNOScanReq) +
+ (pRequest->num_vendor_oui) * (sizeof(struct vendor_oui)));
/*Must translate the mode first*/
ucDot11Mode = (tANI_U8) csrTranslateToWNICfgDot11Mode(pMac,
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/sme_common/sme_Api.c b/drivers/staging/qcacld-2.0/CORE/SME/src/sme_common/sme_Api.c
index 9651045..c4cf992 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/sme_common/sme_Api.c
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/sme_common/sme_Api.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -1051,6 +1051,17 @@
csrReleaseCommand(pMac, pCommand);
}
break;
+ case eSmeCommandNdpDataEndInitiatorRequest:
+ csrLLUnlock(&pMac->sme.smeCmdActiveList);
+ status = csr_process_ndp_data_end_request(pMac,
+ pCommand);
+ if (!HAL_STATUS_SUCCESS(status)) {
+ if (csrLLRemoveEntry(
+ &pMac->sme.smeCmdActiveList,
+ &pCommand->Link, LL_ACCESS_LOCK))
+ csrReleaseCommand(pMac, pCommand);
+ }
+ break;
case eSmeCommandDelStaSession:
csrLLUnlock( &pMac->sme.smeCmdActiveList );
csrProcessDelStaSessionCommand( pMac, pCommand );
@@ -1494,12 +1505,6 @@
break;
}
- status = btcOpen(pMac);
- if ( ! HAL_STATUS_SUCCESS( status ) ) {
- smsLog( pMac, LOGE,
- "btcOpen open failed during initialization with status=%d", status );
- break;
- }
#endif
#ifdef FEATURE_OEM_DATA_SUPPORT
status = oemData_OemDataReqOpen(pMac);
@@ -2182,14 +2187,6 @@
smsLog( pMac, LOGE, "pmcReady failed with status=%d", status );
break;
}
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- if(VOS_STATUS_SUCCESS != btcReady(hHal))
- {
- status = eHAL_STATUS_FAILURE;
- smsLog( pMac, LOGE, "btcReady failed");
- break;
- }
-#endif
#if defined WLAN_FEATURE_VOWIFI
if(VOS_STATUS_SUCCESS != rrmReady(hHal))
@@ -2209,14 +2206,6 @@
smsLog( pMac, LOGE, "csrReady failed with status=%d", status );
break;
}
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- if(VOS_STATUS_SUCCESS != btcReady(hHal))
- {
- status = eHAL_STATUS_FAILURE;
- smsLog( pMac, LOGE, "btcReady failed");
- break;
- }
-#endif
#if defined WLAN_FEATURE_VOWIFI
if(VOS_STATUS_SUCCESS != rrmReady(hHal))
@@ -2915,19 +2904,6 @@
smsLog( pMac, LOGE, "Empty rsp message for meas (eWNI_SME_REMAIN_ON_CHN_RDY_IND), nothing to process");
}
break;
- case eWNI_SME_COEX_IND:
- MTRACE(vos_trace(VOS_MODULE_ID_SME, TRACE_CODE_SME_RX_WDA_MSG,
- NO_SESSION, pMsg->type));
- if(pMsg->bodyptr)
- {
- status = btcHandleCoexInd((void *)pMac, pMsg->bodyptr);
- vos_mem_free(pMsg->bodyptr);
- }
- else
- {
- smsLog(pMac, LOGE, "Empty rsp message for meas (eWNI_SME_COEX_IND), nothing to process");
- }
- break;
#ifdef FEATURE_WLAN_SCAN_PNO
case eWNI_SME_PREF_NETWORK_FOUND_IND:
@@ -3318,7 +3294,22 @@
#ifdef FEATURE_WLAN_EXTSCAN
case eWNI_SME_EXTSCAN_FULL_SCAN_RESULT_IND:
{
- if (pMac->sme.pExtScanIndCb) {
+ tCsrRoamInfo *roam_info;
+ tpSirWifiFullScanResultEvent result =
+ (tpSirWifiFullScanResultEvent) pMsg->bodyptr;
+
+ roam_info = vos_mem_malloc(sizeof(*roam_info));
+ if (roam_info) {
+ vos_mem_zero(roam_info, sizeof(*roam_info));
+ roam_info->pBssDesc = (tSirBssDescription *)(
+ (uint8_t *)&result->bss_description+result->ap.ieLength);
+ csrRoamCallCallback(pMac, 0, roam_info, 0,
+ eCSR_ROAM_UPDATE_SCAN_RESULT, eCSR_ROAM_RESULT_NONE);
+ vos_mem_free(roam_info);
+ } else
+ smsLog( pMac, LOGE, FL("vos_mem_malloc failed:"));
+
+ if (pMac->sme.pExtScanIndCb) {
pMac->sme.pExtScanIndCb(pMac->hHdd,
eSIR_EXTSCAN_FULL_SCAN_RESULT_IND,
pMsg->bodyptr);
@@ -3326,6 +3317,7 @@
smsLog(pMac, LOGE,
FL("callback not registered to process eWNI_SME_EXTSCAN_FULL_SCAN_RESULT_IND"));
}
+
vos_mem_free(pMsg->bodyptr);
break;
}
@@ -3441,6 +3433,9 @@
case eWNI_SME_NDP_INITIATOR_RSP:
case eWNI_SME_NDP_INDICATION:
case eWNI_SME_NDP_RESPONDER_RSP:
+ case eWNI_SME_NDP_END_RSP:
+ case eWNI_SME_NDP_END_IND:
+ case eWNI_SME_NDP_PEER_DEPARTED_IND:
sme_ndp_msg_processor(pMac, pMsg);
break;
default:
@@ -3630,13 +3625,6 @@
#endif
#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- status = btcClose(hHal);
- if ( ! HAL_STATUS_SUCCESS( status ) ) {
- smsLog( pMac, LOGE, "BTC close failed during sme close with status=%d",
- status );
- fail_status = status;
- }
-
status = sme_QosClose(pMac);
if ( ! HAL_STATUS_SUCCESS( status ) ) {
smsLog( pMac, LOGE, "Qos close failed during sme close with status=%d",
@@ -6923,94 +6911,6 @@
}
/* ---------------------------------------------------------------------------
- \fn sme_BtcSignalBtEvent
- \brief API to signal Bluetooth (BT) event to the WLAN driver. Based on the
- BT event type and the current operating mode of Libra (full power,
- BMPS, UAPSD etc), appropriate Bluetooth Coexistence (BTC) strategy
- would be employed.
- \param hHal - The handle returned by macOpen.
- \param pBtEvent - Pointer to a caller allocated object of type tSmeBtEvent
- Caller owns the memory and is responsible for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE BT Event not passed to HAL. This can happen
- if BTC execution mode is set to BTC_WLAN_ONLY
- or BTC_PTA_ONLY.
- VOS_STATUS_SUCCESS BT Event passed to HAL
- ---------------------------------------------------------------------------*/
-VOS_STATUS sme_BtcSignalBtEvent (tHalHandle hHal, tpSmeBtEvent pBtEvent)
-{
- VOS_STATUS status = VOS_STATUS_E_FAILURE;
-
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
-
- MTRACE(vos_trace(VOS_MODULE_ID_SME,
- TRACE_CODE_SME_RX_HDD_BTC_SIGNALEVENT, NO_SESSION, 0));
- if ( eHAL_STATUS_SUCCESS == sme_AcquireGlobalLock( &pMac->sme ) )
- {
- status = btcSignalBTEvent (hHal, pBtEvent);
- sme_ReleaseGlobalLock( &pMac->sme );
- }
-#endif
- return (status);
-}
-
-/* ---------------------------------------------------------------------------
- \fn sme_BtcSetConfig
- \brief API to change the current Bluetooth Coexistence (BTC) configuration
- This function should be invoked only after CFG download has completed.
- Calling it after sme_HDDReadyInd is recommended.
- \param hHal - The handle returned by macOpen.
- \param pSmeBtcConfig - Pointer to a caller allocated object of type tSmeBtcConfig.
- Caller owns the memory and is responsible for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE Config not passed to HAL.
- VOS_STATUS_SUCCESS Config passed to HAL
- ---------------------------------------------------------------------------*/
-VOS_STATUS sme_BtcSetConfig (tHalHandle hHal, tpSmeBtcConfig pSmeBtcConfig)
-{
- VOS_STATUS status = VOS_STATUS_E_FAILURE;
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
- MTRACE(vos_trace(VOS_MODULE_ID_SME,
- TRACE_CODE_SME_RX_HDD_BTC_SETCONFIG, NO_SESSION, 0));
- if ( eHAL_STATUS_SUCCESS == sme_AcquireGlobalLock( &pMac->sme ) )
- {
- status = btcSetConfig (hHal, pSmeBtcConfig);
- sme_ReleaseGlobalLock( &pMac->sme );
- }
-#endif
- return (status);
-}
-
-/* ---------------------------------------------------------------------------
- \fn sme_BtcGetConfig
- \brief API to retrieve the current Bluetooth Coexistence (BTC) configuration
- \param hHal - The handle returned by macOpen.
- \param pSmeBtcConfig - Pointer to a caller allocated object of type
- tSmeBtcConfig. Caller owns the memory and is responsible
- for freeing it.
- \return VOS_STATUS
- VOS_STATUS_E_FAILURE - failure
- VOS_STATUS_SUCCESS success
- ---------------------------------------------------------------------------*/
-VOS_STATUS sme_BtcGetConfig (tHalHandle hHal, tpSmeBtcConfig pSmeBtcConfig)
-{
- VOS_STATUS status = VOS_STATUS_E_FAILURE;
-#ifndef WLAN_MDM_CODE_REDUCTION_OPT
- tpAniSirGlobal pMac = PMAC_STRUCT( hHal );
-
- MTRACE(vos_trace(VOS_MODULE_ID_SME,
- TRACE_CODE_SME_RX_HDD_BTC_GETCONFIG, NO_SESSION, 0));
- if ( eHAL_STATUS_SUCCESS == sme_AcquireGlobalLock( &pMac->sme ) )
- {
- status = btcGetConfig (hHal, pSmeBtcConfig);
- sme_ReleaseGlobalLock( &pMac->sme );
- }
-#endif
- return (status);
-}
-/* ---------------------------------------------------------------------------
\fn sme_SetCfgPrivacy
\brief API to set configure privacy parameters
\param hHal - The handle returned by macOpen.
@@ -9256,8 +9156,8 @@
*Find the connected Infra / P2P_client connected session
*/
if (CSR_IS_SESSION_VALID(pMac, sessionId) &&
- csrIsConnStateInfra(pMac, sessionId))
- {
+ (csrIsConnStateInfra(pMac, sessionId) ||
+ csr_is_ndi_started(pMac, sessionId))) {
pSession = CSR_GET_SESSION( pMac, sessionId );
}
@@ -9267,17 +9167,17 @@
}
pRequestBuf = vos_mem_malloc(sizeof(tSirRcvFltMcAddrList));
- if (NULL == pRequestBuf)
- {
+ if (NULL == pRequestBuf) {
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: Not able to "
"allocate memory for 8023 Multicast List request", __func__);
return eHAL_STATUS_FAILED_ALLOC;
}
- if( !csrIsConnStateConnectedInfra (pMac, sessionId ))
- {
- VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, "%s: Ignoring the "
- "indication as we are not connected", __func__);
+ if (!csrIsConnStateConnectedInfra(pMac, sessionId) &&
+ !csr_is_ndi_started(pMac, sessionId)) {
+ VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+ "%s: Request ignored, session %d is not connected or started",
+ __func__, sessionId);
vos_mem_free(pRequestBuf);
return eHAL_STATUS_FAILURE;
}
@@ -16485,6 +16385,50 @@
return status;
}
+#ifdef WLAN_POWER_DEBUGFS
+/**
+ * sme_power_debug_stats_req() - SME API to collect Power debug stats
+ * @callback_fn: Pointer to the callback function for Power stats event
+ * @power_stats_context: Pointer to context
+ *
+ * Return: eHalStatus
+ */
+eHalStatus sme_power_debug_stats_req(tHalHandle hal, void (*callback_fn)
+ (struct power_stats_response *response,
+ void *context), void *power_stats_context)
+{
+ eHalStatus status = eHAL_STATUS_SUCCESS;
+ VOS_STATUS vos_status = VOS_STATUS_SUCCESS;
+ tpAniSirGlobal mac = PMAC_STRUCT(hal);
+ vos_msg_t vos_message;
+
+ if (eHAL_STATUS_SUCCESS == sme_AcquireGlobalLock(&mac->sme)) {
+ if (NULL == callback_fn) {
+ smsLog(mac, LOGE,
+ FL("Indication callback did not registered"));
+ sme_ReleaseGlobalLock(&mac->sme);
+ return eHAL_STATUS_FAILURE;
+ }
+
+ mac->sme.power_debug_stats_context = power_stats_context;
+ mac->sme.power_stats_resp_callback = callback_fn;
+ vos_message.bodyptr = NULL;
+ vos_message.type = SIR_HAL_POWER_DEBUG_STATS_REQ;
+ vos_status = vos_mq_post_message(VOS_MQ_ID_WDA, &vos_message);
+ if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
+ smsLog(mac, LOGE,
+ FL("not able to post WDA_POWER_DEBUG_STATS_REQ"));
+ status = eHAL_STATUS_FAILURE;
+ }
+ sme_ReleaseGlobalLock(&mac->sme);
+ } else {
+ smsLog(mac, LOGE, FL("sme_AcquireGlobalLock error"));
+ status = eHAL_STATUS_FAILURE;
+ }
+ return status;
+}
+#endif
+
/* ---------------------------------------------------------------------------
\fn sme_SetLinkLayerStatsIndCB
\brief SME API to trigger the stats are available after get request
@@ -19027,3 +18971,139 @@
return status;
}
+
+/*
+ * sme_set_band_specific_pref(): If 5G preference is enabled,set boost/drop
+ * params from ini.
+ * @hal_handle: Handle returned by mac_open
+ * @5g_pref_params: pref params from ini.
+ */
+void sme_set_5g_band_pref(tHalHandle hal_handle,
+ struct sme_5g_band_pref_params *pref_params) {
+
+ tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal_handle);
+ struct roam_ext_params *roam_params;
+ eHalStatus status = eHAL_STATUS_SUCCESS;
+
+ if (!pref_params) {
+ VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+ "Invalid 5G pref params!");
+ return;
+ }
+ status = sme_AcquireGlobalLock( &mac_ctx->sme );
+ if (HAL_STATUS_SUCCESS(status)) {
+ roam_params = &mac_ctx->roam.configParam.roam_params;
+ roam_params->raise_rssi_thresh_5g =
+ pref_params->rssi_boost_threshold_5g;
+ roam_params->raise_factor_5g =
+ pref_params->rssi_boost_factor_5g;
+ roam_params->max_raise_rssi_5g =
+ pref_params->max_rssi_boost_5g;
+ roam_params->drop_rssi_thresh_5g =
+ pref_params->rssi_penalize_threshold_5g;
+ roam_params->drop_factor_5g =
+ pref_params->rssi_penalize_factor_5g;
+ roam_params->max_drop_rssi_5g =
+ pref_params->max_rssi_penalize_5g;
+
+ sme_ReleaseGlobalLock(&mac_ctx->sme);
+ }
+ else
+ VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+ "Unable to acquire global sme lock");
+
+}
+
+eHalStatus sme_set_random_mac(tHalHandle hal,
+ action_frame_random_filter_callback callback,
+ uint32_t session_id, uint8_t *random_mac,
+ void *context)
+{
+
+ eHalStatus status = eHAL_STATUS_SUCCESS;
+ VOS_STATUS vos_status = VOS_STATUS_SUCCESS;
+ tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal);
+ vos_msg_t vos_msg;
+ struct action_frame_random_filter *filter;
+
+ filter = vos_mem_malloc(sizeof(*filter));
+
+ if (!filter) {
+ VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+ FL("Failed to alloc random mac filter"));
+ return eHAL_STATUS_FAILED_ALLOC;
+ }
+ vos_mem_zero(filter, sizeof(*filter));
+
+ filter->session_id = session_id;
+ filter->filter_type = SME_ACTION_FRAME_RANDOM_MAC_SET;
+ filter->callback = callback;
+ filter->context = context;
+ vos_mem_copy(filter->mac_addr, random_mac, VOS_MAC_ADDR_SIZE);
+
+ status = sme_AcquireGlobalLock(&mac_ctx->sme);
+ if (status == eHAL_STATUS_SUCCESS) {
+ /* Serialize the req through MC thread */
+ vos_msg.bodyptr = filter;
+ vos_msg.type = WDA_ACTION_FRAME_RANDOM_MAC;
+ vos_status = vos_mq_post_message(VOS_MQ_ID_WDA, &vos_msg);
+
+ if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
+ VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+ FL("action frame set random mac msg fail"));
+ status = eHAL_STATUS_FAILURE;
+ vos_mem_free(filter);
+ }
+ sme_ReleaseGlobalLock(&mac_ctx->sme);
+ } else {
+ VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+ FL("sme_AcquireGlobalLock failed"));
+ vos_mem_free(filter);
+ }
+ return status;
+}
+
+eHalStatus sme_clear_random_mac(tHalHandle hal, uint32_t session_id,
+ uint8_t *random_mac)
+{
+
+ eHalStatus status = eHAL_STATUS_SUCCESS;
+ VOS_STATUS vos_status = VOS_STATUS_SUCCESS;
+ tpAniSirGlobal mac_ctx = PMAC_STRUCT(hal);
+ vos_msg_t vos_msg;
+ struct action_frame_random_filter *filter;
+
+ filter = vos_mem_malloc(sizeof(*filter));
+
+ if (!filter) {
+ VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+ FL("Failed to alloc random mac filter"));
+ return eHAL_STATUS_FAILED_ALLOC;
+ }
+ vos_mem_zero(filter, sizeof(*filter));
+
+ filter->session_id = session_id;
+ filter->filter_type = SME_ACTION_FRAME_RANDOM_MAC_CLEAR;
+ vos_mem_copy(filter->mac_addr, random_mac, VOS_MAC_ADDR_SIZE);
+
+ status = sme_AcquireGlobalLock(&mac_ctx->sme);
+ if (status == eHAL_STATUS_SUCCESS) {
+ /* Serialize the req through MC thread */
+ vos_msg.bodyptr = filter;
+ vos_msg.type = WDA_ACTION_FRAME_RANDOM_MAC;
+ vos_status = vos_mq_post_message(VOS_MQ_ID_WDA, &vos_msg);
+
+ if (!VOS_IS_STATUS_SUCCESS(vos_status)) {
+ VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+ FL("action frame clear random mac msg fail"));
+ status = eHAL_STATUS_FAILURE;
+ vos_mem_free(filter);
+ }
+ sme_ReleaseGlobalLock(&mac_ctx->sme);
+ } else {
+ VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR,
+ FL("sme_AcquireGlobalLock failed"));
+ vos_mem_free(filter);
+ }
+ return status;
+}
diff --git a/drivers/staging/qcacld-2.0/CORE/SME/src/sme_common/sme_Trace.c b/drivers/staging/qcacld-2.0/CORE/SME/src/sme_common/sme_Trace.c
index 3349175..65ae991 100644
--- a/drivers/staging/qcacld-2.0/CORE/SME/src/sme_common/sme_Trace.c
+++ b/drivers/staging/qcacld-2.0/CORE/SME/src/sme_common/sme_Trace.c
@@ -214,28 +214,28 @@
{
switch (pRecord->code) {
case TRACE_CODE_SME_COMMAND:
- smsLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ smsLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"SME COMMAND:",
smeTraceGetCommandString(pRecord->data),
pRecord->data);
break;
case TRACE_CODE_SME_TX_WDA_MSG:
- smsLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ smsLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"TX WDA Msg:",
macTraceGetWdaMsgString((tANI_U16)pRecord->data),
pRecord->data);
break;
case TRACE_CODE_SME_RX_WDA_MSG:
- smsLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ smsLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"RX WDA Msg:",
macTraceGetSmeMsgString((tANI_U16)pRecord->data),
pRecord->data);
break;
default:
- smsLog(pMac, LOG1, "%04d %012llu S%d %-14s %-30s(0x%x)",
+ smsLog(pMac, LOG1, "%04d %s S%d %-14s %-30s(0x%x)",
recIndex, pRecord->time, pRecord->session,
"RX HDD MSG:",
smeTraceGetRxMsgString(pRecord->code),
diff --git a/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_btc_svc.h b/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_btc_svc.h
deleted file mode 100644
index 7eee2de..0000000
--- a/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_btc_svc.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2012 The Linux Foundation. All rights reserved.
- *
- * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
- *
- *
- * Permission to use, copy, modify, and/or distribute this software for
- * any purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all
- * copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-/*
- * This file was originally distributed by Qualcomm Atheros, Inc.
- * under proprietary terms before Copyright ownership was assigned
- * to the Linux Foundation.
- */
-
-/******************************************************************************
- * wlan_btc_svc.h
- *
- ******************************************************************************/
-
-#ifndef WLAN_BTC_SVC_H
-#define WLAN_BTC_SVC_H
-
-void send_btc_nlink_msg (int type, int dest_pid);
-int btc_activate_service(void *pAdapter);
-
-#endif
diff --git a/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_nlink_srv.h b/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_nlink_srv.h
index a2e2cd9..10a1f25 100644
--- a/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_nlink_srv.h
+++ b/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_nlink_srv.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -66,10 +66,17 @@
#endif /* WLAN_KD_READY_NOTIFIER */
int nl_srv_register(tWlanNlModTypes msg_type, nl_srv_msg_callback msg_handler);
int nl_srv_unregister(tWlanNlModTypes msg_type, nl_srv_msg_callback msg_handler);
-int nl_srv_ucast(struct sk_buff * skb, int dst_pid, int flag);
-int nl_srv_bcast(struct sk_buff * skb);
-int nl_srv_is_initialized(void);
+#ifdef CNSS_GENL
+int nl_srv_ucast(struct sk_buff *skb, int dst_pid, int flag,
+ int app_id, int mcgroup_id);
+int nl_srv_bcast(struct sk_buff *skb, int mcgroup_id, int app_id);
+#else
+int nl_srv_ucast(struct sk_buff *skb, int dst_pid, int flag);
+int nl_srv_bcast(struct sk_buff *skb);
+#endif
+
+int nl_srv_is_initialized(void);
#else
static inline int nl_srv_init(void) { return 0; }
diff --git a/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_ptt_sock_svc.h b/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_ptt_sock_svc.h
index a662d95..a1bdd1c 100644
--- a/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_ptt_sock_svc.h
+++ b/drivers/staging/qcacld-2.0/CORE/SVC/inc/wlan_ptt_sock_svc.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -97,7 +97,7 @@
* Length : 4 bytes [LEN_PAYLOAD]
* Payload : LEN_PAYLOAD bytes
*/
-int ptt_sock_activate_svc(void *pAdapter);
+int ptt_sock_activate_svc(void *hdd_ctx);
int ptt_sock_send_msg_to_app(tAniHdr *wmsg, int radio, int src_mod, int pid);
/*
@@ -114,6 +114,19 @@
tAniNlModTypes type; // module id
int pid; // process id
} tAniNlAppRegReq;
+
+/**
+ * struct sptt_app_reg_req - PTT register request structure
+ * @radio: Radio ID
+ * @wmsg: ANI header
+ *
+ * payload structure received as nl data from PTT app/user space
+ */
+typedef struct sptt_app_reg_req {
+ int radio;
+ tAniHdr wmsg;
+} ptt_app_reg_req;
+
typedef struct sAniNlAppRegRsp {
tAniHdr wniHdr; // Generic WNI msg header
tAniNlAppRegReq regReq; // The original request msg
diff --git a/drivers/staging/qcacld-2.0/CORE/SVC/src/btc/wlan_btc_svc.c b/drivers/staging/qcacld-2.0/CORE/SVC/src/btc/wlan_btc_svc.c
deleted file mode 100644
index 7d4aeaf..0000000
--- a/drivers/staging/qcacld-2.0/CORE/SVC/src/btc/wlan_btc_svc.c
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Copyright (c) 2013 The Linux Foundation. All rights reserved.
- *
- * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
- *
- *
- * Permission to use, copy, modify, and/or distribute this software for
- * any purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all
- * copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-/*
- * This file was originally distributed by Qualcomm Atheros, Inc.
- * under proprietary terms before Copyright ownership was assigned
- * to the Linux Foundation.
- */
-
-
-/******************************************************************************
- * wlan_btc_svc.c
- *
- ******************************************************************************/
-#include <wlan_nlink_srv.h>
-#include <wlan_btc_svc.h>
-#include <halTypes.h>
-#include <vos_status.h>
-#include <btcApi.h>
-#include <wlan_hdd_includes.h>
-#include <vos_trace.h>
-// Global variables
-static struct hdd_context_s *pHddCtx;
-
-static int gWiFiChannel; /* WiFi associated channel 1-13, or 0 (none) */
-static int gAmpChannel; /* AMP associated channel 1-13, or 0 (none) */
-static int gBtcDriverMode = WLAN_HDD_INFRA_STATION; /* Driver mode in BTC */
-
-
-// Forward declrarion
-static int btc_msg_callback (struct sk_buff * skb);
-/*
- * Send a netlink message to the user space.
- * Destination pid as zero implies broadcast
- */
-void send_btc_nlink_msg (int type, int dest_pid)
-{
- struct sk_buff *skb;
- struct nlmsghdr *nlh;
- tAniMsgHdr *aniHdr;
- tWlanAssocData *assocData;
- skb = alloc_skb(NLMSG_SPACE(WLAN_NL_MAX_PAYLOAD), GFP_KERNEL);
- if(skb == NULL) {
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
- "BTC: alloc_skb failed\n");
- return;
- }
- nlh = (struct nlmsghdr *)skb->data;
- nlh->nlmsg_pid = 0; /* from kernel */
- nlh->nlmsg_flags = 0;
- nlh->nlmsg_seq = 0;
- nlh->nlmsg_type = WLAN_NL_MSG_BTC;
- aniHdr = NLMSG_DATA(nlh);
- aniHdr->type = type;
-
- /* Set BTC driver mode correctly based on received events type */
- if(type == WLAN_BTC_SOFTAP_BSS_START)
- {
- /* Event is SoftAP BSS Start set BTC driver mode to SoftAP */
- gBtcDriverMode = WLAN_HDD_SOFTAP;
- }
- if(type == WLAN_STA_ASSOC_DONE_IND)
- {
- /* Event is STA Assoc done set BTC driver mode to INFRA STA*/
- gBtcDriverMode = WLAN_HDD_INFRA_STATION;
- }
-
- switch( type )
- {
- case WLAN_STA_DISASSOC_DONE_IND:
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_LOW,
- "WiFi unassociated; gAmpChannel %d gWiFiChannel %d", gAmpChannel, gWiFiChannel);
-
- /* If AMP is using a channel (non-zero), no message sent.
- Or, if WiFi wasn't using a channel before, no message sent.
- Logic presumes same channel has to be used for WiFi and AMP if both are active.
- In any case, track the WiFi channel in use (none) */
- if((gAmpChannel != 0) || (gWiFiChannel == 0))
- {
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_LOW,
- "No msg for AFH will be sent");
- gWiFiChannel = 0;
- kfree_skb(skb);
- return;
- }
- gWiFiChannel = 0;
-
- /* No Break: Fall into next cases */
-
- case WLAN_MODULE_UP_IND:
- case WLAN_MODULE_DOWN_IND:
- aniHdr->length = 0;
- nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr)));
- skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr)));
- break;
- case WLAN_BTC_SOFTAP_BSS_START:
- case WLAN_BTC_QUERY_STATE_RSP:
- case WLAN_STA_ASSOC_DONE_IND:
- aniHdr->length = sizeof(tWlanAssocData);
- nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + sizeof(tWlanAssocData)));
- assocData = ( tWlanAssocData *)((char*)aniHdr + sizeof(tAniMsgHdr));
-
- assocData->channel = hdd_get_operating_channel( pHddCtx, gBtcDriverMode );
-
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_LOW,
- "New WiFi channel %d gAmpChannel %d gWiFiChannel %d",
- assocData->channel, gAmpChannel, gWiFiChannel);
-
- /* If WiFi has finished associating */
- if(type == WLAN_STA_ASSOC_DONE_IND)
- {
- /* If AMP is using a channel (non-zero), no message sent.
- Or, if the WiFi channel did not change, no message sent.
- Logic presumes same channel has to be used for WiFi and AMP if both are active.
- In any case, track the WiFi channel in use (1-13 or none, in assocData->channel) */
- if((gAmpChannel != 0) || (assocData->channel == gWiFiChannel))
- {
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_LOW,
- "No msg for AFH will be sent");
- gWiFiChannel = assocData->channel;
- kfree_skb(skb);
- return;
- }
- }
- if(type == WLAN_BTC_SOFTAP_BSS_START)
- {
- /*Replace WLAN_BTC_SOFTAP_BSS_START by WLAN_STA_ASSOC_DONE_IND*/
- aniHdr->type = WLAN_STA_ASSOC_DONE_IND;
- }
- gWiFiChannel = assocData->channel;
- skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr)+ sizeof(tWlanAssocData))));
- break;
-
- case WLAN_AMP_ASSOC_DONE_IND:
-
- /* This is an overloaded type. It means that AMP is connected (dest_pid is channel 1-13),
- or it means AMP is now disconnected (dest_pid is 0) */
-
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_LOW,
- "New AMP channel %d gAmpChannel %d gWiFiChannel %d", dest_pid, gAmpChannel, gWiFiChannel);
- /* If WiFi is using a channel (non-zero), no message sent.
- Or, if the AMP channel did not change, no message sent.
- Logic presumes same channel has to be used for WiFi and AMP if both are active.
- In any case, track the AMP channel in use (1-13 or none, in dest_pid) */
- if((gWiFiChannel != 0) || (dest_pid == gAmpChannel))
- {
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO_LOW,
- "No msg for AFH will be sent");
- gAmpChannel = dest_pid;
- kfree_skb(skb);
- return;
- }
-
- gAmpChannel = dest_pid;
-
- /* Fix overloaded parameters and finish message formatting */
- if(dest_pid != 0)
- {
- aniHdr->type = WLAN_STA_ASSOC_DONE_IND;
- aniHdr->length = sizeof(tWlanAssocData);
- nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + sizeof(tWlanAssocData)));
- assocData = ( tWlanAssocData *)((char*)aniHdr + sizeof(tAniMsgHdr));
- assocData->channel = dest_pid;
- skb_put(skb, NLMSG_SPACE((sizeof(tAniMsgHdr)+ sizeof(tWlanAssocData))));
- }
- else
- {
- aniHdr->type = WLAN_STA_DISASSOC_DONE_IND;
- aniHdr->length = 0;
- nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr)));
- skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr)));
- }
- dest_pid = 0;
- break;
-
- default:
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
- "BTC: Attempt to send unknown nlink message %d\n", type);
- kfree_skb(skb);
- return;
- }
- if(dest_pid == 0)
- (void)nl_srv_bcast(skb);
- else
- (void)nl_srv_ucast(skb, dest_pid, MSG_DONTWAIT);
-}
-/*
- * Activate BTC handler. This will register a handler to receive
- * netlink messages addressed to WLAN_NL_MSG_BTC from user space
- */
-int btc_activate_service(void *pAdapter)
-{
- pHddCtx = (struct hdd_context_s*)pAdapter;
-
- //Register the msg handler for msgs addressed to ANI_NL_MSG_BTC
- nl_srv_register(WLAN_NL_MSG_BTC, btc_msg_callback);
- return 0;
-}
-/*
- * Callback function invoked by Netlink service for all netlink
- * messages (from user space) addressed to WLAN_NL_MSG_BTC
- */
-int btc_msg_callback (struct sk_buff * skb)
-{
- struct nlmsghdr *nlh;
- tAniMsgHdr *msg_hdr;
- tSmeBtEvent *btEvent = NULL;
- nlh = (struct nlmsghdr *)skb->data;
- msg_hdr = NLMSG_DATA(nlh);
-
- /* Continue with parsing payload. */
- switch(msg_hdr->type)
- {
- case WLAN_BTC_QUERY_STATE_REQ:
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
- "BTC: Received probe from BTC Service\n");
- send_btc_nlink_msg(WLAN_BTC_QUERY_STATE_RSP, nlh->nlmsg_pid);
- break;
- case WLAN_BTC_BT_EVENT_IND:
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
- "BTC: Received Bluetooth event indication\n");
- if(msg_hdr->length != sizeof(tSmeBtEvent)) {
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
- "BTC: Size mismatch in BT event data\n");
- break;
- }
- btEvent = (tSmeBtEvent*)((char*)msg_hdr + sizeof(tAniMsgHdr));
- (void)sme_BtcSignalBtEvent(pHddCtx->hHal, btEvent);
- break;
- default:
- VOS_TRACE( VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
- "BTC: Received Invalid Msg type [%d]\n", msg_hdr->type);
- break;
- }
- return 0;
-}
diff --git a/drivers/staging/qcacld-2.0/CORE/SVC/src/logging/wlan_logging_sock_svc.c b/drivers/staging/qcacld-2.0/CORE/SVC/src/logging/wlan_logging_sock_svc.c
index 8ebce69..7ded4f7 100644
--- a/drivers/staging/qcacld-2.0/CORE/SVC/src/logging/wlan_logging_sock_svc.c
+++ b/drivers/staging/qcacld-2.0/CORE/SVC/src/logging/wlan_logging_sock_svc.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -48,6 +48,9 @@
#include "limApi.h"
#include "ol_txrx_api.h"
#include "csrApi.h"
+#ifdef CNSS_GENL
+#include <net/cnss_nl.h>
+#endif
#define MAX_NUM_PKT_LOG 32
@@ -81,7 +84,8 @@
#define ANI_NL_MSG_LOG_TYPE 89
#define ANI_NL_MSG_READY_IND_TYPE 90
-#define MAX_LOGMSG_LENGTH 4096
+#define MAX_LOGMSG_LENGTH 2048
+#define MAX_SKBMSG_LENGTH 4096
#define MAX_PKTSTATS_LENGTH 2048
#define MAX_PKTSTATS_BUFF 16
@@ -177,6 +181,7 @@
/* PID of the APP to log the message */
static int gapp_pid = INVALID_PID;
+#ifndef CNSS_GENL
/* Utility function to send a netlink message to an application
* in user space
*/
@@ -230,6 +235,7 @@
return err;
}
+#endif
/**
* is_data_path_module() - To check for a Datapath module
@@ -425,7 +431,7 @@
if (gwlan_logging.log_fe_to_console
&& ((VOS_TRACE_LEVEL_FATAL == log_level)
|| (VOS_TRACE_LEVEL_ERROR == log_level))) {
- pr_info("%s\n", to_be_sent);
+ pr_info("%s %s\n", tbuf, to_be_sent);
}
}
return 0;
@@ -498,6 +504,42 @@
}
/**
+ * nl_srv_bcast_diag() - Wrapper to send bcast msgs to diag events mcast grp
+ * @skb: sk buffer pointer
+ *
+ * Sends the bcast message to diag events multicast group with generic nl socket
+ * if CNSS_GENL is enabled. Else, use the legacy netlink socket to send.
+ *
+ * Return: zero on success, error code otherwise
+ */
+static int nl_srv_bcast_diag(struct sk_buff *skb)
+{
+#ifdef CNSS_GENL
+ return nl_srv_bcast(skb, CLD80211_MCGRP_DIAG_EVENTS, ANI_NL_MSG_PUMAC);
+#else
+ return nl_srv_bcast(skb);
+#endif
+}
+
+/**
+ * nl_srv_bcast_host_logs() - Wrapper to send bcast msgs to host logs mcast grp
+ * @skb: sk buffer pointer
+ *
+ * Sends the bcast message to host logs multicast group with generic nl socket
+ * if CNSS_GENL is enabled. Else, use the legacy netlink socket to send.
+ *
+ * Return: zero on success, error code otherwise
+ */
+static int nl_srv_bcast_host_logs(struct sk_buff *skb)
+{
+#ifdef CNSS_GENL
+ return nl_srv_bcast(skb, CLD80211_MCGRP_HOST_LOGS, ANI_NL_MSG_LOG);
+#else
+ return nl_srv_bcast(skb);
+#endif
+}
+
+/**
* pktlog_send_per_pkt_stats_to_user() - This function is used to send the per
* packet statistics to the user
*
@@ -517,11 +559,11 @@
while (!list_empty(&gwlan_logging.pkt_stat_filled_list)
&& !gwlan_logging.exit) {
- skb_new = dev_alloc_skb(MAX_PKTSTATS_LENGTH);
+ skb_new = dev_alloc_skb(MAX_SKBMSG_LENGTH);
if (skb_new == NULL) {
if (!rate_limit) {
pr_err("%s: dev_alloc_skb() failed for msg size[%d] drop count = %u\n",
- __func__, MAX_LOGMSG_LENGTH,
+ __func__, MAX_SKBMSG_LENGTH,
gwlan_logging.drop_count);
}
rate_limit = 1;
@@ -542,7 +584,7 @@
free_old_skb = true;
goto err;
}
- ret = nl_srv_bcast(pstats_msg->skb);
+ ret = nl_srv_bcast_diag(pstats_msg->skb);
if (ret < 0) {
pr_info("%s: Send Failed %d drop_count = %u\n",
__func__, ret,
@@ -640,7 +682,7 @@
&gwlan_logging.free_list);
spin_unlock_irqrestore(&gwlan_logging.spin_lock, flags);
- ret = nl_srv_bcast(skb);
+ ret = nl_srv_bcast_host_logs(skb);
/* print every 64th drop count */
if (ret < 0 && (!(gwlan_logging.drop_count % 0x40))) {
pr_err("%s: Send Failed %d drop_count = %u\n",
@@ -800,6 +842,30 @@
return 0;
}
+#ifdef CNSS_GENL
+/**
+ * register_logging_sock_handler() - Logging sock handler registration
+ *
+ * Dummy API to register the command handler for logger socket app.
+ *
+ * Return: None
+ */
+static void register_logging_sock_handler(void)
+{
+}
+
+/**
+ * unregister_logging_sock_handler() - Logging sock handler unregistration
+ *
+ * Dummy API to unregister the command handler for logger socket app.
+ *
+ * Return: None
+ */
+static void unregister_logging_sock_handler(void)
+{
+}
+
+#else
/*
* Process all the Netlink messages from Logger Socket app in user space
*/
@@ -858,6 +924,33 @@
return ret;
}
+/**
+ * register_logging_sock_handler() - Logging sock handler registration
+ *
+ * API to register the command handler for logger socket app. Registers
+ * legacy handler
+ *
+ * Return: None
+ */
+static void register_logging_sock_handler(void)
+{
+ nl_srv_register(ANI_NL_MSG_LOG, wlan_logging_proc_sock_rx_msg);
+}
+
+/**
+ * unregister_logging_sock_handler() - Logging sock handler unregistration
+ *
+ * API to unregister the command handler for logger socket app. Unregisters
+ * legacy handler
+ *
+ * Return: None
+ */
+static void unregister_logging_sock_handler(void)
+{
+ nl_srv_unregister(ANI_NL_MSG_LOG, wlan_logging_proc_sock_rx_msg);
+}
+#endif
+
int wlan_logging_sock_activate_svc(int log_fe_to_console, int num_buf)
{
int i, j, pkt_stats_size;
@@ -948,8 +1041,7 @@
gwlan_logging.is_active = true;
gwlan_logging.is_flush_complete = false;
- nl_srv_register(ANI_NL_MSG_LOG, wlan_logging_proc_sock_rx_msg);
-
+ register_logging_sock_handler();
return 0;
err3:
@@ -979,7 +1071,7 @@
if (!gplog_msg)
return 0;
- nl_srv_unregister(ANI_NL_MSG_LOG, wlan_logging_proc_sock_rx_msg);
+ unregister_logging_sock_handler();
clear_default_logtoapp_log_level();
gapp_pid = INVALID_PID;
diff --git a/drivers/staging/qcacld-2.0/CORE/SVC/src/nlink/wlan_nlink_srv.c b/drivers/staging/qcacld-2.0/CORE/SVC/src/nlink/wlan_nlink_srv.c
index 8512bf0..a4a5503f 100644
--- a/drivers/staging/qcacld-2.0/CORE/SVC/src/nlink/wlan_nlink_srv.c
+++ b/drivers/staging/qcacld-2.0/CORE/SVC/src/nlink/wlan_nlink_srv.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -52,6 +52,13 @@
#include <wlan_nlink_srv.h>
#include <vos_trace.h>
+#ifdef CNSS_GENL
+#include <vos_memory.h>
+#include <wlan_nlink_common.h>
+#include <net/genetlink.h>
+#include <net/cnss_nl.h>
+#endif
+
/* Global variables */
static DEFINE_MUTEX(nl_srv_sem);
static struct sock *nl_srv_sock;
@@ -155,6 +162,192 @@
return retcode;
}
+#ifdef CNSS_GENL
+
+/**
+ * nl80211hdr_put() - API to fill genlmsg header
+ * @skb: Sk buffer
+ * @portid: Port ID
+ * @seq: Sequence number
+ * @flags: Flags
+ * @cmd: Command id
+ *
+ * API to fill genl message header for brodcast events to user space
+ *
+ * Return: Pointer to user specific header/payload
+ */
+static inline void *nl80211hdr_put(struct sk_buff *skb, uint32_t portid,
+ uint32_t seq, int flags, uint8_t cmd)
+{
+ struct genl_family *cld80211_fam = cld80211_get_genl_family();
+
+ return genlmsg_put(skb, portid, seq, cld80211_fam, flags, cmd);
+}
+
+/**
+ * cld80211_fill_data() - API to fill payload to nl message
+ * @msg: Sk buffer
+ * @portid: Port ID
+ * @seq: Sequence number
+ * @flags: Flags
+ * @cmd: Command ID
+ * @buf: data buffer/payload to be filled
+ * @len: length of the payload ie. @buf
+ *
+ * API to fill the payload/data of the nl message to be sent
+ *
+ * Return: zero on success
+ */
+static int cld80211_fill_data(struct sk_buff *msg, uint32_t portid,
+ uint32_t seq, int flags, uint8_t cmd,
+ uint8_t *buf, int len)
+{
+ void *hdr;
+ struct nlattr *nest;
+
+ hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
+ if (!hdr) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+ "nl80211 hdr put failed");
+ return -EPERM;
+ }
+
+ nest = nla_nest_start(msg, CLD80211_ATTR_VENDOR_DATA);
+ if (!nest) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+ "nla_nest_start failed");
+ goto nla_put_failure;
+ }
+
+ if (nla_put(msg, CLD80211_ATTR_DATA, len, buf)) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+ "nla_put failed");
+ goto nla_put_failure;
+ }
+
+ nla_nest_end(msg, nest);
+ genlmsg_end(msg, hdr);
+
+ return 0;
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EPERM;
+}
+
+/**
+ * send_msg_to_cld80211() - API to send message to user space Application
+ * @mcgroup_id: Multicast group ID
+ * @pid: Port ID
+ * @app_id: Application ID
+ * @buf: Data/payload buffer to be sent
+ * @len: Length of the data ie. @buf
+ *
+ * API to send the nl message to user space application.
+ *
+ * Return: zero on success
+ */
+static int send_msg_to_cld80211(int mcgroup_id, int pid, int app_id,
+ uint8_t *buf, int len)
+{
+ struct sk_buff *msg;
+ struct genl_family *cld80211_fam = cld80211_get_genl_family();
+ int status;
+ int flags = GFP_KERNEL;
+
+ if (in_interrupt() || irqs_disabled() || in_atomic())
+ flags = GFP_ATOMIC;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags);
+ if (!msg) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+ "nlmsg malloc fails");
+ return -EPERM;
+ }
+
+ status = cld80211_fill_data(msg, pid, 0, 0, app_id, buf, len);
+ if (status) {
+ nlmsg_free(msg);
+ return -EPERM;
+ }
+
+ genlmsg_multicast_netns(cld80211_fam, &init_net, msg, 0,
+ mcgroup_id, flags);
+ return 0;
+}
+
+/**
+ * nl_srv_bcast() - wrapper function to do broadcast events to user space apps
+ * @skb: the socket buffer to send
+ * @mcgroup_id: multicast group id
+ * @app_id: application id
+ *
+ * This function is common wrapper to send broadcast events to different
+ * user space applications.
+ *
+ * return: none
+ */
+int nl_srv_bcast(struct sk_buff *skb, int mcgroup_id, int app_id)
+{
+ struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
+ void *msg = NLMSG_DATA(nlh);
+ uint32_t msg_len = nlmsg_len(nlh);
+ uint8_t *tempbuf;
+ int status;
+
+ tempbuf = (uint8_t *)vos_mem_malloc(msg_len);
+ vos_mem_copy(tempbuf, msg, msg_len);
+ status = send_msg_to_cld80211(mcgroup_id, 0, app_id, tempbuf, msg_len);
+ if (status) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+ "send msg to cld80211 fails for app id %d", app_id);
+ return -EPERM;
+ }
+
+ dev_kfree_skb(skb);
+ vos_mem_free(tempbuf);
+ return 0;
+}
+
+/**
+ * nl_srv_ucast() - wrapper function to do unicast events to user space apps
+ * @skb: the socket buffer to send
+ * @dst_pid: destination process IF
+ * @flag: flags
+ * @app_id: application id
+ * @mcgroup_id: Multicast group ID
+ *
+ * This function is common wrapper to send unicast events to different
+ * user space applications. This internally used broadcast API with multicast
+ * group mcgrp_id. This wrapper serves as a common API in both
+ * new generic netlink infra and legacy implementation.
+ *
+ * return: zero on success, error code otherwise
+ */
+int nl_srv_ucast(struct sk_buff *skb, int dst_pid, int flag,
+ int app_id, int mcgroup_id)
+{
+ struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
+ void *msg = NLMSG_DATA(nlh);
+ uint32_t msg_len = nlmsg_len(nlh);
+ uint8_t *tempbuf;
+ int status;
+
+ tempbuf = (uint8_t *)vos_mem_malloc(msg_len);
+ vos_mem_copy(tempbuf, msg, msg_len);
+ status = send_msg_to_cld80211(mcgroup_id, dst_pid, app_id,
+ tempbuf, msg_len);
+ if (status) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+ "send msg to cld80211 fails for app id %d", app_id);
+ return -EPERM;
+ }
+
+ dev_kfree_skb(skb);
+ vos_mem_free(tempbuf);
+ return 0;
+}
+
+#else
/*
* Unicast the message to the process in user space identfied
* by the dst-pid
@@ -183,10 +376,6 @@
return err;
}
-/*
- * Broadcast the message. Broadcast will return an error if
- * there are no listeners
- */
int nl_srv_bcast(struct sk_buff *skb)
{
int err = 0;
@@ -215,6 +404,8 @@
return err;
}
+#endif
+
/*
* Processes the Netlink socket input queue.
* Dequeue skb's from the socket input queue and process
diff --git a/drivers/staging/qcacld-2.0/CORE/SVC/src/ptt/wlan_ptt_sock_svc.c b/drivers/staging/qcacld-2.0/CORE/SVC/src/ptt/wlan_ptt_sock_svc.c
index a96db58..2ed0580 100644
--- a/drivers/staging/qcacld-2.0/CORE/SVC/src/ptt/wlan_ptt_sock_svc.c
+++ b/drivers/staging/qcacld-2.0/CORE/SVC/src/ptt/wlan_ptt_sock_svc.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -40,6 +40,12 @@
#include <vos_types.h>
#include <vos_trace.h>
#include <wlan_hdd_ftm.h>
+#ifdef CNSS_GENL
+#include <net/cnss_nl.h>
+#else
+
+static struct hdd_context_s *hdd_ctx_handle;
+#endif
#define PTT_SOCK_DEBUG
#ifdef PTT_SOCK_DEBUG
@@ -48,7 +54,6 @@
#define PTT_TRACE(level, args...)
#endif
// Global variables
-static struct hdd_context_s *pAdapterHandle;
#ifdef PTT_SOCK_DEBUG_VERBOSE
//Utility function to perform a hex dump
@@ -64,6 +69,46 @@
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"\n");
}
#endif
+
+/**
+ * nl_srv_ucast_ptt() - Wrapper function to send ucast msgs to PTT
+ * @skb: sk buffer pointer
+ * @dst_pid: Destination PID
+ * @flag: flags
+ *
+ * Sends the ucast message to PTT with generic nl socket if CNSS_GENL
+ * is enabled. Else, use the legacy netlink socket to send.
+ *
+ * Return: zero on success, error code otherwise
+ */
+static int nl_srv_ucast_ptt(struct sk_buff *skb, int dst_pid, int flag)
+{
+#ifdef CNSS_GENL
+ return nl_srv_ucast(skb, dst_pid, flag, ANI_NL_MSG_PUMAC,
+ CLD80211_MCGRP_DIAG_EVENTS);
+#else
+ return nl_srv_ucast(skb, dst_pid, flag);
+#endif
+}
+
+/**
+ * nl_srv_bcast_ptt() - Wrapper function to send bcast msgs to DIAG mcast group
+ * @skb: sk buffer pointer
+ *
+ * Sends the bcast message to DIAG multicast group with generic nl socket
+ * if CNSS_GENL is enabled. Else, use the legacy netlink socket to send.
+ *
+ * Return: zero on success, error code otherwise
+ */
+static int nl_srv_bcast_ptt(struct sk_buff *skb)
+{
+#ifdef CNSS_GENL
+ return nl_srv_bcast(skb, CLD80211_MCGRP_DIAG_EVENTS, ANI_NL_MSG_PUMAC);
+#else
+ return nl_srv_bcast(skb);
+#endif
+}
+
//Utility function to send a netlink message to an application in user space
int ptt_sock_send_msg_to_app(tAniHdr *wmsg, int radio, int src_mod, int pid)
{
@@ -102,9 +147,9 @@
#endif
if (pid != -1) {
- err = nl_srv_ucast(skb, pid, MSG_DONTWAIT);
+ err = nl_srv_ucast_ptt(skb, pid, MSG_DONTWAIT);
} else {
- err = nl_srv_bcast(skb);
+ err = nl_srv_bcast_ptt(skb);
}
if (err) {
PTT_TRACE(VOS_TRACE_LEVEL_INFO,
@@ -113,6 +158,8 @@
}
return err;
}
+
+#ifndef CNSS_GENL
/*
* Process tregisteration request and send registration response messages
* to the PTT Socket App in user space
@@ -128,7 +175,7 @@
rspmsg.regReq.type = reg_req->type;
/* Save the pid */
- pAdapterHandle->ptt_pid = reg_req->pid;
+ hdd_ctx_handle->ptt_pid = reg_req->pid;
rspmsg.regReq.pid= reg_req->pid;
rspmsg.wniHdr.type = cpu_to_be16(ANI_MSG_APP_REG_RSP);
rspmsg.wniHdr.length = cpu_to_be16(sizeof(rspmsg));
@@ -139,6 +186,7 @@
__func__, reg_req->pid);
}
}
+
/*
* Process all the messages from the PTT Socket App in user space
*/
@@ -181,12 +229,72 @@
}
return 0;
}
-int ptt_sock_activate_svc(void *pAdapter)
+#endif
+
+#ifdef CNSS_GENL
+/**
+ * ptt_cmd_handler() - Handler function for PTT commands
+ * @data: Data to be parsed
+ * @data_len: Length of the data received
+ * @ctx: Registered context reference
+ * @pid: Process id of the user space application
+ *
+ * This function handles the command from PTT user space application
+ *
+ * Return: None
+ */
+static void ptt_cmd_handler(const void *data, int data_len, void *ctx, int pid)
{
- pAdapterHandle = (struct hdd_context_s*)pAdapter;
- pAdapterHandle->ptt_pid = INVALID_PID;
+ ptt_app_reg_req *payload;
+ struct nlattr *tb[CLD80211_ATTR_MAX + 1];
+
+ if (nla_parse(tb, CLD80211_ATTR_MAX, data, data_len, NULL)) {
+ PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "Invalid ATTR");
+ return;
+ }
+
+ if (!tb[CLD80211_ATTR_DATA]) {
+ PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "attr ATTR_DATA failed");
+ return;
+ }
+
+ payload = (ptt_app_reg_req *)(nla_data(tb[CLD80211_ATTR_DATA]));
+ switch (payload->wmsg.type) {
+ case ANI_MSG_APP_REG_REQ:
+ ptt_sock_send_msg_to_app(&payload->wmsg, payload->radio,
+ ANI_NL_MSG_PUMAC, pid);
+ break;
+ default:
+ PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "Unknown msg type %d",
+ payload->wmsg.type);
+ break;
+ }
+}
+
+/**
+ * ptt_sock_activate_svc() - API to register PTT/PUMAC command handler
+ * @pAdapter: Pointer to adapter
+ *
+ * API to register the PTT/PUMAC command handlers. Argument @pAdapter
+ * is sent for prototype compatibility between new genl and legacy
+ * implementation
+ *
+ * Return: 0
+ */
+int ptt_sock_activate_svc(void *hdd_ctx)
+{
+ register_cld_cmd_cb(ANI_NL_MSG_PUMAC, ptt_cmd_handler, NULL);
+ register_cld_cmd_cb(ANI_NL_MSG_PTT, ptt_cmd_handler, NULL);
+ return 0;
+}
+#else
+int ptt_sock_activate_svc(void *hdd_ctx)
+{
+ hdd_ctx_handle = (struct hdd_context_s *)hdd_ctx;
+ hdd_ctx_handle->ptt_pid = INVALID_PID;
nl_srv_register(ANI_NL_MSG_PUMAC, ptt_sock_rx_nlink_msg);
nl_srv_register(ANI_NL_MSG_PTT, ptt_sock_rx_nlink_msg);
return 0;
}
+#endif
#endif // PTT_SOCK_SVC_ENABLE
diff --git a/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/dot11f.c b/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/dot11f.c
index c19e9c0..ae53ece 100644
--- a/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/dot11f.c
+++ b/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/dot11f.c
@@ -2142,10 +2142,86 @@
#define SigIeTSPEC ( 0x0016 )
+tANI_U32 dot11fUnpackIeVHTCaps(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEVHTCaps *pDst)
+{
+ tANI_U32 status = DOT11F_PARSE_SUCCESS;
+ tANI_U32 tmp20__;
+ tANI_U16 tmp21__;
+ tANI_U16 tmp22__;
+ (void) pBuf; (void)ielen; /* Shutup the compiler */
+ if (pDst->present) status = DOT11F_DUPLICATE_IE;
+ pDst->present = 1;
+ framesntohl(pCtx, &tmp20__, pBuf, 0);
+ pBuf += 4;
+ ielen -= 4;
+ pDst->maxMPDULen = tmp20__ >> 0 & 0x3;
+ pDst->supportedChannelWidthSet = tmp20__ >> 2 & 0x3;
+ pDst->ldpcCodingCap = tmp20__ >> 4 & 0x1;
+ pDst->shortGI80MHz = tmp20__ >> 5 & 0x1;
+ pDst->shortGI160and80plus80MHz = tmp20__ >> 6 & 0x1;
+ pDst->txSTBC = tmp20__ >> 7 & 0x1;
+ pDst->rxSTBC = tmp20__ >> 8 & 0x7;
+ pDst->suBeamFormerCap = tmp20__ >> 11 & 0x1;
+ pDst->suBeamformeeCap = tmp20__ >> 12 & 0x1;
+ pDst->csnofBeamformerAntSup = tmp20__ >> 13 & 0x7;
+ pDst->numSoundingDim = tmp20__ >> 16 & 0x7;
+ pDst->muBeamformerCap = tmp20__ >> 19 & 0x1;
+ pDst->muBeamformeeCap = tmp20__ >> 20 & 0x1;
+ pDst->vhtTXOPPS = tmp20__ >> 21 & 0x1;
+ pDst->htcVHTCap = tmp20__ >> 22 & 0x1;
+ pDst->maxAMPDULenExp = tmp20__ >> 23 & 0x7;
+ pDst->vhtLinkAdaptCap = tmp20__ >> 26 & 0x3;
+ pDst->rxAntPattern = tmp20__ >> 28 & 0x1;
+ pDst->txAntPattern = tmp20__ >> 29 & 0x1;
+ pDst->reserved1 = tmp20__ >> 30 & 0x3;
+ framesntohs(pCtx, &pDst->rxMCSMap, pBuf, 0);
+ pBuf += 2;
+ ielen -= (tANI_U8)2;
+ framesntohs(pCtx, &tmp21__, pBuf, 0);
+ pBuf += 2;
+ ielen -= 2;
+ pDst->rxHighSupDataRate = tmp21__ >> 0 & 0x1fff;
+ pDst->reserved2 = tmp21__ >> 13 & 0x7;
+ framesntohs(pCtx, &pDst->txMCSMap, pBuf, 0);
+ pBuf += 2;
+ ielen -= (tANI_U8)2;
+ framesntohs(pCtx, &tmp22__, pBuf, 0);
+ pDst->txSupDataRate = tmp22__ >> 0 & 0x1fff;
+ pDst->reserved3 = tmp22__ >> 13 & 0x7;
+ (void)pCtx;
+ return status;
+} /* End dot11fUnpackIeVHTCaps. */
+
+#define SigIeVHTCaps ( 0x0017 )
+
+
+tANI_U32 dot11fUnpackIeVHTOperation(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEVHTOperation *pDst)
+{
+ tANI_U32 status = DOT11F_PARSE_SUCCESS;
+ (void) pBuf; (void)ielen; /* Shutup the compiler */
+ if (pDst->present) status = DOT11F_DUPLICATE_IE;
+ pDst->present = 1;
+ pDst->chanWidth = *pBuf;
+ pBuf += 1;
+ ielen -= (tANI_U8)1;
+ pDst->chanCenterFreqSeg1 = *pBuf;
+ pBuf += 1;
+ ielen -= (tANI_U8)1;
+ pDst->chanCenterFreqSeg2 = *pBuf;
+ pBuf += 1;
+ ielen -= (tANI_U8)1;
+ framesntohs(pCtx, &pDst->basicMCSSet, pBuf, 0);
+ (void)pCtx;
+ return status;
+} /* End dot11fUnpackIeVHTOperation. */
+
+#define SigIeVHTOperation ( 0x0018 )
+
+
tANI_U32 dot11fUnpackIeWMMSchedule(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWMMSchedule *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U16 tmp20__;
+ tANI_U16 tmp23__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
@@ -2157,13 +2233,13 @@
pDst->present = 0;
return ( status | DOT11F_BAD_FIXED_VALUE );
}
- framesntohs(pCtx, &tmp20__, pBuf, 0);
+ framesntohs(pCtx, &tmp23__, pBuf, 0);
pBuf += 2;
ielen -= 2;
- pDst->aggregation = tmp20__ >> 0 & 0x1;
- pDst->tsid = tmp20__ >> 1 & 0xf;
- pDst->direction = tmp20__ >> 5 & 0x3;
- pDst->reserved = tmp20__ >> 7 & 0x1ff;
+ pDst->aggregation = tmp23__ >> 0 & 0x1;
+ pDst->tsid = tmp23__ >> 1 & 0xf;
+ pDst->direction = tmp23__ >> 5 & 0x3;
+ pDst->reserved = tmp23__ >> 7 & 0x1ff;
framesntohl(pCtx, &pDst->service_start_time, pBuf, 0);
pBuf += 4;
ielen -= (tANI_U8)4;
@@ -2178,7 +2254,7 @@
return status;
} /* End dot11fUnpackIeWMMSchedule. */
-#define SigIeWMMSchedule ( 0x0017 )
+#define SigIeWMMSchedule ( 0x0019 )
tANI_U32 dot11fUnpackIeWMMTCLAS(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWMMTCLAS *pDst)
@@ -2275,7 +2351,7 @@
return status;
} /* End dot11fUnpackIeWMMTCLAS. */
-#define SigIeWMMTCLAS ( 0x0018 )
+#define SigIeWMMTCLAS ( 0x001a )
tANI_U32 dot11fUnpackIeWMMTCLASPROC(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWMMTCLASPROC *pDst)
@@ -2297,7 +2373,7 @@
return status;
} /* End dot11fUnpackIeWMMTCLASPROC. */
-#define SigIeWMMTCLASPROC ( 0x0019 )
+#define SigIeWMMTCLASPROC ( 0x001b )
tANI_U32 dot11fUnpackIeWMMTSDelay(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWMMTSDelay *pDst)
@@ -2319,15 +2395,15 @@
return status;
} /* End dot11fUnpackIeWMMTSDelay. */
-#define SigIeWMMTSDelay ( 0x001a )
+#define SigIeWMMTSDelay ( 0x001c )
tANI_U32 dot11fUnpackIeWMMTSPEC(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWMMTSPEC *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U16 tmp21__;
- tANI_U8 tmp22__;
- tANI_U16 tmp23__;
+ tANI_U16 tmp24__;
+ tANI_U8 tmp25__;
+ tANI_U16 tmp26__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
@@ -2339,27 +2415,27 @@
pDst->present = 0;
return ( status | DOT11F_BAD_FIXED_VALUE );
}
- framesntohs(pCtx, &tmp21__, pBuf, 0);
+ framesntohs(pCtx, &tmp24__, pBuf, 0);
pBuf += 2;
ielen -= 2;
- pDst->traffic_type = tmp21__ >> 0 & 0x1;
- pDst->tsid = tmp21__ >> 1 & 0xf;
- pDst->direction = tmp21__ >> 5 & 0x3;
- pDst->access_policy = tmp21__ >> 7 & 0x3;
- pDst->aggregation = tmp21__ >> 9 & 0x1;
- pDst->psb = tmp21__ >> 10 & 0x1;
- pDst->user_priority = tmp21__ >> 11 & 0x7;
- pDst->tsinfo_ack_pol = tmp21__ >> 14 & 0x3;
- tmp22__ = *pBuf;
+ pDst->traffic_type = tmp24__ >> 0 & 0x1;
+ pDst->tsid = tmp24__ >> 1 & 0xf;
+ pDst->direction = tmp24__ >> 5 & 0x3;
+ pDst->access_policy = tmp24__ >> 7 & 0x3;
+ pDst->aggregation = tmp24__ >> 9 & 0x1;
+ pDst->psb = tmp24__ >> 10 & 0x1;
+ pDst->user_priority = tmp24__ >> 11 & 0x7;
+ pDst->tsinfo_ack_pol = tmp24__ >> 14 & 0x3;
+ tmp25__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->tsinfo_rsvd = tmp22__ >> 0 & 0x7f;
- pDst->burst_size_defn = tmp22__ >> 7 & 0x1;
- framesntohs(pCtx, &tmp23__, pBuf, 0);
+ pDst->tsinfo_rsvd = tmp25__ >> 0 & 0x7f;
+ pDst->burst_size_defn = tmp25__ >> 7 & 0x1;
+ framesntohs(pCtx, &tmp26__, pBuf, 0);
pBuf += 2;
ielen -= 2;
- pDst->size = tmp23__ >> 0 & 0x7fff;
- pDst->fixed = tmp23__ >> 15 & 0x1;
+ pDst->size = tmp26__ >> 0 & 0x7fff;
+ pDst->fixed = tmp26__ >> 15 & 0x1;
framesntohs(pCtx, &pDst->max_msdu_size, pBuf, 0);
pBuf += 2;
ielen -= (tANI_U8)2;
@@ -2404,7 +2480,7 @@
return status;
} /* End dot11fUnpackIeWMMTSPEC. */
-#define SigIeWMMTSPEC ( 0x001b )
+#define SigIeWMMTSPEC ( 0x001d )
tANI_U32 dot11fUnpackIeWiderBWChanSwitchAnn(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWiderBWChanSwitchAnn *pDst)
@@ -2424,7 +2500,7 @@
return status;
} /* End dot11fUnpackIeWiderBWChanSwitchAnn. */
-#define SigIeWiderBWChanSwitchAnn ( 0x001c )
+#define SigIeWiderBWChanSwitchAnn ( 0x001e )
tANI_U32 dot11fUnpackIeAID(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEAID *pDst)
@@ -2438,7 +2514,7 @@
return status;
} /* End dot11fUnpackIeAID. */
-#define SigIeAID ( 0x001d )
+#define SigIeAID ( 0x001f )
tANI_U32 dot11fUnpackIeCFParams(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIECFParams *pDst)
@@ -2461,7 +2537,7 @@
return status;
} /* End dot11fUnpackIeCFParams. */
-#define SigIeCFParams ( 0x001e )
+#define SigIeCFParams ( 0x0020 )
tANI_U32 dot11fUnpackIeChallengeText(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEChallengeText *pDst)
@@ -2481,7 +2557,7 @@
return status;
} /* End dot11fUnpackIeChallengeText. */
-#define SigIeChallengeText ( 0x001f )
+#define SigIeChallengeText ( 0x0021 )
tANI_U32 dot11fUnpackIeChanSwitchAnn(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEChanSwitchAnn *pDst)
@@ -2501,7 +2577,7 @@
return status;
} /* End dot11fUnpackIeChanSwitchAnn. */
-#define SigIeChanSwitchAnn ( 0x0020 )
+#define SigIeChanSwitchAnn ( 0x0022 )
static const tFFDefn FFS_ChannelSwitchWrapper[ ] = {
@@ -2530,7 +2606,7 @@
return status;
} /* End dot11fUnpackIeChannelSwitchWrapper. */
-#define SigIeChannelSwitchWrapper ( 0x0021 )
+#define SigIeChannelSwitchWrapper ( 0x0023 )
tANI_U32 dot11fUnpackIeCountry(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIECountry *pDst)
@@ -2561,23 +2637,23 @@
return status;
} /* End dot11fUnpackIeCountry. */
-#define SigIeCountry ( 0x0022 )
+#define SigIeCountry ( 0x0024 )
-#define SigIeDSParams ( 0x0023 )
+#define SigIeDSParams ( 0x0025 )
tANI_U32 dot11fUnpackIeEDCAParamSet(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEEDCAParamSet *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp24__;
- tANI_U8 tmp25__;
- tANI_U8 tmp26__;
tANI_U8 tmp27__;
tANI_U8 tmp28__;
tANI_U8 tmp29__;
tANI_U8 tmp30__;
tANI_U8 tmp31__;
+ tANI_U8 tmp32__;
+ tANI_U8 tmp33__;
+ tANI_U8 tmp34__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
@@ -2587,88 +2663,88 @@
pDst->reserved = *pBuf;
pBuf += 1;
ielen -= (tANI_U8)1;
- tmp24__ = *pBuf;
- pBuf += 1;
- ielen -= 1;
- pDst->acbe_aifsn = tmp24__ >> 0 & 0xf;
- pDst->acbe_acm = tmp24__ >> 4 & 0x1;
- pDst->acbe_aci = tmp24__ >> 5 & 0x3;
- pDst->unused1 = tmp24__ >> 7 & 0x1;
- tmp25__ = *pBuf;
- pBuf += 1;
- ielen -= 1;
- pDst->acbe_acwmin = tmp25__ >> 0 & 0xf;
- pDst->acbe_acwmax = tmp25__ >> 4 & 0xf;
- framesntohs(pCtx, &pDst->acbe_txoplimit, pBuf, 0);
- pBuf += 2;
- ielen -= (tANI_U8)2;
- tmp26__ = *pBuf;
- pBuf += 1;
- ielen -= 1;
- pDst->acbk_aifsn = tmp26__ >> 0 & 0xf;
- pDst->acbk_acm = tmp26__ >> 4 & 0x1;
- pDst->acbk_aci = tmp26__ >> 5 & 0x3;
- pDst->unused2 = tmp26__ >> 7 & 0x1;
tmp27__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->acbk_acwmin = tmp27__ >> 0 & 0xf;
- pDst->acbk_acwmax = tmp27__ >> 4 & 0xf;
- framesntohs(pCtx, &pDst->acbk_txoplimit, pBuf, 0);
- pBuf += 2;
- ielen -= (tANI_U8)2;
+ pDst->acbe_aifsn = tmp27__ >> 0 & 0xf;
+ pDst->acbe_acm = tmp27__ >> 4 & 0x1;
+ pDst->acbe_aci = tmp27__ >> 5 & 0x3;
+ pDst->unused1 = tmp27__ >> 7 & 0x1;
tmp28__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->acvi_aifsn = tmp28__ >> 0 & 0xf;
- pDst->acvi_acm = tmp28__ >> 4 & 0x1;
- pDst->acvi_aci = tmp28__ >> 5 & 0x3;
- pDst->unused3 = tmp28__ >> 7 & 0x1;
+ pDst->acbe_acwmin = tmp28__ >> 0 & 0xf;
+ pDst->acbe_acwmax = tmp28__ >> 4 & 0xf;
+ framesntohs(pCtx, &pDst->acbe_txoplimit, pBuf, 0);
+ pBuf += 2;
+ ielen -= (tANI_U8)2;
tmp29__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->acvi_acwmin = tmp29__ >> 0 & 0xf;
- pDst->acvi_acwmax = tmp29__ >> 4 & 0xf;
- framesntohs(pCtx, &pDst->acvi_txoplimit, pBuf, 0);
- pBuf += 2;
- ielen -= (tANI_U8)2;
+ pDst->acbk_aifsn = tmp29__ >> 0 & 0xf;
+ pDst->acbk_acm = tmp29__ >> 4 & 0x1;
+ pDst->acbk_aci = tmp29__ >> 5 & 0x3;
+ pDst->unused2 = tmp29__ >> 7 & 0x1;
tmp30__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->acvo_aifsn = tmp30__ >> 0 & 0xf;
- pDst->acvo_acm = tmp30__ >> 4 & 0x1;
- pDst->acvo_aci = tmp30__ >> 5 & 0x3;
- pDst->unused4 = tmp30__ >> 7 & 0x1;
+ pDst->acbk_acwmin = tmp30__ >> 0 & 0xf;
+ pDst->acbk_acwmax = tmp30__ >> 4 & 0xf;
+ framesntohs(pCtx, &pDst->acbk_txoplimit, pBuf, 0);
+ pBuf += 2;
+ ielen -= (tANI_U8)2;
tmp31__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->acvo_acwmin = tmp31__ >> 0 & 0xf;
- pDst->acvo_acwmax = tmp31__ >> 4 & 0xf;
+ pDst->acvi_aifsn = tmp31__ >> 0 & 0xf;
+ pDst->acvi_acm = tmp31__ >> 4 & 0x1;
+ pDst->acvi_aci = tmp31__ >> 5 & 0x3;
+ pDst->unused3 = tmp31__ >> 7 & 0x1;
+ tmp32__ = *pBuf;
+ pBuf += 1;
+ ielen -= 1;
+ pDst->acvi_acwmin = tmp32__ >> 0 & 0xf;
+ pDst->acvi_acwmax = tmp32__ >> 4 & 0xf;
+ framesntohs(pCtx, &pDst->acvi_txoplimit, pBuf, 0);
+ pBuf += 2;
+ ielen -= (tANI_U8)2;
+ tmp33__ = *pBuf;
+ pBuf += 1;
+ ielen -= 1;
+ pDst->acvo_aifsn = tmp33__ >> 0 & 0xf;
+ pDst->acvo_acm = tmp33__ >> 4 & 0x1;
+ pDst->acvo_aci = tmp33__ >> 5 & 0x3;
+ pDst->unused4 = tmp33__ >> 7 & 0x1;
+ tmp34__ = *pBuf;
+ pBuf += 1;
+ ielen -= 1;
+ pDst->acvo_acwmin = tmp34__ >> 0 & 0xf;
+ pDst->acvo_acwmax = tmp34__ >> 4 & 0xf;
framesntohs(pCtx, &pDst->acvo_txoplimit, pBuf, 0);
(void)pCtx;
return status;
} /* End dot11fUnpackIeEDCAParamSet. */
-#define SigIeEDCAParamSet ( 0x0024 )
+#define SigIeEDCAParamSet ( 0x0026 )
tANI_U32 dot11fUnpackIeERPInfo(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEERPInfo *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp32__;
+ tANI_U8 tmp35__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
- tmp32__ = *pBuf;
- pDst->non_erp_present = tmp32__ >> 0 & 0x1;
- pDst->use_prot = tmp32__ >> 1 & 0x1;
- pDst->barker_preamble = tmp32__ >> 2 & 0x1;
- pDst->unused = tmp32__ >> 3 & 0x1f;
+ tmp35__ = *pBuf;
+ pDst->non_erp_present = tmp35__ >> 0 & 0x1;
+ pDst->use_prot = tmp35__ >> 1 & 0x1;
+ pDst->barker_preamble = tmp35__ >> 2 & 0x1;
+ pDst->unused = tmp35__ >> 3 & 0x1f;
(void)pCtx;
return status;
} /* End dot11fUnpackIeERPInfo. */
-#define SigIeERPInfo ( 0x0025 )
+#define SigIeERPInfo ( 0x0027 )
tANI_U32 dot11fUnpackIeESECckmOpaque(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEESECckmOpaque *pDst)
@@ -2688,27 +2764,27 @@
return status;
} /* End dot11fUnpackIeESECckmOpaque. */
-#define SigIeESECckmOpaque ( 0x0026 )
+#define SigIeESECckmOpaque ( 0x0028 )
tANI_U32 dot11fUnpackIeESERadMgmtCap(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEESERadMgmtCap *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp33__;
+ tANI_U8 tmp36__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
pDst->mgmt_state = *pBuf;
pBuf += 1;
ielen -= (tANI_U8)1;
- tmp33__ = *pBuf;
- pDst->mbssid_mask = tmp33__ >> 0 & 0x7;
- pDst->reserved = tmp33__ >> 3 & 0x1f;
+ tmp36__ = *pBuf;
+ pDst->mbssid_mask = tmp36__ >> 0 & 0x7;
+ pDst->reserved = tmp36__ >> 3 & 0x1f;
(void)pCtx;
return status;
} /* End dot11fUnpackIeESERadMgmtCap. */
-#define SigIeESERadMgmtCap ( 0x0027 )
+#define SigIeESERadMgmtCap ( 0x0029 )
tANI_U32 dot11fUnpackIeESETrafStrmMet(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEESETrafStrmMet *pDst)
@@ -2728,7 +2804,7 @@
return status;
} /* End dot11fUnpackIeESETrafStrmMet. */
-#define SigIeESETrafStrmMet ( 0x0028 )
+#define SigIeESETrafStrmMet ( 0x002a )
tANI_U32 dot11fUnpackIeESETrafStrmRateSet(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEESETrafStrmRateSet *pDst)
@@ -2751,7 +2827,7 @@
return status;
} /* End dot11fUnpackIeESETrafStrmRateSet. */
-#define SigIeESETrafStrmRateSet ( 0x0029 )
+#define SigIeESETrafStrmRateSet ( 0x002b )
tANI_U32 dot11fUnpackIeESETxmitPower(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEESETxmitPower *pDst)
@@ -2768,7 +2844,7 @@
return status;
} /* End dot11fUnpackIeESETxmitPower. */
-#define SigIeESETxmitPower ( 0x002a )
+#define SigIeESETxmitPower ( 0x002c )
tANI_U32 dot11fUnpackIeESEVersion(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEESEVersion *pDst)
@@ -2782,7 +2858,7 @@
return status;
} /* End dot11fUnpackIeESEVersion. */
-#define SigIeESEVersion ( 0x002b )
+#define SigIeESEVersion ( 0x002d )
tANI_U32 dot11fUnpackIeExtCap(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEExtCap *pDst)
@@ -2807,7 +2883,7 @@
return status;
} /* End dot11fUnpackIeExtCap. */
-#define SigIeExtCap ( 0x002c )
+#define SigIeExtCap ( 0x002e )
tANI_U32 dot11fUnpackIeExtSuppRates(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEExtSuppRates *pDst)
@@ -2835,7 +2911,7 @@
return status;
} /* End dot11fUnpackIeExtSuppRates. */
-#define SigIeExtSuppRates ( 0x002d )
+#define SigIeExtSuppRates ( 0x002f )
tANI_U32 dot11fUnpackIeFHParamSet(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEFHParamSet *pDst)
@@ -2858,7 +2934,7 @@
return status;
} /* End dot11fUnpackIeFHParamSet. */
-#define SigIeFHParamSet ( 0x002e )
+#define SigIeFHParamSet ( 0x0030 )
tANI_U32 dot11fUnpackIeFHParams(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEFHParams *pDst)
@@ -2875,7 +2951,7 @@
return status;
} /* End dot11fUnpackIeFHParams. */
-#define SigIeFHParams ( 0x002f )
+#define SigIeFHParams ( 0x0031 )
tANI_U32 dot11fUnpackIeFHPattTable(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEFHPattTable *pDst)
@@ -2907,7 +2983,7 @@
return status;
} /* End dot11fUnpackIeFHPattTable. */
-#define SigIeFHPattTable ( 0x0030 )
+#define SigIeFHPattTable ( 0x0032 )
static const tFFDefn FFS_FTInfo[ ] = {
@@ -2925,15 +3001,15 @@
tANI_U32 dot11fUnpackIeFTInfo(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEFTInfo *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U16 tmp34__;
+ tANI_U16 tmp37__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
- framesntohs(pCtx, &tmp34__, pBuf, 0);
+ framesntohs(pCtx, &tmp37__, pBuf, 0);
pBuf += 2;
ielen -= 2;
- pDst->reserved = tmp34__ >> 0 & 0xff;
- pDst->IECount = tmp34__ >> 8 & 0xff;
+ pDst->reserved = tmp37__ >> 0 & 0xff;
+ pDst->IECount = tmp37__ >> 8 & 0xff;
DOT11F_MEMCPY(pCtx, pDst->MIC, pBuf, 16);
pBuf += 16;
ielen -= (tANI_U8)16;
@@ -2954,28 +3030,28 @@
return status;
} /* End dot11fUnpackIeFTInfo. */
-#define SigIeFTInfo ( 0x0031 )
+#define SigIeFTInfo ( 0x0033 )
tANI_U32 dot11fUnpackIeHT2040BSSCoexistence(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEHT2040BSSCoexistence *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp35__;
+ tANI_U8 tmp38__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
- tmp35__ = *pBuf;
- pDst->infoRequest = tmp35__ >> 0 & 0x1;
- pDst->fortyMHzIntolerant = tmp35__ >> 1 & 0x1;
- pDst->twentyMHzBssWidthReq = tmp35__ >> 2 & 0x1;
- pDst->obssScanExemptionReq = tmp35__ >> 3 & 0x1;
- pDst->obssScanExemptionGrant = tmp35__ >> 4 & 0x1;
- pDst->unused = tmp35__ >> 5 & 0x7;
+ tmp38__ = *pBuf;
+ pDst->infoRequest = tmp38__ >> 0 & 0x1;
+ pDst->fortyMHzIntolerant = tmp38__ >> 1 & 0x1;
+ pDst->twentyMHzBssWidthReq = tmp38__ >> 2 & 0x1;
+ pDst->obssScanExemptionReq = tmp38__ >> 3 & 0x1;
+ pDst->obssScanExemptionGrant = tmp38__ >> 4 & 0x1;
+ pDst->unused = tmp38__ >> 5 & 0x7;
(void)pCtx;
return status;
} /* End dot11fUnpackIeHT2040BSSCoexistence. */
-#define SigIeHT2040BSSCoexistence ( 0x0032 )
+#define SigIeHT2040BSSCoexistence ( 0x0034 )
tANI_U32 dot11fUnpackIeHT2040BSSIntolerantReport(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEHT2040BSSIntolerantReport *pDst)
@@ -2998,84 +3074,84 @@
return status;
} /* End dot11fUnpackIeHT2040BSSIntolerantReport. */
-#define SigIeHT2040BSSIntolerantReport ( 0x0033 )
+#define SigIeHT2040BSSIntolerantReport ( 0x0035 )
tANI_U32 dot11fUnpackIeHTCaps(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEHTCaps *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U16 tmp36__;
- tANI_U8 tmp37__;
- tANI_U16 tmp38__;
- tANI_U32 tmp39__;
+ tANI_U16 tmp39__;
tANI_U8 tmp40__;
+ tANI_U16 tmp41__;
+ tANI_U32 tmp42__;
+ tANI_U8 tmp43__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
- framesntohs(pCtx, &tmp36__, pBuf, 0);
+ framesntohs(pCtx, &tmp39__, pBuf, 0);
pBuf += 2;
ielen -= 2;
- pDst->advCodingCap = tmp36__ >> 0 & 0x1;
- pDst->supportedChannelWidthSet = tmp36__ >> 1 & 0x1;
- pDst->mimoPowerSave = tmp36__ >> 2 & 0x3;
- pDst->greenField = tmp36__ >> 4 & 0x1;
- pDst->shortGI20MHz = tmp36__ >> 5 & 0x1;
- pDst->shortGI40MHz = tmp36__ >> 6 & 0x1;
- pDst->txSTBC = tmp36__ >> 7 & 0x1;
- pDst->rxSTBC = tmp36__ >> 8 & 0x3;
- pDst->delayedBA = tmp36__ >> 10 & 0x1;
- pDst->maximalAMSDUsize = tmp36__ >> 11 & 0x1;
- pDst->dsssCckMode40MHz = tmp36__ >> 12 & 0x1;
- pDst->psmp = tmp36__ >> 13 & 0x1;
- pDst->stbcControlFrame = tmp36__ >> 14 & 0x1;
- pDst->lsigTXOPProtection = tmp36__ >> 15 & 0x1;
- tmp37__ = *pBuf;
- pBuf += 1;
- ielen -= 1;
- pDst->maxRxAMPDUFactor = tmp37__ >> 0 & 0x3;
- pDst->mpduDensity = tmp37__ >> 2 & 0x7;
- pDst->reserved1 = tmp37__ >> 5 & 0x7;
- DOT11F_MEMCPY(pCtx, pDst->supportedMCSSet, pBuf, 16);
- pBuf += 16;
- ielen -= (tANI_U8)16;
- framesntohs(pCtx, &tmp38__, pBuf, 0);
- pBuf += 2;
- ielen -= 2;
- pDst->pco = tmp38__ >> 0 & 0x1;
- pDst->transitionTime = tmp38__ >> 1 & 0x3;
- pDst->reserved2 = tmp38__ >> 3 & 0x1f;
- pDst->mcsFeedback = tmp38__ >> 8 & 0x3;
- pDst->reserved3 = tmp38__ >> 10 & 0x3f;
- framesntohl(pCtx, &tmp39__, pBuf, 0);
- pBuf += 4;
- ielen -= 4;
- pDst->txBF = tmp39__ >> 0 & 0x1;
- pDst->rxStaggeredSounding = tmp39__ >> 1 & 0x1;
- pDst->txStaggeredSounding = tmp39__ >> 2 & 0x1;
- pDst->rxZLF = tmp39__ >> 3 & 0x1;
- pDst->txZLF = tmp39__ >> 4 & 0x1;
- pDst->implicitTxBF = tmp39__ >> 5 & 0x1;
- pDst->calibration = tmp39__ >> 6 & 0x3;
- pDst->explicitCSITxBF = tmp39__ >> 8 & 0x1;
- pDst->explicitUncompressedSteeringMatrix = tmp39__ >> 9 & 0x1;
- pDst->explicitBFCSIFeedback = tmp39__ >> 10 & 0x7;
- pDst->explicitUncompressedSteeringMatrixFeedback = tmp39__ >> 13 & 0x7;
- pDst->explicitCompressedSteeringMatrixFeedback = tmp39__ >> 16 & 0x7;
- pDst->csiNumBFAntennae = tmp39__ >> 19 & 0x3;
- pDst->uncompressedSteeringMatrixBFAntennae = tmp39__ >> 21 & 0x3;
- pDst->compressedSteeringMatrixBFAntennae = tmp39__ >> 23 & 0x3;
- pDst->reserved4 = tmp39__ >> 25 & 0x7f;
+ pDst->advCodingCap = tmp39__ >> 0 & 0x1;
+ pDst->supportedChannelWidthSet = tmp39__ >> 1 & 0x1;
+ pDst->mimoPowerSave = tmp39__ >> 2 & 0x3;
+ pDst->greenField = tmp39__ >> 4 & 0x1;
+ pDst->shortGI20MHz = tmp39__ >> 5 & 0x1;
+ pDst->shortGI40MHz = tmp39__ >> 6 & 0x1;
+ pDst->txSTBC = tmp39__ >> 7 & 0x1;
+ pDst->rxSTBC = tmp39__ >> 8 & 0x3;
+ pDst->delayedBA = tmp39__ >> 10 & 0x1;
+ pDst->maximalAMSDUsize = tmp39__ >> 11 & 0x1;
+ pDst->dsssCckMode40MHz = tmp39__ >> 12 & 0x1;
+ pDst->psmp = tmp39__ >> 13 & 0x1;
+ pDst->stbcControlFrame = tmp39__ >> 14 & 0x1;
+ pDst->lsigTXOPProtection = tmp39__ >> 15 & 0x1;
tmp40__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->antennaSelection = tmp40__ >> 0 & 0x1;
- pDst->explicitCSIFeedbackTx = tmp40__ >> 1 & 0x1;
- pDst->antennaIndicesFeedbackTx = tmp40__ >> 2 & 0x1;
- pDst->explicitCSIFeedback = tmp40__ >> 3 & 0x1;
- pDst->antennaIndicesFeedback = tmp40__ >> 4 & 0x1;
- pDst->rxAS = tmp40__ >> 5 & 0x1;
- pDst->txSoundingPPDUs = tmp40__ >> 6 & 0x1;
- pDst->reserved5 = tmp40__ >> 7 & 0x1;
+ pDst->maxRxAMPDUFactor = tmp40__ >> 0 & 0x3;
+ pDst->mpduDensity = tmp40__ >> 2 & 0x7;
+ pDst->reserved1 = tmp40__ >> 5 & 0x7;
+ DOT11F_MEMCPY(pCtx, pDst->supportedMCSSet, pBuf, 16);
+ pBuf += 16;
+ ielen -= (tANI_U8)16;
+ framesntohs(pCtx, &tmp41__, pBuf, 0);
+ pBuf += 2;
+ ielen -= 2;
+ pDst->pco = tmp41__ >> 0 & 0x1;
+ pDst->transitionTime = tmp41__ >> 1 & 0x3;
+ pDst->reserved2 = tmp41__ >> 3 & 0x1f;
+ pDst->mcsFeedback = tmp41__ >> 8 & 0x3;
+ pDst->reserved3 = tmp41__ >> 10 & 0x3f;
+ framesntohl(pCtx, &tmp42__, pBuf, 0);
+ pBuf += 4;
+ ielen -= 4;
+ pDst->txBF = tmp42__ >> 0 & 0x1;
+ pDst->rxStaggeredSounding = tmp42__ >> 1 & 0x1;
+ pDst->txStaggeredSounding = tmp42__ >> 2 & 0x1;
+ pDst->rxZLF = tmp42__ >> 3 & 0x1;
+ pDst->txZLF = tmp42__ >> 4 & 0x1;
+ pDst->implicitTxBF = tmp42__ >> 5 & 0x1;
+ pDst->calibration = tmp42__ >> 6 & 0x3;
+ pDst->explicitCSITxBF = tmp42__ >> 8 & 0x1;
+ pDst->explicitUncompressedSteeringMatrix = tmp42__ >> 9 & 0x1;
+ pDst->explicitBFCSIFeedback = tmp42__ >> 10 & 0x7;
+ pDst->explicitUncompressedSteeringMatrixFeedback = tmp42__ >> 13 & 0x7;
+ pDst->explicitCompressedSteeringMatrixFeedback = tmp42__ >> 16 & 0x7;
+ pDst->csiNumBFAntennae = tmp42__ >> 19 & 0x3;
+ pDst->uncompressedSteeringMatrixBFAntennae = tmp42__ >> 21 & 0x3;
+ pDst->compressedSteeringMatrixBFAntennae = tmp42__ >> 23 & 0x3;
+ pDst->reserved4 = tmp42__ >> 25 & 0x7f;
+ tmp43__ = *pBuf;
+ pBuf += 1;
+ ielen -= 1;
+ pDst->antennaSelection = tmp43__ >> 0 & 0x1;
+ pDst->explicitCSIFeedbackTx = tmp43__ >> 1 & 0x1;
+ pDst->antennaIndicesFeedbackTx = tmp43__ >> 2 & 0x1;
+ pDst->explicitCSIFeedback = tmp43__ >> 3 & 0x1;
+ pDst->antennaIndicesFeedback = tmp43__ >> 4 & 0x1;
+ pDst->rxAS = tmp43__ >> 5 & 0x1;
+ pDst->txSoundingPPDUs = tmp43__ >> 6 & 0x1;
+ pDst->reserved5 = tmp43__ >> 7 & 0x1;
pDst->num_rsvd = (tANI_U8)( ielen );
if (ielen > 32){
pDst->present = 0;
@@ -3087,47 +3163,47 @@
return status;
} /* End dot11fUnpackIeHTCaps. */
-#define SigIeHTCaps ( 0x0034 )
+#define SigIeHTCaps ( 0x0036 )
tANI_U32 dot11fUnpackIeHTInfo(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEHTInfo *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp41__;
- tANI_U16 tmp42__;
- tANI_U16 tmp43__;
+ tANI_U8 tmp44__;
+ tANI_U16 tmp45__;
+ tANI_U16 tmp46__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
pDst->primaryChannel = *pBuf;
pBuf += 1;
ielen -= (tANI_U8)1;
- tmp41__ = *pBuf;
+ tmp44__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->secondaryChannelOffset = tmp41__ >> 0 & 0x3;
- pDst->recommendedTxWidthSet = tmp41__ >> 2 & 0x1;
- pDst->rifsMode = tmp41__ >> 3 & 0x1;
- pDst->controlledAccessOnly = tmp41__ >> 4 & 0x1;
- pDst->serviceIntervalGranularity = tmp41__ >> 5 & 0x7;
- framesntohs(pCtx, &tmp42__, pBuf, 0);
+ pDst->secondaryChannelOffset = tmp44__ >> 0 & 0x3;
+ pDst->recommendedTxWidthSet = tmp44__ >> 2 & 0x1;
+ pDst->rifsMode = tmp44__ >> 3 & 0x1;
+ pDst->controlledAccessOnly = tmp44__ >> 4 & 0x1;
+ pDst->serviceIntervalGranularity = tmp44__ >> 5 & 0x7;
+ framesntohs(pCtx, &tmp45__, pBuf, 0);
pBuf += 2;
ielen -= 2;
- pDst->opMode = tmp42__ >> 0 & 0x3;
- pDst->nonGFDevicesPresent = tmp42__ >> 2 & 0x1;
- pDst->transmitBurstLimit = tmp42__ >> 3 & 0x1;
- pDst->obssNonHTStaPresent = tmp42__ >> 4 & 0x1;
- pDst->reserved = tmp42__ >> 5 & 0x7ff;
- framesntohs(pCtx, &tmp43__, pBuf, 0);
+ pDst->opMode = tmp45__ >> 0 & 0x3;
+ pDst->nonGFDevicesPresent = tmp45__ >> 2 & 0x1;
+ pDst->transmitBurstLimit = tmp45__ >> 3 & 0x1;
+ pDst->obssNonHTStaPresent = tmp45__ >> 4 & 0x1;
+ pDst->reserved = tmp45__ >> 5 & 0x7ff;
+ framesntohs(pCtx, &tmp46__, pBuf, 0);
pBuf += 2;
ielen -= 2;
- pDst->basicSTBCMCS = tmp43__ >> 0 & 0x7f;
- pDst->dualCTSProtection = tmp43__ >> 7 & 0x1;
- pDst->secondaryBeacon = tmp43__ >> 8 & 0x1;
- pDst->lsigTXOPProtectionFullSupport = tmp43__ >> 9 & 0x1;
- pDst->pcoActive = tmp43__ >> 10 & 0x1;
- pDst->pcoPhase = tmp43__ >> 11 & 0x1;
- pDst->reserved2 = tmp43__ >> 12 & 0xf;
+ pDst->basicSTBCMCS = tmp46__ >> 0 & 0x7f;
+ pDst->dualCTSProtection = tmp46__ >> 7 & 0x1;
+ pDst->secondaryBeacon = tmp46__ >> 8 & 0x1;
+ pDst->lsigTXOPProtectionFullSupport = tmp46__ >> 9 & 0x1;
+ pDst->pcoActive = tmp46__ >> 10 & 0x1;
+ pDst->pcoPhase = tmp46__ >> 11 & 0x1;
+ pDst->reserved2 = tmp46__ >> 12 & 0xf;
DOT11F_MEMCPY(pCtx, pDst->basicMCSSet, pBuf, 16);
pBuf += 16;
ielen -= (tANI_U8)16;
@@ -3142,7 +3218,7 @@
return status;
} /* End dot11fUnpackIeHTInfo. */
-#define SigIeHTInfo ( 0x0035 )
+#define SigIeHTInfo ( 0x0037 )
tANI_U32 dot11fUnpackIeIBSSParams(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEIBSSParams *pDst)
@@ -3156,7 +3232,7 @@
return status;
} /* End dot11fUnpackIeIBSSParams. */
-#define SigIeIBSSParams ( 0x0036 )
+#define SigIeIBSSParams ( 0x0038 )
tANI_U32 dot11fUnpackIeLinkIdentifier(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIELinkIdentifier *pDst)
@@ -3176,7 +3252,7 @@
return status;
} /* End dot11fUnpackIeLinkIdentifier. */
-#define SigIeLinkIdentifier ( 0x0037 )
+#define SigIeLinkIdentifier ( 0x0039 )
static const tFFDefn FFS_reportBeacon[ ] = {
@@ -3191,22 +3267,22 @@
tANI_U32 dot11fUnpackIeMeasurementReport(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEMeasurementReport *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp44__;
- tANI_U8 tmp45__;
- tANI_U8 tmp46__;
+ tANI_U8 tmp47__;
+ tANI_U8 tmp48__;
+ tANI_U8 tmp49__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
pDst->token = *pBuf;
pBuf += 1;
ielen -= (tANI_U8)1;
- tmp44__ = *pBuf;
+ tmp47__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->late = tmp44__ >> 0 & 0x1;
- pDst->incapable = tmp44__ >> 1 & 0x1;
- pDst->refused = tmp44__ >> 2 & 0x1;
- pDst->unused = tmp44__ >> 3 & 0x1f;
+ pDst->late = tmp47__ >> 0 & 0x1;
+ pDst->incapable = tmp47__ >> 1 & 0x1;
+ pDst->refused = tmp47__ >> 2 & 0x1;
+ pDst->unused = tmp47__ >> 3 & 0x1f;
pDst->type = *pBuf;
pBuf += 1;
ielen -= (tANI_U8)1;
@@ -3228,15 +3304,15 @@
framesntohs(pCtx, &pDst->report.Basic.meas_duration, pBuf, 0);
pBuf += 2;
ielen -= (tANI_U8)2;
- tmp45__ = *pBuf;
+ tmp48__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->report.Basic.bss = tmp45__ >> 0 & 0x1;
- pDst->report.Basic.ofdm_preamble = tmp45__ >> 1 & 0x1;
- pDst->report.Basic.unid_signal = tmp45__ >> 2 & 0x1;
- pDst->report.Basic.rader = tmp45__ >> 3 & 0x1;
- pDst->report.Basic.unmeasured = tmp45__ >> 4 & 0x1;
- pDst->report.Basic.unused = tmp45__ >> 5 & 0x7;
+ pDst->report.Basic.bss = tmp48__ >> 0 & 0x1;
+ pDst->report.Basic.ofdm_preamble = tmp48__ >> 1 & 0x1;
+ pDst->report.Basic.unid_signal = tmp48__ >> 2 & 0x1;
+ pDst->report.Basic.rader = tmp48__ >> 3 & 0x1;
+ pDst->report.Basic.unmeasured = tmp48__ >> 4 & 0x1;
+ pDst->report.Basic.unused = tmp48__ >> 5 & 0x7;
break;
case 1:
pDst->report.CCA.channel = *pBuf;
@@ -3300,11 +3376,11 @@
framesntohs(pCtx, &pDst->report.Beacon.meas_duration, pBuf, 0);
pBuf += 2;
ielen -= (tANI_U8)2;
- tmp46__ = *pBuf;
+ tmp49__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->report.Beacon.condensed_PHY = tmp46__ >> 0 & 0x7f;
- pDst->report.Beacon.reported_frame_type = tmp46__ >> 7 & 0x1;
+ pDst->report.Beacon.condensed_PHY = tmp49__ >> 0 & 0x7f;
+ pDst->report.Beacon.reported_frame_type = tmp49__ >> 7 & 0x1;
pDst->report.Beacon.RCPI = *pBuf;
pBuf += 1;
ielen -= (tANI_U8)1;
@@ -3334,7 +3410,7 @@
return status;
} /* End dot11fUnpackIeMeasurementReport. */
-#define SigIeMeasurementReport ( 0x0038 )
+#define SigIeMeasurementReport ( 0x003a )
static const tFFDefn FFS_measurement_requestBeacon[ ] = {
@@ -3353,22 +3429,22 @@
tANI_U32 dot11fUnpackIeMeasurementRequest(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEMeasurementRequest *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp47__;
+ tANI_U8 tmp50__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
pDst->measurement_token = *pBuf;
pBuf += 1;
ielen -= (tANI_U8)1;
- tmp47__ = *pBuf;
+ tmp50__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->parallel = tmp47__ >> 0 & 0x1;
- pDst->enable = tmp47__ >> 1 & 0x1;
- pDst->request = tmp47__ >> 2 & 0x1;
- pDst->report = tmp47__ >> 3 & 0x1;
- pDst->durationMandatory = tmp47__ >> 4 & 0x1;
- pDst->unused = tmp47__ >> 5 & 0x7;
+ pDst->parallel = tmp50__ >> 0 & 0x1;
+ pDst->enable = tmp50__ >> 1 & 0x1;
+ pDst->request = tmp50__ >> 2 & 0x1;
+ pDst->report = tmp50__ >> 3 & 0x1;
+ pDst->durationMandatory = tmp50__ >> 4 & 0x1;
+ pDst->unused = tmp50__ >> 5 & 0x7;
pDst->measurement_type = *pBuf;
pBuf += 1;
ielen -= (tANI_U8)1;
@@ -3439,28 +3515,28 @@
return status;
} /* End dot11fUnpackIeMeasurementRequest. */
-#define SigIeMeasurementRequest ( 0x0039 )
+#define SigIeMeasurementRequest ( 0x003b )
tANI_U32 dot11fUnpackIeMobilityDomain(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEMobilityDomain *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp48__;
+ tANI_U8 tmp51__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
framesntohs(pCtx, &pDst->MDID, pBuf, 0);
pBuf += 2;
ielen -= (tANI_U8)2;
- tmp48__ = *pBuf;
- pDst->overDSCap = tmp48__ >> 0 & 0x1;
- pDst->resourceReqCap = tmp48__ >> 1 & 0x1;
- pDst->reserved = tmp48__ >> 2 & 0x3f;
+ tmp51__ = *pBuf;
+ pDst->overDSCap = tmp51__ >> 0 & 0x1;
+ pDst->resourceReqCap = tmp51__ >> 1 & 0x1;
+ pDst->reserved = tmp51__ >> 2 & 0x3f;
(void)pCtx;
return status;
} /* End dot11fUnpackIeMobilityDomain. */
-#define SigIeMobilityDomain ( 0x003a )
+#define SigIeMobilityDomain ( 0x003c )
static const tFFDefn FFS_NeighborReport[ ] = {
@@ -3479,31 +3555,31 @@
tANI_U32 dot11fUnpackIeNeighborReport(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIENeighborReport *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp49__;
- tANI_U8 tmp50__;
+ tANI_U8 tmp52__;
+ tANI_U8 tmp53__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
DOT11F_MEMCPY(pCtx, pDst->bssid, pBuf, 6);
pBuf += 6;
ielen -= (tANI_U8)6;
- tmp49__ = *pBuf;
+ tmp52__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->APReachability = tmp49__ >> 0 & 0x3;
- pDst->Security = tmp49__ >> 2 & 0x1;
- pDst->KeyScope = tmp49__ >> 3 & 0x1;
- pDst->SpecMgmtCap = tmp49__ >> 4 & 0x1;
- pDst->QosCap = tmp49__ >> 5 & 0x1;
- pDst->apsd = tmp49__ >> 6 & 0x1;
- pDst->rrm = tmp49__ >> 7 & 0x1;
- tmp50__ = *pBuf;
+ pDst->APReachability = tmp52__ >> 0 & 0x3;
+ pDst->Security = tmp52__ >> 2 & 0x1;
+ pDst->KeyScope = tmp52__ >> 3 & 0x1;
+ pDst->SpecMgmtCap = tmp52__ >> 4 & 0x1;
+ pDst->QosCap = tmp52__ >> 5 & 0x1;
+ pDst->apsd = tmp52__ >> 6 & 0x1;
+ pDst->rrm = tmp52__ >> 7 & 0x1;
+ tmp53__ = *pBuf;
pBuf += 1;
ielen -= 1;
- pDst->DelayedBA = tmp50__ >> 0 & 0x1;
- pDst->ImmBA = tmp50__ >> 1 & 0x1;
- pDst->MobilityDomain = tmp50__ >> 2 & 0x1;
- pDst->reserved = tmp50__ >> 3 & 0x1f;
+ pDst->DelayedBA = tmp53__ >> 0 & 0x1;
+ pDst->ImmBA = tmp53__ >> 1 & 0x1;
+ pDst->MobilityDomain = tmp53__ >> 2 & 0x1;
+ pDst->reserved = tmp53__ >> 3 & 0x1f;
framesntohs(pCtx, &pDst->reserved1, pBuf, 0);
pBuf += 2;
ielen -= (tANI_U8)2;
@@ -3527,7 +3603,7 @@
return status;
} /* End dot11fUnpackIeNeighborReport. */
-#define SigIeNeighborReport ( 0x003b )
+#define SigIeNeighborReport ( 0x003d )
tANI_U32 dot11fUnpackIeOBSSScanParameters(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEOBSSScanParameters *pDst)
@@ -3559,26 +3635,26 @@
return status;
} /* End dot11fUnpackIeOBSSScanParameters. */
-#define SigIeOBSSScanParameters ( 0x003c )
+#define SigIeOBSSScanParameters ( 0x003e )
tANI_U32 dot11fUnpackIeOperatingMode(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEOperatingMode *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp51__;
+ tANI_U8 tmp54__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
- tmp51__ = *pBuf;
- pDst->chanWidth = tmp51__ >> 0 & 0x3;
- pDst->reserved = tmp51__ >> 2 & 0x3;
- pDst->rxNSS = tmp51__ >> 4 & 0x7;
- pDst->rxNSSType = tmp51__ >> 7 & 0x1;
+ tmp54__ = *pBuf;
+ pDst->chanWidth = tmp54__ >> 0 & 0x3;
+ pDst->reserved = tmp54__ >> 2 & 0x3;
+ pDst->rxNSS = tmp54__ >> 4 & 0x7;
+ pDst->rxNSSType = tmp54__ >> 7 & 0x1;
(void)pCtx;
return status;
} /* End dot11fUnpackIeOperatingMode. */
-#define SigIeOperatingMode ( 0x003d )
+#define SigIeOperatingMode ( 0x003f )
static const tTLVDefn TLVS_P2PAssocReq[ ] = {
@@ -3597,7 +3673,7 @@
return status;
} /* End dot11fUnpackIeP2PAssocReq. */
-#define SigIeP2PAssocReq ( 0x003e )
+#define SigIeP2PAssocReq ( 0x0040 )
static const tTLVDefn TLVS_P2PAssocRes[ ] = {
@@ -3615,7 +3691,7 @@
return status;
} /* End dot11fUnpackIeP2PAssocRes. */
-#define SigIeP2PAssocRes ( 0x003f )
+#define SigIeP2PAssocRes ( 0x0041 )
static const tTLVDefn TLVS_P2PBeacon[ ] = {
@@ -3634,7 +3710,7 @@
return status;
} /* End dot11fUnpackIeP2PBeacon. */
-#define SigIeP2PBeacon ( 0x0040 )
+#define SigIeP2PBeacon ( 0x0042 )
static const tTLVDefn TLVS_P2PBeaconProbeRes[ ] = {
@@ -3656,7 +3732,7 @@
return status;
} /* End dot11fUnpackIeP2PBeaconProbeRes. */
-#define SigIeP2PBeaconProbeRes ( 0x0041 )
+#define SigIeP2PBeaconProbeRes ( 0x0043 )
static const tTLVDefn TLVS_P2PDeAuth[ ] = {
@@ -3673,7 +3749,7 @@
return status;
} /* End dot11fUnpackIeP2PDeAuth. */
-#define SigIeP2PDeAuth ( 0x0042 )
+#define SigIeP2PDeAuth ( 0x0044 )
static const tTLVDefn TLVS_P2PDeviceDiscoverabilityReq[ ] = {
@@ -3691,7 +3767,7 @@
return status;
} /* End dot11fUnpackIeP2PDeviceDiscoverabilityReq. */
-#define SigIeP2PDeviceDiscoverabilityReq ( 0x0043 )
+#define SigIeP2PDeviceDiscoverabilityReq ( 0x0045 )
static const tTLVDefn TLVS_P2PDeviceDiscoverabilityRes[ ] = {
@@ -3708,7 +3784,7 @@
return status;
} /* End dot11fUnpackIeP2PDeviceDiscoverabilityRes. */
-#define SigIeP2PDeviceDiscoverabilityRes ( 0x0044 )
+#define SigIeP2PDeviceDiscoverabilityRes ( 0x0046 )
static const tTLVDefn TLVS_P2PDisAssoc[ ] = {
@@ -3725,7 +3801,7 @@
return status;
} /* End dot11fUnpackIeP2PDisAssoc. */
-#define SigIeP2PDisAssoc ( 0x0045 )
+#define SigIeP2PDisAssoc ( 0x0047 )
static const tTLVDefn TLVS_P2PGONegCnf[ ] = {
@@ -3746,7 +3822,7 @@
return status;
} /* End dot11fUnpackIeP2PGONegCnf. */
-#define SigIeP2PGONegCnf ( 0x0046 )
+#define SigIeP2PGONegCnf ( 0x0048 )
static const tTLVDefn TLVS_P2PGONegReq[ ] = {
@@ -3771,7 +3847,7 @@
return status;
} /* End dot11fUnpackIeP2PGONegReq. */
-#define SigIeP2PGONegReq ( 0x0047 )
+#define SigIeP2PGONegReq ( 0x0049 )
static const tTLVDefn TLVS_P2PGONegRes[ ] = {
@@ -3796,7 +3872,7 @@
return status;
} /* End dot11fUnpackIeP2PGONegRes. */
-#define SigIeP2PGONegRes ( 0x0048 )
+#define SigIeP2PGONegRes ( 0x004a )
static const tTLVDefn TLVS_P2PGONegWPS[ ] = {
@@ -3814,7 +3890,7 @@
return status;
} /* End dot11fUnpackIeP2PGONegWPS. */
-#define SigIeP2PGONegWPS ( 0x0049 )
+#define SigIeP2PGONegWPS ( 0x004b )
tANI_U32 dot11fUnpackIeP2PIEOpaque(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEP2PIEOpaque *pDst)
@@ -3834,7 +3910,7 @@
return status;
} /* End dot11fUnpackIeP2PIEOpaque. */
-#define SigIeP2PIEOpaque ( 0x004a )
+#define SigIeP2PIEOpaque ( 0x004c )
static const tTLVDefn TLVS_P2PInvitationReq[ ] = {
@@ -3857,7 +3933,7 @@
return status;
} /* End dot11fUnpackIeP2PInvitationReq. */
-#define SigIeP2PInvitationReq ( 0x004b )
+#define SigIeP2PInvitationReq ( 0x004d )
static const tTLVDefn TLVS_P2PInvitationRes[ ] = {
@@ -3878,7 +3954,7 @@
return status;
} /* End dot11fUnpackIeP2PInvitationRes. */
-#define SigIeP2PInvitationRes ( 0x004c )
+#define SigIeP2PInvitationRes ( 0x004e )
static const tTLVDefn TLVS_P2PNoticeOfAbsence[ ] = {
@@ -3895,7 +3971,7 @@
return status;
} /* End dot11fUnpackIeP2PNoticeOfAbsence. */
-#define SigIeP2PNoticeOfAbsence ( 0x004d )
+#define SigIeP2PNoticeOfAbsence ( 0x004f )
static const tTLVDefn TLVS_P2PPresenceResponse[ ] = {
@@ -3913,7 +3989,7 @@
return status;
} /* End dot11fUnpackIeP2PPresenceResponse. */
-#define SigIeP2PPresenceResponse ( 0x004e )
+#define SigIeP2PPresenceResponse ( 0x0050 )
static const tTLVDefn TLVS_P2PProbeReq[ ] = {
@@ -3934,7 +4010,7 @@
return status;
} /* End dot11fUnpackIeP2PProbeReq. */
-#define SigIeP2PProbeReq ( 0x004f )
+#define SigIeP2PProbeReq ( 0x0051 )
static const tTLVDefn TLVS_P2PProbeRes[ ] = {
@@ -3955,7 +4031,7 @@
return status;
} /* End dot11fUnpackIeP2PProbeRes. */
-#define SigIeP2PProbeRes ( 0x0050 )
+#define SigIeP2PProbeRes ( 0x0052 )
static const tTLVDefn TLVS_P2PProvisionDiscoveryReq[ ] = {
@@ -3974,7 +4050,7 @@
return status;
} /* End dot11fUnpackIeP2PProvisionDiscoveryReq. */
-#define SigIeP2PProvisionDiscoveryReq ( 0x0051 )
+#define SigIeP2PProvisionDiscoveryReq ( 0x0053 )
static const tTLVDefn TLVS_P2PWSCProvisionDiscoveryRes[ ] = {
@@ -3991,7 +4067,7 @@
return status;
} /* End dot11fUnpackIeP2PWSCProvisionDiscoveryRes. */
-#define SigIeP2PWSCProvisionDiscoveryRes ( 0x0052 )
+#define SigIeP2PWSCProvisionDiscoveryRes ( 0x0054 )
tANI_U32 dot11fUnpackIePTIControl(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEPTIControl *pDst)
@@ -4008,27 +4084,27 @@
return status;
} /* End dot11fUnpackIePTIControl. */
-#define SigIePTIControl ( 0x0053 )
+#define SigIePTIControl ( 0x0055 )
tANI_U32 dot11fUnpackIePUBufferStatus(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEPUBufferStatus *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp52__;
+ tANI_U8 tmp55__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
- tmp52__ = *pBuf;
- pDst->ac_bk_traffic_aval = tmp52__ >> 0 & 0x1;
- pDst->ac_be_traffic_aval = tmp52__ >> 1 & 0x1;
- pDst->ac_vi_traffic_aval = tmp52__ >> 2 & 0x1;
- pDst->ac_vo_traffic_aval = tmp52__ >> 3 & 0x1;
- pDst->reserved = tmp52__ >> 4 & 0xf;
+ tmp55__ = *pBuf;
+ pDst->ac_bk_traffic_aval = tmp55__ >> 0 & 0x1;
+ pDst->ac_be_traffic_aval = tmp55__ >> 1 & 0x1;
+ pDst->ac_vi_traffic_aval = tmp55__ >> 2 & 0x1;
+ pDst->ac_vo_traffic_aval = tmp55__ >> 3 & 0x1;
+ pDst->reserved = tmp55__ >> 4 & 0xf;
(void)pCtx;
return status;
} /* End dot11fUnpackIePUBufferStatus. */
-#define SigIePUBufferStatus ( 0x0054 )
+#define SigIePUBufferStatus ( 0x0056 )
tANI_U32 dot11fUnpackIePowerCaps(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEPowerCaps *pDst)
@@ -4045,7 +4121,7 @@
return status;
} /* End dot11fUnpackIePowerCaps. */
-#define SigIePowerCaps ( 0x0055 )
+#define SigIePowerCaps ( 0x0057 )
tANI_U32 dot11fUnpackIePowerConstraints(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEPowerConstraints *pDst)
@@ -4059,7 +4135,7 @@
return status;
} /* End dot11fUnpackIePowerConstraints. */
-#define SigIePowerConstraints ( 0x0056 )
+#define SigIePowerConstraints ( 0x0058 )
tANI_U32 dot11fUnpackIeQBSSLoad(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEQBSSLoad *pDst)
@@ -4079,7 +4155,7 @@
return status;
} /* End dot11fUnpackIeQBSSLoad. */
-#define SigIeQBSSLoad ( 0x0057 )
+#define SigIeQBSSLoad ( 0x0059 )
tANI_U32 dot11fUnpackIeQComVendorIE(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEQComVendorIE *pDst)
@@ -4096,49 +4172,49 @@
return status;
} /* End dot11fUnpackIeQComVendorIE. */
-#define SigIeQComVendorIE ( 0x0058 )
+#define SigIeQComVendorIE ( 0x005a )
tANI_U32 dot11fUnpackIeQOSCapsAp(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEQOSCapsAp *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp53__;
+ tANI_U8 tmp56__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
- tmp53__ = *pBuf;
- pDst->count = tmp53__ >> 0 & 0xf;
- pDst->qack = tmp53__ >> 4 & 0x1;
- pDst->qreq = tmp53__ >> 5 & 0x1;
- pDst->txopreq = tmp53__ >> 6 & 0x1;
- pDst->reserved = tmp53__ >> 7 & 0x1;
+ tmp56__ = *pBuf;
+ pDst->count = tmp56__ >> 0 & 0xf;
+ pDst->qack = tmp56__ >> 4 & 0x1;
+ pDst->qreq = tmp56__ >> 5 & 0x1;
+ pDst->txopreq = tmp56__ >> 6 & 0x1;
+ pDst->reserved = tmp56__ >> 7 & 0x1;
(void)pCtx;
return status;
} /* End dot11fUnpackIeQOSCapsAp. */
-#define SigIeQOSCapsAp ( 0x0059 )
+#define SigIeQOSCapsAp ( 0x005b )
tANI_U32 dot11fUnpackIeQOSCapsStation(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEQOSCapsStation *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U8 tmp54__;
+ tANI_U8 tmp57__;
(void) pBuf; (void)ielen; /* Shutup the compiler */
if (pDst->present) status = DOT11F_DUPLICATE_IE;
pDst->present = 1;
- tmp54__ = *pBuf;
- pDst->acvo_uapsd = tmp54__ >> 0 & 0x1;
- pDst->acvi_uapsd = tmp54__ >> 1 & 0x1;
- pDst->acbk_uapsd = tmp54__ >> 2 & 0x1;
- pDst->acbe_uapsd = tmp54__ >> 3 & 0x1;
- pDst->qack = tmp54__ >> 4 & 0x1;
- pDst->max_sp_length = tmp54__ >> 5 & 0x3;
- pDst->more_data_ack = tmp54__ >> 7 & 0x1;
+ tmp57__ = *pBuf;
+ pDst->acvo_uapsd = tmp57__ >> 0 & 0x1;
+ pDst->acvi_uapsd = tmp57__ >> 1 & 0x1;
+ pDst->acbk_uapsd = tmp57__ >> 2 & 0x1;
+ pDst->acbe_uapsd = tmp57__ >> 3 & 0x1;
+ pDst->qack = tmp57__ >> 4 & 0x1;
+ pDst->max_sp_length = tmp57__ >> 5 & 0x3;
+ pDst->more_data_ack = tmp57__ >> 7 & 0x1;
(void)pCtx;
return status;
} /* End dot11fUnpackIeQOSCapsStation. */
-#define SigIeQOSCapsStation ( 0x005a )
+#define SigIeQOSCapsStation ( 0x005c )
tANI_U32 dot11fUnpackIeQosMapSet(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEQosMapSet *pDst)
@@ -4158,7 +4234,7 @@
return status;
} /* End dot11fUnpackIeQosMapSet. */
-#define SigIeQosMapSet ( 0x005b )
+#define SigIeQosMapSet ( 0x005d )
tANI_U32 dot11fUnpackIeQuiet(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEQuiet *pDst)
@@ -4181,7 +4257,7 @@
return status;
} /* End dot11fUnpackIeQuiet. */
-#define SigIeQuiet ( 0x005c )
+#define SigIeQuiet ( 0x005e )
tANI_U32 dot11fUnpackIeRCPIIE(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIERCPIIE *pDst)
@@ -4195,7 +4271,7 @@
return status;
} /* End dot11fUnpackIeRCPIIE. */
-#define SigIeRCPIIE ( 0x005d )
+#define SigIeRCPIIE ( 0x005f )
static const tFFDefn FFS_RICDataDesc[ ] = {
@@ -4235,7 +4311,7 @@
return status;
} /* End dot11fUnpackIeRICDataDesc. */
-#define SigIeRICDataDesc ( 0x005e )
+#define SigIeRICDataDesc ( 0x0060 )
tANI_U32 dot11fUnpackIeRSN(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIERSN *pDst)
@@ -4338,7 +4414,7 @@
return status;
} /* End dot11fUnpackIeRSN. */
-#define SigIeRSN ( 0x005f )
+#define SigIeRSN ( 0x0061 )
tANI_U32 dot11fUnpackIeRSNIIE(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIERSNIIE *pDst)
@@ -4352,7 +4428,7 @@
return status;
} /* End dot11fUnpackIeRSNIIE. */
-#define SigIeRSNIIE ( 0x0060 )
+#define SigIeRSNIIE ( 0x0062 )
tANI_U32 dot11fUnpackIeRSNOpaque(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIERSNOpaque *pDst)
@@ -4372,7 +4448,7 @@
return status;
} /* End dot11fUnpackIeRSNOpaque. */
-#define SigIeRSNOpaque ( 0x0061 )
+#define SigIeRSNOpaque ( 0x0063 )
tANI_U32 dot11fUnpackIeSuppChannels(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIESuppChannels *pDst)
@@ -4392,7 +4468,7 @@
return status;
} /* End dot11fUnpackIeSuppChannels. */
-#define SigIeSuppChannels ( 0x0062 )
+#define SigIeSuppChannels ( 0x0064 )
tANI_U32 dot11fUnpackIeSuppOperatingClasses(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIESuppOperatingClasses *pDst)
@@ -4412,7 +4488,7 @@
return status;
} /* End dot11fUnpackIeSuppOperatingClasses. */
-#define SigIeSuppOperatingClasses ( 0x0063 )
+#define SigIeSuppOperatingClasses ( 0x0065 )
tANI_U32 dot11fUnpackIeSuppRates(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIESuppRates *pDst)
@@ -4440,7 +4516,7 @@
return status;
} /* End dot11fUnpackIeSuppRates. */
-#define SigIeSuppRates ( 0x0064 )
+#define SigIeSuppRates ( 0x0066 )
tANI_U32 dot11fUnpackIeTIM(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIETIM *pDst)
@@ -4469,7 +4545,7 @@
return status;
} /* End dot11fUnpackIeTIM. */
-#define SigIeTIM ( 0x0065 )
+#define SigIeTIM ( 0x0067 )
tANI_U32 dot11fUnpackIeTPCReport(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIETPCReport *pDst)
@@ -4486,7 +4562,7 @@
return status;
} /* End dot11fUnpackIeTPCReport. */
-#define SigIeTPCReport ( 0x0066 )
+#define SigIeTPCReport ( 0x0068 )
tANI_U32 dot11fUnpackIeTPCRequest(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIETPCRequest *pDst)
@@ -4499,7 +4575,7 @@
return status;
} /* End dot11fUnpackIeTPCRequest. */
-#define SigIeTPCRequest ( 0x0067 )
+#define SigIeTPCRequest ( 0x0069 )
tANI_U32 dot11fUnpackIeTimeAdvertisement(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIETimeAdvertisement *pDst)
@@ -4519,7 +4595,7 @@
return status;
} /* End dot11fUnpackIeTimeAdvertisement. */
-#define SigIeTimeAdvertisement ( 0x0068 )
+#define SigIeTimeAdvertisement ( 0x006a )
tANI_U32 dot11fUnpackIeTimeoutInterval(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIETimeoutInterval *pDst)
@@ -4536,60 +4612,7 @@
return status;
} /* End dot11fUnpackIeTimeoutInterval. */
-#define SigIeTimeoutInterval ( 0x0069 )
-
-
-tANI_U32 dot11fUnpackIeVHTCaps(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEVHTCaps *pDst)
-{
- tANI_U32 status = DOT11F_PARSE_SUCCESS;
- tANI_U32 tmp55__;
- tANI_U16 tmp56__;
- tANI_U16 tmp57__;
- (void) pBuf; (void)ielen; /* Shutup the compiler */
- if (pDst->present) status = DOT11F_DUPLICATE_IE;
- pDst->present = 1;
- framesntohl(pCtx, &tmp55__, pBuf, 0);
- pBuf += 4;
- ielen -= 4;
- pDst->maxMPDULen = tmp55__ >> 0 & 0x3;
- pDst->supportedChannelWidthSet = tmp55__ >> 2 & 0x3;
- pDst->ldpcCodingCap = tmp55__ >> 4 & 0x1;
- pDst->shortGI80MHz = tmp55__ >> 5 & 0x1;
- pDst->shortGI160and80plus80MHz = tmp55__ >> 6 & 0x1;
- pDst->txSTBC = tmp55__ >> 7 & 0x1;
- pDst->rxSTBC = tmp55__ >> 8 & 0x7;
- pDst->suBeamFormerCap = tmp55__ >> 11 & 0x1;
- pDst->suBeamformeeCap = tmp55__ >> 12 & 0x1;
- pDst->csnofBeamformerAntSup = tmp55__ >> 13 & 0x7;
- pDst->numSoundingDim = tmp55__ >> 16 & 0x7;
- pDst->muBeamformerCap = tmp55__ >> 19 & 0x1;
- pDst->muBeamformeeCap = tmp55__ >> 20 & 0x1;
- pDst->vhtTXOPPS = tmp55__ >> 21 & 0x1;
- pDst->htcVHTCap = tmp55__ >> 22 & 0x1;
- pDst->maxAMPDULenExp = tmp55__ >> 23 & 0x7;
- pDst->vhtLinkAdaptCap = tmp55__ >> 26 & 0x3;
- pDst->rxAntPattern = tmp55__ >> 28 & 0x1;
- pDst->txAntPattern = tmp55__ >> 29 & 0x1;
- pDst->reserved1 = tmp55__ >> 30 & 0x3;
- framesntohs(pCtx, &pDst->rxMCSMap, pBuf, 0);
- pBuf += 2;
- ielen -= (tANI_U8)2;
- framesntohs(pCtx, &tmp56__, pBuf, 0);
- pBuf += 2;
- ielen -= 2;
- pDst->rxHighSupDataRate = tmp56__ >> 0 & 0x1fff;
- pDst->reserved2 = tmp56__ >> 13 & 0x7;
- framesntohs(pCtx, &pDst->txMCSMap, pBuf, 0);
- pBuf += 2;
- ielen -= (tANI_U8)2;
- framesntohs(pCtx, &tmp57__, pBuf, 0);
- pDst->txSupDataRate = tmp57__ >> 0 & 0x1fff;
- pDst->reserved3 = tmp57__ >> 13 & 0x7;
- (void)pCtx;
- return status;
-} /* End dot11fUnpackIeVHTCaps. */
-
-#define SigIeVHTCaps ( 0x006a )
+#define SigIeTimeoutInterval ( 0x006b )
tANI_U32 dot11fUnpackIeVHTExtBssLoad(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEVHTExtBssLoad *pDst)
@@ -4615,30 +4638,7 @@
return status;
} /* End dot11fUnpackIeVHTExtBssLoad. */
-#define SigIeVHTExtBssLoad ( 0x006b )
-
-
-tANI_U32 dot11fUnpackIeVHTOperation(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEVHTOperation *pDst)
-{
- tANI_U32 status = DOT11F_PARSE_SUCCESS;
- (void) pBuf; (void)ielen; /* Shutup the compiler */
- if (pDst->present) status = DOT11F_DUPLICATE_IE;
- pDst->present = 1;
- pDst->chanWidth = *pBuf;
- pBuf += 1;
- ielen -= (tANI_U8)1;
- pDst->chanCenterFreqSeg1 = *pBuf;
- pBuf += 1;
- ielen -= (tANI_U8)1;
- pDst->chanCenterFreqSeg2 = *pBuf;
- pBuf += 1;
- ielen -= (tANI_U8)1;
- framesntohs(pCtx, &pDst->basicMCSSet, pBuf, 0);
- (void)pCtx;
- return status;
-} /* End dot11fUnpackIeVHTOperation. */
-
-#define SigIeVHTOperation ( 0x006c )
+#define SigIeVHTExtBssLoad ( 0x006c )
tANI_U32 dot11fUnpackIeVendor1IE(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEVendor1IE *pDst)
@@ -4654,19 +4654,6 @@
#define SigIeVendor1IE ( 0x006d )
-tANI_U32 dot11fUnpackIeVendor2IE(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEVendor2IE *pDst)
-{
- tANI_U32 status = DOT11F_PARSE_SUCCESS;
- (void) pBuf; (void)ielen; /* Shutup the compiler */
- if (pDst->present) status = DOT11F_DUPLICATE_IE;
- pDst->present = 1;
- (void)pCtx;
- return status;
-} /* End dot11fUnpackIeVendor2IE. */
-
-#define SigIeVendor2IE ( 0x006e )
-
-
tANI_U32 dot11fUnpackIeVendor3IE(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEVendor3IE *pDst)
{
tANI_U32 status = DOT11F_PARSE_SUCCESS;
@@ -4677,7 +4664,7 @@
return status;
} /* End dot11fUnpackIeVendor3IE. */
-#define SigIeVendor3IE ( 0x006f )
+#define SigIeVendor3IE ( 0x006e )
tANI_U32 dot11fUnpackIeWAPI(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWAPI *pDst)
@@ -4746,7 +4733,7 @@
return status;
} /* End dot11fUnpackIeWAPI. */
-#define SigIeWAPI ( 0x0070 )
+#define SigIeWAPI ( 0x006f )
tANI_U32 dot11fUnpackIeWAPIOpaque(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWAPIOpaque *pDst)
@@ -4766,7 +4753,7 @@
return status;
} /* End dot11fUnpackIeWAPIOpaque. */
-#define SigIeWAPIOpaque ( 0x0071 )
+#define SigIeWAPIOpaque ( 0x0070 )
tANI_U32 dot11fUnpackIeWFATPC(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWFATPC *pDst)
@@ -4783,7 +4770,7 @@
return status;
} /* End dot11fUnpackIeWFATPC. */
-#define SigIeWFATPC ( 0x0072 )
+#define SigIeWFATPC ( 0x0071 )
tANI_U32 dot11fUnpackIeWFDIEOpaque(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWFDIEOpaque *pDst)
@@ -4803,7 +4790,7 @@
return status;
} /* End dot11fUnpackIeWFDIEOpaque. */
-#define SigIeWFDIEOpaque ( 0x0073 )
+#define SigIeWFDIEOpaque ( 0x0072 )
tANI_U32 dot11fUnpackIeWMMCaps(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWMMCaps *pDst)
@@ -4831,7 +4818,7 @@
return status;
} /* End dot11fUnpackIeWMMCaps. */
-#define SigIeWMMCaps ( 0x0074 )
+#define SigIeWMMCaps ( 0x0073 )
tANI_U32 dot11fUnpackIeWMMInfoAp(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWMMInfoAp *pDst)
@@ -4852,7 +4839,7 @@
return status;
} /* End dot11fUnpackIeWMMInfoAp. */
-#define SigIeWMMInfoAp ( 0x0075 )
+#define SigIeWMMInfoAp ( 0x0074 )
tANI_U32 dot11fUnpackIeWMMInfoStation(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWMMInfoStation *pDst)
@@ -4877,7 +4864,7 @@
return status;
} /* End dot11fUnpackIeWMMInfoStation. */
-#define SigIeWMMInfoStation ( 0x0076 )
+#define SigIeWMMInfoStation ( 0x0075 )
tANI_U32 dot11fUnpackIeWMMParams(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWMMParams *pDst)
@@ -4970,7 +4957,7 @@
return status;
} /* End dot11fUnpackIeWMMParams. */
-#define SigIeWMMParams ( 0x0077 )
+#define SigIeWMMParams ( 0x0076 )
tANI_U32 dot11fUnpackIeWPA(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWPA *pDst)
@@ -5052,7 +5039,7 @@
return status;
} /* End dot11fUnpackIeWPA. */
-#define SigIeWPA ( 0x0078 )
+#define SigIeWPA ( 0x0077 )
tANI_U32 dot11fUnpackIeWPAOpaque(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWPAOpaque *pDst)
@@ -5072,7 +5059,7 @@
return status;
} /* End dot11fUnpackIeWPAOpaque. */
-#define SigIeWPAOpaque ( 0x0079 )
+#define SigIeWPAOpaque ( 0x0078 )
static const tTLVDefn TLVS_WSC[ ] = {
@@ -5110,7 +5097,7 @@
return status;
} /* End dot11fUnpackIeWSC. */
-#define SigIeWSC ( 0x007a )
+#define SigIeWSC ( 0x0079 )
static const tTLVDefn TLVS_WscAssocReq[ ] = {
@@ -5129,7 +5116,7 @@
return status;
} /* End dot11fUnpackIeWscAssocReq. */
-#define SigIeWscAssocReq ( 0x007b )
+#define SigIeWscAssocReq ( 0x007a )
static const tTLVDefn TLVS_WscAssocRes[ ] = {
@@ -5148,7 +5135,7 @@
return status;
} /* End dot11fUnpackIeWscAssocRes. */
-#define SigIeWscAssocRes ( 0x007c )
+#define SigIeWscAssocRes ( 0x007b )
static const tTLVDefn TLVS_WscBeacon[ ] = {
@@ -5173,7 +5160,7 @@
return status;
} /* End dot11fUnpackIeWscBeacon. */
-#define SigIeWscBeacon ( 0x007d )
+#define SigIeWscBeacon ( 0x007c )
static const tTLVDefn TLVS_WscBeaconProbeRes[ ] = {
@@ -5206,7 +5193,7 @@
return status;
} /* End dot11fUnpackIeWscBeaconProbeRes. */
-#define SigIeWscBeaconProbeRes ( 0x007e )
+#define SigIeWscBeaconProbeRes ( 0x007d )
tANI_U32 dot11fUnpackIeWscIEOpaque(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEWscIEOpaque *pDst)
@@ -5226,7 +5213,7 @@
return status;
} /* End dot11fUnpackIeWscIEOpaque. */
-#define SigIeWscIEOpaque ( 0x007f )
+#define SigIeWscIEOpaque ( 0x007e )
static const tTLVDefn TLVS_WscProbeReq[ ] = {
@@ -5257,7 +5244,7 @@
return status;
} /* End dot11fUnpackIeWscProbeReq. */
-#define SigIeWscProbeReq ( 0x0080 )
+#define SigIeWscProbeReq ( 0x007f )
static const tTLVDefn TLVS_WscProbeRes[ ] = {
@@ -5290,7 +5277,7 @@
return status;
} /* End dot11fUnpackIeWscProbeRes. */
-#define SigIeWscProbeRes ( 0x0081 )
+#define SigIeWscProbeRes ( 0x0080 )
static const tTLVDefn TLVS_WscReassocRes[ ] = {
@@ -5309,7 +5296,7 @@
return status;
} /* End dot11fUnpackIeWscReassocRes. */
-#define SigIeWscReassocRes ( 0x0082 )
+#define SigIeWscReassocRes ( 0x0081 )
tANI_U32 dot11fUnpackIeext_chan_switch_ann(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEext_chan_switch_ann *pDst)
@@ -5332,7 +5319,7 @@
return status;
} /* End dot11fUnpackIeext_chan_switch_ann. */
-#define SigIeext_chan_switch_ann ( 0x0083 )
+#define SigIeext_chan_switch_ann ( 0x0082 )
tANI_U32 dot11fUnpackIesec_chan_offset_ele(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEsec_chan_offset_ele *pDst)
@@ -5346,7 +5333,43 @@
return status;
} /* End dot11fUnpackIesec_chan_offset_ele. */
-#define SigIesec_chan_offset_ele ( 0x0084 )
+#define SigIesec_chan_offset_ele ( 0x0083 )
+
+
+ static const tFFDefn FFS_vendor2_ie[ ] = {
+ { NULL, 0, 0, 0,},
+ };
+
+ static const tIEDefn IES_vendor2_ie[ ] = {
+ {offsetof(tDot11fIEvendor2_ie, VHTCaps), offsetof(tDot11fIEVHTCaps, present), 0, "VHTCaps" , 0, 14, 14, SigIeVHTCaps, {0, 0, 0, 0, 0}, 0, DOT11F_EID_VHTCAPS, 0, },
+ {offsetof(tDot11fIEvendor2_ie, VHTOperation), offsetof(tDot11fIEVHTOperation, present), 0, "VHTOperation" , 0, 7, 7, SigIeVHTOperation, {0, 0, 0, 0, 0}, 0, DOT11F_EID_VHTOPERATION, 0, },
+ {0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, },
+ };
+
+tANI_U32 dot11fUnpackIevendor2_ie(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U8 ielen, tDot11fIEvendor2_ie *pDst)
+{
+ tANI_U32 status = DOT11F_PARSE_SUCCESS;
+ (void) pBuf; (void)ielen; /* Shutup the compiler */
+ if (pDst->present) status = DOT11F_DUPLICATE_IE;
+ pDst->present = 1;
+ pDst->type = *pBuf;
+ pBuf += 1;
+ ielen -= (tANI_U8)1;
+ pDst->sub_type = *pBuf;
+ pBuf += 1;
+ ielen -= (tANI_U8)1;
+ (void)pCtx;
+ status |= UnpackCore(pCtx,
+ pBuf,
+ ielen,
+ FFS_vendor2_ie,
+ IES_vendor2_ie,
+ ( tANI_U8* )pDst,
+ sizeof(*pDst));
+ return status;
+} /* End dot11fUnpackIevendor2_ie. */
+
+#define SigIevendor2_ie ( 0x0084 )
static const tFFDefn FFS_AddBAReq[] = {
@@ -6006,6 +6029,7 @@
{offsetof(tDot11fAssocRequest, ExtCap), offsetof(tDot11fIEExtCap, present), 0, "ExtCap" , 0, 10, 11, SigIeExtCap, {0, 0, 0, 0, 0}, 0, DOT11F_EID_EXTCAP, 0, },
{offsetof(tDot11fAssocRequest, OperatingMode), offsetof(tDot11fIEOperatingMode, present), 0, "OperatingMode" , 0, 3, 3, SigIeOperatingMode, {0, 0, 0, 0, 0}, 0, DOT11F_EID_OPERATINGMODE, 0, },
{offsetof(tDot11fAssocRequest, QosMapSet), offsetof(tDot11fIEQosMapSet, present), 0, "QosMapSet" , 0, 2, 62, SigIeQosMapSet, {0, 0, 0, 0, 0}, 0, DOT11F_EID_QOSMAPSET, 0, },
+ {offsetof(tDot11fAssocRequest, vendor2_ie), offsetof(tDot11fIEvendor2_ie, present), 0, "vendor2_ie" , 0, 7, 28, SigIevendor2_ie, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2_IE, 0, },
{0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, };
tANI_U32 dot11fUnpackAssocRequest(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U32 nBuf, tDot11fAssocRequest *pFrm)
@@ -6407,6 +6431,62 @@
FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("num_dscp_exceptions: %d.\n"), pFrm->QosMapSet.num_dscp_exceptions);
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* ) pFrm->QosMapSet.dscp_exceptions, pFrm->QosMapSet.num_dscp_exceptions);
}
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
+ }
}
# endif // DOT11F_DUMP_FRAMES
return status;
@@ -6447,6 +6527,7 @@
{offsetof(tDot11fAssocResponse, ExtCap), offsetof(tDot11fIEExtCap, present), 0, "ExtCap" , 0, 10, 11, SigIeExtCap, {0, 0, 0, 0, 0}, 0, DOT11F_EID_EXTCAP, 0, },
{offsetof(tDot11fAssocResponse, OBSSScanParameters), offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters" , 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, },
{offsetof(tDot11fAssocResponse, QosMapSet), offsetof(tDot11fIEQosMapSet, present), 0, "QosMapSet" , 0, 2, 62, SigIeQosMapSet, {0, 0, 0, 0, 0}, 0, DOT11F_EID_QOSMAPSET, 0, },
+ {offsetof(tDot11fAssocResponse, vendor2_ie), offsetof(tDot11fIEvendor2_ie, present), 0, "vendor2_ie" , 0, 7, 28, SigIevendor2_ie, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2_IE, 0, },
{0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, };
tANI_U32 dot11fUnpackAssocResponse(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U32 nBuf, tDot11fAssocResponse *pFrm)
@@ -7342,6 +7423,62 @@
FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("num_dscp_exceptions: %d.\n"), pFrm->QosMapSet.num_dscp_exceptions);
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* ) pFrm->QosMapSet.dscp_exceptions, pFrm->QosMapSet.num_dscp_exceptions);
}
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
+ }
}
# endif // DOT11F_DUMP_FRAMES
return status;
@@ -7811,7 +7948,7 @@
{offsetof(tDot11fBeacon, WiderBWChanSwitchAnn), offsetof(tDot11fIEWiderBWChanSwitchAnn, present), 0, "WiderBWChanSwitchAnn" , 0, 5, 5, SigIeWiderBWChanSwitchAnn, {0, 0, 0, 0, 0}, 0, DOT11F_EID_WIDERBWCHANSWITCHANN, 0, },
{offsetof(tDot11fBeacon, OBSSScanParameters), offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters" , 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, },
{offsetof(tDot11fBeacon, Vendor1IE), offsetof(tDot11fIEVendor1IE, present), 0, "Vendor1IE" , 0, 5, 5, SigIeVendor1IE, {0, 16, 24, 0, 0}, 3, DOT11F_EID_VENDOR1IE, 0, },
- {offsetof(tDot11fBeacon, Vendor2IE), offsetof(tDot11fIEVendor2IE, present), 0, "Vendor2IE" , 0, 5, 5, SigIeVendor2IE, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2IE, 0, },
+ {offsetof(tDot11fBeacon, vendor2_ie), offsetof(tDot11fIEvendor2_ie, present), 0, "vendor2_ie" , 0, 7, 28, SigIevendor2_ie, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2_IE, 0, },
{offsetof(tDot11fBeacon, Vendor3IE), offsetof(tDot11fIEVendor3IE, present), 0, "Vendor3IE" , 0, 5, 5, SigIeVendor3IE, {0, 22, 50, 0, 0}, 3, DOT11F_EID_VENDOR3IE, 0, },
{offsetof(tDot11fBeacon, ChannelSwitchWrapper), offsetof(tDot11fIEChannelSwitchWrapper, present), 0, "ChannelSwitchWrapper" , 0, 2, 7, SigIeChannelSwitchWrapper, {0, 0, 0, 0, 0}, 0, DOT11F_EID_CHANNELSWITCHWRAPPER, 0, },
{offsetof(tDot11fBeacon, QComVendorIE), offsetof(tDot11fIEQComVendorIE, present), 0, "QComVendorIE" , 0, 7, 7, SigIeQComVendorIE, {0, 160, 198, 0, 0}, 3, DOT11F_EID_QCOMVENDORIE, 0, },
@@ -8687,13 +8824,61 @@
else
{
}
- FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("Vendor2IE:\n"));
- if (!pFrm->Vendor2IE.present)
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
{
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("Not present.\n"));
}
else
{
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
}
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("Vendor3IE:\n"));
if (!pFrm->Vendor3IE.present)
@@ -8879,7 +9064,7 @@
{offsetof(tDot11fBeacon2, WiderBWChanSwitchAnn), offsetof(tDot11fIEWiderBWChanSwitchAnn, present), 0, "WiderBWChanSwitchAnn" , 0, 5, 5, SigIeWiderBWChanSwitchAnn, {0, 0, 0, 0, 0}, 0, DOT11F_EID_WIDERBWCHANSWITCHANN, 0, },
{offsetof(tDot11fBeacon2, OBSSScanParameters), offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters" , 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, },
{offsetof(tDot11fBeacon2, Vendor1IE), offsetof(tDot11fIEVendor1IE, present), 0, "Vendor1IE" , 0, 5, 5, SigIeVendor1IE, {0, 16, 24, 0, 0}, 3, DOT11F_EID_VENDOR1IE, 0, },
- {offsetof(tDot11fBeacon2, Vendor2IE), offsetof(tDot11fIEVendor2IE, present), 0, "Vendor2IE" , 0, 5, 5, SigIeVendor2IE, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2IE, 0, },
+ {offsetof(tDot11fBeacon2, vendor2_ie), offsetof(tDot11fIEvendor2_ie, present), 0, "vendor2_ie" , 0, 7, 28, SigIevendor2_ie, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2_IE, 0, },
{offsetof(tDot11fBeacon2, Vendor3IE), offsetof(tDot11fIEVendor3IE, present), 0, "Vendor3IE" , 0, 5, 5, SigIeVendor3IE, {0, 22, 50, 0, 0}, 3, DOT11F_EID_VENDOR3IE, 0, },
{offsetof(tDot11fBeacon2, ChannelSwitchWrapper), offsetof(tDot11fIEChannelSwitchWrapper, present), 0, "ChannelSwitchWrapper" , 0, 2, 7, SigIeChannelSwitchWrapper, {0, 0, 0, 0, 0}, 0, DOT11F_EID_CHANNELSWITCHWRAPPER, 0, },
{offsetof(tDot11fBeacon2, QComVendorIE), offsetof(tDot11fIEQComVendorIE, present), 0, "QComVendorIE" , 0, 7, 7, SigIeQComVendorIE, {0, 160, 198, 0, 0}, 3, DOT11F_EID_QCOMVENDORIE, 0, },
@@ -9603,13 +9788,61 @@
else
{
}
- FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("Vendor2IE:\n"));
- if (!pFrm->Vendor2IE.present)
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
{
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("Not present.\n"));
}
else
{
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
}
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("Vendor3IE:\n"));
if (!pFrm->Vendor3IE.present)
@@ -9715,7 +9948,7 @@
{offsetof(tDot11fBeaconIEs, WiderBWChanSwitchAnn), offsetof(tDot11fIEWiderBWChanSwitchAnn, present), 0, "WiderBWChanSwitchAnn" , 0, 5, 5, SigIeWiderBWChanSwitchAnn, {0, 0, 0, 0, 0}, 0, DOT11F_EID_WIDERBWCHANSWITCHANN, 0, },
{offsetof(tDot11fBeaconIEs, OBSSScanParameters), offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters" , 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, },
{offsetof(tDot11fBeaconIEs, Vendor1IE), offsetof(tDot11fIEVendor1IE, present), 0, "Vendor1IE" , 0, 5, 5, SigIeVendor1IE, {0, 16, 24, 0, 0}, 3, DOT11F_EID_VENDOR1IE, 0, },
- {offsetof(tDot11fBeaconIEs, Vendor2IE), offsetof(tDot11fIEVendor2IE, present), 0, "Vendor2IE" , 0, 5, 5, SigIeVendor2IE, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2IE, 0, },
+ {offsetof(tDot11fBeaconIEs, vendor2_ie), offsetof(tDot11fIEvendor2_ie, present), 0, "vendor2_ie" , 0, 7, 28, SigIevendor2_ie, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2_IE, 0, },
{offsetof(tDot11fBeaconIEs, Vendor3IE), offsetof(tDot11fIEVendor3IE, present), 0, "Vendor3IE" , 0, 5, 5, SigIeVendor3IE, {0, 22, 50, 0, 0}, 3, DOT11F_EID_VENDOR3IE, 0, },
{offsetof(tDot11fBeaconIEs, ChannelSwitchWrapper), offsetof(tDot11fIEChannelSwitchWrapper, present), 0, "ChannelSwitchWrapper" , 0, 2, 7, SigIeChannelSwitchWrapper, {0, 0, 0, 0, 0}, 0, DOT11F_EID_CHANNELSWITCHWRAPPER, 0, },
{offsetof(tDot11fBeaconIEs, QComVendorIE), offsetof(tDot11fIEQComVendorIE, present), 0, "QComVendorIE" , 0, 7, 7, SigIeQComVendorIE, {0, 160, 198, 0, 0}, 3, DOT11F_EID_QCOMVENDORIE, 0, },
@@ -10698,13 +10931,61 @@
else
{
}
- FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("Vendor2IE:\n"));
- if (!pFrm->Vendor2IE.present)
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
{
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("Not present.\n"));
}
else
{
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
}
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("Vendor3IE:\n"));
if (!pFrm->Vendor3IE.present)
@@ -13091,7 +13372,7 @@
{offsetof(tDot11fProbeResponse, ExtCap), offsetof(tDot11fIEExtCap, present), 0, "ExtCap" , 0, 10, 11, SigIeExtCap, {0, 0, 0, 0, 0}, 0, DOT11F_EID_EXTCAP, 0, },
{offsetof(tDot11fProbeResponse, OBSSScanParameters), offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters" , 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, },
{offsetof(tDot11fProbeResponse, Vendor1IE), offsetof(tDot11fIEVendor1IE, present), 0, "Vendor1IE" , 0, 5, 5, SigIeVendor1IE, {0, 16, 24, 0, 0}, 3, DOT11F_EID_VENDOR1IE, 0, },
- {offsetof(tDot11fProbeResponse, Vendor2IE), offsetof(tDot11fIEVendor2IE, present), 0, "Vendor2IE" , 0, 5, 5, SigIeVendor2IE, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2IE, 0, },
+ {offsetof(tDot11fProbeResponse, vendor2_ie), offsetof(tDot11fIEvendor2_ie, present), 0, "vendor2_ie" , 0, 7, 28, SigIevendor2_ie, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2_IE, 0, },
{offsetof(tDot11fProbeResponse, Vendor3IE), offsetof(tDot11fIEVendor3IE, present), 0, "Vendor3IE" , 0, 5, 5, SigIeVendor3IE, {0, 22, 50, 0, 0}, 3, DOT11F_EID_VENDOR3IE, 0, },
{offsetof(tDot11fProbeResponse, ChannelSwitchWrapper), offsetof(tDot11fIEChannelSwitchWrapper, present), 0, "ChannelSwitchWrapper" , 0, 2, 7, SigIeChannelSwitchWrapper, {0, 0, 0, 0, 0}, 0, DOT11F_EID_CHANNELSWITCHWRAPPER, 0, },
{offsetof(tDot11fProbeResponse, QComVendorIE), offsetof(tDot11fIEQComVendorIE, present), 0, "QComVendorIE" , 0, 7, 7, SigIeQComVendorIE, {0, 160, 198, 0, 0}, 3, DOT11F_EID_QCOMVENDORIE, 0, },
@@ -14021,13 +14302,61 @@
else
{
}
- FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("Vendor2IE:\n"));
- if (!pFrm->Vendor2IE.present)
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
{
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("Not present.\n"));
}
else
{
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
}
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("Vendor3IE:\n"));
if (!pFrm->Vendor3IE.present)
@@ -14495,6 +14824,7 @@
{offsetof(tDot11fReAssocRequest, ExtCap), offsetof(tDot11fIEExtCap, present), 0, "ExtCap" , 0, 10, 11, SigIeExtCap, {0, 0, 0, 0, 0}, 0, DOT11F_EID_EXTCAP, 0, },
{offsetof(tDot11fReAssocRequest, OperatingMode), offsetof(tDot11fIEOperatingMode, present), 0, "OperatingMode" , 0, 3, 3, SigIeOperatingMode, {0, 0, 0, 0, 0}, 0, DOT11F_EID_OPERATINGMODE, 0, },
{offsetof(tDot11fReAssocRequest, QosMapSet), offsetof(tDot11fIEQosMapSet, present), 0, "QosMapSet" , 0, 2, 62, SigIeQosMapSet, {0, 0, 0, 0, 0}, 0, DOT11F_EID_QOSMAPSET, 0, },
+ {offsetof(tDot11fReAssocRequest, vendor2_ie), offsetof(tDot11fIEvendor2_ie, present), 0, "vendor2_ie" , 0, 7, 28, SigIevendor2_ie, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2_IE, 0, },
{0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, };
tANI_U32 dot11fUnpackReAssocRequest(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U32 nBuf, tDot11fReAssocRequest *pFrm)
@@ -15283,6 +15613,62 @@
FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("num_dscp_exceptions: %d.\n"), pFrm->QosMapSet.num_dscp_exceptions);
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* ) pFrm->QosMapSet.dscp_exceptions, pFrm->QosMapSet.num_dscp_exceptions);
}
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
+ }
}
# endif // DOT11F_DUMP_FRAMES
return status;
@@ -15324,6 +15710,7 @@
{offsetof(tDot11fReAssocResponse, ExtCap), offsetof(tDot11fIEExtCap, present), 0, "ExtCap" , 0, 10, 11, SigIeExtCap, {0, 0, 0, 0, 0}, 0, DOT11F_EID_EXTCAP, 0, },
{offsetof(tDot11fReAssocResponse, OBSSScanParameters), offsetof(tDot11fIEOBSSScanParameters, present), 0, "OBSSScanParameters" , 0, 16, 16, SigIeOBSSScanParameters, {0, 0, 0, 0, 0}, 0, DOT11F_EID_OBSSSCANPARAMETERS, 0, },
{offsetof(tDot11fReAssocResponse, QosMapSet), offsetof(tDot11fIEQosMapSet, present), 0, "QosMapSet" , 0, 2, 62, SigIeQosMapSet, {0, 0, 0, 0, 0}, 0, DOT11F_EID_QOSMAPSET, 0, },
+ {offsetof(tDot11fReAssocResponse, vendor2_ie), offsetof(tDot11fIEvendor2_ie, present), 0, "vendor2_ie" , 0, 7, 28, SigIevendor2_ie, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2_IE, 0, },
{0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, };
tANI_U32 dot11fUnpackReAssocResponse(tpAniSirGlobal pCtx, tANI_U8 *pBuf, tANI_U32 nBuf, tDot11fReAssocResponse *pFrm)
@@ -16226,6 +16613,62 @@
FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("num_dscp_exceptions: %d.\n"), pFrm->QosMapSet.num_dscp_exceptions);
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* ) pFrm->QosMapSet.dscp_exceptions, pFrm->QosMapSet.num_dscp_exceptions);
}
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
+ }
}
# endif // DOT11F_DUMP_FRAMES
return status;
@@ -18100,7 +18543,7 @@
{offsetof(tDot11fTimingAdvertisementFrame, TimeAdvertisement), offsetof(tDot11fIETimeAdvertisement, present), 0, "TimeAdvertisement" , 0, 18, 18, SigIeTimeAdvertisement, {0, 0, 0, 0, 0}, 0, DOT11F_EID_TIMEADVERTISEMENT, 0, },
{offsetof(tDot11fTimingAdvertisementFrame, ExtCap), offsetof(tDot11fIEExtCap, present), 0, "ExtCap" , 0, 10, 11, SigIeExtCap, {0, 0, 0, 0, 0}, 0, DOT11F_EID_EXTCAP, 0, },
{offsetof(tDot11fTimingAdvertisementFrame, Vendor1IE), offsetof(tDot11fIEVendor1IE, present), 0, "Vendor1IE" , 0, 5, 5, SigIeVendor1IE, {0, 16, 24, 0, 0}, 3, DOT11F_EID_VENDOR1IE, 0, },
- {offsetof(tDot11fTimingAdvertisementFrame, Vendor2IE), offsetof(tDot11fIEVendor2IE, present), 0, "Vendor2IE" , 0, 5, 5, SigIeVendor2IE, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2IE, 0, },
+ {offsetof(tDot11fTimingAdvertisementFrame, vendor2_ie), offsetof(tDot11fIEvendor2_ie, present), 0, "vendor2_ie" , 0, 7, 28, SigIevendor2_ie, {0, 144, 76, 0, 0}, 3, DOT11F_EID_VENDOR2_IE, 0, },
{offsetof(tDot11fTimingAdvertisementFrame, Vendor3IE), offsetof(tDot11fIEVendor3IE, present), 0, "Vendor3IE" , 0, 5, 5, SigIeVendor3IE, {0, 22, 50, 0, 0}, 3, DOT11F_EID_VENDOR3IE, 0, },
{0, 0, 0, NULL, 0, 0, 0, 0, {0, 0, 0, 0, 0}, 0, 0xff, 0, }, };
@@ -18185,13 +18628,61 @@
else
{
}
- FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("Vendor2IE:\n"));
- if (!pFrm->Vendor2IE.present)
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
{
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("Not present.\n"));
}
else
{
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
}
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("Vendor3IE:\n"));
if (!pFrm->Vendor3IE.present)
@@ -18852,6 +19343,12 @@
case SigIeTSPEC:
status |= dot11fUnpackIeTSPEC(pCtx, pBufRemaining, len, ( tDot11fIETSPEC* )(pFrm + pIe->offset + sizeof(tDot11fIETSPEC)*countOffset) );
break;
+ case SigIeVHTCaps:
+ status |= dot11fUnpackIeVHTCaps(pCtx, pBufRemaining, len, ( tDot11fIEVHTCaps* )(pFrm + pIe->offset + sizeof(tDot11fIEVHTCaps)*countOffset) );
+ break;
+ case SigIeVHTOperation:
+ status |= dot11fUnpackIeVHTOperation(pCtx, pBufRemaining, len, ( tDot11fIEVHTOperation* )(pFrm + pIe->offset + sizeof(tDot11fIEVHTOperation)*countOffset) );
+ break;
case SigIeWMMSchedule:
status |= dot11fUnpackIeWMMSchedule(pCtx, pBufRemaining, len, ( tDot11fIEWMMSchedule* )(pFrm + pIe->offset + sizeof(tDot11fIEWMMSchedule)*countOffset) );
break;
@@ -19117,21 +19614,12 @@
case SigIeTimeoutInterval:
status |= dot11fUnpackIeTimeoutInterval(pCtx, pBufRemaining, len, ( tDot11fIETimeoutInterval* )(pFrm + pIe->offset + sizeof(tDot11fIETimeoutInterval)*countOffset) );
break;
- case SigIeVHTCaps:
- status |= dot11fUnpackIeVHTCaps(pCtx, pBufRemaining, len, ( tDot11fIEVHTCaps* )(pFrm + pIe->offset + sizeof(tDot11fIEVHTCaps)*countOffset) );
- break;
case SigIeVHTExtBssLoad:
status |= dot11fUnpackIeVHTExtBssLoad(pCtx, pBufRemaining, len, ( tDot11fIEVHTExtBssLoad* )(pFrm + pIe->offset + sizeof(tDot11fIEVHTExtBssLoad)*countOffset) );
break;
- case SigIeVHTOperation:
- status |= dot11fUnpackIeVHTOperation(pCtx, pBufRemaining, len, ( tDot11fIEVHTOperation* )(pFrm + pIe->offset + sizeof(tDot11fIEVHTOperation)*countOffset) );
- break;
case SigIeVendor1IE:
status |= dot11fUnpackIeVendor1IE(pCtx, pBufRemaining, len, ( tDot11fIEVendor1IE* )(pFrm + pIe->offset + sizeof(tDot11fIEVendor1IE)*countOffset) );
break;
- case SigIeVendor2IE:
- status |= dot11fUnpackIeVendor2IE(pCtx, pBufRemaining, len, ( tDot11fIEVendor2IE* )(pFrm + pIe->offset + sizeof(tDot11fIEVendor2IE)*countOffset) );
- break;
case SigIeVendor3IE:
status |= dot11fUnpackIeVendor3IE(pCtx, pBufRemaining, len, ( tDot11fIEVendor3IE* )(pFrm + pIe->offset + sizeof(tDot11fIEVendor3IE)*countOffset) );
break;
@@ -19198,6 +19686,9 @@
case SigIesec_chan_offset_ele:
status |= dot11fUnpackIesec_chan_offset_ele(pCtx, pBufRemaining, len, ( tDot11fIEsec_chan_offset_ele* )(pFrm + pIe->offset + sizeof(tDot11fIEsec_chan_offset_ele)*countOffset) );
break;
+ case SigIevendor2_ie:
+ status |= dot11fUnpackIevendor2_ie(pCtx, pBufRemaining, len, ( tDot11fIEvendor2_ie* )(pFrm + pIe->offset + sizeof(tDot11fIEvendor2_ie)*countOffset) );
+ break;
default:
FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR"
": I don't know about the IE signature %d"
@@ -20324,6 +20815,20 @@
return status;
} /* End dot11fGetPackedIEWscReassocRes. */
+tANI_U32 dot11fGetPackedIEvendor2_ie(tpAniSirGlobal pCtx, tDot11fIEvendor2_ie *pIe, tANI_U32 *pnNeeded)
+{
+ tANI_U32 status = DOT11F_PARSE_SUCCESS;
+ (void)pCtx;
+ while ( pIe->present )
+ {
+ *pnNeeded += 1;
+ *pnNeeded += 1;
+ status = GetPackedSizeCore(pCtx, ( tANI_U8* )pIe, pnNeeded, IES_vendor2_ie);
+ break;
+ }
+ return status;
+} /* End dot11fGetPackedIEvendor2_ie. */
+
tANI_U32 dot11fGetPackedAddBAReqSize(tpAniSirGlobal pCtx, tDot11fAddBAReq *pFrm, tANI_U32 *pnNeeded)
{
tANI_U32 status = 0;
@@ -20969,6 +21474,16 @@
byteCount = 55;
pIePresent = ( (tDot11fIETSPEC* )(pFrm + pIe->offset + offset * i ))->present;
break;
+ case SigIeVHTCaps:
+ offset = sizeof(tDot11fIEVHTCaps);
+ byteCount = 12;
+ pIePresent = ( (tDot11fIEVHTCaps* )(pFrm + pIe->offset + offset * i ))->present;
+ break;
+ case SigIeVHTOperation:
+ offset = sizeof(tDot11fIEVHTOperation);
+ byteCount = 5;
+ pIePresent = ( (tDot11fIEVHTOperation* )(pFrm + pIe->offset + offset * i ))->present;
+ break;
case SigIeWMMSchedule:
offset = sizeof(tDot11fIEWMMSchedule);
byteCount = 15;
@@ -21356,31 +21871,16 @@
byteCount = 5;
pIePresent = ( (tDot11fIETimeoutInterval* )(pFrm + pIe->offset + offset * i ))->present;
break;
- case SigIeVHTCaps:
- offset = sizeof(tDot11fIEVHTCaps);
- byteCount = 12;
- pIePresent = ( (tDot11fIEVHTCaps* )(pFrm + pIe->offset + offset * i ))->present;
- break;
case SigIeVHTExtBssLoad:
offset = sizeof(tDot11fIEVHTExtBssLoad);
byteCount = 5;
pIePresent = ( (tDot11fIEVHTExtBssLoad* )(pFrm + pIe->offset + offset * i ))->present;
break;
- case SigIeVHTOperation:
- offset = sizeof(tDot11fIEVHTOperation);
- byteCount = 5;
- pIePresent = ( (tDot11fIEVHTOperation* )(pFrm + pIe->offset + offset * i ))->present;
- break;
case SigIeVendor1IE:
offset = sizeof(tDot11fIEVendor1IE);
byteCount = 0;
pIePresent = ( (tDot11fIEVendor1IE* )(pFrm + pIe->offset + offset * i ))->present;
break;
- case SigIeVendor2IE:
- offset = sizeof(tDot11fIEVendor2IE);
- byteCount = 0;
- pIePresent = ( (tDot11fIEVendor2IE* )(pFrm + pIe->offset + offset * i ))->present;
- break;
case SigIeVendor3IE:
offset = sizeof(tDot11fIEVendor3IE);
byteCount = 0;
@@ -21481,6 +21981,10 @@
byteCount = 1;
pIePresent = ( (tDot11fIEsec_chan_offset_ele* )(pFrm + pIe->offset + offset * i ))->present;
break;
+ case SigIevendor2_ie:
+ offset = sizeof(tDot11fIEvendor2_ie);
+ status |= dot11fGetPackedIEvendor2_ie(pCtx, ( tDot11fIEvendor2_ie* )(pFrm + pIe->offset + offset * i ), pnNeeded);
+ break;
default:
FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR-- I don"
"'t know about the IE signature %d; this is most l"
@@ -24405,6 +24909,120 @@
return DOT11F_PARSE_SUCCESS;
} /* End dot11fPackIeTSPEC. */
+tANI_U32 dot11fPackIeVHTCaps(tpAniSirGlobal pCtx,
+ tDot11fIEVHTCaps *pSrc,
+ tANI_U8 *pBuf,
+ tANI_U32 nBuf,
+ tANI_U32 *pnConsumed)
+{
+ tANI_U8* pIeLen = 0;
+ tANI_U32 nConsumedOnEntry = *pnConsumed;
+ tANI_U32 nNeeded = 0U;
+ tANI_U32 tmp90__;
+ tANI_U16 tmp91__;
+ tANI_U16 tmp92__;
+ nNeeded += 12;
+ while ( pSrc->present )
+ {
+ if ( nNeeded > nBuf ) return DOT11F_BUFFER_OVERFLOW;
+ *pBuf = 191;
+ ++pBuf; ++(*pnConsumed);
+ pIeLen = pBuf;
+ ++pBuf; ++(*pnConsumed);
+ tmp90__ = 0U;
+ tmp90__ |= ( pSrc->maxMPDULen << 0 );
+ tmp90__ |= ( pSrc->supportedChannelWidthSet << 2 );
+ tmp90__ |= ( pSrc->ldpcCodingCap << 4 );
+ tmp90__ |= ( pSrc->shortGI80MHz << 5 );
+ tmp90__ |= ( pSrc->shortGI160and80plus80MHz << 6 );
+ tmp90__ |= ( pSrc->txSTBC << 7 );
+ tmp90__ |= ( pSrc->rxSTBC << 8 );
+ tmp90__ |= ( pSrc->suBeamFormerCap << 11 );
+ tmp90__ |= ( pSrc->suBeamformeeCap << 12 );
+ tmp90__ |= ( pSrc->csnofBeamformerAntSup << 13 );
+ tmp90__ |= ( pSrc->numSoundingDim << 16 );
+ tmp90__ |= ( pSrc->muBeamformerCap << 19 );
+ tmp90__ |= ( pSrc->muBeamformeeCap << 20 );
+ tmp90__ |= ( pSrc->vhtTXOPPS << 21 );
+ tmp90__ |= ( pSrc->htcVHTCap << 22 );
+ tmp90__ |= ( pSrc->maxAMPDULenExp << 23 );
+ tmp90__ |= ( pSrc->vhtLinkAdaptCap << 26 );
+ tmp90__ |= ( pSrc->rxAntPattern << 28 );
+ tmp90__ |= ( pSrc->txAntPattern << 29 );
+ tmp90__ |= ( pSrc->reserved1 << 30 );
+ frameshtonl(pCtx, pBuf, tmp90__, 0);
+ *pnConsumed += 4;
+ pBuf += 4;
+ nBuf -= 4 ;
+ frameshtons(pCtx, pBuf, pSrc->rxMCSMap, 0);
+ *pnConsumed += 2;
+ pBuf += 2;
+ tmp91__ = 0U;
+ tmp91__ |= ( pSrc->rxHighSupDataRate << 0 );
+ tmp91__ |= ( pSrc->reserved2 << 13 );
+ frameshtons(pCtx, pBuf, tmp91__, 0);
+ *pnConsumed += 2;
+ pBuf += 2;
+ nBuf -= 2 ;
+ frameshtons(pCtx, pBuf, pSrc->txMCSMap, 0);
+ *pnConsumed += 2;
+ pBuf += 2;
+ tmp92__ = 0U;
+ tmp92__ |= ( pSrc->txSupDataRate << 0 );
+ tmp92__ |= ( pSrc->reserved3 << 13 );
+ frameshtons(pCtx, pBuf, tmp92__, 0);
+ *pnConsumed += 2;
+ // fieldsEndFlag = 1
+ nBuf -= 2 ;
+ break;
+ }
+ (void)pCtx;
+ if (pIeLen)
+ {
+ *pIeLen = *pnConsumed - nConsumedOnEntry - 2;
+ }
+ return DOT11F_PARSE_SUCCESS;
+} /* End dot11fPackIeVHTCaps. */
+
+tANI_U32 dot11fPackIeVHTOperation(tpAniSirGlobal pCtx,
+ tDot11fIEVHTOperation *pSrc,
+ tANI_U8 *pBuf,
+ tANI_U32 nBuf,
+ tANI_U32 *pnConsumed)
+{
+ tANI_U8* pIeLen = 0;
+ tANI_U32 nConsumedOnEntry = *pnConsumed;
+ tANI_U32 nNeeded = 0U;
+ nNeeded += 5;
+ while ( pSrc->present )
+ {
+ if ( nNeeded > nBuf ) return DOT11F_BUFFER_OVERFLOW;
+ *pBuf = 192;
+ ++pBuf; ++(*pnConsumed);
+ pIeLen = pBuf;
+ ++pBuf; ++(*pnConsumed);
+ *pBuf = pSrc->chanWidth;
+ *pnConsumed += 1;
+ pBuf += 1;
+ *pBuf = pSrc->chanCenterFreqSeg1;
+ *pnConsumed += 1;
+ pBuf += 1;
+ *pBuf = pSrc->chanCenterFreqSeg2;
+ *pnConsumed += 1;
+ pBuf += 1;
+ frameshtons(pCtx, pBuf, pSrc->basicMCSSet, 0);
+ *pnConsumed += 2;
+ // fieldsEndFlag = 1
+ break;
+ }
+ (void)pCtx;
+ if (pIeLen)
+ {
+ *pIeLen = *pnConsumed - nConsumedOnEntry - 2;
+ }
+ return DOT11F_PARSE_SUCCESS;
+} /* End dot11fPackIeVHTOperation. */
+
tANI_U32 dot11fPackIeWMMSchedule(tpAniSirGlobal pCtx,
tDot11fIEWMMSchedule *pSrc,
tANI_U8 *pBuf,
@@ -24414,7 +25032,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U16 tmp90__;
+ tANI_U16 tmp93__;
nNeeded += 15;
while ( pSrc->present )
{
@@ -24436,12 +25054,12 @@
*pBuf = pSrc->version;
*pnConsumed += 1;
pBuf += 1;
- tmp90__ = 0U;
- tmp90__ |= ( pSrc->aggregation << 0 );
- tmp90__ |= ( pSrc->tsid << 1 );
- tmp90__ |= ( pSrc->direction << 5 );
- tmp90__ |= ( pSrc->reserved << 7 );
- frameshtons(pCtx, pBuf, tmp90__, 0);
+ tmp93__ = 0U;
+ tmp93__ |= ( pSrc->aggregation << 0 );
+ tmp93__ |= ( pSrc->tsid << 1 );
+ tmp93__ |= ( pSrc->direction << 5 );
+ tmp93__ |= ( pSrc->reserved << 7 );
+ frameshtons(pCtx, pBuf, tmp93__, 0);
*pnConsumed += 2;
pBuf += 2;
nBuf -= 2 ;
@@ -24680,9 +25298,9 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U16 tmp91__;
- tANI_U8 tmp92__;
- tANI_U16 tmp93__;
+ tANI_U16 tmp94__;
+ tANI_U8 tmp95__;
+ tANI_U16 tmp96__;
nNeeded += 38;
while ( pSrc->present )
{
@@ -24704,30 +25322,30 @@
*pBuf = pSrc->version;
*pnConsumed += 1;
pBuf += 1;
- tmp91__ = 0U;
- tmp91__ |= ( pSrc->traffic_type << 0 );
- tmp91__ |= ( pSrc->tsid << 1 );
- tmp91__ |= ( pSrc->direction << 5 );
- tmp91__ |= ( pSrc->access_policy << 7 );
- tmp91__ |= ( pSrc->aggregation << 9 );
- tmp91__ |= ( pSrc->psb << 10 );
- tmp91__ |= ( pSrc->user_priority << 11 );
- tmp91__ |= ( pSrc->tsinfo_ack_pol << 14 );
- frameshtons(pCtx, pBuf, tmp91__, 0);
+ tmp94__ = 0U;
+ tmp94__ |= ( pSrc->traffic_type << 0 );
+ tmp94__ |= ( pSrc->tsid << 1 );
+ tmp94__ |= ( pSrc->direction << 5 );
+ tmp94__ |= ( pSrc->access_policy << 7 );
+ tmp94__ |= ( pSrc->aggregation << 9 );
+ tmp94__ |= ( pSrc->psb << 10 );
+ tmp94__ |= ( pSrc->user_priority << 11 );
+ tmp94__ |= ( pSrc->tsinfo_ack_pol << 14 );
+ frameshtons(pCtx, pBuf, tmp94__, 0);
*pnConsumed += 2;
pBuf += 2;
nBuf -= 2 ;
- tmp92__ = 0U;
- tmp92__ |= ( pSrc->tsinfo_rsvd << 0 );
- tmp92__ |= ( pSrc->burst_size_defn << 7 );
- *pBuf = tmp92__;
+ tmp95__ = 0U;
+ tmp95__ |= ( pSrc->tsinfo_rsvd << 0 );
+ tmp95__ |= ( pSrc->burst_size_defn << 7 );
+ *pBuf = tmp95__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
- tmp93__ = 0U;
- tmp93__ |= ( pSrc->size << 0 );
- tmp93__ |= ( pSrc->fixed << 15 );
- frameshtons(pCtx, pBuf, tmp93__, 0);
+ tmp96__ = 0U;
+ tmp96__ |= ( pSrc->size << 0 );
+ tmp96__ |= ( pSrc->fixed << 15 );
+ frameshtons(pCtx, pBuf, tmp96__, 0);
*pnConsumed += 2;
pBuf += 2;
nBuf -= 2 ;
@@ -25067,14 +25685,14 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp94__;
- tANI_U8 tmp95__;
- tANI_U8 tmp96__;
tANI_U8 tmp97__;
tANI_U8 tmp98__;
tANI_U8 tmp99__;
tANI_U8 tmp100__;
tANI_U8 tmp101__;
+ tANI_U8 tmp102__;
+ tANI_U8 tmp103__;
+ tANI_U8 tmp104__;
nNeeded += 18;
while ( pSrc->present )
{
@@ -25089,76 +25707,76 @@
*pBuf = pSrc->reserved;
*pnConsumed += 1;
pBuf += 1;
- tmp94__ = 0U;
- tmp94__ |= ( pSrc->acbe_aifsn << 0 );
- tmp94__ |= ( pSrc->acbe_acm << 4 );
- tmp94__ |= ( pSrc->acbe_aci << 5 );
- tmp94__ |= ( pSrc->unused1 << 7 );
- *pBuf = tmp94__;
+ tmp97__ = 0U;
+ tmp97__ |= ( pSrc->acbe_aifsn << 0 );
+ tmp97__ |= ( pSrc->acbe_acm << 4 );
+ tmp97__ |= ( pSrc->acbe_aci << 5 );
+ tmp97__ |= ( pSrc->unused1 << 7 );
+ *pBuf = tmp97__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
- tmp95__ = 0U;
- tmp95__ |= ( pSrc->acbe_acwmin << 0 );
- tmp95__ |= ( pSrc->acbe_acwmax << 4 );
- *pBuf = tmp95__;
+ tmp98__ = 0U;
+ tmp98__ |= ( pSrc->acbe_acwmin << 0 );
+ tmp98__ |= ( pSrc->acbe_acwmax << 4 );
+ *pBuf = tmp98__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
frameshtons(pCtx, pBuf, pSrc->acbe_txoplimit, 0);
*pnConsumed += 2;
pBuf += 2;
- tmp96__ = 0U;
- tmp96__ |= ( pSrc->acbk_aifsn << 0 );
- tmp96__ |= ( pSrc->acbk_acm << 4 );
- tmp96__ |= ( pSrc->acbk_aci << 5 );
- tmp96__ |= ( pSrc->unused2 << 7 );
- *pBuf = tmp96__;
+ tmp99__ = 0U;
+ tmp99__ |= ( pSrc->acbk_aifsn << 0 );
+ tmp99__ |= ( pSrc->acbk_acm << 4 );
+ tmp99__ |= ( pSrc->acbk_aci << 5 );
+ tmp99__ |= ( pSrc->unused2 << 7 );
+ *pBuf = tmp99__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
- tmp97__ = 0U;
- tmp97__ |= ( pSrc->acbk_acwmin << 0 );
- tmp97__ |= ( pSrc->acbk_acwmax << 4 );
- *pBuf = tmp97__;
+ tmp100__ = 0U;
+ tmp100__ |= ( pSrc->acbk_acwmin << 0 );
+ tmp100__ |= ( pSrc->acbk_acwmax << 4 );
+ *pBuf = tmp100__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
frameshtons(pCtx, pBuf, pSrc->acbk_txoplimit, 0);
*pnConsumed += 2;
pBuf += 2;
- tmp98__ = 0U;
- tmp98__ |= ( pSrc->acvi_aifsn << 0 );
- tmp98__ |= ( pSrc->acvi_acm << 4 );
- tmp98__ |= ( pSrc->acvi_aci << 5 );
- tmp98__ |= ( pSrc->unused3 << 7 );
- *pBuf = tmp98__;
+ tmp101__ = 0U;
+ tmp101__ |= ( pSrc->acvi_aifsn << 0 );
+ tmp101__ |= ( pSrc->acvi_acm << 4 );
+ tmp101__ |= ( pSrc->acvi_aci << 5 );
+ tmp101__ |= ( pSrc->unused3 << 7 );
+ *pBuf = tmp101__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
- tmp99__ = 0U;
- tmp99__ |= ( pSrc->acvi_acwmin << 0 );
- tmp99__ |= ( pSrc->acvi_acwmax << 4 );
- *pBuf = tmp99__;
+ tmp102__ = 0U;
+ tmp102__ |= ( pSrc->acvi_acwmin << 0 );
+ tmp102__ |= ( pSrc->acvi_acwmax << 4 );
+ *pBuf = tmp102__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
frameshtons(pCtx, pBuf, pSrc->acvi_txoplimit, 0);
*pnConsumed += 2;
pBuf += 2;
- tmp100__ = 0U;
- tmp100__ |= ( pSrc->acvo_aifsn << 0 );
- tmp100__ |= ( pSrc->acvo_acm << 4 );
- tmp100__ |= ( pSrc->acvo_aci << 5 );
- tmp100__ |= ( pSrc->unused4 << 7 );
- *pBuf = tmp100__;
+ tmp103__ = 0U;
+ tmp103__ |= ( pSrc->acvo_aifsn << 0 );
+ tmp103__ |= ( pSrc->acvo_acm << 4 );
+ tmp103__ |= ( pSrc->acvo_aci << 5 );
+ tmp103__ |= ( pSrc->unused4 << 7 );
+ *pBuf = tmp103__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
- tmp101__ = 0U;
- tmp101__ |= ( pSrc->acvo_acwmin << 0 );
- tmp101__ |= ( pSrc->acvo_acwmax << 4 );
- *pBuf = tmp101__;
+ tmp104__ = 0U;
+ tmp104__ |= ( pSrc->acvo_acwmin << 0 );
+ tmp104__ |= ( pSrc->acvo_acwmax << 4 );
+ *pBuf = tmp104__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
@@ -25184,7 +25802,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp102__;
+ tANI_U8 tmp105__;
nNeeded += 1;
while ( pSrc->present )
{
@@ -25193,12 +25811,12 @@
++pBuf; ++(*pnConsumed);
pIeLen = pBuf;
++pBuf; ++(*pnConsumed);
- tmp102__ = 0U;
- tmp102__ |= ( pSrc->non_erp_present << 0 );
- tmp102__ |= ( pSrc->use_prot << 1 );
- tmp102__ |= ( pSrc->barker_preamble << 2 );
- tmp102__ |= ( pSrc->unused << 3 );
- *pBuf = tmp102__;
+ tmp105__ = 0U;
+ tmp105__ |= ( pSrc->non_erp_present << 0 );
+ tmp105__ |= ( pSrc->use_prot << 1 );
+ tmp105__ |= ( pSrc->barker_preamble << 2 );
+ tmp105__ |= ( pSrc->unused << 3 );
+ *pBuf = tmp105__;
*pnConsumed += 1;
// fieldsEndFlag = 1
nBuf -= 1 ;
@@ -25259,7 +25877,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp103__;
+ tANI_U8 tmp106__;
nNeeded += 2;
while ( pSrc->present )
{
@@ -25279,10 +25897,10 @@
*pBuf = pSrc->mgmt_state;
*pnConsumed += 1;
pBuf += 1;
- tmp103__ = 0U;
- tmp103__ |= ( pSrc->mbssid_mask << 0 );
- tmp103__ |= ( pSrc->reserved << 3 );
- *pBuf = tmp103__;
+ tmp106__ = 0U;
+ tmp106__ |= ( pSrc->mbssid_mask << 0 );
+ tmp106__ |= ( pSrc->reserved << 3 );
+ *pBuf = tmp106__;
*pnConsumed += 1;
// fieldsEndFlag = 1
nBuf -= 1 ;
@@ -25643,7 +26261,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U16 tmp104__;
+ tANI_U16 tmp107__;
tANI_U32 status = DOT11F_PARSE_SUCCESS;
status = dot11fGetPackedIEFTInfo(pCtx, pSrc, &nNeeded);
if ( ! DOT11F_SUCCEEDED( status ) ) return status;
@@ -25654,10 +26272,10 @@
++pBuf; --nBuf; ++(*pnConsumed);
pIeLen = pBuf;
++pBuf; --nBuf; ++(*pnConsumed);
- tmp104__ = 0U;
- tmp104__ |= ( pSrc->reserved << 0 );
- tmp104__ |= ( pSrc->IECount << 8 );
- frameshtons(pCtx, pBuf, tmp104__, 0);
+ tmp107__ = 0U;
+ tmp107__ |= ( pSrc->reserved << 0 );
+ tmp107__ |= ( pSrc->IECount << 8 );
+ frameshtons(pCtx, pBuf, tmp107__, 0);
*pnConsumed += 2;
pBuf += 2;
nBuf -= 2 ;
@@ -25696,7 +26314,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp105__;
+ tANI_U8 tmp108__;
nNeeded += 1;
while ( pSrc->present )
{
@@ -25705,14 +26323,14 @@
++pBuf; ++(*pnConsumed);
pIeLen = pBuf;
++pBuf; ++(*pnConsumed);
- tmp105__ = 0U;
- tmp105__ |= ( pSrc->infoRequest << 0 );
- tmp105__ |= ( pSrc->fortyMHzIntolerant << 1 );
- tmp105__ |= ( pSrc->twentyMHzBssWidthReq << 2 );
- tmp105__ |= ( pSrc->obssScanExemptionReq << 3 );
- tmp105__ |= ( pSrc->obssScanExemptionGrant << 4 );
- tmp105__ |= ( pSrc->unused << 5 );
- *pBuf = tmp105__;
+ tmp108__ = 0U;
+ tmp108__ |= ( pSrc->infoRequest << 0 );
+ tmp108__ |= ( pSrc->fortyMHzIntolerant << 1 );
+ tmp108__ |= ( pSrc->twentyMHzBssWidthReq << 2 );
+ tmp108__ |= ( pSrc->obssScanExemptionReq << 3 );
+ tmp108__ |= ( pSrc->obssScanExemptionGrant << 4 );
+ tmp108__ |= ( pSrc->unused << 5 );
+ *pBuf = tmp108__;
*pnConsumed += 1;
// fieldsEndFlag = 1
nBuf -= 1 ;
@@ -25768,11 +26386,11 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U16 tmp106__;
- tANI_U8 tmp107__;
- tANI_U16 tmp108__;
- tANI_U32 tmp109__;
+ tANI_U16 tmp109__;
tANI_U8 tmp110__;
+ tANI_U16 tmp111__;
+ tANI_U32 tmp112__;
+ tANI_U8 tmp113__;
nNeeded += (pSrc->num_rsvd + 26);
while ( pSrc->present )
{
@@ -25781,77 +26399,77 @@
++pBuf; ++(*pnConsumed);
pIeLen = pBuf;
++pBuf; ++(*pnConsumed);
- tmp106__ = 0U;
- tmp106__ |= ( pSrc->advCodingCap << 0 );
- tmp106__ |= ( pSrc->supportedChannelWidthSet << 1 );
- tmp106__ |= ( pSrc->mimoPowerSave << 2 );
- tmp106__ |= ( pSrc->greenField << 4 );
- tmp106__ |= ( pSrc->shortGI20MHz << 5 );
- tmp106__ |= ( pSrc->shortGI40MHz << 6 );
- tmp106__ |= ( pSrc->txSTBC << 7 );
- tmp106__ |= ( pSrc->rxSTBC << 8 );
- tmp106__ |= ( pSrc->delayedBA << 10 );
- tmp106__ |= ( pSrc->maximalAMSDUsize << 11 );
- tmp106__ |= ( pSrc->dsssCckMode40MHz << 12 );
- tmp106__ |= ( pSrc->psmp << 13 );
- tmp106__ |= ( pSrc->stbcControlFrame << 14 );
- tmp106__ |= ( pSrc->lsigTXOPProtection << 15 );
- frameshtons(pCtx, pBuf, tmp106__, 0);
+ tmp109__ = 0U;
+ tmp109__ |= ( pSrc->advCodingCap << 0 );
+ tmp109__ |= ( pSrc->supportedChannelWidthSet << 1 );
+ tmp109__ |= ( pSrc->mimoPowerSave << 2 );
+ tmp109__ |= ( pSrc->greenField << 4 );
+ tmp109__ |= ( pSrc->shortGI20MHz << 5 );
+ tmp109__ |= ( pSrc->shortGI40MHz << 6 );
+ tmp109__ |= ( pSrc->txSTBC << 7 );
+ tmp109__ |= ( pSrc->rxSTBC << 8 );
+ tmp109__ |= ( pSrc->delayedBA << 10 );
+ tmp109__ |= ( pSrc->maximalAMSDUsize << 11 );
+ tmp109__ |= ( pSrc->dsssCckMode40MHz << 12 );
+ tmp109__ |= ( pSrc->psmp << 13 );
+ tmp109__ |= ( pSrc->stbcControlFrame << 14 );
+ tmp109__ |= ( pSrc->lsigTXOPProtection << 15 );
+ frameshtons(pCtx, pBuf, tmp109__, 0);
*pnConsumed += 2;
pBuf += 2;
nBuf -= 2 ;
- tmp107__ = 0U;
- tmp107__ |= ( pSrc->maxRxAMPDUFactor << 0 );
- tmp107__ |= ( pSrc->mpduDensity << 2 );
- tmp107__ |= ( pSrc->reserved1 << 5 );
- *pBuf = tmp107__;
+ tmp110__ = 0U;
+ tmp110__ |= ( pSrc->maxRxAMPDUFactor << 0 );
+ tmp110__ |= ( pSrc->mpduDensity << 2 );
+ tmp110__ |= ( pSrc->reserved1 << 5 );
+ *pBuf = tmp110__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
DOT11F_MEMCPY(pCtx, pBuf, pSrc->supportedMCSSet, 16);
*pnConsumed += 16;
pBuf += 16;
- tmp108__ = 0U;
- tmp108__ |= ( pSrc->pco << 0 );
- tmp108__ |= ( pSrc->transitionTime << 1 );
- tmp108__ |= ( pSrc->reserved2 << 3 );
- tmp108__ |= ( pSrc->mcsFeedback << 8 );
- tmp108__ |= ( pSrc->reserved3 << 10 );
- frameshtons(pCtx, pBuf, tmp108__, 0);
+ tmp111__ = 0U;
+ tmp111__ |= ( pSrc->pco << 0 );
+ tmp111__ |= ( pSrc->transitionTime << 1 );
+ tmp111__ |= ( pSrc->reserved2 << 3 );
+ tmp111__ |= ( pSrc->mcsFeedback << 8 );
+ tmp111__ |= ( pSrc->reserved3 << 10 );
+ frameshtons(pCtx, pBuf, tmp111__, 0);
*pnConsumed += 2;
pBuf += 2;
nBuf -= 2 ;
- tmp109__ = 0U;
- tmp109__ |= ( pSrc->txBF << 0 );
- tmp109__ |= ( pSrc->rxStaggeredSounding << 1 );
- tmp109__ |= ( pSrc->txStaggeredSounding << 2 );
- tmp109__ |= ( pSrc->rxZLF << 3 );
- tmp109__ |= ( pSrc->txZLF << 4 );
- tmp109__ |= ( pSrc->implicitTxBF << 5 );
- tmp109__ |= ( pSrc->calibration << 6 );
- tmp109__ |= ( pSrc->explicitCSITxBF << 8 );
- tmp109__ |= ( pSrc->explicitUncompressedSteeringMatrix << 9 );
- tmp109__ |= ( pSrc->explicitBFCSIFeedback << 10 );
- tmp109__ |= ( pSrc->explicitUncompressedSteeringMatrixFeedback << 13 );
- tmp109__ |= ( pSrc->explicitCompressedSteeringMatrixFeedback << 16 );
- tmp109__ |= ( pSrc->csiNumBFAntennae << 19 );
- tmp109__ |= ( pSrc->uncompressedSteeringMatrixBFAntennae << 21 );
- tmp109__ |= ( pSrc->compressedSteeringMatrixBFAntennae << 23 );
- tmp109__ |= ( pSrc->reserved4 << 25 );
- frameshtonl(pCtx, pBuf, tmp109__, 0);
+ tmp112__ = 0U;
+ tmp112__ |= ( pSrc->txBF << 0 );
+ tmp112__ |= ( pSrc->rxStaggeredSounding << 1 );
+ tmp112__ |= ( pSrc->txStaggeredSounding << 2 );
+ tmp112__ |= ( pSrc->rxZLF << 3 );
+ tmp112__ |= ( pSrc->txZLF << 4 );
+ tmp112__ |= ( pSrc->implicitTxBF << 5 );
+ tmp112__ |= ( pSrc->calibration << 6 );
+ tmp112__ |= ( pSrc->explicitCSITxBF << 8 );
+ tmp112__ |= ( pSrc->explicitUncompressedSteeringMatrix << 9 );
+ tmp112__ |= ( pSrc->explicitBFCSIFeedback << 10 );
+ tmp112__ |= ( pSrc->explicitUncompressedSteeringMatrixFeedback << 13 );
+ tmp112__ |= ( pSrc->explicitCompressedSteeringMatrixFeedback << 16 );
+ tmp112__ |= ( pSrc->csiNumBFAntennae << 19 );
+ tmp112__ |= ( pSrc->uncompressedSteeringMatrixBFAntennae << 21 );
+ tmp112__ |= ( pSrc->compressedSteeringMatrixBFAntennae << 23 );
+ tmp112__ |= ( pSrc->reserved4 << 25 );
+ frameshtonl(pCtx, pBuf, tmp112__, 0);
*pnConsumed += 4;
pBuf += 4;
nBuf -= 4 ;
- tmp110__ = 0U;
- tmp110__ |= ( pSrc->antennaSelection << 0 );
- tmp110__ |= ( pSrc->explicitCSIFeedbackTx << 1 );
- tmp110__ |= ( pSrc->antennaIndicesFeedbackTx << 2 );
- tmp110__ |= ( pSrc->explicitCSIFeedback << 3 );
- tmp110__ |= ( pSrc->antennaIndicesFeedback << 4 );
- tmp110__ |= ( pSrc->rxAS << 5 );
- tmp110__ |= ( pSrc->txSoundingPPDUs << 6 );
- tmp110__ |= ( pSrc->reserved5 << 7 );
- *pBuf = tmp110__;
+ tmp113__ = 0U;
+ tmp113__ |= ( pSrc->antennaSelection << 0 );
+ tmp113__ |= ( pSrc->explicitCSIFeedbackTx << 1 );
+ tmp113__ |= ( pSrc->antennaIndicesFeedbackTx << 2 );
+ tmp113__ |= ( pSrc->explicitCSIFeedback << 3 );
+ tmp113__ |= ( pSrc->antennaIndicesFeedback << 4 );
+ tmp113__ |= ( pSrc->rxAS << 5 );
+ tmp113__ |= ( pSrc->txSoundingPPDUs << 6 );
+ tmp113__ |= ( pSrc->reserved5 << 7 );
+ *pBuf = tmp113__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
@@ -25877,9 +26495,9 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp111__;
- tANI_U16 tmp112__;
- tANI_U16 tmp113__;
+ tANI_U8 tmp114__;
+ tANI_U16 tmp115__;
+ tANI_U16 tmp116__;
nNeeded += (pSrc->num_rsvd + 22);
while ( pSrc->present )
{
@@ -25891,35 +26509,35 @@
*pBuf = pSrc->primaryChannel;
*pnConsumed += 1;
pBuf += 1;
- tmp111__ = 0U;
- tmp111__ |= ( pSrc->secondaryChannelOffset << 0 );
- tmp111__ |= ( pSrc->recommendedTxWidthSet << 2 );
- tmp111__ |= ( pSrc->rifsMode << 3 );
- tmp111__ |= ( pSrc->controlledAccessOnly << 4 );
- tmp111__ |= ( pSrc->serviceIntervalGranularity << 5 );
- *pBuf = tmp111__;
+ tmp114__ = 0U;
+ tmp114__ |= ( pSrc->secondaryChannelOffset << 0 );
+ tmp114__ |= ( pSrc->recommendedTxWidthSet << 2 );
+ tmp114__ |= ( pSrc->rifsMode << 3 );
+ tmp114__ |= ( pSrc->controlledAccessOnly << 4 );
+ tmp114__ |= ( pSrc->serviceIntervalGranularity << 5 );
+ *pBuf = tmp114__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
- tmp112__ = 0U;
- tmp112__ |= ( pSrc->opMode << 0 );
- tmp112__ |= ( pSrc->nonGFDevicesPresent << 2 );
- tmp112__ |= ( pSrc->transmitBurstLimit << 3 );
- tmp112__ |= ( pSrc->obssNonHTStaPresent << 4 );
- tmp112__ |= ( pSrc->reserved << 5 );
- frameshtons(pCtx, pBuf, tmp112__, 0);
+ tmp115__ = 0U;
+ tmp115__ |= ( pSrc->opMode << 0 );
+ tmp115__ |= ( pSrc->nonGFDevicesPresent << 2 );
+ tmp115__ |= ( pSrc->transmitBurstLimit << 3 );
+ tmp115__ |= ( pSrc->obssNonHTStaPresent << 4 );
+ tmp115__ |= ( pSrc->reserved << 5 );
+ frameshtons(pCtx, pBuf, tmp115__, 0);
*pnConsumed += 2;
pBuf += 2;
nBuf -= 2 ;
- tmp113__ = 0U;
- tmp113__ |= ( pSrc->basicSTBCMCS << 0 );
- tmp113__ |= ( pSrc->dualCTSProtection << 7 );
- tmp113__ |= ( pSrc->secondaryBeacon << 8 );
- tmp113__ |= ( pSrc->lsigTXOPProtectionFullSupport << 9 );
- tmp113__ |= ( pSrc->pcoActive << 10 );
- tmp113__ |= ( pSrc->pcoPhase << 11 );
- tmp113__ |= ( pSrc->reserved2 << 12 );
- frameshtons(pCtx, pBuf, tmp113__, 0);
+ tmp116__ = 0U;
+ tmp116__ |= ( pSrc->basicSTBCMCS << 0 );
+ tmp116__ |= ( pSrc->dualCTSProtection << 7 );
+ tmp116__ |= ( pSrc->secondaryBeacon << 8 );
+ tmp116__ |= ( pSrc->lsigTXOPProtectionFullSupport << 9 );
+ tmp116__ |= ( pSrc->pcoActive << 10 );
+ tmp116__ |= ( pSrc->pcoPhase << 11 );
+ tmp116__ |= ( pSrc->reserved2 << 12 );
+ frameshtons(pCtx, pBuf, tmp116__, 0);
*pnConsumed += 2;
pBuf += 2;
nBuf -= 2 ;
@@ -26014,9 +26632,9 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp114__;
- tANI_U8 tmp115__;
- tANI_U8 tmp116__;
+ tANI_U8 tmp117__;
+ tANI_U8 tmp118__;
+ tANI_U8 tmp119__;
tANI_U32 status = DOT11F_PARSE_SUCCESS;
status = dot11fGetPackedIEMeasurementReport(pCtx, pSrc, &nNeeded);
if ( ! DOT11F_SUCCEEDED( status ) ) return status;
@@ -26030,12 +26648,12 @@
*pBuf = pSrc->token;
*pnConsumed += 1;
pBuf += 1;
- tmp114__ = 0U;
- tmp114__ |= ( pSrc->late << 0 );
- tmp114__ |= ( pSrc->incapable << 1 );
- tmp114__ |= ( pSrc->refused << 2 );
- tmp114__ |= ( pSrc->unused << 3 );
- *pBuf = tmp114__;
+ tmp117__ = 0U;
+ tmp117__ |= ( pSrc->late << 0 );
+ tmp117__ |= ( pSrc->incapable << 1 );
+ tmp117__ |= ( pSrc->refused << 2 );
+ tmp117__ |= ( pSrc->unused << 3 );
+ *pBuf = tmp117__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
@@ -26055,14 +26673,14 @@
frameshtons(pCtx, pBuf, pSrc->report.Basic.meas_duration, 0);
*pnConsumed += 2;
pBuf += 2;
- tmp115__ = 0U;
- tmp115__ |= ( pSrc->report.Basic.bss << 0 );
- tmp115__ |= ( pSrc->report.Basic.ofdm_preamble << 1 );
- tmp115__ |= ( pSrc->report.Basic.unid_signal << 2 );
- tmp115__ |= ( pSrc->report.Basic.rader << 3 );
- tmp115__ |= ( pSrc->report.Basic.unmeasured << 4 );
- tmp115__ |= ( pSrc->report.Basic.unused << 5 );
- *pBuf = tmp115__;
+ tmp118__ = 0U;
+ tmp118__ |= ( pSrc->report.Basic.bss << 0 );
+ tmp118__ |= ( pSrc->report.Basic.ofdm_preamble << 1 );
+ tmp118__ |= ( pSrc->report.Basic.unid_signal << 2 );
+ tmp118__ |= ( pSrc->report.Basic.rader << 3 );
+ tmp118__ |= ( pSrc->report.Basic.unmeasured << 4 );
+ tmp118__ |= ( pSrc->report.Basic.unused << 5 );
+ *pBuf = tmp118__;
*pnConsumed += 1;
// fieldsEndFlag = 1
nBuf -= 1 ;
@@ -26129,10 +26747,10 @@
frameshtons(pCtx, pBuf, pSrc->report.Beacon.meas_duration, 0);
*pnConsumed += 2;
pBuf += 2;
- tmp116__ = 0U;
- tmp116__ |= ( pSrc->report.Beacon.condensed_PHY << 0 );
- tmp116__ |= ( pSrc->report.Beacon.reported_frame_type << 7 );
- *pBuf = tmp116__;
+ tmp119__ = 0U;
+ tmp119__ |= ( pSrc->report.Beacon.condensed_PHY << 0 );
+ tmp119__ |= ( pSrc->report.Beacon.reported_frame_type << 7 );
+ *pBuf = tmp119__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
@@ -26181,7 +26799,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp117__;
+ tANI_U8 tmp120__;
tANI_U32 status = DOT11F_PARSE_SUCCESS;
status = dot11fGetPackedIEMeasurementRequest(pCtx, pSrc, &nNeeded);
if ( ! DOT11F_SUCCEEDED( status ) ) return status;
@@ -26195,14 +26813,14 @@
*pBuf = pSrc->measurement_token;
*pnConsumed += 1;
pBuf += 1;
- tmp117__ = 0U;
- tmp117__ |= ( pSrc->parallel << 0 );
- tmp117__ |= ( pSrc->enable << 1 );
- tmp117__ |= ( pSrc->request << 2 );
- tmp117__ |= ( pSrc->report << 3 );
- tmp117__ |= ( pSrc->durationMandatory << 4 );
- tmp117__ |= ( pSrc->unused << 5 );
- *pBuf = tmp117__;
+ tmp120__ = 0U;
+ tmp120__ |= ( pSrc->parallel << 0 );
+ tmp120__ |= ( pSrc->enable << 1 );
+ tmp120__ |= ( pSrc->request << 2 );
+ tmp120__ |= ( pSrc->report << 3 );
+ tmp120__ |= ( pSrc->durationMandatory << 4 );
+ tmp120__ |= ( pSrc->unused << 5 );
+ *pBuf = tmp120__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
@@ -26291,7 +26909,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp118__;
+ tANI_U8 tmp121__;
nNeeded += 3;
while ( pSrc->present )
{
@@ -26303,11 +26921,11 @@
frameshtons(pCtx, pBuf, pSrc->MDID, 0);
*pnConsumed += 2;
pBuf += 2;
- tmp118__ = 0U;
- tmp118__ |= ( pSrc->overDSCap << 0 );
- tmp118__ |= ( pSrc->resourceReqCap << 1 );
- tmp118__ |= ( pSrc->reserved << 2 );
- *pBuf = tmp118__;
+ tmp121__ = 0U;
+ tmp121__ |= ( pSrc->overDSCap << 0 );
+ tmp121__ |= ( pSrc->resourceReqCap << 1 );
+ tmp121__ |= ( pSrc->reserved << 2 );
+ *pBuf = tmp121__;
*pnConsumed += 1;
// fieldsEndFlag = 1
nBuf -= 1 ;
@@ -26330,8 +26948,8 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp119__;
- tANI_U8 tmp120__;
+ tANI_U8 tmp122__;
+ tANI_U8 tmp123__;
tANI_U32 status = DOT11F_PARSE_SUCCESS;
status = dot11fGetPackedIENeighborReport(pCtx, pSrc, &nNeeded);
if ( ! DOT11F_SUCCEEDED( status ) ) return status;
@@ -26345,24 +26963,24 @@
DOT11F_MEMCPY(pCtx, pBuf, pSrc->bssid, 6);
*pnConsumed += 6;
pBuf += 6;
- tmp119__ = 0U;
- tmp119__ |= ( pSrc->APReachability << 0 );
- tmp119__ |= ( pSrc->Security << 2 );
- tmp119__ |= ( pSrc->KeyScope << 3 );
- tmp119__ |= ( pSrc->SpecMgmtCap << 4 );
- tmp119__ |= ( pSrc->QosCap << 5 );
- tmp119__ |= ( pSrc->apsd << 6 );
- tmp119__ |= ( pSrc->rrm << 7 );
- *pBuf = tmp119__;
+ tmp122__ = 0U;
+ tmp122__ |= ( pSrc->APReachability << 0 );
+ tmp122__ |= ( pSrc->Security << 2 );
+ tmp122__ |= ( pSrc->KeyScope << 3 );
+ tmp122__ |= ( pSrc->SpecMgmtCap << 4 );
+ tmp122__ |= ( pSrc->QosCap << 5 );
+ tmp122__ |= ( pSrc->apsd << 6 );
+ tmp122__ |= ( pSrc->rrm << 7 );
+ *pBuf = tmp122__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
- tmp120__ = 0U;
- tmp120__ |= ( pSrc->DelayedBA << 0 );
- tmp120__ |= ( pSrc->ImmBA << 1 );
- tmp120__ |= ( pSrc->MobilityDomain << 2 );
- tmp120__ |= ( pSrc->reserved << 3 );
- *pBuf = tmp120__;
+ tmp123__ = 0U;
+ tmp123__ |= ( pSrc->DelayedBA << 0 );
+ tmp123__ |= ( pSrc->ImmBA << 1 );
+ tmp123__ |= ( pSrc->MobilityDomain << 2 );
+ tmp123__ |= ( pSrc->reserved << 3 );
+ *pBuf = tmp123__;
*pnConsumed += 1;
pBuf += 1;
nBuf -= 1 ;
@@ -26452,7 +27070,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp121__;
+ tANI_U8 tmp124__;
nNeeded += 1;
while ( pSrc->present )
{
@@ -26461,12 +27079,12 @@
++pBuf; ++(*pnConsumed);
pIeLen = pBuf;
++pBuf; ++(*pnConsumed);
- tmp121__ = 0U;
- tmp121__ |= ( pSrc->chanWidth << 0 );
- tmp121__ |= ( pSrc->reserved << 2 );
- tmp121__ |= ( pSrc->rxNSS << 4 );
- tmp121__ |= ( pSrc->rxNSSType << 7 );
- *pBuf = tmp121__;
+ tmp124__ = 0U;
+ tmp124__ |= ( pSrc->chanWidth << 0 );
+ tmp124__ |= ( pSrc->reserved << 2 );
+ tmp124__ |= ( pSrc->rxNSS << 4 );
+ tmp124__ |= ( pSrc->rxNSSType << 7 );
+ *pBuf = tmp124__;
*pnConsumed += 1;
// fieldsEndFlag = 1
nBuf -= 1 ;
@@ -27420,7 +28038,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp122__;
+ tANI_U8 tmp125__;
nNeeded += 1;
while ( pSrc->present )
{
@@ -27429,13 +28047,13 @@
++pBuf; ++(*pnConsumed);
pIeLen = pBuf;
++pBuf; ++(*pnConsumed);
- tmp122__ = 0U;
- tmp122__ |= ( pSrc->ac_bk_traffic_aval << 0 );
- tmp122__ |= ( pSrc->ac_be_traffic_aval << 1 );
- tmp122__ |= ( pSrc->ac_vi_traffic_aval << 2 );
- tmp122__ |= ( pSrc->ac_vo_traffic_aval << 3 );
- tmp122__ |= ( pSrc->reserved << 4 );
- *pBuf = tmp122__;
+ tmp125__ = 0U;
+ tmp125__ |= ( pSrc->ac_bk_traffic_aval << 0 );
+ tmp125__ |= ( pSrc->ac_be_traffic_aval << 1 );
+ tmp125__ |= ( pSrc->ac_vi_traffic_aval << 2 );
+ tmp125__ |= ( pSrc->ac_vo_traffic_aval << 3 );
+ tmp125__ |= ( pSrc->reserved << 4 );
+ *pBuf = tmp125__;
*pnConsumed += 1;
// fieldsEndFlag = 1
nBuf -= 1 ;
@@ -27596,7 +28214,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp123__;
+ tANI_U8 tmp126__;
nNeeded += 1;
while ( pSrc->present )
{
@@ -27605,13 +28223,13 @@
++pBuf; ++(*pnConsumed);
pIeLen = pBuf;
++pBuf; ++(*pnConsumed);
- tmp123__ = 0U;
- tmp123__ |= ( pSrc->count << 0 );
- tmp123__ |= ( pSrc->qack << 4 );
- tmp123__ |= ( pSrc->qreq << 5 );
- tmp123__ |= ( pSrc->txopreq << 6 );
- tmp123__ |= ( pSrc->reserved << 7 );
- *pBuf = tmp123__;
+ tmp126__ = 0U;
+ tmp126__ |= ( pSrc->count << 0 );
+ tmp126__ |= ( pSrc->qack << 4 );
+ tmp126__ |= ( pSrc->qreq << 5 );
+ tmp126__ |= ( pSrc->txopreq << 6 );
+ tmp126__ |= ( pSrc->reserved << 7 );
+ *pBuf = tmp126__;
*pnConsumed += 1;
// fieldsEndFlag = 1
nBuf -= 1 ;
@@ -27634,7 +28252,7 @@
tANI_U8* pIeLen = 0;
tANI_U32 nConsumedOnEntry = *pnConsumed;
tANI_U32 nNeeded = 0U;
- tANI_U8 tmp124__;
+ tANI_U8 tmp127__;
nNeeded += 1;
while ( pSrc->present )
{
@@ -27643,15 +28261,15 @@
++pBuf; ++(*pnConsumed);
pIeLen = pBuf;
++pBuf; ++(*pnConsumed);
- tmp124__ = 0U;
- tmp124__ |= ( pSrc->acvo_uapsd << 0 );
- tmp124__ |= ( pSrc->acvi_uapsd << 1 );
- tmp124__ |= ( pSrc->acbk_uapsd << 2 );
- tmp124__ |= ( pSrc->acbe_uapsd << 3 );
- tmp124__ |= ( pSrc->qack << 4 );
- tmp124__ |= ( pSrc->max_sp_length << 5 );
- tmp124__ |= ( pSrc->more_data_ack << 7 );
- *pBuf = tmp124__;
+ tmp127__ = 0U;
+ tmp127__ |= ( pSrc->acvo_uapsd << 0 );
+ tmp127__ |= ( pSrc->acvi_uapsd << 1 );
+ tmp127__ |= ( pSrc->acbk_uapsd << 2 );
+ tmp127__ |= ( pSrc->acbe_uapsd << 3 );
+ tmp127__ |= ( pSrc->qack << 4 );
+ tmp127__ |= ( pSrc->max_sp_length << 5 );
+ tmp127__ |= ( pSrc->more_data_ack << 7 );
+ *pBuf = tmp127__;
*pnConsumed += 1;
// fieldsEndFlag = 1
nBuf -= 1 ;
@@ -28182,81 +28800,6 @@
return DOT11F_PARSE_SUCCESS;
} /* End dot11fPackIeTimeoutInterval. */
-tANI_U32 dot11fPackIeVHTCaps(tpAniSirGlobal pCtx,
- tDot11fIEVHTCaps *pSrc,
- tANI_U8 *pBuf,
- tANI_U32 nBuf,
- tANI_U32 *pnConsumed)
-{
- tANI_U8* pIeLen = 0;
- tANI_U32 nConsumedOnEntry = *pnConsumed;
- tANI_U32 nNeeded = 0U;
- tANI_U32 tmp125__;
- tANI_U16 tmp126__;
- tANI_U16 tmp127__;
- nNeeded += 12;
- while ( pSrc->present )
- {
- if ( nNeeded > nBuf ) return DOT11F_BUFFER_OVERFLOW;
- *pBuf = 191;
- ++pBuf; ++(*pnConsumed);
- pIeLen = pBuf;
- ++pBuf; ++(*pnConsumed);
- tmp125__ = 0U;
- tmp125__ |= ( pSrc->maxMPDULen << 0 );
- tmp125__ |= ( pSrc->supportedChannelWidthSet << 2 );
- tmp125__ |= ( pSrc->ldpcCodingCap << 4 );
- tmp125__ |= ( pSrc->shortGI80MHz << 5 );
- tmp125__ |= ( pSrc->shortGI160and80plus80MHz << 6 );
- tmp125__ |= ( pSrc->txSTBC << 7 );
- tmp125__ |= ( pSrc->rxSTBC << 8 );
- tmp125__ |= ( pSrc->suBeamFormerCap << 11 );
- tmp125__ |= ( pSrc->suBeamformeeCap << 12 );
- tmp125__ |= ( pSrc->csnofBeamformerAntSup << 13 );
- tmp125__ |= ( pSrc->numSoundingDim << 16 );
- tmp125__ |= ( pSrc->muBeamformerCap << 19 );
- tmp125__ |= ( pSrc->muBeamformeeCap << 20 );
- tmp125__ |= ( pSrc->vhtTXOPPS << 21 );
- tmp125__ |= ( pSrc->htcVHTCap << 22 );
- tmp125__ |= ( pSrc->maxAMPDULenExp << 23 );
- tmp125__ |= ( pSrc->vhtLinkAdaptCap << 26 );
- tmp125__ |= ( pSrc->rxAntPattern << 28 );
- tmp125__ |= ( pSrc->txAntPattern << 29 );
- tmp125__ |= ( pSrc->reserved1 << 30 );
- frameshtonl(pCtx, pBuf, tmp125__, 0);
- *pnConsumed += 4;
- pBuf += 4;
- nBuf -= 4 ;
- frameshtons(pCtx, pBuf, pSrc->rxMCSMap, 0);
- *pnConsumed += 2;
- pBuf += 2;
- tmp126__ = 0U;
- tmp126__ |= ( pSrc->rxHighSupDataRate << 0 );
- tmp126__ |= ( pSrc->reserved2 << 13 );
- frameshtons(pCtx, pBuf, tmp126__, 0);
- *pnConsumed += 2;
- pBuf += 2;
- nBuf -= 2 ;
- frameshtons(pCtx, pBuf, pSrc->txMCSMap, 0);
- *pnConsumed += 2;
- pBuf += 2;
- tmp127__ = 0U;
- tmp127__ |= ( pSrc->txSupDataRate << 0 );
- tmp127__ |= ( pSrc->reserved3 << 13 );
- frameshtons(pCtx, pBuf, tmp127__, 0);
- *pnConsumed += 2;
- // fieldsEndFlag = 1
- nBuf -= 2 ;
- break;
- }
- (void)pCtx;
- if (pIeLen)
- {
- *pIeLen = *pnConsumed - nConsumedOnEntry - 2;
- }
- return DOT11F_PARSE_SUCCESS;
-} /* End dot11fPackIeVHTCaps. */
-
tANI_U32 dot11fPackIeVHTExtBssLoad(tpAniSirGlobal pCtx,
tDot11fIEVHTExtBssLoad *pSrc,
tANI_U8 *pBuf,
@@ -28299,45 +28842,6 @@
return DOT11F_PARSE_SUCCESS;
} /* End dot11fPackIeVHTExtBssLoad. */
-tANI_U32 dot11fPackIeVHTOperation(tpAniSirGlobal pCtx,
- tDot11fIEVHTOperation *pSrc,
- tANI_U8 *pBuf,
- tANI_U32 nBuf,
- tANI_U32 *pnConsumed)
-{
- tANI_U8* pIeLen = 0;
- tANI_U32 nConsumedOnEntry = *pnConsumed;
- tANI_U32 nNeeded = 0U;
- nNeeded += 5;
- while ( pSrc->present )
- {
- if ( nNeeded > nBuf ) return DOT11F_BUFFER_OVERFLOW;
- *pBuf = 192;
- ++pBuf; ++(*pnConsumed);
- pIeLen = pBuf;
- ++pBuf; ++(*pnConsumed);
- *pBuf = pSrc->chanWidth;
- *pnConsumed += 1;
- pBuf += 1;
- *pBuf = pSrc->chanCenterFreqSeg1;
- *pnConsumed += 1;
- pBuf += 1;
- *pBuf = pSrc->chanCenterFreqSeg2;
- *pnConsumed += 1;
- pBuf += 1;
- frameshtons(pCtx, pBuf, pSrc->basicMCSSet, 0);
- *pnConsumed += 2;
- // fieldsEndFlag = 1
- break;
- }
- (void)pCtx;
- if (pIeLen)
- {
- *pIeLen = *pnConsumed - nConsumedOnEntry - 2;
- }
- return DOT11F_PARSE_SUCCESS;
-} /* End dot11fPackIeVHTOperation. */
-
tANI_U32 dot11fPackIeVendor1IE(tpAniSirGlobal pCtx,
tDot11fIEVendor1IE *pSrc,
tANI_U8 *pBuf,
@@ -28371,39 +28875,6 @@
return DOT11F_PARSE_SUCCESS;
} /* End dot11fPackIeVendor1IE. */
-tANI_U32 dot11fPackIeVendor2IE(tpAniSirGlobal pCtx,
- tDot11fIEVendor2IE *pSrc,
- tANI_U8 *pBuf,
- tANI_U32 nBuf,
- tANI_U32 *pnConsumed)
-{
- tANI_U8* pIeLen = 0;
- tANI_U32 nConsumedOnEntry = *pnConsumed;
- tANI_U32 nNeeded = 0U;
- nNeeded += 0;
- while ( pSrc->present )
- {
- if ( nNeeded > nBuf ) return DOT11F_BUFFER_OVERFLOW;
- *pBuf = 221;
- ++pBuf; ++(*pnConsumed);
- pIeLen = pBuf;
- ++pBuf; ++(*pnConsumed);
- *pBuf = 0x0;
- ++pBuf; ++(*pnConsumed);
- *pBuf = 0x90;
- ++pBuf; ++(*pnConsumed);
- *pBuf = 0x4c;
- ++pBuf; ++(*pnConsumed);
- break;
- }
- (void)pCtx;
- if (pIeLen)
- {
- *pIeLen = *pnConsumed - nConsumedOnEntry - 2;
- }
- return DOT11F_PARSE_SUCCESS;
-} /* End dot11fPackIeVendor2IE. */
-
tANI_U32 dot11fPackIeVendor3IE(tpAniSirGlobal pCtx,
tDot11fIEVendor3IE *pSrc,
tANI_U8 *pBuf,
@@ -29454,6 +29925,54 @@
return DOT11F_PARSE_SUCCESS;
} /* End dot11fPackIesec_chan_offset_ele. */
+tANI_U32 dot11fPackIevendor2_ie(tpAniSirGlobal pCtx,
+ tDot11fIEvendor2_ie *pSrc,
+ tANI_U8 *pBuf,
+ tANI_U32 nBuf,
+ tANI_U32 *pnConsumed)
+{
+ tANI_U8* pIeLen = 0;
+ tANI_U32 nConsumedOnEntry = *pnConsumed;
+ tANI_U32 nNeeded = 0U;
+ tANI_U32 status = DOT11F_PARSE_SUCCESS;
+ status = dot11fGetPackedIEvendor2_ie(pCtx, pSrc, &nNeeded);
+ if ( ! DOT11F_SUCCEEDED( status ) ) return status;
+ while ( pSrc->present )
+ {
+ if ( nNeeded > nBuf ) return DOT11F_BUFFER_OVERFLOW;
+ *pBuf = 221;
+ ++pBuf; --nBuf; ++(*pnConsumed);
+ pIeLen = pBuf;
+ ++pBuf; --nBuf; ++(*pnConsumed);
+ *pBuf = 0x0;
+ ++pBuf; --nBuf; ++(*pnConsumed);
+ *pBuf = 0x90;
+ ++pBuf; --nBuf; ++(*pnConsumed);
+ *pBuf = 0x4c;
+ ++pBuf; --nBuf; ++(*pnConsumed);
+ *pBuf = pSrc->type;
+ *pnConsumed += 1;
+ pBuf += 1;
+ *pBuf = pSrc->sub_type;
+ *pnConsumed += 1;
+ pBuf += 1;
+ status = PackCore(pCtx,
+ (tANI_U8*)pSrc,
+ pBuf,
+ nBuf,
+ pnConsumed,
+ FFS_vendor2_ie,
+ IES_vendor2_ie);
+ break;
+ }
+ (void)pCtx;
+ if (pIeLen)
+ {
+ *pIeLen = *pnConsumed - nConsumedOnEntry - 2;
+ }
+ return status;
+} /* End dot11fPackIevendor2_ie. */
+
tANI_U32 dot11fPackAddBAReq(tpAniSirGlobal pCtx, tDot11fAddBAReq *pFrm, tANI_U8 *pBuf, tANI_U32 nBuf, tANI_U32 *pnConsumed)
{
tANI_U32 i = 0;
@@ -30417,6 +30936,62 @@
FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("num_dscp_exceptions: %d.\n"), pFrm->QosMapSet.num_dscp_exceptions);
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* ) pFrm->QosMapSet.dscp_exceptions, pFrm->QosMapSet.num_dscp_exceptions);
}
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
+ }
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), FRFL("to:\n"));
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCREQUEST), pBuf, nBuf);
}
@@ -31317,6 +31892,62 @@
FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("num_dscp_exceptions: %d.\n"), pFrm->QosMapSet.num_dscp_exceptions);
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* ) pFrm->QosMapSet.dscp_exceptions, pFrm->QosMapSet.num_dscp_exceptions);
}
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
+ }
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), FRFL("to:\n"));
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_ASSOCRESPONSE), pBuf, nBuf);
}
@@ -32587,13 +33218,61 @@
else
{
}
- FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("Vendor2IE:\n"));
- if (!pFrm->Vendor2IE.present)
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
{
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("Not present.\n"));
}
else
{
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
}
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON), FRFL("Vendor3IE:\n"));
if (!pFrm->Vendor3IE.present)
@@ -33444,13 +34123,61 @@
else
{
}
- FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("Vendor2IE:\n"));
- if (!pFrm->Vendor2IE.present)
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
{
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("Not present.\n"));
}
else
{
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
}
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACON2), FRFL("Vendor3IE:\n"));
if (!pFrm->Vendor3IE.present)
@@ -34482,13 +35209,61 @@
else
{
}
- FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("Vendor2IE:\n"));
- if (!pFrm->Vendor2IE.present)
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
{
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("Not present.\n"));
}
else
{
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
}
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_BEACONIES), FRFL("Vendor3IE:\n"));
if (!pFrm->Vendor3IE.present)
@@ -37469,13 +38244,61 @@
else
{
}
- FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("Vendor2IE:\n"));
- if (!pFrm->Vendor2IE.present)
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
{
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("Not present.\n"));
}
else
{
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
}
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_PROBERESPONSE), FRFL("Vendor3IE:\n"));
if (!pFrm->Vendor3IE.present)
@@ -38639,6 +39462,62 @@
FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("num_dscp_exceptions: %d.\n"), pFrm->QosMapSet.num_dscp_exceptions);
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* ) pFrm->QosMapSet.dscp_exceptions, pFrm->QosMapSet.num_dscp_exceptions);
}
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
+ }
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), FRFL("to:\n"));
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCREQUEST), pBuf, nBuf);
}
@@ -39546,6 +40425,62 @@
FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("num_dscp_exceptions: %d.\n"), pFrm->QosMapSet.num_dscp_exceptions);
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* ) pFrm->QosMapSet.dscp_exceptions, pFrm->QosMapSet.num_dscp_exceptions);
}
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
+ }
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), FRFL("to:\n"));
FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_REASSOCRESPONSE), pBuf, nBuf);
}
@@ -41302,13 +42237,61 @@
else
{
}
- FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("Vendor2IE:\n"));
- if (!pFrm->Vendor2IE.present)
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("vendor2_ie:\n"));
+ if (!pFrm->vendor2_ie.present)
{
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("Not present.\n"));
}
else
{
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.type, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.sub_type, 1);
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("VHTCaps:\n"));
+ if (!pFrm->vendor2_ie.VHTCaps.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("maxMPDULen (2): %d\n"), pFrm->vendor2_ie.VHTCaps.maxMPDULen);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("supportedChannelWidthSet (2): %d\n"), pFrm->vendor2_ie.VHTCaps.supportedChannelWidthSet);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("ldpcCodingCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.ldpcCodingCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("shortGI80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("shortGI160and80plus80MHz (1): %d\n"), pFrm->vendor2_ie.VHTCaps.shortGI160and80plus80MHz);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("txSTBC (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("rxSTBC (3): %d\n"), pFrm->vendor2_ie.VHTCaps.rxSTBC);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("suBeamFormerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamFormerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("suBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.suBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("csnofBeamformerAntSup (3): %d\n"), pFrm->vendor2_ie.VHTCaps.csnofBeamformerAntSup);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("numSoundingDim (3): %d\n"), pFrm->vendor2_ie.VHTCaps.numSoundingDim);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("muBeamformerCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformerCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("muBeamformeeCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.muBeamformeeCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("vhtTXOPPS (1): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtTXOPPS);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("htcVHTCap (1): %d\n"), pFrm->vendor2_ie.VHTCaps.htcVHTCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("maxAMPDULenExp (3): %d\n"), pFrm->vendor2_ie.VHTCaps.maxAMPDULenExp);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("vhtLinkAdaptCap (2): %d\n"), pFrm->vendor2_ie.VHTCaps.vhtLinkAdaptCap);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("rxAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.rxAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("txAntPattern (1): %d\n"), pFrm->vendor2_ie.VHTCaps.txAntPattern);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("reserved1 (2): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.rxMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("rxHighSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.rxHighSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("reserved2 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved2);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTCaps.txMCSMap, 2);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("txSupDataRate (13): %d\n"), pFrm->vendor2_ie.VHTCaps.txSupDataRate);
+ FRAMES_LOG1(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("reserved3 (3): %d\n"), pFrm->vendor2_ie.VHTCaps.reserved3);
+ }
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("VHTOperation:\n"));
+ if (!pFrm->vendor2_ie.VHTOperation.present)
+ {
+ FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("Not present.\n"));
+ }
+ else
+ {
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanWidth, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg1, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.chanCenterFreqSeg2, 1);
+ FRAMES_DUMP(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), ( tANI_U8* )&pFrm->vendor2_ie.VHTOperation.basicMCSSet, 2);
+ }
}
FRAMES_LOG0(pCtx, FRAMES_SEV_FOR_FRAME(pCtx, DOT11F_TIMINGADVERTISEMENTFRAME), FRFL("Vendor3IE:\n"));
if (!pFrm->Vendor3IE.present)
@@ -41843,6 +42826,12 @@
case SigIeTSPEC:
status |= dot11fPackIeTSPEC(pCtx, ( tDot11fIETSPEC* )(pSrc + pIe->offset + sizeof(tDot11fIETSPEC) * i ), pBufRemaining, nBufRemaining, &len);
break;
+ case SigIeVHTCaps:
+ status |= dot11fPackIeVHTCaps(pCtx, ( tDot11fIEVHTCaps* )(pSrc + pIe->offset + sizeof(tDot11fIEVHTCaps) * i ), pBufRemaining, nBufRemaining, &len);
+ break;
+ case SigIeVHTOperation:
+ status |= dot11fPackIeVHTOperation(pCtx, ( tDot11fIEVHTOperation* )(pSrc + pIe->offset + sizeof(tDot11fIEVHTOperation) * i ), pBufRemaining, nBufRemaining, &len);
+ break;
case SigIeWMMSchedule:
status |= dot11fPackIeWMMSchedule(pCtx, ( tDot11fIEWMMSchedule* )(pSrc + pIe->offset + sizeof(tDot11fIEWMMSchedule) * i ), pBufRemaining, nBufRemaining, &len);
break;
@@ -42092,21 +43081,12 @@
case SigIeTimeoutInterval:
status |= dot11fPackIeTimeoutInterval(pCtx, ( tDot11fIETimeoutInterval* )(pSrc + pIe->offset + sizeof(tDot11fIETimeoutInterval) * i ), pBufRemaining, nBufRemaining, &len);
break;
- case SigIeVHTCaps:
- status |= dot11fPackIeVHTCaps(pCtx, ( tDot11fIEVHTCaps* )(pSrc + pIe->offset + sizeof(tDot11fIEVHTCaps) * i ), pBufRemaining, nBufRemaining, &len);
- break;
case SigIeVHTExtBssLoad:
status |= dot11fPackIeVHTExtBssLoad(pCtx, ( tDot11fIEVHTExtBssLoad* )(pSrc + pIe->offset + sizeof(tDot11fIEVHTExtBssLoad) * i ), pBufRemaining, nBufRemaining, &len);
break;
- case SigIeVHTOperation:
- status |= dot11fPackIeVHTOperation(pCtx, ( tDot11fIEVHTOperation* )(pSrc + pIe->offset + sizeof(tDot11fIEVHTOperation) * i ), pBufRemaining, nBufRemaining, &len);
- break;
case SigIeVendor1IE:
status |= dot11fPackIeVendor1IE(pCtx, ( tDot11fIEVendor1IE* )(pSrc + pIe->offset + sizeof(tDot11fIEVendor1IE) * i ), pBufRemaining, nBufRemaining, &len);
break;
- case SigIeVendor2IE:
- status |= dot11fPackIeVendor2IE(pCtx, ( tDot11fIEVendor2IE* )(pSrc + pIe->offset + sizeof(tDot11fIEVendor2IE) * i ), pBufRemaining, nBufRemaining, &len);
- break;
case SigIeVendor3IE:
status |= dot11fPackIeVendor3IE(pCtx, ( tDot11fIEVendor3IE* )(pSrc + pIe->offset + sizeof(tDot11fIEVendor3IE) * i ), pBufRemaining, nBufRemaining, &len);
break;
@@ -42173,6 +43153,9 @@
case SigIesec_chan_offset_ele:
status |= dot11fPackIesec_chan_offset_ele(pCtx, ( tDot11fIEsec_chan_offset_ele* )(pSrc + pIe->offset + sizeof(tDot11fIEsec_chan_offset_ele) * i ), pBufRemaining, nBufRemaining, &len);
break;
+ case SigIevendor2_ie:
+ status |= dot11fPackIevendor2_ie(pCtx, ( tDot11fIEvendor2_ie* )(pSrc + pIe->offset + sizeof(tDot11fIEvendor2_ie) * i ), pBufRemaining, nBufRemaining, &len);
+ break;
default:
FRAMES_LOG1(pCtx, FRLOGE, FRFL("INTERNAL ERROR-- I don"
"'t know about the IE %d; this is most likely a b"
diff --git a/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/macTrace.c b/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/macTrace.c
index 7d16f0f..65ed102 100644
--- a/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/macTrace.c
+++ b/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/macTrace.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -498,7 +498,6 @@
CASE_RETURN_STRING(eWNI_SME_ESE_ADJACENT_AP_REPORT);
#endif
CASE_RETURN_STRING(eWNI_SME_REGISTER_MGMT_FRAME_REQ);
- CASE_RETURN_STRING(eWNI_SME_COEX_IND);
#ifdef FEATURE_WLAN_SCAN_PNO
CASE_RETURN_STRING(eWNI_SME_PREF_NETWORK_FOUND_IND);
#endif // FEATURE_WLAN_SCAN_PNO
@@ -998,6 +997,8 @@
CASE_RETURN_STRING(WDA_MIB_STATS_REQ);
CASE_RETURN_STRING(WDA_SET_MIB_STATS_ENABLE);
CASE_RETURN_STRING(WDA_SET_MIB_STATS_DISABLE);
+ CASE_RETURN_STRING(WDA_ACTION_FRAME_RANDOM_MAC);
+ CASE_RETURN_STRING(SIR_HAL_POWER_DEBUG_STATS_REQ);
default:
return((tANI_U8*) "UNKNOWN");
break;
diff --git a/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/parserApi.c b/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/parserApi.c
index d84d584..f1c5a20 100644
--- a/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/parserApi.c
+++ b/drivers/staging/qcacld-2.0/CORE/SYS/legacy/src/utils/src/parserApi.c
@@ -2489,8 +2489,24 @@
}
#endif
pProbeResp->Vendor1IEPresent = pr->Vendor1IE.present;
- pProbeResp->Vendor2IEPresent = pr->Vendor2IE.present;
pProbeResp->Vendor3IEPresent = pr->Vendor3IE.present;
+ pProbeResp->vendor2_ie.present = pr->vendor2_ie.present;
+
+ if (pr->vendor2_ie.present) {
+ pProbeResp->vendor2_ie.type = pr->vendor2_ie.type;
+ pProbeResp->vendor2_ie.sub_type = pr->vendor2_ie.sub_type;
+ }
+ if (pr->vendor2_ie.VHTCaps.present) {
+ vos_mem_copy(&pProbeResp->vendor2_ie.VHTCaps,
+ &pr->vendor2_ie.VHTCaps,
+ sizeof(tDot11fIEVHTCaps));
+ }
+ if (pr->vendor2_ie.VHTOperation.present) {
+ vos_mem_copy(&pProbeResp->vendor2_ie.VHTOperation,
+ &pr->vendor2_ie.VHTOperation,
+ sizeof(tDot11fIEVHTOperation));
+ }
+
vos_mem_free(pr);
return eSIR_SUCCESS;
@@ -2708,6 +2724,22 @@
p_ext_cap->timingMeas, p_ext_cap->fine_time_meas_initiator,
p_ext_cap->fine_time_meas_responder);
}
+
+ pAssocReq->vendor2_ie.present = ar->vendor2_ie.present;
+ if (ar->vendor2_ie.present) {
+ pAssocReq->vendor2_ie.type = ar->vendor2_ie.type;
+ pAssocReq->vendor2_ie.sub_type = ar->vendor2_ie.sub_type;
+
+ if (ar->vendor2_ie.VHTCaps.present) {
+ vos_mem_copy(&pAssocReq->vendor2_ie.VHTCaps,
+ &ar->vendor2_ie.VHTCaps,
+ sizeof(tDot11fIEVHTCaps));
+ limLog(pMac, LOG1,
+ FL("Received Assoc Request with Vendor specific VHT Cap"));
+ limLogVHTCap(pMac, &pAssocReq->VHTCaps);
+ }
+ }
+
vos_mem_free(ar);
return eSIR_SUCCESS;
@@ -2914,6 +2946,28 @@
limLog( pMac, LOG1, FL("Received Assoc Response with Qos Map Set"));
limLogQosMapSet(pMac, &pAssocRsp->QosMapSet);
}
+ pAssocRsp->vendor2_ie.present = ar.vendor2_ie.present;
+ if (ar.vendor2_ie.present) {
+ pAssocRsp->vendor2_ie.type = ar.vendor2_ie.type;
+ pAssocRsp->vendor2_ie.sub_type = ar.vendor2_ie.sub_type;
+ }
+
+ if (ar.vendor2_ie.VHTCaps.present) {
+ vos_mem_copy(&pAssocRsp->vendor2_ie.VHTCaps,
+ &ar.vendor2_ie.VHTCaps,
+ sizeof(tDot11fIEVHTCaps));
+ limLog(pMac, LOGE,
+ FL("Received Assoc Response with Vendor specific VHT Cap"));
+ limLogVHTCap(pMac, &pAssocRsp->VHTCaps);
+ }
+ if (ar.vendor2_ie.VHTOperation.present) {
+ vos_mem_copy(&pAssocRsp->vendor2_ie.VHTOperation,
+ &ar.vendor2_ie.VHTOperation,
+ sizeof(tDot11fIEVHTOperation));
+ limLog(pMac, LOGE,
+ FL("Received Assoc Response with Vendor specific VHT Oper"));
+ limLogVHTOperation(pMac, &pAssocRsp->VHTOperation);
+ }
return eSIR_SUCCESS;
@@ -3679,7 +3733,6 @@
}
pBeaconStruct->Vendor1IEPresent = pBies->Vendor1IE.present;
- pBeaconStruct->Vendor2IEPresent = pBies->Vendor2IE.present;
pBeaconStruct->Vendor3IEPresent = pBies->Vendor3IE.present;
if (pBies->ExtCap.present) {
pBeaconStruct->ExtCap.present = 1;
@@ -3687,6 +3740,25 @@
sizeof(tDot11fIEExtCap));
}
+ pBeaconStruct->vendor2_ie.present = pBies->vendor2_ie.present;
+ if (pBies->vendor2_ie.present) {
+ pBeaconStruct->vendor2_ie.type = pBies->vendor2_ie.type;
+ pBeaconStruct->vendor2_ie.sub_type = pBies->vendor2_ie.sub_type;
+ }
+
+ if (pBies->vendor2_ie.VHTCaps.present) {
+ pBeaconStruct->vendor2_ie.VHTCaps.present = 1;
+ vos_mem_copy(&pBeaconStruct->vendor2_ie.VHTCaps,
+ &pBies->vendor2_ie.VHTCaps,
+ sizeof(tDot11fIEVHTCaps));
+ }
+ if (pBies->vendor2_ie.VHTOperation.present) {
+ pBeaconStruct->vendor2_ie.VHTOperation.present = 1;
+ vos_mem_copy(&pBeaconStruct->vendor2_ie.VHTOperation,
+ &pBies->vendor2_ie.VHTOperation,
+ sizeof(tDot11fIEVHTOperation));
+ }
+
vos_mem_free(pBies);
return eSIR_SUCCESS;
} // End sirParseBeaconIE.
@@ -4014,9 +4086,31 @@
}
pBeaconStruct->Vendor1IEPresent = pBeacon->Vendor1IE.present;
- pBeaconStruct->Vendor2IEPresent = pBeacon->Vendor2IE.present;
pBeaconStruct->Vendor3IEPresent = pBeacon->Vendor3IE.present;
+ pBeaconStruct->vendor2_ie.present = pBeacon->vendor2_ie.present;
+ if (pBeacon->vendor2_ie.present) {
+ pBeaconStruct->vendor2_ie.type = pBeacon->vendor2_ie.type;
+ pBeaconStruct->vendor2_ie.sub_type =
+ pBeacon->vendor2_ie.sub_type;
+ }
+ if (pBeacon->vendor2_ie.present) {
+ PELOG1(limLog(pMac, LOG1,
+ FL("Vendor Specific VHT caps present in Beacon Frame!"));
+ )
+ }
+ if (pBeacon->vendor2_ie.VHTCaps.present) {
+ vos_mem_copy(&pBeaconStruct->vendor2_ie.VHTCaps,
+ &pBeacon->vendor2_ie.VHTCaps,
+ sizeof(tDot11fIEVHTCaps));
+ }
+ if (pBeacon->vendor2_ie.VHTOperation.present) {
+ vos_mem_copy(&pBeaconStruct->vendor2_ie.VHTOperation,
+ &pBeacon->VHTOperation,
+ sizeof(tDot11fIEVHTOperation));
+ }
+
+
#ifdef FEATURE_AP_MCC_CH_AVOIDANCE
if(pBeacon->QComVendorIE.present) {
pBeaconStruct->AvoidChannelIE.present =
diff --git a/drivers/staging/qcacld-2.0/CORE/UTILS/FWLOG/dbglog_host.c b/drivers/staging/qcacld-2.0/CORE/UTILS/FWLOG/dbglog_host.c
index 4577ff9..4585f97 100644
--- a/drivers/staging/qcacld-2.0/CORE/UTILS/FWLOG/dbglog_host.c
+++ b/drivers/staging/qcacld-2.0/CORE/UTILS/FWLOG/dbglog_host.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -42,6 +42,9 @@
#include <wlan_hdd_wext.h>
#include <net/sock.h>
#include <linux/netlink.h>
+#ifdef CNSS_GENL
+#include <net/cnss_nl.h>
+#endif
#ifdef WLAN_OPEN_SOURCE
#include <linux/debugfs.h>
@@ -1636,6 +1639,24 @@
}
#endif /* WLAN_OPEN_SOURCE */
+/**
+ * nl_srv_bcast_fw_logs() - Wrapper func to send bcast msgs to FW logs mcast grp
+ * @skb: sk buffer pointer
+ *
+ * Sends the bcast message to FW logs multicast group with generic nl socket
+ * if CNSS_GENL is enabled. Else, use the legacy netlink socket to send.
+ *
+ * Return: zero on success, error code otherwise
+ */
+static int nl_srv_bcast_fw_logs(struct sk_buff *skb)
+{
+#ifdef CNSS_GENL
+ return nl_srv_bcast(skb, CLD80211_MCGRP_FW_LOGS, WLAN_NL_MSG_CNSS_DIAG);
+#else
+ return nl_srv_bcast(skb);
+#endif
+}
+
/*
* Package the data from the fw diag WMI event handler.
* Pass this data to cnss-diag service
@@ -1647,6 +1668,9 @@
struct sk_buff *skb_out;
struct nlmsghdr *nlh;
int res = 0;
+ tAniNlHdr *wnl;
+ int radio = 0;
+ int msg_len = 0;
if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
return -ENODEV;
@@ -1656,20 +1680,23 @@
if (vos_is_multicast_logging())
{
- skb_out = nlmsg_new(len, 0);
+ msg_len = len + sizeof(radio);
+ skb_out = nlmsg_new(msg_len, 0);
if (!skb_out)
{
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to allocate new skb\n"));
return -1;
}
- nlh = nlmsg_put(skb_out, 0, 0, WLAN_NL_MSG_CNSS_DIAG, len, 0);
- memcpy(nlmsg_data(nlh), buffer, len);
+ nlh = nlmsg_put(skb_out, 0, 0, WLAN_NL_MSG_CNSS_DIAG, msg_len, 0);
+ wnl = (tAniNlHdr *)nlh;
+ wnl->radio = 0;
+ memcpy(nlmsg_data(nlh) + sizeof(radio), buffer, len);
- res = nl_srv_bcast(skb_out);
+ res = nl_srv_bcast_fw_logs(skb_out);
if (res < 0)
{
AR_DEBUG_PRINTF(ATH_DEBUG_RSVD1,
- ("%s: nl_srv_bcast failed 0x%x \n", __func__, res));
+ ("%s: nl_srv_bcast_fw_logs failed 0x%x \n", __func__, res));
return res;
}
}
@@ -1685,6 +1712,8 @@
int res = 0;
struct dbglog_slot *slot;
size_t slot_len;
+ tAniNlHdr *wnl;
+ int radio = 0;
if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
return -ENODEV;
@@ -1693,7 +1722,7 @@
return -EIO;
if (vos_is_multicast_logging()) {
- slot_len = sizeof(*slot) + ATH6KL_FWLOG_PAYLOAD_SIZE;
+ slot_len = sizeof(*slot) + ATH6KL_FWLOG_PAYLOAD_SIZE + sizeof(radio);
skb_out = nlmsg_new(slot_len, 0);
if (!skb_out) {
@@ -1703,7 +1732,9 @@
}
nlh = nlmsg_put(skb_out, 0, 0, WLAN_NL_MSG_CNSS_DIAG, slot_len, 0);
- slot = (struct dbglog_slot *) nlmsg_data(nlh);
+ wnl = (tAniNlHdr *)nlh;
+ wnl->radio = 0;
+ slot = (struct dbglog_slot *) (nlmsg_data(nlh) + sizeof(radio));
slot->diag_type = cmd;
slot->timestamp = cpu_to_le32(jiffies);
slot->length = cpu_to_le32(len);
@@ -1711,10 +1742,10 @@
slot->dropped = get_version;
memcpy(slot->payload, buffer, len);
- res = nl_srv_bcast(skb_out);
+ res = nl_srv_bcast_fw_logs(skb_out);
if (res < 0) {
AR_DEBUG_PRINTF(ATH_DEBUG_RSVD1,
- ("%s: nl_srv_bcast failed 0x%x \n", __func__, res));
+ ("%s: nl_srv_bcast_fw_logs failed 0x%x \n", __func__, res));
return res;
}
}
@@ -1757,11 +1788,11 @@
slot->dropped = cpu_to_le32(dropped);
memcpy(slot->payload, buffer, len);
- res = nl_srv_bcast(skb_out);
+ res = nl_srv_bcast_fw_logs(skb_out);
if (res < 0)
{
AR_DEBUG_PRINTF(ATH_DEBUG_RSVD1,
- ("%s: nl_srv_bcast failed 0x%x \n", __func__, res));
+ ("%s: nl_srv_bcast_fw_logs failed 0x%x \n", __func__, res));
return res;
}
}
@@ -3917,6 +3948,43 @@
}
#endif /* WLAN_OPEN_SOURCE */
+/**
+ * cnss_diag_handle_crash_inject() - API to handle crash inject command
+ * @slot: pointer to struct dbglog_slot
+ *
+ * API to handle CNSS diag crash inject command
+ *
+ * Return: None
+ */
+static void cnss_diag_handle_crash_inject(struct dbglog_slot *slot)
+{
+ switch (slot->diag_type) {
+ case DIAG_TYPE_CRASH_INJECT:
+ if (slot->length != 2) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("crash_inject cmd error\n"));
+ return;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INFO,
+ ("%s : DIAG_TYPE_CRASH_INJECT: %d %d\n", __func__,
+ slot->payload[0], slot->payload[1]));
+ if (!tgt_assert_enable) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_INFO,
+ ("%s: tgt Assert Disabled\n", __func__));
+ return;
+ }
+ process_wma_set_command_twoargs(0,
+ (int)GEN_PARAM_CRASH_INJECT,
+ slot->payload[0],
+ slot->payload[1], GEN_CMD);
+ break;
+ default:
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Unknown cmd[%d] error\n",
+ slot->diag_type));
+ break;
+ }
+}
+
/**---------------------------------------------------------------------------
\brief cnss_diag_msg_callback() - Call back invoked by netlink service
@@ -3930,46 +3998,78 @@
--------------------------------------------------------------------------*/
int cnss_diag_msg_callback(struct sk_buff *skb)
{
- struct nlmsghdr *nlh;
- struct dbglog_slot *slot;
- A_UINT8 *msg;
+ struct nlmsghdr *nlh;
+ A_UINT8 *msg;
- nlh = (struct nlmsghdr *)skb->data;
- if (!nlh)
- {
- AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Netlink header null \n", __func__));
- return -1;
- }
+ nlh = (struct nlmsghdr *)skb->data;
+ if (!nlh) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Netlink header null \n",
+ __func__));
+ return -1;
+ }
- msg = NLMSG_DATA(nlh);
+ msg = NLMSG_DATA(nlh);
+ cnss_diag_handle_crash_inject((struct dbglog_slot *)msg);
- slot = (struct dbglog_slot *)msg;
- switch (slot->diag_type) {
- case DIAG_TYPE_CRASH_INJECT:
- if (slot->length == 2) {
- AR_DEBUG_PRINTF(ATH_DEBUG_INFO,
- ("%s : DIAG_TYPE_CRASH_INJECT: %d %d\n", __func__,
- slot->payload[0], slot->payload[1]));
- if (!tgt_assert_enable) {
- AR_DEBUG_PRINTF(ATH_DEBUG_INFO,
- ("%s: tgt Assert Disabled\n", __func__));
- return 0;
- }
- process_wma_set_command_twoargs(0,
- (int)GEN_PARAM_CRASH_INJECT,
- slot->payload[0],
- slot->payload[1], GEN_CMD);
- }
- else
- AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("crash_inject cmd error\n"));
- break;
- default:
- break;
- }
- return 0;
-
+ return 0;
}
+#ifdef CNSS_GENL
+
+/**
+ * cnss_diag_cmd_handler() - API to handle CNSS diag command
+ * @data: Data received
+ * @data_len: length of the data received
+ * @ctx: Pointer to stored context
+ * @pid: Process ID
+ *
+ * API to handle CNSS diag commands from user space
+ *
+ * Return: None
+ */
+static void cnss_diag_cmd_handler(const void *data, int data_len,
+ void *ctx, int pid)
+{
+ struct dbglog_slot *slot = NULL;
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+ if (nla_parse(tb, CLD80211_ATTR_MAX, data, data_len, NULL)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: nla parse fails \n",
+ __func__));
+ return;
+ }
+
+ if (!tb[CLD80211_ATTR_DATA]) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: attr VENDOR_DATA fails \n",
+ __func__));
+ return;
+ }
+ slot = (struct dbglog_slot *)nla_data(tb[CLD80211_ATTR_DATA]);
+
+ if (!slot) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: data NULL \n", __func__));
+ return;
+ }
+
+ cnss_diag_handle_crash_inject(slot);
+ return;
+}
+
+/**
+ * cnss_diag_activate_service() - API to register CNSS diag cmd handler
+ *
+ * API to register the CNSS diag command handler using new genl infra.
+ * Return type is zero to match with legacy prototype
+ *
+ * Return: 0
+ */
+int cnss_diag_activate_service(void)
+{
+ register_cld_cmd_cb(WLAN_NL_MSG_CNSS_DIAG, cnss_diag_cmd_handler, NULL);
+ return 0;
+}
+
+#else
/**---------------------------------------------------------------------------
\brief cnss_diag_activate_service() - Activate cnss_diag message handler
@@ -3981,7 +4081,7 @@
\return - 0 for success, non zero for failure
--------------------------------------------------------------------------*/
-int cnss_diag_activate_service()
+int cnss_diag_activate_service(void)
{
int ret = 0;
@@ -3995,6 +4095,7 @@
kd_nl_init = TRUE;
return 0;
}
+#endif
A_BOOL
dbglog_wow_print_handler(
diff --git a/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_api.h b/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_api.h
index 76a9ac5..a561d53 100644
--- a/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_api.h
+++ b/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_api.h
@@ -341,6 +341,12 @@
v_U64_t vos_get_monotonic_boottime(void);
+/**
+ * vos_get_monotonic_boottime_ns - Get kenel boottime in ns
+ *
+ * Return: kernel boottime in nano sec
+ */
+v_U64_t vos_get_monotonic_boottime_ns(void);
void vos_trigger_recovery(bool);
#ifdef FEATURE_WLAN_D0WOW
diff --git a/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_timer.h b/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_timer.h
index 8e42742..1063b87 100644
--- a/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_timer.h
+++ b/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_timer.h
@@ -351,4 +351,5 @@
}
unsigned long vos_get_time_of_the_day_ms(void);
+void vos_get_time_of_the_day_in_hr_min_sec_usec(char *tbuf, int len);
#endif // #if !defined __VOSS_TIMER_H
diff --git a/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_trace.h b/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_trace.h
index 74a0187..f5bc981 100644
--- a/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_trace.h
+++ b/drivers/staging/qcacld-2.0/CORE/VOSS/inc/vos_trace.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2014, 2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -111,12 +111,17 @@
#endif
+#define ROW_SIZE 16
+/* Buffer size = data bytes(2 hex chars plus space) + NULL */
+#define BUFFER_SIZE ((ROW_SIZE * 3) + 1)
+
+
/*--------------------------------------------------------------------------
Structure definition
------------------------------------------------------------------------*/
typedef struct svosTraceRecord
{
- v_U64_t time;
+ char time[20];
v_U8_t module;
v_U8_t code;
v_U16_t session;
diff --git a/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_api.c b/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_api.c
index 3004ccc..dda19e9 100644
--- a/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_api.c
+++ b/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_api.c
@@ -2736,7 +2736,8 @@
if ((WLAN_LOG_INDICATOR_HOST_DRIVER == *indicator) &&
((WLAN_LOG_REASON_SME_OUT_OF_CMD_BUF == *reason_code) ||
(WLAN_LOG_REASON_SME_COMMAND_STUCK == *reason_code) ||
- (WLAN_LOG_REASON_STALE_SESSION_FOUND == *reason_code)))
+ (WLAN_LOG_REASON_STALE_SESSION_FOUND == *reason_code) ||
+ (WLAN_LOG_REASON_SCAN_NOT_ALLOWED == *reason_code)))
*is_ssr_needed = true;
else
*is_ssr_needed = false;
@@ -2989,3 +2990,11 @@
wlan_pkt_stats_to_logger_thread(pl_hdr, pkt_dump, data);
}
+
+v_U64_t vos_get_monotonic_boottime_ns(void)
+{
+ struct timespec ts;
+
+ ktime_get_ts(&ts);
+ return timespec_to_ns(&ts);
+}
diff --git a/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_nvitem.c b/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_nvitem.c
index 82afaeb..e69f4a3 100644
--- a/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_nvitem.c
+++ b/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_nvitem.c
@@ -908,7 +908,7 @@
struct ch_params_s *ch_params)
{
eNVChannelEnabledType chan_state = NV_CHANNEL_ENABLE;
- const struct bonded_chan *bonded_chan_ptr;
+ const struct bonded_chan *bonded_chan_ptr = NULL;
uint16_t center_chan;
if (CH_WIDTH_MAX <= ch_params->ch_width)
@@ -2452,4 +2452,3 @@
return VOS_STATUS_SUCCESS;
}
-
diff --git a/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_packet.c b/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_packet.c
index e81c641..9e26e7d 100644
--- a/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_packet.c
+++ b/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_packet.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -270,7 +270,7 @@
/* EAPOL Tracking enabled */
if (VOS_PKT_TRAC_TYPE_EAPOL & tracking_map)
{
- if (adf_nbuf_is_eapol_pkt(skb) == A_STATUS_OK) {
+ if (adf_nbuf_is_eapol_pkt(skb)) {
pkt_proto_type = VOS_PKT_TRAC_TYPE_EAPOL;
return pkt_proto_type;
}
@@ -279,7 +279,7 @@
/* DHCP Tracking enabled */
if (VOS_PKT_TRAC_TYPE_DHCP & tracking_map)
{
- if (adf_nbuf_is_dhcp_pkt(skb) == A_STATUS_OK) {
+ if (adf_nbuf_is_dhcp_pkt(skb)) {
pkt_proto_type = VOS_PKT_TRAC_TYPE_DHCP;
return pkt_proto_type;
}
diff --git a/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_timer.c b/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_timer.c
index 48e3d57..ef41710 100644
--- a/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_timer.c
+++ b/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_timer.c
@@ -869,3 +869,19 @@
(tm.tm_min *60 * 1000) + (tm.tm_sec * 1000)+
(tv.tv_usec/1000));
}
+
+void vos_get_time_of_the_day_in_hr_min_sec_usec(char *tbuf, int len)
+{
+ struct timeval tv;
+ struct rtc_time tm;
+ unsigned long local_time;
+
+ /* Format the Log time R#: [hr:min:sec.microsec] */
+ do_gettimeofday(&tv);
+ /* Convert rtc to local time */
+ local_time = (u32)(tv.tv_sec - (sys_tz.tz_minuteswest * 60));
+ rtc_time_to_tm(local_time, &tm);
+ snprintf(tbuf, len,
+ "[%02d:%02d:%02d.%06lu] ",
+ tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec);
+}
diff --git a/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_trace.c b/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_trace.c
index 0cd7dc7..b25ef46 100644
--- a/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_trace.c
+++ b/drivers/staging/qcacld-2.0/CORE/VOSS/src/vos_trace.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2016-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -335,10 +335,6 @@
}
}
-#define ROW_SIZE 16
-/* Buffer size = data bytes(2 hex chars plus space) + NULL */
-#define BUFFER_SIZE ((ROW_SIZE * 3) + 1)
-
/*----------------------------------------------------------------------------
\brief vos_trace_hex_dump() - Externally called hex dump function
@@ -503,6 +499,7 @@
{
tpvosTraceRecord rec = NULL;
unsigned long flags;
+ char time[20];
if (!gvosTraceData.enable)
{
@@ -514,6 +511,8 @@
return;
}
+ vos_get_time_of_the_day_in_hr_min_sec_usec(time, sizeof(time));
+
/* Aquire the lock so that only one thread at a time can fill the ring buffer */
spin_lock_irqsave(<raceLock, flags);
@@ -551,13 +550,12 @@
gvosTraceData.tail = tail;
}
-
rec = &gvosTraceTbl[gvosTraceData.tail];
rec->code = code;
rec->session = session;
rec->data = data;
- rec->time = adf_get_boottime();
- rec->module = module;
+ snprintf(rec->time, sizeof(rec->time), "%s", time);
+ rec->module = module;
rec->pid = (in_interrupt() ? 0 : current->pid);
gvosTraceData.numSinceLastDump ++;
spin_unlock_irqrestore(<raceLock, flags);
diff --git a/drivers/staging/qcacld-2.0/CORE/WDA/inc/legacy/halMsgApi.h b/drivers/staging/qcacld-2.0/CORE/WDA/inc/legacy/halMsgApi.h
index e268f9c..4d5f05c 100644
--- a/drivers/staging/qcacld-2.0/CORE/WDA/inc/legacy/halMsgApi.h
+++ b/drivers/staging/qcacld-2.0/CORE/WDA/inc/legacy/halMsgApi.h
@@ -742,18 +742,11 @@
#ifdef FEATURE_OEM_DATA_SUPPORT
-#ifndef OEM_DATA_REQ_SIZE
-#define OEM_DATA_REQ_SIZE 280
-#endif
-#ifndef OEM_DATA_RSP_SIZE
-#define OEM_DATA_RSP_SIZE 1724
-#endif
-
typedef struct
{
tSirMacAddr selfMacAddr;
eHalStatus status;
- uint8_t data_len;
+ uint32_t data_len;
uint8_t *data;
} tStartOemDataReq, *tpStartOemDataReq;
diff --git a/drivers/staging/qcacld-2.0/CORE/WDA/inc/wlan_qct_wda.h b/drivers/staging/qcacld-2.0/CORE/WDA/inc/wlan_qct_wda.h
index f969cfa..110186d 100644
--- a/drivers/staging/qcacld-2.0/CORE/WDA/inc/wlan_qct_wda.h
+++ b/drivers/staging/qcacld-2.0/CORE/WDA/inc/wlan_qct_wda.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -1094,6 +1094,7 @@
#define WDA_REMOVE_BCN_FILTER_CMDID SIR_HAL_REMOVE_BCN_FILTER_CMDID
#define WDA_UPDATE_TX_RATE SIR_HAL_UPDATE_TX_RATE
+#define WDA_ACTION_FRAME_RANDOM_MAC SIR_HAL_ACTION_FRAME_RANDOM_MAC
tSirRetStatus wdaPostCtrlMsg(tpAniSirGlobal pMac, tSirMsgQ *pMsg);
diff --git a/drivers/staging/qcacld-2.0/Kbuild b/drivers/staging/qcacld-2.0/Kbuild
index 94e4f04..94ce047 100644
--- a/drivers/staging/qcacld-2.0/Kbuild
+++ b/drivers/staging/qcacld-2.0/Kbuild
@@ -140,6 +140,11 @@
endif
endif
+#Enable Power debugfs feature only if debug_fs is enabled
+ifeq ($(CONFIG_DEBUG_FS), y)
+CONFIG_WLAN_POWER_DEBUGFS := y
+endif
+
ifdef CPTCFG_QCA_CLD_WLAN
WLAN_ROOT := $(BACKPORT_PWD)/drivers/staging/qcacld-2.0
CONFIG_QCA_CLD_WLAN=$(CPTCFG_QCA_CLD_WLAN)
@@ -616,7 +621,6 @@
$(SME_SRC_DIR)/sme_common/sme_FTApi.o \
$(SME_SRC_DIR)/sme_common/sme_Trace.o
-SME_BTC_OBJS := $(SME_SRC_DIR)/btc/btcApi.o
SME_OEM_DATA_OBJS := $(SME_SRC_DIR)/oemData/oemDataApi.o
@@ -632,8 +636,7 @@
SME_NDP_OBJS += $(SME_SRC_DIR)/nan/nan_datapath_api.o
endif
-SME_OBJS := $(SME_BTC_OBJS) \
- $(SME_CCM_OBJS) \
+SME_OBJS := $(SME_CCM_OBJS) \
$(SME_CMN_OBJS) \
$(SME_CSR_OBJS) \
$(SME_OEM_DATA_OBJS) \
@@ -652,9 +655,6 @@
SVC_INC := -I$(WLAN_ROOT)/$(SVC_INC_DIR) \
-I$(WLAN_ROOT)/$(SVC_DIR)/external
-BTC_SRC_DIR := $(SVC_SRC_DIR)/btc
-BTC_OBJS := $(BTC_SRC_DIR)/wlan_btc_svc.o
-
NLINK_SRC_DIR := $(SVC_SRC_DIR)/nlink
NLINK_OBJS := $(NLINK_SRC_DIR)/wlan_nlink_srv.o
@@ -664,8 +664,7 @@
WLAN_LOGGING_SRC_DIR := $(SVC_SRC_DIR)/logging
WLAN_LOGGING_OBJS := $(WLAN_LOGGING_SRC_DIR)/wlan_logging_sock_svc.o
-SVC_OBJS := $(BTC_OBJS) \
- $(NLINK_OBJS) \
+SVC_OBJS := $(NLINK_OBJS) \
$(PTT_OBJS) \
$(WLAN_LOGGING_OBJS)
@@ -1006,6 +1005,10 @@
-DHTC_CRP_DEBUG \
-DWLAN_VOWIFI_DEBUG
+ifeq ($(CONFIG_WLAN_POWER_DEBUGFS), y)
+CDEFINES += -DWLAN_POWER_DEBUGFS
+endif
+
ifeq ($(CONFIG_SCPC_FEATURE), y)
CDEFINES += -DWLAN_SCPC_FEATURE
endif
@@ -1128,6 +1131,10 @@
CDEFINES += -DWLAN_FEATURE_ROAM_OFFLOAD
endif
+ifeq ($(CONFIG_CNSS_GENL), y)
+CDEFINES += -DCNSS_GENL
+endif
+
ifeq ($(CONFIG_PRIMA_WLAN_OKC),y)
CDEFINES += -DFEATURE_WLAN_OKC
endif
@@ -1164,6 +1171,8 @@
# enable the MAC Address auto-generation feature
CDEFINES += -DWLAN_AUTOGEN_MACADDR_FEATURE
+CDEFINES += -DDESC_DUP_DETECT_DEBUG
+
ifeq ($(CONFIG_WLAN_FEATURE_11W),y)
CDEFINES += -DWLAN_FEATURE_11W
endif
diff --git a/drivers/staging/rtl8723au/core/rtw_wlan_util.c b/drivers/staging/rtl8723au/core/rtw_wlan_util.c
index 09c44a5..34f0de6 100644
--- a/drivers/staging/rtl8723au/core/rtw_wlan_util.c
+++ b/drivers/staging/rtl8723au/core/rtw_wlan_util.c
@@ -525,7 +525,7 @@
else
aSifsTime = 16;
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < 4; i++) {
ACI = (pmlmeinfo->WMM_param.ac_param[i].ACI_AIFSN >> 5) & 0x03;
ACM = (pmlmeinfo->WMM_param.ac_param[i].ACI_AIFSN >> 4) & 0x01;
diff --git a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c
index 3d26955..f319160 100644
--- a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c
+++ b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c
@@ -378,7 +378,7 @@
GFP_ATOMIC);
} else {
cfg80211_disconnected(padapter->pnetdev, 0, NULL,
- 0, GFP_ATOMIC);
+ 0, false, GFP_ATOMIC);
}
}
}
diff --git a/drivers/staging/unisys/visorutil/periodic_work.c b/drivers/staging/unisys/visorutil/periodic_work.c
index 3dd1c04..95802d0 100644
--- a/drivers/staging/unisys/visorutil/periodic_work.c
+++ b/drivers/staging/unisys/visorutil/periodic_work.c
@@ -98,8 +98,8 @@
pw->want_to_stop = FALSE;
rc = TRUE; /* yes, TRUE; see visor_periodic_work_stop() */
goto unlock;
- } else if (queue_delayed_work(pw->workqueue, &pw->work,
- pw->jiffy_interval) < 0) {
+ } else if (!queue_delayed_work(pw->workqueue, &pw->work,
+ pw->jiffy_interval)) {
ERRDEV(pw->devnam, "queue_delayed_work failed!");
pw->is_scheduled = FALSE;
rc = FALSE;
@@ -134,8 +134,8 @@
goto unlock;
}
INIT_DELAYED_WORK(&pw->work, &periodic_work_func);
- if (queue_delayed_work(pw->workqueue, &pw->work,
- pw->jiffy_interval) < 0) {
+ if (!queue_delayed_work(pw->workqueue, &pw->work,
+ pw->jiffy_interval)) {
ERRDEV(pw->devnam, "%s queue_delayed_work failed!", __func__);
rc = FALSE;
goto unlock;
diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c
index ea24051..0670fcb 100644
--- a/drivers/staging/vt6655/device_main.c
+++ b/drivers/staging/vt6655/device_main.c
@@ -2818,11 +2818,13 @@
pr_debug(" SIOCSIWSENS\n");
rc = -EOPNOTSUPP;
break;
-
case SIOCGIWAPLIST: {
- char buffer[IW_MAX_AP * (sizeof(struct sockaddr) + sizeof(struct iw_quality))];
+ char *buffer = kzalloc(IW_MAX_AP * (sizeof(struct sockaddr) +
+ sizeof(struct iw_quality)), GFP_KERNEL);
- if (wrq->u.data.pointer) {
+ if (!buffer) {
+ rc = -ENOMEM;
+ } else if (wrq->u.data.pointer) {
rc = iwctl_giwaplist(dev, NULL, &(wrq->u.data), buffer);
if (rc == 0) {
if (copy_to_user(wrq->u.data.pointer,
@@ -2832,6 +2834,7 @@
rc = -EFAULT;
}
}
+ kfree(buffer);
}
break;
@@ -2878,7 +2881,6 @@
pr_debug(" SIOCGIWGENIE\n");
rc = iwctl_giwgenie(dev, NULL, &(wrq->u.data), wrq->u.data.pointer);
break;
-
case SIOCSIWENCODEEXT: {
char extra[sizeof(struct iw_encode_ext)+MAX_KEY_LEN+1];
diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c
index 8942dcb..12594a3 100644
--- a/drivers/staging/wlan-ng/cfg80211.c
+++ b/drivers/staging/wlan-ng/cfg80211.c
@@ -722,7 +722,7 @@
void prism2_disconnected(wlandevice_t *wlandev)
{
cfg80211_disconnected(wlandev->netdev, 0, NULL,
- 0, GFP_KERNEL);
+ 0, false, GFP_KERNEL);
}
void prism2_roamed(wlandevice_t *wlandev)
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 0b1fdfe..450fbad 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -502,7 +502,8 @@
bool scsi_cmd = (cmd->iscsi_opcode == ISCSI_OP_SCSI_CMD);
spin_lock_bh(&conn->cmd_lock);
- if (!list_empty(&cmd->i_conn_node))
+ if (!list_empty(&cmd->i_conn_node) &&
+ !(cmd->se_cmd.transport_state & CMD_T_FABRIC_STOP))
list_del_init(&cmd->i_conn_node);
spin_unlock_bh(&conn->cmd_lock);
@@ -4174,6 +4175,7 @@
static void iscsit_release_commands_from_conn(struct iscsi_conn *conn)
{
+ LIST_HEAD(tmp_list);
struct iscsi_cmd *cmd = NULL, *cmd_tmp = NULL;
struct iscsi_session *sess = conn->sess;
/*
@@ -4182,18 +4184,26 @@
* has been reset -> returned sleeping pre-handler state.
*/
spin_lock_bh(&conn->cmd_lock);
- list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) {
+ list_splice_init(&conn->conn_cmd_list, &tmp_list);
- list_del_init(&cmd->i_conn_node);
- spin_unlock_bh(&conn->cmd_lock);
+ list_for_each_entry(cmd, &tmp_list, i_conn_node) {
+ struct se_cmd *se_cmd = &cmd->se_cmd;
- iscsit_increment_maxcmdsn(cmd, sess);
-
- iscsit_free_cmd(cmd, true);
-
- spin_lock_bh(&conn->cmd_lock);
+ if (se_cmd->se_tfo != NULL) {
+ spin_lock(&se_cmd->t_state_lock);
+ se_cmd->transport_state |= CMD_T_FABRIC_STOP;
+ spin_unlock(&se_cmd->t_state_lock);
+ }
}
spin_unlock_bh(&conn->cmd_lock);
+
+ list_for_each_entry_safe(cmd, cmd_tmp, &tmp_list, i_conn_node) {
+ list_del_init(&cmd->i_conn_node);
+
+ iscsit_increment_maxcmdsn(cmd, sess);
+ iscsit_free_cmd(cmd, true);
+
+ }
}
static void iscsit_stop_timers_for_cmds(
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index eb320e6..b06d3c0 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -1414,8 +1414,9 @@
}
login->zero_tsih = zero_tsih;
- conn->sess->se_sess->sup_prot_ops =
- conn->conn_transport->iscsit_get_sup_prot_ops(conn);
+ if (conn->sess)
+ conn->sess->se_sess->sup_prot_ops =
+ conn->conn_transport->iscsit_get_sup_prot_ops(conn);
tpg = conn->tpg;
if (!tpg) {
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
index 18c2926..578f5d0 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.c
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -804,22 +804,6 @@
if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH))
SET_PSTATE_REPLY_OPTIONAL(param);
/*
- * The GlobalSAN iSCSI Initiator for MacOSX does
- * not respond to MaxBurstLength, FirstBurstLength,
- * DefaultTime2Wait or DefaultTime2Retain parameter keys.
- * So, we set them to 'reply optional' here, and assume the
- * the defaults from iscsi_parameters.h if the initiator
- * is not RFC compliant and the keys are not negotiated.
- */
- if (!strcmp(param->name, MAXBURSTLENGTH))
- SET_PSTATE_REPLY_OPTIONAL(param);
- if (!strcmp(param->name, FIRSTBURSTLENGTH))
- SET_PSTATE_REPLY_OPTIONAL(param);
- if (!strcmp(param->name, DEFAULTTIME2WAIT))
- SET_PSTATE_REPLY_OPTIONAL(param);
- if (!strcmp(param->name, DEFAULTTIME2RETAIN))
- SET_PSTATE_REPLY_OPTIONAL(param);
- /*
* Required for gPXE iSCSI boot client
*/
if (!strcmp(param->name, MAXCONNECTIONS))
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
index 9062bda..fdbf318 100644
--- a/drivers/target/iscsi/iscsi_target_util.c
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -731,21 +731,23 @@
{
struct se_cmd *se_cmd = NULL;
int rc;
+ bool op_scsi = false;
/*
* Determine if a struct se_cmd is associated with
* this struct iscsi_cmd.
*/
switch (cmd->iscsi_opcode) {
case ISCSI_OP_SCSI_CMD:
- se_cmd = &cmd->se_cmd;
- __iscsit_free_cmd(cmd, true, shutdown);
+ op_scsi = true;
/*
* Fallthrough
*/
case ISCSI_OP_SCSI_TMFUNC:
- rc = transport_generic_free_cmd(&cmd->se_cmd, shutdown);
- if (!rc && shutdown && se_cmd && se_cmd->se_sess) {
- __iscsit_free_cmd(cmd, true, shutdown);
+ se_cmd = &cmd->se_cmd;
+ __iscsit_free_cmd(cmd, op_scsi, shutdown);
+ rc = transport_generic_free_cmd(se_cmd, shutdown);
+ if (!rc && shutdown && se_cmd->se_sess) {
+ __iscsit_free_cmd(cmd, op_scsi, shutdown);
target_put_sess_cmd(se_cmd);
}
break;
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index 3ee77db..aced4c4 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -157,7 +157,7 @@
buf = kzalloc(12, GFP_KERNEL);
if (!buf)
- return;
+ goto out_free;
memset(cdb, 0, MAX_COMMAND_SIZE);
cdb[0] = MODE_SENSE;
@@ -172,9 +172,10 @@
* If MODE_SENSE still returns zero, set the default value to 1024.
*/
sdev->sector_size = (buf[9] << 16) | (buf[10] << 8) | (buf[11]);
+out_free:
if (!sdev->sector_size)
sdev->sector_size = 1024;
-out_free:
+
kfree(buf);
}
@@ -317,9 +318,10 @@
sd->lun, sd->queue_depth);
}
- dev->dev_attrib.hw_block_size = sd->sector_size;
+ dev->dev_attrib.hw_block_size =
+ min_not_zero((int)sd->sector_size, 512);
dev->dev_attrib.hw_max_sectors =
- min_t(int, sd->host->max_sectors, queue_max_hw_sectors(q));
+ min_not_zero(sd->host->max_sectors, queue_max_hw_sectors(q));
dev->dev_attrib.hw_queue_depth = sd->queue_depth;
/*
@@ -342,8 +344,10 @@
/*
* For TYPE_TAPE, attempt to determine blocksize with MODE_SENSE.
*/
- if (sd->type == TYPE_TAPE)
+ if (sd->type == TYPE_TAPE) {
pscsi_tape_read_blocksize(dev, sd);
+ dev->dev_attrib.hw_block_size = sd->sector_size;
+ }
return 0;
}
@@ -409,7 +413,7 @@
/*
* Called with struct Scsi_Host->host_lock called.
*/
-static int pscsi_create_type_rom(struct se_device *dev, struct scsi_device *sd)
+static int pscsi_create_type_nondisk(struct se_device *dev, struct scsi_device *sd)
__releases(sh->host_lock)
{
struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
@@ -436,28 +440,6 @@
return 0;
}
-/*
- * Called with struct Scsi_Host->host_lock called.
- */
-static int pscsi_create_type_other(struct se_device *dev,
- struct scsi_device *sd)
- __releases(sh->host_lock)
-{
- struct pscsi_hba_virt *phv = dev->se_hba->hba_ptr;
- struct Scsi_Host *sh = sd->host;
- int ret;
-
- spin_unlock_irq(sh->host_lock);
- ret = pscsi_add_device_to_list(dev, sd);
- if (ret)
- return ret;
-
- pr_debug("CORE_PSCSI[%d] - Added Type: %s for %d:%d:%d:%llu\n",
- phv->phv_host_id, scsi_device_type(sd->type), sh->host_no,
- sd->channel, sd->id, sd->lun);
- return 0;
-}
-
static int pscsi_configure_device(struct se_device *dev)
{
struct se_hba *hba = dev->se_hba;
@@ -545,11 +527,8 @@
case TYPE_DISK:
ret = pscsi_create_type_disk(dev, sd);
break;
- case TYPE_ROM:
- ret = pscsi_create_type_rom(dev, sd);
- break;
default:
- ret = pscsi_create_type_other(dev, sd);
+ ret = pscsi_create_type_nondisk(dev, sd);
break;
}
@@ -606,8 +585,7 @@
else if (pdv->pdv_lld_host)
scsi_host_put(pdv->pdv_lld_host);
- if ((sd->type == TYPE_DISK) || (sd->type == TYPE_ROM))
- scsi_device_put(sd);
+ scsi_device_put(sd);
pdv->pdv_sd = NULL;
}
@@ -1133,7 +1111,6 @@
if (pdv->pdv_bd && pdv->pdv_bd->bd_part)
return pdv->pdv_bd->bd_part->nr_sects;
- dump_stack();
return 0;
}
diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c
index 496133a..2cd7e23 100644
--- a/drivers/target/target_core_sbc.c
+++ b/drivers/target/target_core_sbc.c
@@ -946,9 +946,15 @@
return ret;
break;
case VERIFY:
+ case VERIFY_16:
size = 0;
- sectors = transport_get_sectors_10(cdb);
- cmd->t_task_lba = transport_lba_32(cdb);
+ if (cdb[0] == VERIFY) {
+ sectors = transport_get_sectors_10(cdb);
+ cmd->t_task_lba = transport_lba_32(cdb);
+ } else {
+ sectors = transport_get_sectors_16(cdb);
+ cmd->t_task_lba = transport_lba_64(cdb);
+ }
cmd->execute_cmd = sbc_emulate_noop;
goto check_lba;
case REZERO_UNIT:
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 1a487f9..837d464 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -2451,15 +2451,9 @@
struct se_session *se_sess = se_cmd->se_sess;
bool fabric_stop;
- if (list_empty(&se_cmd->se_cmd_list)) {
- spin_unlock(&se_sess->sess_cmd_lock);
- target_free_cmd_mem(se_cmd);
- se_cmd->se_tfo->release_cmd(se_cmd);
- return;
- }
-
spin_lock(&se_cmd->t_state_lock);
- fabric_stop = (se_cmd->transport_state & CMD_T_FABRIC_STOP);
+ fabric_stop = (se_cmd->transport_state & CMD_T_FABRIC_STOP) &&
+ (se_cmd->transport_state & CMD_T_ABORTED);
spin_unlock(&se_cmd->t_state_lock);
if (se_cmd->cmd_wait_set || fabric_stop) {
diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index 9a1b314..90e55b4 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -587,8 +587,6 @@
target_complete_cmd(cmd->se_cmd, SAM_STAT_CHECK_CONDITION);
cmd->se_cmd = NULL;
- kmem_cache_free(tcmu_cmd_cache, cmd);
-
return 0;
}
diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c
index e9186cd..01ee127 100644
--- a/drivers/target/target_core_xcopy.c
+++ b/drivers/target/target_core_xcopy.c
@@ -698,6 +698,7 @@
rc = target_xcopy_setup_pt_cmd(xpt_cmd, xop, src_dev, &cdb[0],
remote_port, true);
if (rc < 0) {
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
transport_generic_free_cmd(se_cmd, 0);
return rc;
}
@@ -709,6 +710,7 @@
rc = target_xcopy_issue_pt_cmd(xpt_cmd);
if (rc < 0) {
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
transport_generic_free_cmd(se_cmd, 0);
return rc;
}
@@ -759,6 +761,7 @@
remote_port, false);
if (rc < 0) {
struct se_cmd *src_cmd = &xop->src_pt_cmd->se_cmd;
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
/*
* If the failure happened before the t_mem_list hand-off in
* target_xcopy_setup_pt_cmd(), Reset memory + clear flag so that
@@ -774,6 +777,7 @@
rc = target_xcopy_issue_pt_cmd(xpt_cmd);
if (rc < 0) {
+ ec_cmd->scsi_status = xpt_cmd->se_cmd.scsi_status;
se_cmd->se_cmd_flags &= ~SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC;
transport_generic_free_cmd(se_cmd, 0);
return rc;
@@ -860,9 +864,14 @@
out:
xcopy_pt_undepend_remotedev(xop);
kfree(xop);
-
- pr_warn("target_xcopy_do_work: Setting X-COPY CHECK_CONDITION -> sending response\n");
- ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
+ /*
+ * Don't override an error scsi status if it has already been set
+ */
+ if (ec_cmd->scsi_status == SAM_STAT_GOOD) {
+ pr_warn_ratelimited("target_xcopy_do_work: rc: %d, Setting X-COPY"
+ " CHECK_CONDITION -> sending response\n", rc);
+ ec_cmd->scsi_status = SAM_STAT_CHECK_CONDITION;
+ }
target_complete_cmd(ec_cmd, SAM_STAT_CHECK_CONDITION);
}
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index 1967bee..9035fbc 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -98,7 +98,7 @@
long temperature;
int ret;
- ret = tz->ops->get_trip_temp(tz, 0, &temperature);
+ ret = tz->ops->get_crit_temp(tz, &temperature);
if (ret)
return ret;
diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c
index 0dde34e..545c60c 100644
--- a/drivers/thunderbolt/eeprom.c
+++ b/drivers/thunderbolt/eeprom.c
@@ -444,6 +444,7 @@
return tb_drom_parse_entries(sw);
err:
kfree(sw->drom);
+ sw->drom = NULL;
return -EIO;
}
diff --git a/drivers/tty/isicom.c b/drivers/tty/isicom.c
index 858291c..c03ecaa 100644
--- a/drivers/tty/isicom.c
+++ b/drivers/tty/isicom.c
@@ -1055,7 +1055,7 @@
outw(0x8000 | ((port->channel) << (card->shift_count)) | 0x3, base);
outw((length & 0xff) << 8 | 0x00, base);
- outw((length & 0xff00), base);
+ outw((length & 0xff00u), base);
InterruptTheCard(base);
unlock_card(card);
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index bce16e4..db37ee4 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -2045,7 +2045,9 @@
}
}
spin_unlock(&gsm_mux_lock);
- WARN_ON(i == MAX_MUX);
+ /* open failed before registering => nothing to do */
+ if (i == MAX_MUX)
+ return;
/* In theory disconnecting DLCI 0 is sufficient but for some
modems this is apparently not the case. */
diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c
index 644ddb8..6d1e2f7 100644
--- a/drivers/tty/n_hdlc.c
+++ b/drivers/tty/n_hdlc.c
@@ -114,7 +114,7 @@
#define DEFAULT_TX_BUF_COUNT 3
struct n_hdlc_buf {
- struct n_hdlc_buf *link;
+ struct list_head list_item;
int count;
char buf[1];
};
@@ -122,8 +122,7 @@
#define N_HDLC_BUF_SIZE (sizeof(struct n_hdlc_buf) + maxframe)
struct n_hdlc_buf_list {
- struct n_hdlc_buf *head;
- struct n_hdlc_buf *tail;
+ struct list_head list;
int count;
spinlock_t spinlock;
};
@@ -136,7 +135,6 @@
* @backup_tty - TTY to use if tty gets closed
* @tbusy - reentrancy flag for tx wakeup code
* @woke_up - FIXME: describe this field
- * @tbuf - currently transmitting tx buffer
* @tx_buf_list - list of pending transmit frame buffers
* @rx_buf_list - list of received frame buffers
* @tx_free_buf_list - list unused transmit frame buffers
@@ -149,7 +147,6 @@
struct tty_struct *backup_tty;
int tbusy;
int woke_up;
- struct n_hdlc_buf *tbuf;
struct n_hdlc_buf_list tx_buf_list;
struct n_hdlc_buf_list rx_buf_list;
struct n_hdlc_buf_list tx_free_buf_list;
@@ -159,7 +156,8 @@
/*
* HDLC buffer list manipulation functions
*/
-static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list);
+static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
+ struct n_hdlc_buf *buf);
static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
struct n_hdlc_buf *buf);
static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *list);
@@ -209,16 +207,9 @@
{
struct n_hdlc *n_hdlc = tty2n_hdlc(tty);
struct n_hdlc_buf *buf;
- unsigned long flags;
while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list)))
n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf);
- spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags);
- if (n_hdlc->tbuf) {
- n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, n_hdlc->tbuf);
- n_hdlc->tbuf = NULL;
- }
- spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
}
static struct tty_ldisc_ops n_hdlc_ldisc = {
@@ -284,7 +275,6 @@
} else
break;
}
- kfree(n_hdlc->tbuf);
kfree(n_hdlc);
} /* end of n_hdlc_release() */
@@ -403,13 +393,7 @@
n_hdlc->woke_up = 0;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags);
- /* get current transmit buffer or get new transmit */
- /* buffer from list of pending transmit buffers */
-
- tbuf = n_hdlc->tbuf;
- if (!tbuf)
- tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
-
+ tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
while (tbuf) {
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)sending frame %p, count=%d\n",
@@ -421,7 +405,7 @@
/* rollback was possible and has been done */
if (actual == -ERESTARTSYS) {
- n_hdlc->tbuf = tbuf;
+ n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf);
break;
}
/* if transmit error, throw frame away by */
@@ -436,10 +420,7 @@
/* free current transmit buffer */
n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, tbuf);
-
- /* this tx buffer is done */
- n_hdlc->tbuf = NULL;
-
+
/* wait up sleeping writers */
wake_up_interruptible(&tty->write_wait);
@@ -449,10 +430,12 @@
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)frame %p pending\n",
__FILE__,__LINE__,tbuf);
-
- /* buffer not accepted by driver */
- /* set this buffer as pending buffer */
- n_hdlc->tbuf = tbuf;
+
+ /*
+ * the buffer was not accepted by driver,
+ * return it back into tx queue
+ */
+ n_hdlc_buf_return(&n_hdlc->tx_buf_list, tbuf);
break;
}
}
@@ -750,7 +733,8 @@
int error = 0;
int count;
unsigned long flags;
-
+ struct n_hdlc_buf *buf = NULL;
+
if (debuglevel >= DEBUG_LEVEL_INFO)
printk("%s(%d)n_hdlc_tty_ioctl() called %d\n",
__FILE__,__LINE__,cmd);
@@ -764,8 +748,10 @@
/* report count of read data available */
/* in next available frame (if any) */
spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags);
- if (n_hdlc->rx_buf_list.head)
- count = n_hdlc->rx_buf_list.head->count;
+ buf = list_first_entry_or_null(&n_hdlc->rx_buf_list.list,
+ struct n_hdlc_buf, list_item);
+ if (buf)
+ count = buf->count;
else
count = 0;
spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags);
@@ -777,8 +763,10 @@
count = tty_chars_in_buffer(tty);
/* add size of next output frame in queue */
spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock,flags);
- if (n_hdlc->tx_buf_list.head)
- count += n_hdlc->tx_buf_list.head->count;
+ buf = list_first_entry_or_null(&n_hdlc->tx_buf_list.list,
+ struct n_hdlc_buf, list_item);
+ if (buf)
+ count += buf->count;
spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock,flags);
error = put_user(count, (int __user *)arg);
break;
@@ -826,14 +814,14 @@
poll_wait(filp, &tty->write_wait, wait);
/* set bits for operations that won't block */
- if (n_hdlc->rx_buf_list.head)
+ if (!list_empty(&n_hdlc->rx_buf_list.list))
mask |= POLLIN | POLLRDNORM; /* readable */
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
mask |= POLLHUP;
if (tty_hung_up_p(filp))
mask |= POLLHUP;
if (!tty_is_writelocked(tty) &&
- n_hdlc->tx_free_buf_list.head)
+ !list_empty(&n_hdlc->tx_free_buf_list.list))
mask |= POLLOUT | POLLWRNORM; /* writable */
}
return mask;
@@ -853,11 +841,16 @@
if (!n_hdlc)
return NULL;
- n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list);
- n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list);
- n_hdlc_buf_list_init(&n_hdlc->rx_buf_list);
- n_hdlc_buf_list_init(&n_hdlc->tx_buf_list);
-
+ spin_lock_init(&n_hdlc->rx_free_buf_list.spinlock);
+ spin_lock_init(&n_hdlc->tx_free_buf_list.spinlock);
+ spin_lock_init(&n_hdlc->rx_buf_list.spinlock);
+ spin_lock_init(&n_hdlc->tx_buf_list.spinlock);
+
+ INIT_LIST_HEAD(&n_hdlc->rx_free_buf_list.list);
+ INIT_LIST_HEAD(&n_hdlc->tx_free_buf_list.list);
+ INIT_LIST_HEAD(&n_hdlc->rx_buf_list.list);
+ INIT_LIST_HEAD(&n_hdlc->tx_buf_list.list);
+
/* allocate free rx buffer list */
for(i=0;i<DEFAULT_RX_BUF_COUNT;i++) {
buf = kmalloc(N_HDLC_BUF_SIZE, GFP_KERNEL);
@@ -885,63 +878,65 @@
} /* end of n_hdlc_alloc() */
/**
- * n_hdlc_buf_list_init - initialize specified HDLC buffer list
- * @list - pointer to buffer list
+ * n_hdlc_buf_return - put the HDLC buffer after the head of the specified list
+ * @buf_list - pointer to the buffer list
+ * @buf - pointer to the buffer
*/
-static void n_hdlc_buf_list_init(struct n_hdlc_buf_list *list)
+static void n_hdlc_buf_return(struct n_hdlc_buf_list *buf_list,
+ struct n_hdlc_buf *buf)
{
- memset(list, 0, sizeof(*list));
- spin_lock_init(&list->spinlock);
-} /* end of n_hdlc_buf_list_init() */
+ unsigned long flags;
+
+ spin_lock_irqsave(&buf_list->spinlock, flags);
+
+ list_add(&buf->list_item, &buf_list->list);
+ buf_list->count++;
+
+ spin_unlock_irqrestore(&buf_list->spinlock, flags);
+}
/**
* n_hdlc_buf_put - add specified HDLC buffer to tail of specified list
- * @list - pointer to buffer list
+ * @buf_list - pointer to buffer list
* @buf - pointer to buffer
*/
-static void n_hdlc_buf_put(struct n_hdlc_buf_list *list,
+static void n_hdlc_buf_put(struct n_hdlc_buf_list *buf_list,
struct n_hdlc_buf *buf)
{
unsigned long flags;
- spin_lock_irqsave(&list->spinlock,flags);
-
- buf->link=NULL;
- if (list->tail)
- list->tail->link = buf;
- else
- list->head = buf;
- list->tail = buf;
- (list->count)++;
-
- spin_unlock_irqrestore(&list->spinlock,flags);
-
+
+ spin_lock_irqsave(&buf_list->spinlock, flags);
+
+ list_add_tail(&buf->list_item, &buf_list->list);
+ buf_list->count++;
+
+ spin_unlock_irqrestore(&buf_list->spinlock, flags);
} /* end of n_hdlc_buf_put() */
/**
* n_hdlc_buf_get - remove and return an HDLC buffer from list
- * @list - pointer to HDLC buffer list
+ * @buf_list - pointer to HDLC buffer list
*
* Remove and return an HDLC buffer from the head of the specified HDLC buffer
* list.
* Returns a pointer to HDLC buffer if available, otherwise %NULL.
*/
-static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list)
+static struct n_hdlc_buf *n_hdlc_buf_get(struct n_hdlc_buf_list *buf_list)
{
unsigned long flags;
struct n_hdlc_buf *buf;
- spin_lock_irqsave(&list->spinlock,flags);
-
- buf = list->head;
+
+ spin_lock_irqsave(&buf_list->spinlock, flags);
+
+ buf = list_first_entry_or_null(&buf_list->list,
+ struct n_hdlc_buf, list_item);
if (buf) {
- list->head = buf->link;
- (list->count)--;
+ list_del(&buf->list_item);
+ buf_list->count--;
}
- if (!list->head)
- list->tail = NULL;
-
- spin_unlock_irqrestore(&list->spinlock,flags);
+
+ spin_unlock_irqrestore(&buf_list->spinlock, flags);
return buf;
-
} /* end of n_hdlc_buf_get() */
static char hdlc_banner[] __initdata =
diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c
index 74885af..6d1a932 100644
--- a/drivers/tty/nozomi.c
+++ b/drivers/tty/nozomi.c
@@ -823,7 +823,7 @@
struct tty_struct *tty = tty_port_tty_get(&port->port);
int i, ret;
- read_mem32((u32 *) &size, addr, 4);
+ size = __le32_to_cpu(readl(addr));
/* DBG1( "%d bytes port: %d", size, index); */
if (tty && test_bit(TTY_THROTTLED, &tty->flags)) {
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index e5c31ea..04da6f0 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -738,22 +738,16 @@
*/
static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p)
{
- unsigned char old_dll, old_dlm, old_lcr;
- unsigned int id;
+ unsigned char old_lcr;
+ unsigned int id, old_dl;
old_lcr = serial_in(p, UART_LCR);
serial_out(p, UART_LCR, UART_LCR_CONF_MODE_A);
+ old_dl = serial_dl_read(p);
+ serial_dl_write(p, 0);
+ id = serial_dl_read(p);
+ serial_dl_write(p, old_dl);
- old_dll = serial_in(p, UART_DLL);
- old_dlm = serial_in(p, UART_DLM);
-
- serial_out(p, UART_DLL, 0);
- serial_out(p, UART_DLM, 0);
-
- id = serial_in(p, UART_DLL) | serial_in(p, UART_DLM) << 8;
-
- serial_out(p, UART_DLL, old_dll);
- serial_out(p, UART_DLM, old_dlm);
serial_out(p, UART_LCR, old_lcr);
return id;
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index f6e5ef5..54921ac 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -56,6 +56,7 @@
unsigned int nr;
void __iomem *remapped_bar[PCI_NUM_BAR_RESOURCES];
struct pci_serial_quirk *quirk;
+ const struct pciserial_board *board;
int line[0];
};
@@ -2743,6 +2744,8 @@
pbn_b0_4_1152000_200,
pbn_b0_8_1152000_200,
+ pbn_b0_4_1250000,
+
pbn_b0_2_1843200,
pbn_b0_4_1843200,
@@ -2971,6 +2974,13 @@
.uart_offset = 0x200,
},
+ [pbn_b0_4_1250000] = {
+ .flags = FL_BASE0,
+ .num_ports = 4,
+ .base_baud = 1250000,
+ .uart_offset = 8,
+ },
+
[pbn_b0_2_1843200] = {
.flags = FL_BASE0,
.num_ports = 2,
@@ -3859,6 +3869,7 @@
}
}
priv->nr = i;
+ priv->board = board;
return priv;
err_deinit:
@@ -3869,7 +3880,7 @@
}
EXPORT_SYMBOL_GPL(pciserial_init_ports);
-void pciserial_remove_ports(struct serial_private *priv)
+void pciserial_detach_ports(struct serial_private *priv)
{
struct pci_serial_quirk *quirk;
int i;
@@ -3889,7 +3900,11 @@
quirk = find_quirk(priv->dev);
if (quirk->exit)
quirk->exit(priv->dev);
+}
+void pciserial_remove_ports(struct serial_private *priv)
+{
+ pciserial_detach_ports(priv);
kfree(priv);
}
EXPORT_SYMBOL_GPL(pciserial_remove_ports);
@@ -5464,6 +5479,10 @@
{ PCI_DEVICE(0x1c29, 0x1108), .driver_data = pbn_fintek_8 },
{ PCI_DEVICE(0x1c29, 0x1112), .driver_data = pbn_fintek_12 },
+ /* MKS Tenta SCOM-080x serial cards */
+ { PCI_DEVICE(0x1601, 0x0800), .driver_data = pbn_b0_4_1250000 },
+ { PCI_DEVICE(0x1601, 0xa801), .driver_data = pbn_b0_4_1250000 },
+
/*
* These entries match devices with class COMMUNICATION_SERIAL,
* COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL
@@ -5492,7 +5511,7 @@
return PCI_ERS_RESULT_DISCONNECT;
if (priv)
- pciserial_suspend_ports(priv);
+ pciserial_detach_ports(priv);
pci_disable_device(dev);
@@ -5517,9 +5536,18 @@
static void serial8250_io_resume(struct pci_dev *dev)
{
struct serial_private *priv = pci_get_drvdata(dev);
+ const struct pciserial_board *board;
- if (priv)
- pciserial_resume_ports(priv);
+ if (!priv)
+ return;
+
+ board = priv->board;
+ kfree(priv);
+ priv = pciserial_init_ports(dev, board);
+
+ if (!IS_ERR(priv)) {
+ pci_set_drvdata(dev, priv);
+ }
}
static const struct pci_error_handlers serial8250_err_handler = {
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 7c23d69..4691d1e 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -1814,6 +1814,11 @@
UART_PUT_TCR(port, 0);
atmel_port->pdc_tx.ofs = 0;
}
+ /*
+ * in uart_flush_buffer(), the xmit circular buffer has just
+ * been cleared, so we have to reset tx_len accordingly.
+ */
+ sg_dma_len(&atmel_port->sg_tx) = 0;
}
/*
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index d82bfd1..ec9fdbb 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -2221,12 +2221,12 @@
return;
if (!(msm_uport->wakeup.enabled)) {
- enable_irq(msm_uport->wakeup.irq);
- disable_irq(uport->irq);
spin_lock_irqsave(&uport->lock, flags);
msm_uport->wakeup.ignore = 1;
msm_uport->wakeup.enabled = true;
spin_unlock_irqrestore(&uport->lock, flags);
+ disable_irq(uport->irq);
+ enable_irq(msm_uport->wakeup.irq);
} else {
MSM_HS_WARN("%s:Wake up IRQ already enabled", __func__);
}
@@ -2746,6 +2746,7 @@
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
struct msm_hs_tx *tx = &msm_uport->tx;
struct msm_hs_rx *rx = &msm_uport->rx;
+ struct sched_param param = { .sched_priority = 1 };
init_waitqueue_head(&rx->wait);
init_waitqueue_head(&tx->wait);
@@ -2760,6 +2761,8 @@
MSM_HS_ERR("%s(): error creating task", __func__);
goto exit_lh_init;
}
+ sched_setscheduler(rx->task, SCHED_FIFO, ¶m);
+
init_kthread_work(&rx->kwork, msm_serial_hs_rx_work);
init_kthread_worker(&tx->kworker);
@@ -2769,6 +2772,7 @@
MSM_HS_ERR("%s(): error creating task", __func__);
goto exit_lh_init;
}
+ sched_setscheduler(tx->task, SCHED_FIFO, ¶m);
init_kthread_work(&tx->kwork, msm_serial_hs_tx_work);
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index 587d63b..c751604 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -749,6 +749,8 @@
/* check to see if we need to change clock source */
if (ourport->baudclk != clk) {
+ clk_prepare_enable(clk);
+
s3c24xx_serial_setsource(port, clk_sel);
if (!IS_ERR(ourport->baudclk)) {
@@ -756,8 +758,6 @@
ourport->baudclk = ERR_PTR(-EINVAL);
}
- clk_prepare_enable(clk);
-
ourport->baudclk = clk;
ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
}
@@ -1154,7 +1154,7 @@
return -ENODEV;
if (port->mapbase != 0)
- return 0;
+ return -EINVAL;
/* setup info for port */
port->dev = &platdev->dev;
@@ -1204,14 +1204,15 @@
if (IS_ERR(ourport->clk)) {
pr_err("%s: Controller clock not found\n",
dev_name(&platdev->dev));
- return PTR_ERR(ourport->clk);
+ ret = PTR_ERR(ourport->clk);
+ goto err;
}
ret = clk_prepare_enable(ourport->clk);
if (ret) {
pr_err("uart: clock failed to prepare+enable: %d\n", ret);
clk_put(ourport->clk);
- return ret;
+ goto err;
}
/* Keep all interrupts masked and cleared */
@@ -1227,7 +1228,12 @@
/* reset the fifos (and setup the uart) */
s3c24xx_serial_resetport(port, cfg);
+
return 0;
+
+err:
+ port->mapbase = 0;
+ return ret;
}
#ifdef CONFIG_SAMSUNG_CLOCK
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index c107a0f..c859eab 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -1478,6 +1478,9 @@
.type = "serial",
.compatible = "ucc_uart",
},
+ {
+ .compatible = "fsl,t1040-ucc-uart",
+ },
{},
};
MODULE_DEVICE_TABLE(of, ucc_uart_match);
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index c039cfe..15c22d0 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -365,34 +365,22 @@
static void do_compute_shiftstate(void)
{
- unsigned int i, j, k, sym, val;
+ unsigned int k, sym, val;
shift_state = 0;
memset(shift_down, 0, sizeof(shift_down));
- for (i = 0; i < ARRAY_SIZE(key_down); i++) {
-
- if (!key_down[i])
+ for_each_set_bit(k, key_down, min(NR_KEYS, KEY_CNT)) {
+ sym = U(key_maps[0][k]);
+ if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
continue;
- k = i * BITS_PER_LONG;
+ val = KVAL(sym);
+ if (val == KVAL(K_CAPSSHIFT))
+ val = KVAL(K_SHIFT);
- for (j = 0; j < BITS_PER_LONG; j++, k++) {
-
- if (!test_bit(k, key_down))
- continue;
-
- sym = U(key_maps[0][k]);
- if (KTYP(sym) != KT_SHIFT && KTYP(sym) != KT_SLOCK)
- continue;
-
- val = KVAL(sym);
- if (val == KVAL(K_CAPSSHIFT))
- val = KVAL(K_SHIFT);
-
- shift_down[val]++;
- shift_state |= (1 << val);
- }
+ shift_down[val]++;
+ shift_state |= BIT(val);
}
}
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 53c25bc..a57f376 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -863,10 +863,15 @@
if (new_cols == vc->vc_cols && new_rows == vc->vc_rows)
return 0;
+ if (new_screen_size > (4 << 20))
+ return -EINVAL;
newscreen = kmalloc(new_screen_size, GFP_USER);
if (!newscreen)
return -ENOMEM;
+ if (vc == sel_cons)
+ clear_selection();
+
old_rows = vc->vc_rows;
old_row_size = vc->vc_size_row;
@@ -1164,7 +1169,7 @@
break;
case 3: /* erase scroll-back buffer (and whole display) */
scr_memsetw(vc->vc_screenbuf, vc->vc_video_erase_char,
- vc->vc_screenbuf_size >> 1);
+ vc->vc_screenbuf_size);
set_origin(vc);
if (CON_IS_VISIBLE(vc))
update_screen(vc);
@@ -3591,9 +3596,10 @@
goto err;
desc = csw->con_startup();
-
- if (!desc)
+ if (!desc) {
+ retval = -ENODEV;
goto err;
+ }
retval = -EINVAL;
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 2539531..713b9df 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -151,6 +151,8 @@
source "drivers/usb/gadget/Kconfig"
+source "drivers/usb/typec/Kconfig"
+
config USB_LED_TRIG
bool "USB LED Triggers"
depends on LEDS_CLASS && USB_COMMON && LEDS_TRIGGERS
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 8ee5996..2c37484 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -63,3 +63,5 @@
obj-$(CONFIG_USB_COMMON) += common/
obj-$(CONFIG_USBIP_CORE) += usbip/
+
+obj-$(CONFIG_TYPEC) += typec/
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 5c67ee9..0a3e4a7 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -594,6 +594,7 @@
return -ENOMEM;
}
+ spin_lock_init(&ci->lock);
ci->dev = dev;
ci->platdata = dev_get_platdata(dev);
ci->imx28_write_fix = !!(ci->platdata->flags &
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index c42bf8d..7591662 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1794,8 +1794,6 @@
struct device *dev = ci->dev;
int retval = 0;
- spin_lock_init(&ci->lock);
-
ci->gadget.ops = &usb_gadget_ops;
ci->gadget.speed = USB_SPEED_UNKNOWN;
ci->gadget.max_speed = USB_SPEED_HIGH;
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 14ffcd3..a1fa1b7 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -871,8 +871,6 @@
DECLARE_WAITQUEUE(wait, current);
struct async_icount old, new;
- if (arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD ))
- return -EINVAL;
do {
spin_lock_irq(&acm->read_lock);
old = acm->oldcount;
@@ -1341,7 +1339,6 @@
spin_lock_init(&acm->write_lock);
spin_lock_init(&acm->read_lock);
mutex_init(&acm->mutex);
- acm->rx_endpoint = usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress);
acm->is_int_ep = usb_endpoint_xfer_int(epread);
if (acm->is_int_ep)
acm->bInterval = epread->bInterval;
@@ -1391,14 +1388,14 @@
urb->transfer_dma = rb->dma;
if (acm->is_int_ep) {
usb_fill_int_urb(urb, acm->dev,
- acm->rx_endpoint,
+ usb_rcvintpipe(usb_dev, epread->bEndpointAddress),
rb->base,
acm->readsize,
acm_read_bulk_callback, rb,
acm->bInterval);
} else {
usb_fill_bulk_urb(urb, acm->dev,
- acm->rx_endpoint,
+ usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress),
rb->base,
acm->readsize,
acm_read_bulk_callback, rb);
@@ -1725,6 +1722,7 @@
{ USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */
.driver_info = QUIRK_CONTROL_LINE_STATE, },
{ USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
+ { USB_DEVICE(0x2184, 0x0036) }, /* GW Instek AFG-125 */
{ USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
},
/* Motorola H24 HSPA module: */
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 9cca2e7..c15484e 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -95,7 +95,6 @@
struct urb *read_urbs[ACM_NR];
struct acm_rb read_buffers[ACM_NR];
int rx_buflimit;
- int rx_endpoint;
spinlock_t read_lock;
int write_used; /* number of non-empty write buffers */
int transmitting;
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index ec97840..c50740208 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -1163,6 +1163,12 @@
}
}
+ if (!data->bulk_out || !data->bulk_in) {
+ dev_err(&intf->dev, "bulk endpoints not found\n");
+ retcode = -ENODEV;
+ goto err_put;
+ }
+
retcode = get_capabilities(data);
if (retcode)
dev_err(&intf->dev, "can't read capabilities\n");
@@ -1186,6 +1192,7 @@
error_register:
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
+err_put:
kref_put(&data->kref, usbtmc_delete);
return retcode;
}
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index c812fef..27baa36c 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -21,6 +21,7 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mutex.h>
@@ -365,3 +366,4 @@
return state_changed;
}
EXPORT_SYMBOL_GPL(otg_statemachine);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index b2a540b..1fb6303 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -182,8 +182,10 @@
memcpy(&endpoint->desc, d, n);
INIT_LIST_HEAD(&endpoint->urb_list);
- /* Fix up bInterval values outside the legal range. Use 32 ms if no
- * proper value can be guessed. */
+ /*
+ * Fix up bInterval values outside the legal range.
+ * Use 10 or 8 ms if no proper value can be guessed.
+ */
i = 0; /* i = min, j = max, n = default */
j = 255;
if (usb_endpoint_xfer_int(d)) {
@@ -191,17 +193,29 @@
switch (to_usb_device(ddev)->speed) {
case USB_SPEED_SUPER:
case USB_SPEED_HIGH:
- /* Many device manufacturers are using full-speed
+ /*
+ * Many device manufacturers are using full-speed
* bInterval values in high-speed interrupt endpoint
- * descriptors. Try to fix those and fall back to a
- * 32 ms default value otherwise. */
+ * descriptors. Try to fix those and fall back to an
+ * 8-ms default value otherwise.
+ */
n = fls(d->bInterval*8);
if (n == 0)
- n = 9; /* 32 ms = 2^(9-1) uframes */
+ n = 7; /* 8 ms = 2^(7-1) uframes */
j = 16;
/*
* Adjust bInterval for quirked devices.
+ */
+ /*
+ * This quirk fixes bIntervals reported in ms.
+ */
+ if (to_usb_device(ddev)->quirks &
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL) {
+ n = clamp(fls(d->bInterval) + 3, i, j);
+ i = j = n;
+ }
+ /*
* This quirk fixes bIntervals reported in
* linear microframes.
*/
@@ -212,10 +226,12 @@
}
break;
default: /* USB_SPEED_FULL or _LOW */
- /* For low-speed, 10 ms is the official minimum.
+ /*
+ * For low-speed, 10 ms is the official minimum.
* But some "overclocked" devices might want faster
- * polling so we'll allow it. */
- n = 32;
+ * polling so we'll allow it.
+ */
+ n = 10;
break;
}
} else if (usb_endpoint_xfer_isoc(d)) {
@@ -223,10 +239,10 @@
j = 16;
switch (to_usb_device(ddev)->speed) {
case USB_SPEED_HIGH:
- n = 9; /* 32 ms = 2^(9-1) uframes */
+ n = 7; /* 8 ms = 2^(7-1) uframes */
break;
default: /* USB_SPEED_FULL */
- n = 6; /* 32 ms = 2^(6-1) frames */
+ n = 4; /* 8 ms = 2^(4-1) frames */
break;
}
}
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index dfcb5f8..39a8bb8 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1527,11 +1527,17 @@
as->urb->start_frame = uurb->start_frame;
as->urb->number_of_packets = number_of_packets;
as->urb->stream_id = stream_id;
- if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
- ps->dev->speed == USB_SPEED_HIGH)
- as->urb->interval = 1 << min(15, ep->desc.bInterval - 1);
- else
- as->urb->interval = ep->desc.bInterval;
+
+ if (ep->desc.bInterval) {
+ if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
+ ps->dev->speed == USB_SPEED_HIGH ||
+ ps->dev->speed >= USB_SPEED_SUPER)
+ as->urb->interval = 1 <<
+ min(15, ep->desc.bInterval - 1);
+ else
+ as->urb->interval = ep->desc.bInterval;
+ }
+
as->urb->context = as;
as->urb->complete = async_completed;
for (totlen = u = 0; u < number_of_packets; u++) {
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 5e45dfc..928e30e 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -283,7 +283,7 @@
struct usb_device *udev = interface_to_usbdev(intf);
const struct usb_device_id *id;
int error = -ENODEV;
- int lpm_disable_error;
+ int lpm_disable_error = -ENODEV;
dev_dbg(dev, "%s\n", __func__);
@@ -331,12 +331,14 @@
* setting during probe, that should also be fine. usb_set_interface()
* will attempt to disable LPM, and fail if it can't disable it.
*/
- lpm_disable_error = usb_unlocked_disable_lpm(udev);
- if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
- dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
- __func__, driver->name);
- error = lpm_disable_error;
- goto err;
+ if (driver->disable_hub_initiated_lpm) {
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
+ if (lpm_disable_error) {
+ dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
+ __func__, driver->name);
+ error = lpm_disable_error;
+ goto err;
+ }
}
/* Carry out a deferred switch to altsetting 0 */
@@ -386,7 +388,8 @@
struct usb_interface *intf = to_usb_interface(dev);
struct usb_host_endpoint *ep, **eps = NULL;
struct usb_device *udev;
- int i, j, error, r, lpm_disable_error;
+ int i, j, error, r;
+ int lpm_disable_error = -ENODEV;
intf->condition = USB_INTERFACE_UNBINDING;
@@ -394,12 +397,13 @@
udev = interface_to_usbdev(intf);
error = usb_autoresume_device(udev);
- /* Hub-initiated LPM policy may change, so attempt to disable LPM until
+ /* If hub-initiated LPM policy may change, attempt to disable LPM until
* the driver is unbound. If LPM isn't disabled, that's fine because it
* wouldn't be enabled unless all the bound interfaces supported
* hub-initiated LPM.
*/
- lpm_disable_error = usb_unlocked_disable_lpm(udev);
+ if (driver->disable_hub_initiated_lpm)
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
/*
* Terminate all URBs for this interface unless the driver
@@ -502,7 +506,7 @@
struct device *dev = &iface->dev;
struct usb_device *udev;
int retval = 0;
- int lpm_disable_error;
+ int lpm_disable_error = -ENODEV;
if (dev->driver)
return -EBUSY;
@@ -515,12 +519,14 @@
iface->condition = USB_INTERFACE_BOUND;
- /* Disable LPM until this driver is bound. */
- lpm_disable_error = usb_unlocked_disable_lpm(udev);
- if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
- dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
- __func__, driver->name);
- return -ENOMEM;
+ /* See the comment about disabling LPM in usb_probe_interface(). */
+ if (driver->disable_hub_initiated_lpm) {
+ lpm_disable_error = usb_unlocked_disable_lpm(udev);
+ if (lpm_disable_error) {
+ dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
+ __func__, driver->name);
+ return -ENOMEM;
+ }
}
/* Claimed interfaces are initially inactive (suspended) and
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index efc9531..a4c0b85 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -74,6 +74,15 @@
if (companion->bus != pdev->bus ||
PCI_SLOT(companion->devfn) != slot)
continue;
+
+ /*
+ * Companion device should be either UHCI,OHCI or EHCI host
+ * controller, otherwise skip.
+ */
+ if (companion->class != CL_UHCI && companion->class != CL_OHCI &&
+ companion->class != CL_EHCI)
+ continue;
+
companion_hcd = pci_get_drvdata(companion);
if (!companion_hcd || !companion_hcd->self.root_hub)
continue;
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 3690303..408ef59 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -498,8 +498,10 @@
*/
tbuf_size = max_t(u16, sizeof(struct usb_hub_descriptor), wLength);
tbuf = kzalloc(tbuf_size, GFP_KERNEL);
- if (!tbuf)
- return -ENOMEM;
+ if (!tbuf) {
+ status = -ENOMEM;
+ goto err_alloc;
+ }
bufp = tbuf;
@@ -702,6 +704,7 @@
}
kfree(tbuf);
+ err_alloc:
/* any errors get returned through the urb completion */
spin_lock_irq(&hcd_root_hub_lock);
@@ -915,7 +918,7 @@
bus->bandwidth_allocated = 0;
bus->bandwidth_int_reqs = 0;
bus->bandwidth_isoc_reqs = 0;
- mutex_init(&bus->usb_address0_mutex);
+ mutex_init(&bus->devnum_next_mutex);
INIT_LIST_HEAD (&bus->bus_list);
}
@@ -2453,6 +2456,14 @@
return NULL;
}
if (primary_hcd == NULL) {
+ hcd->address0_mutex = kmalloc(sizeof(*hcd->address0_mutex),
+ GFP_KERNEL);
+ if (!hcd->address0_mutex) {
+ kfree(hcd);
+ dev_dbg(dev, "hcd address0 mutex alloc failed\n");
+ return NULL;
+ }
+ mutex_init(hcd->address0_mutex);
hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex),
GFP_KERNEL);
if (!hcd->bandwidth_mutex) {
@@ -2464,6 +2475,7 @@
dev_set_drvdata(dev, hcd);
} else {
mutex_lock(&usb_port_peer_mutex);
+ hcd->address0_mutex = primary_hcd->address0_mutex;
hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex;
hcd->primary_hcd = primary_hcd;
primary_hcd->primary_hcd = primary_hcd;
@@ -2520,24 +2532,23 @@
* Don't deallocate the bandwidth_mutex until the last shared usb_hcd is
* deallocated.
*
- * Make sure to only deallocate the bandwidth_mutex when the primary HCD is
- * freed. When hcd_release() is called for either hcd in a peer set
- * invalidate the peer's ->shared_hcd and ->primary_hcd pointers to
- * block new peering attempts
+ * Make sure to deallocate the bandwidth_mutex only when the last HCD is
+ * freed. When hcd_release() is called for either hcd in a peer set,
+ * invalidate the peer's ->shared_hcd and ->primary_hcd pointers.
*/
static void hcd_release(struct kref *kref)
{
struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
mutex_lock(&usb_port_peer_mutex);
- if (hcd->primary_hcd == hcd)
- kfree(hcd->bandwidth_mutex);
if (hcd->shared_hcd) {
struct usb_hcd *peer = hcd->shared_hcd;
peer->shared_hcd = NULL;
- if (peer->primary_hcd == hcd)
- peer->primary_hcd = NULL;
+ peer->primary_hcd = NULL;
+ } else {
+ kfree(hcd->address0_mutex);
+ kfree(hcd->bandwidth_mutex);
}
mutex_unlock(&usb_port_peer_mutex);
kfree(hcd);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index ab53405b..c3efa8e 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -101,6 +101,8 @@
static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev);
+static void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev);
static inline char *portspeed(struct usb_hub *hub, int portstatus)
{
@@ -878,82 +880,28 @@
}
/*
- * If USB 3.0 ports are placed into the Disabled state, they will no longer
- * detect any device connects or disconnects. This is generally not what the
- * USB core wants, since it expects a disabled port to produce a port status
- * change event when a new device connects.
- *
- * Instead, set the link state to Disabled, wait for the link to settle into
- * that state, clear any change bits, and then put the port into the RxDetect
- * state.
+ * USB-3 does not have a similar link state as USB-2 that will avoid negotiating
+ * a connection with a plugged-in cable but will signal the host when the cable
+ * is unplugged. Disable remote wake and set link state to U3 for USB-3 devices
*/
-static int hub_usb3_port_disable(struct usb_hub *hub, int port1)
-{
- int ret;
- int total_time;
- u16 portchange, portstatus;
-
- if (!hub_is_superspeed(hub->hdev))
- return -EINVAL;
-
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
- if (ret < 0)
- return ret;
-
- /*
- * USB controller Advanced Micro Devices, Inc. [AMD] FCH USB XHCI
- * Controller [1022:7814] will have spurious result making the following
- * usb 3.0 device hotplugging route to the 2.0 root hub and recognized
- * as high-speed device if we set the usb 3.0 port link state to
- * Disabled. Since it's already in USB_SS_PORT_LS_RX_DETECT state, we
- * check the state here to avoid the bug.
- */
- if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_RX_DETECT) {
- dev_dbg(&hub->ports[port1 - 1]->dev,
- "Not disabling port; link state is RxDetect\n");
- return ret;
- }
-
- ret = hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_SS_DISABLED);
- if (ret)
- return ret;
-
- /* Wait for the link to enter the disabled state. */
- for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) {
- ret = hub_port_status(hub, port1, &portstatus, &portchange);
- if (ret < 0)
- return ret;
-
- if ((portstatus & USB_PORT_STAT_LINK_STATE) ==
- USB_SS_PORT_LS_SS_DISABLED)
- break;
- if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- break;
- msleep(HUB_DEBOUNCE_STEP);
- }
- if (total_time >= HUB_DEBOUNCE_TIMEOUT)
- dev_warn(&hub->ports[port1 - 1]->dev,
- "Could not disable after %d ms\n", total_time);
-
- return hub_set_port_link_state(hub, port1, USB_SS_PORT_LS_RX_DETECT);
-}
-
static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
{
struct usb_port *port_dev = hub->ports[port1 - 1];
struct usb_device *hdev = hub->hdev;
int ret = 0;
- if (port_dev->child && set_state)
- usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
if (!hub->error) {
- if (hub_is_superspeed(hub->hdev))
- ret = hub_usb3_port_disable(hub, port1);
- else
+ if (hub_is_superspeed(hub->hdev)) {
+ hub_usb3_port_prepare_disable(hub, port_dev);
+ ret = hub_set_port_link_state(hub, port_dev->portnum,
+ USB_SS_PORT_LS_U3);
+ } else {
ret = usb_clear_port_feature(hdev, port1,
USB_PORT_FEAT_ENABLE);
+ }
}
+ if (port_dev->child && set_state)
+ usb_set_device_state(port_dev->child, USB_STATE_NOTATTACHED);
if (ret && ret != -ENODEV)
dev_err(&port_dev->dev, "cannot disable (err = %d)\n", ret);
return ret;
@@ -1294,8 +1242,6 @@
struct usb_device *hdev = hub->hdev;
int i;
- cancel_delayed_work_sync(&hub->init_work);
-
/* hub_wq and related activity won't re-trigger */
hub->quiescing = 1;
@@ -2062,7 +2008,7 @@
struct usb_bus *bus = udev->bus;
/* be safe when more hub events are proceed in parallel */
- mutex_lock(&bus->usb_address0_mutex);
+ mutex_lock(&bus->devnum_next_mutex);
if (udev->wusb) {
devnum = udev->portnum + 1;
BUG_ON(test_bit(devnum, bus->devmap.devicemap));
@@ -2080,7 +2026,7 @@
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
}
- mutex_unlock(&bus->usb_address0_mutex);
+ mutex_unlock(&bus->devnum_next_mutex);
}
static void release_devnum(struct usb_device *udev)
@@ -2666,8 +2612,15 @@
if (ret < 0)
return ret;
- /* The port state is unknown until the reset completes. */
- if (!(portstatus & USB_PORT_STAT_RESET))
+ /*
+ * The port state is unknown until the reset completes.
+ *
+ * On top of that, some chips may require additional time
+ * to re-establish a connection after the reset is complete,
+ * so also wait for the connection to be re-established.
+ */
+ if (!(portstatus & USB_PORT_STAT_RESET) &&
+ (portstatus & USB_PORT_STAT_CONNECTION))
break;
/* switch to the long delay after two short delay failures */
@@ -4039,6 +3992,26 @@
}
EXPORT_SYMBOL_GPL(usb_unlocked_enable_lpm);
+/* usb3 devices use U3 for disabled, make sure remote wakeup is disabled */
+static void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev)
+{
+ struct usb_device *udev = port_dev->child;
+ int ret;
+
+ if (udev && udev->port_is_suspended && udev->do_remote_wakeup) {
+ ret = hub_set_port_link_state(hub, port_dev->portnum,
+ USB_SS_PORT_LS_U0);
+ if (!ret) {
+ msleep(USB_RESUME_TIMEOUT);
+ ret = usb_disable_remote_wakeup(udev);
+ }
+ if (ret)
+ dev_warn(&udev->dev,
+ "Port disable: can't disable remote wake\n");
+ udev->do_remote_wakeup = 0;
+ }
+}
#else /* CONFIG_PM */
@@ -4046,6 +4019,9 @@
#define hub_resume NULL
#define hub_reset_resume NULL
+static inline void hub_usb3_port_prepare_disable(struct usb_hub *hub,
+ struct usb_port *port_dev) { }
+
int usb_disable_lpm(struct usb_device *udev)
{
return 0;
@@ -4195,7 +4171,7 @@
struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
int connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
- if (!udev->usb2_hw_lpm_capable)
+ if (!udev->usb2_hw_lpm_capable || !udev->bos)
return;
if (hub)
@@ -4258,7 +4234,7 @@
if (oldspeed == USB_SPEED_LOW)
delay = HUB_LONG_RESET_TIME;
- mutex_lock(&hdev->bus->usb_address0_mutex);
+ mutex_lock(hcd->address0_mutex);
/* Reset the device; full speed may morph to high speed */
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
@@ -4534,7 +4510,7 @@
hub_port_disable(hub, port1, 0);
update_devnum(udev, devnum); /* for disconnect processing */
}
- mutex_unlock(&hdev->bus->usb_address0_mutex);
+ mutex_unlock(hcd->address0_mutex);
return retval;
}
@@ -5362,6 +5338,9 @@
goto re_enumerate;
}
+ bos = udev->bos;
+ udev->bos = NULL;
+
for (i = 0; i < SET_CONFIG_TRIES; ++i) {
/* ep0 maxpacket size may change; let the HCD know about it.
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 41e510a..3fdb610 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -44,6 +44,9 @@
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* USB3503 */
+ { USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Microsoft Wireless Laser Mouse 6000 Receiver */
{ USB_DEVICE(0x045e, 0x00e1), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -109,6 +112,12 @@
{ USB_DEVICE(0x04f3, 0x016f), .driver_info =
USB_QUIRK_DEVICE_QUALIFIER },
+ { USB_DEVICE(0x04f3, 0x0381), .driver_info =
+ USB_QUIRK_NO_LPM },
+
+ { USB_DEVICE(0x04f3, 0x21b8), .driver_info =
+ USB_QUIRK_DEVICE_QUALIFIER },
+
/* Roland SC-8820 */
{ USB_DEVICE(0x0582, 0x0007), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -141,6 +150,14 @@
/* M-Systems Flash Disk Pioneers */
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* Baum Vario Ultra */
+ { USB_DEVICE(0x0904, 0x6101), .driver_info =
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+ { USB_DEVICE(0x0904, 0x6102), .driver_info =
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+ { USB_DEVICE(0x0904, 0x6103), .driver_info =
+ USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL },
+
/* Keytouch QWERTY Panel keyboard */
{ USB_DEVICE(0x0926, 0x3333), .driver_info =
USB_QUIRK_CONFIG_INTF_STRINGS },
@@ -154,6 +171,10 @@
/* MAYA44USB sound device */
{ USB_DEVICE(0x0a92, 0x0091), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* ASUS Base Station(T100) */
+ { USB_DEVICE(0x0b05, 0x17e0), .driver_info =
+ USB_QUIRK_IGNORE_REMOTE_WAKEUP },
+
/* Action Semiconductor flash disk */
{ USB_DEVICE(0x10d6, 0x2200), .driver_info =
USB_QUIRK_STRING_FETCH_255 },
@@ -169,20 +190,22 @@
{ USB_DEVICE(0x1908, 0x1315), .driver_info =
USB_QUIRK_HONOR_BNUMINTERFACES },
- /* INTEL VALUE SSD */
- { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* USB3503 */
- { USB_DEVICE(0x0424, 0x3503), .driver_info = USB_QUIRK_RESET_RESUME },
-
- /* ASUS Base Station(T100) */
- { USB_DEVICE(0x0b05, 0x17e0), .driver_info =
- USB_QUIRK_IGNORE_REMOTE_WAKEUP },
-
/* Protocol and OTG Electrical Test Device */
{ USB_DEVICE(0x1a0a, 0x0200), .driver_info =
USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
+ /* Acer C120 LED Projector */
+ { USB_DEVICE(0x1de1, 0xc102), .driver_info = USB_QUIRK_NO_LPM },
+
+ /* Blackmagic Design Intensity Shuttle */
+ { USB_DEVICE(0x1edb, 0xbd3b), .driver_info = USB_QUIRK_NO_LPM },
+
+ /* Blackmagic Design UltraStudio SDI */
+ { USB_DEVICE(0x1edb, 0xbd4f), .driver_info = USB_QUIRK_NO_LPM },
+
+ /* INTEL VALUE SSD */
+ { USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
+
{ } /* terminating entry must be last */
};
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index 3951a65..e184d175 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -106,7 +106,6 @@
static int dwc3_exynos_probe(struct platform_device *pdev)
{
struct dwc3_exynos *exynos;
- struct clk *clk;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
@@ -127,21 +126,13 @@
platform_set_drvdata(pdev, exynos);
- ret = dwc3_exynos_register_phys(exynos);
- if (ret) {
- dev_err(dev, "couldn't register PHYs\n");
- return ret;
- }
+ exynos->dev = dev;
- clk = devm_clk_get(dev, "usbdrd30");
- if (IS_ERR(clk)) {
+ exynos->clk = devm_clk_get(dev, "usbdrd30");
+ if (IS_ERR(exynos->clk)) {
dev_err(dev, "couldn't get clock\n");
return -EINVAL;
}
-
- exynos->dev = dev;
- exynos->clk = clk;
-
clk_prepare_enable(exynos->clk);
exynos->vdd33 = devm_regulator_get(dev, "vdd33");
@@ -166,26 +157,35 @@
goto err3;
}
+ ret = dwc3_exynos_register_phys(exynos);
+ if (ret) {
+ dev_err(dev, "couldn't register PHYs\n");
+ goto err4;
+ }
+
if (node) {
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
dev_err(dev, "failed to add dwc3 core\n");
- goto err4;
+ goto err5;
}
} else {
dev_err(dev, "no device node, failed to add dwc3 core\n");
ret = -ENODEV;
- goto err4;
+ goto err5;
}
return 0;
+err5:
+ platform_device_unregister(exynos->usb2_phy);
+ platform_device_unregister(exynos->usb3_phy);
err4:
regulator_disable(exynos->vdd10);
err3:
regulator_disable(exynos->vdd33);
err2:
- clk_disable_unprepare(clk);
+ clk_disable_unprepare(exynos->clk);
return ret;
}
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 84a5c9b..e16ed6d 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -3188,17 +3188,6 @@
}
}
- mdwc->uc.notify_attached_source = dwc3_msm_notify_attached_source;
- mdwc->uc.pd_vbus_ctrl = dwc3_pd_vbus_ctrl;
- mdwc->uc.vbus_boost_enabled = dwc3_vbus_boost_enabled;
-
- ret = usb_controller_register(&pdev->dev, &mdwc->uc);
- if (ret < 0) {
- dev_err(&pdev->dev,
- "%s:usb_controller_register usb failed\n",
- __func__);
- }
-
ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
if (ret) {
dev_err(&pdev->dev,
@@ -3247,6 +3236,18 @@
goto put_dwc3;
}
+ mdwc->uc.notify_attached_source = dwc3_msm_notify_attached_source;
+ mdwc->uc.pd_vbus_ctrl = dwc3_pd_vbus_ctrl;
+ mdwc->uc.vbus_boost_enabled = dwc3_vbus_boost_enabled;
+
+ ret = usb_controller_register(&pdev->dev, &mdwc->uc);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "%s:usb_controller_register usb failed\n",
+ __func__);
+ goto put_dwc3;
+ }
+
mdwc->irq_to_affin = platform_get_irq(mdwc->dwc3, 0);
mdwc->dwc3_cpu_notifier.notifier_call = dwc3_cpu_notifier_cb;
@@ -3826,7 +3827,8 @@
dev_dbg(mdwc->dev, "!id\n");
mdwc->otg_state = OTG_STATE_A_IDLE;
work = 1;
- mdwc->chg_type = DWC3_INVALID_CHARGER;
+ if (!test_bit(B_SESS_VLD, &mdwc->inputs))
+ mdwc->chg_type = DWC3_INVALID_CHARGER;
} else if (test_bit(B_SESS_VLD, &mdwc->inputs)) {
dev_dbg(mdwc->dev, "b_sess_vld\n");
pr_info("[USB] b_sess_vld, chg_type=%d, PolicyIsDFP=%d, dwc3_vbus_boost=%d\n",
@@ -3913,7 +3915,8 @@
pm_runtime_put_sync(mdwc->dev);
dbg_event(0xFF, "BPER psync",
atomic_read(&mdwc->dev->power.usage_count));
- mdwc->chg_type = DWC3_INVALID_CHARGER;
+ if (!test_bit(B_SESS_VLD, &mdwc->inputs))
+ mdwc->chg_type = DWC3_INVALID_CHARGER;
work = 1;
} else if (test_bit(B_SUSPEND, &mdwc->inputs) &&
test_bit(B_SESS_VLD, &mdwc->inputs)) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 53cfa995..5ff8217 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2555,9 +2555,10 @@
return 1;
}
- if ((event->status & DEPEVT_STATUS_IOC) &&
- (trb->ctrl & DWC3_TRB_CTRL_IOC))
- return 0;
+ if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ if ((event->status & DEPEVT_STATUS_IOC) &&
+ (trb->ctrl & DWC3_TRB_CTRL_IOC))
+ return 0;
return 1;
}
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 3abd637..f020114 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -28,23 +28,23 @@
#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget))
/* DEPCFG parameter 1 */
-#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0)
+#define DWC3_DEPCFG_INT_NUM(n) (((n) & 0x1f) << 0)
#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8)
#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9)
#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10)
#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11)
#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13)
-#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16)
+#define DWC3_DEPCFG_BINTERVAL_M1(n) (((n) & 0xff) << 16)
#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24)
-#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25)
+#define DWC3_DEPCFG_EP_NUMBER(n) (((n) & 0x1f) << 25)
#define DWC3_DEPCFG_BULK_BASED (1 << 30)
#define DWC3_DEPCFG_FIFO_BASED (1 << 31)
/* DEPCFG parameter 0 */
-#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1)
-#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3)
-#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17)
-#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22)
+#define DWC3_DEPCFG_EP_TYPE(n) (((n) & 0x3) << 1)
+#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) (((n) & 0x7ff) << 3)
+#define DWC3_DEPCFG_FIFO_NUMBER(n) (((n) & 0x1f) << 17)
+#define DWC3_DEPCFG_BURST_SIZE(n) (((n) & 0xf) << 22)
#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26)
/* This applies for core versions earlier than 1.94a */
#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31)
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 42b15ad..41fce66 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -270,7 +270,7 @@
static struct usb_device_descriptor device_desc = {
.bLength = sizeof(device_desc),
.bDescriptorType = USB_DT_DEVICE,
- .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bcdUSB = __constant_cpu_to_le16(0x0310),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = __constant_cpu_to_le16(VENDOR_ID),
.idProduct = __constant_cpu_to_le16(PRODUCT_ID),
@@ -582,42 +582,74 @@
struct usb_function *func;
struct usb_function_instance *fi;
struct ffs_data *data;
+ struct android_usb_function *android_func;
+ struct list_head list_item;
};
+#define MAX_FFS_FUNCTIONS 5
+static struct list_head ffs_configs;
+static struct mutex ffs_configs_lock;
+
static int functionfs_ready_callback(struct ffs_data *ffs);
static void functionfs_closed_callback(struct ffs_data *ffs);
static int ffs_function_init(struct android_usb_function *f,
struct usb_composite_dev *cdev)
{
+ int i;
struct functionfs_config *config;
+ struct functionfs_config *next;
struct f_fs_opts *opts;
+ struct android_usb_function *nf;
- f->config = kzalloc(sizeof(struct functionfs_config), GFP_KERNEL);
- if (!f->config)
- return -ENOMEM;
+ INIT_LIST_HEAD(&ffs_configs);
+ mutex_init(&ffs_configs_lock);
- config = f->config;
+ for (i = 0; i < MAX_FFS_FUNCTIONS; i++) {
+ nf = kmalloc(sizeof(struct android_usb_function), GFP_KERNEL);
+ config = kzalloc(sizeof(struct functionfs_config), GFP_KERNEL);
+ if (!nf || !config) {
+ kfree(nf);
+ kfree(config);
+ list_for_each_entry_safe(config, next,
+ &ffs_configs, list_item) {
+ list_del(&config->list_item);
+ usb_put_function_instance(config->fi);
+ kfree(config->android_func);
+ kfree(config);
+ }
+ return -ENOMEM;
+ }
- config->fi = usb_get_function_instance("ffs");
- if (IS_ERR(config->fi))
- return PTR_ERR(config->fi);
+ memcpy(nf, f, sizeof(struct android_usb_function));
+ nf->config = config;
+ config->android_func = nf;
- opts = to_f_fs_opts(config->fi);
- opts->dev->ffs_ready_callback = functionfs_ready_callback;
- opts->dev->ffs_closed_callback = functionfs_closed_callback;
- opts->no_configfs = true;
+ config->fi = usb_get_function_instance("ffs");
+ if (IS_ERR(config->fi))
+ return PTR_ERR(config->fi);
- return ffs_single_dev(opts->dev);
+ opts = to_f_fs_opts(config->fi);
+ opts->dev->ffs_ready_callback = functionfs_ready_callback;
+ opts->dev->ffs_closed_callback = functionfs_closed_callback;
+ opts->no_configfs = true;
+
+ list_add_tail(&config->list_item, &ffs_configs);
+ }
+ return 0;
}
static void ffs_function_cleanup(struct android_usb_function *f)
{
- struct functionfs_config *config = f->config;
- if (config)
- usb_put_function_instance(config->fi);
+ struct functionfs_config *config;
+ struct functionfs_config *next;
- kfree(f->config);
+ list_for_each_entry_safe(config, next, &ffs_configs, list_item) {
+ list_del(&config->list_item);
+ usb_put_function_instance(config->fi);
+ kfree(config->android_func);
+ kfree(config);
+ }
}
static void ffs_function_enable(struct android_usb_function *f)
@@ -688,9 +720,14 @@
{
struct android_dev *dev;
char buff[256];
+ char *aliases;
+ struct functionfs_config *config;
+ struct f_fs_opts *opts;
+ struct list_head *config_ptr;
dev = list_first_entry(&android_dev_list, struct android_dev,
list_item);
+ mutex_lock(&ffs_configs_lock);
mutex_lock(&dev->mutex);
if (dev->enabled) {
@@ -699,9 +736,36 @@
}
strlcpy(buff, buf, sizeof(buff));
- strlcpy(dev->ffs_aliases, strim(buff), sizeof(dev->ffs_aliases));
+ aliases = strim(buff);
+ strlcpy(dev->ffs_aliases, aliases, sizeof(dev->ffs_aliases));
+
+ /* Free old aliases */
+ list_for_each_entry(config, &ffs_configs, list_item) {
+ opts = to_f_fs_opts(config->fi);
+ kfree(opts->dev->name);
+ opts->dev->name = NULL;
+ }
+ config_ptr = ffs_configs.next;
+ while (aliases) {
+ char *alias = strsep(&aliases, ",");
+
+ if (!alias)
+ break;
+ if (config_ptr == &ffs_configs) {
+ pr_err("Too many ffs functions, max is %d\n",
+ MAX_FFS_FUNCTIONS);
+ return -EOVERFLOW;
+ }
+
+ config = list_entry(config_ptr,
+ struct functionfs_config, list_item);
+
+ config->fi->set_inst_name(config->fi, alias);
+ config_ptr = config_ptr->next;
+ }
mutex_unlock(&dev->mutex);
+ mutex_unlock(&ffs_configs_lock);
return size;
}
@@ -725,27 +789,61 @@
static int functionfs_ready_callback(struct ffs_data *ffs)
{
- struct android_dev *dev = ffs_function.android_dev;
- struct functionfs_config *config = ffs_function.config;
+ struct android_dev *dev;
+ struct functionfs_config *config = NULL;
+ struct functionfs_config *cur;
+ struct f_fs_opts *opts;
- if (!dev)
+ mutex_lock(&ffs_configs_lock);
+ list_for_each_entry(cur, &ffs_configs, list_item) {
+ opts = to_f_fs_opts(cur->fi);
+ if (opts->dev->ffs_data == ffs) {
+ config = cur;
+ break;
+ }
+ }
+ if (!config) {
+ pr_err("ffs function %s could not be found!\n",
+ ffs->dev_name);
+ mutex_unlock(&ffs_configs_lock);
return -ENODEV;
+ }
- mutex_lock(&dev->mutex);
+ dev = config->android_func->android_dev;
+
+ if (dev)
+ mutex_lock(&dev->mutex);
config->data = ffs;
config->opened = true;
if (config->enabled && dev)
android_enable(dev);
- mutex_unlock(&dev->mutex);
+ mutex_unlock(&ffs_configs_lock);
+ if (dev)
+ mutex_unlock(&dev->mutex);
return 0;
}
static void functionfs_closed_callback(struct ffs_data *ffs)
{
- struct android_dev *dev = ffs_function.android_dev;
- struct functionfs_config *config = ffs_function.config;
+ struct android_dev *dev;
+ struct functionfs_config *config = NULL;
+ struct functionfs_config *cur;
+
+ mutex_lock(&ffs_configs_lock);
+ list_for_each_entry(cur, &ffs_configs, list_item) {
+ if (cur->data == ffs) {
+ config = cur;
+ break;
+ }
+ }
+ if (!config) {
+ pr_err("ffs closed callback failed %s!\n", ffs->dev_name);
+ mutex_unlock(&ffs_configs_lock);
+ return;
+ }
+ dev = config->android_func->android_dev;
if (dev)
mutex_lock(&dev->mutex);
@@ -761,6 +859,7 @@
config->func = NULL;
}
+ mutex_unlock(&ffs_configs_lock);
if (dev)
mutex_unlock(&dev->mutex);
@@ -2700,7 +2799,6 @@
{
struct mass_storage_function_config *config = f->config;
int ret = 0;
- int i;
struct fsg_opts *fsg_opts;
config->f_ms = usb_get_function(config->f_ms_inst);
@@ -2711,7 +2809,7 @@
ret = usb_add_function(c, config->f_ms);
if (ret) {
- pr_err("Could not bind ms%u config\n", i);
+ pr_err("Could not bind ms:%s config\n", config->f_ms->name);
goto err_usb_add_function;
}
@@ -3324,6 +3422,49 @@
return -EINVAL;
}
+static int android_enable_ffs_function(struct android_dev *dev,
+ struct android_configuration *conf,
+ char *alias)
+{
+ struct functionfs_config *config;
+ struct f_fs_opts *opts;
+ struct android_usb_function *match = NULL;
+ struct android_usb_function_holder *f_holder;
+
+ list_for_each_entry(config, &ffs_configs, list_item) {
+ opts = to_f_fs_opts(config->fi);
+ if (opts->dev->name && !strcmp(opts->dev->name, alias)) {
+ match = config->android_func;
+ break;
+ }
+ }
+ if (!match) {
+ pr_err("ffs function %s was never aliased\n", alias);
+ return -ENODEV;
+ }
+ /* Function has already been enabled. */
+ if (match->android_dev) {
+ pr_err("ffs function %s already enabled\n", alias);
+ return -EBUSY;
+ }
+ if (!opts->dev->ffs_data) {
+ pr_err("ffs function %s was never mounted\n", alias);
+ return -ENODEV;
+ }
+
+
+ f_holder = kzalloc(sizeof(*f_holder), GFP_KERNEL);
+ if (!f_holder) {
+ return -ENOMEM;
+ }
+
+ match->android_dev = dev;
+ f_holder->f = match;
+ list_add_tail(&f_holder->enabled_list, &conf->enabled_functions);
+ pr_debug("ffs func:%s is enabled.\n", alias);
+ return 0;
+}
+
#include "htc_attr.c"
/*-------------------------------------------------------------------------*/
/* /sys/class/android_usb/android%d/ interface */
@@ -3415,10 +3556,12 @@
int is_ffs;
int ffs_enabled = 0;
+ mutex_lock(&ffs_configs_lock);
mutex_lock(&dev->mutex);
if (dev->enabled) {
mutex_unlock(&dev->mutex);
+ mutex_unlock(&ffs_configs_lock);
return -EBUSY;
}
@@ -3472,9 +3615,8 @@
}
if (is_ffs) {
- if (ffs_enabled)
- continue;
- err = android_enable_function(dev, conf, "ffs");
+ err = android_enable_ffs_function(dev,
+ conf, name);
if (err)
pr_err("android_usb: Cannot enable ffs (%d)",
err);
@@ -3502,6 +3644,7 @@
}
mutex_unlock(&dev->mutex);
+ mutex_unlock(&ffs_configs_lock);
return size;
}
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 6e22c1e..2eaab6d2 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -150,7 +150,7 @@
ep_found:
/* commit results */
- _ep->maxpacket = usb_endpoint_maxp(chosen_desc);
+ _ep->maxpacket = usb_endpoint_maxp(chosen_desc) & 0x7ff;
_ep->desc = chosen_desc;
_ep->comp_desc = NULL;
_ep->maxburst = 0;
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index ba83034..ff82e13 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -646,13 +646,15 @@
{
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
int status;
+ __le16 serial_state;
spin_lock(&acm->lock);
if (acm->notify_req) {
dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n",
acm->port_num, acm->serial_state);
+ serial_state = cpu_to_le16(acm->serial_state);
status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
- 0, &acm->serial_state, sizeof(acm->serial_state));
+ 0, &serial_state, sizeof(acm->serial_state));
} else {
acm->pending = true;
status = 0;
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 599a427..ce47b4e 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -38,6 +38,7 @@
#include "configfs.h"
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
+#define ENDPOINT_ALLOC_MAX 1 << 25 /* Max endpoint buffer size, 32 MB */
/* Reference counter handling */
static void ffs_data_get(struct ffs_data *ffs);
@@ -137,6 +138,9 @@
unsigned char _pad;
atomic_t opened;
+
+ unsigned long buf_len;
+ char *buffer;
};
/* ffs_io_data structure ***************************************************/
@@ -614,6 +618,8 @@
}
case FFS_CLOSING:
break;
+ case FFS_DEACTIVATED:
+ break;
}
mutex_unlock(&ffs->mutex);
@@ -791,10 +797,11 @@
spin_unlock_irq(&epfile->ffs->eps_lock);
if (!io_data->read)
- data = kmalloc(data_len + extra_buf_alloc,
- GFP_KERNEL);
- else
- data = kmalloc(data_len, GFP_KERNEL);
+ data_len += extra_buf_alloc;
+
+ data = (data_len > epfile->buf_len || io_data->aio) ?
+ kmalloc(data_len, GFP_KERNEL) :
+ epfile->buffer;
if (unlikely(!data))
return -ENOMEM;
if (io_data->aio && !io_data->read) {
@@ -881,25 +888,16 @@
spin_unlock_irq(&epfile->ffs->eps_lock);
} else {
- struct completion *done;
+ DECLARE_COMPLETION_ONSTACK(done);
req = ep->req;
req->buf = data;
req->length = data_len;
+
+ req->context = &done;
req->complete = ffs_epfile_io_complete;
ret = 0;
- if (io_data->read) {
- reinit_completion(
- &epfile->ffs->epout_completion);
- done = &epfile->ffs->epout_completion;
- req->context = done;
- } else {
- reinit_completion(
- &epfile->ffs->epin_completion);
- done = &epfile->ffs->epin_completion;
- req->context = done;
- }
/* Don't queue another read if previous is still busy */
if (!(io_data->read && ep->is_busy)) {
@@ -912,7 +910,7 @@
if (unlikely(ret < 0)) {
ret = -EIO;
} else if (unlikely(
- wait_for_completion_interruptible(done))) {
+ wait_for_completion_interruptible(&done))) {
spin_lock_irq(&epfile->ffs->eps_lock);
/*
* While we were acquiring lock endpoint got
@@ -946,10 +944,10 @@
if (io_data->read && ret > 0) {
if (io_data->len != MAX_BUF_LEN &&
ret < io_data->len)
- pr_err("less data(%zd) recieved than intended length(%zu)\n",
+ pr_debug("less data(%zd) received than intended length(%zu)\n",
ret, io_data->len);
else if (ret > io_data->len)
- pr_err("More data(%zd) recieved than intended length(%zu)\n",
+ pr_err("More data(%zd) received than intended length(%zu)\n",
ret, io_data->len);
ret = min_t(size_t, ret, io_data->len);
@@ -962,7 +960,8 @@
}
}
}
- kfree(data);
+ if (data_len > epfile->buf_len || io_data->aio)
+ kfree(data);
}
}
@@ -973,7 +972,8 @@
spin_unlock_irq(&epfile->ffs->eps_lock);
mutex_unlock(&epfile->mutex);
error:
- kfree(data);
+ if (data_len > epfile->buf_len || io_data->aio)
+ kfree(data);
if (ret < 0)
pr_err_ratelimited("Error: returning %zd value\n", ret);
return ret;
@@ -1126,6 +1126,9 @@
atomic_set(&epfile->opened, 0);
atomic_set(&epfile->error, 1);
+ epfile->buf_len = 0;
+ kfree(epfile->buffer);
+ epfile->buffer = NULL;
ffs_data_closed(epfile->ffs);
file->private_data = NULL;
@@ -1136,7 +1139,7 @@
unsigned long value)
{
struct ffs_epfile *epfile = file->private_data;
- int ret;
+ int ret = 0;
ENTER();
@@ -1144,7 +1147,7 @@
return -ENODEV;
spin_lock_irq(&epfile->ffs->eps_lock);
- if (likely(epfile->ep)) {
+ if (epfile->ep) {
switch (code) {
case FUNCTIONFS_FIFO_STATUS:
ret = usb_ep_fifo_status(epfile->ep->ep);
@@ -1182,6 +1185,29 @@
ret = -EFAULT;
return ret;
}
+ case FUNCTIONFS_ENDPOINT_ALLOC:
+ {
+ void *temp = epfile->buffer;
+ epfile->buffer = NULL;
+ epfile->buf_len = 0;
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+
+ kfree(temp);
+ if (!value)
+ return 0;
+ if (value > ENDPOINT_ALLOC_MAX)
+ return -EINVAL;
+
+ temp = kzalloc(value, GFP_KERNEL);
+ if (!temp)
+ return -ENOMEM;
+
+ spin_lock_irq(&epfile->ffs->eps_lock);
+ epfile->buffer = temp;
+ epfile->buf_len = value;
+ ret = 0;
+ break;
+ }
default:
ret = -ENOTTY;
}
@@ -1280,6 +1306,7 @@
struct ffs_file_perms perms;
umode_t root_mode;
const char *dev_name;
+ bool no_disconnect;
struct ffs_data *ffs_data;
};
@@ -1350,6 +1377,12 @@
/* Interpret option */
switch (eq - opts) {
+ case 13:
+ if (!memcmp(opts, "no_disconnect", 13))
+ data->no_disconnect = !!value;
+ else
+ goto invalid;
+ break;
case 5:
if (!memcmp(opts, "rmode", 5))
data->root_mode = (value & 0555) | S_IFDIR;
@@ -1414,10 +1447,11 @@
.gid = GLOBAL_ROOT_GID,
},
.root_mode = S_IFDIR | 0500,
+ .no_disconnect = false,
};
struct dentry *rv;
int ret;
- void *ffs_dev;
+ struct ffs_dev *ffs_dev;
struct ffs_data *ffs;
ENTER();
@@ -1430,6 +1464,7 @@
if (unlikely(!ffs))
return ERR_PTR(-ENOMEM);
ffs->file_perms = data.perms;
+ ffs->no_disconnect = data.no_disconnect;
ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
if (unlikely(!ffs->dev_name)) {
@@ -1443,6 +1478,7 @@
return ERR_CAST(ffs_dev);
}
ffs->private_data = ffs_dev;
+ ffs_dev->ffs_data = ffs;
data.ffs_data = ffs;
rv = mount_nodev(t, flags, &data, ffs_sb_fill);
@@ -1461,6 +1497,7 @@
kill_litter_super(sb);
if (sb->s_fs_info) {
ffs_release_dev(sb->s_fs_info);
+ ffs_data_closed(sb->s_fs_info);
ffs_data_put(sb->s_fs_info);
}
}
@@ -1519,7 +1556,11 @@
smp_mb__before_atomic();
atomic_inc(&ffs->ref);
- atomic_inc(&ffs->opened);
+ if (atomic_add_return(1, &ffs->opened) == 1 &&
+ ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
}
static void ffs_data_put(struct ffs_data *ffs)
@@ -1543,6 +1584,21 @@
smp_mb__before_atomic();
if (atomic_dec_and_test(&ffs->opened)) {
+ if (ffs->no_disconnect) {
+ ffs->state = FFS_DEACTIVATED;
+ if (ffs->epfiles) {
+ ffs_epfiles_destroy(ffs->epfiles,
+ ffs->eps_count);
+ ffs->epfiles = NULL;
+ }
+ if (ffs->setup_state == FFS_SETUP_PENDING)
+ __ffs_ep0_stall(ffs);
+ } else {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
+ }
+ if (atomic_read(&ffs->opened) < 0) {
ffs->state = FFS_CLOSING;
ffs_data_reset(ffs);
}
@@ -1565,8 +1621,6 @@
spin_lock_init(&ffs->eps_lock);
init_waitqueue_head(&ffs->ev.waitq);
init_completion(&ffs->ep0req_completion);
- init_completion(&ffs->epout_completion);
- init_completion(&ffs->epin_completion);
/* XXX REVISIT need to update it in some places, or do we? */
ffs->ev.can_stall = 1;
@@ -1737,7 +1791,6 @@
kfree(epfiles);
}
-
static void ffs_func_eps_disable(struct ffs_function *func)
{
struct ffs_ep *ep = func->eps;
@@ -1747,16 +1800,20 @@
spin_lock_irqsave(&func->ffs->eps_lock, flags);
do {
- atomic_set(&epfile->error, 1);
+ if (epfile)
+ atomic_set(&epfile->error, 1);
+
/* pending requests get nuked */
if (likely(ep->ep)) {
usb_ep_disable(ep->ep);
ep->ep->driver_data = NULL;
}
- epfile->ep = NULL;
-
++ep;
- ++epfile;
+
+ if (epfile) {
+ epfile->ep = NULL;
+ ++epfile;
+ }
} while (--count);
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
}
@@ -3024,6 +3081,13 @@
/* Other USB function hooks *************************************************/
+static void ffs_reset_work(struct work_struct *work)
+{
+ struct ffs_data *ffs = container_of(work,
+ struct ffs_data, reset_work);
+ ffs_data_reset(ffs);
+}
+
static int ffs_func_set_alt(struct usb_function *f,
unsigned interface, unsigned alt)
{
@@ -3042,6 +3106,13 @@
ffs->func = NULL;
}
+ if (ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ INIT_WORK(&ffs->reset_work, ffs_reset_work);
+ schedule_work(&ffs->reset_work);
+ return -ENODEV;
+ }
+
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 587114c..8a18ee6 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -362,7 +362,9 @@
/* allocate a bunch of read buffers and queue them all at once. */
for (i = 0; i < midi->qlen && err == 0; i++) {
struct usb_request *req =
- midi_alloc_ep_req(midi->out_ep, midi->buflen);
+ midi_alloc_ep_req(midi->out_ep,
+ max_t(unsigned, midi->buflen,
+ bulk_out_desc.wMaxPacketSize));
if (req == NULL)
return -ENOMEM;
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 945b3bd..f4a0b25 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -643,7 +643,7 @@
uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
uvc_ss_streaming_comp.wBytesPerInterval =
cpu_to_le16(max_packet_size * max_packet_mult *
- opts->streaming_maxburst);
+ (opts->streaming_maxburst + 1));
/* Allocate endpoints. */
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index 030c194..ecac410 100644
--- a/drivers/usb/gadget/function/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -19,6 +19,7 @@
#include <linux/usb/composite.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#ifdef VERBOSE_DEBUG
#ifndef pr_vdebug
@@ -93,6 +94,26 @@
FFS_ACTIVE,
/*
+ * Function is visible to host, but it's not functional. All
+ * setup requests are stalled and transfers on another endpoints
+ * are refused. All epfiles, except ep0, are deleted so there
+ * is no way to perform any operations on them.
+ *
+ * This state is set after closing all functionfs files, when
+ * mount parameter "no_disconnect=1" has been set. Function will
+ * remain in deactivated state until filesystem is umounted or
+ * ep0 is opened again. In the second case functionfs state will
+ * be reset, and it will be ready for descriptors and strings
+ * writing.
+ *
+ * This is useful only when functionfs is composed to gadget
+ * with another function which can perform some critical
+ * operations, and it's strongly desired to have this operations
+ * completed, even after functionfs files closure.
+ */
+ FFS_DEACTIVATED,
+
+ /*
* All endpoints have been closed. This state is also set if
* we encounter an unrecoverable error. The only
* unrecoverable error is situation when after reading strings
@@ -154,8 +175,6 @@
*/
struct usb_request *ep0req; /* P: mutex */
struct completion ep0req_completion; /* P: mutex */
- struct completion epin_completion;
- struct completion epout_completion;
/* reference counter */
atomic_t ref;
@@ -256,6 +275,9 @@
kgid_t gid;
} file_perms;
+ bool no_disconnect;
+ struct work_struct reset_work;
+
/*
* The endpoint files, filled by ffs_epfiles_create(),
* destroyed by ffs_epfiles_destroy().
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index db2becd..fe45311 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -654,6 +654,7 @@
GFP_KERNEL);
if (!priv->iv) {
kfree(priv);
+ value = -ENOMEM;
goto fail;
}
}
@@ -1018,8 +1019,11 @@
struct usb_ep *ep = dev->gadget->ep0;
struct usb_request *req = dev->req;
- if ((retval = setup_req (ep, req, 0)) == 0)
- retval = usb_ep_queue (ep, req, GFP_ATOMIC);
+ if ((retval = setup_req (ep, req, 0)) == 0) {
+ spin_unlock_irq (&dev->lock);
+ retval = usb_ep_queue (ep, req, GFP_KERNEL);
+ spin_lock_irq (&dev->lock);
+ }
dev->state = STATE_DEV_CONNECTED;
/* assume that was SET_CONFIGURATION */
@@ -1550,8 +1554,11 @@
w_length);
if (value < 0)
break;
+
+ spin_unlock (&dev->lock);
value = usb_ep_queue (gadget->ep0, dev->req,
- GFP_ATOMIC);
+ GFP_KERNEL);
+ spin_lock (&dev->lock);
if (value < 0) {
clean_req (gadget->ep0, dev->req);
break;
@@ -1574,11 +1581,14 @@
if (value >= 0 && dev->state != STATE_DEV_SETUP) {
req->length = value;
req->zero = value < w_length;
- value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+
+ spin_unlock (&dev->lock);
+ value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
if (value < 0) {
DBG (dev, "ep_queue --> %d\n", value);
req->status = 0;
}
+ return value;
}
/* device stalls when value < 0 */
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 81dc595..53c747f 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -972,6 +972,8 @@
int rc;
dum = *((void **)dev_get_platdata(&pdev->dev));
+ /* Clear usb_gadget region for new registration to udc-core */
+ memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));
dum->gadget.name = gadget_name;
dum->gadget.ops = &dummy_ops;
dum->gadget.max_speed = USB_SPEED_SUPER;
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index ed27e16..da28978 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -2132,8 +2132,8 @@
platform_set_drvdata(pdev, udc);
- dev_vdbg(&pdev->dev, "%s at 0x%08X mapped to 0x%08X %s\n",
- driver_name, (u32)res->start, (u32 __force)udc->addr,
+ dev_vdbg(&pdev->dev, "%s at 0x%08X mapped to %p %s\n",
+ driver_name, (u32)res->start, udc->addr,
udc->dma_enabled ? "with DMA" : "without DMA");
return 0;
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index aaa0197..aad2535 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -89,7 +89,7 @@
if (!usb1_reset_attempted) {
struct reset_control *usb1_reset;
- usb1_reset = of_reset_control_get(phy_np, "usb");
+ usb1_reset = of_reset_control_get(phy_np, "utmi-pads");
if (IS_ERR(usb1_reset)) {
dev_warn(&pdev->dev,
"can't get utmi-pads reset from the PHY\n");
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index d664eda..af2412f 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -72,7 +72,7 @@
static const char hcd_name [] = "ohci_hcd";
#define STATECHANGE_DELAY msecs_to_jiffies(300)
-#define IO_WATCHDOG_DELAY msecs_to_jiffies(250)
+#define IO_WATCHDOG_DELAY msecs_to_jiffies(275)
#include "ohci.h"
#include "pci-quirks.h"
diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c
index 940304c..02260cf 100644
--- a/drivers/usb/host/uhci-pci.c
+++ b/drivers/usb/host/uhci-pci.c
@@ -129,6 +129,10 @@
if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP)
uhci->wait_for_hp = 1;
+ /* Intel controllers use non-PME wakeup signalling */
+ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL)
+ device_set_run_wake(uhci_dev(uhci), 1);
+
/* Set up pointers to PCI-specific functions */
uhci->reset_hc = uhci_pci_reset_hc;
uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc;
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index f88c049..6aa7112 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -276,6 +276,9 @@
ret = 0;
virt_dev = xhci->devs[slot_id];
+ if (!virt_dev)
+ return -ENODEV;
+
cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
if (!cmd) {
xhci_dbg(xhci, "Couldn't allocate command structure.\n");
@@ -624,8 +627,30 @@
if ((raw_port_status & PORT_RESET) ||
!(raw_port_status & PORT_PE))
return 0xffffffff;
- if (time_after_eq(jiffies,
- bus_state->resume_done[wIndex])) {
+ /* did port event handler already start resume timing? */
+ if (!bus_state->resume_done[wIndex]) {
+ /* If not, maybe we are in a host initated resume? */
+ if (test_bit(wIndex, &bus_state->resuming_ports)) {
+ /* Host initated resume doesn't time the resume
+ * signalling using resume_done[].
+ * It manually sets RESUME state, sleeps 20ms
+ * and sets U0 state. This should probably be
+ * changed, but not right now.
+ */
+ } else {
+ /* port resume was discovered now and here,
+ * start resume timing
+ */
+ unsigned long timeout = jiffies +
+ msecs_to_jiffies(USB_RESUME_TIMEOUT);
+
+ set_bit(wIndex, &bus_state->resuming_ports);
+ bus_state->resume_done[wIndex] = timeout;
+ mod_timer(&hcd->rh_timer, timeout);
+ }
+ /* Has resume been signalled for USB_RESUME_TIME yet? */
+ } else if (time_after_eq(jiffies,
+ bus_state->resume_done[wIndex])) {
int time_left;
xhci_dbg(xhci, "Resume USB2 port %d\n",
@@ -666,13 +691,24 @@
} else {
/*
* The resume has been signaling for less than
- * 20ms. Report the port status as SUSPEND,
- * let the usbcore check port status again
- * and clear resume signaling later.
+ * USB_RESUME_TIME. Report the port status as SUSPEND,
+ * let the usbcore check port status again and clear
+ * resume signaling later.
*/
status |= USB_PORT_STAT_SUSPEND;
}
}
+ /*
+ * Clear stale usb2 resume signalling variables in case port changed
+ * state during resume signalling. For example on error
+ */
+ if ((bus_state->resume_done[wIndex] ||
+ test_bit(wIndex, &bus_state->resuming_ports)) &&
+ (raw_port_status & PORT_PLS_MASK) != XDEV_U3 &&
+ (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) {
+ bus_state->resume_done[wIndex] = 0;
+ clear_bit(wIndex, &bus_state->resuming_ports);
+ }
if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0
&& (raw_port_status & PORT_POWER)
&& (bus_state->suspended_ports & (1 << wIndex))) {
@@ -1173,6 +1209,7 @@
if ((temp & PORT_PE) == 0)
goto error;
+ set_bit(wIndex, &bus_state->resuming_ports);
xhci_set_link_state(xhci, port_array, wIndex,
XDEV_RESUME);
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -1180,6 +1217,7 @@
spin_lock_irqsave(&xhci->lock, flags);
xhci_set_link_state(xhci, port_array, wIndex,
XDEV_U0);
+ clear_bit(wIndex, &bus_state->resuming_ports);
}
bus_state->port_c_suspend |= 1 << wIndex;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index bf6693d..b357642 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1885,6 +1885,12 @@
kfree(xhci->rh_bw);
kfree(xhci->ext_caps);
+ xhci->usb2_ports = NULL;
+ xhci->usb3_ports = NULL;
+ xhci->port_array = NULL;
+ xhci->rh_bw = NULL;
+ xhci->ext_caps = NULL;
+
xhci->page_size = 0;
xhci->page_shift = 0;
xhci->bus_state[0].bus_suspended = 0;
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 2af32e2..4cd0a8d 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -30,6 +30,7 @@
/* Device for a quirk */
#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73
#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
+#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1009 0x1009
#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400
#define PCI_VENDOR_ID_ETRON 0x1b6f
@@ -37,9 +38,12 @@
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
+#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI 0x9cb1
#define PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI 0x22b5
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI 0xa12f
#define PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI 0x9d2f
+#define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8
+#define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8
static const char hcd_name[] = "xhci_hcd";
@@ -99,6 +103,10 @@
xhci->quirks |= XHCI_TRUST_TX_LENGTH;
}
+ if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
+ pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1009)
+ xhci->quirks |= XHCI_BROKEN_STREAMS;
+
if (pdev->vendor == PCI_VENDOR_ID_NEC)
xhci->quirks |= XHCI_NEC_HOST;
@@ -133,13 +141,16 @@
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
- pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI) {
+ (pdev->device == PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_WILDCATPOINT_LP_XHCI)) {
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
}
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
(pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_LP_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_SUNRISEPOINT_H_XHCI ||
- pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI)) {
+ pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI)) {
xhci->quirks |= XHCI_PME_STUCK_QUIRK;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
@@ -281,6 +292,7 @@
struct xhci_hcd *xhci;
xhci = hcd_to_xhci(pci_get_drvdata(dev));
+ xhci->xhc_state |= XHCI_STATE_REMOVING;
if (xhci->shared_hcd) {
usb_remove_hcd(xhci->shared_hcd);
usb_put_hcd(xhci->shared_hcd);
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index d530a99..a953201 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -175,6 +175,9 @@
ret = clk_prepare_enable(clk);
if (ret)
goto put_hcd;
+ } else if (PTR_ERR(clk) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto put_hcd;
}
if (pdev->dev.parent)
@@ -263,6 +266,8 @@
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct clk *clk = xhci->clk;
+ xhci->xhc_state |= XHCI_STATE_REMOVING;
+
pm_runtime_disable(&dev->dev);
device_remove_file(&dev->dev, &dev_attr_config_imod);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 4174994..06d6975 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -289,6 +289,14 @@
temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
+
+ /*
+ * Writing the CMD_RING_ABORT bit should cause a cmd completion event,
+ * however on some host hw the CMD_RING_RUNNING bit is correctly cleared
+ * but the completion event in never sent. Use the cmd timeout timer to
+ * handle those cases. Use twice the time to cover the bit polling retry
+ */
+ mod_timer(&xhci->cmd_timer, jiffies + (2 * XHCI_CMD_DEFAULT_TIMEOUT));
xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
&xhci->op_regs->cmd_ring);
@@ -304,6 +312,7 @@
if (ret < 0) {
xhci_err(xhci, "Stopped the command ring failed, "
"maybe the host is dead\n");
+ del_timer(&xhci->cmd_timer);
xhci->xhc_state |= XHCI_STATE_DYING;
xhci_quiesce(xhci);
xhci_halt(xhci);
@@ -830,6 +839,10 @@
spin_lock_irqsave(&xhci->lock, flags);
ep->stop_cmds_pending--;
+ if (xhci->xhc_state & XHCI_STATE_REMOVING) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return;
+ }
if (xhci->xhc_state & XHCI_STATE_DYING) {
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Stop EP timer ran, but another timer marked "
@@ -883,7 +896,7 @@
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"Calling usb_hc_died()");
- usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
+ usb_hc_died(xhci_to_hcd(xhci));
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
"xHCI host controller is dead.");
}
@@ -1245,22 +1258,21 @@
int ret;
unsigned long flags;
u64 hw_ring_state;
- struct xhci_command *cur_cmd = NULL;
+ bool second_timeout = false;
xhci = (struct xhci_hcd *) data;
/* mark this command to be cancelled */
spin_lock_irqsave(&xhci->lock, flags);
if (xhci->current_cmd) {
- cur_cmd = xhci->current_cmd;
- cur_cmd->status = COMP_CMD_ABORT;
+ if (xhci->current_cmd->status == COMP_CMD_ABORT)
+ second_timeout = true;
+ xhci->current_cmd->status = COMP_CMD_ABORT;
}
-
/* Make sure command ring is running before aborting it */
hw_ring_state = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
if ((xhci->cmd_ring_state & CMD_RING_STATE_RUNNING) &&
(hw_ring_state & CMD_RING_RUNNING)) {
-
spin_unlock_irqrestore(&xhci->lock, flags);
xhci_dbg(xhci, "Command timeout\n");
ret = xhci_abort_cmd_ring(xhci);
@@ -1272,6 +1284,15 @@
}
return;
}
+
+ /* command ring failed to restart, or host removed. Bail out */
+ if (second_timeout || xhci->xhc_state & XHCI_STATE_REMOVING) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg(xhci, "command timed out twice, ring start fail?\n");
+ xhci_cleanup_command_queue(xhci);
+ return;
+ }
+
/* command timeout on stopped ring, ring can't be aborted */
xhci_dbg(xhci, "Command timeout on stopped ring\n");
xhci_handle_stopped_cmd_ring(xhci, xhci->current_cmd);
@@ -1307,12 +1328,6 @@
cmd = list_entry(xhci->cmd_list.next, struct xhci_command, cmd_list);
- if (cmd->command_trb != xhci->cmd_ring->dequeue) {
- xhci_err(xhci,
- "Command completion event does not match command\n");
- return;
- }
-
del_timer(&xhci->cmd_timer);
trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
@@ -1324,6 +1339,13 @@
xhci_handle_stopped_cmd_ring(xhci, cmd);
return;
}
+
+ if (cmd->command_trb != xhci->cmd_ring->dequeue) {
+ xhci_err(xhci,
+ "Command completion event does not match command\n");
+ return;
+ }
+
/*
* Host aborted the command ring, check if the current command was
* supposed to be aborted, otherwise continue normally.
@@ -1573,7 +1595,8 @@
*/
bogus_port_status = true;
goto cleanup;
- } else {
+ } else if (!test_bit(faked_port_index,
+ &bus_state->resuming_ports)) {
xhci_dbg(xhci, "resume HS port %d\n", port_id);
bus_state->resume_done[faked_port_index] = jiffies +
msecs_to_jiffies(USB_RESUME_TIMEOUT);
@@ -3936,7 +3959,9 @@
{
int reserved_trbs = xhci->cmd_ring_reserved_trbs;
int ret;
- if (xhci->xhc_state) {
+
+ if ((xhci->xhc_state & XHCI_STATE_DYING) ||
+ (xhci->xhc_state & XHCI_STATE_HALTED)) {
xhci_dbg(xhci, "xHCI dying or halted, can't queue_command\n");
return -ESHUTDOWN;
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index bbb503d1..ec6227e 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -161,7 +161,9 @@
"waited %u microseconds.\n",
XHCI_MAX_HALT_USEC);
if (!ret)
- xhci->xhc_state &= ~XHCI_STATE_HALTED;
+ /* clear state flags. Including dying, halted or removing */
+ xhci->xhc_state = 0;
+
return ret;
}
@@ -1130,8 +1132,8 @@
/* Resume root hubs only when have pending events. */
status = readl(&xhci->op_regs->status);
if (status & STS_EINT) {
- usb_hcd_resume_root_hub(hcd);
usb_hcd_resume_root_hub(xhci->shared_hcd);
+ usb_hcd_resume_root_hub(hcd);
}
}
@@ -1146,10 +1148,10 @@
/* Re-enable port polling. */
xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
- set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
- usb_hcd_poll_rh_status(hcd);
set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
usb_hcd_poll_rh_status(xhci->shared_hcd);
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ usb_hcd_poll_rh_status(hcd);
return retval;
}
@@ -2774,7 +2776,8 @@
if (ret <= 0)
return ret;
xhci = hcd_to_xhci(hcd);
- if (xhci->xhc_state & XHCI_STATE_DYING)
+ if ((xhci->xhc_state & XHCI_STATE_DYING) ||
+ (xhci->xhc_state & XHCI_STATE_REMOVING))
return -ENODEV;
xhci_dbg(xhci, "%s called for udev %p\n", __func__, udev);
@@ -3820,7 +3823,7 @@
mutex_lock(&xhci->mutex);
- if (xhci->xhc_state) /* dying or halted */
+ if (xhci->xhc_state) /* dying, removing or halted */
goto out;
if (!udev->slot_id) {
@@ -4943,6 +4946,16 @@
goto error;
xhci_dbg(xhci, "Reset complete\n");
+ /*
+ * On some xHCI controllers (e.g. R-Car SoCs), the AC64 bit (bit 0)
+ * of HCCPARAMS1 is set to 1. However, the xHCs don't support 64-bit
+ * address memory pointers actually. So, this driver clears the AC64
+ * bit of xhci->hcc_params to call dma_set_coherent_mask(dev,
+ * DMA_BIT_MASK(32)) in this xhci_gen_setup().
+ */
+ if (xhci->quirks & XHCI_NO_64BIT_SUPPORT)
+ xhci->hcc_params &= ~BIT(0);
+
/* Set dma_mask and coherent_dma_mask to 64-bits,
* if xHC supports 64-bit addressing */
if (HCC_64BIT_ADDR(xhci->hcc_params) &&
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 4457cbb..926038e 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1533,6 +1533,7 @@
*/
#define XHCI_STATE_DYING (1 << 0)
#define XHCI_STATE_HALTED (1 << 1)
+#define XHCI_STATE_REMOVING (1 << 2)
/* Statistics */
int error_bitmask;
unsigned int quirks;
@@ -1567,6 +1568,8 @@
/* For controllers with a broken beyond repair streams implementation */
#define XHCI_BROKEN_STREAMS (1 << 19)
#define XHCI_PME_STUCK_QUIRK (1 << 20)
+#define XHCI_SSIC_PORT_UNUSED (1 << 22)
+#define XHCI_NO_64BIT_SUPPORT (1 << 23)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index 4e38683c..6d4e757 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -346,6 +346,9 @@
if (iface_desc->desc.bInterfaceClass != 0x0A)
return -ENODEV;
+ if (iface_desc->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL)
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index c6bfd13..775690b 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -797,6 +797,21 @@
/* this one will match for the IOWarrior56 only */
dev->int_out_endpoint = endpoint;
}
+
+ if (!dev->int_in_endpoint) {
+ dev_err(&interface->dev, "no interrupt-in endpoint found\n");
+ retval = -ENODEV;
+ goto error;
+ }
+
+ if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56) {
+ if (!dev->int_out_endpoint) {
+ dev_err(&interface->dev, "no interrupt-out endpoint found\n");
+ retval = -ENODEV;
+ goto error;
+ }
+ }
+
/* we have to check the report_size often, so remember it in the endianness suitable for our machine */
dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint);
if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) &&
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index 62cb8cd..66e2d036 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -370,6 +370,10 @@
hdev = interface_to_usbdev(intf);
desc = intf->cur_altsetting;
+
+ if (desc->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
endpoint = &desc->endpoint[0].desc;
/* valid only for SS root hub */
diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
index 0bbafe79..c33ad21 100644
--- a/drivers/usb/misc/usbtest.c
+++ b/drivers/usb/misc/usbtest.c
@@ -303,11 +303,20 @@
module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(mod_pattern, "i/o pattern (0 == zeroes)");
-static inline void simple_fill_buf(struct urb *urb)
+static unsigned get_maxpacket(struct usb_device *udev, int pipe)
+{
+ struct usb_host_endpoint *ep;
+
+ ep = usb_pipe_endpoint(udev, pipe);
+ return le16_to_cpup(&ep->desc.wMaxPacketSize);
+}
+
+static void simple_fill_buf(struct urb *urb)
{
unsigned i;
u8 *buf = urb->transfer_buffer;
unsigned len = urb->transfer_buffer_length;
+ unsigned maxpacket;
switch (pattern) {
default:
@@ -316,8 +325,9 @@
memset(buf, 0, len);
break;
case 1: /* mod63 */
+ maxpacket = get_maxpacket(urb->dev, urb->pipe);
for (i = 0; i < len; i++)
- *buf++ = (u8) (i % 63);
+ *buf++ = (u8) ((i % maxpacket) % 63);
break;
}
}
@@ -349,6 +359,7 @@
u8 expected;
u8 *buf = urb->transfer_buffer;
unsigned len = urb->actual_length;
+ unsigned maxpacket = get_maxpacket(urb->dev, urb->pipe);
int ret = check_guard_bytes(tdev, urb);
if (ret)
@@ -366,7 +377,7 @@
* with set_interface or set_config.
*/
case 1: /* mod63 */
- expected = i % 63;
+ expected = (i % maxpacket) % 63;
break;
/* always fail unsupported patterns */
default:
@@ -478,11 +489,14 @@
}
static struct scatterlist *
-alloc_sglist(int nents, int max, int vary)
+alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe)
{
struct scatterlist *sg;
+ unsigned int n_size = 0;
unsigned i;
unsigned size = max;
+ unsigned maxpacket =
+ get_maxpacket(interface_to_usbdev(dev->intf), pipe);
if (max == 0)
return NULL;
@@ -511,7 +525,8 @@
break;
case 1:
for (j = 0; j < size; j++)
- *buf++ = (u8) (j % 63);
+ *buf++ = (u8) (((j + n_size) % maxpacket) % 63);
+ n_size += size;
break;
}
@@ -530,7 +545,6 @@
{
struct usb_sg_request *req = (struct usb_sg_request *) _req;
- req->status = -ETIMEDOUT;
usb_sg_cancel(req);
}
@@ -561,8 +575,10 @@
mod_timer(&sg_timer, jiffies +
msecs_to_jiffies(SIMPLE_IO_TIMEOUT));
usb_sg_wait(req);
- del_timer_sync(&sg_timer);
- retval = req->status;
+ if (!del_timer_sync(&sg_timer))
+ retval = -ETIMEDOUT;
+ else
+ retval = req->status;
/* FIXME check resulting data pattern */
@@ -2175,7 +2191,8 @@
"TEST 5: write %d sglists %d entries of %d bytes\n",
param->iterations,
param->sglen, param->length);
- sg = alloc_sglist(param->sglen, param->length, 0);
+ sg = alloc_sglist(param->sglen, param->length,
+ 0, dev, dev->out_pipe);
if (!sg) {
retval = -ENOMEM;
break;
@@ -2193,7 +2210,8 @@
"TEST 6: read %d sglists %d entries of %d bytes\n",
param->iterations,
param->sglen, param->length);
- sg = alloc_sglist(param->sglen, param->length, 0);
+ sg = alloc_sglist(param->sglen, param->length,
+ 0, dev, dev->in_pipe);
if (!sg) {
retval = -ENOMEM;
break;
@@ -2210,7 +2228,8 @@
"TEST 7: write/%d %d sglists %d entries 0..%d bytes\n",
param->vary, param->iterations,
param->sglen, param->length);
- sg = alloc_sglist(param->sglen, param->length, param->vary);
+ sg = alloc_sglist(param->sglen, param->length,
+ param->vary, dev, dev->out_pipe);
if (!sg) {
retval = -ENOMEM;
break;
@@ -2227,7 +2246,8 @@
"TEST 8: read/%d %d sglists %d entries 0..%d bytes\n",
param->vary, param->iterations,
param->sglen, param->length);
- sg = alloc_sglist(param->sglen, param->length, param->vary);
+ sg = alloc_sglist(param->sglen, param->length,
+ param->vary, dev, dev->in_pipe);
if (!sg) {
retval = -ENOMEM;
break;
diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c
index 40ef40a..3cb05eb 100644
--- a/drivers/usb/misc/uss720.c
+++ b/drivers/usb/misc/uss720.c
@@ -715,6 +715,11 @@
interface = intf->cur_altsetting;
+ if (interface->desc.bNumEndpoints < 3) {
+ usb_put_dev(usbdev);
+ return -ENODEV;
+ }
+
/*
* Allocate parport interface
*/
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index 5a9b977..3ef6dfc 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -9,9 +9,9 @@
#define RNDIS_REG(x) (0x80 + ((x - 1) * 4))
-#define EP_MODE_AUTOREG_NONE 0
-#define EP_MODE_AUTOREG_ALL_NEOP 1
-#define EP_MODE_AUTOREG_ALWAYS 3
+#define EP_MODE_AUTOREQ_NONE 0
+#define EP_MODE_AUTOREQ_ALL_NEOP 1
+#define EP_MODE_AUTOREQ_ALWAYS 3
#define EP_MODE_DMA_TRANSPARENT 0
#define EP_MODE_DMA_RNDIS 1
@@ -396,19 +396,19 @@
/* auto req */
cppi41_set_autoreq_mode(cppi41_channel,
- EP_MODE_AUTOREG_ALL_NEOP);
+ EP_MODE_AUTOREQ_ALL_NEOP);
} else {
musb_writel(musb->ctrl_base,
RNDIS_REG(cppi41_channel->port_num), 0);
cppi41_set_dma_mode(cppi41_channel,
EP_MODE_DMA_TRANSPARENT);
cppi41_set_autoreq_mode(cppi41_channel,
- EP_MODE_AUTOREG_NONE);
+ EP_MODE_AUTOREQ_NONE);
}
} else {
/* fallback mode */
cppi41_set_dma_mode(cppi41_channel, EP_MODE_DMA_TRANSPARENT);
- cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREG_NONE);
+ cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE);
len = min_t(u32, packet_sz, len);
}
cppi41_channel->prog_len = len;
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 4500610..7da914b 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -583,14 +583,13 @@
musb_writew(ep->regs, MUSB_TXCSR, 0);
/* scrub all previous state, clearing toggle */
- } else {
- csr = musb_readw(ep->regs, MUSB_RXCSR);
- if (csr & MUSB_RXCSR_RXPKTRDY)
- WARNING("rx%d, packet/%d ready?\n", ep->epnum,
- musb_readw(ep->regs, MUSB_RXCOUNT));
-
- musb_h_flush_rxfifo(ep, MUSB_RXCSR_CLRDATATOG);
}
+ csr = musb_readw(ep->regs, MUSB_RXCSR);
+ if (csr & MUSB_RXCSR_RXPKTRDY)
+ WARNING("rx%d, packet/%d ready?\n", ep->epnum,
+ musb_readw(ep->regs, MUSB_RXCOUNT));
+
+ musb_h_flush_rxfifo(ep, MUSB_RXCSR_CLRDATATOG);
/* target addr and (for multipoint) hub addr/port */
if (musb->is_multipoint) {
@@ -950,9 +949,15 @@
if (is_in) {
dma = is_dma_capable() ? ep->rx_channel : NULL;
- /* clear nak timeout bit */
+ /*
+ * Need to stop the transaction by clearing REQPKT first
+ * then the NAK Timeout bit ref MUSBMHDRC USB 2.0 HIGH-SPEED
+ * DUAL-ROLE CONTROLLER Programmer's Guide, section 9.2.2
+ */
rx_csr = musb_readw(epio, MUSB_RXCSR);
rx_csr |= MUSB_RXCSR_H_WZC_BITS;
+ rx_csr &= ~MUSB_RXCSR_H_REQPKT;
+ musb_writew(epio, MUSB_RXCSR, rx_csr);
rx_csr &= ~MUSB_RXCSR_DATAERROR;
musb_writew(epio, MUSB_RXCSR, rx_csr);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index b3b6813..d51f478 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -466,7 +466,7 @@
return NULL;
dparam = &info->driver_param;
- dparam->type = of_id ? (u32)of_id->data : 0;
+ dparam->type = of_id ? (uintptr_t)of_id->data : 0;
if (!of_property_read_u32(dev->of_node, "renesas,buswait", &tmp))
dparam->buswait_bwait = tmp;
gpio = of_get_named_gpio_flags(dev->of_node, "renesas,enable-gpio", 0,
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index b0c97a3..66be318 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -192,7 +192,8 @@
goto __usbhs_pkt_handler_end;
}
- ret = func(pkt, &is_done);
+ if (likely(func))
+ ret = func(pkt, &is_done);
if (is_done)
__usbhsf_pkt_del(pkt);
@@ -818,37 +819,47 @@
{
struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work);
struct usbhs_pipe *pipe = pkt->pipe;
- struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
+ struct usbhs_fifo *fifo;
struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
struct dma_async_tx_descriptor *desc;
- struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
+ struct dma_chan *chan;
struct device *dev = usbhs_priv_to_dev(priv);
enum dma_transfer_direction dir;
+ unsigned long flags;
+ usbhs_lock(priv, flags);
+ fifo = usbhs_pipe_to_fifo(pipe);
+ if (!fifo)
+ goto xfer_work_end;
+
+ chan = usbhsf_dma_chan_get(fifo, pkt);
dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
desc = dmaengine_prep_slave_single(chan, pkt->dma + pkt->actual,
pkt->trans, dir,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc)
- return;
+ goto xfer_work_end;
desc->callback = usbhsf_dma_complete;
desc->callback_param = pipe;
if (dmaengine_submit(desc) < 0) {
dev_err(dev, "Failed to submit dma descriptor\n");
- return;
+ goto xfer_work_end;
}
dev_dbg(dev, " %s %d (%d/ %d)\n",
fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
usbhs_pipe_running(pipe, 1);
- usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
- usbhs_pipe_enable(pipe);
usbhsf_dma_start(pipe, fifo);
+ usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
dma_async_issue_pending(chan);
+ usbhs_pipe_enable(pipe);
+
+xfer_work_end:
+ usbhs_unlock(priv, flags);
}
/*
@@ -894,6 +905,7 @@
pkt->trans = len;
+ usbhsf_tx_irq_ctrl(pipe, 0);
INIT_WORK(&pkt->work, xfer_work);
schedule_work(&pkt->work);
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
index 9a705b1..cf274b8 100644
--- a/drivers/usb/renesas_usbhs/mod.c
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -277,9 +277,16 @@
usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC);
usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
- usbhs_write(priv, BRDYSTS, ~irq_state.brdysts);
+ /*
+ * The driver should not clear the xxxSTS after the line of
+ * "call irq callback functions" because each "if" statement is
+ * possible to call the callback function for avoiding any side effects.
+ */
+ if (irq_state.intsts0 & BRDY)
+ usbhs_write(priv, BRDYSTS, ~irq_state.brdysts);
usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts);
- usbhs_write(priv, BEMPSTS, ~irq_state.bempsts);
+ if (irq_state.intsts0 & BEMP)
+ usbhs_write(priv, BEMPSTS, ~irq_state.bempsts);
/*
* call irq callback functions
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 294d43c..9f5e9fb 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -118,18 +118,34 @@
/*
* queue push/pop
*/
+static void __usbhsg_queue_pop(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq,
+ int status)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+
+ dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
+
+ ureq->req.status = status;
+ spin_unlock(usbhs_priv_to_lock(priv));
+ usb_gadget_giveback_request(&uep->ep, &ureq->req);
+ spin_lock(usbhs_priv_to_lock(priv));
+}
+
static void usbhsg_queue_pop(struct usbhsg_uep *uep,
struct usbhsg_request *ureq,
int status)
{
struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
- struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
- struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+ unsigned long flags;
- dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
-
- ureq->req.status = status;
- usb_gadget_giveback_request(&uep->ep, &ureq->req);
+ usbhs_lock(priv, flags);
+ __usbhsg_queue_pop(uep, ureq, status);
+ usbhs_unlock(priv, flags);
}
static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
@@ -137,10 +153,14 @@
struct usbhs_pipe *pipe = pkt->pipe;
struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
+ unsigned long flags;
ureq->req.actual = pkt->actual;
- usbhsg_queue_pop(uep, ureq, 0);
+ usbhs_lock(priv, flags);
+ if (uep)
+ __usbhsg_queue_pop(uep, ureq, 0);
+ usbhs_unlock(priv, flags);
}
static void usbhsg_queue_push(struct usbhsg_uep *uep,
@@ -558,6 +578,9 @@
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
struct usbhs_pipe *pipe;
int ret = -EIO;
+ unsigned long flags;
+
+ usbhs_lock(priv, flags);
/*
* if it already have pipe,
@@ -566,7 +589,8 @@
if (uep->pipe) {
usbhs_pipe_clear(uep->pipe);
usbhs_pipe_sequence_data0(uep->pipe);
- return 0;
+ ret = 0;
+ goto usbhsg_ep_enable_end;
}
pipe = usbhs_pipe_malloc(priv,
@@ -594,6 +618,9 @@
ret = 0;
}
+usbhsg_ep_enable_end:
+ usbhs_unlock(priv, flags);
+
return ret;
}
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index eac7cca..6bd06a8 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -43,8 +43,8 @@
static int cp210x_tiocmset_port(struct usb_serial_port *port,
unsigned int, unsigned int);
static void cp210x_break_ctl(struct tty_struct *, int);
-static int cp210x_startup(struct usb_serial *);
-static void cp210x_release(struct usb_serial *);
+static int cp210x_port_probe(struct usb_serial_port *);
+static int cp210x_port_remove(struct usb_serial_port *);
static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
static const struct usb_device_id id_table[] = {
@@ -107,6 +107,7 @@
{ USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */
{ USB_DEVICE(0x10C4, 0x8281) }, /* Nanotec Plug & Drive */
{ USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */
+ { USB_DEVICE(0x10C4, 0x82F4) }, /* Starizona MicroTouch */
{ USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */
{ USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */
{ USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */
@@ -116,6 +117,7 @@
{ USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */
{ USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */
{ USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
+ { USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */
{ USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
{ USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
@@ -140,6 +142,8 @@
{ USB_DEVICE(0x10C4, 0xF004) }, /* Elan Digital Systems USBcount50 */
{ USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */
{ USB_DEVICE(0x10CE, 0xEA6A) }, /* Silicon Labs MobiData GPRS USB Modem 100EU */
+ { USB_DEVICE(0x12B8, 0xEC60) }, /* Link G4 ECU */
+ { USB_DEVICE(0x12B8, 0xEC62) }, /* Link G4+ ECU */
{ USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */
{ USB_DEVICE(0x1555, 0x0004) }, /* Owen AC4 USB-RS485 Converter */
{ USB_DEVICE(0x166A, 0x0201) }, /* Clipsal 5500PACA C-Bus Pascal Automation Controller */
@@ -161,6 +165,11 @@
{ USB_DEVICE(0x17F4, 0xAAAA) }, /* Wavesense Jazz blood glucose meter */
{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
+ { USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */
+ { USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
+ { USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
+ { USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */
+ { USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
{ USB_DEVICE(0x1BA4, 0x0002) }, /* Silicon Labs 358x factory default */
@@ -197,7 +206,7 @@
MODULE_DEVICE_TABLE(usb, id_table);
-struct cp210x_serial_private {
+struct cp210x_port_private {
__u8 bInterfaceNumber;
};
@@ -216,8 +225,8 @@
.set_termios = cp210x_set_termios,
.tiocmget = cp210x_tiocmget,
.tiocmset = cp210x_tiocmset,
- .attach = cp210x_startup,
- .release = cp210x_release,
+ .port_probe = cp210x_port_probe,
+ .port_remove = cp210x_port_remove,
.dtr_rts = cp210x_dtr_rts
};
@@ -311,7 +320,7 @@
unsigned int *data, int size)
{
struct usb_serial *serial = port->serial;
- struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
__le32 *buf;
int result, i, length;
@@ -325,7 +334,7 @@
/* Issue the request, attempting to read 'size' bytes */
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
request, REQTYPE_INTERFACE_TO_HOST, 0x0000,
- spriv->bInterfaceNumber, buf, size,
+ port_priv->bInterfaceNumber, buf, size,
USB_CTRL_GET_TIMEOUT);
/* Convert data into an array of integers */
@@ -356,7 +365,7 @@
unsigned int *data, int size)
{
struct usb_serial *serial = port->serial;
- struct cp210x_serial_private *spriv = usb_get_serial_data(serial);
+ struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
__le32 *buf;
int result, i, length;
@@ -375,13 +384,13 @@
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
request, REQTYPE_HOST_TO_INTERFACE, 0x0000,
- spriv->bInterfaceNumber, buf, size,
+ port_priv->bInterfaceNumber, buf, size,
USB_CTRL_SET_TIMEOUT);
} else {
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
request, REQTYPE_HOST_TO_INTERFACE, data[0],
- spriv->bInterfaceNumber, NULL, 0,
+ port_priv->bInterfaceNumber, NULL, 0,
USB_CTRL_SET_TIMEOUT);
}
@@ -775,7 +784,7 @@
} else {
modem_ctl[0] &= ~0x7B;
modem_ctl[0] |= 0x01;
- modem_ctl[1] |= 0x40;
+ modem_ctl[1] = 0x40;
dev_dbg(dev, "%s - flow control = NONE\n", __func__);
}
@@ -835,7 +844,9 @@
unsigned int control;
int result;
- cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
+ result = cp210x_get_config(port, CP210X_GET_MDMSTS, &control, 1);
+ if (result)
+ return result;
result = ((control & CONTROL_DTR) ? TIOCM_DTR : 0)
|((control & CONTROL_RTS) ? TIOCM_RTS : 0)
@@ -863,29 +874,32 @@
cp210x_set_config(port, CP210X_SET_BREAK, &state, 2);
}
-static int cp210x_startup(struct usb_serial *serial)
+static int cp210x_port_probe(struct usb_serial_port *port)
{
+ struct usb_serial *serial = port->serial;
struct usb_host_interface *cur_altsetting;
- struct cp210x_serial_private *spriv;
+ struct cp210x_port_private *port_priv;
- spriv = kzalloc(sizeof(*spriv), GFP_KERNEL);
- if (!spriv)
+ port_priv = kzalloc(sizeof(*port_priv), GFP_KERNEL);
+ if (!port_priv)
return -ENOMEM;
cur_altsetting = serial->interface->cur_altsetting;
- spriv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
+ port_priv->bInterfaceNumber = cur_altsetting->desc.bInterfaceNumber;
- usb_set_serial_data(serial, spriv);
+ usb_set_serial_port_data(port, port_priv);
return 0;
}
-static void cp210x_release(struct usb_serial *serial)
+static int cp210x_port_remove(struct usb_serial_port *port)
{
- struct cp210x_serial_private *spriv;
+ struct cp210x_port_private *port_priv;
- spriv = usb_get_serial_data(serial);
- kfree(spriv);
+ port_priv = usb_get_serial_port_data(port);
+ kfree(port_priv);
+
+ return 0;
}
module_usb_serial_driver(serial_drivers, id_table);
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index 01bf533..244acb12 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -447,6 +447,11 @@
struct usb_serial *serial = port->serial;
struct cypress_private *priv;
+ if (!port->interrupt_out_urb || !port->interrupt_in_urb) {
+ dev_err(&port->dev, "required endpoint is missing\n");
+ return -ENODEV;
+ }
+
priv = kzalloc(sizeof(struct cypress_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -606,12 +611,6 @@
cypress_set_termios(tty, port, &priv->tmp_termios);
/* setup the port and start reading from the device */
- if (!port->interrupt_in_urb) {
- dev_err(&port->dev, "%s - interrupt_in_urb is empty!\n",
- __func__);
- return -1;
- }
-
usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
port->interrupt_in_urb->transfer_buffer,
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 12b0e67..e0b1fe2f 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -1251,8 +1251,27 @@
static int digi_startup(struct usb_serial *serial)
{
+ struct device *dev = &serial->interface->dev;
struct digi_serial *serial_priv;
int ret;
+ int i;
+
+ /* check whether the device has the expected number of endpoints */
+ if (serial->num_port_pointers < serial->type->num_ports + 1) {
+ dev_err(dev, "OOB endpoints missing\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < serial->type->num_ports + 1 ; i++) {
+ if (!serial->port[i]->read_urb) {
+ dev_err(dev, "bulk-in endpoint missing\n");
+ return -ENODEV;
+ }
+ if (!serial->port[i]->write_urb) {
+ dev_err(dev, "bulk-out endpoint missing\n");
+ return -ENODEV;
+ }
+ }
serial_priv = kzalloc(sizeof(*serial_priv), GFP_KERNEL);
if (!serial_priv)
@@ -1464,16 +1483,20 @@
struct usb_serial *serial = port->serial;
struct tty_struct *tty;
struct digi_port *priv = usb_get_serial_port_data(port);
+ unsigned char *buf = urb->transfer_buffer;
int opcode, line, status, val;
int i;
unsigned int rts;
+ if (urb->actual_length < 4)
+ return -1;
+
/* handle each oob command */
- for (i = 0; i < urb->actual_length - 3;) {
- opcode = ((unsigned char *)urb->transfer_buffer)[i++];
- line = ((unsigned char *)urb->transfer_buffer)[i++];
- status = ((unsigned char *)urb->transfer_buffer)[i++];
- val = ((unsigned char *)urb->transfer_buffer)[i++];
+ for (i = 0; i < urb->actual_length - 3; i += 4) {
+ opcode = buf[i];
+ line = buf[i + 1];
+ status = buf[i + 2];
+ val = buf[i + 3];
dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n",
opcode, line, status, val);
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 4c8b3b8..f475e9b 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -644,6 +644,8 @@
{ USB_DEVICE(FTDI_VID, FTDI_ELV_TFD128_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_FM3RX_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS777_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_PALMSENS_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_IVIUM_XSTAT_PID) },
{ USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) },
{ USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) },
{ USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) },
@@ -979,7 +981,8 @@
/* ekey Devices */
{ USB_DEVICE(FTDI_VID, FTDI_EKEY_CONV_USB_PID) },
/* Infineon Devices */
- { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_TC1798_PID, 1) },
+ { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_TC2X7_PID, 1) },
/* GE Healthcare devices */
{ USB_DEVICE(GE_HEALTHCARE_VID, GE_HEALTHCARE_NEMO_TRACKER_PID) },
/* Active Research (Actisense) devices */
@@ -999,6 +1002,11 @@
{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_DISPLAY_PID) },
{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_LITE_PID) },
{ USB_DEVICE(FTDI_VID, CHETCO_SEASMART_ANALOG_PID) },
+ /* ICP DAS I-756xU devices */
+ { USB_DEVICE(ICPDAS_VID, ICPDAS_I7560U_PID) },
+ { USB_DEVICE(ICPDAS_VID, ICPDAS_I7561U_PID) },
+ { USB_DEVICE(ICPDAS_VID, ICPDAS_I7563U_PID) },
+ { USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 792e054..5d0ae77 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -406,6 +406,12 @@
#define FTDI_4N_GALAXY_DE_3_PID 0xF3C2
/*
+ * Ivium Technologies product IDs
+ */
+#define FTDI_PALMSENS_PID 0xf440
+#define FTDI_IVIUM_XSTAT_PID 0xf441
+
+/*
* Linx Technologies product ids
*/
#define LINX_SDMUSBQSS_PID 0xF448 /* Linx SDM-USB-QS-S */
@@ -611,8 +617,9 @@
/*
* Infineon Technologies
*/
-#define INFINEON_VID 0x058b
-#define INFINEON_TRIBOARD_PID 0x0028 /* DAS JTAG TriBoard TC1798 V1.0 */
+#define INFINEON_VID 0x058b
+#define INFINEON_TRIBOARD_TC1798_PID 0x0028 /* DAS JTAG TriBoard TC1798 V1.0 */
+#define INFINEON_TRIBOARD_TC2X7_PID 0x0043 /* DAS JTAG TriBoard TC2X7 V1.0 */
/*
* Acton Research Corp.
@@ -664,6 +671,12 @@
#define INTREPID_NEOVI_PID 0x0701
/*
+ * WICED USB UART
+ */
+#define WICED_VID 0x0A5C
+#define WICED_USB20706V2_PID 0x6422
+
+/*
* Definitions for ID TECH (www.idt-net.com) devices
*/
#define IDTECH_VID 0x0ACD /* ID TECH Vendor ID */
@@ -863,6 +876,14 @@
#define NOVITUS_BONO_E_PID 0x6010
/*
+ * ICPDAS I-756*U devices
+ */
+#define ICPDAS_VID 0x1b5c
+#define ICPDAS_I7560U_PID 0x0103
+#define ICPDAS_I7561U_PID 0x0104
+#define ICPDAS_I7563U_PID 0x0105
+
+/*
* RT Systems programming cables for various ham radios
*/
#define RTSYSTEMS_VID 0x2100 /* Vendor ID */
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index c086697..1947ea0 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -2856,14 +2856,16 @@
/* not set up yet, so do it now */
edge_serial->interrupt_read_urb =
usb_alloc_urb(0, GFP_KERNEL);
- if (!edge_serial->interrupt_read_urb)
- return -ENOMEM;
+ if (!edge_serial->interrupt_read_urb) {
+ response = -ENOMEM;
+ break;
+ }
edge_serial->interrupt_in_buffer =
kmalloc(buffer_size, GFP_KERNEL);
if (!edge_serial->interrupt_in_buffer) {
- usb_free_urb(edge_serial->interrupt_read_urb);
- return -ENOMEM;
+ response = -ENOMEM;
+ break;
}
edge_serial->interrupt_in_endpoint =
endpoint->bEndpointAddress;
@@ -2891,14 +2893,16 @@
/* not set up yet, so do it now */
edge_serial->read_urb =
usb_alloc_urb(0, GFP_KERNEL);
- if (!edge_serial->read_urb)
- return -ENOMEM;
+ if (!edge_serial->read_urb) {
+ response = -ENOMEM;
+ break;
+ }
edge_serial->bulk_in_buffer =
kmalloc(buffer_size, GFP_KERNEL);
if (!edge_serial->bulk_in_buffer) {
- usb_free_urb(edge_serial->read_urb);
- return -ENOMEM;
+ response = -ENOMEM;
+ break;
}
edge_serial->bulk_in_endpoint =
endpoint->bEndpointAddress;
@@ -2924,9 +2928,22 @@
}
}
- if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) {
- dev_err(ddev, "Error - the proper endpoints were not found!\n");
- return -ENODEV;
+ if (response || !interrupt_in_found || !bulk_in_found ||
+ !bulk_out_found) {
+ if (!response) {
+ dev_err(ddev, "expected endpoints not found\n");
+ response = -ENODEV;
+ }
+
+ usb_free_urb(edge_serial->interrupt_read_urb);
+ kfree(edge_serial->interrupt_in_buffer);
+
+ usb_free_urb(edge_serial->read_urb);
+ kfree(edge_serial->bulk_in_buffer);
+
+ kfree(edge_serial);
+
+ return response;
}
/* start interrupt read for this edgeport this interrupt will
@@ -2949,16 +2966,9 @@
{
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
- /* stop reads and writes on all ports */
- /* free up our endpoint stuff */
if (edge_serial->is_epic) {
usb_kill_urb(edge_serial->interrupt_read_urb);
- usb_free_urb(edge_serial->interrupt_read_urb);
- kfree(edge_serial->interrupt_in_buffer);
-
usb_kill_urb(edge_serial->read_urb);
- usb_free_urb(edge_serial->read_urb);
- kfree(edge_serial->bulk_in_buffer);
}
}
@@ -2971,6 +2981,16 @@
{
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
+ if (edge_serial->is_epic) {
+ usb_kill_urb(edge_serial->interrupt_read_urb);
+ usb_free_urb(edge_serial->interrupt_read_urb);
+ kfree(edge_serial->interrupt_in_buffer);
+
+ usb_kill_urb(edge_serial->read_urb);
+ usb_free_urb(edge_serial->read_urb);
+ kfree(edge_serial->bulk_in_buffer);
+ }
+
kfree(edge_serial);
}
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index ddbb8fe..f6f6960 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -1546,6 +1546,12 @@
function = TIUMP_GET_FUNC_FROM_CODE(data[0]);
dev_dbg(dev, "%s - port_number %d, function %d, info 0x%x\n", __func__,
port_number, function, data[1]);
+
+ if (port_number >= edge_serial->serial->num_ports) {
+ dev_err(dev, "bad port number %d\n", port_number);
+ goto exit;
+ }
+
port = edge_serial->serial->port[port_number];
edge_port = usb_get_serial_port_data(port);
if (!edge_port) {
@@ -1626,7 +1632,7 @@
port_number = edge_port->port->port_number;
- if (edge_port->lsr_event) {
+ if (urb->actual_length > 0 && edge_port->lsr_event) {
edge_port->lsr_event = 0;
dev_dbg(dev, "%s ===== Port %u LSR Status = %02x, Data = %02x ======\n",
__func__, port_number, edge_port->lsr_mask, *data);
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index e07b15e..7faa901 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -2376,6 +2376,10 @@
s_priv = usb_get_serial_data(serial);
+ /* Make sure to unlink the URBs submitted in attach. */
+ usb_kill_urb(s_priv->instat_urb);
+ usb_kill_urb(s_priv->indat_urb);
+
usb_free_urb(s_priv->instat_urb);
usb_free_urb(s_priv->indat_urb);
usb_free_urb(s_priv->glocont_urb);
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index e020ad2..53c9013 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -296,7 +296,7 @@
rc = usb_serial_generic_open(tty, port);
if (rc) {
retval = rc;
- goto exit;
+ goto err_free_cfg;
}
rc = usb_control_msg(port->serial->dev,
@@ -315,17 +315,32 @@
dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
rc = klsi_105_get_line_state(port, &line_state);
- if (rc >= 0) {
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_state = line_state;
- spin_unlock_irqrestore(&priv->lock, flags);
- dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
- retval = 0;
- } else
+ if (rc < 0) {
retval = rc;
+ goto err_disable_read;
+ }
-exit:
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_state = line_state;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__,
+ line_state);
+
+ return 0;
+
+err_disable_read:
+ usb_control_msg(port->serial->dev,
+ usb_sndctrlpipe(port->serial->dev, 0),
+ KL5KUSB105A_SIO_CONFIGURE,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
+ 0, /* index */
+ NULL, 0,
+ KLSI_TIMEOUT);
+ usb_serial_generic_close(port);
+err_free_cfg:
kfree(cfg);
+
return retval;
}
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index fd707d6..89726f7 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -376,14 +376,21 @@
static int mct_u232_port_probe(struct usb_serial_port *port)
{
+ struct usb_serial *serial = port->serial;
struct mct_u232_private *priv;
+ /* check first to simplify error handling */
+ if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) {
+ dev_err(&port->dev, "expected endpoint missing\n");
+ return -ENODEV;
+ }
+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Use second interrupt-in endpoint for reading. */
- priv->read_urb = port->serial->port[1]->interrupt_in_urb;
+ priv->read_urb = serial->port[1]->interrupt_in_urb;
priv->read_urb->context = port;
spin_lock_init(&priv->lock);
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index dfd728a..c3b8ae3 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -1239,7 +1239,7 @@
if (urb->transfer_buffer == NULL) {
urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!urb->transfer_buffer)
goto exit;
}
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 3d88eefd..0b547d9 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -1369,8 +1369,8 @@
}
if (urb->transfer_buffer == NULL) {
- urb->transfer_buffer =
- kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+ urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE,
+ GFP_ATOMIC);
if (!urb->transfer_buffer)
goto exit;
}
diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
index 460a406..d029b2f 100644
--- a/drivers/usb/serial/mxuport.c
+++ b/drivers/usb/serial/mxuport.c
@@ -1263,6 +1263,15 @@
return 0;
}
+static void mxuport_release(struct usb_serial *serial)
+{
+ struct usb_serial_port *port0 = serial->port[0];
+ struct usb_serial_port *port1 = serial->port[1];
+
+ usb_serial_generic_close(port1);
+ usb_serial_generic_close(port0);
+}
+
static int mxuport_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct mxuport_port *mxport = usb_get_serial_port_data(port);
@@ -1365,6 +1374,7 @@
.probe = mxuport_probe,
.port_probe = mxuport_port_probe,
.attach = mxuport_attach,
+ .release = mxuport_release,
.calc_num_ports = mxuport_calc_num_ports,
.open = mxuport_open,
.close = mxuport_close,
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index f6c6900..880bbdb 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -129,12 +129,6 @@
static int omninet_open(struct tty_struct *tty, struct usb_serial_port *port)
{
- struct usb_serial *serial = port->serial;
- struct usb_serial_port *wport;
-
- wport = serial->port[1];
- tty_port_tty_set(&wport->port, tty);
-
return usb_serial_generic_open(tty, port);
}
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index c8c4e50..2bf70b6 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -270,8 +270,20 @@
#define TELIT_PRODUCT_CC864_SINGLE 0x1006
#define TELIT_PRODUCT_DE910_DUAL 0x1010
#define TELIT_PRODUCT_UE910_V2 0x1012
+#define TELIT_PRODUCT_LE922_USBCFG1 0x1040
+#define TELIT_PRODUCT_LE922_USBCFG2 0x1041
+#define TELIT_PRODUCT_LE922_USBCFG0 0x1042
+#define TELIT_PRODUCT_LE922_USBCFG3 0x1043
+#define TELIT_PRODUCT_LE922_USBCFG5 0x1045
#define TELIT_PRODUCT_LE920 0x1200
#define TELIT_PRODUCT_LE910 0x1201
+#define TELIT_PRODUCT_LE910_USBCFG4 0x1206
+#define TELIT_PRODUCT_LE920A4_1207 0x1207
+#define TELIT_PRODUCT_LE920A4_1208 0x1208
+#define TELIT_PRODUCT_LE920A4_1211 0x1211
+#define TELIT_PRODUCT_LE920A4_1212 0x1212
+#define TELIT_PRODUCT_LE920A4_1213 0x1213
+#define TELIT_PRODUCT_LE920A4_1214 0x1214
/* ZTE PRODUCTS */
#define ZTE_VENDOR_ID 0x19d2
@@ -368,18 +380,22 @@
#define HAIER_PRODUCT_CE81B 0x10f8
#define HAIER_PRODUCT_CE100 0x2009
-/* Cinterion (formerly Siemens) products */
-#define SIEMENS_VENDOR_ID 0x0681
-#define CINTERION_VENDOR_ID 0x1e2d
+/* Gemalto's Cinterion products (formerly Siemens) */
+#define SIEMENS_VENDOR_ID 0x0681
+#define CINTERION_VENDOR_ID 0x1e2d
+#define CINTERION_PRODUCT_HC25_MDMNET 0x0040
#define CINTERION_PRODUCT_HC25_MDM 0x0047
-#define CINTERION_PRODUCT_HC25_MDMNET 0x0040
+#define CINTERION_PRODUCT_HC28_MDMNET 0x004A /* same for HC28J */
#define CINTERION_PRODUCT_HC28_MDM 0x004C
-#define CINTERION_PRODUCT_HC28_MDMNET 0x004A /* same for HC28J */
#define CINTERION_PRODUCT_EU3_E 0x0051
#define CINTERION_PRODUCT_EU3_P 0x0052
#define CINTERION_PRODUCT_PH8 0x0053
#define CINTERION_PRODUCT_AHXX 0x0055
#define CINTERION_PRODUCT_PLXX 0x0060
+#define CINTERION_PRODUCT_PH8_2RMNET 0x0082
+#define CINTERION_PRODUCT_PH8_AUDIO 0x0083
+#define CINTERION_PRODUCT_AHXX_2RMNET 0x0084
+#define CINTERION_PRODUCT_AHXX_AUDIO 0x0085
/* Olivetti products */
#define OLIVETTI_VENDOR_ID 0x0b3c
@@ -515,6 +531,13 @@
};
#define MAX_BL_NUM 11
+
+/* WeTelecom products */
+#define WETELECOM_VENDOR_ID 0x22de
+#define WETELECOM_PRODUCT_WMD200 0x6801
+#define WETELECOM_PRODUCT_6802 0x6802
+#define WETELECOM_PRODUCT_WMD300 0x6803
+
struct option_blacklist_info {
/* bitfield of interface numbers for OPTION_BLACKLIST_SENDSETUP */
const unsigned long sendsetup;
@@ -608,6 +631,25 @@
.reserved = BIT(8) | BIT(10) | BIT(11),
};
+static const struct option_blacklist_info telit_le920a4_blacklist_1 = {
+ .sendsetup = BIT(0),
+ .reserved = BIT(1),
+};
+
+static const struct option_blacklist_info telit_le922_blacklist_usbcfg0 = {
+ .sendsetup = BIT(2),
+ .reserved = BIT(0) | BIT(1) | BIT(3),
+};
+
+static const struct option_blacklist_info telit_le922_blacklist_usbcfg3 = {
+ .sendsetup = BIT(0),
+ .reserved = BIT(1) | BIT(2) | BIT(3),
+};
+
+static const struct option_blacklist_info cinterion_rmnet2_blacklist = {
+ .reserved = BIT(4) | BIT(5),
+};
+
static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
@@ -1154,10 +1196,32 @@
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1),
+ .driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG2),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG3),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910),
.driver_info = (kernel_ulong_t)&telit_le910_blacklist },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920),
.driver_info = (kernel_ulong_t)&telit_le920_blacklist },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1207) },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1208),
+ .driver_info = (kernel_ulong_t)&telit_le920a4_blacklist_1 },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1211),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1212),
+ .driver_info = (kernel_ulong_t)&telit_le920a4_blacklist_1 },
+ { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1213, 0xff) },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920A4_1214),
+ .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&net_intf1_blacklist },
@@ -1568,7 +1632,79 @@
.driver_info = (kernel_ulong_t)&net_intf3_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&net_intf3_blacklist },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff42, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff43, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff44, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff45, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff46, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff47, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff48, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff49, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4a, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4b, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4c, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4d, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4e, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4f, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff50, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff51, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff52, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff53, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff54, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff55, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff56, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff57, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff58, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff59, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5a, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5b, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5c, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5d, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5e, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5f, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff60, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff61, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff62, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff63, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff64, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff65, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff66, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff67, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff68, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff69, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6a, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6b, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6c, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6d, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6e, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6f, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff70, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff71, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff72, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff73, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff74, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff75, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff76, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff77, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff78, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff79, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7a, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7b, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7c, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7d, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7e, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7f, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff80, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff81, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff82, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff83, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff84, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff85, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff86, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff87, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff88, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff89, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8a, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8b, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8c, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8d, 0xff, 0xff, 0xff) },
@@ -1579,6 +1715,61 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff92, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff9f, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa0, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa1, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa2, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa3, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa4, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa5, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa6, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa7, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa8, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa9, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffaa, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffab, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffac, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffae, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffaf, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb0, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb1, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb2, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb3, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb4, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb5, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb6, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb7, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb8, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb9, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffba, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbb, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbc, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbd, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbe, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbf, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc0, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc1, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc2, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc3, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc4, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc5, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc6, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc7, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc8, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc9, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffca, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcb, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcc, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcd, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffce, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcf, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd0, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd1, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd2, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd3, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd4, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd5, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffec, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffee, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff6, 0xff, 0xff, 0xff) },
@@ -1665,7 +1856,13 @@
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
- { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },
+ { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_2RMNET, 0xff),
+ .driver_info = (kernel_ulong_t)&cinterion_rmnet2_blacklist },
+ { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_AUDIO, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_2RMNET, 0xff) },
+ { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) },
+ { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) },
{ USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) },
{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDM) },
{ USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDMNET) },
@@ -1771,11 +1968,18 @@
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d02, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
+ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */
+ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */
{ USB_DEVICE_INTERFACE_CLASS(0x2020, 0x4000, 0xff) }, /* OLICARD300 - MT6225 */
{ USB_DEVICE(INOVIA_VENDOR_ID, INOVIA_SEW858) },
{ USB_DEVICE(VIATELECOM_VENDOR_ID, VIATELECOM_PRODUCT_CDS7) },
+ { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD200, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_6802, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(WETELECOM_VENDOR_ID, WETELECOM_PRODUCT_WMD300, 0xff, 0xff, 0xff) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, option_ids);
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 9c63897..a69ddcf 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -158,6 +158,10 @@
{DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a9)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */
+ {DEVICE_SWI(0x413c, 0x81b1)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */
+ {DEVICE_SWI(0x413c, 0x81b3)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
+ {DEVICE_SWI(0x413c, 0x81b5)}, /* Dell Wireless 5811e QDL */
+ {DEVICE_SWI(0x413c, 0x81b6)}, /* Dell Wireless 5811e QDL */
/* Huawei devices */
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index 504f5bff..b18974c 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -141,6 +141,7 @@
serial_priv = usb_get_serial_data(serial);
+ usb_kill_urb(serial_priv->read_urb);
usb_free_urb(serial_priv->read_urb);
kfree(serial_priv->read_buffer);
kfree(serial_priv);
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index b2dff0f..236ea43 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -205,6 +205,11 @@
if (!safe)
goto out;
+ if (length < 2) {
+ dev_err(&port->dev, "malformed packet\n");
+ return;
+ }
+
fcs = fcs_compute10(data, length, CRC10_INITFCS);
if (fcs) {
dev_err(&port->dev, "%s - bad CRC %x\n", __func__, fcs);
diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c
index 7064eb8..40de275 100644
--- a/drivers/usb/serial/usb-serial-simple.c
+++ b/drivers/usb/serial/usb-serial-simple.c
@@ -53,7 +53,9 @@
/* Infineon Flashloader driver */
#define FLASHLOADER_IDS() \
- { USB_DEVICE(0x8087, 0x0716) }
+ { USB_DEVICE_INTERFACE_CLASS(0x058b, 0x0041, USB_CLASS_CDC_DATA) }, \
+ { USB_DEVICE(0x8087, 0x0716) }, \
+ { USB_DEVICE(0x8087, 0x0801) }
DEVICE(flashloader, FLASHLOADER_IDS);
/* ViVOpay USB Serial Driver */
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 6fbfc8f..a290891 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -1061,7 +1061,8 @@
serial->disconnected = 0;
- usb_serial_console_init(serial->port[0]->minor);
+ if (num_ports > 0)
+ usb_serial_console_init(serial->port[0]->minor);
exit:
module_put(type->driver.owner);
return 0;
@@ -1416,7 +1417,7 @@
rc = usb_register(udriver);
if (rc)
- return rc;
+ goto failed_usb_register;
for (sd = serial_drivers; *sd; ++sd) {
(*sd)->usb_driver = udriver;
@@ -1434,6 +1435,8 @@
while (sd-- > serial_drivers)
usb_serial_deregister(*sd);
usb_deregister(udriver);
+failed_usb_register:
+ kfree(udriver);
return rc;
}
EXPORT_SYMBOL_GPL(usb_serial_register_drivers);
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 2ef0f0a..1fc03ef 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -2,7 +2,7 @@
* USB Attached SCSI
* Note that this is not the same as the USB Mass Storage driver
*
- * Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013 - 2014
+ * Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013 - 2016
* Copyright Matthew Wilcox for Intel Corp, 2010
* Copyright Sarah Sharp for Intel Corp, 2010
*
@@ -768,6 +768,17 @@
return SUCCESS;
}
+static int uas_target_alloc(struct scsi_target *starget)
+{
+ struct uas_dev_info *devinfo = (struct uas_dev_info *)
+ dev_to_shost(starget->dev.parent)->hostdata;
+
+ if (devinfo->flags & US_FL_NO_REPORT_LUNS)
+ starget->no_report_luns = 1;
+
+ return 0;
+}
+
static int uas_slave_alloc(struct scsi_device *sdev)
{
struct uas_dev_info *devinfo =
@@ -816,6 +827,7 @@
.module = THIS_MODULE,
.name = "uas",
.queuecommand = uas_queuecommand,
+ .target_alloc = uas_target_alloc,
.slave_alloc = uas_slave_alloc,
.slave_configure = uas_slave_configure,
.eh_abort_handler = uas_eh_abort_handler,
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index cd4ba61..eb87c44 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -54,6 +54,13 @@
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_ATA_1X),
+/* Reported-by: David Webb <djw@noc.ac.uk> */
+UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999,
+ "Seagate",
+ "Expansion Desk",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_NO_REPORT_LUNS),
+
/* Reported-by: Hans de Goede <hdegoede@redhat.com> */
UNUSUAL_DEV(0x0bc2, 0x3320, 0x0000, 0x9999,
"Seagate",
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index cda42cf..b770fff 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -480,7 +480,7 @@
US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16 |
US_FL_INITIAL_READ10 | US_FL_WRITE_CACHE |
US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES |
- US_FL_MAX_SECTORS_240);
+ US_FL_MAX_SECTORS_240 | US_FL_NO_REPORT_LUNS);
p = quirks;
while (*p) {
@@ -530,6 +530,9 @@
case 'i':
f |= US_FL_IGNORE_DEVICE;
break;
+ case 'j':
+ f |= US_FL_NO_REPORT_LUNS;
+ break;
case 'l':
f |= US_FL_NOT_LOCKABLE;
break;
diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
new file mode 100644
index 0000000..58ab188d
--- /dev/null
+++ b/drivers/usb/typec/Kconfig
@@ -0,0 +1,17 @@
+
+menu "USB Power Delivery and Type-C drivers"
+
+config TYPEC
+ tristate
+
+config TYPEC_TCPM
+ tristate "USB Type-C Port Controller Manager"
+ depends on USB
+ select TYPEC
+ help
+ The Type-C Port Controller Manager provides a USB PD and USB Type-C
+ state machine for use with Type-C Port Controllers.
+
+source "drivers/usb/typec/fusb302/Kconfig"
+
+endmenu
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
new file mode 100644
index 0000000..d0818ee
--- /dev/null
+++ b/drivers/usb/typec/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_TYPEC) += typec.o
+obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
+obj-y += fusb302/
diff --git a/drivers/usb/typec/fusb302/Kconfig b/drivers/usb/typec/fusb302/Kconfig
new file mode 100644
index 0000000..ead9718
--- /dev/null
+++ b/drivers/usb/typec/fusb302/Kconfig
@@ -0,0 +1,3 @@
+config TYPEC_FUSB302
+ tristate "Fairchild FUSB302 Type-C chip driver"
+ select TYPEC_TCPM
diff --git a/drivers/usb/typec/fusb302/Makefile b/drivers/usb/typec/fusb302/Makefile
new file mode 100644
index 0000000..207efa5
--- /dev/null
+++ b/drivers/usb/typec/fusb302/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o
diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/fusb302/fusb302.c
new file mode 100644
index 0000000..acf29ce
--- /dev/null
+++ b/drivers/usb/typec/fusb302/fusb302.c
@@ -0,0 +1,2056 @@
+/*
+ * Copyright 2016-2017 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Fairchild FUSB302 Type-C Chip Driver
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/ipc_logging.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/power/htc_battery.h>
+#include <linux/power_supply.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/usb/typec.h>
+#include <linux/usb/usb_controller.h>
+#include <linux/usb/usb_typec.h>
+#include <linux/workqueue.h>
+
+#include "fusb302_reg.h"
+#include "../pd.h"
+#include "../tcpm.h"
+
+#define PM_WAKE_DELAY_MS 2000
+
+bool IsPRSwap;
+bool PolicyIsDFP;
+bool PolicyIsSource;
+
+static void *fusb302_log;
+#define fusb302_log(fmt, ...) ipc_log_string(fusb302_log, "%s: " fmt, \
+ __func__, ##__VA_ARGS__)
+#define NUM_LOG_PAGES 20
+
+/*
+ * When the device is SNK, BC_LVL interrupt is used to monitor cc pins
+ * for the current capability offered by the SRC. As FUSB302 chip fires
+ * the BC_LVL interrupt on PD signalings, cc lvl should be handled after
+ * a delay to avoid measuring on PD activities. The delay is slightly
+ * longer than PD_T_PD_DEBPUNCE (10-20ms).
+ */
+#define T_BC_LVL_DEBOUNCE_DELAY_MS 30
+
+enum toggling_mode {
+ TOGGLINE_MODE_OFF,
+ TOGGLING_MODE_DRP,
+ TOGGLING_MODE_SNK,
+ TOGGLING_MODE_SRC,
+};
+
+static const char * const toggling_mode_name[] = {
+ [TOGGLINE_MODE_OFF] = "toggling_OFF",
+ [TOGGLING_MODE_DRP] = "toggling_DRP",
+ [TOGGLING_MODE_SNK] = "toggling_SNK",
+ [TOGGLING_MODE_SRC] = "toggling_SRC",
+};
+
+enum src_current_status {
+ SRC_CURRENT_DEFAULT,
+ SRC_CURRENT_MEDIUM,
+ SRC_CURRENT_HIGH,
+};
+
+static const u8 ra_mda_value[] = {
+ [SRC_CURRENT_DEFAULT] = 4, /* 210mV */
+ [SRC_CURRENT_MEDIUM] = 9, /* 420mV */
+ [SRC_CURRENT_HIGH] = 18, /* 798mV */
+};
+
+static const u8 rd_mda_value[] = {
+ [SRC_CURRENT_DEFAULT] = 38, /* 1638mV */
+ [SRC_CURRENT_MEDIUM] = 38, /* 1638mV */
+ [SRC_CURRENT_HIGH] = 61, /* 2604mV */
+};
+
+struct fusb302_chip {
+ struct device *dev;
+ struct i2c_client *i2c_client;
+ struct tcpm_port *tcpm_port;
+ struct tcpc_dev tcpc_dev;
+
+ struct regulator *vdd;
+ struct regulator *switch_vdd;
+ struct regulator *vbus;
+ struct regulator *vconn;
+
+ struct pinctrl *gpio_pinctrl;
+ int gpio_int_n;
+ int gpio_int_n_irq;
+
+ struct workqueue_struct *wq;
+ struct delayed_work bc_lvl_handler;
+
+ atomic_t pm_suspend;
+ atomic_t i2c_busy;
+
+ /* lock for sharing chip states */
+ struct mutex lock;
+
+ /* chip status */
+ enum toggling_mode toggling_mode;
+ enum src_current_status src_current_status;
+ bool intr_togdone;
+ bool intr_bc_lvl;
+ bool intr_comp_chng;
+
+ /* port status */
+ bool pull_up;
+ bool vconn_on;
+ bool vbus_on;
+ bool charge_on;
+ bool vbus_present;
+ enum typec_cc_polarity cc_polarity;
+ enum typec_cc_status cc1;
+ enum typec_cc_status cc2;
+
+ struct usb_controller *uc;
+ struct usb_typec_ctrl *utc;
+ struct power_supply *batt_psy;
+};
+
+static struct fusb302_chip *__fusb302_chip;
+
+#define FUSB302_RESUME_RETRY 10
+#define FUSB302_RESUME_RETRY_SLEEP 50
+static int fusb302_i2c_write(struct fusb302_chip *chip,
+ u8 address, u8 data)
+{
+ int retry_cnt;
+ int ret = 0;
+
+ atomic_set(&chip->i2c_busy, 1);
+ for (retry_cnt = 0; retry_cnt < FUSB302_RESUME_RETRY; retry_cnt++) {
+ if (atomic_read(&chip->pm_suspend)) {
+ pr_err("fusb302_i2c: pm suspend, retry %d/%d\n",
+ retry_cnt + 1, FUSB302_RESUME_RETRY);
+ msleep(FUSB302_RESUME_RETRY_SLEEP);
+ } else {
+ break;
+ }
+ }
+ ret = i2c_smbus_write_byte_data(chip->i2c_client, address, data);
+ if (ret < 0)
+ fusb302_log("cannot write 0x%02x to 0x%02x, ret=%d\n",
+ data, address, ret);
+ else
+ fusb302_log("0x%02x := 0x%02x\n", address, data);
+ atomic_set(&chip->i2c_busy, 0);
+
+ return ret;
+}
+
+static int fusb302_i2c_block_write(struct fusb302_chip *chip, u8 address,
+ u8 length, const u8 *data)
+{
+ int retry_cnt;
+ int ret = 0;
+
+ if (length <= 0)
+ return ret;
+ atomic_set(&chip->i2c_busy, 1);
+ for (retry_cnt = 0; retry_cnt < FUSB302_RESUME_RETRY; retry_cnt++) {
+ if (atomic_read(&chip->pm_suspend)) {
+ pr_err("fusb302_i2c: pm suspend, retry %d/%d\n",
+ retry_cnt + 1, FUSB302_RESUME_RETRY);
+ msleep(FUSB302_RESUME_RETRY_SLEEP);
+ } else {
+ break;
+ }
+ }
+ ret = i2c_smbus_write_i2c_block_data(chip->i2c_client, address,
+ length, data);
+ if (ret < 0) {
+ fusb302_log("cannot block write 0x%02x, len=%d, ret=%d\n",
+ address, length, ret);
+ } else {
+ fusb302_log("write %d bytes to 0x%02x\n", length, address);
+ while (length > 0) {
+ fusb302_log("%02x", *data);
+ data++;
+ length--;
+ }
+ }
+ atomic_set(&chip->i2c_busy, 0);
+
+ return ret;
+}
+
+static int fusb302_i2c_read(struct fusb302_chip *chip,
+ u8 address, u8 *data)
+{
+ int retry_cnt;
+ int ret = 0;
+
+ atomic_set(&chip->i2c_busy, 1);
+ for (retry_cnt = 0; retry_cnt < FUSB302_RESUME_RETRY; retry_cnt++) {
+ if (atomic_read(&chip->pm_suspend)) {
+ pr_err("fusb302_i2c: pm suspend, retry %d/%d\n",
+ retry_cnt + 1, FUSB302_RESUME_RETRY);
+ msleep(FUSB302_RESUME_RETRY_SLEEP);
+ } else {
+ break;
+ }
+ }
+ ret = i2c_smbus_read_byte_data(chip->i2c_client, address);
+ *data = (u8)ret;
+ if (ret < 0)
+ fusb302_log("cannot read %02x, ret=%d\n", address, ret);
+ else
+ fusb302_log("0x%02x: 0x%02x\n", address, ret);
+ atomic_set(&chip->i2c_busy, 0);
+
+ return ret;
+}
+
+static int fusb302_i2c_block_read(struct fusb302_chip *chip, u8 address,
+ u8 length, u8 *data)
+{
+ int retry_cnt;
+ int ret = 0;
+
+ if (length <= 0)
+ return ret;
+ atomic_set(&chip->i2c_busy, 1);
+ for (retry_cnt = 0; retry_cnt < FUSB302_RESUME_RETRY; retry_cnt++) {
+ if (atomic_read(&chip->pm_suspend)) {
+ pr_err("fusb302_i2c: pm suspend, retry %d/%d\n",
+ retry_cnt + 1, FUSB302_RESUME_RETRY);
+ msleep(FUSB302_RESUME_RETRY_SLEEP);
+ } else {
+ break;
+ }
+ }
+ ret = i2c_smbus_read_i2c_block_data(chip->i2c_client, address,
+ length, data);
+ if (ret < 0) {
+ fusb302_log("cannot block read 0x%02x, len=%d, ret=%d\n",
+ address, length, ret);
+ return ret;
+ }
+ if (ret != length) {
+ fusb302_log("only read %d/%d bytes from 0x%02x\n",
+ ret, length, address);
+ return -EIO;
+ }
+ fusb302_log("read %d bytes from 0x%02x\n", length, address);
+ while (length > 0) {
+ fusb302_log("%02x", *data);
+ data++;
+ length--;
+ }
+ atomic_set(&chip->i2c_busy, 0);
+
+ return ret;
+}
+
+static int fusb302_i2c_mask_write(struct fusb302_chip *chip, u8 address,
+ u8 mask, u8 value)
+{
+ int ret = 0;
+ u8 data;
+
+ ret = fusb302_i2c_read(chip, address, &data);
+ if (ret < 0)
+ return ret;
+ data &= ~mask;
+ data |= value;
+ ret = fusb302_i2c_write(chip, address, data);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int fusb302_i2c_set_bits(struct fusb302_chip *chip, u8 address,
+ u8 set_bits)
+{
+ return fusb302_i2c_mask_write(chip, address, 0x00, set_bits);
+}
+
+static int fusb302_i2c_clear_bits(struct fusb302_chip *chip, u8 address,
+ u8 clear_bits)
+{
+ return fusb302_i2c_mask_write(chip, address, clear_bits, 0x00);
+}
+
+static int fusb302_sw_reset(struct fusb302_chip *chip)
+{
+ int ret = 0;
+
+ ret = fusb302_i2c_write(chip, FUSB_REG_RESET,
+ FUSB_REG_RESET_SW_RESET);
+ if (ret < 0)
+ fusb302_log("cannot sw reset the chip, ret=%d\n", ret);
+ else
+ fusb302_log("sw reset\n");
+
+ return ret;
+}
+
+static int fusb302_enable_tx_auto_retries(struct fusb302_chip *chip)
+{
+ int ret = 0;
+
+ ret = fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL3,
+ FUSB_REG_CONTROL3_N_RETRIES_3 |
+ FUSB_REG_CONTROL3_AUTO_RETRY);
+
+ return ret;
+}
+
+/*
+ * initialize interrupt on the chip
+ * - unmasked interrupt: VBUS_OK
+ */
+static int fusb302_init_interrupt(struct fusb302_chip *chip)
+{
+ int ret = 0;
+
+ ret = fusb302_i2c_write(chip, FUSB_REG_MASK,
+ 0xFF & ~FUSB_REG_MASK_VBUSOK);
+ if (ret < 0)
+ return ret;
+ ret = fusb302_i2c_write(chip, FUSB_REG_MASKA, 0xFF);
+ if (ret < 0)
+ return ret;
+ ret = fusb302_i2c_write(chip, FUSB_REG_MASKB, 0xFF);
+ if (ret < 0)
+ return ret;
+ ret = fusb302_i2c_clear_bits(chip, FUSB_REG_CONTROL0,
+ FUSB_REG_CONTROL0_INT_MASK);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+static int fusb302_set_power_mode(struct fusb302_chip *chip, u8 power_mode)
+{
+ int ret = 0;
+
+ ret = fusb302_i2c_write(chip, FUSB_REG_POWER, power_mode);
+
+ return ret;
+}
+
+static int tcpm_init(struct tcpc_dev *dev)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+ u8 data;
+
+ ret = fusb302_sw_reset(chip);
+ if (ret < 0)
+ return ret;
+ ret = fusb302_enable_tx_auto_retries(chip);
+ if (ret < 0)
+ return ret;
+ ret = fusb302_init_interrupt(chip);
+ if (ret < 0)
+ return ret;
+ ret = fusb302_set_power_mode(chip, FUSB_REG_POWER_PWR_ALL);
+ if (ret < 0)
+ return ret;
+ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &data);
+ if (ret < 0)
+ return ret;
+ chip->vbus_present = !!(FUSB_REG_STATUS0 & FUSB_REG_STATUS0_VBUSOK);
+ ret = fusb302_i2c_read(chip, FUSB_REG_DEVICE_ID, &data);
+ if (ret < 0)
+ return ret;
+ fusb302_log("fusb302 device ID: 0x%02x\n", data);
+
+ return ret;
+}
+
+static int tcpm_get_vbus(struct tcpc_dev *dev)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+
+ mutex_lock(&chip->lock);
+ ret = chip->vbus_present ? 1 : 0;
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int fusb302_set_cc_pull(struct fusb302_chip *chip,
+ bool pull_up, bool pull_down)
+{
+ int ret = 0;
+ u8 data = 0x00;
+ u8 mask = FUSB_REG_SWITCHES0_CC1_PU_EN |
+ FUSB_REG_SWITCHES0_CC2_PU_EN |
+ FUSB_REG_SWITCHES0_CC1_PD_EN |
+ FUSB_REG_SWITCHES0_CC2_PD_EN;
+
+ if (pull_up)
+ data |= (chip->cc_polarity == TYPEC_POLARITY_CC1) ?
+ FUSB_REG_SWITCHES0_CC1_PU_EN :
+ FUSB_REG_SWITCHES0_CC2_PU_EN;
+ if (pull_down)
+ data |= FUSB_REG_SWITCHES0_CC1_PD_EN |
+ FUSB_REG_SWITCHES0_CC2_PD_EN;
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES0,
+ mask, data);
+ if (ret < 0)
+ return ret;
+ chip->pull_up = pull_up;
+
+ return ret;
+}
+
+static int fusb302_set_src_current(struct fusb302_chip *chip,
+ enum src_current_status status)
+{
+ int ret = 0;
+
+ chip->src_current_status = status;
+ switch (status) {
+ case SRC_CURRENT_DEFAULT:
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_CONTROL0,
+ FUSB_REG_CONTROL0_HOST_CUR_MASK,
+ FUSB_REG_CONTROL0_HOST_CUR_DEF);
+ break;
+ case SRC_CURRENT_MEDIUM:
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_CONTROL0,
+ FUSB_REG_CONTROL0_HOST_CUR_MASK,
+ FUSB_REG_CONTROL0_HOST_CUR_MED);
+ break;
+ case SRC_CURRENT_HIGH:
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_CONTROL0,
+ FUSB_REG_CONTROL0_HOST_CUR_MASK,
+ FUSB_REG_CONTROL0_HOST_CUR_HIGH);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int fusb302_set_toggling(struct fusb302_chip *chip,
+ enum toggling_mode mode)
+{
+ int ret = 0;
+
+ /* first disable toggling */
+ ret = fusb302_i2c_clear_bits(chip, FUSB_REG_CONTROL2,
+ FUSB_REG_CONTROL2_TOGGLE);
+ if (ret < 0)
+ return ret;
+ /* mask interrupts for SRC or SNK */
+ ret = fusb302_i2c_set_bits(chip, FUSB_REG_MASK,
+ FUSB_REG_MASK_BC_LVL |
+ FUSB_REG_MASK_COMP_CHNG);
+ if (ret < 0)
+ return ret;
+ chip->intr_bc_lvl = false;
+ chip->intr_comp_chng = false;
+ /* configure toggling mode: none/snk/src/drp */
+ switch (mode) {
+ case TOGGLINE_MODE_OFF:
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_CONTROL2,
+ FUSB_REG_CONTROL2_MODE_MASK,
+ FUSB_REG_CONTROL2_MODE_NONE);
+ if (ret < 0)
+ return ret;
+ break;
+ case TOGGLING_MODE_SNK:
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_CONTROL2,
+ FUSB_REG_CONTROL2_MODE_MASK,
+ FUSB_REG_CONTROL2_MODE_UFP);
+ if (ret < 0)
+ return ret;
+ break;
+ case TOGGLING_MODE_SRC:
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_CONTROL2,
+ FUSB_REG_CONTROL2_MODE_MASK,
+ FUSB_REG_CONTROL2_MODE_DFP);
+ if (ret < 0)
+ return ret;
+ break;
+ case TOGGLING_MODE_DRP:
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_CONTROL2,
+ FUSB_REG_CONTROL2_MODE_MASK,
+ FUSB_REG_CONTROL2_MODE_DRP);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ if (mode == TOGGLINE_MODE_OFF) {
+ /* mask TOGDONE interrupt */
+ ret = fusb302_i2c_set_bits(chip, FUSB_REG_MASKA,
+ FUSB_REG_MASKA_TOGDONE);
+ if (ret < 0)
+ return ret;
+ chip->intr_togdone = false;
+ } else {
+ /* unmask TOGDONE interrupt */
+ ret = fusb302_i2c_clear_bits(chip, FUSB_REG_MASKA,
+ FUSB_REG_MASKA_TOGDONE);
+ if (ret < 0)
+ return ret;
+ chip->intr_togdone = true;
+ /* start toggling */
+ ret = fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL2,
+ FUSB_REG_CONTROL2_TOGGLE);
+ if (ret < 0)
+ return ret;
+ /* during toggling, consider cc as Open */
+ chip->cc1 = TYPEC_CC_OPEN;
+ chip->cc2 = TYPEC_CC_OPEN;
+ }
+ chip->toggling_mode = mode;
+
+ return ret;
+}
+
+static const char * const typec_cc_status_name[] = {
+ [TYPEC_CC_OPEN] = "Open",
+ [TYPEC_CC_RA] = "Ra",
+ [TYPEC_CC_RD] = "Rd",
+ [TYPEC_CC_RP_DEF] = "Rp-def",
+ [TYPEC_CC_RP_1_5] = "Rp-1.5",
+ [TYPEC_CC_RP_3_0] = "Rp-3.0",
+};
+
+static const enum src_current_status cc_src_current[] = {
+ [TYPEC_CC_OPEN] = SRC_CURRENT_DEFAULT,
+ [TYPEC_CC_RA] = SRC_CURRENT_DEFAULT,
+ [TYPEC_CC_RD] = SRC_CURRENT_DEFAULT,
+ [TYPEC_CC_RP_DEF] = SRC_CURRENT_DEFAULT,
+ [TYPEC_CC_RP_1_5] = SRC_CURRENT_MEDIUM,
+ [TYPEC_CC_RP_3_0] = SRC_CURRENT_HIGH,
+};
+
+static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+ bool pull_up, pull_down;
+ u8 rd_mda;
+
+ mutex_lock(&chip->lock);
+ switch (cc) {
+ case TYPEC_CC_OPEN:
+ pull_up = false;
+ pull_down = false;
+ break;
+ case TYPEC_CC_RD:
+ pull_up = false;
+ pull_down = true;
+ break;
+ case TYPEC_CC_RP_DEF:
+ case TYPEC_CC_RP_1_5:
+ case TYPEC_CC_RP_3_0:
+ pull_up = true;
+ pull_down = false;
+ break;
+ default:
+ fusb302_log("unsupported cc value %s\n",
+ typec_cc_status_name[cc]);
+ ret = -EINVAL;
+ goto done;
+ }
+ ret = fusb302_set_toggling(chip, TOGGLINE_MODE_OFF);
+ if (ret < 0) {
+ fusb302_log("cannot stop toggling, ret=%d\n", ret);
+ goto done;
+ }
+ ret = fusb302_set_cc_pull(chip, pull_up, pull_down);
+ if (ret < 0) {
+ fusb302_log("cannot set cc pulling up %s, down %s, ret = %d\n",
+ pull_up ? "True" : "False",
+ pull_down ? "True" : "False",
+ ret);
+ goto done;
+ }
+ /* reset the cc status */
+ chip->cc1 = TYPEC_CC_OPEN;
+ chip->cc2 = TYPEC_CC_OPEN;
+ /* report OPEN status back to TCPM when not in PR_SWAP path*/
+ if (!IsPRSwap)
+ tcpm_cc_change(chip->tcpm_port);
+ /* adjust current for SRC */
+ if (pull_up) {
+ ret = fusb302_set_src_current(chip, cc_src_current[cc]);
+ if (ret < 0) {
+ fusb302_log("cannot set src current %s, ret=%d\n",
+ typec_cc_status_name[cc], ret);
+ goto done;
+ }
+ }
+ /* enable/disable interrupts, BC_LVL for SNK and COMP_CHNG for SRC */
+ if (pull_up) {
+ rd_mda = rd_mda_value[cc_src_current[cc]];
+ ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, rd_mda);
+ if (ret < 0) {
+ fusb302_log("cannot set SRC measure value, ret=%d\n",
+ ret);
+ goto done;
+ }
+
+ /* WAR: Fairchild chip cannot send control packets when
+ * TOGDONE interrupt is enabled. Since, TOGDONE is not
+ * needed during PR_SWAP as CC pin is manupulated, do
+ * enable TOGDONE interrupt to unblock chip on sending
+ * the control messages such as PS_RDY.
+ */
+ if (!IsPRSwap) {
+ ret = fusb302_set_toggling(chip, TOGGLING_MODE_SRC);
+ if (ret < 0) {
+ fusb302_log("cannot set toggling to SRC mode,");
+ fusb302_log("ret=%d\n", ret);
+ goto done;
+ }
+ }
+
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
+ FUSB_REG_MASK_BC_LVL |
+ FUSB_REG_MASK_COMP_CHNG,
+ FUSB_REG_MASK_BC_LVL);
+ if (ret < 0) {
+ fusb302_log("cannot set SRC interrupt, ret=%d\n",
+ ret);
+ goto done;
+ }
+ chip->intr_bc_lvl = false;
+ chip->intr_comp_chng = true;
+ }
+ if (pull_down) {
+ if (!IsPRSwap) {
+ ret = fusb302_set_toggling(chip, TOGGLING_MODE_SNK);
+ if (ret < 0) {
+ fusb302_log("cannot set toggling to SNK mode,");
+ fusb302_log("ret=%d\n", ret);
+ goto done;
+ }
+ }
+
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_MASK,
+ FUSB_REG_MASK_BC_LVL |
+ FUSB_REG_MASK_COMP_CHNG,
+ FUSB_REG_MASK_COMP_CHNG);
+ if (ret < 0) {
+ fusb302_log("cannot set SNK interrupt, ret=%d\n",
+ ret);
+ goto done;
+ }
+ chip->intr_bc_lvl = true;
+ chip->intr_comp_chng = false;
+ }
+ fusb302_log("cc := %s\n", typec_cc_status_name[cc]);
+done:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int tcpm_get_cc(struct tcpc_dev *dev, enum typec_cc_status *cc1,
+ enum typec_cc_status *cc2)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+
+ mutex_lock(&chip->lock);
+ *cc1 = chip->cc1;
+ *cc2 = chip->cc2;
+ fusb302_log("cc1=%s, cc2=%s\n", typec_cc_status_name[*cc1],
+ typec_cc_status_name[*cc2]);
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+
+static int tcpm_set_polarity(struct tcpc_dev *dev,
+ enum typec_cc_polarity polarity)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+ struct pinctrl_state *set_state;
+
+ mutex_lock(&chip->lock);
+
+ fusb302_log("polarity := %d\n", polarity);
+
+ if (!chip->gpio_pinctrl) {
+ fusb302_log("gpio_pinctrl is not avalible\n");
+ ret = -EFAULT;
+ goto done;
+ }
+
+ set_state = pinctrl_lookup_state(chip->gpio_pinctrl,
+ polarity ? "usb3_switch_sel_1" :
+ "usb3_switch_sel_0");
+ if (IS_ERR(set_state)) {
+ ret = PTR_ERR(set_state);
+ fusb302_log(
+ "cannot get fusb302 gpio_pinctrl usb3_switch_sel_%d state, ret=%d\n",
+ polarity,
+ ret);
+ goto done;
+ }
+
+ ret = pinctrl_select_state(chip->gpio_pinctrl, set_state);
+ if (ret < 0)
+ fusb302_log("cannot select state, ret=%d\n", ret);
+done:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int tcpm_set_vconn(struct tcpc_dev *dev, bool on)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+ struct pinctrl_state *set_state;
+ u8 switches0_data = 0x00;
+ u8 switches0_mask = FUSB_REG_SWITCHES0_VCONN_CC1 |
+ FUSB_REG_SWITCHES0_VCONN_CC2;
+
+ mutex_lock(&chip->lock);
+ if (chip->vconn_on == on) {
+ fusb302_log("vconn is already %s\n", on ? "On" : "Off");
+ goto done;
+ }
+
+ if (!chip->vconn) {
+ chip->vconn = devm_regulator_get(chip->dev, "V_USB_boost");
+ if (IS_ERR(chip->vconn)) {
+ fusb302_log("still unable to get vconn regulator\n");
+ ret = -ENODEV;
+ goto done;
+ }
+ }
+
+ if (on) {
+ ret = regulator_enable(chip->vconn);
+ if (ret) {
+ fusb302_log("Unable to enable vconn regulator\n");
+ goto done;
+ }
+ switches0_data = (chip->cc_polarity == TYPEC_POLARITY_CC1) ?
+ FUSB_REG_SWITCHES0_VCONN_CC2 :
+ FUSB_REG_SWITCHES0_VCONN_CC1;
+ } else {
+ ret = regulator_disable(chip->vconn);
+ if (ret) {
+ fusb302_log("Unable to disable vconn regulator\n");
+ goto done;
+ }
+ }
+
+ if (chip->gpio_pinctrl) {
+ set_state = pinctrl_lookup_state(chip->gpio_pinctrl,
+ on ? "vconn_enable" :
+ "vconn_disable");
+ if (IS_ERR(set_state)) {
+ fusb302_log("cannot get pinctrl vconn_control state\n");
+ ret = -ENODEV;
+ goto done;
+ }
+ pinctrl_select_state(chip->gpio_pinctrl, set_state);
+ }
+
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES0,
+ switches0_mask, switches0_data);
+ if (ret < 0)
+ goto done;
+ chip->vconn_on = on;
+ fusb302_log("vconn := %s\n", on ? "On" : "Off");
+done:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int tcpm_set_vbus(struct tcpc_dev *dev, bool on, bool charge)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+
+ mutex_lock(&chip->lock);
+
+ if (chip->vbus_on == on) {
+ fusb302_log("vbus is already %s\n", on ? "On" : "Off");
+ } else {
+ if (chip->uc && chip->uc->pd_vbus_ctrl) {
+ ret = chip->uc->pd_vbus_ctrl(on, true);
+ } else {
+ fusb302_log("no pd_vbus_ctrl\n");
+ if (!chip->vbus) {
+ chip->vbus = devm_regulator_get(chip->dev,
+ "vbus");
+ if (IS_ERR(chip->vbus)) {
+ ret = PTR_ERR(chip->vbus);
+ fusb302_log("cannot get vbus, ret=%d\n",
+ ret);
+ }
+ }
+
+ if (chip->vbus) {
+ if (on)
+ ret = regulator_enable(chip->vbus);
+ else
+ ret = regulator_disable(chip->vbus);
+ }
+ }
+ if (ret < 0) {
+ fusb302_log("cannot %s vbus regulator, ret=%d\n",
+ on ? "enable" : "disable", ret);
+ goto done;
+ }
+ chip->vbus_on = on;
+ fusb302_log("vbus := %s\n", on ? "On" : "Off");
+ }
+ if (chip->charge_on == charge)
+ fusb302_log("charge is already %s\n", charge ? "On" : "Off");
+ else
+ chip->charge_on = charge;
+
+done:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+enum usb_typec_current {
+ USB_TYPEC_CURRENT_NONE = 0,
+ USB_TYPEC_CURRENT_DEFAULT,
+ USB_TYPEC_CURRENT_1_5_A,
+ USB_TYPEC_CURRENT_3_0_A,
+};
+
+static int tcpm_set_current_limit(struct tcpc_dev *dev, u32 max_ma, u32 mv)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+ int dummy_val;
+ struct htc_pd_data pd_data;
+ enum usb_typec_current sink_current;
+
+ mutex_lock(&chip->lock);
+
+ fusb302_log("current limit: %d ma, %d mv\n",
+ max_ma, mv);
+
+ if ((mv == 5000) && (max_ma == 0 || max_ma == 1500 || max_ma == 3000)) {
+ if (max_ma == 0)
+ sink_current = USB_TYPEC_CURRENT_DEFAULT;
+ else if (max_ma == 1500)
+ sink_current = USB_TYPEC_CURRENT_1_5_A;
+ else
+ sink_current = USB_TYPEC_CURRENT_3_0_A;
+ if (chip->utc)
+ chip->utc->sink_current = sink_current;
+
+ if (!chip->batt_psy) {
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (IS_ERR(chip->batt_psy)) {
+ ret = PTR_ERR(chip->batt_psy);
+ fusb302_log(
+ "cannot get battery power supply, ret=%d\n",
+ ret);
+ }
+ }
+
+ if (chip->batt_psy) {
+ ret = chip->batt_psy->set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_TYPEC_SINK_CURRENT,
+ (const union power_supply_propval *)
+ &sink_current);
+ if (ret < 0) {
+ fusb302_log(
+ "cannot set battery sink current, ret=%d\n",
+ ret);
+ }
+ }
+ }
+
+ pd_data.pd_list[0][0] = mv;
+ pd_data.pd_list[0][1] = max_ma;
+
+ htc_battery_pd_charger_support(1, pd_data, &dummy_val);
+
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+
+static int fusb302_pd_tx_flush(struct fusb302_chip *chip)
+{
+ return fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL0,
+ FUSB_REG_CONTROL0_TX_FLUSH);
+}
+
+static int fusb302_pd_rx_flush(struct fusb302_chip *chip)
+{
+ return fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL1,
+ FUSB_REG_CONTROL1_RX_FLUSH);
+}
+
+static int fusb302_pd_set_auto_goodcrc(struct fusb302_chip *chip, bool on)
+{
+ if (on)
+ return fusb302_i2c_set_bits(chip, FUSB_REG_SWITCHES1,
+ FUSB_REG_SWITCHES1_AUTO_GCRC);
+ return fusb302_i2c_clear_bits(chip, FUSB_REG_SWITCHES1,
+ FUSB_REG_SWITCHES1_AUTO_GCRC);
+}
+
+static int fusb302_pd_set_interrupts(struct fusb302_chip *chip, bool on)
+{
+ int ret = 0;
+ u8 mask_interrupts = FUSB_REG_MASK_COLLISION;
+ u8 maska_interrupts = FUSB_REG_MASKA_RETRYFAIL |
+ FUSB_REG_MASKA_HARDSENT |
+ FUSB_REG_MASKA_TX_SUCCESS |
+ FUSB_REG_MASKA_HARDRESET;
+ u8 maskb_interrupts = FUSB_REG_MASKB_GCRCSENT;
+
+ ret = on ?
+ fusb302_i2c_clear_bits(chip, FUSB_REG_MASK, mask_interrupts) :
+ fusb302_i2c_set_bits(chip, FUSB_REG_MASK, mask_interrupts);
+ if (ret < 0)
+ return ret;
+ ret = on ?
+ fusb302_i2c_clear_bits(chip, FUSB_REG_MASKA, maska_interrupts) :
+ fusb302_i2c_set_bits(chip, FUSB_REG_MASKA, maska_interrupts);
+ if (ret < 0)
+ return ret;
+ ret = on ?
+ fusb302_i2c_clear_bits(chip, FUSB_REG_MASKB, maskb_interrupts) :
+ fusb302_i2c_set_bits(chip, FUSB_REG_MASKB, maskb_interrupts);
+ return ret;
+}
+
+/*
+ * requires chip lock: chip->lock;
+ */
+#define NOTIFY_SOURCE_WAR_DELAY_TIMESTAMP_MS 6000
+static int fusb302_notify_uc_data_role_locked(struct fusb302_chip *chip,
+ enum typec_data_role value)
+{
+ u64 ts_msec = local_clock()/1000000;
+
+ fusb302_log("notify_uc_data_role of %d\n", value);
+
+ /*
+ * workaround: for some reason, calling notify_attached_source
+ * at an early boot stage (kernel time < ~3 secs) will leads to
+ * the usb to fail to reattach. As a workaround, fail the call
+ * until NOTIFY_SOURCE_WAR_DELAY_TIMESTAMP_MS after kernel start
+ * booting.
+ */
+ if (ts_msec < NOTIFY_SOURCE_WAR_DELAY_TIMESTAMP_MS) {
+ fusb302_log("WAR: do not notify_attached_source too soon\n");
+ return -EAGAIN;
+ }
+
+ if (chip->uc != NULL && chip->uc->notify_attached_source != NULL) {
+ chip->uc->notify_attached_source(chip->uc, value);
+ return 0;
+ }
+
+ fusb302_log("notify uc data role error\n");
+ return -ENODEV;
+}
+
+static int tcpm_set_pd_rx(struct tcpc_dev *dev, bool on)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+
+ mutex_lock(&chip->lock);
+ ret = fusb302_pd_rx_flush(chip);
+ if (ret < 0) {
+ fusb302_log("cannot flush pd rx buffer, ret=%d\n", ret);
+ goto done;
+ }
+ ret = fusb302_pd_tx_flush(chip);
+ if (ret < 0) {
+ fusb302_log("cannot flush pd tx buffer, ret=%d\n", ret);
+ goto done;
+ }
+ ret = fusb302_pd_set_auto_goodcrc(chip, on);
+ if (ret < 0) {
+ fusb302_log("cannot turn %s auto GCRC, ret=%d\n",
+ on ? "on" : "off", ret);
+ goto done;
+ }
+ ret = fusb302_pd_set_interrupts(chip, on);
+ if (ret < 0) {
+ fusb302_log("cannot turn %s pd interrupts, ret=%d\n",
+ on ? "on" : "off", ret);
+ goto done;
+ }
+ fusb302_log("pd := %s\n", on ? "on" : "off");
+done:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static const char * const typec_role_name[] = {
+ [TYPEC_SINK] = "Sink",
+ [TYPEC_SOURCE] = "Source",
+};
+
+static const char * const typec_data_role_name[] = {
+ [TYPEC_DEVICE] = "Device",
+ [TYPEC_HOST] = "Host",
+};
+
+static int tcpm_set_roles(struct tcpc_dev *dev, bool attached,
+ enum typec_role pwr, enum typec_data_role data)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+ u8 switches1_mask = FUSB_REG_SWITCHES1_POWERROLE |
+ FUSB_REG_SWITCHES1_DATAROLE;
+ u8 switches1_data = 0x00;
+
+ if (!attached)
+ data = TYPEC_DEVICE;
+
+ mutex_lock(&chip->lock);
+
+ ret = fusb302_notify_uc_data_role_locked(chip, data);
+ if (ret < 0)
+ goto done;
+
+ if (pwr == TYPEC_SOURCE)
+ switches1_data |= FUSB_REG_SWITCHES1_POWERROLE;
+ if (data == TYPEC_HOST)
+ switches1_data |= FUSB_REG_SWITCHES1_DATAROLE;
+
+ PolicyIsSource = (pwr == TYPEC_SOURCE) ? true : false;
+ PolicyIsDFP = (data == TYPEC_HOST) ? true : false;
+
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES1,
+ switches1_mask, switches1_data);
+ if (ret < 0) {
+ fusb302_log("unable to set pd header %s, %s, ret=%d\n",
+ typec_role_name[pwr], typec_data_role_name[data],
+ ret);
+ goto done;
+ }
+ fusb302_log("pd header := %s, %s\n", typec_role_name[pwr],
+ typec_data_role_name[data]);
+done:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int tcpm_start_drp_toggling(struct tcpc_dev *dev,
+ enum typec_cc_status cc)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+
+ mutex_lock(&chip->lock);
+ ret = fusb302_set_src_current(chip, cc_src_current[cc]);
+ if (ret < 0) {
+ fusb302_log("unable to set src current %s, ret=%d\n",
+ typec_cc_status_name[cc], ret);
+ goto done;
+ }
+ ret = fusb302_set_toggling(chip, TOGGLING_MODE_DRP);
+ if (ret < 0) {
+ fusb302_log("unable to start drp toggling, ret=%d\n", ret);
+ goto done;
+ }
+ fusb302_log("start drp toggling\n");
+done:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int fusb302_pd_send_message(struct fusb302_chip *chip,
+ const struct pd_message *msg)
+{
+ int ret = 0;
+ u8 buf[40];
+ u8 pos = 0;
+ int len;
+
+ /* SOP tokens */
+ buf[pos++] = FUSB302_TKN_SYNC1;
+ buf[pos++] = FUSB302_TKN_SYNC1;
+ buf[pos++] = FUSB302_TKN_SYNC1;
+ buf[pos++] = FUSB302_TKN_SYNC2;
+
+ len = pd_header_cnt(msg->header) * 4;
+ /* plug 2 for header */
+ len += 2;
+ if (len > 0x1F) {
+ fusb302_log("PD message too long %d (incl. header)\n", len);
+ return -EINVAL;
+ }
+ /* packsym tells the FUSB302 chip that the next X bytes are payload */
+ buf[pos++] = FUSB302_TKN_PACKSYM | (len & 0x1F);
+ buf[pos++] = msg->header & 0xFF;
+ buf[pos++] = (msg->header >> 8) & 0xFF;
+
+ len -= 2;
+ memcpy(&buf[pos], msg->payload, len);
+ pos += len;
+
+ /* CRC */
+ buf[pos++] = FUSB302_TKN_JAMCRC;
+ /* EOP */
+ buf[pos++] = FUSB302_TKN_EOP;
+ /* turn tx off after sending message */
+ buf[pos++] = FUSB302_TKN_TXOFF;
+ /* start transmission */
+ buf[pos++] = FUSB302_TKN_TXON;
+
+ ret = fusb302_i2c_block_write(chip, FUSB_REG_FIFOS, pos, buf);
+ if (ret < 0)
+ return ret;
+ fusb302_log("sending PD message header: %x\n", msg->header);
+ fusb302_log("sending PD message len: %d\n", len);
+
+ return ret;
+}
+
+static int fusb302_pd_send_hardreset(struct fusb302_chip *chip)
+{
+ return fusb302_i2c_set_bits(chip, FUSB_REG_CONTROL3,
+ FUSB_REG_CONTROL3_SEND_HARDRESET);
+}
+
+static const char * const transmit_type_name[] = {
+ [TCPC_TX_SOP] = "SOP",
+ [TCPC_TX_SOP_PRIME] = "SOP'",
+ [TCPC_TX_SOP_PRIME_PRIME] = "SOP''",
+ [TCPC_TX_SOP_DEBUG_PRIME] = "DEBUG'",
+ [TCPC_TX_SOP_DEBUG_PRIME_PRIME] = "DEBUG''",
+ [TCPC_TX_HARD_RESET] = "HARD_RESET",
+ [TCPC_TX_CABLE_RESET] = "CABLE_RESET",
+ [TCPC_TX_BIST_MODE_2] = "BIST_MODE_2",
+};
+
+static int tcpm_pd_transmit(struct tcpc_dev *dev, enum tcpm_transmit_type type,
+ const struct pd_message *msg)
+{
+ struct fusb302_chip *chip = container_of(dev, struct fusb302_chip,
+ tcpc_dev);
+ int ret = 0;
+
+ mutex_lock(&chip->lock);
+ switch (type) {
+ case TCPC_TX_SOP:
+ /*
+ * add 10ms delay because of source cap send before partner
+ * enter wait cap state.
+ */
+ if (pd_header_type_le(msg->header) == PD_DATA_SOURCE_CAP)
+ msleep(10);
+ ret = fusb302_pd_send_message(chip, msg);
+ if (ret < 0)
+ fusb302_log("cannot send PD message, ret=%d\n", ret);
+ break;
+ case TCPC_TX_HARD_RESET:
+ ret = fusb302_pd_send_hardreset(chip);
+ if (ret < 0)
+ fusb302_log("cannot send hardreset, ret=%d\n", ret);
+ break;
+ default:
+ fusb302_log("type %s not supported\n",
+ transmit_type_name[type]);
+ ret = -EINVAL;
+ }
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static enum typec_cc_status fusb302_bc_lvl_to_cc(u8 bc_lvl)
+{
+ if (bc_lvl == FUSB_REG_STATUS0_BC_LVL_1230_MAX)
+ return TYPEC_CC_RP_3_0;
+ if (bc_lvl == FUSB_REG_STATUS0_BC_LVL_600_1230)
+ return TYPEC_CC_RP_1_5;
+ if (bc_lvl == FUSB_REG_STATUS0_BC_LVL_200_600)
+ return TYPEC_CC_RP_DEF;
+ return TYPEC_CC_OPEN;
+}
+
+static void fusb302_bc_lvl_handler_work(struct work_struct *work)
+{
+ struct fusb302_chip *chip = container_of(work, struct fusb302_chip,
+ bc_lvl_handler.work);
+ int ret = 0;
+ u8 status0;
+ u8 bc_lvl;
+ enum typec_cc_status cc_status;
+
+ mutex_lock(&chip->lock);
+ if (!chip->intr_bc_lvl) {
+ fusb302_log("BC_LVL interrupt is turned off, abort\n");
+ goto done;
+ }
+ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0);
+ if (ret < 0)
+ goto done;
+ fusb302_log("BC_LVL handler, status0=0x%02x\n", status0);
+ if (status0 & FUSB_REG_STATUS0_ACTIVITY) {
+ fusb302_log("CC activities detected, delay handling\n");
+ mod_delayed_work(chip->wq, &chip->bc_lvl_handler,
+ msecs_to_jiffies(T_BC_LVL_DEBOUNCE_DELAY_MS));
+ goto done;
+ }
+ bc_lvl = status0 & FUSB_REG_STATUS0_BC_LVL_MASK;
+ cc_status = fusb302_bc_lvl_to_cc(bc_lvl);
+ if (chip->cc_polarity == TYPEC_POLARITY_CC1) {
+ if (chip->cc1 != cc_status) {
+ fusb302_log("cc1: %s -> %s\n",
+ typec_cc_status_name[chip->cc1],
+ typec_cc_status_name[cc_status]);
+ chip->cc1 = cc_status;
+ tcpm_cc_change(chip->tcpm_port);
+ }
+ } else {
+ if (chip->cc2 != cc_status) {
+ fusb302_log("cc2: %s -> %s\n",
+ typec_cc_status_name[chip->cc2],
+ typec_cc_status_name[cc_status]);
+ chip->cc2 = cc_status;
+ tcpm_cc_change(chip->tcpm_port);
+ }
+ }
+
+done:
+ mutex_unlock(&chip->lock);
+}
+
+#define PDO_FIXED_FLAGS \
+ (PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP | PDO_FIXED_USB_COMM)
+
+static const u32 src_pdo[] = {
+ PDO_FIXED(5000, 900, PDO_FIXED_FLAGS),
+};
+
+static const u32 snk_pdo[] = {
+ PDO_FIXED(5000, 3000, PDO_FIXED_FLAGS),
+ PDO_FIXED(9000, 2000, PDO_FIXED_FLAGS),
+ PDO_BATT(4000, 10000, 18000),
+};
+
+static const struct tcpc_config fusb302_tcpc_config = {
+ .src_pdo = src_pdo,
+ .nr_src_pdo = ARRAY_SIZE(src_pdo),
+ .snk_pdo = snk_pdo,
+ .nr_snk_pdo = ARRAY_SIZE(snk_pdo),
+ .max_snk_mv = 9000,
+ .max_snk_ma = 3000,
+ .max_snk_mw = 27000,
+ .operating_snk_mw = 2500,
+ .type = TYPEC_PORT_DRP,
+ .default_role = TYPEC_SINK,
+ .alt_modes = NULL,
+};
+
+static void init_tcpc_dev(struct tcpc_dev *fusb302_tcpc_dev)
+{
+ fusb302_tcpc_dev->config = &fusb302_tcpc_config;
+ fusb302_tcpc_dev->init = tcpm_init;
+ fusb302_tcpc_dev->get_vbus = tcpm_get_vbus;
+ fusb302_tcpc_dev->set_cc = tcpm_set_cc;
+ fusb302_tcpc_dev->get_cc = tcpm_get_cc;
+ fusb302_tcpc_dev->set_polarity = tcpm_set_polarity;
+ fusb302_tcpc_dev->set_vconn = tcpm_set_vconn;
+ fusb302_tcpc_dev->set_vbus = tcpm_set_vbus;
+ fusb302_tcpc_dev->set_current_limit = tcpm_set_current_limit;
+ fusb302_tcpc_dev->set_pd_rx = tcpm_set_pd_rx;
+ fusb302_tcpc_dev->set_roles = tcpm_set_roles;
+ fusb302_tcpc_dev->start_drp_toggling = tcpm_start_drp_toggling;
+ fusb302_tcpc_dev->pd_transmit = tcpm_pd_transmit;
+ fusb302_tcpc_dev->mux = NULL;
+}
+
+#define VDD_3P3_VOL_MIN 3000000 /* uV */
+#define VDD_3P3_VOL_MAX 3300000 /* uV */
+#define VDD_OPTIMUM_MODE 40
+#define SWITCH_VDD_1P8_VOL_MIN 1800000 /* uV */
+#define SWITCH_VDD_1P8_VOL_MAX 1800000 /* uV */
+#define SWITCH_VDD_OPTIMUM_MODE 40
+
+static int init_regulators(struct fusb302_chip *chip)
+{
+ int ret = 0;
+
+ chip->vdd = devm_regulator_get(chip->dev, "vdd");
+ if (IS_ERR(chip->vdd)) {
+ ret = PTR_ERR(chip->vdd);
+ fusb302_log("cannot get vdd, ret=%d\n", ret);
+ return ret;
+ }
+ ret = regulator_set_voltage(chip->vdd,
+ VDD_3P3_VOL_MIN, VDD_3P3_VOL_MAX);
+ if (ret < 0) {
+ fusb302_log("cannot set vdd voltage, ret=%d\n", ret);
+ return ret;
+ }
+ ret = regulator_set_optimum_mode(chip->vdd, VDD_OPTIMUM_MODE);
+ if (ret < 0) {
+ fusb302_log("cannot set vdd optimum, ret=%d\n", ret);
+ return ret;
+ }
+ ret = regulator_enable(chip->vdd);
+ if (ret < 0) {
+ fusb302_log("cannot enable vdd, ret=%d\n", ret);
+ regulator_set_optimum_mode(chip->vdd, 0);
+ return ret;
+ }
+
+ chip->switch_vdd = devm_regulator_get(chip->dev, "switch_vdd");
+ if (IS_ERR(chip->switch_vdd)) {
+ ret = PTR_ERR(chip->switch_vdd);
+ fusb302_log("cannot get switch vdd, ret=%d\n", ret);
+ goto disable_vdd;
+ }
+ ret = regulator_set_voltage(chip->switch_vdd, SWITCH_VDD_1P8_VOL_MIN,
+ SWITCH_VDD_1P8_VOL_MAX);
+ if (ret < 0) {
+ fusb302_log("cannot set switch vdd voltage, ret=%d\n", ret);
+ goto disable_vdd;
+ }
+ ret = regulator_set_optimum_mode(chip->switch_vdd,
+ SWITCH_VDD_OPTIMUM_MODE);
+ if (ret < 0) {
+ fusb302_log("cannot set switch vdd optimum, ret=%d\n", ret);
+ goto disable_vdd;
+ }
+ ret = regulator_enable(chip->switch_vdd);
+ if (ret < 0) {
+ fusb302_log("cannot enable switch vdd, ret=%d\n", ret);
+ regulator_set_optimum_mode(chip->switch_vdd, 0);
+ goto disable_vdd;
+ }
+
+ /* If vbus regulator is not ready, skip here and get it when used.*/
+ chip->vbus = devm_regulator_get(chip->dev, "vbus");
+ if (IS_ERR(chip->vbus)) {
+ fusb302_log("cannot get vbus, ret=%d\n", ret);
+ chip->vbus = NULL;
+ }
+ return ret;
+
+disable_vdd:
+ regulator_set_optimum_mode(chip->vdd, 0);
+ regulator_disable(chip->vdd);
+ return ret;
+}
+
+static const char * const cc_polarity_name[] = {
+ [TYPEC_POLARITY_CC1] = "Polarity_CC1",
+ [TYPEC_POLARITY_CC2] = "Polarity_CC2",
+};
+
+static int fusb302_set_cc_polarity(struct fusb302_chip *chip,
+ enum typec_cc_polarity cc_polarity)
+{
+ int ret = 0;
+ u8 switches0_mask = FUSB_REG_SWITCHES0_CC1_PU_EN |
+ FUSB_REG_SWITCHES0_CC2_PU_EN |
+ FUSB_REG_SWITCHES0_VCONN_CC1 |
+ FUSB_REG_SWITCHES0_VCONN_CC2 |
+ FUSB_REG_SWITCHES0_MEAS_CC1 |
+ FUSB_REG_SWITCHES0_MEAS_CC2;
+ u8 switches0_data = 0x00;
+ u8 switches1_mask = FUSB_REG_SWITCHES1_TXCC1_EN |
+ FUSB_REG_SWITCHES1_TXCC2_EN;
+ u8 switches1_data = 0x00;
+
+ if (cc_polarity == TYPEC_POLARITY_CC1) {
+ switches0_data = FUSB_REG_SWITCHES0_MEAS_CC1;
+ if (chip->vconn_on)
+ switches0_data |= FUSB_REG_SWITCHES0_VCONN_CC2;
+ if (chip->pull_up)
+ switches0_data |= FUSB_REG_SWITCHES0_CC1_PU_EN;
+ switches1_data = FUSB_REG_SWITCHES1_TXCC1_EN;
+ } else {
+ switches0_data = FUSB_REG_SWITCHES0_MEAS_CC2;
+ if (chip->vconn_on)
+ switches0_data |= FUSB_REG_SWITCHES0_VCONN_CC1;
+ if (chip->pull_up)
+ switches0_data |= FUSB_REG_SWITCHES0_CC2_PU_EN;
+ switches1_data = FUSB_REG_SWITCHES1_TXCC2_EN;
+ }
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES0,
+ switches0_mask, switches0_data);
+ if (ret < 0)
+ return ret;
+ ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES1,
+ switches1_mask, switches1_data);
+ if (ret < 0)
+ return ret;
+ chip->cc_polarity = cc_polarity;
+
+ return ret;
+}
+
+static int fusb302_handle_togdone_snk(struct fusb302_chip *chip,
+ u8 togdone_result)
+{
+ int ret = 0;
+ u8 status0;
+ u8 bc_lvl;
+ enum typec_cc_polarity cc_polarity;
+ enum typec_cc_status cc_status_active, cc1, cc2;
+
+ /* set pull_up, pull_down */
+ ret = fusb302_set_cc_pull(chip, false, true);
+ if (ret < 0) {
+ fusb302_log("cannot set cc to pull down, ret=%d\n", ret);
+ return ret;
+ }
+ /* set polarity */
+ cc_polarity = (togdone_result == FUSB_REG_STATUS1A_TOGSS_SNK1) ?
+ TYPEC_POLARITY_CC1 : TYPEC_POLARITY_CC2;
+ ret = fusb302_set_cc_polarity(chip, cc_polarity);
+ if (ret < 0) {
+ fusb302_log("cannot set cc polarity %s, ret=%d\n",
+ cc_polarity_name[cc_polarity], ret);
+ return ret;
+ }
+ /* fusb302_set_cc_polarity() has set the correct measure block */
+ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0);
+ if (ret < 0)
+ return ret;
+ bc_lvl = status0 & FUSB_REG_STATUS0_BC_LVL_MASK;
+ cc_status_active = fusb302_bc_lvl_to_cc(bc_lvl);
+ /* restart toggling if the cc status on the active line is OPEN */
+ if (cc_status_active == TYPEC_CC_OPEN) {
+ fusb302_log("restart toggling as CC_OPEN detected\n");
+ ret = fusb302_set_toggling(chip, chip->toggling_mode);
+ return ret;
+ }
+ /* update tcpm with the new cc value */
+ cc1 = (cc_polarity == TYPEC_POLARITY_CC1) ?
+ cc_status_active : TYPEC_CC_OPEN;
+ cc2 = (cc_polarity == TYPEC_POLARITY_CC2) ?
+ cc_status_active : TYPEC_CC_OPEN;
+ if ((chip->cc1 != cc1) || (chip->cc2 != cc2)) {
+ chip->cc1 = cc1;
+ chip->cc2 = cc2;
+ tcpm_cc_change(chip->tcpm_port);
+ }
+ /* turn off toggling */
+ ret = fusb302_set_toggling(chip, TOGGLINE_MODE_OFF);
+ if (ret < 0) {
+ fusb302_log("cannot set toggling mode off, ret=%d\n", ret);
+ return ret;
+ }
+ /* unmask bc_lvl interrupt */
+ ret = fusb302_i2c_clear_bits(chip, FUSB_REG_MASK, FUSB_REG_MASK_BC_LVL);
+ if (ret < 0) {
+ fusb302_log("cannot unmask bc_lcl interrupt, ret=%d\n", ret);
+ return ret;
+ }
+ chip->intr_bc_lvl = true;
+ fusb302_log("detected cc1=%s, cc2=%s\n", typec_cc_status_name[cc1],
+ typec_cc_status_name[cc2]);
+
+ return ret;
+}
+
+static int fusb302_handle_togdone_src(struct fusb302_chip *chip,
+ u8 togdone_result)
+{
+ /*
+ - set polarity (measure cc, vconn, tx)
+ - set pull_up, pull_down
+ - set cc1, cc2, and update to tcpm_port
+ - set I_COMP interrupt on
+ */
+ int ret = 0;
+ u8 status0;
+ u8 ra_mda = ra_mda_value[chip->src_current_status];
+ u8 rd_mda = rd_mda_value[chip->src_current_status];
+ bool ra_comp, rd_comp;
+ enum typec_cc_polarity cc_polarity;
+ enum typec_cc_status cc_status_active, cc1, cc2;
+
+ /* set pull_up, pull_down */
+ ret = fusb302_set_cc_pull(chip, true, false);
+ if (ret < 0) {
+ fusb302_log("cannot set cc to pull up, ret=%d\n", ret);
+ return ret;
+ }
+ /* set polarity */
+ cc_polarity = (togdone_result == FUSB_REG_STATUS1A_TOGSS_SRC1) ?
+ TYPEC_POLARITY_CC1 : TYPEC_POLARITY_CC2;
+ ret = fusb302_set_cc_polarity(chip, cc_polarity);
+ if (ret < 0) {
+ fusb302_log("cannot set cc polarity %s, ret=%d\n",
+ cc_polarity_name[cc_polarity], ret);
+ return ret;
+ }
+ /* fusb302_set_cc_polarity() has set the correct measure block */
+ ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, rd_mda);
+ if (ret < 0)
+ return ret;
+ usleep_range(50, 100);
+ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0);
+ if (ret < 0)
+ return ret;
+ rd_comp = !!(status0 & FUSB_REG_STATUS0_COMP);
+ if (!rd_comp) {
+ ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, ra_mda);
+ if (ret < 0)
+ return ret;
+ usleep_range(50, 100);
+ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0);
+ if (ret < 0)
+ return ret;
+ ra_comp = !!(status0 & FUSB_REG_STATUS0_COMP);
+ }
+ if (rd_comp)
+ cc_status_active = TYPEC_CC_OPEN;
+ else if (ra_comp)
+ cc_status_active = TYPEC_CC_RD;
+ else
+ /* Ra is not supported, report as Open */
+ cc_status_active = TYPEC_CC_OPEN;
+ /* restart toggling if the cc status on the active line is OPEN */
+ if (cc_status_active == TYPEC_CC_OPEN) {
+ fusb302_log("restart toggling as CC_OPEN detected\n");
+ ret = fusb302_set_toggling(chip, chip->toggling_mode);
+ return ret;
+ }
+ /* update tcpm with the new cc value */
+ cc1 = (cc_polarity == TYPEC_POLARITY_CC1) ?
+ cc_status_active : TYPEC_CC_OPEN;
+ cc2 = (cc_polarity == TYPEC_POLARITY_CC2) ?
+ cc_status_active : TYPEC_CC_OPEN;
+ if ((chip->cc1 != cc1) || (chip->cc2 != cc2)) {
+ chip->cc1 = cc1;
+ chip->cc2 = cc2;
+ tcpm_cc_change(chip->tcpm_port);
+ }
+ /* turn off toggling */
+ ret = fusb302_set_toggling(chip, TOGGLINE_MODE_OFF);
+ if (ret < 0) {
+ fusb302_log("cannot set toggling mode off, ret=%d\n", ret);
+ return ret;
+ }
+ /* set MDAC to Rd threshold, and unmask I_COMP for unplug detection */
+ ret = fusb302_i2c_write(chip, FUSB_REG_MEASURE, rd_mda);
+ if (ret < 0)
+ return ret;
+ /* unmask comp_chng interrupt */
+ ret = fusb302_i2c_clear_bits(chip, FUSB_REG_MASK,
+ FUSB_REG_MASK_COMP_CHNG);
+ if (ret < 0) {
+ fusb302_log("cannot unmask bc_lcl interrupt, ret=%d\n", ret);
+ return ret;
+ }
+ chip->intr_comp_chng = true;
+ fusb302_log("detected cc1=%s, cc2=%s\n", typec_cc_status_name[cc1],
+ typec_cc_status_name[cc2]);
+
+ return ret;
+}
+
+static int fusb302_handle_togdone(struct fusb302_chip *chip)
+{
+ int ret = 0;
+ u8 status1a;
+ u8 togdone_result;
+
+ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS1A, &status1a);
+ if (ret < 0)
+ return ret;
+ togdone_result = (status1a >> FUSB_REG_STATUS1A_TOGSS_POS) &
+ FUSB_REG_STATUS1A_TOGSS_MASK;
+ switch (togdone_result) {
+ case FUSB_REG_STATUS1A_TOGSS_SNK1:
+ case FUSB_REG_STATUS1A_TOGSS_SNK2:
+ return fusb302_handle_togdone_snk(chip, togdone_result);
+ case FUSB_REG_STATUS1A_TOGSS_SRC1:
+ case FUSB_REG_STATUS1A_TOGSS_SRC2:
+ return fusb302_handle_togdone_src(chip, togdone_result);
+ case FUSB_REG_STATUS1A_TOGSS_AA:
+ /* doesn't support */
+ fusb302_log("AudioAccessory not supported\n");
+ fusb302_set_toggling(chip, chip->toggling_mode);
+ break;
+ default:
+ fusb302_log("TOGDONE with an invalid state: %d\n",
+ togdone_result);
+ fusb302_set_toggling(chip, chip->toggling_mode);
+ break;
+ }
+ return ret;
+}
+
+static int fusb302_pd_reset(struct fusb302_chip *chip)
+{
+ return fusb302_i2c_set_bits(chip, FUSB_REG_RESET,
+ FUSB_REG_RESET_PD_RESET);
+}
+
+static int fusb302_pd_read_message(struct fusb302_chip *chip,
+ struct pd_message *msg)
+{
+ int ret = 0;
+ u8 token;
+ u8 crc[4];
+ int len;
+
+ /* first SOP token */
+ ret = fusb302_i2c_read(chip, FUSB_REG_FIFOS, &token);
+ if (ret < 0)
+ return ret;
+ ret = fusb302_i2c_block_read(chip, FUSB_REG_FIFOS, 2,
+ (u8 *)&msg->header);
+ if (ret < 0)
+ return ret;
+ len = pd_header_cnt(msg->header) * 4;
+ /* add 4 to length to include the CRC */
+ if (len > PD_MAX_PAYLOAD * 4) {
+ fusb302_log("PD message too long %d\n", len);
+ return -EINVAL;
+ }
+ if (len > 0) {
+ ret = fusb302_i2c_block_read(chip, FUSB_REG_FIFOS, len,
+ (u8 *)msg->payload);
+ if (ret < 0)
+ return ret;
+ }
+ /* another 4 bytes to read CRC out */
+ ret = fusb302_i2c_block_read(chip, FUSB_REG_FIFOS, 4, crc);
+ if (ret < 0)
+ return ret;
+ fusb302_log("PD message header: %x\n", msg->header);
+ fusb302_log("PD message len: %d\n", len);
+
+ return ret;
+}
+
+static irqreturn_t fusb302_irq_intn(int irq, void *dev_id)
+{
+ struct fusb302_chip *chip = dev_id;
+ int ret = 0;
+ u8 interrupt;
+ u8 interrupta;
+ u8 interruptb;
+ u8 status0;
+ u8 control2;
+ u8 maska;
+ u8 mask1;
+ bool vbus_present;
+ bool comp_result;
+ bool intr_togdone;
+ bool intr_bc_lvl;
+ bool intr_comp_chng;
+ struct pd_message pd_msg;
+
+ pm_wakeup_event(chip->dev, PM_WAKE_DELAY_MS);
+
+ mutex_lock(&chip->lock);
+ /* grab a snapshot of intr flags */
+ intr_togdone = chip->intr_togdone;
+ intr_bc_lvl = chip->intr_bc_lvl;
+ intr_comp_chng = chip->intr_comp_chng;
+
+ ret = fusb302_i2c_read(chip, FUSB_REG_INTERRUPT, &interrupt);
+ if (ret < 0)
+ goto done;
+ ret = fusb302_i2c_read(chip, FUSB_REG_INTERRUPTA, &interrupta);
+ if (ret < 0)
+ goto done;
+ ret = fusb302_i2c_read(chip, FUSB_REG_INTERRUPTB, &interruptb);
+ if (ret < 0)
+ goto done;
+ ret = fusb302_i2c_read(chip, FUSB_REG_STATUS0, &status0);
+ if (ret < 0)
+ goto done;
+ ret = fusb302_i2c_read(chip, FUSB_REG_CONTROL2, &control2);
+ if (ret < 0)
+ goto done;
+ ret = fusb302_i2c_read(chip, FUSB_REG_MASKA, &maska);
+ if (ret < 0)
+ goto done;
+ ret = fusb302_i2c_read(chip, FUSB_REG_MASK, &mask1);
+ if (ret < 0)
+ goto done;
+
+ fusb302_log("IRQ: 0x%02x, a: 0x%02x, b: 0x%02x, status0: 0x%02x",
+ interrupt, interrupta, interruptb, status0);
+ fusb302_log("control2: 0x%02x maska: 0x%02x mask1: 0x%02x\n",
+ control2, maska, mask1);
+
+ if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) {
+ vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK);
+ fusb302_log("IRQ: VBUS_OK, vbus=%s\n",
+ vbus_present ? "On" : "Off");
+ if (vbus_present != chip->vbus_present) {
+ chip->vbus_present = vbus_present;
+ tcpm_vbus_change(chip->tcpm_port);
+ }
+ }
+
+ if ((interrupta & FUSB_REG_INTERRUPTA_TOGDONE) && intr_togdone) {
+ fusb302_log("IRQ: TOGDONE\n");
+ ret = fusb302_handle_togdone(chip);
+ if (ret < 0) {
+ fusb302_log("handle togdone error, ret=%d\n", ret);
+ goto done;
+ }
+ }
+
+ if ((interrupt & FUSB_REG_INTERRUPT_BC_LVL) && intr_bc_lvl) {
+ fusb302_log("IRQ: BC_LVL, handler pending\n");
+ /*
+ * as BC_LVL interrupt can be affected by PD activity,
+ * apply delay to for the handler to wait for the PD
+ * signaling to finish.
+ */
+ mod_delayed_work(chip->wq, &chip->bc_lvl_handler,
+ msecs_to_jiffies(T_BC_LVL_DEBOUNCE_DELAY_MS));
+ }
+
+ if ((interrupt & FUSB_REG_INTERRUPT_COMP_CHNG) && intr_comp_chng) {
+ comp_result = !!(status0 & FUSB_REG_STATUS0_COMP);
+ fusb302_log("IRQ: COMP_CHNG, comp=%s\n",
+ comp_result ? "true" : "false");
+ if (comp_result) {
+ /* cc level > Rd_threashold, detach */
+ if (chip->cc_polarity == TYPEC_POLARITY_CC1)
+ chip->cc1 = TYPEC_CC_OPEN;
+ else
+ chip->cc2 = TYPEC_CC_OPEN;
+ tcpm_cc_change(chip->tcpm_port);
+ }
+ }
+
+ if (interrupt & FUSB_REG_INTERRUPT_COLLISION) {
+ fusb302_log("IRQ: PD collision\n");
+ tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_FAILED);
+ }
+
+ if (interrupta & FUSB_REG_INTERRUPTA_RETRYFAIL) {
+ fusb302_log("IRQ: PD retry failed\n");
+ tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_FAILED);
+ }
+
+ if (interrupta & FUSB_REG_INTERRUPTA_HARDSENT) {
+ fusb302_log("IRQ: PD hardreset sent\n");
+ ret = fusb302_pd_reset(chip);
+ if (ret < 0) {
+ fusb302_log("cannot PD reset, ret=%d\n", ret);
+ goto done;
+ }
+ tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS);
+ }
+
+ if (interrupta & FUSB_REG_INTERRUPTA_TX_SUCCESS) {
+ fusb302_log("IRQ: PD tx success\n");
+ /* read out the received good CRC */
+ ret = fusb302_pd_read_message(chip, &pd_msg);
+ if (ret < 0) {
+ fusb302_log("cannot read in GCRC, ret=%d\n", ret);
+ goto done;
+ }
+ tcpm_pd_transmit_complete(chip->tcpm_port, TCPC_TX_SUCCESS);
+ }
+
+ if (interrupta & FUSB_REG_INTERRUPTA_HARDRESET) {
+ fusb302_log("IRQ: PD received hardreset\n");
+ ret = fusb302_pd_reset(chip);
+ if (ret < 0) {
+ fusb302_log("cannot PD reset, ret=%d\n", ret);
+ goto done;
+ }
+ tcpm_pd_hard_reset(chip->tcpm_port);
+ }
+
+ if (interruptb & FUSB_REG_INTERRUPTB_GCRCSENT) {
+ fusb302_log("IRQ: PD sent good CRC\n");
+ ret = fusb302_pd_read_message(chip, &pd_msg);
+ if (ret < 0) {
+ fusb302_log("cannot read in PD message, ret=%d\n", ret);
+ goto done;
+ }
+ tcpm_pd_receive(chip->tcpm_port, &pd_msg);
+ }
+done:
+ mutex_unlock(&chip->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int init_gpio(struct fusb302_chip *chip)
+{
+ struct device_node *node;
+ struct pinctrl_state *set_state;
+ int ret = 0;
+
+ node = chip->dev->of_node;
+ chip->gpio_pinctrl = devm_pinctrl_get(chip->dev);
+ if (IS_ERR(chip->gpio_pinctrl)) {
+ ret = PTR_ERR(chip->gpio_pinctrl);
+ fusb302_log("cannot get pinctrl, ret=%d\n", ret);
+ return ret;
+ }
+ set_state = pinctrl_lookup_state(chip->gpio_pinctrl, "default");
+ if (IS_ERR(set_state)) {
+ ret = PTR_ERR(chip->gpio_pinctrl);
+ fusb302_log("cannot find the default pinctrl state, ret=%d\n",
+ ret);
+ return ret;
+ }
+ ret = pinctrl_select_state(chip->gpio_pinctrl, set_state);
+ if (ret < 0) {
+ fusb302_log("cannot select state, ret=%d\n", ret);
+ return ret;
+ }
+ chip->gpio_int_n = of_get_named_gpio(node, "fairchild,int_n", 0);
+ if (!gpio_is_valid(chip->gpio_int_n)) {
+ ret = chip->gpio_int_n;
+ fusb302_log("cannot get named GPIO Int_N, ret=%d\n", ret);
+ return ret;
+ }
+ ret = devm_gpio_request(chip->dev, chip->gpio_int_n, "fairchild,int_n");
+ if (ret < 0) {
+ fusb302_log("cannot request GPIO Int_N, ret=%d\n", ret);
+ return ret;
+ }
+ ret = gpio_direction_input(chip->gpio_int_n);
+ if (ret < 0) {
+ fusb302_log("cannot set GPIO Int_N to input, ret=%d\n", ret);
+ return ret;
+ }
+ ret = gpio_to_irq(chip->gpio_int_n);
+ if (ret < 0) {
+ fusb302_log("cannot request IRQ for GPIO Int_N, ret=%d\n", ret);
+ return ret;
+ }
+ chip->gpio_int_n_irq = ret;
+ return 0;
+}
+
+static int fusb302_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct fusb302_chip *chip;
+ struct i2c_adapter *adapter;
+ int ret = 0;
+
+ if (!fusb302_log)
+ fusb302_log = ipc_log_context_create(NUM_LOG_PAGES,
+ "fusb302", 0);
+ adapter = to_i2c_adapter(client->dev.parent);
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ fusb302_log("I2C/SMBus block functionality not supported!\n");
+ return -ENODEV;
+ }
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ chip->i2c_client = client;
+ i2c_set_clientdata(client, chip);
+ chip->dev = &client->dev;
+ mutex_init(&chip->lock);
+
+ /* If batt_psy is not ready in probe, skip here and get it when used. */
+ chip->batt_psy = power_supply_get_by_name("battery");
+ if (IS_ERR(chip->batt_psy)) {
+ ret = PTR_ERR(chip->batt_psy);
+ fusb302_log("cannot get battery power supply, ret=%d\n", ret);
+ chip->batt_psy = NULL;
+ }
+
+ chip->wq = create_singlethread_workqueue(dev_name(chip->dev));
+ if (!chip->wq)
+ return -ENOMEM;
+ INIT_DELAYED_WORK(&chip->bc_lvl_handler, fusb302_bc_lvl_handler_work);
+ init_tcpc_dev(&chip->tcpc_dev);
+
+ ret = init_regulators(chip);
+ if (ret < 0)
+ return ret;
+ ret = init_gpio(chip);
+ if (ret < 0)
+ goto disable_regulators;
+ chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev);
+ if (IS_ERR(chip->tcpm_port)) {
+ ret = PTR_ERR(chip->tcpm_port);
+ fusb302_log("cannot register tcpm port, ret=%d\n", ret);
+ goto disable_regulators;
+ }
+ ret = devm_request_threaded_irq(chip->dev, chip->gpio_int_n_irq,
+ NULL, fusb302_irq_intn,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "fsc_interrupt_int_n", chip);
+ if (ret < 0) {
+ fusb302_log("cannot request IRQ for GPIO Int_N, ret=%d\n", ret);
+ goto disable_regulators;
+ }
+ __fusb302_chip = chip;
+
+ ret = device_init_wakeup(chip->dev, true);
+
+ if (unlikely(ret < 0)) {
+ fusb302_log("wakeup init failed, ret=%d\n", ret);
+ goto disable_regulators;
+ }
+
+ enable_irq_wake(chip->gpio_int_n_irq);
+ return ret;
+
+disable_regulators:
+ regulator_set_optimum_mode(chip->vdd, 0);
+ regulator_disable(chip->vdd);
+ regulator_set_optimum_mode(chip->switch_vdd, 0);
+ regulator_disable(chip->switch_vdd);
+
+ return ret;
+}
+
+static int fusb302_remove(struct i2c_client *client)
+{
+ struct fusb302_chip *chip = i2c_get_clientdata(client);
+
+ tcpm_unregister_port(chip->tcpm_port);
+ regulator_disable(chip->vdd);
+ regulator_disable(chip->switch_vdd);
+
+ return 0;
+}
+
+static int fusb302_pm_suspend(struct device *dev)
+{
+ struct fusb302_chip *chip = dev->driver_data;
+
+ if (atomic_read(&chip->i2c_busy))
+ return -EBUSY;
+ atomic_set(&chip->pm_suspend, 1);
+
+ return 0;
+}
+
+static int fusb302_pm_resume(struct device *dev)
+{
+ struct fusb302_chip *chip = dev->driver_data;
+
+ atomic_set(&chip->pm_suspend, 0);
+
+ return 0;
+}
+
+static const struct of_device_id fusb302_dt_match[] = {
+ {.compatible = "fairchild,fusb302"},
+ {},
+};
+
+static const struct i2c_device_id fusb302_i2c_device_id[] = {
+ {"typec_fusb302", 0},
+ {},
+};
+
+static const struct dev_pm_ops fusb302_pm_ops = {
+ .suspend = fusb302_pm_suspend,
+ .resume = fusb302_pm_resume,
+};
+
+static struct i2c_driver fusb302_driver = {
+ .driver = {
+ .name = "typec_fusb302",
+ .pm = &fusb302_pm_ops,
+ .of_match_table = of_match_ptr(fusb302_dt_match),
+ },
+ .probe = fusb302_probe,
+ .remove = fusb302_remove,
+ .id_table = fusb302_i2c_device_id,
+};
+module_i2c_driver(fusb302_driver);
+
+int usb_controller_register(struct device *parent, struct usb_controller *uc)
+{
+ struct fusb302_chip *chip = __fusb302_chip;
+
+ if (chip == NULL)
+ return -ENODEV;
+
+ mutex_lock(&chip->lock);
+ chip->uc = uc;
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_controller_register);
+
+int usb_typec_ctrl_register(struct device *parent, struct usb_typec_ctrl *utc)
+{
+ struct fusb302_chip *chip = __fusb302_chip;
+
+ if (chip == NULL)
+ return -ENODEV;
+
+ mutex_lock(&chip->lock);
+ chip->utc = utc;
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usb_typec_ctrl_register);
+
+MODULE_AUTHOR("Yueyao Zhu <yueyao@google.com>");
+MODULE_DESCRIPTION("Fairchild FUSB302 Type-C Chip Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/fusb302/fusb302_reg.h b/drivers/usb/typec/fusb302/fusb302_reg.h
new file mode 100644
index 0000000..0682e63
--- /dev/null
+++ b/drivers/usb/typec/fusb302/fusb302_reg.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2016-2017 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Fairchild FUSB302 Type-C Chip Driver
+ */
+
+#ifndef FUSB302_REG_H
+#define FUSB302_REG_H
+
+#define FUSB_REG_DEVICE_ID 0x01
+#define FUSB_REG_SWITCHES0 0x02
+#define FUSB_REG_SWITCHES0_CC2_PU_EN BIT(7)
+#define FUSB_REG_SWITCHES0_CC1_PU_EN BIT(6)
+#define FUSB_REG_SWITCHES0_VCONN_CC2 BIT(5)
+#define FUSB_REG_SWITCHES0_VCONN_CC1 BIT(4)
+#define FUSB_REG_SWITCHES0_MEAS_CC2 BIT(3)
+#define FUSB_REG_SWITCHES0_MEAS_CC1 BIT(2)
+#define FUSB_REG_SWITCHES0_CC2_PD_EN BIT(1)
+#define FUSB_REG_SWITCHES0_CC1_PD_EN BIT(0)
+#define FUSB_REG_SWITCHES1 0x03
+#define FUSB_REG_SWITCHES1_POWERROLE BIT(7)
+#define FUSB_REG_SWITCHES1_SPECREV1 BIT(6)
+#define FUSB_REG_SWITCHES1_SPECREV0 BIT(5)
+#define FUSB_REG_SWITCHES1_DATAROLE BIT(4)
+#define FUSB_REG_SWITCHES1_AUTO_GCRC BIT(2)
+#define FUSB_REG_SWITCHES1_TXCC2_EN BIT(1)
+#define FUSB_REG_SWITCHES1_TXCC1_EN BIT(0)
+#define FUSB_REG_MEASURE 0x04
+#define FUSB_REG_MEASURE_MDAC5 BIT(7)
+#define FUSB_REG_MEASURE_MDAC4 BIT(6)
+#define FUSB_REG_MEASURE_MDAC3 BIT(5)
+#define FUSB_REG_MEASURE_MDAC2 BIT(4)
+#define FUSB_REG_MEASURE_MDAC1 BIT(3)
+#define FUSB_REG_MEASURE_MDAC0 BIT(2)
+#define FUSB_REG_MEASURE_VBUS BIT(1)
+#define FUSB_REG_MEASURE_XXXX5 BIT(0)
+#define FUSB_REG_CONTROL0 0x06
+#define FUSB_REG_CONTROL0_TX_FLUSH BIT(6)
+#define FUSB_REG_CONTROL0_INT_MASK BIT(5)
+#define FUSB_REG_CONTROL0_HOST_CUR_MASK (0xC)
+#define FUSB_REG_CONTROL0_HOST_CUR_HIGH (0xC)
+#define FUSB_REG_CONTROL0_HOST_CUR_MED (0x8)
+#define FUSB_REG_CONTROL0_HOST_CUR_DEF (0x4)
+#define FUSB_REG_CONTROL0_TX_START BIT(0)
+#define FUSB_REG_CONTROL1 0x07
+#define FUSB_REG_CONTROL1_ENSOP2DB BIT(6)
+#define FUSB_REG_CONTROL1_ENSOP1DB BIT(5)
+#define FUSB_REG_CONTROL1_BIST_MODE2 BIT(4)
+#define FUSB_REG_CONTROL1_RX_FLUSH BIT(2)
+#define FUSB_REG_CONTROL1_ENSOP2 BIT(1)
+#define FUSB_REG_CONTROL1_ENSOP1 BIT(0)
+#define FUSB_REG_CONTROL2 0x08
+#define FUSB_REG_CONTROL2_MODE BIT(1)
+#define FUSB_REG_CONTROL2_MODE_MASK (0x6)
+#define FUSB_REG_CONTROL2_MODE_DFP (0x6)
+#define FUSB_REG_CONTROL2_MODE_UFP (0x4)
+#define FUSB_REG_CONTROL2_MODE_DRP (0x2)
+#define FUSB_REG_CONTROL2_MODE_NONE (0x0)
+#define FUSB_REG_CONTROL2_TOGGLE BIT(0)
+#define FUSB_REG_CONTROL3 0x09
+#define FUSB_REG_CONTROL3_SEND_HARDRESET BIT(6)
+#define FUSB_REG_CONTROL3_BIST_TMODE BIT(5) /* 302B Only */
+#define FUSB_REG_CONTROL3_AUTO_HARDRESET BIT(4)
+#define FUSB_REG_CONTROL3_AUTO_SOFTRESET BIT(3)
+#define FUSB_REG_CONTROL3_N_RETRIES BIT(1)
+#define FUSB_REG_CONTROL3_N_RETRIES_MASK (0x6)
+#define FUSB_REG_CONTROL3_N_RETRIES_3 (0x6)
+#define FUSB_REG_CONTROL3_N_RETRIES_2 (0x4)
+#define FUSB_REG_CONTROL3_N_RETRIES_1 (0x2)
+#define FUSB_REG_CONTROL3_AUTO_RETRY BIT(0)
+#define FUSB_REG_MASK 0x0A
+#define FUSB_REG_MASK_VBUSOK BIT(7)
+#define FUSB_REG_MASK_ACTIVITY BIT(6)
+#define FUSB_REG_MASK_COMP_CHNG BIT(5)
+#define FUSB_REG_MASK_CRC_CHK BIT(4)
+#define FUSB_REG_MASK_ALERT BIT(3)
+#define FUSB_REG_MASK_WAKE BIT(2)
+#define FUSB_REG_MASK_COLLISION BIT(1)
+#define FUSB_REG_MASK_BC_LVL BIT(0)
+#define FUSB_REG_POWER 0x0B
+#define FUSB_REG_POWER_PWR BIT(0)
+#define FUSB_REG_POWER_PWR_LOW 0x1
+#define FUSB_REG_POWER_PWR_MEDIUM 0x3
+#define FUSB_REG_POWER_PWR_HIGH 0x7
+#define FUSB_REG_POWER_PWR_ALL 0xF
+#define FUSB_REG_RESET 0x0C
+#define FUSB_REG_RESET_PD_RESET BIT(1)
+#define FUSB_REG_RESET_SW_RESET BIT(0)
+#define FUSB_REG_MASKA 0x0E
+#define FUSB_REG_MASKA_OCP_TEMP BIT(7)
+#define FUSB_REG_MASKA_TOGDONE BIT(6)
+#define FUSB_REG_MASKA_SOFTFAIL BIT(5)
+#define FUSB_REG_MASKA_RETRYFAIL BIT(4)
+#define FUSB_REG_MASKA_HARDSENT BIT(3)
+#define FUSB_REG_MASKA_TX_SUCCESS BIT(2)
+#define FUSB_REG_MASKA_SOFTRESET BIT(1)
+#define FUSB_REG_MASKA_HARDRESET BIT(0)
+#define FUSB_REG_MASKB 0x0F
+#define FUSB_REG_MASKB_GCRCSENT BIT(0)
+#define FUSB_REG_STATUS0A 0x3C
+#define FUSB_REG_STATUS0A_SOFTFAIL BIT(5)
+#define FUSB_REG_STATUS0A_RETRYFAIL BIT(4)
+#define FUSB_REG_STATUS0A_POWER BIT(2)
+#define FUSB_REG_STATUS0A_RX_SOFT_RESET BIT(1)
+#define FUSB_REG_STATUS0A_RX_HARD_RESET BIT(0)
+#define FUSB_REG_STATUS1A 0x3D
+#define FUSB_REG_STATUS1A_TOGSS BIT(3)
+#define FUSB_REG_STATUS1A_TOGSS_RUNNING 0x0
+#define FUSB_REG_STATUS1A_TOGSS_SRC1 0x1
+#define FUSB_REG_STATUS1A_TOGSS_SRC2 0x2
+#define FUSB_REG_STATUS1A_TOGSS_SNK1 0x5
+#define FUSB_REG_STATUS1A_TOGSS_SNK2 0x6
+#define FUSB_REG_STATUS1A_TOGSS_AA 0x7
+#define FUSB_REG_STATUS1A_TOGSS_POS (3)
+#define FUSB_REG_STATUS1A_TOGSS_MASK (0x7)
+#define FUSB_REG_STATUS1A_RXSOP2DB BIT(2)
+#define FUSB_REG_STATUS1A_RXSOP1DB BIT(1)
+#define FUSB_REG_STATUS1A_RXSOP BIT(0)
+#define FUSB_REG_INTERRUPTA 0x3E
+#define FUSB_REG_INTERRUPTA_OCP_TEMP BIT(7)
+#define FUSB_REG_INTERRUPTA_TOGDONE BIT(6)
+#define FUSB_REG_INTERRUPTA_SOFTFAIL BIT(5)
+#define FUSB_REG_INTERRUPTA_RETRYFAIL BIT(4)
+#define FUSB_REG_INTERRUPTA_HARDSENT BIT(3)
+#define FUSB_REG_INTERRUPTA_TX_SUCCESS BIT(2)
+#define FUSB_REG_INTERRUPTA_SOFTRESET BIT(1)
+#define FUSB_REG_INTERRUPTA_HARDRESET BIT(0)
+#define FUSB_REG_INTERRUPTB 0x3F
+#define FUSB_REG_INTERRUPTB_GCRCSENT BIT(0)
+#define FUSB_REG_STATUS0 0x40
+#define FUSB_REG_STATUS0_VBUSOK BIT(7)
+#define FUSB_REG_STATUS0_ACTIVITY BIT(6)
+#define FUSB_REG_STATUS0_COMP BIT(5)
+#define FUSB_REG_STATUS0_CRC_CHK BIT(4)
+#define FUSB_REG_STATUS0_ALERT BIT(3)
+#define FUSB_REG_STATUS0_WAKE BIT(2)
+#define FUSB_REG_STATUS0_BC_LVL_MASK 0x03
+#define FUSB_REG_STATUS0_BC_LVL_0_200 0x0
+#define FUSB_REG_STATUS0_BC_LVL_200_600 0x1
+#define FUSB_REG_STATUS0_BC_LVL_600_1230 0x2
+#define FUSB_REG_STATUS0_BC_LVL_1230_MAX 0x3
+#define FUSB_REG_STATUS0_BC_LVL1 BIT(1)
+#define FUSB_REG_STATUS0_BC_LVL0 BIT(0)
+#define FUSB_REG_STATUS1 0x41
+#define FUSB_REG_STATUS1_RXSOP2 BIT(7)
+#define FUSB_REG_STATUS1_RXSOP1 BIT(6)
+#define FUSB_REG_STATUS1_RX_EMPTY BIT(5)
+#define FUSB_REG_STATUS1_RX_FULL BIT(4)
+#define FUSB_REG_STATUS1_TX_EMPTY BIT(3)
+#define FUSB_REG_STATUS1_TX_FULL BIT(2)
+#define FUSB_REG_INTERRUPT 0x42
+#define FUSB_REG_INTERRUPT_VBUSOK BIT(7)
+#define FUSB_REG_INTERRUPT_ACTIVITY BIT(6)
+#define FUSB_REG_INTERRUPT_COMP_CHNG BIT(5)
+#define FUSB_REG_INTERRUPT_CRC_CHK BIT(4)
+#define FUSB_REG_INTERRUPT_ALERT BIT(3)
+#define FUSB_REG_INTERRUPT_WAKE BIT(2)
+#define FUSB_REG_INTERRUPT_COLLISION BIT(1)
+#define FUSB_REG_INTERRUPT_BC_LVL BIT(0)
+#define FUSB_REG_FIFOS 0x43
+
+/* Tokens defined for the FUSB302 TX FIFO */
+enum fusb302_txfifo_tokens {
+ FUSB302_TKN_TXON = 0xA1,
+ FUSB302_TKN_SYNC1 = 0x12,
+ FUSB302_TKN_SYNC2 = 0x13,
+ FUSB302_TKN_SYNC3 = 0x1B,
+ FUSB302_TKN_RST1 = 0x15,
+ FUSB302_TKN_RST2 = 0x16,
+ FUSB302_TKN_PACKSYM = 0x80,
+ FUSB302_TKN_JAMCRC = 0xFF,
+ FUSB302_TKN_EOP = 0x14,
+ FUSB302_TKN_TXOFF = 0xFE,
+};
+
+#endif
diff --git a/drivers/usb/typec/pd.h b/drivers/usb/typec/pd.h
new file mode 100644
index 0000000..510ef72
--- /dev/null
+++ b/drivers/usb/typec/pd.h
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2015-2017 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_USB_PD_H
+#define __LINUX_USB_PD_H
+
+#include <linux/types.h>
+#include <linux/usb/typec.h>
+
+/* USB PD Messages */
+enum pd_ctrl_msg_type {
+ /* 0 Reserved */
+ PD_CTRL_GOOD_CRC = 1,
+ PD_CTRL_GOTO_MIN = 2,
+ PD_CTRL_ACCEPT = 3,
+ PD_CTRL_REJECT = 4,
+ PD_CTRL_PING = 5,
+ PD_CTRL_PS_RDY = 6,
+ PD_CTRL_GET_SOURCE_CAP = 7,
+ PD_CTRL_GET_SINK_CAP = 8,
+ PD_CTRL_DR_SWAP = 9,
+ PD_CTRL_PR_SWAP = 10,
+ PD_CTRL_VCONN_SWAP = 11,
+ PD_CTRL_WAIT = 12,
+ PD_CTRL_SOFT_RESET = 13,
+ /* 14-15 Reserved */
+};
+
+enum pd_data_msg_type {
+ /* 0 Reserved */
+ PD_DATA_SOURCE_CAP = 1,
+ PD_DATA_REQUEST = 2,
+ PD_DATA_BIST = 3,
+ PD_DATA_SINK_CAP = 4,
+ /* 5-14 Reserved */
+ PD_DATA_VENDOR_DEF = 15,
+};
+
+#define PD_REV10 0x0
+#define PD_REV20 0x1
+
+#define PD_HEADER_CNT_SHIFT 12
+#define PD_HEADER_CNT_MASK 0x7
+#define PD_HEADER_ID_SHIFT 9
+#define PD_HEADER_ID_MASK 0x7
+#define PD_HEADER_PWR_ROLE BIT(8)
+#define PD_HEADER_REV_SHIFT 6
+#define PD_HEADER_REV_MASK 0x3
+#define PD_HEADER_DATA_ROLE BIT(5)
+#define PD_HEADER_TYPE_SHIFT 0
+#define PD_HEADER_TYPE_MASK 0xf
+
+#define PD_HEADER(type, pwr, data, id, cnt) \
+ ((((type) & PD_HEADER_TYPE_MASK) << PD_HEADER_TYPE_SHIFT) | \
+ ((pwr) == TYPEC_SOURCE ? PD_HEADER_PWR_ROLE : 0) | \
+ ((data) == TYPEC_HOST ? PD_HEADER_DATA_ROLE : 0) | \
+ (PD_REV20 << PD_HEADER_REV_SHIFT) | \
+ (((id) & PD_HEADER_ID_MASK) << PD_HEADER_ID_SHIFT) | \
+ (((cnt) & PD_HEADER_CNT_MASK) << PD_HEADER_CNT_SHIFT))
+
+#define PD_HEADER_LE(type, pwr, data, id, cnt) \
+ cpu_to_le16(PD_HEADER((type), (pwr), (data), (id), (cnt)))
+
+static inline unsigned int pd_header_cnt(u16 header)
+{
+ return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK;
+}
+
+static inline unsigned int pd_header_cnt_le(__le16 header)
+{
+ return pd_header_cnt(le16_to_cpu(header));
+}
+
+static inline unsigned int pd_header_type(u16 header)
+{
+ return (header >> PD_HEADER_TYPE_SHIFT) & PD_HEADER_TYPE_MASK;
+}
+
+static inline unsigned int pd_header_type_le(__le16 header)
+{
+ return pd_header_type(le16_to_cpu(header));
+}
+
+static inline unsigned int pd_header_msgid(u16 header)
+{
+ return (header >> PD_HEADER_ID_SHIFT) & PD_HEADER_ID_MASK;
+}
+
+static inline unsigned int pd_header_msgid_le(__le16 header)
+{
+ return pd_header_msgid(le16_to_cpu(header));
+}
+
+#define PD_MAX_PAYLOAD 7
+
+struct pd_message {
+ __le16 header;
+ __le32 payload[PD_MAX_PAYLOAD];
+} __packed;
+
+/* PDO: Power Data Object */
+#define PDO_MAX_OBJECTS 7
+
+enum pd_pdo_type {
+ PDO_TYPE_FIXED = 0,
+ PDO_TYPE_BATT = 1,
+ PDO_TYPE_VAR = 2,
+};
+
+#define PDO_TYPE_SHIFT 30
+#define PDO_TYPE_MASK 0x3
+
+#define PDO_TYPE(t) ((t) << PDO_TYPE_SHIFT)
+
+#define PDO_VOLT_MASK 0x3ff
+#define PDO_CURR_MASK 0x3ff
+#define PDO_PWR_MASK 0x3ff
+
+#define PDO_FIXED_DUAL_ROLE BIT(29) /* Power role swap supported */
+#define PDO_FIXED_SUSPEND BIT(28) /* USB Suspend supported (Source) */
+#define PDO_FIXED_HIGHER_CAP BIT(28) /* Requires more than vSafe5V (Sink) */
+#define PDO_FIXED_EXTPOWER BIT(27) /* Externally powered */
+#define PDO_FIXED_USB_COMM BIT(26) /* USB communications capable */
+#define PDO_FIXED_DATA_SWAP BIT(25) /* Data role swap supported */
+#define PDO_FIXED_VOLT_SHIFT 10 /* 50mV units */
+#define PDO_FIXED_CURR_SHIFT 0 /* 10mA units */
+
+#define PDO_FIXED_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_FIXED_VOLT_SHIFT)
+#define PDO_FIXED_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_FIXED_CURR_SHIFT)
+
+#define PDO_FIXED(mv, ma, flags) \
+ (PDO_TYPE(PDO_TYPE_FIXED) | (flags) | \
+ PDO_FIXED_VOLT(mv) | PDO_FIXED_CURR(ma))
+
+#define PDO_BATT_MAX_VOLT_SHIFT 20 /* 50mV units */
+#define PDO_BATT_MIN_VOLT_SHIFT 10 /* 50mV units */
+#define PDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */
+
+#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MIN_VOLT_SHIFT)
+#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MAX_VOLT_SHIFT)
+#define PDO_BATT_MAX_POWER(mw) ((((mw) / 250) & PDO_PWR_MASK) << PDO_BATT_MAX_PWR_SHIFT)
+
+#define PDO_BATT(min_mv, max_mv, max_mw) \
+ (PDO_TYPE(PDO_TYPE_BATT) | PDO_BATT_MIN_VOLT(min_mv) | \
+ PDO_BATT_MAX_VOLT(max_mv) | PDO_BATT_MAX_POWER(max_mw))
+
+#define PDO_VAR_MAX_VOLT_SHIFT 20 /* 50mV units */
+#define PDO_VAR_MIN_VOLT_SHIFT 10 /* 50mV units */
+#define PDO_VAR_MAX_CURR_SHIFT 0 /* 10mA units */
+
+#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MIN_VOLT_SHIFT)
+#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MAX_VOLT_SHIFT)
+#define PDO_VAR_MAX_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_VAR_MAX_CURR_SHIFT)
+
+#define PDO_VAR(min_mv, max_mv, max_ma) \
+ (PDO_TYPE(PDO_TYPE_VAR) | PDO_VAR_MIN_VOLT(min_mv) | \
+ PDO_VAR_MAX_VOLT(max_mv) | PDO_VAR_MAX_CURR(max_ma))
+
+static inline enum pd_pdo_type pdo_type(u32 pdo)
+{
+ return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK;
+}
+
+static inline unsigned int pdo_fixed_voltage(u32 pdo)
+{
+ return ((pdo >> PDO_FIXED_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
+}
+
+static inline unsigned int pdo_min_voltage(u32 pdo)
+{
+ return ((pdo >> PDO_VAR_MIN_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
+}
+
+static inline unsigned int pdo_max_voltage(u32 pdo)
+{
+ return ((pdo >> PDO_VAR_MAX_VOLT_SHIFT) & PDO_VOLT_MASK) * 50;
+}
+
+static inline unsigned int pdo_max_current(u32 pdo)
+{
+ return ((pdo >> PDO_VAR_MAX_CURR_SHIFT) & PDO_CURR_MASK) * 10;
+}
+
+static inline unsigned int pdo_max_power(u32 pdo)
+{
+ return ((pdo >> PDO_BATT_MAX_PWR_SHIFT) & PDO_PWR_MASK) * 250;
+}
+
+/* RDO: Request Data Object */
+#define RDO_OBJ_POS_SHIFT 28
+#define RDO_OBJ_POS_MASK 0x7
+#define RDO_GIVE_BACK BIT(27) /* Supports reduced operating current */
+#define RDO_CAP_MISMATCH BIT(26) /* Not satisfied by source caps */
+#define RDO_USB_COMM BIT(25) /* USB communications capable */
+#define RDO_NO_SUSPEND BIT(24) /* USB Suspend not supported */
+
+#define RDO_PWR_MASK 0x3ff
+#define RDO_CURR_MASK 0x3ff
+
+#define RDO_FIXED_OP_CURR_SHIFT 10
+#define RDO_FIXED_MAX_CURR_SHIFT 0
+
+#define RDO_OBJ(idx) (((idx) & RDO_OBJ_POS_MASK) << RDO_OBJ_POS_SHIFT)
+
+#define PDO_FIXED_OP_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_OP_CURR_SHIFT)
+#define PDO_FIXED_MAX_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_MAX_CURR_SHIFT)
+
+#define RDO_FIXED(idx, op_ma, max_ma, flags) \
+ (RDO_OBJ(idx) | (flags) | \
+ PDO_FIXED_OP_CURR(op_ma) | PDO_FIXED_MAX_CURR(max_ma))
+
+#define RDO_BATT_OP_PWR_SHIFT 10 /* 250mW units */
+#define RDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */
+
+#define RDO_BATT_OP_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_OP_PWR_SHIFT)
+#define RDO_BATT_MAX_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_MAX_PWR_SHIFT)
+
+#define RDO_BATT(idx, op_mw, max_mw, flags) \
+ (RDO_OBJ(idx) | (flags) | \
+ RDO_BATT_OP_PWR(op_mw) | RDO_BATT_MAX_PWR(max_mw))
+
+static inline unsigned int rdo_index(u32 rdo)
+{
+ return (rdo >> RDO_OBJ_POS_SHIFT) & RDO_OBJ_POS_MASK;
+}
+
+static inline unsigned int rdo_op_current(u32 rdo)
+{
+ return ((rdo >> RDO_FIXED_OP_CURR_SHIFT) & RDO_CURR_MASK) * 10;
+}
+
+static inline unsigned int rdo_max_current(u32 rdo)
+{
+ return ((rdo >> RDO_FIXED_MAX_CURR_SHIFT) &
+ RDO_CURR_MASK) * 10;
+}
+
+static inline unsigned int rdo_op_power(u32 rdo)
+{
+ return ((rdo >> RDO_BATT_OP_PWR_SHIFT) & RDO_PWR_MASK) * 250;
+}
+
+static inline unsigned int rdo_max_power(u32 rdo)
+{
+ return ((rdo >> RDO_BATT_MAX_PWR_SHIFT) & RDO_PWR_MASK) * 250;
+}
+
+/* USB PD timers and counters */
+#define PD_T_NO_RESPONSE 5000 /* 4.5 - 5.5 seconds */
+#define PD_T_DB_DETECT 10000 /* 10 - 15 seconds */
+#define PD_T_SEND_SOURCE_CAP 150 /* 100 - 200 ms */
+#define PD_T_SENDER_RESPONSE 60 /* 24 - 30 ms, relaxed */
+#define PD_T_SOURCE_ACTIVITY 45
+#define PD_T_SINK_ACTIVITY 135
+#define PD_T_SINK_WAIT_CAP 240
+#define PD_T_PS_TRANSITION 500
+#define PD_T_SRC_TRANSITION 35
+#define PD_T_DRP_SNK 40
+#define PD_T_DRP_SRC 30
+#define PD_T_PS_SOURCE_OFF 920
+#define PD_T_PS_SOURCE_ON 480
+#define PD_T_PS_HARD_RESET 30
+#define PD_T_SRC_RECOVER 760
+#define PD_T_SRC_RECOVER_MAX 1000
+#define PD_T_SRC_TURN_ON 275
+#define PD_T_SAFE_0V 650
+#define PD_T_VCONN_SOURCE_ON 100
+#define PD_T_SINK_REQUEST 100 /* 100 ms minimum */
+#define PD_T_ERROR_RECOVERY 100 /* minimum 25 is insufficient */
+
+#define PD_T_DRP_TRY 100 /* 75 - 150 ms */
+#define PD_T_DRP_TRYWAIT 600 /* 400 - 800 ms */
+
+#define PD_T_CC_DEBOUNCE 200 /* 100 - 200 ms */
+#define PD_T_PD_DEBOUNCE 20 /* 10 - 20 ms */
+
+#define PD_N_CAPS_COUNT (PD_T_NO_RESPONSE / PD_T_SEND_SOURCE_CAP)
+#define PD_N_HARD_RESET_COUNT 2
+
+#endif /* __LINUX_USB_PD_H */
diff --git a/drivers/usb/typec/pd_bdo.h b/drivers/usb/typec/pd_bdo.h
new file mode 100644
index 0000000..90b94d9
--- /dev/null
+++ b/drivers/usb/typec/pd_bdo.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2015-2017 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_USB_PD_BDO_H
+#define __LINUX_USB_PD_BDO_H
+
+/* BDO : BIST Data Object */
+#define BDO_MODE_RECV (0 << 28)
+#define BDO_MODE_TRANSMIT (1 << 28)
+#define BDO_MODE_COUNTERS (2 << 28)
+#define BDO_MODE_CARRIER0 (3 << 28)
+#define BDO_MODE_CARRIER1 (4 << 28)
+#define BDO_MODE_CARRIER2 (5 << 28)
+#define BDO_MODE_CARRIER3 (6 << 28)
+#define BDO_MODE_EYE (7 << 28)
+#define BDO_MODE_TESTDATA (8 << 28)
+
+#define BDO_MODE_MASK(mode) ((mode) & 0xf0000000)
+
+#endif
diff --git a/drivers/usb/typec/pd_vdo.h b/drivers/usb/typec/pd_vdo.h
new file mode 100644
index 0000000..d92259f
--- /dev/null
+++ b/drivers/usb/typec/pd_vdo.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2015-2017 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_USB_PD_VDO_H
+#define __LINUX_USB_PD_VDO_H
+
+#include "pd.h"
+
+/*
+ * VDO : Vendor Defined Message Object
+ * VDM object is minimum of VDM header + 6 additional data objects.
+ */
+
+#define VDO_MAX_OBJECTS 6
+#define VDO_MAX_SIZE (VDO_MAX_OBJECTS + 1)
+
+/*
+ * VDM header
+ * ----------
+ * <31:16> :: SVID
+ * <15> :: VDM type ( 1b == structured, 0b == unstructured )
+ * <14:13> :: Structured VDM version (can only be 00 == 1.0 currently)
+ * <12:11> :: reserved
+ * <10:8> :: object position (1-7 valid ... used for enter/exit mode only)
+ * <7:6> :: command type (SVDM only?)
+ * <5> :: reserved (SVDM), command type (UVDM)
+ * <4:0> :: command
+ */
+#define VDO(vid, type, custom) \
+ (((vid) << 16) | \
+ ((type) << 15) | \
+ ((custom) & 0x7FFF))
+
+#define VDO_SVDM_TYPE (1 << 15)
+#define VDO_SVDM_VERS(x) ((x) << 13)
+#define VDO_OPOS(x) ((x) << 8)
+#define VDO_CMDT(x) ((x) << 6)
+#define VDO_OPOS_MASK VDO_OPOS(0x7)
+#define VDO_CMDT_MASK VDO_CMDT(0x3)
+
+#define CMDT_INIT 0
+#define CMDT_RSP_ACK 1
+#define CMDT_RSP_NAK 2
+#define CMDT_RSP_BUSY 3
+
+/* reserved for SVDM ... for Google UVDM */
+#define VDO_SRC_INITIATOR (0 << 5)
+#define VDO_SRC_RESPONDER (1 << 5)
+
+#define CMD_DISCOVER_IDENT 1
+#define CMD_DISCOVER_SVID 2
+#define CMD_DISCOVER_MODES 3
+#define CMD_ENTER_MODE 4
+#define CMD_EXIT_MODE 5
+#define CMD_ATTENTION 6
+
+#define VDO_CMD_VENDOR(x) (((10 + (x)) & 0x1f))
+
+/* ChromeOS specific commands */
+#define VDO_CMD_VERSION VDO_CMD_VENDOR(0)
+#define VDO_CMD_SEND_INFO VDO_CMD_VENDOR(1)
+#define VDO_CMD_READ_INFO VDO_CMD_VENDOR(2)
+#define VDO_CMD_REBOOT VDO_CMD_VENDOR(5)
+#define VDO_CMD_FLASH_ERASE VDO_CMD_VENDOR(6)
+#define VDO_CMD_FLASH_WRITE VDO_CMD_VENDOR(7)
+#define VDO_CMD_ERASE_SIG VDO_CMD_VENDOR(8)
+#define VDO_CMD_PING_ENABLE VDO_CMD_VENDOR(10)
+#define VDO_CMD_CURRENT VDO_CMD_VENDOR(11)
+#define VDO_CMD_FLIP VDO_CMD_VENDOR(12)
+#define VDO_CMD_GET_LOG VDO_CMD_VENDOR(13)
+#define VDO_CMD_CCD_EN VDO_CMD_VENDOR(14)
+
+#define PD_VDO_VID(vdo) ((vdo) >> 16)
+#define PD_VDO_SVDM(vdo) (((vdo) >> 15) & 1)
+#define PD_VDO_OPOS(vdo) (((vdo) >> 8) & 0x7)
+#define PD_VDO_CMD(vdo) ((vdo) & 0x1f)
+#define PD_VDO_CMDT(vdo) (((vdo) >> 6) & 0x3)
+
+/*
+ * SVDM Identity request -> response
+ *
+ * Request is simply properly formatted SVDM header
+ *
+ * Response is 4 data objects:
+ * [0] :: SVDM header
+ * [1] :: Identitiy header
+ * [2] :: Cert Stat VDO
+ * [3] :: (Product | Cable) VDO
+ * [4] :: AMA VDO
+ *
+ */
+#define VDO_INDEX_HDR 0
+#define VDO_INDEX_IDH 1
+#define VDO_INDEX_CSTAT 2
+#define VDO_INDEX_CABLE 3
+#define VDO_INDEX_PRODUCT 3
+#define VDO_INDEX_AMA 4
+
+/*
+ * SVDM Identity Header
+ * --------------------
+ * <31> :: data capable as a USB host
+ * <30> :: data capable as a USB device
+ * <29:27> :: product type
+ * <26> :: modal operation supported (1b == yes)
+ * <25:16> :: Reserved, Shall be set to zero
+ * <15:0> :: USB-IF assigned VID for this cable vendor
+ */
+#define IDH_PTYPE_UNDEF 0
+#define IDH_PTYPE_HUB 1
+#define IDH_PTYPE_PERIPH 2
+#define IDH_PTYPE_PCABLE 3
+#define IDH_PTYPE_ACABLE 4
+#define IDH_PTYPE_AMA 5
+
+#define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \
+ ((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \
+ | (is_modal) << 26 | ((vid) & 0xffff))
+
+#define PD_IDH_PTYPE(vdo) (((vdo) >> 27) & 0x7)
+#define PD_IDH_VID(vdo) ((vdo) & 0xffff)
+#define PD_IDH_MODAL_SUPP(vdo) ((vdo) & (1 << 26))
+
+/*
+ * Cert Stat VDO
+ * -------------
+ * <31:0> : USB-IF assigned XID for this cable
+ */
+#define PD_CSTAT_XID(vdo) (vdo)
+
+/*
+ * Product VDO
+ * -----------
+ * <31:16> : USB Product ID
+ * <15:0> : USB bcdDevice
+ */
+#define VDO_PRODUCT(pid, bcd) (((pid) & 0xffff) << 16 | ((bcd) & 0xffff))
+#define PD_PRODUCT_PID(vdo) (((vdo) >> 16) & 0xffff)
+
+/*
+ * Cable VDO
+ * ---------
+ * <31:28> :: Cable HW version
+ * <27:24> :: Cable FW version
+ * <23:20> :: Reserved, Shall be set to zero
+ * <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C)
+ * <17> :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle)
+ * <16:13> :: cable latency (0001 == <10ns(~1m length))
+ * <12:11> :: cable termination type (11b == both ends active VCONN req)
+ * <10> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
+ * <9> :: SSTX2 Directionality support
+ * <8> :: SSRX1 Directionality support
+ * <7> :: SSRX2 Directionality support
+ * <6:5> :: Vbus current handling capability
+ * <4> :: Vbus through cable (0b == no, 1b == yes)
+ * <3> :: SOP" controller present? (0b == no, 1b == yes)
+ * <2:0> :: USB SS Signaling support
+ */
+#define CABLE_ATYPE 0
+#define CABLE_BTYPE 1
+#define CABLE_CTYPE 2
+#define CABLE_PLUG 0
+#define CABLE_RECEPTACLE 1
+#define CABLE_CURR_1A5 0
+#define CABLE_CURR_3A 1
+#define CABLE_CURR_5A 2
+#define CABLE_USBSS_U2_ONLY 0
+#define CABLE_USBSS_U31_GEN1 1
+#define CABLE_USBSS_U31_GEN2 2
+#define VDO_CABLE(hw, fw, cbl, gdr, lat, term, tx1d, tx2d, rx1d, rx2d, cur,\
+ vps, sopp, usbss) \
+ (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18 \
+ | (gdr) << 17 | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11 \
+ | (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 \
+ | ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3 \
+ | ((usbss) & 0x7))
+
+/*
+ * AMA VDO
+ * ---------
+ * <31:28> :: Cable HW version
+ * <27:24> :: Cable FW version
+ * <23:12> :: Reserved, Shall be set to zero
+ * <11> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
+ * <10> :: SSTX2 Directionality support
+ * <9> :: SSRX1 Directionality support
+ * <8> :: SSRX2 Directionality support
+ * <7:5> :: Vconn power
+ * <4> :: Vconn power required
+ * <3> :: Vbus power required
+ * <2:0> :: USB SS Signaling support
+ */
+#define VDO_AMA(hw, fw, tx1d, tx2d, rx1d, rx2d, vcpwr, vcr, vbr, usbss) \
+ (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 \
+ | (tx1d) << 11 | (tx2d) << 10 | (rx1d) << 9 | (rx2d) << 8 \
+ | ((vcpwr) & 0x7) << 5 | (vcr) << 4 | (vbr) << 3 \
+ | ((usbss) & 0x7))
+
+#define PD_VDO_AMA_VCONN_REQ(vdo) (((vdo) >> 4) & 1)
+#define PD_VDO_AMA_VBUS_REQ(vdo) (((vdo) >> 3) & 1)
+
+#define AMA_VCONN_PWR_1W 0
+#define AMA_VCONN_PWR_1W5 1
+#define AMA_VCONN_PWR_2W 2
+#define AMA_VCONN_PWR_3W 3
+#define AMA_VCONN_PWR_4W 4
+#define AMA_VCONN_PWR_5W 5
+#define AMA_VCONN_PWR_6W 6
+#define AMA_USBSS_U2_ONLY 0
+#define AMA_USBSS_U31_GEN1 1
+#define AMA_USBSS_U31_GEN2 2
+#define AMA_USBSS_BBONLY 3
+
+/*
+ * SVDM Discover SVIDs request -> response
+ *
+ * Request is properly formatted VDM Header with discover SVIDs command.
+ * Response is a set of SVIDs of all all supported SVIDs with all zero's to
+ * mark the end of SVIDs. If more than 12 SVIDs are supported command SHOULD be
+ * repeated.
+ */
+#define VDO_SVID(svid0, svid1) (((svid0) & 0xffff) << 16 | ((svid1) & 0xffff))
+#define PD_VDO_SVID_SVID0(vdo) ((vdo) >> 16)
+#define PD_VDO_SVID_SVID1(vdo) ((vdo) & 0xffff)
+
+/* USB-IF SIDs */
+#define USB_SID_PD 0xff00 /* power delivery */
+#define USB_SID_DISPLAYPORT 0xff01
+#define USB_SID_MHL 0xff02 /* Mobile High-Definition Link */
+
+/* VDM command timeouts (in ms) */
+
+#define PD_T_VDM_UNSTRUCTURED 500
+#define PD_T_VDM_BUSY 100
+#define PD_T_VDM_WAIT_MODE_E 100
+#define PD_T_VDM_SNDR_RSP 30
+#define PD_T_VDM_E_MODE 25
+#define PD_T_VDM_RCVR_RSP 15
+
+#endif /* __LINUX_USB_PD_VDO_H */
diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c
new file mode 100644
index 0000000..319de9c
--- /dev/null
+++ b/drivers/usb/typec/tcpm.c
@@ -0,0 +1,3603 @@
+/*
+ * Copyright 2015-2017 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * USB Power Delivery protocol stack.
+ */
+
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/usb/typec.h>
+#include <linux/workqueue.h>
+
+#include "pd.h"
+#include "pd_vdo.h"
+#include "pd_bdo.h"
+#include "tcpm.h"
+
+#define FOREACH_STATE(S) \
+ S(INVALID_STATE), \
+ S(DRP_TOGGLING), \
+ S(SRC_UNATTACHED), \
+ S(SRC_ATTACH_WAIT), \
+ S(SRC_ATTACHED), \
+ S(SRC_STARTUP), \
+ S(SRC_SEND_CAPABILITIES), \
+ S(SRC_NEGOTIATE_CAPABILITIES), \
+ S(SRC_TRANSITION_SUPPLY), \
+ S(SRC_READY), \
+ S(SRC_WAIT_NEW_CAPABILITIES), \
+ \
+ S(SNK_UNATTACHED), \
+ S(SNK_ATTACH_WAIT), \
+ S(SNK_DEBOUNCED), \
+ S(SNK_ATTACHED), \
+ S(SNK_STARTUP), \
+ S(SNK_DISCOVERY), \
+ S(SNK_DISCOVERY_DEBOUNCE), \
+ S(SNK_DISCOVERY_DEBOUNCE_DONE), \
+ S(SNK_WAIT_CAPABILITIES), \
+ S(SNK_NEGOTIATE_CAPABILITIES), \
+ S(SNK_TRANSITION_SINK), \
+ S(SNK_TRANSITION_SINK_VBUS), \
+ S(SNK_READY), \
+ \
+ S(ACC_UNATTACHED), \
+ S(DEBUG_ACC_ATTACHED), \
+ S(AUDIO_ACC_ATTACHED), \
+ S(AUDIO_ACC_DEBOUNCE), \
+ \
+ S(HARD_RESET_SEND), \
+ S(HARD_RESET_START), \
+ S(SRC_HARD_RESET_VBUS_OFF), \
+ S(SRC_HARD_RESET_VBUS_ON), \
+ S(SNK_HARD_RESET_SINK_OFF), \
+ S(SNK_HARD_RESET_WAIT_VBUS), \
+ S(SNK_HARD_RESET_SINK_ON), \
+ \
+ S(SOFT_RESET), \
+ S(SOFT_RESET_SEND), \
+ \
+ S(DR_SWAP_ACCEPT), \
+ S(DR_SWAP_SEND), \
+ S(DR_SWAP_SEND_TIMEOUT), \
+ S(DR_SWAP_CANCEL), \
+ S(DR_SWAP_CHANGE_DR), \
+ \
+ S(PR_SWAP_ACCEPT), \
+ S(PR_SWAP_SEND), \
+ S(PR_SWAP_SEND_TIMEOUT), \
+ S(PR_SWAP_CANCEL), \
+ S(PR_SWAP_START), \
+ S(PR_SWAP_SRC_SNK_TRANSITION_OFF), \
+ S(PR_SWAP_SRC_SNK_SOURCE_OFF), \
+ S(PR_SWAP_SRC_SNK_SINK_ON), \
+ S(PR_SWAP_SNK_SRC_SINK_OFF), \
+ S(PR_SWAP_SNK_SRC_SOURCE_ON), \
+ \
+ S(VCONN_SWAP_ACCEPT), \
+ S(VCONN_SWAP_SEND), \
+ S(VCONN_SWAP_SEND_TIMEOUT), \
+ S(VCONN_SWAP_CANCEL), \
+ S(VCONN_SWAP_START), \
+ S(VCONN_SWAP_WAIT_FOR_VCONN), \
+ S(VCONN_SWAP_TURN_ON_VCONN), \
+ S(VCONN_SWAP_TURN_OFF_VCONN), \
+ \
+ S(SNK_TRY), \
+ S(SNK_TRY_WAIT), \
+ S(SNK_TRY_WAIT_DEBOUNCE), \
+ S(SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS), \
+ S(SRC_TRYWAIT), \
+ S(SRC_TRYWAIT_DEBOUNCE), \
+ S(SRC_TRYWAIT_UNATTACHED), \
+ \
+ S(SRC_TRY), \
+ S(SRC_TRY_WAIT), \
+ S(SRC_TRY_DEBOUNCE), \
+ S(SNK_TRYWAIT), \
+ S(SNK_TRYWAIT_DEBOUNCE), \
+ S(SNK_TRYWAIT_VBUS), \
+ S(BIST_RX), \
+ \
+ S(ERROR_RECOVERY), \
+ S(PORT_RESET), \
+ S(PORT_RESET_WAIT_OFF)
+
+#define GENERATE_ENUM(e) e
+#define GENERATE_STRING(s) #s
+
+enum tcpm_state {
+ FOREACH_STATE(GENERATE_ENUM)
+};
+
+static const char * const tcpm_states[] = {
+ FOREACH_STATE(GENERATE_STRING)
+};
+
+enum vdm_states {
+ VDM_STATE_ERR_BUSY = -3,
+ VDM_STATE_ERR_SEND = -2,
+ VDM_STATE_ERR_TMOUT = -1,
+ VDM_STATE_DONE = 0,
+ /* Anything >0 represents an active state */
+ VDM_STATE_READY = 1,
+ VDM_STATE_BUSY = 2,
+ VDM_STATE_WAIT_RSP_BUSY = 3,
+};
+
+enum pd_msg_request {
+ PD_MSG_NONE = 0,
+ PD_MSG_CTRL_REJECT,
+ PD_MSG_CTRL_WAIT,
+ PD_MSG_DATA_SINK_CAP,
+ PD_MSG_DATA_SOURCE_CAP,
+};
+
+/* Events from low level driver */
+
+#define TCPM_CC_EVENT BIT(0)
+#define TCPM_VBUS_EVENT BIT(1)
+#define TCPM_RESET_EVENT BIT(2)
+
+#define LOG_BUFFER_ENTRIES 1024
+#define LOG_BUFFER_ENTRY_SIZE 128
+
+/* Alternate mode support */
+
+#define SVID_DISCOVERY_MAX 16
+
+struct pd_mode_data {
+ int svid_index; /* current SVID index */
+ int nsvids;
+ u16 svids[SVID_DISCOVERY_MAX];
+ int altmodes; /* number of alternate modes */
+ struct typec_altmode_desc altmode_desc[SVID_DISCOVERY_MAX];
+};
+
+struct tcpm_port {
+ struct device *dev;
+
+ struct mutex lock; /* tcpm state machine lock */
+ struct workqueue_struct *wq;
+
+ struct typec_capability typec_caps;
+ struct typec_port *typec_port;
+
+ struct tcpc_dev *tcpc;
+
+ enum typec_role vconn_role;
+ enum typec_role pwr_role;
+ enum typec_data_role data_role;
+ enum typec_pwr_opmode pwr_opmode;
+
+ struct usb_pd_identity partner_ident;
+ struct typec_partner_desc partner_desc;
+ struct typec_partner *partner;
+
+ enum typec_cc_status cc_req;
+
+ enum typec_cc_status cc1;
+ enum typec_cc_status cc2;
+ enum typec_cc_polarity polarity;
+
+ bool attached;
+ bool connected;
+ enum typec_port_type port_type;
+ bool vbus_present;
+ bool vbus_never_low;
+ bool vbus_source;
+ bool vbus_charge;
+
+ bool send_discover;
+ bool op_vsafe5v;
+
+ int try_role;
+ int try_snk_count;
+ int try_src_count;
+
+ enum pd_msg_request queued_message;
+
+ enum tcpm_state enter_state;
+ enum tcpm_state prev_state;
+ enum tcpm_state state;
+ enum tcpm_state delayed_state;
+ unsigned long delayed_runtime;
+ unsigned long delay_ms;
+
+ spinlock_t pd_event_lock;
+ u32 pd_events;
+
+ struct work_struct event_work;
+ struct delayed_work state_machine;
+ struct delayed_work vdm_state_machine;
+ bool state_machine_running;
+
+ struct completion tx_complete;
+ enum tcpm_transmit_status tx_status;
+
+ struct mutex swap_lock; /* swap command lock */
+ bool swap_pending;
+ bool non_pd_role_swap;
+ struct completion swap_complete;
+ int swap_status;
+
+ unsigned int message_id;
+ unsigned int caps_count;
+ unsigned int hard_reset_count;
+ bool pd_capable;
+ bool explicit_contract;
+ unsigned int rx_msgid;
+
+ /* Partner capabilities/requests */
+ u32 sink_request;
+ u32 source_caps[PDO_MAX_OBJECTS];
+ unsigned int nr_source_caps;
+ u32 sink_caps[PDO_MAX_OBJECTS];
+ unsigned int nr_sink_caps;
+
+ /* Local capabilities */
+ u32 src_pdo[PDO_MAX_OBJECTS];
+ unsigned int nr_src_pdo;
+ u32 snk_pdo[PDO_MAX_OBJECTS];
+ unsigned int nr_snk_pdo;
+ u32 snk_vdo[VDO_MAX_OBJECTS];
+ unsigned int nr_snk_vdo;
+
+ unsigned int max_snk_mv;
+ unsigned int max_snk_ma;
+ unsigned int max_snk_mw;
+ unsigned int operating_snk_mw;
+
+ /* Requested current / voltage */
+ u32 current_limit;
+ u32 supply_voltage;
+
+ u32 bist_request;
+
+ /* PD state for Vendor Defined Messages */
+ enum vdm_states vdm_state;
+ u32 vdm_retries;
+ /* next Vendor Defined Message to send */
+ u32 vdo_data[VDO_MAX_SIZE];
+ u8 vdo_count;
+ /* VDO to retry if UFP responder replied busy */
+ u32 vdo_retry;
+
+ /* Alternate mode data */
+
+ struct pd_mode_data mode_data;
+ struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX];
+ struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX];
+
+ /* Deadline in jiffies to exit src_try_wait state */
+ unsigned long max_wait;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+ struct mutex logbuffer_lock; /* log buffer access lock */
+ int logbuffer_head;
+ int logbuffer_tail;
+ u8 *logbuffer[LOG_BUFFER_ENTRIES];
+#endif
+};
+
+struct pd_rx_event {
+ struct work_struct work;
+ struct tcpm_port *port;
+ struct pd_message msg;
+};
+
+#define tcpm_cc_is_sink(cc) \
+ ((cc) == TYPEC_CC_RP_DEF || (cc) == TYPEC_CC_RP_1_5 || \
+ (cc) == TYPEC_CC_RP_3_0)
+
+#define tcpm_port_is_sink(port) \
+ ((tcpm_cc_is_sink((port)->cc1) && !tcpm_cc_is_sink((port)->cc2)) || \
+ (tcpm_cc_is_sink((port)->cc2) && !tcpm_cc_is_sink((port)->cc1)))
+
+#define tcpm_cc_is_source(cc) ((cc) == TYPEC_CC_RD)
+#define tcpm_cc_is_audio(cc) ((cc) == TYPEC_CC_RA)
+#define tcpm_cc_is_open(cc) ((cc) == TYPEC_CC_OPEN)
+
+#define tcpm_port_is_source(port) \
+ ((tcpm_cc_is_source((port)->cc1) && \
+ !tcpm_cc_is_source((port)->cc2)) || \
+ (tcpm_cc_is_source((port)->cc2) && \
+ !tcpm_cc_is_source((port)->cc1)))
+
+#define tcpm_port_is_debug(port) \
+ (tcpm_cc_is_source((port)->cc1) && tcpm_cc_is_source((port)->cc2))
+
+#define tcpm_port_is_audio(port) \
+ (tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_audio((port)->cc2))
+
+#define tcpm_port_is_audio_detached(port) \
+ ((tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_open((port)->cc2)) || \
+ (tcpm_cc_is_audio((port)->cc2) && tcpm_cc_is_open((port)->cc1)))
+
+#define tcpm_try_snk(port) \
+ ((port)->try_snk_count == 0 && (port)->try_role == TYPEC_SINK && \
+ (port)->port_type == TYPEC_PORT_DRP)
+
+#define tcpm_try_src(port) \
+ ((port)->try_src_count == 0 && (port)->try_role == TYPEC_SOURCE && \
+ port->port_type == TYPEC_PORT_DRP)
+
+static enum tcpm_state tcpm_default_state(struct tcpm_port *port)
+{
+ if (port->port_type == TYPEC_PORT_DRP) {
+ if (port->try_role == TYPEC_SINK)
+ return SNK_UNATTACHED;
+ else if (port->try_role == TYPEC_SOURCE)
+ return SRC_UNATTACHED;
+ else if (port->tcpc->config->default_role == TYPEC_SINK)
+ return SNK_UNATTACHED;
+ /* Fall through to return SRC_UNATTACHED */
+ } else if (port->port_type == TYPEC_PORT_UFP) {
+ return SNK_UNATTACHED;
+ }
+ return SRC_UNATTACHED;
+}
+
+extern bool IsPRSwap;
+
+static inline
+struct tcpm_port *typec_cap_to_tcpm(const struct typec_capability *cap)
+{
+ return container_of(cap, struct tcpm_port, typec_caps);
+}
+
+static bool tcpm_port_is_disconnected(struct tcpm_port *port)
+{
+ return (!port->attached && port->cc1 == TYPEC_CC_OPEN &&
+ port->cc2 == TYPEC_CC_OPEN) ||
+ (port->attached && ((port->polarity == TYPEC_POLARITY_CC1 &&
+ port->cc1 == TYPEC_CC_OPEN) ||
+ (port->polarity == TYPEC_POLARITY_CC2 &&
+ port->cc2 == TYPEC_CC_OPEN)));
+}
+
+/*
+ * Logging
+ */
+
+#ifdef CONFIG_DEBUG_FS
+
+static void _tcpm_log(struct tcpm_port *port, const char *fmt, va_list args)
+{
+ char tmpbuffer[LOG_BUFFER_ENTRY_SIZE];
+ u64 ts_nsec = local_clock();
+ unsigned long rem_nsec;
+
+ if (!port->logbuffer[port->logbuffer_head]) {
+ port->logbuffer[port->logbuffer_head] =
+ kzalloc(LOG_BUFFER_ENTRY_SIZE, GFP_KERNEL);
+ if (!port->logbuffer[port->logbuffer_head])
+ return;
+ }
+
+ vsnprintf(tmpbuffer, sizeof(tmpbuffer), fmt, args);
+
+ mutex_lock(&port->logbuffer_lock);
+
+ if (port->logbuffer_head < 0 ||
+ port->logbuffer_head >= LOG_BUFFER_ENTRIES) {
+ dev_warn(port->dev,
+ "Bad log buffer index %d\n", port->logbuffer_head);
+ goto abort;
+ }
+
+ if (!port->logbuffer[port->logbuffer_head]) {
+ dev_warn(port->dev,
+ "Log buffer index %d is NULL\n", port->logbuffer_head);
+ goto abort;
+ }
+
+ rem_nsec = do_div(ts_nsec, 1000000000);
+ scnprintf(port->logbuffer[port->logbuffer_head],
+ LOG_BUFFER_ENTRY_SIZE, "[%5lu.%06lu] %s",
+ (unsigned long)ts_nsec, rem_nsec / 1000,
+ tmpbuffer);
+ port->logbuffer_head = (port->logbuffer_head + 1) % LOG_BUFFER_ENTRIES;
+ if (port->logbuffer_head == port->logbuffer_tail)
+ port->logbuffer_tail =
+ (port->logbuffer_tail + 1) % LOG_BUFFER_ENTRIES;
+
+abort:
+ mutex_unlock(&port->logbuffer_lock);
+}
+
+static void tcpm_log(struct tcpm_port *port, const char *fmt, ...)
+{
+ va_list args;
+
+ /* Do not log while disconnected and unattached */
+ if (tcpm_port_is_disconnected(port) &&
+ (port->state == SRC_UNATTACHED || port->state == SNK_UNATTACHED ||
+ port->state == DRP_TOGGLING))
+ return;
+
+ va_start(args, fmt);
+ _tcpm_log(port, fmt, args);
+ va_end(args);
+}
+
+static void tcpm_log_force(struct tcpm_port *port, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ _tcpm_log(port, fmt, args);
+ va_end(args);
+}
+
+static void tcpm_log_source_caps(struct tcpm_port *port)
+{
+ int i;
+
+ for (i = 0; i < port->nr_source_caps; i++) {
+ u32 pdo = port->source_caps[i];
+ enum pd_pdo_type type = pdo_type(pdo);
+ char msg[64];
+
+ switch (type) {
+ case PDO_TYPE_FIXED:
+ scnprintf(msg, sizeof(msg),
+ "%u mV, %u mA [%s%s%s%s%s%s]",
+ pdo_fixed_voltage(pdo),
+ pdo_max_current(pdo),
+ (pdo & PDO_FIXED_DUAL_ROLE) ?
+ "R" : "",
+ (pdo & PDO_FIXED_SUSPEND) ?
+ "S" : "",
+ (pdo & PDO_FIXED_HIGHER_CAP) ?
+ "H" : "",
+ (pdo & PDO_FIXED_USB_COMM) ?
+ "U" : "",
+ (pdo & PDO_FIXED_DATA_SWAP) ?
+ "D" : "",
+ (pdo & PDO_FIXED_EXTPOWER) ?
+ "E" : "");
+ break;
+ case PDO_TYPE_VAR:
+ scnprintf(msg, sizeof(msg),
+ "%u-%u mV, %u mA",
+ pdo_min_voltage(pdo),
+ pdo_max_voltage(pdo),
+ pdo_max_current(pdo));
+ break;
+ case PDO_TYPE_BATT:
+ scnprintf(msg, sizeof(msg),
+ "%u-%u mV, %u mW",
+ pdo_min_voltage(pdo),
+ pdo_max_voltage(pdo),
+ pdo_max_power(pdo));
+ break;
+ default:
+ strlcpy(msg, "undefined", sizeof(msg));
+ break;
+ }
+ tcpm_log(port, " PDO %d: type %d, %s",
+ i, type, msg);
+ }
+}
+
+static int tcpm_seq_show(struct seq_file *s, void *v)
+{
+ struct tcpm_port *port = (struct tcpm_port *)s->private;
+ int tail;
+
+ mutex_lock(&port->logbuffer_lock);
+ tail = port->logbuffer_tail;
+ while (tail != port->logbuffer_head) {
+ if (seq_printf(s, "%s\n", port->logbuffer[tail]) < 0)
+ goto abort;
+ tail = (tail + 1) % LOG_BUFFER_ENTRIES;
+ }
+ port->logbuffer_tail = tail;
+abort:
+ mutex_unlock(&port->logbuffer_lock);
+
+ return 0;
+}
+
+static int tcpm_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tcpm_seq_show, inode->i_private);
+}
+
+static const struct file_operations tcpm_debug_operations = {
+ .open = tcpm_debug_open,
+ .llseek = seq_lseek,
+ .read = seq_read,
+ .release = single_release,
+};
+
+static struct dentry *rootdir;
+
+static int tcpm_debugfs_init(struct tcpm_port *port)
+{
+ mutex_init(&port->logbuffer_lock);
+ /* /sys/kernel/debug/tcpm/usbcX */
+ if (!rootdir) {
+ rootdir = debugfs_create_dir("tcpm", NULL);
+ if (!rootdir)
+ return -ENOMEM;
+ }
+
+ port->dentry = debugfs_create_file(dev_name(port->dev),
+ S_IFREG | 0444, rootdir,
+ port, &tcpm_debug_operations);
+
+ return 0;
+}
+
+static void tcpm_debugfs_exit(struct tcpm_port *port)
+{
+ debugfs_remove(port->dentry);
+}
+
+#else
+
+static void tcpm_log(const struct tcpm_port *port, const char *fmt, ...) { }
+static void tcpm_log_force(struct tcpm_port *port, const char *fmt, ...) { }
+static void tcpm_log_source_caps(struct tcpm_port *port) { }
+static int tcpm_debugfs_init(const struct tcpm_port *port) { return 0; }
+static void tcpm_debugfs_exit(const struct tcpm_port *port) { }
+
+#endif
+
+static int tcpm_pd_transmit(struct tcpm_port *port,
+ enum tcpm_transmit_type type,
+ const struct pd_message *msg)
+{
+ unsigned long timeout;
+ int ret;
+
+ if (msg)
+ tcpm_log(port, "PD TX, header: %#x", le16_to_cpu(msg->header));
+ else
+ tcpm_log(port, "PD TX, type: %#x", type);
+
+ reinit_completion(&port->tx_complete);
+ ret = port->tcpc->pd_transmit(port->tcpc, type, msg);
+ if (ret < 0)
+ return ret;
+
+ mutex_unlock(&port->lock);
+ timeout = wait_for_completion_timeout(&port->tx_complete,
+ msecs_to_jiffies(PD_T_TCPC_TX_TIMEOUT));
+ mutex_lock(&port->lock);
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ switch (port->tx_status) {
+ case TCPC_TX_SUCCESS:
+ port->message_id = (port->message_id + 1) & PD_HEADER_ID_MASK;
+ return 0;
+ case TCPC_TX_DISCARDED:
+ return -EAGAIN;
+ case TCPC_TX_FAILED:
+ default:
+ return -EIO;
+ }
+}
+
+void tcpm_pd_transmit_complete(struct tcpm_port *port,
+ enum tcpm_transmit_status status)
+{
+ tcpm_log(port, "PD TX complete, status: %u", status);
+ port->tx_status = status;
+ complete(&port->tx_complete);
+}
+EXPORT_SYMBOL_GPL(tcpm_pd_transmit_complete);
+
+static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode,
+ enum tcpc_usb_switch config)
+{
+ int ret = 0;
+
+ tcpm_log(port, "Requesting mux mode %d, config %d, polarity %d",
+ mode, config, port->polarity);
+
+ if (port->tcpc->mux)
+ ret = port->tcpc->mux->set(port->tcpc->mux, mode, config,
+ port->polarity);
+
+ return ret;
+}
+
+static int tcpm_set_polarity(struct tcpm_port *port,
+ enum typec_cc_polarity polarity)
+{
+ int ret;
+
+ tcpm_log(port, "polarity %d", polarity);
+
+ ret = port->tcpc->set_polarity(port->tcpc, polarity);
+ if (ret < 0)
+ return ret;
+
+ port->polarity = polarity;
+
+ return 0;
+}
+
+static int tcpm_set_vconn(struct tcpm_port *port, bool enable)
+{
+ int ret;
+
+ tcpm_log(port, "vconn:=%d", enable);
+
+ ret = port->tcpc->set_vconn(port->tcpc, enable);
+ if (!ret) {
+ port->vconn_role = enable ? TYPEC_SOURCE : TYPEC_SINK;
+ typec_set_vconn_role(port->typec_port, port->vconn_role);
+ }
+
+ return ret;
+}
+
+static u32 tcpm_get_current_limit(struct tcpm_port *port)
+{
+ enum typec_cc_status cc;
+ u32 limit;
+
+ cc = port->polarity ? port->cc2 : port->cc1;
+ switch (cc) {
+ case TYPEC_CC_RP_1_5:
+ limit = 1500;
+ break;
+ case TYPEC_CC_RP_3_0:
+ limit = 3000;
+ break;
+ case TYPEC_CC_RP_DEF:
+ default:
+ limit = 0;
+ break;
+ }
+
+ return limit;
+}
+
+static int tcpm_set_current_limit(struct tcpm_port *port, u32 max_ma, u32 mv)
+{
+ int ret = -EOPNOTSUPP;
+
+ tcpm_log(port, "Setting voltage/current limit %u mV %u mA", mv, max_ma);
+
+ if (port->tcpc->set_current_limit)
+ ret = port->tcpc->set_current_limit(port->tcpc, max_ma, mv);
+
+ return ret;
+}
+
+/*
+ * Determine RP value to set based on maximum current supported
+ * by a port if configured as source.
+ * Returns CC value to report to link partner.
+ */
+static enum typec_cc_status tcpm_rp_cc(struct tcpm_port *port)
+{
+ const u32 *src_pdo = port->src_pdo;
+ int nr_pdo = port->nr_src_pdo;
+ int i;
+
+ /*
+ * Search for first entry with matching voltage.
+ * It should report the maximum supported current.
+ */
+ for (i = 0; i < nr_pdo; i++) {
+ const u32 pdo = src_pdo[i];
+
+ if (pdo_type(pdo) == PDO_TYPE_FIXED &&
+ pdo_fixed_voltage(pdo) == 5000) {
+ unsigned int curr = pdo_max_current(pdo);
+
+ if (curr >= 3000)
+ return TYPEC_CC_RP_3_0;
+ else if (curr >= 1500)
+ return TYPEC_CC_RP_1_5;
+ return TYPEC_CC_RP_DEF;
+ }
+ }
+
+ return TYPEC_CC_RP_DEF;
+}
+
+static int tcpm_set_attached_state(struct tcpm_port *port, bool attached)
+{
+ return port->tcpc->set_roles(port->tcpc, attached, port->pwr_role,
+ port->data_role);
+}
+
+static int tcpm_set_roles(struct tcpm_port *port, bool attached,
+ enum typec_role role, enum typec_data_role data)
+{
+ int ret;
+
+ if (data == TYPEC_HOST)
+ ret = tcpm_mux_set(port, TYPEC_MUX_USB,
+ TCPC_USB_SWITCH_CONNECT);
+ else
+ ret = tcpm_mux_set(port, TYPEC_MUX_NONE,
+ TCPC_USB_SWITCH_DISCONNECT);
+ if (ret < 0)
+ return ret;
+
+ ret = port->tcpc->set_roles(port->tcpc, attached, role, data);
+ if (ret < 0)
+ return ret;
+
+ port->pwr_role = role;
+ port->data_role = data;
+ typec_set_data_role(port->typec_port, data);
+ typec_set_pwr_role(port->typec_port, role);
+
+ return 0;
+}
+
+static int tcpm_set_pwr_role(struct tcpm_port *port, enum typec_role role)
+{
+ int ret;
+
+ ret = port->tcpc->set_roles(port->tcpc, true, role,
+ port->data_role);
+ if (ret < 0)
+ return ret;
+
+ port->pwr_role = role;
+ typec_set_pwr_role(port->typec_port, role);
+
+ return 0;
+}
+
+static int tcpm_pd_send_source_caps(struct tcpm_port *port)
+{
+ struct pd_message msg;
+ int i;
+
+ memset(&msg, 0, sizeof(msg));
+ if (!port->nr_src_pdo) {
+ /* No source capabilities defined, sink only */
+ msg.header = PD_HEADER_LE(PD_CTRL_REJECT,
+ port->pwr_role,
+ port->data_role,
+ port->message_id, 0);
+ } else {
+ msg.header = PD_HEADER_LE(PD_DATA_SOURCE_CAP,
+ port->pwr_role,
+ port->data_role,
+ port->message_id,
+ port->nr_src_pdo);
+ }
+ for (i = 0; i < port->nr_src_pdo; i++)
+ msg.payload[i] = cpu_to_le32(port->src_pdo[i]);
+
+ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
+}
+
+static int tcpm_pd_send_sink_caps(struct tcpm_port *port)
+{
+ struct pd_message msg;
+ int i;
+
+ memset(&msg, 0, sizeof(msg));
+ if (!port->nr_snk_pdo) {
+ /* No sink capabilities defined, source only */
+ msg.header = PD_HEADER_LE(PD_CTRL_REJECT,
+ port->pwr_role,
+ port->data_role,
+ port->message_id, 0);
+ } else {
+ msg.header = PD_HEADER_LE(PD_DATA_SINK_CAP,
+ port->pwr_role,
+ port->data_role,
+ port->message_id,
+ port->nr_snk_pdo);
+ }
+ for (i = 0; i < port->nr_snk_pdo; i++)
+ msg.payload[i] = cpu_to_le32(port->snk_pdo[i]);
+
+ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
+}
+
+static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state,
+ unsigned int delay_ms)
+{
+ if (delay_ms) {
+ tcpm_log(port, "pending state change %s -> %s @ %u ms",
+ tcpm_states[port->state], tcpm_states[state],
+ delay_ms);
+ port->delayed_state = state;
+ mod_delayed_work(port->wq, &port->state_machine,
+ msecs_to_jiffies(delay_ms));
+ port->delayed_runtime = jiffies + msecs_to_jiffies(delay_ms);
+ port->delay_ms = delay_ms;
+ } else {
+ tcpm_log(port, "state change %s -> %s",
+ tcpm_states[port->state], tcpm_states[state]);
+ port->delayed_state = INVALID_STATE;
+ port->prev_state = port->state;
+ port->state = state;
+ /*
+ * Don't re-queue the state machine work item if we're currently
+ * in the state machine and we're immediately changing states.
+ * tcpm_state_machine_work() will continue running the state
+ * machine.
+ */
+ if (!port->state_machine_running)
+ mod_delayed_work(port->wq, &port->state_machine, 0);
+ }
+}
+
+static void tcpm_set_state_cond(struct tcpm_port *port, enum tcpm_state state,
+ unsigned int delay_ms)
+{
+ if (port->enter_state == port->state)
+ tcpm_set_state(port, state, delay_ms);
+ else
+ tcpm_log(port,
+ "skipped %sstate change %s -> %s [%u ms], context state %s",
+ delay_ms ? "delayed " : "",
+ tcpm_states[port->state], tcpm_states[state],
+ delay_ms, tcpm_states[port->enter_state]);
+}
+
+static void tcpm_queue_message(struct tcpm_port *port,
+ enum pd_msg_request message)
+{
+ port->queued_message = message;
+ mod_delayed_work(port->wq, &port->state_machine, 0);
+}
+
+/*
+ * VDM/VDO handling functions
+ */
+static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header,
+ const u32 *data, int cnt)
+{
+ port->vdo_count = cnt + 1;
+ port->vdo_data[0] = header;
+ memcpy(&port->vdo_data[1], data, sizeof(u32) * cnt);
+ /* Set ready, vdm state machine will actually send */
+ port->vdm_retries = 0;
+ port->vdm_state = VDM_STATE_READY;
+}
+
+static void svdm_consume_identity(struct tcpm_port *port, const __le32 *payload,
+ int cnt)
+{
+ u32 vdo = le32_to_cpu(payload[VDO_INDEX_IDH]);
+ u32 product = le32_to_cpu(payload[VDO_INDEX_PRODUCT]);
+
+ memset(&port->mode_data, 0, sizeof(port->mode_data));
+
+#if 0 /* Not really a match */
+ switch (PD_IDH_PTYPE(vdo)) {
+ case IDH_PTYPE_UNDEF:
+ port->partner.type = TYPEC_PARTNER_NONE; /* no longer exists */
+ break;
+ case IDH_PTYPE_HUB:
+ break;
+ case IDH_PTYPE_PERIPH:
+ break;
+ case IDH_PTYPE_PCABLE:
+ break;
+ case IDH_PTYPE_ACABLE:
+ break;
+ case IDH_PTYPE_AMA:
+ port->partner.type = TYPEC_PARTNER_ALTMODE;
+ break;
+ default:
+ break;
+ }
+#endif
+
+ port->partner_ident.id_header = vdo;
+ port->partner_ident.cert_stat = le32_to_cpu(payload[VDO_INDEX_CSTAT]);
+ port->partner_ident.product = product;
+
+ typec_partner_set_identity(port->partner);
+
+ tcpm_log(port, "Identity: %04x:%04x.%04x",
+ PD_IDH_VID(vdo),
+ PD_PRODUCT_PID(product), product & 0xffff);
+}
+
+static bool svdm_consume_svids(struct tcpm_port *port, const __le32 *payload,
+ int cnt)
+{
+ struct pd_mode_data *pmdata = &port->mode_data;
+ int i;
+
+ for (i = 1; i < cnt; i++) {
+ u32 p = le32_to_cpu(payload[i]);
+ u16 svid;
+
+ svid = (p >> 16) & 0xffff;
+ if (!svid)
+ return false;
+
+ if (pmdata->nsvids >= SVID_DISCOVERY_MAX)
+ goto abort;
+
+ pmdata->svids[pmdata->nsvids++] = svid;
+ tcpm_log(port, "SVID %d: 0x%x", pmdata->nsvids, svid);
+
+ svid = p & 0xffff;
+ if (!svid)
+ return false;
+
+ if (pmdata->nsvids >= SVID_DISCOVERY_MAX)
+ goto abort;
+
+ pmdata->svids[pmdata->nsvids++] = svid;
+ tcpm_log(port, "SVID %d: 0x%x", pmdata->nsvids, svid);
+ }
+ return true;
+abort:
+ tcpm_log(port, "SVID_DISCOVERY_MAX(%d) too low!", SVID_DISCOVERY_MAX);
+ return false;
+}
+
+static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
+ int cnt)
+{
+ struct pd_mode_data *pmdata = &port->mode_data;
+ struct typec_altmode_desc *paltmode;
+ struct typec_mode_desc *pmode;
+ int i;
+
+ if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
+ /* Already logged in svdm_consume_svids() */
+ return;
+ }
+
+ paltmode = &pmdata->altmode_desc[pmdata->altmodes];
+ memset(paltmode, 0, sizeof(*paltmode));
+
+ paltmode->svid = pmdata->svids[pmdata->svid_index];
+
+ tcpm_log(port, " Alternate mode %d: SVID 0x%04x",
+ pmdata->altmodes, paltmode->svid);
+
+ for (i = 1; i < cnt && paltmode->n_modes < ALTMODE_MAX_MODES; i++) {
+ pmode = &paltmode->modes[paltmode->n_modes];
+ memset(pmode, 0, sizeof(*pmode));
+ pmode->vdo = le32_to_cpu(payload[i]);
+ pmode->index = i - 1;
+ paltmode->n_modes++;
+ tcpm_log(port, " VDO %d: 0x%08x",
+ pmode->index, pmode->vdo);
+ }
+ port->partner_altmode[pmdata->altmodes] =
+ typec_partner_register_altmode(port->partner, paltmode);
+ if (port->partner_altmode[pmdata->altmodes] == NULL) {
+ tcpm_log(port,
+ "Failed to register alternate modes for SVID 0x%04x",
+ paltmode->svid);
+ return;
+ }
+ pmdata->altmodes++;
+}
+
+#define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
+
+static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
+ u32 *response)
+{
+ u32 p0 = le32_to_cpu(payload[0]);
+ int cmd_type = PD_VDO_CMDT(p0);
+ int cmd = PD_VDO_CMD(p0);
+ struct pd_mode_data *modep;
+ int rlen = 0;
+ u16 svid;
+ int i;
+
+ tcpm_log(port, "Rx VDM cmd 0x%x type %d cmd %d len %d",
+ p0, cmd_type, cmd, cnt);
+
+ modep = &port->mode_data;
+
+ switch (cmd_type) {
+ case CMDT_INIT:
+ switch (cmd) {
+ case CMD_DISCOVER_IDENT:
+ /* 6.4.4.3.1: Only respond as UFP (device) */
+ if (port->data_role == TYPEC_DEVICE &&
+ port->nr_snk_vdo) {
+ for (i = 0; i < port->nr_snk_vdo; i++)
+ response[i + 1]
+ = cpu_to_le32(port->snk_vdo[i]);
+ rlen = port->nr_snk_vdo + 1;
+ }
+ break;
+ case CMD_DISCOVER_SVID:
+ break;
+ case CMD_DISCOVER_MODES:
+ break;
+ case CMD_ENTER_MODE:
+ break;
+ case CMD_EXIT_MODE:
+ break;
+ case CMD_ATTENTION:
+ break;
+ default:
+ break;
+ }
+ if (rlen >= 1) {
+ response[0] = p0 | VDO_CMDT(CMDT_RSP_ACK);
+ } else if (rlen == 0) {
+ response[0] = p0 | VDO_CMDT(CMDT_RSP_NAK);
+ rlen = 1;
+ } else {
+ response[0] = p0 | VDO_CMDT(CMDT_RSP_BUSY);
+ rlen = 1;
+ }
+ break;
+ case CMDT_RSP_ACK:
+ /* silently drop message if we are not connected */
+ if (!port->partner) {
+ tcpm_log(port,
+ "port partner is NULL, ignore stray CMDT_RSP_ACK");
+ break;
+ }
+ switch (cmd) {
+ case CMD_DISCOVER_IDENT:
+ /* 6.4.4.3.1 */
+ svdm_consume_identity(port, payload, cnt);
+ response[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID);
+ rlen = 1;
+ break;
+ case CMD_DISCOVER_SVID:
+ /* 6.4.4.3.2 */
+ if (svdm_consume_svids(port, payload, cnt)) {
+ response[0] = VDO(USB_SID_PD, 1,
+ CMD_DISCOVER_SVID);
+ rlen = 1;
+ } else if (modep->nsvids && supports_modal(port)) {
+ response[0] = VDO(modep->svids[0], 1,
+ CMD_DISCOVER_MODES);
+ rlen = 1;
+ }
+ break;
+ case CMD_DISCOVER_MODES:
+ /* 6.4.4.3.3 */
+ svdm_consume_modes(port, payload, cnt);
+ modep->svid_index++;
+ if (modep->svid_index < modep->nsvids) {
+ svid = modep->svids[modep->svid_index];
+ response[0] = VDO(svid, 1, CMD_DISCOVER_MODES);
+ rlen = 1;
+ } else {
+#if 0
+ response[0] = pd_dfp_enter_mode(port, 0, 0);
+ if (response[0])
+ rlen = 1;
+#endif
+ }
+ break;
+ case CMD_ENTER_MODE:
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return rlen;
+}
+
+static void tcpm_handle_vdm_request(struct tcpm_port *port,
+ const __le32 *payload, int cnt)
+{
+ int rlen = 0;
+ u32 response[8] = { };
+ u32 p0 = le32_to_cpu(payload[0]);
+
+ if (port->vdm_state == VDM_STATE_BUSY) {
+ /* If UFP responded busy retry after timeout */
+ if (PD_VDO_CMDT(p0) == CMDT_RSP_BUSY) {
+ port->vdm_state = VDM_STATE_WAIT_RSP_BUSY;
+ port->vdo_retry = (p0 & ~VDO_CMDT_MASK) |
+ CMDT_INIT;
+ mod_delayed_work(port->wq, &port->vdm_state_machine,
+ msecs_to_jiffies(PD_T_VDM_BUSY));
+ return;
+ }
+ port->vdm_state = VDM_STATE_DONE;
+ }
+
+ if (PD_VDO_SVDM(p0))
+ rlen = tcpm_pd_svdm(port, payload, cnt, response);
+#if 0
+ else
+ rlen = tcpm_pd_custom_vdm(port, cnt, payload, response);
+#endif
+
+ if (rlen > 0) {
+ tcpm_queue_vdm(port, response[0], &response[1], rlen - 1);
+ mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
+ }
+}
+
+static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd,
+ const u32 *data, int count)
+{
+ u32 header;
+
+ if (WARN_ON(count > VDO_MAX_SIZE - 1))
+ count = VDO_MAX_SIZE - 1;
+
+ /* set VDM header with VID & CMD */
+ header = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ?
+ 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), cmd);
+ tcpm_queue_vdm(port, header, data, count);
+
+ mod_delayed_work(port->wq, &port->vdm_state_machine, 0);
+}
+
+static unsigned int vdm_ready_timeout(u32 vdm_hdr)
+{
+ unsigned int timeout;
+ int cmd = PD_VDO_CMD(vdm_hdr);
+
+ /* its not a structured VDM command */
+ if (!PD_VDO_SVDM(vdm_hdr))
+ return PD_T_VDM_UNSTRUCTURED;
+
+ switch (PD_VDO_CMDT(vdm_hdr)) {
+ case CMDT_INIT:
+ if (cmd == CMD_ENTER_MODE || cmd == CMD_EXIT_MODE)
+ timeout = PD_T_VDM_WAIT_MODE_E;
+ else
+ timeout = PD_T_VDM_SNDR_RSP;
+ break;
+ default:
+ if (cmd == CMD_ENTER_MODE || cmd == CMD_EXIT_MODE)
+ timeout = PD_T_VDM_E_MODE;
+ else
+ timeout = PD_T_VDM_RCVR_RSP;
+ break;
+ }
+ return timeout;
+}
+
+static void vdm_run_state_machine(struct tcpm_port *port)
+{
+ struct pd_message msg;
+ int i, res;
+
+ switch (port->vdm_state) {
+ case VDM_STATE_READY:
+ /* Only transmit VDM if attached */
+ if (!port->attached) {
+ port->vdm_state = VDM_STATE_ERR_BUSY;
+ break;
+ }
+
+ /*
+ * if there's traffic or we're not in PDO ready state don't send
+ * a VDM.
+ */
+ if (port->state != SRC_READY && port->state != SNK_READY)
+ break;
+
+ /* Prepare and send VDM */
+ memset(&msg, 0, sizeof(msg));
+ msg.header = PD_HEADER_LE(PD_DATA_VENDOR_DEF,
+ port->pwr_role,
+ port->data_role,
+ port->message_id, port->vdo_count);
+ for (i = 0; i < port->vdo_count; i++)
+ msg.payload[i] = cpu_to_le32(port->vdo_data[i]);
+ res = tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
+ if (res < 0) {
+ port->vdm_state = VDM_STATE_ERR_SEND;
+ } else {
+ unsigned long timeout;
+
+ port->vdm_retries = 0;
+ port->vdm_state = VDM_STATE_BUSY;
+ timeout = vdm_ready_timeout(port->vdo_data[0]);
+ mod_delayed_work(port->wq, &port->vdm_state_machine,
+ timeout);
+ }
+ break;
+ case VDM_STATE_WAIT_RSP_BUSY:
+ port->vdo_data[0] = port->vdo_retry;
+ port->vdo_count = 1;
+ port->vdm_state = VDM_STATE_READY;
+ break;
+ case VDM_STATE_BUSY:
+ port->vdm_state = VDM_STATE_ERR_TMOUT;
+ break;
+ case VDM_STATE_ERR_SEND:
+ /*
+ * A partner which does not support USB PD will not reply,
+ * so this is not a fatal error. At the same time, some
+ * devices may not return GoodCRC under some circumstances,
+ * so we need to retry.
+ */
+ if (port->vdm_retries < 3) {
+ tcpm_log(port, "VDM Tx error, retry");
+ port->vdm_retries++;
+ port->vdm_state = VDM_STATE_READY;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void vdm_state_machine_work(struct work_struct *work)
+{
+ struct tcpm_port *port = container_of(work, struct tcpm_port,
+ vdm_state_machine.work);
+ enum vdm_states prev_state;
+
+ mutex_lock(&port->lock);
+
+ /*
+ * Continue running as long as the port is not busy and there was
+ * a state change.
+ */
+ do {
+ prev_state = port->vdm_state;
+ vdm_run_state_machine(port);
+ } while (port->vdm_state != prev_state &&
+ port->vdm_state != VDM_STATE_BUSY);
+
+ mutex_unlock(&port->lock);
+}
+
+/*
+ * PD (data, control) command handling functions
+ */
+static void tcpm_pd_data_request(struct tcpm_port *port,
+ const struct pd_message *msg)
+{
+ enum pd_data_msg_type type = pd_header_type_le(msg->header);
+ unsigned int cnt = pd_header_cnt_le(msg->header);
+ unsigned int i;
+
+ switch (type) {
+ case PD_DATA_SOURCE_CAP:
+ if (port->pwr_role != TYPEC_SINK)
+ break;
+
+ for (i = 0; i < cnt; i++)
+ port->source_caps[i] = le32_to_cpu(msg->payload[i]);
+
+ port->nr_source_caps = cnt;
+
+ tcpm_log_source_caps(port);
+
+ /*
+ * This message may be received even if VBUS is not
+ * present. This is quite unexpected; see USB PD
+ * specification, sections 8.3.3.6.3.1 and 8.3.3.6.3.2.
+ * However, at the same time, we must be ready to
+ * receive this message and respond to it 15ms after
+ * receiving PS_RDY during power swap operations, no matter
+ * if VBUS is available or not (USB PD specification,
+ * section 6.5.9.2).
+ * So we need to accept the message either way,
+ * but be prepared to keep waiting for VBUS after it was
+ * handled.
+ */
+ tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0);
+ break;
+ case PD_DATA_REQUEST:
+ if (port->pwr_role != TYPEC_SOURCE ||
+ cnt != 1) {
+ tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
+ break;
+ }
+ port->sink_request = le32_to_cpu(msg->payload[0]);
+ tcpm_set_state(port, SRC_NEGOTIATE_CAPABILITIES, 0);
+ break;
+ case PD_DATA_SINK_CAP:
+ /* We don't do anything with this at the moment... */
+ for (i = 0; i < cnt; i++)
+ port->sink_caps[i] = le32_to_cpu(msg->payload[i]);
+ port->nr_sink_caps = cnt;
+ break;
+ case PD_DATA_VENDOR_DEF:
+ tcpm_handle_vdm_request(port, msg->payload, cnt);
+ break;
+ case PD_DATA_BIST:
+ if (port->state == SRC_READY || port->state == SNK_READY) {
+ port->bist_request = le32_to_cpu(msg->payload[0]);
+ tcpm_set_state(port, BIST_RX, 0);
+ }
+ break;
+ default:
+ tcpm_log(port, "Unhandled data message type %#x", type);
+ break;
+ }
+}
+
+static void tcpm_pd_ctrl_request(struct tcpm_port *port,
+ const struct pd_message *msg)
+{
+ enum pd_ctrl_msg_type type = pd_header_type_le(msg->header);
+ enum tcpm_state next_state;
+
+ switch (type) {
+ case PD_CTRL_GOOD_CRC:
+ case PD_CTRL_PING:
+ break;
+ case PD_CTRL_GET_SOURCE_CAP:
+ switch (port->state) {
+ case SRC_READY:
+ case SNK_READY:
+ tcpm_queue_message(port, PD_MSG_DATA_SOURCE_CAP);
+ break;
+ default:
+ tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
+ break;
+ }
+ break;
+ case PD_CTRL_GET_SINK_CAP:
+ switch (port->state) {
+ case SRC_READY:
+ case SNK_READY:
+ tcpm_queue_message(port, PD_MSG_DATA_SINK_CAP);
+ break;
+ default:
+ tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
+ break;
+ }
+ break;
+ case PD_CTRL_GOTO_MIN:
+ break;
+ case PD_CTRL_PS_RDY:
+ switch (port->state) {
+ case SNK_TRANSITION_SINK:
+ if (port->vbus_present) {
+ tcpm_set_current_limit(port,
+ port->current_limit,
+ port->supply_voltage);
+ tcpm_set_state(port, SNK_READY, 0);
+ } else {
+ /*
+ * Seen after power swap. Keep waiting for VBUS
+ * in a transitional state.
+ */
+ tcpm_set_state(port,
+ SNK_TRANSITION_SINK_VBUS, 0);
+ }
+ break;
+ case PR_SWAP_SRC_SNK_SOURCE_OFF:
+ tcpm_set_state(port, PR_SWAP_SRC_SNK_SINK_ON, 0);
+ break;
+ case PR_SWAP_SNK_SRC_SINK_OFF:
+ tcpm_set_state(port, PR_SWAP_SNK_SRC_SOURCE_ON, 0);
+ break;
+ case VCONN_SWAP_WAIT_FOR_VCONN:
+ tcpm_set_state(port, VCONN_SWAP_TURN_OFF_VCONN, 0);
+ break;
+ default:
+ break;
+ }
+ break;
+ case PD_CTRL_REJECT:
+ case PD_CTRL_WAIT:
+ switch (port->state) {
+ case SNK_NEGOTIATE_CAPABILITIES:
+ /* USB PD specification, Figure 8-43 */
+ if (port->explicit_contract)
+ next_state = SNK_READY;
+ else
+ next_state = SNK_WAIT_CAPABILITIES;
+ tcpm_set_state(port, next_state, 0);
+ break;
+ case DR_SWAP_SEND:
+ port->swap_status = (type == PD_CTRL_WAIT ?
+ -EAGAIN : -EOPNOTSUPP);
+ tcpm_set_state(port, DR_SWAP_CANCEL, 0);
+ break;
+ case PR_SWAP_SEND:
+ port->swap_status = (type == PD_CTRL_WAIT ?
+ -EAGAIN : -EOPNOTSUPP);
+ tcpm_set_state(port, PR_SWAP_CANCEL, 0);
+ break;
+ case VCONN_SWAP_SEND:
+ port->swap_status = (type == PD_CTRL_WAIT ?
+ -EAGAIN : -EOPNOTSUPP);
+ tcpm_set_state(port, VCONN_SWAP_CANCEL, 0);
+ break;
+ default:
+ break;
+ }
+ break;
+ case PD_CTRL_ACCEPT:
+ switch (port->state) {
+ case SNK_NEGOTIATE_CAPABILITIES:
+ tcpm_set_state(port, SNK_TRANSITION_SINK, 0);
+ break;
+ case SOFT_RESET_SEND:
+ port->message_id = 0;
+ port->rx_msgid = -1;
+ if (port->pwr_role == TYPEC_SOURCE)
+ next_state = SRC_SEND_CAPABILITIES;
+ else
+ next_state = SNK_WAIT_CAPABILITIES;
+ tcpm_set_state(port, next_state, 0);
+ break;
+ case DR_SWAP_SEND:
+ tcpm_set_state(port, DR_SWAP_CHANGE_DR, 0);
+ break;
+ case PR_SWAP_SEND:
+ tcpm_set_state(port, PR_SWAP_START, 0);
+ break;
+ case VCONN_SWAP_SEND:
+ tcpm_set_state(port, VCONN_SWAP_START, 0);
+ break;
+ default:
+ break;
+ }
+ break;
+ case PD_CTRL_SOFT_RESET:
+ tcpm_set_state(port, SOFT_RESET, 0);
+ break;
+ case PD_CTRL_DR_SWAP:
+ if (port->port_type != TYPEC_PORT_DRP) {
+ tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
+ break;
+ }
+ /*
+ * XXX
+ * 6.3.9: If an alternate mode is active, a request to swap
+ * alternate modes shall trigger a port reset.
+ */
+ switch (port->state) {
+ case SRC_READY:
+ case SNK_READY:
+ tcpm_set_state(port, DR_SWAP_ACCEPT, 0);
+ break;
+ default:
+ tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
+ break;
+ }
+ break;
+ case PD_CTRL_PR_SWAP:
+ if (port->port_type != TYPEC_PORT_DRP) {
+ tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
+ break;
+ }
+ switch (port->state) {
+ case SRC_READY:
+ case SNK_READY:
+ tcpm_set_state(port, PR_SWAP_ACCEPT, 0);
+ break;
+ default:
+ tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
+ break;
+ }
+ break;
+ case PD_CTRL_VCONN_SWAP:
+ switch (port->state) {
+ case SRC_READY:
+ case SNK_READY:
+ tcpm_set_state(port, VCONN_SWAP_ACCEPT, 0);
+ break;
+ default:
+ tcpm_queue_message(port, PD_MSG_CTRL_WAIT);
+ break;
+ }
+ break;
+ default:
+ tcpm_log(port, "Unhandled ctrl message type %#x", type);
+ break;
+ }
+}
+
+static void tcpm_pd_rx_handler(struct work_struct *work)
+{
+ struct pd_rx_event *event = container_of(work,
+ struct pd_rx_event, work);
+ const struct pd_message *msg = &event->msg;
+ unsigned int cnt = pd_header_cnt_le(msg->header);
+ struct tcpm_port *port = event->port;
+
+ mutex_lock(&port->lock);
+
+ tcpm_log(port, "PD RX, header: %#x [%d]", le16_to_cpu(msg->header),
+ port->attached);
+
+ if (port->attached) {
+ enum pd_ctrl_msg_type type = pd_header_type_le(msg->header);
+ unsigned int msgid = pd_header_msgid_le(msg->header);
+
+ /*
+ * USB PD standard, 6.6.1.2:
+ * "... if MessageID value in a received Message is the
+ * same as the stored value, the receiver shall return a
+ * GoodCRC Message with that MessageID value and drop
+ * the Message (this is a retry of an already received
+ * Message). Note: this shall not apply to the Soft_Reset
+ * Message which always has a MessageID value of zero."
+ */
+ if (msgid == port->rx_msgid && type != PD_CTRL_SOFT_RESET)
+ goto done;
+ port->rx_msgid = msgid;
+
+ /*
+ * If both ends believe to be DFP/host, we have a data role
+ * mismatch.
+ */
+ if (!!(le16_to_cpu(msg->header) & PD_HEADER_DATA_ROLE) ==
+ (port->data_role == TYPEC_HOST)) {
+ tcpm_log(port,
+ "Data role mismatch, initiating error recovery");
+ tcpm_set_state(port, ERROR_RECOVERY, 0);
+ } else {
+ if (cnt)
+ tcpm_pd_data_request(port, msg);
+ else
+ tcpm_pd_ctrl_request(port, msg);
+ }
+ }
+
+done:
+ mutex_unlock(&port->lock);
+ kfree(event);
+}
+
+void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg)
+{
+ struct pd_rx_event *event;
+
+ event = kzalloc(sizeof(*event), GFP_ATOMIC);
+ if (!event)
+ return;
+
+ INIT_WORK(&event->work, tcpm_pd_rx_handler);
+ event->port = port;
+ memcpy(&event->msg, msg, sizeof(*msg));
+ queue_work(port->wq, &event->work);
+}
+EXPORT_SYMBOL_GPL(tcpm_pd_receive);
+
+static int tcpm_pd_send_control(struct tcpm_port *port,
+ enum pd_ctrl_msg_type type)
+{
+ struct pd_message msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.header = PD_HEADER_LE(type, port->pwr_role,
+ port->data_role,
+ port->message_id, 0);
+
+ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
+}
+
+/*
+ * Send queued message without affecting state.
+ * Return true if state machine should go back to sleep,
+ * false otherwise.
+ */
+static bool tcpm_send_queued_message(struct tcpm_port *port)
+{
+ enum pd_msg_request queued_message;
+
+ do {
+ queued_message = port->queued_message;
+ port->queued_message = PD_MSG_NONE;
+
+ switch (queued_message) {
+ case PD_MSG_CTRL_WAIT:
+ tcpm_pd_send_control(port, PD_CTRL_WAIT);
+ break;
+ case PD_MSG_CTRL_REJECT:
+ tcpm_pd_send_control(port, PD_CTRL_REJECT);
+ break;
+ case PD_MSG_DATA_SINK_CAP:
+ tcpm_pd_send_sink_caps(port);
+ break;
+ case PD_MSG_DATA_SOURCE_CAP:
+ tcpm_pd_send_source_caps(port);
+ break;
+ default:
+ break;
+ }
+ } while (port->queued_message != PD_MSG_NONE);
+
+ if (port->delayed_state != INVALID_STATE) {
+ if (time_is_after_jiffies(port->delayed_runtime)) {
+ mod_delayed_work(port->wq, &port->state_machine,
+ port->delayed_runtime - jiffies);
+ return true;
+ }
+ port->delayed_state = INVALID_STATE;
+ }
+ return false;
+}
+
+static int tcpm_pd_check_request(struct tcpm_port *port)
+{
+ u32 pdo, rdo = port->sink_request;
+ unsigned int max, op, pdo_max, index;
+ enum pd_pdo_type type;
+
+ index = rdo_index(rdo);
+ if (!index || index > port->nr_src_pdo)
+ return -EINVAL;
+
+ pdo = port->src_pdo[index - 1];
+ type = pdo_type(pdo);
+ switch (type) {
+ case PDO_TYPE_FIXED:
+ case PDO_TYPE_VAR:
+ max = rdo_max_current(rdo);
+ op = rdo_op_current(rdo);
+ pdo_max = pdo_max_current(pdo);
+
+ if (op > pdo_max)
+ return -EINVAL;
+ if (max > pdo_max && !(rdo & RDO_CAP_MISMATCH))
+ return -EINVAL;
+
+ if (type == PDO_TYPE_FIXED)
+ tcpm_log(port,
+ "Requested %u mV, %u mA for %u / %u mA",
+ pdo_fixed_voltage(pdo), pdo_max, op, max);
+ else
+ tcpm_log(port,
+ "Requested %u -> %u mV, %u mA for %u / %u mA",
+ pdo_min_voltage(pdo), pdo_max_voltage(pdo),
+ pdo_max, op, max);
+ break;
+ case PDO_TYPE_BATT:
+ max = rdo_max_power(rdo);
+ op = rdo_op_power(rdo);
+ pdo_max = pdo_max_power(pdo);
+
+ if (op > pdo_max)
+ return -EINVAL;
+ if (max > pdo_max && !(rdo & RDO_CAP_MISMATCH))
+ return -EINVAL;
+ tcpm_log(port,
+ "Requested %u -> %u mV, %u mW for %u / %u mW",
+ pdo_min_voltage(pdo), pdo_max_voltage(pdo),
+ pdo_max, op, max);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ port->op_vsafe5v = index == 1;
+
+ return 0;
+}
+
+static int tcpm_pd_select_pdo(struct tcpm_port *port)
+{
+ unsigned int i, max_mw = 0, max_mv = 0;
+ int ret = -EINVAL;
+
+ /*
+ * Select the source PDO providing the most power while staying within
+ * the board's voltage limits. Prefer PDO providing exp
+ */
+ for (i = 0; i < port->nr_source_caps; i++) {
+ u32 pdo = port->source_caps[i];
+ enum pd_pdo_type type = pdo_type(pdo);
+ unsigned int mv, ma, mw;
+
+ if (type == PDO_TYPE_FIXED)
+ mv = pdo_fixed_voltage(pdo);
+ else
+ mv = pdo_min_voltage(pdo);
+
+ if (type == PDO_TYPE_BATT) {
+ mw = pdo_max_power(pdo);
+ } else {
+ ma = min(pdo_max_current(pdo),
+ port->max_snk_ma);
+ mw = ma * mv / 1000;
+ }
+
+ /* Perfer higher voltages if available */
+ if ((mw > max_mw || (mw == max_mw && mv > max_mv)) &&
+ mv <= port->max_snk_mv) {
+ ret = i;
+ max_mw = mw;
+ max_mv = mv;
+ }
+ }
+
+ return ret;
+}
+
+static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo)
+{
+ unsigned int mv, ma, mw, flags;
+ unsigned int max_ma, max_mw;
+ enum pd_pdo_type type;
+ int index;
+ u32 pdo;
+
+ index = tcpm_pd_select_pdo(port);
+ if (index < 0)
+ return -EINVAL;
+ pdo = port->source_caps[index];
+ type = pdo_type(pdo);
+
+ if (type == PDO_TYPE_FIXED)
+ mv = pdo_fixed_voltage(pdo);
+ else
+ mv = pdo_min_voltage(pdo);
+
+ /* Select maximum available current within the board's power limit */
+ if (type == PDO_TYPE_BATT) {
+ mw = pdo_max_power(pdo);
+ ma = 1000 * min(mw, port->max_snk_mw) / mv;
+ } else {
+ ma = min(pdo_max_current(pdo),
+ 1000 * port->max_snk_mw / mv);
+ }
+ ma = min(ma, port->max_snk_ma);
+
+ flags = RDO_USB_COMM | RDO_NO_SUSPEND;
+
+ /* Set mismatch bit if offered power is less than operating power */
+ mw = ma * mv / 1000;
+ max_ma = ma;
+ max_mw = mw;
+ if (mw < port->operating_snk_mw) {
+ flags |= RDO_CAP_MISMATCH;
+ max_mw = port->operating_snk_mw;
+ max_ma = max_mw * 1000 / mv;
+ }
+
+ tcpm_log(port, "cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d",
+ port->cc_req, port->cc1, port->cc2, port->vbus_source,
+ port->vconn_role == TYPEC_SOURCE ? "source" : "sink",
+ port->polarity);
+
+ if (type == PDO_TYPE_BATT) {
+ *rdo = RDO_BATT(index + 1, mw, max_mw, flags);
+
+ tcpm_log(port, "Requesting PDO %d: %u mV, %u mW%s",
+ index, mv, mw,
+ flags & RDO_CAP_MISMATCH ? " [mismatch]" : "");
+ } else {
+ *rdo = RDO_FIXED(index + 1, ma, max_ma, flags);
+
+ tcpm_log(port, "Requesting PDO %d: %u mV, %u mA%s",
+ index, mv, ma,
+ flags & RDO_CAP_MISMATCH ? " [mismatch]" : "");
+ }
+
+ port->current_limit = ma;
+ port->supply_voltage = mv;
+
+ return 0;
+}
+
+static int tcpm_pd_send_request(struct tcpm_port *port)
+{
+ struct pd_message msg;
+ int ret;
+ u32 rdo;
+
+ ret = tcpm_pd_build_request(port, &rdo);
+ if (ret < 0)
+ return ret;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.header = PD_HEADER_LE(PD_DATA_REQUEST,
+ port->pwr_role,
+ port->data_role,
+ port->message_id, 1);
+ msg.payload[0] = cpu_to_le32(rdo);
+
+ return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg);
+}
+
+static int tcpm_set_vbus(struct tcpm_port *port, bool enable)
+{
+ int ret;
+
+ if (enable && port->vbus_charge)
+ return -EINVAL;
+
+ tcpm_log(port, "vbus:=%d charge=%d", enable, port->vbus_charge);
+
+ ret = port->tcpc->set_vbus(port->tcpc, enable, port->vbus_charge);
+ if (ret < 0)
+ return ret;
+
+ port->vbus_source = enable;
+ return 0;
+}
+
+static int tcpm_set_charge(struct tcpm_port *port, bool charge)
+{
+ int ret;
+
+ if (charge && port->vbus_source)
+ return -EINVAL;
+
+ if (charge != port->vbus_charge) {
+ tcpm_log(port, "vbus=%d charge:=%d", port->vbus_source, charge);
+ ret = port->tcpc->set_vbus(port->tcpc, port->vbus_source,
+ charge);
+ if (ret < 0)
+ return ret;
+ }
+ port->vbus_charge = charge;
+ return 0;
+}
+
+static bool tcpm_start_drp_toggling(struct tcpm_port *port)
+{
+ int ret;
+
+ if (port->tcpc->start_drp_toggling &&
+ port->port_type == TYPEC_PORT_DRP) {
+ tcpm_log_force(port, "Start DRP toggling");
+ ret = port->tcpc->start_drp_toggling(port->tcpc,
+ tcpm_rp_cc(port));
+ if (!ret)
+ return true;
+ }
+
+ return false;
+}
+
+static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc)
+{
+ tcpm_log(port, "cc:=%d", cc);
+ port->cc_req = cc;
+ port->tcpc->set_cc(port->tcpc, cc);
+}
+
+static int tcpm_init_vbus(struct tcpm_port *port)
+{
+ int ret;
+
+ ret = port->tcpc->set_vbus(port->tcpc, false, false);
+ port->vbus_source = false;
+ port->vbus_charge = false;
+ return ret;
+}
+
+static int tcpm_init_vconn(struct tcpm_port *port)
+{
+ int ret;
+
+ ret = port->tcpc->set_vconn(port->tcpc, false);
+ port->vconn_role = TYPEC_SINK;
+ return ret;
+}
+
+static void tcpm_typec_connect(struct tcpm_port *port)
+{
+ if (!port->connected) {
+ /* Make sure we don't report stale identity information */
+ memset(&port->partner_ident, 0, sizeof(port->partner_ident));
+ port->partner_desc.usb_pd = port->pd_capable;
+ if (tcpm_port_is_debug(port))
+ port->partner_desc.accessory = TYPEC_ACCESSORY_DEBUG;
+ else if (tcpm_port_is_audio(port))
+ port->partner_desc.accessory = TYPEC_ACCESSORY_AUDIO;
+ else
+ port->partner_desc.accessory = TYPEC_ACCESSORY_NONE;
+ port->partner = typec_register_partner(port->typec_port,
+ &port->partner_desc);
+ port->connected = true;
+ }
+}
+
+static int tcpm_src_attach(struct tcpm_port *port)
+{
+ enum typec_cc_polarity polarity =
+ port->cc2 == TYPEC_CC_RD ? TYPEC_POLARITY_CC2
+ : TYPEC_POLARITY_CC1;
+ int ret;
+
+ if (port->attached)
+ return 0;
+
+ ret = tcpm_set_polarity(port, polarity);
+ if (ret < 0)
+ return ret;
+
+ ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST);
+ if (ret < 0)
+ return ret;
+
+ ret = port->tcpc->set_pd_rx(port->tcpc, true);
+ if (ret < 0)
+ goto out_disable_mux;
+
+ /*
+ * USB Type-C specification, version 1.2,
+ * chapter 4.5.2.2.8.1 (Attached.SRC Requirements)
+ * Enable VCONN only if the non-RD port is set to RA.
+ */
+ if ((polarity == TYPEC_POLARITY_CC1 && port->cc2 == TYPEC_CC_RA) ||
+ (polarity == TYPEC_POLARITY_CC2 && port->cc1 == TYPEC_CC_RA)) {
+ ret = tcpm_set_vconn(port, true);
+ if (ret < 0)
+ goto out_disable_pd;
+ }
+
+ ret = tcpm_set_vbus(port, true);
+ if (ret < 0)
+ goto out_disable_vconn;
+
+ port->pd_capable = false;
+
+ port->partner = NULL;
+
+ port->attached = true;
+ port->send_discover = true;
+
+ return 0;
+
+out_disable_vconn:
+ tcpm_set_vconn(port, false);
+out_disable_pd:
+ port->tcpc->set_pd_rx(port->tcpc, false);
+out_disable_mux:
+ tcpm_mux_set(port, TYPEC_MUX_NONE, TCPC_USB_SWITCH_DISCONNECT);
+ return ret;
+}
+
+static void tcpm_typec_disconnect(struct tcpm_port *port)
+{
+ if (port->connected) {
+ typec_unregister_partner(port->partner);
+ port->partner = NULL;
+ port->connected = false;
+ }
+}
+
+static void tcpm_unregister_altmodes(struct tcpm_port *port)
+{
+ struct pd_mode_data *modep = &port->mode_data;
+ int i;
+
+ for (i = 0; i < modep->altmodes; i++) {
+ typec_unregister_altmode(port->partner_altmode[i]);
+ port->partner_altmode[i] = NULL;
+ }
+
+ memset(modep, 0, sizeof(*modep));
+}
+
+static void tcpm_reset_port(struct tcpm_port *port)
+{
+ tcpm_unregister_altmodes(port);
+ tcpm_typec_disconnect(port);
+ port->attached = false;
+ port->pd_capable = false;
+
+ /*
+ * First Rx ID should be 0; set this to a sentinel of -1 so that
+ * we can check tcpm_pd_rx_handler() if we had seen it before.
+ */
+ port->rx_msgid = -1;
+
+ port->tcpc->set_pd_rx(port->tcpc, false);
+ tcpm_init_vbus(port); /* also disables charging */
+ tcpm_init_vconn(port);
+ tcpm_set_current_limit(port, 0, 0);
+ tcpm_set_polarity(port, TYPEC_POLARITY_CC1);
+ tcpm_set_attached_state(port, false);
+ port->try_src_count = 0;
+ port->try_snk_count = 0;
+}
+
+static void tcpm_detach(struct tcpm_port *port)
+{
+ if (!port->attached)
+ return;
+
+ if (tcpm_port_is_disconnected(port))
+ port->hard_reset_count = 0;
+
+ tcpm_reset_port(port);
+}
+
+static void tcpm_src_detach(struct tcpm_port *port)
+{
+ tcpm_detach(port);
+}
+
+static int tcpm_snk_attach(struct tcpm_port *port)
+{
+ int ret;
+
+ if (port->attached)
+ return 0;
+
+ ret = tcpm_set_polarity(port, port->cc2 != TYPEC_CC_OPEN ?
+ TYPEC_POLARITY_CC2 : TYPEC_POLARITY_CC1);
+ if (ret < 0)
+ return ret;
+
+ ret = tcpm_set_roles(port, true, TYPEC_SINK, TYPEC_DEVICE);
+ if (ret < 0)
+ return ret;
+
+ port->pd_capable = false;
+
+ port->partner = NULL;
+
+ port->attached = true;
+ port->send_discover = true;
+
+ return 0;
+}
+
+static void tcpm_snk_detach(struct tcpm_port *port)
+{
+ tcpm_detach(port);
+
+ /* XXX: (Dis)connect SuperSpeed mux? */
+}
+
+static int tcpm_acc_attach(struct tcpm_port *port)
+{
+ int ret;
+
+ if (port->attached)
+ return 0;
+
+ ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST);
+ if (ret < 0)
+ return ret;
+
+ port->partner = NULL;
+
+ tcpm_typec_connect(port);
+
+ port->attached = true;
+
+ return 0;
+}
+
+static void tcpm_acc_detach(struct tcpm_port *port)
+{
+ tcpm_detach(port);
+}
+
+static inline enum tcpm_state hard_reset_state(struct tcpm_port *port)
+{
+ if (port->hard_reset_count < PD_N_HARD_RESET_COUNT)
+ return HARD_RESET_SEND;
+ if (port->pd_capable)
+ return ERROR_RECOVERY;
+ if (port->pwr_role == TYPEC_SOURCE)
+ return SRC_UNATTACHED;
+ if (port->state == SNK_WAIT_CAPABILITIES)
+ return SNK_READY;
+ return SNK_UNATTACHED;
+}
+
+static inline enum tcpm_state ready_state(struct tcpm_port *port)
+{
+ if (port->pwr_role == TYPEC_SOURCE)
+ return SRC_READY;
+ else
+ return SNK_READY;
+}
+
+static inline enum tcpm_state unattached_state(struct tcpm_port *port)
+{
+ if (port->pwr_role == TYPEC_SOURCE)
+ return SRC_UNATTACHED;
+ else
+ return SNK_UNATTACHED;
+}
+
+static void tcpm_check_send_discover(struct tcpm_port *port)
+{
+ if (port->data_role == TYPEC_HOST && port->send_discover &&
+ port->pd_capable) {
+ tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0);
+ port->send_discover = false;
+ }
+}
+
+static void tcpm_swap_complete(struct tcpm_port *port, int result)
+{
+ if (port->swap_pending) {
+ port->swap_status = result;
+ port->swap_pending = false;
+ port->non_pd_role_swap = false;
+ complete(&port->swap_complete);
+ }
+}
+
+static void run_state_machine(struct tcpm_port *port)
+{
+ int ret;
+ unsigned int msecs;
+
+ port->enter_state = port->state;
+ switch (port->state) {
+ case DRP_TOGGLING:
+ break;
+ /* SRC states */
+ case SRC_UNATTACHED:
+ if (!port->non_pd_role_swap)
+ tcpm_swap_complete(port, -ENOTCONN);
+ tcpm_src_detach(port);
+ if (tcpm_start_drp_toggling(port)) {
+ tcpm_set_state(port, DRP_TOGGLING, 0);
+ break;
+ }
+ tcpm_set_cc(port, tcpm_rp_cc(port));
+ if (port->port_type == TYPEC_PORT_DRP)
+ tcpm_set_state(port, SNK_UNATTACHED, PD_T_DRP_SNK);
+ break;
+ case SRC_ATTACH_WAIT:
+ if (tcpm_port_is_debug(port))
+ tcpm_set_state(port, DEBUG_ACC_ATTACHED,
+ PD_T_CC_DEBOUNCE);
+ else if (tcpm_port_is_audio(port))
+ tcpm_set_state(port, AUDIO_ACC_ATTACHED,
+ PD_T_CC_DEBOUNCE);
+ else if (tcpm_port_is_source(port))
+ tcpm_set_state(port,
+ tcpm_try_snk(port) ? SNK_TRY
+ : SRC_ATTACHED,
+ PD_T_CC_DEBOUNCE);
+ break;
+
+ case SNK_TRY:
+ port->try_snk_count++;
+ /*
+ * Requirements:
+ * - Do not drive vconn or vbus
+ * - Terminate CC pins (both) to Rd
+ * Action:
+ * - Wait for tDRPTry (PD_T_DRP_TRY).
+ * Until then, ignore any state changes.
+ */
+ tcpm_set_cc(port, TYPEC_CC_RD);
+ tcpm_set_state(port, SNK_TRY_WAIT, PD_T_DRP_TRY);
+ break;
+ case SNK_TRY_WAIT:
+ if (tcpm_port_is_sink(port)) {
+ tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE, 0);
+ } else {
+ tcpm_set_state(port, SRC_TRYWAIT, 0);
+ port->max_wait = 0;
+ }
+ break;
+ case SNK_TRY_WAIT_DEBOUNCE:
+ tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS,
+ PD_T_PD_DEBOUNCE);
+ break;
+ case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS:
+ if (port->vbus_present && tcpm_port_is_sink(port))
+ tcpm_set_state(port, SNK_ATTACHED, 0);
+ else {
+ tcpm_set_state(port, SRC_TRYWAIT, 0);
+ port->max_wait = 0;
+ }
+ break;
+ case SRC_TRYWAIT:
+ tcpm_set_cc(port, tcpm_rp_cc(port));
+ if (port->max_wait == 0) {
+ port->max_wait = jiffies +
+ msecs_to_jiffies(PD_T_DRP_TRY);
+ tcpm_set_state(port, SRC_TRYWAIT_UNATTACHED,
+ PD_T_DRP_TRY);
+ } else {
+ if (time_is_after_jiffies(port->max_wait))
+ tcpm_set_state(port, SRC_TRYWAIT_UNATTACHED,
+ jiffies_to_msecs(
+ port->max_wait - jiffies));
+ else
+ tcpm_set_state(port, SNK_UNATTACHED, 0);
+ }
+ break;
+ case SRC_TRYWAIT_DEBOUNCE:
+ tcpm_set_state(port, SRC_ATTACHED, PD_T_CC_DEBOUNCE);
+ break;
+ case SRC_TRYWAIT_UNATTACHED:
+ tcpm_set_state(port, SNK_UNATTACHED, 0);
+ break;
+
+ case SRC_ATTACHED:
+ ret = tcpm_src_attach(port);
+ tcpm_set_state(port, SRC_UNATTACHED,
+ ret < 0 ? 0 : PD_T_PS_SOURCE_ON);
+ break;
+ case SRC_STARTUP:
+ typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_USB);
+ port->pwr_opmode = TYPEC_PWR_MODE_USB;
+ port->caps_count = 0;
+ port->message_id = 0;
+ port->rx_msgid = -1;
+ port->explicit_contract = false;
+ IsPRSwap = false;
+ tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0);
+ break;
+ case SRC_SEND_CAPABILITIES:
+ port->caps_count++;
+ if (port->caps_count > PD_N_CAPS_COUNT) {
+ tcpm_set_state(port, SRC_READY, 0);
+ break;
+ }
+ ret = tcpm_pd_send_source_caps(port);
+ if (ret < 0) {
+ tcpm_set_state(port, SRC_SEND_CAPABILITIES,
+ PD_T_SEND_SOURCE_CAP);
+ } else {
+ /*
+ * Per standard, we should clear the reset counter here.
+ * However, that can result in state machine hang-ups.
+ * Reset it only in READY state to improve stability.
+ */
+ /* port->hard_reset_count = 0; */
+ port->caps_count = 0;
+ port->pd_capable = true;
+ tcpm_set_state_cond(port, hard_reset_state(port),
+ PD_T_SEND_SOURCE_CAP);
+ }
+ break;
+ case SRC_NEGOTIATE_CAPABILITIES:
+ ret = tcpm_pd_check_request(port);
+ if (ret < 0) {
+ tcpm_pd_send_control(port, PD_CTRL_REJECT);
+ if (!port->explicit_contract) {
+ tcpm_set_state(port,
+ SRC_WAIT_NEW_CAPABILITIES, 0);
+ } else {
+ tcpm_set_state(port, SRC_READY, 0);
+ }
+ } else {
+ tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
+ tcpm_set_state(port, SRC_TRANSITION_SUPPLY,
+ PD_T_SRC_TRANSITION);
+ }
+ break;
+ case SRC_TRANSITION_SUPPLY:
+ /* XXX: regulator_set_voltage(vbus, ...) */
+ tcpm_pd_send_control(port, PD_CTRL_PS_RDY);
+ port->explicit_contract = true;
+ typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_PD);
+ port->pwr_opmode = TYPEC_PWR_MODE_PD;
+ tcpm_set_state_cond(port, SRC_READY, 0);
+ break;
+ case SRC_READY:
+#if 1
+ port->hard_reset_count = 0;
+#endif
+ port->try_src_count = 0;
+
+ tcpm_swap_complete(port, 0);
+ tcpm_typec_connect(port);
+ tcpm_check_send_discover(port);
+ /*
+ * 6.3.5
+ * Sending ping messages is not necessary if
+ * - the source operates at vSafe5V
+ * or
+ * - The system is not operating in PD mode
+ * or
+ * - Both partners are connected using a Type-C connector
+ * XXX How do we know that ?
+ */
+ if (port->pwr_opmode == TYPEC_PWR_MODE_PD &&
+ !port->op_vsafe5v) {
+ tcpm_pd_send_control(port, PD_CTRL_PING);
+ tcpm_set_state_cond(port, SRC_READY,
+ PD_T_SOURCE_ACTIVITY);
+ }
+ break;
+ case SRC_WAIT_NEW_CAPABILITIES:
+ /* Nothing to do... */
+ break;
+
+ /* SNK states */
+ case SNK_UNATTACHED:
+ if (!port->non_pd_role_swap)
+ tcpm_swap_complete(port, -ENOTCONN);
+ tcpm_snk_detach(port);
+ if (tcpm_start_drp_toggling(port)) {
+ tcpm_set_state(port, DRP_TOGGLING, 0);
+ break;
+ }
+ tcpm_set_cc(port, TYPEC_CC_RD);
+ if (port->port_type == TYPEC_PORT_DRP)
+ tcpm_set_state(port, SRC_UNATTACHED, PD_T_DRP_SRC);
+ break;
+ case SNK_ATTACH_WAIT:
+ if ((port->cc1 == TYPEC_CC_OPEN &&
+ port->cc2 != TYPEC_CC_OPEN) ||
+ (port->cc1 != TYPEC_CC_OPEN &&
+ port->cc2 == TYPEC_CC_OPEN))
+ tcpm_set_state(port, SNK_DEBOUNCED,
+ PD_T_CC_DEBOUNCE);
+ else if (tcpm_port_is_disconnected(port))
+ tcpm_set_state(port, SNK_UNATTACHED,
+ PD_T_PD_DEBOUNCE);
+ break;
+ case SNK_DEBOUNCED:
+ if (tcpm_port_is_disconnected(port))
+ tcpm_set_state(port, SNK_UNATTACHED,
+ PD_T_PD_DEBOUNCE);
+ else if (port->vbus_present)
+ tcpm_set_state(port,
+ tcpm_try_src(port) ? SRC_TRY
+ : SNK_ATTACHED,
+ 0);
+ else
+ /* Wait for VBUS, but not forever */
+ tcpm_set_state(port, SNK_UNATTACHED, PD_T_PS_SOURCE_ON);
+ break;
+
+ case SRC_TRY:
+ port->try_src_count++;
+ tcpm_set_cc(port, tcpm_rp_cc(port));
+ port->max_wait = 0;
+ tcpm_set_state(port, SRC_TRY_WAIT, 0);
+ break;
+ case SRC_TRY_WAIT:
+ if (port->max_wait == 0) {
+ port->max_wait = jiffies +
+ msecs_to_jiffies(PD_T_DRP_TRY);
+ msecs = PD_T_DRP_TRY;
+ } else {
+ if (time_is_after_jiffies(port->max_wait))
+ msecs = jiffies_to_msecs(port->max_wait -
+ jiffies);
+ else
+ msecs = 0;
+ }
+ tcpm_set_state(port, SNK_TRYWAIT, msecs);
+ break;
+ case SRC_TRY_DEBOUNCE:
+ tcpm_set_state(port, SRC_ATTACHED, PD_T_PD_DEBOUNCE);
+ break;
+ case SNK_TRYWAIT:
+ tcpm_set_cc(port, TYPEC_CC_RD);
+ tcpm_set_state(port, SNK_TRYWAIT_VBUS, PD_T_CC_DEBOUNCE);
+ break;
+ case SNK_TRYWAIT_VBUS:
+ /*
+ * TCPM stays in this state indefinitely until VBUS
+ * is detected as long as Rp is not detected for
+ * more than a time period of tPDDebounce.
+ */
+ if (port->vbus_present && tcpm_port_is_sink(port)) {
+ tcpm_set_state(port, SNK_ATTACHED, 0);
+ break;
+ }
+ if (!tcpm_port_is_sink(port))
+ tcpm_set_state(port, SNK_TRYWAIT_DEBOUNCE, 0);
+ break;
+ case SNK_TRYWAIT_DEBOUNCE:
+ tcpm_set_state(port, SNK_UNATTACHED, PD_T_PD_DEBOUNCE);
+ break;
+ case SNK_ATTACHED:
+ ret = tcpm_snk_attach(port);
+ if (ret < 0)
+ tcpm_set_state(port, SNK_UNATTACHED, 0);
+ else
+ tcpm_set_state(port, SNK_STARTUP, 0);
+ break;
+ case SNK_STARTUP:
+ /* XXX: callback into infrastructure */
+ typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_USB);
+ port->pwr_opmode = TYPEC_PWR_MODE_USB;
+ port->message_id = 0;
+ port->rx_msgid = -1;
+ port->explicit_contract = false;
+ IsPRSwap = false;
+ tcpm_set_state(port, SNK_DISCOVERY, 0);
+ break;
+ case SNK_DISCOVERY:
+ if (port->vbus_present) {
+ tcpm_set_current_limit(port,
+ tcpm_get_current_limit(port),
+ 5000);
+ tcpm_set_charge(port, true);
+ tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0);
+ break;
+ }
+ /*
+ * For DRP, timeouts differ. Also, handling is supposed to be
+ * different and much more complex (dead battery detection;
+ * see USB power delivery specification, section 8.3.3.6.1.5.1).
+ */
+ tcpm_set_state(port, hard_reset_state(port),
+ port->port_type == TYPEC_PORT_DRP ?
+ PD_T_DB_DETECT : PD_T_NO_RESPONSE);
+ break;
+ case SNK_DISCOVERY_DEBOUNCE:
+ tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE_DONE,
+ PD_T_CC_DEBOUNCE);
+ break;
+ case SNK_DISCOVERY_DEBOUNCE_DONE:
+ if (!tcpm_port_is_disconnected(port) &&
+ tcpm_port_is_sink(port) &&
+ time_is_after_jiffies(port->delayed_runtime)) {
+ tcpm_set_state(port, SNK_DISCOVERY,
+ port->delayed_runtime - jiffies);
+ break;
+ }
+ tcpm_set_state(port, unattached_state(port), 0);
+ break;
+ case SNK_WAIT_CAPABILITIES:
+ ret = port->tcpc->set_pd_rx(port->tcpc, true);
+ if (ret < 0) {
+ tcpm_set_state(port, SNK_READY, 0);
+ break;
+ }
+ /*
+ * If VBUS has never been low, and we time out waiting
+ * for source cap, try a soft reset first, in case we
+ * were already in a stable contract before this boot.
+ * Do this only once.
+ */
+ if (port->vbus_never_low) {
+ port->vbus_never_low = false;
+ tcpm_set_state(port, SOFT_RESET_SEND,
+ PD_T_SINK_WAIT_CAP);
+ } else {
+ tcpm_set_state(port, hard_reset_state(port),
+ PD_T_SINK_WAIT_CAP);
+ }
+ break;
+ case SNK_NEGOTIATE_CAPABILITIES:
+ port->pd_capable = true;
+ port->hard_reset_count = 0;
+ ret = tcpm_pd_send_request(port);
+ if (ret < 0) {
+ /* Let the Source send capabilities again. */
+ tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0);
+ } else {
+ tcpm_set_state_cond(port, hard_reset_state(port),
+ PD_T_SENDER_RESPONSE);
+ }
+ break;
+ case SNK_TRANSITION_SINK:
+ case SNK_TRANSITION_SINK_VBUS:
+ tcpm_set_state(port, hard_reset_state(port),
+ PD_T_PS_TRANSITION);
+ break;
+ case SNK_READY:
+ port->try_snk_count = 0;
+ port->explicit_contract = true;
+ typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_PD);
+ port->pwr_opmode = TYPEC_PWR_MODE_PD;
+
+ tcpm_swap_complete(port, 0);
+ tcpm_typec_connect(port);
+ tcpm_check_send_discover(port);
+ break;
+
+ /* Accessory states */
+ case ACC_UNATTACHED:
+ tcpm_acc_detach(port);
+ tcpm_set_state(port, SRC_UNATTACHED, 0);
+ break;
+ case DEBUG_ACC_ATTACHED:
+ case AUDIO_ACC_ATTACHED:
+ ret = tcpm_acc_attach(port);
+ if (ret < 0)
+ tcpm_set_state(port, ACC_UNATTACHED, 0);
+ break;
+ case AUDIO_ACC_DEBOUNCE:
+ tcpm_set_state(port, ACC_UNATTACHED, PD_T_CC_DEBOUNCE);
+ break;
+
+ /* Hard_Reset states */
+ case HARD_RESET_SEND:
+ tcpm_pd_transmit(port, TCPC_TX_HARD_RESET, NULL);
+ tcpm_set_state(port, HARD_RESET_START, 0);
+ break;
+ case HARD_RESET_START:
+ port->hard_reset_count++;
+ port->tcpc->set_pd_rx(port->tcpc, false);
+ tcpm_unregister_altmodes(port);
+ port->send_discover = true;
+ if (port->pwr_role == TYPEC_SOURCE)
+ tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF,
+ PD_T_PS_HARD_RESET);
+ else
+ tcpm_set_state(port, SNK_HARD_RESET_SINK_OFF, 0);
+ break;
+ case SRC_HARD_RESET_VBUS_OFF:
+ tcpm_set_vconn(port, true);
+ tcpm_set_vbus(port, false);
+ tcpm_set_roles(port, false, TYPEC_SOURCE, TYPEC_HOST);
+ tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER);
+ break;
+ case SRC_HARD_RESET_VBUS_ON:
+ tcpm_set_vbus(port, true);
+ port->tcpc->set_pd_rx(port->tcpc, true);
+ tcpm_set_attached_state(port, true);
+ tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON);
+ break;
+ case SNK_HARD_RESET_SINK_OFF:
+ tcpm_set_vconn(port, false);
+ tcpm_set_charge(port, false);
+ tcpm_set_roles(port, false, TYPEC_SINK, TYPEC_DEVICE);
+ /*
+ * VBUS may or may not toggle, depending on the adapter.
+ * If it doesn't toggle, transition to SNK_HARD_RESET_SINK_ON
+ * directly after timeout.
+ */
+ tcpm_set_state(port, SNK_HARD_RESET_SINK_ON, PD_T_SAFE_0V);
+ break;
+ case SNK_HARD_RESET_WAIT_VBUS:
+ /* Assume we're disconnected if VBUS doesn't come back. */
+ tcpm_set_state(port, SNK_UNATTACHED,
+ PD_T_SRC_RECOVER_MAX + PD_T_SRC_TURN_ON);
+ break;
+ case SNK_HARD_RESET_SINK_ON:
+ /* Note: There is no guarantee that VBUS is on in this state */
+ /*
+ * XXX:
+ * The specification suggests that dual mode ports in sink
+ * mode should transition to state PE_SRC_Transition_to_default.
+ * See USB power delivery specification chapter 8.3.3.6.1.3.
+ * This would mean to to
+ * - turn off VCONN, reset power supply
+ * - request hardware reset
+ * - turn on VCONN
+ * - Transition to state PE_Src_Startup
+ * SNK only ports shall transition to state Snk_Startup
+ * (see chapter 8.3.3.3.8).
+ * Similar, dual-mode ports in source mode should transition
+ * to PE_SNK_Transition_to_default.
+ */
+ tcpm_set_attached_state(port, true);
+ tcpm_set_state(port, SNK_STARTUP, 0);
+ break;
+
+ /* Soft_Reset states */
+ case SOFT_RESET:
+ port->message_id = 0;
+ port->rx_msgid = -1;
+ tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
+ if (port->pwr_role == TYPEC_SOURCE)
+ tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0);
+ else
+ tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0);
+ break;
+ case SOFT_RESET_SEND:
+ port->message_id = 0;
+ port->rx_msgid = -1;
+ if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET))
+ tcpm_set_state_cond(port, hard_reset_state(port), 0);
+ else
+ tcpm_set_state_cond(port, hard_reset_state(port),
+ PD_T_SENDER_RESPONSE);
+ break;
+
+ /* DR_Swap states */
+ case DR_SWAP_SEND:
+ tcpm_pd_send_control(port, PD_CTRL_DR_SWAP);
+ tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT,
+ PD_T_SENDER_RESPONSE);
+ break;
+ case DR_SWAP_ACCEPT:
+ tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
+ tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0);
+ break;
+ case DR_SWAP_SEND_TIMEOUT:
+ tcpm_swap_complete(port, -ETIMEDOUT);
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
+ case DR_SWAP_CHANGE_DR:
+ if (port->data_role == TYPEC_HOST) {
+ tcpm_unregister_altmodes(port);
+ tcpm_set_roles(port, true, port->pwr_role,
+ TYPEC_DEVICE);
+ } else {
+ tcpm_set_roles(port, true, port->pwr_role,
+ TYPEC_HOST);
+ port->send_discover = true;
+ }
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
+
+ /* PR_Swap states */
+ case PR_SWAP_ACCEPT:
+ tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
+ tcpm_set_state(port, PR_SWAP_START, 0);
+ break;
+ case PR_SWAP_SEND:
+ tcpm_pd_send_control(port, PD_CTRL_PR_SWAP);
+ tcpm_set_state_cond(port, PR_SWAP_SEND_TIMEOUT,
+ PD_T_SENDER_RESPONSE);
+ break;
+ case PR_SWAP_SEND_TIMEOUT:
+ tcpm_swap_complete(port, -ETIMEDOUT);
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
+ case PR_SWAP_START:
+ IsPRSwap = true;
+ if (port->pwr_role == TYPEC_SOURCE)
+ tcpm_set_state(port, PR_SWAP_SRC_SNK_TRANSITION_OFF,
+ PD_T_SRC_TRANSITION);
+ else
+ tcpm_set_state(port, PR_SWAP_SNK_SRC_SINK_OFF, 0);
+ break;
+ case PR_SWAP_SRC_SNK_TRANSITION_OFF:
+ tcpm_set_vbus(port, false);
+ port->explicit_contract = false;
+ tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF,
+ PD_T_PS_SOURCE_OFF);
+ break;
+ case PR_SWAP_SRC_SNK_SOURCE_OFF:
+ tcpm_set_cc(port, TYPEC_CC_RD);
+ /*
+ * USB-PD standard, 6.2.1.4, Port Power Role:
+ * "During the Power Role Swap Sequence, for the initial Source
+ * Port, the Port Power Role field shall be set to Sink in the
+ * PS_RDY Message indicating that the initial Source’s power
+ * supply is turned off"
+ */
+ tcpm_set_pwr_role(port, TYPEC_SINK);
+ if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) {
+ tcpm_set_state(port, ERROR_RECOVERY, 0);
+ break;
+ }
+ tcpm_set_state_cond(port, SNK_UNATTACHED, PD_T_PS_SOURCE_ON);
+ break;
+ case PR_SWAP_SRC_SNK_SINK_ON:
+ tcpm_set_state(port, SNK_STARTUP, 0);
+ break;
+ case PR_SWAP_SNK_SRC_SINK_OFF:
+ tcpm_set_charge(port, false);
+ tcpm_set_state(port, hard_reset_state(port),
+ PD_T_PS_SOURCE_OFF);
+ break;
+ case PR_SWAP_SNK_SRC_SOURCE_ON:
+ tcpm_set_cc(port, tcpm_rp_cc(port));
+ tcpm_set_vbus(port, true);
+ /*
+ * USB PD standard, 6.2.1.4:
+ * "Subsequent Messages initiated by the Policy Engine,
+ * such as the PS_RDY Message sent to indicate that Vbus
+ * is ready, will have the Port Power Role field set to
+ * Source."
+ */
+ tcpm_set_pwr_role(port, TYPEC_SOURCE);
+ tcpm_pd_send_control(port, PD_CTRL_PS_RDY);
+ tcpm_set_state(port, SRC_STARTUP, 0);
+ break;
+
+ case VCONN_SWAP_ACCEPT:
+ tcpm_pd_send_control(port, PD_CTRL_ACCEPT);
+ tcpm_set_state(port, VCONN_SWAP_START, 0);
+ break;
+ case VCONN_SWAP_SEND:
+ tcpm_pd_send_control(port, PD_CTRL_VCONN_SWAP);
+ tcpm_set_state(port, VCONN_SWAP_SEND_TIMEOUT,
+ PD_T_SENDER_RESPONSE);
+ break;
+ case VCONN_SWAP_SEND_TIMEOUT:
+ tcpm_swap_complete(port, -ETIMEDOUT);
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
+ case VCONN_SWAP_START:
+ if (port->vconn_role == TYPEC_SOURCE)
+ tcpm_set_state(port, VCONN_SWAP_WAIT_FOR_VCONN, 0);
+ else
+ tcpm_set_state(port, VCONN_SWAP_TURN_ON_VCONN, 0);
+ break;
+ case VCONN_SWAP_WAIT_FOR_VCONN:
+ tcpm_set_state(port, hard_reset_state(port),
+ PD_T_VCONN_SOURCE_ON);
+ break;
+ case VCONN_SWAP_TURN_ON_VCONN:
+ tcpm_set_vconn(port, true);
+ tcpm_pd_send_control(port, PD_CTRL_PS_RDY);
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
+ case VCONN_SWAP_TURN_OFF_VCONN:
+ tcpm_set_vconn(port, false);
+ tcpm_set_state(port, ready_state(port), 0);
+ break;
+
+ case DR_SWAP_CANCEL:
+ case PR_SWAP_CANCEL:
+ case VCONN_SWAP_CANCEL:
+ tcpm_swap_complete(port, port->swap_status);
+ IsPRSwap = false;
+ if (port->pwr_role == TYPEC_SOURCE)
+ tcpm_set_state(port, SRC_READY, 0);
+ else
+ tcpm_set_state(port, SNK_READY, 0);
+ break;
+
+ case BIST_RX:
+ switch (BDO_MODE_MASK(port->bist_request)) {
+ case BDO_MODE_CARRIER2:
+ tcpm_pd_transmit(port, TCPC_TX_BIST_MODE_2, NULL);
+ break;
+ default:
+ break;
+ }
+ /* Always switch to unattached state */
+ tcpm_set_state(port, unattached_state(port), 0);
+ break;
+ case ERROR_RECOVERY:
+ IsPRSwap = false;
+ tcpm_swap_complete(port, -EPROTO);
+ tcpm_set_state(port, PORT_RESET, 0);
+ break;
+ case PORT_RESET:
+ tcpm_reset_port(port);
+ tcpm_set_cc(port, TYPEC_CC_OPEN);
+ tcpm_set_state(port, PORT_RESET_WAIT_OFF,
+ PD_T_ERROR_RECOVERY);
+ break;
+ case PORT_RESET_WAIT_OFF:
+ tcpm_set_state(port,
+ tcpm_default_state(port),
+ port->vbus_present ? PD_T_PS_SOURCE_OFF : 0);
+ break;
+ default:
+ WARN(1, "Unexpected port state %d\n", port->state);
+ break;
+ }
+}
+
+static void tcpm_state_machine_work(struct work_struct *work)
+{
+ struct tcpm_port *port = container_of(work, struct tcpm_port,
+ state_machine.work);
+ enum tcpm_state prev_state;
+
+ mutex_lock(&port->lock);
+ port->state_machine_running = true;
+
+ if (port->queued_message && tcpm_send_queued_message(port))
+ goto done;
+
+ /* If we were queued due to a delayed state change, update it now */
+ if (port->delayed_state) {
+ tcpm_log(port, "state change %s -> %s [delayed %ld ms]",
+ tcpm_states[port->state],
+ tcpm_states[port->delayed_state], port->delay_ms);
+ port->prev_state = port->state;
+ port->state = port->delayed_state;
+ port->delayed_state = INVALID_STATE;
+ }
+
+ /*
+ * Continue running as long as we have (non-delayed) state changes
+ * to make.
+ */
+ do {
+ prev_state = port->state;
+ run_state_machine(port);
+ if (port->queued_message)
+ tcpm_send_queued_message(port);
+ } while (port->state != prev_state && !port->delayed_state);
+
+done:
+ port->state_machine_running = false;
+ mutex_unlock(&port->lock);
+}
+
+static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
+ enum typec_cc_status cc2)
+{
+ enum typec_cc_status old_cc1, old_cc2;
+ enum tcpm_state new_state;
+
+ old_cc1 = port->cc1;
+ old_cc2 = port->cc2;
+ port->cc1 = cc1;
+ port->cc2 = cc2;
+
+ tcpm_log_force(port,
+ "CC1: %u -> %u, CC2: %u -> %u [state %s, polarity %d, %s]",
+ old_cc1, cc1, old_cc2, cc2, tcpm_states[port->state],
+ port->polarity,
+ tcpm_port_is_disconnected(port) ? "disconnected"
+ : "connected");
+
+ switch (port->state) {
+ case DRP_TOGGLING:
+ if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) ||
+ tcpm_port_is_source(port))
+ tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
+ else if (tcpm_port_is_sink(port))
+ tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
+ break;
+ case SRC_UNATTACHED:
+ case ACC_UNATTACHED:
+ if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) ||
+ tcpm_port_is_source(port))
+ tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
+ break;
+ case SRC_ATTACH_WAIT:
+ if (tcpm_port_is_disconnected(port) ||
+ tcpm_port_is_audio_detached(port))
+ tcpm_set_state(port, SRC_UNATTACHED, 0);
+ else if (cc1 != old_cc1 || cc2 != old_cc2)
+ tcpm_set_state(port, SRC_ATTACH_WAIT, 0);
+ break;
+ case SRC_ATTACHED:
+ if (tcpm_port_is_disconnected(port))
+ tcpm_set_state(port, SRC_UNATTACHED, 0);
+ break;
+
+ case SNK_UNATTACHED:
+ if (tcpm_port_is_sink(port))
+ tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
+ break;
+ case SNK_ATTACH_WAIT:
+ if ((port->cc1 == TYPEC_CC_OPEN &&
+ port->cc2 != TYPEC_CC_OPEN) ||
+ (port->cc1 != TYPEC_CC_OPEN &&
+ port->cc2 == TYPEC_CC_OPEN))
+ new_state = SNK_DEBOUNCED;
+ else if (tcpm_port_is_disconnected(port))
+ new_state = SNK_UNATTACHED;
+ else
+ break;
+ if (new_state != port->delayed_state)
+ tcpm_set_state(port, SNK_ATTACH_WAIT, 0);
+ break;
+ case SNK_DEBOUNCED:
+ if (tcpm_port_is_disconnected(port))
+ new_state = SNK_UNATTACHED;
+ else if (port->vbus_present)
+ new_state = tcpm_try_src(port) ? SRC_TRY : SNK_ATTACHED;
+ else
+ new_state = SNK_UNATTACHED;
+ if (new_state != port->delayed_state)
+ tcpm_set_state(port, SNK_DEBOUNCED, 0);
+ break;
+ case SNK_READY:
+ if (tcpm_port_is_disconnected(port))
+ tcpm_set_state(port, unattached_state(port), 0);
+ else if (!port->pd_capable &&
+ (cc1 != old_cc1 || cc2 != old_cc2))
+ tcpm_set_current_limit(port,
+ tcpm_get_current_limit(port),
+ 5000);
+ break;
+
+ case AUDIO_ACC_ATTACHED:
+ if (cc1 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_OPEN)
+ tcpm_set_state(port, AUDIO_ACC_DEBOUNCE, 0);
+ break;
+ case AUDIO_ACC_DEBOUNCE:
+ if (tcpm_port_is_audio(port))
+ tcpm_set_state(port, AUDIO_ACC_ATTACHED, 0);
+ break;
+
+ case DEBUG_ACC_ATTACHED:
+ if (cc1 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_OPEN)
+ tcpm_set_state(port, ACC_UNATTACHED, 0);
+ break;
+
+ case SNK_TRY:
+ /* Do nothing, waiting for timeout */
+ break;
+
+ case SNK_DISCOVERY:
+ /* CC line is unstable, wait for debounce */
+ if (tcpm_port_is_disconnected(port))
+ tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE, 0);
+ break;
+ case SNK_DISCOVERY_DEBOUNCE:
+ break;
+
+ case SRC_TRYWAIT:
+ /* Hand over to state machine if needed */
+ if (!port->vbus_present && tcpm_port_is_source(port))
+ tcpm_set_state(port, SRC_TRYWAIT_DEBOUNCE, 0);
+ break;
+ case SRC_TRYWAIT_DEBOUNCE:
+ if (port->vbus_present || !tcpm_port_is_source(port))
+ tcpm_set_state(port, SRC_TRYWAIT, 0);
+ break;
+ case SNK_TRY_WAIT_DEBOUNCE:
+ if (!tcpm_port_is_sink(port)) {
+ port->max_wait = 0;
+ tcpm_set_state(port, SRC_TRYWAIT, 0);
+ }
+ break;
+ case SRC_TRY_WAIT:
+ if (tcpm_port_is_source(port))
+ tcpm_set_state(port, SRC_TRY_DEBOUNCE, 0);
+ break;
+ case SRC_TRY_DEBOUNCE:
+ tcpm_set_state(port, SRC_TRY_WAIT, 0);
+ break;
+ case SNK_TRYWAIT_DEBOUNCE:
+ if (tcpm_port_is_sink(port))
+ tcpm_set_state(port, SNK_TRYWAIT_VBUS, 0);
+ break;
+ case SNK_TRYWAIT_VBUS:
+ if (!tcpm_port_is_sink(port))
+ tcpm_set_state(port, SNK_TRYWAIT_DEBOUNCE, 0);
+ break;
+ case SNK_TRYWAIT:
+ /* Do nothing, waiting for tCCDebounce */
+ break;
+ case PR_SWAP_SNK_SRC_SINK_OFF:
+ case PR_SWAP_SRC_SNK_TRANSITION_OFF:
+ case PR_SWAP_SRC_SNK_SOURCE_OFF:
+ /*
+ * CC state change is expected here; we just turned off power.
+ * Ignore it.
+ */
+ break;
+
+ default:
+ if (tcpm_port_is_disconnected(port))
+ tcpm_set_state(port, unattached_state(port), 0);
+ break;
+ }
+}
+
+static void _tcpm_pd_vbus_on(struct tcpm_port *port)
+{
+ tcpm_log_force(port, "VBUS on");
+ port->vbus_present = true;
+ switch (port->state) {
+ case SNK_TRANSITION_SINK_VBUS:
+ tcpm_set_state(port, SNK_READY, 0);
+ break;
+ case SNK_DISCOVERY:
+ tcpm_set_state(port, SNK_DISCOVERY, 0);
+ break;
+
+ case SNK_DEBOUNCED:
+ tcpm_set_state(port, tcpm_try_src(port) ? SRC_TRY
+ : SNK_ATTACHED,
+ 0);
+ break;
+ case SNK_HARD_RESET_WAIT_VBUS:
+ tcpm_set_state(port, SNK_HARD_RESET_SINK_ON, 0);
+ break;
+ case SRC_ATTACHED:
+ tcpm_set_state(port, SRC_STARTUP, 0);
+ break;
+ case SRC_HARD_RESET_VBUS_ON:
+ tcpm_set_state(port, SRC_STARTUP, 0);
+ break;
+
+ case SNK_TRY:
+ /* Do nothing, waiting for timeout */
+ break;
+ case SRC_TRYWAIT:
+ /* Do nothing, Waiting for Rd to be detected */
+ break;
+ case SRC_TRYWAIT_DEBOUNCE:
+ tcpm_set_state(port, SRC_TRYWAIT, 0);
+ break;
+ case SNK_TRY_WAIT_DEBOUNCE:
+ /* Do nothing, waiting for PD_DEBOUNCE to do be done */
+ break;
+ case SNK_TRYWAIT:
+ /* Do nothing, waiting for tCCDebounce */
+ break;
+ case SNK_TRYWAIT_VBUS:
+ if (tcpm_port_is_sink(port))
+ tcpm_set_state(port, SNK_ATTACHED, 0);
+ break;
+ case SNK_TRYWAIT_DEBOUNCE:
+ /* Do nothing, waiting for Rp */
+ break;
+ case SRC_TRY_WAIT:
+ case SRC_TRY_DEBOUNCE:
+ /* Do nothing, waiting for sink detection */
+ break;
+ default:
+ break;
+ }
+}
+
+static void _tcpm_pd_vbus_off(struct tcpm_port *port)
+{
+ tcpm_log_force(port, "VBUS off");
+ port->vbus_present = false;
+ port->vbus_never_low = false;
+ switch (port->state) {
+ case SNK_HARD_RESET_SINK_OFF:
+ tcpm_set_state(port, SNK_HARD_RESET_WAIT_VBUS, 0);
+ break;
+ case SRC_HARD_RESET_VBUS_OFF:
+ tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, 0);
+ break;
+ case HARD_RESET_SEND:
+ break;
+
+ case SNK_TRY:
+ /* Do nothing, waiting for timeout */
+ break;
+ case SRC_TRYWAIT:
+ /* Hand over to state machine if needed */
+ if (tcpm_port_is_source(port))
+ tcpm_set_state(port, SRC_TRYWAIT_DEBOUNCE, 0);
+ break;
+ case SNK_TRY_WAIT_DEBOUNCE:
+ /* Do nothing, waiting for PD_DEBOUNCE to do be done */
+ break;
+ case SNK_TRYWAIT:
+ case SNK_TRYWAIT_VBUS:
+ case SNK_TRYWAIT_DEBOUNCE:
+ break;
+
+ case SNK_ATTACH_WAIT:
+ tcpm_set_state(port, SNK_UNATTACHED, 0);
+ break;
+
+ case SNK_NEGOTIATE_CAPABILITIES:
+ break;
+
+ case PR_SWAP_SRC_SNK_TRANSITION_OFF:
+ tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF, 0);
+ break;
+
+ case PR_SWAP_SNK_SRC_SINK_OFF:
+ /* Do nothing, expected */
+ break;
+
+ case PORT_RESET_WAIT_OFF:
+ tcpm_set_state(port, tcpm_default_state(port), 0);
+ break;
+ case SRC_TRY_WAIT:
+ case SRC_TRY_DEBOUNCE:
+ /* Do nothing, waiting for sink detection */
+ break;
+ default:
+ if (port->pwr_role == TYPEC_SINK &&
+ port->attached)
+ tcpm_set_state(port, SNK_UNATTACHED, 0);
+ break;
+ }
+}
+
+static void _tcpm_pd_hard_reset(struct tcpm_port *port)
+{
+ tcpm_log_force(port, "Received hard reset");
+ /*
+ * If we keep receiving hard reset requests, executing the hard reset
+ * must have failed. Revert to error recovery if that happens.
+ */
+ tcpm_set_state(port,
+ port->hard_reset_count < PD_N_HARD_RESET_COUNT ?
+ HARD_RESET_START : ERROR_RECOVERY,
+ 0);
+}
+
+static void tcpm_pd_event_handler(struct work_struct *work)
+{
+ struct tcpm_port *port = container_of(work, struct tcpm_port,
+ event_work);
+ u32 events;
+
+ mutex_lock(&port->lock);
+
+ spin_lock(&port->pd_event_lock);
+ while (port->pd_events) {
+ events = port->pd_events;
+ port->pd_events = 0;
+ spin_unlock(&port->pd_event_lock);
+ if (events & TCPM_RESET_EVENT)
+ _tcpm_pd_hard_reset(port);
+ if (events & TCPM_VBUS_EVENT) {
+ bool vbus;
+
+ vbus = port->tcpc->get_vbus(port->tcpc);
+ if (vbus)
+ _tcpm_pd_vbus_on(port);
+ else
+ _tcpm_pd_vbus_off(port);
+ }
+ if (events & TCPM_CC_EVENT) {
+ enum typec_cc_status cc1, cc2;
+
+ if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0)
+ _tcpm_cc_change(port, cc1, cc2);
+ }
+ spin_lock(&port->pd_event_lock);
+ }
+ spin_unlock(&port->pd_event_lock);
+ mutex_unlock(&port->lock);
+}
+
+void tcpm_cc_change(struct tcpm_port *port)
+{
+ spin_lock(&port->pd_event_lock);
+ port->pd_events |= TCPM_CC_EVENT;
+ spin_unlock(&port->pd_event_lock);
+ queue_work(port->wq, &port->event_work);
+}
+EXPORT_SYMBOL_GPL(tcpm_cc_change);
+
+void tcpm_vbus_change(struct tcpm_port *port)
+{
+ spin_lock(&port->pd_event_lock);
+ port->pd_events |= TCPM_VBUS_EVENT;
+ spin_unlock(&port->pd_event_lock);
+ queue_work(port->wq, &port->event_work);
+}
+EXPORT_SYMBOL_GPL(tcpm_vbus_change);
+
+void tcpm_pd_hard_reset(struct tcpm_port *port)
+{
+ spin_lock(&port->pd_event_lock);
+ port->pd_events = TCPM_RESET_EVENT;
+ spin_unlock(&port->pd_event_lock);
+ queue_work(port->wq, &port->event_work);
+}
+EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset);
+
+static int tcpm_dr_set(const struct typec_capability *cap,
+ enum typec_data_role data)
+{
+ struct tcpm_port *port = typec_cap_to_tcpm(cap);
+ int ret;
+
+ mutex_lock(&port->swap_lock);
+ mutex_lock(&port->lock);
+
+ if (port->port_type != TYPEC_PORT_DRP) {
+ ret = -EINVAL;
+ goto port_unlock;
+ }
+ if (port->state != SRC_READY && port->state != SNK_READY) {
+ ret = -EAGAIN;
+ goto port_unlock;
+ }
+
+ if (port->data_role == data) {
+ ret = 0;
+ goto port_unlock;
+ }
+
+ /*
+ * XXX
+ * 6.3.9: If an alternate mode is active, a request to swap
+ * alternate modes shall trigger a port reset.
+ * Reject data role swap request in this case.
+ */
+
+ if (!port->pd_capable) {
+ /*
+ * If the partner is not PD capable, reset the port to
+ * trigger a role change. This can only work if a preferred
+ * role is configured, and if it matches the requested role.
+ */
+ if (port->try_role == TYPEC_NO_PREFERRED_ROLE ||
+ port->try_role == port->pwr_role) {
+ ret = -EINVAL;
+ goto port_unlock;
+ }
+ port->non_pd_role_swap = true;
+ tcpm_set_state(port, PORT_RESET, 0);
+ } else {
+ tcpm_set_state(port, DR_SWAP_SEND, 0);
+ }
+
+ port->swap_status = 0;
+ port->swap_pending = true;
+ reinit_completion(&port->swap_complete);
+ mutex_unlock(&port->lock);
+
+ if (!wait_for_completion_timeout(&port->swap_complete,
+ msecs_to_jiffies(PD_ROLE_SWAP_TIMEOUT)))
+ ret = -ETIMEDOUT;
+ else
+ ret = port->swap_status;
+
+ port->non_pd_role_swap = false;
+ goto swap_unlock;
+
+port_unlock:
+ mutex_unlock(&port->lock);
+swap_unlock:
+ mutex_unlock(&port->swap_lock);
+ return ret;
+}
+
+static int tcpm_pr_set(const struct typec_capability *cap,
+ enum typec_role role)
+{
+ struct tcpm_port *port = typec_cap_to_tcpm(cap);
+ int ret;
+
+ mutex_lock(&port->swap_lock);
+ mutex_lock(&port->lock);
+
+ if (port->port_type != TYPEC_PORT_DRP) {
+ ret = -EINVAL;
+ goto port_unlock;
+ }
+ if (port->state != SRC_READY && port->state != SNK_READY) {
+ ret = -EAGAIN;
+ goto port_unlock;
+ }
+
+ if (role == port->pwr_role) {
+ ret = 0;
+ goto port_unlock;
+ }
+
+ port->swap_status = 0;
+ port->swap_pending = true;
+ reinit_completion(&port->swap_complete);
+ tcpm_set_state(port, PR_SWAP_SEND, 0);
+ mutex_unlock(&port->lock);
+
+ if (!wait_for_completion_timeout(&port->swap_complete,
+ msecs_to_jiffies(PD_ROLE_SWAP_TIMEOUT)))
+ ret = -ETIMEDOUT;
+ else
+ ret = port->swap_status;
+
+ goto swap_unlock;
+
+port_unlock:
+ mutex_unlock(&port->lock);
+swap_unlock:
+ mutex_unlock(&port->swap_lock);
+ return ret;
+}
+
+static int tcpm_vconn_set(const struct typec_capability *cap,
+ enum typec_role role)
+{
+ struct tcpm_port *port = typec_cap_to_tcpm(cap);
+ int ret;
+
+ mutex_lock(&port->swap_lock);
+ mutex_lock(&port->lock);
+
+ if (port->state != SRC_READY && port->state != SNK_READY) {
+ ret = -EAGAIN;
+ goto port_unlock;
+ }
+
+ if (role == port->vconn_role) {
+ ret = 0;
+ goto port_unlock;
+ }
+
+ port->swap_status = 0;
+ port->swap_pending = true;
+ reinit_completion(&port->swap_complete);
+ tcpm_set_state(port, VCONN_SWAP_SEND, 0);
+ mutex_unlock(&port->lock);
+
+ if (!wait_for_completion_timeout(&port->swap_complete,
+ msecs_to_jiffies(PD_ROLE_SWAP_TIMEOUT)))
+ ret = -ETIMEDOUT;
+ else
+ ret = port->swap_status;
+
+ goto swap_unlock;
+
+port_unlock:
+ mutex_unlock(&port->lock);
+swap_unlock:
+ mutex_unlock(&port->swap_lock);
+ return ret;
+}
+
+static int tcpm_try_role(const struct typec_capability *cap, int role)
+{
+ struct tcpm_port *port = typec_cap_to_tcpm(cap);
+ struct tcpc_dev *tcpc = port->tcpc;
+ int ret = 0;
+
+ mutex_lock(&port->lock);
+ if (tcpc->try_role)
+ ret = tcpc->try_role(tcpc, role);
+ if (!ret && !tcpc->config->try_role_hw)
+ port->try_role = role;
+ port->try_src_count = 0;
+ port->try_snk_count = 0;
+ mutex_unlock(&port->lock);
+
+ return ret;
+}
+
+static void tcpm_init(struct tcpm_port *port)
+{
+ enum typec_cc_status cc1, cc2;
+
+ port->tcpc->init(port->tcpc);
+
+ tcpm_reset_port(port);
+
+ /*
+ * XXX
+ * Should possibly wait for VBUS to settle if it was enabled locally
+ * since tcpm_reset_port() will disable VBUS.
+ */
+ port->vbus_present = port->tcpc->get_vbus(port->tcpc);
+ if (port->vbus_present)
+ port->vbus_never_low = true;
+
+ tcpm_set_state(port, tcpm_default_state(port), 0);
+
+ if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0)
+ _tcpm_cc_change(port, cc1, cc2);
+
+ /*
+ * Some adapters need a clean slate at startup, and won't recover
+ * otherwise. So do not try to be fancy and force a clean disconnect.
+ */
+ tcpm_set_state(port, PORT_RESET, 0);
+}
+
+static int tcpm_port_type_set(const struct typec_capability *cap,
+ enum typec_port_type type)
+{
+ struct tcpm_port *port = typec_cap_to_tcpm(cap);
+
+ mutex_lock(&port->lock);
+ if (type == port->port_type)
+ goto port_unlock;
+
+ port->port_type = type;
+
+ if (!port->connected)
+ tcpm_set_state(port, PORT_RESET, 0);
+ else if (type == TYPEC_PORT_UFP) {
+ if (!(port->pwr_role == TYPEC_SINK &&
+ port->data_role == TYPEC_DEVICE))
+ tcpm_set_state(port, PORT_RESET, 0);
+ } else if (type == TYPEC_PORT_DFP) {
+ if (!(port->pwr_role == TYPEC_SOURCE &&
+ port->data_role == TYPEC_HOST))
+ tcpm_set_state(port, PORT_RESET, 0);
+ }
+
+port_unlock:
+ mutex_unlock(&port->lock);
+ return 0;
+}
+
+
+void tcpm_tcpc_reset(struct tcpm_port *port)
+{
+ mutex_lock(&port->lock);
+ /* XXX: Maintain PD connection if possible? */
+ tcpm_init(port);
+ mutex_unlock(&port->lock);
+}
+EXPORT_SYMBOL_GPL(tcpm_tcpc_reset);
+
+static int tcpm_copy_pdos(u32 *dest_pdo, const u32 *src_pdo,
+ unsigned int nr_pdo)
+{
+ unsigned int i;
+
+ if (nr_pdo > PDO_MAX_OBJECTS)
+ nr_pdo = PDO_MAX_OBJECTS;
+
+ for (i = 0; i < nr_pdo; i++)
+ dest_pdo[i] = src_pdo[i];
+
+ return nr_pdo;
+}
+
+static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo,
+ unsigned int nr_vdo)
+{
+ unsigned int i;
+
+ if (nr_vdo > VDO_MAX_OBJECTS)
+ nr_vdo = VDO_MAX_OBJECTS;
+
+ for (i = 0; i < nr_vdo; i++)
+ dest_vdo[i] = src_vdo[i];
+
+ return nr_vdo;
+}
+
+void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo,
+ unsigned int nr_pdo)
+{
+ mutex_lock(&port->lock);
+ port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, pdo, nr_pdo);
+ switch (port->state) {
+ case SRC_UNATTACHED:
+ case SRC_ATTACH_WAIT:
+ case SRC_TRYWAIT:
+ tcpm_set_cc(port, tcpm_rp_cc(port));
+ break;
+ case SRC_SEND_CAPABILITIES:
+ case SRC_NEGOTIATE_CAPABILITIES:
+ case SRC_READY:
+ case SRC_WAIT_NEW_CAPABILITIES:
+ tcpm_set_cc(port, tcpm_rp_cc(port));
+ tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&port->lock);
+}
+EXPORT_SYMBOL_GPL(tcpm_update_source_capabilities);
+
+void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
+ unsigned int nr_pdo,
+ unsigned int max_snk_mv,
+ unsigned int max_snk_ma,
+ unsigned int max_snk_mw,
+ unsigned int operating_snk_mw)
+{
+ mutex_lock(&port->lock);
+ port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, pdo, nr_pdo);
+ port->max_snk_mv = max_snk_mv;
+ port->max_snk_ma = max_snk_ma;
+ port->max_snk_mw = max_snk_mw;
+ port->operating_snk_mw = operating_snk_mw;
+
+ switch (port->state) {
+ case SNK_NEGOTIATE_CAPABILITIES:
+ case SNK_READY:
+ case SNK_TRANSITION_SINK:
+ case SNK_TRANSITION_SINK_VBUS:
+ tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&port->lock);
+}
+EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities);
+
+struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
+{
+ struct tcpm_port *port;
+ int i, err;
+
+ if (!dev || !tcpc || !tcpc->config ||
+ !tcpc->get_vbus || !tcpc->set_cc || !tcpc->get_cc ||
+ !tcpc->set_polarity || !tcpc->set_vconn || !tcpc->set_vbus ||
+ !tcpc->set_pd_rx || !tcpc->set_roles || !tcpc->pd_transmit)
+ return ERR_PTR(-EINVAL);
+
+ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return ERR_PTR(-ENOMEM);
+
+ port->dev = dev;
+ port->tcpc = tcpc;
+
+ mutex_init(&port->lock);
+ mutex_init(&port->swap_lock);
+
+ port->wq = create_singlethread_workqueue(dev_name(dev));
+ if (!port->wq)
+ return ERR_PTR(-ENOMEM);
+ INIT_DELAYED_WORK(&port->state_machine, tcpm_state_machine_work);
+ INIT_DELAYED_WORK(&port->vdm_state_machine, vdm_state_machine_work);
+ INIT_WORK(&port->event_work, tcpm_pd_event_handler);
+
+ spin_lock_init(&port->pd_event_lock);
+
+ init_completion(&port->tx_complete);
+ init_completion(&port->swap_complete);
+
+ port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcpc->config->src_pdo,
+ tcpc->config->nr_src_pdo);
+ port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo,
+ tcpc->config->nr_snk_pdo);
+ port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcpc->config->snk_vdo,
+ tcpc->config->nr_snk_vdo);
+
+ port->max_snk_mv = tcpc->config->max_snk_mv;
+ port->max_snk_ma = tcpc->config->max_snk_ma;
+ port->max_snk_mw = tcpc->config->max_snk_mw;
+ port->operating_snk_mw = tcpc->config->operating_snk_mw;
+ if (!tcpc->config->try_role_hw)
+ port->try_role = tcpc->config->default_role;
+ else
+ port->try_role = TYPEC_NO_PREFERRED_ROLE;
+
+ port->typec_caps.prefer_role = tcpc->config->default_role;
+ port->typec_caps.type = tcpc->config->type;
+ port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */
+ port->typec_caps.pd_revision = 0x0200; /* USB-PD spec release 2.0 */
+ port->typec_caps.dr_set = tcpm_dr_set;
+ port->typec_caps.pr_set = tcpm_pr_set;
+ port->typec_caps.vconn_set = tcpm_vconn_set;
+ port->typec_caps.try_role = tcpm_try_role;
+ port->typec_caps.port_type_set = tcpm_port_type_set;
+
+ port->partner_desc.identity = &port->partner_ident;
+ port->port_type = tcpc->config->type;
+
+ /*
+ * TODO:
+ * - alt_modes, set_alt_mode
+ * - {debug,audio}_accessory
+ */
+
+ port->typec_port = typec_register_port(port->dev, &port->typec_caps);
+ if (!port->typec_port) {
+ err = -ENOMEM;
+ goto out_destroy_wq;
+ }
+
+ if (tcpc->config->alt_modes) {
+ struct typec_altmode_desc *paltmode = tcpc->config->alt_modes;
+
+ i = 0;
+ while (paltmode->svid && i < ARRAY_SIZE(port->port_altmode)) {
+ port->port_altmode[i] =
+ typec_port_register_altmode(port->typec_port,
+ paltmode);
+ if (!port->port_altmode[i]) {
+ tcpm_log(port,
+ "%s: failed to register port alternate mode 0x%x",
+ dev_name(dev), paltmode->svid);
+ break;
+ }
+ i++;
+ paltmode++;
+ }
+ }
+
+ tcpm_debugfs_init(port);
+ mutex_lock(&port->lock);
+ tcpm_init(port);
+ mutex_unlock(&port->lock);
+
+ tcpm_log(port, "%s: registered", dev_name(dev));
+ return port;
+
+out_destroy_wq:
+ destroy_workqueue(port->wq);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(tcpm_register_port);
+
+void tcpm_unregister_port(struct tcpm_port *port)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++)
+ typec_unregister_altmode(port->port_altmode[i]);
+ typec_unregister_port(port->typec_port);
+ tcpm_debugfs_exit(port);
+ destroy_workqueue(port->wq);
+}
+EXPORT_SYMBOL_GPL(tcpm_unregister_port);
+
+MODULE_AUTHOR("Guenter Roeck <groeck@chromium.org>");
+MODULE_DESCRIPTION("USB Type-C Port Manager");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/typec/tcpm.h b/drivers/usb/typec/tcpm.h
new file mode 100644
index 0000000..d37db1e
--- /dev/null
+++ b/drivers/usb/typec/tcpm.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2015-2017 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_USB_TCPM_H
+#define __LINUX_USB_TCPM_H
+
+#include <linux/bitops.h>
+#include <linux/usb/typec.h>
+#include "pd.h"
+
+enum typec_cc_status {
+ TYPEC_CC_OPEN,
+ TYPEC_CC_RA,
+ TYPEC_CC_RD,
+ TYPEC_CC_RP_DEF,
+ TYPEC_CC_RP_1_5,
+ TYPEC_CC_RP_3_0,
+};
+
+enum typec_cc_polarity {
+ TYPEC_POLARITY_CC1,
+ TYPEC_POLARITY_CC2,
+};
+
+/* Time to wait for TCPC to complete transmit */
+#define PD_T_TCPC_TX_TIMEOUT 100 /* in ms */
+#define PD_ROLE_SWAP_TIMEOUT (MSEC_PER_SEC * 10)
+
+enum tcpm_transmit_status {
+ TCPC_TX_SUCCESS = 0,
+ TCPC_TX_DISCARDED = 1,
+ TCPC_TX_FAILED = 2,
+};
+
+enum tcpm_transmit_type {
+ TCPC_TX_SOP = 0,
+ TCPC_TX_SOP_PRIME = 1,
+ TCPC_TX_SOP_PRIME_PRIME = 2,
+ TCPC_TX_SOP_DEBUG_PRIME = 3,
+ TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4,
+ TCPC_TX_HARD_RESET = 5,
+ TCPC_TX_CABLE_RESET = 6,
+ TCPC_TX_BIST_MODE_2 = 7
+};
+
+struct tcpc_config {
+ const u32 *src_pdo;
+ unsigned int nr_src_pdo;
+
+ const u32 *snk_pdo;
+ unsigned int nr_snk_pdo;
+
+ const u32 *snk_vdo;
+ unsigned int nr_snk_vdo;
+
+ unsigned int max_snk_mv;
+ unsigned int max_snk_ma;
+ unsigned int max_snk_mw;
+ unsigned int operating_snk_mw;
+
+ enum typec_port_type type;
+ enum typec_role default_role;
+ bool try_role_hw; /* try.{src,snk} implemented in hardware */
+
+ struct typec_altmode_desc *alt_modes;
+};
+
+enum tcpc_usb_switch {
+ TCPC_USB_SWITCH_CONNECT,
+ TCPC_USB_SWITCH_DISCONNECT,
+ TCPC_USB_SWITCH_RESTORE, /* TODO FIXME */
+};
+
+/* Mux state attributes */
+#define TCPC_MUX_USB_ENABLED BIT(0) /* USB enabled */
+#define TCPC_MUX_DP_ENABLED BIT(1) /* DP enabled */
+#define TCPC_MUX_POLARITY_INVERTED BIT(2) /* Polarity inverted */
+
+/* Mux modes, decoded to attributes */
+enum tcpc_mux_mode {
+ TYPEC_MUX_NONE = 0, /* Open switch */
+ TYPEC_MUX_USB = TCPC_MUX_USB_ENABLED, /* USB only */
+ TYPEC_MUX_DP = TCPC_MUX_DP_ENABLED, /* DP only */
+ TYPEC_MUX_DOCK = TCPC_MUX_USB_ENABLED | /* Both USB and DP */
+ TCPC_MUX_DP_ENABLED,
+};
+
+struct tcpc_mux_dev {
+ int (*set)(struct tcpc_mux_dev *dev, enum tcpc_mux_mode mux_mode,
+ enum tcpc_usb_switch usb_config,
+ enum typec_cc_polarity polarity);
+ bool dfp_only;
+ void *priv_data;
+};
+
+struct tcpc_dev {
+ const struct tcpc_config *config;
+
+ int (*init)(struct tcpc_dev *dev);
+ int (*get_vbus)(struct tcpc_dev *dev);
+ int (*set_cc)(struct tcpc_dev *dev, enum typec_cc_status cc);
+ int (*get_cc)(struct tcpc_dev *dev, enum typec_cc_status *cc1,
+ enum typec_cc_status *cc2);
+ int (*set_polarity)(struct tcpc_dev *dev,
+ enum typec_cc_polarity polarity);
+ int (*set_vconn)(struct tcpc_dev *dev, bool on);
+ int (*set_vbus)(struct tcpc_dev *dev, bool on, bool charge);
+ int (*set_current_limit)(struct tcpc_dev *dev, u32 max_ma, u32 mv);
+ int (*set_pd_rx)(struct tcpc_dev *dev, bool on);
+ int (*set_roles)(struct tcpc_dev *dev, bool attached,
+ enum typec_role role, enum typec_data_role data);
+ int (*start_drp_toggling)(struct tcpc_dev *dev,
+ enum typec_cc_status cc);
+ int (*try_role)(struct tcpc_dev *dev, int role);
+ int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type,
+ const struct pd_message *msg);
+ struct tcpc_mux_dev *mux;
+};
+
+struct tcpm_port;
+
+struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc);
+void tcpm_unregister_port(struct tcpm_port *port);
+
+void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo,
+ unsigned int nr_pdo);
+void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
+ unsigned int nr_pdo,
+ unsigned int max_snk_mv,
+ unsigned int max_snk_ma,
+ unsigned int max_snk_mw,
+ unsigned int operating_snk_mw);
+
+void tcpm_vbus_change(struct tcpm_port *port);
+void tcpm_cc_change(struct tcpm_port *port);
+void tcpm_pd_receive(struct tcpm_port *port,
+ const struct pd_message *msg);
+void tcpm_pd_transmit_complete(struct tcpm_port *port,
+ enum tcpm_transmit_status status);
+void tcpm_pd_hard_reset(struct tcpm_port *port);
+void tcpm_tcpc_reset(struct tcpm_port *port);
+
+#endif /* __LINUX_USB_TCPM_H */
diff --git a/drivers/usb/typec/typec.c b/drivers/usb/typec/typec.c
new file mode 100644
index 0000000..da38ac7
--- /dev/null
+++ b/drivers/usb/typec/typec.c
@@ -0,0 +1,1347 @@
+/*
+ * USB Type-C Connector Class
+ *
+ * Copyright (C) 2017, Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/usb/typec.h>
+
+struct typec_mode {
+ int index;
+ u32 vdo;
+ char *desc;
+ enum typec_port_type roles;
+
+ struct typec_altmode *alt_mode;
+
+ unsigned int active:1;
+
+ char group_name[6];
+ struct attribute_group group;
+ struct attribute *attrs[5];
+ struct device_attribute vdo_attr;
+ struct device_attribute desc_attr;
+ struct device_attribute active_attr;
+ struct device_attribute roles_attr;
+};
+
+struct typec_altmode {
+ struct device dev;
+ u16 svid;
+ int n_modes;
+ struct typec_mode modes[ALTMODE_MAX_MODES];
+ const struct attribute_group *mode_groups[ALTMODE_MAX_MODES];
+};
+
+struct typec_plug {
+ struct device dev;
+ enum typec_plug_index index;
+};
+
+struct typec_cable {
+ struct device dev;
+ enum typec_plug_type type;
+ struct usb_pd_identity *identity;
+ unsigned int active:1;
+};
+
+struct typec_partner {
+ struct device dev;
+ unsigned int usb_pd:1;
+ struct usb_pd_identity *identity;
+ enum typec_accessory accessory;
+};
+
+struct typec_port {
+ unsigned int id;
+ struct device dev;
+
+ int prefer_role;
+ enum typec_data_role data_role;
+ enum typec_role pwr_role;
+ enum typec_role vconn_role;
+ enum typec_pwr_opmode pwr_opmode;
+ enum typec_port_type port_type;
+ struct mutex port_type_lock;
+
+ const struct typec_capability *cap;
+};
+
+#define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
+#define to_typec_plug(_dev_) container_of(_dev_, struct typec_plug, dev)
+#define to_typec_cable(_dev_) container_of(_dev_, struct typec_cable, dev)
+#define to_typec_partner(_dev_) container_of(_dev_, struct typec_partner, dev)
+#define to_altmode(_dev_) container_of(_dev_, struct typec_altmode, dev)
+
+static const struct device_type typec_partner_dev_type;
+static const struct device_type typec_cable_dev_type;
+static const struct device_type typec_plug_dev_type;
+static const struct device_type typec_port_dev_type;
+
+#define is_typec_partner(_dev_) (_dev_->type == &typec_partner_dev_type)
+#define is_typec_cable(_dev_) (_dev_->type == &typec_cable_dev_type)
+#define is_typec_plug(_dev_) (_dev_->type == &typec_plug_dev_type)
+#define is_typec_port(_dev_) (_dev_->type == &typec_port_dev_type)
+
+static DEFINE_IDA(typec_index_ida);
+static struct class *typec_class;
+
+/* Common attributes */
+
+static const char * const typec_accessory_modes[] = {
+ [TYPEC_ACCESSORY_NONE] = "none",
+ [TYPEC_ACCESSORY_AUDIO] = "analog_audio",
+ [TYPEC_ACCESSORY_DEBUG] = "debug",
+};
+
+static struct usb_pd_identity *get_pd_identity(struct device *dev)
+{
+ if (is_typec_partner(dev)) {
+ struct typec_partner *partner = to_typec_partner(dev);
+
+ return partner->identity;
+ } else if (is_typec_cable(dev)) {
+ struct typec_cable *cable = to_typec_cable(dev);
+
+ return cable->identity;
+ }
+ return NULL;
+}
+
+static ssize_t id_header_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_pd_identity *id = get_pd_identity(dev);
+
+ return sprintf(buf, "0x%08x\n", id->id_header);
+}
+static DEVICE_ATTR_RO(id_header);
+
+static ssize_t cert_stat_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_pd_identity *id = get_pd_identity(dev);
+
+ return sprintf(buf, "0x%08x\n", id->cert_stat);
+}
+static DEVICE_ATTR_RO(cert_stat);
+
+static ssize_t product_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_pd_identity *id = get_pd_identity(dev);
+
+ return sprintf(buf, "0x%08x\n", id->product);
+}
+static DEVICE_ATTR_RO(product);
+
+static struct attribute *usb_pd_id_attrs[] = {
+ &dev_attr_id_header.attr,
+ &dev_attr_cert_stat.attr,
+ &dev_attr_product.attr,
+ NULL
+};
+
+static const struct attribute_group usb_pd_id_group = {
+ .name = "identity",
+ .attrs = usb_pd_id_attrs,
+};
+
+static const struct attribute_group *usb_pd_id_groups[] = {
+ &usb_pd_id_group,
+ NULL,
+};
+
+static void typec_report_identity(struct device *dev)
+{
+ sysfs_notify(&dev->kobj, "identity", "id_header");
+ sysfs_notify(&dev->kobj, "identity", "cert_stat");
+ sysfs_notify(&dev->kobj, "identity", "product");
+}
+
+/* ------------------------------------------------------------------------- */
+/* Alternate Modes */
+
+/**
+ * typec_altmode_update_active - Report Enter/Exit mode
+ * @alt: Handle to the alternate mode
+ * @mode: Mode index
+ * @active: True when the mode has been entered
+ *
+ * If a partner or cable plug executes Enter/Exit Mode command successfully, the
+ * drivers use this routine to report the updated state of the mode.
+ */
+void typec_altmode_update_active(struct typec_altmode *alt, int mode,
+ bool active)
+{
+ struct typec_mode *m = &alt->modes[mode];
+ char dir[6];
+
+ if (m->active == active)
+ return;
+
+ m->active = active;
+ snprintf(dir, sizeof(dir), "mode%d", mode);
+ sysfs_notify(&alt->dev.kobj, dir, "active");
+ kobject_uevent(&alt->dev.kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_update_active);
+
+/**
+ * typec_altmode2port - Alternate Mode to USB Type-C port
+ * @alt: The Alternate Mode
+ *
+ * Returns handle to the port that a cable plug or partner with @alt is
+ * connected to.
+ */
+struct typec_port *typec_altmode2port(struct typec_altmode *alt)
+{
+ if (is_typec_plug(alt->dev.parent))
+ return to_typec_port(alt->dev.parent->parent->parent);
+ if (is_typec_partner(alt->dev.parent))
+ return to_typec_port(alt->dev.parent->parent);
+ if (is_typec_port(alt->dev.parent))
+ return to_typec_port(alt->dev.parent);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(typec_altmode2port);
+
+static ssize_t
+typec_altmode_vdo_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_mode *mode = container_of(attr, struct typec_mode,
+ vdo_attr);
+
+ return sprintf(buf, "0x%08x\n", mode->vdo);
+}
+
+static ssize_t
+typec_altmode_desc_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_mode *mode = container_of(attr, struct typec_mode,
+ desc_attr);
+
+ return sprintf(buf, "%s\n", mode->desc ? mode->desc : "");
+}
+
+static ssize_t
+typec_altmode_active_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_mode *mode = container_of(attr, struct typec_mode,
+ active_attr);
+
+ return sprintf(buf, "%s\n", mode->active ? "yes" : "no");
+}
+
+static ssize_t
+typec_altmode_active_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_mode *mode = container_of(attr, struct typec_mode,
+ active_attr);
+ struct typec_port *port = typec_altmode2port(mode->alt_mode);
+ bool activate;
+ int ret;
+
+ if (!port->cap->activate_mode)
+ return -EOPNOTSUPP;
+
+ ret = kstrtobool(buf, &activate);
+ if (ret)
+ return ret;
+
+ ret = port->cap->activate_mode(port->cap, mode->index, activate);
+ if (ret)
+ return ret;
+
+ return size;
+}
+
+static ssize_t
+typec_altmode_roles_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_mode *mode = container_of(attr, struct typec_mode,
+ roles_attr);
+ ssize_t ret;
+
+ switch (mode->roles) {
+ case TYPEC_PORT_DFP:
+ ret = sprintf(buf, "source\n");
+ break;
+ case TYPEC_PORT_UFP:
+ ret = sprintf(buf, "sink\n");
+ break;
+ case TYPEC_PORT_DRP:
+ default:
+ ret = sprintf(buf, "source sink\n");
+ break;
+ }
+ return ret;
+}
+
+static void typec_init_modes(struct typec_altmode *alt,
+ struct typec_mode_desc *desc, bool is_port)
+{
+ int i;
+
+ for (i = 0; i < alt->n_modes; i++, desc++) {
+ struct typec_mode *mode = &alt->modes[i];
+
+ /* Not considering the human readable description critical */
+ mode->desc = kstrdup(desc->desc, GFP_KERNEL);
+ if (desc->desc && !mode->desc)
+ dev_err(&alt->dev, "failed to copy mode%d desc\n", i);
+
+ mode->alt_mode = alt;
+ mode->vdo = desc->vdo;
+ mode->roles = desc->roles;
+ mode->index = desc->index;
+ sprintf(mode->group_name, "mode%d", desc->index);
+
+ sysfs_attr_init(&mode->vdo_attr.attr);
+ mode->vdo_attr.attr.name = "vdo";
+ mode->vdo_attr.attr.mode = 0444;
+ mode->vdo_attr.show = typec_altmode_vdo_show;
+
+ sysfs_attr_init(&mode->desc_attr.attr);
+ mode->desc_attr.attr.name = "description";
+ mode->desc_attr.attr.mode = 0444;
+ mode->desc_attr.show = typec_altmode_desc_show;
+
+ sysfs_attr_init(&mode->active_attr.attr);
+ mode->active_attr.attr.name = "active";
+ mode->active_attr.attr.mode = 0644;
+ mode->active_attr.show = typec_altmode_active_show;
+ mode->active_attr.store = typec_altmode_active_store;
+
+ mode->attrs[0] = &mode->vdo_attr.attr;
+ mode->attrs[1] = &mode->desc_attr.attr;
+ mode->attrs[2] = &mode->active_attr.attr;
+
+ /* With ports, list the roles that the mode is supported with */
+ if (is_port) {
+ sysfs_attr_init(&mode->roles_attr.attr);
+ mode->roles_attr.attr.name = "supported_roles";
+ mode->roles_attr.attr.mode = 0444;
+ mode->roles_attr.show = typec_altmode_roles_show;
+
+ mode->attrs[3] = &mode->roles_attr.attr;
+ }
+
+ mode->group.attrs = mode->attrs;
+ mode->group.name = mode->group_name;
+
+ alt->mode_groups[i] = &mode->group;
+ }
+}
+
+static ssize_t svid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_altmode *alt = to_altmode(dev);
+
+ return sprintf(buf, "%04x\n", alt->svid);
+}
+static DEVICE_ATTR_RO(svid);
+
+static struct attribute *typec_altmode_attrs[] = {
+ &dev_attr_svid.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(typec_altmode);
+
+static void typec_altmode_release(struct device *dev)
+{
+ struct typec_altmode *alt = to_altmode(dev);
+ int i;
+
+ for (i = 0; i < alt->n_modes; i++)
+ kfree(alt->modes[i].desc);
+ kfree(alt);
+}
+
+static const struct device_type typec_altmode_dev_type = {
+ .name = "typec_alternate_mode",
+ .groups = typec_altmode_groups,
+ .release = typec_altmode_release,
+};
+
+static struct typec_altmode *
+typec_register_altmode(struct device *parent, struct typec_altmode_desc *desc)
+{
+ struct typec_altmode *alt;
+ int ret;
+
+ alt = kzalloc(sizeof(*alt), GFP_KERNEL);
+ if (!alt)
+ return NULL;
+
+ alt->svid = desc->svid;
+ alt->n_modes = desc->n_modes;
+ typec_init_modes(alt, desc->modes, is_typec_port(parent));
+
+ alt->dev.parent = parent;
+ alt->dev.groups = alt->mode_groups;
+ alt->dev.type = &typec_altmode_dev_type;
+ dev_set_name(&alt->dev, "svid-%04x", alt->svid);
+
+ ret = device_register(&alt->dev);
+ if (ret) {
+ dev_err(parent, "failed to register alternate mode (%d)\n",
+ ret);
+ put_device(&alt->dev);
+ return NULL;
+ }
+
+ return alt;
+}
+
+/**
+ * typec_unregister_altmode - Unregister Alternate Mode
+ * @alt: The alternate mode to be unregistered
+ *
+ * Unregister device created with typec_partner_register_altmode(),
+ * typec_plug_register_altmode() or typec_port_register_altmode().
+ */
+void typec_unregister_altmode(struct typec_altmode *alt)
+{
+ if (alt)
+ device_unregister(&alt->dev);
+}
+EXPORT_SYMBOL_GPL(typec_unregister_altmode);
+
+/* ------------------------------------------------------------------------- */
+/* Type-C Partners */
+
+static ssize_t accessory_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_partner *p = to_typec_partner(dev);
+
+ return sprintf(buf, "%s\n", typec_accessory_modes[p->accessory]);
+}
+static DEVICE_ATTR_RO(accessory_mode);
+
+static ssize_t supports_usb_power_delivery_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_partner *p = to_typec_partner(dev);
+
+ return sprintf(buf, "%s\n", p->usb_pd ? "yes" : "no");
+}
+static DEVICE_ATTR_RO(supports_usb_power_delivery);
+
+static struct attribute *typec_partner_attrs[] = {
+ &dev_attr_accessory_mode.attr,
+ &dev_attr_supports_usb_power_delivery.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(typec_partner);
+
+static void typec_partner_release(struct device *dev)
+{
+ struct typec_partner *partner = to_typec_partner(dev);
+
+ kfree(partner);
+}
+
+static const struct device_type typec_partner_dev_type = {
+ .name = "typec_partner",
+ .groups = typec_partner_groups,
+ .release = typec_partner_release,
+};
+
+/**
+ * typec_partner_set_identity - Report result from Discover Identity command
+ * @partner: The partner updated identity values
+ *
+ * This routine is used to report that the result of Discover Identity USB power
+ * delivery command has become available.
+ */
+int typec_partner_set_identity(struct typec_partner *partner)
+{
+ if (!partner->identity)
+ return -EINVAL;
+
+ typec_report_identity(&partner->dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_partner_set_identity);
+
+/**
+ * typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode
+ * @partner: USB Type-C Partner that supports the alternate mode
+ * @desc: Description of the alternate mode
+ *
+ * This routine is used to register each alternate mode individually that
+ * @partner has listed in response to Discover SVIDs command. The modes for a
+ * SVID listed in response to Discover Modes command need to be listed in an
+ * array in @desc.
+ *
+ * Returns handle to the alternate mode on success or NULL on failure.
+ */
+struct typec_altmode *
+typec_partner_register_altmode(struct typec_partner *partner,
+ struct typec_altmode_desc *desc)
+{
+ return typec_register_altmode(&partner->dev, desc);
+}
+EXPORT_SYMBOL_GPL(typec_partner_register_altmode);
+
+/**
+ * typec_register_partner - Register a USB Type-C Partner
+ * @port: The USB Type-C Port the partner is connected to
+ * @desc: Description of the partner
+ *
+ * Registers a device for USB Type-C Partner described in @desc.
+ *
+ * Returns handle to the partner on success or NULL on failure.
+ */
+struct typec_partner *typec_register_partner(struct typec_port *port,
+ struct typec_partner_desc *desc)
+{
+ struct typec_partner *partner;
+ int ret;
+
+ partner = kzalloc(sizeof(*partner), GFP_KERNEL);
+ if (!partner)
+ return NULL;
+
+ partner->usb_pd = desc->usb_pd;
+ partner->accessory = desc->accessory;
+
+ if (desc->identity) {
+ /*
+ * Creating directory for the identity only if the driver is
+ * able to provide data to it.
+ */
+ partner->dev.groups = usb_pd_id_groups;
+ partner->identity = desc->identity;
+ }
+
+ partner->dev.class = typec_class;
+ partner->dev.parent = &port->dev;
+ partner->dev.type = &typec_partner_dev_type;
+ dev_set_name(&partner->dev, "%s-partner", dev_name(&port->dev));
+
+ ret = device_register(&partner->dev);
+ if (ret) {
+ dev_err(&port->dev, "failed to register partner (%d)\n", ret);
+ put_device(&partner->dev);
+ return NULL;
+ }
+
+ return partner;
+}
+EXPORT_SYMBOL_GPL(typec_register_partner);
+
+/**
+ * typec_unregister_partner - Unregister a USB Type-C Partner
+ * @partner: The partner to be unregistered
+ *
+ * Unregister device created with typec_register_partner().
+ */
+void typec_unregister_partner(struct typec_partner *partner)
+{
+ if (partner)
+ device_unregister(&partner->dev);
+}
+EXPORT_SYMBOL_GPL(typec_unregister_partner);
+
+/* ------------------------------------------------------------------------- */
+/* Type-C Cable Plugs */
+
+static void typec_plug_release(struct device *dev)
+{
+ struct typec_plug *plug = to_typec_plug(dev);
+
+ kfree(plug);
+}
+
+static const struct device_type typec_plug_dev_type = {
+ .name = "typec_plug",
+ .release = typec_plug_release,
+};
+
+/**
+ * typec_plug_register_altmode - Register USB Type-C Cable Plug Alternate Mode
+ * @plug: USB Type-C Cable Plug that supports the alternate mode
+ * @desc: Description of the alternate mode
+ *
+ * This routine is used to register each alternate mode individually that @plug
+ * has listed in response to Discover SVIDs command. The modes for a SVID that
+ * the plug lists in response to Discover Modes command need to be listed in an
+ * array in @desc.
+ *
+ * Returns handle to the alternate mode on success or NULL on failure.
+ */
+struct typec_altmode *
+typec_plug_register_altmode(struct typec_plug *plug,
+ struct typec_altmode_desc *desc)
+{
+ return typec_register_altmode(&plug->dev, desc);
+}
+EXPORT_SYMBOL_GPL(typec_plug_register_altmode);
+
+/**
+ * typec_register_plug - Register a USB Type-C Cable Plug
+ * @cable: USB Type-C Cable with the plug
+ * @desc: Description of the cable plug
+ *
+ * Registers a device for USB Type-C Cable Plug described in @desc. A USB Type-C
+ * Cable Plug represents a plug with electronics in it that can response to USB
+ * Power Delivery SOP Prime or SOP Double Prime packages.
+ *
+ * Returns handle to the cable plug on success or NULL on failure.
+ */
+struct typec_plug *typec_register_plug(struct typec_cable *cable,
+ struct typec_plug_desc *desc)
+{
+ struct typec_plug *plug;
+ char name[8];
+ int ret;
+
+ plug = kzalloc(sizeof(*plug), GFP_KERNEL);
+ if (!plug)
+ return NULL;
+
+ sprintf(name, "plug%d", desc->index);
+
+ plug->index = desc->index;
+ plug->dev.class = typec_class;
+ plug->dev.parent = &cable->dev;
+ plug->dev.type = &typec_plug_dev_type;
+ dev_set_name(&plug->dev, "%s-%s", dev_name(cable->dev.parent), name);
+
+ ret = device_register(&plug->dev);
+ if (ret) {
+ dev_err(&cable->dev, "failed to register plug (%d)\n", ret);
+ put_device(&plug->dev);
+ return NULL;
+ }
+
+ return plug;
+}
+EXPORT_SYMBOL_GPL(typec_register_plug);
+
+/**
+ * typec_unregister_plug - Unregister a USB Type-C Cable Plug
+ * @plug: The cable plug to be unregistered
+ *
+ * Unregister device created with typec_register_plug().
+ */
+void typec_unregister_plug(struct typec_plug *plug)
+{
+ if (plug)
+ device_unregister(&plug->dev);
+}
+EXPORT_SYMBOL_GPL(typec_unregister_plug);
+
+/* Type-C Cables */
+
+static ssize_t
+type_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct typec_cable *cable = to_typec_cable(dev);
+
+ return sprintf(buf, "%s\n", cable->active ? "active" : "passive");
+}
+static DEVICE_ATTR_RO(type);
+
+static const char * const typec_plug_types[] = {
+ [USB_PLUG_NONE] = "unknown",
+ [USB_PLUG_TYPE_A] = "type-a",
+ [USB_PLUG_TYPE_B] = "type-b",
+ [USB_PLUG_TYPE_C] = "type-c",
+ [USB_PLUG_CAPTIVE] = "captive",
+};
+
+static ssize_t plug_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct typec_cable *cable = to_typec_cable(dev);
+
+ return sprintf(buf, "%s\n", typec_plug_types[cable->type]);
+}
+static DEVICE_ATTR_RO(plug_type);
+
+static struct attribute *typec_cable_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_plug_type.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(typec_cable);
+
+static void typec_cable_release(struct device *dev)
+{
+ struct typec_cable *cable = to_typec_cable(dev);
+
+ kfree(cable);
+}
+
+static const struct device_type typec_cable_dev_type = {
+ .name = "typec_cable",
+ .groups = typec_cable_groups,
+ .release = typec_cable_release,
+};
+
+/**
+ * typec_cable_set_identity - Report result from Discover Identity command
+ * @cable: The cable updated identity values
+ *
+ * This routine is used to report that the result of Discover Identity USB power
+ * delivery command has become available.
+ */
+int typec_cable_set_identity(struct typec_cable *cable)
+{
+ if (!cable->identity)
+ return -EINVAL;
+
+ typec_report_identity(&cable->dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_cable_set_identity);
+
+/**
+ * typec_register_cable - Register a USB Type-C Cable
+ * @port: The USB Type-C Port the cable is connected to
+ * @desc: Description of the cable
+ *
+ * Registers a device for USB Type-C Cable described in @desc. The cable will be
+ * parent for the optional cable plug devises.
+ *
+ * Returns handle to the cable on success or NULL on failure.
+ */
+struct typec_cable *typec_register_cable(struct typec_port *port,
+ struct typec_cable_desc *desc)
+{
+ struct typec_cable *cable;
+ int ret;
+
+ cable = kzalloc(sizeof(*cable), GFP_KERNEL);
+ if (!cable)
+ return NULL;
+
+ cable->type = desc->type;
+ cable->active = desc->active;
+
+ if (desc->identity) {
+ /*
+ * Creating directory for the identity only if the driver is
+ * able to provide data to it.
+ */
+ cable->dev.groups = usb_pd_id_groups;
+ cable->identity = desc->identity;
+ }
+
+ cable->dev.class = typec_class;
+ cable->dev.parent = &port->dev;
+ cable->dev.type = &typec_cable_dev_type;
+ dev_set_name(&cable->dev, "%s-cable", dev_name(&port->dev));
+
+ ret = device_register(&cable->dev);
+ if (ret) {
+ dev_err(&port->dev, "failed to register cable (%d)\n", ret);
+ put_device(&cable->dev);
+ return NULL;
+ }
+
+ return cable;
+}
+EXPORT_SYMBOL_GPL(typec_register_cable);
+
+/**
+ * typec_unregister_cable - Unregister a USB Type-C Cable
+ * @cable: The cable to be unregistered
+ *
+ * Unregister device created with typec_register_cable().
+ */
+void typec_unregister_cable(struct typec_cable *cable)
+{
+ if (cable)
+ device_unregister(&cable->dev);
+}
+EXPORT_SYMBOL_GPL(typec_unregister_cable);
+
+/* ------------------------------------------------------------------------- */
+/* USB Type-C ports */
+
+static const char * const typec_roles[] = {
+ [TYPEC_SINK] = "sink",
+ [TYPEC_SOURCE] = "source",
+};
+
+static const char * const typec_data_roles[] = {
+ [TYPEC_DEVICE] = "device",
+ [TYPEC_HOST] = "host",
+};
+
+static const char * const typec_port_types[] = {
+ [TYPEC_PORT_DFP] = "source",
+ [TYPEC_PORT_UFP] = "sink",
+ [TYPEC_PORT_DRP] = "dual",
+};
+
+static const char * const typec_port_types_drp[] = {
+ [TYPEC_PORT_DFP] = "dual [source] sink",
+ [TYPEC_PORT_UFP] = "dual source [sink]",
+ [TYPEC_PORT_DRP] = "[dual] source sink",
+};
+
+static ssize_t
+preferred_role_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ int role;
+ int ret;
+
+ if (port->cap->type != TYPEC_PORT_DRP) {
+ dev_dbg(dev, "Preferred role only supported with DRP ports\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!port->cap->try_role) {
+ dev_dbg(dev, "Setting preferred role not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ role = sysfs_match_string(typec_roles, buf);
+ if (role < 0) {
+ if (sysfs_streq(buf, "none"))
+ role = TYPEC_NO_PREFERRED_ROLE;
+ else
+ return -EINVAL;
+ }
+
+ ret = port->cap->try_role(port->cap, role);
+ if (ret)
+ return ret;
+
+ port->prefer_role = role;
+ return size;
+}
+
+static ssize_t
+preferred_role_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ if (port->cap->type != TYPEC_PORT_DRP)
+ return 0;
+
+ if (port->prefer_role < 0)
+ return 0;
+
+ return sprintf(buf, "%s\n", typec_roles[port->prefer_role]);
+}
+static DEVICE_ATTR_RW(preferred_role);
+
+static ssize_t data_role_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ int ret;
+
+ if (!port->cap->dr_set) {
+ dev_dbg(dev, "data role swapping not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = sysfs_match_string(typec_data_roles, buf);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&port->port_type_lock);
+ if (port->port_type != TYPEC_PORT_DRP) {
+ dev_dbg(dev, "port type fixed at \"%s\"",
+ typec_port_types[port->port_type]);
+ ret = -EOPNOTSUPP;
+ goto unlock_and_ret;
+ }
+
+ ret = port->cap->dr_set(port->cap, ret);
+ if (ret)
+ goto unlock_and_ret;
+
+ ret = size;
+unlock_and_ret:
+ mutex_unlock(&port->port_type_lock);
+ return ret;
+}
+
+static ssize_t data_role_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ if (port->cap->type == TYPEC_PORT_DRP)
+ return sprintf(buf, "%s\n", port->data_role == TYPEC_HOST ?
+ "[host] device" : "host [device]");
+
+ return sprintf(buf, "[%s]\n", typec_data_roles[port->data_role]);
+}
+static DEVICE_ATTR_RW(data_role);
+
+static ssize_t power_role_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ int ret;
+
+ if (!port->cap->pd_revision) {
+ dev_dbg(dev, "USB Power Delivery not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!port->cap->pr_set) {
+ dev_dbg(dev, "power role swapping not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (port->pwr_opmode != TYPEC_PWR_MODE_PD) {
+ dev_dbg(dev, "partner unable to swap power role\n");
+ return -EIO;
+ }
+
+ ret = sysfs_match_string(typec_roles, buf);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&port->port_type_lock);
+ if (port->port_type != TYPEC_PORT_DRP) {
+ dev_dbg(dev, "port type fixed at \"%s\"",
+ typec_port_types[port->port_type]);
+ ret = -EOPNOTSUPP;
+ goto unlock_and_ret;
+ }
+
+ ret = port->cap->pr_set(port->cap, ret);
+ if (ret)
+ goto unlock_and_ret;
+
+ ret = size;
+unlock_and_ret:
+ mutex_unlock(&port->port_type_lock);
+ return ret;
+}
+
+static ssize_t power_role_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ if (port->cap->type == TYPEC_PORT_DRP)
+ return sprintf(buf, "%s\n", port->pwr_role == TYPEC_SOURCE ?
+ "[source] sink" : "source [sink]");
+
+ return sprintf(buf, "[%s]\n", typec_roles[port->pwr_role]);
+}
+static DEVICE_ATTR_RW(power_role);
+
+static ssize_t
+port_type_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ int ret;
+ enum typec_port_type type;
+
+ if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) {
+ dev_dbg(dev, "changing port type not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = sysfs_match_string(typec_port_types, buf);
+ if (ret < 0)
+ return ret;
+
+ type = ret;
+ mutex_lock(&port->port_type_lock);
+
+ if (port->port_type == type) {
+ ret = size;
+ goto unlock_and_ret;
+ }
+
+ ret = port->cap->port_type_set(port->cap, type);
+ if (ret)
+ goto unlock_and_ret;
+
+ port->port_type = type;
+ ret = size;
+
+unlock_and_ret:
+ mutex_unlock(&port->port_type_lock);
+ return ret;
+}
+
+static ssize_t
+port_type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ if (port->cap->type == TYPEC_PORT_DRP)
+ return sprintf(buf, "%s\n",
+ typec_port_types_drp[port->port_type]);
+
+ return sprintf(buf, "[%s]\n", typec_port_types[port->cap->type]);
+}
+static DEVICE_ATTR_RW(port_type);
+
+static const char * const typec_pwr_opmodes[] = {
+ [TYPEC_PWR_MODE_USB] = "default",
+ [TYPEC_PWR_MODE_1_5A] = "1.5A",
+ [TYPEC_PWR_MODE_3_0A] = "3.0A",
+ [TYPEC_PWR_MODE_PD] = "usb_power_delivery",
+};
+
+static ssize_t power_operation_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ return sprintf(buf, "%s\n", typec_pwr_opmodes[port->pwr_opmode]);
+}
+static DEVICE_ATTR_RO(power_operation_mode);
+
+static ssize_t vconn_source_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct typec_port *port = to_typec_port(dev);
+ bool source;
+ int ret;
+
+ if (!port->cap->pd_revision) {
+ dev_dbg(dev, "VCONN swap depends on USB Power Delivery\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!port->cap->vconn_set) {
+ dev_dbg(dev, "VCONN swapping not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = kstrtobool(buf, &source);
+ if (ret)
+ return ret;
+
+ ret = port->cap->vconn_set(port->cap, (enum typec_role)source);
+ if (ret)
+ return ret;
+
+ return size;
+}
+
+static ssize_t vconn_source_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ return sprintf(buf, "%s\n",
+ port->vconn_role == TYPEC_SOURCE ? "yes" : "no");
+}
+static DEVICE_ATTR_RW(vconn_source);
+
+static ssize_t supported_accessory_modes_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+ ssize_t ret = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(port->cap->accessory); i++) {
+ if (port->cap->accessory[i])
+ ret += sprintf(buf + ret, "%s ",
+ typec_accessory_modes[port->cap->accessory[i]]);
+ }
+
+ if (!ret)
+ return sprintf(buf, "none\n");
+
+ buf[ret - 1] = '\n';
+
+ return ret;
+}
+static DEVICE_ATTR_RO(supported_accessory_modes);
+
+static ssize_t usb_typec_revision_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *port = to_typec_port(dev);
+ u16 rev = port->cap->revision;
+
+ return sprintf(buf, "%d.%d\n", (rev >> 8) & 0xff, (rev >> 4) & 0xf);
+}
+static DEVICE_ATTR_RO(usb_typec_revision);
+
+static ssize_t usb_power_delivery_revision_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct typec_port *p = to_typec_port(dev);
+
+ return sprintf(buf, "%d\n", (p->cap->pd_revision >> 8) & 0xff);
+}
+static DEVICE_ATTR_RO(usb_power_delivery_revision);
+
+static struct attribute *typec_attrs[] = {
+ &dev_attr_data_role.attr,
+ &dev_attr_power_operation_mode.attr,
+ &dev_attr_power_role.attr,
+ &dev_attr_preferred_role.attr,
+ &dev_attr_supported_accessory_modes.attr,
+ &dev_attr_usb_power_delivery_revision.attr,
+ &dev_attr_usb_typec_revision.attr,
+ &dev_attr_vconn_source.attr,
+ &dev_attr_port_type.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(typec);
+
+static int typec_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ int ret;
+
+ ret = add_uevent_var(env, "TYPEC_PORT=%s", dev_name(dev));
+ if (ret)
+ dev_err(dev, "failed to add uevent TYPEC_PORT\n");
+
+ return ret;
+}
+
+static void typec_release(struct device *dev)
+{
+ struct typec_port *port = to_typec_port(dev);
+
+ ida_simple_remove(&typec_index_ida, port->id);
+ kfree(port);
+}
+
+static const struct device_type typec_port_dev_type = {
+ .name = "typec_port",
+ .groups = typec_groups,
+ .uevent = typec_uevent,
+ .release = typec_release,
+};
+
+/* --------------------------------------- */
+/* Driver callbacks to report role updates */
+
+/**
+ * typec_set_data_role - Report data role change
+ * @port: The USB Type-C Port where the role was changed
+ * @role: The new data role
+ *
+ * This routine is used by the port drivers to report data role changes.
+ */
+void typec_set_data_role(struct typec_port *port, enum typec_data_role role)
+{
+ if (port->data_role == role)
+ return;
+
+ port->data_role = role;
+ sysfs_notify(&port->dev.kobj, NULL, "data_role");
+ kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(typec_set_data_role);
+
+/**
+ * typec_set_pwr_role - Report power role change
+ * @port: The USB Type-C Port where the role was changed
+ * @role: The new data role
+ *
+ * This routine is used by the port drivers to report power role changes.
+ */
+void typec_set_pwr_role(struct typec_port *port, enum typec_role role)
+{
+ if (port->pwr_role == role)
+ return;
+
+ port->pwr_role = role;
+ sysfs_notify(&port->dev.kobj, NULL, "power_role");
+ kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(typec_set_pwr_role);
+
+/**
+ * typec_set_pwr_role - Report VCONN source change
+ * @port: The USB Type-C Port which VCONN role changed
+ * @role: Source when @port is sourcing VCONN, or Sink when it's not
+ *
+ * This routine is used by the port drivers to report if the VCONN source is
+ * changes.
+ */
+void typec_set_vconn_role(struct typec_port *port, enum typec_role role)
+{
+ if (port->vconn_role == role)
+ return;
+
+ port->vconn_role = role;
+ sysfs_notify(&port->dev.kobj, NULL, "vconn_source");
+ kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(typec_set_vconn_role);
+
+/**
+ * typec_set_pwr_opmode - Report changed power operation mode
+ * @port: The USB Type-C Port where the mode was changed
+ * @opmode: New power operation mode
+ *
+ * This routine is used by the port drivers to report changed power operation
+ * mode in @port. The modes are USB (default), 1.5A, 3.0A as defined in USB
+ * Type-C specification, and "USB Power Delivery" when the power levels are
+ * negotiated with methods defined in USB Power Delivery specification.
+ */
+void typec_set_pwr_opmode(struct typec_port *port,
+ enum typec_pwr_opmode opmode)
+{
+ if (port->pwr_opmode == opmode)
+ return;
+
+ port->pwr_opmode = opmode;
+ sysfs_notify(&port->dev.kobj, NULL, "power_operation_mode");
+ kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(typec_set_pwr_opmode);
+
+/* --------------------------------------- */
+
+/**
+ * typec_port_register_altmode - Register USB Type-C Port Alternate Mode
+ * @port: USB Type-C Port that supports the alternate mode
+ * @desc: Description of the alternate mode
+ *
+ * This routine is used to register an alternate mode that @port is capable of
+ * supporting.
+ *
+ * Returns handle to the alternate mode on success or NULL on failure.
+ */
+struct typec_altmode *
+typec_port_register_altmode(struct typec_port *port,
+ struct typec_altmode_desc *desc)
+{
+ return typec_register_altmode(&port->dev, desc);
+}
+EXPORT_SYMBOL_GPL(typec_port_register_altmode);
+
+/**
+ * typec_register_port - Register a USB Type-C Port
+ * @parent: Parent device
+ * @cap: Description of the port
+ *
+ * Registers a device for USB Type-C Port described in @cap.
+ *
+ * Returns handle to the port on success or NULL on failure.
+ */
+struct typec_port *typec_register_port(struct device *parent,
+ const struct typec_capability *cap)
+{
+ struct typec_port *port;
+ int role;
+ int ret;
+ int id;
+
+ port = kzalloc(sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return NULL;
+
+ id = ida_simple_get(&typec_index_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ kfree(port);
+ return NULL;
+ }
+
+ if (cap->type == TYPEC_PORT_DFP)
+ role = TYPEC_SOURCE;
+ else if (cap->type == TYPEC_PORT_UFP)
+ role = TYPEC_SINK;
+ else
+ role = cap->prefer_role;
+
+ if (role == TYPEC_SOURCE) {
+ port->data_role = TYPEC_HOST;
+ port->pwr_role = TYPEC_SOURCE;
+ port->vconn_role = TYPEC_SOURCE;
+ } else {
+ port->data_role = TYPEC_DEVICE;
+ port->pwr_role = TYPEC_SINK;
+ port->vconn_role = TYPEC_SINK;
+ }
+
+ port->id = id;
+ port->cap = cap;
+ port->port_type = cap->type;
+ mutex_init(&port->port_type_lock);
+ port->prefer_role = cap->prefer_role;
+
+ port->dev.class = typec_class;
+ port->dev.parent = parent;
+ port->dev.type = &typec_port_dev_type;
+ dev_set_name(&port->dev, "port%d", id);
+
+ ret = device_register(&port->dev);
+ if (ret) {
+ dev_err(parent, "failed to register port (%d)\n", ret);
+ put_device(&port->dev);
+ return NULL;
+ }
+
+ return port;
+}
+EXPORT_SYMBOL_GPL(typec_register_port);
+
+/**
+ * typec_unregister_port - Unregister a USB Type-C Port
+ * @port: The port to be unregistered
+ *
+ * Unregister device created with typec_register_port().
+ */
+void typec_unregister_port(struct typec_port *port)
+{
+ if (port)
+ device_unregister(&port->dev);
+}
+EXPORT_SYMBOL_GPL(typec_unregister_port);
+
+static int __init typec_init(void)
+{
+ typec_class = class_create(THIS_MODULE, "typec");
+ return PTR_ERR_OR_ZERO(typec_class);
+}
+subsys_initcall(typec_init);
+
+static void __exit typec_exit(void)
+{
+ class_destroy(typec_class);
+ ida_destroy(&typec_index_ida);
+}
+module_exit(typec_exit);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("USB Type-C Connector Class");
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index facaaf0..e40da77 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -741,6 +741,17 @@
if (!(size > 0))
return 0;
+ if (size > urb->transfer_buffer_length) {
+ /* should not happen, probably malicious packet */
+ if (ud->side == USBIP_STUB) {
+ usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
+ return 0;
+ } else {
+ usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
+ return -EPIPE;
+ }
+ }
+
ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size);
if (ret != size) {
dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret);
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c
index 252c7bd..d01496f 100644
--- a/drivers/usb/wusbcore/wa-hc.c
+++ b/drivers/usb/wusbcore/wa-hc.c
@@ -39,6 +39,9 @@
int result;
struct device *dev = &iface->dev;
+ if (iface->cur_altsetting->desc.bNumEndpoints < 3)
+ return -ENODEV;
+
result = wa_rpipes_create(wa);
if (result < 0)
goto error_rpipes_create;
diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c
index 0257f35..e75bbe5 100644
--- a/drivers/uwb/hwa-rc.c
+++ b/drivers/uwb/hwa-rc.c
@@ -825,6 +825,9 @@
struct hwarc *hwarc;
struct device *dev = &iface->dev;
+ if (iface->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
result = -ENOMEM;
uwb_rc = uwb_rc_alloc();
if (uwb_rc == NULL) {
diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c
index 2bfc846..6345e85 100644
--- a/drivers/uwb/i1480/dfu/usb.c
+++ b/drivers/uwb/i1480/dfu/usb.c
@@ -362,6 +362,9 @@
result);
}
+ if (iface->cur_altsetting->desc.bNumEndpoints < 1)
+ return -ENODEV;
+
result = -ENOMEM;
i1480_usb = kzalloc(sizeof(*i1480_usb), GFP_KERNEL);
if (i1480_usb == NULL) {
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index bc7bc4e..9c0b80b 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -489,8 +489,9 @@
} else if (cmd == VFIO_DEVICE_SET_IRQS) {
struct vfio_irq_set hdr;
+ size_t size;
u8 *data = NULL;
- int ret = 0;
+ int max, ret = 0;
minsz = offsetofend(struct vfio_irq_set, count);
@@ -498,23 +499,31 @@
return -EFAULT;
if (hdr.argsz < minsz || hdr.index >= VFIO_PCI_NUM_IRQS ||
+ hdr.count >= (U32_MAX - hdr.start) ||
hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK |
VFIO_IRQ_SET_ACTION_TYPE_MASK))
return -EINVAL;
- if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
- size_t size;
- int max = vfio_pci_get_irq_count(vdev, hdr.index);
+ max = vfio_pci_get_irq_count(vdev, hdr.index);
+ if (hdr.start >= max || hdr.start + hdr.count > max)
+ return -EINVAL;
- if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL)
- size = sizeof(uint8_t);
- else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD)
- size = sizeof(int32_t);
- else
- return -EINVAL;
+ switch (hdr.flags & VFIO_IRQ_SET_DATA_TYPE_MASK) {
+ case VFIO_IRQ_SET_DATA_NONE:
+ size = 0;
+ break;
+ case VFIO_IRQ_SET_DATA_BOOL:
+ size = sizeof(uint8_t);
+ break;
+ case VFIO_IRQ_SET_DATA_EVENTFD:
+ size = sizeof(int32_t);
+ break;
+ default:
+ return -EINVAL;
+ }
- if (hdr.argsz - minsz < hdr.count * size ||
- hdr.start >= max || hdr.start + hdr.count > max)
+ if (size) {
+ if (hdr.argsz - minsz < hdr.count * size)
return -EINVAL;
data = memdup_user((void __user *)(arg + minsz),
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c
index e8d695b..bc57919 100644
--- a/drivers/vfio/pci/vfio_pci_intrs.c
+++ b/drivers/vfio/pci/vfio_pci_intrs.c
@@ -466,7 +466,7 @@
if (!is_irq_none(vdev))
return -EINVAL;
- vdev->ctx = kzalloc(nvec * sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
+ vdev->ctx = kcalloc(nvec, sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
if (!vdev->ctx)
return -ENOMEM;
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index a67a3c4..d35b845 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -2266,7 +2266,6 @@
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
- select INPUT_XEN_KBDDEV_FRONTEND if INPUT_MISC
select XEN_XENBUS_FRONTEND
default y
help
diff --git a/drivers/video/fbdev/da8xx-fb.c b/drivers/video/fbdev/da8xx-fb.c
index 10c876c..4515e4c 100644
--- a/drivers/video/fbdev/da8xx-fb.c
+++ b/drivers/video/fbdev/da8xx-fb.c
@@ -209,8 +209,7 @@
.lower_margin = 2,
.hsync_len = 0,
.vsync_len = 0,
- .sync = FB_SYNC_CLK_INVERT |
- FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .sync = FB_SYNC_CLK_INVERT,
},
/* Sharp LK043T1DG01 */
[1] = {
@@ -224,7 +223,7 @@
.lower_margin = 2,
.hsync_len = 41,
.vsync_len = 10,
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .sync = 0,
.flag = 0,
},
[2] = {
@@ -239,7 +238,7 @@
.lower_margin = 10,
.hsync_len = 10,
.vsync_len = 10,
- .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .sync = 0,
.flag = 0,
},
[3] = {
diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
index 09dc447..4230af1 100644
--- a/drivers/video/fbdev/xen-fbfront.c
+++ b/drivers/video/fbdev/xen-fbfront.c
@@ -644,7 +644,6 @@
break;
case XenbusStateInitWait:
-InitWait:
xenbus_switch_state(dev, XenbusStateConnected);
break;
@@ -655,7 +654,8 @@
* get Connected twice here.
*/
if (dev->state != XenbusStateConnected)
- goto InitWait; /* no InitWait seen yet, fudge it */
+ /* no InitWait seen yet, fudge it */
+ xenbus_switch_state(dev, XenbusStateConnected);
if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
"request-update", "%d", &val) < 0)
diff --git a/drivers/video/msm/mdss/mdss_dba_utils.c b/drivers/video/msm/mdss/mdss_dba_utils.c
index 371ef59..872394e 100644
--- a/drivers/video/msm/mdss/mdss_dba_utils.c
+++ b/drivers/video/msm/mdss/mdss_dba_utils.c
@@ -633,7 +633,6 @@
struct mdss_dba_utils_data *udata = NULL;
struct msm_dba_reg_info info;
struct cec_abstract_init_data cec_abst_init_data;
- void *cec_abst_data;
int ret = 0;
if (!uid) {
@@ -722,7 +721,7 @@
udata->cec_abst_data = cec_abstract_init(&cec_abst_init_data);
if (IS_ERR_OR_NULL(udata->cec_abst_data)) {
pr_err("error initializing cec abstract module\n");
- ret = PTR_ERR(cec_abst_data);
+ ret = PTR_ERR(udata->cec_abst_data);
goto error;
}
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c
index cbf9055..f8eff56 100644
--- a/drivers/video/msm/mdss/mdss_dsi.c
+++ b/drivers/video/msm/mdss/mdss_dsi.c
@@ -1940,7 +1940,7 @@
{
struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
struct mdss_dsi_ctrl_pdata *sctrl_pdata = NULL;
- struct mdss_panel_info *pinfo, *spinfo;
+ struct mdss_panel_info *pinfo, *spinfo = NULL;
int rc = 0;
if (pdata == NULL) {
@@ -1951,7 +1951,7 @@
ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
panel_data);
if (IS_ERR_OR_NULL(ctrl_pdata)) {
- pr_err("Invalid sctrl_pdata = %lu\n", PTR_ERR(ctrl_pdata));
+ pr_err("Invalid ctrl_pdata = %lu\n", PTR_ERR(ctrl_pdata));
return PTR_ERR(ctrl_pdata);
}
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 8e02cbf7..8d44739 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -3264,7 +3264,7 @@
struct mdp_layer_commit_v1 *commit_v1;
struct mdp_output_layer *output_layer;
struct mdss_panel_info *pinfo;
- bool wait_for_finish, wb_change = false;
+ bool wait_for_finish, update = false, wb_change = false;
int ret = -EPERM;
u32 old_xres, old_yres, old_format;
@@ -3316,6 +3316,7 @@
output_layer->buffer.width,
output_layer->buffer.height,
output_layer->buffer.format);
+ update = true;
}
}
ret = mfd->mdp.atomic_validate(mfd, file, commit_v1);
@@ -3356,7 +3357,7 @@
ret = mdss_fb_pan_idle(mfd);
end:
- if (ret && (mfd->panel.type == WRITEBACK_PANEL) && wb_change)
+ if (update && ret && (mfd->panel.type == WRITEBACK_PANEL) && wb_change)
mdss_fb_update_resolution(mfd, old_xres, old_yres, old_format);
return ret;
}
diff --git a/drivers/video/msm/mdss/mdss_hdmi_hdcp.c b/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
index 6256c9a..448909d 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_hdcp.c
@@ -438,6 +438,7 @@
/* Wait for HDCP keys to be checked and validated */
timeout_count = 100;
+ link0_status = DSS_REG_R(io, HDMI_HDCP_LINK0_STATUS);
keys_state = (link0_status >> 28) & 0x7;
while ((keys_state != HDCP_KEYS_STATE_VALID) &&
--timeout_count) {
@@ -1675,4 +1676,3 @@
{
return ((struct hdmi_hdcp_ctrl *)input)->ops;
}
-
diff --git a/drivers/video/msm/mdss/mdss_hdmi_util.c b/drivers/video/msm/mdss/mdss_hdmi_util.c
index 555ba1b..3971bff 100644
--- a/drivers/video/msm/mdss/mdss_hdmi_util.c
+++ b/drivers/video/msm/mdss/mdss_hdmi_util.c
@@ -734,7 +734,7 @@
u32 reg_val, ndx, time_out_count, wait_time;
struct hdmi_tx_ddc_data *ddc_data;
int status;
- int busy_wait_us;
+ int busy_wait_us = 0;
if (!ddc_ctrl || !ddc_ctrl->io) {
pr_err("invalid input\n");
@@ -1212,7 +1212,7 @@
u32 time_out_count;
struct hdmi_tx_ddc_data *ddc_data;
u32 wait_time;
- int busy_wait_us;
+ int busy_wait_us = 0;
if (!ddc_ctrl || !ddc_ctrl->io) {
pr_err("invalid input\n");
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 17f5a65..69e38ab 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -782,7 +782,7 @@
int mdss_mdp_irq_enable(u32 intr_type, u32 intf_num)
{
- int irq_idx, idx;
+ int irq_idx;
unsigned long irq_flags;
int ret = 0;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
@@ -801,7 +801,7 @@
spin_lock_irqsave(&mdp_lock, irq_flags);
if (mdata->mdp_irq_mask[irq.reg_idx] & irq.irq_mask) {
pr_warn("MDSS MDP IRQ-0x%x is already set, mask=%x\n",
- irq.irq_mask, mdata->mdp_irq_mask[idx]);
+ irq.irq_mask, mdata->mdp_irq_mask[irq.reg_idx]);
ret = -EBUSY;
} else {
pr_debug("MDP IRQ mask old=%x new=%x\n",
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index abf0f31..2b8c052 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -918,7 +918,6 @@
{
u32 prefill_us = 0;
u32 prefill_amortized = 0;
- struct mdss_data_type *mdata;
struct mdss_mdp_mixer *mixer;
struct mdss_panel_info *pinfo;
u32 fps, v_total;
@@ -938,7 +937,7 @@
prefill_us = __get_min_prefill_line_time_us(ctl);
/* if pipe is amortizable, add the amortized prefill contribution */
- if (mdss_mdp_is_amortizable_pipe(pipe, mixer, mdata)) {
+ if (mdss_mdp_is_amortizable_pipe(pipe, mixer, ctl->mdata)) {
prefill_amortized = mult_frac(USEC_PER_SEC, pipe->src.y,
fps * v_total);
prefill_us += prefill_amortized;
diff --git a/drivers/video/msm/mdss/mdss_mdp_layer.c b/drivers/video/msm/mdss/mdss_mdp_layer.c
index c26737a..977a148 100644
--- a/drivers/video/msm/mdss/mdss_mdp_layer.c
+++ b/drivers/video/msm/mdss/mdss_mdp_layer.c
@@ -1588,7 +1588,7 @@
static int __validate_layers(struct msm_fb_data_type *mfd,
struct file *file, struct mdp_layer_commit_v1 *commit)
{
- int ret, i;
+ int ret, i = 0;
int rec_ndx[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
int rec_release_ndx[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
int rec_destroy_ndx[MDSS_MDP_PIPE_MAX_RECTS] = { 0 };
@@ -1598,7 +1598,7 @@
u32 mixer_mux, dst_x;
int layer_count = commit->input_layer_cnt;
- struct mdss_mdp_pipe *pipe, *tmp, *left_blend_pipe;
+ struct mdss_mdp_pipe *pipe = NULL, *tmp, *left_blend_pipe;
struct mdss_mdp_pipe *right_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_mdp_pipe *left_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
@@ -2260,4 +2260,3 @@
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
return rc;
}
-
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 7428630..a964274 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -1712,7 +1712,7 @@
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_mdp_ctl *sctl;
- int rc;
+ int rc = 0;
pr_debug("fb%d switch to mode=%x\n", mfd->index, mode);
ATRACE_FUNC();
@@ -1792,6 +1792,10 @@
} else if (mode == MIPI_VIDEO_PANEL) {
if (ctl->ops.wait_pingpong)
rc = ctl->ops.wait_pingpong(ctl, NULL);
+ if (rc) {
+ pr_err("wait for pp failed\n");
+ return rc;
+ }
mdss_mdp_update_panel_info(mfd, 0, 0);
mdss_mdp_switch_to_vid_mode(ctl, 1);
mdss_mdp_ctl_stop(ctl, MDSS_PANEL_POWER_OFF);
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index dc6d8f0..f0b3f55 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -1931,7 +1931,7 @@
if (disp_num < MDSS_BLOCK_DISP_NUM)
flags = mdss_pp_res->pp_disp_flags[disp_num];
else
- flags = 0;
+ return -EINVAL;
if (mixer->num == MDSS_MDP_INTF_LAYERMIXER3)
lm_bitmask = BIT(20);
@@ -2193,7 +2193,7 @@
{
u32 ad_flags, flags, dspp_num, opmode = 0, ad_bypass;
struct mdp_pgc_lut_data *pgc_config;
- struct pp_sts_type *pp_sts;
+ struct pp_sts_type *pp_sts = NULL;
char __iomem *base, *addr = NULL;
int ret = 0;
struct mdss_data_type *mdata;
@@ -2241,7 +2241,7 @@
flags = mdss_pp_res->pp_disp_flags[disp_num];
} else {
- flags = 0;
+ return -EINVAL;
}
mixer_cnt = mdss_mdp_get_ctl_mixers(disp_num, mixer_id);
@@ -2545,7 +2545,7 @@
if (ret)
pr_err("Updated reg_bus_scale failed, ret = %d", ret);
}
- if (IS_PP_RESUME_COMMIT(flags))
+ if (disp_num < MDSS_BLOCK_DISP_NUM && IS_PP_RESUME_COMMIT(flags))
mdss_pp_res->pp_disp_flags[disp_num] &=
~PP_FLAGS_RESUME_COMMIT;
mutex_unlock(&mdss_pp_mutex);
diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c
index 83330e1..ea799fd 100644
--- a/drivers/video/msm/mdss/mdss_mdp_util.c
+++ b/drivers/video/msm/mdss/mdss_mdp_util.c
@@ -438,7 +438,7 @@
uv_bpp_numer = 2;
y_bpp_denom = 1;
uv_bpp_denom = 1;
- } else if (fmt->format == MDP_Y_CBCR_H2V2_TP10_UBWC) {
+ } else { // fmt->format == MDP_Y_CBCR_H2V2_TP10_UBWC
y_stride_alignment = 192;
uv_stride_alignment = 96;
y_height_alignment = 16;
diff --git a/drivers/video/msm/mdss/mdss_rotator.c b/drivers/video/msm/mdss/mdss_rotator.c
index 5910a69..0f2d285 100644
--- a/drivers/video/msm/mdss/mdss_rotator.c
+++ b/drivers/video/msm/mdss/mdss_rotator.c
@@ -697,7 +697,7 @@
struct mdss_rot_hw_resource *hw;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
u32 pipe_ndx, offset = mdss_mdp_get_wb_ctl_support(mdata, true);
- int ret;
+ int ret = 0;
hw = devm_kzalloc(&mgr->pdev->dev, sizeof(struct mdss_rot_hw_resource),
GFP_KERNEL);
@@ -758,8 +758,10 @@
if (ret)
goto error;
- if (pipe_id >= mdata->ndma_pipes)
+ if (pipe_id >= mdata->ndma_pipes) {
+ ret = -EINVAL;
goto error;
+ }
pipe_ndx = mdata->dma_pipes[pipe_id].ndx;
hw->pipe = mdss_mdp_pipe_assign(mdata, hw->mixer,
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 440d78f..771d9e7 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -178,6 +178,8 @@
num = min(num, ARRAY_SIZE(vb->pfns));
mutex_lock(&vb->balloon_lock);
+ /* We can't release more pages than taken */
+ num = min(num, (size_t)vb->num_pages);
for (vb->num_pfns = 0; vb->num_pfns < num;
vb->num_pfns += VIRTIO_BALLOON_PAGES_PER_PAGE) {
page = balloon_page_dequeue(vb_dev_info);
@@ -353,6 +355,8 @@
* Prime this virtqueue with one buffer so the hypervisor can
* use it to signal us later (it can't be broken yet!).
*/
+ update_balloon_stats(vb);
+
sg_init_one(&sg, vb->stats, sizeof vb->stats);
if (virtqueue_add_outbuf(vb->stats_vq, &sg, 1, vb, GFP_KERNEL)
< 0)
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c
index 3838795..4bf7a34 100644
--- a/drivers/xen/events/events_base.c
+++ b/drivers/xen/events/events_base.c
@@ -483,9 +483,20 @@
struct physdev_eoi eoi = { .irq = pirq_from_irq(data->irq) };
int rc = 0;
- irq_move_irq(data);
+ if (!VALID_EVTCHN(evtchn))
+ return;
- if (VALID_EVTCHN(evtchn))
+ if (unlikely(irqd_is_setaffinity_pending(data)) &&
+ likely(!irqd_irq_disabled(data))) {
+ int masked = test_and_set_mask(evtchn);
+
+ clear_evtchn(evtchn);
+
+ irq_move_masked_irq(data);
+
+ if (!masked)
+ unmask_evtchn(evtchn);
+ } else
clear_evtchn(evtchn);
if (pirq_needs_eoi(data->irq)) {
@@ -1360,9 +1371,20 @@
{
int evtchn = evtchn_from_irq(data->irq);
- irq_move_irq(data);
+ if (!VALID_EVTCHN(evtchn))
+ return;
- if (VALID_EVTCHN(evtchn))
+ if (unlikely(irqd_is_setaffinity_pending(data)) &&
+ likely(!irqd_irq_disabled(data))) {
+ int masked = test_and_set_mask(evtchn);
+
+ clear_evtchn(evtchn);
+
+ irq_move_masked_irq(data);
+
+ if (!masked)
+ unmask_evtchn(evtchn);
+ } else
clear_evtchn(evtchn);
}
diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c
index 59fc190f..57dbeef 100644
--- a/drivers/xen/xen-acpi-processor.c
+++ b/drivers/xen/xen-acpi-processor.c
@@ -27,10 +27,10 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/syscore_ops.h>
#include <linux/acpi.h>
#include <acpi/processor.h>
#include <xen/xen.h>
-#include <xen/xen-ops.h>
#include <xen/interface/platform.h>
#include <asm/xen/hypercall.h>
@@ -423,36 +423,7 @@
return 0;
}
-static int __init check_prereq(void)
-{
- struct cpuinfo_x86 *c = &cpu_data(0);
- if (!xen_initial_domain())
- return -ENODEV;
-
- if (!acpi_gbl_FADT.smi_command)
- return -ENODEV;
-
- if (c->x86_vendor == X86_VENDOR_INTEL) {
- if (!cpu_has(c, X86_FEATURE_EST))
- return -ENODEV;
-
- return 0;
- }
- if (c->x86_vendor == X86_VENDOR_AMD) {
- /* Copied from powernow-k8.h, can't include ../cpufreq/powernow
- * as we get compile warnings for the static functions.
- */
-#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
-#define USE_HW_PSTATE 0x00000080
- u32 eax, ebx, ecx, edx;
- cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
- if ((edx & USE_HW_PSTATE) != USE_HW_PSTATE)
- return -ENODEV;
- return 0;
- }
- return -ENODEV;
-}
/* acpi_perf_data is a pointer to percpu data. */
static struct acpi_processor_performance __percpu *acpi_perf_data;
@@ -495,24 +466,42 @@
return rc;
}
-static int xen_acpi_processor_resume(struct notifier_block *nb,
- unsigned long action, void *data)
+static void xen_acpi_processor_resume_worker(struct work_struct *dummy)
{
+ int rc;
+
bitmap_zero(acpi_ids_done, nr_acpi_bits);
- return xen_upload_processor_pm_data();
+
+ rc = xen_upload_processor_pm_data();
+ if (rc != 0)
+ pr_info("ACPI data upload failed, error = %d\n", rc);
}
-struct notifier_block xen_acpi_processor_resume_nb = {
- .notifier_call = xen_acpi_processor_resume,
+static void xen_acpi_processor_resume(void)
+{
+ static DECLARE_WORK(wq, xen_acpi_processor_resume_worker);
+
+ /*
+ * xen_upload_processor_pm_data() calls non-atomic code.
+ * However, the context for xen_acpi_processor_resume is syscore
+ * with only the boot CPU online and in an atomic context.
+ *
+ * So defer the upload for some point safer.
+ */
+ schedule_work(&wq);
+}
+
+static struct syscore_ops xap_syscore_ops = {
+ .resume = xen_acpi_processor_resume,
};
static int __init xen_acpi_processor_init(void)
{
unsigned int i;
- int rc = check_prereq();
+ int rc;
- if (rc)
- return rc;
+ if (!xen_initial_domain())
+ return -ENODEV;
nr_acpi_bits = get_max_acpi_id() + 1;
acpi_ids_done = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL);
@@ -556,7 +545,7 @@
if (rc)
goto err_unregister;
- xen_resume_notifier_register(&xen_acpi_processor_resume_nb);
+ register_syscore_ops(&xap_syscore_ops);
return 0;
err_unregister:
@@ -575,7 +564,7 @@
{
int i;
- xen_resume_notifier_unregister(&xen_acpi_processor_resume_nb);
+ unregister_syscore_ops(&xap_syscore_ops);
kfree(acpi_ids_done);
kfree(acpi_id_present);
kfree(acpi_id_cst_present);
diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c
index 9c23420..47a4177 100644
--- a/drivers/xen/xen-pciback/conf_space.c
+++ b/drivers/xen/xen-pciback/conf_space.c
@@ -183,8 +183,7 @@
field_start = OFFSET(cfg_entry);
field_end = OFFSET(cfg_entry) + field->size;
- if ((req_start >= field_start && req_start < field_end)
- || (req_end > field_start && req_end <= field_end)) {
+ if (req_end > field_start && field_end > req_start) {
err = conf_space_read(dev, cfg_entry, field_start,
&tmp_val);
if (err)
@@ -230,8 +229,7 @@
field_start = OFFSET(cfg_entry);
field_end = OFFSET(cfg_entry) + field->size;
- if ((req_start >= field_start && req_start < field_end)
- || (req_end > field_start && req_end <= field_end)) {
+ if (req_end > field_start && field_end > req_start) {
tmp_val = 0;
err = xen_pcibk_config_read(dev, field_start,
diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c
index 85534ea..0e0eb10 100644
--- a/drivers/xen/xenbus/xenbus_dev_frontend.c
+++ b/drivers/xen/xenbus/xenbus_dev_frontend.c
@@ -316,26 +316,31 @@
rc = -ENOMEM;
goto out;
}
+ } else if (msg_type == XS_TRANSACTION_END) {
+ list_for_each_entry(trans, &u->transactions, list)
+ if (trans->handle.id == u->u.msg.tx_id)
+ break;
+ if (&trans->list == &u->transactions)
+ return -ESRCH;
}
reply = xenbus_dev_request_and_reply(&u->u.msg);
if (IS_ERR(reply)) {
- kfree(trans);
+ if (msg_type == XS_TRANSACTION_START)
+ kfree(trans);
rc = PTR_ERR(reply);
goto out;
}
if (msg_type == XS_TRANSACTION_START) {
- trans->handle.id = simple_strtoul(reply, NULL, 0);
-
- list_add(&trans->list, &u->transactions);
- } else if (msg_type == XS_TRANSACTION_END) {
- list_for_each_entry(trans, &u->transactions, list)
- if (trans->handle.id == u->u.msg.tx_id)
- break;
- BUG_ON(&trans->list == &u->transactions);
+ if (u->u.msg.type == XS_ERROR)
+ kfree(trans);
+ else {
+ trans->handle.id = simple_strtoul(reply, NULL, 0);
+ list_add(&trans->list, &u->transactions);
+ }
+ } else if (u->u.msg.type == XS_TRANSACTION_END) {
list_del(&trans->list);
-
kfree(trans);
}
diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c
index ba804f3..ce65591 100644
--- a/drivers/xen/xenbus/xenbus_xs.c
+++ b/drivers/xen/xenbus/xenbus_xs.c
@@ -250,9 +250,6 @@
mutex_unlock(&xs_state.request_mutex);
- if (IS_ERR(ret))
- return ret;
-
if ((msg->type == XS_TRANSACTION_END) ||
((req_msg.type == XS_TRANSACTION_START) &&
(msg->type == XS_ERROR)))
diff --git a/fs/Makefile b/fs/Makefile
index d2698be..340227e 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -119,6 +119,7 @@
obj-$(CONFIG_HPPFS) += hppfs/
obj-$(CONFIG_CACHEFILES) += cachefiles/
obj-$(CONFIG_DEBUG_FS) += debugfs/
+obj-$(CONFIG_TRACING) += tracefs/
obj-$(CONFIG_OCFS2_FS) += ocfs2/
obj-$(CONFIG_BTRFS_FS) += btrfs/
obj-$(CONFIG_GFS2_FS) += gfs2/
diff --git a/fs/attr.c b/fs/attr.c
index 6530ced..3492683 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -187,7 +187,7 @@
* the file open for write, as there can be no conflicting delegation in
* that case.
*/
-int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
+int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
{
struct inode *inode = dentry->d_inode;
umode_t mode = inode->i_mode;
@@ -262,7 +262,9 @@
if (error)
return error;
- if (inode->i_op->setattr)
+ if (mnt && inode->i_op->setattr2)
+ error = inode->i_op->setattr2(mnt, dentry, attr);
+ else if (inode->i_op->setattr)
error = inode->i_op->setattr(dentry, attr);
else
error = simple_setattr(dentry, attr);
@@ -275,4 +277,10 @@
return error;
}
+EXPORT_SYMBOL(notify_change2);
+
+int notify_change(struct dentry * dentry, struct iattr * attr, struct inode **delegated_inode)
+{
+ return notify_change2(NULL, dentry, attr, delegated_inode);
+}
EXPORT_SYMBOL(notify_change);
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index 8e98cf9..cecd252 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -79,9 +79,13 @@
};
#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */
-#define AUTOFS_INF_NO_RCU (1<<1) /* the dentry is being considered
+#define AUTOFS_INF_WANT_EXPIRE (1<<1) /* the dentry is being considered
* for expiry, so RCU_walk is
- * not permitted
+ * not permitted. If it progresses to
+ * actual expiry attempt, the flag is
+ * not cleared when EXPIRING is set -
+ * in that case it gets cleared only
+ * when it comes to clearing EXPIRING.
*/
#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index dcdec6f..513b8e5 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -321,19 +321,17 @@
if (ino->flags & AUTOFS_INF_PENDING)
goto out;
if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
- ino->flags |= AUTOFS_INF_NO_RCU;
+ ino->flags |= AUTOFS_INF_WANT_EXPIRE;
spin_unlock(&sbi->fs_lock);
synchronize_rcu();
spin_lock(&sbi->fs_lock);
if (!autofs4_direct_busy(mnt, root, timeout, do_now)) {
ino->flags |= AUTOFS_INF_EXPIRING;
- smp_mb();
- ino->flags &= ~AUTOFS_INF_NO_RCU;
init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
return root;
}
- ino->flags &= ~AUTOFS_INF_NO_RCU;
+ ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
}
out:
spin_unlock(&sbi->fs_lock);
@@ -425,6 +423,7 @@
}
return NULL;
}
+
/*
* Find an eligible tree to time-out
* A tree is eligible if :-
@@ -440,6 +439,7 @@
struct dentry *root = sb->s_root;
struct dentry *dentry;
struct dentry *expired;
+ struct dentry *found;
struct autofs_info *ino;
if (!root)
@@ -450,31 +450,46 @@
dentry = NULL;
while ((dentry = get_next_positive_subdir(dentry, root))) {
+ int flags = how;
+
spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(dentry);
- if (ino->flags & AUTOFS_INF_NO_RCU)
- expired = NULL;
- else
- expired = should_expire(dentry, mnt, timeout, how);
- if (!expired) {
+ if (ino->flags & AUTOFS_INF_WANT_EXPIRE) {
spin_unlock(&sbi->fs_lock);
continue;
}
+ spin_unlock(&sbi->fs_lock);
+
+ expired = should_expire(dentry, mnt, timeout, flags);
+ if (!expired)
+ continue;
+
+ spin_lock(&sbi->fs_lock);
ino = autofs4_dentry_ino(expired);
- ino->flags |= AUTOFS_INF_NO_RCU;
+ ino->flags |= AUTOFS_INF_WANT_EXPIRE;
spin_unlock(&sbi->fs_lock);
synchronize_rcu();
- spin_lock(&sbi->fs_lock);
- if (should_expire(expired, mnt, timeout, how)) {
- if (expired != dentry)
- dput(dentry);
- goto found;
- }
- ino->flags &= ~AUTOFS_INF_NO_RCU;
+ /* Make sure a reference is not taken on found if
+ * things have changed.
+ */
+ flags &= ~AUTOFS_EXP_LEAVES;
+ found = should_expire(expired, mnt, timeout, how);
+ if (!found || found != expired)
+ /* Something has changed, continue */
+ goto next;
+
+ if (expired != dentry)
+ dput(dentry);
+
+ spin_lock(&sbi->fs_lock);
+ goto found;
+next:
+ spin_lock(&sbi->fs_lock);
+ ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
+ spin_unlock(&sbi->fs_lock);
if (expired != dentry)
dput(expired);
- spin_unlock(&sbi->fs_lock);
}
return NULL;
@@ -482,17 +497,8 @@
DPRINTK("returning %p %.*s",
expired, (int)expired->d_name.len, expired->d_name.name);
ino->flags |= AUTOFS_INF_EXPIRING;
- smp_mb();
- ino->flags &= ~AUTOFS_INF_NO_RCU;
init_completion(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
- spin_lock(&sbi->lookup_lock);
- spin_lock(&expired->d_parent->d_lock);
- spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED);
- list_move(&expired->d_parent->d_subdirs, &expired->d_child);
- spin_unlock(&expired->d_lock);
- spin_unlock(&expired->d_parent->d_lock);
- spin_unlock(&sbi->lookup_lock);
return expired;
}
@@ -501,15 +507,27 @@
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
int status;
+ int state;
/* Block on any pending expire */
- if (!(ino->flags & (AUTOFS_INF_EXPIRING | AUTOFS_INF_NO_RCU)))
+ if (!(ino->flags & AUTOFS_INF_WANT_EXPIRE))
return 0;
if (rcu_walk)
return -ECHILD;
+retry:
spin_lock(&sbi->fs_lock);
- if (ino->flags & AUTOFS_INF_EXPIRING) {
+ state = ino->flags & (AUTOFS_INF_WANT_EXPIRE | AUTOFS_INF_EXPIRING);
+ if (state == AUTOFS_INF_WANT_EXPIRE) {
+ spin_unlock(&sbi->fs_lock);
+ /*
+ * Possibly being selected for expire, wait until
+ * it's selected or not.
+ */
+ schedule_timeout_uninterruptible(HZ/10);
+ goto retry;
+ }
+ if (state & AUTOFS_INF_EXPIRING) {
spin_unlock(&sbi->fs_lock);
DPRINTK("waiting for expire %p name=%.*s",
@@ -561,7 +579,7 @@
ino = autofs4_dentry_ino(dentry);
/* avoid rapid-fire expire attempts if expiry fails */
ino->last_used = now;
- ino->flags &= ~AUTOFS_INF_EXPIRING;
+ ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
complete_all(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
@@ -589,7 +607,7 @@
spin_lock(&sbi->fs_lock);
/* avoid rapid-fire expire attempts if expiry fails */
ino->last_used = now;
- ino->flags &= ~AUTOFS_INF_EXPIRING;
+ ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
complete_all(&ino->expire_complete);
spin_unlock(&sbi->fs_lock);
dput(dentry);
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 0822c9e..21b5bb1 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -459,7 +459,7 @@
* a mount-trap.
*/
struct inode *inode;
- if (ino->flags & (AUTOFS_INF_EXPIRING | AUTOFS_INF_NO_RCU))
+ if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
return 0;
if (d_mountpoint(dentry))
return 0;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 1cf9619..4641b19 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -570,6 +570,98 @@
#endif
}
+#ifdef CONFIG_KUSER_HELPERS_SELECTIVE_DISABLE
+#define ANDROID_NOTE_OWNER"Android"
+#define ANDROID_KUSER_HELPER_TYPE 0x3L
+#define ANDROID_KUSER_HELPER_ON 0x1L
+
+static int should_call_arch_setup_additional_pages(struct linux_binprm *bprm,
+ struct elfhdr *elf_ex,
+ struct elf_phdr *elf_ppnt)
+{
+ Elf64_Half i;
+
+ /* We want to allow vdso, but not kuser_helpers */
+ if (elf_ex->e_ident[EI_CLASS] == ELFCLASS64)
+ return true;
+
+ for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) {
+ int retval;
+ void *elf_pnotes;
+ struct elf32_note *next_elf_notep;
+ Elf64_Xword left_to_read;
+
+ if (elf_ppnt->p_type != PT_NOTE)
+ continue;
+
+ /*
+ * This code is seeing if we have a special note.
+ * The note tells us that this binary still needs
+ * arch_setup_additional_pages to be called.
+ */
+ if (elf_ppnt->p_filesz < sizeof(struct elf32_note))
+ break;
+
+ elf_pnotes = kmalloc(elf_ppnt->p_filesz, GFP_KERNEL);
+ if (!elf_pnotes)
+ return -ENOMEM;
+
+ retval = kernel_read(bprm->file, elf_ppnt->p_offset,
+ (char *)elf_pnotes, elf_ppnt->p_filesz);
+ if (retval < 0) {
+ kfree(elf_pnotes);
+ return retval;
+ }
+
+ if((Elf64_Xword) retval != elf_ppnt->p_filesz) {
+ kfree(elf_pnotes);
+ return -EIO;
+ }
+
+ /*
+ * Now that we have read in all the notes and find ours
+ */
+ next_elf_notep = (struct elf32_note *)elf_pnotes;
+ left_to_read = elf_ppnt->p_filesz;
+ while (left_to_read >= sizeof(struct elf32_note)) {
+ char *note_namep;
+
+ left_to_read -= sizeof(struct elf32_note);
+
+ /* Sanity check on the name and desc length*/
+ if (((Elf64_Xword) next_elf_notep->n_namesz +
+ (Elf64_Xword) next_elf_notep->n_descsz) >
+ left_to_read)
+ break;
+
+ note_namep = (char *)next_elf_notep +
+ sizeof(struct elf32_note);
+ left_to_read -= next_elf_notep->n_namesz;
+ left_to_read -= next_elf_notep->n_descsz;
+
+ if ((sizeof(ANDROID_NOTE_OWNER) ==
+ next_elf_notep->n_namesz) &&
+ (next_elf_notep->n_type ==
+ ANDROID_KUSER_HELPER_TYPE) &&
+ strncmp(note_namep, ANDROID_NOTE_OWNER,
+ next_elf_notep->n_namesz) == 0) {
+ kfree(elf_pnotes);
+ return true;
+ }
+
+ next_elf_notep = (struct elf32_note *)
+ (note_namep +
+ next_elf_notep->n_namesz +
+ next_elf_notep->n_descsz);
+ }
+
+ kfree(elf_pnotes);
+ }
+
+ return false;
+}
+#endif
+
static int load_elf_binary(struct linux_binprm *bprm)
{
struct file *interpreter = NULL; /* to shut gcc up */
@@ -915,16 +1007,27 @@
}
}
- kfree(elf_phdata);
-
set_binfmt(&elf_format);
#ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
- retval = arch_setup_additional_pages(bprm, !!elf_interpreter);
+#ifdef CONFIG_KUSER_HELPERS_SELECTIVE_DISABLE
+ retval = should_call_arch_setup_additional_pages(bprm, &loc->elf_ex,
+ elf_phdata);
if (retval < 0)
goto out;
+
+ if (retval) {
+#endif
+ retval = arch_setup_additional_pages(bprm, !!elf_interpreter);
+ if (retval < 0)
+ goto out;
+#ifdef CONFIG_KUSER_HELPERS_SELECTIVE_DISABLE
+ }
+#endif
#endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
+ kfree(elf_phdata);
+
install_exec_creds(bprm);
retval = create_elf_tables(bprm, &loc->elf_ex,
load_addr, interp_load_addr);
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 1d9c9f3..543dcf9 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -694,7 +694,7 @@
return true; /* already a holder */
else if (bdev->bd_holder != NULL)
return false; /* held by someone else */
- else if (bdev->bd_contains == bdev)
+ else if (whole == bdev)
return true; /* is a whole device which isn't held */
else if (whole->bd_holder == bd_may_claim)
@@ -1727,6 +1727,7 @@
spin_lock(&inode_sb_list_lock);
list_for_each_entry(inode, &blockdev_superblock->s_inodes, i_sb_list) {
struct address_space *mapping = inode->i_mapping;
+ struct block_device *bdev;
spin_lock(&inode->i_lock);
if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW) ||
@@ -1747,8 +1748,12 @@
*/
iput(old_inode);
old_inode = inode;
+ bdev = I_BDEV(inode);
- func(I_BDEV(inode), arg);
+ mutex_lock(&bdev->bd_mutex);
+ if (bdev->bd_openers)
+ func(bdev, arg);
+ mutex_unlock(&bdev->bd_mutex);
spin_lock(&inode_sb_list_lock);
}
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index f54511d..39c68ef 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -1542,6 +1542,7 @@
trans->transid, root->fs_info->generation);
if (!should_cow_block(trans, root, buf)) {
+ trans->dirty = true;
*cow_ret = buf;
return 0;
}
@@ -2762,8 +2763,10 @@
* then we don't want to set the path blocking,
* so we test it here
*/
- if (!should_cow_block(trans, root, b))
+ if (!should_cow_block(trans, root, b)) {
+ trans->dirty = true;
goto cow_done;
+ }
/*
* must have write locks on this node and the
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index ba5aec7..42d11e7 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3866,6 +3866,7 @@
/* ioctl.c */
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+long btrfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
void btrfs_update_iflags(struct inode *inode);
void btrfs_inherit_iflags(struct inode *inode, struct inode *dir);
int btrfs_is_empty_uuid(u8 *uuid);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 950479f..a067065 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -7237,7 +7237,7 @@
set_extent_dirty(&trans->transaction->dirty_pages, buf->start,
buf->start + buf->len - 1, GFP_NOFS);
}
- trans->blocks_used++;
+ trans->dirty = true;
/* this returns a buffer locked for blocking */
return buf;
}
@@ -9196,9 +9196,8 @@
int ret = 0;
list_for_each_entry_safe(block_group, tmp, &trans->new_bgs, bg_list) {
- list_del_init(&block_group->bg_list);
if (ret)
- continue;
+ goto next;
spin_lock(&block_group->lock);
memcpy(&item, &block_group->item, sizeof(item));
@@ -9213,6 +9212,8 @@
key.objectid, key.offset);
if (ret)
btrfs_abort_transaction(trans, extent_root, ret);
+next:
+ list_del_init(&block_group->bg_list);
}
}
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index cb239dd..b1131c3 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -2163,7 +2163,7 @@
next = next_state(state);
- failrec = (struct io_failure_record *)state->private;
+ failrec = (struct io_failure_record *)(unsigned long)state->private;
free_extent_state(state);
kfree(failrec);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index e557e4c..2ad4cb3 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -2797,7 +2797,7 @@
.fallocate = btrfs_fallocate,
.unlocked_ioctl = btrfs_ioctl,
#ifdef CONFIG_COMPAT
- .compat_ioctl = btrfs_ioctl,
+ .compat_ioctl = btrfs_compat_ioctl,
#endif
};
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index c8d287f..ef677cdf 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -9513,7 +9513,7 @@
.iterate = btrfs_real_readdir,
.unlocked_ioctl = btrfs_ioctl,
#ifdef CONFIG_COMPAT
- .compat_ioctl = btrfs_ioctl,
+ .compat_ioctl = btrfs_compat_ioctl,
#endif
.release = btrfs_release_file,
.fsync = btrfs_sync_file,
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 31c9f64..dd8526a 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -1627,6 +1627,9 @@
int namelen;
int ret = 0;
+ if (!S_ISDIR(file_inode(file)->i_mode))
+ return -ENOTDIR;
+
ret = mnt_want_write_file(file);
if (ret)
goto out;
@@ -1656,7 +1659,7 @@
src_inode = file_inode(src.file);
if (src_inode->i_sb != file_inode(file)->i_sb) {
- btrfs_info(BTRFS_I(src_inode)->root->fs_info,
+ btrfs_info(BTRFS_I(file_inode(file))->root->fs_info,
"Snapshot src from another FS");
ret = -EXDEV;
} else if (!inode_owner_or_capable(src_inode)) {
@@ -1684,6 +1687,9 @@
struct btrfs_ioctl_vol_args *vol_args;
int ret;
+ if (!S_ISDIR(file_inode(file)->i_mode))
+ return -ENOTDIR;
+
vol_args = memdup_user(arg, sizeof(*vol_args));
if (IS_ERR(vol_args))
return PTR_ERR(vol_args);
@@ -1707,6 +1713,9 @@
bool readonly = false;
struct btrfs_qgroup_inherit *inherit = NULL;
+ if (!S_ISDIR(file_inode(file)->i_mode))
+ return -ENOTDIR;
+
vol_args = memdup_user(arg, sizeof(*vol_args));
if (IS_ERR(vol_args))
return PTR_ERR(vol_args);
@@ -2335,6 +2344,9 @@
int ret;
int err = 0;
+ if (!S_ISDIR(dir->i_mode))
+ return -ENOTDIR;
+
vol_args = memdup_user(arg, sizeof(*vol_args));
if (IS_ERR(vol_args))
return PTR_ERR(vol_args);
@@ -5495,3 +5507,24 @@
return -ENOTTY;
}
+
+#ifdef CONFIG_COMPAT
+long btrfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case FS_IOC32_GETFLAGS:
+ cmd = FS_IOC_GETFLAGS;
+ break;
+ case FS_IOC32_SETFLAGS:
+ cmd = FS_IOC_SETFLAGS;
+ break;
+ case FS_IOC32_GETVERSION:
+ cmd = FS_IOC_GETVERSION;
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return btrfs_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
+}
+#endif
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 7ceaaf2..cf6d11b 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -262,7 +262,7 @@
trans->aborted = errno;
/* Nothing used. The other threads that have joined this
* transaction may be able to continue. */
- if (!trans->blocks_used) {
+ if (!trans->dirty && list_empty(&trans->new_bgs)) {
const char *errstr;
errstr = btrfs_decode_error(errno);
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 7dce00b..30dbf31 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -472,7 +472,6 @@
h->transid = cur_trans->transid;
h->transaction = cur_trans;
- h->blocks_used = 0;
h->bytes_reserved = 0;
h->root = root;
h->delayed_ref_updates = 0;
@@ -723,7 +722,7 @@
if (!list_empty(&trans->ordered)) {
spin_lock(&info->trans_lock);
- list_splice(&trans->ordered, &cur_trans->pending_ordered);
+ list_splice_init(&trans->ordered, &cur_trans->pending_ordered);
spin_unlock(&info->trans_lock);
}
@@ -1732,7 +1731,7 @@
}
spin_lock(&root->fs_info->trans_lock);
- list_splice(&trans->ordered, &cur_trans->pending_ordered);
+ list_splice_init(&trans->ordered, &cur_trans->pending_ordered);
if (cur_trans->state >= TRANS_STATE_COMMIT_START) {
spin_unlock(&root->fs_info->trans_lock);
atomic_inc(&cur_trans->use_count);
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 1ba9c3e..1cf5de3 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -88,7 +88,6 @@
u64 qgroup_reserved;
unsigned long use_count;
unsigned long blocks_reserved;
- unsigned long blocks_used;
unsigned long delayed_ref_updates;
struct btrfs_transaction *transaction;
struct btrfs_block_rsv *block_rsv;
@@ -98,6 +97,7 @@
bool allocating_chunk;
bool reloc_reserved;
bool sync;
+ bool dirty;
unsigned int type;
/*
* this root is only needed to validate that the root passed to
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index 3b68c75..001b338 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -1782,12 +1782,11 @@
next:
/* check the next slot in the tree to see if it is a valid item */
nritems = btrfs_header_nritems(path->nodes[0]);
+ path->slots[0]++;
if (path->slots[0] >= nritems) {
ret = btrfs_next_leaf(root, path);
if (ret)
goto out;
- } else {
- path->slots[0]++;
}
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
@@ -2453,14 +2452,12 @@
int index, int error)
{
struct btrfs_log_ctx *ctx;
+ struct btrfs_log_ctx *safe;
- if (!error) {
- INIT_LIST_HEAD(&root->log_ctxs[index]);
- return;
- }
-
- list_for_each_entry(ctx, &root->log_ctxs[index], list)
+ list_for_each_entry_safe(ctx, safe, &root->log_ctxs[index], list) {
+ list_del_init(&ctx->list);
ctx->log_ret = error;
+ }
INIT_LIST_HEAD(&root->log_ctxs[index]);
}
@@ -2604,6 +2601,8 @@
}
if (log_root_tree->log_transid_committed >= root_log_ctx.log_transid) {
+ blk_finish_plug(&plug);
+ list_del_init(&root_log_ctx.list);
mutex_unlock(&log_root_tree->log_mutex);
ret = root_log_ctx.log_ret;
goto out;
@@ -2688,13 +2687,9 @@
mutex_unlock(&root->log_mutex);
out_wake_log_root:
- /*
- * We needn't get log_mutex here because we are sure all
- * the other tasks are blocked.
- */
+ mutex_lock(&log_root_tree->log_mutex);
btrfs_remove_all_log_ctxs(log_root_tree, index2, ret);
- mutex_lock(&log_root_tree->log_mutex);
log_root_tree->log_transid_committed++;
atomic_set(&log_root_tree->log_commit[index2], 0);
mutex_unlock(&log_root_tree->log_mutex);
@@ -2702,10 +2697,8 @@
if (waitqueue_active(&log_root_tree->log_commit_wait[index2]))
wake_up(&log_root_tree->log_commit_wait[index2]);
out:
- /* See above. */
- btrfs_remove_all_log_ctxs(root, index1, ret);
-
mutex_lock(&root->log_mutex);
+ btrfs_remove_all_log_ctxs(root, index1, ret);
root->log_transid_committed++;
atomic_set(&root->log_commit[index1], 0);
mutex_unlock(&root->log_mutex);
@@ -3929,6 +3922,308 @@
return 0;
}
+/*
+ * At the moment we always log all xattrs. This is to figure out at log replay
+ * time which xattrs must have their deletion replayed. If a xattr is missing
+ * in the log tree and exists in the fs/subvol tree, we delete it. This is
+ * because if a xattr is deleted, the inode is fsynced and a power failure
+ * happens, causing the log to be replayed the next time the fs is mounted,
+ * we want the xattr to not exist anymore (same behaviour as other filesystems
+ * with a journal, ext3/4, xfs, f2fs, etc).
+ */
+static int btrfs_log_all_xattrs(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct inode *inode,
+ struct btrfs_path *path,
+ struct btrfs_path *dst_path)
+{
+ int ret;
+ struct btrfs_key key;
+ const u64 ino = btrfs_ino(inode);
+ int ins_nr = 0;
+ int start_slot = 0;
+
+ key.objectid = ino;
+ key.type = BTRFS_XATTR_ITEM_KEY;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ while (true) {
+ int slot = path->slots[0];
+ struct extent_buffer *leaf = path->nodes[0];
+ int nritems = btrfs_header_nritems(leaf);
+
+ if (slot >= nritems) {
+ if (ins_nr > 0) {
+ u64 last_extent = 0;
+
+ ret = copy_items(trans, inode, dst_path, path,
+ &last_extent, start_slot,
+ ins_nr, 1, 0);
+ /* can't be 1, extent items aren't processed */
+ ASSERT(ret <= 0);
+ if (ret < 0)
+ return ret;
+ ins_nr = 0;
+ }
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0)
+ return ret;
+ else if (ret > 0)
+ break;
+ continue;
+ }
+
+ btrfs_item_key_to_cpu(leaf, &key, slot);
+ if (key.objectid != ino || key.type != BTRFS_XATTR_ITEM_KEY)
+ break;
+
+ if (ins_nr == 0)
+ start_slot = slot;
+ ins_nr++;
+ path->slots[0]++;
+ cond_resched();
+ }
+ if (ins_nr > 0) {
+ u64 last_extent = 0;
+
+ ret = copy_items(trans, inode, dst_path, path,
+ &last_extent, start_slot,
+ ins_nr, 1, 0);
+ /* can't be 1, extent items aren't processed */
+ ASSERT(ret <= 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * If the no holes feature is enabled we need to make sure any hole between the
+ * last extent and the i_size of our inode is explicitly marked in the log. This
+ * is to make sure that doing something like:
+ *
+ * 1) create file with 128Kb of data
+ * 2) truncate file to 64Kb
+ * 3) truncate file to 256Kb
+ * 4) fsync file
+ * 5) <crash/power failure>
+ * 6) mount fs and trigger log replay
+ *
+ * Will give us a file with a size of 256Kb, the first 64Kb of data match what
+ * the file had in its first 64Kb of data at step 1 and the last 192Kb of the
+ * file correspond to a hole. The presence of explicit holes in a log tree is
+ * what guarantees that log replay will remove/adjust file extent items in the
+ * fs/subvol tree.
+ *
+ * Here we do not need to care about holes between extents, that is already done
+ * by copy_items(). We also only need to do this in the full sync path, where we
+ * lookup for extents from the fs/subvol tree only. In the fast path case, we
+ * lookup the list of modified extent maps and if any represents a hole, we
+ * insert a corresponding extent representing a hole in the log tree.
+ */
+static int btrfs_log_trailing_hole(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct inode *inode,
+ struct btrfs_path *path)
+{
+ int ret;
+ struct btrfs_key key;
+ u64 hole_start;
+ u64 hole_size;
+ struct extent_buffer *leaf;
+ struct btrfs_root *log = root->log_root;
+ const u64 ino = btrfs_ino(inode);
+ const u64 i_size = i_size_read(inode);
+
+ if (!btrfs_fs_incompat(root->fs_info, NO_HOLES))
+ return 0;
+
+ key.objectid = ino;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = (u64)-1;
+
+ ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
+ ASSERT(ret != 0);
+ if (ret < 0)
+ return ret;
+
+ ASSERT(path->slots[0] > 0);
+ path->slots[0]--;
+ leaf = path->nodes[0];
+ btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
+
+ if (key.objectid != ino || key.type != BTRFS_EXTENT_DATA_KEY) {
+ /* inode does not have any extents */
+ hole_start = 0;
+ hole_size = i_size;
+ } else {
+ struct btrfs_file_extent_item *extent;
+ u64 len;
+
+ /*
+ * If there's an extent beyond i_size, an explicit hole was
+ * already inserted by copy_items().
+ */
+ if (key.offset >= i_size)
+ return 0;
+
+ extent = btrfs_item_ptr(leaf, path->slots[0],
+ struct btrfs_file_extent_item);
+
+ if (btrfs_file_extent_type(leaf, extent) ==
+ BTRFS_FILE_EXTENT_INLINE) {
+ len = btrfs_file_extent_inline_len(leaf,
+ path->slots[0],
+ extent);
+ ASSERT(len == i_size);
+ return 0;
+ }
+
+ len = btrfs_file_extent_num_bytes(leaf, extent);
+ /* Last extent goes beyond i_size, no need to log a hole. */
+ if (key.offset + len > i_size)
+ return 0;
+ hole_start = key.offset + len;
+ hole_size = i_size - hole_start;
+ }
+ btrfs_release_path(path);
+
+ /* Last extent ends at i_size. */
+ if (hole_size == 0)
+ return 0;
+
+ hole_size = ALIGN(hole_size, root->sectorsize);
+ ret = btrfs_insert_file_extent(trans, log, ino, hole_start, 0, 0,
+ hole_size, 0, hole_size, 0, 0, 0);
+ return ret;
+}
+
+/*
+ * When we are logging a new inode X, check if it doesn't have a reference that
+ * matches the reference from some other inode Y created in a past transaction
+ * and that was renamed in the current transaction. If we don't do this, then at
+ * log replay time we can lose inode Y (and all its files if it's a directory):
+ *
+ * mkdir /mnt/x
+ * echo "hello world" > /mnt/x/foobar
+ * sync
+ * mv /mnt/x /mnt/y
+ * mkdir /mnt/x # or touch /mnt/x
+ * xfs_io -c fsync /mnt/x
+ * <power fail>
+ * mount fs, trigger log replay
+ *
+ * After the log replay procedure, we would lose the first directory and all its
+ * files (file foobar).
+ * For the case where inode Y is not a directory we simply end up losing it:
+ *
+ * echo "123" > /mnt/foo
+ * sync
+ * mv /mnt/foo /mnt/bar
+ * echo "abc" > /mnt/foo
+ * xfs_io -c fsync /mnt/foo
+ * <power fail>
+ *
+ * We also need this for cases where a snapshot entry is replaced by some other
+ * entry (file or directory) otherwise we end up with an unreplayable log due to
+ * attempts to delete the snapshot entry (entry of type BTRFS_ROOT_ITEM_KEY) as
+ * if it were a regular entry:
+ *
+ * mkdir /mnt/x
+ * btrfs subvolume snapshot /mnt /mnt/x/snap
+ * btrfs subvolume delete /mnt/x/snap
+ * rmdir /mnt/x
+ * mkdir /mnt/x
+ * fsync /mnt/x or fsync some new file inside it
+ * <power fail>
+ *
+ * The snapshot delete, rmdir of x, mkdir of a new x and the fsync all happen in
+ * the same transaction.
+ */
+static int btrfs_check_ref_name_override(struct extent_buffer *eb,
+ const int slot,
+ const struct btrfs_key *key,
+ struct inode *inode)
+{
+ int ret;
+ struct btrfs_path *search_path;
+ char *name = NULL;
+ u32 name_len = 0;
+ u32 item_size = btrfs_item_size_nr(eb, slot);
+ u32 cur_offset = 0;
+ unsigned long ptr = btrfs_item_ptr_offset(eb, slot);
+
+ search_path = btrfs_alloc_path();
+ if (!search_path)
+ return -ENOMEM;
+ search_path->search_commit_root = 1;
+ search_path->skip_locking = 1;
+
+ while (cur_offset < item_size) {
+ u64 parent;
+ u32 this_name_len;
+ u32 this_len;
+ unsigned long name_ptr;
+ struct btrfs_dir_item *di;
+
+ if (key->type == BTRFS_INODE_REF_KEY) {
+ struct btrfs_inode_ref *iref;
+
+ iref = (struct btrfs_inode_ref *)(ptr + cur_offset);
+ parent = key->offset;
+ this_name_len = btrfs_inode_ref_name_len(eb, iref);
+ name_ptr = (unsigned long)(iref + 1);
+ this_len = sizeof(*iref) + this_name_len;
+ } else {
+ struct btrfs_inode_extref *extref;
+
+ extref = (struct btrfs_inode_extref *)(ptr +
+ cur_offset);
+ parent = btrfs_inode_extref_parent(eb, extref);
+ this_name_len = btrfs_inode_extref_name_len(eb, extref);
+ name_ptr = (unsigned long)&extref->name;
+ this_len = sizeof(*extref) + this_name_len;
+ }
+
+ if (this_name_len > name_len) {
+ char *new_name;
+
+ new_name = krealloc(name, this_name_len, GFP_NOFS);
+ if (!new_name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ name_len = this_name_len;
+ name = new_name;
+ }
+
+ read_extent_buffer(eb, name, name_ptr, this_name_len);
+ di = btrfs_lookup_dir_item(NULL, BTRFS_I(inode)->root,
+ search_path, parent,
+ name, this_name_len, 0);
+ if (di && !IS_ERR(di)) {
+ ret = 1;
+ goto out;
+ } else if (IS_ERR(di)) {
+ ret = PTR_ERR(di);
+ goto out;
+ }
+ btrfs_release_path(search_path);
+
+ cur_offset += this_len;
+ }
+ ret = 0;
+out:
+ btrfs_free_path(search_path);
+ kfree(name);
+ return ret;
+}
+
/* log a single inode in the tree log.
* At least one parent directory for this inode must exist in the tree
* or be logged already.
@@ -3967,6 +4262,7 @@
u64 ino = btrfs_ino(inode);
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
u64 logged_isize = 0;
+ bool need_log_inode_item = true;
path = btrfs_alloc_path();
if (!path)
@@ -4056,11 +4352,6 @@
} else {
if (inode_only == LOG_INODE_ALL)
fast_search = true;
- ret = log_inode_item(trans, log, dst_path, inode);
- if (ret) {
- err = ret;
- goto out_unlock;
- }
goto log_extents;
}
@@ -4083,6 +4374,44 @@
if (min_key.type > max_key.type)
break;
+ if (min_key.type == BTRFS_INODE_ITEM_KEY)
+ need_log_inode_item = false;
+
+ if ((min_key.type == BTRFS_INODE_REF_KEY ||
+ min_key.type == BTRFS_INODE_EXTREF_KEY) &&
+ BTRFS_I(inode)->generation == trans->transid) {
+ ret = btrfs_check_ref_name_override(path->nodes[0],
+ path->slots[0],
+ &min_key, inode);
+ if (ret < 0) {
+ err = ret;
+ goto out_unlock;
+ } else if (ret > 0) {
+ err = 1;
+ btrfs_set_log_full_commit(root->fs_info, trans);
+ goto out_unlock;
+ }
+ }
+
+ /* Skip xattrs, we log them later with btrfs_log_all_xattrs() */
+ if (min_key.type == BTRFS_XATTR_ITEM_KEY) {
+ if (ins_nr == 0)
+ goto next_slot;
+ ret = copy_items(trans, inode, dst_path, path,
+ &last_extent, ins_start_slot,
+ ins_nr, inode_only, logged_isize);
+ if (ret < 0) {
+ err = ret;
+ goto out_unlock;
+ }
+ ins_nr = 0;
+ if (ret) {
+ btrfs_release_path(path);
+ continue;
+ }
+ goto next_slot;
+ }
+
src = path->nodes[0];
if (ins_nr && ins_start_slot + ins_nr == path->slots[0]) {
ins_nr++;
@@ -4150,9 +4479,26 @@
ins_nr = 0;
}
+ btrfs_release_path(path);
+ btrfs_release_path(dst_path);
+ err = btrfs_log_all_xattrs(trans, root, inode, path, dst_path);
+ if (err)
+ goto out_unlock;
+ if (max_key.type >= BTRFS_EXTENT_DATA_KEY && !fast_search) {
+ btrfs_release_path(path);
+ btrfs_release_path(dst_path);
+ err = btrfs_log_trailing_hole(trans, root, inode, path);
+ if (err)
+ goto out_unlock;
+ }
log_extents:
btrfs_release_path(path);
btrfs_release_path(dst_path);
+ if (need_log_inode_item) {
+ err = log_inode_item(trans, log, dst_path, inode);
+ if (err)
+ goto out_unlock;
+ }
if (fast_search) {
ret = btrfs_log_changed_extents(trans, root, inode, dst_path,
&logged_list, ctx);
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
index 3182273..1418daa 100644
--- a/fs/cifs/cifs_fs_sb.h
+++ b/fs/cifs/cifs_fs_sb.h
@@ -46,6 +46,9 @@
#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
#define CIFS_MOUNT_MAP_SFM_CHR 0x800000 /* SFM/MAC mapping for illegal chars */
+#define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible
+ * root mountable
+ */
struct cifs_sb_info {
struct rb_root tlink_tree;
@@ -67,5 +70,6 @@
struct backing_dev_info bdi;
struct delayed_work prune_tlinks;
struct rcu_head rcu;
+ char *prepath;
};
#endif /* _CIFS_FS_SB_H */
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
index f4cf200..79450fa 100644
--- a/fs/cifs/cifs_spnego.c
+++ b/fs/cifs/cifs_spnego.c
@@ -24,10 +24,13 @@
#include <linux/string.h>
#include <keys/user-type.h>
#include <linux/key-type.h>
+#include <linux/keyctl.h>
#include <linux/inet.h>
#include "cifsglob.h"
#include "cifs_spnego.h"
#include "cifs_debug.h"
+#include "cifsproto.h"
+static const struct cred *spnego_cred;
/* create a new cifs key */
static int
@@ -102,6 +105,7 @@
size_t desc_len;
struct key *spnego_key;
const char *hostname = server->hostname;
+ const struct cred *saved_cred;
/* length of fields (with semicolons): ver=0xyz ip4=ipaddress
host=hostname sec=mechanism uid=0xFF user=username */
@@ -163,7 +167,9 @@
sprintf(dp, ";pid=0x%x", current->pid);
cifs_dbg(FYI, "key description = %s\n", description);
+ saved_cred = override_creds(spnego_cred);
spnego_key = request_key(&cifs_spnego_key_type, description, "");
+ revert_creds(saved_cred);
#ifdef CONFIG_CIFS_DEBUG2
if (cifsFYI && !IS_ERR(spnego_key)) {
@@ -177,3 +183,64 @@
kfree(description);
return spnego_key;
}
+
+int
+init_cifs_spnego(void)
+{
+ struct cred *cred;
+ struct key *keyring;
+ int ret;
+
+ cifs_dbg(FYI, "Registering the %s key type\n",
+ cifs_spnego_key_type.name);
+
+ /*
+ * Create an override credential set with special thread keyring for
+ * spnego upcalls.
+ */
+
+ cred = prepare_kernel_cred(NULL);
+ if (!cred)
+ return -ENOMEM;
+
+ keyring = keyring_alloc(".cifs_spnego",
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+ KEY_USR_VIEW | KEY_USR_READ,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL);
+ if (IS_ERR(keyring)) {
+ ret = PTR_ERR(keyring);
+ goto failed_put_cred;
+ }
+
+ ret = register_key_type(&cifs_spnego_key_type);
+ if (ret < 0)
+ goto failed_put_key;
+
+ /*
+ * instruct request_key() to use this special keyring as a cache for
+ * the results it looks up
+ */
+ set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags);
+ cred->thread_keyring = keyring;
+ cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
+ spnego_cred = cred;
+
+ cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring));
+ return 0;
+
+failed_put_key:
+ key_put(keyring);
+failed_put_cred:
+ put_cred(cred);
+ return ret;
+}
+
+void
+exit_cifs_spnego(void)
+{
+ key_revoke(spnego_cred->thread_keyring);
+ unregister_key_type(&cifs_spnego_key_type);
+ put_cred(spnego_cred);
+ cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name);
+}
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
index 0303c67..f7fb4b8 100644
--- a/fs/cifs/cifs_unicode.c
+++ b/fs/cifs/cifs_unicode.c
@@ -136,6 +136,12 @@
case SFM_SLASH:
*target = '\\';
break;
+ case SFM_SPACE:
+ *target = ' ';
+ break;
+ case SFM_PERIOD:
+ *target = '.';
+ break;
default:
return false;
}
@@ -364,7 +370,7 @@
return dest_char;
}
-static __le16 convert_to_sfm_char(char src_char)
+static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
{
__le16 dest_char;
@@ -387,6 +393,18 @@
case '|':
dest_char = cpu_to_le16(SFM_PIPE);
break;
+ case '.':
+ if (end_of_string)
+ dest_char = cpu_to_le16(SFM_PERIOD);
+ else
+ dest_char = 0;
+ break;
+ case ' ':
+ if (end_of_string)
+ dest_char = cpu_to_le16(SFM_SPACE);
+ else
+ dest_char = 0;
+ break;
default:
dest_char = 0;
}
@@ -424,9 +442,16 @@
/* see if we must remap this char */
if (map_chars == SFU_MAP_UNI_RSVD)
dst_char = convert_to_sfu_char(src_char);
- else if (map_chars == SFM_MAP_UNI_RSVD)
- dst_char = convert_to_sfm_char(src_char);
- else
+ else if (map_chars == SFM_MAP_UNI_RSVD) {
+ bool end_of_string;
+
+ if (i == srclen - 1)
+ end_of_string = true;
+ else
+ end_of_string = false;
+
+ dst_char = convert_to_sfm_char(src_char, end_of_string);
+ } else
dst_char = 0;
/*
* FIXME: We can not handle remapping backslash (UNI_SLASH)
diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h
index bdc52cb..479bc0a 100644
--- a/fs/cifs/cifs_unicode.h
+++ b/fs/cifs/cifs_unicode.h
@@ -64,6 +64,8 @@
#define SFM_LESSTHAN ((__u16) 0xF023)
#define SFM_PIPE ((__u16) 0xF027)
#define SFM_SLASH ((__u16) 0xF026)
+#define SFM_PERIOD ((__u16) 0xF028)
+#define SFM_SPACE ((__u16) 0xF029)
/*
* Mapping mechanism to use when one of the seven reserved characters is
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index cee9889..78404806 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -727,24 +727,26 @@
memcpy(ses->auth_key.response + baselen, tiblob, tilen);
+ mutex_lock(&ses->server->srv_mutex);
+
rc = crypto_hmacmd5_alloc(ses->server);
if (rc) {
cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc);
- goto setup_ntlmv2_rsp_ret;
+ goto unlock;
}
/* calculate ntlmv2_hash */
rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);
if (rc) {
cifs_dbg(VFS, "could not get v2 hash rc %d\n", rc);
- goto setup_ntlmv2_rsp_ret;
+ goto unlock;
}
/* calculate first part of the client response (CR1) */
rc = CalcNTLMv2_response(ses, ntlmv2_hash);
if (rc) {
cifs_dbg(VFS, "Could not calculate CR1 rc: %d\n", rc);
- goto setup_ntlmv2_rsp_ret;
+ goto unlock;
}
/* now calculate the session key for NTLMv2 */
@@ -753,13 +755,13 @@
if (rc) {
cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",
__func__);
- goto setup_ntlmv2_rsp_ret;
+ goto unlock;
}
rc = crypto_shash_init(&ses->server->secmech.sdeschmacmd5->shash);
if (rc) {
cifs_dbg(VFS, "%s: Could not init hmacmd5\n", __func__);
- goto setup_ntlmv2_rsp_ret;
+ goto unlock;
}
rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash,
@@ -767,7 +769,7 @@
CIFS_HMAC_MD5_HASH_SIZE);
if (rc) {
cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
- goto setup_ntlmv2_rsp_ret;
+ goto unlock;
}
rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
@@ -775,6 +777,8 @@
if (rc)
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
+unlock:
+ mutex_unlock(&ses->server->srv_mutex);
setup_ntlmv2_rsp_ret:
kfree(tiblob);
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 9d7996e..103e8b6 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -678,6 +678,14 @@
goto out_cifs_sb;
}
+ if (volume_info->prepath) {
+ cifs_sb->prepath = kstrdup(volume_info->prepath, GFP_KERNEL);
+ if (cifs_sb->prepath == NULL) {
+ root = ERR_PTR(-ENOMEM);
+ goto out_cifs_sb;
+ }
+ }
+
cifs_setup_cifs_sb(volume_info, cifs_sb);
rc = cifs_mount(cifs_sb, volume_info);
@@ -716,7 +724,11 @@
sb->s_flags |= MS_ACTIVE;
}
- root = cifs_get_root(volume_info, sb);
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
+ root = dget(sb->s_root);
+ else
+ root = cifs_get_root(volume_info, sb);
+
if (IS_ERR(root))
goto out_super;
@@ -1249,7 +1261,7 @@
goto out_destroy_mids;
#ifdef CONFIG_CIFS_UPCALL
- rc = register_key_type(&cifs_spnego_key_type);
+ rc = init_cifs_spnego();
if (rc)
goto out_destroy_request_bufs;
#endif /* CONFIG_CIFS_UPCALL */
@@ -1272,7 +1284,7 @@
out_register_key_type:
#endif
#ifdef CONFIG_CIFS_UPCALL
- unregister_key_type(&cifs_spnego_key_type);
+ exit_cifs_spnego();
out_destroy_request_bufs:
#endif
cifs_destroy_request_bufs();
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 02a33e5..99745da 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -223,6 +223,7 @@
/* verify the message */
int (*check_message)(char *, unsigned int);
bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
+ int (*handle_cancelled_mid)(char *, struct TCP_Server_Info *);
void (*downgrade_oplock)(struct TCP_Server_Info *,
struct cifsInodeInfo *, bool);
/* process transaction2 response */
@@ -615,6 +616,8 @@
#ifdef CONFIG_CIFS_SMB2
unsigned int max_read;
unsigned int max_write;
+ struct delayed_work reconnect; /* reconnect workqueue job */
+ struct mutex reconnect_mutex; /* prevent simultaneous reconnects */
#endif /* CONFIG_CIFS_SMB2 */
};
@@ -814,6 +817,7 @@
struct cifs_tcon {
struct list_head tcon_list;
int tc_count;
+ struct list_head rlist; /* reconnect list */
struct list_head openFileList;
struct cifs_ses *ses; /* pointer to session associated with */
char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
@@ -888,7 +892,6 @@
bool need_reconnect:1; /* connection reset, tid now invalid */
#ifdef CONFIG_CIFS_SMB2
bool print:1; /* set if connection to printer share */
- bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */
__le32 capabilities;
__u32 share_flags;
__u32 maximal_access;
@@ -1262,12 +1265,19 @@
void *callback_data; /* general purpose pointer for callback */
void *resp_buf; /* pointer to received SMB header */
int mid_state; /* wish this were enum but can not pass to wait_event */
+ unsigned int mid_flags;
__le16 command; /* smb command code */
bool large_buf:1; /* if valid response, is pointer to large buf */
bool multiRsp:1; /* multiple trans2 responses for one request */
bool multiEnd:1; /* both received */
};
+struct close_cancelled_open {
+ struct cifs_fid fid;
+ struct cifs_tcon *tcon;
+ struct work_struct work;
+};
+
/* Make code in transport.c a little cleaner by moving
update of optional stats into function below */
#ifdef CONFIG_CIFS_STATS2
@@ -1399,6 +1409,9 @@
#define MID_RESPONSE_MALFORMED 0x10
#define MID_SHUTDOWN 0x20
+/* Flags */
+#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */
+
/* Types of response buffer returned from SendReceive2 */
#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
#define CIFS_SMALL_BUFFER 1
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index c31ce98..26f1954 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -60,6 +60,8 @@
} while (0)
extern int init_cifs_idmap(void);
extern void exit_cifs_idmap(void);
+extern int init_cifs_spnego(void);
+extern void exit_cifs_spnego(void);
extern char *build_path_from_dentry(struct dentry *);
extern char *cifs_build_path_to_root(struct smb_vol *vol,
struct cifs_sb_info *cifs_sb,
@@ -205,6 +207,9 @@
struct tcon_link *tlink,
struct cifs_pending_open *open);
extern void cifs_del_pending_open(struct cifs_pending_open *open);
+extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
+ int from_reconnect);
+extern void cifs_put_tcon(struct cifs_tcon *tcon);
#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
extern void cifs_dfs_release_automount_timer(void);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 9924c8c..845148d 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1422,6 +1422,8 @@
length = discard_remaining_data(server);
dequeue_mid(mid, rdata->result);
+ mid->resp_buf = server->smallbuf;
+ server->smallbuf = NULL;
return length;
}
@@ -1536,6 +1538,8 @@
return cifs_readv_discard(server, mid);
dequeue_mid(mid, false);
+ mid->resp_buf = server->smallbuf;
+ server->smallbuf = NULL;
return length;
}
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index c750973..0923f9c 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -52,6 +52,9 @@
#include "nterr.h"
#include "rfc1002pdu.h"
#include "fscache.h"
+#ifdef CONFIG_CIFS_SMB2
+#include "smb2proto.h"
+#endif
#define CIFS_PORT 445
#define RFC1001_PORT 139
@@ -413,7 +416,9 @@
* server->ops->need_neg() == true. Also, no need to ping if
* we got a response recently.
*/
- if (!server->ops->need_neg || server->ops->need_neg(server) ||
+
+ if (server->tcpStatus == CifsNeedReconnect ||
+ server->tcpStatus == CifsExiting || server->tcpStatus == CifsNew ||
(server->ops->can_echo && !server->ops->can_echo(server)) ||
time_before(jiffies, server->lstrp + SMB_ECHO_INTERVAL - HZ))
goto requeue_echo;
@@ -907,10 +912,19 @@
server->lstrp = jiffies;
if (mid_entry != NULL) {
+ if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) &&
+ mid_entry->mid_state == MID_RESPONSE_RECEIVED &&
+ server->ops->handle_cancelled_mid)
+ server->ops->handle_cancelled_mid(
+ mid_entry->resp_buf,
+ server);
+
if (!mid_entry->multiRsp || mid_entry->multiEnd)
mid_entry->callback(mid_entry);
- } else if (!server->ops->is_oplock_break ||
- !server->ops->is_oplock_break(buf, server)) {
+ } else if (server->ops->is_oplock_break &&
+ server->ops->is_oplock_break(buf, server)) {
+ cifs_dbg(FYI, "Received oplock break\n");
+ } else {
cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n",
atomic_read(&midCount));
cifs_dump_mem("Received Data is: ", buf,
@@ -2062,8 +2076,8 @@
return NULL;
}
-static void
-cifs_put_tcp_session(struct TCP_Server_Info *server)
+void
+cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
{
struct task_struct *task;
@@ -2080,6 +2094,19 @@
cancel_delayed_work_sync(&server->echo);
+#ifdef CONFIG_CIFS_SMB2
+ if (from_reconnect)
+ /*
+ * Avoid deadlock here: reconnect work calls
+ * cifs_put_tcp_session() at its end. Need to be sure
+ * that reconnect work does nothing with server pointer after
+ * that step.
+ */
+ cancel_delayed_work(&server->reconnect);
+ else
+ cancel_delayed_work_sync(&server->reconnect);
+#endif
+
spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock);
@@ -2144,6 +2171,10 @@
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
+#ifdef CONFIG_CIFS_SMB2
+ INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
+ mutex_init(&tcp_ses->reconnect_mutex);
+#endif
memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
sizeof(tcp_ses->srcaddr));
memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
@@ -2296,7 +2327,7 @@
spin_unlock(&cifs_tcp_ses_lock);
sesInfoFree(ses);
- cifs_put_tcp_session(server);
+ cifs_put_tcp_session(server, 0);
}
#ifdef CONFIG_KEYS
@@ -2469,7 +2500,7 @@
mutex_unlock(&ses->session_mutex);
/* existing SMB ses has a server reference already */
- cifs_put_tcp_session(server);
+ cifs_put_tcp_session(server, 0);
free_xid(xid);
return ses;
}
@@ -2559,7 +2590,7 @@
return NULL;
}
-static void
+void
cifs_put_tcon(struct cifs_tcon *tcon)
{
unsigned int xid;
@@ -3430,6 +3461,44 @@
return volume_info;
}
+static int
+cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
+ unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifs_sb_info *cifs_sb,
+ char *full_path)
+{
+ int rc;
+ char *s;
+ char sep, tmp;
+
+ sep = CIFS_DIR_SEP(cifs_sb);
+ s = full_path;
+
+ rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, "");
+ while (rc == 0) {
+ /* skip separators */
+ while (*s == sep)
+ s++;
+ if (!*s)
+ break;
+ /* next separator */
+ while (*s && *s != sep)
+ s++;
+
+ /*
+ * temporarily null-terminate the path at the end of
+ * the current component
+ */
+ tmp = *s;
+ *s = 0;
+ rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
+ full_path);
+ *s = tmp;
+ }
+ return rc;
+}
+
int
cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *volume_info)
{
@@ -3556,6 +3625,16 @@
kfree(full_path);
goto mount_fail_check;
}
+
+ rc = cifs_are_all_path_components_accessible(server,
+ xid, tcon, cifs_sb,
+ full_path);
+ if (rc != 0) {
+ cifs_dbg(VFS, "cannot query dirs between root and final path, "
+ "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+ rc = 0;
+ }
kfree(full_path);
}
@@ -3619,7 +3698,7 @@
else if (ses)
cifs_put_smb_ses(ses);
else
- cifs_put_tcp_session(server);
+ cifs_put_tcp_session(server, 0);
bdi_destroy(&cifs_sb->bdi);
}
@@ -3819,6 +3898,7 @@
bdi_destroy(&cifs_sb->bdi);
kfree(cifs_sb->mountdata);
+ kfree(cifs_sb->prepath);
call_rcu(&cifs_sb->rcu, delayed_free);
}
@@ -3923,7 +4003,7 @@
ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info);
if (IS_ERR(ses)) {
tcon = (struct cifs_tcon *)ses;
- cifs_put_tcp_session(master_tcon->ses->server);
+ cifs_put_tcp_session(master_tcon->ses->server, 0);
goto out;
}
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index b72bc29..ed7b6f7 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -84,6 +84,7 @@
struct dentry *temp;
int namelen;
int dfsplen;
+ int pplen = 0;
char *full_path;
char dirsep;
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
@@ -95,8 +96,12 @@
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
else
dfsplen = 0;
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
+ pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0;
+
cifs_bp_rename_retry:
- namelen = dfsplen;
+ namelen = dfsplen + pplen;
seq = read_seqbegin(&rename_lock);
rcu_read_lock();
for (temp = direntry; !IS_ROOT(temp);) {
@@ -137,7 +142,7 @@
}
}
rcu_read_unlock();
- if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
+ if (namelen != dfsplen + pplen || read_seqretry(&rename_lock, seq)) {
cifs_dbg(FYI, "did not end path lookup where expected. namelen=%ddfsplen=%d\n",
namelen, dfsplen);
/* presumably this is only possible if racing with a rename
@@ -153,6 +158,17 @@
those safely to '/' if any are found in the middle of the prepath */
/* BB test paths to Windows with '/' in the midst of prepath */
+ if (pplen) {
+ int i;
+
+ cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath);
+ memcpy(full_path+dfsplen+1, cifs_sb->prepath, pplen-1);
+ full_path[dfsplen] = '\\';
+ for (i = 0; i < pplen-1; i++)
+ if (full_path[dfsplen+1+i] == '/')
+ full_path[dfsplen+1+i] = CIFS_DIR_SEP(cifs_sb);
+ }
+
if (dfsplen) {
strncpy(full_path, tcon->treeName, dfsplen);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
@@ -229,6 +245,13 @@
goto cifs_create_get_file_info;
}
+ if (S_ISDIR(newinode->i_mode)) {
+ CIFSSMBClose(xid, tcon, fid->netfid);
+ iput(newinode);
+ rc = -EISDIR;
+ goto out;
+ }
+
if (!S_ISREG(newinode->i_mode)) {
/*
* The server may allow us to open things like
@@ -399,10 +422,14 @@
if (rc != 0) {
cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n",
rc);
- if (server->ops->close)
- server->ops->close(xid, tcon, fid);
- goto out;
+ goto out_err;
}
+
+ if (S_ISDIR(newinode->i_mode)) {
+ rc = -EISDIR;
+ goto out_err;
+ }
+
d_drop(direntry);
d_add(direntry, newinode);
@@ -410,6 +437,13 @@
kfree(buf);
kfree(full_path);
return rc;
+
+out_err:
+ if (server->ops->close)
+ server->ops->close(xid, tcon, fid);
+ if (newinode)
+ iput(newinode);
+ goto out;
}
int
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 9431449..a3baded 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -2542,7 +2542,7 @@
wdata->credits = credits;
if (!wdata->cfile->invalidHandle ||
- !cifs_reopen_file(wdata->cfile, false))
+ !(rc = cifs_reopen_file(wdata->cfile, false)))
rc = server->ops->async_writev(wdata,
cifs_uncached_writedata_release);
if (rc) {
@@ -2977,7 +2977,7 @@
rdata->credits = credits;
if (!rdata->cfile->invalidHandle ||
- !cifs_reopen_file(rdata->cfile, true))
+ !(rc = cifs_reopen_file(rdata->cfile, true)))
rc = server->ops->async_readv(rdata);
error:
if (rc) {
@@ -3564,7 +3564,7 @@
}
if (!rdata->cfile->invalidHandle ||
- !cifs_reopen_file(rdata->cfile, true))
+ !(rc = cifs_reopen_file(rdata->cfile, true)))
rc = server->ops->async_readv(rdata);
if (rc) {
add_credits_and_wake_if(server, rdata->credits, 0);
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index c88a827..ff226cc 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -958,10 +958,26 @@
struct inode *inode = NULL;
long rc;
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ char *path = NULL;
+ int len;
+
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
+ && cifs_sb->prepath) {
+ len = strlen(cifs_sb->prepath);
+ path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL);
+ if (path == NULL)
+ return ERR_PTR(-ENOMEM);
+ path[0] = '/';
+ memcpy(path+1, cifs_sb->prepath, len);
+ } else {
+ path = kstrdup("", GFP_KERNEL);
+ if (path == NULL)
+ return ERR_PTR(-ENOMEM);
+ }
xid = get_xid();
if (tcon->unix_ext) {
- rc = cifs_get_inode_info_unix(&inode, "", sb, xid);
+ rc = cifs_get_inode_info_unix(&inode, path, sb, xid);
/* some servers mistakenly claim POSIX support */
if (rc != -EOPNOTSUPP)
goto iget_no_retry;
@@ -969,7 +985,8 @@
tcon->unix_ext = false;
}
- rc = cifs_get_inode_info(&inode, "", NULL, sb, xid, NULL);
+ convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
+ rc = cifs_get_inode_info(&inode, path, NULL, sb, xid, NULL);
iget_no_retry:
if (!inode) {
@@ -998,6 +1015,7 @@
}
out:
+ kfree(path);
/* can not call macro free_xid here since in a void func
* TODO: This is no longer true
*/
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index b333ff6..abae6dd 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -926,6 +926,7 @@
/* Subtract the NTFS time offset, then convert to 1s intervals. */
s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
+ u64 abs_t;
/*
* Unfortunately can not use normal 64 bit division on 32 bit arch, but
@@ -933,13 +934,14 @@
* to special case them
*/
if (t < 0) {
- t = -t;
- ts.tv_nsec = (long)(do_div(t, 10000000) * 100);
+ abs_t = -t;
+ ts.tv_nsec = (long)(do_div(abs_t, 10000000) * 100);
ts.tv_nsec = -ts.tv_nsec;
- ts.tv_sec = -t;
+ ts.tv_sec = -abs_t;
} else {
- ts.tv_nsec = (long)do_div(t, 10000000) * 100;
- ts.tv_sec = t;
+ abs_t = t;
+ ts.tv_nsec = (long)do_div(abs_t, 10000000) * 100;
+ ts.tv_sec = abs_t;
}
return ts;
diff --git a/fs/cifs/ntlmssp.h b/fs/cifs/ntlmssp.h
index 848249f..3079b38 100644
--- a/fs/cifs/ntlmssp.h
+++ b/fs/cifs/ntlmssp.h
@@ -133,6 +133,6 @@
int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, struct cifs_ses *ses);
-int build_ntlmssp_auth_blob(unsigned char *pbuffer, u16 *buflen,
+int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses,
const struct nls_table *nls_cp);
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 57db63f..693da83 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -364,19 +364,43 @@
sec_blob->DomainName.MaximumLength = 0;
}
-/* We do not malloc the blob, it is passed in pbuffer, because its
- maximum possible size is fixed and small, making this approach cleaner.
- This function returns the length of the data in the blob */
-int build_ntlmssp_auth_blob(unsigned char *pbuffer,
+static int size_of_ntlmssp_blob(struct cifs_ses *ses)
+{
+ int sz = sizeof(AUTHENTICATE_MESSAGE) + ses->auth_key.len
+ - CIFS_SESS_KEY_SIZE + CIFS_CPHTXT_SIZE + 2;
+
+ if (ses->domainName)
+ sz += 2 * strnlen(ses->domainName, CIFS_MAX_DOMAINNAME_LEN);
+ else
+ sz += 2;
+
+ if (ses->user_name)
+ sz += 2 * strnlen(ses->user_name, CIFS_MAX_USERNAME_LEN);
+ else
+ sz += 2;
+
+ return sz;
+}
+
+int build_ntlmssp_auth_blob(unsigned char **pbuffer,
u16 *buflen,
struct cifs_ses *ses,
const struct nls_table *nls_cp)
{
int rc;
- AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer;
+ AUTHENTICATE_MESSAGE *sec_blob;
__u32 flags;
unsigned char *tmp;
+ rc = setup_ntlmv2_rsp(ses, nls_cp);
+ if (rc) {
+ cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
+ *buflen = 0;
+ goto setup_ntlmv2_ret;
+ }
+ *pbuffer = kmalloc(size_of_ntlmssp_blob(ses), GFP_KERNEL);
+ sec_blob = (AUTHENTICATE_MESSAGE *)*pbuffer;
+
memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
sec_blob->MessageType = NtLmAuthenticate;
@@ -391,7 +415,7 @@
flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
}
- tmp = pbuffer + sizeof(AUTHENTICATE_MESSAGE);
+ tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
sec_blob->NegotiateFlags = cpu_to_le32(flags);
sec_blob->LmChallengeResponse.BufferOffset =
@@ -399,23 +423,27 @@
sec_blob->LmChallengeResponse.Length = 0;
sec_blob->LmChallengeResponse.MaximumLength = 0;
- sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer);
- rc = setup_ntlmv2_rsp(ses, nls_cp);
- if (rc) {
- cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc);
- goto setup_ntlmv2_ret;
- }
- memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
- ses->auth_key.len - CIFS_SESS_KEY_SIZE);
- tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
+ sec_blob->NtChallengeResponse.BufferOffset =
+ cpu_to_le32(tmp - *pbuffer);
+ if (ses->user_name != NULL) {
+ memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
+ ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
- sec_blob->NtChallengeResponse.Length =
- cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
- sec_blob->NtChallengeResponse.MaximumLength =
- cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ sec_blob->NtChallengeResponse.Length =
+ cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ sec_blob->NtChallengeResponse.MaximumLength =
+ cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ } else {
+ /*
+ * don't send an NT Response for anonymous access
+ */
+ sec_blob->NtChallengeResponse.Length = 0;
+ sec_blob->NtChallengeResponse.MaximumLength = 0;
+ }
if (ses->domainName == NULL) {
- sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
+ sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->DomainName.Length = 0;
sec_blob->DomainName.MaximumLength = 0;
tmp += 2;
@@ -424,14 +452,14 @@
len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName,
CIFS_MAX_USERNAME_LEN, nls_cp);
len *= 2; /* unicode is 2 bytes each */
- sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
+ sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->DomainName.Length = cpu_to_le16(len);
sec_blob->DomainName.MaximumLength = cpu_to_le16(len);
tmp += len;
}
if (ses->user_name == NULL) {
- sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);
+ sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->UserName.Length = 0;
sec_blob->UserName.MaximumLength = 0;
tmp += 2;
@@ -440,13 +468,13 @@
len = cifs_strtoUTF16((__le16 *)tmp, ses->user_name,
CIFS_MAX_USERNAME_LEN, nls_cp);
len *= 2; /* unicode is 2 bytes each */
- sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);
+ sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->UserName.Length = cpu_to_le16(len);
sec_blob->UserName.MaximumLength = cpu_to_le16(len);
tmp += len;
}
- sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - pbuffer);
+ sec_blob->WorkstationName.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->WorkstationName.Length = 0;
sec_blob->WorkstationName.MaximumLength = 0;
tmp += 2;
@@ -455,19 +483,19 @@
(ses->ntlmssp->server_flags & NTLMSSP_NEGOTIATE_EXTENDED_SEC))
&& !calc_seckey(ses)) {
memcpy(tmp, ses->ntlmssp->ciphertext, CIFS_CPHTXT_SIZE);
- sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer);
+ sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->SessionKey.Length = cpu_to_le16(CIFS_CPHTXT_SIZE);
sec_blob->SessionKey.MaximumLength =
cpu_to_le16(CIFS_CPHTXT_SIZE);
tmp += CIFS_CPHTXT_SIZE;
} else {
- sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer);
+ sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - *pbuffer);
sec_blob->SessionKey.Length = 0;
sec_blob->SessionKey.MaximumLength = 0;
}
+ *buflen = tmp - *pbuffer;
setup_ntlmv2_ret:
- *buflen = tmp - pbuffer;
return rc;
}
@@ -670,20 +698,24 @@
pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE;
- /* no capabilities flags in old lanman negotiation */
- pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
+ if (ses->user_name != NULL) {
+ /* no capabilities flags in old lanman negotiation */
+ pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
- /* Calculate hash with password and copy into bcc_ptr.
- * Encryption Key (stored as in cryptkey) gets used if the
- * security mode bit in Negottiate Protocol response states
- * to use challenge/response method (i.e. Password bit is 1).
- */
- rc = calc_lanman_hash(ses->password, ses->server->cryptkey,
- ses->server->sec_mode & SECMODE_PW_ENCRYPT ?
- true : false, lnm_session_key);
+ /* Calculate hash with password and copy into bcc_ptr.
+ * Encryption Key (stored as in cryptkey) gets used if the
+ * security mode bit in Negottiate Protocol response states
+ * to use challenge/response method (i.e. Password bit is 1).
+ */
+ rc = calc_lanman_hash(ses->password, ses->server->cryptkey,
+ ses->server->sec_mode & SECMODE_PW_ENCRYPT ?
+ true : false, lnm_session_key);
- memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
- bcc_ptr += CIFS_AUTH_RESP_SIZE;
+ memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE);
+ bcc_ptr += CIFS_AUTH_RESP_SIZE;
+ } else {
+ pSMB->old_req.PasswordLength = 0;
+ }
/*
* can not sign if LANMAN negotiated so no need
@@ -769,27 +801,32 @@
capabilities = cifs_ssetup_hdr(ses, pSMB);
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
- pSMB->req_no_secext.CaseInsensitivePasswordLength =
- cpu_to_le16(CIFS_AUTH_RESP_SIZE);
- pSMB->req_no_secext.CaseSensitivePasswordLength =
- cpu_to_le16(CIFS_AUTH_RESP_SIZE);
+ if (ses->user_name != NULL) {
+ pSMB->req_no_secext.CaseInsensitivePasswordLength =
+ cpu_to_le16(CIFS_AUTH_RESP_SIZE);
+ pSMB->req_no_secext.CaseSensitivePasswordLength =
+ cpu_to_le16(CIFS_AUTH_RESP_SIZE);
- /* calculate ntlm response and session key */
- rc = setup_ntlm_response(ses, sess_data->nls_cp);
- if (rc) {
- cifs_dbg(VFS, "Error %d during NTLM authentication\n",
- rc);
- goto out;
+ /* calculate ntlm response and session key */
+ rc = setup_ntlm_response(ses, sess_data->nls_cp);
+ if (rc) {
+ cifs_dbg(VFS, "Error %d during NTLM authentication\n",
+ rc);
+ goto out;
+ }
+
+ /* copy ntlm response */
+ memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
+ CIFS_AUTH_RESP_SIZE);
+ bcc_ptr += CIFS_AUTH_RESP_SIZE;
+ memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
+ CIFS_AUTH_RESP_SIZE);
+ bcc_ptr += CIFS_AUTH_RESP_SIZE;
+ } else {
+ pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
+ pSMB->req_no_secext.CaseSensitivePasswordLength = 0;
}
- /* copy ntlm response */
- memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
- CIFS_AUTH_RESP_SIZE);
- bcc_ptr += CIFS_AUTH_RESP_SIZE;
- memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
- CIFS_AUTH_RESP_SIZE);
- bcc_ptr += CIFS_AUTH_RESP_SIZE;
-
if (ses->capabilities & CAP_UNICODE) {
/* unicode strings must be word aligned */
if (sess_data->iov[0].iov_len % 2) {
@@ -878,23 +915,27 @@
/* LM2 password would be here if we supported it */
pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
- /* calculate nlmv2 response and session key */
- rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp);
- if (rc) {
- cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc);
- goto out;
+ if (ses->user_name != NULL) {
+ /* calculate nlmv2 response and session key */
+ rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp);
+ if (rc) {
+ cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc);
+ goto out;
+ }
+
+ memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
+ ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
+
+ /* set case sensitive password length after tilen may get
+ * assigned, tilen is 0 otherwise.
+ */
+ pSMB->req_no_secext.CaseSensitivePasswordLength =
+ cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
+ } else {
+ pSMB->req_no_secext.CaseSensitivePasswordLength = 0;
}
- memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE,
- ses->auth_key.len - CIFS_SESS_KEY_SIZE);
- bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE;
-
- /* set case sensitive password length after tilen may get
- * assigned, tilen is 0 otherwise.
- */
- pSMB->req_no_secext.CaseSensitivePasswordLength =
- cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE);
-
if (ses->capabilities & CAP_UNICODE) {
if (sess_data->iov[0].iov_len % 2) {
*bcc_ptr = 0;
@@ -1245,7 +1286,7 @@
struct cifs_ses *ses = sess_data->ses;
__u16 bytes_remaining;
char *bcc_ptr;
- char *ntlmsspblob = NULL;
+ unsigned char *ntlmsspblob = NULL;
u16 blob_len;
cifs_dbg(FYI, "rawntlmssp session setup authenticate phase\n");
@@ -1258,19 +1299,7 @@
/* Build security blob before we assemble the request */
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
smb_buf = (struct smb_hdr *)pSMB;
- /*
- * 5 is an empirical value, large enough to hold
- * authenticate message plus max 10 of av paris,
- * domain, user, workstation names, flags, etc.
- */
- ntlmsspblob = kzalloc(5*sizeof(struct _AUTHENTICATE_MESSAGE),
- GFP_KERNEL);
- if (!ntlmsspblob) {
- rc = -ENOMEM;
- goto out;
- }
-
- rc = build_ntlmssp_auth_blob(ntlmsspblob,
+ rc = build_ntlmssp_auth_blob(&ntlmsspblob,
&blob_len, ses, sess_data->nls_cp);
if (rc)
goto out_free_ntlmsspblob;
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index d297903..edaabb8 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -1014,6 +1014,15 @@
return !cfile->srch_inf.endOfSearch && !cfile->invalidHandle;
}
+static bool
+cifs_can_echo(struct TCP_Server_Info *server)
+{
+ if (server->tcpStatus == CifsGood)
+ return true;
+
+ return false;
+}
+
struct smb_version_operations smb1_operations = {
.send_cancel = send_nt_cancel,
.compare_fids = cifs_compare_fids,
@@ -1048,6 +1057,7 @@
.get_dfs_refer = CIFSGetDFSRefer,
.qfs_tcon = cifs_qfs_tcon,
.is_path_accessible = cifs_is_path_accessible,
+ .can_echo = cifs_can_echo,
.query_path_info = cifs_query_path_info,
.query_file_info = cifs_query_file_info,
.get_srv_inum = cifs_get_srv_inum,
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
index 4599294..b87b075 100644
--- a/fs/cifs/smb2file.c
+++ b/fs/cifs/smb2file.c
@@ -241,7 +241,7 @@
* and check it for zero before using.
*/
max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf;
- if (!max_buf) {
+ if (max_buf < sizeof(struct smb2_lock_element)) {
free_xid(xid);
return -EINVAL;
}
diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h
index bc0bb9c..0ffa180 100644
--- a/fs/cifs/smb2glob.h
+++ b/fs/cifs/smb2glob.h
@@ -44,6 +44,7 @@
#define SMB2_OP_DELETE 7
#define SMB2_OP_HARDLINK 8
#define SMB2_OP_SET_EOF 9
+#define SMB2_OP_RMDIR 10
/* Used when constructing chained read requests. */
#define CHAINED_REQUEST 1
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 899bbc8..4f0231e 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -80,6 +80,10 @@
* SMB2_open() call.
*/
break;
+ case SMB2_OP_RMDIR:
+ tmprc = SMB2_rmdir(xid, tcon, fid.persistent_fid,
+ fid.volatile_fid);
+ break;
case SMB2_OP_RENAME:
tmprc = SMB2_rename(xid, tcon, fid.persistent_fid,
fid.volatile_fid, (__le16 *)data);
@@ -191,8 +195,8 @@
struct cifs_sb_info *cifs_sb)
{
return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
- CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE,
- NULL, SMB2_OP_DELETE);
+ CREATE_NOT_FILE,
+ NULL, SMB2_OP_RMDIR);
}
int
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 1a08a34..b35c139 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -628,3 +628,47 @@
cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n");
return false;
}
+
+void
+smb2_cancelled_close_fid(struct work_struct *work)
+{
+ struct close_cancelled_open *cancelled = container_of(work,
+ struct close_cancelled_open, work);
+
+ cifs_dbg(VFS, "Close unmatched open\n");
+
+ SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid,
+ cancelled->fid.volatile_fid);
+ cifs_put_tcon(cancelled->tcon);
+ kfree(cancelled);
+}
+
+int
+smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
+{
+ struct smb2_hdr *hdr = (struct smb2_hdr *)buffer;
+ struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
+ struct cifs_tcon *tcon;
+ struct close_cancelled_open *cancelled;
+
+ if (hdr->Command != SMB2_CREATE || hdr->Status != STATUS_SUCCESS)
+ return 0;
+
+ cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
+ if (!cancelled)
+ return -ENOMEM;
+
+ tcon = smb2_find_smb_tcon(server, hdr->SessionId, hdr->TreeId);
+ if (!tcon) {
+ kfree(cancelled);
+ return -ENOENT;
+ }
+
+ cancelled->fid.persistent_fid = rsp->PersistentFileId;
+ cancelled->fid.volatile_fid = rsp->VolatileFileId;
+ cancelled->tcon = tcon;
+ INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
+ queue_work(cifsiod_wq, &cancelled->work);
+
+ return 0;
+}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 51f5251..56dba52 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -972,6 +972,9 @@
get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
}
+#define SMB2_SYMLINK_STRUCT_SIZE \
+ (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
+
static int
smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
const char *full_path, char **target_path,
@@ -984,7 +987,10 @@
struct cifs_fid fid;
struct smb2_err_rsp *err_buf = NULL;
struct smb2_symlink_err_rsp *symlink;
- unsigned int sub_len, sub_offset;
+ unsigned int sub_len;
+ unsigned int sub_offset;
+ unsigned int print_len;
+ unsigned int print_offset;
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
@@ -1005,11 +1011,33 @@
kfree(utf16_path);
return -ENOENT;
}
+
+ if (le32_to_cpu(err_buf->ByteCount) < sizeof(struct smb2_symlink_err_rsp) ||
+ get_rfc1002_length(err_buf) + 4 < SMB2_SYMLINK_STRUCT_SIZE) {
+ kfree(utf16_path);
+ return -ENOENT;
+ }
+
/* open must fail on symlink - reset rc */
rc = 0;
symlink = (struct smb2_symlink_err_rsp *)err_buf->ErrorData;
sub_len = le16_to_cpu(symlink->SubstituteNameLength);
sub_offset = le16_to_cpu(symlink->SubstituteNameOffset);
+ print_len = le16_to_cpu(symlink->PrintNameLength);
+ print_offset = le16_to_cpu(symlink->PrintNameOffset);
+
+ if (get_rfc1002_length(err_buf) + 4 <
+ SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len) {
+ kfree(utf16_path);
+ return -ENOENT;
+ }
+
+ if (get_rfc1002_length(err_buf) + 4 <
+ SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len) {
+ kfree(utf16_path);
+ return -ENOENT;
+ }
+
*target_path = cifs_strndup_from_utf16(
(char *)symlink->PathBuffer + sub_offset,
sub_len, true, cifs_sb->local_nls);
@@ -1433,6 +1461,8 @@
.clear_stats = smb2_clear_stats,
.print_stats = smb2_print_stats,
.is_oplock_break = smb2_is_valid_oplock_break,
+ .handle_cancelled_mid = smb2_handle_cancelled_mid,
+ .handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb2_downgrade_oplock,
.need_neg = smb2_need_neg,
.negotiate = smb2_negotiate,
@@ -1514,6 +1544,8 @@
.print_stats = smb2_print_stats,
.dump_share_caps = smb2_dump_share_caps,
.is_oplock_break = smb2_is_valid_oplock_break,
+ .handle_cancelled_mid = smb2_handle_cancelled_mid,
+ .handle_cancelled_mid = smb2_handle_cancelled_mid,
.downgrade_oplock = smb2_downgrade_oplock,
.need_neg = smb2_need_neg,
.negotiate = smb2_negotiate,
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 3398ac7..1f3e6c74 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -46,6 +46,7 @@
#include "smb2status.h"
#include "smb2glob.h"
#include "cifspdu.h"
+#include "cifs_spnego.h"
/*
* The following table defines the expected "StructureSize" of SMB2 requests
@@ -263,7 +264,7 @@
case SMB2_CHANGE_NOTIFY:
case SMB2_QUERY_INFO:
case SMB2_SET_INFO:
- return -EAGAIN;
+ rc = -EAGAIN;
}
unload_nls(nls_codepage);
return rc;
@@ -427,20 +428,15 @@
cifs_dbg(FYI, "missing security blob on negprot\n");
rc = cifs_enable_signing(server, ses->sign);
-#ifdef CONFIG_SMB2_ASN1 /* BB REMOVEME when updated asn1.c ready */
if (rc)
goto neg_exit;
- if (blob_length)
- rc = decode_neg_token_init(security_blob, blob_length,
- &server->sec_type);
- if (rc == 1)
- rc = 0;
- else if (rc == 0) {
- rc = -EIO;
- goto neg_exit;
+ if (blob_length) {
+ rc = decode_negTokenInit(security_blob, blob_length, server);
+ if (rc == 1)
+ rc = 0;
+ else if (rc == 0)
+ rc = -EIO;
}
-#endif
-
neg_exit:
free_rsp_buf(resp_buftype, rsp);
return rc;
@@ -534,8 +530,9 @@
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
struct TCP_Server_Info *server = ses->server;
u16 blob_length = 0;
- char *security_blob;
- char *ntlmssp_blob = NULL;
+ struct key *spnego_key = NULL;
+ char *security_blob = NULL;
+ unsigned char *ntlmssp_blob = NULL;
bool use_spnego = false; /* else use raw ntlmssp */
cifs_dbg(FYI, "Session Setup\n");
@@ -562,7 +559,8 @@
ses->ntlmssp->sesskey_per_smbsess = true;
/* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
- ses->sectype = RawNTLMSSP;
+ if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
+ ses->sectype = RawNTLMSSP;
ssetup_ntlmssp_authenticate:
if (phase == NtLmChallenge)
@@ -591,7 +589,48 @@
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field and 1 for pad */
iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
- if (phase == NtLmNegotiate) {
+
+ if (ses->sectype == Kerberos) {
+#ifdef CONFIG_CIFS_UPCALL
+ struct cifs_spnego_msg *msg;
+
+ spnego_key = cifs_get_spnego_key(ses);
+ if (IS_ERR(spnego_key)) {
+ rc = PTR_ERR(spnego_key);
+ spnego_key = NULL;
+ goto ssetup_exit;
+ }
+
+ msg = spnego_key->payload.data;
+ /*
+ * check version field to make sure that cifs.upcall is
+ * sending us a response in an expected form
+ */
+ if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
+ cifs_dbg(VFS,
+ "bad cifs.upcall version. Expected %d got %d",
+ CIFS_SPNEGO_UPCALL_VERSION, msg->version);
+ rc = -EKEYREJECTED;
+ goto ssetup_exit;
+ }
+ ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
+ GFP_KERNEL);
+ if (!ses->auth_key.response) {
+ cifs_dbg(VFS,
+ "Kerberos can't allocate (%u bytes) memory",
+ msg->sesskey_len);
+ rc = -ENOMEM;
+ goto ssetup_exit;
+ }
+ ses->auth_key.len = msg->sesskey_len;
+ blob_length = msg->secblob_len;
+ iov[1].iov_base = msg->data + msg->sesskey_len;
+ iov[1].iov_len = blob_length;
+#else
+ rc = -EOPNOTSUPP;
+ goto ssetup_exit;
+#endif /* CONFIG_CIFS_UPCALL */
+ } else if (phase == NtLmNegotiate) { /* if not krb5 must be ntlmssp */
ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
GFP_KERNEL);
if (ntlmssp_blob == NULL) {
@@ -614,15 +653,11 @@
/* with raw NTLMSSP we don't encapsulate in SPNEGO */
security_blob = ntlmssp_blob;
}
+ iov[1].iov_base = security_blob;
+ iov[1].iov_len = blob_length;
} else if (phase == NtLmAuthenticate) {
req->hdr.SessionId = ses->Suid;
- ntlmssp_blob = kzalloc(sizeof(struct _NEGOTIATE_MESSAGE) + 500,
- GFP_KERNEL);
- if (ntlmssp_blob == NULL) {
- rc = -ENOMEM;
- goto ssetup_exit;
- }
- rc = build_ntlmssp_auth_blob(ntlmssp_blob, &blob_length, ses,
+ rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
nls_cp);
if (rc) {
cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n",
@@ -641,6 +676,8 @@
} else {
security_blob = ntlmssp_blob;
}
+ iov[1].iov_base = security_blob;
+ iov[1].iov_len = blob_length;
} else {
cifs_dbg(VFS, "illegal ntlmssp phase\n");
rc = -EIO;
@@ -652,8 +689,6 @@
cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
1 /* pad */ - 4 /* rfc1001 len */);
req->SecurityBufferLength = cpu_to_le16(blob_length);
- iov[1].iov_base = security_blob;
- iov[1].iov_len = blob_length;
inc_rfc1001_len(req, blob_length - 1 /* pad */);
@@ -664,6 +699,7 @@
kfree(security_blob);
rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
+ ses->Suid = rsp->hdr.SessionId;
if (resp_buftype != CIFS_NO_BUFFER &&
rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
if (phase != NtLmNegotiate) {
@@ -681,7 +717,6 @@
/* NTLMSSP Negotiate sent now processing challenge (response) */
phase = NtLmChallenge; /* process ntlmssp challenge */
rc = 0; /* MORE_PROCESSING is not an error here but expected */
- ses->Suid = rsp->hdr.SessionId;
rc = decode_ntlmssp_challenge(rsp->Buffer,
le16_to_cpu(rsp->SecurityBufferLength), ses);
}
@@ -738,6 +773,10 @@
kfree(ses->auth_key.response);
ses->auth_key.response = NULL;
}
+ if (spnego_key) {
+ key_invalidate(spnego_key);
+ key_put(spnego_key);
+ }
kfree(ses->ntlmssp);
return rc;
@@ -815,9 +854,6 @@
else
return -EIO;
- if (tcon && tcon->bad_network_name)
- return -ENOENT;
-
unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL);
if (unc_path == NULL)
return -ENOMEM;
@@ -829,6 +865,10 @@
return -EINVAL;
}
+ /* SMB2 TREE_CONNECT request must be called with TreeId == 0 */
+ if (tcon)
+ tcon->tid = 0;
+
rc = small_smb2_init(SMB2_TREE_CONNECT, tcon, (void **) &req);
if (rc) {
kfree(unc_path);
@@ -907,8 +947,6 @@
tcon_error_exit:
if (rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) {
cifs_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree);
- if (tcon)
- tcon->bad_network_name = true;
}
goto tcon_exit;
}
@@ -1573,6 +1611,54 @@
add_credits(server, credits_received, CIFS_ECHO_OP);
}
+void smb2_reconnect_server(struct work_struct *work)
+{
+ struct TCP_Server_Info *server = container_of(work,
+ struct TCP_Server_Info, reconnect.work);
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon, *tcon2;
+ struct list_head tmp_list;
+ int tcon_exist = false;
+
+ /* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
+ mutex_lock(&server->reconnect_mutex);
+
+ INIT_LIST_HEAD(&tmp_list);
+ cifs_dbg(FYI, "Need negotiate, reconnecting tcons\n");
+
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ if (tcon->need_reconnect) {
+ tcon->tc_count++;
+ list_add_tail(&tcon->rlist, &tmp_list);
+ tcon_exist = true;
+ }
+ }
+ }
+ /*
+ * Get the reference to server struct to be sure that the last call of
+ * cifs_put_tcon() in the loop below won't release the server pointer.
+ */
+ if (tcon_exist)
+ server->srv_count++;
+
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
+ smb2_reconnect(SMB2_ECHO, tcon);
+ list_del_init(&tcon->rlist);
+ cifs_put_tcon(tcon);
+ }
+
+ cifs_dbg(FYI, "Reconnecting tcons finished\n");
+ mutex_unlock(&server->reconnect_mutex);
+
+ /* now we can safely release srv struct */
+ if (tcon_exist)
+ cifs_put_tcp_session(server, 1);
+}
+
int
SMB2_echo(struct TCP_Server_Info *server)
{
@@ -1584,6 +1670,12 @@
cifs_dbg(FYI, "In echo request\n");
+ if (server->tcpStatus == CifsNeedNegotiate) {
+ /* No need to send echo on newly established connections */
+ queue_delayed_work(cifsiod_wq, &server->reconnect, 0);
+ return rc;
+ }
+
rc = small_smb2_init(SMB2_ECHO, NULL, (void **)&req);
if (rc)
return rc;
@@ -2336,6 +2428,22 @@
}
int
+SMB2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid)
+{
+ __u8 delete_pending = 1;
+ void *data;
+ unsigned int size;
+
+ data = &delete_pending;
+ size = 1; /* sizeof __u8 */
+
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, FILE_DISPOSITION_INFORMATION, 1, &data,
+ &size);
+}
+
+int
SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, __le16 *target_file)
{
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 79dc650..adc5234 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -47,6 +47,10 @@
struct smb_rqst *rqst);
extern struct mid_q_entry *smb2_setup_async_request(
struct TCP_Server_Info *server, struct smb_rqst *rqst);
+extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
+ __u64 ses_id);
+extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server,
+ __u64 ses_id, __u32 tid);
extern int smb2_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server);
extern int smb3_calc_signature(struct smb_rqst *rqst,
@@ -95,6 +99,7 @@
extern int smb2_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid);
extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
+extern void smb2_reconnect_server(struct work_struct *work);
/*
* SMB2 Worker functions - most of protocol specific implementation details
@@ -140,6 +145,8 @@
extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
__le16 *target_file);
+extern int SMB2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid);
extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid,
__le16 *target_file);
@@ -154,6 +161,9 @@
extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
const u64 persistent_fid, const u64 volatile_fid,
const __u8 oplock_level);
+extern int smb2_handle_cancelled_mid(char *buffer,
+ struct TCP_Server_Info *server);
+void smb2_cancelled_close_fid(struct work_struct *work);
extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id,
struct kstatfs *FSData);
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index 5111e72..0767a1c 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -115,22 +115,68 @@
}
static struct cifs_ses *
-smb2_find_smb_ses(struct smb2_hdr *smb2hdr, struct TCP_Server_Info *server)
+smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
{
struct cifs_ses *ses;
- spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
- if (ses->Suid != smb2hdr->SessionId)
+ if (ses->Suid != ses_id)
continue;
- spin_unlock(&cifs_tcp_ses_lock);
return ses;
}
- spin_unlock(&cifs_tcp_ses_lock);
return NULL;
}
+struct cifs_ses *
+smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id)
+{
+ struct cifs_ses *ses;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ ses = smb2_find_smb_ses_unlocked(server, ses_id);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ return ses;
+}
+
+static struct cifs_tcon *
+smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid)
+{
+ struct cifs_tcon *tcon;
+
+ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+ if (tcon->tid != tid)
+ continue;
+ ++tcon->tc_count;
+ return tcon;
+ }
+
+ return NULL;
+}
+
+/*
+ * Obtain tcon corresponding to the tid in the given
+ * cifs_ses
+ */
+
+struct cifs_tcon *
+smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid)
+{
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+
+ spin_lock(&cifs_tcp_ses_lock);
+ ses = smb2_find_smb_ses_unlocked(server, ses_id);
+ if (!ses) {
+ spin_unlock(&cifs_tcp_ses_lock);
+ return NULL;
+ }
+ tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid);
+ spin_unlock(&cifs_tcp_ses_lock);
+
+ return tcon;
+}
int
smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
@@ -143,7 +189,7 @@
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
struct cifs_ses *ses;
- ses = smb2_find_smb_ses(smb2_pdu, server);
+ ses = smb2_find_smb_ses(server, smb2_pdu->SessionId);
if (!ses) {
cifs_dbg(VFS, "%s: Could not find session\n", __func__);
return 0;
@@ -314,7 +360,7 @@
struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
struct cifs_ses *ses;
- ses = smb2_find_smb_ses(smb2_pdu, server);
+ ses = smb2_find_smb_ses(server, smb2_pdu->SessionId);
if (!ses) {
cifs_dbg(VFS, "%s: Could not find session\n", __func__);
return 0;
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 66106f6..27dfb1e 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -784,9 +784,11 @@
rc = wait_for_response(ses->server, midQ);
if (rc != 0) {
+ cifs_dbg(FYI, "Cancelling wait for mid %llu\n", midQ->mid);
send_cancel(ses->server, buf, midQ);
spin_lock(&GlobalMid_Lock);
if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
+ midQ->mid_flags |= MID_WAIT_CANCELLED;
midQ->callback = DeleteMidQEntry;
spin_unlock(&GlobalMid_Lock);
cifs_small_buf_release(buf);
diff --git a/fs/coredump.c b/fs/coredump.c
index 7eb6181..bb4a7cc 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -1,6 +1,7 @@
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/fdtable.h>
+#include <linux/freezer.h>
#include <linux/mm.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
@@ -393,7 +394,9 @@
if (core_waiters > 0) {
struct core_thread *ptr;
+ freezer_do_not_count();
wait_for_completion(&core_state->startup);
+ freezer_count();
/*
* Wait for all the threads to become inactive, so that
* all the thread context (extended register state, like
@@ -707,7 +710,7 @@
goto close_fail;
if (!(cprm.file->f_mode & FMODE_CAN_WRITE))
goto close_fail;
- if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file))
+ if (do_truncate2(cprm.file->f_path.mnt, cprm.file->f_path.dentry, 0, 0, cprm.file))
goto close_fail;
}
diff --git a/fs/dcache.c b/fs/dcache.c
index 05b75fa..97136d3 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -510,7 +510,7 @@
* dentry_iput drops the locks, at which point nobody (except
* transient RCU lookups) can reach this dentry.
*/
- BUG_ON((int)dentry->d_lockref.count > 0);
+ BUG_ON(dentry->d_lockref.count > 0);
this_cpu_dec(nr_dentry);
if (dentry->d_op && dentry->d_op->d_release)
dentry->d_op->d_release(dentry);
@@ -554,7 +554,6 @@
failed:
spin_unlock(&dentry->d_lock);
- cpu_relax();
return dentry; /* try again with same dentry */
}
@@ -563,7 +562,7 @@
struct dentry *parent = dentry->d_parent;
if (IS_ROOT(dentry))
return NULL;
- if (unlikely((int)dentry->d_lockref.count < 0))
+ if (unlikely(dentry->d_lockref.count < 0))
return NULL;
if (likely(spin_trylock(&parent->d_lock)))
return parent;
@@ -592,6 +591,110 @@
return parent;
}
+/*
+ * Try to do a lockless dput(), and return whether that was successful.
+ *
+ * If unsuccessful, we return false, having already taken the dentry lock.
+ *
+ * The caller needs to hold the RCU read lock, so that the dentry is
+ * guaranteed to stay around even if the refcount goes down to zero!
+ */
+static inline bool fast_dput(struct dentry *dentry)
+{
+ int ret;
+ unsigned int d_flags;
+
+ /*
+ * If we have a d_op->d_delete() operation, we sould not
+ * let the dentry count go to zero, so use "put__or_lock".
+ */
+ if (unlikely(dentry->d_flags & DCACHE_OP_DELETE))
+ return lockref_put_or_lock(&dentry->d_lockref);
+
+ /*
+ * .. otherwise, we can try to just decrement the
+ * lockref optimistically.
+ */
+ ret = lockref_put_return(&dentry->d_lockref);
+
+ /*
+ * If the lockref_put_return() failed due to the lock being held
+ * by somebody else, the fast path has failed. We will need to
+ * get the lock, and then check the count again.
+ */
+ if (unlikely(ret < 0)) {
+ spin_lock(&dentry->d_lock);
+ if (dentry->d_lockref.count > 1) {
+ dentry->d_lockref.count--;
+ spin_unlock(&dentry->d_lock);
+ return 1;
+ }
+ return 0;
+ }
+
+ /*
+ * If we weren't the last ref, we're done.
+ */
+ if (ret)
+ return 1;
+
+ /*
+ * Careful, careful. The reference count went down
+ * to zero, but we don't hold the dentry lock, so
+ * somebody else could get it again, and do another
+ * dput(), and we need to not race with that.
+ *
+ * However, there is a very special and common case
+ * where we don't care, because there is nothing to
+ * do: the dentry is still hashed, it does not have
+ * a 'delete' op, and it's referenced and already on
+ * the LRU list.
+ *
+ * NOTE! Since we aren't locked, these values are
+ * not "stable". However, it is sufficient that at
+ * some point after we dropped the reference the
+ * dentry was hashed and the flags had the proper
+ * value. Other dentry users may have re-gotten
+ * a reference to the dentry and change that, but
+ * our work is done - we can leave the dentry
+ * around with a zero refcount.
+ */
+ smp_rmb();
+ d_flags = ACCESS_ONCE(dentry->d_flags);
+ d_flags &= DCACHE_REFERENCED | DCACHE_LRU_LIST;
+
+ /* Nothing to do? Dropping the reference was all we needed? */
+ if (d_flags == (DCACHE_REFERENCED | DCACHE_LRU_LIST) && !d_unhashed(dentry))
+ return 1;
+
+ /*
+ * Not the fast normal case? Get the lock. We've already decremented
+ * the refcount, but we'll need to re-check the situation after
+ * getting the lock.
+ */
+ spin_lock(&dentry->d_lock);
+
+ /*
+ * Did somebody else grab a reference to it in the meantime, and
+ * we're no longer the last user after all? Alternatively, somebody
+ * else could have killed it and marked it dead. Either way, we
+ * don't need to do anything else.
+ */
+ if (dentry->d_lockref.count) {
+ spin_unlock(&dentry->d_lock);
+ return 1;
+ }
+
+ /*
+ * Re-get the reference we optimistically dropped. We hold the
+ * lock, and we just tested that it was zero, so we can just
+ * set it to 1.
+ */
+ dentry->d_lockref.count = 1;
+ return 0;
+}
+
+
/*
* This is dput
*
@@ -624,8 +727,16 @@
return;
repeat:
- if (lockref_put_or_lock(&dentry->d_lockref))
+ might_sleep();
+
+ rcu_read_lock();
+ if (likely(fast_dput(dentry))) {
+ rcu_read_unlock();
return;
+ }
+
+ /* Slow case: now with the dentry lock held */
+ rcu_read_unlock();
/* Unreachable? Get rid of it */
if (unlikely(d_unhashed(dentry)))
@@ -649,8 +760,10 @@
kill_it:
dentry = dentry_kill(dentry);
- if (dentry)
+ if (dentry) {
+ cond_resched();
goto repeat;
+ }
}
EXPORT_SYMBOL(dput);
@@ -815,7 +928,7 @@
* We found an inuse dentry which was not removed from
* the LRU because of laziness during lookup. Do not free it.
*/
- if ((int)dentry->d_lockref.count > 0) {
+ if (dentry->d_lockref.count > 0) {
spin_unlock(&dentry->d_lock);
if (parent)
spin_unlock(&parent->d_lock);
@@ -1486,7 +1599,7 @@
struct dentry *dentry = __d_alloc(parent->d_sb, name);
if (!dentry)
return NULL;
-
+ dentry->d_flags |= DCACHE_RCUACCESS;
spin_lock(&parent->d_lock);
/*
* don't need child lock because it is not subject
@@ -2318,7 +2431,6 @@
{
BUG_ON(!d_unhashed(entry));
hlist_bl_lock(b);
- entry->d_flags |= DCACHE_RCUACCESS;
hlist_bl_add_head_rcu(&entry->d_hash, b);
hlist_bl_unlock(b);
}
@@ -2537,6 +2649,7 @@
/* ... and switch them in the tree */
if (IS_ROOT(dentry)) {
/* splicing a tree */
+ dentry->d_flags |= DCACHE_RCUACCESS;
dentry->d_parent = target->d_parent;
target->d_parent = target;
list_del_init(&target->d_child);
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 6f0ce53..960f294 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -34,93 +34,16 @@
static int debugfs_mount_count;
static bool debugfs_registered;
-static struct inode *debugfs_get_inode(struct super_block *sb, umode_t mode, dev_t dev,
- void *data, const struct file_operations *fops)
-
+static struct inode *debugfs_get_inode(struct super_block *sb)
{
struct inode *inode = new_inode(sb);
-
if (inode) {
inode->i_ino = get_next_ino();
- inode->i_mode = mode;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- switch (mode & S_IFMT) {
- default:
- init_special_inode(inode, mode, dev);
- break;
- case S_IFREG:
- inode->i_fop = fops ? fops : &debugfs_file_operations;
- inode->i_private = data;
- break;
- case S_IFLNK:
- inode->i_op = &debugfs_link_operations;
- inode->i_private = data;
- break;
- case S_IFDIR:
- inode->i_op = &simple_dir_inode_operations;
- inode->i_fop = &simple_dir_operations;
-
- /* directory inodes start off with i_nlink == 2
- * (for "." entry) */
- inc_nlink(inode);
- break;
- }
}
return inode;
}
-/* SMP-safe */
-static int debugfs_mknod(struct inode *dir, struct dentry *dentry,
- umode_t mode, dev_t dev, void *data,
- const struct file_operations *fops)
-{
- struct inode *inode;
- int error = -EPERM;
-
- if (dentry->d_inode)
- return -EEXIST;
-
- inode = debugfs_get_inode(dir->i_sb, mode, dev, data, fops);
- if (inode) {
- d_instantiate(dentry, inode);
- dget(dentry);
- error = 0;
- }
- return error;
-}
-
-static int debugfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
-{
- int res;
-
- mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
- res = debugfs_mknod(dir, dentry, mode, 0, NULL, NULL);
- if (!res) {
- inc_nlink(dir);
- fsnotify_mkdir(dir, dentry);
- }
- return res;
-}
-
-static int debugfs_link(struct inode *dir, struct dentry *dentry, umode_t mode,
- void *data)
-{
- mode = (mode & S_IALLUGO) | S_IFLNK;
- return debugfs_mknod(dir, dentry, mode, 0, data, NULL);
-}
-
-static int debugfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
- void *data, const struct file_operations *fops)
-{
- int res;
-
- mode = (mode & S_IALLUGO) | S_IFREG;
- res = debugfs_mknod(dir, dentry, mode, 0, data, fops);
- if (!res)
- fsnotify_create(dir, dentry);
- return res;
-}
-
static inline int debugfs_positive(struct dentry *dentry)
{
return dentry->d_inode && !d_unhashed(dentry);
@@ -261,6 +184,18 @@
.evict_inode = debugfs_evict_inode,
};
+static struct vfsmount *debugfs_automount(struct path *path)
+{
+ struct vfsmount *(*f)(void *);
+ f = (struct vfsmount *(*)(void *))path->dentry->d_fsdata;
+ return f(path->dentry->d_inode->i_private);
+}
+
+static const struct dentry_operations debugfs_dops = {
+ .d_delete = always_delete_dentry,
+ .d_automount = debugfs_automount,
+};
+
static int debug_fill_super(struct super_block *sb, void *data, int silent)
{
static struct tree_descr debug_files[] = {{""}};
@@ -285,6 +220,7 @@
goto fail;
sb->s_op = &debugfs_super_operations;
+ sb->s_d_op = &debugfs_dops;
debugfs_apply_options(sb);
@@ -311,11 +247,9 @@
};
MODULE_ALIAS_FS("debugfs");
-static struct dentry *__create_file(const char *name, umode_t mode,
- struct dentry *parent, void *data,
- const struct file_operations *fops)
+static struct dentry *start_creating(const char *name, struct dentry *parent)
{
- struct dentry *dentry = NULL;
+ struct dentry *dentry;
int error;
pr_debug("debugfs: creating file '%s'\n",name);
@@ -323,7 +257,7 @@
error = simple_pin_fs(&debug_fs_type, &debugfs_mount,
&debugfs_mount_count);
if (error)
- goto exit;
+ return ERR_PTR(error);
/* If the parent is not specified, we create it in the root.
* We need the root dentry to do this, which is in the super
@@ -335,31 +269,26 @@
mutex_lock(&parent->d_inode->i_mutex);
dentry = lookup_one_len(name, parent, strlen(name));
- if (!IS_ERR(dentry)) {
- switch (mode & S_IFMT) {
- case S_IFDIR:
- error = debugfs_mkdir(parent->d_inode, dentry, mode);
-
- break;
- case S_IFLNK:
- error = debugfs_link(parent->d_inode, dentry, mode,
- data);
- break;
- default:
- error = debugfs_create(parent->d_inode, dentry, mode,
- data, fops);
- break;
- }
+ if (!IS_ERR(dentry) && dentry->d_inode) {
dput(dentry);
- } else
- error = PTR_ERR(dentry);
- mutex_unlock(&parent->d_inode->i_mutex);
-
- if (error) {
- dentry = NULL;
- simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+ dentry = ERR_PTR(-EEXIST);
}
-exit:
+ if (IS_ERR(dentry))
+ mutex_unlock(&parent->d_inode->i_mutex);
+ return dentry;
+}
+
+static struct dentry *failed_creating(struct dentry *dentry)
+{
+ mutex_unlock(&dentry->d_parent->d_inode->i_mutex);
+ dput(dentry);
+ simple_release_fs(&debugfs_mount, &debugfs_mount_count);
+ return NULL;
+}
+
+static struct dentry *end_creating(struct dentry *dentry)
+{
+ mutex_unlock(&dentry->d_parent->d_inode->i_mutex);
return dentry;
}
@@ -393,15 +322,27 @@
struct dentry *parent, void *data,
const struct file_operations *fops)
{
- switch (mode & S_IFMT) {
- case S_IFREG:
- case 0:
- break;
- default:
- BUG();
- }
+ struct dentry *dentry;
+ struct inode *inode;
- return __create_file(name, mode, parent, data, fops);
+ if (!(mode & S_IFMT))
+ mode |= S_IFREG;
+ BUG_ON(!S_ISREG(mode));
+ dentry = start_creating(name, parent);
+
+ if (IS_ERR(dentry))
+ return NULL;
+
+ inode = debugfs_get_inode(dentry->d_sb);
+ if (unlikely(!inode))
+ return failed_creating(dentry);
+
+ inode->i_mode = mode;
+ inode->i_fop = fops ? fops : &debugfs_file_operations;
+ inode->i_private = data;
+ d_instantiate(dentry, inode);
+ fsnotify_create(dentry->d_parent->d_inode, dentry);
+ return end_creating(dentry);
}
EXPORT_SYMBOL_GPL(debugfs_create_file);
@@ -425,12 +366,65 @@
*/
struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
{
- return __create_file(name, S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
- parent, NULL, NULL);
+ struct dentry *dentry = start_creating(name, parent);
+ struct inode *inode;
+
+ if (IS_ERR(dentry))
+ return NULL;
+
+ inode = debugfs_get_inode(dentry->d_sb);
+ if (unlikely(!inode))
+ return failed_creating(dentry);
+
+ inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inc_nlink(inode);
+ d_instantiate(dentry, inode);
+ inc_nlink(dentry->d_parent->d_inode);
+ fsnotify_mkdir(dentry->d_parent->d_inode, dentry);
+ return end_creating(dentry);
}
EXPORT_SYMBOL_GPL(debugfs_create_dir);
/**
+ * debugfs_create_automount - create automount point in the debugfs filesystem
+ * @name: a pointer to a string containing the name of the file to create.
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is NULL, then the
+ * file will be created in the root of the debugfs filesystem.
+ * @f: function to be called when pathname resolution steps on that one.
+ * @data: opaque argument to pass to f().
+ *
+ * @f should return what ->d_automount() would.
+ */
+struct dentry *debugfs_create_automount(const char *name,
+ struct dentry *parent,
+ struct vfsmount *(*f)(void *),
+ void *data)
+{
+ struct dentry *dentry = start_creating(name, parent);
+ struct inode *inode;
+
+ if (IS_ERR(dentry))
+ return NULL;
+
+ inode = debugfs_get_inode(dentry->d_sb);
+ if (unlikely(!inode))
+ return failed_creating(dentry);
+
+ inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+ inode->i_flags |= S_AUTOMOUNT;
+ inode->i_private = data;
+ dentry->d_fsdata = (void *)f;
+ d_instantiate(dentry, inode);
+ return end_creating(dentry);
+}
+EXPORT_SYMBOL(debugfs_create_automount);
+
+/**
* debugfs_create_symlink- create a symbolic link in the debugfs filesystem
* @name: a pointer to a string containing the name of the symbolic link to
* create.
@@ -456,17 +450,28 @@
struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
const char *target)
{
- struct dentry *result;
- char *link;
-
- link = kstrdup(target, GFP_KERNEL);
+ struct dentry *dentry;
+ struct inode *inode;
+ char *link = kstrdup(target, GFP_KERNEL);
if (!link)
return NULL;
- result = __create_file(name, S_IFLNK | S_IRWXUGO, parent, link, NULL);
- if (!result)
+ dentry = start_creating(name, parent);
+ if (IS_ERR(dentry)) {
kfree(link);
- return result;
+ return NULL;
+ }
+
+ inode = debugfs_get_inode(dentry->d_sb);
+ if (unlikely(!inode)) {
+ kfree(link);
+ return failed_creating(dentry);
+ }
+ inode->i_mode = S_IFLNK | S_IRWXUGO;
+ inode->i_op = &debugfs_link_operations;
+ inode->i_private = link;
+ d_instantiate(dentry, inode);
+ return end_creating(dentry);
}
EXPORT_SYMBOL_GPL(debugfs_create_symlink);
diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c
index d68eb1b..e83d404 100644
--- a/fs/ecryptfs/file.c
+++ b/fs/ecryptfs/file.c
@@ -178,6 +178,19 @@
return rc;
}
+static int ecryptfs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct file *lower_file = ecryptfs_file_to_lower(file);
+ /*
+ * Don't allow mmap on top of file systems that don't support it
+ * natively. If FILESYSTEM_MAX_STACK_DEPTH > 2 or ecryptfs
+ * allows recursive mounting, this will need to be extended.
+ */
+ if (!lower_file->f_op->mmap)
+ return -ENODEV;
+ return generic_file_mmap(file, vma);
+}
+
/**
* ecryptfs_open
* @inode: inode speciying file to open
@@ -398,7 +411,7 @@
#ifdef CONFIG_COMPAT
.compat_ioctl = ecryptfs_compat_ioctl,
#endif
- .mmap = generic_file_mmap,
+ .mmap = ecryptfs_mmap,
.open = ecryptfs_open,
.flush = ecryptfs_flush,
.release = ecryptfs_release,
diff --git a/fs/exec.c b/fs/exec.c
index b7a5f46..569a6f2 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -19,7 +19,7 @@
* current->executable is only used by the procfs. This allows a dispatch
* table to check for several different types of binary formats. We keep
* trying until we recognize the file or we run out of supported binary
- * formats.
+ * formats.
*/
#include <linux/slab.h>
@@ -1083,6 +1083,13 @@
flush_thread();
current->personality &= ~bprm->per_clear;
+ /*
+ * We have to apply CLOEXEC before we change whether the process is
+ * dumpable (in setup_new_exec) to avoid a race with a process in userspace
+ * trying to access the should-be-closed file descriptors of a process
+ * undergoing exec(2).
+ */
+ do_close_on_exec(current->files);
return 0;
out:
@@ -1092,7 +1099,7 @@
void would_dump(struct linux_binprm *bprm, struct file *file)
{
- if (inode_permission(file_inode(file), MAY_READ) < 0)
+ if (inode_permission2(file->f_path.mnt, file_inode(file), MAY_READ) < 0)
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
}
EXPORT_SYMBOL(would_dump);
@@ -1132,7 +1139,6 @@
group */
current->self_exec_id++;
flush_signal_handlers(current, 0);
- do_close_on_exec(current->files);
}
EXPORT_SYMBOL(setup_new_exec);
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index 83a6f49..cb38608 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -209,6 +209,9 @@
memset(bh->b_data, 0, sb->s_blocksize);
bit_max = ext4_num_base_meta_clusters(sb, block_group);
+ if ((bit_max >> 3) >= bh->b_size)
+ return -EIO;
+
for (bit = 0; bit < bit_max; bit++)
ext4_set_bit(bit, bh->b_data);
diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
index 2d9ab13..7af26a9 100644
--- a/fs/ext4/crypto.c
+++ b/fs/ext4/crypto.c
@@ -34,6 +34,7 @@
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <linux/spinlock_types.h>
+#include <linux/namei.h>
#include "ext4_extents.h"
#include "xattr.h"
@@ -486,6 +487,9 @@
struct ext4_crypt_info *ci;
int dir_has_key, cached_with_key;
+ if (flags & LOOKUP_RCU)
+ return -ECHILD;
+
dir = dget_parent(dentry);
if (!ext4_encrypted_inode(d_inode(dir))) {
dput(dir);
diff --git a/fs/ext4/crypto_fname.c b/fs/ext4/crypto_fname.c
index 09bee35..919a2c7 100644
--- a/fs/ext4/crypto_fname.c
+++ b/fs/ext4/crypto_fname.c
@@ -344,7 +344,7 @@
memcpy(buf+4, &hinfo->minor_hash, 4);
} else
memset(buf, 0, 8);
- memcpy(buf + 8, iname->name + iname->len - 16, 16);
+ memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16);
oname->name[0] = '_';
ret = digest_encode(buf, 24, oname->name+1);
oname->len = ret + 1;
diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
index ad05069..e4f4fc4 100644
--- a/fs/ext4/crypto_policy.c
+++ b/fs/ext4/crypto_policy.c
@@ -102,6 +102,9 @@
int ext4_process_policy(const struct ext4_encryption_policy *policy,
struct inode *inode)
{
+ if (!inode_owner_or_capable(inode))
+ return -EACCES;
+
if (policy->version != 0)
return -EINVAL;
@@ -145,20 +148,38 @@
int ext4_is_child_context_consistent_with_parent(struct inode *parent,
struct inode *child)
{
- struct ext4_crypt_info *parent_ci, *child_ci;
+ const struct ext4_crypt_info *parent_ci, *child_ci;
+ struct ext4_encryption_context parent_ctx, child_ctx;
int res;
- if ((parent == NULL) || (child == NULL)) {
- pr_err("parent %p child %p\n", parent, child);
- WARN_ON(1); /* Should never happen */
- return 0;
- }
- /* no restrictions if the parent directory is not encrypted */
+ /* No restrictions on file types which are never encrypted */
+ if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) &&
+ !S_ISLNK(child->i_mode))
+ return 1;
+
+ /* No restrictions if the parent directory is unencrypted */
if (!ext4_encrypted_inode(parent))
return 1;
- /* if the child directory is not encrypted, this is always a problem */
+
+ /* Encrypted directories must not contain unencrypted files */
if (!ext4_encrypted_inode(child))
return 0;
+
+ /*
+ * Both parent and child are encrypted, so verify they use the same
+ * encryption policy. Compare the fscrypt_info structs if the keys are
+ * available, otherwise retrieve and compare the fscrypt_contexts.
+ *
+ * Note that the fscrypt_context retrieval will be required frequently
+ * when accessing an encrypted directory tree without the key.
+ * Performance-wise this is not a big deal because we already don't
+ * really optimize for file access without the key (to the extent that
+ * such access is even possible), given that any attempted access
+ * already causes a fscrypt_context retrieval and keyring search.
+ *
+ * In any case, if an unexpected error occurs, fall back to "forbidden".
+ */
+
res = ext4_get_encryption_info(parent);
if (res)
return 0;
@@ -167,17 +188,35 @@
return 0;
parent_ci = EXT4_I(parent)->i_crypt_info;
child_ci = EXT4_I(child)->i_crypt_info;
- if (!parent_ci && !child_ci)
- return 1;
- if (!parent_ci || !child_ci)
+ if (parent_ci && child_ci) {
+ return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key,
+ EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
+ (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
+ (parent_ci->ci_filename_mode ==
+ child_ci->ci_filename_mode) &&
+ (parent_ci->ci_flags == child_ci->ci_flags);
+ }
+
+ res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
+ EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+ &parent_ctx, sizeof(parent_ctx));
+ if (res != sizeof(parent_ctx))
return 0;
- return (memcmp(parent_ci->ci_master_key,
- child_ci->ci_master_key,
- EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
- (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
- (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
- (parent_ci->ci_flags == child_ci->ci_flags));
+ res = ext4_xattr_get(child, EXT4_XATTR_INDEX_ENCRYPTION,
+ EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+ &child_ctx, sizeof(child_ctx));
+ if (res != sizeof(child_ctx))
+ return 0;
+
+ return memcmp(parent_ctx.master_key_descriptor,
+ child_ctx.master_key_descriptor,
+ EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
+ (parent_ctx.contents_encryption_mode ==
+ child_ctx.contents_encryption_mode) &&
+ (parent_ctx.filenames_encryption_mode ==
+ child_ctx.filenames_encryption_mode) &&
+ (parent_ctx.flags == child_ctx.flags);
}
/**
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 46fe950..89bd9704 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -234,6 +234,7 @@
#define EXT4_MAX_BLOCK_SIZE 65536
#define EXT4_MIN_BLOCK_LOG_SIZE 10
#define EXT4_MAX_BLOCK_LOG_SIZE 16
+#define EXT4_MAX_CLUSTER_LOG_SIZE 30
#ifdef __KERNEL__
# define EXT4_BLOCK_SIZE(s) ((s)->s_blocksize)
#else
@@ -826,6 +827,29 @@
#include "extents_status.h"
/*
+ * Lock subclasses for i_data_sem in the ext4_inode_info structure.
+ *
+ * These are needed to avoid lockdep false positives when we need to
+ * allocate blocks to the quota inode during ext4_map_blocks(), while
+ * holding i_data_sem for a normal (non-quota) inode. Since we don't
+ * do quota tracking for the quota inode, this avoids deadlock (as
+ * well as infinite recursion, since it isn't turtles all the way
+ * down...)
+ *
+ * I_DATA_SEM_NORMAL - Used for most inodes
+ * I_DATA_SEM_OTHER - Used by move_inode.c for the second normal inode
+ * where the second inode has larger inode number
+ * than the first
+ * I_DATA_SEM_QUOTA - Used for quota inodes only
+ */
+enum {
+ I_DATA_SEM_NORMAL = 0,
+ I_DATA_SEM_OTHER,
+ I_DATA_SEM_QUOTA,
+};
+
+
+/*
* fourth extended file system inode data in memory
*/
struct ext4_inode_info {
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index c3d25b2..193687f 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -375,9 +375,13 @@
ext4_fsblk_t block = ext4_ext_pblock(ext);
int len = ext4_ext_get_actual_len(ext);
ext4_lblk_t lblock = le32_to_cpu(ext->ee_block);
- ext4_lblk_t last = lblock + len - 1;
- if (len == 0 || lblock > last)
+ /*
+ * We allow neither:
+ * - zero length
+ * - overflow/wrap-around
+ */
+ if (lblock + len <= lblock)
return 0;
return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len);
}
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 25fb2a5..e7f6b35 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -1051,6 +1051,17 @@
if (err)
goto fail_drop;
+ /*
+ * Since the encryption xattr will always be unique, create it first so
+ * that it's less likely to end up in an external xattr block and
+ * prevent its deduplication.
+ */
+ if (encrypt) {
+ err = ext4_inherit_context(dir, inode);
+ if (err)
+ goto fail_free_drop;
+ }
+
err = ext4_init_acl(handle, inode, dir);
if (err)
goto fail_free_drop;
@@ -1072,12 +1083,6 @@
ei->i_datasync_tid = handle->h_transaction->t_tid;
}
- if (encrypt) {
- err = ext4_inherit_context(dir, inode);
- if (err)
- goto fail_free_drop;
- }
-
err = ext4_mark_inode_dirty(handle, inode);
if (err) {
ext4_std_error(sb, err);
@@ -1108,22 +1113,20 @@
unsigned long max_ino = le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count);
ext4_group_t block_group;
int bit;
- struct buffer_head *bitmap_bh;
+ struct buffer_head *bitmap_bh = NULL;
struct inode *inode = NULL;
- long err = -EIO;
+ int err = -EIO;
- /* Error cases - e2fsck has already cleaned up for us */
- if (ino > max_ino) {
- ext4_warning(sb, "bad orphan ino %lu! e2fsck was run?", ino);
- goto error;
- }
+ if (ino < EXT4_FIRST_INO(sb) || ino > max_ino)
+ goto bad_orphan;
block_group = (ino - 1) / EXT4_INODES_PER_GROUP(sb);
bit = (ino - 1) % EXT4_INODES_PER_GROUP(sb);
bitmap_bh = ext4_read_inode_bitmap(sb, block_group);
if (!bitmap_bh) {
- ext4_warning(sb, "inode bitmap error for orphan %lu", ino);
- goto error;
+ ext4_error(sb, "inode bitmap error %ld for orphan %lu",
+ ino, PTR_ERR(bitmap_bh));
+ return (struct inode *) bitmap_bh;
}
/* Having the inode bit set should be a 100% indicator that this
@@ -1134,15 +1137,21 @@
goto bad_orphan;
inode = ext4_iget(sb, ino);
- if (IS_ERR(inode))
- goto iget_failed;
+ if (IS_ERR(inode)) {
+ err = PTR_ERR(inode);
+ ext4_error(sb, "couldn't read orphan inode %lu (err %d)",
+ ino, err);
+ return inode;
+ }
/*
- * If the orphans has i_nlinks > 0 then it should be able to be
- * truncated, otherwise it won't be removed from the orphan list
- * during processing and an infinite loop will result.
+ * If the orphans has i_nlinks > 0 then it should be able to
+ * be truncated, otherwise it won't be removed from the orphan
+ * list during processing and an infinite loop will result.
+ * Similarly, it must not be a bad inode.
*/
- if (inode->i_nlink && !ext4_can_truncate(inode))
+ if ((inode->i_nlink && !ext4_can_truncate(inode)) ||
+ is_bad_inode(inode))
goto bad_orphan;
if (NEXT_ORPHAN(inode) > max_ino)
@@ -1150,29 +1159,25 @@
brelse(bitmap_bh);
return inode;
-iget_failed:
- err = PTR_ERR(inode);
- inode = NULL;
bad_orphan:
- ext4_warning(sb, "bad orphan inode %lu! e2fsck was run?", ino);
- printk(KERN_WARNING "ext4_test_bit(bit=%d, block=%llu) = %d\n",
- bit, (unsigned long long)bitmap_bh->b_blocknr,
- ext4_test_bit(bit, bitmap_bh->b_data));
- printk(KERN_WARNING "inode=%p\n", inode);
+ ext4_error(sb, "bad orphan inode %lu", ino);
+ if (bitmap_bh)
+ printk(KERN_ERR "ext4_test_bit(bit=%d, block=%llu) = %d\n",
+ bit, (unsigned long long)bitmap_bh->b_blocknr,
+ ext4_test_bit(bit, bitmap_bh->b_data));
if (inode) {
- printk(KERN_WARNING "is_bad_inode(inode)=%d\n",
+ printk(KERN_ERR "is_bad_inode(inode)=%d\n",
is_bad_inode(inode));
- printk(KERN_WARNING "NEXT_ORPHAN(inode)=%u\n",
+ printk(KERN_ERR "NEXT_ORPHAN(inode)=%u\n",
NEXT_ORPHAN(inode));
- printk(KERN_WARNING "max_ino=%lu\n", max_ino);
- printk(KERN_WARNING "i_nlink=%u\n", inode->i_nlink);
+ printk(KERN_ERR "max_ino=%lu\n", max_ino);
+ printk(KERN_ERR "i_nlink=%u\n", inode->i_nlink);
/* Avoid freeing blocks if we got a bad deleted inode */
if (inode->i_nlink == 0)
inode->i_blocks = 0;
iput(inode);
}
brelse(bitmap_bh);
-error:
return ERR_PTR(err);
}
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 674a2f2..9e570f0 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -336,8 +336,10 @@
len -= EXT4_MIN_INLINE_DATA_SIZE;
value = kzalloc(len, GFP_NOFS);
- if (!value)
+ if (!value) {
+ error = -ENOMEM;
goto out;
+ }
error = ext4_xattr_ibody_get(inode, i.name_index, i.name,
value, len);
@@ -1145,10 +1147,9 @@
set_buffer_uptodate(dir_block);
err = ext4_handle_dirty_dirent_node(handle, inode, dir_block);
if (err)
- goto out;
+ return err;
set_buffer_verified(dir_block);
-out:
- return err;
+ return ext4_mark_inode_dirty(handle, inode);
}
static int ext4_convert_inline_data_nolock(handle_t *handle,
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index fe9fbdc..8f74acc 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -207,9 +207,9 @@
* Note that directories do not have this problem because they
* don't use page cache.
*/
- if (ext4_should_journal_data(inode) &&
- (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode)) &&
- inode->i_ino != EXT4_JOURNAL_INO) {
+ if (inode->i_ino != EXT4_JOURNAL_INO &&
+ ext4_should_journal_data(inode) &&
+ (S_ISLNK(inode->i_mode) || S_ISREG(inode->i_mode))) {
journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
tid_t commit_tid = EXT4_I(inode)->i_datasync_tid;
@@ -2546,13 +2546,36 @@
done = true;
}
}
- ext4_journal_stop(handle);
+ /*
+ * Caution: If the handle is synchronous,
+ * ext4_journal_stop() can wait for transaction commit
+ * to finish which may depend on writeback of pages to
+ * complete or on page lock to be released. In that
+ * case, we have to wait until after after we have
+ * submitted all the IO, released page locks we hold,
+ * and dropped io_end reference (for extent conversion
+ * to be able to complete) before stopping the handle.
+ */
+ if (!ext4_handle_valid(handle) || handle->h_sync == 0) {
+ ext4_journal_stop(handle);
+ handle = NULL;
+ }
/* Submit prepared bio */
ext4_io_submit(&mpd.io_submit);
/* Unlock pages we didn't use */
mpage_release_unused_pages(&mpd, give_up_on_write);
- /* Drop our io_end reference we got from init */
- ext4_put_io_end(mpd.io_submit.io_end);
+ /*
+ * Drop our io_end reference we got from init. We have
+ * to be careful and use deferred io_end finishing if
+ * we are still holding the transaction as we can
+ * release the last reference to io_end which may end
+ * up doing unwritten extent conversion.
+ */
+ if (handle) {
+ ext4_put_io_end_defer(mpd.io_submit.io_end);
+ ext4_journal_stop(handle);
+ } else
+ ext4_put_io_end(mpd.io_submit.io_end);
if (ret == -ENOSPC && sbi->s_journal) {
/*
@@ -4984,6 +5007,8 @@
might_sleep();
trace_ext4_mark_inode_dirty(inode, _RET_IP_);
err = ext4_reserve_inode_write(handle, inode, &iloc);
+ if (err)
+ return err;
if (ext4_handle_valid(handle) &&
EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize &&
!ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND)) {
@@ -5014,9 +5039,7 @@
}
}
}
- if (!err)
- err = ext4_mark_iloc_dirty(handle, inode, &iloc);
- return err;
+ return ext4_mark_iloc_dirty(handle, inode, &iloc);
}
/*
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index 21d077c..0030151 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -644,7 +644,17 @@
goto encryption_policy_out;
}
+ err = mnt_want_write_file(filp);
+ if (err)
+ goto encryption_policy_out;
+
+ mutex_lock(&inode->i_mutex);
+
err = ext4_process_policy(&policy, inode);
+
+ mutex_unlock(&inode->i_mutex);
+
+ mnt_drop_write_file(filp);
encryption_policy_out:
return err;
#else
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 07b13c1..2309de2 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -668,7 +668,7 @@
ext4_grpblk_t min;
ext4_grpblk_t max;
ext4_grpblk_t chunk;
- unsigned short border;
+ unsigned int border;
BUG_ON(len > EXT4_CLUSTERS_PER_GROUP(sb));
@@ -1248,6 +1248,7 @@
static int mb_find_order_for_block(struct ext4_buddy *e4b, int block)
{
int order = 1;
+ int bb_incr = 1 << (e4b->bd_blkbits - 1);
void *bb;
BUG_ON(e4b->bd_bitmap == e4b->bd_buddy);
@@ -1260,7 +1261,8 @@
/* this block is part of buddy of order 'order' */
return order;
}
- bb += 1 << (e4b->bd_blkbits - order);
+ bb += bb_incr;
+ bb_incr >>= 1;
order++;
}
return 0;
@@ -2252,7 +2254,7 @@
struct ext4_group_info *grinfo;
struct sg {
struct ext4_group_info info;
- ext4_grpblk_t counters[16];
+ ext4_grpblk_t counters[EXT4_MAX_BLOCK_LOG_SIZE + 2];
} sg;
group--;
@@ -2553,7 +2555,7 @@
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
unsigned i, j;
- unsigned offset;
+ unsigned offset, offset_incr;
unsigned max;
int ret;
@@ -2582,11 +2584,13 @@
i = 1;
offset = 0;
+ offset_incr = 1 << (sb->s_blocksize_bits - 1);
max = sb->s_blocksize << 2;
do {
sbi->s_mb_offsets[i] = offset;
sbi->s_mb_maxs[i] = max;
- offset += 1 << (sb->s_blocksize_bits - i);
+ offset += offset_incr;
+ offset_incr = offset_incr >> 1;
max = max >> 1;
i++;
} while (i <= sb->s_blocksize_bits + 1);
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index f498c34..36e383e 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -60,10 +60,10 @@
{
if (first < second) {
down_write(&EXT4_I(first)->i_data_sem);
- down_write_nested(&EXT4_I(second)->i_data_sem, SINGLE_DEPTH_NESTING);
+ down_write_nested(&EXT4_I(second)->i_data_sem, I_DATA_SEM_OTHER);
} else {
down_write(&EXT4_I(second)->i_data_sem);
- down_write_nested(&EXT4_I(first)->i_data_sem, SINGLE_DEPTH_NESTING);
+ down_write_nested(&EXT4_I(first)->i_data_sem, I_DATA_SEM_OTHER);
}
}
@@ -491,6 +491,13 @@
return -EBUSY;
}
+ if (IS_NOQUOTA(orig_inode) || IS_NOQUOTA(donor_inode)) {
+ ext4_debug("ext4 move extent: The argument files should "
+ "not be quota files [ino:orig %lu, donor %lu]\n",
+ orig_inode->i_ino, donor_inode->i_ino);
+ return -EBUSY;
+ }
+
/* Ext4 move extent supports only extent based file */
if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS))) {
ext4_debug("ext4 move extent: orig file is not extents "
@@ -594,6 +601,14 @@
ext4_should_journal_data(donor_inode)) {
return -EINVAL;
}
+
+ if (ext4_encrypted_inode(orig_inode) ||
+ ext4_encrypted_inode(donor_inode)) {
+ ext4_msg(orig_inode->i_sb, KERN_ERR,
+ "Online defrag not supported for encrypted files");
+ return -EOPNOTSUPP;
+ }
+
/* Protect orig and donor inodes against a truncate */
lock_two_nondirectories(orig_inode, donor_inode);
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 858b097..51512b8 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1233,9 +1233,9 @@
if (unlikely(!name)) {
if (fname->usr_fname->name[0] == '_') {
int ret;
- if (de->name_len < 16)
+ if (de->name_len <= 32)
return 0;
- ret = memcmp(de->name + de->name_len - 16,
+ ret = memcmp(de->name + ((de->name_len - 17) & ~15),
fname->crypto_buf.name + 8, 16);
return (ret == 0) ? 1 : 0;
}
@@ -2812,7 +2812,7 @@
* list entries can cause panics at unmount time.
*/
mutex_lock(&sbi->s_orphan_lock);
- list_del(&EXT4_I(inode)->i_orphan);
+ list_del_init(&EXT4_I(inode)->i_orphan);
mutex_unlock(&sbi->s_orphan_lock);
}
}
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index e199bc2..a102b34 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -501,14 +501,16 @@
gfp_t gfp_flags = GFP_NOFS;
retry_encrypt:
- if (ext4_using_hardware_encryption(inode))
+ if (ext4_using_hardware_encryption(inode)) {
io->io_crypt_inode = inode;
- else
+ } else {
+ io->io_crypt_inode = NULL;
data_page = ext4_encrypt(inode, page, gfp_flags);
+ }
if (IS_ERR(data_page)) {
ret = PTR_ERR(data_page);
- if (ret == ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
+ if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
if (io->io_bio) {
ext4_io_submit(io);
congestion_wait(BLK_RW_ASYNC, HZ/50);
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index c2fb648..8841f25 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1290,9 +1290,9 @@
return -1;
}
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA)) {
- ext4_msg(sb, KERN_ERR, "Cannot set journaled quota options "
- "when QUOTA feature is enabled");
- return -1;
+ ext4_msg(sb, KERN_INFO, "Journaled quota options "
+ "ignored when QUOTA feature is enabled");
+ return 1;
}
qname = match_strdup(args);
if (!qname) {
@@ -1641,10 +1641,10 @@
}
if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
EXT4_FEATURE_RO_COMPAT_QUOTA)) {
- ext4_msg(sb, KERN_ERR,
- "Cannot set journaled quota options "
+ ext4_msg(sb, KERN_INFO,
+ "Quota format mount options ignored "
"when QUOTA feature is enabled");
- return -1;
+ return 1;
}
sbi->s_jquota_fmt = m->mount_opt;
#endif
@@ -1696,11 +1696,11 @@
#ifdef CONFIG_QUOTA
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) &&
(test_opt(sb, USRQUOTA) || test_opt(sb, GRPQUOTA))) {
- ext4_msg(sb, KERN_ERR, "Cannot set quota options when QUOTA "
- "feature is enabled");
- return 0;
- }
- if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
+ ext4_msg(sb, KERN_INFO, "Quota feature enabled, usrquota and grpquota "
+ "mount options ignored.");
+ clear_opt(sb, USRQUOTA);
+ clear_opt(sb, GRPQUOTA);
+ } else if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
clear_opt(sb, USRQUOTA);
@@ -2076,6 +2076,7 @@
/* Called at mount-time, super-block is locked */
static int ext4_check_descriptors(struct super_block *sb,
+ ext4_fsblk_t sb_block,
ext4_group_t *first_not_zeroed)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
@@ -2106,6 +2107,11 @@
grp = i;
block_bitmap = ext4_block_bitmap(sb, gdp);
+ if (block_bitmap == sb_block) {
+ ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
+ "Block bitmap for group %u overlaps "
+ "superblock", i);
+ }
if (block_bitmap < first_block || block_bitmap > last_block) {
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
"Block bitmap for group %u not in group "
@@ -2113,6 +2119,11 @@
return 0;
}
inode_bitmap = ext4_inode_bitmap(sb, gdp);
+ if (inode_bitmap == sb_block) {
+ ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
+ "Inode bitmap for group %u overlaps "
+ "superblock", i);
+ }
if (inode_bitmap < first_block || inode_bitmap > last_block) {
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
"Inode bitmap for group %u not in group "
@@ -2120,6 +2131,11 @@
return 0;
}
inode_table = ext4_inode_table(sb, gdp);
+ if (inode_table == sb_block) {
+ ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
+ "Inode table for group %u overlaps "
+ "superblock", i);
+ }
if (inode_table < first_block ||
inode_table + sbi->s_itb_per_group - 1 > last_block) {
ext4_msg(sb, KERN_ERR, "ext4_check_descriptors: "
@@ -2223,6 +2239,16 @@
while (es->s_last_orphan) {
struct inode *inode;
+ /*
+ * We may have encountered an error during cleanup; if
+ * so, skip the rest.
+ */
+ if (EXT4_SB(sb)->s_mount_state & EXT4_ERROR_FS) {
+ jbd_debug(1, "Skipping orphan recovery on fs with errors.\n");
+ es->s_last_orphan = 0;
+ break;
+ }
+
inode = ext4_orphan_get(sb, le32_to_cpu(es->s_last_orphan));
if (IS_ERR(inode)) {
es->s_last_orphan = 0;
@@ -3320,10 +3346,15 @@
ext4_set_bit(s++, buf);
count++;
}
- for (j = ext4_bg_num_gdb(sb, grp); j > 0; j--) {
- ext4_set_bit(EXT4_B2C(sbi, s++), buf);
- count++;
+ j = ext4_bg_num_gdb(sb, grp);
+ if (s + j > EXT4_BLOCKS_PER_GROUP(sb)) {
+ ext4_error(sb, "Invalid number of block group "
+ "descriptor blocks: %d", j);
+ j = EXT4_BLOCKS_PER_GROUP(sb) - s;
}
+ count += j;
+ for (; j > 0; j--)
+ ext4_set_bit(EXT4_B2C(sbi, s++), buf);
}
if (!count)
return 0;
@@ -3685,7 +3716,15 @@
if (blocksize < EXT4_MIN_BLOCK_SIZE ||
blocksize > EXT4_MAX_BLOCK_SIZE) {
ext4_msg(sb, KERN_ERR,
- "Unsupported filesystem blocksize %d", blocksize);
+ "Unsupported filesystem blocksize %d (%d log_block_size)",
+ blocksize, le32_to_cpu(es->s_log_block_size));
+ goto failed_mount;
+ }
+ if (le32_to_cpu(es->s_log_block_size) >
+ (EXT4_MAX_BLOCK_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
+ ext4_msg(sb, KERN_ERR,
+ "Invalid log block size: %u",
+ le32_to_cpu(es->s_log_block_size));
goto failed_mount;
}
@@ -3696,6 +3735,13 @@
goto failed_mount;
}
+ if (le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) > (blocksize / 4)) {
+ ext4_msg(sb, KERN_ERR,
+ "Number of reserved GDT blocks insanely large: %d",
+ le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks));
+ goto failed_mount;
+ }
+
if (sb->s_blocksize != blocksize) {
/* Validate the filesystem blocksize */
if (!sb_set_blocksize(sb, blocksize)) {
@@ -3761,12 +3807,16 @@
sbi->s_blocks_per_group = le32_to_cpu(es->s_blocks_per_group);
sbi->s_inodes_per_group = le32_to_cpu(es->s_inodes_per_group);
- if (EXT4_INODE_SIZE(sb) == 0 || EXT4_INODES_PER_GROUP(sb) == 0)
- goto cantfind_ext4;
sbi->s_inodes_per_block = blocksize / EXT4_INODE_SIZE(sb);
if (sbi->s_inodes_per_block == 0)
goto cantfind_ext4;
+ if (sbi->s_inodes_per_group < sbi->s_inodes_per_block ||
+ sbi->s_inodes_per_group > blocksize * 8) {
+ ext4_msg(sb, KERN_ERR, "invalid inodes per group: %lu\n",
+ sbi->s_blocks_per_group);
+ goto failed_mount;
+ }
sbi->s_itb_per_group = sbi->s_inodes_per_group /
sbi->s_inodes_per_block;
sbi->s_desc_per_block = blocksize / EXT4_DESC_SIZE(sb);
@@ -3807,6 +3857,13 @@
"block size (%d)", clustersize, blocksize);
goto failed_mount;
}
+ if (le32_to_cpu(es->s_log_cluster_size) >
+ (EXT4_MAX_CLUSTER_LOG_SIZE - EXT4_MIN_BLOCK_LOG_SIZE)) {
+ ext4_msg(sb, KERN_ERR,
+ "Invalid log cluster size: %u",
+ le32_to_cpu(es->s_log_cluster_size));
+ goto failed_mount;
+ }
sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) -
le32_to_cpu(es->s_log_block_size);
sbi->s_clusters_per_group =
@@ -3843,13 +3900,6 @@
}
sbi->s_cluster_ratio = clustersize / blocksize;
- if (sbi->s_inodes_per_group > blocksize * 8) {
- ext4_msg(sb, KERN_ERR,
- "#inodes per group too big: %lu",
- sbi->s_inodes_per_group);
- goto failed_mount;
- }
-
/* Do we have standard group size of clustersize * 8 blocks ? */
if (sbi->s_blocks_per_group == clustersize << 3)
set_opt2(sb, STD_GROUP_SIZE);
@@ -3937,7 +3987,7 @@
goto failed_mount2;
}
}
- if (!ext4_check_descriptors(sb, &first_not_zeroed)) {
+ if (!ext4_check_descriptors(sb, logical_sb_block, &first_not_zeroed)) {
ext4_msg(sb, KERN_ERR, "group descriptors corrupted!");
goto failed_mount2;
}
@@ -5224,6 +5274,20 @@
EXT4_SB(sb)->s_jquota_fmt, type);
}
+static void lockdep_set_quota_inode(struct inode *inode, int subclass)
+{
+ struct ext4_inode_info *ei = EXT4_I(inode);
+
+ /* The first argument of lockdep_set_subclass has to be
+ * *exactly* the same as the argument to init_rwsem() --- in
+ * this case, in init_once() --- or lockdep gets unhappy
+ * because the name of the lock is set using the
+ * stringification of the argument to init_rwsem().
+ */
+ (void) ei; /* shut up clang warning if !CONFIG_LOCKDEP */
+ lockdep_set_subclass(&ei->i_data_sem, subclass);
+}
+
/*
* Standard function to be called on quota_on
*/
@@ -5263,8 +5327,12 @@
if (err)
return err;
}
-
- return dquot_quota_on(sb, type, format_id, path);
+ lockdep_set_quota_inode(path->dentry->d_inode, I_DATA_SEM_QUOTA);
+ err = dquot_quota_on(sb, type, format_id, path);
+ if (err)
+ lockdep_set_quota_inode(path->dentry->d_inode,
+ I_DATA_SEM_NORMAL);
+ return err;
}
static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
@@ -5290,8 +5358,11 @@
/* Don't account quota for quota files to avoid recursion */
qf_inode->i_flags |= S_NOQUOTA;
+ lockdep_set_quota_inode(qf_inode, I_DATA_SEM_QUOTA);
err = dquot_enable(qf_inode, type, format_id, flags);
iput(qf_inode);
+ if (err)
+ lockdep_set_quota_inode(qf_inode, I_DATA_SEM_NORMAL);
return err;
}
diff --git a/fs/f2fs/debug.c b/fs/f2fs/debug.c
index 0a91ab8..8e53d80 100644
--- a/fs/f2fs/debug.c
+++ b/fs/f2fs/debug.c
@@ -295,6 +295,7 @@
}
static const struct file_operations stat_fops = {
+ .owner = THIS_MODULE,
.open = stat_open,
.read = seq_read,
.llseek = seq_lseek,
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index f12b098..e0b83eb 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -772,6 +772,79 @@
return result;
}
+static inline bool sanity_check_area_boundary(struct super_block *sb,
+ struct f2fs_super_block *raw_super)
+{
+ u32 segment0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr);
+ u32 cp_blkaddr = le32_to_cpu(raw_super->cp_blkaddr);
+ u32 sit_blkaddr = le32_to_cpu(raw_super->sit_blkaddr);
+ u32 nat_blkaddr = le32_to_cpu(raw_super->nat_blkaddr);
+ u32 ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr);
+ u32 main_blkaddr = le32_to_cpu(raw_super->main_blkaddr);
+ u32 segment_count_ckpt = le32_to_cpu(raw_super->segment_count_ckpt);
+ u32 segment_count_sit = le32_to_cpu(raw_super->segment_count_sit);
+ u32 segment_count_nat = le32_to_cpu(raw_super->segment_count_nat);
+ u32 segment_count_ssa = le32_to_cpu(raw_super->segment_count_ssa);
+ u32 segment_count_main = le32_to_cpu(raw_super->segment_count_main);
+ u32 segment_count = le32_to_cpu(raw_super->segment_count);
+ u32 log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
+
+ if (segment0_blkaddr != cp_blkaddr) {
+ f2fs_msg(sb, KERN_INFO,
+ "Mismatch start address, segment0(%u) cp_blkaddr(%u)",
+ segment0_blkaddr, cp_blkaddr);
+ return true;
+ }
+
+ if (cp_blkaddr + (segment_count_ckpt << log_blocks_per_seg) !=
+ sit_blkaddr) {
+ f2fs_msg(sb, KERN_INFO,
+ "Wrong CP boundary, start(%u) end(%u) blocks(%u)",
+ cp_blkaddr, sit_blkaddr,
+ segment_count_ckpt << log_blocks_per_seg);
+ return true;
+ }
+
+ if (sit_blkaddr + (segment_count_sit << log_blocks_per_seg) !=
+ nat_blkaddr) {
+ f2fs_msg(sb, KERN_INFO,
+ "Wrong SIT boundary, start(%u) end(%u) blocks(%u)",
+ sit_blkaddr, nat_blkaddr,
+ segment_count_sit << log_blocks_per_seg);
+ return true;
+ }
+
+ if (nat_blkaddr + (segment_count_nat << log_blocks_per_seg) !=
+ ssa_blkaddr) {
+ f2fs_msg(sb, KERN_INFO,
+ "Wrong NAT boundary, start(%u) end(%u) blocks(%u)",
+ nat_blkaddr, ssa_blkaddr,
+ segment_count_nat << log_blocks_per_seg);
+ return true;
+ }
+
+ if (ssa_blkaddr + (segment_count_ssa << log_blocks_per_seg) !=
+ main_blkaddr) {
+ f2fs_msg(sb, KERN_INFO,
+ "Wrong SSA boundary, start(%u) end(%u) blocks(%u)",
+ ssa_blkaddr, main_blkaddr,
+ segment_count_ssa << log_blocks_per_seg);
+ return true;
+ }
+
+ if (main_blkaddr + (segment_count_main << log_blocks_per_seg) !=
+ segment0_blkaddr + (segment_count << log_blocks_per_seg)) {
+ f2fs_msg(sb, KERN_INFO,
+ "Wrong MAIN_AREA boundary, start(%u) end(%u) blocks(%u)",
+ main_blkaddr,
+ segment0_blkaddr + (segment_count << log_blocks_per_seg),
+ segment_count_main << log_blocks_per_seg);
+ return true;
+ }
+
+ return false;
+}
+
static int sanity_check_raw_super(struct super_block *sb,
struct f2fs_super_block *raw_super)
{
@@ -801,6 +874,14 @@
return 1;
}
+ /* check log blocks per segment */
+ if (le32_to_cpu(raw_super->log_blocks_per_seg) != 9) {
+ f2fs_msg(sb, KERN_INFO,
+ "Invalid log blocks per segment (%u)\n",
+ le32_to_cpu(raw_super->log_blocks_per_seg));
+ return 1;
+ }
+
/* Currently, support 512/1024/2048/4096 bytes sector size */
if (le32_to_cpu(raw_super->log_sectorsize) >
F2FS_MAX_LOG_SECTOR_SIZE ||
@@ -827,6 +908,22 @@
return 1;
}
+ /* check reserved ino info */
+ if (le32_to_cpu(raw_super->node_ino) != 1 ||
+ le32_to_cpu(raw_super->meta_ino) != 2 ||
+ le32_to_cpu(raw_super->root_ino) != 3) {
+ f2fs_msg(sb, KERN_INFO,
+ "Invalid Fs Meta Ino: node(%u) meta(%u) root(%u)",
+ le32_to_cpu(raw_super->node_ino),
+ le32_to_cpu(raw_super->meta_ino),
+ le32_to_cpu(raw_super->root_ino));
+ return 1;
+ }
+
+ /* check CP/SIT/NAT/SSA/MAIN_AREA area boundary */
+ if (sanity_check_area_boundary(sb, raw_super))
+ return 1;
+
return 0;
}
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 756aead..75b5a15 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -1266,6 +1266,16 @@
return 0;
}
+static void fat_dummy_inode_init(struct inode *inode)
+{
+ /* Initialize this dummy inode to work as no-op. */
+ MSDOS_I(inode)->mmu_private = 0;
+ MSDOS_I(inode)->i_start = 0;
+ MSDOS_I(inode)->i_logstart = 0;
+ MSDOS_I(inode)->i_attrs = 0;
+ MSDOS_I(inode)->i_pos = 0;
+}
+
static int fat_read_root(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
@@ -1711,12 +1721,13 @@
fat_inode = new_inode(sb);
if (!fat_inode)
goto out_fail;
- MSDOS_I(fat_inode)->i_pos = 0;
+ fat_dummy_inode_init(fat_inode);
sbi->fat_inode = fat_inode;
fsinfo_inode = new_inode(sb);
if (!fsinfo_inode)
goto out_fail;
+ fat_dummy_inode_init(fsinfo_inode);
fsinfo_inode->i_ino = MSDOS_FSINFO_INO;
sbi->fsinfo_inode = fsinfo_inode;
insert_inode_hash(fsinfo_inode);
diff --git a/fs/fs_struct.c b/fs/fs_struct.c
index 7dca743..005dcb4 100644
--- a/fs/fs_struct.c
+++ b/fs/fs_struct.c
@@ -127,6 +127,7 @@
}
return fs;
}
+EXPORT_SYMBOL_GPL(copy_fs_struct);
int unshare_fs_struct(void)
{
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 2470213..4cc8971 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1741,9 +1741,10 @@
* vmtruncate() doesn't allow for this case, so do the rlimit checking
* and the actual truncation by hand.
*/
-int fuse_do_setattr(struct inode *inode, struct iattr *attr,
+int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
struct file *file)
{
+ struct inode *inode = d_inode(dentry);
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_req *req;
@@ -1863,9 +1864,9 @@
return -EACCES;
if (attr->ia_valid & ATTR_FILE)
- return fuse_do_setattr(inode, attr, attr->ia_file);
+ return fuse_do_setattr(entry, attr, attr->ia_file);
else
- return fuse_do_setattr(inode, attr, NULL);
+ return fuse_do_setattr(entry, attr, NULL);
}
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 3ffa163..064c647 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -467,6 +467,15 @@
fuse_sync_writes(inode);
mutex_unlock(&inode->i_mutex);
+ if (test_bit(AS_ENOSPC, &file->f_mapping->flags) &&
+ test_and_clear_bit(AS_ENOSPC, &file->f_mapping->flags))
+ err = -ENOSPC;
+ if (test_bit(AS_EIO, &file->f_mapping->flags) &&
+ test_and_clear_bit(AS_EIO, &file->f_mapping->flags))
+ err = -EIO;
+ if (err)
+ return err;
+
req = fuse_get_req_nofail_nopages(fc, file);
memset(&inarg, 0, sizeof(inarg));
inarg.fh = ff->fh;
@@ -512,6 +521,21 @@
goto out;
fuse_sync_writes(inode);
+
+ /*
+ * Due to implementation of fuse writeback
+ * filemap_write_and_wait_range() does not catch errors.
+ * We have to do this directly after fuse_sync_writes()
+ */
+ if (test_bit(AS_ENOSPC, &file->f_mapping->flags) &&
+ test_and_clear_bit(AS_ENOSPC, &file->f_mapping->flags))
+ err = -ENOSPC;
+ if (test_bit(AS_EIO, &file->f_mapping->flags) &&
+ test_and_clear_bit(AS_EIO, &file->f_mapping->flags))
+ err = -EIO;
+ if (err)
+ goto out;
+
err = sync_inode_metadata(inode, 1);
if (err)
goto out;
@@ -2926,7 +2950,7 @@
attr.ia_file = file;
attr.ia_valid |= ATTR_FILE;
- fuse_do_setattr(inode, &attr, file);
+ fuse_do_setattr(file->f_path.dentry, &attr, file);
}
static inline loff_t fuse_round_up(loff_t off)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 4305111..b6ff103 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -907,7 +907,7 @@
int fuse_flush_times(struct inode *inode, struct fuse_file *ff);
int fuse_write_inode(struct inode *inode, struct writeback_control *wbc);
-int fuse_do_setattr(struct inode *inode, struct iattr *attr,
+int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
struct file *file);
#endif /* _FS_FUSE_I_H */
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 03ac368..d84be5a 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -939,7 +939,7 @@
arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |
FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
- FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
+ FUSE_FLOCK_LOCKS | FUSE_HAS_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO | FUSE_ASYNC_DIO |
FUSE_WRITEBACK_CACHE | FUSE_NO_OPEN_SUPPORT;
req->in.h.opcode = FUSE_INIT;
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 5d4261ff5..f3508f4 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -749,12 +749,15 @@
u64 *leaf_out)
{
__be64 *hash;
+ int error;
hash = gfs2_dir_get_hash_table(dip);
- if (IS_ERR(hash))
- return PTR_ERR(hash);
- *leaf_out = be64_to_cpu(*(hash + index));
- return 0;
+ error = PTR_ERR_OR_ZERO(hash);
+
+ if (!error)
+ *leaf_out = be64_to_cpu(*(hash + index));
+
+ return error;
}
static int get_first_leaf(struct gfs2_inode *dip, u32 index,
diff --git a/fs/inode.c b/fs/inode.c
index 56d1d2b..da55347 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1598,7 +1598,7 @@
}
EXPORT_SYMBOL(should_remove_suid);
-static int __remove_suid(struct dentry *dentry, int kill)
+static int __remove_suid(struct vfsmount *mnt, struct dentry *dentry, int kill)
{
struct iattr newattrs;
@@ -1607,7 +1607,7 @@
* Note we call this on write, so notify_change will not
* encounter any conflicting delegations:
*/
- return notify_change(dentry, &newattrs, NULL);
+ return notify_change2(mnt, dentry, &newattrs, NULL);
}
int file_remove_suid(struct file *file)
@@ -1630,7 +1630,7 @@
if (killpriv)
error = security_inode_killpriv(dentry);
if (!error && killsuid)
- error = __remove_suid(dentry, killsuid);
+ error = __remove_suid(file->f_path.mnt, dentry, killsuid);
if (!error)
inode_has_no_xattr(inode);
diff --git a/fs/internal.h b/fs/internal.h
index ff683fc..7644ead 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -83,9 +83,11 @@
* super.c
*/
extern int do_remount_sb(struct super_block *, int, void *, int);
+extern int do_remount_sb2(struct vfsmount *, struct super_block *, int,
+ void *, int);
extern bool grab_super_passive(struct super_block *sb);
extern struct dentry *mount_fs(struct file_system_type *,
- int, const char *, void *);
+ int, const char *, struct vfsmount *, void *);
extern struct super_block *user_get_super(dev_t);
/*
diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c
index d67a16f..350f67f 100644
--- a/fs/isofs/inode.c
+++ b/fs/isofs/inode.c
@@ -690,6 +690,11 @@
pri_bh = NULL;
root_found:
+ /* We don't support read-write mounts */
+ if (!(s->s_flags & MS_RDONLY)) {
+ error = -EACCES;
+ goto out_freebh;
+ }
if (joliet_level && (pri == NULL || !opt.rock)) {
/* This is the case of Joliet with the norock mount flag.
@@ -1503,9 +1508,6 @@
static struct dentry *isofs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
- /* We don't support read-write mounts */
- if (!(flags & MS_RDONLY))
- return ERR_PTR(-EACCES);
return mount_bdev(fs_type, flags, dev_name, data, isofs_fill_super);
}
diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c
index 735d752..204659a 100644
--- a/fs/isofs/rock.c
+++ b/fs/isofs/rock.c
@@ -203,6 +203,8 @@
int retnamlen = 0;
int truncate = 0;
int ret = 0;
+ char *p;
+ int len;
if (!ISOFS_SB(inode->i_sb)->s_rock)
return 0;
@@ -267,12 +269,17 @@
rr->u.NM.flags);
break;
}
- if ((strlen(retname) + rr->len - 5) >= 254) {
+ len = rr->len - 5;
+ if (retnamlen + len >= 254) {
truncate = 1;
break;
}
- strncat(retname, rr->u.NM.name, rr->len - 5);
- retnamlen += rr->len - 5;
+ p = memchr(rr->u.NM.name, '\0', len);
+ if (unlikely(p))
+ len = p - rr->u.NM.name;
+ memcpy(retname + retnamlen, rr->u.NM.name, len);
+ retnamlen += len;
+ retname[retnamlen] = '\0';
break;
case SIG('R', 'E'):
kfree(rs.buffer);
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index ff2f2e6..2abbb2b 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -1087,6 +1087,7 @@
JBUFFER_TRACE(jh, "file as BJ_Reserved");
spin_lock(&journal->j_list_lock);
__jbd2_journal_file_buffer(jh, transaction, BJ_Reserved);
+ spin_unlock(&journal->j_list_lock);
} else if (jh->b_transaction == journal->j_committing_transaction) {
/* first access by this transaction */
jh->b_modified = 0;
@@ -1094,8 +1095,8 @@
JBUFFER_TRACE(jh, "set next transaction");
spin_lock(&journal->j_list_lock);
jh->b_next_transaction = transaction;
+ spin_unlock(&journal->j_list_lock);
}
- spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh);
/*
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index 4429d6d..efb1f7b 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -828,21 +828,35 @@
mutex_lock(&kernfs_mutex);
list_for_each_entry(info, &kernfs_root(kn)->supers, node) {
+ struct kernfs_node *parent;
struct inode *inode;
- struct dentry *dentry;
+ /*
+ * We want fsnotify_modify() on @kn but as the
+ * modifications aren't originating from userland don't
+ * have the matching @file available. Look up the inodes
+ * and generate the events manually.
+ */
inode = ilookup(info->sb, kn->ino);
if (!inode)
continue;
- dentry = d_find_any_alias(inode);
- if (dentry) {
- fsnotify_parent(NULL, dentry, FS_MODIFY);
- fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE,
- NULL, 0);
- dput(dentry);
+ parent = kernfs_get_parent(kn);
+ if (parent) {
+ struct inode *p_inode;
+
+ p_inode = ilookup(info->sb, parent->ino);
+ if (p_inode) {
+ fsnotify(p_inode, FS_MODIFY | FS_EVENT_ON_CHILD,
+ inode, FSNOTIFY_EVENT_INODE, kn->name, 0);
+ iput(p_inode);
+ }
+
+ kernfs_put(parent);
}
+ fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE,
+ kn->name, 0);
iput(inode);
}
diff --git a/fs/locks.c b/fs/locks.c
index 298d1f5..253ca5c 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -1580,7 +1580,7 @@
{
struct file_lock *fl, **before, **my_before = NULL, *lease;
struct dentry *dentry = filp->f_path.dentry;
- struct inode *inode = dentry->d_inode;
+ struct inode *inode = file_inode(filp);
bool is_deleg = (*flp)->fl_flags & FL_DELEG;
int error;
LIST_HEAD(dispose);
diff --git a/fs/namei.c b/fs/namei.c
index 46e02f4..46fe2c3 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -368,9 +368,11 @@
* flag in inode->i_opflags, that says "this has not special
* permission function, use the fast case".
*/
-static inline int do_inode_permission(struct inode *inode, int mask)
+static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask)
{
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
+ if (likely(mnt && inode->i_op->permission2))
+ return inode->i_op->permission2(mnt, inode, mask);
if (likely(inode->i_op->permission))
return inode->i_op->permission(inode, mask);
@@ -394,7 +396,7 @@
* This does not check for a read-only file system. You probably want
* inode_permission().
*/
-int __inode_permission(struct inode *inode, int mask)
+int __inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
{
int retval;
@@ -406,7 +408,7 @@
return -EACCES;
}
- retval = do_inode_permission(inode, mask);
+ retval = do_inode_permission(mnt, inode, mask);
if (retval)
return retval;
@@ -414,7 +416,14 @@
if (retval)
return retval;
- return security_inode_permission(inode, mask);
+ retval = security_inode_permission(inode, mask);
+ return retval;
+}
+EXPORT_SYMBOL(__inode_permission2);
+
+int __inode_permission(struct inode *inode, int mask)
+{
+ return __inode_permission2(NULL, inode, mask);
}
EXPORT_SYMBOL(__inode_permission);
@@ -450,14 +459,20 @@
*
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
*/
-int inode_permission(struct inode *inode, int mask)
+int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
{
int retval;
retval = sb_permission(inode->i_sb, inode, mask);
if (retval)
return retval;
- return __inode_permission(inode, mask);
+ return __inode_permission2(mnt, inode, mask);
+}
+EXPORT_SYMBOL(inode_permission2);
+
+int inode_permission(struct inode *inode, int mask)
+{
+ return inode_permission2(NULL, inode, mask);
}
EXPORT_SYMBOL(inode_permission);
@@ -1512,13 +1527,13 @@
static inline int may_lookup(struct nameidata *nd)
{
if (nd->flags & LOOKUP_RCU) {
- int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
+ int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
if (err != -ECHILD)
return err;
if (unlazy_walk(nd, NULL))
return -ECHILD;
}
- return inode_permission(nd->inode, MAY_EXEC);
+ return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC);
}
static inline int handle_dots(struct nameidata *nd, int type)
@@ -1856,11 +1871,12 @@
nd->depth = 0;
if (flags & LOOKUP_ROOT) {
struct dentry *root = nd->root.dentry;
+ struct vfsmount *mnt = nd->root.mnt;
struct inode *inode = root->d_inode;
if (*name) {
if (!d_can_lookup(root))
return -ENOTDIR;
- retval = inode_permission(inode, MAY_EXEC);
+ retval = inode_permission2(mnt, inode, MAY_EXEC);
if (retval)
return retval;
}
@@ -2114,6 +2130,7 @@
/**
* lookup_one_len - filesystem helper to lookup single pathname component
* @name: pathname component to lookup
+ * @mnt: mount we are looking up on
* @base: base directory to lookup from
* @len: maximum length @len should be interpreted to
*
@@ -2122,7 +2139,7 @@
* nameidata argument is passed to the filesystem methods and a filesystem
* using this helper needs to be prepared for that.
*/
-struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len)
{
struct qstr this;
unsigned int c;
@@ -2156,12 +2173,18 @@
return ERR_PTR(err);
}
- err = inode_permission(base->d_inode, MAY_EXEC);
+ err = inode_permission2(mnt, base->d_inode, MAY_EXEC);
if (err)
return ERR_PTR(err);
return __lookup_hash(&this, base, 0);
}
+EXPORT_SYMBOL(lookup_one_len2);
+
+struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+{
+ return lookup_one_len2(name, NULL, base, len);
+}
EXPORT_SYMBOL(lookup_one_len);
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
@@ -2441,7 +2464,7 @@
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
-static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
+static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *victim, bool isdir)
{
struct inode *inode = victim->d_inode;
int error;
@@ -2453,7 +2476,7 @@
BUG_ON(victim->d_parent->d_inode != dir);
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
- error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
if (error)
return error;
if (IS_APPEND(dir))
@@ -2484,14 +2507,14 @@
* 3. We should have write and exec permissions on dir
* 4. We can't do it if dir is immutable (done in permission())
*/
-static inline int may_create(struct inode *dir, struct dentry *child)
+static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child)
{
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
- return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
}
/*
@@ -2538,10 +2561,10 @@
}
EXPORT_SYMBOL(unlock_rename);
-int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
- bool want_excl)
+int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry,
+ umode_t mode, bool want_excl)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -2563,11 +2586,19 @@
return error;
}
+EXPORT_SYMBOL(vfs_create2);
+
+int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+ bool want_excl)
+{
+ return vfs_create2(NULL, dir, dentry, mode, want_excl);
+}
EXPORT_SYMBOL(vfs_create);
static int may_open(struct path *path, int acc_mode, int flag)
{
struct dentry *dentry = path->dentry;
+ struct vfsmount *mnt = path->mnt;
struct inode *inode = dentry->d_inode;
int error;
@@ -2596,7 +2627,7 @@
break;
}
- error = inode_permission(inode, acc_mode);
+ error = inode_permission2(mnt, inode, acc_mode);
if (error)
return error;
@@ -2631,7 +2662,7 @@
if (!error)
error = security_path_truncate(path);
if (!error) {
- error = do_truncate(path->dentry, 0,
+ error = do_truncate2(path->mnt, path->dentry, 0,
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
filp);
}
@@ -2652,7 +2683,7 @@
if (error)
return error;
- error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
if (error)
return error;
@@ -2803,22 +2834,10 @@
dentry = lookup_real(dir, dentry, nd->flags);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
-
- if (create_error) {
- int open_flag = op->open_flag;
-
- error = create_error;
- if ((open_flag & O_EXCL)) {
- if (!dentry->d_inode)
- goto out;
- } else if (!dentry->d_inode) {
- goto out;
- } else if ((open_flag & O_TRUNC) &&
- S_ISREG(dentry->d_inode->i_mode)) {
- goto out;
- }
- /* will fail later, go on to get the right error */
- }
+ }
+ if (create_error && !dentry->d_inode) {
+ error = create_error;
+ goto out;
}
looked_up:
path->dentry = dentry;
@@ -2850,6 +2869,7 @@
bool got_write, int *opened)
{
struct dentry *dir = nd->path.dentry;
+ struct vfsmount *mnt = nd->path.mnt;
struct inode *dir_inode = dir->d_inode;
struct dentry *dentry;
int error;
@@ -2897,7 +2917,7 @@
error = security_path_mknod(&nd->path, dentry, mode, 0);
if (error)
goto out_dput;
- error = vfs_create(dir->d_inode, dentry, mode,
+ error = vfs_create2(mnt, dir->d_inode, dentry, mode,
nd->flags & LOOKUP_EXCL);
if (error)
goto out_dput;
@@ -3169,7 +3189,7 @@
if (unlikely(error))
goto out;
/* we want directory to be writable */
- error = inode_permission(nd->inode, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(nd->path.mnt, nd->inode, MAY_WRITE | MAY_EXEC);
if (error)
goto out2;
dentry = nd->path.dentry;
@@ -3419,9 +3439,9 @@
}
EXPORT_SYMBOL(user_path_create);
-int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
+int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -3453,6 +3473,12 @@
return error;
}
+EXPORT_SYMBOL(vfs_mknod2);
+
+int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
+{
+ return vfs_mknod2(NULL, dir, dentry, mode, dev);
+}
EXPORT_SYMBOL(vfs_mknod);
static int may_mknod(umode_t mode)
@@ -3495,10 +3521,10 @@
goto out;
switch (mode & S_IFMT) {
case 0: case S_IFREG:
- error = vfs_create(path.dentry->d_inode,dentry,mode,true);
+ error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true);
break;
case S_IFCHR: case S_IFBLK:
- error = vfs_mknod(path.dentry->d_inode,dentry,mode,
+ error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode,
new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
@@ -3519,9 +3545,9 @@
return sys_mknodat(AT_FDCWD, filename, mode, dev);
}
-int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
unsigned max_links = dir->i_sb->s_max_links;
if (error)
@@ -3543,6 +3569,12 @@
fsnotify_mkdir(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_mkdir2);
+
+int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ return vfs_mkdir2(NULL, dir, dentry, mode);
+}
EXPORT_SYMBOL(vfs_mkdir);
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
@@ -3561,7 +3593,7 @@
mode &= ~current_umask();
error = security_path_mkdir(&path, dentry, mode);
if (!error)
- error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
+ error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -3600,9 +3632,9 @@
}
EXPORT_SYMBOL(dentry_unhash);
-int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry)
{
- int error = may_delete(dir, dentry, 1);
+ int error = may_delete(mnt, dir, dentry, 1);
if (error)
return error;
@@ -3637,6 +3669,10 @@
d_delete(dentry);
return error;
}
+int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ return vfs_rmdir2(NULL, dir, dentry);
+}
EXPORT_SYMBOL(vfs_rmdir);
static long do_rmdir(int dfd, const char __user *pathname)
@@ -3680,7 +3716,7 @@
error = security_path_rmdir(&nd.path, dentry);
if (error)
goto exit3;
- error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+ error = vfs_rmdir2(nd.path.mnt, nd.path.dentry->d_inode, dentry);
exit3:
dput(dentry);
exit2:
@@ -3719,10 +3755,10 @@
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported.
*/
-int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
+int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
{
struct inode *target = dentry->d_inode;
- int error = may_delete(dir, dentry, 0);
+ int error = may_delete(mnt, dir, dentry, 0);
if (error)
return error;
@@ -3757,6 +3793,12 @@
return error;
}
+EXPORT_SYMBOL(vfs_unlink2);
+
+int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegated_inode)
+{
+ return vfs_unlink2(NULL, dir, dentry, delegated_inode);
+}
EXPORT_SYMBOL(vfs_unlink);
/*
@@ -3802,7 +3844,7 @@
error = security_path_unlink(&nd.path, dentry);
if (error)
goto exit2;
- error = vfs_unlink(nd.path.dentry->d_inode, dentry, &delegated_inode);
+ error = vfs_unlink2(nd.path.mnt, nd.path.dentry->d_inode, dentry, &delegated_inode);
exit2:
dput(dentry);
}
@@ -3852,9 +3894,9 @@
return do_unlinkat(AT_FDCWD, pathname);
}
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -3871,6 +3913,12 @@
fsnotify_create(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_symlink2);
+
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+{
+ return vfs_symlink2(NULL, dir, dentry, oldname);
+}
EXPORT_SYMBOL(vfs_symlink);
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
@@ -3893,7 +3941,7 @@
error = security_path_symlink(&path, dentry, from->name);
if (!error)
- error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
+ error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -3928,7 +3976,7 @@
* be appropriate for callers that expect the underlying filesystem not
* to be NFS exported.
*/
-int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
+int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
{
struct inode *inode = old_dentry->d_inode;
unsigned max_links = dir->i_sb->s_max_links;
@@ -3937,7 +3985,7 @@
if (!inode)
return -ENOENT;
- error = may_create(dir, new_dentry);
+ error = may_create(mnt, dir, new_dentry);
if (error)
return error;
@@ -3980,6 +4028,12 @@
fsnotify_link(dir, inode, new_dentry);
return error;
}
+EXPORT_SYMBOL(vfs_link2);
+
+int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry, struct inode **delegated_inode)
+{
+ return vfs_link2(NULL, old_dentry, dir, new_dentry, delegated_inode);
+}
EXPORT_SYMBOL(vfs_link);
/*
@@ -4035,7 +4089,7 @@
error = security_path_link(old_path.dentry, &new_path, new_dentry);
if (error)
goto out_dput;
- error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
+ error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
out_dput:
done_path_create(&new_path, new_dentry);
if (delegated_inode) {
@@ -4110,7 +4164,8 @@
* ->i_mutex on parents, which works but leads to some truly excessive
* locking].
*/
-int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+int vfs_rename2(struct vfsmount *mnt,
+ struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
struct inode **delegated_inode, unsigned int flags)
{
@@ -4125,19 +4180,19 @@
if (source == target)
return 0;
- error = may_delete(old_dir, old_dentry, is_dir);
+ error = may_delete(mnt, old_dir, old_dentry, is_dir);
if (error)
return error;
if (!target) {
- error = may_create(new_dir, new_dentry);
+ error = may_create(mnt, new_dir, new_dentry);
} else {
new_is_dir = d_is_dir(new_dentry);
if (!(flags & RENAME_EXCHANGE))
- error = may_delete(new_dir, new_dentry, is_dir);
+ error = may_delete(mnt, new_dir, new_dentry, is_dir);
else
- error = may_delete(new_dir, new_dentry, new_is_dir);
+ error = may_delete(mnt, new_dir, new_dentry, new_is_dir);
}
if (error)
return error;
@@ -4154,12 +4209,12 @@
*/
if (new_dir != old_dir) {
if (is_dir) {
- error = inode_permission(source, MAY_WRITE);
+ error = inode_permission2(mnt, source, MAY_WRITE);
if (error)
return error;
}
if ((flags & RENAME_EXCHANGE) && new_is_dir) {
- error = inode_permission(target, MAY_WRITE);
+ error = inode_permission2(mnt, target, MAY_WRITE);
if (error)
return error;
}
@@ -4242,6 +4297,14 @@
return error;
}
+EXPORT_SYMBOL(vfs_rename2);
+
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ struct inode **delegated_inode, unsigned int flags)
+{
+ return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry, delegated_inode, flags);
+}
EXPORT_SYMBOL(vfs_rename);
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
@@ -4356,7 +4419,7 @@
&newnd.path, new_dentry, flags);
if (error)
goto exit5;
- error = vfs_rename(old_dir->d_inode, old_dentry,
+ error = vfs_rename2(oldnd.path.mnt, old_dir->d_inode, old_dentry,
new_dir->d_inode, new_dentry,
&delegated_inode, flags);
exit5:
@@ -4401,7 +4464,7 @@
int vfs_whiteout(struct inode *dir, struct dentry *dentry)
{
- int error = may_create(dir, dentry);
+ int error = may_create(NULL, dir, dentry);
if (error)
return error;
diff --git a/fs/namespace.c b/fs/namespace.c
index ff5ba8c..c04a68d 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -568,6 +568,7 @@
static void free_vfsmnt(struct mount *mnt)
{
+ kfree(mnt->mnt.data);
kfree(mnt->mnt_devname);
#ifdef CONFIG_SMP
free_percpu(mnt->mnt_pcp);
@@ -901,11 +902,21 @@
if (!mnt)
return ERR_PTR(-ENOMEM);
+ mnt->mnt.data = NULL;
+ if (type->alloc_mnt_data) {
+ mnt->mnt.data = type->alloc_mnt_data();
+ if (!mnt->mnt.data) {
+ mnt_free_id(mnt);
+ free_vfsmnt(mnt);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
if (flags & MS_KERNMOUNT)
mnt->mnt.mnt_flags = MNT_INTERNAL;
- root = mount_fs(type, flags, name, data);
+ root = mount_fs(type, flags, name, &mnt->mnt, data);
if (IS_ERR(root)) {
+ kfree(mnt->mnt.data);
mnt_free_id(mnt);
free_vfsmnt(mnt);
return ERR_CAST(root);
@@ -933,6 +944,14 @@
if (!mnt)
return ERR_PTR(-ENOMEM);
+ if (sb->s_op->clone_mnt_data) {
+ mnt->mnt.data = sb->s_op->clone_mnt_data(old->mnt.data);
+ if (!mnt->mnt.data) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ }
+
if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE))
mnt->mnt_group_id = 0; /* not a peer of original */
else
@@ -1000,6 +1019,7 @@
return mnt;
out_free:
+ kfree(mnt->mnt.data);
mnt_free_id(mnt);
free_vfsmnt(mnt);
return ERR_PTR(err);
@@ -1496,6 +1516,7 @@
goto out_unlock;
lock_mount_hash();
+ event++;
while (!hlist_empty(&mp->m_list)) {
mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
umount_tree(mnt, 0);
@@ -2139,8 +2160,14 @@
err = change_mount_flags(path->mnt, flags);
else if (!capable(CAP_SYS_ADMIN))
err = -EPERM;
- else
- err = do_remount_sb(sb, flags, data, 0);
+ else {
+ err = do_remount_sb2(path->mnt, sb, flags, data, 0);
+ namespace_lock();
+ lock_mount_hash();
+ propagate_remount(mnt);
+ unlock_mount_hash();
+ namespace_unlock();
+ }
if (!err) {
lock_mount_hash();
mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
@@ -2333,8 +2360,10 @@
mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV;
}
if (type->fs_flags & FS_USERNS_VISIBLE) {
- if (!fs_fully_visible(type, &mnt_flags))
+ if (!fs_fully_visible(type, &mnt_flags)) {
+ put_filesystem(type);
return -EPERM;
+ }
}
}
@@ -3175,7 +3204,7 @@
list_for_each_entry(child, &mnt->mnt_mounts, mnt_child) {
struct inode *inode = child->mnt_mountpoint->d_inode;
/* Only worry about locked mounts */
- if (!(mnt->mnt.mnt_flags & MNT_LOCKED))
+ if (!(child->mnt.mnt_flags & MNT_LOCKED))
continue;
if (!S_ISDIR(inode->i_mode))
goto next;
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index 351be920..3b8cdb8 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -303,6 +303,7 @@
err_socks:
svc_rpcb_cleanup(serv, net);
err_bind:
+ nn->cb_users[minorversion]--;
dprintk("NFS: Couldn't create callback socket: err = %d; "
"net = %p\n", ret, net);
return ret;
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 7a8d67c..29b7a75 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1529,9 +1529,9 @@
err = PTR_ERR(inode);
trace_nfs_atomic_open_exit(dir, ctx, open_flags, err);
put_nfs_open_context(ctx);
+ d_drop(dentry);
switch (err) {
case -ENOENT:
- d_drop(dentry);
d_add(dentry, NULL);
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
break;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 2ab6f00..c85da0f 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -410,7 +410,7 @@
*/
if (!PageUptodate(page)) {
unsigned pglen = nfs_page_length(page);
- unsigned end = offset + len;
+ unsigned end = offset + copied;
if (pglen == 0) {
zero_user_segments(page, 0, offset,
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 575fb0e..dbd0100 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2635,12 +2635,11 @@
call_close |= is_wronly;
else if (is_wronly)
calldata->arg.fmode |= FMODE_WRITE;
+ if (calldata->arg.fmode != (FMODE_READ|FMODE_WRITE))
+ call_close |= is_rdwr;
} else if (is_rdwr)
calldata->arg.fmode |= FMODE_READ|FMODE_WRITE;
- if (calldata->arg.fmode == 0)
- call_close |= is_rdwr;
-
if (!nfs4_valid_open_stateid(state))
call_close = 0;
spin_unlock(&state->owner->so_lock);
@@ -3058,16 +3057,13 @@
struct nfs_fsinfo *info,
bool auth_probe)
{
- int status;
+ int status = 0;
- switch (auth_probe) {
- case false:
+ if (!auth_probe)
status = nfs4_lookup_root(server, fhandle, info);
- if (status != -NFS4ERR_WRONGSEC)
- break;
- default:
+
+ if (auth_probe || status == NFS4ERR_WRONGSEC)
status = nfs4_do_find_root_sec(server, fhandle, info);
- }
if (status == 0)
status = nfs4_server_capabilities(server, fhandle);
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 6067f2a..395b93f 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1204,6 +1204,9 @@
dprintk("NFS: nfs_updatepage(%pD2 %d@%lld)\n",
file, count, (long long)(page_file_offset(page) + offset));
+ if (!count)
+ goto out;
+
if (nfs_can_extend_write(file, page, inode)) {
count = max(count + offset, nfs_page_length(page));
offset = 0;
@@ -1214,7 +1217,7 @@
nfs_set_pageerror(page);
else
__set_page_dirty_nobuffers(page);
-
+out:
dprintk("NFS: nfs_updatepage returns %d (isize %lld)\n",
status, (long long)i_size_read(inode));
return status;
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c
index ac54ea6..67bdb78 100644
--- a/fs/nfsd/nfs2acl.c
+++ b/fs/nfsd/nfs2acl.c
@@ -104,22 +104,21 @@
goto out;
inode = fh->fh_dentry->d_inode;
- if (!IS_POSIXACL(inode) || !inode->i_op->set_acl) {
- error = -EOPNOTSUPP;
- goto out_errno;
- }
error = fh_want_write(fh);
if (error)
goto out_errno;
- error = inode->i_op->set_acl(inode, argp->acl_access, ACL_TYPE_ACCESS);
+ fh_lock(fh);
+
+ error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access);
if (error)
- goto out_drop_write;
- error = inode->i_op->set_acl(inode, argp->acl_default,
- ACL_TYPE_DEFAULT);
+ goto out_drop_lock;
+ error = set_posix_acl(inode, ACL_TYPE_DEFAULT, argp->acl_default);
if (error)
- goto out_drop_write;
+ goto out_drop_lock;
+
+ fh_unlock(fh);
fh_drop_write(fh);
@@ -131,7 +130,8 @@
posix_acl_release(argp->acl_access);
posix_acl_release(argp->acl_default);
return nfserr;
-out_drop_write:
+out_drop_lock:
+ fh_unlock(fh);
fh_drop_write(fh);
out_errno:
nfserr = nfserrno(error);
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c
index 34cbbab..78562dd 100644
--- a/fs/nfsd/nfs3acl.c
+++ b/fs/nfsd/nfs3acl.c
@@ -95,22 +95,20 @@
goto out;
inode = fh->fh_dentry->d_inode;
- if (!IS_POSIXACL(inode) || !inode->i_op->set_acl) {
- error = -EOPNOTSUPP;
- goto out_errno;
- }
error = fh_want_write(fh);
if (error)
goto out_errno;
- error = inode->i_op->set_acl(inode, argp->acl_access, ACL_TYPE_ACCESS);
- if (error)
- goto out_drop_write;
- error = inode->i_op->set_acl(inode, argp->acl_default,
- ACL_TYPE_DEFAULT);
+ fh_lock(fh);
-out_drop_write:
+ error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access);
+ if (error)
+ goto out_drop_lock;
+ error = set_posix_acl(inode, ACL_TYPE_DEFAULT, argp->acl_default);
+
+out_drop_lock:
+ fh_unlock(fh);
fh_drop_write(fh);
out_errno:
nfserr = nfserrno(error);
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index 59fd766..df90268 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -830,9 +830,6 @@
dentry = fhp->fh_dentry;
inode = dentry->d_inode;
- if (!inode->i_op->set_acl || !IS_POSIXACL(inode))
- return nfserr_attrnotsupp;
-
if (S_ISDIR(inode->i_mode))
flags = NFS4_ACL_DIR;
@@ -842,16 +839,19 @@
if (host_error < 0)
goto out_nfserr;
- host_error = inode->i_op->set_acl(inode, pacl, ACL_TYPE_ACCESS);
+ fh_lock(fhp);
+
+ host_error = set_posix_acl(inode, ACL_TYPE_ACCESS, pacl);
if (host_error < 0)
- goto out_release;
+ goto out_drop_lock;
if (S_ISDIR(inode->i_mode)) {
- host_error = inode->i_op->set_acl(inode, dpacl,
- ACL_TYPE_DEFAULT);
+ host_error = set_posix_acl(inode, ACL_TYPE_DEFAULT, dpacl);
}
-out_release:
+out_drop_lock:
+ fh_unlock(fhp);
+
posix_acl_release(pacl);
posix_acl_release(dpacl);
out_nfserr:
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 7cbdf1b..9701a76 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -630,22 +630,6 @@
}
}
-static struct rpc_clnt *create_backchannel_client(struct rpc_create_args *args)
-{
- struct rpc_xprt *xprt;
-
- if (args->protocol != XPRT_TRANSPORT_BC_TCP)
- return rpc_create(args);
-
- xprt = args->bc_xprt->xpt_bc_xprt;
- if (xprt) {
- xprt_get(xprt);
- return rpc_create_xprt(args, xprt);
- }
-
- return rpc_create(args);
-}
-
static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses)
{
int maxtime = max_cb_time(clp->net);
@@ -688,7 +672,7 @@
args.authflavor = ses->se_cb_sec.flavor;
}
/* Create RPC client */
- client = create_backchannel_client(&args);
+ client = rpc_create(&args);
if (IS_ERR(client)) {
dprintk("NFSD: couldn't create callback client: %ld\n",
PTR_ERR(client));
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index b6c3a87..0b9476a 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3757,7 +3757,7 @@
nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct nfsd4_open *open)
{
__be32 status;
- unsigned char old_deny_bmap;
+ unsigned char old_deny_bmap = stp->st_deny_bmap;
if (!test_access(open->op_share_access, stp))
return nfs4_get_vfs_file(rqstp, fp, cur_fh, stp, open);
@@ -3766,7 +3766,6 @@
spin_lock(&fp->fi_lock);
status = nfs4_file_check_deny(fp, open->op_share_deny);
if (status == nfs_ok) {
- old_deny_bmap = stp->st_deny_bmap;
set_deny(open->op_share_deny, stp);
fp->fi_share_deny |=
(open->op_share_deny & NFS4_SHARE_DENY_BOTH);
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index b868073..0ce7ce3 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -59,13 +59,61 @@
nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
struct nfsd_attrstat *resp)
{
+ struct iattr *iap = &argp->attrs;
+ struct svc_fh *fhp;
__be32 nfserr;
+
dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n",
SVCFH_fmt(&argp->fh),
argp->attrs.ia_valid, (long) argp->attrs.ia_size);
- fh_copy(&resp->fh, &argp->fh);
- nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs,0, (time_t)0);
+ fhp = fh_copy(&resp->fh, &argp->fh);
+
+ /*
+ * NFSv2 does not differentiate between "set-[ac]time-to-now"
+ * which only requires access, and "set-[ac]time-to-X" which
+ * requires ownership.
+ * So if it looks like it might be "set both to the same time which
+ * is close to now", and if inode_change_ok fails, then we
+ * convert to "set to now" instead of "set to explicit time"
+ *
+ * We only call inode_change_ok as the last test as technically
+ * it is not an interface that we should be using.
+ */
+#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
+#define MAX_TOUCH_TIME_ERROR (30*60)
+ if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET &&
+ iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec) {
+ /*
+ * Looks probable.
+ *
+ * Now just make sure time is in the right ballpark.
+ * Solaris, at least, doesn't seem to care what the time
+ * request is. We require it be within 30 minutes of now.
+ */
+ time_t delta = iap->ia_atime.tv_sec - get_seconds();
+ struct inode *inode;
+
+ nfserr = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP);
+ if (nfserr)
+ goto done;
+ inode = d_inode(fhp->fh_dentry);
+
+ if (delta < 0)
+ delta = -delta;
+ if (delta < MAX_TOUCH_TIME_ERROR &&
+ inode_change_ok(inode, iap) != 0) {
+ /*
+ * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
+ * This will cause notify_change to set these times
+ * to "now"
+ */
+ iap->ia_valid &= ~BOTH_TIME_SET;
+ }
+ }
+
+ nfserr = nfsd_setattr(rqstp, fhp, iap, 0, (time_t)0);
+done:
return nfsd_return_attrs(nfserr, resp);
}
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 752d56b..a89654b 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -646,6 +646,37 @@
return nfserr;
}
+/*
+ * A write procedure can have a large argument, and a read procedure can
+ * have a large reply, but no NFSv2 or NFSv3 procedure has argument and
+ * reply that can both be larger than a page. The xdr code has taken
+ * advantage of this assumption to be a sloppy about bounds checking in
+ * some cases. Pending a rewrite of the NFSv2/v3 xdr code to fix that
+ * problem, we enforce these assumptions here:
+ */
+static bool nfs_request_too_big(struct svc_rqst *rqstp,
+ struct svc_procedure *proc)
+{
+ /*
+ * The ACL code has more careful bounds-checking and is not
+ * susceptible to this problem:
+ */
+ if (rqstp->rq_prog != NFS_PROGRAM)
+ return false;
+ /*
+ * Ditto NFSv4 (which can in theory have argument and reply both
+ * more than a page):
+ */
+ if (rqstp->rq_vers >= 4)
+ return false;
+ /* The reply will be small, we're OK: */
+ if (proc->pc_xdrressize > 0 &&
+ proc->pc_xdrressize < XDR_QUADLEN(PAGE_SIZE))
+ return false;
+
+ return rqstp->rq_arg.len > PAGE_SIZE;
+}
+
int
nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
{
@@ -658,6 +689,11 @@
rqstp->rq_vers, rqstp->rq_proc);
proc = rqstp->rq_procinfo;
+ if (nfs_request_too_big(rqstp, proc)) {
+ dprintk("nfsd: NFSv%d argument too large\n", rqstp->rq_vers);
+ *statp = rpc_garbage_args;
+ return 1;
+ }
/*
* Give the xdr decoder a chance to change this if it wants
* (necessary in the NFSv4.0 compound case)
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 989129e..35cbf57 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -301,42 +301,6 @@
static void
nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
{
- /*
- * NFSv2 does not differentiate between "set-[ac]time-to-now"
- * which only requires access, and "set-[ac]time-to-X" which
- * requires ownership.
- * So if it looks like it might be "set both to the same time which
- * is close to now", and if inode_change_ok fails, then we
- * convert to "set to now" instead of "set to explicit time"
- *
- * We only call inode_change_ok as the last test as technically
- * it is not an interface that we should be using.
- */
-#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
-#define MAX_TOUCH_TIME_ERROR (30*60)
- if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET &&
- iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec) {
- /*
- * Looks probable.
- *
- * Now just make sure time is in the right ballpark.
- * Solaris, at least, doesn't seem to care what the time
- * request is. We require it be within 30 minutes of now.
- */
- time_t delta = iap->ia_atime.tv_sec - get_seconds();
- if (delta < 0)
- delta = -delta;
- if (delta < MAX_TOUCH_TIME_ERROR &&
- inode_change_ok(inode, iap) != 0) {
- /*
- * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
- * This will cause notify_change to set these times
- * to "now"
- */
- iap->ia_valid &= ~BOTH_TIME_SET;
- }
- }
-
/* sanitize the mode change */
if (iap->ia_valid & ATTR_MODE) {
iap->ia_mode &= S_IALLUGO;
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index 9da25fe..ed3a2d9 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -443,7 +443,7 @@
if (!sbp || le16_to_cpu(sbp->s_magic) != NILFS_SUPER_MAGIC)
return 0;
bytes = le16_to_cpu(sbp->s_bytes);
- if (bytes > BLOCK_SIZE)
+ if (bytes < sumoff + 4 || bytes > BLOCK_SIZE)
return 0;
crc = crc32_le(le32_to_cpu(sbp->s_crc_seed), (unsigned char *)sbp,
sumoff);
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index 30d3add..f6298b9 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -67,18 +67,7 @@
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
- wait_event(group->fanotify_data.access_waitq, event->response ||
- atomic_read(&group->fanotify_data.bypass_perm));
-
- if (!event->response) { /* bypass_perm set */
- /*
- * Event was canceled because group is being destroyed. Remove
- * it from group's event list because we are responsible for
- * freeing the permission event.
- */
- fsnotify_remove_event(group, &event->fae.fse);
- return 0;
- }
+ wait_event(group->fanotify_data.access_waitq, event->response);
/* userspace responded, convert to something usable */
switch (event->response) {
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index c991616..49de994 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -358,16 +358,20 @@
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
struct fanotify_perm_event_info *event, *next;
+ struct fsnotify_event *fsn_event;
/*
- * There may be still new events arriving in the notification queue
- * but since userspace cannot use fanotify fd anymore, no event can
- * enter or leave access_list by now.
+ * Stop new events from arriving in the notification queue. since
+ * userspace cannot use fanotify fd anymore, no event can enter or
+ * leave access_list by now either.
+ */
+ fsnotify_group_stop_queueing(group);
+
+ /*
+ * Process all permission events on access_list and notification queue
+ * and simulate reply from userspace.
*/
spin_lock(&group->fanotify_data.access_lock);
-
- atomic_inc(&group->fanotify_data.bypass_perm);
-
list_for_each_entry_safe(event, next, &group->fanotify_data.access_list,
fae.fse.list) {
pr_debug("%s: found group=%p event=%p\n", __func__, group,
@@ -379,12 +383,21 @@
spin_unlock(&group->fanotify_data.access_lock);
/*
- * Since bypass_perm is set, newly queued events will not wait for
- * access response. Wake up the already sleeping ones now.
- * synchronize_srcu() in fsnotify_destroy_group() will wait for all
- * processes sleeping in fanotify_handle_event() waiting for access
- * response and thus also for all permission events to be freed.
+ * Destroy all non-permission events. For permission events just
+ * dequeue them and set the response. They will be freed once the
+ * response is consumed and fanotify_get_response() returns.
*/
+ mutex_lock(&group->notification_mutex);
+ while (!fsnotify_notify_queue_is_empty(group)) {
+ fsn_event = fsnotify_remove_first_event(group);
+ if (!(fsn_event->mask & FAN_ALL_PERM_EVENTS))
+ fsnotify_destroy_event(group, fsn_event);
+ else
+ FANOTIFY_PE(fsn_event)->response = FAN_ALLOW;
+ }
+ mutex_unlock(&group->notification_mutex);
+
+ /* Response for all permission events it set, wakeup waiters */
wake_up(&group->fanotify_data.access_waitq);
#endif
@@ -475,7 +488,7 @@
}
/* you can only watch an inode if you have read permissions on it */
- ret = inode_permission(path->dentry->d_inode, MAY_READ);
+ ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
if (ret)
path_put(path);
out:
@@ -742,7 +755,6 @@
spin_lock_init(&group->fanotify_data.access_lock);
init_waitqueue_head(&group->fanotify_data.access_waitq);
INIT_LIST_HEAD(&group->fanotify_data.access_list);
- atomic_set(&group->fanotify_data.bypass_perm, 0);
#endif
switch (flags & FAN_ALL_CLASS_BITS) {
case FAN_CLASS_NOTIF:
diff --git a/fs/notify/group.c b/fs/notify/group.c
index d16b62c..18eb30c 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -40,6 +40,17 @@
}
/*
+ * Stop queueing new events for this group. Once this function returns
+ * fsnotify_add_event() will not add any new events to the group's queue.
+ */
+void fsnotify_group_stop_queueing(struct fsnotify_group *group)
+{
+ mutex_lock(&group->notification_mutex);
+ group->shutdown = true;
+ mutex_unlock(&group->notification_mutex);
+}
+
+/*
* Trying to get rid of a group. Remove all marks, flush all events and release
* the group reference.
* Note that another thread calling fsnotify_clear_marks_by_group() may still
@@ -47,6 +58,14 @@
*/
void fsnotify_destroy_group(struct fsnotify_group *group)
{
+ /*
+ * Stop queueing new events. The code below is careful enough to not
+ * require this but fanotify needs to stop queuing events even before
+ * fsnotify_destroy_group() is called and this makes the other callers
+ * of fsnotify_destroy_group() to see the same behavior.
+ */
+ fsnotify_group_stop_queueing(group);
+
/* clear all inode marks for this group */
fsnotify_clear_marks_by_group(group);
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index ed833ba..b2ddc88 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -338,7 +338,7 @@
if (error)
return error;
/* you can only watch an inode if you have read permissions on it */
- error = inode_permission(path->dentry->d_inode, MAY_READ);
+ error = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
if (error)
path_put(path);
return error;
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
index a95d8e0..e455e83 100644
--- a/fs/notify/notification.c
+++ b/fs/notify/notification.c
@@ -82,7 +82,8 @@
* Add an event to the group notification queue. The group can later pull this
* event off the queue to deal with. The function returns 0 if the event was
* added to the queue, 1 if the event was merged with some other queued event,
- * 2 if the queue of events has overflown.
+ * 2 if the event was not queued - either the queue of events has overflown
+ * or the group is shutting down.
*/
int fsnotify_add_event(struct fsnotify_group *group,
struct fsnotify_event *event,
@@ -96,6 +97,11 @@
mutex_lock(&group->notification_mutex);
+ if (group->shutdown) {
+ mutex_unlock(&group->notification_mutex);
+ return 2;
+ }
+
if (group->q_len >= group->max_events) {
ret = 2;
/* Queue overflow event only if it isn't already queued */
@@ -126,21 +132,6 @@
}
/*
- * Remove @event from group's notification queue. It is the responsibility of
- * the caller to destroy the event.
- */
-void fsnotify_remove_event(struct fsnotify_group *group,
- struct fsnotify_event *event)
-{
- mutex_lock(&group->notification_mutex);
- if (!list_empty(&event->list)) {
- list_del_init(&event->list);
- group->q_len--;
- }
- mutex_unlock(&group->notification_mutex);
-}
-
-/*
* Remove and return the first event from the notification list. It is the
* responsibility of the caller to destroy the obtained event
*/
diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
index b6c86fe..c7641f6 100644
--- a/fs/ocfs2/acl.c
+++ b/fs/ocfs2/acl.c
@@ -241,10 +241,12 @@
case ACL_TYPE_ACCESS:
name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
if (acl) {
- umode_t mode = inode->i_mode;
+ umode_t mode;
+
ret = posix_acl_update_mode(inode, &mode, &acl);
if (ret)
return ret;
+
ret = ocfs2_acl_set_mode(inode, di_bh,
handle, mode);
if (ret)
@@ -304,3 +306,90 @@
return acl;
}
+
+int ocfs2_acl_chmod(struct inode *inode, struct buffer_head *bh)
+{
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ struct posix_acl *acl;
+ int ret;
+
+ if (S_ISLNK(inode->i_mode))
+ return -EOPNOTSUPP;
+
+ if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
+ return 0;
+
+ acl = ocfs2_get_acl_nolock(inode, ACL_TYPE_ACCESS, bh);
+ if (IS_ERR(acl) || !acl)
+ return PTR_ERR(acl);
+ ret = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
+ if (ret)
+ return ret;
+ ret = ocfs2_set_acl(NULL, inode, NULL, ACL_TYPE_ACCESS,
+ acl, NULL, NULL);
+ posix_acl_release(acl);
+ return ret;
+}
+
+/*
+ * Initialize the ACLs of a new inode. If parent directory has default ACL,
+ * then clone to new inode. Called from ocfs2_mknod.
+ */
+int ocfs2_init_acl(handle_t *handle,
+ struct inode *inode,
+ struct inode *dir,
+ struct buffer_head *di_bh,
+ struct buffer_head *dir_bh,
+ struct ocfs2_alloc_context *meta_ac,
+ struct ocfs2_alloc_context *data_ac)
+{
+ struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
+ struct posix_acl *acl = NULL;
+ int ret = 0, ret2;
+ umode_t mode;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
+ acl = ocfs2_get_acl_nolock(dir, ACL_TYPE_DEFAULT,
+ dir_bh);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ }
+ if (!acl) {
+ mode = inode->i_mode & ~current_umask();
+ ret = ocfs2_acl_set_mode(inode, di_bh, handle, mode);
+ if (ret) {
+ mlog_errno(ret);
+ goto cleanup;
+ }
+ }
+ }
+ if ((osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) && acl) {
+ if (S_ISDIR(inode->i_mode)) {
+ ret = ocfs2_set_acl(handle, inode, di_bh,
+ ACL_TYPE_DEFAULT, acl,
+ meta_ac, data_ac);
+ if (ret)
+ goto cleanup;
+ }
+ mode = inode->i_mode;
+ ret = __posix_acl_create(&acl, GFP_NOFS, &mode);
+ if (ret < 0)
+ return ret;
+
+ ret2 = ocfs2_acl_set_mode(inode, di_bh, handle, mode);
+ if (ret2) {
+ mlog_errno(ret2);
+ ret = ret2;
+ goto cleanup;
+ }
+ if (ret > 0) {
+ ret = ocfs2_set_acl(handle, inode,
+ di_bh, ACL_TYPE_ACCESS,
+ acl, meta_ac, data_ac);
+ }
+ }
+cleanup:
+ posix_acl_release(acl);
+ return ret;
+}
diff --git a/fs/ocfs2/acl.h b/fs/ocfs2/acl.h
index 3fce68d..2783a75 100644
--- a/fs/ocfs2/acl.h
+++ b/fs/ocfs2/acl.h
@@ -35,5 +35,10 @@
struct posix_acl *acl,
struct ocfs2_alloc_context *meta_ac,
struct ocfs2_alloc_context *data_ac);
+extern int ocfs2_acl_chmod(struct inode *, struct buffer_head *);
+extern int ocfs2_init_acl(handle_t *, struct inode *, struct inode *,
+ struct buffer_head *, struct buffer_head *,
+ struct ocfs2_alloc_context *,
+ struct ocfs2_alloc_context *);
#endif /* OCFS2_ACL_H */
diff --git a/fs/ocfs2/dlm/dlmconvert.c b/fs/ocfs2/dlm/dlmconvert.c
index f909313..2e11658 100644
--- a/fs/ocfs2/dlm/dlmconvert.c
+++ b/fs/ocfs2/dlm/dlmconvert.c
@@ -262,7 +262,6 @@
struct dlm_lock *lock, int flags, int type)
{
enum dlm_status status;
- u8 old_owner = res->owner;
mlog(0, "type=%d, convert_type=%d, busy=%d\n", lock->ml.type,
lock->ml.convert_type, res->state & DLM_LOCK_RES_IN_PROGRESS);
@@ -329,7 +328,6 @@
spin_lock(&res->spinlock);
res->state &= ~DLM_LOCK_RES_IN_PROGRESS;
- lock->convert_pending = 0;
/* if it failed, move it back to granted queue.
* if master returns DLM_NORMAL and then down before sending ast,
* it may have already been moved to granted queue, reset to
@@ -338,12 +336,14 @@
if (status != DLM_NOTQUEUED)
dlm_error(status);
dlm_revert_pending_convert(res, lock);
- } else if ((res->state & DLM_LOCK_RES_RECOVERING) ||
- (old_owner != res->owner)) {
- mlog(0, "res %.*s is in recovering or has been recovered.\n",
- res->lockname.len, res->lockname.name);
+ } else if (!lock->convert_pending) {
+ mlog(0, "%s: res %.*s, owner died and lock has been moved back "
+ "to granted list, retry convert.\n",
+ dlm->name, res->lockname.len, res->lockname.name);
status = DLM_RECOVERING;
}
+
+ lock->convert_pending = 0;
bail:
spin_unlock(&res->spinlock);
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index e6e8d64..2adcb98 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1249,18 +1249,18 @@
if (size_change)
ocfs2_rw_unlock(inode, 1);
bail:
- brelse(bh);
/* Release quota pointers in case we acquired them */
for (qtype = 0; qtype < OCFS2_MAXQUOTAS; qtype++)
dqput(transfer_to[qtype]);
if (!status && attr->ia_valid & ATTR_MODE) {
- status = posix_acl_chmod(inode, inode->i_mode);
+ status = ocfs2_acl_chmod(inode, bh);
if (status < 0)
mlog_errno(status);
}
+ brelse(bh);
return status;
}
@@ -1515,7 +1515,8 @@
u64 start, u64 len)
{
int ret = 0;
- u64 tmpend, end = start + len;
+ u64 tmpend = 0;
+ u64 end = start + len;
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
unsigned int csize = osb->s_clustersize;
handle_t *handle;
@@ -1547,18 +1548,31 @@
}
/*
- * We want to get the byte offset of the end of the 1st cluster.
+ * If start is on a cluster boundary and end is somewhere in another
+ * cluster, we have not COWed the cluster starting at start, unless
+ * end is also within the same cluster. So, in this case, we skip this
+ * first call to ocfs2_zero_range_for_truncate() truncate and move on
+ * to the next one.
*/
- tmpend = (u64)osb->s_clustersize + (start & ~(osb->s_clustersize - 1));
- if (tmpend > end)
- tmpend = end;
+ if ((start & (csize - 1)) != 0) {
+ /*
+ * We want to get the byte offset of the end of the 1st
+ * cluster.
+ */
+ tmpend = (u64)osb->s_clustersize +
+ (start & ~(osb->s_clustersize - 1));
+ if (tmpend > end)
+ tmpend = end;
- trace_ocfs2_zero_partial_clusters_range1((unsigned long long)start,
- (unsigned long long)tmpend);
+ trace_ocfs2_zero_partial_clusters_range1(
+ (unsigned long long)start,
+ (unsigned long long)tmpend);
- ret = ocfs2_zero_range_for_truncate(inode, handle, start, tmpend);
- if (ret)
- mlog_errno(ret);
+ ret = ocfs2_zero_range_for_truncate(inode, handle, start,
+ tmpend);
+ if (ret)
+ mlog_errno(ret);
+ }
if (tmpend < end) {
/*
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index 9fc1dae..8f7f5de 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -253,7 +253,6 @@
struct ocfs2_dir_lookup_result lookup = { NULL, };
sigset_t oldset;
int did_block_signals = 0;
- struct posix_acl *default_acl = NULL, *acl = NULL;
struct ocfs2_dentry_lock *dl = NULL;
trace_ocfs2_mknod(dir, dentry, dentry->d_name.len, dentry->d_name.name,
@@ -356,14 +355,6 @@
goto leave;
}
- status = posix_acl_create(dir, &mode, &default_acl, &acl);
- if (status) {
- mlog_errno(status);
- goto leave;
- }
- /* update inode->i_mode after mask with "umask". */
- inode->i_mode = mode;
-
handle = ocfs2_start_trans(osb, ocfs2_mknod_credits(osb->sb,
S_ISDIR(mode),
xattr_credits));
@@ -412,16 +403,8 @@
inc_nlink(dir);
}
- if (default_acl) {
- status = ocfs2_set_acl(handle, inode, new_fe_bh,
- ACL_TYPE_DEFAULT, default_acl,
- meta_ac, data_ac);
- }
- if (!status && acl) {
- status = ocfs2_set_acl(handle, inode, new_fe_bh,
- ACL_TYPE_ACCESS, acl,
- meta_ac, data_ac);
- }
+ status = ocfs2_init_acl(handle, inode, dir, new_fe_bh, parent_fe_bh,
+ meta_ac, data_ac);
if (status < 0) {
mlog_errno(status);
@@ -463,10 +446,6 @@
d_instantiate(dentry, inode);
status = 0;
leave:
- if (default_acl)
- posix_acl_release(default_acl);
- if (acl)
- posix_acl_release(acl);
if (status < 0 && did_quota_inode)
dquot_free_inode(inode);
if (handle)
diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c
index d81f6e2..18e8b4d 100644
--- a/fs/ocfs2/refcounttree.c
+++ b/fs/ocfs2/refcounttree.c
@@ -4268,20 +4268,12 @@
struct inode *inode = old_dentry->d_inode;
struct buffer_head *old_bh = NULL;
struct inode *new_orphan_inode = NULL;
- struct posix_acl *default_acl, *acl;
- umode_t mode;
if (!ocfs2_refcount_tree(OCFS2_SB(inode->i_sb)))
return -EOPNOTSUPP;
- mode = inode->i_mode;
- error = posix_acl_create(dir, &mode, &default_acl, &acl);
- if (error) {
- mlog_errno(error);
- goto out;
- }
- error = ocfs2_create_inode_in_orphan(dir, mode,
+ error = ocfs2_create_inode_in_orphan(dir, inode->i_mode,
&new_orphan_inode);
if (error) {
mlog_errno(error);
@@ -4320,16 +4312,11 @@
/* If the security isn't preserved, we need to re-initialize them. */
if (!preserve) {
error = ocfs2_init_security_and_acl(dir, new_orphan_inode,
- &new_dentry->d_name,
- default_acl, acl);
+ &new_dentry->d_name);
if (error)
mlog_errno(error);
}
out:
- if (default_acl)
- posix_acl_release(default_acl);
- if (acl)
- posix_acl_release(acl);
if (!error) {
error = ocfs2_mv_orphaned_inode_to_new(dir, new_orphan_inode,
new_dentry);
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index 016f01d..c237008 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -7207,12 +7207,10 @@
*/
int ocfs2_init_security_and_acl(struct inode *dir,
struct inode *inode,
- const struct qstr *qstr,
- struct posix_acl *default_acl,
- struct posix_acl *acl)
+ const struct qstr *qstr)
{
- struct buffer_head *dir_bh = NULL;
int ret = 0;
+ struct buffer_head *dir_bh = NULL;
ret = ocfs2_init_security_get(inode, dir, qstr, NULL);
if (ret) {
@@ -7225,11 +7223,9 @@
mlog_errno(ret);
goto leave;
}
-
- if (!ret && default_acl)
- ret = ocfs2_iop_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
- if (!ret && acl)
- ret = ocfs2_iop_set_acl(inode, acl, ACL_TYPE_ACCESS);
+ ret = ocfs2_init_acl(NULL, inode, dir, NULL, dir_bh, NULL, NULL);
+ if (ret)
+ mlog_errno(ret);
ocfs2_inode_unlock(dir, 0);
brelse(dir_bh);
diff --git a/fs/ocfs2/xattr.h b/fs/ocfs2/xattr.h
index f10d5b9..1633cc1 100644
--- a/fs/ocfs2/xattr.h
+++ b/fs/ocfs2/xattr.h
@@ -94,7 +94,5 @@
bool preserve_security);
int ocfs2_init_security_and_acl(struct inode *dir,
struct inode *inode,
- const struct qstr *qstr,
- struct posix_acl *default_acl,
- struct posix_acl *acl);
+ const struct qstr *qstr);
#endif /* OCFS2_XATTR_H */
diff --git a/fs/open.c b/fs/open.c
index 1651f35..d7e0e1b 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -34,8 +34,8 @@
#include "internal.h"
-int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
- struct file *filp)
+int do_truncate2(struct vfsmount *mnt, struct dentry *dentry, loff_t length,
+ unsigned int time_attrs, struct file *filp)
{
int ret;
struct iattr newattrs;
@@ -58,17 +58,24 @@
mutex_lock(&dentry->d_inode->i_mutex);
/* Note any delegations or leases have already been broken: */
- ret = notify_change(dentry, &newattrs, NULL);
+ ret = notify_change2(mnt, dentry, &newattrs, NULL);
mutex_unlock(&dentry->d_inode->i_mutex);
return ret;
}
+int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
+ struct file *filp)
+{
+ return do_truncate2(NULL, dentry, length, time_attrs, filp);
+}
long vfs_truncate(struct path *path, loff_t length)
{
struct inode *inode;
+ struct vfsmount *mnt;
long error;
inode = path->dentry->d_inode;
+ mnt = path->mnt;
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
if (S_ISDIR(inode->i_mode))
@@ -80,7 +87,7 @@
if (error)
goto out;
- error = inode_permission(inode, MAY_WRITE);
+ error = inode_permission2(mnt, inode, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
@@ -104,7 +111,7 @@
if (!error)
error = security_path_truncate(path);
if (!error)
- error = do_truncate(path->dentry, length, 0, NULL);
+ error = do_truncate2(mnt, path->dentry, length, 0, NULL);
put_write_and_out:
put_write_access(inode);
@@ -153,6 +160,7 @@
{
struct inode *inode;
struct dentry *dentry;
+ struct vfsmount *mnt;
struct fd f;
int error;
@@ -169,6 +177,7 @@
small = 0;
dentry = f.file->f_path.dentry;
+ mnt = f.file->f_path.mnt;
inode = dentry->d_inode;
error = -EINVAL;
if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
@@ -188,7 +197,7 @@
if (!error)
error = security_path_truncate(&f.file->f_path);
if (!error)
- error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
+ error = do_truncate2(mnt, dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
sb_end_write(inode->i_sb);
out_putf:
fdput(f);
@@ -322,6 +331,7 @@
struct cred *override_cred;
struct path path;
struct inode *inode;
+ struct vfsmount *mnt;
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
@@ -352,6 +362,7 @@
goto out;
inode = path.dentry->d_inode;
+ mnt = path.mnt;
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
/*
@@ -363,7 +374,7 @@
goto out_path_release;
}
- res = inode_permission(inode, mode | MAY_ACCESS);
+ res = inode_permission2(mnt, inode, mode | MAY_ACCESS);
/* SuS v2 requires we report a read only fs too */
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
goto out_path_release;
@@ -407,7 +418,7 @@
if (error)
goto out;
- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;
@@ -427,6 +438,7 @@
{
struct fd f = fdget_raw(fd);
struct inode *inode;
+ struct vfsmount *mnt;
int error = -EBADF;
error = -EBADF;
@@ -434,12 +446,13 @@
goto out;
inode = file_inode(f.file);
+ mnt = f.file->f_path.mnt;
error = -ENOTDIR;
if (!S_ISDIR(inode->i_mode))
goto out_putf;
- error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(mnt, inode, MAY_EXEC | MAY_CHDIR);
if (!error)
set_fs_pwd(current->fs, &f.file->f_path);
out_putf:
@@ -458,7 +471,7 @@
if (error)
goto out;
- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;
@@ -498,7 +511,7 @@
goto out_unlock;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- error = notify_change(path->dentry, &newattrs, &delegated_inode);
+ error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
out_unlock:
mutex_unlock(&inode->i_mutex);
if (delegated_inode) {
@@ -578,7 +591,7 @@
mutex_lock(&inode->i_mutex);
error = security_path_chown(path, uid, gid);
if (!error)
- error = notify_change(path->dentry, &newattrs, &delegated_inode);
+ error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
mutex_unlock(&inode->i_mutex);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 54d62bd..ca48dff 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -48,6 +48,8 @@
}
for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
+ if (ovl_is_private_xattr(name))
+ continue;
retry:
size = vfs_getxattr(old, name, value, value_size);
if (size == -ERANGE)
@@ -127,6 +129,8 @@
len -= bytes;
}
+ if (!error)
+ error = vfs_fsync(new_file, 0);
fput(new_file);
out_fput:
fput(old_file);
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index ddb1dc9..d124faf 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -505,6 +505,7 @@
struct dentry *upper;
struct dentry *opaquedir = NULL;
int err;
+ int flags = 0;
if (is_dir) {
opaquedir = ovl_check_empty_and_clear(dentry);
@@ -517,46 +518,39 @@
if (err)
goto out_dput;
+ upper = lookup_one_len(dentry->d_name.name, upperdir,
+ dentry->d_name.len);
+ err = PTR_ERR(upper);
+ if (IS_ERR(upper))
+ goto out_unlock;
+
+ err = -ESTALE;
+ if ((opaquedir && upper != opaquedir) ||
+ (!opaquedir && ovl_dentry_upper(dentry) &&
+ upper != ovl_dentry_upper(dentry))) {
+ goto out_dput_upper;
+ }
+
whiteout = ovl_whiteout(workdir, dentry);
err = PTR_ERR(whiteout);
if (IS_ERR(whiteout))
- goto out_unlock;
+ goto out_dput_upper;
- upper = ovl_dentry_upper(dentry);
- if (!upper) {
- upper = lookup_one_len(dentry->d_name.name, upperdir,
- dentry->d_name.len);
- err = PTR_ERR(upper);
- if (IS_ERR(upper))
- goto kill_whiteout;
+ if (d_is_dir(upper))
+ flags = RENAME_EXCHANGE;
- err = ovl_do_rename(wdir, whiteout, udir, upper, 0);
- dput(upper);
- if (err)
- goto kill_whiteout;
- } else {
- int flags = 0;
+ err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
+ if (err)
+ goto kill_whiteout;
+ if (flags)
+ ovl_cleanup(wdir, upper);
- if (opaquedir)
- upper = opaquedir;
- err = -ESTALE;
- if (upper->d_parent != upperdir)
- goto kill_whiteout;
-
- if (is_dir)
- flags |= RENAME_EXCHANGE;
-
- err = ovl_do_rename(wdir, whiteout, udir, upper, flags);
- if (err)
- goto kill_whiteout;
-
- if (is_dir)
- ovl_cleanup(wdir, upper);
- }
ovl_dentry_version_inc(dentry->d_parent);
out_d_drop:
d_drop(dentry);
dput(whiteout);
+out_dput_upper:
+ dput(upper);
out_unlock:
unlock_rename(workdir, upperdir);
out_dput:
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index e696ba3..6d52041 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -54,6 +54,10 @@
upperdentry = ovl_dentry_upper(dentry);
mutex_lock(&upperdentry->d_inode->i_mutex);
+
+ if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
+ attr->ia_valid &= ~ATTR_MODE;
+
err = notify_change(upperdentry, attr, NULL);
if (!err)
ovl_copyattr(upperdentry->d_inode, dentry->d_inode);
@@ -204,8 +208,7 @@
return realinode->i_op->readlink(realpath.dentry, buf, bufsiz);
}
-
-static bool ovl_is_private_xattr(const char *name)
+bool ovl_is_private_xattr(const char *name)
{
return strncmp(name, "trusted.overlay.", 14) == 0;
}
@@ -260,7 +263,8 @@
struct path realpath;
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
ssize_t res;
- int off;
+ size_t len;
+ char *s;
res = vfs_listxattr(realpath.dentry, list, size);
if (res <= 0 || size == 0)
@@ -270,17 +274,19 @@
return res;
/* filter out private xattrs */
- for (off = 0; off < res;) {
- char *s = list + off;
- size_t slen = strlen(s) + 1;
+ for (s = list, len = res; len;) {
+ size_t slen = strnlen(s, len) + 1;
- BUG_ON(off + slen > res);
+ /* underlying fs providing us with an broken xattr list? */
+ if (WARN_ON(slen > len))
+ return -EIO;
+ len -= slen;
if (ovl_is_private_xattr(s)) {
res -= slen;
- memmove(s, s + slen, res - off);
+ memmove(s, s + slen, len);
} else {
- off += slen;
+ s += slen;
}
}
@@ -395,12 +401,11 @@
if (!inode)
return NULL;
- mode &= S_IFMT;
-
inode->i_ino = get_next_ino();
inode->i_mode = mode;
inode->i_flags |= S_NOATIME | S_NOCMTIME;
+ mode &= S_IFMT;
switch (mode) {
case S_IFDIR:
inode->i_private = oe;
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 1714fcc..f13557f 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -166,6 +166,7 @@
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
int ovl_removexattr(struct dentry *dentry, const char *name);
struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags);
+bool ovl_is_private_xattr(const char *name);
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
struct ovl_entry *oe);
@@ -173,6 +174,7 @@
{
to->i_uid = from->i_uid;
to->i_gid = from->i_gid;
+ to->i_mode = from->i_mode;
}
/* dir.c */
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index b2361a1..b5bddae 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -545,6 +545,10 @@
struct kstat stat = {
.mode = S_IFDIR | 0,
};
+ struct iattr attr = {
+ .ia_valid = ATTR_MODE,
+ .ia_mode = stat.mode,
+ };
if (work->d_inode) {
err = -EEXIST;
@@ -560,6 +564,21 @@
err = ovl_create_real(dir, work, &stat, NULL, NULL, true);
if (err)
goto out_dput;
+
+ err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_DEFAULT);
+ if (err && err != -ENODATA && err != -EOPNOTSUPP)
+ goto out_dput;
+
+ err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_ACCESS);
+ if (err && err != -ENODATA && err != -EOPNOTSUPP)
+ goto out_dput;
+
+ /* Clear any inherited mode bits */
+ mutex_lock(&work->d_inode->i_mutex);
+ err = notify_change(work, &attr, NULL);
+ mutex_unlock(&work->d_inode->i_mutex);
+ if (err)
+ goto out_dput;
}
out_unlock:
mutex_unlock(&dir->i_mutex);
diff --git a/fs/pipe.c b/fs/pipe.c
index 21981e5..e3ba6c3 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -39,6 +39,12 @@
*/
unsigned int pipe_min_size = PAGE_SIZE;
+/* Maximum allocatable pages per user. Hard limit is unset by default, soft
+ * matches default values.
+ */
+unsigned long pipe_user_pages_hard;
+unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR;
+
/*
* We use a start+len construction, which provides full use of the
* allocated memory.
@@ -585,20 +591,49 @@
return retval;
}
+static void account_pipe_buffers(struct pipe_inode_info *pipe,
+ unsigned long old, unsigned long new)
+{
+ atomic_long_add(new - old, &pipe->user->pipe_bufs);
+}
+
+static bool too_many_pipe_buffers_soft(struct user_struct *user)
+{
+ return pipe_user_pages_soft &&
+ atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft;
+}
+
+static bool too_many_pipe_buffers_hard(struct user_struct *user)
+{
+ return pipe_user_pages_hard &&
+ atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard;
+}
+
struct pipe_inode_info *alloc_pipe_info(void)
{
struct pipe_inode_info *pipe;
pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL);
if (pipe) {
- pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL);
+ unsigned long pipe_bufs = PIPE_DEF_BUFFERS;
+ struct user_struct *user = get_current_user();
+
+ if (!too_many_pipe_buffers_hard(user)) {
+ if (too_many_pipe_buffers_soft(user))
+ pipe_bufs = 1;
+ pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL);
+ }
+
if (pipe->bufs) {
init_waitqueue_head(&pipe->wait);
pipe->r_counter = pipe->w_counter = 1;
- pipe->buffers = PIPE_DEF_BUFFERS;
+ pipe->buffers = pipe_bufs;
+ pipe->user = user;
+ account_pipe_buffers(pipe, 0, pipe_bufs);
mutex_init(&pipe->mutex);
return pipe;
}
+ free_uid(user);
kfree(pipe);
}
@@ -609,6 +644,8 @@
{
int i;
+ account_pipe_buffers(pipe, pipe->buffers, 0);
+ free_uid(pipe->user);
for (i = 0; i < pipe->buffers; i++) {
struct pipe_buffer *buf = pipe->bufs + i;
if (buf->ops)
@@ -999,6 +1036,7 @@
memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer));
}
+ account_pipe_buffers(pipe, pipe->buffers, nr_pages);
pipe->curbuf = 0;
kfree(pipe->bufs);
pipe->bufs = bufs;
@@ -1070,6 +1108,11 @@
if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) {
ret = -EPERM;
goto out;
+ } else if ((too_many_pipe_buffers_hard(pipe->user) ||
+ too_many_pipe_buffers_soft(pipe->user)) &&
+ !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) {
+ ret = -EPERM;
+ goto out;
}
ret = pipe_set_size(pipe, nr_pages);
break;
diff --git a/fs/pnode.c b/fs/pnode.c
index aae331a..c35d03d 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -198,10 +198,15 @@
/* all accesses are serialized by namespace_sem */
static struct user_namespace *user_ns;
-static struct mount *last_dest, *last_source, *dest_master;
+static struct mount *last_dest, *first_source, *last_source, *dest_master;
static struct mountpoint *mp;
static struct hlist_head *list;
+static inline bool peers(struct mount *m1, struct mount *m2)
+{
+ return m1->mnt_group_id == m2->mnt_group_id && m1->mnt_group_id;
+}
+
static int propagate_one(struct mount *m)
{
struct mount *child;
@@ -212,24 +217,26 @@
/* skip if mountpoint isn't covered by it */
if (!is_subdir(mp->m_dentry, m->mnt.mnt_root))
return 0;
- if (m->mnt_group_id == last_dest->mnt_group_id) {
+ if (peers(m, last_dest)) {
type = CL_MAKE_SHARED;
} else {
struct mount *n, *p;
+ bool done;
for (n = m; ; n = p) {
p = n->mnt_master;
- if (p == dest_master || IS_MNT_MARKED(p)) {
- while (last_dest->mnt_master != p) {
- last_source = last_source->mnt_master;
- last_dest = last_source->mnt_parent;
- }
- if (n->mnt_group_id != last_dest->mnt_group_id) {
- last_source = last_source->mnt_master;
- last_dest = last_source->mnt_parent;
- }
+ if (p == dest_master || IS_MNT_MARKED(p))
break;
- }
}
+ do {
+ struct mount *parent = last_source->mnt_parent;
+ if (last_source == first_source)
+ break;
+ done = parent->mnt_master == p;
+ if (done && peers(n, parent))
+ break;
+ last_source = last_source->mnt_master;
+ } while (!done);
+
type = CL_SLAVE;
/* beginning of peer group among the slaves? */
if (IS_MNT_SHARED(m))
@@ -280,6 +287,7 @@
*/
user_ns = current->nsproxy->mnt_ns->user_ns;
last_dest = dest_mnt;
+ first_source = source_mnt;
last_source = source_mnt;
mp = dest_mp;
list = tree_list;
@@ -403,3 +411,37 @@
__propagate_umount(mnt);
return 0;
}
+
+/*
+ * Iterates over all slaves, and slaves of slaves.
+ */
+static struct mount *next_descendent(struct mount *root, struct mount *cur)
+{
+ if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list))
+ return first_slave(cur);
+ do {
+ struct mount *master = cur->mnt_master;
+
+ if (!master || cur->mnt_slave.next != &master->mnt_slave_list) {
+ struct mount *next = next_slave(cur);
+
+ return (next == root) ? NULL : next;
+ }
+ cur = master;
+ } while (cur != root);
+ return NULL;
+}
+
+void propagate_remount(struct mount *mnt)
+{
+ struct mount *m = mnt;
+ struct super_block *sb = mnt->mnt.mnt_sb;
+
+ if (sb->s_op->copy_mnt_data) {
+ m = next_descendent(mnt, m);
+ while (m) {
+ sb->s_op->copy_mnt_data(m->mnt.data, mnt->mnt.data);
+ m = next_descendent(mnt, m);
+ }
+ }
+}
diff --git a/fs/pnode.h b/fs/pnode.h
index 16afc3d..a407d32 100644
--- a/fs/pnode.h
+++ b/fs/pnode.h
@@ -42,6 +42,7 @@
struct hlist_head *);
int propagate_umount(struct hlist_head *);
int propagate_mount_busy(struct mount *, int);
+void propagate_remount(struct mount *);
void mnt_release_group_id(struct mount *);
int get_dominating_id(struct mount *mnt, const struct path *root);
unsigned int mnt_get_count(struct mount *mnt);
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 73aad91..38c9193 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -818,6 +818,28 @@
return error;
}
+int
+set_posix_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+ if (!IS_POSIXACL(inode))
+ return -EOPNOTSUPP;
+ if (!inode->i_op->set_acl)
+ return -EOPNOTSUPP;
+
+ if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
+ return acl ? -EACCES : 0;
+ if (!inode_owner_or_capable(inode))
+ return -EPERM;
+
+ if (acl) {
+ int ret = posix_acl_valid(acl);
+ if (ret)
+ return ret;
+ }
+ return inode->i_op->set_acl(inode, acl, type);
+}
+EXPORT_SYMBOL(set_posix_acl);
+
static int
posix_acl_xattr_set(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags, int type)
@@ -826,30 +848,12 @@
struct posix_acl *acl = NULL;
int ret;
- if (!IS_POSIXACL(inode))
- return -EOPNOTSUPP;
- if (!inode->i_op->set_acl)
- return -EOPNOTSUPP;
-
- if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode))
- return value ? -EACCES : 0;
- if (!inode_owner_or_capable(inode))
- return -EPERM;
-
if (value) {
acl = posix_acl_from_xattr(&init_user_ns, value, size);
if (IS_ERR(acl))
return PTR_ERR(acl);
-
- if (acl) {
- ret = posix_acl_valid(acl);
- if (ret)
- goto out;
- }
}
-
- ret = inode->i_op->set_acl(inode, acl, type);
-out:
+ ret = set_posix_acl(inode, type, acl);
posix_acl_release(acl);
return ret;
}
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 6307f87..5699b3c 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -305,7 +305,8 @@
static inline void task_cap(struct seq_file *m, struct task_struct *p)
{
const struct cred *cred;
- kernel_cap_t cap_inheritable, cap_permitted, cap_effective, cap_bset;
+ kernel_cap_t cap_inheritable, cap_permitted, cap_effective,
+ cap_bset, cap_ambient;
rcu_read_lock();
cred = __task_cred(p);
@@ -313,12 +314,14 @@
cap_permitted = cred->cap_permitted;
cap_effective = cred->cap_effective;
cap_bset = cred->cap_bset;
+ cap_ambient = cred->cap_ambient;
rcu_read_unlock();
render_cap_t(m, "CapInh:\t", &cap_inheritable);
render_cap_t(m, "CapPrm:\t", &cap_permitted);
render_cap_t(m, "CapEff:\t", &cap_effective);
render_cap_t(m, "CapBnd:\t", &cap_bset);
+ render_cap_t(m, "CapAmb:\t", &cap_ambient);
}
static inline void task_seccomp(struct seq_file *m, struct task_struct *p)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index d523b23..f0bba6d 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -76,6 +76,7 @@
#include <linux/printk.h>
#include <linux/cgroup.h>
#include <linux/cpuset.h>
+#include <linux/cpufreq.h>
#include <linux/audit.h>
#include <linux/poll.h>
#include <linux/nsproxy.h>
@@ -2637,6 +2638,9 @@
#ifdef CONFIG_CHECKPOINT_RESTORE
REG("timers", S_IRUGO, proc_timers_operations),
#endif
+#ifdef CONFIG_CPU_FREQ_STAT
+ ONE("time_in_state", 0444, proc_time_in_state_show),
+#endif
};
static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
@@ -2891,6 +2895,44 @@
}
/*
+ * proc_tid_comm_permission is a special permission function exclusively
+ * used for the node /proc/<pid>/task/<tid>/comm.
+ * It bypasses generic permission checks in the case where a task of the same
+ * task group attempts to access the node.
+ * The rational behind this is that glibc and bionic access this node for
+ * cross thread naming (pthread_set/getname_np(!self)). However, if
+ * PR_SET_DUMPABLE gets set to 0 this node among others becomes uid=0 gid=0,
+ * which locks out the cross thread naming implementation.
+ * This function makes sure that the node is always accessible for members of
+ * same thread group.
+ */
+static int proc_tid_comm_permission(struct inode *inode, int mask)
+{
+ bool is_same_tgroup;
+ struct task_struct *task;
+
+ task = get_proc_task(inode);
+ if (!task)
+ return -ESRCH;
+ is_same_tgroup = same_thread_group(current, task);
+ put_task_struct(task);
+
+ if (likely(is_same_tgroup && !(mask & MAY_EXEC))) {
+ /* This file (/proc/<pid>/task/<tid>/comm) can always be
+ * read or written by the members of the corresponding
+ * thread group.
+ */
+ return 0;
+ }
+
+ return generic_permission(inode, mask);
+}
+
+static const struct inode_operations proc_tid_comm_inode_operations = {
+ .permission = proc_tid_comm_permission,
+};
+
+/*
* Tasks
*/
static const struct pid_entry tid_base_stuff[] = {
@@ -2908,7 +2950,9 @@
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations),
#endif
- REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
+ NOD("comm", S_IFREG|S_IRUGO|S_IWUSR,
+ &proc_tid_comm_inode_operations,
+ &proc_pid_set_comm_operations, {}),
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
ONE("syscall", S_IRUSR, proc_pid_syscall),
#endif
@@ -2976,6 +3020,9 @@
REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
REG("setgroups", S_IRUGO|S_IWUSR, proc_setgroups_operations),
#endif
+#ifdef CONFIG_CPU_FREQ_STAT
+ ONE("time_in_state", 0444, proc_time_in_state_show),
+#endif
};
static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx)
diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c
index 91a4e642..b4bf663 100644
--- a/fs/proc/kcore.c
+++ b/fs/proc/kcore.c
@@ -430,6 +430,7 @@
static ssize_t
read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
{
+ char *buf = file->private_data;
ssize_t acc = 0;
size_t size, tsz;
size_t elf_buflen;
@@ -500,23 +501,20 @@
if (clear_user(buffer, tsz))
return -EFAULT;
} else if (is_vmalloc_or_module_addr((void *)start)) {
- char * elf_buf;
-
- elf_buf = kzalloc(tsz, GFP_KERNEL);
- if (!elf_buf)
- return -ENOMEM;
- vread(elf_buf, (char *)start, tsz);
+ vread(buf, (char *)start, tsz);
/* we have to zero-fill user buffer even if no read */
- if (copy_to_user(buffer, elf_buf, tsz)) {
- kfree(elf_buf);
+ if (copy_to_user(buffer, buf, tsz))
return -EFAULT;
- }
- kfree(elf_buf);
} else {
if (kern_addr_valid(start)) {
unsigned long n;
- n = copy_to_user(buffer, (char *)start, tsz);
+ /*
+ * Using bounce buffer to bypass the
+ * hardened user copy kernel text checks.
+ */
+ memcpy(buf, (char *) start, tsz);
+ n = copy_to_user(buffer, buf, tsz);
/*
* We cannot distinguish between fault on source
* and fault on destination. When this happens
@@ -549,6 +547,11 @@
{
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
+
+ filp->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!filp->private_data)
+ return -ENOMEM;
+
if (kcore_need_update)
kcore_update_ram();
if (i_size_read(inode) != proc_root_kcore->size) {
@@ -559,10 +562,16 @@
return 0;
}
+static int release_kcore(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
static const struct file_operations proc_kcore_operations = {
.read = read_kcore,
.open = open_kcore,
+ .release = release_kcore,
.llseek = default_llseek,
};
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 9e772f1..7863ed5 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -121,6 +121,13 @@
if (IS_ERR(sb))
return ERR_CAST(sb);
+ /*
+ * procfs isn't actually a stacking filesystem; however, there is
+ * too much magic going on inside it to permit stacking things on
+ * top of it
+ */
+ sb->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
+
if (!proc_parse_options(options, ns)) {
deactivate_locked_super(sb);
return ERR_PTR(-EINVAL);
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 4adcf78..f0ad208 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -502,20 +502,50 @@
u64 swap_pss;
};
+static void smaps_account(struct mem_size_stats *mss, struct page *page,
+ unsigned long size, bool young, bool dirty)
+{
+ int mapcount;
-static void smaps_pte_entry(pte_t ptent, unsigned long addr,
- unsigned long ptent_size, struct mm_walk *walk)
+ if (PageAnon(page))
+ mss->anonymous += size;
+
+ mss->resident += size;
+ /* Accumulate the size in pages that have been accessed. */
+ if (young || PageReferenced(page))
+ mss->referenced += size;
+ mapcount = page_mapcount(page);
+ if (mapcount >= 2) {
+ u64 pss_delta;
+
+ if (dirty || PageDirty(page))
+ mss->shared_dirty += size;
+ else
+ mss->shared_clean += size;
+ pss_delta = (u64)size << PSS_SHIFT;
+ do_div(pss_delta, mapcount);
+ mss->pss += pss_delta;
+ } else {
+ if (dirty || PageDirty(page))
+ mss->private_dirty += size;
+ else
+ mss->private_clean += size;
+ mss->pss += (u64)size << PSS_SHIFT;
+ }
+}
+
+static void smaps_pte_entry(pte_t *pte, unsigned long addr,
+ struct mm_walk *walk)
{
struct mem_size_stats *mss = walk->private;
struct vm_area_struct *vma = mss->vma;
pgoff_t pgoff = linear_page_index(vma, addr);
struct page *page = NULL;
- int mapcount;
- if (pte_present(ptent)) {
- page = vm_normal_page(vma, addr, ptent);
- } else if (is_swap_pte(ptent)) {
- swp_entry_t swpent = pte_to_swp_entry(ptent);
+ if (pte_present(*pte)) {
+ page = vm_normal_page(vma, addr, *pte);
+ } else if (is_swap_pte(*pte)) {
+ swp_entry_t swpent = pte_to_swp_entry(*pte);
if (!non_swap_entry(swpent)) {
int mapcount;
@@ -532,40 +562,43 @@
}
} else if (is_migration_entry(swpent))
page = migration_entry_to_page(swpent);
- } else if (pte_file(ptent)) {
- if (pte_to_pgoff(ptent) != pgoff)
- mss->nonlinear += ptent_size;
+ } else if (pte_file(*pte)) {
+ if (pte_to_pgoff(*pte) != pgoff)
+ mss->nonlinear += PAGE_SIZE;
}
if (!page)
return;
- if (PageAnon(page))
- mss->anonymous += ptent_size;
-
if (page->index != pgoff)
- mss->nonlinear += ptent_size;
+ mss->nonlinear += PAGE_SIZE;
- mss->resident += ptent_size;
- /* Accumulate the size in pages that have been accessed. */
- if (pte_young(ptent) || PageReferenced(page))
- mss->referenced += ptent_size;
- mapcount = page_mapcount(page);
- if (mapcount >= 2) {
- if (pte_dirty(ptent) || PageDirty(page))
- mss->shared_dirty += ptent_size;
- else
- mss->shared_clean += ptent_size;
- mss->pss += (ptent_size << PSS_SHIFT) / mapcount;
- } else {
- if (pte_dirty(ptent) || PageDirty(page))
- mss->private_dirty += ptent_size;
- else
- mss->private_clean += ptent_size;
- mss->pss += (ptent_size << PSS_SHIFT);
- }
+ smaps_account(mss, page, PAGE_SIZE, pte_young(*pte), pte_dirty(*pte));
}
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr,
+ struct mm_walk *walk)
+{
+ struct mem_size_stats *mss = walk->private;
+ struct vm_area_struct *vma = mss->vma;
+ struct page *page;
+
+ /* FOLL_DUMP will return -EFAULT on huge zero page */
+ page = follow_trans_huge_pmd(vma, addr, pmd, FOLL_DUMP);
+ if (IS_ERR_OR_NULL(page))
+ return;
+ mss->anonymous_thp += HPAGE_PMD_SIZE;
+ smaps_account(mss, page, HPAGE_PMD_SIZE,
+ pmd_young(*pmd), pmd_dirty(*pmd));
+}
+#else
+static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr,
+ struct mm_walk *walk)
+{
+}
+#endif
+
static int smaps_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
struct mm_walk *walk)
{
@@ -575,9 +608,8 @@
spinlock_t *ptl;
if (pmd_trans_huge_lock(pmd, vma, &ptl) == 1) {
- smaps_pte_entry(*(pte_t *)pmd, addr, HPAGE_PMD_SIZE, walk);
+ smaps_pmd_entry(pmd, addr, walk);
spin_unlock(ptl);
- mss->anonymous_thp += HPAGE_PMD_SIZE;
return 0;
}
@@ -590,7 +622,7 @@
*/
pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
for (; addr != end; pte++, addr += PAGE_SIZE)
- smaps_pte_entry(*pte, addr, PAGE_SIZE, walk);
+ smaps_pte_entry(pte, addr, walk);
pte_unmap_unlock(pte - 1, ptl);
cond_resched();
return 0;
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c
index 73ca174..f68d0fe 100644
--- a/fs/proc_namespace.c
+++ b/fs/proc_namespace.c
@@ -112,7 +112,9 @@
if (err)
goto out;
show_mnt_opts(m, mnt);
- if (sb->s_op->show_options)
+ if (sb->s_op->show_options2)
+ err = sb->s_op->show_options2(mnt, m, mnt_path.dentry);
+ else if (sb->s_op->show_options)
err = sb->s_op->show_options(m, mnt_path.dentry);
seq_puts(m, " 0 0\n");
out:
@@ -173,7 +175,9 @@
err = show_sb_opts(m, sb);
if (err)
goto out;
- if (sb->s_op->show_options)
+ if (sb->s_op->show_options2) {
+ err = sb->s_op->show_options2(mnt, m, mnt->mnt_root);
+ } else if (sb->s_op->show_options)
err = sb->s_op->show_options(m, mnt->mnt_root);
seq_putc(m, '\n');
out:
diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c
index 971928a..0466b55 100644
--- a/fs/sdcardfs/dentry.c
+++ b/fs/sdcardfs/dentry.c
@@ -34,6 +34,8 @@
struct dentry *parent_lower_dentry = NULL;
struct dentry *lower_cur_parent_dentry = NULL;
struct dentry *lower_dentry = NULL;
+ struct inode *inode;
+ struct sdcardfs_inode_data *data;
if (flags & LOOKUP_RCU)
return -ECHILD;
@@ -46,7 +48,8 @@
spin_unlock(&dentry->d_lock);
/* check uninitialized obb_dentry and
- * whether the base obbpath has been changed or not */
+ * whether the base obbpath has been changed or not
+ */
if (is_obbpath_invalid(dentry)) {
d_drop(dentry);
return 0;
@@ -59,6 +62,14 @@
lower_dentry = lower_path.dentry;
lower_cur_parent_dentry = dget_parent(lower_dentry);
+ if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) {
+ err = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
+ if (err == 0) {
+ d_drop(dentry);
+ goto out;
+ }
+ }
+
spin_lock(&lower_dentry->d_lock);
if (d_unhashed(lower_dentry)) {
spin_unlock(&lower_dentry->d_lock);
@@ -76,17 +87,13 @@
if (dentry < lower_dentry) {
spin_lock(&dentry->d_lock);
- spin_lock(&lower_dentry->d_lock);
+ spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED);
} else {
spin_lock(&lower_dentry->d_lock);
- spin_lock(&dentry->d_lock);
+ spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
}
- if (dentry->d_name.len != lower_dentry->d_name.len) {
- __d_drop(dentry);
- err = 0;
- } else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name,
- dentry->d_name.len) != 0) {
+ if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) {
__d_drop(dentry);
err = 0;
}
@@ -98,6 +105,21 @@
spin_unlock(&dentry->d_lock);
spin_unlock(&lower_dentry->d_lock);
}
+ if (!err)
+ goto out;
+
+ /* If our top's inode is gone, we may be out of date */
+ inode = igrab(dentry->d_inode);
+ if (inode) {
+ data = top_data_get(SDCARDFS_I(inode));
+ if (!data || data->abandoned) {
+ d_drop(dentry);
+ err = 0;
+ }
+ if (data)
+ data_put(data);
+ iput(inode);
+ }
out:
dput(parent_dentry);
@@ -110,12 +132,10 @@
static void sdcardfs_d_release(struct dentry *dentry)
{
/* release and reset the lower paths */
- if(has_graft_path(dentry)) {
+ if (has_graft_path(dentry))
sdcardfs_put_reset_orig_path(dentry);
- }
sdcardfs_put_reset_lower_path(dentry);
free_dentry_private_data(dentry);
- return;
}
static int sdcardfs_hash_ci(const struct dentry *dentry,
@@ -132,12 +152,10 @@
unsigned long hash;
name = qstr->name;
- //len = vfat_striptail_len(qstr);
len = qstr->len;
hash = init_name_hash();
while (len--)
- //hash = partial_name_hash(nls_tolower(t, *name++), hash);
hash = partial_name_hash(tolower(*name++), hash);
qstr->hash = end_name_hash(hash);
@@ -151,35 +169,25 @@
const struct dentry *dentry,
unsigned int len, const char *str, const struct qstr *name)
{
- /* This function is copy of vfat_cmpi */
- // FIXME Should we support national language?
- //struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io;
- //unsigned int alen, blen;
+ /* FIXME Should we support national language? */
- /* A filename cannot end in '.' or we treat it like it has none */
- /*
- alen = vfat_striptail_len(name);
- blen = __vfat_striptail_len(len, str);
- if (alen == blen) {
- if (nls_strnicmp(t, name->name, str, alen) == 0)
- return 0;
- }
- */
if (name->len == len) {
- if (strncasecmp(name->name, str, len) == 0)
+ if (str_n_case_eq(name->name, str, len))
return 0;
}
return 1;
}
-static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) {
+static void sdcardfs_canonical_path(const struct path *path,
+ struct path *actual_path)
+{
sdcardfs_get_real_lower(path->dentry, actual_path);
}
const struct dentry_operations sdcardfs_ci_dops = {
.d_revalidate = sdcardfs_d_revalidate,
.d_release = sdcardfs_d_release,
- .d_hash = sdcardfs_hash_ci,
+ .d_hash = sdcardfs_hash_ci,
.d_compare = sdcardfs_cmp_ci,
.d_canonical_path = sdcardfs_canonical_path,
};
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index 128b3e5..d567edd 100644
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -26,100 +26,302 @@
struct sdcardfs_inode_info *pi = SDCARDFS_I(parent);
struct sdcardfs_inode_info *ci = SDCARDFS_I(child);
- ci->perm = PERM_INHERIT;
- ci->userid = pi->userid;
- ci->d_uid = pi->d_uid;
- ci->under_android = pi->under_android;
+ ci->data->perm = PERM_INHERIT;
+ ci->data->userid = pi->data->userid;
+ ci->data->d_uid = pi->data->d_uid;
+ ci->data->under_android = pi->data->under_android;
+ ci->data->under_cache = pi->data->under_cache;
+ ci->data->under_obb = pi->data->under_obb;
+ set_top(ci, pi->top_data);
}
/* helper function for derived state */
-void setup_derived_state(struct inode *inode, perm_t perm,
- userid_t userid, uid_t uid, bool under_android)
+void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
+ uid_t uid, bool under_android,
+ struct sdcardfs_inode_data *top)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
- info->perm = perm;
- info->userid = userid;
- info->d_uid = uid;
- info->under_android = under_android;
+ info->data->perm = perm;
+ info->data->userid = userid;
+ info->data->d_uid = uid;
+ info->data->under_android = under_android;
+ info->data->under_cache = false;
+ info->data->under_obb = false;
+ set_top(info, top);
}
-/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */
-void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry)
+/* While renaming, there is a point where we want the path from dentry,
+ * but the name from newdentry
+ */
+void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
+ const struct qstr *name)
{
- struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_data *parent_data =
+ SDCARDFS_I(parent->d_inode)->data;
appid_t appid;
+ unsigned long user_num;
+ int err;
+ struct qstr q_Android = QSTR_LITERAL("Android");
+ struct qstr q_data = QSTR_LITERAL("data");
+ struct qstr q_obb = QSTR_LITERAL("obb");
+ struct qstr q_media = QSTR_LITERAL("media");
+ struct qstr q_cache = QSTR_LITERAL("cache");
/* By default, each inode inherits from its parent.
* the properties are maintained on its private fields
* because the inode attributes will be modified with that of
* its lower inode.
- * The derived state will be updated on the last
- * stage of each system call by fix_derived_permission(inode).
+ * These values are used by our custom permission call instead
+ * of using the inode permissions.
*/
inherit_derived_state(parent->d_inode, dentry->d_inode);
+ /* Files don't get special labels */
+ if (!S_ISDIR(dentry->d_inode->i_mode))
+ return;
/* Derive custom permissions based on parent and current node */
- switch (parent_info->perm) {
- case PERM_INHERIT:
- /* Already inherited above */
- break;
- case PERM_PRE_ROOT:
- /* Legacy internal layout places users at top level */
- info->perm = PERM_ROOT;
- info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10);
- break;
- case PERM_ROOT:
- /* Assume masked off by default. */
- if (!strcasecmp(newdentry->d_name.name, "Android")) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID;
- info->under_android = true;
- }
- break;
- case PERM_ANDROID:
- if (!strcasecmp(newdentry->d_name.name, "data")) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID_DATA;
- } else if (!strcasecmp(newdentry->d_name.name, "obb")) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID_OBB;
- /* Single OBB directory is always shared */
- } else if (!strcasecmp(newdentry->d_name.name, "media")) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID_MEDIA;
- }
- break;
- case PERM_ANDROID_DATA:
- case PERM_ANDROID_OBB:
- case PERM_ANDROID_MEDIA:
- appid = get_appid(sbi->pkgl_id, newdentry->d_name.name);
- if (appid != 0) {
- info->d_uid = multiuser_get_uid(parent_info->userid, appid);
- }
- break;
+ switch (parent_data->perm) {
+ case PERM_INHERIT:
+ case PERM_ANDROID_PACKAGE_CACHE:
+ /* Already inherited above */
+ break;
+ case PERM_PRE_ROOT:
+ /* Legacy internal layout places users at top level */
+ info->data->perm = PERM_ROOT;
+ err = kstrtoul(name->name, 10, &user_num);
+ if (err)
+ info->data->userid = 0;
+ else
+ info->data->userid = user_num;
+ set_top(info, info->data);
+ break;
+ case PERM_ROOT:
+ /* Assume masked off by default. */
+ if (qstr_case_eq(name, &q_Android)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->data->perm = PERM_ANDROID;
+ info->data->under_android = true;
+ set_top(info, info->data);
+ }
+ break;
+ case PERM_ANDROID:
+ if (qstr_case_eq(name, &q_data)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->data->perm = PERM_ANDROID_DATA;
+ set_top(info, info->data);
+ } else if (qstr_case_eq(name, &q_obb)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->data->perm = PERM_ANDROID_OBB;
+ info->data->under_obb = true;
+ set_top(info, info->data);
+ /* Single OBB directory is always shared */
+ } else if (qstr_case_eq(name, &q_media)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->data->perm = PERM_ANDROID_MEDIA;
+ set_top(info, info->data);
+ }
+ break;
+ case PERM_ANDROID_OBB:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ info->data->perm = PERM_ANDROID_PACKAGE;
+ appid = get_appid(name->name);
+ if (appid != 0 && !is_excluded(name->name, parent_data->userid))
+ info->data->d_uid =
+ multiuser_get_uid(parent_data->userid, appid);
+ set_top(info, info->data);
+ break;
+ case PERM_ANDROID_PACKAGE:
+ if (qstr_case_eq(name, &q_cache)) {
+ info->data->perm = PERM_ANDROID_PACKAGE_CACHE;
+ info->data->under_cache = true;
+ }
+ break;
}
}
void get_derived_permission(struct dentry *parent, struct dentry *dentry)
{
- get_derived_permission_new(parent, dentry, dentry);
+ get_derived_permission_new(parent, dentry, &dentry->d_name);
}
-void get_derive_permissions_recursive(struct dentry *parent) {
- struct dentry *dentry;
- list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
- if (dentry && dentry->d_inode) {
- mutex_lock(&dentry->d_inode->i_mutex);
- get_derived_permission(parent, dentry);
- fix_derived_permission(dentry->d_inode);
- get_derive_permissions_recursive(dentry);
- mutex_unlock(&dentry->d_inode->i_mutex);
+static appid_t get_type(const char *name)
+{
+ const char *ext = strrchr(name, '.');
+ appid_t id;
+
+ if (ext && ext[0]) {
+ ext = &ext[1];
+ id = get_ext_gid(ext);
+ return id?:AID_MEDIA_RW;
+ }
+ return AID_MEDIA_RW;
+}
+
+void fixup_lower_ownership(struct dentry *dentry, const char *name)
+{
+ struct path path;
+ struct inode *inode;
+ struct inode *delegated_inode = NULL;
+ int error;
+ struct sdcardfs_inode_info *info;
+ struct sdcardfs_inode_data *info_d;
+ struct sdcardfs_inode_data *info_top;
+ perm_t perm;
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ uid_t uid = sbi->options.fs_low_uid;
+ gid_t gid = sbi->options.fs_low_gid;
+ struct iattr newattrs;
+
+ info = SDCARDFS_I(dentry->d_inode);
+ info_d = info->data;
+ perm = info_d->perm;
+ if (info_d->under_obb) {
+ perm = PERM_ANDROID_OBB;
+ } else if (info_d->under_cache) {
+ perm = PERM_ANDROID_PACKAGE_CACHE;
+ } else if (perm == PERM_INHERIT) {
+ info_top = top_data_get(info);
+ perm = info_top->perm;
+ data_put(info_top);
+ }
+
+ switch (perm) {
+ case PERM_ROOT:
+ case PERM_ANDROID:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ case PERM_ANDROID_PACKAGE:
+ case PERM_ANDROID_PACKAGE_CACHE:
+ uid = multiuser_get_uid(info_d->userid, uid);
+ break;
+ case PERM_ANDROID_OBB:
+ uid = AID_MEDIA_OBB;
+ break;
+ case PERM_PRE_ROOT:
+ default:
+ break;
+ }
+ switch (perm) {
+ case PERM_ROOT:
+ case PERM_ANDROID:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
+ else
+ gid = multiuser_get_uid(info_d->userid, get_type(name));
+ break;
+ case PERM_ANDROID_OBB:
+ gid = AID_MEDIA_OBB;
+ break;
+ case PERM_ANDROID_PACKAGE:
+ if (uid_is_app(info_d->d_uid))
+ gid = multiuser_get_ext_gid(info_d->d_uid);
+ else
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
+ break;
+ case PERM_ANDROID_PACKAGE_CACHE:
+ if (uid_is_app(info_d->d_uid))
+ gid = multiuser_get_ext_cache_gid(info_d->d_uid);
+ else
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
+ break;
+ case PERM_PRE_ROOT:
+ default:
+ break;
+ }
+
+ sdcardfs_get_lower_path(dentry, &path);
+ inode = path.dentry->d_inode;
+ if (path.dentry->d_inode->i_gid.val != gid || path.dentry->d_inode->i_uid.val != uid) {
+retry_deleg:
+ newattrs.ia_valid = ATTR_GID | ATTR_UID | ATTR_FORCE;
+ newattrs.ia_uid = make_kuid(current_user_ns(), uid);
+ newattrs.ia_gid = make_kgid(current_user_ns(), gid);
+ if (!S_ISDIR(inode->i_mode))
+ newattrs.ia_valid |=
+ ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+ mutex_lock(&inode->i_mutex);
+ error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid);
+ if (!error)
+ error = notify_change2(path.mnt, path.dentry, &newattrs, &delegated_inode);
+ mutex_unlock(&inode->i_mutex);
+ if (delegated_inode) {
+ error = break_deleg_wait(&delegated_inode);
+ if (!error)
+ goto retry_deleg;
+ }
+ if (error)
+ pr_debug("sdcardfs: Failed to touch up lower fs gid/uid for %s\n", name);
+ }
+ sdcardfs_put_lower_path(dentry, &path);
+}
+
+static int descendant_may_need_fixup(struct sdcardfs_inode_data *data,
+ struct limit_search *limit)
+{
+ if (data->perm == PERM_ROOT)
+ return (limit->flags & BY_USERID) ?
+ data->userid == limit->userid : 1;
+ if (data->perm == PERM_PRE_ROOT || data->perm == PERM_ANDROID)
+ return 1;
+ return 0;
+}
+
+static int needs_fixup(perm_t perm)
+{
+ if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB
+ || perm == PERM_ANDROID_MEDIA)
+ return 1;
+ return 0;
+}
+
+static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit, int depth)
+{
+ struct dentry *child;
+ struct sdcardfs_inode_info *info;
+
+ /*
+ * All paths will terminate their recursion on hitting PERM_ANDROID_OBB,
+ * PERM_ANDROID_MEDIA, or PERM_ANDROID_DATA. This happens at a depth of
+ * at most 3.
+ */
+ WARN(depth > 3, "%s: Max expected depth exceeded!\n", __func__);
+ spin_lock_nested(&dentry->d_lock, depth);
+ if (!dentry->d_inode) {
+ spin_unlock(&dentry->d_lock);
+ return;
+ }
+ info = SDCARDFS_I(dentry->d_inode);
+
+ if (needs_fixup(info->data->perm)) {
+ list_for_each_entry(child, &dentry->d_subdirs, d_child) {
+ spin_lock_nested(&child->d_lock, depth + 1);
+ if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) {
+ if (child->d_inode) {
+ get_derived_permission(dentry, child);
+ fixup_tmp_permissions(child->d_inode);
+ spin_unlock(&child->d_lock);
+ break;
+ }
+ }
+ spin_unlock(&child->d_lock);
+ }
+ } else if (descendant_may_need_fixup(info->data, limit)) {
+ list_for_each_entry(child, &dentry->d_subdirs, d_child) {
+ __fixup_perms_recursive(child, limit, depth + 1);
}
}
+ spin_unlock(&dentry->d_lock);
+}
+
+void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit)
+{
+ __fixup_perms_recursive(dentry, limit, 0);
}
/* main function for updating derived permission */
@@ -127,41 +329,38 @@
{
struct dentry *parent;
- if(!dentry || !dentry->d_inode) {
- printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
+ if (!dentry || !dentry->d_inode) {
+ pr_err("sdcardfs: %s: invalid dentry\n", __func__);
return;
}
/* FIXME:
* 1. need to check whether the dentry is updated or not
* 2. remove the root dentry update
*/
- mutex_lock(&dentry->d_inode->i_mutex);
- if(IS_ROOT(dentry)) {
- //setup_default_pre_root_state(dentry->d_inode);
- } else {
+ if (!IS_ROOT(dentry)) {
parent = dget_parent(dentry);
- if(parent) {
+ if (parent) {
get_derived_permission(parent, dentry);
dput(parent);
}
}
- fix_derived_permission(dentry->d_inode);
- mutex_unlock(&dentry->d_inode->i_mutex);
+ fixup_tmp_permissions(dentry->d_inode);
}
int need_graft_path(struct dentry *dentry)
{
int ret = 0;
struct dentry *parent = dget_parent(dentry);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_info *parent_info = SDCARDFS_I(parent->d_inode);
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ struct qstr obb = QSTR_LITERAL("obb");
- if(parent_info->perm == PERM_ANDROID &&
- !strcasecmp(dentry->d_name.name, "obb")) {
+ if (parent_info->data->perm == PERM_ANDROID &&
+ qstr_case_eq(&dentry->d_name, &obb)) {
/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
- if(!(sbi->options.multiuser == false
- && parent_info->userid == 0)) {
+ if (!(sbi->options.multiuser == false
+ && parent_info->data->userid == 0)) {
ret = 1;
}
}
@@ -175,36 +374,40 @@
struct sdcardfs_dentry_info *di = SDCARDFS_D(dent);
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb);
char *path_buf, *obbpath_s;
+ int need_put = 0;
+ struct path lower_path;
/* check the base obbpath has been changed.
* this routine can check an uninitialized obb dentry as well.
- * regarding the uninitialized obb, refer to the sdcardfs_mkdir() */
+ * regarding the uninitialized obb, refer to the sdcardfs_mkdir()
+ */
spin_lock(&di->lock);
- if(di->orig_path.dentry) {
- if(!di->lower_path.dentry) {
+ if (di->orig_path.dentry) {
+ if (!di->lower_path.dentry) {
ret = 1;
} else {
path_get(&di->lower_path);
- //lower_parent = lock_parent(lower_path->dentry);
path_buf = kmalloc(PATH_MAX, GFP_ATOMIC);
- if(!path_buf) {
+ if (!path_buf) {
ret = 1;
- printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__);
+ pr_err("sdcardfs: fail to allocate path_buf in %s.\n", __func__);
} else {
obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX);
if (d_unhashed(di->lower_path.dentry) ||
- strcasecmp(sbi->obbpath_s, obbpath_s)) {
+ !str_case_eq(sbi->obbpath_s, obbpath_s)) {
ret = 1;
}
kfree(path_buf);
}
- //unlock_dir(lower_parent);
- path_put(&di->lower_path);
+ pathcpy(&lower_path, &di->lower_path);
+ need_put = 1;
}
}
spin_unlock(&di->lock);
+ if (need_put)
+ path_put(&lower_path);
return ret;
}
@@ -212,17 +415,18 @@
{
int ret = 0;
struct dentry *parent = dget_parent(dentry);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_info *parent_info = SDCARDFS_I(parent->d_inode);
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ struct qstr q_obb = QSTR_LITERAL("obb");
spin_lock(&SDCARDFS_D(dentry)->lock);
if (sbi->options.multiuser) {
- if(parent_info->perm == PERM_PRE_ROOT &&
- !strcasecmp(dentry->d_name.name, "obb")) {
+ if (parent_info->data->perm == PERM_PRE_ROOT &&
+ qstr_case_eq(&dentry->d_name, &q_obb)) {
ret = 1;
}
- } else if (parent_info->perm == PERM_ANDROID &&
- !strcasecmp(dentry->d_name.name, "obb")) {
+ } else if (parent_info->data->perm == PERM_ANDROID &&
+ qstr_case_eq(&dentry->d_name, &q_obb)) {
ret = 1;
}
spin_unlock(&SDCARDFS_D(dentry)->lock);
@@ -232,7 +436,8 @@
/* The lower_path will be stored to the dentry's orig_path
* and the base obbpath will be copyed to the lower_path variable.
* if an error returned, there's no change in the lower_path
- * returns: -ERRNO if error (0: no error) */
+ * returns: -ERRNO if error (0: no error)
+ */
int setup_obb_dentry(struct dentry *dentry, struct path *lower_path)
{
int err = 0;
@@ -241,23 +446,24 @@
/* A local obb dentry must have its own orig_path to support rmdir
* and mkdir of itself. Usually, we expect that the sbi->obbpath
- * is avaiable on this stage. */
+ * is avaiable on this stage.
+ */
sdcardfs_set_orig_path(dentry, lower_path);
err = kern_path(sbi->obbpath_s,
LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath);
- if(!err) {
+ if (!err) {
/* the obbpath base has been found */
- printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n");
pathcpy(lower_path, &obbpath);
} else {
/* if the sbi->obbpath is not available, we can optionally
* setup the lower_path with its orig_path.
* but, the current implementation just returns an error
* because the sdcard daemon also regards this case as
- * a lookup fail. */
- printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n");
+ * a lookup fail.
+ */
+ pr_info("sdcardfs: the sbi->obbpath is not available\n");
}
return err;
}
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index 8291117..8b0d5d2 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -65,7 +65,7 @@
/* check disk space */
if (!check_min_free_space(dentry, count, 0)) {
- printk(KERN_INFO "No minimum free space.\n");
+ pr_err("No minimum free space.\n");
return -ENOSPC;
}
@@ -113,6 +113,10 @@
if (lower_file->f_op->unlocked_ioctl)
err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
+ /* some ioctls can change inode attributes (EXT2_IOC_SETFLAGS) */
+ if (!err)
+ sdcardfs_copy_and_fix_attrs(file_inode(file),
+ file_inode(lower_file));
out:
return err;
}
@@ -160,8 +164,7 @@
lower_file = sdcardfs_lower_file(file);
if (willwrite && !lower_file->f_mapping->a_ops->writepage) {
err = -EINVAL;
- printk(KERN_ERR "sdcardfs: lower file system does not "
- "support writeable mmap\n");
+ pr_err("sdcardfs: lower file system does not support writeable mmap\n");
goto out;
}
@@ -173,16 +176,10 @@
if (!SDCARDFS_F(file)->lower_vm_ops) {
err = lower_file->f_op->mmap(lower_file, vma);
if (err) {
- printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err);
+ pr_err("sdcardfs: lower mmap failed %d\n", err);
goto out;
}
saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */
- err = do_munmap(current->mm, vma->vm_start,
- vma->vm_end - vma->vm_start);
- if (err) {
- printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err);
- goto out;
- }
}
/*
@@ -195,6 +192,9 @@
file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */
if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */
SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops;
+ vma->vm_private_data = file;
+ get_file(lower_file);
+ vma->vm_file = lower_file;
out:
return err;
@@ -216,16 +216,13 @@
goto out_err;
}
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(parent->d_inode, &dentry->d_name)) {
err = -EACCES;
goto out_err;
}
/* save current_cred and override it */
- OVERRIDE_CRED(sbi, saved_cred);
+ OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode));
file->private_data =
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
@@ -251,9 +248,8 @@
if (err)
kfree(SDCARDFS_F(file));
- else {
+ else
sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode));
- }
out_revert_cred:
REVERT_CRED(saved_cred);
@@ -323,6 +319,85 @@
return err;
}
+/*
+ * Sdcardfs cannot use generic_file_llseek as ->llseek, because it would
+ * only set the offset of the upper file. So we have to implement our
+ * own method to set both the upper and lower file offsets
+ * consistently.
+ */
+static loff_t sdcardfs_file_llseek(struct file *file, loff_t offset, int whence)
+{
+ int err;
+ struct file *lower_file;
+
+ err = generic_file_llseek(file, offset, whence);
+ if (err < 0)
+ goto out;
+
+ lower_file = sdcardfs_lower_file(file);
+ err = generic_file_llseek(lower_file, offset, whence);
+
+out:
+ return err;
+}
+
+/*
+ * Sdcardfs read_iter, redirect modified iocb to lower read_iter
+ */
+ssize_t sdcardfs_read_iter(struct kiocb *iocb, struct iov_iter *iter)
+{
+ int err;
+ struct file *file = iocb->ki_filp, *lower_file;
+
+ lower_file = sdcardfs_lower_file(file);
+ if (!lower_file->f_op->read_iter) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ get_file(lower_file); /* prevent lower_file from being released */
+ iocb->ki_filp = lower_file;
+ err = lower_file->f_op->read_iter(iocb, iter);
+ iocb->ki_filp = file;
+ fput(lower_file);
+ /* update upper inode atime as needed */
+ if (err >= 0 || err == -EIOCBQUEUED)
+ fsstack_copy_attr_atime(file->f_path.dentry->d_inode,
+ file_inode(lower_file));
+out:
+ return err;
+}
+
+/*
+ * Sdcardfs write_iter, redirect modified iocb to lower write_iter
+ */
+ssize_t sdcardfs_write_iter(struct kiocb *iocb, struct iov_iter *iter)
+{
+ int err;
+ struct file *file = iocb->ki_filp, *lower_file;
+
+ lower_file = sdcardfs_lower_file(file);
+ if (!lower_file->f_op->write_iter) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ get_file(lower_file); /* prevent lower_file from being released */
+ iocb->ki_filp = lower_file;
+ err = lower_file->f_op->write_iter(iocb, iter);
+ iocb->ki_filp = file;
+ fput(lower_file);
+ /* update upper inode times/sizes as needed */
+ if (err >= 0 || err == -EIOCBQUEUED) {
+ fsstack_copy_inode_size(file->f_path.dentry->d_inode,
+ file_inode(lower_file));
+ fsstack_copy_attr_times(file->f_path.dentry->d_inode,
+ file_inode(lower_file));
+ }
+out:
+ return err;
+}
+
const struct file_operations sdcardfs_main_fops = {
.llseek = generic_file_llseek,
.read = sdcardfs_read,
@@ -337,11 +412,13 @@
.release = sdcardfs_file_release,
.fsync = sdcardfs_fsync,
.fasync = sdcardfs_fasync,
+ .read_iter = sdcardfs_read_iter,
+ .write_iter = sdcardfs_write_iter,
};
/* trimmed directory options */
const struct file_operations sdcardfs_dir_fops = {
- .llseek = generic_file_llseek,
+ .llseek = sdcardfs_file_llseek,
.read = generic_read_dir,
.iterate = sdcardfs_readdir,
.unlocked_ioctl = sdcardfs_unlocked_ioctl,
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 69060ee..39b8b2b 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -19,18 +19,26 @@
*/
#include "sdcardfs.h"
+#include <linux/fs_struct.h>
+#include <linux/ratelimit.h>
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
-const struct cred * override_fsids(struct sdcardfs_sb_info* sbi)
+const struct cred *override_fsids(struct sdcardfs_sb_info *sbi,
+ struct sdcardfs_inode_data *data)
{
- struct cred * cred;
- const struct cred * old_cred;
+ struct cred *cred;
+ const struct cred *old_cred;
+ uid_t uid;
cred = prepare_creds();
if (!cred)
return NULL;
- cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid);
+ if (data->under_obb)
+ uid = AID_MEDIA_OBB;
+ else
+ uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid);
+ cred->fsuid = make_kuid(&init_user_ns, uid);
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
old_cred = override_creds(cred);
@@ -39,9 +47,9 @@
}
/* Do not directly use this function, use REVERT_CRED() instead. */
-void revert_fsids(const struct cred * old_cred)
+void revert_fsids(const struct cred *old_cred)
{
- const struct cred * cur_cred;
+ const struct cred *cur_cred;
cur_cred = current->cred;
revert_creds(old_cred);
@@ -53,38 +61,54 @@
{
int err;
struct dentry *lower_dentry;
+ struct vfsmount *lower_dentry_mnt;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
const struct cred *saved_cred = NULL;
+ struct fs_struct *saved_fs;
+ struct fs_struct *copied_fs;
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_dentry_mnt = lower_path.mnt;
lower_parent_dentry = lock_parent(lower_dentry);
/* set last 16bytes of mode field to 0664 */
mode = (mode & S_IFMT) | 00664;
- err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, want_excl);
+
+ /* temporarily change umask for lower fs write */
+ saved_fs = current->fs;
+ copied_fs = copy_fs_struct(current->fs);
+ if (!copied_fs) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+ current->fs = copied_fs;
+ current->fs->umask = 0;
+ err = vfs_create2(lower_dentry_mnt, lower_parent_dentry->d_inode, lower_dentry, mode, want_excl);
if (err)
goto out;
- err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid);
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path,
+ SDCARDFS_I(dir)->data->userid);
if (err)
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode);
+ fixup_lower_ownership(dentry, dentry->d_name.name);
out:
+ current->fs = saved_fs;
+ free_fs_struct(copied_fs);
+out_unlock:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED(saved_cred);
@@ -138,28 +162,27 @@
{
int err;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
struct dentry *lower_dir_dentry;
struct path lower_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
dget(lower_dentry);
lower_dir_dentry = lock_parent(lower_dentry);
- err = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
+ err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry, NULL);
/*
* Note: unlinking on top of NFS can cause silly-renamed files.
@@ -219,14 +242,15 @@
}
#endif
-static int touch(char *abs_path, mode_t mode) {
+static int touch(char *abs_path, mode_t mode)
+{
struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode);
+
if (IS_ERR(filp)) {
if (PTR_ERR(filp) == -EEXIST) {
return 0;
- }
- else {
- printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n",
+ } else {
+ pr_err("sdcardfs: failed to open(%s): %ld\n",
abs_path, PTR_ERR(filp));
return PTR_ERR(filp);
}
@@ -240,31 +264,29 @@
int err;
int make_nomedia_in_obb = 0;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
const struct cred *saved_cred = NULL;
- struct sdcardfs_inode_info *pi = SDCARDFS_I(dir);
- char *page_buf;
- char *nomedia_dir_name;
- char *nomedia_fullpath;
- int fullpath_namelen;
+ struct sdcardfs_inode_data *pd = SDCARDFS_I(dir)->data;
int touch_err = 0;
+ struct fs_struct *saved_fs;
+ struct fs_struct *copied_fs;
+ struct qstr q_obb = QSTR_LITERAL("obb");
+ struct qstr q_data = QSTR_LITERAL("data");
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
/* check disk space */
if (!check_min_free_space(dentry, 0, 1)) {
- printk(KERN_INFO "sdcardfs: No minimum free space.\n");
+ pr_err("sdcardfs: No minimum free space.\n");
err = -ENOSPC;
goto out_revert;
}
@@ -272,87 +294,84 @@
/* the lower_dentry is negative here */
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_parent_dentry = lock_parent(lower_dentry);
/* set last 16bytes of mode field to 0775 */
mode = (mode & S_IFMT) | 00775;
- err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode);
- if (err)
+ /* temporarily change umask for lower fs write */
+ saved_fs = current->fs;
+ copied_fs = copy_fs_struct(current->fs);
+ if (!copied_fs) {
+ err = -ENOMEM;
+ unlock_dir(lower_parent_dentry);
+ goto out_unlock;
+ }
+ current->fs = copied_fs;
+ current->fs->umask = 0;
+ err = vfs_mkdir2(lower_mnt, lower_parent_dentry->d_inode, lower_dentry, mode);
+
+ if (err) {
+ unlock_dir(lower_parent_dentry);
goto out;
+ }
/* if it is a local obb dentry, setup it with the base obbpath */
- if(need_graft_path(dentry)) {
+ if (need_graft_path(dentry)) {
err = setup_obb_dentry(dentry, &lower_path);
- if(err) {
+ if (err) {
/* if the sbi->obbpath is not available, the lower_path won't be
* changed by setup_obb_dentry() but the lower path is saved to
* its orig_path. this dentry will be revalidated later.
- * but now, the lower_path should be NULL */
+ * but now, the lower_path should be NULL
+ */
sdcardfs_put_reset_lower_path(dentry);
/* the newly created lower path which saved to its orig_path or
* the lower_path is the base obbpath.
- * therefore, an additional path_get is required */
+ * therefore, an additional path_get is required
+ */
path_get(&lower_path);
} else
make_nomedia_in_obb = 1;
}
- err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
- if (err)
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pd->userid);
+ if (err) {
+ unlock_dir(lower_parent_dentry);
goto out;
+ }
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode);
/* update number of links on parent directory */
set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
-
- if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb"))
- && (pi->perm == PERM_ANDROID) && (pi->userid == 0))
+ fixup_lower_ownership(dentry, dentry->d_name.name);
+ unlock_dir(lower_parent_dentry);
+ if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb))
+ && (pd->perm == PERM_ANDROID) && (pd->userid == 0))
make_nomedia_in_obb = 1;
/* When creating /Android/data and /Android/obb, mark them as .nomedia */
if (make_nomedia_in_obb ||
- ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) {
-
- page_buf = (char *)__get_free_page(GFP_KERNEL);
- if (!page_buf) {
- printk(KERN_ERR "sdcardfs: failed to allocate page buf\n");
- goto out;
- }
-
- nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE);
- if (IS_ERR(nomedia_dir_name)) {
- free_page((unsigned long)page_buf);
- printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n");
- goto out;
- }
-
- fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1;
- fullpath_namelen += strlen("/.nomedia");
- nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL);
- if (!nomedia_fullpath) {
- free_page((unsigned long)page_buf);
- printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n");
- goto out;
- }
-
- strcpy(nomedia_fullpath, nomedia_dir_name);
- free_page((unsigned long)page_buf);
- strcat(nomedia_fullpath, "/.nomedia");
- touch_err = touch(nomedia_fullpath, 0664);
+ ((pd->perm == PERM_ANDROID)
+ && (qstr_case_eq(&dentry->d_name, &q_data)))) {
+ REVERT_CRED(saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dentry->d_inode));
+ set_fs_pwd(current->fs, &lower_path);
+ touch_err = touch(".nomedia", 0664);
if (touch_err) {
- printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n",
- nomedia_fullpath, touch_err);
- kfree(nomedia_fullpath);
+ pr_err("sdcardfs: failed to create .nomedia in %s: %d\n",
+ lower_path.dentry->d_name.name, touch_err);
goto out;
}
- kfree(nomedia_fullpath);
}
out:
- unlock_dir(lower_parent_dentry);
+ current->fs = saved_fs;
+ free_fs_struct(copied_fs);
+out_unlock:
sdcardfs_put_lower_path(dentry, &lower_path);
out_revert:
REVERT_CRED(saved_cred);
@@ -364,29 +383,29 @@
{
struct dentry *lower_dentry;
struct dentry *lower_dir_dentry;
+ struct vfsmount *lower_mnt;
int err;
struct path lower_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
/* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
- * the dentry on the original path should be deleted. */
+ * the dentry on the original path should be deleted.
+ */
sdcardfs_get_real_lower(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_dir_dentry = lock_parent(lower_dentry);
- err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
+ err = vfs_rmdir2(lower_mnt, lower_dir_dentry->d_inode, lower_dentry);
if (err)
goto out;
@@ -450,27 +469,25 @@
struct dentry *lower_new_dentry = NULL;
struct dentry *lower_old_dir_dentry = NULL;
struct dentry *lower_new_dir_dentry = NULL;
+ struct vfsmount *lower_mnt = NULL;
struct dentry *trap = NULL;
- struct dentry *new_parent = NULL;
struct path lower_old_path, lower_new_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) ||
- !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " new_dentry: %s, task:%s\n",
- __func__, new_dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) ||
+ !check_caller_access_to_name(new_dir, &new_dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir));
sdcardfs_get_real_lower(old_dentry, &lower_old_path);
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
lower_old_dentry = lower_old_path.dentry;
lower_new_dentry = lower_new_path.dentry;
+ lower_mnt = lower_old_path.mnt;
lower_old_dir_dentry = dget_parent(lower_old_dentry);
lower_new_dir_dentry = dget_parent(lower_new_dentry);
@@ -486,7 +503,8 @@
goto out;
}
- err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
+ err = vfs_rename2(lower_mnt,
+ lower_old_dir_dentry->d_inode, lower_old_dentry,
lower_new_dir_dentry->d_inode, lower_new_dentry,
NULL, 0);
if (err)
@@ -499,25 +517,11 @@
if (new_dir != old_dir) {
sdcardfs_copy_and_fix_attrs(old_dir, lower_old_dir_dentry->d_inode);
fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode);
-
- /* update the derived permission of the old_dentry
- * with its new parent
- */
- new_parent = dget_parent(new_dentry);
- if(new_parent) {
- if(old_dentry->d_inode) {
- update_derived_permission_lock(old_dentry);
- }
- dput(new_parent);
- }
}
- /* At this point, not all dentry information has been moved, so
- * we pass along new_dentry for the name.*/
- mutex_lock(&old_dentry->d_inode->i_mutex);
- get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry);
- fix_derived_permission(old_dentry->d_inode);
- get_derive_permissions_recursive(old_dentry);
- mutex_unlock(&old_dentry->d_inode->i_mutex);
+ get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name);
+ fixup_tmp_permissions(old_dentry->d_inode);
+ fixup_lower_ownership(old_dentry, new_dentry->d_name.name);
+ d_invalidate(old_dentry); /* Can't fixup ownership recursively :( */
out:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
dput(lower_old_dir_dentry);
@@ -588,16 +592,61 @@
}
#endif
-static int sdcardfs_permission(struct inode *inode, int mask)
+static int sdcardfs_permission_wrn(struct inode *inode, int mask)
+{
+ WARN_RATELIMIT(1, "sdcardfs does not support permission. Use permission2.\n");
+ return -EINVAL;
+}
+
+void copy_attrs(struct inode *dest, const struct inode *src)
+{
+ dest->i_mode = src->i_mode;
+ dest->i_uid = src->i_uid;
+ dest->i_gid = src->i_gid;
+ dest->i_rdev = src->i_rdev;
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+ dest->i_blkbits = src->i_blkbits;
+ dest->i_flags = src->i_flags;
+#ifdef CONFIG_FS_POSIX_ACL
+ dest->i_acl = src->i_acl;
+#endif
+#ifdef CONFIG_SECURITY
+ dest->i_security = src->i_security;
+#endif
+}
+
+static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask)
{
int err;
+ struct inode tmp;
+ struct sdcardfs_inode_data *top = top_data_get(SDCARDFS_I(inode));
+
+ if (!top)
+ return -EINVAL;
/*
* Permission check on sdcardfs inode.
* Calling process should have AID_SDCARD_RW permission
+ * Since generic_permission only needs i_mode, i_uid,
+ * i_gid, and i_sb, we can create a fake inode to pass
+ * this information down in.
+ *
+ * The underlying code may attempt to take locks in some
+ * cases for features we're not using, but if that changes,
+ * locks must be dealt with to avoid undefined behavior.
*/
- err = generic_permission(inode, mask);
-
+ copy_attrs(&tmp, inode);
+ tmp.i_uid = make_kuid(&init_user_ns, top->d_uid);
+ tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top));
+ tmp.i_mode = (inode->i_mode & S_IFMT)
+ | get_mode(mnt, SDCARDFS_I(inode), top);
+ data_put(top);
+ tmp.i_sb = inode->i_sb;
+ if (IS_POSIXACL(inode))
+ pr_warn("%s: This may be undefined behavior...\n", __func__);
+ err = generic_permission(&tmp, mask);
/* XXX
* Original sdcardfs code calls inode_permission(lower_inode,.. )
* for checking inode permission. But doing such things here seems
@@ -614,6 +663,7 @@
* we check it with AID_MEDIA_RW permission
*/
struct inode *lower_inode;
+
OVERRIDE_CRED(SDCARDFS_SB(inode->sb));
lower_inode = sdcardfs_lower_inode(inode);
@@ -626,47 +676,85 @@
}
-static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
+static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia)
+{
+ WARN_RATELIMIT(1, "sdcardfs does not support setattr. User setattr2.\n");
+ return -EINVAL;
+}
+
+static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia)
{
int err;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct inode *inode;
struct inode *lower_inode;
struct path lower_path;
struct iattr lower_ia;
struct dentry *parent;
+ struct inode tmp;
+ struct sdcardfs_inode_data *top;
+ const struct cred *saved_cred = NULL;
inode = dentry->d_inode;
+ top = top_data_get(SDCARDFS_I(inode));
+
+ if (!top)
+ return -EINVAL;
+
+ /*
+ * Permission check on sdcardfs inode.
+ * Calling process should have AID_SDCARD_RW permission
+ * Since generic_permission only needs i_mode, i_uid,
+ * i_gid, and i_sb, we can create a fake inode to pass
+ * this information down in.
+ *
+ * The underlying code may attempt to take locks in some
+ * cases for features we're not using, but if that changes,
+ * locks must be dealt with to avoid undefined behavior.
+ *
+ */
+ copy_attrs(&tmp, inode);
+ tmp.i_uid = make_kuid(&init_user_ns, top->d_uid);
+ tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top));
+ tmp.i_mode = (inode->i_mode & S_IFMT)
+ | get_mode(mnt, SDCARDFS_I(inode), top);
+ tmp.i_size = i_size_read(inode);
+ data_put(top);
+ tmp.i_sb = inode->i_sb;
/*
* Check if user has permission to change inode. We don't check if
* this user can change the lower inode: that should happen when
* calling notify_change on the lower inode.
*/
- err = inode_change_ok(inode, ia);
+ /* prepare our own lower struct iattr (with the lower file) */
+ memcpy(&lower_ia, ia, sizeof(lower_ia));
+ /* Allow touch updating timestamps. A previous permission check ensures
+ * we have write access. Changes to mode, owner, and group are ignored
+ */
+ ia->ia_valid |= ATTR_FORCE;
+ err = inode_change_ok(&tmp, ia);
- /* no vfs_XXX operations required, cred overriding will be skipped. wj*/
if (!err) {
/* check the Android group ID */
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(parent->d_inode, &dentry->d_name))
err = -EACCES;
- }
dput(parent);
}
if (err)
goto out_err;
+ /* save current_cred and override it */
+ OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode));
+
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_inode = sdcardfs_lower_inode(inode);
- /* prepare our own lower struct iattr (with the lower file) */
- memcpy(&lower_ia, ia, sizeof(lower_ia));
if (ia->ia_valid & ATTR_FILE)
lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file);
@@ -683,7 +771,7 @@
if (current->mm)
down_write(¤t->mm->mmap_sem);
if (ia->ia_valid & ATTR_SIZE) {
- err = inode_newsize_ok(inode, ia->ia_size);
+ err = inode_newsize_ok(&tmp, ia->ia_size);
if (err) {
if (current->mm)
up_write(¤t->mm->mmap_sem);
@@ -706,7 +794,7 @@
* tries to open(), unlink(), then ftruncate() a file.
*/
mutex_lock(&lower_dentry->d_inode->i_mutex);
- err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */
+ err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */
NULL);
mutex_unlock(&lower_dentry->d_inode->i_mutex);
if (current->mm)
@@ -725,48 +813,68 @@
out:
sdcardfs_put_lower_path(dentry, &lower_path);
+ REVERT_CRED(saved_cred);
out_err:
return err;
}
+static int sdcardfs_fillattr(struct vfsmount *mnt,
+ struct inode *inode, struct kstat *stat)
+{
+ struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
+ struct sdcardfs_inode_data *top = top_data_get(info);
+
+ if (!top)
+ return -EINVAL;
+
+ stat->dev = inode->i_sb->s_dev;
+ stat->ino = inode->i_ino;
+ stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top);
+ stat->nlink = inode->i_nlink;
+ stat->uid = make_kuid(&init_user_ns, top->d_uid);
+ stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top));
+ stat->rdev = inode->i_rdev;
+ stat->size = i_size_read(inode);
+ stat->atime = inode->i_atime;
+ stat->mtime = inode->i_mtime;
+ stat->ctime = inode->i_ctime;
+ stat->blksize = (1 << inode->i_blkbits);
+ stat->blocks = inode->i_blocks;
+ data_put(top);
+ return 0;
+}
+
static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
- struct dentry *lower_dentry;
- struct inode *inode;
- struct inode *lower_inode;
+ struct kstat lower_stat;
struct path lower_path;
struct dentry *parent;
+ int err;
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(parent->d_inode, &dentry->d_name)) {
dput(parent);
return -EACCES;
}
dput(parent);
- inode = dentry->d_inode;
-
sdcardfs_get_lower_path(dentry, &lower_path);
- lower_dentry = lower_path.dentry;
- lower_inode = sdcardfs_lower_inode(inode);
-
-
- sdcardfs_copy_and_fix_attrs(inode, lower_inode);
- fsstack_copy_inode_size(inode, lower_inode);
-
-
- generic_fillattr(inode, stat);
+ err = vfs_getattr(&lower_path, &lower_stat);
+ if (err)
+ goto out;
+ sdcardfs_copy_and_fix_attrs(dentry->d_inode,
+ lower_path.dentry->d_inode);
+ err = sdcardfs_fillattr(mnt, dentry->d_inode, stat);
+ stat->blocks = lower_stat.blocks;
+out:
sdcardfs_put_lower_path(dentry, &lower_path);
- return 0;
+ return err;
}
const struct inode_operations sdcardfs_symlink_iops = {
- .permission = sdcardfs_permission,
- .setattr = sdcardfs_setattr,
+ .permission2 = sdcardfs_permission,
+ .setattr2 = sdcardfs_setattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
* These methods are *NOT* perfectly tested.
@@ -779,14 +887,14 @@
const struct inode_operations sdcardfs_dir_iops = {
.create = sdcardfs_create,
.lookup = sdcardfs_lookup,
-#if 0
- .permission = sdcardfs_permission,
-#endif
+ .permission = sdcardfs_permission_wrn,
+ .permission2 = sdcardfs_permission,
.unlink = sdcardfs_unlink,
.mkdir = sdcardfs_mkdir,
.rmdir = sdcardfs_rmdir,
.rename = sdcardfs_rename,
- .setattr = sdcardfs_setattr,
+ .setattr = sdcardfs_setattr_wrn,
+ .setattr2 = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
@@ -798,7 +906,9 @@
};
const struct inode_operations sdcardfs_main_iops = {
- .permission = sdcardfs_permission,
- .setattr = sdcardfs_setattr,
+ .permission = sdcardfs_permission_wrn,
+ .permission2 = sdcardfs_permission,
+ .setattr = sdcardfs_setattr_wrn,
+ .setattr2 = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
};
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
index a01b06a..369b94e 100644
--- a/fs/sdcardfs/lookup.c
+++ b/fs/sdcardfs/lookup.c
@@ -36,8 +36,7 @@
void sdcardfs_destroy_dentry_cache(void)
{
- if (sdcardfs_dentry_cachep)
- kmem_cache_destroy(sdcardfs_dentry_cachep);
+ kmem_cache_destroy(sdcardfs_dentry_cachep);
}
void free_dentry_private_data(struct dentry *dentry)
@@ -72,7 +71,8 @@
static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/)
{
struct inode *current_lower_inode = sdcardfs_lower_inode(inode);
- userid_t current_userid = SDCARDFS_I(inode)->userid;
+ userid_t current_userid = SDCARDFS_I(inode)->data->userid;
+
if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode &&
current_userid == ((struct inode_data *)candidate_data)->id)
return 1; /* found a match */
@@ -91,7 +91,9 @@
struct sdcardfs_inode_info *info;
struct inode_data data;
struct inode *inode; /* the new inode to return */
- int err;
+
+ if (!igrab(lower_inode))
+ return ERR_PTR(-ESTALE);
data.id = id;
data.lower_inode = lower_inode;
@@ -102,26 +104,23 @@
* instead.
*/
lower_inode->i_ino, /* hashval */
- sdcardfs_inode_test, /* inode comparison function */
+ sdcardfs_inode_test, /* inode comparison function */
sdcardfs_inode_set, /* inode init function */
&data); /* data passed to test+set fxns */
if (!inode) {
- err = -EACCES;
iput(lower_inode);
- return ERR_PTR(err);
+ return ERR_PTR(-ENOMEM);
}
- /* if found a cached inode, then just return it */
- if (!(inode->i_state & I_NEW))
+ /* if found a cached inode, then just return it (after iput) */
+ if (!(inode->i_state & I_NEW)) {
+ iput(lower_inode);
return inode;
+ }
/* initialize new inode */
info = SDCARDFS_I(inode);
inode->i_ino = lower_inode->i_ino;
- if (!igrab(lower_inode)) {
- err = -ESTALE;
- return ERR_PTR(err);
- }
sdcardfs_set_lower_inode(inode, lower_inode);
inode->i_version++;
@@ -164,27 +163,25 @@
}
/*
- * Connect a sdcardfs inode dentry/inode with several lower ones. This is
- * the classic stackable file system "vnode interposition" action.
- *
- * @dentry: sdcardfs's dentry which interposes on lower one
- * @sb: sdcardfs's super_block
- * @lower_path: the lower path (caller does path_get/put)
+ * Helper interpose routine, called directly by ->lookup to handle
+ * spliced dentries.
*/
-int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
- struct path *lower_path, userid_t id)
+static struct dentry *__sdcardfs_interpose(struct dentry *dentry,
+ struct super_block *sb,
+ struct path *lower_path,
+ userid_t id)
{
- int err = 0;
struct inode *inode;
struct inode *lower_inode;
struct super_block *lower_sb;
+ struct dentry *ret_dentry;
lower_inode = lower_path->dentry->d_inode;
lower_sb = sdcardfs_lower_super(sb);
/* check that the lower file system didn't cross a mount point */
if (lower_inode->i_sb != lower_sb) {
- err = -EXDEV;
+ ret_dentry = ERR_PTR(-EXDEV);
goto out;
}
@@ -196,14 +193,54 @@
/* inherit lower inode number for sdcardfs's inode */
inode = sdcardfs_iget(sb, lower_inode, id);
if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
+ ret_dentry = ERR_CAST(inode);
goto out;
}
- d_add(dentry, inode);
+ ret_dentry = d_splice_alias(inode, dentry);
+ dentry = ret_dentry ?: dentry;
update_derived_permission_lock(dentry);
out:
- return err;
+ return ret_dentry;
+}
+
+/*
+ * Connect an sdcardfs inode dentry/inode with several lower ones. This is
+ * the classic stackable file system "vnode interposition" action.
+ *
+ * @dentry: sdcardfs's dentry which interposes on lower one
+ * @sb: sdcardfs's super_block
+ * @lower_path: the lower path (caller does path_get/put)
+ */
+int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
+ struct path *lower_path, userid_t id)
+{
+ struct dentry *ret_dentry;
+
+ ret_dentry = __sdcardfs_interpose(dentry, sb, lower_path, id);
+ return PTR_ERR(ret_dentry);
+}
+
+struct sdcardfs_name_data {
+ struct dir_context ctx;
+ const struct qstr *to_find;
+ char *name;
+ bool found;
+};
+
+static int sdcardfs_name_match(void *__buf, const char *name, int namelen,
+ loff_t offset, u64 ino, unsigned int d_type)
+{
+ struct sdcardfs_name_data *buf = (struct sdcardfs_name_data *) __buf;
+ struct qstr candidate = QSTR_INIT(name, namelen);
+
+ if (qstr_case_eq(buf->to_find, &candidate)) {
+ memcpy(buf->name, name, namelen);
+ buf->name[namelen] = 0;
+ buf->found = true;
+ return 1;
+ }
+ return 0;
}
/*
@@ -219,9 +256,10 @@
struct vfsmount *lower_dir_mnt;
struct dentry *lower_dir_dentry = NULL;
struct dentry *lower_dentry;
- const char *name;
+ const struct qstr *name;
struct path lower_path;
- struct qstr this;
+ struct qstr dname;
+ struct dentry *ret_dentry = NULL;
struct sdcardfs_sb_info *sbi;
sbi = SDCARDFS_SB(dentry->d_sb);
@@ -231,47 +269,90 @@
if (IS_ROOT(dentry))
goto out;
- name = dentry->d_name.name;
+ name = &dentry->d_name;
/* now start the actual lookup procedure */
lower_dir_dentry = lower_parent_path->dentry;
lower_dir_mnt = lower_parent_path->mnt;
/* Use vfs_path_lookup to check if the dentry exists or not */
- err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
+ err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name->name, 0,
&lower_path);
+ /* check for other cases */
+ if (err == -ENOENT) {
+ struct file *file;
+ const struct cred *cred = current_cred();
+
+ struct sdcardfs_name_data buffer = {
+ .ctx.actor = sdcardfs_name_match,
+ .to_find = name,
+ .name = __getname(),
+ .found = false,
+ };
+
+ if (!buffer.name) {
+ err = -ENOMEM;
+ goto out;
+ }
+ file = dentry_open(lower_parent_path, O_RDONLY, cred);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto put_name;
+ }
+ err = iterate_dir(file, &buffer.ctx);
+ fput(file);
+ if (err)
+ goto put_name;
+
+ if (buffer.found)
+ err = vfs_path_lookup(lower_dir_dentry,
+ lower_dir_mnt,
+ buffer.name, 0,
+ &lower_path);
+ else
+ err = -ENOENT;
+put_name:
+ __putname(buffer.name);
+ }
/* no error: handle positive dentries */
if (!err) {
/* check if the dentry is an obb dentry
* if true, the lower_inode must be replaced with
- * the inode of the graft path */
+ * the inode of the graft path
+ */
- if(need_graft_path(dentry)) {
+ if (need_graft_path(dentry)) {
/* setup_obb_dentry()
- * The lower_path will be stored to the dentry's orig_path
+ * The lower_path will be stored to the dentry's orig_path
* and the base obbpath will be copyed to the lower_path variable.
* if an error returned, there's no change in the lower_path
- * returns: -ERRNO if error (0: no error) */
+ * returns: -ERRNO if error (0: no error)
+ */
err = setup_obb_dentry(dentry, &lower_path);
- if(err) {
+ if (err) {
/* if the sbi->obbpath is not available, we can optionally
* setup the lower_path with its orig_path.
* but, the current implementation just returns an error
* because the sdcard daemon also regards this case as
- * a lookup fail. */
- printk(KERN_INFO "sdcardfs: base obbpath is not available\n");
+ * a lookup fail.
+ */
+ pr_info("sdcardfs: base obbpath is not available\n");
sdcardfs_put_reset_orig_path(dentry);
goto out;
}
}
sdcardfs_set_lower_path(dentry, &lower_path);
- err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id);
- if (err) /* path_put underlying path on error */
+ ret_dentry =
+ __sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id);
+ if (IS_ERR(ret_dentry)) {
+ err = PTR_ERR(ret_dentry);
+ /* path_put underlying path on error */
sdcardfs_put_reset_lower_path(dentry);
+ }
goto out;
}
@@ -283,21 +364,24 @@
goto out;
/* instatiate a new negative dentry */
- this.name = name;
- this.len = strlen(name);
- this.hash = full_name_hash(this.name, this.len);
- lower_dentry = d_lookup(lower_dir_dentry, &this);
- if (lower_dentry)
- goto setup_lower;
+ dname.name = name->name;
+ dname.len = name->len;
- lower_dentry = d_alloc(lower_dir_dentry, &this);
+ /* See if the low-level filesystem might want
+ * to use its own hash
+ */
+ lower_dentry = d_hash_and_lookup(lower_dir_dentry, &dname);
+ if (IS_ERR(lower_dentry))
+ return lower_dentry;
if (!lower_dentry) {
- err = -ENOMEM;
+ /* We called vfs_path_lookup earlier, and did not get a negative
+ * dentry then. Don't confuse the lower filesystem by forcing
+ * one on it now...
+ */
+ err = -ENOENT;
goto out;
}
- d_add(lower_dentry, NULL); /* instantiate and hash */
-setup_lower:
lower_path.dentry = lower_dentry;
lower_path.mnt = mntget(lower_dir_mnt);
sdcardfs_set_lower_path(dentry, &lower_path);
@@ -311,14 +395,16 @@
err = 0;
out:
- return ERR_PTR(err);
+ if (err)
+ return ERR_PTR(err);
+ return ret_dentry;
}
/*
* On success:
- * fills dentry object appropriate values and returns NULL.
+ * fills dentry object appropriate values and returns NULL.
* On fail (== error)
- * returns error ptr
+ * returns error ptr
*
* @dir : Parent inode. It is locked (dir->i_mutex)
* @dentry : Target dentry to lookup. we should set each of fields.
@@ -335,16 +421,13 @@
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
+ if (!check_caller_access_to_name(parent->d_inode, &dentry->d_name)) {
ret = ERR_PTR(-EACCES);
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
goto out_err;
- }
+ }
/* save current_cred and override it */
- OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(parent, &lower_parent_path);
@@ -355,21 +438,19 @@
goto out;
}
- ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid);
+ ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path,
+ SDCARDFS_I(dir)->data->userid);
if (IS_ERR(ret))
- {
goto out;
- }
if (ret)
dentry = ret;
if (dentry->d_inode) {
fsstack_copy_attr_times(dentry->d_inode,
sdcardfs_lower_inode(dentry->d_inode));
- /* get drived permission */
- mutex_lock(&dentry->d_inode->i_mutex);
+ /* get derived permission */
get_derived_permission(parent, dentry);
- fix_derived_permission(dentry->d_inode);
- mutex_unlock(&dentry->d_inode->i_mutex);
+ fixup_tmp_permissions(dentry->d_inode);
+ fixup_lower_ownership(dentry, dentry->d_name.name);
}
/* update parent directory's atime */
fsstack_copy_attr_atime(parent->d_inode,
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index 8b51a12..129d98e 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -28,9 +28,8 @@
Opt_fsgid,
Opt_gid,
Opt_debug,
- Opt_lower_fs,
Opt_mask,
- Opt_multiuser, // May need?
+ Opt_multiuser,
Opt_userid,
Opt_reserved_mb,
Opt_err,
@@ -49,7 +48,8 @@
};
static int parse_options(struct super_block *sb, char *options, int silent,
- int *debug, struct sdcardfs_mount_options *opts)
+ int *debug, struct sdcardfs_vfsmount_options *vfsopts,
+ struct sdcardfs_mount_options *opts)
{
char *p;
substring_t args[MAX_OPT_ARGS];
@@ -58,10 +58,10 @@
/* by default, we use AID_MEDIA_RW as uid, gid */
opts->fs_low_uid = AID_MEDIA_RW;
opts->fs_low_gid = AID_MEDIA_RW;
- opts->mask = 0;
+ vfsopts->mask = 0;
opts->multiuser = false;
opts->fs_user_id = 0;
- opts->gid = 0;
+ vfsopts->gid = 0;
/* by default, 0MB is reserved */
opts->reserved_mb = 0;
@@ -72,6 +72,7 @@
while ((p = strsep(&options, ",")) != NULL) {
int token;
+
if (!*p)
continue;
@@ -94,7 +95,7 @@
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
- opts->gid = option;
+ vfsopts->gid = option;
break;
case Opt_userid:
if (match_int(&args[0], &option))
@@ -104,7 +105,7 @@
case Opt_mask:
if (match_int(&args[0], &option))
return 0;
- opts->mask = option;
+ vfsopts->mask = option;
break;
case Opt_multiuser:
opts->multiuser = true;
@@ -116,25 +117,81 @@
break;
/* unknown option */
default:
- if (!silent) {
- printk( KERN_ERR "Unrecognized mount option \"%s\" "
- "or missing value", p);
- }
+ if (!silent)
+ pr_err("Unrecognized mount option \"%s\" or missing value", p);
return -EINVAL;
}
}
if (*debug) {
- printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug);
- printk( KERN_INFO "sdcardfs : options - uid:%d\n",
+ pr_info("sdcardfs : options - debug:%d\n", *debug);
+ pr_info("sdcardfs : options - uid:%d\n",
opts->fs_low_uid);
- printk( KERN_INFO "sdcardfs : options - gid:%d\n",
+ pr_info("sdcardfs : options - gid:%d\n",
opts->fs_low_gid);
}
return 0;
}
+int parse_options_remount(struct super_block *sb, char *options, int silent,
+ struct sdcardfs_vfsmount_options *vfsopts)
+{
+ char *p;
+ substring_t args[MAX_OPT_ARGS];
+ int option;
+ int debug;
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ int token;
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, sdcardfs_tokens, args);
+
+ switch (token) {
+ case Opt_debug:
+ debug = 1;
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ return 0;
+ vfsopts->gid = option;
+
+ break;
+ case Opt_mask:
+ if (match_int(&args[0], &option))
+ return 0;
+ vfsopts->mask = option;
+ break;
+ case Opt_multiuser:
+ case Opt_userid:
+ case Opt_fsuid:
+ case Opt_fsgid:
+ case Opt_reserved_mb:
+ pr_warn("Option \"%s\" can't be changed during remount\n", p);
+ break;
+ /* unknown option */
+ default:
+ if (!silent)
+ pr_err("Unrecognized mount option \"%s\" or missing value", p);
+ return -EINVAL;
+ }
+ }
+
+ if (debug) {
+ pr_info("sdcardfs : options - debug:%d\n", debug);
+ pr_info("sdcardfs : options - gid:%d\n", vfsopts->gid);
+ pr_info("sdcardfs : options - mask:%d\n", vfsopts->mask);
+ }
+
+ return 0;
+}
+
#if 0
/*
* our custom d_alloc_root work-alike
@@ -164,57 +221,58 @@
#endif
DEFINE_MUTEX(sdcardfs_super_list_lock);
-LIST_HEAD(sdcardfs_super_list);
EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock);
+LIST_HEAD(sdcardfs_super_list);
EXPORT_SYMBOL_GPL(sdcardfs_super_list);
/*
* There is no need to lock the sdcardfs_super_info's rwsem as there is no
* way anyone can have a reference to the superblock at this point in time.
*/
-static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
- void *raw_data, int silent)
+static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
+ const char *dev_name, void *raw_data, int silent)
{
int err = 0;
int debug;
struct super_block *lower_sb;
struct path lower_path;
struct sdcardfs_sb_info *sb_info;
+ struct sdcardfs_vfsmount_options *mnt_opt = mnt->data;
struct inode *inode;
- printk(KERN_INFO "sdcardfs version 2.0\n");
+ pr_info("sdcardfs version 2.0\n");
if (!dev_name) {
- printk(KERN_ERR
- "sdcardfs: read_super: missing dev_name argument\n");
+ pr_err("sdcardfs: read_super: missing dev_name argument\n");
err = -EINVAL;
goto out;
}
- printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name);
- printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data);
+ pr_info("sdcardfs: dev_name -> %s\n", dev_name);
+ pr_info("sdcardfs: options -> %s\n", (char *)raw_data);
+ pr_info("sdcardfs: mnt -> %p\n", mnt);
/* parse lower path */
err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
&lower_path);
if (err) {
- printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name);
+ pr_err("sdcardfs: error accessing lower directory '%s'\n", dev_name);
goto out;
}
/* allocate superblock private data */
sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL);
if (!SDCARDFS_SB(sb)) {
- printk(KERN_CRIT "sdcardfs: read_super: out of memory\n");
+ pr_crit("sdcardfs: read_super: out of memory\n");
err = -ENOMEM;
goto out_free;
}
sb_info = sb->s_fs_info;
/* parse options */
- err = parse_options(sb, raw_data, silent, &debug, &sb_info->options);
+ err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options);
if (err) {
- printk(KERN_ERR "sdcardfs: invalid options\n");
+ pr_err("sdcardfs: invalid options\n");
goto out_freesbi;
}
@@ -274,23 +332,24 @@
/* setup permission policy */
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
mutex_lock(&sdcardfs_super_list_lock);
- if(sb_info->options.multiuser) {
- setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false);
+ if (sb_info->options.multiuser) {
+ setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT,
+ sb_info->options.fs_user_id, AID_ROOT,
+ false, SDCARDFS_I(sb->s_root->d_inode)->data);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
- /*err = prepare_dir(sb_info->obbpath_s,
- sb_info->options.fs_low_uid,
- sb_info->options.fs_low_gid, 00755);*/
} else {
- setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false);
+ setup_derived_state(sb->s_root->d_inode, PERM_ROOT,
+ sb_info->options.fs_user_id, AID_ROOT,
+ false, SDCARDFS_I(sb->s_root->d_inode)->data);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
}
- fix_derived_permission(sb->s_root->d_inode);
+ fixup_tmp_permissions(sb->s_root->d_inode);
sb_info->sb = sb;
list_add(&sb_info->list, &sdcardfs_super_list);
mutex_unlock(&sdcardfs_super_list_lock);
if (!silent)
- printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n",
+ pr_info("sdcardfs: mounted on top of %s type %s\n",
dev_name, lower_sb->s_type->name);
goto out; /* all is well */
@@ -313,9 +372,11 @@
}
/* A feature which supports mount_nodev() with options */
-static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data,
- int (*fill_super)(struct super_block *, const char *, void *, int))
+static struct dentry *mount_nodev_with_options(struct vfsmount *mnt,
+ struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data,
+ int (*fill_super)(struct vfsmount *, struct super_block *,
+ const char *, void *, int))
{
int error;
@@ -326,7 +387,7 @@
s->s_flags = flags;
- error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0);
+ error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0);
if (error) {
deactivate_locked_super(s);
return ERR_PTR(error);
@@ -335,19 +396,34 @@
return dget(s->s_root);
}
-struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags,
+static struct dentry *sdcardfs_mount(struct vfsmount *mnt,
+ struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
/*
* dev_name is a lower_path_name,
* raw_data is a option string.
*/
- return mount_nodev_with_options(fs_type, flags, dev_name,
- raw_data, sdcardfs_read_super);
+ return mount_nodev_with_options(mnt, fs_type, flags, dev_name,
+ raw_data, sdcardfs_read_super);
}
-void sdcardfs_kill_sb(struct super_block *sb) {
+static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *raw_data)
+{
+ WARN(1, "sdcardfs does not support mount. Use mount2.\n");
+ return ERR_PTR(-EINVAL);
+}
+
+void *sdcardfs_alloc_mnt_data(void)
+{
+ return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
+}
+
+void sdcardfs_kill_sb(struct super_block *sb)
+{
struct sdcardfs_sb_info *sbi;
+
if (sb->s_magic == SDCARDFS_SUPER_MAGIC) {
sbi = SDCARDFS_SB(sb);
mutex_lock(&sdcardfs_super_list_lock);
@@ -360,10 +436,13 @@
static struct file_system_type sdcardfs_fs_type = {
.owner = THIS_MODULE,
.name = SDCARDFS_NAME,
- .mount = sdcardfs_mount,
+ .mount = sdcardfs_mount_wrn,
+ .mount2 = sdcardfs_mount,
+ .alloc_mnt_data = sdcardfs_alloc_mnt_data,
.kill_sb = sdcardfs_kill_sb,
.fs_flags = 0,
};
+MODULE_ALIAS_FS(SDCARDFS_NAME);
static int __init init_sdcardfs_fs(void)
{
@@ -399,10 +478,15 @@
pr_info("Completed sdcardfs module unload\n");
}
-MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University"
- " (http://www.fsl.cs.sunysb.edu/)");
-MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION
- " (http://wrapfs.filesystems.org/)");
+/* Original wrapfs authors */
+MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University (http://www.fsl.cs.sunysb.edu/)");
+
+/* Original sdcardfs authors */
+MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil System Memory Lab., Samsung Electronics");
+
+/* Current maintainer */
+MODULE_AUTHOR("Daniel Rosenberg, Google");
+MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION);
MODULE_LICENSE("GPL");
module_init(init_sdcardfs_fs);
diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c
index acbaee0..f618961 100644
--- a/fs/sdcardfs/mmap.c
+++ b/fs/sdcardfs/mmap.c
@@ -23,28 +23,46 @@
static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
int err;
- struct file *file, *lower_file;
+ struct file *file;
const struct vm_operations_struct *lower_vm_ops;
- struct vm_area_struct lower_vma;
- memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
- file = lower_vma.vm_file;
+ file = (struct file *)vma->vm_private_data;
lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
BUG_ON(!lower_vm_ops);
- lower_file = sdcardfs_lower_file(file);
- /*
- * XXX: vm_ops->fault may be called in parallel. Because we have to
- * resort to temporarily changing the vma->vm_file to point to the
- * lower file, a concurrent invocation of sdcardfs_fault could see a
- * different value. In this workaround, we keep a different copy of
- * the vma structure in our stack, so we never expose a different
- * value of the vma->vm_file called to us, even temporarily. A
- * better fix would be to change the calling semantics of ->fault to
- * take an explicit file pointer.
- */
- lower_vma.vm_file = lower_file;
- err = lower_vm_ops->fault(&lower_vma, vmf);
+ err = lower_vm_ops->fault(vma, vmf);
+ return err;
+}
+
+static void sdcardfs_vm_open(struct vm_area_struct *vma)
+{
+ struct file *file = (struct file *)vma->vm_private_data;
+
+ get_file(file);
+}
+
+static void sdcardfs_vm_close(struct vm_area_struct *vma)
+{
+ struct file *file = (struct file *)vma->vm_private_data;
+
+ fput(file);
+}
+
+static int sdcardfs_page_mkwrite(struct vm_area_struct *vma,
+ struct vm_fault *vmf)
+{
+ int err = 0;
+ struct file *file;
+ const struct vm_operations_struct *lower_vm_ops;
+
+ file = (struct file *)vma->vm_private_data;
+ lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
+ BUG_ON(!lower_vm_ops);
+ if (!lower_vm_ops->page_mkwrite)
+ goto out;
+
+ err = lower_vm_ops->page_mkwrite(vma, vmf);
+out:
return err;
}
@@ -52,30 +70,20 @@
struct iov_iter *iter, loff_t pos)
{
/*
- * This function returns zero on purpose in order to support direct IO.
- * __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null.
- *
- * However, this function won't be called by certain file operations
- * including generic fs functions. * reads and writes are delivered to
- * the lower file systems and the direct IOs will be handled by them.
- *
- * NOTE: exceptionally, on the recent kernels (since Linux 3.8.x),
- * swap_writepage invokes this function directly.
+ * This function should never be called directly. We need it
+ * to exist, to get past a check in open_check_o_direct(),
+ * which is called from do_last().
*/
- printk(KERN_INFO "%s, operation is not supported\n", __func__);
- return 0;
+ return -EINVAL;
}
-/*
- * XXX: the default address_space_ops for sdcardfs is empty. We cannot set
- * our inode->i_mapping->a_ops to NULL because too many code paths expect
- * the a_ops vector to be non-NULL.
- */
const struct address_space_operations sdcardfs_aops = {
- /* empty on purpose */
.direct_IO = sdcardfs_direct_IO,
};
const struct vm_operations_struct sdcardfs_vm_ops = {
.fault = sdcardfs_fault,
+ .page_mkwrite = sdcardfs_page_mkwrite,
+ .open = sdcardfs_vm_open,
+ .close = sdcardfs_vm_close,
};
diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h
index 923ba10..85341e7 100644
--- a/fs/sdcardfs/multiuser.h
+++ b/fs/sdcardfs/multiuser.h
@@ -18,20 +18,36 @@
* General Public License.
*/
-#define MULTIUSER_APP_PER_USER_RANGE 100000
+#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
+#define AID_APP_START 10000 /* first app user */
+#define AID_APP_END 19999 /* last app user */
+#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
+#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
+#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */
+#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */
+#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
typedef uid_t userid_t;
typedef uid_t appid_t;
-static inline userid_t multiuser_get_user_id(uid_t uid) {
- return uid / MULTIUSER_APP_PER_USER_RANGE;
+static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id)
+{
+ return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
}
-static inline appid_t multiuser_get_app_id(uid_t uid) {
- return uid % MULTIUSER_APP_PER_USER_RANGE;
+static inline bool uid_is_app(uid_t uid)
+{
+ appid_t appid = uid % AID_USER_OFFSET;
+
+ return appid >= AID_APP_START && appid <= AID_APP_END;
}
-static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
- return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
+static inline gid_t multiuser_get_ext_cache_gid(uid_t uid)
+{
+ return uid - AID_APP_START + AID_EXT_CACHE_GID_START;
}
+static inline gid_t multiuser_get_ext_gid(uid_t uid)
+{
+ return uid - AID_APP_START + AID_EXT_GID_START;
+}
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
index 10b98b1..28e07dd 100644
--- a/fs/sdcardfs/packagelist.c
+++ b/fs/sdcardfs/packagelist.c
@@ -20,8 +20,10 @@
#include "sdcardfs.h"
#include <linux/hashtable.h>
+#include <linux/ctype.h>
#include <linux/delay.h>
-
+#include <linux/radix-tree.h>
+#include <linux/dcache.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -29,389 +31,840 @@
#include <linux/configfs.h>
-#define STRING_BUF_SIZE (512)
-
struct hashtable_entry {
struct hlist_node hlist;
- void *key;
- unsigned int value;
+ struct hlist_node dlist; /* for deletion cleanup */
+ struct qstr key;
+ atomic_t value;
};
-struct sb_list {
- struct super_block *sb;
- struct list_head list;
-};
+static DEFINE_HASHTABLE(package_to_appid, 8);
+static DEFINE_HASHTABLE(package_to_userid, 8);
+static DEFINE_HASHTABLE(ext_to_groupid, 8);
-struct packagelist_data {
- DECLARE_HASHTABLE(package_to_appid,8);
- struct mutex hashtable_lock;
-
-};
-
-static struct packagelist_data *pkgl_data_all;
static struct kmem_cache *hashtable_entry_cachep;
-static unsigned int str_hash(const char *key) {
- int i;
- unsigned int h = strlen(key);
- char *data = (char *)key;
+static unsigned int full_name_case_hash(const unsigned char *name, unsigned int len)
+{
+ unsigned long hash = init_name_hash();
- for (i = 0; i < strlen(key); i++) {
- h = h * 31 + *data;
- data++;
- }
- return h;
+ while (len--)
+ hash = partial_name_hash(tolower(*name++), hash);
+ return end_name_hash(hash);
}
-appid_t get_appid(void *pkgl_id, const char *app_name)
+static inline void qstr_init(struct qstr *q, const char *name)
{
- struct packagelist_data *pkgl_dat = pkgl_data_all;
+ q->name = name;
+ q->len = strlen(q->name);
+ q->hash = full_name_case_hash(q->name, q->len);
+}
+
+static inline int qstr_copy(const struct qstr *src, struct qstr *dest)
+{
+ dest->name = kstrdup(src->name, GFP_KERNEL);
+ dest->hash_len = src->hash_len;
+ return !!dest->name;
+}
+
+
+static appid_t __get_appid(const struct qstr *key)
+{
struct hashtable_entry *hash_cur;
- unsigned int hash = str_hash(app_name);
+ unsigned int hash = key->hash;
appid_t ret_id;
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
- if (!strcasecmp(app_name, hash_cur->key)) {
- ret_id = (appid_t)hash_cur->value;
- mutex_unlock(&pkgl_dat->hashtable_lock);
+ rcu_read_lock();
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ ret_id = atomic_read(&hash_cur->value);
+ rcu_read_unlock();
return ret_id;
}
}
- mutex_unlock(&pkgl_dat->hashtable_lock);
+ rcu_read_unlock();
return 0;
}
+appid_t get_appid(const char *key)
+{
+ struct qstr q;
+
+ qstr_init(&q, key);
+ return __get_appid(&q);
+}
+
+static appid_t __get_ext_gid(const struct qstr *key)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+ appid_t ret_id;
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ ret_id = atomic_read(&hash_cur->value);
+ rcu_read_unlock();
+ return ret_id;
+ }
+ }
+ rcu_read_unlock();
+ return 0;
+}
+
+appid_t get_ext_gid(const char *key)
+{
+ struct qstr q;
+
+ qstr_init(&q, key);
+ return __get_ext_gid(&q);
+}
+
+static appid_t __is_excluded(const struct qstr *app_name, userid_t user)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = app_name->hash;
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (atomic_read(&hash_cur->value) == user &&
+ qstr_case_eq(app_name, &hash_cur->key)) {
+ rcu_read_unlock();
+ return 1;
+ }
+ }
+ rcu_read_unlock();
+ return 0;
+}
+
+appid_t is_excluded(const char *key, userid_t user)
+{
+ struct qstr q;
+
+ qstr_init(&q, key);
+ return __is_excluded(&q, user);
+}
+
/* Kernel has already enforced everything we returned through
* derive_permissions_locked(), so this is used to lock down access
- * even further, such as enforcing that apps hold sdcard_rw. */
-int check_caller_access_to_name(struct inode *parent_node, const char* name) {
+ * even further, such as enforcing that apps hold sdcard_rw.
+ */
+int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name)
+{
+ struct qstr q_autorun = QSTR_LITERAL("autorun.inf");
+ struct qstr q__android_secure = QSTR_LITERAL(".android_secure");
+ struct qstr q_android_secure = QSTR_LITERAL("android_secure");
/* Always block security-sensitive files at root */
- if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) {
- if (!strcasecmp(name, "autorun.inf")
- || !strcasecmp(name, ".android_secure")
- || !strcasecmp(name, "android_secure")) {
+ if (parent_node && SDCARDFS_I(parent_node)->data->perm == PERM_ROOT) {
+ if (qstr_case_eq(name, &q_autorun)
+ || qstr_case_eq(name, &q__android_secure)
+ || qstr_case_eq(name, &q_android_secure)) {
return 0;
}
}
/* Root always has access; access for any other UIDs should always
- * be controlled through packages.list. */
- if (from_kuid(&init_user_ns, current_fsuid()) == 0) {
+ * be controlled through packages.list.
+ */
+ if (from_kuid(&init_user_ns, current_fsuid()) == 0)
return 1;
- }
/* No extra permissions to enforce */
return 1;
}
/* This function is used when file opening. The open flags must be
- * checked before calling check_caller_access_to_name() */
-int open_flags_to_access_mode(int open_flags) {
- if((open_flags & O_ACCMODE) == O_RDONLY) {
- return 0; /* R_OK */
- } else if ((open_flags & O_ACCMODE) == O_WRONLY) {
- return 1; /* W_OK */
- } else {
- /* Probably O_RDRW, but treat as default to be safe */
- return 1; /* R_OK | W_OK */
- }
-}
-
-static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key,
- unsigned int value)
+ * checked before calling check_caller_access_to_name()
+ */
+int open_flags_to_access_mode(int open_flags)
{
- struct hashtable_entry *hash_cur;
- struct hashtable_entry *new_entry;
- unsigned int hash = str_hash(key);
-
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
- if (!strcasecmp(key, hash_cur->key)) {
- hash_cur->value = value;
- return 0;
- }
- }
- new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL);
- if (!new_entry)
- return -ENOMEM;
- new_entry->key = kstrdup(key, GFP_KERNEL);
- new_entry->value = value;
- hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash);
- return 0;
+ if ((open_flags & O_ACCMODE) == O_RDONLY)
+ return 0; /* R_OK */
+ if ((open_flags & O_ACCMODE) == O_WRONLY)
+ return 1; /* W_OK */
+ /* Probably O_RDRW, but treat as default to be safe */
+ return 1; /* R_OK | W_OK */
}
-static void fixup_perms(struct super_block *sb) {
- if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) {
- mutex_lock(&sb->s_root->d_inode->i_mutex);
- get_derive_permissions_recursive(sb->s_root);
- mutex_unlock(&sb->s_root->d_inode->i_mutex);
- }
-}
+static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key,
+ appid_t value)
+{
+ struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep,
+ GFP_KERNEL);
+ if (!ret)
+ return NULL;
+ INIT_HLIST_NODE(&ret->dlist);
+ INIT_HLIST_NODE(&ret->hlist);
-static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key,
- unsigned int value) {
- int ret;
- struct sdcardfs_sb_info *sbinfo;
- mutex_lock(&sdcardfs_super_list_lock);
- mutex_lock(&pkgl_dat->hashtable_lock);
- ret = insert_str_to_int_lock(pkgl_dat, key, value);
- mutex_unlock(&pkgl_dat->hashtable_lock);
-
- list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
- if (sbinfo) {
- fixup_perms(sbinfo->sb);
- }
+ if (!qstr_copy(key, &ret->key)) {
+ kmem_cache_free(hashtable_entry_cachep, ret);
+ return NULL;
}
- mutex_unlock(&sdcardfs_super_list_lock);
+
+ atomic_set(&ret->value, value);
return ret;
}
-static void remove_str_to_int_lock(struct hashtable_entry *h_entry) {
- kfree(h_entry->key);
- hash_del(&h_entry->hlist);
- kmem_cache_free(hashtable_entry_cachep, h_entry);
+static int insert_packagelist_appid_entry_locked(const struct qstr *key, appid_t value)
+{
+ struct hashtable_entry *hash_cur;
+ struct hashtable_entry *new_entry;
+ unsigned int hash = key->hash;
+
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ atomic_set(&hash_cur->value, value);
+ return 0;
+ }
+ }
+ new_entry = alloc_hashtable_entry(key, value);
+ if (!new_entry)
+ return -ENOMEM;
+ hash_add_rcu(package_to_appid, &new_entry->hlist, hash);
+ return 0;
}
-static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key)
+static int insert_ext_gid_entry_locked(const struct qstr *key, appid_t value)
+{
+ struct hashtable_entry *hash_cur;
+ struct hashtable_entry *new_entry;
+ unsigned int hash = key->hash;
+
+ /* An extension can only belong to one gid */
+ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key))
+ return -EINVAL;
+ }
+ new_entry = alloc_hashtable_entry(key, value);
+ if (!new_entry)
+ return -ENOMEM;
+ hash_add_rcu(ext_to_groupid, &new_entry->hlist, hash);
+ return 0;
+}
+
+static int insert_userid_exclude_entry_locked(const struct qstr *key, userid_t value)
+{
+ struct hashtable_entry *hash_cur;
+ struct hashtable_entry *new_entry;
+ unsigned int hash = key->hash;
+
+ /* Only insert if not already present */
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (atomic_read(&hash_cur->value) == value &&
+ qstr_case_eq(key, &hash_cur->key))
+ return 0;
+ }
+ new_entry = alloc_hashtable_entry(key, value);
+ if (!new_entry)
+ return -ENOMEM;
+ hash_add_rcu(package_to_userid, &new_entry->hlist, hash);
+ return 0;
+}
+
+static void fixup_all_perms_name(const struct qstr *key)
{
struct sdcardfs_sb_info *sbinfo;
- struct hashtable_entry *hash_cur;
- unsigned int hash = str_hash(key);
+ struct limit_search limit = {
+ .flags = BY_NAME,
+ .name = QSTR_INIT(key->name, key->len),
+ };
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+ if (sbinfo_has_sdcard_magic(sbinfo))
+ fixup_perms_recursive(sbinfo->sb->s_root, &limit);
+ }
+}
+
+static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid)
+{
+ struct sdcardfs_sb_info *sbinfo;
+ struct limit_search limit = {
+ .flags = BY_NAME | BY_USERID,
+ .name = QSTR_INIT(key->name, key->len),
+ .userid = userid,
+ };
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+ if (sbinfo_has_sdcard_magic(sbinfo))
+ fixup_perms_recursive(sbinfo->sb->s_root, &limit);
+ }
+}
+
+static void fixup_all_perms_userid(userid_t userid)
+{
+ struct sdcardfs_sb_info *sbinfo;
+ struct limit_search limit = {
+ .flags = BY_USERID,
+ .userid = userid,
+ };
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+ if (sbinfo_has_sdcard_magic(sbinfo))
+ fixup_perms_recursive(sbinfo->sb->s_root, &limit);
+ }
+}
+
+static int insert_packagelist_entry(const struct qstr *key, appid_t value)
+{
+ int err;
+
mutex_lock(&sdcardfs_super_list_lock);
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
- if (!strcasecmp(key, hash_cur->key)) {
- remove_str_to_int_lock(hash_cur);
+ err = insert_packagelist_appid_entry_locked(key, value);
+ if (!err)
+ fixup_all_perms_name(key);
+ mutex_unlock(&sdcardfs_super_list_lock);
+
+ return err;
+}
+
+static int insert_ext_gid_entry(const struct qstr *key, appid_t value)
+{
+ int err;
+
+ mutex_lock(&sdcardfs_super_list_lock);
+ err = insert_ext_gid_entry_locked(key, value);
+ mutex_unlock(&sdcardfs_super_list_lock);
+
+ return err;
+}
+
+static int insert_userid_exclude_entry(const struct qstr *key, userid_t value)
+{
+ int err;
+
+ mutex_lock(&sdcardfs_super_list_lock);
+ err = insert_userid_exclude_entry_locked(key, value);
+ if (!err)
+ fixup_all_perms_name_userid(key, value);
+ mutex_unlock(&sdcardfs_super_list_lock);
+
+ return err;
+}
+
+static void free_hashtable_entry(struct hashtable_entry *entry)
+{
+ kfree(entry->key.name);
+ kmem_cache_free(hashtable_entry_cachep, entry);
+}
+
+static void remove_packagelist_entry_locked(const struct qstr *key)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+ struct hlist_node *h_t;
+ HLIST_HEAD(free_list);
+
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
+ }
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
break;
}
}
- mutex_unlock(&pkgl_dat->hashtable_lock);
- list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
- if (sbinfo) {
- fixup_perms(sbinfo->sb);
- }
- }
- mutex_unlock(&sdcardfs_super_list_lock);
- return;
+ synchronize_rcu();
+ hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
+ free_hashtable_entry(hash_cur);
}
-static void remove_all_hashentrys(struct packagelist_data *pkgl_dat)
+static void remove_packagelist_entry(const struct qstr *key)
+{
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_packagelist_entry_locked(key);
+ fixup_all_perms_name(key);
+ mutex_unlock(&sdcardfs_super_list_lock);
+}
+
+static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+
+ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key) && atomic_read(&hash_cur->value) == group) {
+ hash_del_rcu(&hash_cur->hlist);
+ synchronize_rcu();
+ free_hashtable_entry(hash_cur);
+ break;
+ }
+ }
+}
+
+static void remove_ext_gid_entry(const struct qstr *key, gid_t group)
+{
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_ext_gid_entry_locked(key, group);
+ mutex_unlock(&sdcardfs_super_list_lock);
+}
+
+static void remove_userid_all_entry_locked(userid_t userid)
{
struct hashtable_entry *hash_cur;
struct hlist_node *h_t;
+ HLIST_HEAD(free_list);
int i;
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist)
- remove_str_to_int_lock(hash_cur);
- mutex_unlock(&pkgl_dat->hashtable_lock);
- hash_init(pkgl_dat->package_to_appid);
-}
-static struct packagelist_data * packagelist_create(void)
-{
- struct packagelist_data *pkgl_dat;
-
- pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO);
- if (!pkgl_dat) {
- printk(KERN_ERR "sdcardfs: Failed to create hash\n");
- return ERR_PTR(-ENOMEM);
+ hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) {
+ if (atomic_read(&hash_cur->value) == userid) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
}
-
- mutex_init(&pkgl_dat->hashtable_lock);
- hash_init(pkgl_dat->package_to_appid);
-
- return pkgl_dat;
+ synchronize_rcu();
+ hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) {
+ free_hashtable_entry(hash_cur);
+ }
}
-static void packagelist_destroy(struct packagelist_data *pkgl_dat)
+static void remove_userid_all_entry(userid_t userid)
{
- remove_all_hashentrys(pkgl_dat);
- printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n");
- kfree(pkgl_dat);
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_userid_all_entry_locked(userid);
+ fixup_all_perms_userid(userid);
+ mutex_unlock(&sdcardfs_super_list_lock);
}
-struct package_appid {
+static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key) &&
+ atomic_read(&hash_cur->value) == userid) {
+ hash_del_rcu(&hash_cur->hlist);
+ synchronize_rcu();
+ free_hashtable_entry(hash_cur);
+ break;
+ }
+ }
+}
+
+static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid)
+{
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_userid_exclude_entry_locked(key, userid);
+ fixup_all_perms_name_userid(key, userid);
+ mutex_unlock(&sdcardfs_super_list_lock);
+}
+
+static void packagelist_destroy(void)
+{
+ struct hashtable_entry *hash_cur;
+ struct hlist_node *h_t;
+ HLIST_HEAD(free_list);
+ int i;
+
+ mutex_lock(&sdcardfs_super_list_lock);
+ hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
+ hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
+ synchronize_rcu();
+ hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
+ free_hashtable_entry(hash_cur);
+ mutex_unlock(&sdcardfs_super_list_lock);
+ pr_info("sdcardfs: destroyed packagelist pkgld\n");
+}
+
+struct package_details {
struct config_item item;
- int add_pid;
+ struct qstr name;
};
-static inline struct package_appid *to_package_appid(struct config_item *item)
+static inline struct package_details *to_package_details(struct config_item *item)
{
- return item ? container_of(item, struct package_appid, item) : NULL;
+ return item ? container_of(item, struct package_details, item) : NULL;
}
-static struct configfs_attribute package_appid_attr_add_pid = {
- .ca_owner = THIS_MODULE,
- .ca_name = "appid",
- .ca_mode = S_IRUGO | S_IWUGO,
-};
+CONFIGFS_ATTR_STRUCT(package_details);
+#define PACKAGE_DETAILS_ATTR(_name, _mode, _show, _store) \
+struct package_details_attribute package_details_attr_##_name = __CONFIGFS_ATTR(_name, _mode, _show, _store)
+#define PACKAGE_DETAILS_ATTRIBUTE(name) (&package_details_attr_##name.attr)
-static struct configfs_attribute *package_appid_attrs[] = {
- &package_appid_attr_add_pid,
- NULL,
-};
-
-static ssize_t package_appid_attr_show(struct config_item *item,
- struct configfs_attribute *attr,
+static ssize_t package_details_appid_show(struct package_details *package_details,
char *page)
{
- ssize_t count;
- count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name));
- return count;
+ return scnprintf(page, PAGE_SIZE, "%u\n", __get_appid(&package_details->name));
}
-static ssize_t package_appid_attr_store(struct config_item *item,
- struct configfs_attribute *attr,
+static ssize_t package_details_appid_store(struct package_details *package_details,
const char *page, size_t count)
{
- struct package_appid *package_appid = to_package_appid(item);
- unsigned long tmp;
- char *p = (char *) page;
+ unsigned int tmp;
int ret;
- tmp = simple_strtoul(p, &p, 10);
- if (!p || (*p && (*p != '\n')))
- return -EINVAL;
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
- if (tmp > INT_MAX)
- return -ERANGE;
- ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp);
- package_appid->add_pid = tmp;
+ ret = insert_packagelist_entry(&package_details->name, tmp);
+
if (ret)
return ret;
return count;
}
-static void package_appid_release(struct config_item *item)
+static ssize_t package_details_excluded_userids_show(struct package_details *package_details,
+ char *page)
{
- printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name);
- /* item->ci_name is freed already, so we rely on the dentry */
- remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name);
- kfree(to_package_appid(item));
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = package_details->name.hash;
+ int count = 0;
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(&package_details->name, &hash_cur->key))
+ count += scnprintf(page + count, PAGE_SIZE - count,
+ "%d ", atomic_read(&hash_cur->value));
+ }
+ rcu_read_unlock();
+ if (count)
+ count--;
+ count += scnprintf(page + count, PAGE_SIZE - count, "\n");
+ return count;
}
-static struct configfs_item_operations package_appid_item_ops = {
- .release = package_appid_release,
- .show_attribute = package_appid_attr_show,
- .store_attribute = package_appid_attr_store,
-};
-
-static struct config_item_type package_appid_type = {
- .ct_item_ops = &package_appid_item_ops,
- .ct_attrs = package_appid_attrs,
- .ct_owner = THIS_MODULE,
-};
-
-
-struct sdcardfs_packages {
- struct config_group group;
-};
-
-static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item)
+static ssize_t package_details_excluded_userids_store(struct package_details *package_details,
+ const char *page, size_t count)
{
- return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL;
+ unsigned int tmp;
+ int ret;
+
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
+
+ ret = insert_userid_exclude_entry(&package_details->name, tmp);
+
+ if (ret)
+ return ret;
+
+ return count;
}
-static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name)
+static ssize_t package_details_clear_userid_store(struct package_details *package_details,
+ const char *page, size_t count)
{
- struct package_appid *package_appid;
+ unsigned int tmp;
+ int ret;
- package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL);
- if (!package_appid)
- return ERR_PTR(-ENOMEM);
-
- config_item_init_type_name(&package_appid->item, name,
- &package_appid_type);
-
- package_appid->add_pid = 0;
-
- return &package_appid->item;
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
+ remove_userid_exclude_entry(&package_details->name, tmp);
+ return count;
}
-static struct configfs_attribute sdcardfs_packages_attr_description = {
- .ca_owner = THIS_MODULE,
- .ca_name = "packages_gid.list",
- .ca_mode = S_IRUGO,
-};
+static void package_details_release(struct config_item *item)
+{
+ struct package_details *package_details = to_package_details(item);
-static struct configfs_attribute *sdcardfs_packages_attrs[] = {
- &sdcardfs_packages_attr_description,
+ pr_info("sdcardfs: removing %s\n", package_details->name.name);
+ remove_packagelist_entry(&package_details->name);
+ kfree(package_details->name.name);
+ kfree(package_details);
+}
+
+PACKAGE_DETAILS_ATTR(appid, S_IRUGO | S_IWUGO, package_details_appid_show, package_details_appid_store);
+PACKAGE_DETAILS_ATTR(excluded_userids, S_IRUGO | S_IWUGO,
+ package_details_excluded_userids_show, package_details_excluded_userids_store);
+PACKAGE_DETAILS_ATTR(clear_userid, S_IWUGO, NULL, package_details_clear_userid_store);
+
+static struct configfs_attribute *package_details_attrs[] = {
+ PACKAGE_DETAILS_ATTRIBUTE(appid),
+ PACKAGE_DETAILS_ATTRIBUTE(excluded_userids),
+ PACKAGE_DETAILS_ATTRIBUTE(clear_userid),
NULL,
};
-static ssize_t packages_attr_show(struct config_item *item,
- struct configfs_attribute *attr,
+CONFIGFS_ATTR_OPS(package_details);
+
+static struct configfs_item_operations package_details_item_ops = {
+ .release = package_details_release,
+ .show_attribute = package_details_attr_show,
+ .store_attribute = package_details_attr_store,
+};
+
+static struct config_item_type package_appid_type = {
+ .ct_item_ops = &package_details_item_ops,
+ .ct_attrs = package_details_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+struct extensions_value {
+ struct config_group group;
+ unsigned int num;
+};
+
+struct extension_details {
+ struct config_item item;
+ struct qstr name;
+ unsigned int num;
+};
+
+static inline struct extensions_value *to_extensions_value(struct config_item *item)
+{
+ return item ? container_of(to_config_group(item), struct extensions_value, group) : NULL;
+}
+
+static inline struct extension_details *to_extension_details(struct config_item *item)
+{
+ return item ? container_of(item, struct extension_details, item) : NULL;
+}
+
+static void extension_details_release(struct config_item *item)
+{
+ struct extension_details *extension_details = to_extension_details(item);
+
+ pr_info("sdcardfs: No longer mapping %s files to gid %d\n",
+ extension_details->name.name, extension_details->num);
+ remove_ext_gid_entry(&extension_details->name, extension_details->num);
+ kfree(extension_details->name.name);
+ kfree(extension_details);
+}
+
+static struct configfs_item_operations extension_details_item_ops = {
+ .release = extension_details_release,
+};
+
+static struct config_item_type extension_details_type = {
+ .ct_item_ops = &extension_details_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *extension_details_make_item(struct config_group *group, const char *name)
+{
+ struct extensions_value *extensions_value = to_extensions_value(&group->cg_item);
+ struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL);
+ const char *tmp;
+ int ret;
+
+ if (!extension_details)
+ return ERR_PTR(-ENOMEM);
+
+ tmp = kstrdup(name, GFP_KERNEL);
+ if (!tmp) {
+ kfree(extension_details);
+ return ERR_PTR(-ENOMEM);
+ }
+ qstr_init(&extension_details->name, tmp);
+ ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num);
+
+ if (ret) {
+ kfree(extension_details->name.name);
+ kfree(extension_details);
+ return ERR_PTR(ret);
+ }
+ config_item_init_type_name(&extension_details->item, name, &extension_details_type);
+
+ return &extension_details->item;
+}
+
+static struct configfs_group_operations extensions_value_group_ops = {
+ .make_item = extension_details_make_item,
+};
+
+static struct config_item_type extensions_name_type = {
+ .ct_group_ops = &extensions_value_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *extensions_make_group(struct config_group *group, const char *name)
+{
+ struct extensions_value *extensions_value;
+ unsigned int tmp;
+ int ret;
+
+ extensions_value = kzalloc(sizeof(struct extensions_value), GFP_KERNEL);
+ if (!extensions_value)
+ return ERR_PTR(-ENOMEM);
+ ret = kstrtouint(name, 10, &tmp);
+ if (ret) {
+ kfree(extensions_value);
+ return ERR_PTR(ret);
+ }
+
+ extensions_value->num = tmp;
+ config_group_init_type_name(&extensions_value->group, name,
+ &extensions_name_type);
+ return &extensions_value->group;
+}
+
+static void extensions_drop_group(struct config_group *group, struct config_item *item)
+{
+ struct extensions_value *value = to_extensions_value(item);
+
+ pr_info("sdcardfs: No longer mapping any files to gid %d\n", value->num);
+ kfree(value);
+}
+
+static struct configfs_group_operations extensions_group_ops = {
+ .make_group = extensions_make_group,
+ .drop_item = extensions_drop_group,
+};
+
+static struct config_item_type extensions_type = {
+ .ct_group_ops = &extensions_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+struct config_group extension_group = {
+ .cg_item = {
+ .ci_namebuf = "extensions",
+ .ci_type = &extensions_type,
+ },
+};
+
+struct packages {
+ struct configfs_subsystem subsystem;
+};
+
+static inline struct packages *to_packages(struct config_item *item)
+{
+ return item ? container_of(to_configfs_subsystem(to_config_group(item)), struct packages, subsystem) : NULL;
+}
+
+CONFIGFS_ATTR_STRUCT(packages);
+#define PACKAGES_ATTR(_name, _mode, _show, _store) \
+struct packages_attribute packages_attr_##_name = __CONFIGFS_ATTR(_name, _mode, _show, _store)
+#define PACKAGES_ATTR_RO(_name, _show) \
+struct packages_attribute packages_attr_##_name = __CONFIGFS_ATTR_RO(_name, _show)
+
+static struct config_item *packages_make_item(struct config_group *group, const char *name)
+{
+ struct package_details *package_details;
+ const char *tmp;
+
+ package_details = kzalloc(sizeof(struct package_details), GFP_KERNEL);
+ if (!package_details)
+ return ERR_PTR(-ENOMEM);
+ tmp = kstrdup(name, GFP_KERNEL);
+ if (!tmp) {
+ kfree(package_details);
+ return ERR_PTR(-ENOMEM);
+ }
+ qstr_init(&package_details->name, tmp);
+ config_item_init_type_name(&package_details->item, name,
+ &package_appid_type);
+
+ return &package_details->item;
+}
+
+static ssize_t packages_list_show(struct packages *packages,
char *page)
{
- struct hashtable_entry *hash_cur;
- struct hlist_node *h_t;
+ struct hashtable_entry *hash_cur_app;
+ struct hashtable_entry *hash_cur_user;
int i;
int count = 0, written = 0;
- char errormsg[] = "<truncated>\n";
+ const char errormsg[] = "<truncated>\n";
+ unsigned int hash;
- mutex_lock(&pkgl_data_all->hashtable_lock);
- hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) {
- written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value);
- if (count + written == PAGE_SIZE - sizeof(errormsg)) {
+ rcu_read_lock();
+ hash_for_each_rcu(package_to_appid, i, hash_cur_app, hlist) {
+ written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n",
+ hash_cur_app->key.name, atomic_read(&hash_cur_app->value));
+ hash = hash_cur_app->key.hash;
+ hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) {
+ if (qstr_case_eq(&hash_cur_app->key, &hash_cur_user->key)) {
+ written += scnprintf(page + count + written - 1,
+ PAGE_SIZE - sizeof(errormsg) - count - written + 1,
+ " %d\n", atomic_read(&hash_cur_user->value)) - 1;
+ }
+ }
+ if (count + written == PAGE_SIZE - sizeof(errormsg) - 1) {
count += scnprintf(page + count, PAGE_SIZE - count, errormsg);
break;
}
count += written;
}
- mutex_unlock(&pkgl_data_all->hashtable_lock);
+ rcu_read_unlock();
return count;
}
-static void sdcardfs_packages_release(struct config_item *item)
+static ssize_t packages_remove_userid_store(struct packages *packages,
+ const char *page, size_t count)
{
+ unsigned int tmp;
+ int ret;
- printk(KERN_INFO "sdcardfs: destroyed something?\n");
- kfree(to_sdcardfs_packages(item));
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
+ remove_userid_all_entry(tmp);
+ return count;
}
-static struct configfs_item_operations sdcardfs_packages_item_ops = {
- .release = sdcardfs_packages_release,
- .show_attribute = packages_attr_show,
+struct packages_attribute packages_attr_packages_gid_list = __CONFIGFS_ATTR_RO(packages_gid.list, packages_list_show);
+PACKAGES_ATTR(remove_userid, S_IWUGO, NULL, packages_remove_userid_store);
+
+static struct configfs_attribute *packages_attrs[] = {
+ &packages_attr_packages_gid_list.attr,
+ &packages_attr_remove_userid.attr,
+ NULL,
+};
+
+CONFIGFS_ATTR_OPS(packages)
+static struct configfs_item_operations packages_item_ops = {
+ .show_attribute = packages_attr_show,
+ .store_attribute = packages_attr_store,
};
/*
* Note that, since no extra work is required on ->drop_item(),
* no ->drop_item() is provided.
*/
-static struct configfs_group_operations sdcardfs_packages_group_ops = {
- .make_item = sdcardfs_packages_make_item,
+static struct configfs_group_operations packages_group_ops = {
+ .make_item = packages_make_item,
};
-static struct config_item_type sdcardfs_packages_type = {
- .ct_item_ops = &sdcardfs_packages_item_ops,
- .ct_group_ops = &sdcardfs_packages_group_ops,
- .ct_attrs = sdcardfs_packages_attrs,
+static struct config_item_type packages_type = {
+ .ct_item_ops = &packages_item_ops,
+ .ct_group_ops = &packages_group_ops,
+ .ct_attrs = packages_attrs,
.ct_owner = THIS_MODULE,
};
-static struct configfs_subsystem sdcardfs_packages_subsys = {
- .su_group = {
- .cg_item = {
- .ci_namebuf = "sdcardfs",
- .ci_type = &sdcardfs_packages_type,
+struct config_group *sd_default_groups[] = {
+ &extension_group,
+ NULL,
+};
+
+static struct packages sdcardfs_packages = {
+ .subsystem = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "sdcardfs",
+ .ci_type = &packages_type,
+ },
+ .default_groups = sd_default_groups,
},
},
};
static int configfs_sdcardfs_init(void)
{
- int ret;
- struct configfs_subsystem *subsys = &sdcardfs_packages_subsys;
+ int ret, i;
+ struct configfs_subsystem *subsys = &sdcardfs_packages.subsystem;
+ for (i = 0; sd_default_groups[i]; i++)
+ config_group_init(sd_default_groups[i]);
config_group_init(&subsys->su_group);
mutex_init(&subsys->su_mutex);
ret = configfs_register_subsystem(subsys);
if (ret) {
- printk(KERN_ERR "Error %d while registering subsystem %s\n",
+ pr_err("Error %d while registering subsystem %s\n",
ret,
subsys->su_group.cg_item.ci_namebuf);
}
@@ -420,7 +873,7 @@
static void configfs_sdcardfs_exit(void)
{
- configfs_unregister_subsystem(&sdcardfs_packages_subsys);
+ configfs_unregister_subsystem(&sdcardfs_packages.subsystem);
}
int packagelist_init(void)
@@ -429,19 +882,17 @@
kmem_cache_create("packagelist_hashtable_entry",
sizeof(struct hashtable_entry), 0, 0, NULL);
if (!hashtable_entry_cachep) {
- printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n");
+ pr_err("sdcardfs: failed creating pkgl_hashtable entry slab cache\n");
return -ENOMEM;
}
- pkgl_data_all = packagelist_create();
configfs_sdcardfs_init();
- return 0;
+ return 0;
}
void packagelist_exit(void)
{
configfs_sdcardfs_exit();
- packagelist_destroy(pkgl_data_all);
- if (hashtable_entry_cachep)
- kmem_cache_destroy(hashtable_entry_cachep);
+ packagelist_destroy();
+ kmem_cache_destroy(hashtable_entry_cachep);
}
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
index 62c59da..4c5c1aa 100644
--- a/fs/sdcardfs/sdcardfs.h
+++ b/fs/sdcardfs/sdcardfs.h
@@ -29,6 +29,7 @@
#include <linux/dcache.h>
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/aio.h>
#include <linux/mm.h>
#include <linux/mount.h>
#include <linux/namei.h>
@@ -52,7 +53,7 @@
#define SDCARDFS_ROOT_INO 1
/* useful for tracking code reachability */
-#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__)
+#define UDBG pr_default("DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__)
#define SDCARDFS_DIRENT_SIZE 256
@@ -65,71 +66,90 @@
#define AID_SDCARD_PICS 1033 /* external storage photos access */
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
#define AID_SDCARD_ALL 1035 /* access all users external storage */
+#define AID_MEDIA_OBB 1059 /* obb files */
+
+#define AID_SDCARD_IMAGE 1057
#define AID_PACKAGE_INFO 1027
-#define fix_derived_permission(x) \
+
+/*
+ * Permissions are handled by our permission function.
+ * We don't want anyone who happens to look at our inode value to prematurely
+ * block access, so store more permissive values. These are probably never
+ * used.
+ */
+#define fixup_tmp_permissions(x) \
do { \
- (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \
- (x)->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(x))); \
- (x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\
+ (x)->i_uid = make_kuid(&init_user_ns, \
+ SDCARDFS_I(x)->data->d_uid); \
+ (x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \
+ (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\
} while (0)
-
/* OVERRIDE_CRED() and REVERT_CRED()
- * OVERRID_CRED()
- * backup original task->cred
- * and modifies task->cred->fsuid/fsgid to specified value.
+ * OVERRIDE_CRED()
+ * backup original task->cred
+ * and modifies task->cred->fsuid/fsgid to specified value.
* REVERT_CRED()
- * restore original task->cred->fsuid/fsgid.
+ * restore original task->cred->fsuid/fsgid.
* These two macro should be used in pair, and OVERRIDE_CRED() should be
* placed at the beginning of a function, right after variable declaration.
*/
-#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \
- saved_cred = override_fsids(sdcardfs_sbi); \
- if (!saved_cred) { return -ENOMEM; }
+#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \
+ do { \
+ saved_cred = override_fsids(sdcardfs_sbi, info->data); \
+ if (!saved_cred) \
+ return -ENOMEM; \
+ } while (0)
-#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \
- saved_cred = override_fsids(sdcardfs_sbi); \
- if (!saved_cred) { return ERR_PTR(-ENOMEM); }
+#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \
+ do { \
+ saved_cred = override_fsids(sdcardfs_sbi, info->data); \
+ if (!saved_cred) \
+ return ERR_PTR(-ENOMEM); \
+ } while (0)
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
-#define DEBUG_CRED() \
- printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \
- __FUNCTION__, __LINE__, \
- (int)current->cred->fsuid, \
- (int)current->cred->fsgid);
-
/* Android 5.0 support */
/* Permission mode for a specific node. Controls how file permissions
- * are derived for children nodes. */
+ * are derived for children nodes.
+ */
typedef enum {
- /* Nothing special; this node should just inherit from its parent. */
- PERM_INHERIT,
- /* This node is one level above a normal root; used for legacy layouts
- * which use the first level to represent user_id. */
- PERM_PRE_ROOT,
- /* This node is "/" */
- PERM_ROOT,
- /* This node is "/Android" */
- PERM_ANDROID,
- /* This node is "/Android/data" */
- PERM_ANDROID_DATA,
- /* This node is "/Android/obb" */
- PERM_ANDROID_OBB,
- /* This node is "/Android/media" */
- PERM_ANDROID_MEDIA,
+ /* Nothing special; this node should just inherit from its parent. */
+ PERM_INHERIT,
+ /* This node is one level above a normal root; used for legacy layouts
+ * which use the first level to represent user_id.
+ */
+ PERM_PRE_ROOT,
+ /* This node is "/" */
+ PERM_ROOT,
+ /* This node is "/Android" */
+ PERM_ANDROID,
+ /* This node is "/Android/data" */
+ PERM_ANDROID_DATA,
+ /* This node is "/Android/obb" */
+ PERM_ANDROID_OBB,
+ /* This node is "/Android/media" */
+ PERM_ANDROID_MEDIA,
+ /* This node is "/Android/[data|media|obb]/[package]" */
+ PERM_ANDROID_PACKAGE,
+ /* This node is "/Android/[data|media|obb]/[package]/cache" */
+ PERM_ANDROID_PACKAGE_CACHE,
} perm_t;
struct sdcardfs_sb_info;
struct sdcardfs_mount_options;
+struct sdcardfs_inode_info;
+struct sdcardfs_inode_data;
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
-const struct cred * override_fsids(struct sdcardfs_sb_info* sbi);
+const struct cred *override_fsids(struct sdcardfs_sb_info *sbi,
+ struct sdcardfs_inode_data *data);
/* Do not directly use this function, use REVERT_CRED() instead. */
-void revert_fsids(const struct cred * old_cred);
+void revert_fsids(const struct cred *old_cred);
/* operations vectors defined in specific files */
extern const struct file_operations sdcardfs_main_fops;
@@ -161,14 +181,26 @@
const struct vm_operations_struct *lower_vm_ops;
};
-/* sdcardfs inode data in memory */
-struct sdcardfs_inode_info {
- struct inode *lower_inode;
- /* state derived based on current position in hierachy */
+struct sdcardfs_inode_data {
+ struct kref refcount;
+ bool abandoned;
+
perm_t perm;
userid_t userid;
uid_t d_uid;
bool under_android;
+ bool under_cache;
+ bool under_obb;
+};
+
+/* sdcardfs inode data in memory */
+struct sdcardfs_inode_info {
+ struct inode *lower_inode;
+ /* state derived based on current position in hierarchy */
+ struct sdcardfs_inode_data *data;
+
+ /* top folder for ownership */
+ struct sdcardfs_inode_data *top_data;
struct inode vfs_inode;
};
@@ -185,18 +217,25 @@
uid_t fs_low_uid;
gid_t fs_low_gid;
userid_t fs_user_id;
- gid_t gid;
- mode_t mask;
bool multiuser;
unsigned int reserved_mb;
};
+struct sdcardfs_vfsmount_options {
+ gid_t gid;
+ mode_t mask;
+};
+
+extern int parse_options_remount(struct super_block *sb, char *options, int silent,
+ struct sdcardfs_vfsmount_options *vfsopts);
+
/* sdcardfs super-block data in memory */
struct sdcardfs_sb_info {
struct super_block *sb;
struct super_block *lower_sb;
/* derived perm policy : some of options have been added
- * to sdcardfs_mount_options (Android 4.4 support) */
+ * to sdcardfs_mount_options (Android 4.4 support)
+ */
struct sdcardfs_mount_options options;
spinlock_t lock; /* protects obbpath */
char *obbpath_s;
@@ -307,7 +346,7 @@
{ \
struct path pname; \
spin_lock(&SDCARDFS_D(dent)->lock); \
- if(SDCARDFS_D(dent)->pname.dentry) { \
+ if (SDCARDFS_D(dent)->pname.dentry) { \
pathcpy(&pname, &SDCARDFS_D(dent)->pname); \
SDCARDFS_D(dent)->pname.dentry = NULL; \
SDCARDFS_D(dent)->pname.mnt = NULL; \
@@ -321,38 +360,97 @@
SDCARDFS_DENT_FUNC(lower_path)
SDCARDFS_DENT_FUNC(orig_path)
-static inline int get_gid(struct sdcardfs_inode_info *info) {
- struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
- if (sb_info->options.gid == AID_SDCARD_RW) {
+static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo)
+{
+ return sbinfo && sbinfo->sb
+ && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
+}
+
+static inline struct sdcardfs_inode_data *data_get(
+ struct sdcardfs_inode_data *data)
+{
+ if (data)
+ kref_get(&data->refcount);
+ return data;
+}
+
+static inline struct sdcardfs_inode_data *top_data_get(
+ struct sdcardfs_inode_info *info)
+{
+ return data_get(info->top_data);
+}
+
+extern void data_release(struct kref *ref);
+
+static inline void data_put(struct sdcardfs_inode_data *data)
+{
+ kref_put(&data->refcount, data_release);
+}
+
+static inline void release_own_data(struct sdcardfs_inode_info *info)
+{
+ /*
+ * This happens exactly once per inode. At this point, the inode that
+ * originally held this data is about to be freed, and all references
+ * to it are held as a top value, and will likely be released soon.
+ */
+ info->data->abandoned = true;
+ data_put(info->data);
+}
+
+static inline void set_top(struct sdcardfs_inode_info *info,
+ struct sdcardfs_inode_data *top)
+{
+ struct sdcardfs_inode_data *old_top = info->top_data;
+
+ if (top)
+ data_get(top);
+ info->top_data = top;
+ if (old_top)
+ data_put(old_top);
+}
+
+static inline int get_gid(struct vfsmount *mnt,
+ struct sdcardfs_inode_data *data)
+{
+ struct sdcardfs_vfsmount_options *opts = mnt->data;
+
+ if (opts->gid == AID_SDCARD_RW)
/* As an optimization, certain trusted system components only run
* as owner but operate across all users. Since we're now handing
* out the sdcard_rw GID only to trusted apps, we're okay relaxing
* the user boundary enforcement for the default view. The UIDs
- * assigned to app directories are still multiuser aware. */
+ * assigned to app directories are still multiuser aware.
+ */
return AID_SDCARD_RW;
- } else {
- return multiuser_get_uid(info->userid, sb_info->options.gid);
- }
+ else
+ return multiuser_get_uid(data->userid, opts->gid);
}
-static inline int get_mode(struct sdcardfs_inode_info *info) {
+
+static inline int get_mode(struct vfsmount *mnt,
+ struct sdcardfs_inode_info *info,
+ struct sdcardfs_inode_data *data)
+{
int owner_mode;
int filtered_mode;
- struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
- int visible_mode = 0775 & ~sb_info->options.mask;
+ struct sdcardfs_vfsmount_options *opts = mnt->data;
+ int visible_mode = 0775 & ~opts->mask;
- if (info->perm == PERM_PRE_ROOT) {
+
+ if (data->perm == PERM_PRE_ROOT) {
/* Top of multi-user view should always be visible to ensure
- * secondary users can traverse inside. */
+ * secondary users can traverse inside.
+ */
visible_mode = 0711;
- } else if (info->under_android) {
+ } else if (data->under_android) {
/* Block "other" access to Android directories, since only apps
* belonging to a specific user should be in there; we still
- * leave +x open for the default view. */
- if (sb_info->options.gid == AID_SDCARD_RW) {
+ * leave +x open for the default view.
+ */
+ if (opts->gid == AID_SDCARD_RW)
visible_mode = visible_mode & ~0006;
- } else {
+ else
visible_mode = visible_mode & ~0007;
- }
}
owner_mode = info->lower_inode->i_mode & 0700;
filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
@@ -377,7 +475,7 @@
/* in case of a local obb dentry
* the orig_path should be returned
*/
- if(has_graft_path(dent))
+ if (has_graft_path(dent))
sdcardfs_get_orig_path(dent, real_lower);
else
sdcardfs_get_lower_path(dent, real_lower);
@@ -386,7 +484,7 @@
static inline void sdcardfs_put_real_lower(const struct dentry *dent,
struct path *real_lower)
{
- if(has_graft_path(dent))
+ if (has_graft_path(dent))
sdcardfs_put_orig_path(dent, real_lower);
else
sdcardfs_put_lower_path(dent, real_lower);
@@ -396,20 +494,32 @@
extern struct list_head sdcardfs_super_list;
/* for packagelist.c */
-extern appid_t get_appid(void *pkgl_id, const char *app_name);
-extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
+extern appid_t get_appid(const char *app_name);
+extern appid_t get_ext_gid(const char *app_name);
+extern appid_t is_excluded(const char *app_name, userid_t userid);
+extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name);
extern int open_flags_to_access_mode(int open_flags);
extern int packagelist_init(void);
extern void packagelist_exit(void);
/* for derived_perm.c */
+#define BY_NAME (1 << 0)
+#define BY_USERID (1 << 1)
+struct limit_search {
+ unsigned int flags;
+ struct qstr name;
+ userid_t userid;
+};
+
extern void setup_derived_state(struct inode *inode, perm_t perm,
- userid_t userid, uid_t uid, bool under_android);
+ userid_t userid, uid_t uid, bool under_android,
+ struct sdcardfs_inode_data *top);
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
-extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry);
-extern void get_derive_permissions_recursive(struct dentry *parent);
+extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name);
+extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
extern void update_derived_permission_lock(struct dentry *dentry);
+void fixup_lower_ownership(struct dentry *dentry, const char *name);
extern int need_graft_path(struct dentry *dentry);
extern int is_base_obbpath(struct dentry *dentry);
extern int is_obbpath_invalid(struct dentry *dentry);
@@ -419,6 +529,7 @@
static inline struct dentry *lock_parent(struct dentry *dentry)
{
struct dentry *dir = dget_parent(dentry);
+
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
return dir;
}
@@ -444,7 +555,7 @@
goto out_unlock;
}
- err = vfs_mkdir(parent.dentry->d_inode, dent, mode);
+ err = vfs_mkdir2(parent.mnt, parent.dentry->d_inode, dent, mode);
if (err) {
if (err == -EEXIST)
err = 0;
@@ -455,7 +566,7 @@
attrs.ia_gid = make_kgid(&init_user_ns, gid);
attrs.ia_valid = ATTR_UID | ATTR_GID;
mutex_lock(&dent->d_inode->i_mutex);
- notify_change(dent, &attrs, NULL);
+ notify_change2(parent.mnt, dent, &attrs, NULL);
mutex_unlock(&dent->d_inode->i_mutex);
out_dput:
@@ -513,12 +624,16 @@
return 1;
}
-/* Copies attrs and maintains sdcardfs managed attrs */
+/*
+ * Copies attrs and maintains sdcardfs managed attrs
+ * Since our permission check handles all special permissions, set those to be open
+ */
static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src)
{
- dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest));
- dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid);
- dest->i_gid = make_kgid(&init_user_ns, get_gid(SDCARDFS_I(dest)));
+ dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG |
+ S_IROTH | S_IXOTH; /* 0775 */
+ dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->data->d_uid);
+ dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW);
dest->i_rdev = src->i_rdev;
dest->i_atime = src->i_atime;
dest->i_mtime = src->i_mtime;
@@ -527,4 +642,23 @@
dest->i_flags = src->i_flags;
set_nlink(dest, src->i_nlink);
}
+
+static inline bool str_case_eq(const char *s1, const char *s2)
+{
+ return !strcasecmp(s1, s2);
+}
+
+static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len)
+{
+ return !strncasecmp(s1, s2, len);
+}
+
+static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2)
+{
+ return q1->len == q2->len && str_case_eq(q1->name, q2->name);
+}
+
+/* */
+#define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1)
+
#endif /* not _SDCARDFS_H_ */
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index 1d64901..7f4539b 100644
--- a/fs/sdcardfs/super.c
+++ b/fs/sdcardfs/super.c
@@ -26,6 +26,23 @@
*/
static struct kmem_cache *sdcardfs_inode_cachep;
+/*
+ * To support the top references, we must track some data separately.
+ * An sdcardfs_inode_info always has a reference to its data, and once set up,
+ * also has a reference to its top. The top may be itself, in which case it
+ * holds two references to its data. When top is changed, it takes a ref to the
+ * new data and then drops the ref to the old data.
+ */
+static struct kmem_cache *sdcardfs_inode_data_cachep;
+
+void data_release(struct kref *ref)
+{
+ struct sdcardfs_inode_data *data =
+ container_of(ref, struct sdcardfs_inode_data, refcount);
+
+ kmem_cache_free(sdcardfs_inode_data_cachep, data);
+}
+
/* final actions when unmounting a file system */
static void sdcardfs_put_super(struct super_block *sb)
{
@@ -36,7 +53,7 @@
if (!spd)
return;
- if(spd->obbpath_s) {
+ if (spd->obbpath_s) {
kfree(spd->obbpath_s);
path_put(&spd->obbpath);
}
@@ -64,7 +81,7 @@
if (sbi->options.reserved_mb) {
/* Invalid statfs informations. */
if (buf->f_bsize == 0) {
- printk(KERN_ERR "Returned block size is zero.\n");
+ pr_err("Returned block size is zero.\n");
return -EINVAL;
}
@@ -100,8 +117,7 @@
* SILENT, but anything else left over is an error.
*/
if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) {
- printk(KERN_ERR
- "sdcardfs: remount flags 0x%x unsupported\n", *flags);
+ pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags);
err = -EINVAL;
}
@@ -109,6 +125,54 @@
}
/*
+ * @mnt: mount point we are remounting
+ * @sb: superblock we are remounting
+ * @flags: numeric mount options
+ * @options: mount options string
+ */
+static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb,
+ int *flags, char *options)
+{
+ int err = 0;
+
+ /*
+ * The VFS will take care of "ro" and "rw" flags among others. We
+ * can safely accept a few flags (RDONLY, MANDLOCK), and honor
+ * SILENT, but anything else left over is an error.
+ */
+ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) {
+ pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags);
+ err = -EINVAL;
+ }
+ pr_info("Remount options were %s for vfsmnt %p.\n", options, mnt);
+ err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data);
+
+
+ return err;
+}
+
+static void *sdcardfs_clone_mnt_data(void *data)
+{
+ struct sdcardfs_vfsmount_options *opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
+ struct sdcardfs_vfsmount_options *old = data;
+
+ if (!opt)
+ return NULL;
+ opt->gid = old->gid;
+ opt->mask = old->mask;
+ return opt;
+}
+
+static void sdcardfs_copy_mnt_data(void *data, void *newdata)
+{
+ struct sdcardfs_vfsmount_options *old = data;
+ struct sdcardfs_vfsmount_options *new = newdata;
+
+ old->gid = new->gid;
+ old->mask = new->mask;
+}
+
+/*
* Called by iput() when the inode reference count reached zero
* and the inode is not hashed anywhere. Used to clear anything
* that needs to be, before the inode is completely destroyed and put
@@ -119,6 +183,7 @@
struct inode *lower_inode;
truncate_inode_pages(&inode->i_data, 0);
+ set_top(SDCARDFS_I(inode), NULL);
clear_inode(inode);
/*
* Decrement a reference to a lower_inode, which was incremented
@@ -132,6 +197,7 @@
static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
{
struct sdcardfs_inode_info *i;
+ struct sdcardfs_inode_data *d;
i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL);
if (!i)
@@ -140,13 +206,31 @@
/* memset everything up to the inode to 0 */
memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode));
+ d = kmem_cache_alloc(sdcardfs_inode_data_cachep,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!d) {
+ kmem_cache_free(sdcardfs_inode_cachep, i);
+ return NULL;
+ }
+
+ i->data = d;
+ kref_init(&d->refcount);
+
i->vfs_inode.i_version = 1;
return &i->vfs_inode;
}
+static void i_callback(struct rcu_head *head)
+{
+ struct inode *inode = container_of(head, struct inode, i_rcu);
+
+ release_own_data(SDCARDFS_I(inode));
+ kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
+}
+
static void sdcardfs_destroy_inode(struct inode *inode)
{
- kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
+ call_rcu(&inode->i_rcu, i_callback);
}
/* sdcardfs inode cache constructor */
@@ -159,22 +243,31 @@
int sdcardfs_init_inode_cache(void)
{
- int err = 0;
-
sdcardfs_inode_cachep =
kmem_cache_create("sdcardfs_inode_cache",
sizeof(struct sdcardfs_inode_info), 0,
SLAB_RECLAIM_ACCOUNT, init_once);
+
if (!sdcardfs_inode_cachep)
- err = -ENOMEM;
- return err;
+ return -ENOMEM;
+
+ sdcardfs_inode_data_cachep =
+ kmem_cache_create("sdcardfs_inode_data_cache",
+ sizeof(struct sdcardfs_inode_data), 0,
+ SLAB_RECLAIM_ACCOUNT, NULL);
+ if (!sdcardfs_inode_data_cachep) {
+ kmem_cache_destroy(sdcardfs_inode_cachep);
+ return -ENOMEM;
+ }
+
+ return 0;
}
/* sdcardfs inode cache destructor */
void sdcardfs_destroy_inode_cache(void)
{
- if (sdcardfs_inode_cachep)
- kmem_cache_destroy(sdcardfs_inode_cachep);
+ kmem_cache_destroy(sdcardfs_inode_data_cachep);
+ kmem_cache_destroy(sdcardfs_inode_cachep);
}
/*
@@ -190,19 +283,25 @@
lower_sb->s_op->umount_begin(lower_sb);
}
-static int sdcardfs_show_options(struct seq_file *m, struct dentry *root)
+static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m,
+ struct dentry *root)
{
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
struct sdcardfs_mount_options *opts = &sbi->options;
+ struct sdcardfs_vfsmount_options *vfsopts = mnt->data;
if (opts->fs_low_uid != 0)
- seq_printf(m, ",uid=%u", opts->fs_low_uid);
+ seq_printf(m, ",fsuid=%u", opts->fs_low_uid);
if (opts->fs_low_gid != 0)
- seq_printf(m, ",gid=%u", opts->fs_low_gid);
-
+ seq_printf(m, ",fsgid=%u", opts->fs_low_gid);
+ if (vfsopts->gid != 0)
+ seq_printf(m, ",gid=%u", vfsopts->gid);
if (opts->multiuser)
- seq_printf(m, ",multiuser");
-
+ seq_puts(m, ",multiuser");
+ if (vfsopts->mask)
+ seq_printf(m, ",mask=%u", vfsopts->mask);
+ if (opts->fs_user_id)
+ seq_printf(m, ",userid=%u", opts->fs_user_id);
if (opts->reserved_mb != 0)
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
@@ -213,9 +312,12 @@
.put_super = sdcardfs_put_super,
.statfs = sdcardfs_statfs,
.remount_fs = sdcardfs_remount_fs,
+ .remount_fs2 = sdcardfs_remount_fs2,
+ .clone_mnt_data = sdcardfs_clone_mnt_data,
+ .copy_mnt_data = sdcardfs_copy_mnt_data,
.evict_inode = sdcardfs_evict_inode,
.umount_begin = sdcardfs_umount_begin,
- .show_options = sdcardfs_show_options,
+ .show_options2 = sdcardfs_show_options,
.alloc_inode = sdcardfs_alloc_inode,
.destroy_inode = sdcardfs_destroy_inode,
.drop_inode = generic_delete_inode,
diff --git a/fs/seq_file.c b/fs/seq_file.c
index 3857b72..fbb1688 100644
--- a/fs/seq_file.c
+++ b/fs/seq_file.c
@@ -219,8 +219,10 @@
size -= n;
buf += n;
copied += n;
- if (!m->count)
+ if (!m->count) {
+ m->from = 0;
m->index++;
+ }
if (!size)
goto Done;
}
diff --git a/fs/super.c b/fs/super.c
index cdf6dec..2693e90 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -682,7 +682,8 @@
}
/**
- * do_remount_sb - asks filesystem to change mount options.
+ * do_remount_sb2 - asks filesystem to change mount options.
+ * @mnt: mount we are looking at
* @sb: superblock in question
* @flags: numeric part of options
* @data: the rest of options
@@ -690,7 +691,7 @@
*
* Alters the mount options of a mounted file system.
*/
-int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
+int do_remount_sb2(struct vfsmount *mnt, struct super_block *sb, int flags, void *data, int force)
{
int retval;
int remount_ro;
@@ -732,7 +733,16 @@
}
}
- if (sb->s_op->remount_fs) {
+ if (mnt && sb->s_op->remount_fs2) {
+ retval = sb->s_op->remount_fs2(mnt, sb, &flags, data);
+ if (retval) {
+ if (!force)
+ goto cancel_readonly;
+ /* If forced remount, go ahead despite any errors */
+ WARN(1, "forced remount of a %s fs returned %i\n",
+ sb->s_type->name, retval);
+ }
+ } else if (sb->s_op->remount_fs) {
retval = sb->s_op->remount_fs(sb, &flags, data);
if (retval) {
if (!force)
@@ -764,6 +774,11 @@
return retval;
}
+int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
+{
+ return do_remount_sb2(NULL, sb, flags, data, force);
+}
+
static void do_emergency_remount(struct work_struct *work)
{
struct super_block *sb, *p = NULL;
@@ -1086,7 +1101,7 @@
EXPORT_SYMBOL(mount_single);
struct dentry *
-mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
+mount_fs(struct file_system_type *type, int flags, const char *name, struct vfsmount *mnt, void *data)
{
struct dentry *root;
struct super_block *sb;
@@ -1103,7 +1118,10 @@
goto out_free_secdata;
}
- root = type->mount(type, flags, name, data);
+ if (type->mount2)
+ root = type->mount2(mnt, type, flags, name, data);
+ else
+ root = type->mount(type, flags, name, data);
if (IS_ERR(root)) {
error = PTR_ERR(root);
goto out_free_secdata;
diff --git a/fs/sync.c b/fs/sync.c
index bdc729d..cdaf1d2 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -205,6 +205,7 @@
if (f.file) {
ret = vfs_fsync(f.file, datasync);
fdput(f);
+ inc_syscfs(current);
}
return ret;
}
diff --git a/fs/tracefs/Makefile b/fs/tracefs/Makefile
new file mode 100644
index 0000000..82fa35b
--- /dev/null
+++ b/fs/tracefs/Makefile
@@ -0,0 +1,4 @@
+tracefs-objs := inode.o
+
+obj-$(CONFIG_TRACING) += tracefs.o
+
diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
new file mode 100644
index 0000000..d92bdf3
--- /dev/null
+++ b/fs/tracefs/inode.c
@@ -0,0 +1,650 @@
+/*
+ * inode.c - part of tracefs, a pseudo file system for activating tracing
+ *
+ * Based on debugfs by: Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt <srostedt@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * tracefs is the file system that is used by the tracing infrastructure.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/kobject.h>
+#include <linux/namei.h>
+#include <linux/tracefs.h>
+#include <linux/fsnotify.h>
+#include <linux/seq_file.h>
+#include <linux/parser.h>
+#include <linux/magic.h>
+#include <linux/slab.h>
+
+#define TRACEFS_DEFAULT_MODE 0700
+
+static struct vfsmount *tracefs_mount;
+static int tracefs_mount_count;
+static bool tracefs_registered;
+
+static ssize_t default_read_file(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return 0;
+}
+
+static ssize_t default_write_file(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return count;
+}
+
+static const struct file_operations tracefs_file_operations = {
+ .read = default_read_file,
+ .write = default_write_file,
+ .open = simple_open,
+ .llseek = noop_llseek,
+};
+
+static struct tracefs_dir_ops {
+ int (*mkdir)(const char *name);
+ int (*rmdir)(const char *name);
+} tracefs_ops;
+
+static char *get_dname(struct dentry *dentry)
+{
+ const char *dname;
+ char *name;
+ int len = dentry->d_name.len;
+
+ dname = dentry->d_name.name;
+ name = kmalloc(len + 1, GFP_KERNEL);
+ if (!name)
+ return NULL;
+ memcpy(name, dname, len);
+ name[len] = 0;
+ return name;
+}
+
+static int tracefs_syscall_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode)
+{
+ char *name;
+ int ret;
+
+ name = get_dname(dentry);
+ if (!name)
+ return -ENOMEM;
+
+ /*
+ * The mkdir call can call the generic functions that create
+ * the files within the tracefs system. It is up to the individual
+ * mkdir routine to handle races.
+ */
+ mutex_unlock(&inode->i_mutex);
+ ret = tracefs_ops.mkdir(name);
+ mutex_lock(&inode->i_mutex);
+
+ kfree(name);
+
+ return ret;
+}
+
+static int tracefs_syscall_rmdir(struct inode *inode, struct dentry *dentry)
+{
+ char *name;
+ int ret;
+
+ name = get_dname(dentry);
+ if (!name)
+ return -ENOMEM;
+
+ /*
+ * The rmdir call can call the generic functions that create
+ * the files within the tracefs system. It is up to the individual
+ * rmdir routine to handle races.
+ * This time we need to unlock not only the parent (inode) but
+ * also the directory that is being deleted.
+ */
+ mutex_unlock(&inode->i_mutex);
+ mutex_unlock(&dentry->d_inode->i_mutex);
+
+ ret = tracefs_ops.rmdir(name);
+
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
+ mutex_lock(&dentry->d_inode->i_mutex);
+
+ kfree(name);
+
+ return ret;
+}
+
+static const struct inode_operations tracefs_dir_inode_operations = {
+ .lookup = simple_lookup,
+ .mkdir = tracefs_syscall_mkdir,
+ .rmdir = tracefs_syscall_rmdir,
+};
+
+static struct inode *tracefs_get_inode(struct super_block *sb)
+{
+ struct inode *inode = new_inode(sb);
+ if (inode) {
+ inode->i_ino = get_next_ino();
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ }
+ return inode;
+}
+
+struct tracefs_mount_opts {
+ kuid_t uid;
+ kgid_t gid;
+ umode_t mode;
+};
+
+enum {
+ Opt_uid,
+ Opt_gid,
+ Opt_mode,
+ Opt_err
+};
+
+static const match_table_t tokens = {
+ {Opt_uid, "uid=%u"},
+ {Opt_gid, "gid=%u"},
+ {Opt_mode, "mode=%o"},
+ {Opt_err, NULL}
+};
+
+struct tracefs_fs_info {
+ struct tracefs_mount_opts mount_opts;
+};
+
+static int tracefs_parse_options(char *data, struct tracefs_mount_opts *opts)
+{
+ substring_t args[MAX_OPT_ARGS];
+ int option;
+ int token;
+ kuid_t uid;
+ kgid_t gid;
+ char *p;
+
+ opts->mode = TRACEFS_DEFAULT_MODE;
+
+ while ((p = strsep(&data, ",")) != NULL) {
+ if (!*p)
+ continue;
+
+ token = match_token(p, tokens, args);
+ switch (token) {
+ case Opt_uid:
+ if (match_int(&args[0], &option))
+ return -EINVAL;
+ uid = make_kuid(current_user_ns(), option);
+ if (!uid_valid(uid))
+ return -EINVAL;
+ opts->uid = uid;
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ return -EINVAL;
+ gid = make_kgid(current_user_ns(), option);
+ if (!gid_valid(gid))
+ return -EINVAL;
+ opts->gid = gid;
+ break;
+ case Opt_mode:
+ if (match_octal(&args[0], &option))
+ return -EINVAL;
+ opts->mode = option & S_IALLUGO;
+ break;
+ /*
+ * We might like to report bad mount options here;
+ * but traditionally tracefs has ignored all mount options
+ */
+ }
+ }
+
+ return 0;
+}
+
+static int tracefs_apply_options(struct super_block *sb)
+{
+ struct tracefs_fs_info *fsi = sb->s_fs_info;
+ struct inode *inode = sb->s_root->d_inode;
+ struct tracefs_mount_opts *opts = &fsi->mount_opts;
+
+ inode->i_mode &= ~S_IALLUGO;
+ inode->i_mode |= opts->mode;
+
+ inode->i_uid = opts->uid;
+ inode->i_gid = opts->gid;
+
+ return 0;
+}
+
+static int tracefs_remount(struct super_block *sb, int *flags, char *data)
+{
+ int err;
+ struct tracefs_fs_info *fsi = sb->s_fs_info;
+
+ sync_filesystem(sb);
+ err = tracefs_parse_options(data, &fsi->mount_opts);
+ if (err)
+ goto fail;
+
+ tracefs_apply_options(sb);
+
+fail:
+ return err;
+}
+
+static int tracefs_show_options(struct seq_file *m, struct dentry *root)
+{
+ struct tracefs_fs_info *fsi = root->d_sb->s_fs_info;
+ struct tracefs_mount_opts *opts = &fsi->mount_opts;
+
+ if (!uid_eq(opts->uid, GLOBAL_ROOT_UID))
+ seq_printf(m, ",uid=%u",
+ from_kuid_munged(&init_user_ns, opts->uid));
+ if (!gid_eq(opts->gid, GLOBAL_ROOT_GID))
+ seq_printf(m, ",gid=%u",
+ from_kgid_munged(&init_user_ns, opts->gid));
+ if (opts->mode != TRACEFS_DEFAULT_MODE)
+ seq_printf(m, ",mode=%o", opts->mode);
+
+ return 0;
+}
+
+static const struct super_operations tracefs_super_operations = {
+ .statfs = simple_statfs,
+ .remount_fs = tracefs_remount,
+ .show_options = tracefs_show_options,
+};
+
+static int trace_fill_super(struct super_block *sb, void *data, int silent)
+{
+ static struct tree_descr trace_files[] = {{""}};
+ struct tracefs_fs_info *fsi;
+ int err;
+
+ save_mount_options(sb, data);
+
+ fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL);
+ sb->s_fs_info = fsi;
+ if (!fsi) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ err = tracefs_parse_options(data, &fsi->mount_opts);
+ if (err)
+ goto fail;
+
+ err = simple_fill_super(sb, TRACEFS_MAGIC, trace_files);
+ if (err)
+ goto fail;
+
+ sb->s_op = &tracefs_super_operations;
+
+ tracefs_apply_options(sb);
+
+ return 0;
+
+fail:
+ kfree(fsi);
+ sb->s_fs_info = NULL;
+ return err;
+}
+
+static struct dentry *trace_mount(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data)
+{
+ return mount_single(fs_type, flags, data, trace_fill_super);
+}
+
+static struct file_system_type trace_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "tracefs",
+ .mount = trace_mount,
+ .kill_sb = kill_litter_super,
+};
+MODULE_ALIAS_FS("tracefs");
+
+static struct dentry *start_creating(const char *name, struct dentry *parent)
+{
+ struct dentry *dentry;
+ int error;
+
+ pr_debug("tracefs: creating file '%s'\n",name);
+
+ error = simple_pin_fs(&trace_fs_type, &tracefs_mount,
+ &tracefs_mount_count);
+ if (error)
+ return ERR_PTR(error);
+
+ /* If the parent is not specified, we create it in the root.
+ * We need the root dentry to do this, which is in the super
+ * block. A pointer to that is in the struct vfsmount that we
+ * have around.
+ */
+ if (!parent)
+ parent = tracefs_mount->mnt_root;
+
+ mutex_lock(&parent->d_inode->i_mutex);
+ dentry = lookup_one_len(name, parent, strlen(name));
+ if (!IS_ERR(dentry) && dentry->d_inode) {
+ dput(dentry);
+ dentry = ERR_PTR(-EEXIST);
+ }
+ if (IS_ERR(dentry))
+ mutex_unlock(&parent->d_inode->i_mutex);
+ return dentry;
+}
+
+static struct dentry *failed_creating(struct dentry *dentry)
+{
+ mutex_unlock(&dentry->d_parent->d_inode->i_mutex);
+ dput(dentry);
+ simple_release_fs(&tracefs_mount, &tracefs_mount_count);
+ return NULL;
+}
+
+static struct dentry *end_creating(struct dentry *dentry)
+{
+ mutex_unlock(&dentry->d_parent->d_inode->i_mutex);
+ return dentry;
+}
+
+/**
+ * tracefs_create_file - create a file in the tracefs filesystem
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have.
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is NULL, then the
+ * file will be created in the root of the tracefs filesystem.
+ * @data: a pointer to something that the caller will want to get to later
+ * on. The inode.i_private pointer will point to this value on
+ * the open() call.
+ * @fops: a pointer to a struct file_operations that should be used for
+ * this file.
+ *
+ * This is the basic "create a file" function for tracefs. It allows for a
+ * wide range of flexibility in creating a file, or a directory (if you want
+ * to create a directory, the tracefs_create_dir() function is
+ * recommended to be used instead.)
+ *
+ * This function will return a pointer to a dentry if it succeeds. This
+ * pointer must be passed to the tracefs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here.) If an error occurs, %NULL will be returned.
+ *
+ * If tracefs is not enabled in the kernel, the value -%ENODEV will be
+ * returned.
+ */
+struct dentry *tracefs_create_file(const char *name, umode_t mode,
+ struct dentry *parent, void *data,
+ const struct file_operations *fops)
+{
+ struct dentry *dentry;
+ struct inode *inode;
+
+ if (!(mode & S_IFMT))
+ mode |= S_IFREG;
+ BUG_ON(!S_ISREG(mode));
+ dentry = start_creating(name, parent);
+
+ if (IS_ERR(dentry))
+ return NULL;
+
+ inode = tracefs_get_inode(dentry->d_sb);
+ if (unlikely(!inode))
+ return failed_creating(dentry);
+
+ inode->i_mode = mode;
+ inode->i_fop = fops ? fops : &tracefs_file_operations;
+ inode->i_private = data;
+ d_instantiate(dentry, inode);
+ fsnotify_create(dentry->d_parent->d_inode, dentry);
+ return end_creating(dentry);
+}
+
+static struct dentry *__create_dir(const char *name, struct dentry *parent,
+ const struct inode_operations *ops)
+{
+ struct dentry *dentry = start_creating(name, parent);
+ struct inode *inode;
+
+ if (IS_ERR(dentry))
+ return NULL;
+
+ inode = tracefs_get_inode(dentry->d_sb);
+ if (unlikely(!inode))
+ return failed_creating(dentry);
+
+ inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+ inode->i_op = ops;
+ inode->i_fop = &simple_dir_operations;
+
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inc_nlink(inode);
+ d_instantiate(dentry, inode);
+ inc_nlink(dentry->d_parent->d_inode);
+ fsnotify_mkdir(dentry->d_parent->d_inode, dentry);
+ return end_creating(dentry);
+}
+
+/**
+ * tracefs_create_dir - create a directory in the tracefs filesystem
+ * @name: a pointer to a string containing the name of the directory to
+ * create.
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is NULL, then the
+ * directory will be created in the root of the tracefs filesystem.
+ *
+ * This function creates a directory in tracefs with the given name.
+ *
+ * This function will return a pointer to a dentry if it succeeds. This
+ * pointer must be passed to the tracefs_remove() function when the file is
+ * to be removed. If an error occurs, %NULL will be returned.
+ *
+ * If tracing is not enabled in the kernel, the value -%ENODEV will be
+ * returned.
+ */
+struct dentry *tracefs_create_dir(const char *name, struct dentry *parent)
+{
+ return __create_dir(name, parent, &simple_dir_inode_operations);
+}
+
+/**
+ * tracefs_create_instance_dir - create the tracing instances directory
+ * @name: The name of the instances directory to create
+ * @parent: The parent directory that the instances directory will exist
+ * @mkdir: The function to call when a mkdir is performed.
+ * @rmdir: The function to call when a rmdir is performed.
+ *
+ * Only one instances directory is allowed.
+ *
+ * The instances directory is special as it allows for mkdir and rmdir to
+ * to be done by userspace. When a mkdir or rmdir is performed, the inode
+ * locks are released and the methhods passed in (@mkdir and @rmdir) are
+ * called without locks and with the name of the directory being created
+ * within the instances directory.
+ *
+ * Returns the dentry of the instances directory.
+ */
+struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *parent,
+ int (*mkdir)(const char *name),
+ int (*rmdir)(const char *name))
+{
+ struct dentry *dentry;
+
+ /* Only allow one instance of the instances directory. */
+ if (WARN_ON(tracefs_ops.mkdir || tracefs_ops.rmdir))
+ return NULL;
+
+ dentry = __create_dir(name, parent, &tracefs_dir_inode_operations);
+ if (!dentry)
+ return NULL;
+
+ tracefs_ops.mkdir = mkdir;
+ tracefs_ops.rmdir = rmdir;
+
+ return dentry;
+}
+
+static inline int tracefs_positive(struct dentry *dentry)
+{
+ return dentry->d_inode && !d_unhashed(dentry);
+}
+
+static int __tracefs_remove(struct dentry *dentry, struct dentry *parent)
+{
+ int ret = 0;
+
+ if (tracefs_positive(dentry)) {
+ if (dentry->d_inode) {
+ dget(dentry);
+ switch (dentry->d_inode->i_mode & S_IFMT) {
+ case S_IFDIR:
+ ret = simple_rmdir(parent->d_inode, dentry);
+ break;
+ default:
+ simple_unlink(parent->d_inode, dentry);
+ break;
+ }
+ if (!ret)
+ d_delete(dentry);
+ dput(dentry);
+ }
+ }
+ return ret;
+}
+
+/**
+ * tracefs_remove - removes a file or directory from the tracefs filesystem
+ * @dentry: a pointer to a the dentry of the file or directory to be
+ * removed.
+ *
+ * This function removes a file or directory in tracefs that was previously
+ * created with a call to another tracefs function (like
+ * tracefs_create_file() or variants thereof.)
+ */
+void tracefs_remove(struct dentry *dentry)
+{
+ struct dentry *parent;
+ int ret;
+
+ if (IS_ERR_OR_NULL(dentry))
+ return;
+
+ parent = dentry->d_parent;
+ if (!parent || !parent->d_inode)
+ return;
+
+ mutex_lock(&parent->d_inode->i_mutex);
+ ret = __tracefs_remove(dentry, parent);
+ mutex_unlock(&parent->d_inode->i_mutex);
+ if (!ret)
+ simple_release_fs(&tracefs_mount, &tracefs_mount_count);
+}
+
+/**
+ * tracefs_remove_recursive - recursively removes a directory
+ * @dentry: a pointer to a the dentry of the directory to be removed.
+ *
+ * This function recursively removes a directory tree in tracefs that
+ * was previously created with a call to another tracefs function
+ * (like tracefs_create_file() or variants thereof.)
+ */
+void tracefs_remove_recursive(struct dentry *dentry)
+{
+ struct dentry *child, *parent;
+
+ if (IS_ERR_OR_NULL(dentry))
+ return;
+
+ parent = dentry->d_parent;
+ if (!parent || !parent->d_inode)
+ return;
+
+ parent = dentry;
+ down:
+ mutex_lock(&parent->d_inode->i_mutex);
+ loop:
+ /*
+ * The parent->d_subdirs is protected by the d_lock. Outside that
+ * lock, the child can be unlinked and set to be freed which can
+ * use the d_u.d_child as the rcu head and corrupt this list.
+ */
+ spin_lock(&parent->d_lock);
+ list_for_each_entry(child, &parent->d_subdirs, d_child) {
+ if (!tracefs_positive(child))
+ continue;
+
+ /* perhaps simple_empty(child) makes more sense */
+ if (!list_empty(&child->d_subdirs)) {
+ spin_unlock(&parent->d_lock);
+ mutex_unlock(&parent->d_inode->i_mutex);
+ parent = child;
+ goto down;
+ }
+
+ spin_unlock(&parent->d_lock);
+
+ if (!__tracefs_remove(child, parent))
+ simple_release_fs(&tracefs_mount, &tracefs_mount_count);
+
+ /*
+ * The parent->d_lock protects agaist child from unlinking
+ * from d_subdirs. When releasing the parent->d_lock we can
+ * no longer trust that the next pointer is valid.
+ * Restart the loop. We'll skip this one with the
+ * tracefs_positive() check.
+ */
+ goto loop;
+ }
+ spin_unlock(&parent->d_lock);
+
+ mutex_unlock(&parent->d_inode->i_mutex);
+ child = parent;
+ parent = parent->d_parent;
+ mutex_lock(&parent->d_inode->i_mutex);
+
+ if (child != dentry)
+ /* go up */
+ goto loop;
+
+ if (!__tracefs_remove(child, parent))
+ simple_release_fs(&tracefs_mount, &tracefs_mount_count);
+ mutex_unlock(&parent->d_inode->i_mutex);
+}
+
+/**
+ * tracefs_initialized - Tells whether tracefs has been registered
+ */
+bool tracefs_initialized(void)
+{
+ return tracefs_registered;
+}
+
+static struct kobject *trace_kobj;
+
+static int __init tracefs_init(void)
+{
+ int retval;
+
+ trace_kobj = kobject_create_and_add("tracing", kernel_kobj);
+ if (!trace_kobj)
+ return -EINVAL;
+
+ retval = register_filesystem(&trace_fs_type);
+ if (!retval)
+ tracefs_registered = true;
+
+ return retval;
+}
+core_initcall(tracefs_init);
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 1b9354c..b84a1cb 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -54,6 +54,7 @@
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/slab.h>
+#include <linux/migrate.h>
static int read_block(struct inode *inode, void *addr, unsigned int block,
struct ubifs_data_node *dn)
@@ -1462,6 +1463,26 @@
return ret;
}
+#ifdef CONFIG_MIGRATION
+static int ubifs_migrate_page(struct address_space *mapping,
+ struct page *newpage, struct page *page, enum migrate_mode mode)
+{
+ int rc;
+
+ rc = migrate_page_move_mapping(mapping, newpage, page, NULL, mode, 0);
+ if (rc != MIGRATEPAGE_SUCCESS)
+ return rc;
+
+ if (PagePrivate(page)) {
+ ClearPagePrivate(page);
+ SetPagePrivate(newpage);
+ }
+
+ migrate_page_copy(newpage, page);
+ return MIGRATEPAGE_SUCCESS;
+}
+#endif
+
static int ubifs_releasepage(struct page *page, gfp_t unused_gfp_flags)
{
/*
@@ -1602,6 +1623,9 @@
.write_end = ubifs_write_end,
.invalidatepage = ubifs_invalidatepage,
.set_page_dirty = ubifs_set_page_dirty,
+#ifdef CONFIG_MIGRATION
+ .migratepage = ubifs_migrate_page,
+#endif
.releasepage = ubifs_releasepage,
};
diff --git a/fs/ubifs/tnc_commit.c b/fs/ubifs/tnc_commit.c
index b45345d..51157da 100644
--- a/fs/ubifs/tnc_commit.c
+++ b/fs/ubifs/tnc_commit.c
@@ -370,7 +370,7 @@
p = c->gap_lebs;
do {
- ubifs_assert(p < c->gap_lebs + sizeof(int) * c->lst.idx_lebs);
+ ubifs_assert(p < c->gap_lebs + c->lst.idx_lebs);
written = layout_leb_in_gaps(c, p);
if (written < 0) {
err = written;
diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c
index c470832..9816244 100644
--- a/fs/ubifs/xattr.c
+++ b/fs/ubifs/xattr.c
@@ -173,6 +173,7 @@
host_ui->xattr_cnt -= 1;
host_ui->xattr_size -= CALC_DENT_SIZE(nm->len);
host_ui->xattr_size -= CALC_XATTR_BYTES(size);
+ host_ui->xattr_names -= nm->len;
mutex_unlock(&host_ui->ui_mutex);
out_free:
make_bad_inode(inode);
@@ -533,6 +534,7 @@
host_ui->xattr_cnt += 1;
host_ui->xattr_size += CALC_DENT_SIZE(nm->len);
host_ui->xattr_size += CALC_XATTR_BYTES(ui->data_len);
+ host_ui->xattr_names += nm->len;
mutex_unlock(&host_ui->ui_mutex);
ubifs_release_budget(c, &req);
make_bad_inode(inode);
diff --git a/fs/utimes.c b/fs/utimes.c
index aa138d6..dfb4575 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -97,14 +97,14 @@
goto mnt_drop_write_and_out;
if (!inode_owner_or_capable(inode)) {
- error = inode_permission(inode, MAY_WRITE);
+ error = inode_permission2(path->mnt, inode, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
}
}
retry_deleg:
mutex_lock(&inode->i_mutex);
- error = notify_change(path->dentry, &newattrs, &delegated_inode);
+ error = notify_change2(path->mnt, path->dentry, &newattrs, &delegated_inode);
mutex_unlock(&inode->i_mutex);
if (delegated_inode) {
error = break_deleg_wait(&delegated_inode);
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index eff3421..2300743 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -515,6 +515,7 @@
}
const struct xfs_buf_ops xfs_agfl_buf_ops = {
+ .name = "xfs_agfl",
.verify_read = xfs_agfl_read_verify,
.verify_write = xfs_agfl_write_verify,
};
@@ -2271,6 +2272,7 @@
}
const struct xfs_buf_ops xfs_agf_buf_ops = {
+ .name = "xfs_agf",
.verify_read = xfs_agf_read_verify,
.verify_write = xfs_agf_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_alloc_btree.c b/fs/xfs/libxfs/xfs_alloc_btree.c
index e0e83e2..3bd94d5 100644
--- a/fs/xfs/libxfs/xfs_alloc_btree.c
+++ b/fs/xfs/libxfs/xfs_alloc_btree.c
@@ -380,6 +380,7 @@
}
const struct xfs_buf_ops xfs_allocbt_buf_ops = {
+ .name = "xfs_allocbt",
.verify_read = xfs_allocbt_read_verify,
.verify_write = xfs_allocbt_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index b7cd0a0..905b0cd 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -251,6 +251,7 @@
}
const struct xfs_buf_ops xfs_attr3_leaf_buf_ops = {
+ .name = "xfs_attr3_leaf",
.verify_read = xfs_attr3_leaf_read_verify,
.verify_write = xfs_attr3_leaf_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c
index 7510ab8..fda9c2a 100644
--- a/fs/xfs/libxfs/xfs_attr_remote.c
+++ b/fs/xfs/libxfs/xfs_attr_remote.c
@@ -198,6 +198,7 @@
}
const struct xfs_buf_ops xfs_attr3_rmt_buf_ops = {
+ .name = "xfs_attr3_rmt",
.verify_read = xfs_attr3_rmt_read_verify,
.verify_write = xfs_attr3_rmt_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
index fba7533..e6bde91 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.c
+++ b/fs/xfs/libxfs/xfs_bmap_btree.c
@@ -722,6 +722,7 @@
}
const struct xfs_buf_ops xfs_bmbt_buf_ops = {
+ .name = "xfs_bmbt",
.verify_read = xfs_bmbt_read_verify,
.verify_write = xfs_bmbt_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_da_btree.c b/fs/xfs/libxfs/xfs_da_btree.c
index fd82753..837b366 100644
--- a/fs/xfs/libxfs/xfs_da_btree.c
+++ b/fs/xfs/libxfs/xfs_da_btree.c
@@ -243,6 +243,7 @@
}
const struct xfs_buf_ops xfs_da3_node_buf_ops = {
+ .name = "xfs_da3_node",
.verify_read = xfs_da3_node_read_verify,
.verify_write = xfs_da3_node_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index 9628cec..11a3bee 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -123,6 +123,7 @@
}
const struct xfs_buf_ops xfs_dir3_block_buf_ops = {
+ .name = "xfs_dir3_block",
.verify_read = xfs_dir3_block_read_verify,
.verify_write = xfs_dir3_block_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_dir2_data.c b/fs/xfs/libxfs/xfs_dir2_data.c
index fdd803f..47504a1 100644
--- a/fs/xfs/libxfs/xfs_dir2_data.c
+++ b/fs/xfs/libxfs/xfs_dir2_data.c
@@ -302,11 +302,13 @@
}
const struct xfs_buf_ops xfs_dir3_data_buf_ops = {
+ .name = "xfs_dir3_data",
.verify_read = xfs_dir3_data_read_verify,
.verify_write = xfs_dir3_data_write_verify,
};
static const struct xfs_buf_ops xfs_dir3_data_reada_buf_ops = {
+ .name = "xfs_dir3_data_reada",
.verify_read = xfs_dir3_data_reada_verify,
.verify_write = xfs_dir3_data_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index a19174e..a03f595 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -244,11 +244,13 @@
}
const struct xfs_buf_ops xfs_dir3_leaf1_buf_ops = {
+ .name = "xfs_dir3_leaf1",
.verify_read = xfs_dir3_leaf1_read_verify,
.verify_write = xfs_dir3_leaf1_write_verify,
};
const struct xfs_buf_ops xfs_dir3_leafn_buf_ops = {
+ .name = "xfs_dir3_leafn",
.verify_read = xfs_dir3_leafn_read_verify,
.verify_write = xfs_dir3_leafn_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_dir2_node.c b/fs/xfs/libxfs/xfs_dir2_node.c
index 2ae6ac2..75431b22 100644
--- a/fs/xfs/libxfs/xfs_dir2_node.c
+++ b/fs/xfs/libxfs/xfs_dir2_node.c
@@ -149,6 +149,7 @@
}
const struct xfs_buf_ops xfs_dir3_free_buf_ops = {
+ .name = "xfs_dir3_free",
.verify_read = xfs_dir3_free_read_verify,
.verify_write = xfs_dir3_free_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_dquot_buf.c b/fs/xfs/libxfs/xfs_dquot_buf.c
index 89b7ddf..7c45d32 100644
--- a/fs/xfs/libxfs/xfs_dquot_buf.c
+++ b/fs/xfs/libxfs/xfs_dquot_buf.c
@@ -193,8 +193,7 @@
if (mp->m_quotainfo)
ndquots = mp->m_quotainfo->qi_dqperchunk;
else
- ndquots = xfs_calc_dquots_per_chunk(
- XFS_BB_TO_FSB(mp, bp->b_length));
+ ndquots = xfs_calc_dquots_per_chunk(bp->b_length);
for (i = 0; i < ndquots; i++, d++) {
if (!xfs_verify_cksum((char *)d, sizeof(struct xfs_dqblk),
@@ -303,6 +302,7 @@
}
const struct xfs_buf_ops xfs_dquot_buf_ops = {
+ .name = "xfs_dquot",
.verify_read = xfs_dquot_buf_read_verify,
.verify_write = xfs_dquot_buf_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index 23dcb72..196f2969 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -2105,6 +2105,7 @@
}
const struct xfs_buf_ops xfs_agi_buf_ops = {
+ .name = "xfs_agi",
.verify_read = xfs_agi_read_verify,
.verify_write = xfs_agi_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_ialloc_btree.c b/fs/xfs/libxfs/xfs_ialloc_btree.c
index c9b06f3..14ae3e2 100644
--- a/fs/xfs/libxfs/xfs_ialloc_btree.c
+++ b/fs/xfs/libxfs/xfs_ialloc_btree.c
@@ -297,6 +297,7 @@
}
const struct xfs_buf_ops xfs_inobt_buf_ops = {
+ .name = "xfs_inobt",
.verify_read = xfs_inobt_read_verify,
.verify_write = xfs_inobt_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 8249599..fb958db 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -141,11 +141,13 @@
}
const struct xfs_buf_ops xfs_inode_buf_ops = {
+ .name = "xfs_inode",
.verify_read = xfs_inode_buf_read_verify,
.verify_write = xfs_inode_buf_write_verify,
};
const struct xfs_buf_ops xfs_inode_buf_ra_ops = {
+ .name = "xxfs_inode_ra",
.verify_read = xfs_inode_buf_readahead_verify,
.verify_write = xfs_inode_buf_write_verify,
};
@@ -302,6 +304,14 @@
if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC))
return false;
+ /* don't allow invalid i_size */
+ if (be64_to_cpu(dip->di_size) & (1ULL << 63))
+ return false;
+
+ /* No zero-length symlinks. */
+ if (S_ISLNK(be16_to_cpu(dip->di_mode)) && dip->di_size == 0)
+ return false;
+
/* only version 3 or greater inodes are extensively verified here */
if (dip->di_version < 3)
return true;
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index 5f902fa..0c95d77b 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -606,7 +606,8 @@
* Only check the in progress field for the primary superblock as
* mkfs.xfs doesn't clear it from secondary superblocks.
*/
- return xfs_mount_validate_sb(mp, &sb, bp->b_bn == XFS_SB_DADDR,
+ return xfs_mount_validate_sb(mp, &sb,
+ bp->b_maps[0].bm_bn == XFS_SB_DADDR,
check_version);
}
@@ -704,11 +705,13 @@
}
const struct xfs_buf_ops xfs_sb_buf_ops = {
+ .name = "xfs_sb",
.verify_read = xfs_sb_read_verify,
.verify_write = xfs_sb_write_verify,
};
const struct xfs_buf_ops xfs_sb_quiet_buf_ops = {
+ .name = "xfs_sb_quiet",
.verify_read = xfs_sb_quiet_read_verify,
.verify_write = xfs_sb_write_verify,
};
diff --git a/fs/xfs/libxfs/xfs_symlink_remote.c b/fs/xfs/libxfs/xfs_symlink_remote.c
index a7dce9a..ccbbbbc 100644
--- a/fs/xfs/libxfs/xfs_symlink_remote.c
+++ b/fs/xfs/libxfs/xfs_symlink_remote.c
@@ -166,6 +166,7 @@
}
const struct xfs_buf_ops xfs_symlink_buf_ops = {
+ .name = "xfs_symlink",
.verify_read = xfs_symlink_read_verify,
.verify_write = xfs_symlink_write_verify,
};
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index f5b2453..5dcd602 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -153,6 +153,12 @@
rwsem_acquire_read(&VFS_I(ip)->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
0, 1, _THIS_IP_);
+ /* we abort the update if there was an IO error */
+ if (ioend->io_error) {
+ xfs_trans_cancel(tp, 0);
+ return ioend->io_error;
+ }
+
xfs_ilock(ip, XFS_ILOCK_EXCL);
isize = xfs_new_eof(ip, ioend->io_offset + ioend->io_size);
if (!isize) {
@@ -208,14 +214,17 @@
ioend->io_error = -EIO;
goto done;
}
- if (ioend->io_error)
- goto done;
/*
* For unwritten extents we need to issue transactions to convert a
* range to normal written extens after the data I/O has finished.
+ * Detecting and handling completion IO errors is done individually
+ * for each case as different cleanup operations need to be performed
+ * on error.
*/
if (ioend->io_type == XFS_IO_UNWRITTEN) {
+ if (ioend->io_error)
+ goto done;
error = xfs_iomap_write_unwritten(ip, ioend->io_offset,
ioend->io_size);
} else if (ioend->io_isdirect && xfs_ioend_is_append(ioend)) {
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 2810026..f7af2ee 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -1622,6 +1622,7 @@
xfs_trans_t *tp;
xfs_bstat_t *sbp = &sxp->sx_stat;
xfs_ifork_t *tempifp, *ifp, *tifp;
+ xfs_extnum_t nextents;
int src_log_flags, target_log_flags;
int error = 0;
int aforkblks = 0;
@@ -1802,7 +1803,8 @@
* pointer. Otherwise it's already NULL or
* pointing to the extent.
*/
- if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) {
+ nextents = ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ if (nextents <= XFS_INLINE_EXTS) {
ifp->if_u1.if_extents =
ifp->if_u2.if_inline_ext;
}
@@ -1821,7 +1823,8 @@
* pointer. Otherwise it's already NULL or
* pointing to the extent.
*/
- if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) {
+ nextents = tip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
+ if (nextents <= XFS_INLINE_EXTS) {
tifp->if_u1.if_extents =
tifp->if_u2.if_inline_ext;
}
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 284c2b2..f2492bd 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -376,6 +376,7 @@
out_free_pages:
for (i = 0; i < bp->b_page_count; i++)
__free_page(bp->b_pages[i]);
+ bp->b_flags &= ~_XBF_PAGES;
return error;
}
diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h
index 82002c0..136319c 100644
--- a/fs/xfs/xfs_buf.h
+++ b/fs/xfs/xfs_buf.h
@@ -131,6 +131,7 @@
struct xfs_buf_map (map) = { .bm_bn = (blkno), .bm_len = (numblk) };
struct xfs_buf_ops {
+ char *name;
void (*verify_read)(struct xfs_buf *);
void (*verify_write)(struct xfs_buf *);
};
diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
index b92fd7bc..d55a1ce 100644
--- a/fs/xfs/xfs_error.c
+++ b/fs/xfs/xfs_error.c
@@ -166,9 +166,9 @@
{
struct xfs_mount *mp = bp->b_target->bt_mount;
- xfs_alert(mp, "Metadata %s detected at %pF, block 0x%llx",
+ xfs_alert(mp, "Metadata %s detected at %pF, %s block 0x%llx",
bp->b_error == -EFSBADCRC ? "CRC error" : "corruption",
- __return_address, bp->b_bn);
+ __return_address, bp->b_ops->name, bp->b_bn);
xfs_alert(mp, "Unmount and run xfs_repair");
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 32e95c7..44e5598 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2969,13 +2969,14 @@
* We need to check under the i_flags_lock for a valid inode
* here. Skip it if it is not valid or the wrong inode.
*/
- spin_lock(&ip->i_flags_lock);
- if (!ip->i_ino ||
+ spin_lock(&iq->i_flags_lock);
+ if (!iq->i_ino ||
+ __xfs_iflags_test(iq, XFS_ISTALE) ||
(XFS_INO_TO_AGINO(mp, iq->i_ino) & mask) != first_index) {
- spin_unlock(&ip->i_flags_lock);
+ spin_unlock(&iq->i_flags_lock);
continue;
}
- spin_unlock(&ip->i_flags_lock);
+ spin_unlock(&iq->i_flags_lock);
/*
* Do an un-protected check to see if the inode is dirty and
@@ -3091,7 +3092,7 @@
struct xfs_buf **bpp)
{
struct xfs_mount *mp = ip->i_mount;
- struct xfs_buf *bp;
+ struct xfs_buf *bp = NULL;
struct xfs_dinode *dip;
int error;
@@ -3133,14 +3134,22 @@
}
/*
- * Get the buffer containing the on-disk inode.
+ * Get the buffer containing the on-disk inode. We are doing a try-lock
+ * operation here, so we may get an EAGAIN error. In that case, we
+ * simply want to return with the inode still dirty.
+ *
+ * If we get any other error, we effectively have a corruption situation
+ * and we cannot flush the inode, so we treat it the same as failing
+ * xfs_iflush_int().
*/
error = xfs_imap_to_bp(mp, NULL, &ip->i_imap, &dip, &bp, XBF_TRYLOCK,
0);
- if (error || !bp) {
+ if (error == -EAGAIN) {
xfs_ifunlock(ip);
return error;
}
+ if (error)
+ goto corrupt_out;
/*
* First flush out the inode that xfs_iflush was called with.
@@ -3168,7 +3177,8 @@
return 0;
corrupt_out:
- xfs_buf_relse(bp);
+ if (bp)
+ xfs_buf_relse(bp);
xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
cluster_corrupt_out:
error = -EFSCORRUPTED;
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 5942b9f..315f96c 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -3861,6 +3861,7 @@
agi->agi_unlinked[bucket] = cpu_to_be32(NULLAGINO);
offset = offsetof(xfs_agi_t, agi_unlinked) +
(sizeof(xfs_agino_t) * bucket);
+ xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF);
xfs_trans_log_buf(tp, agibp, offset,
(offset + sizeof(xfs_agino_t) - 1));
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 9f622fe..73b6c8f 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1248,6 +1248,22 @@
/* ro -> rw */
if ((mp->m_flags & XFS_MOUNT_RDONLY) && !(*flags & MS_RDONLY)) {
+ if (mp->m_flags & XFS_MOUNT_NORECOVERY) {
+ xfs_warn(mp,
+ "ro->rw transition prohibited on norecovery mount");
+ return -EINVAL;
+ }
+
+ if (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5 &&
+ xfs_sb_has_ro_compat_feature(sbp,
+ XFS_SB_FEAT_RO_COMPAT_UNKNOWN)) {
+ xfs_warn(mp,
+"ro->rw transition prohibited on unknown (0x%x) ro-compat filesystem",
+ (sbp->sb_features_ro_compat &
+ XFS_SB_FEAT_RO_COMPAT_UNKNOWN));
+ return -EINVAL;
+ }
+
mp->m_flags &= ~XFS_MOUNT_RDONLY;
/*
diff --git a/include/asm-generic/uaccess.h b/include/asm-generic/uaccess.h
index 72d8803..32901d1 100644
--- a/include/asm-generic/uaccess.h
+++ b/include/asm-generic/uaccess.h
@@ -163,9 +163,10 @@
#define put_user(x, ptr) \
({ \
+ void *__p = (ptr); \
might_fault(); \
- access_ok(VERIFY_WRITE, ptr, sizeof(*ptr)) ? \
- __put_user(x, ptr) : \
+ access_ok(VERIFY_WRITE, __p, sizeof(*ptr)) ? \
+ __put_user((x), ((__typeof__(*(ptr)) *)__p)) : \
-EFAULT; \
})
@@ -225,17 +226,22 @@
#define get_user(x, ptr) \
({ \
+ const void *__p = (ptr); \
might_fault(); \
- access_ok(VERIFY_READ, ptr, sizeof(*ptr)) ? \
- __get_user(x, ptr) : \
- -EFAULT; \
+ access_ok(VERIFY_READ, __p, sizeof(*ptr)) ? \
+ __get_user((x), (__typeof__(*(ptr)) *)__p) : \
+ ((x) = (__typeof__(*(ptr)))0,-EFAULT); \
})
#ifndef __get_user_fn
static inline int __get_user_fn(size_t size, const void __user *ptr, void *x)
{
- size = __copy_from_user(x, ptr, size);
- return size ? -EFAULT : size;
+ size_t n = __copy_from_user(x, ptr, size);
+ if (unlikely(n)) {
+ memset(x + (size - n), 0, n);
+ return -EFAULT;
+ }
+ return 0;
}
#define __get_user_fn(sz, u, k) __get_user_fn(sz, u, k)
@@ -255,11 +261,13 @@
static inline long copy_from_user(void *to,
const void __user * from, unsigned long n)
{
+ unsigned long res = n;
might_fault();
- if (access_ok(VERIFY_READ, from, n))
- return __copy_from_user(to, from, n);
- else
- return n;
+ if (likely(access_ok(VERIFY_READ, from, n)))
+ res = __copy_from_user(to, from, n);
+ if (unlikely(res))
+ memset(to + (n - res), 0, res);
+ return res;
}
static inline long copy_to_user(void __user *to,
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index ac78910..390eae19 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -227,6 +227,14 @@
*(.data..init_task)
/*
+ * Allow architectures to handle ro_after_init data on their
+ * own by defining an empty RO_AFTER_INIT_DATA.
+ */
+#ifndef RO_AFTER_INIT_DATA
+#define RO_AFTER_INIT_DATA *(.data..ro_after_init)
+#endif
+
+/*
* Read only Data
*/
#define RO_DATA_SECTION(align) \
@@ -234,6 +242,7 @@
.rodata : AT(ADDR(.rodata) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start_rodata) = .; \
*(.rodata) *(.rodata.*) \
+ RO_AFTER_INIT_DATA /* Read only after init */ \
*(__vermagic) /* Kernel version magic */ \
. = ALIGN(8); \
VMLINUX_SYMBOL(__start___tracepoints_ptrs) = .; \
diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h
index 3b4af1d7..a25414c 100644
--- a/include/crypto/internal/hash.h
+++ b/include/crypto/internal/hash.h
@@ -173,6 +173,16 @@
return crypto_alloc_instance2(name, alg, ahash_instance_headroom());
}
+static inline void ahash_request_complete(struct ahash_request *req, int err)
+{
+ req->base.complete(&req->base, err);
+}
+
+static inline u32 ahash_request_flags(struct ahash_request *req)
+{
+ return req->base.flags;
+}
+
static inline struct crypto_ahash *crypto_spawn_ahash(
struct crypto_ahash_spawn *spawn)
{
diff --git a/include/drm/drm_cache.h b/include/drm/drm_cache.h
index 461a055..cebecff 100644
--- a/include/drm/drm_cache.h
+++ b/include/drm/drm_cache.h
@@ -39,6 +39,8 @@
{
#if defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
return false;
+#elif defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON3)
+ return false;
#else
return true;
#endif
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index f4ad254..39d099a 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -119,7 +119,7 @@
int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info);
int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper);
-bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel);
+int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel);
int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper);
int drm_fb_helper_debug_enter(struct fb_info *info);
int drm_fb_helper_debug_leave(struct fb_info *info);
diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
index 0ccf7f2..3f3367c 100644
--- a/include/drm/ttm/ttm_bo_api.h
+++ b/include/drm/ttm/ttm_bo_api.h
@@ -316,6 +316,20 @@
*/
extern int ttm_bo_wait(struct ttm_buffer_object *bo, bool lazy,
bool interruptible, bool no_wait);
+
+/**
+ * ttm_bo_mem_compat - Check if proposed placement is compatible with a bo
+ *
+ * @placement: Return immediately if buffer is busy.
+ * @mem: The struct ttm_mem_reg indicating the region where the bo resides
+ * @new_flags: Describes compatible placement found
+ *
+ * Returns true if the placement is compatible
+ */
+extern bool ttm_bo_mem_compat(struct ttm_placement *placement,
+ struct ttm_mem_reg *mem,
+ uint32_t *new_flags);
+
/**
* ttm_bo_validate
*
diff --git a/include/drm/ttm/ttm_object.h b/include/drm/ttm/ttm_object.h
index ed953f9..1487011 100644
--- a/include/drm/ttm/ttm_object.h
+++ b/include/drm/ttm/ttm_object.h
@@ -229,6 +229,8 @@
* @ref_type: The type of reference.
* @existed: Upon completion, indicates that an identical reference object
* already existed, and the refcount was upped on that object instead.
+ * @require_existed: Fail with -EPERM if an identical ref object didn't
+ * already exist.
*
* Checks that the base object is shareable and adds a ref object to it.
*
@@ -243,7 +245,8 @@
*/
extern int ttm_ref_object_add(struct ttm_object_file *tfile,
struct ttm_base_object *base,
- enum ttm_ref_type ref_type, bool *existed);
+ enum ttm_ref_type ref_type, bool *existed,
+ bool require_existed);
extern bool ttm_ref_object_exists(struct ttm_object_file *tfile,
struct ttm_base_object *base);
diff --git a/include/linux/atomic.h b/include/linux/atomic.h
index 5b08a85..41ea776 100644
--- a/include/linux/atomic.h
+++ b/include/linux/atomic.h
@@ -2,6 +2,329 @@
#ifndef _LINUX_ATOMIC_H
#define _LINUX_ATOMIC_H
#include <asm/atomic.h>
+#include <asm/barrier.h>
+
+/*
+ * Relaxed variants of xchg, cmpxchg and some atomic operations.
+ *
+ * We support four variants:
+ *
+ * - Fully ordered: The default implementation, no suffix required.
+ * - Acquire: Provides ACQUIRE semantics, _acquire suffix.
+ * - Release: Provides RELEASE semantics, _release suffix.
+ * - Relaxed: No ordering guarantees, _relaxed suffix.
+ *
+ * For compound atomics performing both a load and a store, ACQUIRE
+ * semantics apply only to the load and RELEASE semantics only to the
+ * store portion of the operation. Note that a failed cmpxchg_acquire
+ * does -not- imply any memory ordering constraints.
+ *
+ * See Documentation/memory-barriers.txt for ACQUIRE/RELEASE definitions.
+ */
+
+#ifndef atomic_read_acquire
+#define atomic_read_acquire(v) smp_load_acquire(&(v)->counter)
+#endif
+
+#ifndef atomic_set_release
+#define atomic_set_release(v, i) smp_store_release(&(v)->counter, (i))
+#endif
+
+/*
+ * The idea here is to build acquire/release variants by adding explicit
+ * barriers on top of the relaxed variant. In the case where the relaxed
+ * variant is already fully ordered, no additional barriers are needed.
+ */
+#define __atomic_op_acquire(op, args...) \
+({ \
+ typeof(op##_relaxed(args)) __ret = op##_relaxed(args); \
+ smp_mb__after_atomic(); \
+ __ret; \
+})
+
+#define __atomic_op_release(op, args...) \
+({ \
+ smp_mb__before_atomic(); \
+ op##_relaxed(args); \
+})
+
+#define __atomic_op_fence(op, args...) \
+({ \
+ typeof(op##_relaxed(args)) __ret; \
+ smp_mb__before_atomic(); \
+ __ret = op##_relaxed(args); \
+ smp_mb__after_atomic(); \
+ __ret; \
+})
+
+/* atomic_add_return_relaxed */
+#ifndef atomic_add_return_relaxed
+#define atomic_add_return_relaxed atomic_add_return
+#define atomic_add_return_acquire atomic_add_return
+#define atomic_add_return_release atomic_add_return
+
+#else /* atomic_add_return_relaxed */
+
+#ifndef atomic_add_return_acquire
+#define atomic_add_return_acquire(...) \
+ __atomic_op_acquire(atomic_add_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic_add_return_release
+#define atomic_add_return_release(...) \
+ __atomic_op_release(atomic_add_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic_add_return
+#define atomic_add_return(...) \
+ __atomic_op_fence(atomic_add_return, __VA_ARGS__)
+#endif
+#endif /* atomic_add_return_relaxed */
+
+/* atomic_sub_return_relaxed */
+#ifndef atomic_sub_return_relaxed
+#define atomic_sub_return_relaxed atomic_sub_return
+#define atomic_sub_return_acquire atomic_sub_return
+#define atomic_sub_return_release atomic_sub_return
+
+#else /* atomic_sub_return_relaxed */
+
+#ifndef atomic_sub_return_acquire
+#define atomic_sub_return_acquire(...) \
+ __atomic_op_acquire(atomic_sub_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic_sub_return_release
+#define atomic_sub_return_release(...) \
+ __atomic_op_release(atomic_sub_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic_sub_return
+#define atomic_sub_return(...) \
+ __atomic_op_fence(atomic_sub_return, __VA_ARGS__)
+#endif
+#endif /* atomic_sub_return_relaxed */
+
+/* atomic_xchg_relaxed */
+#ifndef atomic_xchg_relaxed
+#define atomic_xchg_relaxed atomic_xchg
+#define atomic_xchg_acquire atomic_xchg
+#define atomic_xchg_release atomic_xchg
+
+#else /* atomic_xchg_relaxed */
+
+#ifndef atomic_xchg_acquire
+#define atomic_xchg_acquire(...) \
+ __atomic_op_acquire(atomic_xchg, __VA_ARGS__)
+#endif
+
+#ifndef atomic_xchg_release
+#define atomic_xchg_release(...) \
+ __atomic_op_release(atomic_xchg, __VA_ARGS__)
+#endif
+
+#ifndef atomic_xchg
+#define atomic_xchg(...) \
+ __atomic_op_fence(atomic_xchg, __VA_ARGS__)
+#endif
+#endif /* atomic_xchg_relaxed */
+
+/* atomic_cmpxchg_relaxed */
+#ifndef atomic_cmpxchg_relaxed
+#define atomic_cmpxchg_relaxed atomic_cmpxchg
+#define atomic_cmpxchg_acquire atomic_cmpxchg
+#define atomic_cmpxchg_release atomic_cmpxchg
+
+#else /* atomic_cmpxchg_relaxed */
+
+#ifndef atomic_cmpxchg_acquire
+#define atomic_cmpxchg_acquire(...) \
+ __atomic_op_acquire(atomic_cmpxchg, __VA_ARGS__)
+#endif
+
+#ifndef atomic_cmpxchg_release
+#define atomic_cmpxchg_release(...) \
+ __atomic_op_release(atomic_cmpxchg, __VA_ARGS__)
+#endif
+
+#ifndef atomic_cmpxchg
+#define atomic_cmpxchg(...) \
+ __atomic_op_fence(atomic_cmpxchg, __VA_ARGS__)
+#endif
+#endif /* atomic_cmpxchg_relaxed */
+
+#ifndef atomic64_read_acquire
+#define atomic64_read_acquire(v) smp_load_acquire(&(v)->counter)
+#endif
+
+#ifndef atomic64_set_release
+#define atomic64_set_release(v, i) smp_store_release(&(v)->counter, (i))
+#endif
+
+/* atomic64_add_return_relaxed */
+#ifndef atomic64_add_return_relaxed
+#define atomic64_add_return_relaxed atomic64_add_return
+#define atomic64_add_return_acquire atomic64_add_return
+#define atomic64_add_return_release atomic64_add_return
+
+#else /* atomic64_add_return_relaxed */
+
+#ifndef atomic64_add_return_acquire
+#define atomic64_add_return_acquire(...) \
+ __atomic_op_acquire(atomic64_add_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_add_return_release
+#define atomic64_add_return_release(...) \
+ __atomic_op_release(atomic64_add_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_add_return
+#define atomic64_add_return(...) \
+ __atomic_op_fence(atomic64_add_return, __VA_ARGS__)
+#endif
+#endif /* atomic64_add_return_relaxed */
+
+/* atomic64_sub_return_relaxed */
+#ifndef atomic64_sub_return_relaxed
+#define atomic64_sub_return_relaxed atomic64_sub_return
+#define atomic64_sub_return_acquire atomic64_sub_return
+#define atomic64_sub_return_release atomic64_sub_return
+
+#else /* atomic64_sub_return_relaxed */
+
+#ifndef atomic64_sub_return_acquire
+#define atomic64_sub_return_acquire(...) \
+ __atomic_op_acquire(atomic64_sub_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_sub_return_release
+#define atomic64_sub_return_release(...) \
+ __atomic_op_release(atomic64_sub_return, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_sub_return
+#define atomic64_sub_return(...) \
+ __atomic_op_fence(atomic64_sub_return, __VA_ARGS__)
+#endif
+#endif /* atomic64_sub_return_relaxed */
+
+/* atomic64_xchg_relaxed */
+#ifndef atomic64_xchg_relaxed
+#define atomic64_xchg_relaxed atomic64_xchg
+#define atomic64_xchg_acquire atomic64_xchg
+#define atomic64_xchg_release atomic64_xchg
+
+#else /* atomic64_xchg_relaxed */
+
+#ifndef atomic64_xchg_acquire
+#define atomic64_xchg_acquire(...) \
+ __atomic_op_acquire(atomic64_xchg, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_xchg_release
+#define atomic64_xchg_release(...) \
+ __atomic_op_release(atomic64_xchg, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_xchg
+#define atomic64_xchg(...) \
+ __atomic_op_fence(atomic64_xchg, __VA_ARGS__)
+#endif
+#endif /* atomic64_xchg_relaxed */
+
+/* atomic64_cmpxchg_relaxed */
+#ifndef atomic64_cmpxchg_relaxed
+#define atomic64_cmpxchg_relaxed atomic64_cmpxchg
+#define atomic64_cmpxchg_acquire atomic64_cmpxchg
+#define atomic64_cmpxchg_release atomic64_cmpxchg
+
+#else /* atomic64_cmpxchg_relaxed */
+
+#ifndef atomic64_cmpxchg_acquire
+#define atomic64_cmpxchg_acquire(...) \
+ __atomic_op_acquire(atomic64_cmpxchg, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_cmpxchg_release
+#define atomic64_cmpxchg_release(...) \
+ __atomic_op_release(atomic64_cmpxchg, __VA_ARGS__)
+#endif
+
+#ifndef atomic64_cmpxchg
+#define atomic64_cmpxchg(...) \
+ __atomic_op_fence(atomic64_cmpxchg, __VA_ARGS__)
+#endif
+#endif /* atomic64_cmpxchg_relaxed */
+
+/* cmpxchg_relaxed */
+#ifndef cmpxchg_relaxed
+#define cmpxchg_relaxed cmpxchg
+#define cmpxchg_acquire cmpxchg
+#define cmpxchg_release cmpxchg
+
+#else /* cmpxchg_relaxed */
+
+#ifndef cmpxchg_acquire
+#define cmpxchg_acquire(...) \
+ __atomic_op_acquire(cmpxchg, __VA_ARGS__)
+#endif
+
+#ifndef cmpxchg_release
+#define cmpxchg_release(...) \
+ __atomic_op_release(cmpxchg, __VA_ARGS__)
+#endif
+
+#ifndef cmpxchg
+#define cmpxchg(...) \
+ __atomic_op_fence(cmpxchg, __VA_ARGS__)
+#endif
+#endif /* cmpxchg_relaxed */
+
+/* cmpxchg64_relaxed */
+#ifndef cmpxchg64_relaxed
+#define cmpxchg64_relaxed cmpxchg64
+#define cmpxchg64_acquire cmpxchg64
+#define cmpxchg64_release cmpxchg64
+
+#else /* cmpxchg64_relaxed */
+
+#ifndef cmpxchg64_acquire
+#define cmpxchg64_acquire(...) \
+ __atomic_op_acquire(cmpxchg64, __VA_ARGS__)
+#endif
+
+#ifndef cmpxchg64_release
+#define cmpxchg64_release(...) \
+ __atomic_op_release(cmpxchg64, __VA_ARGS__)
+#endif
+
+#ifndef cmpxchg64
+#define cmpxchg64(...) \
+ __atomic_op_fence(cmpxchg64, __VA_ARGS__)
+#endif
+#endif /* cmpxchg64_relaxed */
+
+/* xchg_relaxed */
+#ifndef xchg_relaxed
+#define xchg_relaxed xchg
+#define xchg_acquire xchg
+#define xchg_release xchg
+
+#else /* xchg_relaxed */
+
+#ifndef xchg_acquire
+#define xchg_acquire(...) __atomic_op_acquire(xchg, __VA_ARGS__)
+#endif
+
+#ifndef xchg_release
+#define xchg_release(...) __atomic_op_release(xchg, __VA_ARGS__)
+#endif
+
+#ifndef xchg
+#define xchg(...) __atomic_op_fence(xchg, __VA_ARGS__)
+#endif
+#endif /* xchg_relaxed */
/**
* atomic_add_unless - add unless the number is already a given value
diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h
index 729f48e..828198d 100644
--- a/include/linux/bcma/bcma.h
+++ b/include/linux/bcma/bcma.h
@@ -154,6 +154,7 @@
#define BCMA_CORE_DEFAULT 0xFFF
#define BCMA_MAX_NR_CORES 16
+#define BCMA_CORE_SIZE 0x1000
/* Chip IDs of PCIe devices */
#define BCMA_CHIP_ID_BCM4313 0x4313
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index c07070f..9631038 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -618,7 +618,7 @@
#define list_entry_rq(ptr) list_entry((ptr), struct request, queuelist)
-#define rq_data_dir(rq) (((rq)->cmd_flags & 1) != 0)
+#define rq_data_dir(rq) ((int)((rq)->cmd_flags & 1))
/*
* Driver can handle struct request, if it either has an old style
diff --git a/include/linux/cache.h b/include/linux/cache.h
index 17e7e82..1be04f8 100644
--- a/include/linux/cache.h
+++ b/include/linux/cache.h
@@ -12,10 +12,24 @@
#define SMP_CACHE_BYTES L1_CACHE_BYTES
#endif
+/*
+ * __read_mostly is used to keep rarely changing variables out of frequently
+ * updated cachelines. If an architecture doesn't support it, ignore the
+ * hint.
+ */
#ifndef __read_mostly
#define __read_mostly
#endif
+/*
+ * __ro_after_init is used to mark things that are read-only after init (i.e.
+ * after mark_rodata_ro() has been called). These are effectively read-only,
+ * but may get written to during init, so can't live in .rodata (via "const").
+ */
+#ifndef __ro_after_init
+#define __ro_after_init __attribute__((__section__(".data..ro_after_init")))
+#endif
+
#ifndef ____cacheline_aligned
#define ____cacheline_aligned __attribute__((__aligned__(SMP_CACHE_BYTES)))
#endif
diff --git a/include/linux/can/core.h b/include/linux/can/core.h
index a087500..df08a41 100644
--- a/include/linux/can/core.h
+++ b/include/linux/can/core.h
@@ -45,10 +45,9 @@
extern int can_proto_register(const struct can_proto *cp);
extern void can_proto_unregister(const struct can_proto *cp);
-extern int can_rx_register(struct net_device *dev, canid_t can_id,
- canid_t mask,
- void (*func)(struct sk_buff *, void *),
- void *data, char *ident);
+int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
+ void (*func)(struct sk_buff *, void *),
+ void *data, char *ident, struct sock *sk);
extern void can_rx_unregister(struct net_device *dev, canid_t can_id,
canid_t mask,
diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h
index b37ea95..4909388 100644
--- a/include/linux/can/dev.h
+++ b/include/linux/can/dev.h
@@ -31,6 +31,7 @@
* CAN common private data
*/
struct can_priv {
+ struct net_device *dev;
struct can_device_stats can_stats;
struct can_bittiming bittiming, data_bittiming;
@@ -39,11 +40,14 @@
struct can_clock clock;
enum can_state state;
- u32 ctrlmode;
- u32 ctrlmode_supported;
+
+ /* CAN controller features - see include/uapi/linux/can/netlink.h */
+ u32 ctrlmode; /* current options setting */
+ u32 ctrlmode_supported; /* options that can be modified by netlink */
+ u32 ctrlmode_static; /* static enabled options for driver/hardware */
int restart_ms;
- struct timer_list restart_timer;
+ struct delayed_work restart_work;
int (*do_set_bittiming)(struct net_device *dev);
int (*do_set_data_bittiming)(struct net_device *dev);
@@ -105,6 +109,21 @@
return skb->len == CANFD_MTU;
}
+/* helper to define static CAN controller features at device creation time */
+static inline void can_set_static_ctrlmode(struct net_device *dev,
+ u32 static_mode)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* alloc_candev() succeeded => netdev_priv() is valid at this point */
+ priv->ctrlmode = static_mode;
+ priv->ctrlmode_static = static_mode;
+
+ /* override MTU which was set by default in can_setup()? */
+ if (static_mode & CAN_CTRLMODE_FD)
+ dev->mtu = CANFD_MTU;
+}
+
/* get data length from can_dlc with sanitized can_dlc */
u8 can_dlc2len(u8 can_dlc);
diff --git a/include/linux/capability.h b/include/linux/capability.h
index aa93e5e..c2eb39f 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -40,8 +40,6 @@
struct dentry;
struct user_namespace;
-struct user_namespace *current_user_ns(void);
-
extern const kernel_cap_t __cap_empty_set;
extern const kernel_cap_t __cap_init_eff_set;
diff --git a/include/linux/ceph/osdmap.h b/include/linux/ceph/osdmap.h
index 561ea89..e5ea74c 100644
--- a/include/linux/ceph/osdmap.h
+++ b/include/linux/ceph/osdmap.h
@@ -49,7 +49,7 @@
case CEPH_POOL_TYPE_EC:
return false;
default:
- BUG_ON(1);
+ BUG();
}
}
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 8efb40e..b9c842a 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -199,7 +199,7 @@
#define unreachable() __builtin_unreachable()
/* Mark a function definition as prohibited from being cloned. */
-#define __noclone __attribute__((__noclone__))
+#define __noclone __attribute__((__noclone__, __optimize__("no-tracer")))
#endif /* GCC_VERSION >= 40500 */
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 01ac9a8..4e49156 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -653,13 +653,18 @@
#ifdef CONFIG_CPU_FREQ_STAT
void acct_update_power(struct task_struct *p, cputime_t cputime);
-
+void cpufreq_task_stats_init(struct task_struct *p);
+void cpufreq_task_stats_exit(struct task_struct *p);
+void cpufreq_task_stats_remove_uids(uid_t uid_start, uid_t uid_end);
+int proc_time_in_state_show(struct seq_file *m, struct pid_namespace *ns,
+ struct pid *pid, struct task_struct *p);
#else
-
-static inline void acct_update_power(struct task_struct *p, cputime_t cputime)
-{
-}
-
+static inline void acct_update_power(struct task_struct *p,
+ cputime_t cputime) {}
+static inline void cpufreq_task_stats_init(struct task_struct *p) {}
+static inline void cpufreq_task_stats_exit(struct task_struct *p) {}
+static inline void cpufreq_task_stats_remove_uids(uid_t uid_start,
+ uid_t uid_end) {}
#endif
struct sched_domain;
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index 39c6b6e..c73c9a5 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -150,10 +150,8 @@
return 1;
}
-static inline int cpumask_set_cpu_local_first(int i, int numa_node, cpumask_t *dstp)
+static inline unsigned int cpumask_local_spread(unsigned int i, int node)
{
- set_bit(0, cpumask_bits(dstp));
-
return 0;
}
@@ -207,7 +205,7 @@
int cpumask_next_and(int n, const struct cpumask *, const struct cpumask *);
int cpumask_any_but(const struct cpumask *mask, unsigned int cpu);
-int cpumask_set_cpu_local_first(int i, int numa_node, cpumask_t *dstp);
+unsigned int cpumask_local_spread(unsigned int i, int node);
/**
* for_each_cpu - iterate over every cpu in a mask
diff --git a/include/linux/cred.h b/include/linux/cred.h
index 2fb2ca2..46432b0 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -122,6 +122,7 @@
kernel_cap_t cap_permitted; /* caps we're permitted */
kernel_cap_t cap_effective; /* caps we can actually use */
kernel_cap_t cap_bset; /* capability bounding set */
+ kernel_cap_t cap_ambient; /* Ambient capability set */
#ifdef CONFIG_KEYS
unsigned char jit_keyring; /* default keyring to attach requested
* keys to */
@@ -197,6 +198,13 @@
}
#endif
+static inline bool cap_ambient_invariant_ok(const struct cred *cred)
+{
+ return cap_issubset(cred->cap_ambient,
+ cap_intersect(cred->cap_permitted,
+ cred->cap_inheritable));
+}
+
/**
* get_new_cred - Get a reference on a new set of credentials
* @cred: The new credentials to reference
@@ -354,7 +362,10 @@
#ifdef CONFIG_USER_NS
#define current_user_ns() (current_cred_xxx(user_ns))
#else
-#define current_user_ns() (&init_user_ns)
+static inline struct user_namespace *current_user_ns(void)
+{
+ return &init_user_ns;
+}
#endif
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
index 4d0b4d1..06822af 100644
--- a/include/linux/debugfs.h
+++ b/include/linux/debugfs.h
@@ -55,6 +55,11 @@
struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
const char *dest);
+struct dentry *debugfs_create_automount(const char *name,
+ struct dentry *parent,
+ struct vfsmount *(*f)(void *),
+ void *data);
+
void debugfs_remove(struct dentry *dentry);
void debugfs_remove_recursive(struct dentry *dentry);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 835dabb..3c684a9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1422,13 +1422,21 @@
* VFS helper functions..
*/
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
+extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool);
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
+extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t);
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
+extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, dev_t);
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
+extern int vfs_symlink2(struct vfsmount *, struct inode *, struct dentry *, const char *);
extern int vfs_link(struct dentry *, struct inode *, struct dentry *, struct inode **);
+extern int vfs_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *, struct inode **);
extern int vfs_rmdir(struct inode *, struct dentry *);
+extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *);
extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
+extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *, struct inode **);
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
+extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
extern int vfs_whiteout(struct inode *, struct dentry *);
/*
@@ -1530,6 +1538,7 @@
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
void * (*follow_link) (struct dentry *, struct nameidata *);
int (*permission) (struct inode *, int);
+ int (*permission2) (struct vfsmount *, struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
@@ -1547,6 +1556,7 @@
int (*rename2) (struct inode *, struct dentry *,
struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct dentry *, struct iattr *);
+ int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
@@ -1590,9 +1600,13 @@
int (*unfreeze_fs) (struct super_block *);
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
+ int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *);
+ void *(*clone_mnt_data) (void *);
+ void (*copy_mnt_data) (void *, void *);
void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct dentry *);
+ int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *);
int (*show_devname)(struct seq_file *, struct dentry *);
int (*show_path)(struct seq_file *, struct dentry *);
int (*show_stats)(struct seq_file *, struct dentry *);
@@ -1804,6 +1818,9 @@
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
+ struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int,
+ const char *, void *);
+ void *(*alloc_mnt_data) (void);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
@@ -2071,6 +2088,8 @@
extern long vfs_truncate(struct path *, loff_t);
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
struct file *filp);
+extern int do_truncate2(struct vfsmount *, struct dentry *, loff_t start,
+ unsigned int time_attrs, struct file *filp);
extern int do_fallocate(struct file *file, int mode, loff_t offset,
loff_t len);
extern long do_sys_open(int dfd, const char __user *filename, int flags,
@@ -2291,8 +2310,11 @@
extern sector_t bmap(struct inode *, sector_t);
#endif
extern int notify_change(struct dentry *, struct iattr *, struct inode **);
+extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *, struct inode **);
extern int inode_permission(struct inode *, int);
+extern int inode_permission2(struct vfsmount *, struct inode *, int);
extern int __inode_permission(struct inode *, int);
+extern int __inode_permission2(struct vfsmount *, struct inode *, int);
extern int generic_permission(struct inode *, int);
extern int __check_sticky(struct inode *dir, struct inode *inode);
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index ca060d7..b36d7a9 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -150,6 +150,7 @@
#define FS_PRIO_1 1 /* fanotify content based access control */
#define FS_PRIO_2 2 /* fanotify pre-content access */
unsigned int priority;
+ bool shutdown; /* group is being shut down, don't queue more events */
/* stores all fastpath marks assoc with this group so they can be cleaned on unregister */
struct mutex mark_mutex; /* protect marks_list */
@@ -181,7 +182,6 @@
spinlock_t access_lock;
struct list_head access_list;
wait_queue_head_t access_waitq;
- atomic_t bypass_perm;
#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */
int f_flags;
unsigned int max_marks;
@@ -314,6 +314,8 @@
extern void fsnotify_get_group(struct fsnotify_group *group);
/* drop reference on a group from fsnotify_alloc_group */
extern void fsnotify_put_group(struct fsnotify_group *group);
+/* group destruction begins, stop queuing new events */
+extern void fsnotify_group_stop_queueing(struct fsnotify_group *group);
/* destroy group */
extern void fsnotify_destroy_group(struct fsnotify_group *group);
/* fasync handler function */
@@ -326,8 +328,6 @@
struct fsnotify_event *event,
int (*merge)(struct list_head *,
struct fsnotify_event *));
-/* Remove passed event from groups notification queue */
-extern void fsnotify_remove_event(struct fsnotify_group *group, struct fsnotify_event *event);
/* true if the group notification queue is empty */
extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
/* return, but do not dequeue the first event on the notification queue */
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 662697b..dc008b9 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -39,6 +39,12 @@
# define FTRACE_FORCE_LIST_FUNC 0
#endif
+/* Main tracing buffer and events set up */
+#ifdef CONFIG_TRACING
+void trace_init(void);
+#else
+static inline void trace_init(void) { }
+#endif
struct module;
struct ftrace_hash;
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h
index 8f424ca..b3c28a6 100644
--- a/include/linux/ftrace_event.h
+++ b/include/linux/ftrace_event.h
@@ -595,7 +595,7 @@
char *filter_str);
extern void ftrace_profile_free_filter(struct perf_event *event);
extern void *perf_trace_buf_prepare(int size, unsigned short type,
- struct pt_regs *regs, int *rctxp);
+ struct pt_regs **regs, int *rctxp);
static inline void
perf_trace_buf_submit(void *raw_data, int size, int rctx, u64 addr,
diff --git a/include/linux/hash.h b/include/linux/hash.h
index d0494c3..a75b100 100644
--- a/include/linux/hash.h
+++ b/include/linux/hash.h
@@ -33,12 +33,28 @@
#error Wordsize not 32 or 64
#endif
+/*
+ * The above primes are actively bad for hashing, since they are
+ * too sparse. The 32-bit one is mostly ok, the 64-bit one causes
+ * real problems. Besides, the "prime" part is pointless for the
+ * multiplicative hash.
+ *
+ * Although a random odd number will do, it turns out that the golden
+ * ratio phi = (sqrt(5)-1)/2, or its negative, has particularly nice
+ * properties.
+ *
+ * These are the negative, (1 - phi) = (phi^2) = (3 - sqrt(5))/2.
+ * (See Knuth vol 3, section 6.4, exercise 9.)
+ */
+#define GOLDEN_RATIO_32 0x61C88647
+#define GOLDEN_RATIO_64 0x61C8864680B583EBull
+
static __always_inline u64 hash_64(u64 val, unsigned int bits)
{
u64 hash = val;
-#if defined(CONFIG_ARCH_HAS_FAST_MULTIPLIER) && BITS_PER_LONG == 64
- hash = hash * GOLDEN_RATIO_PRIME_64;
+#if BITS_PER_LONG == 64
+ hash = hash * GOLDEN_RATIO_64;
#else
/* Sigh, gcc can't optimise this alone like it does for 32 bits. */
u64 n = hash;
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 14020c7..e619293 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -415,15 +415,14 @@
return &mm->page_table_lock;
}
-static inline bool hugepages_supported(void)
-{
- /*
- * Some platform decide whether they support huge pages at boot
- * time. On these, such as powerpc, HPAGE_SHIFT is set to 0 when
- * there is no such support
- */
- return HPAGE_SHIFT != 0;
-}
+#ifndef hugepages_supported
+/*
+ * Some platform decide whether they support huge pages at boot
+ * time. Some of them, such as powerpc, set HPAGE_SHIFT to 0
+ * when there is no such support
+ */
+#define hugepages_supported() (HPAGE_SHIFT != 0)
+#endif
#else /* CONFIG_HUGETLB_PAGE */
struct hstate {};
diff --git a/include/linux/i8042.h b/include/linux/i8042.h
index 0f9bafa..d98780c 100644
--- a/include/linux/i8042.h
+++ b/include/linux/i8042.h
@@ -62,7 +62,6 @@
void i8042_lock_chip(void);
void i8042_unlock_chip(void);
int i8042_command(unsigned char *param, int command);
-bool i8042_check_port_owner(const struct serio *);
int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
struct serio *serio));
int i8042_remove_filter(bool (*filter)(unsigned char data, unsigned char str,
@@ -83,11 +82,6 @@
return -ENODEV;
}
-static inline bool i8042_check_port_owner(const struct serio *serio)
-{
- return false;
-}
-
static inline int i8042_install_filter(bool (*filter)(unsigned char data, unsigned char str,
struct serio *serio))
{
diff --git a/include/linux/init.h b/include/linux/init.h
index 2df8e8d..28ec4d1 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -153,6 +153,10 @@
void __init load_default_modules(void);
int __init init_rootfs(void);
+#ifdef CONFIG_DEBUG_RODATA
+void mark_rodata_ro(void);
+#endif
+
extern void (*late_time_init)(void);
extern bool initcall_debug;
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index bf50ec0..1d4008c 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -18,6 +18,7 @@
__s32 dad_transmits;
__s32 rtr_solicits;
__s32 rtr_solicit_interval;
+ __s32 rtr_solicit_max_interval;
__s32 rtr_solicit_delay;
__s32 force_mld_version;
__s32 mldv1_unsolicited_report_interval;
@@ -34,6 +35,7 @@
__s32 accept_ra_rtr_pref;
__s32 rtr_probe_interval;
#ifdef CONFIG_IPV6_ROUTE_INFO
+ __s32 accept_ra_rt_info_min_plen;
__s32 accept_ra_rt_info_max_plen;
#endif
#endif
diff --git a/include/linux/irq.h b/include/linux/irq.h
index ca77537..60d8446 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -850,6 +850,16 @@
static inline void irq_gc_unlock(struct irq_chip_generic *gc) { }
#endif
+/*
+ * The irqsave variants are for usage in non interrupt code. Do not use
+ * them in irq_chip callbacks. Use irq_gc_lock() instead.
+ */
+#define irq_gc_lock_irqsave(gc, flags) \
+ raw_spin_lock_irqsave(&(gc)->lock, flags)
+
+#define irq_gc_unlock_irqrestore(gc, flags) \
+ raw_spin_unlock_irqrestore(&(gc)->lock, flags)
+
static inline void irq_reg_writel(struct irq_chip_generic *gc,
u32 val, int reg_offset)
{
@@ -867,7 +877,9 @@
else
return readl(gc->reg_base + reg_offset);
}
+
#ifdef CONFIG_HTC_POWER_DEBUG
void htc_show_interrupts(void);
#endif
+
#endif /* _LINUX_IRQ_H */
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index a575c1b..f0b4c48 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -270,6 +270,18 @@
#define ICC_SRE_EL2_SRE (1 << 0)
#define ICC_SRE_EL2_ENABLE (1 << 3)
+#define ICC_SGI1R_TARGET_LIST_SHIFT 0
+#define ICC_SGI1R_TARGET_LIST_MASK (0xffff << ICC_SGI1R_TARGET_LIST_SHIFT)
+#define ICC_SGI1R_AFFINITY_1_SHIFT 16
+#define ICC_SGI1R_AFFINITY_1_MASK (0xff << ICC_SGI1R_AFFINITY_1_SHIFT)
+#define ICC_SGI1R_SGI_ID_SHIFT 24
+#define ICC_SGI1R_SGI_ID_MASK (0xfULL << ICC_SGI1R_SGI_ID_SHIFT)
+#define ICC_SGI1R_AFFINITY_2_SHIFT 32
+#define ICC_SGI1R_AFFINITY_2_MASK (0xffULL << ICC_SGI1R_AFFINITY_1_SHIFT)
+#define ICC_SGI1R_IRQ_ROUTING_MODE_BIT 40
+#define ICC_SGI1R_AFFINITY_3_SHIFT 48
+#define ICC_SGI1R_AFFINITY_3_MASK (0xffULL << ICC_SGI1R_AFFINITY_1_SHIFT)
+
/*
* System register definitions
*/
diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h
index 6883e19..e4a205d 100644
--- a/include/linux/kallsyms.h
+++ b/include/linux/kallsyms.h
@@ -122,7 +122,7 @@
static inline void print_ip_sym(unsigned long ip)
{
- printk("[<%p>] %pS\n", (void *) ip, (void *) ip);
+ printk("[<%pP>] %pS\n", (void *) ip, (void *) ip);
}
#endif /*_LINUX_KALLSYMS_H*/
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index dce9cda..0b4ca42 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -350,6 +350,7 @@
int __must_check kstrtos16(const char *s, unsigned int base, s16 *res);
int __must_check kstrtou8(const char *s, unsigned int base, u8 *res);
int __must_check kstrtos8(const char *s, unsigned int base, s8 *res);
+int __must_check kstrtobool(const char *s, bool *res);
int __must_check kstrtoull_from_user(const char __user *s, size_t count, unsigned int base, unsigned long long *res);
int __must_check kstrtoll_from_user(const char __user *s, size_t count, unsigned int base, long long *res);
@@ -361,6 +362,7 @@
int __must_check kstrtos16_from_user(const char __user *s, size_t count, unsigned int base, s16 *res);
int __must_check kstrtou8_from_user(const char __user *s, size_t count, unsigned int base, u8 *res);
int __must_check kstrtos8_from_user(const char __user *s, size_t count, unsigned int base, s8 *res);
+int __must_check kstrtobool_from_user(const char __user *s, size_t count, bool *res);
static inline int __must_check kstrtou64_from_user(const char __user *s, size_t count, unsigned int base, u64 *res)
{
@@ -434,6 +436,7 @@
extern int panic_on_oops;
extern int panic_on_unrecovered_nmi;
extern int panic_on_io_nmi;
+extern int sysctl_panic_on_rcu_stall;
extern int sysctl_panic_on_stackoverflow;
/*
* Only to be used by arch init code. If the user over-wrote the default
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index e4d8f70..aa75be9 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -177,8 +177,8 @@
void *val);
int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr,
int len, struct kvm_io_device *dev);
-int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
- struct kvm_io_device *dev);
+void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
+ struct kvm_io_device *dev);
#ifdef CONFIG_KVM_ASYNC_PF
struct kvm_async_pf {
diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h
index ff82a32..439cde6 100644
--- a/include/linux/lockd/lockd.h
+++ b/include/linux/lockd/lockd.h
@@ -351,7 +351,8 @@
static inline int nlm_compare_locks(const struct file_lock *fl1,
const struct file_lock *fl2)
{
- return fl1->fl_pid == fl2->fl_pid
+ return file_inode(fl1->fl_file) == file_inode(fl2->fl_file)
+ && fl1->fl_pid == fl2->fl_pid
&& fl1->fl_owner == fl2->fl_owner
&& fl1->fl_start == fl2->fl_start
&& fl1->fl_end == fl2->fl_end
diff --git a/include/linux/lockref.h b/include/linux/lockref.h
index 4bfde0e..b10b122d 100644
--- a/include/linux/lockref.h
+++ b/include/linux/lockref.h
@@ -28,12 +28,13 @@
#endif
struct {
spinlock_t lock;
- unsigned int count;
+ int count;
};
};
};
extern void lockref_get(struct lockref *);
+extern int lockref_put_return(struct lockref *);
extern int lockref_get_not_zero(struct lockref *);
extern int lockref_get_or_lock(struct lockref *);
extern int lockref_put_or_lock(struct lockref *);
diff --git a/include/linux/log2.h b/include/linux/log2.h
index fd7ff3d..f38fae2 100644
--- a/include/linux/log2.h
+++ b/include/linux/log2.h
@@ -16,12 +16,6 @@
#include <linux/bitops.h>
/*
- * deal with unrepresentable constant logarithms
- */
-extern __attribute__((const, noreturn))
-int ____ilog2_NaN(void);
-
-/*
* non-constant log of base 2 calculators
* - the arch may override these in asm/bitops.h if they can be implemented
* more efficiently than using fls() and fls64()
@@ -85,7 +79,7 @@
#define ilog2(n) \
( \
__builtin_constant_p(n) ? ( \
- (n) < 1 ? ____ilog2_NaN() : \
+ (n) < 2 ? 0 : \
(n) & (1ULL << 63) ? 63 : \
(n) & (1ULL << 62) ? 62 : \
(n) & (1ULL << 61) ? 61 : \
@@ -148,10 +142,7 @@
(n) & (1ULL << 4) ? 4 : \
(n) & (1ULL << 3) ? 3 : \
(n) & (1ULL << 2) ? 2 : \
- (n) & (1ULL << 1) ? 1 : \
- (n) & (1ULL << 0) ? 0 : \
- ____ilog2_NaN() \
- ) : \
+ 1 ) : \
(sizeof(n) <= 4) ? \
__ilog2_u32(n) : \
__ilog2_u64(n) \
diff --git a/include/linux/memory-state-time.h b/include/linux/memory-state-time.h
new file mode 100644
index 0000000..d2212b0
--- /dev/null
+++ b/include/linux/memory-state-time.h
@@ -0,0 +1,42 @@
+/* include/linux/memory-state-time.h
+ *
+ * Copyright (C) 2016 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/workqueue.h>
+
+#define UPDATE_MEMORY_STATE(BLOCK, VALUE) BLOCK->update_call(BLOCK, VALUE)
+
+struct memory_state_update_block;
+
+typedef void (*memory_state_update_fn_t)(struct memory_state_update_block *ub,
+ int value);
+
+/* This struct is populated when you pass it to a memory_state_register*
+ * function. The update_call function is used for an update and defined in the
+ * typedef memory_state_update_fn_t
+ */
+struct memory_state_update_block {
+ memory_state_update_fn_t update_call;
+ int id;
+};
+
+/* Register a frequency struct memory_state_update_block to provide updates to
+ * memory_state_time about frequency changes using its update_call function.
+ */
+struct memory_state_update_block *memory_state_register_frequency_source(void);
+
+/* Register a bandwidth struct memory_state_update_block to provide updates to
+ * memory_state_time about bandwidth changes using its update_call function.
+ */
+struct memory_state_update_block *memory_state_register_bandwidth_source(void);
diff --git a/include/linux/mfd/samsung/s2mps11.h b/include/linux/mfd/samsung/s2mps11.h
index 7981a9d..ad81a1a 100644
--- a/include/linux/mfd/samsung/s2mps11.h
+++ b/include/linux/mfd/samsung/s2mps11.h
@@ -173,10 +173,12 @@
#define S2MPS11_LDO_VSEL_MASK 0x3F
#define S2MPS11_BUCK_VSEL_MASK 0xFF
+#define S2MPS11_BUCK9_VSEL_MASK 0x1F
#define S2MPS11_ENABLE_MASK (0x03 << S2MPS11_ENABLE_SHIFT)
#define S2MPS11_ENABLE_SHIFT 0x06
#define S2MPS11_LDO_N_VOLTAGES (S2MPS11_LDO_VSEL_MASK + 1)
#define S2MPS11_BUCK_N_VOLTAGES (S2MPS11_BUCK_VSEL_MASK + 1)
+#define S2MPS11_BUCK9_N_VOLTAGES (S2MPS11_BUCK9_VSEL_MASK + 1)
#define S2MPS11_RAMP_DELAY 25000 /* uV/us */
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 43c952e..74d0128 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -134,6 +134,9 @@
u8 barrier_en;
u8 fw_version; /* 254 */
+ u8 pre_eol_info; /* 267 */
+ u8 lifetime_est_a; /* 268 */
+ u8 lifetime_est_b; /* 269 */
unsigned int feature_support;
#define MMC_DISCARD_FEATURE BIT(0) /* CMD38 feature */
};
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index ed6638a..b79e521 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -284,6 +284,9 @@
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */
#define EXT_CSD_FW_VERSION 254 /* RO */
+#define EXT_CSD_PRE_EOL_INFO 267 /* RO */
+#define EXT_CSD_DEV_LIFE_TIME_EST_TYP_A 268 /* RO */
+#define EXT_CSD_DEV_LIFE_TIME_EST_TYP_B 269 /* RO */
#define EXT_CSD_CMDQ_DEPTH 307 /* RO */
#define EXT_CSD_CMDQ_SUPPORT 308 /* RO */
#define EXT_CSD_BARRIER_SUPPORT 486 /* RO */
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index b101221..bcd77f6 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -74,10 +74,12 @@
#ifdef CONFIG_CMA
bool is_cma_pageblock(struct page *page);
# define is_migrate_cma(migratetype) unlikely((migratetype) == MIGRATE_CMA)
+# define is_migrate_cma_page(_page) (get_pageblock_migratetype(_page) == MIGRATE_CMA)
# define get_cma_migrate_type() MIGRATE_CMA
#else
# define is_cma_pageblock(page) false
# define is_migrate_cma(migratetype) false
+# define is_migrate_cma_page(_page) false
# define get_cma_migrate_type() MIGRATE_MOVABLE
#endif
diff --git a/include/linux/mount.h b/include/linux/mount.h
index c2c561d..7c9e561 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -66,6 +66,7 @@
struct dentry *mnt_root; /* root of the mounted tree */
struct super_block *mnt_sb; /* pointer to superblock */
int mnt_flags;
+ void *data;
};
struct file; /* forward dec */
diff --git a/include/linux/mroute.h b/include/linux/mroute.h
index 79aaa9f..d5277fc 100644
--- a/include/linux/mroute.h
+++ b/include/linux/mroute.h
@@ -103,5 +103,5 @@
struct rtmsg;
extern int ipmr_get_route(struct net *net, struct sk_buff *skb,
__be32 saddr, __be32 daddr,
- struct rtmsg *rtm, int nowait);
+ struct rtmsg *rtm, int nowait, u32 portid);
#endif
diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h
index 66982e7..f831155 100644
--- a/include/linux/mroute6.h
+++ b/include/linux/mroute6.h
@@ -115,7 +115,7 @@
struct rtmsg;
extern int ip6mr_get_route(struct net *net, struct sk_buff *skb,
- struct rtmsg *rtm, int nowait);
+ struct rtmsg *rtm, int nowait, u32 portid);
#ifdef CONFIG_IPV6_MROUTE
extern struct sock *mroute6_socket(struct net *net, struct sk_buff *skb);
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h
index 5f487d7..78957a9 100644
--- a/include/linux/mtd/map.h
+++ b/include/linux/mtd/map.h
@@ -122,18 +122,13 @@
#endif
#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
-# ifdef map_bankwidth
-# undef map_bankwidth
-# define map_bankwidth(map) ((map)->bankwidth)
-# undef map_bankwidth_is_large
-# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
-# undef map_words
-# define map_words(map) map_calc_words(map)
-# else
-# define map_bankwidth(map) 32
-# define map_bankwidth_is_large(map) (1)
-# define map_words(map) map_calc_words(map)
-# endif
+/* always use indirect access for 256-bit to preserve kernel stack */
+# undef map_bankwidth
+# define map_bankwidth(map) ((map)->bankwidth)
+# undef map_bankwidth_is_large
+# define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+# undef map_words
+# define map_words(map) map_calc_words(map)
#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
#undef MAX_MAP_BANKWIDTH
#define MAX_MAP_BANKWIDTH 32
diff --git a/include/linux/namei.h b/include/linux/namei.h
index c9ad7a1..f6ff630 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -75,6 +75,7 @@
const char *, unsigned int, struct path *);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
+extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int);
extern int follow_down_one(struct path *);
extern int follow_down(struct path *);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 943a8301..753b23f 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1888,22 +1888,17 @@
/* Number of segments aggregated. */
u16 count;
- /* This is non-zero if the packet may be of the same flow. */
- u8 same_flow;
-
- /* Free the skb? */
- u8 free;
-#define NAPI_GRO_FREE 1
-#define NAPI_GRO_FREE_STOLEN_HEAD 2
-
/* jiffies when first packet was created/queued */
unsigned long age;
/* Used in ipv6_gro_receive() and foo-over-udp */
u16 proto;
- /* Used in udp_gro_receive */
- u8 udp_mark:1;
+ /* This is non-zero if the packet may be of the same flow. */
+ u8 same_flow:1;
+
+ /* Used in tunnel GRO receive */
+ u8 encap_mark:1;
/* GRO checksum is valid */
u8 csum_valid:1;
@@ -1911,9 +1906,16 @@
/* Number of checksums via CHECKSUM_UNNECESSARY */
u8 csum_cnt:3;
+ /* Free the skb? */
+ u8 free:2;
+#define NAPI_GRO_FREE 1
+#define NAPI_GRO_FREE_STOLEN_HEAD 2
+
/* Used in foo-over-udp, set in udp[46]_gro_receive */
u8 is_ipv6:1;
+ /* 7 bit hole */
+
/* used to support CHECKSUM_COMPLETE for tunneling protocols */
__wsum csum;
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
index a3e215b..cc615e2 100644
--- a/include/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -239,11 +239,22 @@
int xt_register_matches(struct xt_match *match, unsigned int n);
void xt_unregister_matches(struct xt_match *match, unsigned int n);
+int xt_check_entry_offsets(const void *base, const char *elems,
+ unsigned int target_offset,
+ unsigned int next_offset);
+
+unsigned int *xt_alloc_entry_offsets(unsigned int size);
+bool xt_find_jump_offset(const unsigned int *offsets,
+ unsigned int target, unsigned int size);
+
int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto,
bool inv_proto);
int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
bool inv_proto);
+void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
+ struct xt_counters_info *info, bool compat);
+
struct xt_table *xt_register_table(struct net *net,
const struct xt_table *table,
struct xt_table_info *bootstrap,
@@ -421,7 +432,7 @@
int xt_compat_calc_jump(u_int8_t af, unsigned int offset);
int xt_compat_match_offset(const struct xt_match *match);
-int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
+void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
unsigned int *size);
int xt_compat_match_to_user(const struct xt_entry_match *m,
void __user **dstptr, unsigned int *size);
@@ -431,6 +442,9 @@
unsigned int *size);
int xt_compat_target_to_user(const struct xt_entry_target *t,
void __user **dstptr, unsigned int *size);
+int xt_compat_check_entry_offsets(const void *base, const char *elems,
+ unsigned int target_offset,
+ unsigned int next_offset);
#endif /* CONFIG_COMPAT */
#endif /* _X_TABLES_H */
diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h
index ca60fbd..1c67155 100644
--- a/include/linux/netfilter/xt_qtaguid.h
+++ b/include/linux/netfilter/xt_qtaguid.h
@@ -10,4 +10,5 @@
#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET
#define xt_qtaguid_match_info xt_owner_match_info
+int qtaguid_untag(struct socket *sock, bool kernel);
#endif /* _XT_QTAGUID_MATCH_H */
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 2022788..6a4455f 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -603,56 +603,56 @@
*/
static inline int fault_in_multipages_writeable(char __user *uaddr, int size)
{
- int ret = 0;
char __user *end = uaddr + size - 1;
if (unlikely(size == 0))
- return ret;
+ return 0;
+ if (unlikely(uaddr > end))
+ return -EFAULT;
/*
* Writing zeroes into userspace here is OK, because we know that if
* the zero gets there, we'll be overwriting it.
*/
- while (uaddr <= end) {
- ret = __put_user(0, uaddr);
- if (ret != 0)
- return ret;
+ do {
+ if (unlikely(__put_user(0, uaddr) != 0))
+ return -EFAULT;
uaddr += PAGE_SIZE;
- }
+ } while (uaddr <= end);
/* Check whether the range spilled into the next page. */
if (((unsigned long)uaddr & PAGE_MASK) ==
((unsigned long)end & PAGE_MASK))
- ret = __put_user(0, end);
+ return __put_user(0, end);
- return ret;
+ return 0;
}
static inline int fault_in_multipages_readable(const char __user *uaddr,
int size)
{
volatile char c;
- int ret = 0;
const char __user *end = uaddr + size - 1;
if (unlikely(size == 0))
- return ret;
+ return 0;
- while (uaddr <= end) {
- ret = __get_user(c, uaddr);
- if (ret != 0)
- return ret;
+ if (unlikely(uaddr > end))
+ return -EFAULT;
+
+ do {
+ if (unlikely(__get_user(c, uaddr) != 0))
+ return -EFAULT;
uaddr += PAGE_SIZE;
- }
+ } while (uaddr <= end);
/* Check whether the range spilled into the next page. */
if (((unsigned long)uaddr & PAGE_MASK) ==
((unsigned long)end & PAGE_MASK)) {
- ret = __get_user(c, end);
- (void)c;
+ return __get_user(c, end);
}
- return ret;
+ return 0;
}
int add_to_page_cache_locked(struct page *page, struct address_space *mapping,
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 1fa99a3..1ce77d9 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2478,6 +2478,13 @@
#define PCI_DEVICE_ID_KORENIX_JETCARDF2 0x1700
#define PCI_DEVICE_ID_KORENIX_JETCARDF3 0x17ff
+#define PCI_VENDOR_ID_NETRONOME 0x19ee
+#define PCI_DEVICE_ID_NETRONOME_NFP3200 0x3200
+#define PCI_DEVICE_ID_NETRONOME_NFP3240 0x3240
+#define PCI_DEVICE_ID_NETRONOME_NFP4000 0x4000
+#define PCI_DEVICE_ID_NETRONOME_NFP6000 0x6000
+#define PCI_DEVICE_ID_NETRONOME_NFP6000_VF 0x6003
+
#define PCI_VENDOR_ID_QMI 0x1a32
#define PCI_VENDOR_ID_AZWAVE 0x1a3b
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index c21af65..902bb08 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -482,6 +482,7 @@
*/
struct mutex mutex;
+ struct list_head active_ctx_list;
struct list_head pinned_groups;
struct list_head flexible_groups;
struct list_head event_list;
@@ -530,9 +531,12 @@
struct perf_event_context *task_ctx;
int active_oncpu;
int exclusive;
+
+ raw_spinlock_t hrtimer_lock;
struct hrtimer hrtimer;
ktime_t hrtimer_interval;
- struct list_head rotation_list;
+ unsigned int hrtimer_active;
+
struct pmu *unique_pmu;
struct perf_cgroup *cgrp;
};
@@ -663,6 +667,7 @@
extern struct static_key perf_swevent_enabled[PERF_COUNT_SW_MAX];
+extern void ___perf_sw_event(u32, u64, struct pt_regs *, u64);
extern void __perf_sw_event(u32, u64, struct pt_regs *, u64);
#ifndef perf_arch_fetch_caller_regs
@@ -687,14 +692,25 @@
static __always_inline void
perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
{
- struct pt_regs hot_regs;
-
- if (static_key_false(&perf_swevent_enabled[event_id])) {
- if (!regs) {
- perf_fetch_caller_regs(&hot_regs);
- regs = &hot_regs;
- }
+ if (static_key_false(&perf_swevent_enabled[event_id]))
__perf_sw_event(event_id, nr, regs, addr);
+}
+
+DECLARE_PER_CPU(struct pt_regs, __perf_regs[4]);
+
+/*
+ * 'Special' version for the scheduler, it hard assumes no recursion,
+ * which is guaranteed by us not actually scheduling inside other swevents
+ * because those disable preemption.
+ */
+static __always_inline void
+perf_sw_event_sched(u32 event_id, u64 nr, u64 addr)
+{
+ if (static_key_false(&perf_swevent_enabled[event_id])) {
+ struct pt_regs *regs = this_cpu_ptr(&__perf_regs[0]);
+
+ perf_fetch_caller_regs(regs);
+ ___perf_sw_event(event_id, nr, regs, addr);
}
}
@@ -710,7 +726,7 @@
static inline void perf_event_task_sched_out(struct task_struct *prev,
struct task_struct *next)
{
- perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, NULL, 0);
+ perf_sw_event_sched(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 0);
if (static_key_false(&perf_sched_events.key))
__perf_event_task_sched_out(prev, next);
@@ -826,6 +842,8 @@
static inline void
perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) { }
static inline void
+perf_sw_event_sched(u32 event_id, u64 nr, u64 addr) { }
+static inline void
perf_bp_event(struct perf_event *event, void *data) { }
static inline int perf_register_guest_info_callbacks
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index eb8b8ac..24f5470 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -42,6 +42,7 @@
* @fasync_readers: reader side fasync
* @fasync_writers: writer side fasync
* @bufs: the circular array of pipe buffers
+ * @user: the user who created this pipe
**/
struct pipe_inode_info {
struct mutex mutex;
@@ -57,6 +58,7 @@
struct fasync_struct *fasync_readers;
struct fasync_struct *fasync_writers;
struct pipe_buffer *bufs;
+ struct user_struct *user;
};
/*
@@ -123,6 +125,8 @@
void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *);
extern unsigned int pipe_max_size, pipe_min_size;
+extern unsigned long pipe_user_pages_hard;
+extern unsigned long pipe_user_pages_soft;
int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *);
diff --git a/include/linux/platform_data/asoc-s3c.h b/include/linux/platform_data/asoc-s3c.h
index a6591c6..e8d9f84 100644
--- a/include/linux/platform_data/asoc-s3c.h
+++ b/include/linux/platform_data/asoc-s3c.h
@@ -38,6 +38,10 @@
*/
struct s3c_audio_pdata {
int (*cfg_gpio)(struct platform_device *);
+ void *dma_playback;
+ void *dma_capture;
+ void *dma_play_sec;
+ void *dma_capture_mic;
union {
struct samsung_i2s i2s;
} type;
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index e90628c..84e526a 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -299,6 +299,7 @@
#ifdef CONFIG_PWM_SYSFS
void pwmchip_sysfs_export(struct pwm_chip *chip);
void pwmchip_sysfs_unexport(struct pwm_chip *chip);
+void pwmchip_sysfs_unexport_children(struct pwm_chip *chip);
#else
static inline void pwmchip_sysfs_export(struct pwm_chip *chip)
{
@@ -307,6 +308,10 @@
static inline void pwmchip_sysfs_unexport(struct pwm_chip *chip)
{
}
+
+static inline void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
+{
+}
#endif /* CONFIG_PWM_SYSFS */
#endif /* __LINUX_PWM_H */
diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h
index 22fb153..f9f3a45 100644
--- a/include/linux/radix-tree.h
+++ b/include/linux/radix-tree.h
@@ -386,6 +386,7 @@
void **radix_tree_iter_retry(struct radix_tree_iter *iter)
{
iter->next_index = iter->index;
+ iter->tags = 0;
return NULL;
}
diff --git a/include/linux/sched.h b/include/linux/sched.h
index fb5d116..03951bc1 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -774,6 +774,7 @@
#endif
unsigned long locked_shm; /* How many pages of mlocked shm ? */
unsigned long unix_inflight; /* How many files in flight in unix sockets */
+ atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */
#ifdef CONFIG_KEYS
struct key *uid_keyring; /* UID specific keyring */
@@ -1049,6 +1050,32 @@
typedef
const struct sched_group_energy * const(*sched_domain_energy_f)(int cpu);
+struct energy_env {
+ struct sched_group *sg_top;
+ struct sched_group *sg_cap;
+ struct sched_group *sg;
+
+ int src_cpu;
+ int dst_cpu;
+ int util_delta;
+ struct task_struct *task;
+
+ int cap_idx;
+ int payoff;
+
+ int nrg_delta;
+ int prf_delta;
+ struct {
+ unsigned int energy;
+ unsigned int capacity;
+ unsigned int utilization;
+
+ int speedup_idx;
+ int delay_idx;
+ int perf_idx;
+ } before, after;
+};
+
#define SDTL_OVERLAP 0x01
struct sd_data {
@@ -1245,6 +1272,8 @@
unsigned long timeout;
unsigned long watchdog_stamp;
unsigned int time_slice;
+ bool schedtune_enqueued;
+ struct hrtimer schedtune_timer;
struct sched_rt_entity *back;
#ifdef CONFIG_RT_GROUP_SCHED
@@ -1468,6 +1497,8 @@
cputime_t utime, stime, utimescaled, stimescaled;
cputime_t gtime;
+ atomic64_t *time_in_state;
+ unsigned int max_states;
unsigned long long cpu_power;
#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
struct cputime prev_cputime;
@@ -3107,6 +3138,11 @@
{
tsk->ioac.syscw++;
}
+
+static inline void inc_syscfs(struct task_struct *tsk)
+{
+ tsk->ioac.syscfs++;
+}
#else
static inline void add_rchar(struct task_struct *tsk, ssize_t amt)
{
@@ -3123,6 +3159,9 @@
static inline void inc_syscw(struct task_struct *tsk)
{
}
+static inline void inc_syscfs(struct task_struct *tsk)
+{
+}
#endif
#ifndef TASK_SIZE_OF
diff --git a/include/linux/serio.h b/include/linux/serio.h
index 9f779c7..27ae809 100644
--- a/include/linux/serio.h
+++ b/include/linux/serio.h
@@ -29,7 +29,8 @@
struct serio_device_id id;
- spinlock_t lock; /* protects critical sections from port's interrupt handler */
+ /* Protects critical sections from port's interrupt handler */
+ spinlock_t lock;
int (*write)(struct serio *, unsigned char);
int (*open)(struct serio *);
@@ -38,16 +39,29 @@
void (*stop)(struct serio *);
struct serio *parent;
- struct list_head child_node; /* Entry in parent->children list */
+ /* Entry in parent->children list */
+ struct list_head child_node;
struct list_head children;
- unsigned int depth; /* level of nesting in serio hierarchy */
+ /* Level of nesting in serio hierarchy */
+ unsigned int depth;
- struct serio_driver *drv; /* accessed from interrupt, must be protected by serio->lock and serio->sem */
- struct mutex drv_mutex; /* protects serio->drv so attributes can pin driver */
+ /*
+ * serio->drv is accessed from interrupt handlers; when modifying
+ * caller should acquire serio->drv_mutex and serio->lock.
+ */
+ struct serio_driver *drv;
+ /* Protects serio->drv so attributes can pin current driver */
+ struct mutex drv_mutex;
struct device dev;
struct list_head node;
+
+ /*
+ * For use by PS/2 layer when several ports share hardware and
+ * may get indigestion when exposed to concurrent access (i8042).
+ */
+ struct mutex *ps2_cmd_mutex;
};
#define to_serio_port(d) container_of(d, struct serio, dev)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 82a04fd7..a8c9906 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -188,6 +188,7 @@
#else
#define MAX_SKB_FRAGS (65536/PAGE_SIZE + 1)
#endif
+extern int sysctl_max_skb_frags;
typedef struct skb_frag_struct skb_frag_t;
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 5f97037..899d13e 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -146,6 +146,18 @@
void kzfree(const void *);
size_t ksize(const void *);
+#ifdef CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR
+const char *__check_heap_object(const void *ptr, unsigned long n,
+ struct page *page);
+#else
+static inline const char *__check_heap_object(const void *ptr,
+ unsigned long n,
+ struct page *page)
+{
+ return NULL;
+}
+#endif
+
/*
* Some archs want to perform DMA into kmalloc caches and need a guaranteed
* alignment larger than the alignment of a 64-bit integer.
diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h
index e565d7c..68c4ec7 100644
--- a/include/linux/slub_def.h
+++ b/include/linux/slub_def.h
@@ -81,6 +81,7 @@
int reserved; /* Reserved bytes at the end of slabs */
const char *name; /* Name (only for display!) */
struct list_head list; /* List of slab caches */
+ int red_left_pad; /* Left redzone padding size */
#ifdef CONFIG_SYSFS
struct kobject kobj; /* For sysfs */
#endif
diff --git a/include/linux/string.h b/include/linux/string.h
index fd04ba5..5fe7c98 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -124,7 +124,21 @@
extern void argv_free(char **argv);
extern bool sysfs_streq(const char *s1, const char *s2);
-extern int strtobool(const char *s, bool *res);
+extern int kstrtobool(const char *s, bool *res);
+static inline int strtobool(const char *s, bool *res)
+{
+ return kstrtobool(s, res);
+}
+int __sysfs_match_string(const char * const *array, size_t n, const char *s);
+
+/**
+ * sysfs_match_string - matches given string in an array
+ * @_a: array of strings
+ * @_s: string to match with
+ *
+ * Helper for __sysfs_match_string(). Calculates the size of @a automatically.
+ */
+#define sysfs_match_string(_a, _s) __sysfs_match_string(_a, ARRAY_SIZE(_a), _s)
#ifdef CONFIG_BINARY_PRINTF
int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args);
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index b363a0f..3fbae68 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -131,8 +131,6 @@
#define RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT (1UL << 9)
struct rpc_clnt *rpc_create(struct rpc_create_args *args);
-struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
- struct rpc_xprt *xprt);
struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,
const struct rpc_program *, u32);
void rpc_task_reset_client(struct rpc_task *task, struct rpc_clnt *clnt);
diff --git a/include/linux/swap.h b/include/linux/swap.h
index b7f64ac..6ade2d8 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -316,7 +316,7 @@
extern void lru_add_drain_cpu(int cpu);
extern void lru_add_drain_all(void);
extern void rotate_reclaimable_page(struct page *page);
-extern void deactivate_page(struct page *page);
+extern void deactivate_file_page(struct page *page);
extern void swap_setup(void);
extern void add_page_to_unevictable_list(struct page *page);
diff --git a/include/linux/task_io_accounting.h b/include/linux/task_io_accounting.h
index bdf855c..2dd338f 100644
--- a/include/linux/task_io_accounting.h
+++ b/include/linux/task_io_accounting.h
@@ -18,6 +18,8 @@
u64 syscr;
/* # of write syscalls */
u64 syscw;
+ /* # of fsync syscalls */
+ u64 syscfs;
#endif /* CONFIG_TASK_XACCT */
#ifdef CONFIG_TASK_IO_ACCOUNTING
diff --git a/include/linux/task_io_accounting_ops.h b/include/linux/task_io_accounting_ops.h
index 4d090f9..1b505c8 100644
--- a/include/linux/task_io_accounting_ops.h
+++ b/include/linux/task_io_accounting_ops.h
@@ -96,6 +96,7 @@
dst->wchar += src->wchar;
dst->syscr += src->syscr;
dst->syscw += src->syscw;
+ dst->syscfs += src->syscfs;
}
#else
static inline void task_chr_io_accounting_add(struct task_io_accounting *dst,
diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h
index ff307b5..4cf8951 100644
--- a/include/linux/thread_info.h
+++ b/include/linux/thread_info.h
@@ -145,6 +145,31 @@
#error "no set_restore_sigmask() provided and default one won't work"
#endif
+#ifndef CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES
+static inline int arch_within_stack_frames(const void * const stack,
+ const void * const stackend,
+ const void *obj, unsigned long len)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_HARDENED_USERCOPY
+extern void __check_object_size(const void *ptr, unsigned long n,
+ bool to_user);
+
+static __always_inline void check_object_size(const void *ptr, unsigned long n,
+ bool to_user)
+{
+ if (!__builtin_constant_p(n))
+ __check_object_size(ptr, n, to_user);
+}
+#else
+static inline void check_object_size(const void *ptr, unsigned long n,
+ bool to_user)
+{ }
+#endif /* CONFIG_HARDENED_USERCOPY */
+
#endif /* __KERNEL__ */
#endif /* _LINUX_THREAD_INFO_H */
diff --git a/include/linux/timekeeping.h b/include/linux/timekeeping.h
index 1caa6b0..f431751 100644
--- a/include/linux/timekeeping.h
+++ b/include/linux/timekeeping.h
@@ -166,6 +166,8 @@
extern u64 ktime_get_mono_fast_ns(void);
+extern u64 ktime_get_boot_fast_ns(void);
+
/*
* Timespec interfaces utilizing the ktime based ones
*/
diff --git a/include/linux/trace_clock.h b/include/linux/trace_clock.h
index 5ad5b6b..1d7ca27 100644
--- a/include/linux/trace_clock.h
+++ b/include/linux/trace_clock.h
@@ -19,6 +19,5 @@
extern u64 notrace trace_clock_jiffies(void);
extern u64 notrace trace_clock_global(void);
extern u64 notrace trace_clock_counter(void);
-extern u64 notrace trace_clock_boot(void);
#endif /* _LINUX_TRACE_CLOCK_H */
diff --git a/include/linux/tracefs.h b/include/linux/tracefs.h
new file mode 100644
index 0000000..5b727a1
--- /dev/null
+++ b/include/linux/tracefs.h
@@ -0,0 +1,45 @@
+/*
+ * tracefs.h - a pseudo file system for activating tracing
+ *
+ * Based on debugfs by: 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt <srostedt@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * tracefs is the file system that is used by the tracing infrastructure.
+ *
+ */
+
+#ifndef _TRACEFS_H_
+#define _TRACEFS_H_
+
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+
+#include <linux/types.h>
+
+struct file_operations;
+
+#ifdef CONFIG_TRACING
+
+struct dentry *tracefs_create_file(const char *name, umode_t mode,
+ struct dentry *parent, void *data,
+ const struct file_operations *fops);
+
+struct dentry *tracefs_create_dir(const char *name, struct dentry *parent);
+
+void tracefs_remove(struct dentry *dentry);
+void tracefs_remove_recursive(struct dentry *dentry);
+
+struct dentry *tracefs_create_instance_dir(const char *name, struct dentry *parent,
+ int (*mkdir)(const char *name),
+ int (*rmdir)(const char *name));
+
+bool tracefs_initialized(void);
+
+#endif /* CONFIG_TRACING */
+
+#endif
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index ecd3319..5e38c6d 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -107,4 +107,11 @@
extern long notrace probe_kernel_write(void *dst, const void *src, size_t size);
extern long notrace __probe_kernel_write(void *dst, const void *src, size_t size);
+#ifndef user_access_begin
+#define user_access_begin() do { } while (0)
+#define user_access_end() do { } while (0)
+#define unsafe_get_user(x, ptr, err) do { if (unlikely(__get_user(x, ptr))) goto err; } while (0)
+#define unsafe_put_user(x, ptr, err) do { if (unlikely(__put_user(x, ptr))) goto err; } while (0)
+#endif
+
#endif /* __LINUX_UACCESS_H__ */
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 24b7377..7b8fb81 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -367,14 +367,13 @@
int devnum_next; /* Next open device number in
* round-robin allocation */
+ struct mutex devnum_next_mutex; /* devnum_next mutex */
struct usb_devmap devmap; /* device address allocation map */
struct usb_device *root_hub; /* Root hub */
struct usb_bus *hs_companion; /* Companion EHCI bus, if any */
struct list_head bus_list; /* list of busses */
- struct mutex usb_address0_mutex; /* unaddressed device mutex */
-
int bandwidth_allocated; /* on this bus: how much of the time
* reserved for periodic (intr/iso)
* requests is used, on average?
@@ -1069,7 +1068,7 @@
* for interfaces bound to this driver.
* @soft_unbind: if set to 1, the USB core will not kill URBs and disable
* endpoints before calling the driver's disconnect method.
- * @disable_hub_initiated_lpm: if set to 0, the USB core will not allow hubs
+ * @disable_hub_initiated_lpm: if set to 1, the USB core will not allow hubs
* to initiate lower power link state transitions when an idle timeout
* occurs. Device-initiated USB 3.0 link PM will still be allowed.
*
diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h
index daec99a..1c88b17 100644
--- a/include/linux/usb/ehci_def.h
+++ b/include/linux/usb/ehci_def.h
@@ -178,11 +178,11 @@
* PORTSCx
*/
/* HOSTPC: offset 0x84 */
- u32 hostpc[1]; /* HOSTPC extension */
+ u32 hostpc[0]; /* HOSTPC extension */
#define HOSTPC_PHCD (1<<22) /* Phy clock disable */
#define HOSTPC_PSPD (3<<25) /* Port speed detection */
- u32 reserved5[16];
+ u32 reserved5[17];
/* USBMODE_EX: offset 0xc8 */
u32 usbmode_ex; /* USB Device mode extension */
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 9a30c1e..db3903f 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -169,6 +169,7 @@
* bandwidth_mutex should be dropped after a successful control message
* to the device, or resetting the bandwidth after a failed attempt.
*/
+ struct mutex *address0_mutex;
struct mutex *bandwidth_mutex;
struct usb_hcd *shared_hcd;
struct usb_hcd *primary_hcd;
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 1d0043d..de2a722 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -50,4 +50,10 @@
/* device can't handle Link Power Management */
#define USB_QUIRK_NO_LPM BIT(10)
+/*
+ * Device reports its bInterval as linear frames instead of the
+ * USB 2.0 calculation.
+ */
+#define USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL BIT(11)
+
#endif /* __LINUX_USB_QUIRKS_H */
diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h
index d5952bb..c1209d5 100644
--- a/include/linux/usb/renesas_usbhs.h
+++ b/include/linux/usb/renesas_usbhs.h
@@ -153,7 +153,7 @@
*/
int pio_dma_border; /* default is 64byte */
- u32 type;
+ uintptr_t type;
u32 enable_gpio;
/*
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
new file mode 100644
index 0000000..b174248a
--- /dev/null
+++ b/include/linux/usb/typec.h
@@ -0,0 +1,247 @@
+
+#ifndef __LINUX_USB_TYPEC_H
+#define __LINUX_USB_TYPEC_H
+
+#include <linux/types.h>
+
+/* XXX: Once we have a header for USB Power Delivery, this belongs there */
+#define ALTMODE_MAX_MODES 6
+
+/* USB Type-C Specification releases */
+#define USB_TYPEC_REV_1_0 0x100 /* 1.0 */
+#define USB_TYPEC_REV_1_1 0x110 /* 1.1 */
+#define USB_TYPEC_REV_1_2 0x120 /* 1.2 */
+
+struct typec_altmode;
+struct typec_partner;
+struct typec_cable;
+struct typec_plug;
+struct typec_port;
+
+struct fwnode_handle;
+
+enum typec_port_type {
+ TYPEC_PORT_DFP,
+ TYPEC_PORT_UFP,
+ TYPEC_PORT_DRP,
+};
+
+enum typec_plug_type {
+ USB_PLUG_NONE,
+ USB_PLUG_TYPE_A,
+ USB_PLUG_TYPE_B,
+ USB_PLUG_TYPE_C,
+ USB_PLUG_CAPTIVE,
+};
+
+enum typec_data_role {
+ TYPEC_DEVICE,
+ TYPEC_HOST,
+};
+
+enum typec_role {
+ TYPEC_SINK,
+ TYPEC_SOURCE,
+};
+
+enum typec_pwr_opmode {
+ TYPEC_PWR_MODE_USB,
+ TYPEC_PWR_MODE_1_5A,
+ TYPEC_PWR_MODE_3_0A,
+ TYPEC_PWR_MODE_PD,
+};
+
+enum typec_accessory {
+ TYPEC_ACCESSORY_NONE,
+ TYPEC_ACCESSORY_AUDIO,
+ TYPEC_ACCESSORY_DEBUG,
+};
+
+#define TYPEC_MAX_ACCESSORY 3
+
+/*
+ * struct usb_pd_identity - USB Power Delivery identity data
+ * @id_header: ID Header VDO
+ * @cert_stat: Cert Stat VDO
+ * @product: Product VDO
+ *
+ * USB power delivery Discover Identity command response data.
+ *
+ * REVISIT: This is USB Power Delivery specific information, so this structure
+ * probable belongs to USB Power Delivery header file once we have them.
+ */
+struct usb_pd_identity {
+ u32 id_header;
+ u32 cert_stat;
+ u32 product;
+};
+
+int typec_partner_set_identity(struct typec_partner *partner);
+int typec_cable_set_identity(struct typec_cable *cable);
+
+/*
+ * struct typec_mode_desc - Individual Mode of an Alternate Mode
+ * @index: Index of the Mode within the SVID
+ * @vdo: VDO returned by Discover Modes USB PD command
+ * @desc: Optional human readable description of the mode
+ * @roles: Only for ports. DRP if the mode is available in both roles
+ *
+ * Description of a mode of an Alternate Mode which a connector, cable plug or
+ * partner supports. Every mode will have it's own sysfs group. The details are
+ * the VDO returned by discover modes command, description for the mode and
+ * active flag telling has the mode being entered or not.
+ */
+struct typec_mode_desc {
+ int index;
+ u32 vdo;
+ char *desc;
+ /* Only used with ports */
+ enum typec_port_type roles;
+};
+
+/*
+ * struct typec_altmode_desc - USB Type-C Alternate Mode Descriptor
+ * @svid: Standard or Vendor ID
+ * @n_modes: Number of modes
+ * @modes: Array of modes supported by the Alternate Mode
+ *
+ * Representation of an Alternate Mode that has SVID assigned by USB-IF. The
+ * array of modes will list the modes of a particular SVID that are supported by
+ * a connector, partner of a cable plug.
+ */
+struct typec_altmode_desc {
+ u16 svid;
+ int n_modes;
+ struct typec_mode_desc modes[ALTMODE_MAX_MODES];
+};
+
+struct typec_altmode
+*typec_partner_register_altmode(struct typec_partner *partner,
+ struct typec_altmode_desc *desc);
+struct typec_altmode
+*typec_plug_register_altmode(struct typec_plug *plug,
+ struct typec_altmode_desc *desc);
+struct typec_altmode
+*typec_port_register_altmode(struct typec_port *port,
+ struct typec_altmode_desc *desc);
+void typec_unregister_altmode(struct typec_altmode *altmode);
+
+struct typec_port *typec_altmode2port(struct typec_altmode *alt);
+
+void typec_altmode_update_active(struct typec_altmode *alt, int mode,
+ bool active);
+
+enum typec_plug_index {
+ TYPEC_PLUG_SOP_P,
+ TYPEC_PLUG_SOP_PP,
+};
+
+/*
+ * struct typec_plug_desc - USB Type-C Cable Plug Descriptor
+ * @index: SOP Prime for the plug connected to DFP and SOP Double Prime for the
+ * plug connected to UFP
+ *
+ * Represents USB Type-C Cable Plug.
+ */
+struct typec_plug_desc {
+ enum typec_plug_index index;
+};
+
+/*
+ * struct typec_cable_desc - USB Type-C Cable Descriptor
+ * @type: The plug type from USB PD Cable VDO
+ * @active: Is the cable active or passive
+ * @identity: Result of Discover Identity command
+ *
+ * Represents USB Type-C Cable attached to USB Type-C port.
+ */
+struct typec_cable_desc {
+ enum typec_plug_type type;
+ unsigned int active:1;
+ struct usb_pd_identity *identity;
+};
+
+/*
+ * struct typec_partner_desc - USB Type-C Partner Descriptor
+ * @usb_pd: USB Power Delivery support
+ * @accessory: Audio, Debug or none.
+ * @identity: Discover Identity command data
+ *
+ * Details about a partner that is attached to USB Type-C port. If @identity
+ * member exists when partner is registered, a directory named "identity" is
+ * created to sysfs for the partner device.
+ */
+struct typec_partner_desc {
+ unsigned int usb_pd:1;
+ enum typec_accessory accessory;
+ struct usb_pd_identity *identity;
+};
+
+/*
+ * struct typec_capability - USB Type-C Port Capabilities
+ * @role: DFP (Host-only), UFP (Device-only) or DRP (Dual Role)
+ * @revision: USB Type-C Specification release. Binary coded decimal
+ * @pd_revision: USB Power Delivery Specification revision if supported
+ * @prefer_role: Initial role preference
+ * @accessory: Supported Accessory Modes
+ * @fwnode: Optional fwnode of the port
+ * @try_role: Set data role preference for DRP port
+ * @dr_set: Set Data Role
+ * @pr_set: Set Power Role
+ * @vconn_set: Set VCONN Role
+ * @activate_mode: Enter/exit given Alternate Mode
+ * @port_type_set: Set port type
+ *
+ * Static capabilities of a single USB Type-C port.
+ */
+struct typec_capability {
+ enum typec_port_type type;
+ u16 revision; /* 0120H = "1.2" */
+ u16 pd_revision; /* 0300H = "3.0" */
+ int prefer_role;
+ enum typec_accessory accessory[TYPEC_MAX_ACCESSORY];
+
+ struct fwnode_handle *fwnode;
+
+ int (*try_role)(const struct typec_capability *,
+ int role);
+
+ int (*dr_set)(const struct typec_capability *,
+ enum typec_data_role);
+ int (*pr_set)(const struct typec_capability *,
+ enum typec_role);
+ int (*vconn_set)(const struct typec_capability *,
+ enum typec_role);
+
+ int (*activate_mode)(const struct typec_capability *,
+ int mode, int activate);
+ int (*port_type_set)(const struct typec_capability *,
+ enum typec_port_type);
+
+};
+
+/* Specific to try_role(). Indicates the user want's to clear the preference. */
+#define TYPEC_NO_PREFERRED_ROLE (-1)
+
+struct typec_port *typec_register_port(struct device *parent,
+ const struct typec_capability *cap);
+void typec_unregister_port(struct typec_port *port);
+
+struct typec_partner *typec_register_partner(struct typec_port *port,
+ struct typec_partner_desc *desc);
+void typec_unregister_partner(struct typec_partner *partner);
+
+struct typec_cable *typec_register_cable(struct typec_port *port,
+ struct typec_cable_desc *desc);
+void typec_unregister_cable(struct typec_cable *cable);
+
+struct typec_plug *typec_register_plug(struct typec_cable *cable,
+ struct typec_plug_desc *desc);
+void typec_unregister_plug(struct typec_plug *plug);
+
+void typec_set_data_role(struct typec_port *port, enum typec_data_role role);
+void typec_set_pwr_role(struct typec_port *port, enum typec_role role);
+void typec_set_vconn_role(struct typec_port *port, enum typec_role role);
+void typec_set_pwr_opmode(struct typec_port *port, enum typec_pwr_opmode mode);
+
+#endif /* __LINUX_USB_TYPEC_H */
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
index 7f5f78b..245f57d 100644
--- a/include/linux/usb_usual.h
+++ b/include/linux/usb_usual.h
@@ -79,6 +79,8 @@
/* Cannot handle MI_REPORT_SUPPORTED_OPERATION_CODES */ \
US_FLAG(MAX_SECTORS_240, 0x08000000) \
/* Sets max_sectors to 240 */ \
+ US_FLAG(NO_REPORT_LUNS, 0x10000000) \
+ /* Cannot handle REPORT_LUNS */ \
#define US_FLAG(name, value) US_FL_##name = value ,
enum { US_DO_ALL_FLAGS };
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 004cae9..d336260 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -1,8 +1,9 @@
#ifndef _ADDRCONF_H
#define _ADDRCONF_H
-#define MAX_RTR_SOLICITATIONS 3
+#define MAX_RTR_SOLICITATIONS -1 /* unlimited */
#define RTR_SOLICITATION_INTERVAL (4*HZ)
+#define RTR_SOLICITATION_MAX_INTERVAL (3600*HZ) /* 1 hour */
#define MIN_VALID_LIFETIME (2*3600) /* 2 hours */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index a6392dd..01edbe7 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5,6 +5,7 @@
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2015 Intel Deutschland GmbH
*
* 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
@@ -68,6 +69,14 @@
#define CFG80211_CONNECT_PREV_BSSID 1
#define CFG80211_CONNECT_BSS 1
#define CFG80211_ABORT_SCAN 1
+#define CFG80211_INFORM_BSS_FRAME_DATA 1
+#define CFG80211_SCAN_RANDOM_MAC_ADDR 1
+#define CFG80211_MULTI_SCAN_PLAN_BACKPORT 1
+#define CFG80211_UPDATE_CONNECT_PARAMS 1
+#define CFG80211_DISCONNECTED_V2 1
+#define CFG80211_CONNECT_TIMEOUT 1
+#define CFG80211_RAND_TA_FOR_PUBLIC_ACTION_FRAME 1
+#define CFG80211_REPORT_BETTER_BSS_IN_SCHED_SCAN 1
/*
* wireless hardware capability structures
@@ -688,6 +697,18 @@
struct mac_address mac_addrs[];
};
+/*
+ * cfg80211_bitrate_mask - masks for bitrate control
+ */
+struct cfg80211_bitrate_mask {
+ struct {
+ u32 legacy;
+ u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
+ u16 vht_mcs[NL80211_VHT_NSS_MAX];
+ enum nl80211_txrate_gi gi;
+ } control[IEEE80211_NUM_BANDS];
+};
+
/**
* struct cfg80211_ap_settings - AP configuration
*
@@ -712,6 +733,7 @@
* MAC address based access control
* @pbss: If set, start as a PCP instead of AP. Relevant for DMG
* networks.
+ * @beacon_rate: bitrate to be used for beacons
*/
struct cfg80211_ap_settings {
struct cfg80211_chan_def chandef;
@@ -731,6 +753,7 @@
bool p2p_opp_ps;
const struct cfg80211_acl_data *acl;
bool pbss;
+ struct cfg80211_bitrate_mask beacon_rate;
};
/**
@@ -1376,6 +1399,7 @@
* @beacon_interval: beacon interval to use
* @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a]
* @basic_rates: basic rates to use when creating the mesh
+ * @beacon_rate: bitrate to be used for beacons
*
* These parameters are fixed when the mesh is created.
*/
@@ -1396,6 +1420,7 @@
u16 beacon_interval;
int mcast_rate[IEEE80211_NUM_BANDS];
u32 basic_rates;
+ struct cfg80211_bitrate_mask beacon_rate;
};
/**
@@ -1467,6 +1492,10 @@
* @aborted: (internal) scan request was notified as aborted
* @notified: (internal) scan request was notified as done or aborted
* @no_cck: used to send probe requests at non CCK rate in 2GHz band
+ * @mac_addr: MAC address used with randomisation
+ * @mac_addr_mask: MAC address mask used with randomisation, bits that
+ * are 0 in the mask should be randomised, bits that are 1 should
+ * be taken from the @mac_addr
* @bssid: BSSID to scan for (most commonly, the wildcard BSSID)
*/
struct cfg80211_scan_request {
@@ -1484,6 +1513,9 @@
u8 bssid[ETH_ALEN] __aligned(2);
+ u8 mac_addr[ETH_ALEN] __aligned(2);
+ u8 mac_addr_mask[ETH_ALEN] __aligned(2);
+
/* internal */
struct wiphy *wiphy;
unsigned long scan_start;
@@ -1494,6 +1526,17 @@
struct ieee80211_channel *channels[0];
};
+static inline void get_random_mask_addr(u8 *buf, const u8 *addr, const u8 *mask)
+{
+ int i;
+
+ get_random_bytes(buf, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++) {
+ buf[i] &= ~mask[i];
+ buf[i] |= addr[i] & mask[i];
+ }
+}
+
/**
* struct cfg80211_match_set - sets of attributes to match
*
@@ -1506,13 +1549,37 @@
};
/**
+ * struct cfg80211_sched_scan_plan - scan plan for scheduled scan
+ *
+ * @interval: interval between scheduled scan iterations. In seconds.
+ * @iterations: number of scan iterations in this scan plan. Zero means
+ * infinite loop.
+ * The last scan plan will always have this parameter set to zero,
+ * all other scan plans will have a finite number of iterations.
+ */
+struct cfg80211_sched_scan_plan {
+ u32 interval;
+ u32 iterations;
+};
+
+/**
+ * struct cfg80211_bss_select_adjust - BSS selection with RSSI adjustment.
+ *
+ * @band: band of BSS which should match for RSSI level adjustment.
+ * @delta: value of RSSI level adjustment.
+ */
+struct cfg80211_bss_select_adjust {
+ enum nl80211_band band;
+ s8 delta;
+};
+
+/**
* struct cfg80211_sched_scan_request - scheduled scan request description
*
* @ssids: SSIDs to scan for (passed in the probe_reqs in active scans)
* @n_ssids: number of SSIDs
* @n_channels: total number of channels to scan
* @scan_width: channel width for scanning
- * @interval: interval between each scheduled scan cycle
* @ie: optional information element(s) to add into Probe Request or %NULL
* @ie_len: length of ie in octets
* @flags: bit field of flags controlling operation
@@ -1524,18 +1591,34 @@
* @wiphy: the wiphy this was for
* @dev: the interface
* @scan_start: start time of the scheduled scan
+ * @scan_plans: scan plans to be executed in this scheduled scan. Lowest
+ * index must be executed first.
+ * @n_scan_plans: number of scan plans, at least 1.
* @channels: channels to scan
* @min_rssi_thold: for drivers only supporting a single threshold, this
* contains the minimum over all matchsets
+ * @mac_addr: MAC address used with randomisation
+ * @mac_addr_mask: MAC address mask used with randomisation, bits that
+ * are 0 in the mask should be randomised, bits that are 1 should
+ * be taken from the @mac_addr
* @owner_nlportid: netlink portid of owner (if this should is a request
* owned by a particular socket)
+ * @relative_rssi_set: Indicates whether @relative_rssi is set or not.
+ * @relative_rssi: Relative RSSI threshold in dB to restrict scan result
+ * reporting in connected state to cases where a matching BSS is determined
+ * to have better or slightly worse RSSI than the current connected BSS.
+ * The relative RSSI threshold values are ignored in disconnected state.
+ * @rssi_adjust: delta dB of RSSI preference to be given to the BSSs that belong
+ * to the specified band while deciding whether a better BSS is reported
+ * using @relative_rssi. If delta is a negative number, the BSSs that
+ * belong to the specified band will be penalized by delta dB in relative
+ * comparisions.
*/
struct cfg80211_sched_scan_request {
struct cfg80211_ssid *ssids;
int n_ssids;
u32 n_channels;
enum nl80211_bss_scan_width scan_width;
- u32 interval;
const u8 *ie;
size_t ie_len;
u32 flags;
@@ -1543,10 +1626,20 @@
int n_match_sets;
s32 min_rssi_thold;
+ u8 mac_addr[ETH_ALEN] __aligned(2);
+ u8 mac_addr_mask[ETH_ALEN] __aligned(2);
+
+ bool relative_rssi_set;
+ s8 relative_rssi;
+ struct cfg80211_bss_select_adjust rssi_adjust;
+
/* internal */
struct wiphy *wiphy;
struct net_device *dev;
unsigned long scan_start;
+ struct cfg80211_sched_scan_plan *scan_plans;
+ int n_scan_plans;
+
u32 owner_nlportid;
/* keep last */
@@ -1567,6 +1660,26 @@
};
/**
+ * struct cfg80211_inform_bss - BSS inform data
+ * @chan: channel the frame was received on
+ * @scan_width: scan width that was used
+ * @signal: signal strength value, according to the wiphy's
+ * signal type
+ * @boottime_ns: timestamp (CLOCK_BOOTTIME) when the information was
+ * received; should match the time when the frame was actually
+ * received by the device (not just by the host, in case it was
+ * buffered on the device) and be accurate to about 10ms.
+ * If the frame isn't buffered, just passing the return value of
+ * ktime_get_boot_ns() is likely appropriate.
+ */
+struct cfg80211_inform_bss {
+ struct ieee80211_channel *chan;
+ enum nl80211_bss_scan_width scan_width;
+ s32 signal;
+ u64 boottime_ns;
+};
+
+/**
* struct cfg80211_bss_ie_data - BSS entry IE data
* @tsf: TSF contained in the frame that carried these IEs
* @rcu_head: internal use, for freeing
@@ -1807,6 +1920,22 @@
};
/**
+ * struct cfg80211_bss_selection - connection parameters for BSS selection.
+ *
+ * @behaviour: requested BSS selection behaviour.
+ * @param: parameters for requestion behaviour.
+ * @band_pref: preferred band for %NL80211_BSS_SELECT_ATTR_BAND_PREF.
+ * @adjust: parameters for %NL80211_BSS_SELECT_ATTR_RSSI_ADJUST.
+ */
+struct cfg80211_bss_selection {
+ enum nl80211_bss_select_attr behaviour;
+ union {
+ enum ieee80211_band band_pref;
+ struct cfg80211_bss_select_adjust adjust;
+ } param;
+};
+
+/**
* struct cfg80211_connect_params - Connection parameters
*
* This structure provides information needed to complete IEEE 802.11
@@ -1843,6 +1972,7 @@
* @vht_capa_mask: The bits of vht_capa which are to be used.
* @pbss: if set, connect to a PCP instead of AP. Valid for DMG
* networks.
+ * @bss_select: criteria to be used for BSS selection.
* @prev_bssid: previous BSSID, if not %NULL use reassociate frame
*/
struct cfg80211_connect_params {
@@ -1867,10 +1997,23 @@
struct ieee80211_vht_cap vht_capa;
struct ieee80211_vht_cap vht_capa_mask;
bool pbss;
+ struct cfg80211_bss_selection bss_select;
const u8 *prev_bssid;
};
/**
+ * enum cfg80211_connect_params_changed - Connection parameters being updated
+ *
+ * This enum provides information of all connect parameters that
+ * have to be updated as part of update_connect_params() call.
+ *
+ * @UPDATE_ASSOC_IES: Indicates whether association request IEs are updated
+ */
+enum cfg80211_connect_params_changed {
+ UPDATE_ASSOC_IES = BIT(0),
+};
+
+/**
* enum wiphy_params_flags - set_wiphy_params bitfield values
* @WIPHY_PARAM_RETRY_SHORT: wiphy->retry_short has changed
* @WIPHY_PARAM_RETRY_LONG: wiphy->retry_long has changed
@@ -1888,17 +2031,6 @@
WIPHY_PARAM_DYN_ACK = 1 << 5,
};
-/*
- * cfg80211_bitrate_mask - masks for bitrate control
- */
-struct cfg80211_bitrate_mask {
- struct {
- u32 legacy;
- u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
- u16 vht_mcs[NL80211_VHT_NSS_MAX];
- enum nl80211_txrate_gi gi;
- } control[IEEE80211_NUM_BANDS];
-};
/**
* struct cfg80211_pmksa - PMK Security Association
*
@@ -2257,9 +2389,31 @@
* (invoked with the wireless_dev mutex held)
*
* @connect: Connect to the ESS with the specified parameters. When connected,
- * call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
- * If the connection fails for some reason, call cfg80211_connect_result()
- * with the status from the AP.
+ * call cfg80211_connect_result()/cfg80211_connect_bss() with status code
+ * %WLAN_STATUS_SUCCESS. If the connection fails for some reason, call
+ * cfg80211_connect_result()/cfg80211_connect_bss() with the status code
+ * from the AP or cfg80211_connect_timeout() if no frame with status code
+ * was received.
+ * The driver is allowed to roam to other BSSes within the ESS when the
+ * other BSS matches the connect parameters. When such roaming is initiated
+ * by the driver, the driver is expected to verify that the target matches
+ * the configured security parameters and to use Reassociation Request
+ * frame instead of Association Request frame.
+ * The connect function can also be used to request the driver to perform a
+ * specific roam when connected to an ESS. In that case, the prev_bssid
+ * parameter is set to the BSSID of the currently associated BSS as an
+ * indication of requesting reassociation.
+ * In both the driver-initiated and new connect() call initiated roaming
+ * cases, the result of roaming is indicated with a call to
+ * cfg80211_roamed() or cfg80211_roamed_bss().
+ * (invoked with the wireless_dev mutex held)
+ * @update_connect_params: Update the connect parameters while connected to a
+ * BSS. The updated parameters can be used by driver/firmware for
+ * subsequent BSS selection (roaming) decisions and to form the
+ * Authentication/(Re)Association Request frames. This call does not
+ * request an immediate disassociation or reassociation with the current
+ * BSS, i.e., this impacts only subsequent (re)associations. The bits in
+ * changed are defined in &enum cfg80211_connect_params_changed.
* (invoked with the wireless_dev mutex held)
* @disconnect: Disconnect from the BSS/ESS.
* (invoked with the wireless_dev mutex held)
@@ -2507,6 +2661,10 @@
int (*connect)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme);
+ int (*update_connect_params)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_connect_params *sme,
+ u32 changed);
int (*disconnect)(struct wiphy *wiphy, struct net_device *dev,
u16 reason_code);
@@ -2917,6 +3075,24 @@
};
/**
+ * struct wiphy_iftype_ext_capab - extended capabilities per interface type
+ * @iftype: interface type
+ * @extended_capabilities: extended capabilities supported by the driver,
+ * additional capabilities might be supported by userspace; these are the
+ * 802.11 extended capabilities ("Extended Capabilities element") and are
+ * in the same format as in the information element. See IEEE Std
+ * 802.11-2012 8.4.2.29 for the defined fields.
+ * @extended_capabilities_mask: mask of the valid values
+ * @extended_capabilities_len: length of the extended capabilities
+ */
+struct wiphy_iftype_ext_capab {
+ enum nl80211_iftype iftype;
+ const u8 *extended_capabilities;
+ const u8 *extended_capabilities_mask;
+ u8 extended_capabilities_len;
+};
+
+/**
* struct wiphy - wireless hardware description
* @reg_notifier: the driver's regulatory notification callback,
* note that if your driver uses wiphy_apply_custom_regulatory()
@@ -2965,6 +3141,8 @@
* @regulatory_flags: wiphy regulatory flags, see
* &enum ieee80211_regulatory_flags
* @features: features advertised to nl80211, see &enum nl80211_feature_flags.
+ * @ext_features: extended features advertised to nl80211, see
+ * &enum nl80211_ext_feature_index.
* @bss_priv_size: each BSS struct has private data allocated with it,
* this variable determines its size
* @max_scan_ssids: maximum number of SSIDs the device can scan for in
@@ -2979,6 +3157,12 @@
* include fixed IEs like supported rates
* @max_sched_scan_ie_len: same as max_scan_ie_len, but for scheduled
* scans
+ * @max_sched_scan_plans: maximum number of scan plans (scan interval and number
+ * of iterations) for scheduled scan supported by the device.
+ * @max_sched_scan_plan_interval: maximum interval (in seconds) for a
+ * single scan plan supported by the device.
+ * @max_sched_scan_plan_iterations: maximum number of iterations for a single
+ * scan plan supported by the device.
* @coverage_class: current coverage class
* @fw_version: firmware version for ethtool reporting
* @hw_version: hardware version for ethtool reporting
@@ -3025,9 +3209,14 @@
* additional capabilities might be supported by userspace; these are
* the 802.11 extended capabilities ("Extended Capabilities element")
* and are in the same format as in the information element. See
- * 802.11-2012 8.4.2.29 for the defined fields.
+ * 802.11-2012 8.4.2.29 for the defined fields. These are the default
+ * extended capabilities to be used if the capabilities are not specified
+ * for a specific interface type in iftype_ext_capab.
* @extended_capabilities_mask: mask of the valid values
* @extended_capabilities_len: length of the extended capabilities
+ * @iftype_ext_capab: array of extended capabilities per interface type
+ * @num_iftype_ext_capab: number of interface types for which extended
+ * capabilities are specified separately.
* @coalesce: packet coalescing support information
*
* @vendor_commands: array of vendor commands supported by the hardware
@@ -3050,6 +3239,9 @@
* low rssi when a frame is heard on different channel, then it should set
* this variable to the maximal offset for which it can compensate.
* This value should be set in MHz.
+ * @bss_select_support: bitmask indicating the BSS selection criteria supported
+ * by the driver in the .connect() callback. The bit position maps to the
+ * attribute indices defined in &enum nl80211_bss_select_attr.
*/
struct wiphy {
/* assign these fields before you register the wiphy */
@@ -3074,6 +3266,7 @@
u16 max_acl_mac_addrs;
u32 flags, regulatory_flags, features;
+ u8 ext_features[DIV_ROUND_UP(NUM_NL80211_EXT_FEATURES, 8)];
u32 ap_sme_capa;
@@ -3085,6 +3278,10 @@
u8 max_match_sets;
u16 max_scan_ie_len;
u16 max_sched_scan_ie_len;
+ u32 max_sched_scan_plans;
+ u32 max_sched_scan_plan_interval;
+ u32 max_sched_scan_plan_iterations;
+
int n_cipher_suites;
const u32 *cipher_suites;
@@ -3120,6 +3317,9 @@
const u8 *extended_capabilities, *extended_capabilities_mask;
u8 extended_capabilities_len;
+ const struct wiphy_iftype_ext_capab *iftype_ext_capab;
+ unsigned int num_iftype_ext_capab;
+
/* If multiple wiphys are registered and you're handed e.g.
* a regular netdev with assigned ieee80211_ptr, you won't
* know whether it points to a wiphy your driver has registered
@@ -3170,6 +3370,8 @@
u8 max_num_csa_counters;
u8 max_adj_channel_rssi_comp;
+ u32 bss_select_support;
+
char priv[0] __aligned(NETDEV_ALIGN);
};
@@ -3861,14 +4063,11 @@
void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy);
/**
- * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame
- *
+ * cfg80211_inform_bss_frame_data - inform cfg80211 of a received BSS frame
* @wiphy: the wiphy reporting the BSS
- * @rx_channel: The channel the frame was received on
- * @scan_width: width of the control channel
+ * @data: the BSS metadata
* @mgmt: the management frame (probe response or beacon)
* @len: length of the management frame
- * @signal: the signal strength, type depends on the wiphy's signal_type
* @gfp: context flags
*
* This informs cfg80211 that BSS information was found and
@@ -3878,11 +4077,26 @@
* Or %NULL on error.
*/
struct cfg80211_bss * __must_check
+cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
+ struct cfg80211_inform_bss *data,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
struct ieee80211_channel *rx_channel,
enum nl80211_bss_scan_width scan_width,
struct ieee80211_mgmt *mgmt, size_t len,
- s32 signal, gfp_t gfp);
+ s32 signal, gfp_t gfp)
+{
+ struct cfg80211_inform_bss data = {
+ .chan = rx_channel,
+ .scan_width = scan_width,
+ .signal = signal,
+ };
+
+ return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp);
+}
static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss_frame(struct wiphy *wiphy,
@@ -3890,9 +4104,13 @@
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp)
{
- return cfg80211_inform_bss_width_frame(wiphy, rx_channel,
- NL80211_BSS_CHAN_WIDTH_20,
- mgmt, len, signal, gfp);
+ struct cfg80211_inform_bss data = {
+ .chan = rx_channel,
+ .scan_width = NL80211_BSS_CHAN_WIDTH_20,
+ .signal = signal,
+ };
+
+ return cfg80211_inform_bss_frame_data(wiphy, &data, mgmt, len, gfp);
}
/**
@@ -3909,11 +4127,10 @@
};
/**
- * cfg80211_inform_bss_width - inform cfg80211 of a new BSS
+ * cfg80211_inform_bss_data - inform cfg80211 of a new BSS
*
* @wiphy: the wiphy reporting the BSS
- * @rx_channel: The channel the frame was received on
- * @scan_width: width of the control channel
+ * @data: the BSS metadata
* @ftype: frame type (if known)
* @bssid: the BSSID of the BSS
* @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
@@ -3921,7 +4138,6 @@
* @beacon_interval: the beacon interval announced by the peer
* @ie: additional IEs sent by the peer
* @ielen: length of the additional IEs
- * @signal: the signal strength, type depends on the wiphy's signal_type
* @gfp: context flags
*
* This informs cfg80211 that BSS information was found and
@@ -3931,13 +4147,32 @@
* Or %NULL on error.
*/
struct cfg80211_bss * __must_check
+cfg80211_inform_bss_data(struct wiphy *wiphy,
+ struct cfg80211_inform_bss *data,
+ enum cfg80211_bss_frame_type ftype,
+ const u8 *bssid, u64 tsf, u16 capability,
+ u16 beacon_interval, const u8 *ie, size_t ielen,
+ gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss_width(struct wiphy *wiphy,
struct ieee80211_channel *rx_channel,
enum nl80211_bss_scan_width scan_width,
enum cfg80211_bss_frame_type ftype,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
- s32 signal, gfp_t gfp);
+ s32 signal, gfp_t gfp)
+{
+ struct cfg80211_inform_bss data = {
+ .chan = rx_channel,
+ .scan_width = scan_width,
+ .signal = signal,
+ };
+
+ return cfg80211_inform_bss_data(wiphy, &data, ftype, bssid, tsf,
+ capability, beacon_interval, ie, ielen,
+ gfp);
+}
static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss(struct wiphy *wiphy,
@@ -3947,11 +4182,15 @@
u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp)
{
- return cfg80211_inform_bss_width(wiphy, rx_channel,
- NL80211_BSS_CHAN_WIDTH_20, ftype,
- bssid, tsf, capability,
- beacon_interval, ie, ielen, signal,
- gfp);
+ struct cfg80211_inform_bss data = {
+ .chan = rx_channel,
+ .scan_width = NL80211_BSS_CHAN_WIDTH_20,
+ .signal = signal,
+ };
+
+ return cfg80211_inform_bss_data(wiphy, &data, ftype, bssid, tsf,
+ capability, beacon_interval, ie, ielen,
+ gfp);
}
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
@@ -4437,7 +4676,7 @@
void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
struct cfg80211_bss *bss, const u8 *req_ie,
size_t req_ie_len, const u8 *resp_ie,
- size_t resp_ie_len, u16 status, gfp_t gfp);
+ size_t resp_ie_len, int status, gfp_t gfp);
/**
* cfg80211_connect_result - notify cfg80211 of connection result
@@ -4467,6 +4706,29 @@
}
/**
+ * cfg80211_connect_timeout - notify cfg80211 of connection timeout
+ *
+ * @dev: network device
+ * @bssid: the BSSID of the AP
+ * @req_ie: association request IEs (maybe be %NULL)
+ * @req_ie_len: association request IEs length
+ * @gfp: allocation flags
+ *
+ * It should be called by the underlying driver whenever connect() has failed
+ * in a sequence where no explicit authentication/association rejection was
+ * received from the AP. This could happen, e.g., due to not being able to send
+ * out the Authentication or Association Request frame or timing out while
+ * waiting for the response.
+ */
+static inline void
+cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid,
+ const u8 *req_ie, size_t req_ie_len, gfp_t gfp)
+{
+ cfg80211_connect_bss(dev, bssid, NULL, req_ie, req_ie_len, NULL, 0, -1,
+ gfp);
+}
+
+/**
* cfg80211_roamed - notify cfg80211 of roaming
*
* @dev: network device
@@ -4520,13 +4782,15 @@
* @ie: information elements of the deauth/disassoc frame (may be %NULL)
* @ie_len: length of IEs
* @reason: reason code for the disconnection, set it to 0 if unknown
+ * @locally_generated: disconnection was requested locally
* @gfp: allocation flags
*
* After it calls this function, the driver should enter an idle state
* and not try to connect to any AP any more.
*/
void cfg80211_disconnected(struct net_device *dev, u16 reason,
- const u8 *ie, size_t ie_len, gfp_t gfp);
+ const u8 *ie, size_t ie_len,
+ bool locally_generated, gfp_t gfp);
/**
* cfg80211_ready_on_channel - notification of remain_on_channel start
@@ -5010,6 +5274,42 @@
*/
void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy);
+/**
+ * wiphy_ext_feature_set - set the extended feature flag
+ *
+ * @wiphy: the wiphy to modify.
+ * @ftidx: extended feature bit index.
+ *
+ * The extended features are flagged in multiple bytes (see
+ * &struct wiphy.@ext_features)
+ */
+static inline void wiphy_ext_feature_set(struct wiphy *wiphy,
+ enum nl80211_ext_feature_index ftidx)
+{
+ u8 *ft_byte;
+
+ ft_byte = &wiphy->ext_features[ftidx / 8];
+ *ft_byte |= BIT(ftidx % 8);
+}
+
+/**
+ * wiphy_ext_feature_isset - check the extended feature flag
+ *
+ * @wiphy: the wiphy to modify.
+ * @ftidx: extended feature bit index.
+ *
+ * The extended features are flagged in multiple bytes (see
+ * &struct wiphy.@ext_features)
+ */
+static inline bool
+wiphy_ext_feature_isset(struct wiphy *wiphy,
+ enum nl80211_ext_feature_index ftidx)
+{
+ u8 ft_byte;
+
+ ft_byte = wiphy->ext_features[ftidx / 8];
+ return (ft_byte & BIT(ftidx % 8)) != 0;
+}
/* ethtool helper */
void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h
index 3ebb168..a34b141 100644
--- a/include/net/cipso_ipv4.h
+++ b/include/net/cipso_ipv4.h
@@ -309,6 +309,10 @@
}
for (opt_iter = 6; opt_iter < opt_len;) {
+ if (opt_iter + 1 == opt_len) {
+ err_offset = opt_iter;
+ goto out;
+ }
tag_len = opt[opt_iter + 1];
if ((tag_len == 0) || (tag_len > (opt_len - opt_iter))) {
err_offset = opt_iter + 1;
diff --git a/include/net/cnss_nl.h b/include/net/cnss_nl.h
new file mode 100644
index 0000000..86c2fcc
--- /dev/null
+++ b/include/net/cnss_nl.h
@@ -0,0 +1,100 @@
+/* Copyright (c) 2017, The Linux Foundation. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _NET_CNSS_GENETLINK_H_
+#define _NET_CNSS_GENETLINK_H_
+
+#define CLD80211_MAX_COMMANDS 40
+#define CLD80211_MAX_NL_DATA 4096
+
+/**
+ * enum cld80211_attr - Driver/Application embeds the data in nlmsg with the
+ * help of below attributes
+ *
+ * @CLD80211_ATTR_VENDOR_DATA: Embed all other attributes in this nested
+ * attribute.
+ * @CLD80211_ATTR_DATA: Embed complete data in this attribute
+ *
+ * Any new message in future can be added as another attribute
+ */
+enum cld80211_attr {
+ CLD80211_ATTR_VENDOR_DATA = 1,
+ CLD80211_ATTR_DATA,
+ /* add new attributes above here */
+
+ __CLD80211_ATTR_AFTER_LAST,
+ CLD80211_ATTR_MAX = __CLD80211_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum cld80211_multicast_groups - List of multicast groups supported
+ *
+ * @CLD80211_MCGRP_SVC_MSGS: WLAN service message will be sent to this group.
+ * Ex: Status ind messages
+ * @CLD80211_MCGRP_HOST_LOGS: All logging related messages from driver will be
+ * sent to this multicast group
+ * @CLD80211_MCGRP_FW_LOGS: Firmware logging messages will be sent to this group
+ * @CLD80211_MCGRP_PER_PKT_STATS: Messages related packet stats debugging infra
+ * will be sent to this group
+ * @CLD80211_MCGRP_DIAG_EVENTS: Driver/Firmware status logging diag events will
+ * be sent to this group
+ * @CLD80211_MCGRP_FATAL_EVENTS: Any fatal message generated in driver/firmware
+ * will be sent to this group
+ * @CLD80211_MCGRP_OEM_MSGS: All OEM message will be sent to this group
+ * Ex: LOWI messages
+ */
+enum cld80211_multicast_groups {
+ CLD80211_MCGRP_SVC_MSGS,
+ CLD80211_MCGRP_HOST_LOGS,
+ CLD80211_MCGRP_FW_LOGS,
+ CLD80211_MCGRP_PER_PKT_STATS,
+ CLD80211_MCGRP_DIAG_EVENTS,
+ CLD80211_MCGRP_FATAL_EVENTS,
+ CLD80211_MCGRP_OEM_MSGS,
+};
+
+/**
+ * typedef cld80211_cb - Callback to be called when an nlmsg is received with
+ * the registered cmd_id command from userspace
+ * @data: Payload of the message to be sent to driver
+ * @data_len: Length of the payload
+ * @cb_ctx: callback context to be returned to driver when the callback
+ * is called
+ * @pid: process id of the sender
+ */
+typedef void (*cld80211_cb)(const void *data, int data_len,
+ void *cb_ctx, int pid);
+
+/**
+ * register_cld_cmd_cb() - Allows cld driver to register for commands with
+ * callback
+ * @cmd_id: Command to be registered. Valid range [1, CLD80211_MAX_COMMANDS]
+ * @cb: Callback to be called when an nlmsg is received with cmd_id command
+ * from userspace
+ * @cb_ctx: context provided by driver; Send this as cb_ctx of func()
+ * to driver
+ */
+int register_cld_cmd_cb(u8 cmd_id, cld80211_cb cb, void *cb_ctx);
+
+/**
+ * deregister_cld_cmd_cb() - Allows cld driver to de-register the command it
+ * has already registered
+ * @cmd_id: Command to be deregistered.
+ */
+int deregister_cld_cmd_cb(u8 cmd_id);
+
+/**
+ * cld80211_get_genl_family() - Returns current netlink family context
+ */
+struct genl_family *cld80211_get_genl_family(void);
+
+#endif /* _NET_CNSS_GENETLINK_H_ */
diff --git a/include/net/codel.h b/include/net/codel.h
index aeee280..7302a4d 100644
--- a/include/net/codel.h
+++ b/include/net/codel.h
@@ -158,11 +158,13 @@
* struct codel_stats - contains codel shared variables and stats
* @maxpacket: largest packet we've seen so far
* @drop_count: temp count of dropped packets in dequeue()
+ * @drop_len: bytes of dropped packets in dequeue()
* ecn_mark: number of packets we ECN marked instead of dropping
*/
struct codel_stats {
u32 maxpacket;
u32 drop_count;
+ u32 drop_len;
u32 ecn_mark;
};
@@ -297,6 +299,7 @@
vars->rec_inv_sqrt);
goto end;
}
+ stats->drop_len += qdisc_pkt_len(skb);
qdisc_drop(skb, sch);
stats->drop_count++;
skb = dequeue_func(vars, sch);
@@ -319,6 +322,7 @@
if (params->ecn && INET_ECN_set_ce(skb)) {
stats->ecn_mark++;
} else {
+ stats->drop_len += qdisc_pkt_len(skb);
qdisc_drop(skb, sch);
stats->drop_count++;
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index c10c06f..ea98762 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -8,6 +8,11 @@
#include <net/flow.h>
#include <net/rtnetlink.h>
+struct fib_kuid_range {
+ kuid_t start;
+ kuid_t end;
+};
+
struct fib_rule {
struct list_head list;
int iifindex;
@@ -28,8 +33,7 @@
int suppress_prefixlen;
char iifname[IFNAMSIZ];
char oifname[IFNAMSIZ];
- kuid_t uid_start;
- kuid_t uid_end;
+ struct fib_kuid_range uid_range;
struct rcu_head rcu;
};
@@ -88,13 +92,10 @@
[FRA_FWMARK] = { .type = NLA_U32 }, \
[FRA_FWMASK] = { .type = NLA_U32 }, \
[FRA_TABLE] = { .type = NLA_U32 }, \
- [FRA_GOTO] = { .type = NLA_U32 }, \
- [FRA_UID_START] = { .type = NLA_U32 }, \
- [FRA_UID_END] = { .type = NLA_U32 }, \
[FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \
[FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \
- [FRA_GOTO] = { .type = NLA_U32 }
-
+ [FRA_GOTO] = { .type = NLA_U32 }, \
+ [FRA_UID_RANGE] = { .len = sizeof(struct fib_rule_uid_range) }
static inline void fib_rule_get(struct fib_rule *rule)
{
diff --git a/include/net/flow.h b/include/net/flow.h
index da7743e..546f6d6 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -31,7 +31,7 @@
#define FLOWI_FLAG_ANYSRC 0x01
#define FLOWI_FLAG_KNOWN_NH 0x02
__u32 flowic_secid;
- kuid_t flowic_uid;
+ kuid_t flowic_uid;
};
union flowi_uli {
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index 98e5f95..fdf595a 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -201,6 +201,7 @@
struct ipv6_devstat stats;
struct timer_list rs_timer;
+ __s32 rs_interval; /* in jiffies */
__u8 rs_probes;
__u8 addr_gen_mode;
diff --git a/include/net/ip.h b/include/net/ip.h
index e6943e3..bbf31a1 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -235,6 +235,7 @@
#endif
extern int sysctl_reserved_port_bind;
+__be32 inet_current_timestamp(void);
/* From inetpeer.c */
extern int inet_peer_threshold;
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index d513308..a74faaa 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -110,7 +110,8 @@
void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, int oif,
u32 mark, kuid_t uid);
void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu);
-void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark);
+void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
+ kuid_t uid);
void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
u32 mark);
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk);
diff --git a/include/net/route.h b/include/net/route.h
index af3d4ac0..4c468eb 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -140,8 +140,7 @@
flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos,
RT_SCOPE_UNIVERSE, proto,
sk ? inet_sk_flowi_flags(sk) : 0,
- daddr, saddr, dport, sport,
- sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID);
+ daddr, saddr, dport, sport, sock_net_uid(net, sk));
if (sk)
security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
return ip_route_output_flow(net, fl4, sk);
@@ -251,7 +250,7 @@
flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE,
protocol, flow_flags, dst, src, dport, sport,
- sock_i_uid(sk));
+ sk->sk_uid);
}
static inline struct rtable *ip_route_connect(struct flowi4 *fl4,
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index 5ccfe16..f10b014 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -393,7 +393,8 @@
struct Qdisc *qdisc);
void qdisc_reset(struct Qdisc *qdisc);
void qdisc_destroy(struct Qdisc *qdisc);
-void qdisc_tree_decrease_qlen(struct Qdisc *qdisc, unsigned int n);
+void qdisc_tree_reduce_backlog(struct Qdisc *qdisc, unsigned int n,
+ unsigned int len);
struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue,
const struct Qdisc_ops *ops);
struct Qdisc *qdisc_create_dflt(struct netdev_queue *dev_queue,
@@ -689,6 +690,23 @@
sch->qstats.backlog = 0;
}
+static inline struct Qdisc *qdisc_replace(struct Qdisc *sch, struct Qdisc *new,
+ struct Qdisc **pold)
+{
+ struct Qdisc *old;
+
+ sch_tree_lock(sch);
+ old = *pold;
+ *pold = new;
+ if (old != NULL) {
+ qdisc_tree_reduce_backlog(old, old->q.qlen, old->qstats.backlog);
+ qdisc_reset(old);
+ }
+ sch_tree_unlock(sch);
+
+ return old;
+}
+
static inline unsigned int __qdisc_queue_drop(struct Qdisc *sch,
struct sk_buff_head *list)
{
diff --git a/include/net/sock.h b/include/net/sock.h
index 2a7a1ed..1858562 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -423,6 +423,7 @@
void *sk_security;
#endif
__u32 sk_mark;
+ kuid_t sk_uid;
u32 sk_classid;
struct cg_proto *sk_cgrp;
void (*sk_state_change)(struct sock *sk);
@@ -1737,6 +1738,7 @@
sk->sk_wq = parent->wq;
parent->sk = sk;
sk_set_socket(sk, parent);
+ sk->sk_uid = SOCK_INODE(parent)->i_uid;
security_sock_graft(sk, parent);
write_unlock_bh(&sk->sk_callback_lock);
}
@@ -1744,6 +1746,11 @@
kuid_t sock_i_uid(struct sock *sk);
unsigned long sock_i_ino(struct sock *sk);
+static inline kuid_t sock_net_uid(const struct net *net, const struct sock *sk)
+{
+ return sk ? sk->sk_uid : make_kuid(net->user_ns, 0);
+}
+
static inline struct dst_entry *
__sk_dst_get(struct sock *sk)
{
diff --git a/include/rdma/ib.h b/include/rdma/ib.h
index cf8f9e7..a6b9370 100644
--- a/include/rdma/ib.h
+++ b/include/rdma/ib.h
@@ -34,6 +34,7 @@
#define _RDMA_IB_H
#include <linux/types.h>
+#include <linux/sched.h>
struct ib_addr {
union {
@@ -86,4 +87,19 @@
__u64 sib_scope_id;
};
+/*
+ * The IB interfaces that use write() as bi-directional ioctl() are
+ * fundamentally unsafe, since there are lots of ways to trigger "write()"
+ * calls from various contexts with elevated privileges. That includes the
+ * traditional suid executable error message writes, but also various kernel
+ * interfaces that can write to file descriptors.
+ *
+ * This function provides protection for the legacy API by restricting the
+ * calling context.
+ */
+static inline bool ib_safe_file_access(struct file *filp)
+{
+ return filp->f_cred == current_cred() && segment_eq(get_fs(), USER_DS);
+}
+
#endif /* _RDMA_IB_H */
diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h
index ce55906..2ecac20 100644
--- a/include/rdma/ib_addr.h
+++ b/include/rdma/ib_addr.h
@@ -184,10 +184,12 @@
dev = dev_get_by_index(&init_net, dev_addr->bound_dev_if);
if (dev) {
- ip4 = (struct in_device *)dev->ip_ptr;
- if (ip4 && ip4->ifa_list && ip4->ifa_list->ifa_address)
+ ip4 = in_dev_get(dev);
+ if (ip4 && ip4->ifa_list && ip4->ifa_list->ifa_address) {
ipv6_addr_set_v4mapped(ip4->ifa_list->ifa_address,
(struct in6_addr *)gid);
+ in_dev_put(ip4);
+ }
dev_put(dev);
}
}
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index 728c9ad..5cea174 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -196,6 +196,7 @@
struct iscsi_task *task; /* xmit task in progress */
/* xmit */
+ spinlock_t taskqueuelock; /* protects the next three lists */
struct list_head mgmtqueue; /* mgmt (control) xmit queue */
struct list_head cmdqueue; /* data-path cmd queue */
struct list_head requeue; /* tasks needing another run */
diff --git a/include/trace/events/critical.h b/include/trace/events/critical.h
new file mode 100644
index 0000000..bfd58dd
--- /dev/null
+++ b/include/trace/events/critical.h
@@ -0,0 +1,55 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM critical
+
+#if !defined(_TRACE_CRITICAL_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_CRITICAL_H
+
+#include <linux/ktime.h>
+#include <linux/tracepoint.h>
+#include <linux/string.h>
+
+TRACE_EVENT(critical_start,
+
+ TP_PROTO(unsigned long ip, unsigned long parent_ip),
+
+ TP_ARGS(ip, parent_ip),
+
+ TP_STRUCT__entry(
+ __array(char, func, 16)
+ __array(char, parent, 16)
+ ),
+
+ TP_fast_assign(
+ snprintf(__entry->func, 16, "%pf", (void *)ip);
+ snprintf(__entry->parent, 16, "%pf", (void *)parent_ip);
+ ),
+
+ TP_printk("caller=%s parent=%s\n",
+ __entry->func,
+ __entry->parent)
+);
+
+TRACE_EVENT(critical_end,
+
+ TP_PROTO(unsigned long ip, unsigned long parent_ip),
+
+ TP_ARGS(ip, parent_ip),
+
+ TP_STRUCT__entry(
+ __array(char, func, 16)
+ __array(char, parent, 16)
+ ),
+
+ TP_fast_assign(
+ snprintf(__entry->func, 16, "%pf", (void *)ip);
+ snprintf(__entry->parent, 16, "%pf", (void *)parent_ip);
+ ),
+
+ TP_printk("caller=%s parent=%s\n",
+ __entry->func,
+ __entry->parent)
+);
+#endif /* _TRACE_CRITICAL_H */
+
+/* This part ust be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index 0ec2917..2dff88b 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -55,9 +55,9 @@
*/
DECLARE_EVENT_CLASS(sched_wakeup_template,
- TP_PROTO(struct task_struct *p, int success),
+ TP_PROTO(struct task_struct *p),
- TP_ARGS(__perf_task(p), success),
+ TP_ARGS(__perf_task(p)),
TP_STRUCT__entry(
__array( char, comm, TASK_COMM_LEN )
@@ -71,25 +71,37 @@
memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
__entry->pid = p->pid;
__entry->prio = p->prio;
- __entry->success = success;
+ __entry->success = 1; /* rudiment, kill when possible */
__entry->target_cpu = task_cpu(p);
),
- TP_printk("comm=%s pid=%d prio=%d success=%d target_cpu=%03d",
+ TP_printk("comm=%s pid=%d prio=%d target_cpu=%03d",
__entry->comm, __entry->pid, __entry->prio,
- __entry->success, __entry->target_cpu)
+ __entry->target_cpu)
);
+/*
+ * Tracepoint called when waking a task; this tracepoint is guaranteed to be
+ * called from the waking context.
+ */
+DEFINE_EVENT(sched_wakeup_template, sched_waking,
+ TP_PROTO(struct task_struct *p),
+ TP_ARGS(p));
+
+/*
+ * Tracepoint called when the task is actually woken; p->state == TASK_RUNNNG.
+ * It it not always called from the waking context.
+ */
DEFINE_EVENT(sched_wakeup_template, sched_wakeup,
- TP_PROTO(struct task_struct *p, int success),
- TP_ARGS(p, success));
+ TP_PROTO(struct task_struct *p),
+ TP_ARGS(p));
/*
* Tracepoint for waking up a new task:
*/
DEFINE_EVENT(sched_wakeup_template, sched_wakeup_new,
- TP_PROTO(struct task_struct *p, int success),
- TP_ARGS(p, success));
+ TP_PROTO(struct task_struct *p),
+ TP_ARGS(p));
#ifdef CREATE_TRACE_POINTS
static inline long __trace_sched_switch_state(struct task_struct *p)
@@ -830,58 +842,130 @@
/*
* Tracepoint for accounting sched group energy
*/
+
TRACE_EVENT(sched_energy_diff,
- TP_PROTO(struct task_struct *tsk, int scpu, int dcpu, int udelta,
- int nrgb, int nrga, int nrgd, int capb, int capa, int capd,
- int nrgn, int nrgp),
+ TP_PROTO(struct energy_env *eenv),
- TP_ARGS(tsk, scpu, dcpu, udelta,
- nrgb, nrga, nrgd, capb, capa, capd,
- nrgn, nrgp),
+ TP_ARGS(eenv),
TP_STRUCT__entry(
__array( char, comm, TASK_COMM_LEN )
__field( pid_t, pid )
- __field( int, scpu )
- __field( int, dcpu )
- __field( int, udelta )
- __field( int, nrgb )
- __field( int, nrga )
- __field( int, nrgd )
- __field( int, capb )
- __field( int, capa )
- __field( int, capd )
- __field( int, nrgn )
- __field( int, nrgp )
+ __field( int, s_cpu )
+ __field( int, d_cpu )
+ __field( int, utl_d )
+
+ __field( int, cap_b )
+ __field( int, nrg_b )
+ __field( int, prf_b )
+
+ __field( int, cap_a )
+ __field( int, nrg_a )
+ __field( int, prf_a )
+
+ __field( int, nrg_d )
+ __field( int, prf_d )
),
TP_fast_assign(
- memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
- __entry->pid = tsk->pid;
- __entry->scpu = scpu;
- __entry->dcpu = dcpu;
- __entry->udelta = udelta;
- __entry->nrgb = nrgb;
- __entry->nrga = nrga;
- __entry->nrgd = nrgd;
- __entry->capb = capb;
- __entry->capa = capa;
- __entry->capd = capd;
- __entry->nrgn = nrgn;
- __entry->nrgp = nrgp;
+ memcpy(__entry->comm, eenv->task->comm, TASK_COMM_LEN);
+ __entry->pid = eenv->task->pid;
+ __entry->s_cpu = eenv->src_cpu;
+ __entry->d_cpu = eenv->dst_cpu;
+ __entry->utl_d = eenv->util_delta;
+
+ __entry->cap_b = eenv->before.capacity;
+ __entry->nrg_b = eenv->before.energy;
+ __entry->prf_b = eenv->before.perf_idx;
+
+ __entry->cap_a = eenv->after.capacity;
+ __entry->nrg_a = eenv->after.energy;
+ __entry->prf_a = eenv->after.perf_idx;
+
+ __entry->nrg_d = eenv->nrg_delta;
+ __entry->prf_d = eenv->prf_delta;
),
- TP_printk("pid=%d comm=%s "
- "src_cpu=%d dst_cpu=%d usage_delta=%d "
- "nrg_before=%d nrg_after=%d nrg_diff=%d "
- "cap_before=%d cap_after=%d cap_delta=%d "
- "nrg_delta=%d nrg_payoff=%d",
- __entry->pid, __entry->comm,
- __entry->scpu, __entry->dcpu, __entry->udelta,
- __entry->nrgb, __entry->nrga, __entry->nrgd,
- __entry->capb, __entry->capa, __entry->capd,
- __entry->nrgn, __entry->nrgp)
+ TP_printk("pid=%d comm=%s s_cpu=%d d_cpu=%d utl_d=%d "
+ "cap_b=%d cap_a=%d "
+ "nrg_b=%u nrg_a=%u nrg_d=%d "
+ "prf_b=%u prf_a=%u prf_d=%d",
+ __entry->pid, __entry->comm,
+ __entry->s_cpu, __entry->d_cpu,
+ __entry->utl_d,
+ __entry->cap_b, __entry->cap_a,
+ __entry->nrg_b, __entry->nrg_a,
+ __entry->nrg_d,
+ __entry->prf_b, __entry->prf_a,
+ __entry->prf_d)
+
+);
+
+TRACE_EVENT(sched_energy_perf_deltas,
+
+
+ TP_PROTO(struct energy_env *eenv),
+
+ TP_ARGS(eenv),
+
+ TP_STRUCT__entry(
+ __array( char, comm, TASK_COMM_LEN )
+ __field( pid_t, pid )
+ __field( int, s_cpu )
+ __field( int, d_cpu )
+ __field( int, utl_d )
+
+ __field( unsigned, cap_b )
+ __field( unsigned, cpu_b )
+ __field( unsigned, spi_b )
+ __field( unsigned, dli_b )
+
+ __field( unsigned, cap_a )
+ __field( unsigned, cpu_a )
+ __field( unsigned, spi_a )
+ __field( unsigned, dli_a )
+
+ __field( int, nrg_d )
+ __field( int, prf_d )
+ ),
+
+ TP_fast_assign(
+ memcpy(__entry->comm, eenv->task->comm, TASK_COMM_LEN);
+ __entry->pid = eenv->task->pid;
+ __entry->s_cpu = eenv->src_cpu;
+ __entry->d_cpu = eenv->dst_cpu;
+ __entry->utl_d = eenv->util_delta;
+
+ __entry->cap_b = eenv->before.capacity;
+ __entry->cpu_b = eenv->before.utilization;
+ __entry->spi_b = eenv->before.speedup_idx;
+ __entry->dli_b = eenv->before.delay_idx;
+
+ __entry->cap_a = eenv->after.capacity;
+ __entry->cpu_a = eenv->after.utilization;
+ __entry->spi_a = eenv->after.speedup_idx;
+ __entry->dli_a = eenv->after.delay_idx;
+
+ __entry->prf_d = eenv->prf_delta;
+ __entry->nrg_d = eenv->nrg_delta;
+ ),
+
+ TP_printk("pid=%d comm=%s s_cpu=%d d_cpu=%d utl_d=%d "
+ "cap_b=%u cap_a=%u "
+ "cpu_b=%u cpu_a=%u "
+ "spi_b=%u spi_a=%u "
+ "dli_b=%u dli_a=%u "
+ "prf_d=%d nrg_d=%d",
+ __entry->pid, __entry->comm,
+ __entry->s_cpu, __entry->d_cpu,
+ __entry->utl_d,
+ __entry->cap_b, __entry->cap_a,
+ __entry->cpu_b, __entry->cpu_a,
+ __entry->spi_b, __entry->spi_a,
+ __entry->dli_b, __entry->dli_a,
+ __entry->prf_d, __entry->nrg_d)
+
);
/*
@@ -943,49 +1027,49 @@
#ifdef CONFIG_SCHED_WALT
struct rq;
+extern unsigned int sched_ravg_window;
TRACE_EVENT(walt_update_task_ravg,
TP_PROTO(struct task_struct *p, struct rq *rq, int evt,
- u64 wallclock, u64 irqtime),
+ u64 wallclock, u64 irqtime),
TP_ARGS(p, rq, evt, wallclock, irqtime),
TP_STRUCT__entry(
- __array( char, comm, TASK_COMM_LEN )
- __field( pid_t, pid )
- __field( pid_t, cur_pid )
+ __array(char, comm, TASK_COMM_LEN )
+ __field(pid_t, pid )
+ __field(pid_t, cur_pid )
__field(unsigned int, cur_freq )
- __field( u64, wallclock )
- __field( u64, mark_start )
- __field( u64, delta_m )
- __field( u64, win_start )
- __field( u64, delta )
- __field( u64, irqtime )
- __field( int, evt )
+ __field(u64, wallclock )
+ __field(u64, mark_start )
+ __field(u64, win_start )
+ __field(u64, irqtime )
+ __field(int, evt )
__field(unsigned int, demand )
__field(unsigned int, sum )
- __field( int, cpu )
- __field( u64, cs )
- __field( u64, ps )
- __field( u32, curr_window )
- __field( u32, prev_window )
- __field( u32, active_windows )
+ __field(unsigned int, walt_avg )
+ __field(int, cpu )
+ __field(u64, cs )
+ __field(u64, ps )
+ __field(u32, curr_window )
+ __field(u32, prev_window )
+ __field(u32, active_windows )
),
TP_fast_assign(
__entry->wallclock = wallclock;
__entry->win_start = rq->window_start;
- __entry->delta = (wallclock - rq->window_start);
__entry->evt = evt;
__entry->cpu = rq->cpu;
__entry->cur_pid = rq->curr->pid;
__entry->cur_freq = rq->cur_freq;
- memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
+ memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
__entry->pid = p->pid;
__entry->mark_start = p->ravg.mark_start;
- __entry->delta_m = (wallclock - p->ravg.mark_start);
__entry->demand = p->ravg.demand;
__entry->sum = p->ravg.sum;
+ __entry->walt_avg = p->ravg.demand << 10;
+ do_div(__entry->walt_avg, walt_ravg_window);
__entry->irqtime = irqtime;
__entry->cs = rq->curr_runnable_sum;
__entry->ps = rq->prev_runnable_sum;
@@ -994,17 +1078,20 @@
__entry->active_windows = p->ravg.active_windows;
),
- TP_printk("wc %llu ws %llu delta %llu event %d cpu %d cur_freq %u cur_pid %d task %d (%s) ms %llu delta %llu demand %u sum %u irqtime %llu"
- " cs %llu ps %llu cur_window %u prev_window %u active_wins %u"
- , __entry->wallclock, __entry->win_start, __entry->delta,
+ TP_printk("wclock=%llu win_start=%llu event=%d cpu=%d "
+ "cur_freq=%u cur_pid=%d pid=%d comm=%s mrk_start=%llu "
+ "demand=%u sum=%u walt_avg=%u irqtime=%llu "
+ "cur_rsum=%llu pre_rsum=%llu "
+ "cur_wdw=%u pre_wdw=%u act_wds=%u",
+ __entry->wallclock, __entry->win_start,
__entry->evt, __entry->cpu,
__entry->cur_freq, __entry->cur_pid,
__entry->pid, __entry->comm, __entry->mark_start,
- __entry->delta_m, __entry->demand,
- __entry->sum, __entry->irqtime,
+ __entry->demand, __entry->sum,
+ __entry->walt_avg, __entry->irqtime,
__entry->cs, __entry->ps,
__entry->curr_window, __entry->prev_window,
- __entry->active_windows
+ __entry->active_windows
)
);
@@ -1016,34 +1103,35 @@
TP_ARGS(rq, p, runtime, samples, evt),
TP_STRUCT__entry(
- __array( char, comm, TASK_COMM_LEN )
- __field( pid_t, pid )
+ __array(char, comm, TASK_COMM_LEN )
+ __field(pid_t, pid )
__field(unsigned int, runtime )
- __field( int, samples )
- __field( int, evt )
- __field( u64, demand )
+ __field(int, samples )
+ __field(int, evt )
+ __field(u64, demand )
__field(unsigned int, walt_avg )
__field(unsigned int, pelt_avg )
- __array( u32, hist, RAVG_HIST_SIZE_MAX)
- __field( int, cpu )
+ __array(u32, hist, RAVG_HIST_SIZE_MAX)
+ __field(int, cpu )
),
TP_fast_assign(
- memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
+ memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
__entry->pid = p->pid;
__entry->runtime = runtime;
__entry->samples = samples;
__entry->evt = evt;
__entry->demand = p->ravg.demand;
- __entry->walt_avg = (__entry->demand << 10) / walt_ravg_window,
+ __entry->walt_avg = p->ravg.demand << 10;
+ do_div(__entry->walt_avg, walt_ravg_window);
__entry->pelt_avg = p->se.avg.util_avg;
- memcpy(__entry->hist, p->ravg.sum_history,
- RAVG_HIST_SIZE_MAX * sizeof(u32));
+ memcpy(__entry->hist, p->ravg.sum_history,
+ RAVG_HIST_SIZE_MAX * sizeof(u32));
__entry->cpu = rq->cpu;
),
- TP_printk("%d (%s): runtime %u samples %d event %d demand %llu"
- " walt %u pelt %u (hist: %u %u %u %u %u) cpu %d",
+ TP_printk("pid=%d comm=%s runtime=%u samples=%d event=%d demand=%llu "
+ "walt=%u pelt=%u h0=%u h1=%u h2=%u h3=%u h4=%u cpu=%d",
__entry->pid, __entry->comm,
__entry->runtime, __entry->samples, __entry->evt,
__entry->demand,
@@ -1062,9 +1150,10 @@
TP_STRUCT__entry(
__field(int, cpu )
+ __array(char, comm, TASK_COMM_LEN )
__field(int, pid )
- __field( u64, cs )
- __field( u64, ps )
+ __field(u64, cs )
+ __field(u64, ps )
),
TP_fast_assign(
@@ -1072,11 +1161,13 @@
__entry->cs = rq->curr_runnable_sum;
__entry->ps = rq->prev_runnable_sum;
__entry->pid = p->pid;
+ memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
),
- TP_printk("cpu %d: cs %llu ps %llu pid %d",
- __entry->cpu, __entry->cs, __entry->ps,
- __entry->pid)
+ TP_printk("cpu=%d pid=%d comm=%s "
+ "cur_ravg=%llu pre_ravg=%llu",
+ __entry->cpu, __entry->pid, __entry->comm,
+ __entry->cs, __entry->ps)
);
#endif /* CONFIG_SCHED_WALT */
diff --git a/include/trace/events/syscalls.h b/include/trace/events/syscalls.h
index 14e49c7..b35533b 100644
--- a/include/trace/events/syscalls.h
+++ b/include/trace/events/syscalls.h
@@ -1,5 +1,6 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM raw_syscalls
+#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE syscalls
#if !defined(_TRACE_EVENTS_SYSCALLS_H) || defined(TRACE_HEADER_MULTI_READ)
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h
index 8705d57..ea2e9f1 100644
--- a/include/trace/ftrace.h
+++ b/include/trace/ftrace.h
@@ -775,7 +775,7 @@
struct ftrace_event_call *event_call = __data; \
struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\
struct ftrace_raw_##call *entry; \
- struct pt_regs __regs; \
+ struct pt_regs *__regs; \
u64 __addr = 0, __count = 1; \
struct task_struct *__task = NULL; \
struct hlist_head *head; \
@@ -794,18 +794,19 @@
sizeof(u64)); \
__entry_size -= sizeof(u32); \
\
- perf_fetch_caller_regs(&__regs); \
entry = perf_trace_buf_prepare(__entry_size, \
event_call->event.type, &__regs, &rctx); \
if (!entry) \
return; \
\
+ perf_fetch_caller_regs(__regs); \
+ \
tstruct \
\
{ assign; } \
\
perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \
- __count, &__regs, head, __task); \
+ __count, __regs, head, __task); \
}
/*
diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h
index 743e300..b1385da 100644
--- a/include/uapi/linux/fib_rules.h
+++ b/include/uapi/linux/fib_rules.h
@@ -29,6 +29,11 @@
__u32 flags;
};
+struct fib_rule_uid_range {
+ __u32 start;
+ __u32 end;
+};
+
enum {
FRA_UNSPEC,
FRA_DST, /* destination address */
@@ -49,8 +54,9 @@
FRA_TABLE, /* Extended table id */
FRA_FWMASK, /* mask for netfilter mark */
FRA_OIFNAME,
- FRA_UID_START, /* UID range */
- FRA_UID_END,
+ FRA_PAD,
+ FRA_L3MDEV, /* iif or oif is l3mdev goto its table */
+ FRA_UID_RANGE, /* UID range */
__FRA_MAX
};
diff --git a/include/uapi/linux/hw_breakpoint.h b/include/uapi/linux/hw_breakpoint.h
index b04000a..2b65efd 100644
--- a/include/uapi/linux/hw_breakpoint.h
+++ b/include/uapi/linux/hw_breakpoint.h
@@ -4,7 +4,11 @@
enum {
HW_BREAKPOINT_LEN_1 = 1,
HW_BREAKPOINT_LEN_2 = 2,
+ HW_BREAKPOINT_LEN_3 = 3,
HW_BREAKPOINT_LEN_4 = 4,
+ HW_BREAKPOINT_LEN_5 = 5,
+ HW_BREAKPOINT_LEN_6 = 6,
+ HW_BREAKPOINT_LEN_7 = 7,
HW_BREAKPOINT_LEN_8 = 8,
};
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index fe5c517..b894231 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -167,7 +167,21 @@
DEVCONF_ACCEPT_RA_FROM_LOCAL,
DEVCONF_USE_OPTIMISTIC,
DEVCONF_ACCEPT_RA_PREFIX_ROUTE,
+ DEVCONF_ACCEPT_RA_MTU,
+ DEVCONF_STABLE_SECRET,
DEVCONF_USE_OIF_ADDRS_ONLY,
+ DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT,
+ DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN,
+ DEVCONF_DROP_UNICAST_IN_L2_MULTICAST,
+ DEVCONF_DROP_UNSOLICITED_NA,
+ DEVCONF_KEEP_ADDR_ON_DOWN,
+ DEVCONF_RTR_SOLICIT_MAX_INTERVAL,
+ DEVCONF_SEG6_ENABLED,
+ DEVCONF_SEG6_REQUIRE_HMAC,
+ DEVCONF_ENHANCED_DAD,
+ DEVCONF_ADDR_GEN_MODE,
+ DEVCONF_DISABLE_POLICY,
+ DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN,
DEVCONF_MAX
};
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index 4c38b5f..fc8c9a4 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -52,7 +52,7 @@
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
-#define SDCARDFS_SUPER_MAGIC 0xb550ca10
+#define SDCARDFS_SUPER_MAGIC 0x5dca2df5
#define SMB_SUPER_MAGIC 0x517B
#define CGROUP_SUPER_MAGIC 0x27e0eb
@@ -60,6 +60,8 @@
#define STACK_END_MAGIC 0x57AC6E9D
+#define TRACEFS_MAGIC 0x74726163
+
#define V9FS_MAGIC 0x01021997
#define BDEVFS_MAGIC 0x62646576
diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index de33c60..578e813 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -105,8 +105,10 @@
#define NETLINK_PKTINFO 3
#define NETLINK_BROADCAST_ERROR 4
#define NETLINK_NO_ENOBUFS 5
+#ifndef __KERNEL__
#define NETLINK_RX_RING 6
#define NETLINK_TX_RING 7
+#endif
struct nl_pktinfo {
__u32 group;
@@ -129,6 +131,7 @@
__u32 nm_gid;
};
+#ifndef __KERNEL__
enum nl_mmap_status {
NL_MMAP_STATUS_UNUSED,
NL_MMAP_STATUS_RESERVED,
@@ -140,6 +143,7 @@
#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO
#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
+#endif
#define NET_MAJOR 36 /* Major 36 is reserved for networking */
diff --git a/include/uapi/linux/netlink_diag.h b/include/uapi/linux/netlink_diag.h
index f2159d3..d7939939 100644
--- a/include/uapi/linux/netlink_diag.h
+++ b/include/uapi/linux/netlink_diag.h
@@ -48,6 +48,8 @@
#define NDIAG_SHOW_MEMINFO 0x00000001 /* show memory info of a socket */
#define NDIAG_SHOW_GROUPS 0x00000002 /* show groups of a netlink socket */
+#ifndef __KERNEL__
#define NDIAG_SHOW_RING_CFG 0x00000004 /* show ring configuration */
+#endif
#endif
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index bd70c24..e4086c8 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -10,6 +10,7 @@
* Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
* Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
* Copyright 2008 Colin McCabe <colin@cozybit.com>
+ * Copyright 2015 Intel Deutschland GmbH
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -290,7 +291,7 @@
* @NL80211_CMD_GET_SCAN: get scan results
* @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
* %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
- * probe requests at CCK rate or not. %NL80211_ATTR_MAC can be used to
+ * probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to
* specify a BSSID to scan for; if not included, the wildcard BSSID will
* be used.
* @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
@@ -299,7 +300,15 @@
* partial scan results may be available
*
* @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain
- * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL.
+ * intervals and certain number of cycles, as specified by
+ * %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+ * not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+ * scheduled scan will run in an infinite loop with the specified
+ * interval. These attributes are mutually exculsive,
+ * i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+ * NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+ * If for some reason scheduled scan is aborted by the driver, all scan
+ * plans are canceled (including scan plans that did not start yet).
* Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
* are passed, they are used in the probe requests. For
* broadcast, a broadcast SSID must be passed (ie. an empty
@@ -444,7 +453,12 @@
* This attribute is ignored if driver does not support roam scan.
* It is also sent as an event, with the BSSID and response IEs when the
* connection is established or failed to be established. This can be
- * determined by the STATUS_CODE attribute.
+ * determined by the %NL80211_ATTR_STATUS_CODE attribute (0 = success,
+ * non-zero = failure). If %NL80211_ATTR_TIMED_OUT is included in the
+ * event, the connection attempt failed due to not being able to initiate
+ * authentication/association or not receiving a response from the AP.
+ * Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as
+ * well to remain backwards compatible.
* @NL80211_CMD_ROAM: request that the card roam (currently not implemented),
* sent as an event when the card/driver roamed by itself.
* @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
@@ -782,6 +796,47 @@
* not running. The driver indicates the status of the scan through
* cfg80211_scan_done().
*
+ * @NL80211_CMD_START_NAN: Start NAN operation, identified by its
+ * %NL80211_ATTR_WDEV interface. This interface must have been previously
+ * created with %NL80211_CMD_NEW_INTERFACE. After it has been started, the
+ * NAN interface will create or join a cluster. This command must have a
+ * valid %NL80211_ATTR_NAN_MASTER_PREF attribute and optional
+ * %NL80211_ATTR_NAN_DUAL attributes.
+ * After this command NAN functions can be added.
+ * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by
+ * its %NL80211_ATTR_WDEV interface.
+ * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined
+ * with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this
+ * operation returns the strictly positive and unique instance id
+ * (%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE)
+ * of the function upon success.
+ * Since instance ID's can be re-used, this cookie is the right
+ * way to identify the function. This will avoid races when a termination
+ * event is handled by the user space after it has already added a new
+ * function that got the same instance id from the kernel as the one
+ * which just terminated.
+ * This cookie may be used in NAN events even before the command
+ * returns, so userspace shouldn't process NAN events until it processes
+ * the response to this command.
+ * Look at %NL80211_ATTR_SOCKET_OWNER as well.
+ * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie.
+ * This command is also used as a notification sent when a NAN function is
+ * terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID
+ * and %NL80211_ATTR_COOKIE attributes.
+ * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN configuration. NAN
+ * must be operational (%NL80211_CMD_START_NAN was executed).
+ * It must contain at least one of the following attributes:
+ * %NL80211_ATTR_NAN_MASTER_PREF, %NL80211_ATTR_NAN_DUAL.
+ * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
+ * This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+ * %NL80211_ATTR_COOKIE.
+ *
+ * @NL80211_CMD_UPDATE_CONNECT_PARAMS: Update one or more connect parameters
+ * for subsequent roaming cases if the driver or firmware uses internal
+ * BSS selection. This command can be issued only while connected and it
+ * does not result in a change for the current association. Currently,
+ * only the %NL80211_ATTR_IE data is used and updated with this command.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -970,6 +1025,17 @@
NL80211_CMD_ABORT_SCAN,
+ NL80211_CMD_START_NAN,
+ NL80211_CMD_STOP_NAN,
+ NL80211_CMD_ADD_NAN_FUNCTION,
+ NL80211_CMD_DEL_NAN_FUNCTION,
+ NL80211_CMD_CHANGE_NAN_CONFIG,
+ NL80211_CMD_NAN_MATCH,
+
+ NL80211_CMD_SET_MULTICAST_TO_UNICAST,
+
+ NL80211_CMD_UPDATE_CONNECT_PARAMS,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1284,7 +1350,13 @@
* enum nl80211_band value is used as the index (nla_type() of the nested
* data. If a band is not included, it will be configured to allow all
* rates based on negotiated supported rates information. This attribute
- * is used with %NL80211_CMD_SET_TX_BITRATE_MASK.
+ * is used with %NL80211_CMD_SET_TX_BITRATE_MASK and with starting AP,
+ * and joining mesh networks (not IBSS yet). In the later case, it must
+ * specify just a single bitrate, which is to be used for the beacon.
+ * The driver must also specify support for this with the extended
+ * features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ * NL80211_EXT_FEATURE_BEACON_RATE_HT and
+ * NL80211_EXT_FEATURE_BEACON_RATE_VHT.
*
* @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
* at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
@@ -1699,10 +1771,148 @@
* @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see
* &enum nl80211_smps_mode.
*
+ * @NL80211_ATTR_OPER_CLASS: operating class
+ *
+ * @NL80211_ATTR_MAC_MASK: MAC address mask
+ *
+ * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device
+ * is self-managing its regulatory information and any regulatory domain
+ * obtained from it is coming from the device's wiphy and not the global
+ * cfg80211 regdomain.
+ *
+ * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte
+ * array. The feature flags are identified by their bit index (see &enum
+ * nl80211_ext_feature_index). The bit index is ordered starting at the
+ * least-significant bit of the first byte in the array, ie. bit index 0
+ * is located at bit 0 of byte 0. bit index 25 would be located at bit 1
+ * of byte 3 (u8 array).
+ *
+ * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be
+ * returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY
+ * may return a survey entry without a channel indicating global radio
+ * statistics (only some values are valid and make sense.)
+ * For devices that don't return such an entry even then, the information
+ * should be contained in the result as the sum of the respective counters
+ * over all channels.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
+ * scheduled scan is started. Or the delay before a WoWLAN
+ * net-detect scan is started, counting from the moment the
+ * system is suspended. This value is a u32, in seconds.
+
+ * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+ * is operating in an indoor environment.
+ *
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for
+ * scheduled scan supported by the device (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for
+ * a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in
+ * a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan.
+ * Each scan plan defines the number of scan iterations and the interval
+ * between scans. The last scan plan will always run infinitely,
+ * thus it must not specify the number of iterations, only the interval
+ * between scans. The scan plans are executed sequentially.
+ * Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
* @NL80211_ATTR_PBSS: flag attribute. If set it means operate
* in a PBSS. Specified in %NL80211_CMD_CONNECT to request
* connecting to a PCP, and in %NL80211_CMD_START_AP to start
* a PCP instead of AP. Relevant for DMG networks only.
+ * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the
+ * BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains
+ * attributes according &enum nl80211_bss_select_attr to indicate what
+ * BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT
+ * it contains the behaviour-specific attribute containing the parameters for
+ * BSS selection to be done by driver and/or firmware.
+ *
+ * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported
+ * or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status
+ *
+ * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment
+ *
+ * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
+ * %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
+ * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per
+ * interface type.
+ *
+ * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO
+ * groupID for monitor mode.
+ * The first 8 bytes are a mask that defines the membership in each
+ * group (there are 64 groups, group 0 and 63 are reserved),
+ * each bit represents a group and set to 1 for being a member in
+ * that group and 0 for not being a member.
+ * The remaining 16 bytes define the position in each group: 2 bits for
+ * each group.
+ * (smaller group numbers represented on most significant bits and bigger
+ * group numbers on least significant bits.)
+ * This attribute is used only if all interfaces are in monitor mode.
+ * Set this attribute in order to monitor packets using the given MU-MIMO
+ * groupID data.
+ * to turn off that feature set all the bits of the groupID to zero.
+ * @NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR: mac address for the sniffer to follow
+ * when using MU-MIMO air sniffer.
+ * to turn that feature off set an invalid mac address
+ * (e.g. FF:FF:FF:FF:FF:FF)
+ *
+ * @NL80211_ATTR_SCAN_START_TIME_TSF: The time at which the scan was actually
+ * started (u64). The time is the TSF of the BSS the interface that
+ * requested the scan is connected to (if available, otherwise this
+ * attribute must not be included).
+ * @NL80211_ATTR_SCAN_START_TIME_TSF_BSSID: The BSS according to which
+ * %NL80211_ATTR_SCAN_START_TIME_TSF is set.
+ * @NL80211_ATTR_MEASUREMENT_DURATION: measurement duration in TUs (u16). If
+ * %NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY is not set, this is the
+ * maximum measurement duration allowed. This attribute is used with
+ * measurement requests. It can also be used with %NL80211_CMD_TRIGGER_SCAN
+ * if the scan is used for beacon report radio measurement.
+ * @NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY: flag attribute that indicates
+ * that the duration specified with %NL80211_ATTR_MEASUREMENT_DURATION is
+ * mandatory. If this flag is not set, the duration is the maximum duration
+ * and the actual measurement duration may be shorter.
+ *
+ * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is
+ * used to pull the stored data for mesh peer in power save state.
+ *
+ * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by
+ * %NL80211_CMD_START_NAN and optionally with
+ * %NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0.
+ * Also, values 1 and 255 are reserved for certification purposes and
+ * should not be used during a normal device operation.
+ * @NL80211_ATTR_NAN_DUAL: NAN dual band operation config (see
+ * &enum nl80211_nan_dual_band_conf). This attribute is used with
+ * %NL80211_CMD_START_NAN and optionally with
+ * %NL80211_CMD_CHANGE_NAN_CONFIG.
+ * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See
+ * &enum nl80211_nan_func_attributes for description of this nested
+ * attribute.
+ * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute.
+ * See &enum nl80211_nan_match_attributes.
+ * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame
+ * protection.
+ * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association
+ * Request/Response frame protection. This attribute contains the 16 octet
+ * STA Nonce followed by 16 octets of AP Nonce.
+ *
+ * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast
+ * packets should be send out as unicast to all stations (flag attribute).
+ *
+ * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also
+ * used in various commands/events for specifying the BSSID.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which
+ * other BSSs has to be better or slightly worse than the current
+ * connected BSS so that they get reported to user space.
+ * This will give an opportunity to userspace to consider connecting to
+ * other matching BSSs which have better or slightly worse RSSI than
+ * the current connected BSS by using an offloaded operation to avoid
+ * unnecessary wakeups.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
+ * the specified band is to be adjusted before doing
+ * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out
+ * better BSSs. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2079,6 +2289,39 @@
NL80211_ATTR_PBSS,
+ NL80211_ATTR_BSS_SELECT,
+
+ NL80211_ATTR_STA_SUPPORT_P2P_PS,
+
+ NL80211_ATTR_PAD,
+
+ NL80211_ATTR_IFTYPE_EXT_CAPA,
+
+ NL80211_ATTR_MU_MIMO_GROUP_DATA,
+ NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR,
+
+ NL80211_ATTR_SCAN_START_TIME_TSF,
+ NL80211_ATTR_SCAN_START_TIME_TSF_BSSID,
+ NL80211_ATTR_MEASUREMENT_DURATION,
+ NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY,
+
+ NL80211_ATTR_MESH_PEER_AID,
+
+ NL80211_ATTR_NAN_MASTER_PREF,
+ NL80211_ATTR_NAN_DUAL,
+ NL80211_ATTR_NAN_FUNC,
+ NL80211_ATTR_NAN_MATCH,
+
+ NL80211_ATTR_FILS_KEK,
+ NL80211_ATTR_FILS_NONCES,
+
+ NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED,
+
+ NL80211_ATTR_BSSID,
+
+ NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+ NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -2685,6 +2928,13 @@
* how this API was implemented in the past. Also, due to the same problem,
* the only way to create a matchset with only an RSSI filter (with this
* attribute) is if there's only a single matchset with the RSSI attribute.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether
+ * %NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or
+ * relative to current bss's RSSI.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for
+ * BSS-es in the specified band is to be adjusted before doing
+ * RSSI-based BSS selection. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
* @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
* attribute number currently defined
* @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -2694,6 +2944,8 @@
NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
+ NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
/* keep last */
__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@@ -3227,6 +3479,9 @@
* (not present if no beacon frame has been received yet)
* @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
* @NL80211_BSS_TSF is known to be from a probe response (flag attribute)
+ * @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry
+ * was last updated by a received frame. The value is expected to be
+ * accurate to about 10ms. (u64, nanoseconds)
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -3246,6 +3501,7 @@
NL80211_BSS_CHAN_WIDTH,
NL80211_BSS_BEACON_TSF,
NL80211_BSS_PRESP_DATA,
+ NL80211_BSS_LAST_SEEN_BOOTTIME,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -4131,6 +4387,18 @@
* multiplexing powersave, ie. can turn off all but one chain
* and then wake the rest up as required after, for example,
* rts/cts handshake.
+ * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a
+ * random MAC address during scan (if the device is unassociated); the
+ * %NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC
+ * address mask/value will be used.
+ * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports
+ * using a random MAC address for every scan iteration during scheduled
+ * scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ * be set for scheduled scan and the MAC address mask/value will be used.
+ * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a
+ * random MAC address for every scan iteration during "net detect", i.e.
+ * scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ * be set for scheduled scan and the MAC address mask/value will be used.
*/
enum nl80211_feature_flags {
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
@@ -4159,6 +4427,72 @@
NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23,
NL80211_FEATURE_STATIC_SMPS = 1 << 24,
NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25,
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR = 1 << 29,
+ NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = 1 << 30,
+ NL80211_FEATURE_ND_RANDOM_MAC_ADDR = 1 << 31,
+};
+
+/**
+ * enum nl80211_ext_feature_index - bit index of extended features.
+ * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates.
+ * @NL80211_EXT_FEATURE_RRM: This driver supports RRM. When featured, user can
+ * can request to use RRM (see %NL80211_ATTR_USE_RRM) with
+ * %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set
+ * the ASSOC_REQ_USE_RRM flag in the association request even if
+ * NL80211_FEATURE_QUIET is not advertized.
+ * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air
+ * sniffer which means that it can be configured to hear packets from
+ * certain groups which can be configured by the
+ * %NL80211_ATTR_MU_MIMO_GROUP_DATA attribute,
+ * or can be configured to follow a station by configuring the
+ * %NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR attribute.
+ * @NL80211_EXT_FEATURE_SCAN_START_TIME: This driver includes the actual
+ * time the scan started in scan results event. The time is the TSF of
+ * the BSS that the interface that requested the scan is connected to
+ * (if available).
+ * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the
+ * time the last beacon/probe was received. The time is the TSF of the
+ * BSS that the interface that requested the scan is connected to
+ * (if available).
+ * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
+ * channel dwell time.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+ * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+ * configuration (AP/mesh) with HT rates.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+ * configuration (AP/mesh) with VHT rates.
+ * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup
+ * with user space SME (NL80211_CMD_AUTHENTICATE) in station mode.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA
+ * in @NL80211_CMD_FRAME while not associated.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports
+ * randomized TA in @NL80211_CMD_FRAME while associated.
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
+ * for reporting BSSs with better RSSI than the current connected BSS
+ * (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
+ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+enum nl80211_ext_feature_index {
+ NL80211_EXT_FEATURE_VHT_IBSS,
+ NL80211_EXT_FEATURE_RRM,
+ NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER,
+ NL80211_EXT_FEATURE_SCAN_START_TIME,
+ NL80211_EXT_FEATURE_BSS_PARENT_TSF,
+ NL80211_EXT_FEATURE_SET_SCAN_DWELL,
+ NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ NL80211_EXT_FEATURE_BEACON_RATE_HT,
+ NL80211_EXT_FEATURE_BEACON_RATE_VHT,
+ NL80211_EXT_FEATURE_FILS_STA,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
+ NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
+
+ /* add new features before the definition below */
+ NUM_NL80211_EXT_FEATURES,
+ MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1
};
/**
@@ -4207,11 +4541,21 @@
* dangerous because will destroy stations performance as a lot of frames
* will be lost while scanning off-channel, therefore it must be used only
* when really needed
+ * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or
+ * for scheduled scan: a different one for every scan iteration). When the
+ * flag is set, depending on device capabilities the @NL80211_ATTR_MAC and
+ * @NL80211_ATTR_MAC_MASK attributes may also be given in which case only
+ * the masked bits will be preserved from the MAC address and the remainder
+ * randomised. If the attributes are not given full randomisation (46 bits,
+ * locally administered 1, multicast 0) is assumed.
+ * This flag must not be requested when the feature isn't supported, check
+ * the nl80211 feature flags for the device.
*/
enum nl80211_scan_flags {
NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
NL80211_SCAN_FLAG_FLUSH = 1<<1,
NL80211_SCAN_FLAG_AP = 1<<2,
+ NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
};
/**
@@ -4370,4 +4714,72 @@
NL80211_TDLS_PEER_WMM = 1<<2,
};
+/**
+ * enum nl80211_sched_scan_plan - scanning plan for scheduled scan
+ * @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In
+ * seconds (u32).
+ * @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this
+ * scan plan (u32). The last scan plan must not specify this attribute
+ * because it will run infinitely. A value of zero is invalid as it will
+ * make the scan plan meaningless.
+ * @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number
+ * currently defined
+ * @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_plan {
+ __NL80211_SCHED_SCAN_PLAN_INVALID,
+ NL80211_SCHED_SCAN_PLAN_INTERVAL,
+ NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+
+ /* keep last */
+ __NL80211_SCHED_SCAN_PLAN_AFTER_LAST,
+ NL80211_SCHED_SCAN_PLAN_MAX =
+ __NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
+};
+
+/**
+ * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters.
+ *
+ * @band: band of BSS that must match for RSSI value adjustment.
+ * @delta: value used to adjust the RSSI value of matching BSS.
+ */
+struct nl80211_bss_select_rssi_adjust {
+ __u8 band;
+ __s8 delta;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_bss_select_attr - attributes for bss selection.
+ *
+ * @__NL80211_BSS_SELECT_ATTR_INVALID: reserved.
+ * @NL80211_BSS_SELECT_ATTR_RSSI: Flag indicating only RSSI-based BSS selection
+ * is requested.
+ * @NL80211_BSS_SELECT_ATTR_BAND_PREF: attribute indicating BSS
+ * selection should be done such that the specified band is preferred.
+ * When there are multiple BSS-es in the preferred band, the driver
+ * shall use RSSI-based BSS selection as a second step. The value of
+ * this attribute is according to &enum nl80211_band (u32).
+ * @NL80211_BSS_SELECT_ATTR_RSSI_ADJUST: When present the RSSI level for
+ * BSS-es in the specified band is to be adjusted before doing
+ * RSSI-based BSS selection. The attribute value is a packed structure
+ * value as specified by &struct nl80211_bss_select_rssi_adjust.
+ * @NL80211_BSS_SELECT_ATTR_MAX: highest bss select attribute number.
+ * @__NL80211_BSS_SELECT_ATTR_AFTER_LAST: internal use.
+ *
+ * One and only one of these attributes are found within %NL80211_ATTR_BSS_SELECT
+ * for %NL80211_CMD_CONNECT. It specifies the required BSS selection behaviour
+ * which the driver shall use.
+ */
+enum nl80211_bss_select_attr {
+ __NL80211_BSS_SELECT_ATTR_INVALID,
+ NL80211_BSS_SELECT_ATTR_RSSI,
+ NL80211_BSS_SELECT_ATTR_BAND_PREF,
+ NL80211_BSS_SELECT_ATTR_RSSI_ADJUST,
+
+ /* keep last */
+ __NL80211_BSS_SELECT_ATTR_AFTER_LAST,
+ NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/include/uapi/linux/packet_diag.h b/include/uapi/linux/packet_diag.h
index d08c63f..0c5d5dd 100644
--- a/include/uapi/linux/packet_diag.h
+++ b/include/uapi/linux/packet_diag.h
@@ -64,7 +64,7 @@
__u32 pdmc_count;
__u16 pdmc_type;
__u16 pdmc_alen;
- __u8 pdmc_addr[MAX_ADDR_LEN];
+ __u8 pdmc_addr[32]; /* MAX_ADDR_LEN */
};
struct packet_diag_ring {
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 933ff2a..3fc82ff5 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -188,4 +188,11 @@
#define PR_SET_VMA 0x53564d41
# define PR_SET_VMA_ANON_NAME 0
+/* Control the ambient capability set */
+#define PR_CAP_AMBIENT 47
+# define PR_CAP_AMBIENT_IS_SET 1
+# define PR_CAP_AMBIENT_RAISE 2
+# define PR_CAP_AMBIENT_LOWER 3
+# define PR_CAP_AMBIENT_CLEAR_ALL 4
+
#endif /* _LINUX_PRCTL_H */
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 01757b7..7acf1d6 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -297,6 +297,13 @@
RTA_TABLE,
RTA_MARK,
RTA_MFC_STATS,
+ RTA_VIA,
+ RTA_NEWDST,
+ RTA_PREF,
+ RTA_ENCAP_TYPE,
+ RTA_ENCAP,
+ RTA_EXPIRES,
+ RTA_PAD,
RTA_UID,
__RTA_MAX
};
diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h
index 3d055ef..9780e8a 100644
--- a/include/uapi/linux/sysctl.h
+++ b/include/uapi/linux/sysctl.h
@@ -570,6 +570,7 @@
NET_IPV6_PROXY_NDP=23,
NET_IPV6_ACCEPT_SOURCE_ROUTE=25,
NET_IPV6_ACCEPT_RA_FROM_LOCAL=26,
+ NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27,
__NET_IPV6_MAX
};
diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h
index 178d4ae..9a61306 100644
--- a/include/uapi/linux/usb/functionfs.h
+++ b/include/uapi/linux/usb/functionfs.h
@@ -282,6 +282,11 @@
#define FUNCTIONFS_ENDPOINT_DESC _IOR('g', 130, \
struct usb_endpoint_descriptor)
+/*
+ * Sets a buffer length for which all r/w operations under that size use a
+ * preallocated buffer. Behavior of larger operations does not change.
+ */
+#define FUNCTIONFS_ENDPOINT_ALLOC _IOR('g', 231, __u32)
#endif /* _UAPI__LINUX_FUNCTIONFS_H__ */
diff --git a/include/uapi/linux/v4l2-dv-timings.h b/include/uapi/linux/v4l2-dv-timings.h
index 6c8f159..63467ce 100644
--- a/include/uapi/linux/v4l2-dv-timings.h
+++ b/include/uapi/linux/v4l2-dv-timings.h
@@ -175,70 +175,80 @@
#define V4L2_DV_BT_CEA_3840X2160P24 { \
.type = V4L2_DV_BT_656_1120, \
- V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
297000000, 1276, 88, 296, 8, 10, 72, 0, 0, 0, \
V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
}
#define V4L2_DV_BT_CEA_3840X2160P25 { \
.type = V4L2_DV_BT_656_1120, \
- V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
297000000, 1056, 88, 296, 8, 10, 72, 0, 0, 0, \
V4L2_DV_BT_STD_CEA861, 0) \
}
#define V4L2_DV_BT_CEA_3840X2160P30 { \
.type = V4L2_DV_BT_656_1120, \
- V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
297000000, 176, 88, 296, 8, 10, 72, 0, 0, 0, \
V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
}
#define V4L2_DV_BT_CEA_3840X2160P50 { \
.type = V4L2_DV_BT_656_1120, \
- V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
594000000, 1056, 88, 296, 8, 10, 72, 0, 0, 0, \
V4L2_DV_BT_STD_CEA861, 0) \
}
#define V4L2_DV_BT_CEA_3840X2160P60 { \
.type = V4L2_DV_BT_656_1120, \
- V4L2_INIT_BT_TIMINGS(3840, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ V4L2_INIT_BT_TIMINGS(3840, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
594000000, 176, 88, 296, 8, 10, 72, 0, 0, 0, \
V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
}
#define V4L2_DV_BT_CEA_4096X2160P24 { \
.type = V4L2_DV_BT_656_1120, \
- V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
297000000, 1020, 88, 296, 8, 10, 72, 0, 0, 0, \
V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
}
#define V4L2_DV_BT_CEA_4096X2160P25 { \
.type = V4L2_DV_BT_656_1120, \
- V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
297000000, 968, 88, 128, 8, 10, 72, 0, 0, 0, \
V4L2_DV_BT_STD_CEA861, 0) \
}
#define V4L2_DV_BT_CEA_4096X2160P30 { \
.type = V4L2_DV_BT_656_1120, \
- V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
297000000, 88, 88, 128, 8, 10, 72, 0, 0, 0, \
V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
}
#define V4L2_DV_BT_CEA_4096X2160P50 { \
.type = V4L2_DV_BT_656_1120, \
- V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
594000000, 968, 88, 128, 8, 10, 72, 0, 0, 0, \
V4L2_DV_BT_STD_CEA861, 0) \
}
#define V4L2_DV_BT_CEA_4096X2160P60 { \
.type = V4L2_DV_BT_656_1120, \
- V4L2_INIT_BT_TIMINGS(4096, 2160, 0, V4L2_DV_HSYNC_POS_POL, \
+ V4L2_INIT_BT_TIMINGS(4096, 2160, 0, \
+ V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \
594000000, 88, 88, 128, 8, 10, 72, 0, 0, 0, \
V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \
}
diff --git a/include/uapi/scsi/ufs/ufs.h b/include/uapi/scsi/ufs/ufs.h
index cd82b76..84ef887 100644
--- a/include/uapi/scsi/ufs/ufs.h
+++ b/include/uapi/scsi/ufs/ufs.h
@@ -51,7 +51,7 @@
QUERY_DESC_IDN_RFU_1 = 0x6,
QUERY_DESC_IDN_GEOMETRY = 0x7,
QUERY_DESC_IDN_POWER = 0x8,
- QUERY_DESC_IDN_RFU_2 = 0x9,
+ QUERY_DESC_IDN_HEALTH = 0x9,
QUERY_DESC_IDN_MAX,
};
diff --git a/include/uapi/video/msm_hdmi_hdcp_mgr.h b/include/uapi/video/msm_hdmi_hdcp_mgr.h
index 85fa918..0676072 100644
--- a/include/uapi/video/msm_hdmi_hdcp_mgr.h
+++ b/include/uapi/video/msm_hdmi_hdcp_mgr.h
@@ -1,5 +1,5 @@
#ifndef _UAPI__HDMI_HDCP_MGR_H
-#define _UAPI__MSM_HDMI_HDCP_MGR_H
+#define _UAPI__HDMI_HDCP_MGR_H
enum DS_TYPE { /* type of downstream device */
DS_UNKNOWN,
@@ -51,4 +51,4 @@
uint32_t max_dev_exceeded;
};
-#endif /* _UAPI__MSM_HDMI_HDCP_MGR_H */
+#endif /* _UAPI__HDMI_HDCP_MGR_H */
diff --git a/init/Kconfig b/init/Kconfig
index e6df41e..27fbe0f 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -678,30 +678,32 @@
Say Y here if you are working with real-time apps or heavy loads
Say N here if you are unsure.
-config RCU_BOOST_PRIO
- int "Real-time priority to boost RCU readers to"
- range 1 99
- depends on RCU_BOOST
- default 1
+config RCU_KTHREAD_PRIO
+ int "Real-time priority to use for RCU worker threads"
+ range 1 99 if RCU_BOOST
+ range 0 99 if !RCU_BOOST
+ default 1 if RCU_BOOST
+ default 0 if !RCU_BOOST
help
- This option specifies the real-time priority to which long-term
- preempted RCU readers are to be boosted. If you are working
- with a real-time application that has one or more CPU-bound
- threads running at a real-time priority level, you should set
- RCU_BOOST_PRIO to a priority higher then the highest-priority
- real-time CPU-bound thread. The default RCU_BOOST_PRIO value
- of 1 is appropriate in the common case, which is real-time
+ This option specifies the SCHED_FIFO priority value that will be
+ assigned to the rcuc/n and rcub/n threads and is also the value
+ used for RCU_BOOST (if enabled). If you are working with a
+ real-time application that has one or more CPU-bound threads
+ running at a real-time priority level, you should set
+ RCU_KTHREAD_PRIO to a priority higher than the highest-priority
+ real-time CPU-bound application thread. The default RCU_KTHREAD_PRIO
+ value of 1 is appropriate in the common case, which is real-time
applications that do not have any CPU-bound threads.
Some real-time applications might not have a single real-time
thread that saturates a given CPU, but instead might have
multiple real-time threads that, taken together, fully utilize
- that CPU. In this case, you should set RCU_BOOST_PRIO to
+ that CPU. In this case, you should set RCU_KTHREAD_PRIO to
a priority higher than the lowest-priority thread that is
conspiring to prevent the CPU from running any non-real-time
tasks. For example, if one thread at priority 10 and another
thread at priority 5 are between themselves fully consuming
- the CPU time on a given CPU, then RCU_BOOST_PRIO should be
+ the CPU time on a given CPU, then RCU_KTHREAD_PRIO should be
set to priority 6 or higher.
Specify the real-time priority, or take the default if unsure.
@@ -1737,6 +1739,7 @@
config SLAB
bool "SLAB"
+ select HAVE_HARDENED_USERCOPY_ALLOCATOR
help
The regular slab allocator that is established and known to work
well in all environments. It organizes cache hot objects in
@@ -1744,6 +1747,7 @@
config SLUB
bool "SLUB (Unqueued Allocator)"
+ select HAVE_HARDENED_USERCOPY_ALLOCATOR
help
SLUB is a slab allocator that minimizes cache line usage
instead of managing queues of cached objects (SLAB approach).
diff --git a/init/main.c b/init/main.c
index b3df114b..ab277cd 100644
--- a/init/main.c
+++ b/init/main.c
@@ -95,9 +95,6 @@
extern void init_IRQ(void);
extern void fork_init(unsigned long);
extern void radix_tree_init(void);
-#ifndef CONFIG_DEBUG_RODATA
-static inline void mark_rodata_ro(void) { }
-#endif
/*
* Debug helper: via this flag we know that we are in 'early bootup code'
@@ -578,6 +575,10 @@
local_irq_disable();
idr_init_cache();
rcu_init();
+
+ /* trace_printk() and trace points may be used after this */
+ trace_init();
+
context_tracking_init();
radix_tree_init();
/* init some links before init_ISA_irqs() */
@@ -931,6 +932,28 @@
static noinline void __init kernel_init_freeable(void);
+#ifdef CONFIG_DEBUG_RODATA
+static bool rodata_enabled = true;
+static int __init set_debug_rodata(char *str)
+{
+ return strtobool(str, &rodata_enabled);
+}
+__setup("rodata=", set_debug_rodata);
+
+static void mark_readonly(void)
+{
+ if (rodata_enabled)
+ mark_rodata_ro();
+ else
+ pr_info("Kernel memory protection disabled.\n");
+}
+#else
+static inline void mark_readonly(void)
+{
+ pr_warn("This architecture does not have kernel memory protection.\n");
+}
+#endif
+
static int __ref kernel_init(void *unused)
{
int ret;
@@ -939,7 +962,7 @@
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();
free_initmem();
- mark_rodata_ro();
+ mark_readonly();
system_state = SYSTEM_RUNNING;
numa_default_policy();
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index f65a044..3949468 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -751,7 +751,7 @@
}
mode &= ~current_umask();
- ret = vfs_create(dir, path->dentry, mode, true);
+ ret = vfs_create2(path->mnt, dir, path->dentry, mode, true);
path->dentry->d_fsdata = NULL;
if (ret)
return ERR_PTR(ret);
@@ -767,7 +767,7 @@
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
return ERR_PTR(-EINVAL);
acc = oflag2acc[oflag & O_ACCMODE];
- if (inode_permission(path->dentry->d_inode, acc))
+ if (inode_permission2(path->mnt, path->dentry->d_inode, acc))
return ERR_PTR(-EACCES);
return dentry_open(path, oflag, current_cred());
}
@@ -800,7 +800,7 @@
ro = mnt_want_write(mnt); /* we'll drop it in any case */
error = 0;
mutex_lock(&root->d_inode->i_mutex);
- path.dentry = lookup_one_len(name->name, root, strlen(name->name));
+ path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name));
if (IS_ERR(path.dentry)) {
error = PTR_ERR(path.dentry);
goto out_putfd;
@@ -871,7 +871,7 @@
if (err)
goto out_name;
mutex_lock_nested(&mnt->mnt_root->d_inode->i_mutex, I_MUTEX_PARENT);
- dentry = lookup_one_len(name->name, mnt->mnt_root,
+ dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root,
strlen(name->name));
if (IS_ERR(dentry)) {
err = PTR_ERR(dentry);
@@ -883,7 +883,7 @@
err = -ENOENT;
} else {
ihold(inode);
- err = vfs_unlink(dentry->d_parent->d_inode, dentry, NULL);
+ err = vfs_unlink2(mnt, dentry->d_parent->d_inode, dentry, NULL);
}
dput(dentry);
diff --git a/ipc/msg.c b/ipc/msg.c
index cfc8b38..02e72d3 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -678,7 +678,7 @@
rcu_read_lock();
ipc_lock_object(&msq->q_perm);
- ipc_rcu_putref(msq, ipc_rcu_free);
+ ipc_rcu_putref(msq, msg_rcu_free);
/* raced with RMID? */
if (!ipc_valid_object(&msq->q_perm)) {
err = -EIDRM;
diff --git a/ipc/sem.c b/ipc/sem.c
index 85ad28a..e59935d 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -441,7 +441,7 @@
static inline void sem_lock_and_putref(struct sem_array *sma)
{
sem_lock(sma, NULL, -1);
- ipc_rcu_putref(sma, ipc_rcu_free);
+ ipc_rcu_putref(sma, sem_rcu_free);
}
static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s)
@@ -1384,7 +1384,7 @@
rcu_read_unlock();
sem_io = ipc_alloc(sizeof(ushort)*nsems);
if (sem_io == NULL) {
- ipc_rcu_putref(sma, ipc_rcu_free);
+ ipc_rcu_putref(sma, sem_rcu_free);
return -ENOMEM;
}
@@ -1418,20 +1418,20 @@
if (nsems > SEMMSL_FAST) {
sem_io = ipc_alloc(sizeof(ushort)*nsems);
if (sem_io == NULL) {
- ipc_rcu_putref(sma, ipc_rcu_free);
+ ipc_rcu_putref(sma, sem_rcu_free);
return -ENOMEM;
}
}
if (copy_from_user(sem_io, p, nsems*sizeof(ushort))) {
- ipc_rcu_putref(sma, ipc_rcu_free);
+ ipc_rcu_putref(sma, sem_rcu_free);
err = -EFAULT;
goto out_free;
}
for (i = 0; i < nsems; i++) {
if (sem_io[i] > SEMVMX) {
- ipc_rcu_putref(sma, ipc_rcu_free);
+ ipc_rcu_putref(sma, sem_rcu_free);
err = -ERANGE;
goto out_free;
}
@@ -1721,7 +1721,7 @@
/* step 2: allocate new undo structure */
new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
if (!new) {
- ipc_rcu_putref(sma, ipc_rcu_free);
+ ipc_rcu_putref(sma, sem_rcu_free);
return ERR_PTR(-ENOMEM);
}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index cc3416f..3a6e0101 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -73,6 +73,7 @@
#include <linux/compat.h>
#include <linux/ctype.h>
#include <linux/string.h>
+#include <linux/uaccess.h>
#include <uapi/linux/limits.h>
#include "audit.h"
@@ -82,7 +83,8 @@
#define AUDITSC_SUCCESS 1
#define AUDITSC_FAILURE 2
-/* no execve audit message should be longer than this (userspace limits) */
+/* no execve audit message should be longer than this (userspace limits),
+ * see the note near the top of audit_log_execve_info() about this value */
#define MAX_EXECVE_AUDIT_LEN 7500
/* max length to print of cmdline/proctitle value during audit */
@@ -1010,185 +1012,178 @@
return rc;
}
-/*
- * to_send and len_sent accounting are very loose estimates. We aren't
- * really worried about a hard cap to MAX_EXECVE_AUDIT_LEN so much as being
- * within about 500 bytes (next page boundary)
- *
- * why snprintf? an int is up to 12 digits long. if we just assumed when
- * logging that a[%d]= was going to be 16 characters long we would be wasting
- * space in every audit message. In one 7500 byte message we can log up to
- * about 1000 min size arguments. That comes down to about 50% waste of space
- * if we didn't do the snprintf to find out how long arg_num_len was.
- */
-static int audit_log_single_execve_arg(struct audit_context *context,
- struct audit_buffer **ab,
- int arg_num,
- size_t *len_sent,
- const char __user *p,
- char *buf)
-{
- char arg_num_len_buf[12];
- const char __user *tmp_p = p;
- /* how many digits are in arg_num? 5 is the length of ' a=""' */
- size_t arg_num_len = snprintf(arg_num_len_buf, 12, "%d", arg_num) + 5;
- size_t len, len_left, to_send;
- size_t max_execve_audit_len = MAX_EXECVE_AUDIT_LEN;
- unsigned int i, has_cntl = 0, too_long = 0;
- int ret;
-
- /* strnlen_user includes the null we don't want to send */
- len_left = len = strnlen_user(p, MAX_ARG_STRLEN) - 1;
-
- /*
- * We just created this mm, if we can't find the strings
- * we just copied into it something is _very_ wrong. Similar
- * for strings that are too long, we should not have created
- * any.
- */
- if (unlikely((len == -1) || len > MAX_ARG_STRLEN - 1)) {
- WARN_ON(1);
- send_sig(SIGKILL, current, 0);
- return -1;
- }
-
- /* walk the whole argument looking for non-ascii chars */
- do {
- if (len_left > MAX_EXECVE_AUDIT_LEN)
- to_send = MAX_EXECVE_AUDIT_LEN;
- else
- to_send = len_left;
- ret = copy_from_user(buf, tmp_p, to_send);
- /*
- * There is no reason for this copy to be short. We just
- * copied them here, and the mm hasn't been exposed to user-
- * space yet.
- */
- if (ret) {
- WARN_ON(1);
- send_sig(SIGKILL, current, 0);
- return -1;
- }
- buf[to_send] = '\0';
- has_cntl = audit_string_contains_control(buf, to_send);
- if (has_cntl) {
- /*
- * hex messages get logged as 2 bytes, so we can only
- * send half as much in each message
- */
- max_execve_audit_len = MAX_EXECVE_AUDIT_LEN / 2;
- break;
- }
- len_left -= to_send;
- tmp_p += to_send;
- } while (len_left > 0);
-
- len_left = len;
-
- if (len > max_execve_audit_len)
- too_long = 1;
-
- /* rewalk the argument actually logging the message */
- for (i = 0; len_left > 0; i++) {
- int room_left;
-
- if (len_left > max_execve_audit_len)
- to_send = max_execve_audit_len;
- else
- to_send = len_left;
-
- /* do we have space left to send this argument in this ab? */
- room_left = MAX_EXECVE_AUDIT_LEN - arg_num_len - *len_sent;
- if (has_cntl)
- room_left -= (to_send * 2);
- else
- room_left -= to_send;
- if (room_left < 0) {
- *len_sent = 0;
- audit_log_end(*ab);
- *ab = audit_log_start(context, GFP_KERNEL, AUDIT_EXECVE);
- if (!*ab)
- return 0;
- }
-
- /*
- * first record needs to say how long the original string was
- * so we can be sure nothing was lost.
- */
- if ((i == 0) && (too_long))
- audit_log_format(*ab, " a%d_len=%zu", arg_num,
- has_cntl ? 2*len : len);
-
- /*
- * normally arguments are small enough to fit and we already
- * filled buf above when we checked for control characters
- * so don't bother with another copy_from_user
- */
- if (len >= max_execve_audit_len)
- ret = copy_from_user(buf, p, to_send);
- else
- ret = 0;
- if (ret) {
- WARN_ON(1);
- send_sig(SIGKILL, current, 0);
- return -1;
- }
- buf[to_send] = '\0';
-
- /* actually log it */
- audit_log_format(*ab, " a%d", arg_num);
- if (too_long)
- audit_log_format(*ab, "[%d]", i);
- audit_log_format(*ab, "=");
- if (has_cntl)
- audit_log_n_hex(*ab, buf, to_send);
- else
- audit_log_string(*ab, buf);
-
- p += to_send;
- len_left -= to_send;
- *len_sent += arg_num_len;
- if (has_cntl)
- *len_sent += to_send * 2;
- else
- *len_sent += to_send;
- }
- /* include the null we didn't log */
- return len + 1;
-}
-
static void audit_log_execve_info(struct audit_context *context,
struct audit_buffer **ab)
{
- int i, len;
- size_t len_sent = 0;
- const char __user *p;
+ long len_max;
+ long len_rem;
+ long len_full;
+ long len_buf;
+ long len_abuf;
+ long len_tmp;
+ bool require_data;
+ bool encode;
+ unsigned int iter;
+ unsigned int arg;
+ char *buf_head;
char *buf;
+ const char __user *p = (const char __user *)current->mm->arg_start;
- p = (const char __user *)current->mm->arg_start;
+ /* NOTE: this buffer needs to be large enough to hold all the non-arg
+ * data we put in the audit record for this argument (see the
+ * code below) ... at this point in time 96 is plenty */
+ char abuf[96];
- audit_log_format(*ab, "argc=%d", context->execve.argc);
+ /* NOTE: we set MAX_EXECVE_AUDIT_LEN to a rather arbitrary limit, the
+ * current value of 7500 is not as important as the fact that it
+ * is less than 8k, a setting of 7500 gives us plenty of wiggle
+ * room if we go over a little bit in the logging below */
+ WARN_ON_ONCE(MAX_EXECVE_AUDIT_LEN > 7500);
+ len_max = MAX_EXECVE_AUDIT_LEN;
- /*
- * we need some kernel buffer to hold the userspace args. Just
- * allocate one big one rather than allocating one of the right size
- * for every single argument inside audit_log_single_execve_arg()
- * should be <8k allocation so should be pretty safe.
- */
- buf = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL);
- if (!buf) {
+ /* scratch buffer to hold the userspace args */
+ buf_head = kmalloc(MAX_EXECVE_AUDIT_LEN + 1, GFP_KERNEL);
+ if (!buf_head) {
audit_panic("out of memory for argv string");
return;
}
+ buf = buf_head;
- for (i = 0; i < context->execve.argc; i++) {
- len = audit_log_single_execve_arg(context, ab, i,
- &len_sent, p, buf);
- if (len <= 0)
- break;
- p += len;
- }
- kfree(buf);
+ audit_log_format(*ab, "argc=%d", context->execve.argc);
+
+ len_rem = len_max;
+ len_buf = 0;
+ len_full = 0;
+ require_data = true;
+ encode = false;
+ iter = 0;
+ arg = 0;
+ do {
+ /* NOTE: we don't ever want to trust this value for anything
+ * serious, but the audit record format insists we
+ * provide an argument length for really long arguments,
+ * e.g. > MAX_EXECVE_AUDIT_LEN, so we have no choice but
+ * to use strncpy_from_user() to obtain this value for
+ * recording in the log, although we don't use it
+ * anywhere here to avoid a double-fetch problem */
+ if (len_full == 0)
+ len_full = strnlen_user(p, MAX_ARG_STRLEN) - 1;
+
+ /* read more data from userspace */
+ if (require_data) {
+ /* can we make more room in the buffer? */
+ if (buf != buf_head) {
+ memmove(buf_head, buf, len_buf);
+ buf = buf_head;
+ }
+
+ /* fetch as much as we can of the argument */
+ len_tmp = strncpy_from_user(&buf_head[len_buf], p,
+ len_max - len_buf);
+ if (len_tmp == -EFAULT) {
+ /* unable to copy from userspace */
+ send_sig(SIGKILL, current, 0);
+ goto out;
+ } else if (len_tmp == (len_max - len_buf)) {
+ /* buffer is not large enough */
+ require_data = true;
+ /* NOTE: if we are going to span multiple
+ * buffers force the encoding so we stand
+ * a chance at a sane len_full value and
+ * consistent record encoding */
+ encode = true;
+ len_full = len_full * 2;
+ p += len_tmp;
+ } else {
+ require_data = false;
+ if (!encode)
+ encode = audit_string_contains_control(
+ buf, len_tmp);
+ /* try to use a trusted value for len_full */
+ if (len_full < len_max)
+ len_full = (encode ?
+ len_tmp * 2 : len_tmp);
+ p += len_tmp + 1;
+ }
+ len_buf += len_tmp;
+ buf_head[len_buf] = '\0';
+
+ /* length of the buffer in the audit record? */
+ len_abuf = (encode ? len_buf * 2 : len_buf + 2);
+ }
+
+ /* write as much as we can to the audit log */
+ if (len_buf > 0) {
+ /* NOTE: some magic numbers here - basically if we
+ * can't fit a reasonable amount of data into the
+ * existing audit buffer, flush it and start with
+ * a new buffer */
+ if ((sizeof(abuf) + 8) > len_rem) {
+ len_rem = len_max;
+ audit_log_end(*ab);
+ *ab = audit_log_start(context,
+ GFP_KERNEL, AUDIT_EXECVE);
+ if (!*ab)
+ goto out;
+ }
+
+ /* create the non-arg portion of the arg record */
+ len_tmp = 0;
+ if (require_data || (iter > 0) ||
+ ((len_abuf + sizeof(abuf)) > len_rem)) {
+ if (iter == 0) {
+ len_tmp += snprintf(&abuf[len_tmp],
+ sizeof(abuf) - len_tmp,
+ " a%d_len=%lu",
+ arg, len_full);
+ }
+ len_tmp += snprintf(&abuf[len_tmp],
+ sizeof(abuf) - len_tmp,
+ " a%d[%d]=", arg, iter++);
+ } else
+ len_tmp += snprintf(&abuf[len_tmp],
+ sizeof(abuf) - len_tmp,
+ " a%d=", arg);
+ WARN_ON(len_tmp >= sizeof(abuf));
+ abuf[sizeof(abuf) - 1] = '\0';
+
+ /* log the arg in the audit record */
+ audit_log_format(*ab, "%s", abuf);
+ len_rem -= len_tmp;
+ len_tmp = len_buf;
+ if (encode) {
+ if (len_abuf > len_rem)
+ len_tmp = len_rem / 2; /* encoding */
+ audit_log_n_hex(*ab, buf, len_tmp);
+ len_rem -= len_tmp * 2;
+ len_abuf -= len_tmp * 2;
+ } else {
+ if (len_abuf > len_rem)
+ len_tmp = len_rem - 2; /* quotes */
+ audit_log_n_string(*ab, buf, len_tmp);
+ len_rem -= len_tmp + 2;
+ /* don't subtract the "2" because we still need
+ * to add quotes to the remaining string */
+ len_abuf -= len_tmp;
+ }
+ len_buf -= len_tmp;
+ buf += len_tmp;
+ }
+
+ /* ready to move to the next argument? */
+ if ((len_buf == 0) && !require_data) {
+ arg++;
+ iter = 0;
+ len_full = 0;
+ require_data = true;
+ encode = false;
+ }
+ } while (arg < context->execve.argc);
+
+ /* NOTE: the caller handles the final audit_log_end() call */
+
+out:
+ kfree(buf_head);
}
static void show_special(struct audit_context *context, int *call_panic)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 67f5f17..eac45b6 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1737,7 +1737,6 @@
if (IS_ERR(map)) {
verbose("fd %d is not pointing to valid bpf_map\n",
insn->imm);
- fdput(f);
return PTR_ERR(map);
}
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 359a50e..f3bb48f 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -4594,7 +4594,7 @@
err = cgroup_idr_alloc(&ss->css_idr, NULL, 2, 0, GFP_NOWAIT);
if (err < 0)
- goto err_free_percpu_ref;
+ goto err_free_css;
css->id = err;
if (visible) {
@@ -4626,9 +4626,6 @@
list_del_rcu(&css->sibling);
cgroup_clear_dir(css->cgroup, 1 << css->ss->id);
err_free_id:
- cgroup_idr_remove(&ss->css_idr, css->id);
-err_free_percpu_ref:
- percpu_ref_exit(&css->refcnt);
err_free_css:
call_rcu(&css->rcu_head, css_free_rcu_fn);
return err;
diff --git a/kernel/configs/tiny.config b/kernel/configs/tiny.config
index c2de56a..7fa0c4a 100644
--- a/kernel/configs/tiny.config
+++ b/kernel/configs/tiny.config
@@ -1,4 +1,12 @@
+# CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+# CONFIG_KERNEL_GZIP is not set
+# CONFIG_KERNEL_BZIP2 is not set
+# CONFIG_KERNEL_LZMA is not set
CONFIG_KERNEL_XZ=y
+# CONFIG_KERNEL_LZO is not set
+# CONFIG_KERNEL_LZ4 is not set
CONFIG_OPTIMIZE_INLINING=y
+# CONFIG_SLAB is not set
+# CONFIG_SLUB is not set
CONFIG_SLOB=y
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index f1bfd65..43cd82d 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -324,8 +324,7 @@
/*
* Return in pmask the portion of a cpusets's cpus_allowed that
* are online. If none are online, walk up the cpuset hierarchy
- * until we find one that does have some online cpus. The top
- * cpuset always has some cpus online.
+ * until we find one that does have some online cpus.
*
* One way or another, we guarantee to return some non-empty subset
* of cpu_online_mask.
@@ -334,8 +333,20 @@
*/
static void guarantee_online_cpus(struct cpuset *cs, struct cpumask *pmask)
{
- while (!cpumask_intersects(cs->effective_cpus, cpu_online_mask))
+ while (!cpumask_intersects(cs->effective_cpus, cpu_online_mask)) {
cs = parent_cs(cs);
+ if (unlikely(!cs)) {
+ /*
+ * The top cpuset doesn't have any online cpu as a
+ * consequence of a race between cpuset_hotplug_work
+ * and cpu hotplug notifier. But we know the top
+ * cpuset's effective_cpus is on its way to to be
+ * identical to cpu_online_mask.
+ */
+ cpumask_copy(pmask, cpu_online_mask);
+ return;
+ }
+ }
cpumask_and(pmask, cs->effective_cpus, cpu_online_mask);
}
@@ -2055,7 +2066,6 @@
mutex_unlock(&cpuset_mutex);
}
-
static int cpuset_allow_attach(struct cgroup_subsys_state *css,
struct cgroup_taskset *tset)
{
@@ -2073,6 +2083,20 @@
return 0;
}
+/*
+ * Make sure the new task conform to the current state of its parent,
+ * which could have been changed by cpuset just after it inherits the
+ * state from the parent and before it sits on the cgroup's task list.
+ */
+void cpuset_fork(struct task_struct *task)
+{
+ if (task_css_is_root(task, cpuset_cgrp_id))
+ return;
+
+ set_cpus_allowed_ptr(task, ¤t->cpus_allowed);
+ task->mems_allowed = current->mems_allowed;
+}
+
struct cgroup_subsys cpuset_cgrp_subsys = {
.css_alloc = cpuset_css_alloc,
.css_online = cpuset_css_online,
@@ -2083,6 +2107,7 @@
.cancel_attach = cpuset_cancel_attach,
.attach = cpuset_attach,
.bind = cpuset_bind,
+ .fork = cpuset_fork,
.legacy_cftypes = files,
.early_init = 1,
};
diff --git a/kernel/debug/kdb/kdb_bp.c b/kernel/debug/kdb/kdb_bp.c
index b20d544..b8a3fa1 100644
--- a/kernel/debug/kdb/kdb_bp.c
+++ b/kernel/debug/kdb/kdb_bp.c
@@ -153,13 +153,11 @@
} else {
kdb_printf("%s: failed to set breakpoint at 0x%lx\n",
__func__, bp->bp_addr);
-#ifdef CONFIG_DEBUG_RODATA
if (!bp->bp_type) {
kdb_printf("Software breakpoints are unavailable.\n"
- " Change the kernel CONFIG_DEBUG_RODATA=n\n"
+ " Boot the kernel with rodata=off\n"
" OR use hw breaks: help bph\n");
}
-#endif
return 1;
}
return 0;
diff --git a/kernel/events/core.c b/kernel/events/core.c
index b344ab8..b25fcc7 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -49,9 +49,11 @@
static struct workqueue_struct *perf_wq;
+typedef int (*remote_function_f)(void *);
+
struct remote_function_call {
struct task_struct *p;
- int (*func)(void *info);
+ remote_function_f func;
void *info;
int ret;
};
@@ -84,7 +86,7 @@
* -EAGAIN - when the process moved away
*/
static int
-task_function_call(struct task_struct *p, int (*func) (void *info), void *info)
+task_function_call(struct task_struct *p, remote_function_f func, void *info)
{
struct remote_function_call data = {
.p = p,
@@ -108,7 +110,7 @@
*
* returns: @func return value or -ENXIO when the cpu is offline
*/
-static int cpu_function_call(int cpu, int (*func) (void *info), void *info)
+static int cpu_function_call(int cpu, remote_function_f func, void *info)
{
struct remote_function_call data = {
.p = NULL,
@@ -772,62 +774,31 @@
/*
* function must be called with interrupts disbled
*/
-static enum hrtimer_restart perf_cpu_hrtimer_handler(struct hrtimer *hr)
+static enum hrtimer_restart perf_mux_hrtimer_handler(struct hrtimer *hr)
{
struct perf_cpu_context *cpuctx;
- enum hrtimer_restart ret = HRTIMER_NORESTART;
int rotations = 0;
WARN_ON(!irqs_disabled());
cpuctx = container_of(hr, struct perf_cpu_context, hrtimer);
-
rotations = perf_rotate_context(cpuctx);
- /*
- * arm timer if needed
- */
- if (rotations) {
+ raw_spin_lock(&cpuctx->hrtimer_lock);
+ if (rotations)
hrtimer_forward_now(hr, cpuctx->hrtimer_interval);
- ret = HRTIMER_RESTART;
- }
+ else
+ cpuctx->hrtimer_active = 0;
+ raw_spin_unlock(&cpuctx->hrtimer_lock);
- return ret;
+ return rotations ? HRTIMER_RESTART : HRTIMER_NORESTART;
}
-/* CPU is going down */
-void perf_cpu_hrtimer_cancel(int cpu)
+static void __perf_mux_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
{
- struct perf_cpu_context *cpuctx;
- struct pmu *pmu;
- unsigned long flags;
-
- if (WARN_ON(cpu != smp_processor_id()))
- return;
-
- local_irq_save(flags);
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(pmu, &pmus, entry) {
- cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
-
- if (pmu->task_ctx_nr == perf_sw_context)
- continue;
-
- hrtimer_cancel(&cpuctx->hrtimer);
- }
-
- rcu_read_unlock();
-
- local_irq_restore(flags);
-}
-
-static void __perf_cpu_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
-{
- struct hrtimer *hr = &cpuctx->hrtimer;
+ struct hrtimer *timer = &cpuctx->hrtimer;
struct pmu *pmu = cpuctx->ctx.pmu;
- int timer;
+ u64 interval;
/* no multiplexing needed for SW PMU */
if (pmu->task_ctx_nr == perf_sw_context)
@@ -837,31 +808,36 @@
* check default is sane, if not set then force to
* default interval (1/tick)
*/
- timer = pmu->hrtimer_interval_ms;
- if (timer < 1)
- timer = pmu->hrtimer_interval_ms = PERF_CPU_HRTIMER;
+ interval = pmu->hrtimer_interval_ms;
+ if (interval < 1)
+ interval = pmu->hrtimer_interval_ms = PERF_CPU_HRTIMER;
- cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * timer);
+ cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * interval);
- hrtimer_init(hr, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
- hr->function = perf_cpu_hrtimer_handler;
+ raw_spin_lock_init(&cpuctx->hrtimer_lock);
+ hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
+ timer->function = perf_mux_hrtimer_handler;
}
-static void perf_cpu_hrtimer_restart(struct perf_cpu_context *cpuctx)
+static int perf_mux_hrtimer_restart(struct perf_cpu_context *cpuctx)
{
- struct hrtimer *hr = &cpuctx->hrtimer;
+ struct hrtimer *timer = &cpuctx->hrtimer;
struct pmu *pmu = cpuctx->ctx.pmu;
+ unsigned long flags;
/* not for SW PMU */
if (pmu->task_ctx_nr == perf_sw_context)
- return;
+ return 0;
- if (hrtimer_active(hr))
- return;
+ raw_spin_lock_irqsave(&cpuctx->hrtimer_lock, flags);
+ if (!cpuctx->hrtimer_active) {
+ cpuctx->hrtimer_active = 1;
+ hrtimer_forward_now(timer, cpuctx->hrtimer_interval);
+ hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED);
+ }
+ raw_spin_unlock_irqrestore(&cpuctx->hrtimer_lock, flags);
- if (!hrtimer_callback_running(hr))
- __hrtimer_start_range_ns(hr, cpuctx->hrtimer_interval,
- 0, HRTIMER_MODE_REL_PINNED, 0);
+ return 0;
}
void perf_pmu_disable(struct pmu *pmu)
@@ -878,22 +854,32 @@
pmu->pmu_enable(pmu);
}
-static DEFINE_PER_CPU(struct list_head, rotation_list);
+static DEFINE_PER_CPU(struct list_head, active_ctx_list);
/*
- * perf_pmu_rotate_start() and perf_rotate_context() are fully serialized
- * because they're strictly cpu affine and rotate_start is called with IRQs
- * disabled, while rotate_context is called from IRQ context.
+ * perf_event_ctx_activate(), perf_event_ctx_deactivate(), and
+ * perf_event_task_tick() are fully serialized because they're strictly cpu
+ * affine and perf_event_ctx{activate,deactivate} are called with IRQs
+ * disabled, while perf_event_task_tick is called from IRQ context.
*/
-static void perf_pmu_rotate_start(struct pmu *pmu)
+static void perf_event_ctx_activate(struct perf_event_context *ctx)
{
- struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
- struct list_head *head = this_cpu_ptr(&rotation_list);
+ struct list_head *head = this_cpu_ptr(&active_ctx_list);
WARN_ON(!irqs_disabled());
- if (list_empty(&cpuctx->rotation_list))
- list_add(&cpuctx->rotation_list, head);
+ WARN_ON(!list_empty(&ctx->active_ctx_list));
+
+ list_add(&ctx->active_ctx_list, head);
+}
+
+static void perf_event_ctx_deactivate(struct perf_event_context *ctx)
+{
+ WARN_ON(!irqs_disabled());
+
+ WARN_ON(list_empty(&ctx->active_ctx_list));
+
+ list_del_init(&ctx->active_ctx_list);
}
static void get_ctx(struct perf_event_context *ctx)
@@ -1232,8 +1218,6 @@
ctx->nr_branch_stack++;
list_add_rcu(&event->event_entry, &ctx->event_list);
- if (!ctx->nr_events)
- perf_pmu_rotate_start(ctx->pmu);
ctx->nr_events++;
if (event->attr.inherit_stat)
ctx->nr_stat++;
@@ -1555,7 +1539,8 @@
if (!is_software_event(event))
cpuctx->active_oncpu--;
- ctx->nr_active--;
+ if (!--ctx->nr_active)
+ perf_event_ctx_deactivate(ctx);
if (event->attr.freq && event->attr.sample_freq)
ctx->nr_freq--;
if (event->attr.exclusive || !cpuctx->active_oncpu)
@@ -1881,7 +1866,8 @@
if (!is_software_event(event))
cpuctx->active_oncpu++;
- ctx->nr_active++;
+ if (!ctx->nr_active++)
+ perf_event_ctx_activate(ctx);
if (event->attr.freq && event->attr.sample_freq)
ctx->nr_freq++;
@@ -1914,7 +1900,7 @@
if (event_sched_in(group_event, cpuctx, ctx)) {
pmu->cancel_txn(pmu);
- perf_cpu_hrtimer_restart(cpuctx);
+ perf_mux_hrtimer_restart(cpuctx);
return -EAGAIN;
}
@@ -1961,7 +1947,7 @@
pmu->cancel_txn(pmu);
- perf_cpu_hrtimer_restart(cpuctx);
+ perf_mux_hrtimer_restart(cpuctx);
return -EAGAIN;
}
@@ -2234,7 +2220,7 @@
*/
if (leader != event) {
group_sched_out(leader, cpuctx, ctx);
- perf_cpu_hrtimer_restart(cpuctx);
+ perf_mux_hrtimer_restart(cpuctx);
}
if (leader->attr.pinned) {
update_group_times(leader);
@@ -2738,12 +2724,6 @@
perf_pmu_enable(ctx->pmu);
perf_ctx_unlock(cpuctx, ctx);
-
- /*
- * Since these rotations are per-cpu, we need to ensure the
- * cpu-context we got scheduled on is actually rotating.
- */
- perf_pmu_rotate_start(ctx->pmu);
}
/*
@@ -3031,25 +3011,18 @@
list_rotate_left(&ctx->flexible_groups);
}
-/*
- * perf_pmu_rotate_start() and perf_rotate_context() are fully serialized
- * because they're strictly cpu affine and rotate_start is called with IRQs
- * disabled, while rotate_context is called from IRQ context.
- */
static int perf_rotate_context(struct perf_cpu_context *cpuctx)
{
struct perf_event_context *ctx = NULL;
- int rotate = 0, remove = 1;
+ int rotate = 0;
if (cpuctx->ctx.nr_events) {
- remove = 0;
if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active)
rotate = 1;
}
ctx = cpuctx->task_ctx;
if (ctx && ctx->nr_events) {
- remove = 0;
if (ctx->nr_events != ctx->nr_active)
rotate = 1;
}
@@ -3073,8 +3046,6 @@
perf_pmu_enable(cpuctx->ctx.pmu);
perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
done:
- if (remove)
- list_del_init(&cpuctx->rotation_list);
return rotate;
}
@@ -3092,9 +3063,8 @@
void perf_event_task_tick(void)
{
- struct list_head *head = this_cpu_ptr(&rotation_list);
- struct perf_cpu_context *cpuctx, *tmp;
- struct perf_event_context *ctx;
+ struct list_head *head = this_cpu_ptr(&active_ctx_list);
+ struct perf_event_context *ctx, *tmp;
int throttled;
WARN_ON(!irqs_disabled());
@@ -3102,14 +3072,8 @@
__this_cpu_inc(perf_throttled_seq);
throttled = __this_cpu_xchg(perf_throttled_count, 0);
- list_for_each_entry_safe(cpuctx, tmp, head, rotation_list) {
- ctx = &cpuctx->ctx;
+ list_for_each_entry_safe(ctx, tmp, head, active_ctx_list)
perf_adjust_freq_unthr_context(ctx, throttled);
-
- ctx = cpuctx->task_ctx;
- if (ctx)
- perf_adjust_freq_unthr_context(ctx, throttled);
- }
}
static int event_enable_on_exec(struct perf_event *event,
@@ -3271,6 +3235,7 @@
{
raw_spin_lock_init(&ctx->lock);
mutex_init(&ctx->mutex);
+ INIT_LIST_HEAD(&ctx->active_ctx_list);
INIT_LIST_HEAD(&ctx->pinned_groups);
INIT_LIST_HEAD(&ctx->flexible_groups);
INIT_LIST_HEAD(&ctx->event_list);
@@ -6075,6 +6040,8 @@
rcu_read_unlock();
}
+DEFINE_PER_CPU(struct pt_regs, __perf_regs[4]);
+
int perf_swevent_get_recursion_context(void)
{
struct swevent_htable *swhash = this_cpu_ptr(&swevent_htable);
@@ -6090,21 +6057,30 @@
put_recursion_context(swhash->recursion, rctx);
}
-void __perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
+void ___perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
{
struct perf_sample_data data;
+
+ if (WARN_ON_ONCE(!regs))
+ return;
+
+ perf_sample_data_init(&data, addr, 0);
+ do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, &data, regs);
+}
+
+void __perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
+{
int rctx;
preempt_disable_notrace();
rctx = perf_swevent_get_recursion_context();
- if (rctx < 0)
- return;
+ if (unlikely(rctx < 0))
+ goto fail;
- perf_sample_data_init(&data, addr, 0);
-
- do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, &data, regs);
+ ___perf_sw_event(event_id, nr, regs, addr);
perf_swevent_put_recursion_context(rctx);
+fail:
preempt_enable_notrace();
}
@@ -6527,9 +6503,8 @@
} else {
period = max_t(u64, 10000, hwc->sample_period);
}
- __hrtimer_start_range_ns(&hwc->hrtimer,
- ns_to_ktime(period), 0,
- HRTIMER_MODE_REL_PINNED, 0);
+ hrtimer_start(&hwc->hrtimer, ns_to_ktime(period),
+ HRTIMER_MODE_REL_PINNED);
}
static void perf_swevent_cancel_hrtimer(struct perf_event *event)
@@ -6824,6 +6799,8 @@
return snprintf(page, PAGE_SIZE-1, "%d\n", pmu->hrtimer_interval_ms);
}
+static DEFINE_MUTEX(mux_interval_mutex);
+
static ssize_t
perf_event_mux_interval_ms_store(struct device *dev,
struct device_attribute *attr,
@@ -6843,17 +6820,21 @@
if (timer == pmu->hrtimer_interval_ms)
return count;
+ mutex_lock(&mux_interval_mutex);
pmu->hrtimer_interval_ms = timer;
/* update all cpuctx for this PMU */
- for_each_possible_cpu(cpu) {
+ get_online_cpus();
+ for_each_online_cpu(cpu) {
struct perf_cpu_context *cpuctx;
cpuctx = per_cpu_ptr(pmu->pmu_cpu_context, cpu);
cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * timer);
- if (hrtimer_active(&cpuctx->hrtimer))
- hrtimer_forward_now(&cpuctx->hrtimer, cpuctx->hrtimer_interval);
+ cpu_function_call(cpu,
+ (remote_function_f)perf_mux_hrtimer_restart, cpuctx);
}
+ put_online_cpus();
+ mutex_unlock(&mux_interval_mutex);
return count;
}
@@ -6958,9 +6939,8 @@
lockdep_set_class(&cpuctx->ctx.lock, &cpuctx_lock);
cpuctx->ctx.pmu = pmu;
- __perf_cpu_hrtimer_init(cpuctx, cpu);
+ __perf_mux_hrtimer_init(cpuctx, cpu);
- INIT_LIST_HEAD(&cpuctx->rotation_list);
cpuctx->unique_pmu = pmu;
}
@@ -8333,7 +8313,7 @@
ret = inherit_task_group(event, parent, parent_ctx,
child, ctxn, &inherited_all);
if (ret)
- break;
+ goto out_unlock;
}
/*
@@ -8349,7 +8329,7 @@
ret = inherit_task_group(event, parent, parent_ctx,
child, ctxn, &inherited_all);
if (ret)
- break;
+ goto out_unlock;
}
raw_spin_lock_irqsave(&parent_ctx->lock, flags);
@@ -8377,6 +8357,7 @@
}
raw_spin_unlock_irqrestore(&parent_ctx->lock, flags);
+out_unlock:
mutex_unlock(&parent_ctx->mutex);
perf_unpin_context(parent_ctx);
@@ -8415,7 +8396,7 @@
for_each_possible_cpu(cpu) {
swhash = &per_cpu(swevent_htable, cpu);
mutex_init(&swhash->hlist_mutex);
- INIT_LIST_HEAD(&per_cpu(rotation_list, cpu));
+ INIT_LIST_HEAD(&per_cpu(active_ctx_list, cpu));
}
}
@@ -8435,22 +8416,11 @@
}
#if defined CONFIG_HOTPLUG_CPU || defined CONFIG_KEXEC
-static void perf_pmu_rotate_stop(struct pmu *pmu)
-{
- struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
-
- WARN_ON(!irqs_disabled());
-
- list_del_init(&cpuctx->rotation_list);
-}
-
static void __perf_event_exit_context(void *__info)
{
struct remove_event re = { .detach_group = true };
struct perf_event_context *ctx = __info;
- perf_pmu_rotate_stop(ctx->pmu);
-
rcu_read_lock();
list_for_each_entry_rcu(re.event, &ctx->event_list, event_entry)
__perf_remove_from_context(&re);
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index ed8f2cd..4e10bb0 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -179,8 +179,10 @@
mmu_notifier_invalidate_range_start(mm, mmun_start, mmun_end);
err = -EAGAIN;
ptep = page_check_address(page, mm, addr, &ptl, 0);
- if (!ptep)
+ if (!ptep) {
+ mem_cgroup_cancel_charge(kpage, memcg);
goto unlock;
+ }
get_page(kpage);
page_add_new_anon_rmap(kpage, vma, addr);
@@ -207,7 +209,6 @@
err = 0;
unlock:
- mem_cgroup_cancel_charge(kpage, memcg);
mmu_notifier_invalidate_range_end(mm, mmun_start, mmun_end);
unlock_page(page);
return err;
diff --git a/kernel/exit.c b/kernel/exit.c
index d5542fa..479bf80 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -54,6 +54,7 @@
#include <linux/writeback.h>
#include <linux/shm.h>
#include <linux/kcov.h>
+#include <linux/cpufreq.h>
#include "sched/tune.h"
@@ -176,6 +177,9 @@
{
struct task_struct *leader;
int zap_leader;
+#ifdef CONFIG_CPU_FREQ_STAT
+ cpufreq_task_stats_exit(p);
+#endif
repeat:
/* don't need to get the RCU readlock here - the process is dead and
* can't be modifying its own credentials. But shut RCU-lockdep up */
@@ -951,17 +955,28 @@
task_pid_type(p, wo->wo_type) == wo->wo_pid;
}
-static int eligible_child(struct wait_opts *wo, struct task_struct *p)
+static int
+eligible_child(struct wait_opts *wo, bool ptrace, struct task_struct *p)
{
if (!eligible_pid(wo, p))
return 0;
- /* Wait for all children (clone and not) if __WALL is set;
- * otherwise, wait for clone children *only* if __WCLONE is
- * set; otherwise, wait for non-clone children *only*. (Note:
- * A "clone" child here is one that reports to its parent
- * using a signal other than SIGCHLD.) */
- if (((p->exit_signal != SIGCHLD) ^ !!(wo->wo_flags & __WCLONE))
- && !(wo->wo_flags & __WALL))
+
+ /*
+ * Wait for all children (clone and not) if __WALL is set or
+ * if it is traced by us.
+ */
+ if (ptrace || (wo->wo_flags & __WALL))
+ return 1;
+
+ /*
+ * Otherwise, wait for clone children *only* if __WCLONE is set;
+ * otherwise, wait for non-clone children *only*.
+ *
+ * Note: a "clone" child here is one that reports to its parent
+ * using a signal other than SIGCHLD, or a non-leader thread which
+ * we can only see if it is traced by us.
+ */
+ if ((p->exit_signal != SIGCHLD) ^ !!(wo->wo_flags & __WCLONE))
return 0;
return 1;
@@ -1334,7 +1349,7 @@
if (unlikely(exit_state == EXIT_DEAD))
return 0;
- ret = eligible_child(wo, p);
+ ret = eligible_child(wo, ptrace, p);
if (!ret)
return ret;
diff --git a/kernel/futex.c b/kernel/futex.c
index d9d6380..54ebb63 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -1157,10 +1157,20 @@
*/
newval = FUTEX_WAITERS | task_pid_vnr(new_owner);
- if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))
+ if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) {
ret = -EFAULT;
- else if (curval != uval)
- ret = -EINVAL;
+ } else if (curval != uval) {
+ /*
+ * If a unconditional UNLOCK_PI operation (user space did not
+ * try the TID->0 transition) raced with a waiter setting the
+ * FUTEX_WAITERS flag between get_user() and locking the hash
+ * bucket lock, retry the operation.
+ */
+ if ((FUTEX_TID_MASK & curval) == uval)
+ ret = -EAGAIN;
+ else
+ ret = -EINVAL;
+ }
if (ret) {
raw_spin_unlock(&pi_state->pi_mutex.wait_lock);
return ret;
@@ -1371,8 +1381,8 @@
if (likely(&hb1->chain != &hb2->chain)) {
plist_del(&q->list, &hb1->chain);
hb_waiters_dec(hb1);
- plist_add(&q->list, &hb2->chain);
hb_waiters_inc(hb2);
+ plist_add(&q->list, &hb2->chain);
q->lock_ptr = &hb2->lock;
}
get_futex_key_refs(key2);
@@ -2419,6 +2429,15 @@
*/
if (ret == -EFAULT)
goto pi_faulted;
+ /*
+ * A unconditional UNLOCK_PI op raced against a waiter
+ * setting the FUTEX_WAITERS bit. Try again.
+ */
+ if (ret == -EAGAIN) {
+ spin_unlock(&hb->lock);
+ put_futex_key(&key);
+ goto retry;
+ }
goto out_unlock;
}
@@ -2548,7 +2567,6 @@
{
struct hrtimer_sleeper timeout, *to = NULL;
struct rt_mutex_waiter rt_waiter;
- struct rt_mutex *pi_mutex = NULL;
struct futex_hash_bucket *hb;
union futex_key key2 = FUTEX_KEY_INIT;
struct futex_q q = futex_q_init;
@@ -2632,6 +2650,8 @@
if (q.pi_state && (q.pi_state->owner != current)) {
spin_lock(q.lock_ptr);
ret = fixup_pi_state_owner(uaddr2, &q, current);
+ if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current)
+ rt_mutex_unlock(&q.pi_state->pi_mutex);
/*
* Drop the reference to the pi state which
* the requeue_pi() code acquired for us.
@@ -2640,6 +2660,8 @@
spin_unlock(q.lock_ptr);
}
} else {
+ struct rt_mutex *pi_mutex;
+
/*
* We have been woken up by futex_unlock_pi(), a timeout, or a
* signal. futex_unlock_pi() will not destroy the lock_ptr nor
@@ -2663,18 +2685,19 @@
if (res)
ret = (res < 0) ? res : 0;
+ /*
+ * If fixup_pi_state_owner() faulted and was unable to handle
+ * the fault, unlock the rt_mutex and return the fault to
+ * userspace.
+ */
+ if (ret && rt_mutex_owner(pi_mutex) == current)
+ rt_mutex_unlock(pi_mutex);
+
/* Unqueue and drop the lock. */
unqueue_me_pi(&q);
}
- /*
- * If fixup_pi_state_owner() faulted and was unable to handle the
- * fault, unlock the rt_mutex and return the fault to userspace.
- */
- if (ret == -EFAULT) {
- if (pi_mutex && rt_mutex_owner(pi_mutex) == current)
- rt_mutex_unlock(pi_mutex);
- } else if (ret == -EINTR) {
+ if (ret == -EINTR) {
/*
* We've already been requeued, but cannot restart by calling
* futex_lock_pi() directly. We could restart this syscall, but
diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c
index 2abbaf8..56eb8f1 100644
--- a/kernel/irq/pm.c
+++ b/kernel/irq/pm.c
@@ -21,10 +21,6 @@
desc->istate |= IRQS_SUSPENDED | IRQS_PENDING;
desc->depth++;
irq_disable(desc);
- log_suspend_abort_reason("Wakeup IRQ %d %s pending",
- desc->irq_data.irq,
- (desc->action && desc->action->name) ?
- desc->action->name : "");
pm_system_wakeup();
return true;
}
diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c
index dadbf88..e3fe1dd 100644
--- a/kernel/locking/mutex.c
+++ b/kernel/locking/mutex.c
@@ -471,9 +471,6 @@
if (!hold_ctx)
return 0;
- if (unlikely(ctx == hold_ctx))
- return -EALREADY;
-
if (ctx->stamp - hold_ctx->stamp <= LONG_MAX &&
(ctx->stamp != hold_ctx->stamp || ctx > hold_ctx)) {
#ifdef CONFIG_DEBUG_MUTEXES
@@ -499,6 +496,12 @@
unsigned long flags;
int ret;
+ if (use_ww_ctx) {
+ struct ww_mutex *ww = container_of(lock, struct ww_mutex, base);
+ if (unlikely(ww_ctx == READ_ONCE(ww->ctx)))
+ return -EALREADY;
+ }
+
preempt_disable();
mutex_acquire_nest(&lock->dep_map, subclass, 0, nest_lock, ip);
diff --git a/kernel/module.c b/kernel/module.c
index d20ebc7..27b6e13 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2460,13 +2460,18 @@
#endif
#ifdef CONFIG_MODULE_SIG
-static int module_sig_check(struct load_info *info)
+static int module_sig_check(struct load_info *info, int flags)
{
int err = -ENOKEY;
const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
const void *mod = info->hdr;
- if (info->len > markerlen &&
+ /*
+ * Require flags == 0, as a module with version information
+ * removed is no longer the module that was signed
+ */
+ if (flags == 0 &&
+ info->len > markerlen &&
memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
/* We truncate the module to discard the signature */
info->len -= markerlen;
@@ -2485,7 +2490,7 @@
return err;
}
#else /* !CONFIG_MODULE_SIG */
-static int module_sig_check(struct load_info *info)
+static int module_sig_check(struct load_info *info, int flags)
{
return 0;
}
@@ -3251,7 +3256,7 @@
long err;
char *after_dashes;
- err = module_sig_check(info);
+ err = module_sig_check(info, flags);
if (err)
goto free_copy;
diff --git a/kernel/padata.c b/kernel/padata.c
index 161402f..1e3f977 100644
--- a/kernel/padata.c
+++ b/kernel/padata.c
@@ -189,19 +189,20 @@
reorder = &next_queue->reorder;
+ spin_lock(&reorder->lock);
if (!list_empty(&reorder->list)) {
padata = list_entry(reorder->list.next,
struct padata_priv, list);
- spin_lock(&reorder->lock);
list_del_init(&padata->list);
atomic_dec(&pd->reorder_objects);
- spin_unlock(&reorder->lock);
pd->processed++;
+ spin_unlock(&reorder->lock);
goto out;
}
+ spin_unlock(&reorder->lock);
if (__this_cpu_read(pd->pqueue->cpu_index) == next_queue->cpu_index) {
padata = ERR_PTR(-ENODATA);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index a7f48f5..ac7402c 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -3240,7 +3240,7 @@
{
dump_stack_print_info(log_lvl);
- printk("%stask: %p ti: %p task.ti: %p\n",
+ printk("%stask: %pP ti: %pP task.ti: %pP\n",
log_lvl, current, current_thread_info(),
task_thread_info(current));
}
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 0856b97..8d2c107 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -144,11 +144,17 @@
WARN_ON(!task->ptrace || task->parent != current);
+ /*
+ * PTRACE_LISTEN can allow ptrace_trap_notify to wake us up remotely.
+ * Recheck state under the lock to close this race.
+ */
spin_lock_irq(&task->sighand->siglock);
- if (__fatal_signal_pending(task))
- wake_up_state(task, __TASK_TRACED);
- else
- task->state = TASK_TRACED;
+ if (task->state == __TASK_TRACED) {
+ if (__fatal_signal_pending(task))
+ wake_up_state(task, __TASK_TRACED);
+ else
+ task->state = TASK_TRACED;
+ }
spin_unlock_irq(&task->sighand->siglock);
}
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index aba6a55..ee8481d 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -125,6 +125,8 @@
NUM_RCU_LVL_4,
};
int rcu_num_nodes __read_mostly = NUM_RCU_NODES; /* Total # rcu_nodes in use. */
+/* panic() on RCU Stall sysctl. */
+int sysctl_panic_on_rcu_stall __read_mostly;
/*
* The rcu_scheduler_active variable transitions from zero to one just
@@ -152,23 +154,14 @@
*/
static int rcu_scheduler_fully_active __read_mostly;
-#ifdef CONFIG_RCU_BOOST
-
-/*
- * Control variables for per-CPU and per-rcu_node kthreads. These
- * handle all flavors of RCU.
- */
-static DEFINE_PER_CPU(struct task_struct *, rcu_cpu_kthread_task);
-DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_status);
-DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops);
-DEFINE_PER_CPU(char, rcu_cpu_has_work);
-
-#endif /* #ifdef CONFIG_RCU_BOOST */
-
static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu);
static void invoke_rcu_core(void);
static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp);
+/* rcuc/rcub kthread realtime priority */
+static int kthread_prio = CONFIG_RCU_KTHREAD_PRIO;
+module_param(kthread_prio, int, 0644);
+
/*
* Track the rcutorture test sequence number and the update version
* number within a given test. The rcutorture_testseq is incremented
@@ -1045,6 +1038,12 @@
}
}
+static inline void panic_on_rcu_stall(void)
+{
+ if (sysctl_panic_on_rcu_stall)
+ panic("RCU Stall\n");
+}
+
static void print_other_cpu_stall(struct rcu_state *rsp, unsigned long gpnum)
{
int cpu;
@@ -1074,6 +1073,8 @@
*/
pr_err("INFO: %s detected stalls on CPUs/tasks:",
rsp->name);
+ trace_printk("INFO: %s detected stalls on other CPUs\n",
+ rsp->name);
print_cpu_stall_info_begin();
rcu_for_each_leaf_node(rsp, rnp) {
raw_spin_lock_irqsave(&rnp->lock, flags);
@@ -1127,6 +1128,8 @@
rcu_print_detail_task_stall(rsp);
+ panic_on_rcu_stall();
+
force_quiescent_state(rsp); /* Kick them all. */
}
@@ -1143,6 +1146,8 @@
* RCU CPU stall warnings.
*/
pr_err("INFO: %s self-detected stall on CPU", rsp->name);
+ trace_printk("INFO: %s self-detected stall on CPU\n",
+ rsp->name);
print_cpu_stall_info_begin();
print_cpu_stall_info(rsp, smp_processor_id());
print_cpu_stall_info_end();
@@ -1159,6 +1164,8 @@
3 * rcu_jiffies_till_stall_check() + 3;
raw_spin_unlock_irqrestore(&rnp->lock, flags);
+ panic_on_rcu_stall();
+
/*
* Attempt to revive the RCU machinery by forcing a context switch.
*
@@ -3535,17 +3542,35 @@
static int __init rcu_spawn_gp_kthread(void)
{
unsigned long flags;
+ int kthread_prio_in = kthread_prio;
struct rcu_node *rnp;
struct rcu_state *rsp;
+ struct sched_param sp;
struct task_struct *t;
+ /* Force priority into range. */
+ if (IS_ENABLED(CONFIG_RCU_BOOST) && kthread_prio < 1)
+ kthread_prio = 1;
+ else if (kthread_prio < 0)
+ kthread_prio = 0;
+ else if (kthread_prio > 99)
+ kthread_prio = 99;
+ if (kthread_prio != kthread_prio_in)
+ pr_alert("rcu_spawn_gp_kthread(): Limited prio to %d from %d\n",
+ kthread_prio, kthread_prio_in);
+
rcu_scheduler_fully_active = 1;
for_each_rcu_flavor(rsp) {
- t = kthread_run(rcu_gp_kthread, rsp, "%s", rsp->name);
+ t = kthread_create(rcu_gp_kthread, rsp, "%s", rsp->name);
BUG_ON(IS_ERR(t));
rnp = rcu_get_root(rsp);
raw_spin_lock_irqsave(&rnp->lock, flags);
rsp->gp_kthread = t;
+ if (kthread_prio) {
+ sp.sched_priority = kthread_prio;
+ sched_setscheduler_nocheck(t, SCHED_FIFO, &sp);
+ }
+ wake_up_process(t);
raw_spin_unlock_irqrestore(&rnp->lock, flags);
}
rcu_spawn_nocb_kthreads();
diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h
index c303301..cfd62692 100644
--- a/kernel/rcu/tree_plugin.h
+++ b/kernel/rcu/tree_plugin.h
@@ -30,14 +30,20 @@
#include <linux/smpboot.h>
#include "../time/tick-internal.h"
-#define RCU_KTHREAD_PRIO 1
-
#ifdef CONFIG_RCU_BOOST
+
#include "../locking/rtmutex_common.h"
-#define RCU_BOOST_PRIO CONFIG_RCU_BOOST_PRIO
-#else
-#define RCU_BOOST_PRIO RCU_KTHREAD_PRIO
-#endif
+
+/*
+ * Control variables for per-CPU and per-rcu_node kthreads. These
+ * handle all flavors of RCU.
+ */
+static DEFINE_PER_CPU(struct task_struct *, rcu_cpu_kthread_task);
+DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_status);
+DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops);
+DEFINE_PER_CPU(char, rcu_cpu_has_work);
+
+#endif /* #ifdef CONFIG_RCU_BOOST */
#ifdef CONFIG_RCU_NOCB_CPU
static cpumask_var_t rcu_nocb_mask; /* CPUs to have callbacks offloaded. */
@@ -85,6 +91,9 @@
pr_info("\tBoot-time adjustment of leaf fanout to %d.\n", rcu_fanout_leaf);
if (nr_cpu_ids != NR_CPUS)
pr_info("\tRCU restricting CPUs from NR_CPUS=%d to nr_cpu_ids=%d.\n", NR_CPUS, nr_cpu_ids);
+#ifdef CONFIG_RCU_BOOST
+ pr_info("\tRCU kthread priority: %d.\n", kthread_prio);
+#endif
}
#ifdef CONFIG_TREE_PREEMPT_RCU
@@ -397,10 +406,18 @@
}
#ifdef CONFIG_RCU_BOOST
- /* Unboost if we were boosted. */
+ /*
+ * Unboost if we were boosted.
+ * Disable preemption to make sure completion is signalled
+ * without having the task de-scheduled with its priority
+ * lowered (in which case we're left with no boosted thread
+ * and possible RCU starvation).
+ */
if (drop_boost_mutex) {
+ preempt_disable();
rt_mutex_unlock(&rnp->boost_mtx);
complete(&rnp->boost_completion);
+ preempt_enable();
}
#endif /* #ifdef CONFIG_RCU_BOOST */
@@ -431,7 +448,7 @@
raw_spin_unlock_irqrestore(&rnp->lock, flags);
return;
}
- t = list_entry(rnp->gp_tasks,
+ t = list_entry(rnp->gp_tasks->prev,
struct task_struct, rcu_node_entry);
list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry)
sched_show_task(t);
@@ -496,7 +513,7 @@
if (!rcu_preempt_blocked_readers_cgp(rnp))
return 0;
rcu_print_task_stall_begin(rnp);
- t = list_entry(rnp->gp_tasks,
+ t = list_entry(rnp->gp_tasks->prev,
struct task_struct, rcu_node_entry);
list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry) {
pr_cont(" P%d", t->pid);
@@ -1326,7 +1343,7 @@
smp_mb__after_unlock_lock();
rnp->boost_kthread_task = t;
raw_spin_unlock_irqrestore(&rnp->lock, flags);
- sp.sched_priority = RCU_BOOST_PRIO;
+ sp.sched_priority = kthread_prio;
sched_setscheduler_nocheck(t, SCHED_FIFO, &sp);
wake_up_process(t); /* get to TASK_INTERRUPTIBLE quickly. */
return 0;
@@ -1343,7 +1360,7 @@
{
struct sched_param sp;
- sp.sched_priority = RCU_KTHREAD_PRIO;
+ sp.sched_priority = kthread_prio;
sched_setscheduler_nocheck(current, SCHED_FIFO, &sp);
}
diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile
index 55bb7af..66689d7 100644
--- a/kernel/sched/Makefile
+++ b/kernel/sched/Makefile
@@ -2,6 +2,15 @@
CFLAGS_REMOVE_clock.o = -pg
endif
+# KASAN instrumentation is temporarily disabled for energy.o due to the repeated
+# reports that caused the kernel to not boot as seen in b/31800756. Should a fix
+# be provided, this line can be removed again. But given that KCOV is also disabled
+# for this module, it might be worth thinking about whether or not we should also
+# just turn off KASAN instrumentation entirely here.
+KASAN_SANITIZE_core.o := n
+KASAN_SANITIZE_energy.o := n
+KASAN_SANITIZE_fair.o := n
+
# These files are disabled because they produce non-interesting flaky coverage
# that is not a function of syscall inputs. E.g. involuntary context switches.
KCOV_INSTRUMENT := n
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 4faddbb..2539abe 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -74,6 +74,7 @@
#include <linux/binfmts.h>
#include <linux/context_tracking.h>
#include <linux/compiler.h>
+#include <linux/cpufreq.h>
#include <asm/switch_to.h>
#include <asm/tlb.h>
@@ -93,26 +94,7 @@
#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>
#include "walt.h"
-
-void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period)
-{
- unsigned long delta;
- ktime_t soft, hard, now;
-
- for (;;) {
- if (hrtimer_active(period_timer))
- break;
-
- now = hrtimer_cb_get_time(period_timer);
- hrtimer_forward(period_timer, now, period);
-
- soft = hrtimer_get_softexpires(period_timer);
- hard = hrtimer_get_expires(period_timer);
- delta = ktime_to_ns(ktime_sub(hard, soft));
- __hrtimer_start_range_ns(period_timer, soft, delta,
- HRTIMER_MODE_ABS_PINNED, 0);
- }
-}
+#include "tune.h"
DEFINE_MUTEX(sched_domains_mutex);
DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
@@ -426,12 +408,11 @@
#ifdef CONFIG_SMP
-static int __hrtick_restart(struct rq *rq)
+static void __hrtick_restart(struct rq *rq)
{
struct hrtimer *timer = &rq->hrtick_timer;
- ktime_t time = hrtimer_get_softexpires(timer);
- return __hrtimer_start_range_ns(timer, time, 0, HRTIMER_MODE_ABS_PINNED, 0);
+ hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED);
}
/*
@@ -511,8 +492,8 @@
* doesn't make sense. Rely on vruntime for fairness.
*/
delay = max_t(u64, delay, 10000LL);
- __hrtimer_start_range_ns(&rq->hrtick_timer, ns_to_ktime(delay), 0,
- HRTIMER_MODE_REL_PINNED, 0);
+ hrtimer_start(&rq->hrtick_timer, ns_to_ktime(delay),
+ HRTIMER_MODE_REL_PINNED);
}
static inline void init_hrtick(void)
@@ -1047,7 +1028,11 @@
}
/*
- * Can drop rq->lock because from sched_class::switched_from() methods drop it.
+ * switched_from, switched_to and prio_changed must _NOT_ drop rq->lock,
+ * use the balance_callback list if you want balancing.
+ *
+ * this means any call to check_class_changed() must be followed by a call to
+ * balance_callback().
*/
static inline void check_class_changed(struct rq *rq, struct task_struct *p,
const struct sched_class *prev_class,
@@ -1056,7 +1041,7 @@
if (prev_class != p->sched_class) {
if (prev_class->switched_from)
prev_class->switched_from(rq, p);
- /* Possble rq->lock 'hole'. */
+
p->sched_class->switched_to(rq, p);
} else if (oldprio != p->prio || dl_task(p))
p->sched_class->prio_changed(rq, p, oldprio);
@@ -1313,7 +1298,7 @@
if (p->sched_class->migrate_task_rq)
p->sched_class->migrate_task_rq(p, new_cpu);
p->se.nr_migrations++;
- perf_sw_event(PERF_COUNT_SW_CPU_MIGRATIONS, 1, NULL, 0);
+ perf_sw_event_sched(PERF_COUNT_SW_CPU_MIGRATIONS, 1, 0);
walt_fixup_busy_time(p, new_cpu);
}
@@ -1727,12 +1712,16 @@
ttwu_do_wakeup(struct rq *rq, struct task_struct *p, int wake_flags)
{
check_preempt_curr(rq, p, wake_flags);
- trace_sched_wakeup(p, true);
-
p->state = TASK_RUNNING;
+ trace_sched_wakeup(p);
+
#ifdef CONFIG_SMP
- if (p->sched_class->task_woken)
+ if (p->sched_class->task_woken) {
+ /*
+ * XXX can drop rq->lock; most likely ok.
+ */
p->sched_class->task_woken(rq, p);
+ }
if (rq->idle_stamp) {
u64 delta = rq_clock(rq) - rq->idle_stamp;
@@ -1938,9 +1927,33 @@
if (!(p->state & state))
goto out;
+ trace_sched_waking(p);
+
success = 1; /* we're going to change ->state */
cpu = task_cpu(p);
+ /*
+ * Ensure we load p->on_rq _after_ p->state, otherwise it would
+ * be possible to, falsely, observe p->on_rq == 0 and get stuck
+ * in smp_cond_load_acquire() below.
+ *
+ * sched_ttwu_pending() try_to_wake_up()
+ * [S] p->on_rq = 1; [L] P->state
+ * UNLOCK rq->lock -----.
+ * \
+ * +--- RMB
+ * schedule() /
+ * LOCK rq->lock -----'
+ * UNLOCK rq->lock
+ *
+ * [task p]
+ * [S] p->state = UNINTERRUPTIBLE [L] p->on_rq
+ *
+ * Pairs with the UNLOCK+LOCK on rq->lock from the
+ * last wakeup of our task and the schedule that got our task
+ * current.
+ */
+ smp_rmb();
if (p->on_rq && ttwu_remote(p, wake_flags))
goto stat;
@@ -2118,10 +2131,15 @@
memset(&p->se.statistics, 0, sizeof(p->se.statistics));
#endif
+#ifdef CONFIG_CPU_FREQ_STAT
+ cpufreq_task_stats_init(p);
+#endif
+
RB_CLEAR_NODE(&p->dl.rb_node);
init_dl_task_timer(&p->dl);
__dl_clear_params(p);
+ init_rt_schedtune_timer(&p->rt);
INIT_LIST_HEAD(&p->rt.run_list);
#ifdef CONFIG_PREEMPT_NOTIFIERS
@@ -2425,7 +2443,7 @@
walt_mark_task_starting(p);
activate_task(rq, p, ENQUEUE_WAKEUP_NEW);
p->on_rq = TASK_ON_RQ_QUEUED;
- trace_sched_wakeup_new(p, true);
+ trace_sched_wakeup_new(p);
check_preempt_curr(rq, p, WF_FORK);
#ifdef CONFIG_SMP
if (p->sched_class->task_woken)
@@ -2582,23 +2600,35 @@
#ifdef CONFIG_SMP
/* rq->lock is NOT held, but preemption is disabled */
-static inline void post_schedule(struct rq *rq)
+static void __balance_callback(struct rq *rq)
{
- if (rq->post_schedule) {
- unsigned long flags;
+ struct callback_head *head, *next;
+ void (*func)(struct rq *rq);
+ unsigned long flags;
- raw_spin_lock_irqsave(&rq->lock, flags);
- if (rq->curr->sched_class->post_schedule)
- rq->curr->sched_class->post_schedule(rq);
- raw_spin_unlock_irqrestore(&rq->lock, flags);
+ raw_spin_lock_irqsave(&rq->lock, flags);
+ head = rq->balance_callback;
+ rq->balance_callback = NULL;
+ while (head) {
+ func = (void (*)(struct rq *))head->func;
+ next = head->next;
+ head->next = NULL;
+ head = next;
- rq->post_schedule = 0;
+ func(rq);
}
+ raw_spin_unlock_irqrestore(&rq->lock, flags);
+}
+
+static inline void balance_callback(struct rq *rq)
+{
+ if (unlikely(rq->balance_callback))
+ __balance_callback(rq);
}
#else
-static inline void post_schedule(struct rq *rq)
+static inline void balance_callback(struct rq *rq)
{
}
@@ -2619,7 +2649,7 @@
* FIXME: do we need to worry about rq being invalidated by the
* task_switch?
*/
- post_schedule(rq);
+ balance_callback(rq);
if (current->set_child_tid)
put_user(task_pid_vnr(current), current->set_child_tid);
@@ -2846,7 +2876,7 @@
static void sched_freq_tick_pelt(int cpu)
{
- unsigned long cpu_utilization = cpu_util(cpu, UTIL_EST);
+ unsigned long cpu_utilization = boosted_cpu_util(cpu);
unsigned long capacity_curr = capacity_curr_of(cpu);
struct sched_capacity_reqs *scr;
@@ -2880,6 +2910,10 @@
* NOTE: WALT tracks a single CPU signal for all the scheduling
* classes, thus this margin is going to be added to the DL class as
* well, which is something we do not do in sched_freq_tick_pelt case.
+ *
+ * TODO:
+ * Here we're adding margin, but we're also adding margin in cpufreq.
+ * There shouldn't be a double addition.
*/
cpu_utilization = add_capacity_margin(cpu_utilization);
if (cpu_utilization <= capacity_curr)
@@ -2891,7 +2925,6 @@
* extra boost.
*/
set_cfs_cpu_capacity(cpu, true, cpu_utilization);
-
}
#define _sched_freq_tick(cpu) sched_freq_tick_walt(cpu)
#else
@@ -3246,7 +3279,7 @@
} else
raw_spin_unlock_irq(&rq->lock);
- post_schedule(rq);
+ balance_callback(rq);
sched_preempt_enable_no_resched();
if (need_resched())
@@ -3509,7 +3542,11 @@
check_class_changed(rq, p, prev_class, oldprio);
out_unlock:
+ preempt_disable(); /* avoid rq from going away on us */
__task_rq_unlock(rq);
+
+ balance_callback(rq);
+ preempt_enable();
}
#endif
@@ -4054,10 +4091,17 @@
}
check_class_changed(rq, p, prev_class, oldprio);
+ preempt_disable(); /* avoid rq from going away on us */
task_rq_unlock(rq, p, &flags);
rt_mutex_adjust_pi(p);
+ /*
+ * Run balance callbacks after we've adjusted the PI chain.
+ */
+ balance_callback(rq);
+ preempt_enable();
+
return 0;
}
@@ -5002,14 +5046,16 @@
/*
* reset the NMI-timeout, listing all files on a slow
* console might take a lot of time:
+ * Also, reset softlockup watchdogs on all CPUs, because
+ * another CPU might be blocked waiting for us to process
+ * an IPI.
*/
touch_nmi_watchdog();
+ touch_all_softlockup_watchdogs();
if (!state_filter || (p->state & state_filter))
sched_show_task(p);
}
- touch_all_softlockup_watchdogs();
-
#ifdef CONFIG_SCHED_DEBUG
sysrq_sched_debug_show();
#endif
@@ -6408,6 +6454,28 @@
sd->groups->sge = fn(cpu);
}
+#ifdef CONFIG_SCHED_DEBUG
+void set_energy_aware()
+{
+ sched_feat_set("ENERGY_AWARE");
+}
+void clear_energy_aware()
+{
+ sched_feat_set("NO_ENERGY_AWARE");
+}
+#else
+struct static_key __read_mostly __energy_aware = STATIC_KEY_INIT_FALSE;
+
+void set_energy_aware()
+{
+ static_key_slow_inc(&__energy_aware);
+}
+void clear_energy_aware()
+{
+ static_key_slow_dec(&__energy_aware);
+}
+#endif /* CONFIG_SCHED_DEBUG */
+
/*
* Initializers for schedule domains
* Non-inlined to reduce accumulated stack pressure in build_sched_domains()
@@ -7516,7 +7584,7 @@
rq->sd = NULL;
rq->rd = NULL;
rq->cpu_capacity = rq->cpu_capacity_orig = SCHED_CAPACITY_SCALE;
- rq->post_schedule = 0;
+ rq->balance_callback = NULL;
rq->active_balance = 0;
rq->next_balance = jiffies;
rq->push_cpu = 0;
@@ -8402,10 +8470,8 @@
__refill_cfs_bandwidth_runtime(cfs_b);
/* restart the period timer (if active) to handle new period expiry */
- if (runtime_enabled && cfs_b->timer_active) {
- /* force a reprogram */
- __start_cfs_bandwidth(cfs_b, true);
- }
+ if (runtime_enabled)
+ start_cfs_bandwidth(cfs_b);
raw_spin_unlock_irq(&cfs_b->lock);
for_each_online_cpu(i) {
diff --git a/kernel/sched/cpufreq_sched.c b/kernel/sched/cpufreq_sched.c
index 8635dae0..b5ab896 100644
--- a/kernel/sched/cpufreq_sched.c
+++ b/kernel/sched/cpufreq_sched.c
@@ -237,10 +237,25 @@
scr = &per_cpu(cpu_sched_capacity_reqs, cpu);
- new_capacity = scr->cfs + scr->rt;
+#ifdef CONFIG_SCHED_WALT
+ if (!walt_disabled && sysctl_sched_use_walt_cpu_util) {
+ /*
+ * Same WALT signal is set at different places, take the max
+ * reported utilization
+ */
+ new_capacity = max(scr->cfs, scr->rt);
+ new_capacity = max(new_capacity, scr->dl);
+ } else {
+ /*
+ * For PELT, utilization is aggregated
+ */
+ new_capacity = scr->cfs + scr->rt + scr->dl;
+ }
+#else
+ new_capacity = scr->cfs + scr->rt + scr->dl;
+#endif
new_capacity = new_capacity * capacity_margin
/ SCHED_CAPACITY_SCALE;
- new_capacity += scr->dl;
if (new_capacity == scr->total)
return;
@@ -327,6 +342,9 @@
{
struct gov_data *gd = policy->governor_data;
+ if (!gd)
+ return -EBUSY;
+
clear_sched_freq();
if (cpufreq_driver_slow) {
kthread_stop(gd->task);
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index a2d36e8..a10023e 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -231,9 +231,17 @@
return dl_task(prev);
}
-static inline void set_post_schedule(struct rq *rq)
+static DEFINE_PER_CPU(struct callback_head, dl_balance_head);
+
+static void push_dl_tasks(struct rq *);
+
+static inline void queue_push_tasks(struct rq *rq)
{
- rq->post_schedule = has_pushable_dl_tasks(rq);
+ if (!has_pushable_dl_tasks(rq))
+ return;
+
+ queue_balance_callback(rq, &per_cpu(dl_balance_head, rq->cpu),
+ push_dl_tasks);
}
static struct rq *find_lock_later_rq(struct task_struct *task, struct rq *rq);
@@ -319,7 +327,7 @@
return 0;
}
-static inline void set_post_schedule(struct rq *rq)
+static inline void queue_push_tasks(struct rq *rq)
{
}
#endif /* CONFIG_SMP */
@@ -1186,7 +1194,7 @@
start_hrtick_dl(rq, p);
#endif
- set_post_schedule(rq);
+ queue_push_tasks(rq);
return p;
}
@@ -1607,11 +1615,6 @@
return ret;
}
-static void post_schedule_dl(struct rq *rq)
-{
- push_dl_tasks(rq);
-}
-
/*
* Since the task is not running and a reschedule is not going to happen
* anytime soon on its runqueue, we try pushing it away now.
@@ -1812,7 +1815,6 @@
.set_cpus_allowed = set_cpus_allowed_dl,
.rq_online = rq_online_dl,
.rq_offline = rq_offline_dl,
- .post_schedule = post_schedule_dl,
.task_woken = task_woken_dl,
#endif
diff --git a/kernel/sched/energy.c b/kernel/sched/energy.c
index b0656b7..30c82bb 100644
--- a/kernel/sched/energy.c
+++ b/kernel/sched/energy.c
@@ -27,6 +27,8 @@
#include <linux/sched_energy.h>
#include <linux/stddef.h>
+#include "sched.h"
+
struct sched_group_energy *sge_array[NR_CPUS][NR_SD_LEVELS];
static void free_resources(void)
@@ -59,12 +61,14 @@
for_each_possible_cpu(cpu) {
cn = of_get_cpu_node(cpu, NULL);
if (!cn) {
- pr_warn("CPU device node missing for CPU %d\n", cpu);
+ if (sched_feat(ENERGY_AWARE))
+ pr_warn("CPU device node missing for CPU %d\n", cpu);
return;
}
if (!of_find_property(cn, "sched-energy-costs", NULL)) {
- pr_warn("CPU device node has no sched-energy-costs\n");
+ if (sched_feat(ENERGY_AWARE))
+ pr_warn("CPU device node has no sched-energy-costs\n");
return;
}
@@ -75,7 +79,8 @@
prop = of_find_property(cp, "busy-cost-data", NULL);
if (!prop || !prop->value) {
- pr_warn("No busy-cost data, skipping sched_energy init\n");
+ if (sched_feat(ENERGY_AWARE))
+ pr_warn("No busy-cost data, skipping sched_energy init\n");
goto out;
}
@@ -97,7 +102,8 @@
prop = of_find_property(cp, "idle-cost-data", NULL);
if (!prop || !prop->value) {
- pr_warn("No idle-cost data, skipping sched_energy init\n");
+ if (sched_feat(ENERGY_AWARE))
+ pr_warn("No idle-cost data, skipping sched_energy init\n");
goto out;
}
@@ -117,6 +123,7 @@
}
pr_info("Sched-energy-costs installed from DT\n");
+ set_energy_aware();
return;
out:
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 2e73693..6a25aa1 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -3274,16 +3274,7 @@
if (cfs_b->quota == RUNTIME_INF)
amount = min_amount;
else {
- /*
- * If the bandwidth pool has become inactive, then at least one
- * period must have elapsed since the last consumption.
- * Refresh the global state and ensure bandwidth timer becomes
- * active.
- */
- if (!cfs_b->timer_active) {
- __refill_cfs_bandwidth_runtime(cfs_b);
- __start_cfs_bandwidth(cfs_b, false);
- }
+ start_cfs_bandwidth(cfs_b);
if (cfs_b->runtime > 0) {
amount = min(cfs_b->runtime, min_amount);
@@ -3432,6 +3423,7 @@
struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg);
struct sched_entity *se;
long task_delta, dequeue = 1;
+ bool empty;
se = cfs_rq->tg->se[cpu_of(rq_of(cfs_rq))];
@@ -3461,13 +3453,21 @@
cfs_rq->throttled = 1;
cfs_rq->throttled_clock = rq_clock(rq);
raw_spin_lock(&cfs_b->lock);
+ empty = list_empty(&cfs_rq->throttled_list);
+
/*
* Add to the _head_ of the list, so that an already-started
* distribute_cfs_runtime will not see us
*/
list_add_rcu(&cfs_rq->throttled_list, &cfs_b->throttled_cfs_rq);
- if (!cfs_b->timer_active)
- __start_cfs_bandwidth(cfs_b, false);
+
+ /*
+ * If we're the first throttled task, make sure the bandwidth
+ * timer is running.
+ */
+ if (empty)
+ start_cfs_bandwidth(cfs_b);
+
raw_spin_unlock(&cfs_b->lock);
}
@@ -3582,13 +3582,6 @@
if (cfs_b->idle && !throttled)
goto out_deactivate;
- /*
- * if we have relooped after returning idle once, we need to update our
- * status as actually running, so that other cpus doing
- * __start_cfs_bandwidth will stop trying to cancel us.
- */
- cfs_b->timer_active = 1;
-
__refill_cfs_bandwidth_runtime(cfs_b);
if (!throttled) {
@@ -3633,7 +3626,6 @@
return 0;
out_deactivate:
- cfs_b->timer_active = 0;
return 1;
}
@@ -3648,7 +3640,7 @@
* Are we near the end of the current quota period?
*
* Requires cfs_b->lock for hrtimer_expires_remaining to be safe against the
- * hrtimer base being cleared by __hrtimer_start_range_ns. In the case of
+ * hrtimer base being cleared by hrtimer_start. In the case of
* migrate_hrtimers, base is never cleared, so we are fine.
*/
static int runtime_refresh_within(struct cfs_bandwidth *cfs_b, u64 min_expire)
@@ -3676,8 +3668,9 @@
if (runtime_refresh_within(cfs_b, min_left))
return;
- start_bandwidth_timer(&cfs_b->slack_timer,
- ns_to_ktime(cfs_bandwidth_slack_period));
+ hrtimer_start(&cfs_b->slack_timer,
+ ns_to_ktime(cfs_bandwidth_slack_period),
+ HRTIMER_MODE_REL);
}
/* we know any runtime found here is valid as update_curr() precedes return */
@@ -3797,6 +3790,7 @@
{
struct cfs_bandwidth *cfs_b =
container_of(timer, struct cfs_bandwidth, slack_timer);
+
do_sched_cfs_slack_timer(cfs_b);
return HRTIMER_NORESTART;
@@ -3806,20 +3800,19 @@
{
struct cfs_bandwidth *cfs_b =
container_of(timer, struct cfs_bandwidth, period_timer);
- ktime_t now;
int overrun;
int idle = 0;
raw_spin_lock(&cfs_b->lock);
for (;;) {
- now = hrtimer_cb_get_time(timer);
- overrun = hrtimer_forward(timer, now, cfs_b->period);
-
+ overrun = hrtimer_forward_now(timer, cfs_b->period);
if (!overrun)
break;
idle = do_sched_cfs_period_timer(cfs_b, overrun);
}
+ if (idle)
+ cfs_b->period_active = 0;
raw_spin_unlock(&cfs_b->lock);
return idle ? HRTIMER_NORESTART : HRTIMER_RESTART;
@@ -3833,7 +3826,7 @@
cfs_b->period = ns_to_ktime(default_cfs_period());
INIT_LIST_HEAD(&cfs_b->throttled_cfs_rq);
- hrtimer_init(&cfs_b->period_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_init(&cfs_b->period_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
cfs_b->period_timer.function = sched_cfs_period_timer;
hrtimer_init(&cfs_b->slack_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
cfs_b->slack_timer.function = sched_cfs_slack_timer;
@@ -3845,28 +3838,15 @@
INIT_LIST_HEAD(&cfs_rq->throttled_list);
}
-/* requires cfs_b->lock, may release to reprogram timer */
-void __start_cfs_bandwidth(struct cfs_bandwidth *cfs_b, bool force)
+void start_cfs_bandwidth(struct cfs_bandwidth *cfs_b)
{
- /*
- * The timer may be active because we're trying to set a new bandwidth
- * period or because we're racing with the tear-down path
- * (timer_active==0 becomes visible before the hrtimer call-back
- * terminates). In either case we ensure that it's re-programmed
- */
- while (unlikely(hrtimer_active(&cfs_b->period_timer)) &&
- hrtimer_try_to_cancel(&cfs_b->period_timer) < 0) {
- /* bounce the lock to allow do_sched_cfs_period_timer to run */
- raw_spin_unlock(&cfs_b->lock);
- cpu_relax();
- raw_spin_lock(&cfs_b->lock);
- /* if someone else restarted the timer then we're done */
- if (!force && cfs_b->timer_active)
- return;
- }
+ lockdep_assert_held(&cfs_b->lock);
- cfs_b->timer_active = 1;
- start_bandwidth_timer(&cfs_b->period_timer, cfs_b->period);
+ if (!cfs_b->period_active) {
+ cfs_b->period_active = 1;
+ hrtimer_forward_now(&cfs_b->period_timer, cfs_b->period);
+ hrtimer_start_expires(&cfs_b->period_timer, HRTIMER_MODE_ABS_PINNED);
+ }
}
static void destroy_cfs_bandwidth(struct cfs_bandwidth *cfs_b)
@@ -4009,9 +3989,6 @@
#ifdef CONFIG_SMP
static bool cpu_overutilized(int cpu);
-static inline unsigned long boosted_cpu_util(int cpu);
-#else
-#define boosted_cpu_util(cpu) cpu_util(cpu, UTIL_EST)
#endif
#ifdef CONFIG_CPU_FREQ_GOV_SCHED
@@ -4081,6 +4058,25 @@
#ifdef CONFIG_SMP
+ /*
+ * Update SchedTune accouting.
+ *
+ * We do it before updating the CPU capacity to ensure the
+ * boost value of the current task is accounted for in the
+ * selection of the OPP.
+ *
+ * We do it also in the case where we enqueue a trottled task;
+ * we could argue that a throttled task should not boost a CPU,
+ * however:
+ * a) properly implementing CPU boosting considering throttled
+ * tasks will increase a lot the complexity of the solution
+ * b) it's not easy to quantify the benefits introduced by
+ * such a more complex solution.
+ * Thus, for the time being we go for the simple solution and boost
+ * also for throttled RQs.
+ */
+ schedtune_enqueue_task(p, cpu_of(rq));
+
if (!se) {
walt_inc_cumulative_runnable_avg(rq, p);
if (!task_new && !rq->rd->overutilized &&
@@ -4106,9 +4102,6 @@
/* Update RQ estimated utilization */
cfs_rq->avg.util_est += task_util_est(p);
- /* Update SchedTune accouting */
- schedtune_enqueue_task(p, cpu_of(rq));
-
#endif /* CONFIG_SMP */
hrtick_update(rq);
@@ -4175,6 +4168,15 @@
#ifdef CONFIG_SMP
+ /*
+ * Update SchedTune accouting
+ *
+ * We do it before updating the CPU capacity to ensure the
+ * boost value of the current task is accounted for in the
+ * selection of the OPP.
+ */
+ schedtune_dequeue_task(p, cpu_of(rq));
+
if (!se) {
walt_dec_cumulative_runnable_avg(rq, p);
@@ -4208,9 +4210,6 @@
if (task_sleep)
p->se.avg.util_est = p->se.avg.util_avg;
- /* Update SchedTune accouting */
- schedtune_dequeue_task(p, cpu_of(rq));
-
#endif /* CONFIG_SMP */
hrtick_update(rq);
@@ -4618,29 +4617,6 @@
return sched_feat(ENERGY_AWARE);
}
-struct energy_env {
- struct sched_group *sg_top;
- struct sched_group *sg_cap;
- int cap_idx;
- int util_delta;
- int src_cpu;
- int dst_cpu;
- int energy;
- int payoff;
- struct task_struct *task;
- struct {
- int before;
- int after;
- int delta;
- int diff;
- } nrg;
- struct {
- int before;
- int after;
- int delta;
- } cap;
-};
-
/*
* __cpu_norm_util() returns the cpu util relative to a specific capacity,
* i.e. it's busy ratio, in the range [0..SCHED_LOAD_SCALE] which is useful for
@@ -4698,10 +4674,11 @@
* estimate (more busy).
*/
static unsigned
-long group_norm_util(struct energy_env *eenv, struct sched_group *sg)
+long group_norm_util(struct energy_env *eenv)
{
int i, delta;
unsigned long util_sum = 0;
+ struct sched_group *sg = eenv->sg;
unsigned long capacity = sg->sge->cap_states[eenv->cap_idx].cap;
for_each_cpu(i, sched_group_cpus(sg)) {
@@ -4714,20 +4691,60 @@
return util_sum;
}
-static int find_new_capacity(struct energy_env *eenv,
- const struct sched_group_energy const *sge)
+#ifdef CONFIG_SCHED_TUNE
+static inline int
+find_min_capacity(struct energy_env *eenv)
{
- int idx;
+ const struct sched_group_energy const *sge = eenv->sg->sge;
+ unsigned long min_capacity, cur_capacity;
+ int min_cap_idx, cap_idx;
+ unsigned long min_util;
+
+ /* Non boosted tasks do not affect the minimum capacity */
+ if (!schedtune_task_boost(eenv->task))
+ return eenv->cap_idx;
+
+ /* Find minimum capacity to satify the task boost value */
+ min_util = boosted_task_util(eenv->task);
+ for (min_cap_idx = 0; min_cap_idx < (sge->nr_cap_states-1); min_cap_idx++) {
+ if (sge->cap_states[min_cap_idx].cap >= min_util)
+ break;
+ }
+ min_capacity = sge->cap_states[min_cap_idx].cap;
+
+ /* The current capacity is the one computed by the caller */
+ cur_capacity = sge->cap_states[eenv->cap_idx].cap;
+
+ /*
+ * Compute the minumum CPU capacity required to support task boosting
+ * within this SG.
+ */
+ cur_capacity = max(min_capacity, cur_capacity);
+ cap_idx = max(eenv->cap_idx, min_cap_idx);
+
+ return cap_idx;
+}
+#else
+#define find_min_capacity(eenv) eenv->cap_idx
+#endif /* CONFIG_SCHED_TUNE */
+
+static int find_new_capacity(struct energy_env *eenv)
+{
+ const struct sched_group_energy const *sge = eenv->sg->sge;
unsigned long util = group_max_util(eenv);
+ int idx;
for (idx = 0; idx < sge->nr_cap_states; idx++) {
if (sge->cap_states[idx].cap >= util)
break;
}
-
+ /* Keep track of SG's capacity index */
eenv->cap_idx = idx;
- return idx;
+ /* Update SG's capacity based on boost value of the current task */
+ eenv->cap_idx = find_min_capacity(eenv);
+
+ return eenv->cap_idx;
}
static int group_idle_state(struct sched_group *sg)
@@ -4745,6 +4762,75 @@
}
/*
+ * Compute energy for the eenv's SG (i.e. eenv->sg).
+ *
+ * This works in two iterations:
+ * first iteration, before moving the utilization, i.e.
+ * util_delta == 0
+ * second iteration, after moving the utilization, i.e.
+ * util_delta != 0
+ */
+static void before_after_energy(struct energy_env *eenv)
+{
+
+ int sg_busy_energy, sg_idle_energy;
+ struct sched_group *sg = eenv->sg;
+ unsigned long util_delta;
+ unsigned long group_util;
+ int cap_idx, idle_idx;
+ int total_energy = 0;
+ unsigned int cap;
+ bool after;
+
+ util_delta = eenv->util_delta;
+ eenv->util_delta = 0;
+ after = false;
+
+compute_after:
+
+ idle_idx = group_idle_state(sg);
+
+ cap_idx = find_new_capacity(eenv);
+ group_util = group_norm_util(eenv);
+ cap = sg->sge->cap_states[cap_idx].cap;
+
+ sg_busy_energy = group_util * sg->sge->cap_states[cap_idx].power;
+ sg_busy_energy >>= SCHED_CAPACITY_SHIFT;
+
+ sg_idle_energy = SCHED_CAPACITY_SCALE - group_util;
+ sg_idle_energy *= sg->sge->idle_states[idle_idx].power;
+ sg_idle_energy >>= SCHED_CAPACITY_SHIFT;
+
+ total_energy = sg_busy_energy + sg_idle_energy;
+
+ /* Account for "after" metrics */
+ if (after) {
+ if (sg->group_weight == 1 &&
+ cpumask_test_cpu(eenv->dst_cpu, sched_group_cpus(sg))) {
+ eenv->after.utilization = group_util;
+ eenv->after.capacity = cap;
+ }
+ eenv->after.energy += total_energy;
+ return;
+ }
+
+ /* Account for "before" metrics */
+ if (sg->group_weight == 1 &&
+ cpumask_test_cpu(eenv->src_cpu, sched_group_cpus(sg))) {
+ eenv->after.utilization = group_util;
+ eenv->before.capacity = cap;
+ }
+ eenv->before.energy += total_energy;
+
+ /* Setup eenv for the "after" case */
+ eenv->util_delta = util_delta;
+ after = true;
+
+ goto compute_after;
+
+}
+
+/*
* sched_group_energy(): Computes the absolute energy consumption of cpus
* belonging to the sched_group including shared resources shared only by
* members of the group. Iterates over all cpus in the hierarchy below the
@@ -4757,9 +4843,9 @@
static int sched_group_energy(struct energy_env *eenv)
{
struct sched_domain *sd;
- int cpu, total_energy = 0;
struct cpumask visit_cpus;
struct sched_group *sg;
+ int cpu;
WARN_ON(!eenv->sg_top->sge);
@@ -4775,16 +4861,7 @@
* sched_group?
*/
sd = rcu_dereference(per_cpu(sd_scs, cpu));
-
- if (!sd)
- /*
- * We most probably raced with hotplug; returning a
- * wrong energy estimation is better than entering an
- * infinite loop.
- */
- return -EINVAL;
-
- if (sd->parent)
+ if (sd && sd->parent)
sg_shared_cap = sd->parent->groups;
for_each_domain(cpu, sd) {
@@ -4795,41 +4872,12 @@
break;
do {
- unsigned long group_util;
- int sg_busy_energy, sg_idle_energy;
- int cap_idx, idle_idx;
-
+ eenv->sg_cap = sg;
if (sg_shared_cap && sg_shared_cap->group_weight >= sg->group_weight)
eenv->sg_cap = sg_shared_cap;
- else
- eenv->sg_cap = sg;
- cap_idx = find_new_capacity(eenv, sg->sge);
-
- if (sg->group_weight == 1) {
- /* Remove capacity of src CPU (before task move) */
- if (eenv->util_delta == 0 &&
- cpumask_test_cpu(eenv->src_cpu, sched_group_cpus(sg))) {
- eenv->cap.before = sg->sge->cap_states[cap_idx].cap;
- eenv->cap.delta -= eenv->cap.before;
- }
- /* Add capacity of dst CPU (after task move) */
- if (eenv->util_delta != 0 &&
- cpumask_test_cpu(eenv->dst_cpu, sched_group_cpus(sg))) {
- eenv->cap.after = sg->sge->cap_states[cap_idx].cap;
- eenv->cap.delta += eenv->cap.after;
- }
- }
-
- idle_idx = group_idle_state(sg);
- group_util = group_norm_util(eenv, sg);
- sg_busy_energy = (group_util * sg->sge->cap_states[cap_idx].power)
- >> SCHED_CAPACITY_SHIFT;
- sg_idle_energy = ((SCHED_LOAD_SCALE-group_util)
- * sg->sge->idle_states[idle_idx].power)
- >> SCHED_CAPACITY_SHIFT;
-
- total_energy += sg_busy_energy + sg_idle_energy;
+ eenv->sg = sg;
+ before_after_energy(eenv);
if (!sd->child)
cpumask_xor(&visit_cpus, &visit_cpus, sched_group_cpus(sg));
@@ -4844,7 +4892,6 @@
continue;
}
- eenv->energy = total_energy;
return 0;
}
@@ -4853,6 +4900,54 @@
return cpu != -1 && cpumask_test_cpu(cpu, sched_group_cpus(sg));
}
+static inline int normalize_energy(int energy_diff);
+
+#define eenv_before(__X) eenv->before.__X
+#define eenv_after(__X) eenv->after.__X
+#define eenv_delta(__X) eenv->after.__X - eenv->before.__X
+
+static inline void
+__update_perf_energy_deltas(struct energy_env *eenv)
+{
+ unsigned long task_util = eenv->util_delta;
+
+ /*
+ * SpeedUp Index
+ *
+ * SPI := cpu_capacity - task_util
+ *
+ * which estimate how sooner a task will complete when running
+ * on an higher OPP wrt the minimum required.
+ */
+ eenv_before(speedup_idx) = eenv_before(capacity) - task_util;
+ eenv_after(speedup_idx) = eenv_after(capacity) - task_util;
+
+ /*
+ * Delay Index
+ *
+ * DLI := 1024 * (cpu_util - task_util) / cpu_util
+ *
+ * which represents the "fraction" of CPU bandwidth consumed by other
+ * tasks in the worst case, i.e. assuming all other tasks runs before.
+ *
+ * NOTE: in the above formula we assume that "cpu_util" includes
+ * already the task utilization.
+ */
+ eenv_before(delay_idx) = SCHED_CAPACITY_SCALE;
+ eenv_before(delay_idx) *= (eenv_before(utilization) - task_util);
+ eenv_before(delay_idx) /= eenv_before(utilization);
+ eenv_after(delay_idx) = SCHED_CAPACITY_SCALE;
+ eenv_after(delay_idx) *= (eenv_after(utilization) - task_util);
+ eenv_after(delay_idx) /= eenv_after(utilization);
+
+ /* Performance Variation */
+ eenv->prf_delta = eenv_delta(speedup_idx) - eenv_delta(delay_idx);
+
+ /* Energy Variation */
+ eenv->nrg_delta = normalize_energy(eenv_delta(energy));
+
+}
+
/*
* energy_diff(): Estimate the energy impact of changing the utilization
* distribution. eenv specifies the change: utilisation amount, source, and
@@ -4864,57 +4959,34 @@
{
struct sched_domain *sd;
struct sched_group *sg;
- int sd_cpu = -1, energy_before = 0, energy_after = 0;
-
- struct energy_env eenv_before = {
- .util_delta = 0,
- .src_cpu = eenv->src_cpu,
- .dst_cpu = eenv->dst_cpu,
- .nrg = { 0, 0, 0, 0},
- .cap = { 0, 0, 0 },
- };
+ int sd_cpu = -1;
if (eenv->src_cpu == eenv->dst_cpu)
return 0;
sd_cpu = (eenv->src_cpu != -1) ? eenv->src_cpu : eenv->dst_cpu;
sd = rcu_dereference(per_cpu(sd_ea, sd_cpu));
-
if (!sd)
return 0; /* Error */
sg = sd->groups;
-
do {
- if (cpu_in_sg(sg, eenv->src_cpu) || cpu_in_sg(sg, eenv->dst_cpu)) {
- eenv_before.sg_top = eenv->sg_top = sg;
+ if (!cpu_in_sg(sg, eenv->src_cpu) &&
+ !cpu_in_sg(sg, eenv->dst_cpu))
+ continue;
- if (sched_group_energy(&eenv_before))
- return 0; /* Invalid result abort */
- energy_before += eenv_before.energy;
+ eenv->sg_top = sg;
+ if (sched_group_energy(eenv))
+ return 0; /* Invalid result abort */
- /* Keep track of SRC cpu (before) capacity */
- eenv->cap.before = eenv_before.cap.before;
- eenv->cap.delta = eenv_before.cap.delta;
-
- if (sched_group_energy(eenv))
- return 0; /* Invalid result abort */
- energy_after += eenv->energy;
- }
} while (sg = sg->next, sg != sd->groups);
- eenv->nrg.before = energy_before;
- eenv->nrg.after = energy_after;
- eenv->nrg.diff = eenv->nrg.after - eenv->nrg.before;
- eenv->payoff = 0;
+ __update_perf_energy_deltas(eenv);
- trace_sched_energy_diff(eenv->task,
- eenv->src_cpu, eenv->dst_cpu, eenv->util_delta,
- eenv->nrg.before, eenv->nrg.after, eenv->nrg.diff,
- eenv->cap.before, eenv->cap.after, eenv->cap.delta,
- eenv->nrg.delta, eenv->payoff);
+ trace_sched_energy_diff(eenv);
+ trace_sched_energy_perf_deltas(eenv);
- return eenv->nrg.diff;
+ return eenv->nrg_delta;
}
#ifdef CONFIG_SCHED_TUNE
@@ -4923,16 +4995,16 @@
/*
* System energy normalization
- * Returns the normalized value, in the range [0..SCHED_LOAD_SCALE],
+ * Returns the normalized value, in the range [0..SCHED_CAPACITY_SCALE],
* corresponding to the specified energy variation.
*/
static inline int
normalize_energy(int energy_diff)
{
u32 normalized_nrg;
+#ifdef CONFIG_SCHED_DEBUG
int max_delta;
-#ifdef CONFIG_SCHED_DEBUG
/* Check for boundaries */
max_delta = schedtune_target_nrg.max_power;
max_delta -= schedtune_target_nrg.min_power;
@@ -4943,7 +5015,7 @@
normalized_nrg = (energy_diff < 0) ? -energy_diff : energy_diff;
/* Scale by energy magnitude */
- normalized_nrg <<= SCHED_LOAD_SHIFT;
+ normalized_nrg <<= SCHED_CAPACITY_SHIFT;
/* Normalize on max energy for target platform */
normalized_nrg = reciprocal_divide(
@@ -4952,26 +5024,29 @@
return (energy_diff < 0) ? -normalized_nrg : normalized_nrg;
}
+static inline bool filter_energy(void)
+{
+ return sched_feat(ENERGY_FILTER);
+}
+
static inline int
energy_diff(struct energy_env *eenv)
{
- int boost = schedtune_task_boost(eenv->task);
- int nrg_delta;
+ int boost;
/* Conpute "absolute" energy diff */
__energy_diff(eenv);
+ if (!filter_energy())
+ return eenv->nrg_delta;
/* Return energy diff when boost margin is 0 */
+ boost = schedtune_task_boost(eenv->task);
if (boost == 0)
- return eenv->nrg.diff;
-
- /* Compute normalized energy diff */
- nrg_delta = normalize_energy(eenv->nrg.diff);
- eenv->nrg.delta = nrg_delta;
+ return eenv->nrg_delta;
eenv->payoff = schedtune_accept_deltas(
- eenv->nrg.delta,
- eenv->cap.delta,
+ eenv->nrg_delta,
+ eenv->prf_delta,
eenv->task);
/*
@@ -5079,24 +5154,8 @@
return 1;
}
-static inline unsigned long task_util(struct task_struct *p, bool use_pelt)
-{
-
-#ifdef CONFIG_SCHED_WALT
- if (!walt_disabled && sysctl_sched_use_walt_task_util) {
- unsigned long demand = p->ravg.demand;
- return (demand << 10) / walt_ravg_window;
- }
-#endif
- if (use_util_est() && !use_pelt)
- return p->se.avg.util_est;
- return p->se.avg.util_avg;
-}
-
unsigned int capacity_margin = 1280; /* ~20% margin */
-static inline unsigned long boosted_task_util(struct task_struct *task);
-
static inline bool __task_fits(struct task_struct *p, int cpu, int util)
{
unsigned long capacity = capacity_of(cpu);
@@ -5130,112 +5189,6 @@
return (capacity_of(cpu) * 1024) < (cpu_util(cpu, UTIL_AVG) * capacity_margin);
}
-#ifdef CONFIG_SCHED_TUNE
-
-static long
-schedtune_margin(unsigned long signal, long boost)
-{
- long long margin = 0;
-
- /*
- * Signal proportional compensation (SPC)
- *
- * The Boost (B) value is used to compute a Margin (M) which is
- * proportional to the complement of the original Signal (S):
- * M = B * (SCHED_LOAD_SCALE - S), if B is positive
- * M = B * S, if B is negative
- * The obtained M could be used by the caller to "boost" S.
- */
- if (boost >= 0) {
- margin = SCHED_LOAD_SCALE - signal;
- margin *= boost;
- } else
- margin = -signal * boost;
- /*
- * Fast integer division by constant:
- * Constant : (C) = 100
- * Precision : 0.1% (P) = 0.1
- * Reference : C * 100 / P (R) = 100000
- *
- * Thus:
- * Shift bits : ceil(log(R,2)) (S) = 17
- * Mult const : round(2^S/C) (M) = 1311
- *
- *
- */
- margin *= 1311;
- margin >>= 17;
-
- if (boost < 0)
- margin *= -1;
- return margin;
-}
-
-static inline int
-schedtune_cpu_margin(unsigned long util, int cpu)
-{
- int boost = schedtune_cpu_boost(cpu);
-
- if (boost == 0)
- return 0;
-
- return schedtune_margin(util, boost);
-}
-
-static inline long
-schedtune_task_margin(struct task_struct *task)
-{
- int boost = schedtune_task_boost(task);
- unsigned long util;
- long margin;
-
- if (boost == 0)
- return 0;
-
- util = task_util(task, UTIL_AVG);
- margin = schedtune_margin(util, boost);
-
- return margin;
-}
-
-#else /* CONFIG_SCHED_TUNE */
-
-static inline int
-schedtune_cpu_margin(unsigned long util, int cpu)
-{
- return 0;
-}
-
-static inline int
-schedtune_task_margin(struct task_struct *task)
-{
- return 0;
-}
-
-#endif /* CONFIG_SCHED_TUNE */
-
-static inline unsigned long
-boosted_cpu_util(int cpu)
-{
- unsigned long util = cpu_util(cpu, UTIL_EST);
- long margin = schedtune_cpu_margin(util, cpu);
-
- trace_sched_boost_cpu(cpu, util, margin);
-
- return util + margin;
-}
-
-static inline unsigned long
-boosted_task_util(struct task_struct *task)
-{
- unsigned long util = task_util(task, UTIL_EST);
- long margin = schedtune_task_margin(task);
-
- trace_sched_boost_task(task, util, margin);
-
- return util + margin;
-}
-
/*
* find_idlest_group finds and returns the least busy CPU group within the
* domain.
@@ -5462,9 +5415,10 @@
int best_idle_cpu = -1;
int best_idle_cstate = INT_MAX;
int backup_cpu = -1;
- unsigned long task_util_boosted, new_util;
+ unsigned long min_util;
+ unsigned long new_util;
- task_util_boosted = boosted_task_util(p);
+ min_util = boosted_task_util(p);
for (iter_cpu = 0; iter_cpu < NR_CPUS; iter_cpu++) {
int cur_capacity;
struct rq *rq;
@@ -5483,13 +5437,14 @@
* so prev_cpu will receive a negative bias due to the double
* accounting. However, the blocked utilization may be zero.
*/
- new_util = cpu_util(i, UTIL_EST) + task_util_boosted;
+ new_util = cpu_util(i, UTIL_EST) + task_util(p, UTIL_EST);
/*
* Ensure minimum capacity to grant the required boost.
* The target CPU can be already at a capacity level higher
* than the one required to boost the task.
*/
+ new_util = max(min_util, new_util);
if (new_util > capacity_orig_of(i))
continue;
@@ -5514,17 +5469,19 @@
if (new_util < cur_capacity) {
if (cpu_rq(i)->nr_running) {
- if(prefer_idle) {
- // Find a target cpu with lowest
- // utilization.
+ if (!prefer_idle) {
+ /* Find a target cpu with highest
+ * utilization.
+ */
if (target_util == 0 ||
target_util < new_util) {
target_cpu = i;
target_util = new_util;
}
} else {
- // Find a target cpu with highest
- // utilization.
+ /* Find a target cpu with lowest
+ * utilization.
+ */
if (target_util == 0 ||
target_util > new_util) {
target_cpu = i;
@@ -5555,23 +5512,16 @@
return target_cpu;
}
-static int energy_aware_wake_cpu(struct task_struct *p, int target, int sync)
+static int energy_aware_wake_cpu(struct task_struct *p, int target)
{
struct sched_domain *sd;
struct sched_group *sg, *sg_target;
int target_max_cap = INT_MAX;
int target_cpu = task_cpu(p);
- unsigned long task_util_boosted, new_util;
+ unsigned long min_util;
+ unsigned long new_util;
int i;
- if (sysctl_sched_sync_hint_enable && sync) {
- int cpu = smp_processor_id();
- cpumask_t search_cpus;
- cpumask_and(&search_cpus, tsk_cpus_allowed(p), cpu_online_mask);
- if (cpumask_test_cpu(cpu, &search_cpus))
- return cpu;
- }
-
sd = rcu_dereference(per_cpu(sd_ea, task_cpu(p)));
if (!sd)
@@ -5605,21 +5555,22 @@
}
} while (sg = sg->next, sg != sd->groups);
- task_util_boosted = boosted_task_util(p);
/* Find cpu with sufficient capacity */
+ min_util = boosted_task_util(p);
for_each_cpu_and(i, tsk_cpus_allowed(p), sched_group_cpus(sg_target)) {
/*
* p's blocked utilization is still accounted for on prev_cpu
* so prev_cpu will receive a negative bias due to the double
* accounting. However, the blocked utilization may be zero.
*/
- new_util = cpu_util(i, UTIL_EST) + task_util_boosted;
+ new_util = cpu_util(i, UTIL_EST) + task_util(p, UTIL_EST);
/*
* Ensure minimum capacity to grant the required boost.
* The target CPU can be already at a capacity level higher
* than the one required to boost the task.
*/
+ new_util = max(min_util, new_util);
if (new_util > capacity_orig_of(i))
continue;
@@ -5723,10 +5674,26 @@
}
if (!sd) {
- if (energy_aware() && !cpu_rq(cpu)->rd->overutilized)
- new_cpu = energy_aware_wake_cpu(p, prev_cpu, sync);
- else if (sd_flag & SD_BALANCE_WAKE) /* XXX always ? */
- new_cpu = select_idle_sibling(p, new_cpu);
+ int sync_used = 0;
+ bool about_to_idle = (cpu_rq(cpu)->nr_running < 2);
+
+ if (sysctl_sched_sync_hint_enable && sync
+ && about_to_idle) {
+ cpumask_t search_cpus;
+ cpumask_and(&search_cpus, tsk_cpus_allowed(p),
+ cpu_online_mask);
+ if (cpumask_test_cpu(cpu, &search_cpus)) {
+ sync_used = 1;
+ new_cpu = cpu;
+ }
+ }
+
+ if (!sync_used) {
+ if (energy_aware() && !cpu_rq(cpu)->rd->overutilized)
+ new_cpu = energy_aware_wake_cpu(p, prev_cpu);
+ else if (sd_flag & SD_BALANCE_WAKE) /* XXX always ? */
+ new_cpu = select_idle_sibling(p, new_cpu);
+ }
} else while (sd) {
struct sched_group *group;
@@ -8213,7 +8180,7 @@
struct sched_domain *sd;
int pulled_task = 0;
u64 curr_cost = 0;
- long removed_util;
+ long removed_util = 0;
idle_enter_fair(this_rq);
diff --git a/kernel/sched/features.h b/kernel/sched/features.h
index 2666a5c..a93988d 100644
--- a/kernel/sched/features.h
+++ b/kernel/sched/features.h
@@ -51,6 +51,19 @@
*/
SCHED_FEAT(TTWU_QUEUE, true)
+#ifdef HAVE_RT_PUSH_IPI
+/*
+ * In order to avoid a thundering herd attack of CPUs that are
+ * lowering their priorities at the same time, and there being
+ * a single CPU that has an RT task that can migrate and is waiting
+ * to run, where the other CPUs will try to take that CPUs
+ * rq lock and possibly create a large contention, sending an
+ * IPI to that CPU and let that CPU push the RT task to where
+ * it should go may be a better scenario.
+ */
+SCHED_FEAT(RT_PUSH_IPI, true)
+#endif
+
SCHED_FEAT(FORCE_SD_OVERLAP, false)
SCHED_FEAT(RT_RUNTIME_SHARE, true)
SCHED_FEAT(LB_MIN, false)
@@ -85,9 +98,16 @@
* Energy aware scheduling. Use platform energy model to guide scheduling
* decisions optimizing for energy efficiency.
*/
-SCHED_FEAT(ENERGY_AWARE, true)
+SCHED_FEAT(ENERGY_AWARE, false)
/*
* UtilEstimation. Use estimated CPU utiliation.
*/
SCHED_FEAT(UTIL_EST, false)
+
+/*
+ * SchedTune. Use Performance/Energy filtering function to evaluate the
+ * trade off between energy consumption and performance impact when comparing
+ * previous and next candidate CPUs.
+ */
+SCHED_FEAT(ENERGY_FILTER, true)
diff --git a/kernel/sched/loadavg.c b/kernel/sched/loadavg.c
index ef71590..b0b93fd 100644
--- a/kernel/sched/loadavg.c
+++ b/kernel/sched/loadavg.c
@@ -99,10 +99,13 @@
static unsigned long
calc_load(unsigned long load, unsigned long exp, unsigned long active)
{
- load *= exp;
- load += active * (FIXED_1 - exp);
- load += 1UL << (FSHIFT - 1);
- return load >> FSHIFT;
+ unsigned long newload;
+
+ newload = load * exp + active * (FIXED_1 - exp);
+ if (active >= load)
+ newload += FIXED_1-1;
+
+ return newload / FIXED_1;
}
#ifdef CONFIG_NO_HZ_COMMON
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 56316f2..d1c0952 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -7,8 +7,11 @@
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/irq_work.h>
+#include <linux/hrtimer.h>
#include "walt.h"
+#include "tune.h"
int sched_rr_timeslice = RR_TIMESLICE;
@@ -20,19 +23,22 @@
{
struct rt_bandwidth *rt_b =
container_of(timer, struct rt_bandwidth, rt_period_timer);
- ktime_t now;
- int overrun;
int idle = 0;
+ int overrun;
+ raw_spin_lock(&rt_b->rt_runtime_lock);
for (;;) {
- now = hrtimer_cb_get_time(timer);
- overrun = hrtimer_forward(timer, now, rt_b->rt_period);
-
+ overrun = hrtimer_forward_now(timer, rt_b->rt_period);
if (!overrun)
break;
+ raw_spin_unlock(&rt_b->rt_runtime_lock);
idle = do_sched_rt_period_timer(rt_b, overrun);
+ raw_spin_lock(&rt_b->rt_runtime_lock);
}
+ if (idle)
+ rt_b->rt_period_active = 0;
+ raw_spin_unlock(&rt_b->rt_runtime_lock);
return idle ? HRTIMER_NORESTART : HRTIMER_RESTART;
}
@@ -54,14 +60,19 @@
if (!rt_bandwidth_enabled() || rt_b->rt_runtime == RUNTIME_INF)
return;
- if (hrtimer_active(&rt_b->rt_period_timer))
- return;
-
raw_spin_lock(&rt_b->rt_runtime_lock);
- start_bandwidth_timer(&rt_b->rt_period_timer, rt_b->rt_period);
+ if (!rt_b->rt_period_active) {
+ rt_b->rt_period_active = 1;
+ hrtimer_forward_now(&rt_b->rt_period_timer, rt_b->rt_period);
+ hrtimer_start_expires(&rt_b->rt_period_timer, HRTIMER_MODE_ABS_PINNED);
+ }
raw_spin_unlock(&rt_b->rt_runtime_lock);
}
+#ifdef CONFIG_SMP
+static void push_irq_work_func(struct irq_work *work);
+#endif
+
void init_rt_rq(struct rt_rq *rt_rq, struct rq *rq)
{
struct rt_prio_array *array;
@@ -81,7 +92,14 @@
rt_rq->rt_nr_migratory = 0;
rt_rq->overloaded = 0;
plist_head_init(&rt_rq->pushable_tasks);
+
+#ifdef HAVE_RT_PUSH_IPI
+ rt_rq->push_flags = 0;
+ rt_rq->push_cpu = nr_cpu_ids;
+ raw_spin_lock_init(&rt_rq->push_lock);
+ init_irq_work(&rt_rq->push_work, push_irq_work_func);
#endif
+#endif /* CONFIG_SMP */
/* We start is dequeued state, because no RT tasks are queued */
rt_rq->rt_queued = 0;
@@ -247,7 +265,7 @@
#ifdef CONFIG_SMP
-static int pull_rt_task(struct rq *this_rq);
+static void pull_rt_task(struct rq *this_rq);
static inline bool need_pull_rt_task(struct rq *rq, struct task_struct *prev)
{
@@ -341,13 +359,25 @@
return !plist_head_empty(&rq->rt.pushable_tasks);
}
-static inline void set_post_schedule(struct rq *rq)
+static DEFINE_PER_CPU(struct callback_head, rt_push_head);
+static DEFINE_PER_CPU(struct callback_head, rt_pull_head);
+
+static void push_rt_tasks(struct rq *);
+static void pull_rt_task(struct rq *);
+
+static inline void queue_push_tasks(struct rq *rq)
{
- /*
- * We detect this state here so that we can avoid taking the RQ
- * lock again later if there is no need to push
- */
- rq->post_schedule = has_pushable_tasks(rq);
+ if (!has_pushable_tasks(rq))
+ return;
+
+ queue_balance_callback(rq, &per_cpu(rt_push_head, rq->cpu),
+ push_rt_tasks);
+}
+
+static inline void queue_pull_task(struct rq *rq)
+{
+ queue_balance_callback(rq, &per_cpu(rt_pull_head, rq->cpu),
+ pull_rt_task);
}
static void enqueue_pushable_task(struct rq *rq, struct task_struct *p)
@@ -399,12 +429,11 @@
return false;
}
-static inline int pull_rt_task(struct rq *this_rq)
+static inline void pull_rt_task(struct rq *this_rq)
{
- return 0;
}
-static inline void set_post_schedule(struct rq *rq)
+static inline void queue_push_tasks(struct rq *rq)
{
}
#endif /* CONFIG_SMP */
@@ -968,6 +997,67 @@
return 0;
}
+/* TODO: Make configurable */
+#define RT_SCHEDTUNE_INTERVAL 50000000ULL
+
+static void sched_rt_update_capacity_req(struct rq *rq, bool tick);
+
+static enum hrtimer_restart rt_schedtune_timer(struct hrtimer *timer)
+{
+ struct sched_rt_entity *rt_se = container_of(timer,
+ struct sched_rt_entity,
+ schedtune_timer);
+ struct task_struct *p = rt_task_of(rt_se);
+ struct rq *rq = task_rq(p);
+
+ raw_spin_lock(&rq->lock);
+
+ /*
+ * Nothing to do if:
+ * - task has switched runqueues
+ * - task isn't RT anymore
+ */
+ if (rq != task_rq(p) || (p->sched_class != &rt_sched_class))
+ goto out;
+
+ /*
+ * If task got enqueued back during callback time, it means we raced
+ * with the enqueue on another cpu, that's Ok, just do nothing as
+ * enqueue path would have tried to cancel us and we shouldn't run
+ * Also check the schedtune_enqueued flag as class-switch on a
+ * sleeping task may have already canceled the timer and done dq
+ */
+ if (p->on_rq || rt_se->schedtune_enqueued == false)
+ goto out;
+
+ /*
+ * RT task is no longer active, cancel boost
+ */
+ rt_se->schedtune_enqueued = false;
+ schedtune_dequeue_task(p, cpu_of(rq));
+ sched_rt_update_capacity_req(rq, false);
+out:
+ raw_spin_unlock(&rq->lock);
+ return HRTIMER_NORESTART;
+}
+
+void init_rt_schedtune_timer(struct sched_rt_entity *rt_se)
+{
+ struct hrtimer *timer = &rt_se->schedtune_timer;
+
+ hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ timer->function = rt_schedtune_timer;
+ rt_se->schedtune_enqueued = false;
+}
+
+static void start_schedtune_timer(struct sched_rt_entity *rt_se)
+{
+ struct hrtimer *timer = &rt_se->schedtune_timer;
+
+ hrtimer_start(timer, ns_to_ktime(RT_SCHEDTUNE_INTERVAL),
+ HRTIMER_MODE_REL_PINNED);
+}
+
/*
* Update the current task's runtime statistics. Skip current tasks that
* are not in our scheduling class.
@@ -1299,8 +1389,33 @@
enqueue_rt_entity(rt_se, flags & ENQUEUE_HEAD);
walt_inc_cumulative_runnable_avg(rq, p);
- if (!task_current(rq, p) && p->nr_cpus_allowed > 1)
+ if (!task_current(rq, p) && p->nr_cpus_allowed > 1) {
enqueue_pushable_task(rq, p);
+ }
+ if (!schedtune_task_boost(p))
+ return;
+
+ /*
+ * If schedtune timer is active, that means a boost was already
+ * done, just cancel the timer so that deboost doesn't happen.
+ * Otherwise, increase the boost.
+ */
+ hrtimer_try_to_cancel(&rt_se->schedtune_timer);
+
+ /*
+ * schedtune_enqueued can be true in the following situation:
+ * enqueue_task_rt grabs rq lock before timer fires
+ * or before its callback acquires rq lock
+ * schedtune_enqueued can be false if timer callback is running
+ * and timer just released rq lock, or if the timer finished
+ * running and canceling the boost
+ */
+ if (rt_se->schedtune_enqueued == true)
+ return;
+
+ rt_se->schedtune_enqueued = true;
+ schedtune_enqueue_task(p, cpu_of(rq));
+ sched_rt_update_capacity_req(rq, false);
}
static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int flags)
@@ -1312,6 +1427,18 @@
walt_dec_cumulative_runnable_avg(rq, p);
dequeue_pushable_task(rq, p);
+
+ if (rt_se->schedtune_enqueued == false)
+ return;
+
+ if (flags == DEQUEUE_SLEEP) {
+ start_schedtune_timer(rt_se);
+ return;
+ }
+
+ rt_se->schedtune_enqueued = false;
+ schedtune_dequeue_task(p, cpu_of(rq));
+ sched_rt_update_capacity_req(rq, false);
}
/*
@@ -1352,6 +1479,30 @@
static int find_lowest_rq(struct task_struct *task);
/*
+ * Determine if destination CPU explicity disable softirqs,
+ * this is different from CPUs which are running softirqs.
+ * pc is the preempt count to check.
+ */
+static bool softirq_masked(int pc)
+{
+ return !!((pc & SOFTIRQ_MASK)>= SOFTIRQ_DISABLE_OFFSET);
+}
+
+static bool is_top_app_cpu(int cpu)
+{
+ bool boosted = (schedtune_cpu_boost(cpu) > 0);
+
+ return boosted;
+}
+
+static bool is_top_app(struct task_struct *cur)
+{
+ bool boosted = (schedtune_task_boost(cur) > 0);
+
+ return boosted;
+}
+
+/*
* Return whether the task on the given cpu is currently non-preemptible
* while handling a potentially long softint, or if the task is likely
* to block preemptions soon because it is a ksoftirq thread that is
@@ -1363,9 +1514,41 @@
__u32 softirqs = per_cpu(active_softirqs, cpu) |
__IRQ_STAT(cpu, __softirq_pending);
struct task_struct *cpu_ksoftirqd = per_cpu(ksoftirqd, cpu);
+ int task_pc = 0;
+
+ if (task) {
+ if (is_top_app(task))
+ return true;
+ task_pc = task_preempt_count(task);
+ }
+
+ if (is_top_app_cpu(cpu))
+ return true;
+
+ if (softirq_masked(task_pc))
+ return true;
+
return ((softirqs & LONG_SOFTIRQ_MASK) &&
(task == cpu_ksoftirqd ||
- task_thread_info(task)->preempt_count & SOFTIRQ_MASK));
+ task_pc & SOFTIRQ_MASK));
+}
+
+static void schedtune_dequeue_rt(struct rq *rq, struct task_struct *p)
+{
+ struct sched_rt_entity *rt_se = &p->rt;
+
+ BUG_ON(!raw_spin_is_locked(&rq->lock));
+
+ if (rt_se->schedtune_enqueued == false)
+ return;
+
+ /* Incase of class change cancel any active timers */
+ hrtimer_try_to_cancel(&rt_se->schedtune_timer);
+
+ /* schedtune_enqueued is true, deboost it */
+ rt_se->schedtune_enqueued = false;
+ schedtune_dequeue_task(p, task_cpu(p));
+ sched_rt_update_capacity_req(rq, false);
}
static int
@@ -1420,12 +1603,12 @@
(curr->nr_cpus_allowed < 2 ||
curr->prio <= p->prio)))) {
int target = find_lowest_rq(p);
- /*
+ /*
* If cpu is non-preemptible, prefer remote cpu
* even if it's running a higher-prio task.
- * Otherwise: Possible race. Don't bother moving it if the
+ * Otherwise: Possible race. Don't bother moving it if the
* destination CPU is not running a lower priority task.
- */
+ */
if (target != -1 &&
(may_not_preempt ||
p->prio < cpu_rq(target)->rt.highest_prio.curr))
@@ -1434,6 +1617,19 @@
rcu_read_unlock();
out:
+ /*
+ * If previous CPU was different, make sure to cancel any active
+ * schedtune timers and deboost.
+ */
+ if (task_cpu(p) != cpu) {
+ unsigned long fl;
+ struct rq *prq = task_rq(p);
+
+ raw_spin_lock_irqsave(&prq->lock, fl);
+ schedtune_dequeue_rt(prq, p);
+ raw_spin_unlock_irqrestore(&prq->lock, fl);
+ }
+
return cpu;
}
@@ -1489,14 +1685,34 @@
}
#ifdef CONFIG_SMP
-static void sched_rt_update_capacity_req(struct rq *rq)
+
+static void sched_rt_update_capacity_req(struct rq *rq, bool tick)
{
u64 total, used, age_stamp, avg;
s64 delta;
+ int cpu = cpu_of(rq);
if (!sched_freq())
return;
+#ifdef CONFIG_SCHED_WALT
+ if (!walt_disabled && sysctl_sched_use_walt_cpu_util) {
+ unsigned long cpu_utilization = boosted_cpu_util(cpu);
+ unsigned long capacity_curr = capacity_curr_of(cpu);
+ int req = 1;
+
+ /*
+ * During a tick, we don't throttle frequency down, just update
+ * the rt utilization.
+ */
+ if (tick && cpu_utilization <= capacity_curr)
+ req = 0;
+
+ set_rt_cpu_capacity(cpu, req, cpu_utilization);
+
+ return;
+ }
+#endif
sched_avg_update(rq);
/*
* Since we're reading these variables without serialization make sure
@@ -1515,10 +1731,10 @@
if (unlikely(used > SCHED_CAPACITY_SCALE))
used = SCHED_CAPACITY_SCALE;
- set_rt_cpu_capacity(rq->cpu, 1, (unsigned long)(used));
+ set_rt_cpu_capacity(cpu, 1, (unsigned long)(used));
}
#else
-static inline void sched_rt_update_capacity_req(struct rq *rq)
+static inline void sched_rt_update_capacity_req(struct rq *rq, bool tick)
{ }
#endif
@@ -1591,7 +1807,7 @@
* This value will be the used as an estimation of the next
* activity.
*/
- sched_rt_update_capacity_req(rq);
+ sched_rt_update_capacity_req(rq, false);
return NULL;
}
@@ -1602,7 +1818,7 @@
/* The running task is never eligible for pushing */
dequeue_pushable_task(rq, p);
- set_post_schedule(rq);
+ queue_push_tasks(rq);
return p;
}
@@ -1725,7 +1941,9 @@
cpu = cpumask_any(lowest_mask);
if (cpu < nr_cpu_ids)
return cpu;
- return -1;
+
+ cpu = -1;
+ return cpu;
}
/* Will lock the rq it finds */
@@ -1743,6 +1961,16 @@
lowest_rq = cpu_rq(cpu);
+ if (lowest_rq->rt.highest_prio.curr <= task->prio) {
+ /*
+ * Target rq has tasks of equal or higher priority,
+ * retrying does not release any lock and is unlikely
+ * to yield a different result.
+ */
+ lowest_rq = NULL;
+ break;
+ }
+
/* if the prio of this runqueue changed, try again */
if (double_lock_balance(rq, lowest_rq)) {
/*
@@ -1889,14 +2117,173 @@
;
}
-static int pull_rt_task(struct rq *this_rq)
+#ifdef HAVE_RT_PUSH_IPI
+/*
+ * The search for the next cpu always starts at rq->cpu and ends
+ * when we reach rq->cpu again. It will never return rq->cpu.
+ * This returns the next cpu to check, or nr_cpu_ids if the loop
+ * is complete.
+ *
+ * rq->rt.push_cpu holds the last cpu returned by this function,
+ * or if this is the first instance, it must hold rq->cpu.
+ */
+static int rto_next_cpu(struct rq *rq)
{
- int this_cpu = this_rq->cpu, ret = 0, cpu;
+ int prev_cpu = rq->rt.push_cpu;
+ int cpu;
+
+ cpu = cpumask_next(prev_cpu, rq->rd->rto_mask);
+
+ /*
+ * If the previous cpu is less than the rq's CPU, then it already
+ * passed the end of the mask, and has started from the beginning.
+ * We end if the next CPU is greater or equal to rq's CPU.
+ */
+ if (prev_cpu < rq->cpu) {
+ if (cpu >= rq->cpu)
+ return nr_cpu_ids;
+
+ } else if (cpu >= nr_cpu_ids) {
+ /*
+ * We passed the end of the mask, start at the beginning.
+ * If the result is greater or equal to the rq's CPU, then
+ * the loop is finished.
+ */
+ cpu = cpumask_first(rq->rd->rto_mask);
+ if (cpu >= rq->cpu)
+ return nr_cpu_ids;
+ }
+ rq->rt.push_cpu = cpu;
+
+ /* Return cpu to let the caller know if the loop is finished or not */
+ return cpu;
+}
+
+static int find_next_push_cpu(struct rq *rq)
+{
+ struct rq *next_rq;
+ int cpu;
+
+ while (1) {
+ cpu = rto_next_cpu(rq);
+ if (cpu >= nr_cpu_ids)
+ break;
+ next_rq = cpu_rq(cpu);
+
+ /* Make sure the next rq can push to this rq */
+ if (next_rq->rt.highest_prio.next < rq->rt.highest_prio.curr)
+ break;
+ }
+
+ return cpu;
+}
+
+#define RT_PUSH_IPI_EXECUTING 1
+#define RT_PUSH_IPI_RESTART 2
+
+static void tell_cpu_to_push(struct rq *rq)
+{
+ int cpu;
+
+ if (rq->rt.push_flags & RT_PUSH_IPI_EXECUTING) {
+ raw_spin_lock(&rq->rt.push_lock);
+ /* Make sure it's still executing */
+ if (rq->rt.push_flags & RT_PUSH_IPI_EXECUTING) {
+ /*
+ * Tell the IPI to restart the loop as things have
+ * changed since it started.
+ */
+ rq->rt.push_flags |= RT_PUSH_IPI_RESTART;
+ raw_spin_unlock(&rq->rt.push_lock);
+ return;
+ }
+ raw_spin_unlock(&rq->rt.push_lock);
+ }
+
+ /* When here, there's no IPI going around */
+
+ rq->rt.push_cpu = rq->cpu;
+ cpu = find_next_push_cpu(rq);
+ if (cpu >= nr_cpu_ids)
+ return;
+
+ rq->rt.push_flags = RT_PUSH_IPI_EXECUTING;
+
+ irq_work_queue_on(&rq->rt.push_work, cpu);
+}
+
+/* Called from hardirq context */
+static void try_to_push_tasks(void *arg)
+{
+ struct rt_rq *rt_rq = arg;
+ struct rq *rq, *src_rq;
+ int this_cpu;
+ int cpu;
+
+ this_cpu = rt_rq->push_cpu;
+
+ /* Paranoid check */
+ BUG_ON(this_cpu != smp_processor_id());
+
+ rq = cpu_rq(this_cpu);
+ src_rq = rq_of_rt_rq(rt_rq);
+
+again:
+ if (has_pushable_tasks(rq)) {
+ raw_spin_lock(&rq->lock);
+ push_rt_task(rq);
+ raw_spin_unlock(&rq->lock);
+ }
+
+ /* Pass the IPI to the next rt overloaded queue */
+ raw_spin_lock(&rt_rq->push_lock);
+ /*
+ * If the source queue changed since the IPI went out,
+ * we need to restart the search from that CPU again.
+ */
+ if (rt_rq->push_flags & RT_PUSH_IPI_RESTART) {
+ rt_rq->push_flags &= ~RT_PUSH_IPI_RESTART;
+ rt_rq->push_cpu = src_rq->cpu;
+ }
+
+ cpu = find_next_push_cpu(src_rq);
+
+ if (cpu >= nr_cpu_ids)
+ rt_rq->push_flags &= ~RT_PUSH_IPI_EXECUTING;
+ raw_spin_unlock(&rt_rq->push_lock);
+
+ if (cpu >= nr_cpu_ids)
+ return;
+
+ /*
+ * It is possible that a restart caused this CPU to be
+ * chosen again. Don't bother with an IPI, just see if we
+ * have more to push.
+ */
+ if (unlikely(cpu == rq->cpu))
+ goto again;
+
+ /* Try the next RT overloaded CPU */
+ irq_work_queue_on(&rt_rq->push_work, cpu);
+}
+
+static void push_irq_work_func(struct irq_work *work)
+{
+ struct rt_rq *rt_rq = container_of(work, struct rt_rq, push_work);
+
+ try_to_push_tasks(rt_rq);
+}
+#endif /* HAVE_RT_PUSH_IPI */
+
+static void pull_rt_task(struct rq *this_rq)
+{
+ int this_cpu = this_rq->cpu, cpu;
+ bool resched = false;
struct task_struct *p;
struct rq *src_rq;
if (likely(!rt_overloaded(this_rq)))
- return 0;
+ return;
/*
* Match the barrier from rt_set_overloaded; this guarantees that if we
@@ -1904,6 +2291,13 @@
*/
smp_rmb();
+#ifdef HAVE_RT_PUSH_IPI
+ if (sched_feat(RT_PUSH_IPI)) {
+ tell_cpu_to_push(this_rq);
+ return;
+ }
+#endif
+
for_each_cpu(cpu, this_rq->rd->rto_mask) {
if (this_cpu == cpu)
continue;
@@ -1953,7 +2347,7 @@
if (p->prio < src_rq->curr->prio)
goto skip;
- ret = 1;
+ resched = true;
deactivate_task(src_rq, p, 0);
set_task_cpu(p, this_cpu);
@@ -1969,12 +2363,8 @@
double_unlock_balance(this_rq, src_rq);
}
- return ret;
-}
-
-static void post_schedule_rt(struct rq *rq)
-{
- push_rt_tasks(rq);
+ if (resched)
+ resched_curr(this_rq);
}
/*
@@ -2061,6 +2451,13 @@
static void switched_from_rt(struct rq *rq, struct task_struct *p)
{
/*
+ * On class switch from rt, always cancel active schedtune timers,
+ * this handles the cases where we switch class for a task that is
+ * already rt-dequeued but has a running timer.
+ */
+ schedtune_dequeue_rt(rq, p);
+
+ /*
* If there are other RT tasks then we will reschedule
* and the scheduling of the other RT tasks will handle
* the balancing. But if we are the last RT task
@@ -2070,8 +2467,7 @@
if (!task_on_rq_queued(p) || rq->rt.rt_nr_running)
return;
- if (pull_rt_task(rq))
- resched_curr(rq);
+ queue_pull_task(rq);
}
void __init init_sched_rt_class(void)
@@ -2092,8 +2488,6 @@
*/
static void switched_to_rt(struct rq *rq, struct task_struct *p)
{
- int check_resched = 1;
-
/*
* If we are already running, then there's nothing
* that needs to be done. But if we are not running
@@ -2103,12 +2497,10 @@
*/
if (task_on_rq_queued(p) && rq->curr != p) {
#ifdef CONFIG_SMP
- if (p->nr_cpus_allowed > 1 && rq->rt.overloaded &&
- /* Don't resched if we changed runqueues */
- push_rt_task(rq) && rq != task_rq(p))
- check_resched = 0;
+ if (p->nr_cpus_allowed > 1 && rq->rt.overloaded)
+ queue_push_tasks(rq);
#endif /* CONFIG_SMP */
- if (check_resched && p->prio < rq->curr->prio)
+ if (p->prio < rq->curr->prio)
resched_curr(rq);
}
}
@@ -2130,14 +2522,13 @@
* may need to pull tasks to this runqueue.
*/
if (oldprio < p->prio)
- pull_rt_task(rq);
+ queue_pull_task(rq);
+
/*
* If there's a higher priority task waiting to run
- * then reschedule. Note, the above pull_rt_task
- * can release the rq lock and p could migrate.
- * Only reschedule if p is still on the same runqueue.
+ * then reschedule.
*/
- if (p->prio > rq->rt.highest_prio.curr && rq->curr == p)
+ if (p->prio > rq->rt.highest_prio.curr)
resched_curr(rq);
#else
/* For UP simply resched on drop of prio */
@@ -2184,7 +2575,7 @@
update_curr_rt(rq);
if (rq->rt.rt_nr_running)
- sched_rt_update_capacity_req(rq);
+ sched_rt_update_capacity_req(rq, true);
watchdog(rq, p);
@@ -2251,7 +2642,6 @@
.set_cpus_allowed = set_cpus_allowed_rt,
.rq_online = rq_online_rt,
.rq_offline = rq_offline_rt,
- .post_schedule = post_schedule_rt,
.task_woken = task_woken_rt,
.switched_from = switched_from_rt,
#endif
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index fba847e..6a0d101 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1,4 +1,3 @@
-
#include <linux/sched.h>
#include <linux/sched/sysctl.h>
#include <linux/sched/rt.h>
@@ -6,6 +5,7 @@
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/stop_machine.h>
+#include <linux/irq_work.h>
#include <linux/tick.h>
#include <linux/slab.h>
@@ -136,6 +136,7 @@
ktime_t rt_period;
u64 rt_runtime;
struct hrtimer rt_period_timer;
+ unsigned int rt_period_active;
};
void __dl_clear_params(struct task_struct *p);
@@ -201,7 +202,7 @@
s64 hierarchical_quota;
u64 runtime_expires;
- int idle, timer_active;
+ int idle, period_active;
struct hrtimer period_timer, slack_timer;
struct list_head throttled_cfs_rq;
@@ -296,7 +297,7 @@
extern int sched_group_set_shares(struct task_group *tg, unsigned long shares);
extern void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth *cfs_b);
-extern void __start_cfs_bandwidth(struct cfs_bandwidth *cfs_b, bool force);
+extern void start_cfs_bandwidth(struct cfs_bandwidth *cfs_b);
extern void unthrottle_cfs_rq(struct cfs_rq *cfs_rq);
extern void free_rt_sched_group(struct task_group *tg);
@@ -420,6 +421,11 @@
return sysctl_sched_rt_runtime >= 0;
}
+/* RT IPI pull logic requires IRQ_WORK */
+#ifdef CONFIG_IRQ_WORK
+# define HAVE_RT_PUSH_IPI
+#endif
+
/* Real-Time classes' related field in a runqueue: */
struct rt_rq {
struct rt_prio_array active;
@@ -437,7 +443,13 @@
unsigned long rt_nr_total;
int overloaded;
struct plist_head pushable_tasks;
+#ifdef HAVE_RT_PUSH_IPI
+ int push_flags;
+ int push_cpu;
+ struct irq_work push_work;
+ raw_spinlock_t push_lock;
#endif
+#endif /* CONFIG_SMP */
int rt_queued;
int rt_throttled;
@@ -615,9 +627,10 @@
unsigned long cpu_capacity;
unsigned long cpu_capacity_orig;
+ struct callback_head *balance_callback;
+
unsigned char idle_balance;
/* For active balancing */
- int post_schedule;
int active_balance;
int push_cpu;
struct cpu_stop_work active_balance_work;
@@ -752,6 +765,21 @@
#ifdef CONFIG_SMP
+static inline void
+queue_balance_callback(struct rq *rq,
+ struct callback_head *head,
+ void (*func)(struct rq *rq))
+{
+ lockdep_assert_held(&rq->lock);
+
+ if (unlikely(head->next))
+ return;
+
+ head->func = (void (*)(struct callback_head *))func;
+ head->next = rq->balance_callback;
+ rq->balance_callback = head;
+}
+
extern void sched_ttwu_pending(void);
#define rcu_dereference_check_sched_domain(p) \
@@ -850,6 +878,9 @@
unsigned long cpumask[0];
};
+void set_energy_aware(void);
+void clear_energy_aware(void);
+
static inline struct cpumask *sched_group_cpus(struct sched_group *sg)
{
return to_cpumask(sg->cpumask);
@@ -1186,7 +1217,6 @@
int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags);
void (*migrate_task_rq)(struct task_struct *p, int next_cpu);
- void (*post_schedule) (struct rq *this_rq);
void (*task_waking) (struct task_struct *task);
void (*task_woken) (struct rq *this_rq, struct task_struct *task);
@@ -1316,6 +1346,7 @@
extern struct dl_bandwidth def_dl_bandwidth;
extern void init_dl_bandwidth(struct dl_bandwidth *dl_b, u64 period, u64 runtime);
+extern void init_rt_schedtune_timer(struct sched_rt_entity *rt_se);
extern void init_dl_task_timer(struct sched_dl_entity *dl_se);
unsigned long to_ratio(u64 period, u64 runtime);
@@ -1451,6 +1482,21 @@
extern unsigned int walt_ravg_window;
extern unsigned int walt_disabled;
+static inline unsigned long task_util(struct task_struct *p, bool use_pelt)
+{
+
+#ifdef CONFIG_SCHED_WALT
+ if (!walt_disabled && sysctl_sched_use_walt_task_util) {
+ unsigned long demand = p->ravg.demand;
+ return (demand << 10) / walt_ravg_window;
+ }
+#endif
+ if (use_util_est() && !use_pelt)
+ return p->se.avg.util_est;
+ return p->se.avg.util_avg;
+}
+
+
/*
* cpu_util returns the amount of capacity of a CPU that is used by CFS
* tasks. The unit of the return value must be the one of capacity so we can
@@ -1522,45 +1568,32 @@
{
struct sched_capacity_reqs *scr = &per_cpu(cpu_sched_capacity_reqs, cpu);
-#ifdef CONFIG_SCHED_WALT
- if (!walt_disabled && sysctl_sched_use_walt_cpu_util) {
- int rtdl = scr->rt + scr->dl;
- /*
- * WALT tracks the utilization of a CPU considering the load
- * generated by all the scheduling classes.
- * Since the following call to:
- * update_cpu_capacity
- * is already adding the RT and DL utilizations let's remove
- * these contributions from the WALT signal.
- */
- if (capacity > rtdl)
- capacity -= rtdl;
- else
- capacity = 0;
- }
-#endif
- if (scr->cfs != capacity) {
- scr->cfs = capacity;
- update_cpu_capacity_request(cpu, request);
- }
+ if (scr->cfs == capacity)
+ return;
+ scr->cfs = capacity;
+ update_cpu_capacity_request(cpu, request);
}
static inline void set_rt_cpu_capacity(int cpu, bool request,
unsigned long capacity)
{
- if (per_cpu(cpu_sched_capacity_reqs, cpu).rt != capacity) {
- per_cpu(cpu_sched_capacity_reqs, cpu).rt = capacity;
- update_cpu_capacity_request(cpu, request);
- }
+ struct sched_capacity_reqs *scr = &per_cpu(cpu_sched_capacity_reqs, cpu);
+
+ if (scr->rt == capacity)
+ return;
+ scr->rt = capacity;
+ update_cpu_capacity_request(cpu, request);
}
static inline void set_dl_cpu_capacity(int cpu, bool request,
unsigned long capacity)
{
- if (per_cpu(cpu_sched_capacity_reqs, cpu).dl != capacity) {
- per_cpu(cpu_sched_capacity_reqs, cpu).dl = capacity;
- update_cpu_capacity_request(cpu, request);
- }
+ struct sched_capacity_reqs *scr = &per_cpu(cpu_sched_capacity_reqs, cpu);
+
+ if (scr->dl == capacity)
+ return;
+ scr->dl = capacity;
+ update_cpu_capacity_request(cpu, request);
}
#else
static inline bool sched_freq(void) { return false; }
@@ -1584,8 +1617,6 @@
static inline void sched_avg_update(struct rq *rq) { }
#endif
-extern void start_bandwidth_timer(struct hrtimer *period_timer, ktime_t period);
-
extern struct rq *lock_rq_of(struct task_struct *p, unsigned long *flags);
extern void unlock_rq_of(struct rq *rq, struct task_struct *p, unsigned long *flags);
diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c
index a41d329..e1ef0d2 100644
--- a/kernel/sched/tune.c
+++ b/kernel/sched/tune.c
@@ -17,6 +17,7 @@
unsigned int sysctl_sched_cfs_boost __read_mostly;
+static struct reciprocal_value schedtune_spc_rdiv;
extern struct target_nrg schedtune_target_nrg;
/* Performance Boost region (B) threshold params */
@@ -205,7 +206,7 @@
* implementation especially for the computation of the per-CPU boost
* value
*/
-#define BOOSTGROUPS_COUNT 4
+#define BOOSTGROUPS_COUNT 5
/* Array of configured boostgroups */
static struct schedtune *allocated_group[BOOSTGROUPS_COUNT] = {
@@ -737,6 +738,7 @@
for_each_possible_cpu(cpu) {
bg = &per_cpu(cpu_boost_groups, cpu);
memset(bg, 0, sizeof(struct boost_groups));
+ raw_spin_lock_init(&bg->lock);
}
pr_info("schedtune: configured to support %d boost groups\n",
@@ -892,6 +894,79 @@
}
}
+static long
+schedtune_margin(unsigned long signal, long boost)
+{
+ long long margin = 0;
+
+ /*
+ * Signal proportional compensation (SPC)
+ *
+ * The Boost (B) value is used to compute a Margin (M) which is
+ * proportional to the complement of the original Signal (S):
+ * M = B * (SCHED_CAPACITY_SCALE - S)
+ * The obtained M could be used by the caller to "boost" S.
+ */
+ if (boost >= 0) {
+ margin = SCHED_CAPACITY_SCALE - signal;
+ margin *= boost;
+ } else
+ margin = -signal * boost;
+
+ margin = reciprocal_divide(margin, schedtune_spc_rdiv);
+
+ if (boost < 0)
+ margin *= -1;
+ return margin;
+}
+
+static inline int
+schedtune_cpu_margin(unsigned long util, int cpu)
+{
+ int boost = schedtune_cpu_boost(cpu);
+
+ if (boost == 0)
+ return 0;
+
+ return schedtune_margin(util, boost);
+}
+
+static inline long
+schedtune_task_margin(struct task_struct *task)
+{
+ int boost = schedtune_task_boost(task);
+ unsigned long util;
+ long margin;
+
+ if (boost == 0)
+ return 0;
+
+ util = task_util(task, UTIL_AVG);
+ margin = schedtune_margin(util, boost);
+
+ return margin;
+}
+
+unsigned long boosted_cpu_util(int cpu)
+{
+ unsigned long util = cpu_util(cpu, UTIL_EST);
+ long margin = schedtune_cpu_margin(util, cpu);
+
+ trace_sched_boost_cpu(cpu, util, margin);
+
+ return util + margin;
+}
+
+unsigned long boosted_task_util(struct task_struct *task)
+{
+ unsigned long util = task_util(task, UTIL_EST);
+ long margin = schedtune_task_margin(task);
+
+ trace_sched_boost_task(task, util, margin);
+
+ return util + margin;
+}
+
/*
* Initialize the constants required to compute normalized energy.
* The values of these constants depends on the EM data for the specific
@@ -947,9 +1022,12 @@
pr_info("schedtune: configured to support global boosting only\n");
#endif
+ schedtune_spc_rdiv = reciprocal_value(100);
+
return 0;
nodata:
+ pr_warning("schedtune: disabled!\n");
rcu_read_unlock();
return -EINVAL;
}
diff --git a/kernel/sched/tune.h b/kernel/sched/tune.h
index a00fb32..2ae37d4 100644
--- a/kernel/sched/tune.h
+++ b/kernel/sched/tune.h
@@ -39,6 +39,13 @@
int schedtune_accept_deltas(int nrg_delta, int cap_delta,
struct task_struct *task);
+#ifdef CONFIG_SMP
+unsigned long boosted_cpu_util(int cpu);
+#else
+#define boosted_cpu_util(cpu) cpu_util(cpu, UTIL_EST);
+#endif
+unsigned long boosted_task_util(struct task_struct *task);
+
#else /* CONFIG_SCHED_TUNE */
#define schedtune_cpu_boost(cpu) 0
@@ -51,4 +58,7 @@
#define schedtune_accept_deltas(nrg_delta, cap_delta, task) nrg_delta
+#define boosted_cpu_util(cpu) cpu_util(cpu, UTIL_EST);
+#define boosted_task_util(cpu) task_util(cpu, UTIL_EST);
+
#endif /* CONFIG_SCHED_TUNE */
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index b4358f8..aafdf1b 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -128,6 +128,7 @@
static int __maybe_unused four = 4;
static unsigned long one_ul = 1;
static int one_hundred = 100;
+static int __maybe_unused one_hundred_neg = -100;
#ifdef CONFIG_PRINTK
static int ten_thousand = 10000;
#endif
@@ -514,7 +515,7 @@
.mode = 0644,
#endif
.proc_handler = &sysctl_sched_cfs_boost_handler,
- .extra1 = &zero,
+ .extra1 = &one_hundred_neg,
.extra2 = &one_hundred,
},
#endif
@@ -891,7 +892,7 @@
.mode = 0644,
.proc_handler = proc_dointvec_minmax_sysadmin,
.extra1 = &zero,
- .extra2 = &two,
+ .extra2 = &four,
},
#endif
{
@@ -1196,6 +1197,17 @@
.proc_handler = proc_dointvec,
},
#endif
+#if defined(CONFIG_TREE_RCU) || defined(CONFIG_PREEMPT_RCU)
+ {
+ .procname = "panic_on_rcu_stall",
+ .data = &sysctl_panic_on_rcu_stall,
+ .maxlen = sizeof(sysctl_panic_on_rcu_stall),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+#endif
{ }
/*
* NOTE: do not add new entries to this table unless you have read
@@ -1789,6 +1801,20 @@
.proc_handler = &pipe_proc_fn,
.extra1 = &pipe_min_size,
},
+ {
+ .procname = "pipe-user-pages-hard",
+ .data = &pipe_user_pages_hard,
+ .maxlen = sizeof(pipe_user_pages_hard),
+ .mode = 0644,
+ .proc_handler = proc_doulongvec_minmax,
+ },
+ {
+ .procname = "pipe-user-pages-soft",
+ .data = &pipe_user_pages_soft,
+ .maxlen = sizeof(pipe_user_pages_soft),
+ .mode = 0644,
+ .proc_handler = proc_doulongvec_minmax,
+ },
{ }
};
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index d705bbd..a6c8b45 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -814,6 +814,9 @@
if (delta.tv64 < 0)
return 0;
+ if (WARN_ON(timer->state & HRTIMER_STATE_ENQUEUED))
+ return 0;
+
if (interval.tv64 < timer->base->resolution.tv64)
interval.tv64 = timer->base->resolution.tv64;
@@ -904,10 +907,10 @@
* remove hrtimer, called with base lock held
*/
static inline int
-remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base)
+remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, bool restart)
{
if (hrtimer_is_queued(timer)) {
- unsigned long state;
+ unsigned long state = timer->state;
int reprogram;
/*
@@ -921,12 +924,15 @@
debug_deactivate(timer);
timer_stats_hrtimer_clear_start_info(timer);
reprogram = base->cpu_base == this_cpu_ptr(&hrtimer_bases);
- /*
- * We must preserve the CALLBACK state flag here,
- * otherwise we could move the timer base in
- * switch_hrtimer_base.
- */
- state = timer->state & HRTIMER_STATE_CALLBACK;
+
+ if (!restart) {
+ /*
+ * We must preserve the CALLBACK state flag here,
+ * otherwise we could move the timer base in
+ * switch_hrtimer_base.
+ */
+ state &= HRTIMER_STATE_CALLBACK;
+ }
__remove_hrtimer(timer, base, state, reprogram);
return 1;
}
@@ -944,7 +950,7 @@
base = lock_hrtimer_base(timer, &flags);
/* Remove an active timer from the queue: */
- ret = remove_hrtimer(timer, base);
+ ret = remove_hrtimer(timer, base, true);
if (mode & HRTIMER_MODE_REL) {
tim = ktime_add_safe(tim, base->get_time());
@@ -1065,7 +1071,7 @@
base = lock_hrtimer_base(timer, &flags);
if (!hrtimer_callback_running(timer))
- ret = remove_hrtimer(timer, base);
+ ret = remove_hrtimer(timer, base, false);
unlock_hrtimer_base(timer, &flags);
@@ -1225,11 +1231,14 @@
* Note: We clear the CALLBACK bit after enqueue_hrtimer and
* we do not reprogramm the event hardware. Happens either in
* hrtimer_start_range_ns() or in hrtimer_interrupt()
+ *
+ * Note: Because we dropped the cpu_base->lock above,
+ * hrtimer_start_range_ns() can have popped in and enqueued
+ * the timer for us already.
*/
- if (restart != HRTIMER_NORESTART) {
- BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
+ if (restart != HRTIMER_NORESTART &&
+ !(timer->state & HRTIMER_STATE_ENQUEUED))
enqueue_hrtimer(timer, base);
- }
WARN_ON_ONCE(!(timer->state & HRTIMER_STATE_CALLBACK));
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index 9e5b41d..15299d0 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -784,6 +784,7 @@
timer->it.cpu.expires = 0;
sample_to_timespec(timer->it_clock, timer->it.cpu.expires,
&itp->it_value);
+ return;
} else {
cpu_timer_sample_group(timer->it_clock, p, &now);
unlock_task_sighand(p, &flags);
@@ -1202,11 +1203,12 @@
cputime_t *newval, cputime_t *oldval)
{
unsigned long long now;
+ int ret;
WARN_ON_ONCE(clock_idx == CPUCLOCK_SCHED);
- cpu_timer_sample_group(clock_idx, tsk, &now);
+ ret = cpu_timer_sample_group(clock_idx, tsk, &now);
- if (oldval) {
+ if (!ret && oldval) {
/*
* We are setting itimer. The *oldval is absolute and we update
* it to be relative, *newval argument is relative and we update
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index c596236..1dbc7a2 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -333,6 +333,18 @@
}
EXPORT_SYMBOL_GPL(ktime_get_mono_fast_ns);
+/*
+ * NMI safe and fast access to boot clock. We can't do tk_core seqcount reads
+ * here as this will livelock in NMI context so we sacrifice accuracy for
+ * safety. Worst case we may miss an update to tk->offs_boot.
+ */
+u64 notrace ktime_get_boot_fast_ns(void)
+{
+ struct timekeeper *tk = &tk_core.timekeeper;
+ return (ktime_get_mono_fast_ns() + ktime_to_ns(tk->offs_boot));
+}
+EXPORT_SYMBOL_GPL(ktime_get_boot_fast_ns);
+
#ifdef CONFIG_GENERIC_TIME_VSYSCALL_OLD
static inline void update_vsyscall(struct timekeeper *tk)
diff --git a/kernel/time/timekeeping_debug.c b/kernel/time/timekeeping_debug.c
index f6bd652..107310a 100644
--- a/kernel/time/timekeeping_debug.c
+++ b/kernel/time/timekeeping_debug.c
@@ -23,7 +23,9 @@
#include "timekeeping_internal.h"
-static unsigned int sleep_time_bin[32] = {0};
+#define NUM_BINS 32
+
+static unsigned int sleep_time_bin[NUM_BINS] = {0};
static int tk_debug_show_sleep_time(struct seq_file *s, void *data)
{
@@ -69,6 +71,9 @@
void tk_debug_account_sleep_time(struct timespec64 *t)
{
- sleep_time_bin[fls(t->tv_sec)]++;
+ /* Cap bin index so we don't overflow the array */
+ int bin = min(fls(t->tv_sec), NUM_BINS-1);
+
+ sleep_time_bin[bin]++;
}
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 7e84101..b865857 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1028,13 +1028,27 @@
*/
void add_timer_on(struct timer_list *timer, int cpu)
{
- struct tvec_base *base = per_cpu(tvec_bases, cpu);
+ struct tvec_base *new_base = per_cpu(tvec_bases, cpu);
+ struct tvec_base *base;
unsigned long flags;
timer_stats_timer_set_start_info(timer);
BUG_ON(timer_pending(timer) || !timer->function);
- spin_lock_irqsave(&base->lock, flags);
- timer_set_base(timer, base);
+
+ /*
+ * If @timer was on a different CPU, it should be migrated with the
+ * old base locked to prevent other operations proceeding with the
+ * wrong base locked. See lock_timer_base().
+ */
+ base = lock_timer_base(timer, &flags);
+ if (base != new_base) {
+ timer_set_base(timer, NULL);
+ spin_unlock(&base->lock);
+ base = new_base;
+ spin_lock(&base->lock);
+ timer_set_base(timer, base);
+ }
+
debug_activate(timer, timer->expires);
internal_add_timer(base, timer);
spin_unlock_irqrestore(&base->lock, flags);
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index cb9b324..74c5c84 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -240,6 +240,12 @@
enabled. This option and the irqs-off timing option can be
used together or separately.)
+config TRACE_CRITICAL_SECTION_EVENTS
+ bool "Trace critical sections as trace events"
+ default n
+ select IRQSOFF_TRACER
+ select PREEMPT_TRACER
+
config SCHED_TRACER
bool "Scheduling Latency Tracer"
select GENERIC_TRACER
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index d1eff3d..a53787c 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -18,7 +18,7 @@
#include <linux/kallsyms.h>
#include <linux/seq_file.h>
#include <linux/suspend.h>
-#include <linux/debugfs.h>
+#include <linux/tracefs.h>
#include <linux/hardirq.h>
#include <linux/kthread.h>
#include <linux/uaccess.h>
@@ -1002,7 +1002,7 @@
.stat_show = function_stat_show
};
-static __init void ftrace_profile_debugfs(struct dentry *d_tracer)
+static __init void ftrace_profile_tracefs(struct dentry *d_tracer)
{
struct ftrace_profile_stat *stat;
struct dentry *entry;
@@ -1038,15 +1038,15 @@
}
}
- entry = debugfs_create_file("function_profile_enabled", 0644,
+ entry = tracefs_create_file("function_profile_enabled", 0644,
d_tracer, NULL, &ftrace_profile_fops);
if (!entry)
- pr_warning("Could not create debugfs "
+ pr_warning("Could not create tracefs "
"'function_profile_enabled' entry\n");
}
#else /* CONFIG_FUNCTION_PROFILER */
-static __init void ftrace_profile_debugfs(struct dentry *d_tracer)
+static __init void ftrace_profile_tracefs(struct dentry *d_tracer)
{
}
#endif /* CONFIG_FUNCTION_PROFILER */
@@ -3408,23 +3408,24 @@
ftrace_probe_registered = 1;
}
-static void __disable_ftrace_function_probe(void)
+static bool __disable_ftrace_function_probe(void)
{
int i;
if (!ftrace_probe_registered)
- return;
+ return false;
for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
struct hlist_head *hhd = &ftrace_func_hash[i];
if (hhd->first)
- return;
+ return false;
}
/* no more funcs left */
ftrace_shutdown(&trace_probe_ops, 0);
ftrace_probe_registered = 0;
+ return true;
}
@@ -3550,6 +3551,7 @@
__unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops,
void *data, int flags)
{
+ struct ftrace_ops_hash old_hash_ops;
struct ftrace_func_entry *rec_entry;
struct ftrace_func_probe *entry;
struct ftrace_func_probe *p;
@@ -3563,6 +3565,7 @@
int i, len = 0;
char *search;
int ret;
+ bool disabled;
if (glob && (strcmp(glob, "*") == 0 || !strlen(glob)))
glob = NULL;
@@ -3579,6 +3582,10 @@
mutex_lock(&trace_probe_ops.func_hash->regex_lock);
+ old_hash_ops.filter_hash = old_hash;
+ /* Probes only have filters */
+ old_hash_ops.notrace_hash = NULL;
+
hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, *orig_hash);
if (!hash)
/* Hmm, should report this somehow */
@@ -3616,12 +3623,17 @@
}
}
mutex_lock(&ftrace_lock);
- __disable_ftrace_function_probe();
+ disabled = __disable_ftrace_function_probe();
/*
* Remove after the disable is called. Otherwise, if the last
* probe is removed, a null hash means *all enabled*.
*/
ret = ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash);
+
+ /* still need to update the function call sites */
+ if (ftrace_enabled && !disabled)
+ ftrace_run_modify_code(&trace_probe_ops, FTRACE_UPDATE_CALLS,
+ &old_hash_ops);
synchronize_sched();
if (!ret)
free_ftrace_hash_rcu(old_hash);
@@ -4487,7 +4499,7 @@
mutex_unlock(&ftrace_lock);
}
-static __init int ftrace_init_dyn_debugfs(struct dentry *d_tracer)
+static __init int ftrace_init_dyn_tracefs(struct dentry *d_tracer)
{
trace_create_file("available_filter_functions", 0444,
@@ -4769,7 +4781,7 @@
}
core_initcall(ftrace_nodyn_init);
-static inline int ftrace_init_dyn_debugfs(struct dentry *d_tracer) { return 0; }
+static inline int ftrace_init_dyn_tracefs(struct dentry *d_tracer) { return 0; }
static inline void ftrace_startup_enable(int command) { }
static inline void ftrace_startup_all(int command) { }
/* Keep as macros so we do not need to define the commands */
@@ -5218,24 +5230,24 @@
.release = ftrace_pid_release,
};
-static __init int ftrace_init_debugfs(void)
+static __init int ftrace_init_tracefs(void)
{
struct dentry *d_tracer;
d_tracer = tracing_init_dentry();
- if (!d_tracer)
+ if (IS_ERR(d_tracer))
return 0;
- ftrace_init_dyn_debugfs(d_tracer);
+ ftrace_init_dyn_tracefs(d_tracer);
trace_create_file("set_ftrace_pid", 0644, d_tracer,
NULL, &ftrace_pid_fops);
- ftrace_profile_debugfs(d_tracer);
+ ftrace_profile_tracefs(d_tracer);
return 0;
}
-fs_initcall(ftrace_init_debugfs);
+fs_initcall(ftrace_init_tracefs);
/**
* ftrace_kill - kill ftrace
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 7d370a6..f485680 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -466,7 +466,8 @@
raw_spinlock_t reader_lock; /* serialize readers */
arch_spinlock_t lock;
struct lock_class_key lock_key;
- unsigned int nr_pages;
+ unsigned long nr_pages;
+ unsigned int current_context;
struct list_head *pages;
struct buffer_page *head_page; /* read from head */
struct buffer_page *tail_page; /* write to tail */
@@ -486,7 +487,7 @@
u64 write_stamp;
u64 read_stamp;
/* ring buffer pages to update, > 0 to add, < 0 to remove */
- int nr_pages_to_update;
+ long nr_pages_to_update;
struct list_head new_pages; /* new pages to add */
struct work_struct update_pages_work;
struct completion update_done;
@@ -1165,10 +1166,10 @@
return 0;
}
-static int __rb_allocate_pages(int nr_pages, struct list_head *pages, int cpu)
+static int __rb_allocate_pages(long nr_pages, struct list_head *pages, int cpu)
{
- int i;
struct buffer_page *bpage, *tmp;
+ long i;
for (i = 0; i < nr_pages; i++) {
struct page *page;
@@ -1205,7 +1206,7 @@
}
static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
- unsigned nr_pages)
+ unsigned long nr_pages)
{
LIST_HEAD(pages);
@@ -1230,7 +1231,7 @@
}
static struct ring_buffer_per_cpu *
-rb_allocate_cpu_buffer(struct ring_buffer *buffer, int nr_pages, int cpu)
+rb_allocate_cpu_buffer(struct ring_buffer *buffer, long nr_pages, int cpu)
{
struct ring_buffer_per_cpu *cpu_buffer;
struct buffer_page *bpage;
@@ -1330,8 +1331,9 @@
struct lock_class_key *key)
{
struct ring_buffer *buffer;
+ long nr_pages;
int bsize;
- int cpu, nr_pages;
+ int cpu;
/* keep it in its own cache line */
buffer = kzalloc(ALIGN(sizeof(*buffer), cache_line_size()),
@@ -1457,12 +1459,12 @@
}
static int
-rb_remove_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned int nr_pages)
+rb_remove_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned long nr_pages)
{
struct list_head *tail_page, *to_remove, *next_page;
struct buffer_page *to_remove_page, *tmp_iter_page;
struct buffer_page *last_page, *first_page;
- unsigned int nr_removed;
+ unsigned long nr_removed;
unsigned long head_bit;
int page_entries;
@@ -1679,7 +1681,7 @@
int cpu_id)
{
struct ring_buffer_per_cpu *cpu_buffer;
- unsigned nr_pages;
+ unsigned long nr_pages;
int cpu, err = 0;
/*
@@ -2679,11 +2681,11 @@
* just so happens that it is the same bit corresponding to
* the current context.
*/
-static DEFINE_PER_CPU(unsigned int, current_context);
-static __always_inline int trace_recursive_lock(void)
+static __always_inline int
+trace_recursive_lock(struct ring_buffer_per_cpu *cpu_buffer)
{
- unsigned int val = __this_cpu_read(current_context);
+ unsigned int val = cpu_buffer->current_context;
int bit;
if (in_interrupt()) {
@@ -2700,23 +2702,21 @@
return 1;
val |= (1 << bit);
- __this_cpu_write(current_context, val);
+ cpu_buffer->current_context = val;
return 0;
}
-static __always_inline void trace_recursive_unlock(void)
+static __always_inline void
+trace_recursive_unlock(struct ring_buffer_per_cpu *cpu_buffer)
{
- unsigned int val = __this_cpu_read(current_context);
-
- val &= val & (val - 1);
- __this_cpu_write(current_context, val);
+ cpu_buffer->current_context &= cpu_buffer->current_context - 1;
}
#else
-#define trace_recursive_lock() (0)
-#define trace_recursive_unlock() do { } while (0)
+#define trace_recursive_lock(cpu_buffer) (0)
+#define trace_recursive_unlock(cpu_buffer) do { } while (0)
#endif
@@ -2748,35 +2748,34 @@
/* If we are tracing schedule, we don't want to recurse */
preempt_disable_notrace();
- if (atomic_read(&buffer->record_disabled))
- goto out_nocheck;
-
- if (trace_recursive_lock())
- goto out_nocheck;
+ if (unlikely(atomic_read(&buffer->record_disabled)))
+ goto out;
cpu = raw_smp_processor_id();
- if (!cpumask_test_cpu(cpu, buffer->cpumask))
+ if (unlikely(!cpumask_test_cpu(cpu, buffer->cpumask)))
goto out;
cpu_buffer = buffer->buffers[cpu];
- if (atomic_read(&cpu_buffer->record_disabled))
+ if (unlikely(atomic_read(&cpu_buffer->record_disabled)))
goto out;
- if (length > BUF_MAX_DATA_SIZE)
+ if (unlikely(length > BUF_MAX_DATA_SIZE))
+ goto out;
+
+ if (unlikely(trace_recursive_lock(cpu_buffer)))
goto out;
event = rb_reserve_next_event(buffer, cpu_buffer, length);
if (!event)
- goto out;
+ goto out_unlock;
return event;
+ out_unlock:
+ trace_recursive_unlock(cpu_buffer);
out:
- trace_recursive_unlock();
-
- out_nocheck:
preempt_enable_notrace();
return NULL;
}
@@ -2866,7 +2865,7 @@
rb_wakeups(buffer, cpu_buffer);
- trace_recursive_unlock();
+ trace_recursive_unlock(cpu_buffer);
preempt_enable_notrace();
@@ -2977,7 +2976,7 @@
out:
rb_end_commit(cpu_buffer);
- trace_recursive_unlock();
+ trace_recursive_unlock(cpu_buffer);
preempt_enable_notrace();
@@ -3452,11 +3451,23 @@
int ring_buffer_iter_empty(struct ring_buffer_iter *iter)
{
struct ring_buffer_per_cpu *cpu_buffer;
+ struct buffer_page *reader;
+ struct buffer_page *head_page;
+ struct buffer_page *commit_page;
+ unsigned commit;
cpu_buffer = iter->cpu_buffer;
- return iter->head_page == cpu_buffer->commit_page &&
- iter->head == rb_commit_index(cpu_buffer);
+ /* Remember, trace recording is off when iterator is in use */
+ reader = cpu_buffer->reader_page;
+ head_page = cpu_buffer->head_page;
+ commit_page = cpu_buffer->commit_page;
+ commit = rb_page_commit(commit_page);
+
+ return ((iter->head_page == commit_page && iter->head == commit) ||
+ (iter->head_page == reader && commit_page == head_page &&
+ head_page->read == commit &&
+ iter->head == rb_page_commit(cpu_buffer->reader_page)));
}
EXPORT_SYMBOL_GPL(ring_buffer_iter_empty);
@@ -4654,8 +4665,9 @@
struct ring_buffer *buffer =
container_of(self, struct ring_buffer, cpu_notify);
long cpu = (long)hcpu;
- int cpu_i, nr_pages_same;
- unsigned int nr_pages;
+ long nr_pages_same;
+ int cpu_i;
+ unsigned long nr_pages;
switch (action) {
case CPU_UP_PREPARE:
@@ -4883,9 +4895,9 @@
rb_data[cpu].cnt = cpu;
rb_threads[cpu] = kthread_create(rb_test, &rb_data[cpu],
"rbtester/%d", cpu);
- if (WARN_ON(!rb_threads[cpu])) {
+ if (WARN_ON(IS_ERR(rb_threads[cpu]))) {
pr_cont("FAILED\n");
- ret = -1;
+ ret = PTR_ERR(rb_threads[cpu]);
goto out_free;
}
@@ -4895,9 +4907,9 @@
/* Now create the rb hammer! */
rb_hammer = kthread_run(rb_hammer_test, NULL, "rbhammer");
- if (WARN_ON(!rb_hammer)) {
+ if (WARN_ON(IS_ERR(rb_hammer))) {
pr_cont("FAILED\n");
- ret = -1;
+ ret = PTR_ERR(rb_hammer);
goto out_free;
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index b301119..52aa9e5 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -20,6 +20,7 @@
#include <linux/notifier.h>
#include <linux/irqflags.h>
#include <linux/debugfs.h>
+#include <linux/tracefs.h>
#include <linux/pagemap.h>
#include <linux/hardirq.h>
#include <linux/linkage.h>
@@ -31,6 +32,7 @@
#include <linux/splice.h>
#include <linux/kdebug.h>
#include <linux/string.h>
+#include <linux/mount.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/ctype.h>
@@ -40,6 +42,7 @@
#include <linux/fs.h>
#include <linux/sched/rt.h>
#include <linux/coresight-stm.h>
+#include <linux/vmalloc.h>
#include "trace.h"
#include "trace_output.h"
@@ -832,7 +835,7 @@
{ trace_clock_jiffies, "uptime", 0 },
{ trace_clock, "perf", 1 },
{ ktime_get_mono_fast_ns, "mono", 1 },
- { trace_clock_boot, "boot", 1 },
+ { ktime_get_boot_fast_ns, "boot", 1 },
ARCH_TRACE_CLOCKS
};
@@ -1286,7 +1289,6 @@
#define SAVED_CMDLINES_DEFAULT 128
#define NO_CMDLINE_MAP UINT_MAX
-static unsigned saved_tgids[SAVED_CMDLINES_DEFAULT];
static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED;
struct saved_cmdlines_buffer {
unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1];
@@ -1294,6 +1296,7 @@
unsigned cmdline_num;
int cmdline_idx;
char *saved_cmdlines;
+ int *saved_tgids;
};
static struct saved_cmdlines_buffer *savedcmd;
@@ -1313,17 +1316,24 @@
static int allocate_cmdlines_buffer(unsigned int val,
struct saved_cmdlines_buffer *s)
{
- s->map_cmdline_to_pid = kmalloc(val * sizeof(*s->map_cmdline_to_pid),
- GFP_KERNEL);
+ s->map_cmdline_to_pid = vmalloc(val * sizeof(*s->map_cmdline_to_pid));
if (!s->map_cmdline_to_pid)
return -ENOMEM;
- s->saved_cmdlines = kmalloc(val * TASK_COMM_LEN, GFP_KERNEL);
+ s->saved_cmdlines = vmalloc(val * TASK_COMM_LEN);
if (!s->saved_cmdlines) {
- kfree(s->map_cmdline_to_pid);
+ vfree(s->map_cmdline_to_pid);
return -ENOMEM;
}
+ s->saved_tgids = vmalloc(val * sizeof(*s->saved_tgids));
+ if (!s->saved_tgids) {
+ vfree(s->saved_cmdlines);
+ vfree(s->map_cmdline_to_pid);
+ return -ENOMEM;
+ }
+ memset(s->saved_tgids, 0, val * sizeof(*s->saved_tgids));
+
s->cmdline_idx = 0;
s->cmdline_num = val;
memset(&s->map_pid_to_cmdline, NO_CMDLINE_MAP,
@@ -1525,7 +1535,7 @@
}
set_cmdline(idx, tsk->comm);
- saved_tgids[idx] = tsk->tgid;
+ savedcmd->saved_tgids[idx] = tsk->tgid;
arch_spin_unlock(&trace_cmdline_lock);
return 1;
@@ -1577,7 +1587,7 @@
arch_spin_lock(&trace_cmdline_lock);
map = savedcmd->map_pid_to_cmdline[pid];
if (map != NO_CMDLINE_MAP)
- tgid = saved_tgids[map];
+ tgid = savedcmd->saved_tgids[map];
else
tgid = -1;
@@ -3892,8 +3902,9 @@
static void free_saved_cmdlines_buffer(struct saved_cmdlines_buffer *s)
{
- kfree(s->saved_cmdlines);
- kfree(s->map_cmdline_to_pid);
+ vfree(s->saved_cmdlines);
+ vfree(s->saved_tgids);
+ vfree(s->map_cmdline_to_pid);
kfree(s);
}
@@ -3959,13 +3970,13 @@
int pid;
int i;
- file_buf = kmalloc(SAVED_CMDLINES_DEFAULT*(16+1+16), GFP_KERNEL);
+ file_buf = vmalloc(savedcmd->cmdline_num*(16+1+16));
if (!file_buf)
return -ENOMEM;
buf = file_buf;
- for (i = 0; i < SAVED_CMDLINES_DEFAULT; i++) {
+ for (i = 0; i < savedcmd->cmdline_num; i++) {
int tgid;
int r;
@@ -3982,7 +3993,7 @@
len = simple_read_from_buffer(ubuf, cnt, ppos,
file_buf, len);
- kfree(file_buf);
+ vfree(file_buf);
return len;
}
@@ -5909,11 +5920,13 @@
return ret;
out_reg:
+ ret = alloc_snapshot(&global_trace);
+ if (ret < 0)
+ goto out;
+
ret = register_ftrace_function_probe(glob, ops, count);
- if (ret >= 0)
- alloc_snapshot(&global_trace);
-
+ out:
return ret < 0 ? ret : 0;
}
@@ -5930,28 +5943,19 @@
static inline __init int register_snapshot_cmd(void) { return 0; }
#endif /* defined(CONFIG_TRACER_SNAPSHOT) && defined(CONFIG_DYNAMIC_FTRACE) */
-struct dentry *tracing_init_dentry_tr(struct trace_array *tr)
+static struct dentry *tracing_get_dentry(struct trace_array *tr)
{
- if (tr->dir)
- return tr->dir;
+ if (WARN_ON(!tr->dir))
+ return ERR_PTR(-ENODEV);
- if (!debugfs_initialized())
+ /* Top directory uses NULL as the parent */
+ if (tr->flags & TRACE_ARRAY_FL_GLOBAL)
return NULL;
- if (tr->flags & TRACE_ARRAY_FL_GLOBAL)
- tr->dir = debugfs_create_dir("tracing", NULL);
-
- if (!tr->dir)
- pr_warn_once("Could not create debugfs directory 'tracing'\n");
-
+ /* All sub buffers have a descriptor */
return tr->dir;
}
-struct dentry *tracing_init_dentry(void)
-{
- return tracing_init_dentry_tr(&global_trace);
-}
-
static struct dentry *tracing_dentry_percpu(struct trace_array *tr, int cpu)
{
struct dentry *d_tracer;
@@ -5959,14 +5963,14 @@
if (tr->percpu_dir)
return tr->percpu_dir;
- d_tracer = tracing_init_dentry_tr(tr);
- if (!d_tracer)
+ d_tracer = tracing_get_dentry(tr);
+ if (IS_ERR(d_tracer))
return NULL;
- tr->percpu_dir = debugfs_create_dir("per_cpu", d_tracer);
+ tr->percpu_dir = tracefs_create_dir("per_cpu", d_tracer);
WARN_ONCE(!tr->percpu_dir,
- "Could not create debugfs directory 'per_cpu/%d'\n", cpu);
+ "Could not create tracefs directory 'per_cpu/%d'\n", cpu);
return tr->percpu_dir;
}
@@ -5983,7 +5987,7 @@
}
static void
-tracing_init_debugfs_percpu(struct trace_array *tr, long cpu)
+tracing_init_tracefs_percpu(struct trace_array *tr, long cpu)
{
struct dentry *d_percpu = tracing_dentry_percpu(tr, cpu);
struct dentry *d_cpu;
@@ -5993,9 +5997,9 @@
return;
snprintf(cpu_dir, 30, "cpu%ld", cpu);
- d_cpu = debugfs_create_dir(cpu_dir, d_percpu);
+ d_cpu = tracefs_create_dir(cpu_dir, d_percpu);
if (!d_cpu) {
- pr_warning("Could not create debugfs '%s' entry\n", cpu_dir);
+ pr_warning("Could not create tracefs '%s' entry\n", cpu_dir);
return;
}
@@ -6147,9 +6151,9 @@
{
struct dentry *ret;
- ret = debugfs_create_file(name, mode, parent, data, fops);
+ ret = tracefs_create_file(name, mode, parent, data, fops);
if (!ret)
- pr_warning("Could not create debugfs '%s' entry\n", name);
+ pr_warning("Could not create tracefs '%s' entry\n", name);
return ret;
}
@@ -6162,13 +6166,13 @@
if (tr->options)
return tr->options;
- d_tracer = tracing_init_dentry_tr(tr);
- if (!d_tracer)
+ d_tracer = tracing_get_dentry(tr);
+ if (IS_ERR(d_tracer))
return NULL;
- tr->options = debugfs_create_dir("options", d_tracer);
+ tr->options = tracefs_create_dir("options", d_tracer);
if (!tr->options) {
- pr_warning("Could not create debugfs directory 'options'\n");
+ pr_warning("Could not create tracefs directory 'options'\n");
return NULL;
}
@@ -6237,7 +6241,7 @@
return;
for (cnt = 0; topts[cnt].opt; cnt++)
- debugfs_remove(topts[cnt].entry);
+ tracefs_remove(topts[cnt].entry);
kfree(topts);
}
@@ -6326,7 +6330,7 @@
struct dentry *trace_instance_dir;
static void
-init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer);
+init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer);
static int
allocate_trace_buffer(struct trace_array *tr, struct trace_buffer *buf, int size)
@@ -6403,7 +6407,7 @@
#endif
}
-static int new_instance_create(const char *name)
+static int instance_mkdir(const char *name)
{
struct trace_array *tr;
int ret;
@@ -6442,17 +6446,17 @@
if (allocate_trace_buffers(tr, trace_buf_size) < 0)
goto out_free_tr;
- tr->dir = debugfs_create_dir(name, trace_instance_dir);
+ tr->dir = tracefs_create_dir(name, trace_instance_dir);
if (!tr->dir)
goto out_free_tr;
ret = event_trace_add_tracer(tr->dir, tr);
if (ret) {
- debugfs_remove_recursive(tr->dir);
+ tracefs_remove_recursive(tr->dir);
goto out_free_tr;
}
- init_tracer_debugfs(tr, tr->dir);
+ init_tracer_tracefs(tr, tr->dir);
list_add(&tr->list, &ftrace_trace_arrays);
@@ -6473,7 +6477,7 @@
}
-static int instance_delete(const char *name)
+static int instance_rmdir(const char *name)
{
struct trace_array *tr;
int found = 0;
@@ -6514,82 +6518,17 @@
return ret;
}
-static int instance_mkdir (struct inode *inode, struct dentry *dentry, umode_t mode)
-{
- struct dentry *parent;
- int ret;
-
- /* Paranoid: Make sure the parent is the "instances" directory */
- parent = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias);
- if (WARN_ON_ONCE(parent != trace_instance_dir))
- return -ENOENT;
-
- /*
- * The inode mutex is locked, but debugfs_create_dir() will also
- * take the mutex. As the instances directory can not be destroyed
- * or changed in any other way, it is safe to unlock it, and
- * let the dentry try. If two users try to make the same dir at
- * the same time, then the new_instance_create() will determine the
- * winner.
- */
- mutex_unlock(&inode->i_mutex);
-
- ret = new_instance_create(dentry->d_iname);
-
- mutex_lock(&inode->i_mutex);
-
- return ret;
-}
-
-static int instance_rmdir(struct inode *inode, struct dentry *dentry)
-{
- struct dentry *parent;
- int ret;
-
- /* Paranoid: Make sure the parent is the "instances" directory */
- parent = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias);
- if (WARN_ON_ONCE(parent != trace_instance_dir))
- return -ENOENT;
-
- /* The caller did a dget() on dentry */
- mutex_unlock(&dentry->d_inode->i_mutex);
-
- /*
- * The inode mutex is locked, but debugfs_create_dir() will also
- * take the mutex. As the instances directory can not be destroyed
- * or changed in any other way, it is safe to unlock it, and
- * let the dentry try. If two users try to make the same dir at
- * the same time, then the instance_delete() will determine the
- * winner.
- */
- mutex_unlock(&inode->i_mutex);
-
- ret = instance_delete(dentry->d_iname);
-
- mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
- mutex_lock(&dentry->d_inode->i_mutex);
-
- return ret;
-}
-
-static const struct inode_operations instance_dir_inode_operations = {
- .lookup = simple_lookup,
- .mkdir = instance_mkdir,
- .rmdir = instance_rmdir,
-};
-
static __init void create_trace_instances(struct dentry *d_tracer)
{
- trace_instance_dir = debugfs_create_dir("instances", d_tracer);
+ trace_instance_dir = tracefs_create_instance_dir("instances", d_tracer,
+ instance_mkdir,
+ instance_rmdir);
if (WARN_ON(!trace_instance_dir))
return;
-
- /* Hijack the dir inode operations, to allow mkdir */
- trace_instance_dir->d_inode->i_op = &instance_dir_inode_operations;
}
static void
-init_tracer_debugfs(struct trace_array *tr, struct dentry *d_tracer)
+init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
{
int cpu;
@@ -6646,21 +6585,77 @@
#endif
for_each_tracing_cpu(cpu)
- tracing_init_debugfs_percpu(tr, cpu);
+ tracing_init_tracefs_percpu(tr, cpu);
}
-static __init int tracer_init_debugfs(void)
+static struct vfsmount *trace_automount(void *ingore)
+{
+ struct vfsmount *mnt;
+ struct file_system_type *type;
+
+ /*
+ * To maintain backward compatibility for tools that mount
+ * debugfs to get to the tracing facility, tracefs is automatically
+ * mounted to the debugfs/tracing directory.
+ */
+ type = get_fs_type("tracefs");
+ if (!type)
+ return NULL;
+ mnt = vfs_kern_mount(type, 0, "tracefs", NULL);
+ put_filesystem(type);
+ if (IS_ERR(mnt))
+ return NULL;
+ mntget(mnt);
+
+ return mnt;
+}
+
+/**
+ * tracing_init_dentry - initialize top level trace array
+ *
+ * This is called when creating files or directories in the tracing
+ * directory. It is called via fs_initcall() by any of the boot up code
+ * and expects to return the dentry of the top level tracing directory.
+ */
+struct dentry *tracing_init_dentry(void)
+{
+ struct trace_array *tr = &global_trace;
+
+ /* The top level trace array uses NULL as parent */
+ if (tr->dir)
+ return NULL;
+
+ if (WARN_ON(!debugfs_initialized()))
+ return ERR_PTR(-ENODEV);
+
+ /*
+ * As there may still be users that expect the tracing
+ * files to exist in debugfs/tracing, we must automount
+ * the tracefs file system there, so older tools still
+ * work with the newer kerenl.
+ */
+ tr->dir = debugfs_create_automount("tracing", NULL,
+ trace_automount, NULL);
+ if (!tr->dir) {
+ pr_warn_once("Could not create debugfs directory 'tracing'\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return NULL;
+}
+
+static __init int tracer_init_tracefs(void)
{
struct dentry *d_tracer;
trace_access_lock_init();
d_tracer = tracing_init_dentry();
- if (!d_tracer)
+ if (IS_ERR(d_tracer))
return 0;
- init_tracer_debugfs(&global_trace, d_tracer);
+ init_tracer_tracefs(&global_trace, d_tracer);
trace_create_file("tracing_thresh", 0644, d_tracer,
&global_trace, &tracing_thresh_fops);
@@ -6882,7 +6877,6 @@
int ring_buf_size;
int ret = -ENOMEM;
-
if (!alloc_cpumask_var(&tracing_buffer_mask, GFP_KERNEL))
goto out;
@@ -6980,6 +6974,13 @@
return ret;
}
+void __init trace_init(void)
+{
+ tracer_alloc_buffers();
+ init_ftrace_syscalls();
+ trace_event_init();
+}
+
__init static int clear_boot_tracer(void)
{
/*
@@ -6999,6 +7000,5 @@
return 0;
}
-early_initcall(tracer_alloc_buffers);
-fs_initcall(tracer_init_debugfs);
+fs_initcall(tracer_init_tracefs);
late_initcall(clear_boot_tracer);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 0d2f587..9abe011 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -333,7 +333,7 @@
/**
- * struct tracer - a specific tracer and its callbacks to interact with debugfs
+ * struct tracer - a specific tracer and its callbacks to interact with tracefs
* @name: the name chosen to select it on the available_tracers file
* @init: called when one switches to this tracer (echo name > current_tracer)
* @reset: called when one switches to another tracer
@@ -541,7 +541,6 @@
void *data,
const struct file_operations *fops);
-struct dentry *tracing_init_dentry_tr(struct trace_array *tr);
struct dentry *tracing_init_dentry(void);
struct ring_buffer_event;
@@ -1313,4 +1312,17 @@
#define perf_ftrace_event_register NULL
#endif
+#ifdef CONFIG_FTRACE_SYSCALLS
+void init_ftrace_syscalls(void);
+#else
+static inline void init_ftrace_syscalls(void) { }
+#endif
+
+#ifdef CONFIG_EVENT_TRACING
+void trace_event_init(void);
+#else
+static inline void __init trace_event_init(void) { }
+#endif
+
+
#endif /* _LINUX_KERNEL_TRACE_H */
diff --git a/kernel/trace/trace_clock.c b/kernel/trace/trace_clock.c
index 0bce235..57b67b1 100644
--- a/kernel/trace/trace_clock.c
+++ b/kernel/trace/trace_clock.c
@@ -135,17 +135,3 @@
{
return atomic64_add_return(1, &trace_counter);
}
-
-/*
- * trace_clock_boot(): use CLOCK_BOOTTIME for tracing
- * This should be equivalent to trace_clock_global for most users,
- * but it has the added advantage of tracking time spent in suspend.
- */
-u64 notrace trace_clock_boot(void)
-{
- struct timespec uptime;
- get_monotonic_boottime(&uptime);
- return (uptime.tv_sec * 1000000000ull) + uptime.tv_nsec;
-}
-
-EXPORT_SYMBOL_GPL(trace_clock_boot);
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index 17b5a67..4843900 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -262,7 +262,7 @@
}
void *perf_trace_buf_prepare(int size, unsigned short type,
- struct pt_regs *regs, int *rctxp)
+ struct pt_regs **regs, int *rctxp)
{
struct trace_entry *entry;
unsigned long flags;
@@ -281,6 +281,8 @@
if (*rctxp < 0)
return NULL;
+ if (regs)
+ *regs = this_cpu_ptr(&__perf_regs[*rctxp]);
raw_data = this_cpu_ptr(perf_trace_buf[*rctxp]);
/* zero the dead bytes from align to not leak stack to user */
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 6e7575f..7f879c8 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -13,7 +13,7 @@
#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/kthread.h>
-#include <linux/debugfs.h>
+#include <linux/tracefs.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/ctype.h>
@@ -449,7 +449,7 @@
return;
if (!--dir->nr_events) {
- debugfs_remove_recursive(dir->entry);
+ tracefs_remove_recursive(dir->entry);
list_del(&dir->list);
__put_system_dir(dir);
}
@@ -468,7 +468,7 @@
}
spin_unlock(&dir->d_lock);
- debugfs_remove_recursive(dir);
+ tracefs_remove_recursive(dir);
}
list_del(&file->list);
@@ -1493,7 +1493,7 @@
} else
__get_system(system);
- dir->entry = debugfs_create_dir(name, parent);
+ dir->entry = tracefs_create_dir(name, parent);
if (!dir->entry) {
pr_warn("Failed to create system directory %s\n", name);
__put_system(system);
@@ -1506,12 +1506,12 @@
dir->subsystem = system;
file->system = dir;
- entry = debugfs_create_file("filter", 0644, dir->entry, dir,
+ entry = tracefs_create_file("filter", 0644, dir->entry, dir,
&ftrace_subsystem_filter_fops);
if (!entry) {
kfree(system->filter);
system->filter = NULL;
- pr_warn("Could not create debugfs '%s/filter' entry\n", name);
+ pr_warn("Could not create tracefs '%s/filter' entry\n", name);
}
trace_create_file("enable", 0644, dir->entry, dir,
@@ -1552,9 +1552,9 @@
d_events = parent;
name = ftrace_event_name(call);
- file->dir = debugfs_create_dir(name, d_events);
+ file->dir = tracefs_create_dir(name, d_events);
if (!file->dir) {
- pr_warn("Could not create debugfs '%s' directory\n", name);
+ pr_warn("Could not create tracefs '%s' directory\n", name);
return -1;
}
@@ -1585,8 +1585,13 @@
trace_create_file("filter", 0644, file->dir, file,
&ftrace_event_filter_fops);
- trace_create_file("trigger", 0644, file->dir, file,
- &event_trigger_fops);
+ /*
+ * Only event directories that can be enabled should have
+ * triggers.
+ */
+ if (!(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE))
+ trace_create_file("trigger", 0644, file->dir, file,
+ &event_trigger_fops);
trace_create_file("format", 0444, file->dir, call,
&ftrace_event_format_fops);
@@ -2195,7 +2200,7 @@
/*
* The top level array has already had its ftrace_event_file
* descriptors created in order to allow for early events to
- * be recorded. This function is called after the debugfs has been
+ * be recorded. This function is called after the tracefs has been
* initialized, and we now have to create the files associated
* to the events.
*/
@@ -2278,16 +2283,16 @@
struct dentry *d_events;
struct dentry *entry;
- entry = debugfs_create_file("set_event", 0644, parent,
+ entry = tracefs_create_file("set_event", 0644, parent,
tr, &ftrace_set_event_fops);
if (!entry) {
- pr_warn("Could not create debugfs 'set_event' entry\n");
+ pr_warn("Could not create tracefs 'set_event' entry\n");
return -ENOMEM;
}
- d_events = debugfs_create_dir("events", parent);
+ d_events = tracefs_create_dir("events", parent);
if (!d_events) {
- pr_warn("Could not create debugfs 'events' directory\n");
+ pr_warn("Could not create tracefs 'events' directory\n");
return -ENOMEM;
}
@@ -2379,7 +2384,7 @@
down_write(&trace_event_sem);
__trace_remove_event_dirs(tr);
- debugfs_remove_recursive(tr->event_dir);
+ tracefs_remove_recursive(tr->event_dir);
up_write(&trace_event_sem);
tr->event_dir = NULL;
@@ -2457,13 +2462,13 @@
return -ENODEV;
d_tracer = tracing_init_dentry();
- if (!d_tracer)
+ if (IS_ERR(d_tracer))
return 0;
- entry = debugfs_create_file("available_events", 0444, d_tracer,
+ entry = tracefs_create_file("available_events", 0444, d_tracer,
tr, &ftrace_avail_fops);
if (!entry)
- pr_warn("Could not create debugfs 'available_events' entry\n");
+ pr_warn("Could not create tracefs 'available_events' entry\n");
if (trace_define_common_fields())
pr_warn("tracing: Failed to allocate common fields");
@@ -2479,8 +2484,14 @@
#endif
return 0;
}
-early_initcall(event_trace_memsetup);
-core_initcall(event_trace_enable);
+
+void __init trace_event_init(void)
+{
+ event_trace_memsetup();
+ init_ftrace_syscalls();
+ event_trace_enable();
+}
+
fs_initcall(event_trace_init);
#ifdef CONFIG_FTRACE_STARTUP_TEST
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 70c2f41..df1f237 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -6,7 +6,6 @@
* is Copyright (c) Steven Rostedt <srostedt@redhat.com>
*
*/
-#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/ftrace.h>
#include <linux/slab.h>
@@ -156,7 +155,7 @@
* The curr_ret_stack is initialized to -1 and get increased
* in this function. So it can be less than -1 only if it was
* filtered out via ftrace_graph_notrace_addr() which can be
- * set from set_graph_notrace file in debugfs by user.
+ * set from set_graph_notrace file in tracefs by user.
*/
if (current->curr_ret_stack < -1)
return -EBUSY;
@@ -341,6 +340,13 @@
if (ftrace_graph_notrace_addr(trace->func))
return 1;
+ /*
+ * Stop here if tracing_threshold is set. We only write function return
+ * events to the ring buffer.
+ */
+ if (tracing_thresh)
+ return 1;
+
local_irq_save(flags);
cpu = raw_smp_processor_id();
data = per_cpu_ptr(tr->trace_buffer.data, cpu);
@@ -358,14 +364,6 @@
return ret;
}
-static int trace_graph_thresh_entry(struct ftrace_graph_ent *trace)
-{
- if (tracing_thresh)
- return 1;
- else
- return trace_graph_entry(trace);
-}
-
static void
__trace_graph_function(struct trace_array *tr,
unsigned long ip, unsigned long flags, int pc)
@@ -463,7 +461,7 @@
set_graph_array(tr);
if (tracing_thresh)
ret = register_ftrace_graph(&trace_graph_thresh_return,
- &trace_graph_thresh_entry);
+ &trace_graph_entry);
else
ret = register_ftrace_graph(&trace_graph_return,
&trace_graph_entry);
@@ -882,6 +880,10 @@
cpu_data = per_cpu_ptr(data->cpu_data, cpu);
+ /* If a graph tracer ignored set_graph_notrace */
+ if (call->depth < -1)
+ call->depth += FTRACE_NOTRACE_DEPTH;
+
/*
* Comments display at + 1 to depth. Since
* this is a leaf function, keep the comments
@@ -890,7 +892,8 @@
cpu_data->depth = call->depth - 1;
/* No need to keep this function around for this depth */
- if (call->depth < FTRACE_RETFUNC_DEPTH)
+ if (call->depth < FTRACE_RETFUNC_DEPTH &&
+ !WARN_ON_ONCE(call->depth < 0))
cpu_data->enter_funcs[call->depth] = 0;
}
@@ -927,11 +930,16 @@
struct fgraph_cpu_data *cpu_data;
int cpu = iter->cpu;
+ /* If a graph tracer ignored set_graph_notrace */
+ if (call->depth < -1)
+ call->depth += FTRACE_NOTRACE_DEPTH;
+
cpu_data = per_cpu_ptr(data->cpu_data, cpu);
cpu_data->depth = call->depth;
/* Save this function pointer to see if the exit matches */
- if (call->depth < FTRACE_RETFUNC_DEPTH)
+ if (call->depth < FTRACE_RETFUNC_DEPTH &&
+ !WARN_ON_ONCE(call->depth < 0))
cpu_data->enter_funcs[call->depth] = call->func;
}
@@ -1184,7 +1192,8 @@
*/
cpu_data->depth = trace->depth - 1;
- if (trace->depth < FTRACE_RETFUNC_DEPTH) {
+ if (trace->depth < FTRACE_RETFUNC_DEPTH &&
+ !WARN_ON_ONCE(trace->depth < 0)) {
if (cpu_data->enter_funcs[trace->depth] != trace->func)
func_match = 0;
cpu_data->enter_funcs[trace->depth] = 0;
@@ -1582,12 +1591,12 @@
.llseek = generic_file_llseek,
};
-static __init int init_graph_debugfs(void)
+static __init int init_graph_tracefs(void)
{
struct dentry *d_tracer;
d_tracer = tracing_init_dentry();
- if (!d_tracer)
+ if (IS_ERR(d_tracer))
return 0;
trace_create_file("max_graph_depth", 0644, d_tracer,
@@ -1595,7 +1604,7 @@
return 0;
}
-fs_initcall(init_graph_debugfs);
+fs_initcall(init_graph_tracefs);
static __init int init_graph_trace(void)
{
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index 9bb104f7..3fc86e7 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -15,6 +15,8 @@
#include <linux/module.h>
#include <linux/ftrace.h>
#include <linux/fs.h>
+#define CREATE_TRACE_POINTS
+#include <trace/events/critical.h>
#include "trace.h"
@@ -22,6 +24,7 @@
static int tracer_enabled __read_mostly;
static DEFINE_PER_CPU(int, tracing_cpu);
+static DEFINE_PER_CPU(int, tracing_events_cpu);
static DEFINE_RAW_SPINLOCK(max_trace_lock);
@@ -118,8 +121,12 @@
return 0;
local_save_flags(*flags);
- /* slight chance to get a false positive on tracing_cpu */
- if (!irqs_disabled_flags(*flags))
+ /*
+ * Slight chance to get a false positive on tracing_cpu,
+ * although I'm starting to think there isn't a chance.
+ * Leave this for now just to be paranoid.
+ */
+ if (!irqs_disabled_flags(*flags) && !preempt_count())
return 0;
*data = per_cpu_ptr(tr->trace_buffer.data, cpu);
@@ -361,6 +368,44 @@
__trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc);
}
+#ifdef CONFIG_TRACE_CRITICAL_SECTION_EVENTS
+/*
+ * Called when either preempt or irq are turned off
+ */
+static inline void
+start_critical_event(unsigned long ip, unsigned long parent_ip)
+{
+ int cpu;
+
+ cpu = raw_smp_processor_id();
+
+ if (per_cpu(tracing_events_cpu, cpu))
+ return;
+
+ trace_critical_start(ip, parent_ip);
+
+ per_cpu(tracing_events_cpu, cpu) = 1;
+}
+
+/*
+ * Called when both preempt and irq are turned back on
+ */
+static inline void
+stop_critical_event(unsigned long ip, unsigned long parent_ip)
+{
+ int cpu;
+
+ cpu = raw_smp_processor_id();
+
+ if (unlikely(per_cpu(tracing_events_cpu, cpu)))
+ per_cpu(tracing_events_cpu, cpu) = 0;
+ else
+ return;
+
+ trace_critical_end(ip, parent_ip);
+}
+#endif
+
static inline void
start_critical_timing(unsigned long ip, unsigned long parent_ip)
{
@@ -433,6 +478,10 @@
/* start and stop critical timings used to for stoppage (in idle) */
void start_critical_timings(void)
{
+#ifdef CONFIG_TRACE_CRITICAL_SECTION_EVENTS
+ if (irqs_disabled() || preempt_count())
+ start_critical_event(CALLER_ADDR0, CALLER_ADDR1);
+#endif
if (preempt_trace() || irq_trace())
start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
@@ -440,6 +489,10 @@
void stop_critical_timings(void)
{
+#ifdef CONFIG_TRACE_CRITICAL_SECTION_EVENTS
+ if (irqs_disabled() || preempt_count())
+ stop_critical_event(CALLER_ADDR0, CALLER_ADDR1);
+#endif
if (preempt_trace() || irq_trace())
stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
@@ -482,6 +535,10 @@
*/
void trace_hardirqs_on(void)
{
+#ifdef CONFIG_TRACE_CRITICAL_SECTION_EVENTS
+ if (!preempt_count())
+ stop_critical_event(CALLER_ADDR0, CALLER_ADDR1);
+#endif
if (!preempt_trace() && irq_trace())
stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
@@ -489,6 +546,10 @@
void trace_hardirqs_off(void)
{
+#ifdef CONFIG_TRACE_CRITICAL_SECTION_EVENTS
+ if (!preempt_count())
+ start_critical_event(CALLER_ADDR0, CALLER_ADDR1);
+#endif
if (!preempt_trace() && irq_trace())
start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
@@ -496,6 +557,10 @@
__visible void trace_hardirqs_on_caller(unsigned long caller_addr)
{
+#ifdef CONFIG_TRACE_CRITICAL_SECTION_EVENTS
+ if (!preempt_count())
+ stop_critical_event(CALLER_ADDR0, caller_addr);
+#endif
if (!preempt_trace() && irq_trace())
stop_critical_timing(CALLER_ADDR0, caller_addr);
}
@@ -503,6 +568,10 @@
__visible void trace_hardirqs_off_caller(unsigned long caller_addr)
{
+#ifdef CONFIG_TRACE_CRITICAL_SECTION_EVENTS
+ if (!preempt_count())
+ start_critical_event(CALLER_ADDR0, caller_addr);
+#endif
if (!preempt_trace() && irq_trace())
start_critical_timing(CALLER_ADDR0, caller_addr);
}
@@ -514,12 +583,20 @@
#ifdef CONFIG_PREEMPT_TRACER
void trace_preempt_on(unsigned long a0, unsigned long a1)
{
+#ifdef CONFIG_TRACE_CRITICAL_SECTION_EVENTS
+ if (!irqs_disabled())
+ stop_critical_event(a0, a1);
+#endif
if (preempt_trace() && !irq_trace())
stop_critical_timing(a0, a1);
}
void trace_preempt_off(unsigned long a0, unsigned long a1)
{
+#ifdef CONFIG_TRACE_CRITICAL_SECTION_EVENTS
+ if (!irqs_disabled())
+ start_critical_event(a0, a1);
+#endif
if (preempt_trace() && !irq_trace())
start_critical_timing(a0, a1);
}
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 282f6e4..e47bea3 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -1158,7 +1158,7 @@
size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32);
- entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx);
+ entry = perf_trace_buf_prepare(size, call->event.type, NULL, &rctx);
if (!entry)
return;
@@ -1189,7 +1189,7 @@
size = ALIGN(__size + sizeof(u32), sizeof(u64));
size -= sizeof(u32);
- entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx);
+ entry = perf_trace_buf_prepare(size, call->event.type, NULL, &rctx);
if (!entry)
return;
@@ -1320,7 +1320,7 @@
return ret;
}
-/* Make a debugfs interface for controlling probe points */
+/* Make a tracefs interface for controlling probe points */
static __init int init_kprobe_trace(void)
{
struct dentry *d_tracer;
@@ -1330,23 +1330,23 @@
return -EINVAL;
d_tracer = tracing_init_dentry();
- if (!d_tracer)
+ if (IS_ERR(d_tracer))
return 0;
- entry = debugfs_create_file("kprobe_events", 0644, d_tracer,
+ entry = tracefs_create_file("kprobe_events", 0644, d_tracer,
NULL, &kprobe_events_ops);
/* Event list interface */
if (!entry)
- pr_warning("Could not create debugfs "
+ pr_warning("Could not create tracefs "
"'kprobe_events' entry\n");
/* Profile interface */
- entry = debugfs_create_file("kprobe_profile", 0444, d_tracer,
+ entry = tracefs_create_file("kprobe_profile", 0444, d_tracer,
NULL, &kprobe_profile_ops);
if (!entry)
- pr_warning("Could not create debugfs "
+ pr_warning("Could not create tracefs "
"'kprobe_profile' entry\n");
return 0;
}
diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c
index 50f7500..d90c647 100644
--- a/kernel/trace/trace_printk.c
+++ b/kernel/trace/trace_printk.c
@@ -38,6 +38,10 @@
static inline struct trace_bprintk_fmt *lookup_format(const char *fmt)
{
struct trace_bprintk_fmt *pos;
+
+ if (!fmt)
+ return ERR_PTR(-EINVAL);
+
list_for_each_entry(pos, &trace_bprintk_fmt_list, list) {
if (!strcmp(pos->fmt, fmt))
return pos;
@@ -59,7 +63,8 @@
for (iter = start; iter < end; iter++) {
struct trace_bprintk_fmt *tb_fmt = lookup_format(*iter);
if (tb_fmt) {
- *iter = tb_fmt->fmt;
+ if (!IS_ERR(tb_fmt))
+ *iter = tb_fmt->fmt;
continue;
}
@@ -352,7 +357,7 @@
struct dentry *d_tracer;
d_tracer = tracing_init_dentry();
- if (!d_tracer)
+ if (IS_ERR(d_tracer))
return 0;
trace_create_file("printk_formats", 0444, d_tracer,
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 4f815fb..19aff63 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -25,7 +25,7 @@
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/smp.h>
-#include <linux/debugfs.h>
+#include <linux/tracefs.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c
index 3f34dc9..9586cde 100644
--- a/kernel/trace/trace_sched_switch.c
+++ b/kernel/trace/trace_sched_switch.c
@@ -106,7 +106,7 @@
}
static void
-probe_sched_wakeup(void *ignore, struct task_struct *wakee, int success)
+probe_sched_wakeup(void *ignore, struct task_struct *wakee)
{
struct trace_array_cpu *data;
unsigned long flags;
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
index 19bd892..808258c 100644
--- a/kernel/trace/trace_sched_wakeup.c
+++ b/kernel/trace/trace_sched_wakeup.c
@@ -460,7 +460,7 @@
}
static void
-probe_wakeup(void *ignore, struct task_struct *p, int success)
+probe_wakeup(void *ignore, struct task_struct *p)
{
struct trace_array_cpu *data;
int cpu = smp_processor_id();
diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c
index 16eddb3..e3099ff 100644
--- a/kernel/trace/trace_stack.c
+++ b/kernel/trace/trace_stack.c
@@ -462,7 +462,7 @@
struct dentry *d_tracer;
d_tracer = tracing_init_dentry();
- if (!d_tracer)
+ if (IS_ERR(d_tracer))
return 0;
trace_create_file("stack_max_size", 0644, d_tracer,
diff --git a/kernel/trace/trace_stat.c b/kernel/trace/trace_stat.c
index 7af6736..6cf9353 100644
--- a/kernel/trace/trace_stat.c
+++ b/kernel/trace/trace_stat.c
@@ -12,7 +12,7 @@
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/rbtree.h>
-#include <linux/debugfs.h>
+#include <linux/tracefs.h>
#include "trace_stat.h"
#include "trace.h"
@@ -65,7 +65,7 @@
static void destroy_session(struct stat_session *session)
{
- debugfs_remove(session->file);
+ tracefs_remove(session->file);
__reset_stat_session(session);
mutex_destroy(&session->stat_mutex);
kfree(session);
@@ -276,12 +276,12 @@
struct dentry *d_tracing;
d_tracing = tracing_init_dentry();
- if (!d_tracing)
+ if (IS_ERR(d_tracing))
return 0;
- stat_dir = debugfs_create_dir("trace_stat", d_tracing);
+ stat_dir = tracefs_create_dir("trace_stat", d_tracing);
if (!stat_dir)
- pr_warning("Could not create debugfs "
+ pr_warning("Could not create tracefs "
"'trace_stat' entry\n");
return 0;
}
@@ -291,7 +291,7 @@
if (!stat_dir && tracing_stat_init())
return -ENODEV;
- session->file = debugfs_create_file(session->ts->name, 0644,
+ session->file = tracefs_create_file(session->ts->name, 0644,
stat_dir,
session, &tracing_stat_fops);
if (!session->file)
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c
index 5097a8e..565ace8 100644
--- a/kernel/trace/trace_syscalls.c
+++ b/kernel/trace/trace_syscalls.c
@@ -523,7 +523,7 @@
return (unsigned long)sys_call_table[nr];
}
-static int __init init_ftrace_syscalls(void)
+void __init init_ftrace_syscalls(void)
{
struct syscall_metadata *meta;
unsigned long addr;
@@ -533,7 +533,7 @@
GFP_KERNEL);
if (!syscalls_metadata) {
WARN_ON(1);
- return -ENOMEM;
+ return;
}
for (i = 0; i < NR_syscalls; i++) {
@@ -545,10 +545,7 @@
meta->syscall_nr = i;
syscalls_metadata[i] = meta;
}
-
- return 0;
}
-early_initcall(init_ftrace_syscalls);
#ifdef CONFIG_PERF_EVENTS
@@ -586,7 +583,7 @@
size -= sizeof(u32);
rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size,
- sys_data->enter_event->event.type, regs, &rctx);
+ sys_data->enter_event->event.type, NULL, &rctx);
if (!rec)
return;
@@ -659,7 +656,7 @@
size -= sizeof(u32);
rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size,
- sys_data->exit_event->event.type, regs, &rctx);
+ sys_data->exit_event->event.type, NULL, &rctx);
if (!rec)
return;
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index e6d55e3..94bc5ea 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -1115,7 +1115,7 @@
if (hlist_empty(head))
goto out;
- entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx);
+ entry = perf_trace_buf_prepare(size, call->event.type, NULL, &rctx);
if (!entry)
goto out;
@@ -1325,7 +1325,7 @@
struct dentry *d_tracer;
d_tracer = tracing_init_dentry();
- if (!d_tracer)
+ if (IS_ERR(d_tracer))
return 0;
trace_create_file("uprobe_events", 0644, d_tracer,
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index a2e37c5d..cc96896 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -39,6 +39,7 @@
cred->cap_inheritable = CAP_EMPTY_SET;
cred->cap_permitted = CAP_FULL_SET;
cred->cap_effective = CAP_FULL_SET;
+ cred->cap_ambient = CAP_EMPTY_SET;
cred->cap_bset = CAP_FULL_SET;
#ifdef CONFIG_KEYS
key_put(cred->request_key_auth);
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 3d7bb06..02f435e 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -624,6 +624,35 @@
*/
smp_wmb();
set_work_data(work, (unsigned long)pool_id << WORK_OFFQ_POOL_SHIFT, 0);
+ /*
+ * The following mb guarantees that previous clear of a PENDING bit
+ * will not be reordered with any speculative LOADS or STORES from
+ * work->current_func, which is executed afterwards. This possible
+ * reordering can lead to a missed execution on attempt to qeueue
+ * the same @work. E.g. consider this case:
+ *
+ * CPU#0 CPU#1
+ * ---------------------------- --------------------------------
+ *
+ * 1 STORE event_indicated
+ * 2 queue_work_on() {
+ * 3 test_and_set_bit(PENDING)
+ * 4 } set_..._and_clear_pending() {
+ * 5 set_work_data() # clear bit
+ * 6 smp_mb()
+ * 7 work->current_func() {
+ * 8 LOAD event_indicated
+ * }
+ *
+ * Without an explicit full barrier speculative LOAD on line 8 can
+ * be executed before CPU#0 does STORE on line 1. If that happens,
+ * CPU#0 observes the PENDING bit is still set and new execution of
+ * a @work is not queued in a hope, that CPU#1 will eventually
+ * finish the queued @work. Meanwhile CPU#1 does not see
+ * event_indicated is set, because speculative LOAD was executed
+ * before actual STORE.
+ */
+ smp_mb();
}
static void clear_work_data(struct work_struct *work)
@@ -4516,6 +4545,17 @@
pool->attrs->cpumask) < 0);
spin_lock_irq(&pool->lock);
+
+ /*
+ * XXX: CPU hotplug notifiers are weird and can call DOWN_FAILED
+ * w/o preceding DOWN_PREPARE. Work around it. CPU hotplug is
+ * being reworked and this can go away in time.
+ */
+ if (!(pool->flags & POOL_DISASSOCIATED)) {
+ spin_unlock_irq(&pool->lock);
+ return;
+ }
+
pool->flags &= ~POOL_DISASSOCIATED;
for_each_pool_worker(worker, pool) {
diff --git a/lib/cpumask.c b/lib/cpumask.c
index b6513a9..c0bd0df 100644
--- a/lib/cpumask.c
+++ b/lib/cpumask.c
@@ -166,64 +166,42 @@
#endif
/**
- * cpumask_set_cpu_local_first - set i'th cpu with local numa cpu's first
- *
+ * cpumask_local_spread - select the i'th cpu with local numa cpu's first
* @i: index number
- * @numa_node: local numa_node
- * @dstp: cpumask with the relevant cpu bit set according to the policy
+ * @node: local numa_node
*
- * This function sets the cpumask according to a numa aware policy.
- * cpumask could be used as an affinity hint for the IRQ related to a
- * queue. When the policy is to spread queues across cores - local cores
- * first.
+ * This function selects an online CPU according to a numa aware policy;
+ * local cpus are returned first, followed by non-local ones, then it
+ * wraps around.
*
- * Returns 0 on success, -ENOMEM for no memory, and -EAGAIN when failed to set
- * the cpu bit and need to re-call the function.
+ * It's not very efficient, but useful for setup.
*/
-int cpumask_set_cpu_local_first(int i, int numa_node, cpumask_t *dstp)
+unsigned int cpumask_local_spread(unsigned int i, int node)
{
- cpumask_var_t mask;
int cpu;
- int ret = 0;
- if (!zalloc_cpumask_var(&mask, GFP_KERNEL))
- return -ENOMEM;
-
+ /* Wrap: we always want a cpu. */
i %= num_online_cpus();
- if (numa_node == -1 || !cpumask_of_node(numa_node)) {
- /* Use all online cpu's for non numa aware system */
- cpumask_copy(mask, cpu_online_mask);
+ if (node == -1) {
+ for_each_cpu(cpu, cpu_online_mask)
+ if (i-- == 0)
+ return cpu;
} else {
- int n;
+ /* NUMA first. */
+ for_each_cpu_and(cpu, cpumask_of_node(node), cpu_online_mask)
+ if (i-- == 0)
+ return cpu;
- cpumask_and(mask,
- cpumask_of_node(numa_node), cpu_online_mask);
+ for_each_cpu(cpu, cpu_online_mask) {
+ /* Skip NUMA nodes, done above. */
+ if (cpumask_test_cpu(cpu, cpumask_of_node(node)))
+ continue;
- n = cpumask_weight(mask);
- if (i >= n) {
- i -= n;
-
- /* If index > number of local cpu's, mask out local
- * cpu's
- */
- cpumask_andnot(mask, cpu_online_mask, mask);
+ if (i-- == 0)
+ return cpu;
}
}
-
- for_each_cpu(cpu, mask) {
- if (--i < 0)
- goto out;
- }
-
- ret = -EAGAIN;
-
-out:
- free_cpumask_var(mask);
-
- if (!ret)
- cpumask_set_cpu(cpu, dstp);
-
- return ret;
+ BUG();
}
-EXPORT_SYMBOL(cpumask_set_cpu_local_first);
+EXPORT_SYMBOL(cpumask_local_spread);
diff --git a/lib/dma-debug.c b/lib/dma-debug.c
index 1ff0fd0..c83ac43 100644
--- a/lib/dma-debug.c
+++ b/lib/dma-debug.c
@@ -646,9 +646,9 @@
spin_lock_irqsave(&free_entries_lock, flags);
if (list_empty(&free_entries)) {
- pr_err("DMA-API: debugging out of memory - disabling\n");
global_disable = true;
spin_unlock_irqrestore(&free_entries_lock, flags);
+ pr_err("DMA-API: debugging out of memory - disabling\n");
return NULL;
}
diff --git a/lib/kstrtox.c b/lib/kstrtox.c
index ec8da78..fc641e3 100644
--- a/lib/kstrtox.c
+++ b/lib/kstrtox.c
@@ -321,6 +321,56 @@
}
EXPORT_SYMBOL(kstrtos8);
+/**
+ * kstrtobool - convert common user inputs into boolean values
+ * @s: input string
+ * @res: result
+ *
+ * This routine returns 0 iff the first character is one of 'Yy1Nn0'.
+ * Otherwise it will return -EINVAL. Value pointed to by res is
+ * updated upon finding a match.
+ */
+int kstrtobool(const char *s, bool *res)
+{
+ if (!s)
+ return -EINVAL;
+
+ switch (s[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ *res = true;
+ return 0;
+ case 'n':
+ case 'N':
+ case '0':
+ *res = false;
+ return 0;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(kstrtobool);
+
+/*
+ * Since "base" would be a nonsense argument, this open-codes the
+ * _from_user helper instead of using the helper macro below.
+ */
+int kstrtobool_from_user(const char __user *s, size_t count, bool *res)
+{
+ /* Longest string needed to differentiate, newline, terminator */
+ char buf[4];
+
+ count = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, s, count))
+ return -EFAULT;
+ buf[count] = '\0';
+ return kstrtobool(buf, res);
+}
+EXPORT_SYMBOL(kstrtobool_from_user);
+
#define kstrto_from_user(f, g, type) \
int f(const char __user *s, size_t count, unsigned int base, type *res) \
{ \
diff --git a/lib/lockref.c b/lib/lockref.c
index d2233de..ecb9a665 100644
--- a/lib/lockref.c
+++ b/lib/lockref.c
@@ -60,7 +60,7 @@
EXPORT_SYMBOL(lockref_get);
/**
- * lockref_get_not_zero - Increments count unless the count is 0
+ * lockref_get_not_zero - Increments count unless the count is 0 or dead
* @lockref: pointer to lockref structure
* Return: 1 if count updated successfully or 0 if count was zero
*/
@@ -70,7 +70,7 @@
CMPXCHG_LOOP(
new.count++;
- if (!old.count)
+ if (old.count <= 0)
return 0;
,
return 1;
@@ -78,7 +78,7 @@
spin_lock(&lockref->lock);
retval = 0;
- if (lockref->count) {
+ if (lockref->count > 0) {
lockref->count++;
retval = 1;
}
@@ -88,7 +88,7 @@
EXPORT_SYMBOL(lockref_get_not_zero);
/**
- * lockref_get_or_lock - Increments count unless the count is 0
+ * lockref_get_or_lock - Increments count unless the count is 0 or dead
* @lockref: pointer to lockref structure
* Return: 1 if count updated successfully or 0 if count was zero
* and we got the lock instead.
@@ -97,14 +97,14 @@
{
CMPXCHG_LOOP(
new.count++;
- if (!old.count)
+ if (old.count <= 0)
break;
,
return 1;
);
spin_lock(&lockref->lock);
- if (!lockref->count)
+ if (lockref->count <= 0)
return 0;
lockref->count++;
spin_unlock(&lockref->lock);
@@ -113,6 +113,26 @@
EXPORT_SYMBOL(lockref_get_or_lock);
/**
+ * lockref_put_return - Decrement reference count if possible
+ * @lockref: pointer to lockref structure
+ *
+ * Decrement the reference count and return the new value.
+ * If the lockref was dead or locked, return an error.
+ */
+int lockref_put_return(struct lockref *lockref)
+{
+ CMPXCHG_LOOP(
+ new.count--;
+ if (old.count <= 0)
+ return -1;
+ ,
+ return new.count;
+ );
+ return -1;
+}
+EXPORT_SYMBOL(lockref_put_return);
+
+/**
* lockref_put_or_lock - decrements count unless count <= 1 before decrement
* @lockref: pointer to lockref structure
* Return: 1 if count updated successfully or 0 if count <= 1 and lock taken
@@ -158,7 +178,7 @@
CMPXCHG_LOOP(
new.count++;
- if ((int)old.count < 0)
+ if (old.count < 0)
return 0;
,
return 1;
@@ -166,7 +186,7 @@
spin_lock(&lockref->lock);
retval = 0;
- if ((int) lockref->count >= 0) {
+ if (lockref->count >= 0) {
lockref->count++;
retval = 1;
}
diff --git a/lib/lz4/lz4defs.h b/lib/lz4/lz4defs.h
index abcecdc..0710a62 100644
--- a/lib/lz4/lz4defs.h
+++ b/lib/lz4/lz4defs.h
@@ -11,8 +11,7 @@
/*
* Detects 64 bits mode
*/
-#if (defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) \
- || defined(__ppc64__) || defined(__LP64__))
+#if defined(CONFIG_64BIT)
#define LZ4_ARCH64 1
#else
#define LZ4_ARCH64 0
@@ -35,6 +34,10 @@
#define PUT4(s, d) (A32(d) = A32(s))
#define PUT8(s, d) (A64(d) = A64(s))
+
+#define LZ4_READ_LITTLEENDIAN_16(d, s, p) \
+ (d = s - A16(p))
+
#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \
do { \
A16(p) = v; \
@@ -51,10 +54,13 @@
#define PUT8(s, d) \
put_unaligned(get_unaligned((const u64 *) s), (u64 *) d)
-#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \
- do { \
- put_unaligned(v, (u16 *)(p)); \
- p += 2; \
+#define LZ4_READ_LITTLEENDIAN_16(d, s, p) \
+ (d = s - get_unaligned_le16(p))
+
+#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \
+ do { \
+ put_unaligned_le16(v, (u16 *)(p)); \
+ p += 2; \
} while (0)
#endif
@@ -140,9 +146,6 @@
#endif
-#define LZ4_READ_LITTLEENDIAN_16(d, s, p) \
- (d = s - get_unaligned_le16(p))
-
#define LZ4_WILDCOPY(s, d, e) \
do { \
LZ4_COPYPACKET(s, d); \
diff --git a/lib/string.c b/lib/string.c
index 9e1b904..e2108ee 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -550,33 +550,30 @@
EXPORT_SYMBOL(sysfs_streq);
/**
- * strtobool - convert common user inputs into boolean values
- * @s: input string
- * @res: result
+ * __sysfs_match_string - matches given string in an array
+ * @array: array of strings
+ * @n: number of strings in the array or -1 for NULL terminated arrays
+ * @str: string to match with
*
- * This routine returns 0 iff the first character is one of 'Yy1Nn0'.
- * Otherwise it will return -EINVAL. Value pointed to by res is
- * updated upon finding a match.
+ * Returns index of @str in the @array or -EINVAL, just like match_string().
+ * Uses sysfs_streq instead of strcmp for matching.
*/
-int strtobool(const char *s, bool *res)
+int __sysfs_match_string(const char * const *array, size_t n, const char *str)
{
- switch (s[0]) {
- case 'y':
- case 'Y':
- case '1':
- *res = true;
- break;
- case 'n':
- case 'N':
- case '0':
- *res = false;
- break;
- default:
- return -EINVAL;
+ const char *item;
+ int index;
+
+ for (index = 0; index < n; index++) {
+ item = array[index];
+ if (!item)
+ break;
+ if (sysfs_streq(item, str))
+ return index;
}
- return 0;
+
+ return -EINVAL;
}
-EXPORT_SYMBOL(strtobool);
+EXPORT_SYMBOL(__sysfs_match_string);
#ifndef __HAVE_ARCH_MEMSET
/**
diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
index e238ec0..bd8c773 100644
--- a/lib/strncpy_from_user.c
+++ b/lib/strncpy_from_user.c
@@ -1,4 +1,5 @@
#include <linux/module.h>
+#include <linux/thread_info.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -55,8 +56,8 @@
unsigned long c, data;
/* Fall back to byte-at-a-time if we get a page fault */
- if (unlikely(__get_user(c,(unsigned long __user *)(src+res))))
- break;
+ unsafe_get_user(c, (unsigned long __user *)(src+res), byte_at_a_time);
+
*(unsigned long *)(dst+res) = c;
if (has_zero(c, &data, &constants)) {
data = prep_zero_mask(c, data, &constants);
@@ -71,8 +72,7 @@
while (max) {
char c;
- if (unlikely(__get_user(c,src+res)))
- return -EFAULT;
+ unsafe_get_user(c,src+res, efault);
dst[res] = c;
if (!c)
return res;
@@ -91,6 +91,7 @@
* Nope: we hit the address space limit, and we still had more
* characters the caller would have wanted. That's an EFAULT.
*/
+efault:
return -EFAULT;
}
@@ -123,7 +124,13 @@
src_addr = (unsigned long)src;
if (likely(src_addr < max_addr)) {
unsigned long max = max_addr - src_addr;
- return do_strncpy_from_user(dst, src, count, max);
+ long retval;
+
+ check_object_size(dst, count, false);
+ user_access_begin();
+ retval = do_strncpy_from_user(dst, src, count, max);
+ user_access_end();
+ return retval;
}
return -EFAULT;
}
diff --git a/lib/strnlen_user.c b/lib/strnlen_user.c
index 1164961..02a49fc 100644
--- a/lib/strnlen_user.c
+++ b/lib/strnlen_user.c
@@ -45,8 +45,7 @@
src -= align;
max += align;
- if (unlikely(__get_user(c,(unsigned long __user *)src)))
- return 0;
+ unsafe_get_user(c, (unsigned long __user *)src, efault);
c |= aligned_byte_mask(align);
for (;;) {
@@ -61,8 +60,7 @@
if (unlikely(max <= sizeof(unsigned long)))
break;
max -= sizeof(unsigned long);
- if (unlikely(__get_user(c,(unsigned long __user *)(src+res))))
- return 0;
+ unsafe_get_user(c, (unsigned long __user *)(src+res), efault);
}
res -= align;
@@ -77,6 +75,7 @@
* Nope: we hit the address space limit, and we still had more
* characters the caller would have wanted. That's 0.
*/
+efault:
return 0;
}
@@ -104,7 +103,12 @@
src_addr = (unsigned long)str;
if (likely(src_addr < max_addr)) {
unsigned long max = max_addr - src_addr;
- return do_strnlen_user(str, count, max);
+ long retval;
+
+ user_access_begin();
+ retval = do_strnlen_user(str, count, max);
+ user_access_end();
+ return retval;
}
return 0;
}
@@ -132,7 +136,12 @@
src_addr = (unsigned long)str;
if (likely(src_addr < max_addr)) {
unsigned long max = max_addr - src_addr;
- return do_strnlen_user(str, ~0ul, max);
+ long retval;
+
+ user_access_begin();
+ retval = do_strnlen_user(str, ~0ul, max);
+ user_access_end();
+ return retval;
}
return 0;
}
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index ec337f64..705f4f0 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -378,6 +378,32 @@
s16 precision; /* # of digits/chars */
};
+int kptr_restrict __read_mostly = 4;
+
+/*
+ * Always cleanse %p and %pK specifiers
+ */
+static inline int kptr_restrict_always_cleanse_pointers(void)
+{
+ return kptr_restrict >= 3;
+}
+
+/*
+ * Always cleanse physical addresses (%pa* specifiers)
+ */
+static inline int kptr_restrict_cleanse_addresses(void)
+{
+ return kptr_restrict >= 4;
+}
+
+/*
+ * Always cleanse resource addresses (%p[rR] specifiers)
+ */
+static inline int kptr_restrict_cleanse_resources(void)
+{
+ return kptr_restrict >= 4;
+}
+
static noinline_for_stack
char *number(char *buf, char *end, unsigned long long num,
struct printf_spec spec)
@@ -696,6 +722,7 @@
char *p = sym, *pend = sym + sizeof(sym);
int decode = (fmt[0] == 'R') ? 1 : 0;
+ int cleanse = kptr_restrict_cleanse_resources();
const struct printf_spec *specp;
*p++ = '[';
@@ -723,10 +750,11 @@
p = string(p, pend, "size ", str_spec);
p = number(p, pend, resource_size(res), *specp);
} else {
- p = number(p, pend, res->start, *specp);
+ p = number(p, pend, cleanse ? 0UL : res->start, *specp);
if (res->start != res->end) {
*p++ = '-';
- p = number(p, pend, res->end, *specp);
+ p = number(p, pend,
+ cleanse ? res->end - res->start : res->end, *specp);
}
}
if (decode) {
@@ -746,6 +774,7 @@
*p = '\0';
return string(buf, end, sym, spec);
+
}
static noinline_for_stack
@@ -1237,11 +1266,11 @@
break;
}
- return number(buf, end, num, spec);
+ return number(buf, end,
+ kptr_restrict_cleanse_addresses() ? 0UL : num,
+ spec);
}
-int kptr_restrict __read_mostly;
-
/*
* Show a '%p' thing. A kernel extension is that the '%p' is followed
* by an extra set of alphanumeric characters that are extended format
@@ -1306,6 +1335,7 @@
* Do not use this feature without some mechanism to verify the
* correctness of the format string and va_list arguments.
* - 'K' For a kernel pointer that should be hidden from unprivileged users
+ * - 'P' For a kernel pointer that should be shown to all users
* - 'NF' For a netdev_features_t
* - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with
* a certain separator (' ' by default):
@@ -1322,6 +1352,15 @@
* Note: The difference between 'S' and 'F' is that on ia64 and ppc64
* function pointers are really function descriptors, which contain a
* pointer to the real address.
+ *
+ * Note: That for kptr_restrict set to 3, %p and %pK have the same
+ * meaning.
+ *
+ * Note: That for kptr_restrict set to 4, %pa will null out the physical
+ * address.
+ *
+ * Note: That for kptr_restrict set to 4, %p[rR] will null out the memory
+ * address.
*/
static noinline_for_stack
char *pointer(const char *fmt, char *buf, char *end, void *ptr,
@@ -1329,7 +1368,7 @@
{
int default_width = 2 * sizeof(void *) + (spec.flags & SPECIAL ? 2 : 0);
- if (!ptr && *fmt != 'K') {
+ if (!ptr && *fmt != 'K' && !kptr_restrict_always_cleanse_pointers()) {
/*
* Print (null) with the same width as a pointer so it makes
* tabular output look nice.
@@ -1403,48 +1442,6 @@
va_end(va);
return buf;
}
- case 'K':
- /*
- * %pK cannot be used in IRQ context because its test
- * for CAP_SYSLOG would be meaningless.
- */
- if (kptr_restrict && (in_irq() || in_serving_softirq() ||
- in_nmi())) {
- if (spec.field_width == -1)
- spec.field_width = default_width;
- return string(buf, end, "pK-error", spec);
- }
-
- switch (kptr_restrict) {
- case 0:
- /* Always print %pK values */
- break;
- case 1: {
- /*
- * Only print the real pointer value if the current
- * process has CAP_SYSLOG and is running with the
- * same credentials it started with. This is because
- * access to files is checked at open() time, but %pK
- * checks permission at read() time. We don't want to
- * leak pointer values if a binary opens a file using
- * %pK and then elevates privileges before reading it.
- */
- const struct cred *cred = current_cred();
-
- if (!has_capability_noaudit(current, CAP_SYSLOG) ||
- !uid_eq(cred->euid, cred->uid) ||
- !gid_eq(cred->egid, cred->gid))
- ptr = NULL;
- break;
- }
- case 2:
- default:
- /* Always print 0's for %pK */
- ptr = NULL;
- break;
- }
- break;
-
case 'N':
switch (fmt[1]) {
case 'F':
@@ -1459,6 +1456,63 @@
return dentry_name(buf, end,
((const struct file *)ptr)->f_path.dentry,
spec, fmt);
+ case 'P':
+ /*
+ * an explicitly whitelisted kernel pointer should never be
+ * cleansed
+ */
+ break;
+ default:
+ /*
+ * plain %p, no extension, check if we should always cleanse and
+ * treat like %pK.
+ */
+ if (!kptr_restrict_always_cleanse_pointers()) {
+ break;
+ }
+ /* fallthrough */
+ case 'K':
+ switch (kptr_restrict) {
+ case 0:
+ /* Always print %p values */
+ break;
+ case 1: {
+ const struct cred *cred;
+
+ /*
+ * kptr_restrict==1 cannot be used in IRQ context
+ * because its test for CAP_SYSLOG would be meaningless.
+ */
+ if (in_irq() || in_serving_softirq() || in_nmi()) {
+ if (spec.field_width == -1)
+ spec.field_width = default_width;
+ return string(buf, end, "pK-error", spec);
+ }
+
+ /*
+ * Only print the real pointer value if the current
+ * process has CAP_SYSLOG and is running with the
+ * same credentials it started with. This is because
+ * access to files is checked at open() time, but %p
+ * checks permission at read() time. We don't want to
+ * leak pointer values if a binary opens a file using
+ * %pK and then elevates privileges before reading it.
+ */
+ cred = current_cred();
+ if (!has_capability_noaudit(current, CAP_SYSLOG) ||
+ !uid_eq(cred->euid, cred->uid) ||
+ !gid_eq(cred->egid, cred->gid))
+ ptr = NULL;
+ break;
+ }
+ case 2: /* restrict only %pK */
+ case 3: /* restrict all non-extensioned %p and %pK */
+ case 4: /* restrict all non-extensioned %p, %pK, %pa*, %p[rR] */
+ default:
+ ptr = NULL;
+ break;
+ }
+ break;
}
spec.flags |= SMALL;
if (spec.field_width == -1) {
@@ -1467,7 +1521,7 @@
}
spec.base = 16;
- return number(buf, end, (unsigned long) ptr, spec);
+ return number(buf, end, (unsigned long long) ptr, spec);
}
/*
@@ -1728,7 +1782,7 @@
/* Reject out-of-range values early. Large positive sizes are
used for unknown buffer sizes. */
- if (WARN_ON_ONCE((int) size < 0))
+ if (WARN_ON_ONCE(size > INT_MAX))
return 0;
str = buf;
diff --git a/mm/Makefile b/mm/Makefile
index 3e0a5ab..459c1bf 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -20,6 +20,9 @@
KCOV_INSTRUMENT_mmzone.o := n
KCOV_INSTRUMENT_vmstat.o := n
+# Since __builtin_frame_address does work as used, disable the warning.
+CFLAGS_usercopy.o += $(call cc-disable-warning, frame-address)
+
mmu-y := nommu.o
mmu-$(CONFIG_MMU) := fremap.o gup.o highmem.o memory.o mincore.o \
mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \
@@ -94,3 +97,4 @@
obj-$(CONFIG_MEMORY_BALLOON) += balloon_compaction.o
obj-$(CONFIG_PROCESS_RECLAIM) += process_reclaim.o
obj-$(CONFIG_CMA_DEBUGFS) += cma_debug.o
+obj-$(CONFIG_HARDENED_USERCOPY) += usercopy.o
diff --git a/mm/cma.c b/mm/cma.c
index e1218e2..35d045e 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -171,7 +171,8 @@
return -EINVAL;
/* ensure minimal alignment requied by mm core */
- alignment = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
+ alignment = PAGE_SIZE <<
+ max_t(unsigned long, MAX_ORDER - 1, pageblock_order);
/* alignment should be aligned with order_per_bit */
if (!IS_ALIGNED(alignment >> PAGE_SHIFT, 1 << order_per_bit))
@@ -253,8 +254,8 @@
* migratetype page by page allocator's buddy algorithm. In the case,
* you couldn't get a contiguous memory, which is not what we want.
*/
- alignment = max(alignment,
- (phys_addr_t)PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order));
+ alignment = max(alignment, (phys_addr_t)PAGE_SIZE <<
+ max_t(unsigned long, MAX_ORDER - 1, pageblock_order));
base = ALIGN(base, alignment);
size = ALIGN(size, alignment);
limit &= ~(alignment - 1);
diff --git a/mm/compaction.c b/mm/compaction.c
index d52b847..b43e513 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -374,6 +374,24 @@
if (!valid_page)
valid_page = page;
+
+ /*
+ * For compound pages such as THP and hugetlbfs, we can save
+ * potentially a lot of iterations if we skip them at once.
+ * The check is racy, but we can consider only valid values
+ * and the only danger is skipping too much.
+ */
+ if (PageCompound(page)) {
+ unsigned int comp_order = compound_order(page);
+
+ if (likely(comp_order < MAX_ORDER)) {
+ blockpfn += (1UL << comp_order) - 1;
+ cursor += (1UL << comp_order) - 1;
+ }
+
+ goto isolate_fail;
+ }
+
if (!PageBuddy(page))
goto isolate_fail;
@@ -405,18 +423,23 @@
/* Found a free page, break it into order-0 pages */
isolated = split_free_page(page);
+ if (!isolated)
+ break;
+
total_isolated += isolated;
+ cc->nr_freepages += isolated;
for (i = 0; i < isolated; i++) {
list_add(&page->lru, freelist);
page++;
}
-
- /* If a page was split, advance to the end of it */
- if (isolated) {
- blockpfn += isolated - 1;
- cursor += isolated - 1;
- continue;
+ if (!strict && cc->nr_migratepages <= cc->nr_freepages) {
+ blockpfn += isolated;
+ break;
}
+ /* Advance to the end of split page */
+ blockpfn += isolated - 1;
+ cursor += isolated - 1;
+ continue;
isolate_fail:
if (strict)
@@ -426,6 +449,16 @@
}
+ if (locked)
+ spin_unlock_irqrestore(&cc->zone->lock, flags);
+
+ /*
+ * There is a tiny chance that we have read bogus compound_order(),
+ * so be careful to not go outside of the pageblock.
+ */
+ if (unlikely(blockpfn > end_pfn))
+ blockpfn = end_pfn;
+
/* Record how far we have got within the block */
*start_pfn = blockpfn;
@@ -439,9 +472,6 @@
if (strict && blockpfn < end_pfn)
total_isolated = 0;
- if (locked)
- spin_unlock_irqrestore(&cc->zone->lock, flags);
-
/* Update the pageblock-skip if the whole pageblock was scanned */
if (blockpfn == end_pfn)
update_pageblock_skip(cc, valid_page, total_isolated, false);
@@ -812,16 +842,8 @@
pfn = isolate_migratepages_block(cc, pfn, block_end_pfn,
ISOLATE_UNEVICTABLE);
- /*
- * In case of fatal failure, release everything that might
- * have been isolated in the previous iteration, and signal
- * the failure back to caller.
- */
- if (!pfn) {
- putback_movable_pages(&cc->migratepages);
- cc->nr_migratepages = 0;
+ if (!pfn)
break;
- }
if (cc->nr_migratepages == COMPACT_CLUSTER_MAX)
break;
@@ -900,7 +922,12 @@
/* Found a block suitable for isolating free pages from. */
isolated = isolate_freepages_block(cc, &isolate_start_pfn,
- block_end_pfn, freelist, false);
+ block_end_pfn, freelist, false);
+ /* If isolation failed early, do not continue needlessly */
+ if (!isolated && isolate_start_pfn < block_end_pfn &&
+ cc->nr_migratepages > cc->nr_freepages)
+ break;
+
nr_freepages += isolated;
/*
diff --git a/mm/gup.c b/mm/gup.c
index 834b45a..ce1630b 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -320,7 +320,7 @@
* reCOWed by userspace write).
*/
if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
- *flags |= FOLL_COW;
+ *flags |= FOLL_COW;
return 0;
}
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index de98415..2e39d4e 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2081,10 +2081,9 @@
* page fault if needed.
*/
return 0;
- if (vma->vm_ops)
+ if (vma->vm_ops || (vm_flags & VM_NO_THP))
/* khugepaged not yet working on file or special mappings */
return 0;
- VM_BUG_ON_VMA(vm_flags & VM_NO_THP, vma);
hstart = (vma->vm_start + ~HPAGE_PMD_MASK) & HPAGE_PMD_MASK;
hend = vma->vm_end & HPAGE_PMD_MASK;
if (hstart < hend)
@@ -2407,8 +2406,7 @@
return false;
if (is_vma_temporary_stack(vma))
return false;
- VM_BUG_ON_VMA(vma->vm_flags & VM_NO_THP, vma);
- return true;
+ return !(vma->vm_flags & VM_NO_THP);
}
static void collapse_huge_page(struct mm_struct *mm,
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 549bf5a..63a6518 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1675,6 +1675,10 @@
* and reducing the surplus.
*/
spin_unlock(&hugetlb_lock);
+
+ /* yield cpu to avoid soft lockup */
+ cond_resched();
+
if (hstate_is_gigantic(h))
ret = alloc_fresh_gigantic_page(h, nodes_allowed);
else
@@ -3749,6 +3753,7 @@
{
struct page *page = NULL;
spinlock_t *ptl;
+ pte_t pte;
retry:
ptl = pmd_lockptr(mm, pmd);
spin_lock(ptl);
@@ -3758,13 +3763,14 @@
*/
if (!pmd_huge(*pmd))
goto out;
- if (pmd_present(*pmd)) {
+ pte = huge_ptep_get((pte_t *)pmd);
+ if (pte_present(pte)) {
page = pte_page(*(pte_t *)pmd) +
((address & ~PMD_MASK) >> PAGE_SHIFT);
if (flags & FOLL_GET)
get_page(page);
} else {
- if (is_hugetlb_entry_migration(huge_ptep_get((pte_t *)pmd))) {
+ if (is_hugetlb_entry_migration(pte)) {
spin_unlock(ptl);
__migration_entry_wait(mm, (pte_t *)pmd, ptl);
goto retry;
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index e0b3e94..9f658dc 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -240,6 +240,7 @@
"=================================\n");
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
spin_unlock_irqrestore(&report_lock, flags);
+ BUG_ON(info->is_write);
kasan_enable_current();
}
diff --git a/mm/ksm.c b/mm/ksm.c
index 131be13..720798f 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -301,7 +301,8 @@
{
struct rmap_item *rmap_item;
- rmap_item = kmem_cache_zalloc(rmap_item_cache, GFP_KERNEL);
+ rmap_item = kmem_cache_zalloc(rmap_item_cache, GFP_KERNEL |
+ __GFP_NORETRY | __GFP_NOWARN);
if (rmap_item)
ksm_rmap_items++;
return rmap_item;
diff --git a/mm/memory.c b/mm/memory.c
index c25077a..8e65576 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3171,7 +3171,6 @@
if (vma->vm_ops)
return do_linear_fault(mm, vma, address,
pte, pmd, flags, entry);
-
return do_anonymous_page(mm, vma, address,
pte, pmd, flags);
}
diff --git a/mm/migrate.c b/mm/migrate.c
index 7204f74..603fd72 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -421,6 +421,7 @@
return MIGRATEPAGE_SUCCESS;
}
+EXPORT_SYMBOL(migrate_page_move_mapping);
/*
* The expected number of remaining references is the same as that
@@ -580,6 +581,7 @@
if (PageWriteback(newpage))
end_page_writeback(newpage);
}
+EXPORT_SYMBOL(migrate_page_copy);
/************************************************************
* Migration functions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 58f6727..ac8157d 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5546,15 +5546,18 @@
sizeof(arch_zone_lowest_possible_pfn));
memset(arch_zone_highest_possible_pfn, 0,
sizeof(arch_zone_highest_possible_pfn));
- arch_zone_lowest_possible_pfn[0] = find_min_pfn_with_active_regions();
- arch_zone_highest_possible_pfn[0] = max_zone_pfn[0];
- for (i = 1; i < MAX_NR_ZONES; i++) {
+
+ start_pfn = find_min_pfn_with_active_regions();
+
+ for (i = 0; i < MAX_NR_ZONES; i++) {
if (i == ZONE_MOVABLE)
continue;
- arch_zone_lowest_possible_pfn[i] =
- arch_zone_highest_possible_pfn[i-1];
- arch_zone_highest_possible_pfn[i] =
- max(max_zone_pfn[i], arch_zone_lowest_possible_pfn[i]);
+
+ end_pfn = max(max_zone_pfn[i], start_pfn);
+ arch_zone_lowest_possible_pfn[i] = start_pfn;
+ arch_zone_highest_possible_pfn[i] = end_pfn;
+
+ start_pfn = end_pfn;
}
arch_zone_lowest_possible_pfn[ZONE_MOVABLE] = 0;
arch_zone_highest_possible_pfn[ZONE_MOVABLE] = 0;
diff --git a/mm/page_isolation.c b/mm/page_isolation.c
index b38f6c0..b685cc1 100644
--- a/mm/page_isolation.c
+++ b/mm/page_isolation.c
@@ -283,11 +283,11 @@
* now as a simple work-around, we use the next node for destination.
*/
if (PageHuge(page)) {
- nodemask_t src = nodemask_of_node(page_to_nid(page));
- nodemask_t dst;
- nodes_complement(dst, src);
+ int node = next_online_node(page_to_nid(page));
+ if (node == MAX_NUMNODES)
+ node = first_online_node;
return alloc_huge_page_node(page_hstate(compound_head(page)),
- next_node(page_to_nid(page), dst));
+ node);
}
if (PageHighMem(page))
diff --git a/mm/percpu.c b/mm/percpu.c
index 5ae6e02..f7da3a3 100644
--- a/mm/percpu.c
+++ b/mm/percpu.c
@@ -1012,8 +1012,11 @@
mutex_unlock(&pcpu_alloc_mutex);
}
- if (chunk != pcpu_reserved_chunk)
+ if (chunk != pcpu_reserved_chunk) {
+ spin_lock_irqsave(&pcpu_lock, flags);
pcpu_nr_empty_pop_pages -= occ_pages;
+ spin_unlock_irqrestore(&pcpu_lock, flags);
+ }
if (pcpu_nr_empty_pop_pages < PCPU_EMPTY_POP_PAGES_LOW)
pcpu_schedule_balance_work();
diff --git a/mm/shmem.c b/mm/shmem.c
index 26472ff..b159434 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2140,9 +2140,11 @@
NULL);
if (error) {
/* Remove the !PageUptodate pages we added */
- shmem_undo_range(inode,
- (loff_t)start << PAGE_CACHE_SHIFT,
- (loff_t)index << PAGE_CACHE_SHIFT, true);
+ if (index > start) {
+ shmem_undo_range(inode,
+ (loff_t)start << PAGE_CACHE_SHIFT,
+ ((loff_t)index << PAGE_CACHE_SHIFT) - 1, true);
+ }
goto undone;
}
diff --git a/mm/slab.c b/mm/slab.c
index b7f9f64..918ffe2 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -4214,6 +4214,36 @@
module_init(slab_proc_init);
#endif
+#ifdef CONFIG_HARDENED_USERCOPY
+/*
+ * Rejects objects that are incorrectly sized.
+ *
+ * Returns NULL if check passes, otherwise const char * to name of cache
+ * to indicate an error.
+ */
+const char *__check_heap_object(const void *ptr, unsigned long n,
+ struct page *page)
+{
+ struct kmem_cache *cachep;
+ unsigned int objnr;
+ unsigned long offset;
+
+ /* Find and validate object. */
+ cachep = page->slab_cache;
+ objnr = obj_to_index(cachep, page, (void *)ptr);
+ BUG_ON(objnr >= cachep->num);
+
+ /* Find offset within object. */
+ offset = ptr - index_to_obj(cachep, page, objnr) - obj_offset(cachep);
+
+ /* Allow address range falling entirely within object size. */
+ if (offset <= cachep->object_size && n <= cachep->object_size - offset)
+ return NULL;
+
+ return cachep->name;
+}
+#endif /* CONFIG_HARDENED_USERCOPY */
+
/**
* ksize - get the actual amount of memory allocated for a given object
* @objp: Pointer to the object
diff --git a/mm/slub.c b/mm/slub.c
index bbfbf3d8..58a1a1e 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -124,6 +124,14 @@
#endif
}
+static inline void *fixup_red_left(struct kmem_cache *s, void *p)
+{
+ if (kmem_cache_debug(s) && s->flags & SLAB_RED_ZONE)
+ p += s->red_left_pad;
+
+ return p;
+}
+
static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s)
{
#ifdef CONFIG_SLUB_CPU_PARTIAL
@@ -224,24 +232,6 @@
* Core slab cache functions
*******************************************************************/
-/* Verify that a pointer has an address that is valid within a slab page */
-static inline int check_valid_pointer(struct kmem_cache *s,
- struct page *page, const void *object)
-{
- void *base;
-
- if (!object)
- return 1;
-
- base = page_address(page);
- if (object < base || object >= base + page->objects * s->size ||
- (object - base) % s->size) {
- return 0;
- }
-
- return 1;
-}
-
static inline void *get_freepointer(struct kmem_cache *s, void *object)
{
return *(void **)(object + s->offset);
@@ -271,12 +261,14 @@
/* Loop over all objects in a slab */
#define for_each_object(__p, __s, __addr, __objects) \
- for (__p = (__addr); __p < (__addr) + (__objects) * (__s)->size;\
- __p += (__s)->size)
+ for (__p = fixup_red_left(__s, __addr); \
+ __p < (__addr) + (__objects) * (__s)->size; \
+ __p += (__s)->size)
#define for_each_object_idx(__p, __idx, __s, __addr, __objects) \
- for (__p = (__addr), __idx = 1; __idx <= __objects;\
- __p += (__s)->size, __idx++)
+ for (__p = fixup_red_left(__s, __addr), __idx = 1; \
+ __idx <= __objects; \
+ __p += (__s)->size, __idx++)
/* Determine object index from a given position */
static inline int slab_index(void *p, struct kmem_cache *s, void *addr)
@@ -456,6 +448,22 @@
set_bit(slab_index(p, s, addr), map);
}
+static inline int size_from_object(struct kmem_cache *s)
+{
+ if (s->flags & SLAB_RED_ZONE)
+ return s->size - s->red_left_pad;
+
+ return s->size;
+}
+
+static inline void *restore_red_left(struct kmem_cache *s, void *p)
+{
+ if (s->flags & SLAB_RED_ZONE)
+ p -= s->red_left_pad;
+
+ return p;
+}
+
/*
* Debug settings:
*/
@@ -489,6 +497,26 @@
/*
* Object debugging
*/
+
+/* Verify that a pointer has an address that is valid within a slab page */
+static inline int check_valid_pointer(struct kmem_cache *s,
+ struct page *page, void *object)
+{
+ void *base;
+
+ if (!object)
+ return 1;
+
+ base = page_address(page);
+ object = restore_red_left(s, object);
+ if (object < base || object >= base + page->objects * s->size ||
+ (object - base) % s->size) {
+ return 0;
+ }
+
+ return 1;
+}
+
static void print_section(char *text, u8 *addr, unsigned int length)
{
metadata_access_enable();
@@ -628,7 +656,9 @@
pr_err("INFO: Object 0x%p @offset=%tu fp=0x%p\n\n",
p, p - addr, get_freepointer(s, p));
- if (p > addr + 16)
+ if (s->flags & SLAB_RED_ZONE)
+ print_section("Redzone ", p - s->red_left_pad, s->red_left_pad);
+ else if (p > addr + 16)
print_section("Bytes b4 ", p - 16, 16);
print_section("Object ", p, min_t(unsigned long, s->object_size,
@@ -645,9 +675,9 @@
if (s->flags & SLAB_STORE_USER)
off += 2 * sizeof(struct track);
- if (off != s->size)
+ if (off != size_from_object(s))
/* Beginning of the filler is the free pointer */
- print_section("Padding ", p + off, s->size - off);
+ print_section("Padding ", p + off, size_from_object(s) - off);
dump_stack();
}
@@ -688,6 +718,9 @@
{
u8 *p = object;
+ if (s->flags & SLAB_RED_ZONE)
+ memset(p - s->red_left_pad, val, s->red_left_pad);
+
if (s->flags & __OBJECT_POISON) {
memset(p, POISON_FREE, s->object_size - 1);
p[s->object_size - 1] = POISON_END;
@@ -781,11 +814,11 @@
/* We also have user information there */
off += 2 * sizeof(struct track);
- if (s->size == off)
+ if (size_from_object(s) == off)
return 1;
return check_bytes_and_report(s, page, p, "Object padding",
- p + off, POISON_INUSE, s->size - off);
+ p + off, POISON_INUSE, size_from_object(s) - off);
}
/* Check the pad bytes at the end of a slab page */
@@ -830,6 +863,10 @@
if (s->flags & SLAB_RED_ZONE) {
if (!check_bytes_and_report(s, page, object, "Redzone",
+ object - s->red_left_pad, val, s->red_left_pad))
+ return 0;
+
+ if (!check_bytes_and_report(s, page, object, "Redzone",
endobject, val, s->inuse - s->object_size))
return 0;
} else {
@@ -1455,7 +1492,7 @@
set_freepointer(s, p, NULL);
}
- page->freelist = start;
+ page->freelist = fixup_red_left(s, start);
page->inuse = page->objects;
page->frozen = 1;
out:
@@ -3059,7 +3096,7 @@
*/
size += 2 * sizeof(struct track);
- if (flags & SLAB_RED_ZONE)
+ if (flags & SLAB_RED_ZONE) {
/*
* Add some empty padding so that we can catch
* overwrites from earlier objects rather than let
@@ -3068,6 +3105,11 @@
* of the object.
*/
size += sizeof(void *);
+
+ s->red_left_pad = sizeof(void *);
+ s->red_left_pad = ALIGN(s->red_left_pad, s->align);
+ size += s->red_left_pad;
+ }
#endif
/*
@@ -3361,6 +3403,46 @@
EXPORT_SYMBOL(__kmalloc_node);
#endif
+#ifdef CONFIG_HARDENED_USERCOPY
+/*
+ * Rejects objects that are incorrectly sized.
+ *
+ * Returns NULL if check passes, otherwise const char * to name of cache
+ * to indicate an error.
+ */
+const char *__check_heap_object(const void *ptr, unsigned long n,
+ struct page *page)
+{
+ struct kmem_cache *s;
+ unsigned long offset;
+ size_t object_size;
+
+ /* Find object and usable object size. */
+ s = page->slab_cache;
+ object_size = slab_ksize(s);
+
+ /* Reject impossible pointers. */
+ if (ptr < page_address(page))
+ return s->name;
+
+ /* Find offset within object. */
+ offset = (ptr - page_address(page)) % s->size;
+
+ /* Adjust for redzone and reject if within the redzone. */
+ if (kmem_cache_debug(s) && s->flags & SLAB_RED_ZONE) {
+ if (offset < s->red_left_pad)
+ return s->name;
+ offset -= s->red_left_pad;
+ }
+
+ /* Allow address range falling entirely within object size. */
+ if (offset <= object_size && n <= object_size - offset)
+ return NULL;
+
+ return s->name;
+}
+#endif /* CONFIG_HARDENED_USERCOPY */
+
static size_t __ksize(const void *object)
{
struct page *page;
diff --git a/mm/swap.c b/mm/swap.c
index 8a12b33..e657ba6 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -42,7 +42,7 @@
static DEFINE_PER_CPU(struct pagevec, lru_add_pvec);
static DEFINE_PER_CPU(struct pagevec, lru_rotate_pvecs);
-static DEFINE_PER_CPU(struct pagevec, lru_deactivate_pvecs);
+static DEFINE_PER_CPU(struct pagevec, lru_deactivate_file_pvecs);
/*
* This path almost never happens for VM activity - pages are normally
@@ -475,7 +475,7 @@
page_cache_get(page);
local_irq_save(flags);
pvec = this_cpu_ptr(&lru_rotate_pvecs);
- if (!pagevec_add(pvec, page))
+ if (!pagevec_add(pvec, page) || PageCompound(page))
pagevec_move_tail(pvec);
local_irq_restore(flags);
}
@@ -531,7 +531,7 @@
struct pagevec *pvec = &get_cpu_var(activate_page_pvecs);
page_cache_get(page);
- if (!pagevec_add(pvec, page))
+ if (!pagevec_add(pvec, page) || PageCompound(page))
pagevec_lru_move_fn(pvec, __activate_page, NULL);
put_cpu_var(activate_page_pvecs);
}
@@ -623,9 +623,8 @@
struct pagevec *pvec = &get_cpu_var(lru_add_pvec);
page_cache_get(page);
- if (!pagevec_space(pvec))
+ if (!pagevec_add(pvec, page) || PageCompound(page))
__pagevec_lru_add(pvec);
- pagevec_add(pvec, page);
put_cpu_var(lru_add_pvec);
}
@@ -743,7 +742,7 @@
* be write it out by flusher threads as this is much more effective
* than the single-page writeout from reclaim.
*/
-static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec,
+static void lru_deactivate_file_fn(struct page *page, struct lruvec *lruvec,
void *arg)
{
int lru, file;
@@ -811,36 +810,36 @@
local_irq_restore(flags);
}
- pvec = &per_cpu(lru_deactivate_pvecs, cpu);
+ pvec = &per_cpu(lru_deactivate_file_pvecs, cpu);
if (pagevec_count(pvec))
- pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL);
+ pagevec_lru_move_fn(pvec, lru_deactivate_file_fn, NULL);
activate_page_drain(cpu);
}
/**
- * deactivate_page - forcefully deactivate a page
+ * deactivate_file_page - forcefully deactivate a file page
* @page: page to deactivate
*
* This function hints the VM that @page is a good reclaim candidate,
* for example if its invalidation fails due to the page being dirty
* or under writeback.
*/
-void deactivate_page(struct page *page)
+void deactivate_file_page(struct page *page)
{
/*
- * In a workload with many unevictable page such as mprotect, unevictable
- * page deactivation for accelerating reclaim is pointless.
+ * In a workload with many unevictable page such as mprotect,
+ * unevictable page deactivation for accelerating reclaim is pointless.
*/
if (PageUnevictable(page))
return;
if (likely(get_page_unless_zero(page))) {
- struct pagevec *pvec = &get_cpu_var(lru_deactivate_pvecs);
+ struct pagevec *pvec = &get_cpu_var(lru_deactivate_file_pvecs);
- if (!pagevec_add(pvec, page))
- pagevec_lru_move_fn(pvec, lru_deactivate_fn, NULL);
- put_cpu_var(lru_deactivate_pvecs);
+ if (!pagevec_add(pvec, page) || PageCompound(page))
+ pagevec_lru_move_fn(pvec, lru_deactivate_file_fn, NULL);
+ put_cpu_var(lru_deactivate_file_pvecs);
}
}
@@ -872,7 +871,7 @@
if (pagevec_count(&per_cpu(lru_add_pvec, cpu)) ||
pagevec_count(&per_cpu(lru_rotate_pvecs, cpu)) ||
- pagevec_count(&per_cpu(lru_deactivate_pvecs, cpu)) ||
+ pagevec_count(&per_cpu(lru_deactivate_file_pvecs, cpu)) ||
need_activate_page_drain(cpu)) {
INIT_WORK(work, lru_add_drain_per_cpu);
schedule_work_on(cpu, work);
diff --git a/mm/swapfile.c b/mm/swapfile.c
index ea0e5d7..542a2d1 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -2249,6 +2249,8 @@
swab32s(&swap_header->info.version);
swab32s(&swap_header->info.last_page);
swab32s(&swap_header->info.nr_badpages);
+ if (swap_header->info.nr_badpages > MAX_SWAP_BADPAGES)
+ return 0;
for (i = 0; i < swap_header->info.nr_badpages; i++)
swab32s(&swap_header->info.badpages[i]);
}
diff --git a/mm/truncate.c b/mm/truncate.c
index 965fb7c..ef735c9 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -580,7 +580,7 @@
* of interest and try to speed up its reclaim.
*/
if (!ret)
- deactivate_page(page);
+ deactivate_file_page(page);
count += ret;
}
pagevec_remove_exceptionals(&pvec);
diff --git a/mm/usercopy.c b/mm/usercopy.c
new file mode 100644
index 0000000..b34996a
--- /dev/null
+++ b/mm/usercopy.c
@@ -0,0 +1,278 @@
+/*
+ * This implements the various checks for CONFIG_HARDENED_USERCOPY*,
+ * which are designed to protect kernel memory from needless exposure
+ * and overwrite under many unintended conditions. This code is based
+ * on PAX_USERCOPY, which is:
+ *
+ * Copyright (C) 2001-2016 PaX Team, Bradley Spengler, Open Source
+ * Security 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/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <asm/sections.h>
+
+enum {
+ BAD_STACK = -1,
+ NOT_STACK = 0,
+ GOOD_FRAME,
+ GOOD_STACK,
+};
+
+/*
+ * Checks if a given pointer and length is contained by the current
+ * stack frame (if possible).
+ *
+ * Returns:
+ * NOT_STACK: not at all on the stack
+ * GOOD_FRAME: fully within a valid stack frame
+ * GOOD_STACK: fully on the stack (when can't do frame-checking)
+ * BAD_STACK: error condition (invalid stack position or bad stack frame)
+ */
+static noinline int check_stack_object(const void *obj, unsigned long len)
+{
+ const void * const stack = task_stack_page(current);
+ const void * const stackend = stack + THREAD_SIZE;
+ int ret;
+
+ /* Object is not on the stack at all. */
+ if (obj + len <= stack || stackend <= obj)
+ return NOT_STACK;
+
+ /*
+ * Reject: object partially overlaps the stack (passing the
+ * the check above means at least one end is within the stack,
+ * so if this check fails, the other end is outside the stack).
+ */
+ if (obj < stack || stackend < obj + len)
+ return BAD_STACK;
+
+ /* Check if object is safely within a valid frame. */
+ ret = arch_within_stack_frames(stack, stackend, obj, len);
+ if (ret)
+ return ret;
+
+ return GOOD_STACK;
+}
+
+static void report_usercopy(const void *ptr, unsigned long len,
+ bool to_user, const char *type)
+{
+ pr_emerg("kernel memory %s attempt detected %s %p (%s) (%lu bytes)\n",
+ to_user ? "exposure" : "overwrite",
+ to_user ? "from" : "to", ptr, type ? : "unknown", len);
+ /*
+ * For greater effect, it would be nice to do do_group_exit(),
+ * but BUG() actually hooks all the lock-breaking and per-arch
+ * Oops code, so that is used here instead.
+ */
+ BUG();
+}
+
+/* Returns true if any portion of [ptr,ptr+n) over laps with [low,high). */
+static bool overlaps(const void *ptr, unsigned long n, unsigned long low,
+ unsigned long high)
+{
+ unsigned long check_low = (uintptr_t)ptr;
+ unsigned long check_high = check_low + n;
+
+ /* Does not overlap if entirely above or entirely below. */
+ if (check_low >= high || check_high <= low)
+ return false;
+
+ return true;
+}
+
+/* Is this address range in the kernel text area? */
+static inline const char *check_kernel_text_object(const void *ptr,
+ unsigned long n)
+{
+ unsigned long textlow = (unsigned long)_stext;
+ unsigned long texthigh = (unsigned long)_etext;
+ unsigned long textlow_linear, texthigh_linear;
+
+ if (overlaps(ptr, n, textlow, texthigh))
+ return "<kernel text>";
+
+ /*
+ * Some architectures have virtual memory mappings with a secondary
+ * mapping of the kernel text, i.e. there is more than one virtual
+ * kernel address that points to the kernel image. It is usually
+ * when there is a separate linear physical memory mapping, in that
+ * __pa() is not just the reverse of __va(). This can be detected
+ * and checked:
+ */
+ textlow_linear = (unsigned long)__va(__pa(textlow));
+ /* No different mapping: we're done. */
+ if (textlow_linear == textlow)
+ return NULL;
+
+ /* Check the secondary mapping... */
+ texthigh_linear = (unsigned long)__va(__pa(texthigh));
+ if (overlaps(ptr, n, textlow_linear, texthigh_linear))
+ return "<linear kernel text>";
+
+ return NULL;
+}
+
+static inline const char *check_bogus_address(const void *ptr, unsigned long n)
+{
+ /* Reject if object wraps past end of memory. */
+ if ((unsigned long)ptr + n < (unsigned long)ptr)
+ return "<wrapped address>";
+
+ /* Reject if NULL or ZERO-allocation. */
+ if (ZERO_OR_NULL_PTR(ptr))
+ return "<null>";
+
+ return NULL;
+}
+
+/* Checks for allocs that are marked in some way as spanning multiple pages. */
+static inline const char *check_page_span(const void *ptr, unsigned long n,
+ struct page *page, bool to_user)
+{
+#ifdef CONFIG_HARDENED_USERCOPY_PAGESPAN
+ const void *end = ptr + n - 1;
+ struct page *endpage;
+ bool is_reserved, is_cma;
+
+ /*
+ * Sometimes the kernel data regions are not marked Reserved (see
+ * check below). And sometimes [_sdata,_edata) does not cover
+ * rodata and/or bss, so check each range explicitly.
+ */
+
+ /* Allow reads of kernel rodata region (if not marked as Reserved). */
+ if (ptr >= (const void *)__start_rodata &&
+ end <= (const void *)__end_rodata) {
+ if (!to_user)
+ return "<rodata>";
+ return NULL;
+ }
+
+ /* Allow kernel data region (if not marked as Reserved). */
+ if (ptr >= (const void *)_sdata && end <= (const void *)_edata)
+ return NULL;
+
+ /* Allow kernel bss region (if not marked as Reserved). */
+ if (ptr >= (const void *)__bss_start &&
+ end <= (const void *)__bss_stop)
+ return NULL;
+
+ /* Is the object wholly within one base page? */
+ if (likely(((unsigned long)ptr & (unsigned long)PAGE_MASK) ==
+ ((unsigned long)end & (unsigned long)PAGE_MASK)))
+ return NULL;
+
+ /* Allow if fully inside the same compound (__GFP_COMP) page. */
+ endpage = virt_to_head_page(end);
+ if (likely(endpage == page))
+ return NULL;
+
+ /*
+ * Reject if range is entirely either Reserved (i.e. special or
+ * device memory), or CMA. Otherwise, reject since the object spans
+ * several independently allocated pages.
+ */
+ is_reserved = PageReserved(page);
+ is_cma = is_migrate_cma_page(page);
+ if (!is_reserved && !is_cma)
+ return "<spans multiple pages>";
+
+ for (ptr += PAGE_SIZE; ptr <= end; ptr += PAGE_SIZE) {
+ page = virt_to_head_page(ptr);
+ if (is_reserved && !PageReserved(page))
+ return "<spans Reserved and non-Reserved pages>";
+ if (is_cma && !is_migrate_cma_page(page))
+ return "<spans CMA and non-CMA pages>";
+ }
+#endif
+
+ return NULL;
+}
+
+static inline const char *check_heap_object(const void *ptr, unsigned long n,
+ bool to_user)
+{
+ struct page *page;
+
+ /*
+ * Some architectures (arm64) return true for virt_addr_valid() on
+ * vmalloced addresses. Work around this by checking for vmalloc
+ * first.
+ */
+ if (is_vmalloc_addr(ptr))
+ return NULL;
+
+ if (!virt_addr_valid(ptr))
+ return NULL;
+
+ page = virt_to_head_page(ptr);
+
+ /* Check slab allocator for flags and size. */
+ if (PageSlab(page))
+ return __check_heap_object(ptr, n, page);
+
+ /* Verify object does not incorrectly span multiple pages. */
+ return check_page_span(ptr, n, page, to_user);
+}
+
+/*
+ * Validates that the given object is:
+ * - not bogus address
+ * - known-safe heap or stack object
+ * - not in kernel text
+ */
+void __check_object_size(const void *ptr, unsigned long n, bool to_user)
+{
+ const char *err;
+
+ /* Skip all tests if size is zero. */
+ if (!n)
+ return;
+
+ /* Check for invalid addresses. */
+ err = check_bogus_address(ptr, n);
+ if (err)
+ goto report;
+
+ /* Check for bad heap object. */
+ err = check_heap_object(ptr, n, to_user);
+ if (err)
+ goto report;
+
+ /* Check for bad stack object. */
+ switch (check_stack_object(ptr, n)) {
+ case NOT_STACK:
+ /* Object is not touching the current process stack. */
+ break;
+ case GOOD_FRAME:
+ case GOOD_STACK:
+ /*
+ * Object is either in the correct frame (when it
+ * is possible to check) or just generally on the
+ * process stack (when frame checking not available).
+ */
+ return;
+ default:
+ err = "<process stack>";
+ goto report;
+ }
+
+ /* Check for object in kernel to avoid text exposure. */
+ err = check_kernel_text_object(ptr, n);
+ if (!err)
+ return;
+
+report:
+ report_usercopy(ptr, n, to_user, err);
+}
+EXPORT_SYMBOL(__check_object_size);
diff --git a/net/9p/client.c b/net/9p/client.c
index 53fe98e..d43baab 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -2106,6 +2106,10 @@
trace_9p_protocol_dump(clnt, req->rc);
goto free_and_error;
}
+ if (rsize < count) {
+ pr_err("bogus RREADDIR count (%d > %d)\n", count, rsize);
+ count = rsize;
+ }
p9_debug(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count);
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 35f76f2..bbb1d04 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -88,6 +88,15 @@
neigh_node = NULL;
spin_lock_bh(&orig_node->neigh_list_lock);
+ /* curr_router used earlier may not be the current orig_ifinfo->router
+ * anymore because it was dereferenced outside of the neigh_list_lock
+ * protected region. After the new best neighbor has replace the current
+ * best neighbor the reference counter needs to decrease. Consequently,
+ * the code needs to ensure the curr_router variable contains a pointer
+ * to the replaced best neighbor.
+ */
+ curr_router = rcu_dereference_protected(orig_ifinfo->router, true);
+
rcu_assign_pointer(orig_ifinfo->router, neigh_node);
spin_unlock_bh(&orig_node->neigh_list_lock);
batadv_orig_ifinfo_free_ref(orig_ifinfo);
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index 3d64ed2..6004c2d 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -611,6 +611,9 @@
if (pending) {
hlist_del(&forw_packet->list);
+ if (!forw_packet->own)
+ atomic_inc(&bat_priv->bcast_queue_left);
+
batadv_forw_packet_free(forw_packet);
}
}
@@ -638,6 +641,9 @@
if (pending) {
hlist_del(&forw_packet->list);
+ if (!forw_packet->own)
+ atomic_inc(&bat_priv->batman_queue_left);
+
batadv_forw_packet_free(forw_packet);
}
}
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 492b059..0bb7cae 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -378,11 +378,17 @@
*/
nf_reset(skb);
+ if (unlikely(!pskb_may_pull(skb, ETH_HLEN)))
+ goto dropped;
+
vid = batadv_get_vid(skb, 0);
ethhdr = eth_hdr(skb);
switch (ntohs(ethhdr->h_proto)) {
case ETH_P_8021Q:
+ if (!pskb_may_pull(skb, VLAN_ETH_HLEN))
+ goto dropped;
+
vhdr = (struct vlan_ethhdr *)skb->data;
if (vhdr->h_vlan_encapsulated_proto != ethertype)
@@ -394,8 +400,6 @@
}
/* skb->dev & skb->pkt_type are set here */
- if (unlikely(!pskb_may_pull(skb, ETH_HLEN)))
- goto dropped;
skb->protocol = eth_type_trans(skb, soft_iface);
/* should not be necessary anymore as we use skb_pull_rcsum()
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 80732d3..eebd2da 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -46,9 +46,9 @@
unsigned short channel;
};
-static inline int hci_test_bit(int nr, void *addr)
+static inline int hci_test_bit(int nr, const void *addr)
{
- return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
+ return *((const __u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
}
/* Security filter */
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 1389bde..9f27c93 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -922,7 +922,7 @@
break;
}
- if (get_user(opt, (u32 __user *) optval)) {
+ if (get_user(opt, (u16 __user *) optval)) {
err = -EFAULT;
break;
}
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index 8d423bc..f876f70 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -21,18 +21,19 @@
#include <asm/uaccess.h>
#include "br_private.h"
-/* called with RTNL */
static int get_bridge_ifindices(struct net *net, int *indices, int num)
{
struct net_device *dev;
int i = 0;
- for_each_netdev(net, dev) {
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
if (i >= num)
break;
if (dev->priv_flags & IFF_EBRIDGE)
indices[i++] = dev->ifindex;
}
+ rcu_read_unlock();
return i;
}
diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c
index 48da2c5..6a10b88 100644
--- a/net/bridge/netfilter/nft_reject_bridge.c
+++ b/net/bridge/netfilter/nft_reject_bridge.c
@@ -375,6 +375,8 @@
if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
goto nla_put_failure;
break;
+ default:
+ break;
}
return 0;
diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c
index 1be0b52..5add8e7 100644
--- a/net/caif/cfpkt_skbuff.c
+++ b/net/caif/cfpkt_skbuff.c
@@ -286,7 +286,7 @@
else
skb_trim(skb, len);
- return cfpkt_getlen(pkt);
+ return cfpkt_getlen(pkt);
}
/* Need to expand SKB */
diff --git a/net/can/af_can.c b/net/can/af_can.c
index d6030d6..ee6eee7 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -446,6 +446,7 @@
* @func: callback function on filter match
* @data: returned parameter for callback function
* @ident: string for calling module identification
+ * @sk: socket pointer (might be NULL)
*
* Description:
* Invokes the callback function with the received sk_buff and the given
@@ -469,7 +470,7 @@
*/
int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
void (*func)(struct sk_buff *, void *), void *data,
- char *ident)
+ char *ident, struct sock *sk)
{
struct receiver *r;
struct hlist_head *rl;
@@ -497,6 +498,7 @@
r->func = func;
r->data = data;
r->ident = ident;
+ r->sk = sk;
hlist_add_head_rcu(&r->list, rl);
d->entries++;
@@ -521,8 +523,11 @@
static void can_rx_delete_receiver(struct rcu_head *rp)
{
struct receiver *r = container_of(rp, struct receiver, rcu);
+ struct sock *sk = r->sk;
kmem_cache_free(rcv_cache, r);
+ if (sk)
+ sock_put(sk);
}
/**
@@ -597,8 +602,11 @@
spin_unlock(&can_rcvlists_lock);
/* schedule the receiver item for deletion */
- if (r)
+ if (r) {
+ if (r->sk)
+ sock_hold(r->sk);
call_rcu(&r->rcu, can_rx_delete_receiver);
+ }
}
EXPORT_SYMBOL(can_rx_unregister);
diff --git a/net/can/af_can.h b/net/can/af_can.h
index fca0fe9..b86f512 100644
--- a/net/can/af_can.h
+++ b/net/can/af_can.h
@@ -50,13 +50,14 @@
struct receiver {
struct hlist_node list;
- struct rcu_head rcu;
canid_t can_id;
canid_t mask;
unsigned long matches;
void (*func)(struct sk_buff *, void *);
void *data;
char *ident;
+ struct sock *sk;
+ struct rcu_head rcu;
};
#define CAN_SFF_RCV_ARRAY_SZ (1 << CAN_SFF_ID_BITS)
diff --git a/net/can/bcm.c b/net/can/bcm.c
index dcb75c0..51c208f 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1169,7 +1169,7 @@
err = can_rx_register(dev, op->can_id,
REGMASK(op->can_id),
bcm_rx_handler, op,
- "bcm");
+ "bcm", sk);
op->rx_reg_dev = dev;
dev_put(dev);
@@ -1178,7 +1178,7 @@
} else
err = can_rx_register(NULL, op->can_id,
REGMASK(op->can_id),
- bcm_rx_handler, op, "bcm");
+ bcm_rx_handler, op, "bcm", sk);
if (err) {
/* this bcm rx op is broken -> remove it */
list_del(&op->list);
diff --git a/net/can/gw.c b/net/can/gw.c
index 050a211..d492158 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c
@@ -441,7 +441,7 @@
{
return can_rx_register(gwj->src.dev, gwj->ccgw.filter.can_id,
gwj->ccgw.filter.can_mask, can_can_gw_rcv,
- gwj, "gw");
+ gwj, "gw", NULL);
}
static inline void cgw_unregister_filter(struct cgw_job *gwj)
diff --git a/net/can/raw.c b/net/can/raw.c
index 081e81f..bdca782 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -164,7 +164,7 @@
for (i = 0; i < count; i++) {
err = can_rx_register(dev, filter[i].can_id,
filter[i].can_mask,
- raw_rcv, sk, "raw");
+ raw_rcv, sk, "raw", sk);
if (err) {
/* clean up successfully registered filters */
while (--i >= 0)
@@ -185,7 +185,7 @@
if (err_mask)
err = can_rx_register(dev, 0, err_mask | CAN_ERR_FLAG,
- raw_rcv, sk, "raw");
+ raw_rcv, sk, "raw", sk);
return err;
}
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 1519051..0861598 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -1979,6 +1979,19 @@
dout("process_connect on %p tag %d\n", con, (int)con->in_tag);
+ if (con->auth_reply_buf) {
+ /*
+ * Any connection that defines ->get_authorizer()
+ * should also define ->verify_authorizer_reply().
+ * See get_connect_authorizer().
+ */
+ ret = con->ops->verify_authorizer_reply(con, 0);
+ if (ret < 0) {
+ con->error_msg = "bad authorize reply";
+ return ret;
+ }
+ }
+
switch (con->in_reply.tag) {
case CEPH_MSGR_TAG_FEATURES:
pr_err("%s%lld %s feature set mismatch,"
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index b8c3fde..8bb2e0c 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -1167,6 +1167,114 @@
}
/*
+ * Encoding order is (new_up_client, new_state, new_weight). Need to
+ * apply in the (new_weight, new_state, new_up_client) order, because
+ * an incremental map may look like e.g.
+ *
+ * new_up_client: { osd=6, addr=... } # set osd_state and addr
+ * new_state: { osd=6, xorstate=EXISTS } # clear osd_state
+ */
+static int decode_new_up_state_weight(void **p, void *end,
+ struct ceph_osdmap *map)
+{
+ void *new_up_client;
+ void *new_state;
+ void *new_weight_end;
+ u32 len;
+
+ new_up_client = *p;
+ ceph_decode_32_safe(p, end, len, e_inval);
+ len *= sizeof(u32) + sizeof(struct ceph_entity_addr);
+ ceph_decode_need(p, end, len, e_inval);
+ *p += len;
+
+ new_state = *p;
+ ceph_decode_32_safe(p, end, len, e_inval);
+ len *= sizeof(u32) + sizeof(u8);
+ ceph_decode_need(p, end, len, e_inval);
+ *p += len;
+
+ /* new_weight */
+ ceph_decode_32_safe(p, end, len, e_inval);
+ while (len--) {
+ s32 osd;
+ u32 w;
+
+ ceph_decode_need(p, end, 2*sizeof(u32), e_inval);
+ osd = ceph_decode_32(p);
+ w = ceph_decode_32(p);
+ BUG_ON(osd >= map->max_osd);
+ pr_info("osd%d weight 0x%x %s\n", osd, w,
+ w == CEPH_OSD_IN ? "(in)" :
+ (w == CEPH_OSD_OUT ? "(out)" : ""));
+ map->osd_weight[osd] = w;
+
+ /*
+ * If we are marking in, set the EXISTS, and clear the
+ * AUTOOUT and NEW bits.
+ */
+ if (w) {
+ map->osd_state[osd] |= CEPH_OSD_EXISTS;
+ map->osd_state[osd] &= ~(CEPH_OSD_AUTOOUT |
+ CEPH_OSD_NEW);
+ }
+ }
+ new_weight_end = *p;
+
+ /* new_state (up/down) */
+ *p = new_state;
+ len = ceph_decode_32(p);
+ while (len--) {
+ s32 osd;
+ u8 xorstate;
+ int ret;
+
+ osd = ceph_decode_32(p);
+ xorstate = ceph_decode_8(p);
+ if (xorstate == 0)
+ xorstate = CEPH_OSD_UP;
+ BUG_ON(osd >= map->max_osd);
+ if ((map->osd_state[osd] & CEPH_OSD_UP) &&
+ (xorstate & CEPH_OSD_UP))
+ pr_info("osd%d down\n", osd);
+ if ((map->osd_state[osd] & CEPH_OSD_EXISTS) &&
+ (xorstate & CEPH_OSD_EXISTS)) {
+ pr_info("osd%d does not exist\n", osd);
+ ret = set_primary_affinity(map, osd,
+ CEPH_OSD_DEFAULT_PRIMARY_AFFINITY);
+ if (ret)
+ return ret;
+ memset(map->osd_addr + osd, 0, sizeof(*map->osd_addr));
+ map->osd_state[osd] = 0;
+ } else {
+ map->osd_state[osd] ^= xorstate;
+ }
+ }
+
+ /* new_up_client */
+ *p = new_up_client;
+ len = ceph_decode_32(p);
+ while (len--) {
+ s32 osd;
+ struct ceph_entity_addr addr;
+
+ osd = ceph_decode_32(p);
+ ceph_decode_copy(p, &addr, sizeof(addr));
+ ceph_decode_addr(&addr);
+ BUG_ON(osd >= map->max_osd);
+ pr_info("osd%d up\n", osd);
+ map->osd_state[osd] |= CEPH_OSD_EXISTS | CEPH_OSD_UP;
+ map->osd_addr[osd] = addr;
+ }
+
+ *p = new_weight_end;
+ return 0;
+
+e_inval:
+ return -EINVAL;
+}
+
+/*
* decode and apply an incremental map update.
*/
struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
@@ -1265,49 +1373,10 @@
__remove_pg_pool(&map->pg_pools, pi);
}
- /* new_up */
- ceph_decode_32_safe(p, end, len, e_inval);
- while (len--) {
- u32 osd;
- struct ceph_entity_addr addr;
- ceph_decode_32_safe(p, end, osd, e_inval);
- ceph_decode_copy_safe(p, end, &addr, sizeof(addr), e_inval);
- ceph_decode_addr(&addr);
- pr_info("osd%d up\n", osd);
- BUG_ON(osd >= map->max_osd);
- map->osd_state[osd] |= CEPH_OSD_UP;
- map->osd_addr[osd] = addr;
- }
-
- /* new_state */
- ceph_decode_32_safe(p, end, len, e_inval);
- while (len--) {
- u32 osd;
- u8 xorstate;
- ceph_decode_32_safe(p, end, osd, e_inval);
- xorstate = **(u8 **)p;
- (*p)++; /* clean flag */
- if (xorstate == 0)
- xorstate = CEPH_OSD_UP;
- if (xorstate & CEPH_OSD_UP)
- pr_info("osd%d down\n", osd);
- if (osd < map->max_osd)
- map->osd_state[osd] ^= xorstate;
- }
-
- /* new_weight */
- ceph_decode_32_safe(p, end, len, e_inval);
- while (len--) {
- u32 osd, off;
- ceph_decode_need(p, end, sizeof(u32)*2, e_inval);
- osd = ceph_decode_32(p);
- off = ceph_decode_32(p);
- pr_info("osd%d weight 0x%x %s\n", osd, off,
- off == CEPH_OSD_IN ? "(in)" :
- (off == CEPH_OSD_OUT ? "(out)" : ""));
- if (osd < map->max_osd)
- map->osd_weight[osd] = off;
- }
+ /* new_up_client, new_state, new_weight */
+ err = decode_new_up_state_weight(p, end, map);
+ if (err)
+ goto bad;
/* new_pg_temp */
err = decode_new_pg_temp(p, end, map);
diff --git a/net/core/dev.c b/net/core/dev.c
index 8c92085..f91c964 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1613,24 +1613,19 @@
static struct static_key netstamp_needed __read_mostly;
#ifdef HAVE_JUMP_LABEL
-/* We are not allowed to call static_key_slow_dec() from irq context
- * If net_disable_timestamp() is called from irq context, defer the
- * static_key_slow_dec() calls.
- */
static atomic_t netstamp_needed_deferred;
+static void netstamp_clear(struct work_struct *work)
+{
+ int deferred = atomic_xchg(&netstamp_needed_deferred, 0);
+
+ while (deferred--)
+ static_key_slow_dec(&netstamp_needed);
+}
+static DECLARE_WORK(netstamp_work, netstamp_clear);
#endif
void net_enable_timestamp(void)
{
-#ifdef HAVE_JUMP_LABEL
- int deferred = atomic_xchg(&netstamp_needed_deferred, 0);
-
- if (deferred) {
- while (--deferred)
- static_key_slow_dec(&netstamp_needed);
- return;
- }
-#endif
static_key_slow_inc(&netstamp_needed);
}
EXPORT_SYMBOL(net_enable_timestamp);
@@ -1638,12 +1633,12 @@
void net_disable_timestamp(void)
{
#ifdef HAVE_JUMP_LABEL
- if (in_interrupt()) {
- atomic_inc(&netstamp_needed_deferred);
- return;
- }
-#endif
+ /* net_disable_timestamp() can be called from non process context */
+ atomic_inc(&netstamp_needed_deferred);
+ schedule_work(&netstamp_work);
+#else
static_key_slow_dec(&netstamp_needed);
+#endif
}
EXPORT_SYMBOL(net_disable_timestamp);
@@ -2802,6 +2797,7 @@
}
return head;
}
+EXPORT_SYMBOL_GPL(validate_xmit_skb_list);
static void qdisc_pkt_len_init(struct sk_buff *skb)
{
@@ -4115,7 +4111,7 @@
NAPI_GRO_CB(skb)->same_flow = 0;
NAPI_GRO_CB(skb)->flush = 0;
NAPI_GRO_CB(skb)->free = 0;
- NAPI_GRO_CB(skb)->udp_mark = 0;
+ NAPI_GRO_CB(skb)->encap_mark = 0;
/* Setup for GRO checksum validation */
switch (skb->ip_summed) {
@@ -5013,6 +5009,7 @@
static int __netdev_adjacent_dev_insert(struct net_device *dev,
struct net_device *adj_dev,
+ u16 ref_nr,
struct list_head *dev_list,
void *private, bool master)
{
@@ -5022,7 +5019,7 @@
adj = __netdev_find_adj(dev, adj_dev, dev_list);
if (adj) {
- adj->ref_nr++;
+ adj->ref_nr += ref_nr;
return 0;
}
@@ -5032,7 +5029,7 @@
adj->dev = adj_dev;
adj->master = master;
- adj->ref_nr = 1;
+ adj->ref_nr = ref_nr;
adj->private = private;
dev_hold(adj_dev);
@@ -5071,6 +5068,7 @@
static void __netdev_adjacent_dev_remove(struct net_device *dev,
struct net_device *adj_dev,
+ u16 ref_nr,
struct list_head *dev_list)
{
struct netdev_adjacent *adj;
@@ -5083,10 +5081,10 @@
BUG();
}
- if (adj->ref_nr > 1) {
- pr_debug("%s to %s ref_nr-- = %d\n", dev->name, adj_dev->name,
- adj->ref_nr-1);
- adj->ref_nr--;
+ if (adj->ref_nr > ref_nr) {
+ pr_debug("%s to %s ref_nr-%d = %d\n", dev->name, adj_dev->name,
+ ref_nr, adj->ref_nr-ref_nr);
+ adj->ref_nr -= ref_nr;
return;
}
@@ -5105,21 +5103,22 @@
static int __netdev_adjacent_dev_link_lists(struct net_device *dev,
struct net_device *upper_dev,
+ u16 ref_nr,
struct list_head *up_list,
struct list_head *down_list,
void *private, bool master)
{
int ret;
- ret = __netdev_adjacent_dev_insert(dev, upper_dev, up_list, private,
- master);
+ ret = __netdev_adjacent_dev_insert(dev, upper_dev, ref_nr, up_list,
+ private, master);
if (ret)
return ret;
- ret = __netdev_adjacent_dev_insert(upper_dev, dev, down_list, private,
- false);
+ ret = __netdev_adjacent_dev_insert(upper_dev, dev, ref_nr, down_list,
+ private, false);
if (ret) {
- __netdev_adjacent_dev_remove(dev, upper_dev, up_list);
+ __netdev_adjacent_dev_remove(dev, upper_dev, ref_nr, up_list);
return ret;
}
@@ -5127,9 +5126,10 @@
}
static int __netdev_adjacent_dev_link(struct net_device *dev,
- struct net_device *upper_dev)
+ struct net_device *upper_dev,
+ u16 ref_nr)
{
- return __netdev_adjacent_dev_link_lists(dev, upper_dev,
+ return __netdev_adjacent_dev_link_lists(dev, upper_dev, ref_nr,
&dev->all_adj_list.upper,
&upper_dev->all_adj_list.lower,
NULL, false);
@@ -5137,17 +5137,19 @@
static void __netdev_adjacent_dev_unlink_lists(struct net_device *dev,
struct net_device *upper_dev,
+ u16 ref_nr,
struct list_head *up_list,
struct list_head *down_list)
{
- __netdev_adjacent_dev_remove(dev, upper_dev, up_list);
- __netdev_adjacent_dev_remove(upper_dev, dev, down_list);
+ __netdev_adjacent_dev_remove(dev, upper_dev, ref_nr, up_list);
+ __netdev_adjacent_dev_remove(upper_dev, dev, ref_nr, down_list);
}
static void __netdev_adjacent_dev_unlink(struct net_device *dev,
- struct net_device *upper_dev)
+ struct net_device *upper_dev,
+ u16 ref_nr)
{
- __netdev_adjacent_dev_unlink_lists(dev, upper_dev,
+ __netdev_adjacent_dev_unlink_lists(dev, upper_dev, ref_nr,
&dev->all_adj_list.upper,
&upper_dev->all_adj_list.lower);
}
@@ -5156,17 +5158,17 @@
struct net_device *upper_dev,
void *private, bool master)
{
- int ret = __netdev_adjacent_dev_link(dev, upper_dev);
+ int ret = __netdev_adjacent_dev_link(dev, upper_dev, 1);
if (ret)
return ret;
- ret = __netdev_adjacent_dev_link_lists(dev, upper_dev,
+ ret = __netdev_adjacent_dev_link_lists(dev, upper_dev, 1,
&dev->adj_list.upper,
&upper_dev->adj_list.lower,
private, master);
if (ret) {
- __netdev_adjacent_dev_unlink(dev, upper_dev);
+ __netdev_adjacent_dev_unlink(dev, upper_dev, 1);
return ret;
}
@@ -5176,8 +5178,8 @@
static void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev,
struct net_device *upper_dev)
{
- __netdev_adjacent_dev_unlink(dev, upper_dev);
- __netdev_adjacent_dev_unlink_lists(dev, upper_dev,
+ __netdev_adjacent_dev_unlink(dev, upper_dev, 1);
+ __netdev_adjacent_dev_unlink_lists(dev, upper_dev, 1,
&dev->adj_list.upper,
&upper_dev->adj_list.lower);
}
@@ -5218,7 +5220,7 @@
list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) {
pr_debug("Interlinking %s with %s, non-neighbour\n",
i->dev->name, j->dev->name);
- ret = __netdev_adjacent_dev_link(i->dev, j->dev);
+ ret = __netdev_adjacent_dev_link(i->dev, j->dev, i->ref_nr);
if (ret)
goto rollback_mesh;
}
@@ -5228,7 +5230,7 @@
list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) {
pr_debug("linking %s's upper device %s with %s\n",
upper_dev->name, i->dev->name, dev->name);
- ret = __netdev_adjacent_dev_link(dev, i->dev);
+ ret = __netdev_adjacent_dev_link(dev, i->dev, i->ref_nr);
if (ret)
goto rollback_upper_mesh;
}
@@ -5237,7 +5239,7 @@
list_for_each_entry(i, &dev->all_adj_list.lower, list) {
pr_debug("linking %s's lower device %s with %s\n", dev->name,
i->dev->name, upper_dev->name);
- ret = __netdev_adjacent_dev_link(i->dev, upper_dev);
+ ret = __netdev_adjacent_dev_link(i->dev, upper_dev, i->ref_nr);
if (ret)
goto rollback_lower_mesh;
}
@@ -5250,7 +5252,7 @@
list_for_each_entry(i, &dev->all_adj_list.lower, list) {
if (i == to_i)
break;
- __netdev_adjacent_dev_unlink(i->dev, upper_dev);
+ __netdev_adjacent_dev_unlink(i->dev, upper_dev, i->ref_nr);
}
i = NULL;
@@ -5260,7 +5262,7 @@
list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) {
if (i == to_i)
break;
- __netdev_adjacent_dev_unlink(dev, i->dev);
+ __netdev_adjacent_dev_unlink(dev, i->dev, i->ref_nr);
}
i = j = NULL;
@@ -5272,7 +5274,7 @@
list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) {
if (i == to_i && j == to_j)
break;
- __netdev_adjacent_dev_unlink(i->dev, j->dev);
+ __netdev_adjacent_dev_unlink(i->dev, j->dev, i->ref_nr);
}
if (i == to_i)
break;
@@ -5348,16 +5350,16 @@
*/
list_for_each_entry(i, &dev->all_adj_list.lower, list)
list_for_each_entry(j, &upper_dev->all_adj_list.upper, list)
- __netdev_adjacent_dev_unlink(i->dev, j->dev);
+ __netdev_adjacent_dev_unlink(i->dev, j->dev, i->ref_nr);
/* remove also the devices itself from lower/upper device
* list
*/
list_for_each_entry(i, &dev->all_adj_list.lower, list)
- __netdev_adjacent_dev_unlink(i->dev, upper_dev);
+ __netdev_adjacent_dev_unlink(i->dev, upper_dev, i->ref_nr);
list_for_each_entry(i, &upper_dev->all_adj_list.upper, list)
- __netdev_adjacent_dev_unlink(dev, i->dev);
+ __netdev_adjacent_dev_unlink(dev, i->dev, i->ref_nr);
call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev);
}
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index cfe1120..654c013 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -17,6 +17,11 @@
#include <net/sock.h>
#include <net/fib_rules.h>
+static const struct fib_kuid_range fib_kuid_range_unset = {
+ KUIDT_INIT(0),
+ KUIDT_INIT(~0),
+};
+
int fib_default_rule_add(struct fib_rules_ops *ops,
u32 pref, u32 table, u32 flags)
{
@@ -31,9 +36,8 @@
r->pref = pref;
r->table = table;
r->flags = flags;
- r->uid_start = INVALID_UID;
- r->uid_end = INVALID_UID;
r->fr_net = hold_net(ops->fro_net);
+ r->uid_range = fib_kuid_range_unset;
r->suppress_prefixlen = -1;
r->suppress_ifgroup = -1;
@@ -184,21 +188,32 @@
}
EXPORT_SYMBOL_GPL(fib_rules_unregister);
-static inline kuid_t fib_nl_uid(struct nlattr *nla)
+static int uid_range_set(struct fib_kuid_range *range)
{
- return make_kuid(current_user_ns(), nla_get_u32(nla));
+ return uid_valid(range->start) && uid_valid(range->end);
}
-static int nla_put_uid(struct sk_buff *skb, int idx, kuid_t uid)
+static struct fib_kuid_range nla_get_kuid_range(struct nlattr **tb)
{
- return nla_put_u32(skb, idx, from_kuid_munged(current_user_ns(), uid));
+ struct fib_rule_uid_range *in;
+ struct fib_kuid_range out;
+
+ in = (struct fib_rule_uid_range *)nla_data(tb[FRA_UID_RANGE]);
+
+ out.start = make_kuid(current_user_ns(), in->start);
+ out.end = make_kuid(current_user_ns(), in->end);
+
+ return out;
}
-static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule)
+static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range)
{
- return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) ||
- (uid_gte(fl->flowi_uid, rule->uid_start) &&
- uid_lte(fl->flowi_uid, rule->uid_end));
+ struct fib_rule_uid_range out = {
+ from_kuid_munged(current_user_ns(), range->start),
+ from_kuid_munged(current_user_ns(), range->end)
+ };
+
+ return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out);
}
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
@@ -215,7 +230,8 @@
if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask)
goto out;
- if (!fib_uid_range_match(fl, rule))
+ if (uid_lt(fl->flowi_uid, rule->uid_range.start) ||
+ uid_gt(fl->flowi_uid, rule->uid_range.end))
goto out;
ret = ops->match(rule, fl, flags);
@@ -400,17 +416,19 @@
} else if (rule->action == FR_ACT_GOTO)
goto errout_free;
- /* UID start and end must either both be valid or both unspecified. */
- rule->uid_start = rule->uid_end = INVALID_UID;
- if (tb[FRA_UID_START] || tb[FRA_UID_END]) {
- if (tb[FRA_UID_START] && tb[FRA_UID_END]) {
- rule->uid_start = fib_nl_uid(tb[FRA_UID_START]);
- rule->uid_end = fib_nl_uid(tb[FRA_UID_END]);
+ if (tb[FRA_UID_RANGE]) {
+ if (current_user_ns() != net->user_ns) {
+ err = -EPERM;
+ goto errout_free;
}
- if (!uid_valid(rule->uid_start) ||
- !uid_valid(rule->uid_end) ||
- !uid_lte(rule->uid_start, rule->uid_end))
- goto errout_free;
+
+ rule->uid_range = nla_get_kuid_range(tb);
+
+ if (!uid_range_set(&rule->uid_range) ||
+ !uid_lte(rule->uid_range.start, rule->uid_range.end))
+ goto errout_free;
+ } else {
+ rule->uid_range = fib_kuid_range_unset;
}
err = ops->configure(rule, skb, frh, tb);
@@ -472,6 +490,7 @@
struct fib_rules_ops *ops = NULL;
struct fib_rule *rule, *tmp;
struct nlattr *tb[FRA_MAX+1];
+ struct fib_kuid_range range;
int err = -EINVAL;
if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh)))
@@ -491,6 +510,14 @@
if (err < 0)
goto errout;
+ if (tb[FRA_UID_RANGE]) {
+ range = nla_get_kuid_range(tb);
+ if (!uid_range_set(&range))
+ goto errout;
+ } else {
+ range = fib_kuid_range_unset;
+ }
+
list_for_each_entry(rule, &ops->rules_list, list) {
if (frh->action && (frh->action != rule->action))
continue;
@@ -519,12 +546,9 @@
(rule->mark_mask != nla_get_u32(tb[FRA_FWMASK])))
continue;
- if (tb[FRA_UID_START] &&
- !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START])))
- continue;
-
- if (tb[FRA_UID_END] &&
- !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END])))
+ if (uid_range_set(&range) &&
+ (!uid_eq(rule->uid_range.start, range.start) ||
+ !uid_eq(rule->uid_range.end, range.end)))
continue;
if (!ops->compare(rule, frh, tb))
@@ -586,8 +610,7 @@
+ nla_total_size(4) /* FRA_SUPPRESS_IFGROUP */
+ nla_total_size(4) /* FRA_FWMARK */
+ nla_total_size(4) /* FRA_FWMASK */
- + nla_total_size(4) /* FRA_UID_START */
- + nla_total_size(4); /* FRA_UID_END */
+ + nla_total_size(sizeof(struct fib_kuid_range));
if (ops->nlmsg_payload)
payload += ops->nlmsg_payload(rule);
@@ -644,10 +667,8 @@
nla_put_u32(skb, FRA_FWMASK, rule->mark_mask)) ||
(rule->target &&
nla_put_u32(skb, FRA_GOTO, rule->target)) ||
- (uid_valid(rule->uid_start) &&
- nla_put_uid(skb, FRA_UID_START, rule->uid_start)) ||
- (uid_valid(rule->uid_end) &&
- nla_put_uid(skb, FRA_UID_END, rule->uid_end)))
+ (uid_range_set(&rule->uid_range) &&
+ nla_put_uid_range(skb, &rule->uid_range)))
goto nla_put_failure;
if (rule->suppress_ifgroup != -1) {
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index da94b6b..aaf4341 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -876,7 +876,8 @@
if (skb)
skb = skb_copy(skb, GFP_ATOMIC);
write_unlock(&neigh->lock);
- neigh->ops->solicit(neigh, skb);
+ if (neigh->ops->solicit)
+ neigh->ops->solicit(neigh, skb);
atomic_inc(&neigh->probes);
kfree_skb(skb);
}
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index e0ad5d1..ae73e1b 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -105,15 +105,21 @@
while ((skb = skb_dequeue(&npinfo->txq))) {
struct net_device *dev = skb->dev;
struct netdev_queue *txq;
+ unsigned int q_index;
if (!netif_device_present(dev) || !netif_running(dev)) {
kfree_skb(skb);
continue;
}
- txq = skb_get_tx_queue(dev, skb);
-
local_irq_save(flags);
+ /* check if skb->queue_mapping is still valid */
+ q_index = skb_get_queue_mapping(skb);
+ if (unlikely(q_index >= dev->real_num_tx_queues)) {
+ q_index = q_index % dev->real_num_tx_queues;
+ skb_set_queue_mapping(skb, q_index);
+ }
+ txq = netdev_get_tx_queue(dev, q_index);
HARD_TX_LOCK(dev, txq, smp_processor_id());
if (netif_xmit_frozen_or_stopped(txq) ||
netpoll_start_xmit(skb, dev, txq) != NETDEV_TX_OK) {
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 4ff3eac..b3aa632 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -211,8 +211,8 @@
#define T_REMDEV (1<<3) /* Remove one dev */
/* If lock -- protects updating of if_list */
-#define if_lock(t) spin_lock(&(t->if_lock));
-#define if_unlock(t) spin_unlock(&(t->if_lock));
+#define if_lock(t) mutex_lock(&(t->if_lock));
+#define if_unlock(t) mutex_unlock(&(t->if_lock));
/* Used to help with determining the pkts on receive */
#define PKTGEN_MAGIC 0xbe9be955
@@ -418,7 +418,7 @@
};
struct pktgen_thread {
- spinlock_t if_lock; /* for list of devices */
+ struct mutex if_lock; /* for list of devices */
struct list_head if_list; /* All device here */
struct list_head th_list;
struct task_struct *tsk;
@@ -1952,11 +1952,13 @@
{
struct pktgen_thread *t;
+ mutex_lock(&pktgen_thread_lock);
+
list_for_each_entry(t, &pn->pktgen_threads, th_list) {
struct pktgen_dev *pkt_dev;
- rcu_read_lock();
- list_for_each_entry_rcu(pkt_dev, &t->if_list, list) {
+ if_lock(t);
+ list_for_each_entry(pkt_dev, &t->if_list, list) {
if (pkt_dev->odev != dev)
continue;
@@ -1971,8 +1973,9 @@
dev->name);
break;
}
- rcu_read_unlock();
+ if_unlock(t);
}
+ mutex_unlock(&pktgen_thread_lock);
}
static int pktgen_device_event(struct notifier_block *unused,
@@ -3656,7 +3659,7 @@
return -ENOMEM;
}
- spin_lock_init(&t->if_lock);
+ mutex_init(&t->if_lock);
t->cpu = cpu;
INIT_LIST_HEAD(&t->if_list);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index ff68dfc..31d2aad 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -77,6 +77,8 @@
struct kmem_cache *skbuff_head_cache __read_mostly;
static struct kmem_cache *skbuff_fclone_cache __read_mostly;
+int sysctl_max_skb_frags __read_mostly = MAX_SKB_FRAGS;
+EXPORT_SYMBOL(sysctl_max_skb_frags);
/**
* skb_panic - private function for out-of-line support
@@ -3638,13 +3640,14 @@
{
struct sock *sk = skb->sk;
- /* take a reference to prevent skb_orphan() from freeing the socket */
- sock_hold(sk);
-
- *skb_hwtstamps(skb) = *hwtstamps;
- __skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND);
-
- sock_put(sk);
+ /* Take a reference to prevent skb_orphan() from freeing the socket,
+ * but only if the socket refcount is not zero.
+ */
+ if (likely(atomic_inc_not_zero(&sk->sk_refcnt))) {
+ *skb_hwtstamps(skb) = *hwtstamps;
+ __skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND);
+ sock_put(sk);
+ }
}
EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp);
@@ -3682,7 +3685,7 @@
{
struct sock *sk = skb->sk;
struct sock_exterr_skb *serr;
- int err;
+ int err = 1;
skb->wifi_acked_valid = 1;
skb->wifi_acked = acked;
@@ -3692,14 +3695,15 @@
serr->ee.ee_errno = ENOMSG;
serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS;
- /* take a reference to prevent skb_orphan() from freeing the socket */
- sock_hold(sk);
-
- err = sock_queue_err_skb(sk, skb);
+ /* Take a reference to prevent skb_orphan() from freeing the socket,
+ * but only if the socket refcount is not zero.
+ */
+ if (likely(atomic_inc_not_zero(&sk->sk_refcnt))) {
+ err = sock_queue_err_skb(sk, skb);
+ sock_put(sk);
+ }
if (err)
kfree_skb(skb);
-
- sock_put(sk);
}
EXPORT_SYMBOL_GPL(skb_complete_wifi_ack);
diff --git a/net/core/sock.c b/net/core/sock.c
index 29ec4ed..2141acd 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1420,6 +1420,11 @@
pr_debug("%s: optmem leakage (%d bytes) detected\n",
__func__, atomic_read(&sk->sk_omem_alloc));
+ if (sk->sk_frag.page) {
+ put_page(sk->sk_frag.page);
+ sk->sk_frag.page = NULL;
+ }
+
if (sk->sk_peer_cred)
put_cred(sk->sk_peer_cred);
put_pid(sk->sk_peer_pid);
@@ -1524,6 +1529,12 @@
is_charged = sk_filter_charge(newsk, filter);
if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk))) {
+ /* We need to make sure that we don't uncharge the new
+ * socket if we couldn't charge it in the first place
+ * as otherwise we uncharge the parent's filter.
+ */
+ if (!is_charged)
+ RCU_INIT_POINTER(newsk->sk_filter, NULL);
/* It is still raw copy of parent, so invalidate
* destructor and make plain sk_free() */
newsk->sk_destruct = NULL;
@@ -2292,8 +2303,11 @@
sk->sk_type = sock->type;
sk->sk_wq = sock->wq;
sock->sk = sk;
- } else
+ sk->sk_uid = SOCK_INODE(sock)->i_uid;
+ } else {
sk->sk_wq = NULL;
+ sk->sk_uid = make_kuid(sock_net(sk)->user_ns, 0);
+ }
spin_lock_init(&sk->sk_dst_lock);
rwlock_init(&sk->sk_callback_lock);
@@ -2598,11 +2612,6 @@
sk_refcnt_debug_release(sk);
- if (sk->sk_frag.page) {
- put_page(sk->sk_frag.page);
- sk->sk_frag.page = NULL;
- }
-
sock_put(sk);
}
EXPORT_SYMBOL(sk_common_release);
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index 8725b91..cd386d2 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -23,9 +23,11 @@
#include <net/pkt_sched.h>
static int zero = 0;
+static int one = 1;
static int ushort_max = USHRT_MAX;
static int min_sndbuf = SOCK_MIN_SNDBUF;
static int min_rcvbuf = SOCK_MIN_RCVBUF;
+static int max_skb_frags = MAX_SKB_FRAGS;
#ifdef CONFIG_RPS
static int rps_sock_flow_sysctl(struct ctl_table *table, int write,
@@ -362,6 +364,15 @@
.mode = 0644,
.proc_handler = proc_dointvec
},
+ {
+ .procname = "max_skb_frags",
+ .data = &sysctl_max_skb_frags,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &one,
+ .extra2 = &max_skb_frags,
+ },
{ }
};
diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
index f053198..5e3a730 100644
--- a/net/dccp/ccids/ccid2.c
+++ b/net/dccp/ccids/ccid2.c
@@ -749,6 +749,7 @@
for (i = 0; i < hc->tx_seqbufc; i++)
kfree(hc->tx_seqbuf[i]);
hc->tx_seqbufc = 0;
+ dccp_ackvec_parsed_cleanup(&hc->tx_av_chunks);
}
static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index bacd6f1..625bb2d 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -261,7 +261,8 @@
switch (type) {
case ICMP_REDIRECT:
- dccp_do_redirect(skb, sk);
+ if (!sock_owned_by_user(sk))
+ dccp_do_redirect(skb, sk);
goto out;
case ICMP_SOURCE_QUENCH:
/* Just silently ignore these. */
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 9b7f51a..3a54900 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -118,10 +118,12 @@
np = inet6_sk(sk);
if (type == NDISC_REDIRECT) {
- struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
+ if (!sock_owned_by_user(sk)) {
+ struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
- if (dst)
- dst->ops->redirect(dst, sk, skb);
+ if (dst)
+ dst->ops->redirect(dst, sk, skb);
+ }
goto out;
}
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index a3873f5..57a21da 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -122,6 +122,7 @@
/* It is still raw copy of parent, so invalidate
* destructor and make plain sk_free() */
newsk->sk_destruct = NULL;
+ bh_unlock_sock(newsk);
sk_free(newsk);
return NULL;
}
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index daccc4a..4047341 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -1042,10 +1042,13 @@
if (!fld.daddr) {
fld.daddr = fld.saddr;
- err = -EADDRNOTAVAIL;
if (dev_out)
dev_put(dev_out);
+ err = -EINVAL;
dev_out = init_net.loopback_dev;
+ if (!dev_out->dn_ptr)
+ goto out;
+ err = -EADDRNOTAVAIL;
dev_hold(dev_out);
if (!fld.daddr) {
fld.daddr =
@@ -1118,6 +1121,8 @@
if (dev_out == NULL)
goto out;
dn_db = rcu_dereference_raw(dev_out->dn_ptr);
+ if (!dn_db)
+ goto e_inval;
/* Possible improvement - check all devices for local addr */
if (dn_dev_islocal(dev_out, fld.daddr)) {
dev_put(dev_out);
@@ -1159,6 +1164,8 @@
dev_put(dev_out);
dev_out = init_net.loopback_dev;
dev_hold(dev_out);
+ if (!dev_out->dn_ptr)
+ goto e_inval;
fld.flowidn_oif = dev_out->ifindex;
if (res.fi)
dn_fib_info_put(res.fi);
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index db333aa..f83a390 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -89,6 +89,7 @@
#include <linux/netfilter_ipv4.h>
#include <linux/random.h>
#include <linux/slab.h>
+#include <linux/netfilter/xt_qtaguid.h>
#include <asm/uaccess.h>
@@ -417,6 +418,9 @@
if (sk) {
long timeout;
+#ifdef CONFIG_NETFILTER_XT_MATCH_QTAGUID
+ qtaguid_untag(sock, true);
+#endif
sock_rps_reset_flow(sk);
/* Applications forget to leave groups before exiting */
@@ -1409,6 +1413,45 @@
return pp;
}
+static struct sk_buff **ipip_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ if (NAPI_GRO_CB(skb)->encap_mark) {
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
+ }
+
+ NAPI_GRO_CB(skb)->encap_mark = 1;
+
+ return inet_gro_receive(head, skb);
+}
+
+#define SECONDS_PER_DAY 86400
+
+/* inet_current_timestamp - Return IP network timestamp
+ *
+ * Return milliseconds since midnight in network byte order.
+ */
+__be32 inet_current_timestamp(void)
+{
+ u32 secs;
+ u32 msecs;
+ struct timespec64 ts;
+
+ ktime_get_real_ts64(&ts);
+
+ /* Get secs since midnight. */
+ (void)div_u64_rem(ts.tv_sec, SECONDS_PER_DAY, &secs);
+ /* Convert to msecs. */
+ msecs = secs * MSEC_PER_SEC;
+ /* Convert nsec to msec. */
+ msecs += (u32)ts.tv_nsec / NSEC_PER_MSEC;
+
+ /* Convert to network byte order. */
+ return htons(msecs);
+}
+EXPORT_SYMBOL(inet_current_timestamp);
+
int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
{
if (sk->sk_family == AF_INET)
@@ -1451,6 +1494,13 @@
return err;
}
+static int ipip_gro_complete(struct sk_buff *skb, int nhoff)
+{
+ skb->encapsulation = 1;
+ skb_shinfo(skb)->gso_type |= SKB_GSO_IPIP;
+ return inet_gro_complete(skb, nhoff);
+}
+
int inet_ctl_sock_create(struct sock **sk, unsigned short family,
unsigned short type, unsigned char protocol,
struct net *net)
@@ -1667,8 +1717,8 @@
static const struct net_offload ipip_offload = {
.callbacks = {
.gso_segment = inet_gso_segment,
- .gro_receive = inet_gro_receive,
- .gro_complete = inet_gro_complete,
+ .gro_receive = ipip_gro_receive,
+ .gro_complete = ipip_gro_complete,
},
};
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index bc7c966..e08003d 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -1655,6 +1655,10 @@
goto validate_return_locked;
}
+ if (opt_iter + 1 == opt_len) {
+ err_offset = opt_iter;
+ goto validate_return_locked;
+ }
tag_len = tag[1];
if (tag_len > (opt_len - opt_iter)) {
err_offset = opt_iter + 1;
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 1df243c..7fa207e 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -335,6 +335,9 @@
ASSERT_RTNL();
+ if (in_dev->dead)
+ goto no_promotions;
+
/* 1. Deleting primary ifaddr forces deletion all secondaries
* unless alias promotion is set
**/
@@ -381,6 +384,7 @@
fib_del_ifaddr(ifa, ifa1);
}
+no_promotions:
/* 2. Unlink it */
*ifap = ifa1->ifa_next;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 69facd4..97548fc 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -815,6 +815,9 @@
subnet = 1;
}
+ if (in_dev->dead)
+ goto no_promotions;
+
/* Deletion is more complicated than add.
* We should take care of not to delete too much :-)
*
@@ -890,6 +893,7 @@
}
}
+no_promotions:
if (!(ok & BRD_OK))
fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
if (subnet && ifa->ifa_prefixlen < 31) {
@@ -958,7 +962,8 @@
net = sock_net(skb->sk);
nlh = nlmsg_hdr(skb);
- if (skb->len < NLMSG_HDRLEN || skb->len < nlh->nlmsg_len ||
+ if (skb->len < nlmsg_total_size(sizeof(*frn)) ||
+ skb->len < nlh->nlmsg_len ||
nlmsg_len(nlh) < sizeof(*frn))
return;
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index 8ce8e82..f7ae11d 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -113,6 +113,14 @@
u8 proto = NAPI_GRO_CB(skb)->proto;
const struct net_offload **offloads;
+ /* We can clear the encap_mark for FOU as we are essentially doing
+ * one of two possible things. We are either adding an L4 tunnel
+ * header to the outer L3 tunnel header, or we are are simply
+ * treating the GRE tunnel header as though it is a UDP protocol
+ * specific header such as VXLAN or GENEVE.
+ */
+ NAPI_GRO_CB(skb)->encap_mark = 0;
+
rcu_read_lock();
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
ops = rcu_dereference(offloads[proto]);
@@ -217,6 +225,14 @@
skb_gro_pull(skb, guehlen);
+ /* We can clear the encap_mark for GUE as we are essentially doing
+ * one of two possible things. We are either adding an L4 tunnel
+ * header to the outer L3 tunnel header, or we are are simply
+ * treating the GRE tunnel header as though it is a UDP protocol
+ * specific header such as VXLAN or GENEVE.
+ */
+ NAPI_GRO_CB(skb)->encap_mark = 0;
+
/* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
skb_gro_postpull_rcsum(skb, guehdr, guehlen);
diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c
index abc50b4..cc7b082 100644
--- a/net/ipv4/gre_offload.c
+++ b/net/ipv4/gre_offload.c
@@ -128,6 +128,11 @@
struct packet_offload *ptype;
__be16 type;
+ if (NAPI_GRO_CB(skb)->encap_mark)
+ goto out;
+
+ NAPI_GRO_CB(skb)->encap_mark = 1;
+
off = skb_gro_offset(skb);
hlen = off + sizeof(*greh);
greh = skb_gro_header_fast(skb, off);
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 5882f58..7775a47 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -423,6 +423,7 @@
fl4.daddr = daddr;
fl4.saddr = saddr;
fl4.flowi4_mark = mark;
+ fl4.flowi4_uid = sock_net_uid(net, NULL);
fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
fl4.flowi4_proto = IPPROTO_ICMP;
security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
@@ -454,6 +455,7 @@
param->replyopts.opt.opt.faddr : iph->saddr);
fl4->saddr = saddr;
fl4->flowi4_mark = mark;
+ fl4->flowi4_uid = sock_net_uid(net, NULL);
fl4->flowi4_tos = RT_TOS(tos);
fl4->flowi4_proto = IPPROTO_ICMP;
fl4->fl4_icmp_type = type;
@@ -902,7 +904,6 @@
*/
static void icmp_timestamp(struct sk_buff *skb)
{
- struct timespec tv;
struct icmp_bxm icmp_param;
/*
* Too short.
@@ -913,9 +914,7 @@
/*
* Fill in the current time as ms since midnight UT:
*/
- getnstimeofday(&tv);
- icmp_param.data.times[1] = htonl((tv.tv_sec % 86400) * MSEC_PER_SEC +
- tv.tv_nsec / NSEC_PER_MSEC);
+ icmp_param.data.times[1] = inet_current_timestamp();
icmp_param.data.times[2] = icmp_param.data.times[1];
if (skb_copy_bits(skb, 0, &icmp_param.data.times[0], 4))
BUG();
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 4e89166..1da0020 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -418,7 +418,7 @@
flags,
(opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr,
ireq->ir_loc_addr, ireq->ir_rmt_port, inet_sk(sk)->inet_sport,
- sock_i_uid(sk));
+ sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
if (IS_ERR(rt))
@@ -455,7 +455,7 @@
sk->sk_protocol, inet_sk_flowi_flags(sk),
(opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr,
ireq->ir_loc_addr, ireq->ir_rmt_port, inet_sk(sk)->inet_sport,
- sock_i_uid(sk));
+ sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
if (IS_ERR(rt))
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index 5b3d91b..0e4632e 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -58,10 +58,9 @@
if (opt->ts_needaddr)
ip_rt_get_source(iph+opt->ts+iph[opt->ts+2]-9, skb, rt);
if (opt->ts_needtime) {
- struct timespec tv;
__be32 midtime;
- getnstimeofday(&tv);
- midtime = htonl((tv.tv_sec % 86400) * MSEC_PER_SEC + tv.tv_nsec / NSEC_PER_MSEC);
+
+ midtime = inet_current_timestamp();
memcpy(iph+opt->ts+iph[opt->ts+2]-5, &midtime, 4);
}
return;
@@ -415,11 +414,10 @@
break;
}
if (timeptr) {
- struct timespec tv;
- u32 midtime;
- getnstimeofday(&tv);
- midtime = (tv.tv_sec % 86400) * MSEC_PER_SEC + tv.tv_nsec / NSEC_PER_MSEC;
- put_unaligned_be32(midtime, timeptr);
+ __be32 midtime;
+
+ midtime = inet_current_timestamp();
+ memcpy(timeptr, &midtime, 4);
opt->is_changed = 1;
}
} else if ((optptr[3]&0xF) != IPOPT_TS_PRESPEC) {
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 1b7f6da..2348cc8 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -2188,7 +2188,7 @@
int ipmr_get_route(struct net *net, struct sk_buff *skb,
__be32 saddr, __be32 daddr,
- struct rtmsg *rtm, int nowait)
+ struct rtmsg *rtm, int nowait, u32 portid)
{
struct mfc_cache *cache;
struct mr_table *mrt;
@@ -2233,6 +2233,7 @@
return -ENOMEM;
}
+ NETLINK_CB(skb2).portid = portid;
skb_push(skb2, sizeof(struct iphdr));
skb_reset_network_header(skb2);
iph = ip_hdr(skb2);
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 738e62d..b3bfa51 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -367,7 +367,8 @@
* there are loops. Puts hook bitmask in comefrom.
*/
static int mark_source_chains(const struct xt_table_info *newinfo,
- unsigned int valid_hooks, void *entry0)
+ unsigned int valid_hooks, void *entry0,
+ unsigned int *offsets)
{
unsigned int hook;
@@ -435,6 +436,8 @@
size = e->next_offset;
e = (struct arpt_entry *)
(entry0 + pos + size);
+ if (pos + size >= newinfo->size)
+ return 0;
e->counters.pcnt = pos;
pos += size;
} else {
@@ -454,9 +457,16 @@
/* This a jump; chase it. */
duprintf("Jump rule %u -> %u\n",
pos, newpos);
+ if (!xt_find_jump_offset(offsets, newpos,
+ newinfo->number))
+ return 0;
+ e = (struct arpt_entry *)
+ (entry0 + newpos);
} else {
/* ... this is a fallthru */
newpos = pos + e->next_offset;
+ if (newpos >= newinfo->size)
+ return 0;
}
e = (struct arpt_entry *)
(entry0 + newpos);
@@ -470,23 +480,6 @@
return 1;
}
-static inline int check_entry(const struct arpt_entry *e)
-{
- const struct xt_entry_target *t;
-
- if (!arp_checkentry(&e->arp))
- return -EINVAL;
-
- if (e->target_offset + sizeof(struct xt_entry_target) > e->next_offset)
- return -EINVAL;
-
- t = arpt_get_target_c(e);
- if (e->target_offset + t->u.target_size > e->next_offset)
- return -EINVAL;
-
- return 0;
-}
-
static inline int check_target(struct arpt_entry *e, const char *name)
{
struct xt_entry_target *t = arpt_get_target(e);
@@ -576,7 +569,11 @@
return -EINVAL;
}
- err = check_entry(e);
+ if (!arp_checkentry(&e->arp))
+ return -EINVAL;
+
+ err = xt_check_entry_offsets(e, e->elems, e->target_offset,
+ e->next_offset);
if (err)
return err;
@@ -624,6 +621,7 @@
const struct arpt_replace *repl)
{
struct arpt_entry *iter;
+ unsigned int *offsets;
unsigned int i;
int ret = 0;
@@ -637,8 +635,10 @@
}
duprintf("translate_table: size %u\n", newinfo->size);
+ offsets = xt_alloc_entry_offsets(newinfo->number);
+ if (!offsets)
+ return -ENOMEM;
i = 0;
-
/* Walk through entries, checking offsets. */
xt_entry_foreach(iter, entry0, newinfo->size) {
ret = check_entry_size_and_hooks(iter, newinfo, entry0,
@@ -647,7 +647,9 @@
repl->underflow,
repl->valid_hooks);
if (ret != 0)
- break;
+ goto out_free;
+ if (i < repl->num_entries)
+ offsets[i] = (void *)iter - entry0;
++i;
if (strcmp(arpt_get_target(iter)->u.user.name,
XT_ERROR_TARGET) == 0)
@@ -655,12 +657,13 @@
}
duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret);
if (ret != 0)
- return ret;
+ goto out_free;
+ ret = -EINVAL;
if (i != repl->num_entries) {
duprintf("translate_table: %u not %u entries\n",
i, repl->num_entries);
- return -EINVAL;
+ goto out_free;
}
/* Check hooks all assigned */
@@ -671,19 +674,20 @@
if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
duprintf("Invalid hook entry %u %u\n",
i, repl->hook_entry[i]);
- return -EINVAL;
+ goto out_free;
}
if (newinfo->underflow[i] == 0xFFFFFFFF) {
duprintf("Invalid underflow %u %u\n",
i, repl->underflow[i]);
- return -EINVAL;
+ goto out_free;
}
}
- if (!mark_source_chains(newinfo, repl->valid_hooks, entry0)) {
- duprintf("Looping hook\n");
- return -ELOOP;
+ if (!mark_source_chains(newinfo, repl->valid_hooks, entry0, offsets)) {
+ ret = -ELOOP;
+ goto out_free;
}
+ kvfree(offsets);
/* Finally, each sanity check must pass */
i = 0;
@@ -710,6 +714,9 @@
}
return ret;
+ out_free:
+ kvfree(offsets);
+ return ret;
}
static void get_counters(const struct xt_table_info *t,
@@ -1116,56 +1123,18 @@
unsigned int i, curcpu;
struct xt_counters_info tmp;
struct xt_counters *paddc;
- unsigned int num_counters;
- const char *name;
- int size;
- void *ptmp;
struct xt_table *t;
const struct xt_table_info *private;
int ret = 0;
void *loc_cpu_entry;
struct arpt_entry *iter;
unsigned int addend;
-#ifdef CONFIG_COMPAT
- struct compat_xt_counters_info compat_tmp;
- if (compat) {
- ptmp = &compat_tmp;
- size = sizeof(struct compat_xt_counters_info);
- } else
-#endif
- {
- ptmp = &tmp;
- size = sizeof(struct xt_counters_info);
- }
+ paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
+ if (IS_ERR(paddc))
+ return PTR_ERR(paddc);
- if (copy_from_user(ptmp, user, size) != 0)
- return -EFAULT;
-
-#ifdef CONFIG_COMPAT
- if (compat) {
- num_counters = compat_tmp.num_counters;
- name = compat_tmp.name;
- } else
-#endif
- {
- num_counters = tmp.num_counters;
- name = tmp.name;
- }
-
- if (len != size + num_counters * sizeof(struct xt_counters))
- return -EINVAL;
-
- paddc = vmalloc(len - size);
- if (!paddc)
- return -ENOMEM;
-
- if (copy_from_user(paddc, user + size, len - size) != 0) {
- ret = -EFAULT;
- goto free;
- }
-
- t = xt_find_table_lock(net, NFPROTO_ARP, name);
+ t = xt_find_table_lock(net, NFPROTO_ARP, tmp.name);
if (IS_ERR_OR_NULL(t)) {
ret = t ? PTR_ERR(t) : -ENOENT;
goto free;
@@ -1173,7 +1142,7 @@
local_bh_disable();
private = t->private;
- if (private->number != num_counters) {
+ if (private->number != tmp.num_counters) {
ret = -EINVAL;
goto unlock_up_free;
}
@@ -1199,6 +1168,18 @@
}
#ifdef CONFIG_COMPAT
+struct compat_arpt_replace {
+ char name[XT_TABLE_MAXNAMELEN];
+ u32 valid_hooks;
+ u32 num_entries;
+ u32 size;
+ u32 hook_entry[NF_ARP_NUMHOOKS];
+ u32 underflow[NF_ARP_NUMHOOKS];
+ u32 num_counters;
+ compat_uptr_t counters;
+ struct compat_arpt_entry entries[0];
+};
+
static inline void compat_release_entry(struct compat_arpt_entry *e)
{
struct xt_entry_target *t;
@@ -1207,20 +1188,17 @@
module_put(t->u.kernel.target->me);
}
-static inline int
+static int
check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
struct xt_table_info *newinfo,
unsigned int *size,
const unsigned char *base,
- const unsigned char *limit,
- const unsigned int *hook_entries,
- const unsigned int *underflows,
- const char *name)
+ const unsigned char *limit)
{
struct xt_entry_target *t;
struct xt_target *target;
unsigned int entry_offset;
- int ret, off, h;
+ int ret, off;
duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 ||
@@ -1237,8 +1215,11 @@
return -EINVAL;
}
- /* For purposes of check_entry casting the compat entry is fine */
- ret = check_entry((struct arpt_entry *)e);
+ if (!arp_checkentry(&e->arp))
+ return -EINVAL;
+
+ ret = xt_compat_check_entry_offsets(e, e->elems, e->target_offset,
+ e->next_offset);
if (ret)
return ret;
@@ -1262,17 +1243,6 @@
if (ret)
goto release_target;
- /* Check hooks & underflows */
- for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
- if ((unsigned char *)e - base == hook_entries[h])
- newinfo->hook_entry[h] = hook_entries[h];
- if ((unsigned char *)e - base == underflows[h])
- newinfo->underflow[h] = underflows[h];
- }
-
- /* Clear counters and comefrom */
- memset(&e->counters, 0, sizeof(e->counters));
- e->comefrom = 0;
return 0;
release_target:
@@ -1281,18 +1251,17 @@
return ret;
}
-static int
+static void
compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr,
- unsigned int *size, const char *name,
+ unsigned int *size,
struct xt_table_info *newinfo, unsigned char *base)
{
struct xt_entry_target *t;
struct xt_target *target;
struct arpt_entry *de;
unsigned int origsize;
- int ret, h;
+ int h;
- ret = 0;
origsize = *size;
de = (struct arpt_entry *)*dstptr;
memcpy(de, e, sizeof(struct arpt_entry));
@@ -1313,145 +1282,82 @@
if ((unsigned char *)de - base < newinfo->underflow[h])
newinfo->underflow[h] -= origsize - *size;
}
- return ret;
}
-static int translate_compat_table(const char *name,
- unsigned int valid_hooks,
- struct xt_table_info **pinfo,
+static int translate_compat_table(struct xt_table_info **pinfo,
void **pentry0,
- unsigned int total_size,
- unsigned int number,
- unsigned int *hook_entries,
- unsigned int *underflows)
+ const struct compat_arpt_replace *compatr)
{
unsigned int i, j;
struct xt_table_info *newinfo, *info;
void *pos, *entry0, *entry1;
struct compat_arpt_entry *iter0;
- struct arpt_entry *iter1;
+ struct arpt_replace repl;
unsigned int size;
int ret = 0;
info = *pinfo;
entry0 = *pentry0;
- size = total_size;
- info->number = number;
-
- /* Init all hooks to impossible value. */
- for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
- info->hook_entry[i] = 0xFFFFFFFF;
- info->underflow[i] = 0xFFFFFFFF;
- }
+ size = compatr->size;
+ info->number = compatr->num_entries;
duprintf("translate_compat_table: size %u\n", info->size);
j = 0;
xt_compat_lock(NFPROTO_ARP);
- xt_compat_init_offsets(NFPROTO_ARP, number);
+ xt_compat_init_offsets(NFPROTO_ARP, compatr->num_entries);
/* Walk through entries, checking offsets. */
- xt_entry_foreach(iter0, entry0, total_size) {
+ xt_entry_foreach(iter0, entry0, compatr->size) {
ret = check_compat_entry_size_and_hooks(iter0, info, &size,
entry0,
- entry0 + total_size,
- hook_entries,
- underflows,
- name);
+ entry0 + compatr->size);
if (ret != 0)
goto out_unlock;
++j;
}
ret = -EINVAL;
- if (j != number) {
+ if (j != compatr->num_entries) {
duprintf("translate_compat_table: %u not %u entries\n",
- j, number);
+ j, compatr->num_entries);
goto out_unlock;
}
- /* Check hooks all assigned */
- for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
- /* Only hooks which are valid */
- if (!(valid_hooks & (1 << i)))
- continue;
- if (info->hook_entry[i] == 0xFFFFFFFF) {
- duprintf("Invalid hook entry %u %u\n",
- i, hook_entries[i]);
- goto out_unlock;
- }
- if (info->underflow[i] == 0xFFFFFFFF) {
- duprintf("Invalid underflow %u %u\n",
- i, underflows[i]);
- goto out_unlock;
- }
- }
-
ret = -ENOMEM;
newinfo = xt_alloc_table_info(size);
if (!newinfo)
goto out_unlock;
- newinfo->number = number;
+ newinfo->number = compatr->num_entries;
for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
newinfo->hook_entry[i] = info->hook_entry[i];
newinfo->underflow[i] = info->underflow[i];
}
entry1 = newinfo->entries[raw_smp_processor_id()];
pos = entry1;
- size = total_size;
- xt_entry_foreach(iter0, entry0, total_size) {
- ret = compat_copy_entry_from_user(iter0, &pos, &size,
- name, newinfo, entry1);
- if (ret != 0)
- break;
- }
+ size = compatr->size;
+ xt_entry_foreach(iter0, entry0, compatr->size)
+ compat_copy_entry_from_user(iter0, &pos, &size,
+ newinfo, entry1);
+
+ /* all module references in entry0 are now gone */
+
xt_compat_flush_offsets(NFPROTO_ARP);
xt_compat_unlock(NFPROTO_ARP);
+
+ memcpy(&repl, compatr, sizeof(*compatr));
+
+ for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
+ repl.hook_entry[i] = newinfo->hook_entry[i];
+ repl.underflow[i] = newinfo->underflow[i];
+ }
+
+ repl.num_counters = 0;
+ repl.counters = NULL;
+ repl.size = newinfo->size;
+ ret = translate_table(newinfo, entry1, &repl);
if (ret)
goto free_newinfo;
- ret = -ELOOP;
- if (!mark_source_chains(newinfo, valid_hooks, entry1))
- goto free_newinfo;
-
- i = 0;
- xt_entry_foreach(iter1, entry1, newinfo->size) {
- ret = check_target(iter1, name);
- if (ret != 0)
- break;
- ++i;
- if (strcmp(arpt_get_target(iter1)->u.user.name,
- XT_ERROR_TARGET) == 0)
- ++newinfo->stacksize;
- }
- if (ret) {
- /*
- * The first i matches need cleanup_entry (calls ->destroy)
- * because they had called ->check already. The other j-i
- * entries need only release.
- */
- int skip = i;
- j -= i;
- xt_entry_foreach(iter0, entry0, newinfo->size) {
- if (skip-- > 0)
- continue;
- if (j-- == 0)
- break;
- compat_release_entry(iter0);
- }
- xt_entry_foreach(iter1, entry1, newinfo->size) {
- if (i-- == 0)
- break;
- cleanup_entry(iter1);
- }
- xt_free_table_info(newinfo);
- return ret;
- }
-
- /* And one copy for every other CPU */
- for_each_possible_cpu(i)
- if (newinfo->entries[i] && newinfo->entries[i] != entry1)
- memcpy(newinfo->entries[i], entry1, newinfo->size);
-
*pinfo = newinfo;
*pentry0 = entry1;
xt_free_table_info(info);
@@ -1459,31 +1365,18 @@
free_newinfo:
xt_free_table_info(newinfo);
-out:
- xt_entry_foreach(iter0, entry0, total_size) {
+ return ret;
+out_unlock:
+ xt_compat_flush_offsets(NFPROTO_ARP);
+ xt_compat_unlock(NFPROTO_ARP);
+ xt_entry_foreach(iter0, entry0, compatr->size) {
if (j-- == 0)
break;
compat_release_entry(iter0);
}
return ret;
-out_unlock:
- xt_compat_flush_offsets(NFPROTO_ARP);
- xt_compat_unlock(NFPROTO_ARP);
- goto out;
}
-struct compat_arpt_replace {
- char name[XT_TABLE_MAXNAMELEN];
- u32 valid_hooks;
- u32 num_entries;
- u32 size;
- u32 hook_entry[NF_ARP_NUMHOOKS];
- u32 underflow[NF_ARP_NUMHOOKS];
- u32 num_counters;
- compat_uptr_t counters;
- struct compat_arpt_entry entries[0];
-};
-
static int compat_do_replace(struct net *net, void __user *user,
unsigned int len)
{
@@ -1514,10 +1407,7 @@
goto free_newinfo;
}
- ret = translate_compat_table(tmp.name, tmp.valid_hooks,
- &newinfo, &loc_cpu_entry, tmp.size,
- tmp.num_entries, tmp.hook_entry,
- tmp.underflow);
+ ret = translate_compat_table(&newinfo, &loc_cpu_entry, &tmp);
if (ret != 0)
goto free_newinfo;
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 2c8fb72..4e40f2e 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -443,7 +443,8 @@
there are loops. Puts hook bitmask in comefrom. */
static int
mark_source_chains(const struct xt_table_info *newinfo,
- unsigned int valid_hooks, void *entry0)
+ unsigned int valid_hooks, void *entry0,
+ unsigned int *offsets)
{
unsigned int hook;
@@ -516,6 +517,8 @@
size = e->next_offset;
e = (struct ipt_entry *)
(entry0 + pos + size);
+ if (pos + size >= newinfo->size)
+ return 0;
e->counters.pcnt = pos;
pos += size;
} else {
@@ -534,9 +537,16 @@
/* This a jump; chase it. */
duprintf("Jump rule %u -> %u\n",
pos, newpos);
+ if (!xt_find_jump_offset(offsets, newpos,
+ newinfo->number))
+ return 0;
+ e = (struct ipt_entry *)
+ (entry0 + newpos);
} else {
/* ... this is a fallthru */
newpos = pos + e->next_offset;
+ if (newpos >= newinfo->size)
+ return 0;
}
e = (struct ipt_entry *)
(entry0 + newpos);
@@ -564,25 +574,6 @@
}
static int
-check_entry(const struct ipt_entry *e)
-{
- const struct xt_entry_target *t;
-
- if (!ip_checkentry(&e->ip))
- return -EINVAL;
-
- if (e->target_offset + sizeof(struct xt_entry_target) >
- e->next_offset)
- return -EINVAL;
-
- t = ipt_get_target_c(e);
- if (e->target_offset + t->u.target_size > e->next_offset)
- return -EINVAL;
-
- return 0;
-}
-
-static int
check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
{
const struct ipt_ip *ip = par->entryinfo;
@@ -738,7 +729,11 @@
return -EINVAL;
}
- err = check_entry(e);
+ if (!ip_checkentry(&e->ip))
+ return -EINVAL;
+
+ err = xt_check_entry_offsets(e, e->elems, e->target_offset,
+ e->next_offset);
if (err)
return err;
@@ -793,6 +788,7 @@
const struct ipt_replace *repl)
{
struct ipt_entry *iter;
+ unsigned int *offsets;
unsigned int i;
int ret = 0;
@@ -806,6 +802,9 @@
}
duprintf("translate_table: size %u\n", newinfo->size);
+ offsets = xt_alloc_entry_offsets(newinfo->number);
+ if (!offsets)
+ return -ENOMEM;
i = 0;
/* Walk through entries, checking offsets. */
xt_entry_foreach(iter, entry0, newinfo->size) {
@@ -815,17 +814,20 @@
repl->underflow,
repl->valid_hooks);
if (ret != 0)
- return ret;
+ goto out_free;
+ if (i < repl->num_entries)
+ offsets[i] = (void *)iter - entry0;
++i;
if (strcmp(ipt_get_target(iter)->u.user.name,
XT_ERROR_TARGET) == 0)
++newinfo->stacksize;
}
+ ret = -EINVAL;
if (i != repl->num_entries) {
duprintf("translate_table: %u not %u entries\n",
i, repl->num_entries);
- return -EINVAL;
+ goto out_free;
}
/* Check hooks all assigned */
@@ -836,17 +838,20 @@
if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
duprintf("Invalid hook entry %u %u\n",
i, repl->hook_entry[i]);
- return -EINVAL;
+ goto out_free;
}
if (newinfo->underflow[i] == 0xFFFFFFFF) {
duprintf("Invalid underflow %u %u\n",
i, repl->underflow[i]);
- return -EINVAL;
+ goto out_free;
}
}
- if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
- return -ELOOP;
+ if (!mark_source_chains(newinfo, repl->valid_hooks, entry0, offsets)) {
+ ret = -ELOOP;
+ goto out_free;
+ }
+ kvfree(offsets);
/* Finally, each sanity check must pass */
i = 0;
@@ -873,6 +878,9 @@
}
return ret;
+ out_free:
+ kvfree(offsets);
+ return ret;
}
static void
@@ -1303,56 +1311,18 @@
unsigned int i, curcpu;
struct xt_counters_info tmp;
struct xt_counters *paddc;
- unsigned int num_counters;
- const char *name;
- int size;
- void *ptmp;
struct xt_table *t;
const struct xt_table_info *private;
int ret = 0;
void *loc_cpu_entry;
struct ipt_entry *iter;
unsigned int addend;
-#ifdef CONFIG_COMPAT
- struct compat_xt_counters_info compat_tmp;
- if (compat) {
- ptmp = &compat_tmp;
- size = sizeof(struct compat_xt_counters_info);
- } else
-#endif
- {
- ptmp = &tmp;
- size = sizeof(struct xt_counters_info);
- }
+ paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
+ if (IS_ERR(paddc))
+ return PTR_ERR(paddc);
- if (copy_from_user(ptmp, user, size) != 0)
- return -EFAULT;
-
-#ifdef CONFIG_COMPAT
- if (compat) {
- num_counters = compat_tmp.num_counters;
- name = compat_tmp.name;
- } else
-#endif
- {
- num_counters = tmp.num_counters;
- name = tmp.name;
- }
-
- if (len != size + num_counters * sizeof(struct xt_counters))
- return -EINVAL;
-
- paddc = vmalloc(len - size);
- if (!paddc)
- return -ENOMEM;
-
- if (copy_from_user(paddc, user + size, len - size) != 0) {
- ret = -EFAULT;
- goto free;
- }
-
- t = xt_find_table_lock(net, AF_INET, name);
+ t = xt_find_table_lock(net, AF_INET, tmp.name);
if (IS_ERR_OR_NULL(t)) {
ret = t ? PTR_ERR(t) : -ENOENT;
goto free;
@@ -1360,7 +1330,7 @@
local_bh_disable();
private = t->private;
- if (private->number != num_counters) {
+ if (private->number != tmp.num_counters) {
ret = -EINVAL;
goto unlock_up_free;
}
@@ -1439,7 +1409,6 @@
static int
compat_find_calc_match(struct xt_entry_match *m,
- const char *name,
const struct ipt_ip *ip,
unsigned int hookmask,
int *size)
@@ -1475,17 +1444,14 @@
struct xt_table_info *newinfo,
unsigned int *size,
const unsigned char *base,
- const unsigned char *limit,
- const unsigned int *hook_entries,
- const unsigned int *underflows,
- const char *name)
+ const unsigned char *limit)
{
struct xt_entry_match *ematch;
struct xt_entry_target *t;
struct xt_target *target;
unsigned int entry_offset;
unsigned int j;
- int ret, off, h;
+ int ret, off;
duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
@@ -1502,8 +1468,11 @@
return -EINVAL;
}
- /* For purposes of check_entry casting the compat entry is fine */
- ret = check_entry((struct ipt_entry *)e);
+ if (!ip_checkentry(&e->ip))
+ return -EINVAL;
+
+ ret = xt_compat_check_entry_offsets(e, e->elems,
+ e->target_offset, e->next_offset);
if (ret)
return ret;
@@ -1511,8 +1480,8 @@
entry_offset = (void *)e - (void *)base;
j = 0;
xt_ematch_foreach(ematch, e) {
- ret = compat_find_calc_match(ematch, name,
- &e->ip, e->comefrom, &off);
+ ret = compat_find_calc_match(ematch, &e->ip, e->comefrom,
+ &off);
if (ret != 0)
goto release_matches;
++j;
@@ -1535,17 +1504,6 @@
if (ret)
goto out;
- /* Check hooks & underflows */
- for (h = 0; h < NF_INET_NUMHOOKS; h++) {
- if ((unsigned char *)e - base == hook_entries[h])
- newinfo->hook_entry[h] = hook_entries[h];
- if ((unsigned char *)e - base == underflows[h])
- newinfo->underflow[h] = underflows[h];
- }
-
- /* Clear counters and comefrom */
- memset(&e->counters, 0, sizeof(e->counters));
- e->comefrom = 0;
return 0;
out:
@@ -1559,19 +1517,18 @@
return ret;
}
-static int
+static void
compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr,
- unsigned int *size, const char *name,
+ unsigned int *size,
struct xt_table_info *newinfo, unsigned char *base)
{
struct xt_entry_target *t;
struct xt_target *target;
struct ipt_entry *de;
unsigned int origsize;
- int ret, h;
+ int h;
struct xt_entry_match *ematch;
- ret = 0;
origsize = *size;
de = (struct ipt_entry *)*dstptr;
memcpy(de, e, sizeof(struct ipt_entry));
@@ -1580,199 +1537,105 @@
*dstptr += sizeof(struct ipt_entry);
*size += sizeof(struct ipt_entry) - sizeof(struct compat_ipt_entry);
- xt_ematch_foreach(ematch, e) {
- ret = xt_compat_match_from_user(ematch, dstptr, size);
- if (ret != 0)
- return ret;
- }
+ xt_ematch_foreach(ematch, e)
+ xt_compat_match_from_user(ematch, dstptr, size);
+
de->target_offset = e->target_offset - (origsize - *size);
t = compat_ipt_get_target(e);
target = t->u.kernel.target;
xt_compat_target_from_user(t, dstptr, size);
de->next_offset = e->next_offset - (origsize - *size);
+
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
if ((unsigned char *)de - base < newinfo->hook_entry[h])
newinfo->hook_entry[h] -= origsize - *size;
if ((unsigned char *)de - base < newinfo->underflow[h])
newinfo->underflow[h] -= origsize - *size;
}
- return ret;
-}
-
-static int
-compat_check_entry(struct ipt_entry *e, struct net *net, const char *name)
-{
- struct xt_entry_match *ematch;
- struct xt_mtchk_param mtpar;
- unsigned int j;
- int ret = 0;
-
- j = 0;
- mtpar.net = net;
- mtpar.table = name;
- mtpar.entryinfo = &e->ip;
- mtpar.hook_mask = e->comefrom;
- mtpar.family = NFPROTO_IPV4;
- xt_ematch_foreach(ematch, e) {
- ret = check_match(ematch, &mtpar);
- if (ret != 0)
- goto cleanup_matches;
- ++j;
- }
-
- ret = check_target(e, net, name);
- if (ret)
- goto cleanup_matches;
- return 0;
-
- cleanup_matches:
- xt_ematch_foreach(ematch, e) {
- if (j-- == 0)
- break;
- cleanup_match(ematch, net);
- }
- return ret;
}
static int
translate_compat_table(struct net *net,
- const char *name,
- unsigned int valid_hooks,
struct xt_table_info **pinfo,
void **pentry0,
- unsigned int total_size,
- unsigned int number,
- unsigned int *hook_entries,
- unsigned int *underflows)
+ const struct compat_ipt_replace *compatr)
{
unsigned int i, j;
struct xt_table_info *newinfo, *info;
void *pos, *entry0, *entry1;
struct compat_ipt_entry *iter0;
- struct ipt_entry *iter1;
+ struct ipt_replace repl;
unsigned int size;
int ret;
info = *pinfo;
entry0 = *pentry0;
- size = total_size;
- info->number = number;
-
- /* Init all hooks to impossible value. */
- for (i = 0; i < NF_INET_NUMHOOKS; i++) {
- info->hook_entry[i] = 0xFFFFFFFF;
- info->underflow[i] = 0xFFFFFFFF;
- }
+ size = compatr->size;
+ info->number = compatr->num_entries;
duprintf("translate_compat_table: size %u\n", info->size);
j = 0;
xt_compat_lock(AF_INET);
- xt_compat_init_offsets(AF_INET, number);
+ xt_compat_init_offsets(AF_INET, compatr->num_entries);
/* Walk through entries, checking offsets. */
- xt_entry_foreach(iter0, entry0, total_size) {
+ xt_entry_foreach(iter0, entry0, compatr->size) {
ret = check_compat_entry_size_and_hooks(iter0, info, &size,
entry0,
- entry0 + total_size,
- hook_entries,
- underflows,
- name);
+ entry0 + compatr->size);
if (ret != 0)
goto out_unlock;
++j;
}
ret = -EINVAL;
- if (j != number) {
+ if (j != compatr->num_entries) {
duprintf("translate_compat_table: %u not %u entries\n",
- j, number);
+ j, compatr->num_entries);
goto out_unlock;
}
- /* Check hooks all assigned */
- for (i = 0; i < NF_INET_NUMHOOKS; i++) {
- /* Only hooks which are valid */
- if (!(valid_hooks & (1 << i)))
- continue;
- if (info->hook_entry[i] == 0xFFFFFFFF) {
- duprintf("Invalid hook entry %u %u\n",
- i, hook_entries[i]);
- goto out_unlock;
- }
- if (info->underflow[i] == 0xFFFFFFFF) {
- duprintf("Invalid underflow %u %u\n",
- i, underflows[i]);
- goto out_unlock;
- }
- }
-
ret = -ENOMEM;
newinfo = xt_alloc_table_info(size);
if (!newinfo)
goto out_unlock;
- newinfo->number = number;
+ newinfo->number = compatr->num_entries;
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
- newinfo->hook_entry[i] = info->hook_entry[i];
- newinfo->underflow[i] = info->underflow[i];
+ newinfo->hook_entry[i] = compatr->hook_entry[i];
+ newinfo->underflow[i] = compatr->underflow[i];
}
entry1 = newinfo->entries[raw_smp_processor_id()];
pos = entry1;
- size = total_size;
- xt_entry_foreach(iter0, entry0, total_size) {
- ret = compat_copy_entry_from_user(iter0, &pos, &size,
- name, newinfo, entry1);
- if (ret != 0)
- break;
- }
+ size = compatr->size;
+ xt_entry_foreach(iter0, entry0, compatr->size)
+ compat_copy_entry_from_user(iter0, &pos, &size,
+ newinfo, entry1);
+
+ /* all module references in entry0 are now gone.
+ * entry1/newinfo contains a 64bit ruleset that looks exactly as
+ * generated by 64bit userspace.
+ *
+ * Call standard translate_table() to validate all hook_entrys,
+ * underflows, check for loops, etc.
+ */
xt_compat_flush_offsets(AF_INET);
xt_compat_unlock(AF_INET);
+
+ memcpy(&repl, compatr, sizeof(*compatr));
+
+ for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+ repl.hook_entry[i] = newinfo->hook_entry[i];
+ repl.underflow[i] = newinfo->underflow[i];
+ }
+
+ repl.num_counters = 0;
+ repl.counters = NULL;
+ repl.size = newinfo->size;
+ ret = translate_table(net, newinfo, entry1, &repl);
if (ret)
goto free_newinfo;
- ret = -ELOOP;
- if (!mark_source_chains(newinfo, valid_hooks, entry1))
- goto free_newinfo;
-
- i = 0;
- xt_entry_foreach(iter1, entry1, newinfo->size) {
- ret = compat_check_entry(iter1, net, name);
- if (ret != 0)
- break;
- ++i;
- if (strcmp(ipt_get_target(iter1)->u.user.name,
- XT_ERROR_TARGET) == 0)
- ++newinfo->stacksize;
- }
- if (ret) {
- /*
- * The first i matches need cleanup_entry (calls ->destroy)
- * because they had called ->check already. The other j-i
- * entries need only release.
- */
- int skip = i;
- j -= i;
- xt_entry_foreach(iter0, entry0, newinfo->size) {
- if (skip-- > 0)
- continue;
- if (j-- == 0)
- break;
- compat_release_entry(iter0);
- }
- xt_entry_foreach(iter1, entry1, newinfo->size) {
- if (i-- == 0)
- break;
- cleanup_entry(iter1, net);
- }
- xt_free_table_info(newinfo);
- return ret;
- }
-
- /* And one copy for every other CPU */
- for_each_possible_cpu(i)
- if (newinfo->entries[i] && newinfo->entries[i] != entry1)
- memcpy(newinfo->entries[i], entry1, newinfo->size);
-
*pinfo = newinfo;
*pentry0 = entry1;
xt_free_table_info(info);
@@ -1780,17 +1643,16 @@
free_newinfo:
xt_free_table_info(newinfo);
-out:
- xt_entry_foreach(iter0, entry0, total_size) {
+ return ret;
+out_unlock:
+ xt_compat_flush_offsets(AF_INET);
+ xt_compat_unlock(AF_INET);
+ xt_entry_foreach(iter0, entry0, compatr->size) {
if (j-- == 0)
break;
compat_release_entry(iter0);
}
return ret;
-out_unlock:
- xt_compat_flush_offsets(AF_INET);
- xt_compat_unlock(AF_INET);
- goto out;
}
static int
@@ -1824,10 +1686,7 @@
goto free_newinfo;
}
- ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
- &newinfo, &loc_cpu_entry, tmp.size,
- tmp.num_entries, tmp.hook_entry,
- tmp.underflow);
+ ret = translate_compat_table(net, &newinfo, &loc_cpu_entry, &tmp);
if (ret != 0)
goto free_newinfo;
diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
index 3f326bf..1eda519 100644
--- a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
+++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c
@@ -114,10 +114,18 @@
unsigned long event,
void *ptr)
{
- struct net_device *dev = ((struct in_ifaddr *)ptr)->ifa_dev->dev;
+ struct in_device *idev = ((struct in_ifaddr *)ptr)->ifa_dev;
struct netdev_notifier_info info;
- netdev_notifier_info_init(&info, dev);
+ /* The masq_dev_notifier will catch the case of the device going
+ * down. So if the inetdev is dead and being destroyed we have
+ * no work to do. Otherwise this is an individual address removal
+ * and we have to perform the flush.
+ */
+ if (idev->dead)
+ return NOTIFY_DONE;
+
+ netdev_notifier_info_init(&info, idev->dev);
return masq_device_event(this, event, &info);
}
diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c
index ed33299c5..95fe37c 100644
--- a/net/ipv4/netfilter/nft_reject_ipv4.c
+++ b/net/ipv4/netfilter/nft_reject_ipv4.c
@@ -32,6 +32,8 @@
case NFT_REJECT_TCP_RST:
nf_send_reset(pkt->skb, pkt->ops->hooknum);
break;
+ default:
+ break;
}
data[NFT_REG_VERDICT].verdict = NF_DROP;
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index f65379c..f6d58b0 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -154,17 +154,18 @@
void ping_unhash(struct sock *sk)
{
struct inet_sock *isk = inet_sk(sk);
+
pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
+ write_lock_bh(&ping_table.lock);
if (sk_hashed(sk)) {
- write_lock_bh(&ping_table.lock);
hlist_nulls_del(&sk->sk_nulls_node);
sk_nulls_node_init(&sk->sk_nulls_node);
sock_put(sk);
isk->inet_num = 0;
isk->inet_sport = 0;
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
- write_unlock_bh(&ping_table.lock);
}
+ write_unlock_bh(&ping_table.lock);
}
EXPORT_SYMBOL_GPL(ping_unhash);
@@ -644,6 +645,8 @@
{
struct sk_buff *skb = skb_peek(&sk->sk_write_queue);
+ if (!skb)
+ return 0;
pfh->wcheck = csum_partial((char *)&pfh->icmph,
sizeof(struct icmphdr), pfh->wcheck);
pfh->icmph.checksum = csum_fold(pfh->wcheck);
@@ -790,7 +793,7 @@
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE, sk->sk_protocol,
inet_sk_flowi_flags(sk), faddr, saddr, 0, 0,
- sock_i_uid(sk));
+ sk->sk_uid);
security_sk_classify_flow(sk, flowi4_to_flowi(&fl4));
rt = ip_route_output_flow(net, &fl4, sk);
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 6167b55..e5fae6b 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -582,8 +582,7 @@
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk) |
(inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
- daddr, saddr, 0, 0,
- sock_i_uid(sk));
+ daddr, saddr, 0, 0, sk->sk_uid);
if (!inet->hdrincl) {
err = raw_probe_proto_opt(&fl4, msg);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index f5174c8..d77c54a 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -499,7 +499,8 @@
}
EXPORT_SYMBOL(__ip_select_ident);
-static void __build_flow_key(struct flowi4 *fl4, struct sock *sk,
+static void __build_flow_key(const struct net *net, struct flowi4 *fl4,
+ const struct sock *sk,
const struct iphdr *iph,
int oif, u8 tos,
u8 prot, u32 mark, int flow_flags)
@@ -516,22 +517,23 @@
RT_SCOPE_UNIVERSE, prot,
flow_flags,
iph->daddr, iph->saddr, 0, 0,
- sk ? sock_i_uid(sk) : GLOBAL_ROOT_UID);
+ sock_net_uid(net, sk));
}
static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb,
- struct sock *sk)
+ const struct sock *sk)
{
+ const struct net *net = dev_net(skb->dev);
const struct iphdr *iph = ip_hdr(skb);
int oif = skb->dev->ifindex;
u8 tos = RT_TOS(iph->tos);
u8 prot = iph->protocol;
u32 mark = skb->mark;
- __build_flow_key(fl4, sk, iph, oif, tos, prot, mark, 0);
+ __build_flow_key(net, fl4, sk, iph, oif, tos, prot, mark, 0);
}
-static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk)
+static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk)
{
const struct inet_sock *inet = inet_sk(sk);
const struct ip_options_rcu *inet_opt;
@@ -545,12 +547,11 @@
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk),
- daddr, inet->inet_saddr, 0, 0,
- sock_i_uid(sk));
+ daddr, inet->inet_saddr, 0, 0, sk->sk_uid);
rcu_read_unlock();
}
-static void ip_rt_build_flow_key(struct flowi4 *fl4, struct sock *sk,
+static void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk,
const struct sk_buff *skb)
{
if (skb)
@@ -794,7 +795,7 @@
rt = (struct rtable *) dst;
- __build_flow_key(&fl4, sk, iph, oif, tos, prot, mark, 0);
+ __build_flow_key(sock_net(sk), &fl4, sk, iph, oif, tos, prot, mark, 0);
__ip_do_redirect(rt, skb, &fl4, true);
}
@@ -1009,7 +1010,7 @@
if (!mark)
mark = IP4_REPLY_MARK(net, skb->mark);
- __build_flow_key(&fl4, NULL, iph, oif,
+ __build_flow_key(net, &fl4, NULL, iph, oif,
RT_TOS(iph->tos), protocol, mark, flow_flags);
rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
@@ -1025,7 +1026,7 @@
struct flowi4 fl4;
struct rtable *rt;
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+ __build_flow_key(sock_net(sk), &fl4, sk, iph, 0, 0, 0, 0, 0);
if (!fl4.flowi4_mark)
fl4.flowi4_mark = IP4_REPLY_MARK(sock_net(sk), skb->mark);
@@ -1044,6 +1045,7 @@
struct rtable *rt;
struct dst_entry *odst = NULL;
bool new = false;
+ struct net *net = sock_net(sk);
bh_lock_sock(sk);
@@ -1057,7 +1059,7 @@
goto out;
}
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+ __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0);
rt = (struct rtable *)odst;
if (odst->obsolete && odst->ops->check(odst, 0) == NULL) {
@@ -1097,7 +1099,7 @@
struct flowi4 fl4;
struct rtable *rt;
- __build_flow_key(&fl4, NULL, iph, oif,
+ __build_flow_key(net, &fl4, NULL, iph, oif,
RT_TOS(iph->tos), protocol, mark, flow_flags);
rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
@@ -1112,9 +1114,10 @@
const struct iphdr *iph = (const struct iphdr *) skb->data;
struct flowi4 fl4;
struct rtable *rt;
+ struct net *net = sock_net(sk);
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
- rt = __ip_route_output_key(sock_net(sk), &fl4);
+ __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0);
+ rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
__ip_do_redirect(rt, skb, &fl4, false);
ip_rt_put(rt);
@@ -1837,6 +1840,7 @@
{
int res;
+ tos &= IPTOS_RT_MASK;
rcu_read_lock();
/* Multicast recognition logic is moved from route cache to here.
@@ -1926,6 +1930,18 @@
*/
if (fi && res->prefixlen < 4)
fi = NULL;
+ } else if ((type == RTN_LOCAL) && (orig_oif != 0) &&
+ (orig_oif != dev_out->ifindex)) {
+ /* For local routes that require a particular output interface
+ * we do not want to cache the result. Caching the result
+ * causes incorrect behaviour when there are multiple source
+ * addresses on the interface, the end result being that if the
+ * intended recipient is waiting on that interface for the
+ * packet he won't receive it because it will be delivered on
+ * the loopback interface and the IP_PKTINFO ipi_ifindex will
+ * be set to the loopback interface as well.
+ */
+ fi = NULL;
}
fnhe = NULL;
@@ -2368,7 +2384,8 @@
IPV4_DEVCONF_ALL(net, MC_FORWARDING)) {
int err = ipmr_get_route(net, skb,
fl4->saddr, fl4->daddr,
- r, nowait);
+ r, nowait, portid);
+
if (err <= 0) {
if (!nowait) {
if (err == 0)
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 088b6ab..8940e73 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -336,9 +336,8 @@
flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark,
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP,
inet_sk_flowi_flags(sk),
- (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr,
- ireq->ir_loc_addr, th->source, th->dest,
- sock_i_uid(sk));
+ opt->srr ? opt->faddr : ireq->ir_rmt_addr,
+ ireq->ir_loc_addr, th->source, th->dest, sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(&fl4));
rt = ip_route_output_key(sock_net(sk), &fl4);
if (IS_ERR(rt)) {
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 06d22e4..03b66b1 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -107,10 +107,10 @@
kgid_t *data = table->data;
struct net *net =
container_of(table->data, struct net, ipv4.ping_group_range.range);
- write_seqlock(&net->ipv4.ip_local_ports.lock);
+ write_seqlock_bh(&net->ipv4.ip_local_ports.lock);
data[0] = low;
data[1] = high;
- write_sequnlock(&net->ipv4.ip_local_ports.lock);
+ write_sequnlock_bh(&net->ipv4.ip_local_ports.lock);
}
/* Validate changes from /proc interface. */
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 9fe51f8..331cf94 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -955,7 +955,7 @@
i = skb_shinfo(skb)->nr_frags;
can_coalesce = skb_can_coalesce(skb, i, page, offset);
- if (!can_coalesce && i >= MAX_SKB_FRAGS) {
+ if (!can_coalesce && i >= sysctl_max_skb_frags) {
tcp_mark_push(tp, skb);
goto new_segment;
}
@@ -1241,7 +1241,7 @@
if (!skb_can_coalesce(skb, i, pfrag->page,
pfrag->offset)) {
- if (i == MAX_SKB_FRAGS || !sg) {
+ if (i == sysctl_max_skb_frags || !sg) {
tcp_mark_push(tp, skb);
goto new_segment;
}
diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c
index 20de011..9332ec1 100644
--- a/net/ipv4/tcp_cubic.c
+++ b/net/ipv4/tcp_cubic.c
@@ -154,6 +154,27 @@
tcp_sk(sk)->snd_ssthresh = initial_ssthresh;
}
+static void bictcp_cwnd_event(struct sock *sk, enum tcp_ca_event event)
+{
+ if (event == CA_EVENT_TX_START) {
+ struct bictcp *ca = inet_csk_ca(sk);
+ u32 now = tcp_time_stamp;
+ s32 delta;
+
+ delta = now - tcp_sk(sk)->lsndtime;
+
+ /* We were application limited (idle) for a while.
+ * Shift epoch_start to keep cwnd growth to cubic curve.
+ */
+ if (ca->epoch_start && delta > 0) {
+ ca->epoch_start += delta;
+ if (after(ca->epoch_start, now))
+ ca->epoch_start = now;
+ }
+ return;
+ }
+}
+
/* calculate the cubic root of x using a table lookup followed by one
* Newton-Raphson iteration.
* Avg err ~= 0.195%
@@ -440,6 +461,7 @@
.cong_avoid = bictcp_cong_avoid,
.set_state = bictcp_state,
.undo_cwnd = bictcp_undo_cwnd,
+ .cwnd_event = bictcp_cwnd_event,
.pkts_acked = bictcp_acked,
.owner = THIS_MODULE,
.name = "cubic",
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index ace8bb5..75f4640 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2361,10 +2361,9 @@
}
#if IS_ENABLED(CONFIG_IPV6)
else if (sk->sk_family == AF_INET6) {
- struct ipv6_pinfo *np = inet6_sk(sk);
pr_debug("Undo %s %pI6/%u c%u l%u ss%u/%u p%u\n",
msg,
- &np->daddr, ntohs(inet->inet_dport),
+ &sk->sk_v6_daddr, ntohs(inet->inet_dport),
tp->snd_cwnd, tcp_left_out(tp),
tp->snd_ssthresh, tp->prior_ssthresh,
tp->packets_out);
@@ -3325,8 +3324,7 @@
/* unprotected vars, we dont care of overwrites */
static u32 challenge_timestamp;
static unsigned int challenge_count;
- u32 now = jiffies / HZ;
- u32 count;
+ u32 count, now = jiffies / HZ;
if (now != challenge_timestamp) {
u32 half = (sysctl_tcp_challenge_ack_limit + 1) >> 1;
@@ -5297,6 +5295,7 @@
struct inet_connection_sock *icsk = inet_csk(sk);
tcp_set_state(sk, TCP_ESTABLISHED);
+ icsk->icsk_ack.lrcvtime = tcp_time_stamp;
if (skb != NULL) {
icsk->icsk_af_ops->sk_rx_dst_set(sk, skb);
@@ -5500,7 +5499,6 @@
* to stand against the temptation 8) --ANK
*/
inet_csk_schedule_ack(sk);
- icsk->icsk_ack.lrcvtime = tcp_time_stamp;
tcp_enter_quickack_mode(sk);
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
TCP_DELACK_MAX, TCP_RTO_MAX);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 81c3261..214a50e 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -271,10 +271,13 @@
*/
void tcp_v4_mtu_reduced(struct sock *sk)
{
- struct dst_entry *dst;
struct inet_sock *inet = inet_sk(sk);
- u32 mtu = tcp_sk(sk)->mtu_info;
+ struct dst_entry *dst;
+ u32 mtu;
+ if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))
+ return;
+ mtu = tcp_sk(sk)->mtu_info;
dst = inet_csk_update_pmtu(sk, mtu);
if (!dst)
return;
@@ -386,7 +389,8 @@
switch (type) {
case ICMP_REDIRECT:
- do_redirect(icmp_skb, sk);
+ if (!sock_owned_by_user(sk))
+ do_redirect(icmp_skb, sk);
goto out;
case ICMP_SOURCE_QUENCH:
/* Just silently ignore these. */
@@ -683,6 +687,7 @@
arg.bound_dev_if = sk->sk_bound_dev_if;
arg.tos = ip_hdr(skb)->tos;
+ arg.uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL);
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, &TCP_SKB_CB(skb)->header.h4.opt,
ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
@@ -704,7 +709,8 @@
outside socket context is ugly, certainly. What can I do?
*/
-static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
+static void tcp_v4_send_ack(const struct sock *sk, struct sk_buff *skb,
+ u32 seq, u32 ack,
u32 win, u32 tsval, u32 tsecr, int oif,
struct tcp_md5sig_key *key,
int reply_flags, u8 tos)
@@ -719,7 +725,7 @@
];
} rep;
struct ip_reply_arg arg;
- struct net *net = dev_net(skb_dst(skb)->dev);
+ struct net *net = sock_net(sk);
memset(&rep.th, 0, sizeof(struct tcphdr));
memset(&arg, 0, sizeof(arg));
@@ -768,6 +774,7 @@
if (oif)
arg.bound_dev_if = oif;
arg.tos = tos;
+ arg.uid = sock_net_uid(net, sk_fullsock(sk) ? sk : NULL);
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, &TCP_SKB_CB(skb)->header.h4.opt,
ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
@@ -781,7 +788,7 @@
struct inet_timewait_sock *tw = inet_twsk(sk);
struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
- tcp_v4_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+ tcp_v4_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
tcp_time_stamp + tcptw->tw_ts_offset,
tcptw->tw_ts_recent,
@@ -800,7 +807,7 @@
/* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
* sk->sk_state == TCP_SYN_RECV -> for Fast Open.
*/
- tcp_v4_send_ack(skb, (sk->sk_state == TCP_LISTEN) ?
+ tcp_v4_send_ack(sk, skb, (sk->sk_state == TCP_LISTEN) ?
tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt,
tcp_rsk(req)->rcv_nxt, req->rcv_wnd,
tcp_time_stamp,
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index ed9c9a9..2e423af 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -228,7 +228,7 @@
struct dst_entry *dst)
{
struct tcp_metrics_block *tm;
- struct inetpeer_addr saddr, daddr;
+ struct inetpeer_addr uninitialized_var(saddr), uninitialized_var(daddr);
unsigned int hash;
struct net *net;
@@ -550,7 +550,7 @@
*/
if (crtt > tp->srtt_us) {
/* Set RTO like tcp_rtt_estimator(), but from cached RTT. */
- crtt /= 8 * USEC_PER_MSEC;
+ crtt /= 8 * USEC_PER_SEC / HZ;
inet_csk(sk)->icsk_rto = crtt + max(2 * crtt, tcp_rto_min(sk));
} else if (tp->srtt_us == 0) {
/* RFC6298: 5.7 We've failed to get a valid RTT sample from
@@ -968,7 +968,7 @@
static int tcp_metrics_nl_cmd_get(struct sk_buff *skb, struct genl_info *info)
{
struct tcp_metrics_block *tm;
- struct inetpeer_addr saddr, daddr;
+ struct inetpeer_addr uninitialized_var(saddr), daddr;
unsigned int hash;
struct sk_buff *msg;
struct net *net = genl_info_net(info);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index c78aaca..482c177 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -413,6 +413,7 @@
newtp->srtt_us = 0;
newtp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT);
newicsk->icsk_rto = TCP_TIMEOUT_INIT;
+ newicsk->icsk_ack.lrcvtime = tcp_time_stamp;
newtp->packets_out = 0;
newtp->retrans_out = 0;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 9cb15b1..2b64c9f 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1869,12 +1869,14 @@
len = 0;
tcp_for_write_queue_from_safe(skb, next, sk) {
copy = min_t(int, skb->len, probe_size - len);
- if (nskb->ip_summed)
+ if (nskb->ip_summed) {
skb_copy_bits(skb, 0, skb_put(nskb, copy), copy);
- else
- nskb->csum = skb_copy_and_csum_bits(skb, 0,
- skb_put(nskb, copy),
- copy, nskb->csum);
+ } else {
+ __wsum csum = skb_copy_and_csum_bits(skb, 0,
+ skb_put(nskb, copy),
+ copy, 0);
+ nskb->csum = csum_block_add(nskb->csum, csum, len);
+ }
if (skb->len <= copy) {
/* We've eaten all the data from this skb.
@@ -2291,9 +2293,11 @@
int full_space = min_t(int, tp->window_clamp, allowed_space);
int window;
- if (mss > full_space)
+ if (unlikely(mss > full_space)) {
mss = full_space;
-
+ if (mss <= 0)
+ return 0;
+ }
if (free_space < (full_space >> 1)) {
icsk->icsk_ack.quick = 0;
@@ -2479,7 +2483,8 @@
* copying overhead: fragmentation, tunneling, mangling etc.
*/
if (atomic_read(&sk->sk_wmem_alloc) >
- min(sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), sk->sk_sndbuf))
+ min_t(u32, sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2),
+ sk->sk_sndbuf))
return -EAGAIN;
if (skb_still_in_host_queue(sk, skb))
@@ -2532,8 +2537,10 @@
*/
if (unlikely((NET_IP_ALIGN && ((unsigned long)skb->data & 3)) ||
skb_headroom(skb) >= 0xFFFF)) {
- struct sk_buff *nskb = __pskb_copy(skb, MAX_TCP_HEADER,
- GFP_ATOMIC);
+ struct sk_buff *nskb;
+
+ skb_mstamp_get(&skb->skb_mstamp);
+ nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC);
err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
-ENOBUFS;
} else {
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 342f64e..a46f68a 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -241,7 +241,8 @@
sk_mem_reclaim_partial(sk);
- if (sk->sk_state == TCP_CLOSE || !(icsk->icsk_ack.pending & ICSK_ACK_TIMER))
+ if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) ||
+ !(icsk->icsk_ack.pending & ICSK_ACK_TIMER))
goto out;
if (time_after(icsk->icsk_ack.timeout, jiffies)) {
@@ -520,7 +521,8 @@
struct inet_connection_sock *icsk = inet_csk(sk);
int event;
- if (sk->sk_state == TCP_CLOSE || !icsk->icsk_pending)
+ if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) ||
+ !icsk->icsk_pending)
goto out;
if (time_after(icsk->icsk_timeout, jiffies)) {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index a7716ce..6e793c5 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1013,7 +1013,7 @@
RT_SCOPE_UNIVERSE, sk->sk_protocol,
inet_sk_flowi_flags(sk),
faddr, saddr, dport, inet->inet_sport,
- sock_i_uid(sk));
+ sk->sk_uid);
security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
@@ -1973,10 +1973,14 @@
if (!in_dev)
return;
- ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr,
- iph->protocol);
- if (!ours)
- return;
+ /* we are supposed to accept bcast packets */
+ if (skb->pkt_type == PACKET_MULTICAST) {
+ ours = ip_check_mc_rcu(in_dev, iph->daddr, iph->saddr,
+ iph->protocol);
+ if (!ours)
+ return;
+ }
+
sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr,
uh->source, iph->saddr, dif);
} else if (skb->pkt_type == PACKET_HOST) {
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 6480cea..e6d05ae 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -266,14 +266,14 @@
unsigned int off = skb_gro_offset(skb);
int flush = 1;
- if (NAPI_GRO_CB(skb)->udp_mark ||
+ if (NAPI_GRO_CB(skb)->encap_mark ||
(skb->ip_summed != CHECKSUM_PARTIAL &&
NAPI_GRO_CB(skb)->csum_cnt == 0 &&
!NAPI_GRO_CB(skb)->csum_valid))
goto out;
- /* mark that this skb passed once through the udp gro layer */
- NAPI_GRO_CB(skb)->udp_mark = 1;
+ /* mark that this skb passed once through the tunnel gro layer */
+ NAPI_GRO_CB(skb)->encap_mark = 1;
rcu_read_lock();
uo_priv = rcu_dereference(udp_offload_base);
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index ebac64b..4bafe5d 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -107,6 +107,27 @@
return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
}
+static inline s32 rfc3315_s14_backoff_init(s32 irt)
+{
+ /* multiply 'initial retransmission time' by 0.9 .. 1.1 */
+ u64 tmp = (900000 + prandom_u32() % 200001) * (u64)irt;
+ do_div(tmp, 1000000);
+ return (s32)tmp;
+}
+
+static inline s32 rfc3315_s14_backoff_update(s32 rt, s32 mrt)
+{
+ /* multiply 'retransmission timeout' by 1.9 .. 2.1 */
+ u64 tmp = (1900000 + prandom_u32() % 200001) * (u64)rt;
+ do_div(tmp, 1000000);
+ if ((s32)tmp > mrt) {
+ /* multiply 'maximum retransmission time' by 0.9 .. 1.1 */
+ tmp = (900000 + prandom_u32() % 200001) * (u64)mrt;
+ do_div(tmp, 1000000);
+ }
+ return (s32)tmp;
+}
+
#ifdef CONFIG_SYSCTL
static int addrconf_sysctl_register(struct inet6_dev *idev);
static void addrconf_sysctl_unregister(struct inet6_dev *idev);
@@ -179,6 +200,7 @@
.dad_transmits = 1,
.rtr_solicits = MAX_RTR_SOLICITATIONS,
.rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
+ .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL,
.rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
.use_tempaddr = 0,
.temp_valid_lft = TEMP_VALID_LIFETIME,
@@ -193,6 +215,7 @@
.accept_ra_rtr_pref = 1,
.rtr_probe_interval = 60 * HZ,
#ifdef CONFIG_IPV6_ROUTE_INFO
+ .accept_ra_rt_info_min_plen = 0,
.accept_ra_rt_info_max_plen = 0,
#endif
#endif
@@ -219,6 +242,7 @@
.dad_transmits = 1,
.rtr_solicits = MAX_RTR_SOLICITATIONS,
.rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
+ .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL,
.rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
.use_tempaddr = 0,
.temp_valid_lft = TEMP_VALID_LIFETIME,
@@ -233,6 +257,7 @@
.accept_ra_rtr_pref = 1,
.rtr_probe_interval = 60 * HZ,
#ifdef CONFIG_IPV6_ROUTE_INFO
+ .accept_ra_rt_info_min_plen = 0,
.accept_ra_rt_info_max_plen = 0,
#endif
#endif
@@ -1760,6 +1785,7 @@
spin_unlock_bh(&ifp->state_lock);
addrconf_mod_dad_work(ifp, 0);
+ in6_ifa_put(ifp);
}
/* Join to solicited addr multicast group.
@@ -2804,7 +2830,7 @@
* lo device down, release this obsolete dst and
* reallocate a new router for ifa.
*/
- if (sp_ifa->rt->dst.obsolete > 0) {
+ if (!atomic_read(&sp_ifa->rt->rt6i_ref)) {
ip6_rt_put(sp_ifa->rt);
sp_ifa->rt = NULL;
} else {
@@ -3247,7 +3273,7 @@
if (idev->if_flags & IF_RA_RCVD)
goto out;
- if (idev->rs_probes++ < idev->cnf.rtr_solicits) {
+ if (idev->rs_probes++ < idev->cnf.rtr_solicits || idev->cnf.rtr_solicits < 0) {
write_unlock(&idev->lock);
if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
ndisc_send_rs(dev, &lladdr,
@@ -3256,11 +3282,13 @@
goto put;
write_lock(&idev->lock);
+ idev->rs_interval = rfc3315_s14_backoff_update(
+ idev->rs_interval, idev->cnf.rtr_solicit_max_interval);
/* The wait after the last probe can be shorter */
addrconf_mod_rs_timer(idev, (idev->rs_probes ==
idev->cnf.rtr_solicits) ?
idev->cnf.rtr_solicit_delay :
- idev->cnf.rtr_solicit_interval);
+ idev->rs_interval);
} else {
/*
* Note: we do not support deprecated "all on-link"
@@ -3399,6 +3427,7 @@
addrconf_dad_begin(ifp);
goto out;
} else if (action == DAD_ABORT) {
+ in6_ifa_hold(ifp);
addrconf_dad_stop(ifp, 1);
goto out;
}
@@ -3487,7 +3516,7 @@
send_mld = ifp->scope == IFA_LINK && ipv6_lonely_lladdr(ifp);
send_rs = send_mld &&
ipv6_accept_ra(ifp->idev) &&
- ifp->idev->cnf.rtr_solicits > 0 &&
+ ifp->idev->cnf.rtr_solicits != 0 &&
(dev->flags&IFF_LOOPBACK) == 0;
read_unlock_bh(&ifp->idev->lock);
@@ -3509,10 +3538,11 @@
write_lock_bh(&ifp->idev->lock);
spin_lock(&ifp->lock);
+ ifp->idev->rs_interval = rfc3315_s14_backoff_init(
+ ifp->idev->cnf.rtr_solicit_interval);
ifp->idev->rs_probes = 1;
ifp->idev->if_flags |= IF_RS_SENT;
- addrconf_mod_rs_timer(ifp->idev,
- ifp->idev->cnf.rtr_solicit_interval);
+ addrconf_mod_rs_timer(ifp->idev, ifp->idev->rs_interval);
spin_unlock(&ifp->lock);
write_unlock_bh(&ifp->idev->lock);
}
@@ -4426,6 +4456,8 @@
array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits;
array[DEVCONF_RTR_SOLICIT_INTERVAL] =
jiffies_to_msecs(cnf->rtr_solicit_interval);
+ array[DEVCONF_RTR_SOLICIT_MAX_INTERVAL] =
+ jiffies_to_msecs(cnf->rtr_solicit_max_interval);
array[DEVCONF_RTR_SOLICIT_DELAY] =
jiffies_to_msecs(cnf->rtr_solicit_delay);
array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version;
@@ -4446,6 +4478,7 @@
array[DEVCONF_RTR_PROBE_INTERVAL] =
jiffies_to_msecs(cnf->rtr_probe_interval);
#ifdef CONFIG_IPV6_ROUTE_INFO
+ array[DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN] = cnf->accept_ra_rt_info_min_plen;
array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen;
#endif
#endif
@@ -4617,7 +4650,7 @@
return -EINVAL;
if (!ipv6_accept_ra(idev))
return -EINVAL;
- if (idev->cnf.rtr_solicits <= 0)
+ if (idev->cnf.rtr_solicits == 0)
return -EINVAL;
write_lock_bh(&idev->lock);
@@ -4642,8 +4675,10 @@
if (update_rs) {
idev->if_flags |= IF_RS_SENT;
+ idev->rs_interval = rfc3315_s14_backoff_init(
+ idev->cnf.rtr_solicit_interval);
idev->rs_probes = 1;
- addrconf_mod_rs_timer(idev, idev->cnf.rtr_solicit_interval);
+ addrconf_mod_rs_timer(idev, idev->rs_interval);
}
/* Well, that's kinda nasty ... */
@@ -5180,6 +5215,13 @@
.proc_handler = proc_dointvec_jiffies,
},
{
+ .procname = "router_solicitation_max_interval",
+ .data = &ipv6_devconf.rtr_solicit_max_interval,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
+ {
.procname = "router_solicitation_delay",
.data = &ipv6_devconf.rtr_solicit_delay,
.maxlen = sizeof(int),
@@ -5282,6 +5324,13 @@
},
#ifdef CONFIG_IPV6_ROUTE_INFO
{
+ .procname = "accept_ra_rt_info_min_plen",
+ .data = &ipv6_devconf.accept_ra_rt_info_min_plen,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
.procname = "accept_ra_rt_info_max_plen",
.data = &ipv6_devconf.accept_ra_rt_info_max_plen,
.maxlen = sizeof(int),
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index a5d503e..ad965dd 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -692,7 +692,7 @@
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = inet->inet_dport;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
rcu_read_lock();
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index bb93d3b..bacb85c 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -660,9 +660,10 @@
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 2742564..aa733ae 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -161,7 +161,7 @@
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = inet->inet_dport;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST))
fl6.flowi6_oif = np->mcast_oif;
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 0958354..6c51fbd 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -439,9 +439,10 @@
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c
index 958a187..462ccfe 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -260,7 +260,11 @@
return -EINVAL;
}
}
- return -ENOENT;
+ if (!found)
+ return -ENOENT;
+ if (fragoff)
+ *fragoff = _frag_off;
+ break;
}
hdrlen = 8;
} else if (nexthdr == NEXTHDR_AUTH) {
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 1415f13..439aaaf 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -91,9 +91,10 @@
struct net *net = dev_net(skb->dev);
if (type == ICMPV6_PKT_TOOBIG)
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
else if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
if (!(type & ICMPV6_INFOMSG_MASK))
if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST)
@@ -475,6 +476,7 @@
fl6.flowi6_oif = iif;
fl6.fl6_icmp_type = type;
fl6.fl6_icmp_code = code;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
sk = icmpv6_xmit_lock(net);
@@ -578,6 +580,7 @@
fl6.flowi6_oif = skb->dev->ifindex;
fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
fl6.flowi6_mark = mark;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
sk = icmpv6_xmit_lock(net);
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index ca655c9..fd119da3 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -85,7 +85,7 @@
fl6->flowi6_mark = ireq->ir_mark;
fl6->fl6_dport = ireq->ir_rmt_port;
fl6->fl6_sport = htons(ireq->ir_num);
- fl6->flowi6_uid = sock_i_uid(sk);
+ fl6->flowi6_uid = sk->sk_uid;
security_req_classify_flow(req, flowi6_to_flowi(fl6));
dst = ip6_dst_lookup_flow(sk, fl6, final_p);
@@ -209,7 +209,7 @@
fl6->flowi6_mark = sk->sk_mark;
fl6->fl6_sport = inet->inet_sport;
fl6->fl6_dport = inet->inet_dport;
- fl6->flowi6_uid = sock_i_uid(sk);
+ fl6->flowi6_uid = sk->sk_uid;
security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
rcu_read_lock();
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index a73db91..140f44b 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -800,6 +800,8 @@
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
if (err != 0) {
/* XXX: send ICMP error even if DF is not set. */
@@ -850,6 +852,8 @@
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
if (err != 0) {
if (err == -EMSGSIZE)
@@ -892,7 +896,6 @@
encap_limit = t->parms.encap_limit;
memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
- fl6.flowi6_proto = skb->protocol;
err = ip6gre_xmit2(skb, dev, 0, &fl6, encap_limit, &mtu);
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 01e12d0..219b9d24b 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -258,6 +258,19 @@
return pp;
}
+static struct sk_buff **sit_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ if (NAPI_GRO_CB(skb)->encap_mark) {
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
+ }
+
+ NAPI_GRO_CB(skb)->encap_mark = 1;
+
+ return ipv6_gro_receive(head, skb);
+}
+
static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
{
const struct net_offload *ops;
@@ -292,7 +305,7 @@
static const struct net_offload sit_offload = {
.callbacks = {
.gso_segment = ipv6_gso_segment,
- .gro_receive = ipv6_gro_receive,
+ .gro_receive = sit_gro_receive,
.gro_complete = ipv6_gro_complete,
},
};
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 9cb94cf..6d54990 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -273,12 +273,12 @@
t = netdev_priv(dev);
+ dev->rtnl_link_ops = &ip6_link_ops;
err = register_netdevice(dev);
if (err < 0)
goto out;
strcpy(t->parms.name, dev->name);
- dev->rtnl_link_ops = &ip6_link_ops;
dev_hold(dev);
ip6_tnl_link(ip6n, t);
@@ -407,18 +407,19 @@
__u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw)
{
- const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw;
- __u8 nexthdr = ipv6h->nexthdr;
- __u16 off = sizeof(*ipv6h);
+ const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)raw;
+ unsigned int nhoff = raw - skb->data;
+ unsigned int off = nhoff + sizeof(*ipv6h);
+ u8 next, nexthdr = ipv6h->nexthdr;
while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) {
- __u16 optlen = 0;
struct ipv6_opt_hdr *hdr;
- if (raw + off + sizeof(*hdr) > skb->data &&
- !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr)))
+ u16 optlen;
+
+ if (!pskb_may_pull(skb, off + sizeof(*hdr)))
break;
- hdr = (struct ipv6_opt_hdr *) (raw + off);
+ hdr = (struct ipv6_opt_hdr *)(skb->data + off);
if (nexthdr == NEXTHDR_FRAGMENT) {
struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr;
if (frag_hdr->frag_off)
@@ -429,20 +430,29 @@
} else {
optlen = ipv6_optlen(hdr);
}
+ /* cache hdr->nexthdr, since pskb_may_pull() might
+ * invalidate hdr
+ */
+ next = hdr->nexthdr;
if (nexthdr == NEXTHDR_DEST) {
- __u16 i = off + 2;
+ u16 i = 2;
+
+ /* Remember : hdr is no longer valid at this point. */
+ if (!pskb_may_pull(skb, off + optlen))
+ break;
+
while (1) {
struct ipv6_tlv_tnl_enc_lim *tel;
/* No more room for encapsulation limit */
- if (i + sizeof (*tel) > off + optlen)
+ if (i + sizeof(*tel) > optlen)
break;
- tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i];
+ tel = (struct ipv6_tlv_tnl_enc_lim *)(skb->data + off + i);
/* return index of option if found and valid */
if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT &&
tel->length == 1)
- return i;
+ return i + off - nhoff;
/* else jump to next option */
if (tel->type)
i += tel->length + 2;
@@ -450,7 +460,7 @@
i++;
}
}
- nexthdr = hdr->nexthdr;
+ nexthdr = next;
off += optlen;
}
return 0;
@@ -1087,6 +1097,8 @@
memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
fl6.flowi6_proto = IPPROTO_IPIP;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
dsfield = ipv4_get_dsfield(iph);
if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
@@ -1138,6 +1150,7 @@
memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6));
fl6.flowi6_proto = IPPROTO_IPV6;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
dsfield = ipv6_get_dsfield(ipv6h);
if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index 9c4a1f5..754c41c 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -560,9 +560,10 @@
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
@@ -645,6 +646,10 @@
u->link = p->link;
u->i_key = p->i_key;
u->o_key = p->o_key;
+ if (u->i_key)
+ u->i_flags |= GRE_KEY;
+ if (u->o_key)
+ u->o_flags |= GRE_KEY;
u->proto = p->proto;
memcpy(u->name, p->name, sizeof(u->name));
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 4ca7cdd..4b7e2fa 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -776,7 +776,8 @@
* Delete a VIF entry
*/
-static int mif6_delete(struct mr6_table *mrt, int vifi, struct list_head *head)
+static int mif6_delete(struct mr6_table *mrt, int vifi, int notify,
+ struct list_head *head)
{
struct mif_device *v;
struct net_device *dev;
@@ -822,7 +823,7 @@
dev->ifindex, &in6_dev->cnf);
}
- if (v->flags & MIFF_REGISTER)
+ if ((v->flags & MIFF_REGISTER) && !notify)
unregister_netdevice_queue(dev, head);
dev_put(dev);
@@ -1331,7 +1332,6 @@
struct mr6_table *mrt;
struct mif_device *v;
int ct;
- LIST_HEAD(list);
if (event != NETDEV_UNREGISTER)
return NOTIFY_DONE;
@@ -1340,10 +1340,9 @@
v = &mrt->vif6_table[0];
for (ct = 0; ct < mrt->maxvif; ct++, v++) {
if (v->dev == dev)
- mif6_delete(mrt, ct, &list);
+ mif6_delete(mrt, ct, 1, NULL);
}
}
- unregister_netdevice_many(&list);
return NOTIFY_DONE;
}
@@ -1552,7 +1551,7 @@
for (i = 0; i < mrt->maxvif; i++) {
if (!all && (mrt->vif6_table[i].flags & VIFF_STATIC))
continue;
- mif6_delete(mrt, i, &list);
+ mif6_delete(mrt, i, 0, &list);
}
unregister_netdevice_many(&list);
@@ -1705,7 +1704,7 @@
if (copy_from_user(&mifi, optval, sizeof(mifi_t)))
return -EFAULT;
rtnl_lock();
- ret = mif6_delete(mrt, mifi, NULL);
+ ret = mif6_delete(mrt, mifi, 0, NULL);
rtnl_unlock();
return ret;
@@ -2276,8 +2275,8 @@
return 1;
}
-int ip6mr_get_route(struct net *net,
- struct sk_buff *skb, struct rtmsg *rtm, int nowait)
+int ip6mr_get_route(struct net *net, struct sk_buff *skb, struct rtmsg *rtm,
+ int nowait, u32 portid)
{
int err;
struct mr6_table *mrt;
@@ -2322,6 +2321,7 @@
return -ENOMEM;
}
+ NETLINK_CB(skb2).portid = portid;
skb_reset_transport_header(skb2);
skb_put(skb2, sizeof(struct ipv6hdr));
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index b247bac..54d165b 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -74,9 +74,10 @@
return 0;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, skb->dev->ifindex, 0);
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
return 0;
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 1c34d75..a3033cd 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1325,6 +1325,8 @@
if (ri->prefix_len == 0 &&
!in6_dev->cnf.accept_ra_defrtr)
continue;
+ if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen)
+ continue;
if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
continue;
rt6_route_rcv(skb->dev, (u8 *)p, (p->nd_opt_len) << 3,
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index d38e6a8..e48d26b 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -27,6 +27,7 @@
struct flowi6 fl6 = {
.flowi6_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0,
.flowi6_mark = skb->mark,
+ .flowi6_uid = sock_net_uid(net, skb->sk),
.daddr = iph->daddr,
.saddr = iph->saddr,
};
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index bc0615b1..cb3cc2a 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -453,7 +453,8 @@
there are loops. Puts hook bitmask in comefrom. */
static int
mark_source_chains(const struct xt_table_info *newinfo,
- unsigned int valid_hooks, void *entry0)
+ unsigned int valid_hooks, void *entry0,
+ unsigned int *offsets)
{
unsigned int hook;
@@ -526,6 +527,8 @@
size = e->next_offset;
e = (struct ip6t_entry *)
(entry0 + pos + size);
+ if (pos + size >= newinfo->size)
+ return 0;
e->counters.pcnt = pos;
pos += size;
} else {
@@ -544,9 +547,16 @@
/* This a jump; chase it. */
duprintf("Jump rule %u -> %u\n",
pos, newpos);
+ if (!xt_find_jump_offset(offsets, newpos,
+ newinfo->number))
+ return 0;
+ e = (struct ip6t_entry *)
+ (entry0 + newpos);
} else {
/* ... this is a fallthru */
newpos = pos + e->next_offset;
+ if (newpos >= newinfo->size)
+ return 0;
}
e = (struct ip6t_entry *)
(entry0 + newpos);
@@ -573,25 +583,6 @@
module_put(par.match->me);
}
-static int
-check_entry(const struct ip6t_entry *e)
-{
- const struct xt_entry_target *t;
-
- if (!ip6_checkentry(&e->ipv6))
- return -EINVAL;
-
- if (e->target_offset + sizeof(struct xt_entry_target) >
- e->next_offset)
- return -EINVAL;
-
- t = ip6t_get_target_c(e);
- if (e->target_offset + t->u.target_size > e->next_offset)
- return -EINVAL;
-
- return 0;
-}
-
static int check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
{
const struct ip6t_ip6 *ipv6 = par->entryinfo;
@@ -749,7 +740,11 @@
return -EINVAL;
}
- err = check_entry(e);
+ if (!ip6_checkentry(&e->ipv6))
+ return -EINVAL;
+
+ err = xt_check_entry_offsets(e, e->elems, e->target_offset,
+ e->next_offset);
if (err)
return err;
@@ -803,6 +798,7 @@
const struct ip6t_replace *repl)
{
struct ip6t_entry *iter;
+ unsigned int *offsets;
unsigned int i;
int ret = 0;
@@ -816,6 +812,9 @@
}
duprintf("translate_table: size %u\n", newinfo->size);
+ offsets = xt_alloc_entry_offsets(newinfo->number);
+ if (!offsets)
+ return -ENOMEM;
i = 0;
/* Walk through entries, checking offsets. */
xt_entry_foreach(iter, entry0, newinfo->size) {
@@ -825,17 +824,20 @@
repl->underflow,
repl->valid_hooks);
if (ret != 0)
- return ret;
+ goto out_free;
+ if (i < repl->num_entries)
+ offsets[i] = (void *)iter - entry0;
++i;
if (strcmp(ip6t_get_target(iter)->u.user.name,
XT_ERROR_TARGET) == 0)
++newinfo->stacksize;
}
+ ret = -EINVAL;
if (i != repl->num_entries) {
duprintf("translate_table: %u not %u entries\n",
i, repl->num_entries);
- return -EINVAL;
+ goto out_free;
}
/* Check hooks all assigned */
@@ -846,17 +848,20 @@
if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
duprintf("Invalid hook entry %u %u\n",
i, repl->hook_entry[i]);
- return -EINVAL;
+ goto out_free;
}
if (newinfo->underflow[i] == 0xFFFFFFFF) {
duprintf("Invalid underflow %u %u\n",
i, repl->underflow[i]);
- return -EINVAL;
+ goto out_free;
}
}
- if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
- return -ELOOP;
+ if (!mark_source_chains(newinfo, repl->valid_hooks, entry0, offsets)) {
+ ret = -ELOOP;
+ goto out_free;
+ }
+ kvfree(offsets);
/* Finally, each sanity check must pass */
i = 0;
@@ -883,6 +888,9 @@
}
return ret;
+ out_free:
+ kvfree(offsets);
+ return ret;
}
static void
@@ -1313,56 +1321,17 @@
unsigned int i, curcpu;
struct xt_counters_info tmp;
struct xt_counters *paddc;
- unsigned int num_counters;
- char *name;
- int size;
- void *ptmp;
struct xt_table *t;
const struct xt_table_info *private;
int ret = 0;
const void *loc_cpu_entry;
struct ip6t_entry *iter;
unsigned int addend;
-#ifdef CONFIG_COMPAT
- struct compat_xt_counters_info compat_tmp;
- if (compat) {
- ptmp = &compat_tmp;
- size = sizeof(struct compat_xt_counters_info);
- } else
-#endif
- {
- ptmp = &tmp;
- size = sizeof(struct xt_counters_info);
- }
-
- if (copy_from_user(ptmp, user, size) != 0)
- return -EFAULT;
-
-#ifdef CONFIG_COMPAT
- if (compat) {
- num_counters = compat_tmp.num_counters;
- name = compat_tmp.name;
- } else
-#endif
- {
- num_counters = tmp.num_counters;
- name = tmp.name;
- }
-
- if (len != size + num_counters * sizeof(struct xt_counters))
- return -EINVAL;
-
- paddc = vmalloc(len - size);
- if (!paddc)
- return -ENOMEM;
-
- if (copy_from_user(paddc, user + size, len - size) != 0) {
- ret = -EFAULT;
- goto free;
- }
-
- t = xt_find_table_lock(net, AF_INET6, name);
+ paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
+ if (IS_ERR(paddc))
+ return PTR_ERR(paddc);
+ t = xt_find_table_lock(net, AF_INET6, tmp.name);
if (IS_ERR_OR_NULL(t)) {
ret = t ? PTR_ERR(t) : -ENOENT;
goto free;
@@ -1371,7 +1340,7 @@
local_bh_disable();
private = t->private;
- if (private->number != num_counters) {
+ if (private->number != tmp.num_counters) {
ret = -EINVAL;
goto unlock_up_free;
}
@@ -1451,7 +1420,6 @@
static int
compat_find_calc_match(struct xt_entry_match *m,
- const char *name,
const struct ip6t_ip6 *ipv6,
unsigned int hookmask,
int *size)
@@ -1487,17 +1455,14 @@
struct xt_table_info *newinfo,
unsigned int *size,
const unsigned char *base,
- const unsigned char *limit,
- const unsigned int *hook_entries,
- const unsigned int *underflows,
- const char *name)
+ const unsigned char *limit)
{
struct xt_entry_match *ematch;
struct xt_entry_target *t;
struct xt_target *target;
unsigned int entry_offset;
unsigned int j;
- int ret, off, h;
+ int ret, off;
duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
@@ -1514,8 +1479,11 @@
return -EINVAL;
}
- /* For purposes of check_entry casting the compat entry is fine */
- ret = check_entry((struct ip6t_entry *)e);
+ if (!ip6_checkentry(&e->ipv6))
+ return -EINVAL;
+
+ ret = xt_compat_check_entry_offsets(e, e->elems,
+ e->target_offset, e->next_offset);
if (ret)
return ret;
@@ -1523,8 +1491,8 @@
entry_offset = (void *)e - (void *)base;
j = 0;
xt_ematch_foreach(ematch, e) {
- ret = compat_find_calc_match(ematch, name,
- &e->ipv6, e->comefrom, &off);
+ ret = compat_find_calc_match(ematch, &e->ipv6, e->comefrom,
+ &off);
if (ret != 0)
goto release_matches;
++j;
@@ -1547,17 +1515,6 @@
if (ret)
goto out;
- /* Check hooks & underflows */
- for (h = 0; h < NF_INET_NUMHOOKS; h++) {
- if ((unsigned char *)e - base == hook_entries[h])
- newinfo->hook_entry[h] = hook_entries[h];
- if ((unsigned char *)e - base == underflows[h])
- newinfo->underflow[h] = underflows[h];
- }
-
- /* Clear counters and comefrom */
- memset(&e->counters, 0, sizeof(e->counters));
- e->comefrom = 0;
return 0;
out:
@@ -1571,18 +1528,17 @@
return ret;
}
-static int
+static void
compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
- unsigned int *size, const char *name,
+ unsigned int *size,
struct xt_table_info *newinfo, unsigned char *base)
{
struct xt_entry_target *t;
struct ip6t_entry *de;
unsigned int origsize;
- int ret, h;
+ int h;
struct xt_entry_match *ematch;
- ret = 0;
origsize = *size;
de = (struct ip6t_entry *)*dstptr;
memcpy(de, e, sizeof(struct ip6t_entry));
@@ -1591,11 +1547,9 @@
*dstptr += sizeof(struct ip6t_entry);
*size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
- xt_ematch_foreach(ematch, e) {
- ret = xt_compat_match_from_user(ematch, dstptr, size);
- if (ret != 0)
- return ret;
- }
+ xt_ematch_foreach(ematch, e)
+ xt_compat_match_from_user(ematch, dstptr, size);
+
de->target_offset = e->target_offset - (origsize - *size);
t = compat_ip6t_get_target(e);
xt_compat_target_from_user(t, dstptr, size);
@@ -1607,182 +1561,83 @@
if ((unsigned char *)de - base < newinfo->underflow[h])
newinfo->underflow[h] -= origsize - *size;
}
- return ret;
-}
-
-static int compat_check_entry(struct ip6t_entry *e, struct net *net,
- const char *name)
-{
- unsigned int j;
- int ret = 0;
- struct xt_mtchk_param mtpar;
- struct xt_entry_match *ematch;
-
- j = 0;
- mtpar.net = net;
- mtpar.table = name;
- mtpar.entryinfo = &e->ipv6;
- mtpar.hook_mask = e->comefrom;
- mtpar.family = NFPROTO_IPV6;
- xt_ematch_foreach(ematch, e) {
- ret = check_match(ematch, &mtpar);
- if (ret != 0)
- goto cleanup_matches;
- ++j;
- }
-
- ret = check_target(e, net, name);
- if (ret)
- goto cleanup_matches;
- return 0;
-
- cleanup_matches:
- xt_ematch_foreach(ematch, e) {
- if (j-- == 0)
- break;
- cleanup_match(ematch, net);
- }
- return ret;
}
static int
translate_compat_table(struct net *net,
- const char *name,
- unsigned int valid_hooks,
struct xt_table_info **pinfo,
void **pentry0,
- unsigned int total_size,
- unsigned int number,
- unsigned int *hook_entries,
- unsigned int *underflows)
+ const struct compat_ip6t_replace *compatr)
{
unsigned int i, j;
struct xt_table_info *newinfo, *info;
void *pos, *entry0, *entry1;
struct compat_ip6t_entry *iter0;
- struct ip6t_entry *iter1;
+ struct ip6t_replace repl;
unsigned int size;
int ret = 0;
info = *pinfo;
entry0 = *pentry0;
- size = total_size;
- info->number = number;
-
- /* Init all hooks to impossible value. */
- for (i = 0; i < NF_INET_NUMHOOKS; i++) {
- info->hook_entry[i] = 0xFFFFFFFF;
- info->underflow[i] = 0xFFFFFFFF;
- }
+ size = compatr->size;
+ info->number = compatr->num_entries;
duprintf("translate_compat_table: size %u\n", info->size);
j = 0;
xt_compat_lock(AF_INET6);
- xt_compat_init_offsets(AF_INET6, number);
+ xt_compat_init_offsets(AF_INET6, compatr->num_entries);
/* Walk through entries, checking offsets. */
- xt_entry_foreach(iter0, entry0, total_size) {
+ xt_entry_foreach(iter0, entry0, compatr->size) {
ret = check_compat_entry_size_and_hooks(iter0, info, &size,
entry0,
- entry0 + total_size,
- hook_entries,
- underflows,
- name);
+ entry0 + compatr->size);
if (ret != 0)
goto out_unlock;
++j;
}
ret = -EINVAL;
- if (j != number) {
+ if (j != compatr->num_entries) {
duprintf("translate_compat_table: %u not %u entries\n",
- j, number);
+ j, compatr->num_entries);
goto out_unlock;
}
- /* Check hooks all assigned */
- for (i = 0; i < NF_INET_NUMHOOKS; i++) {
- /* Only hooks which are valid */
- if (!(valid_hooks & (1 << i)))
- continue;
- if (info->hook_entry[i] == 0xFFFFFFFF) {
- duprintf("Invalid hook entry %u %u\n",
- i, hook_entries[i]);
- goto out_unlock;
- }
- if (info->underflow[i] == 0xFFFFFFFF) {
- duprintf("Invalid underflow %u %u\n",
- i, underflows[i]);
- goto out_unlock;
- }
- }
-
ret = -ENOMEM;
newinfo = xt_alloc_table_info(size);
if (!newinfo)
goto out_unlock;
- newinfo->number = number;
+ newinfo->number = compatr->num_entries;
for (i = 0; i < NF_INET_NUMHOOKS; i++) {
- newinfo->hook_entry[i] = info->hook_entry[i];
- newinfo->underflow[i] = info->underflow[i];
+ newinfo->hook_entry[i] = compatr->hook_entry[i];
+ newinfo->underflow[i] = compatr->underflow[i];
}
entry1 = newinfo->entries[raw_smp_processor_id()];
pos = entry1;
- size = total_size;
- xt_entry_foreach(iter0, entry0, total_size) {
- ret = compat_copy_entry_from_user(iter0, &pos, &size,
- name, newinfo, entry1);
- if (ret != 0)
- break;
- }
+ size = compatr->size;
+ xt_entry_foreach(iter0, entry0, compatr->size)
+ compat_copy_entry_from_user(iter0, &pos, &size,
+ newinfo, entry1);
+
+ /* all module references in entry0 are now gone. */
xt_compat_flush_offsets(AF_INET6);
xt_compat_unlock(AF_INET6);
+
+ memcpy(&repl, compatr, sizeof(*compatr));
+
+ for (i = 0; i < NF_INET_NUMHOOKS; i++) {
+ repl.hook_entry[i] = newinfo->hook_entry[i];
+ repl.underflow[i] = newinfo->underflow[i];
+ }
+
+ repl.num_counters = 0;
+ repl.counters = NULL;
+ repl.size = newinfo->size;
+ ret = translate_table(net, newinfo, entry1, &repl);
if (ret)
goto free_newinfo;
- ret = -ELOOP;
- if (!mark_source_chains(newinfo, valid_hooks, entry1))
- goto free_newinfo;
-
- i = 0;
- xt_entry_foreach(iter1, entry1, newinfo->size) {
- ret = compat_check_entry(iter1, net, name);
- if (ret != 0)
- break;
- ++i;
- if (strcmp(ip6t_get_target(iter1)->u.user.name,
- XT_ERROR_TARGET) == 0)
- ++newinfo->stacksize;
- }
- if (ret) {
- /*
- * The first i matches need cleanup_entry (calls ->destroy)
- * because they had called ->check already. The other j-i
- * entries need only release.
- */
- int skip = i;
- j -= i;
- xt_entry_foreach(iter0, entry0, newinfo->size) {
- if (skip-- > 0)
- continue;
- if (j-- == 0)
- break;
- compat_release_entry(iter0);
- }
- xt_entry_foreach(iter1, entry1, newinfo->size) {
- if (i-- == 0)
- break;
- cleanup_entry(iter1, net);
- }
- xt_free_table_info(newinfo);
- return ret;
- }
-
- /* And one copy for every other CPU */
- for_each_possible_cpu(i)
- if (newinfo->entries[i] && newinfo->entries[i] != entry1)
- memcpy(newinfo->entries[i], entry1, newinfo->size);
-
*pinfo = newinfo;
*pentry0 = entry1;
xt_free_table_info(info);
@@ -1790,17 +1645,16 @@
free_newinfo:
xt_free_table_info(newinfo);
-out:
- xt_entry_foreach(iter0, entry0, total_size) {
+ return ret;
+out_unlock:
+ xt_compat_flush_offsets(AF_INET6);
+ xt_compat_unlock(AF_INET6);
+ xt_entry_foreach(iter0, entry0, compatr->size) {
if (j-- == 0)
break;
compat_release_entry(iter0);
}
return ret;
-out_unlock:
- xt_compat_flush_offsets(AF_INET6);
- xt_compat_unlock(AF_INET6);
- goto out;
}
static int
@@ -1834,10 +1688,7 @@
goto free_newinfo;
}
- ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
- &newinfo, &loc_cpu_entry, tmp.size,
- tmp.num_entries, tmp.hook_entry,
- tmp.underflow);
+ ret = translate_compat_table(net, &newinfo, &loc_cpu_entry, &tmp);
if (ret != 0)
goto free_newinfo;
diff --git a/net/ipv6/netfilter/nft_reject_ipv6.c b/net/ipv6/netfilter/nft_reject_ipv6.c
index 0bc19fa..367bd48 100644
--- a/net/ipv6/netfilter/nft_reject_ipv6.c
+++ b/net/ipv6/netfilter/nft_reject_ipv6.c
@@ -34,6 +34,8 @@
case NFT_REJECT_TCP_RST:
nf_send_reset6(net, pkt->skb, pkt->ops->hooknum);
break;
+ default:
+ break;
}
data[NFT_REG_VERDICT].verdict = NF_DROP;
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
index 51f5bce..55597fc7 100644
--- a/net/ipv6/ping.c
+++ b/net/ipv6/ping.c
@@ -142,7 +142,7 @@
fl6.daddr = *daddr;
fl6.flowi6_oif = oif;
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
fl6.fl6_icmp_type = user_icmph.icmp6_type;
fl6.fl6_icmp_code = user_icmph.icmp6_code;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index bc3459d..80f8f8a 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -769,7 +769,7 @@
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
if (sin6) {
if (addr_len < SIN6_LEN_RFC2133)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index babc601..7835411 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1179,7 +1179,7 @@
void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
{
ip6_update_pmtu(skb, sock_net(sk), mtu,
- sk->sk_bound_dev_if, sk->sk_mark, sock_i_uid(sk));
+ sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid);
}
EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
@@ -1254,7 +1254,8 @@
flags, __ip6_route_redirect);
}
-void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
+void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
+ kuid_t uid)
{
const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
struct dst_entry *dst;
@@ -1267,6 +1268,7 @@
fl6.daddr = iph->daddr;
fl6.saddr = iph->saddr;
fl6.flowlabel = ip6_flowinfo(iph);
+ fl6.flowi6_uid = uid;
dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
rt6_do_redirect(dst, NULL, skb);
@@ -1288,6 +1290,7 @@
fl6.flowi6_mark = mark;
fl6.daddr = msg->dest;
fl6.saddr = iph->daddr;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
dst = ip6_route_redirect(net, &fl6, &iph->saddr);
rt6_do_redirect(dst, NULL, skb);
@@ -1296,7 +1299,8 @@
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
{
- ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
+ ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
+ sk->sk_uid);
}
EXPORT_SYMBOL_GPL(ip6_sk_redirect);
@@ -1728,6 +1732,8 @@
continue;
if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
continue;
+ if (cfg->fc_protocol && cfg->fc_protocol != rt->rt6i_protocol)
+ continue;
dst_hold(&rt->dst);
read_unlock_bh(&table->tb6_lock);
@@ -2600,7 +2606,9 @@
if (iif) {
#ifdef CONFIG_IPV6_MROUTE
if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) {
- int err = ip6mr_get_route(net, skb, rtm, nowait);
+ int err = ip6mr_get_route(net, skb, rtm, nowait,
+ portid);
+
if (err <= 0) {
if (!nowait) {
if (err == 0)
@@ -2717,6 +2725,7 @@
nla_get_u32(tb[RTA_UID]));
else
fl6.flowi6_uid = iif ? INVALID_UID : current_uid();
+
if (iif) {
struct net_device *dev;
int flags = 0;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 45eae1e..eb36219 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -1388,6 +1388,7 @@
tunnel->dst_cache = alloc_percpu(struct ip_tunnel_dst);
if (!tunnel->dst_cache) {
free_percpu(dev->tstats);
+ dev->tstats = NULL;
return -ENOMEM;
}
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index b52a246d..4218d95 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -247,7 +247,7 @@
fl6.flowi6_mark = ireq->ir_mark;
fl6.fl6_dport = ireq->ir_rmt_port;
fl6.fl6_sport = inet_sk(sk)->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
security_req_classify_flow(req, flowi6_to_flowi(&fl6));
dst = ip6_dst_lookup_flow(sk, &fl6, final_p);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 4fc96a0..ff458e8 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -236,7 +236,7 @@
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = usin->sin6_port;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
final_p = fl6_update_dst(&fl6, opt, &final);
@@ -374,10 +374,12 @@
np = inet6_sk(sk);
if (type == NDISC_REDIRECT) {
- struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
+ if (!sock_owned_by_user(sk)) {
+ struct dst_entry *dst = __sk_dst_check(sk, np->dst_cookie);
- if (dst)
- dst->ops->redirect(dst, sk, skb);
+ if (dst)
+ dst->ops->redirect(dst, sk, skb);
+ }
goto out;
}
@@ -856,6 +858,7 @@
fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);
fl6.fl6_dport = t1->dest;
fl6.fl6_sport = t1->source;
+ fl6.flowi6_uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
/* Pass a socket to ip6_dst_lookup either it is for RST
@@ -1034,6 +1037,16 @@
return 0; /* don't send reset */
}
+static void tcp_v6_restore_cb(struct sk_buff *skb)
+{
+ /* We need to move header back to the beginning if xfrm6_policy_check()
+ * and tcp_v6_fill_cb() are going to be called again.
+ * ip6_datagram_recv_specific_ctl() also expects IP6CB to be there.
+ */
+ memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
+ sizeof(struct inet6_skb_parm));
+}
+
static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
struct dst_entry *dst)
@@ -1163,8 +1176,10 @@
sk_gfp_atomic(sk, GFP_ATOMIC));
consume_skb(ireq->pktopts);
ireq->pktopts = NULL;
- if (newnp->pktoptions)
+ if (newnp->pktoptions) {
+ tcp_v6_restore_cb(newnp->pktoptions);
skb_set_owner_r(newnp->pktoptions, newsk);
+ }
}
newnp->opt = NULL;
newnp->mcast_oif = tcp_v6_iif(skb);
@@ -1364,6 +1379,7 @@
np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb));
if (ipv6_opt_accepted(sk, opt_skb, &TCP_SKB_CB(opt_skb)->header.h6)) {
skb_set_owner_r(opt_skb, sk);
+ tcp_v6_restore_cb(opt_skb);
opt_skb = xchg(&np->pktoptions, opt_skb);
} else {
__kfree_skb(opt_skb);
@@ -1397,15 +1413,6 @@
TCP_SKB_CB(skb)->sacked = 0;
}
-static void tcp_v6_restore_cb(struct sk_buff *skb)
-{
- /* We need to move header back to the beginning if xfrm6_policy_check()
- * and tcp_v6_fill_cb() are going to be called again.
- */
- memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
- sizeof(struct inet6_skb_parm));
-}
-
static int tcp_v6_rcv(struct sk_buff *skb)
{
const struct tcphdr *th;
@@ -1732,7 +1739,9 @@
destp = ntohs(inet->inet_dport);
srcp = ntohs(inet->inet_sport);
- if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
+ if (icsk->icsk_pending == ICSK_TIME_RETRANS ||
+ icsk->icsk_pending == ICSK_TIME_EARLY_RETRANS ||
+ icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) {
timer_active = 1;
timer_expires = icsk->icsk_timeout;
} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index f167e45..01f3fca 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -900,11 +900,9 @@
ret = udpv6_queue_rcv_skb(sk, skb);
sock_put(sk);
- /* a return value > 0 means to resubmit the input, but
- * it wants the return to be -protocol, or 0
- */
+ /* a return value > 0 means to resubmit the input */
if (ret > 0)
- return -ret;
+ return ret;
return 0;
}
@@ -1216,7 +1214,7 @@
fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
if (msg->msg_controllen) {
opt = &opt_space;
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index b3f6ec0..1cc2b7b5 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -1036,8 +1036,11 @@
}
/* Check if we have opened a local TSAP */
- if (!self->tsap)
- irda_open_tsap(self, LSAP_ANY, addr->sir_name);
+ if (!self->tsap) {
+ err = irda_open_tsap(self, LSAP_ANY, addr->sir_name);
+ if (err)
+ goto out;
+ }
/* Move to connecting socket, start sending Connect Requests */
sock->state = SS_CONNECTING;
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 68aa9ff..e9ec7d2 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -273,6 +273,7 @@
int l2tp_nl_register_ops(enum l2tp_pwtype pw_type,
const struct l2tp_nl_cmd_ops *ops);
void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);
+int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg);
/* Session reference counts. Incremented when code obtains a reference
* to a session.
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index 142f036..85285f4 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -11,6 +11,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <asm/ioctls.h>
#include <linux/icmp.h>
#include <linux/module.h>
#include <linux/skbuff.h>
@@ -123,12 +124,11 @@
struct l2tp_tunnel *tunnel = NULL;
int length;
- /* Point to L2TP header */
- optr = ptr = skb->data;
-
if (!pskb_may_pull(skb, 4))
goto discard;
+ /* Point to L2TP header */
+ optr = ptr = skb->data;
session_id = ntohl(*((__be32 *) ptr));
ptr += 4;
@@ -156,6 +156,9 @@
if (!pskb_may_pull(skb, length))
goto discard;
+ /* Point to L2TP header */
+ optr = ptr = skb->data;
+ ptr += 4;
pr_debug("%s: ip recv\n", tunnel->name);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, ptr, length);
}
@@ -380,7 +383,7 @@
drop:
IP_INC_STATS(sock_net(sk), IPSTATS_MIB_INDISCARDS);
kfree_skb(skb);
- return -1;
+ return 0;
}
/* Userspace will call sendmsg() on the tunnel socket to send L2TP
@@ -553,6 +556,30 @@
return err ? err : copied;
}
+int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ struct sk_buff *skb;
+ int amount;
+
+ switch (cmd) {
+ case SIOCOUTQ:
+ amount = sk_wmem_alloc_get(sk);
+ break;
+ case SIOCINQ:
+ spin_lock_bh(&sk->sk_receive_queue.lock);
+ skb = skb_peek(&sk->sk_receive_queue);
+ amount = skb ? skb->len : 0;
+ spin_unlock_bh(&sk->sk_receive_queue.lock);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return put_user(amount, (int __user *)arg);
+}
+EXPORT_SYMBOL(l2tp_ioctl);
+
static struct proto l2tp_ip_prot = {
.name = "L2TP/IP",
.owner = THIS_MODULE,
@@ -561,7 +588,7 @@
.bind = l2tp_ip_bind,
.connect = l2tp_ip_connect,
.disconnect = l2tp_ip_disconnect,
- .ioctl = udp_ioctl,
+ .ioctl = l2tp_ioctl,
.destroy = l2tp_ip_destroy_sock,
.setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt,
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index 17b7481..0bd7b17 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -135,12 +135,11 @@
struct l2tp_tunnel *tunnel = NULL;
int length;
- /* Point to L2TP header */
- optr = ptr = skb->data;
-
if (!pskb_may_pull(skb, 4))
goto discard;
+ /* Point to L2TP header */
+ optr = ptr = skb->data;
session_id = ntohl(*((__be32 *) ptr));
ptr += 4;
@@ -168,6 +167,9 @@
if (!pskb_may_pull(skb, length))
goto discard;
+ /* Point to L2TP header */
+ optr = ptr = skb->data;
+ ptr += 4;
pr_debug("%s: ip recv\n", tunnel->name);
print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, ptr, length);
}
@@ -517,6 +519,7 @@
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_mark = sk->sk_mark;
+ fl6.flowi6_uid = sk->sk_uid;
if (lsa) {
if (addr_len < SIN6_LEN_RFC2133)
@@ -714,7 +717,7 @@
.bind = l2tp_ip6_bind,
.connect = l2tp_ip6_connect,
.disconnect = l2tp_ip6_disconnect,
- .ioctl = udp_ioctl,
+ .ioctl = l2tp_ioctl,
.destroy = l2tp_ip6_destroy_sock,
.setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt,
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index b704a93..5a234fbe 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -469,6 +469,10 @@
static void pppol2tp_session_destruct(struct sock *sk)
{
struct l2tp_session *session = sk->sk_user_data;
+
+ skb_queue_purge(&sk->sk_receive_queue);
+ skb_queue_purge(&sk->sk_write_queue);
+
if (session) {
sk->sk_user_data = NULL;
BUG_ON(session->magic != L2TP_SESSION_MAGIC);
@@ -507,9 +511,6 @@
l2tp_session_queue_purge(session);
sock_put(sk);
}
- skb_queue_purge(&sk->sk_receive_queue);
- skb_queue_purge(&sk->sk_write_queue);
-
release_sock(sk);
/* This will delete the session context via
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index bb9cbc1..3e86918 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -626,6 +626,7 @@
if (llc->cmsg_flags & LLC_CMSG_PKTINFO) {
struct llc_pktinfo info;
+ memset(&info, 0, sizeof(info));
info.lpi_ifindex = llc_sk(skb->sk)->dev->ifindex;
llc_pdu_decode_dsap(skb, &info.lpi_sap);
llc_pdu_decode_da(skb, info.lpi_mac);
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index 42dc2e4..9c68d0b 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -821,7 +821,10 @@
* another trick required to cope with how the PROCOM state
* machine works. -acme
*/
+ skb_orphan(skb);
+ sock_hold(sk);
skb->sk = sk;
+ skb->destructor = sock_efree;
}
if (!sock_owned_by_user(sk))
llc_conn_rcv(sk, skb);
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c
index 06033f6..cdc1b62 100644
--- a/net/llc/llc_sap.c
+++ b/net/llc/llc_sap.c
@@ -290,7 +290,10 @@
ev->type = LLC_SAP_EV_TYPE_PDU;
ev->reason = 0;
+ skb_orphan(skb);
+ sock_hold(sk);
skb->sk = sk;
+ skb->destructor = sock_efree;
llc_sap_state_process(sap, skb);
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 2de1525..e95b345 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -865,7 +865,7 @@
/* free all potentially still buffered bcast frames */
local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
- skb_queue_purge(&sdata->u.ap.ps.bc_buf);
+ ieee80211_purge_tx_queue(&local->hw, &sdata->u.ap.ps.bc_buf);
mutex_lock(&local->mtx);
ieee80211_vif_copy_chanctx_to_vlans(sdata, true);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 2bbc820..ac04e38 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -161,6 +161,10 @@
del_timer_sync(&sta->plink_timer);
}
+ /* make sure no readers can access nexthop sta from here on */
+ mesh_path_flush_by_nexthop(sta);
+ synchronize_net();
+
if (changed)
ieee80211_mbss_info_change_notify(sdata, changed);
}
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 7757d6d..4efe457 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -116,6 +116,7 @@
break;
}
+ flush_delayed_work(&sdata->dec_tailroom_needed_wk);
drv_remove_interface(local, sdata);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 074cdfa..bb6f6ef 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2040,16 +2040,22 @@
if (!(status->rx_flags & IEEE80211_RX_AMSDU))
return RX_CONTINUE;
- if (ieee80211_has_a4(hdr->frame_control) &&
- rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
- !rx->sdata->u.vlan.sta)
- return RX_DROP_UNUSABLE;
+ if (unlikely(ieee80211_has_a4(hdr->frame_control))) {
+ switch (rx->sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ if (!rx->sdata->u.vlan.sta)
+ return RX_DROP_UNUSABLE;
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (!rx->sdata->u.mgd.use_4addr)
+ return RX_DROP_UNUSABLE;
+ break;
+ default:
+ return RX_DROP_UNUSABLE;
+ }
+ }
- if (is_multicast_ether_addr(hdr->addr1) &&
- ((rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
- rx->sdata->u.vlan.sta) ||
- (rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
- rx->sdata->u.mgd.use_4addr)))
+ if (is_multicast_ether_addr(hdr->addr1))
return RX_DROP_UNUSABLE;
skb->dev = dev;
@@ -2090,7 +2096,7 @@
struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- u16 q, hdrlen;
+ u16 ac, q, hdrlen;
hdr = (struct ieee80211_hdr *) skb->data;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -2160,7 +2166,8 @@
ether_addr_equal(sdata->vif.addr, hdr->addr3))
return RX_CONTINUE;
- q = ieee80211_select_queue_80211(sdata, skb, hdr);
+ ac = ieee80211_select_queue_80211(sdata, skb, hdr);
+ q = sdata->vif.hw_queue[ac];
if (ieee80211_queue_stopped(&local->hw, q)) {
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_congestion);
return RX_DROP_MONITOR;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 45e78282..655abb9 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -353,7 +353,7 @@
skb = skb_dequeue(&ps->bc_buf);
if (skb) {
purged++;
- dev_kfree_skb(skb);
+ ieee80211_free_txskb(&local->hw, skb);
}
total += skb_queue_len(&ps->bc_buf);
}
@@ -436,7 +436,7 @@
if (skb_queue_len(&ps->bc_buf) >= AP_MAX_BC_BUFFER) {
ps_dbg(tx->sdata,
"BC TX buffer full - dropping the oldest frame\n");
- dev_kfree_skb(skb_dequeue(&ps->bc_buf));
+ ieee80211_free_txskb(&tx->local->hw, skb_dequeue(&ps->bc_buf));
} else
tx->local->total_ps_buffered++;
@@ -2999,7 +2999,7 @@
sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev);
if (!ieee80211_tx_prepare(sdata, &tx, skb))
break;
- dev_kfree_skb_any(skb);
+ ieee80211_free_txskb(hw, skb);
}
info = IEEE80211_SKB_CB(skb);
diff --git a/net/netfilter/ipvs/ip_vs_pe_sip.c b/net/netfilter/ipvs/ip_vs_pe_sip.c
index bed5f70..bb318e4 100644
--- a/net/netfilter/ipvs/ip_vs_pe_sip.c
+++ b/net/netfilter/ipvs/ip_vs_pe_sip.c
@@ -88,7 +88,7 @@
dptr = skb->data + dataoff;
datalen = skb->len - dataoff;
- if (get_callid(dptr, dataoff, datalen, &matchoff, &matchlen))
+ if (get_callid(dptr, 0, datalen, &matchoff, &matchlen))
return -EINVAL;
/* N.B: pe_data is only set on success,
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index e8c0cb5..bf193e3 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -1777,6 +1777,7 @@
int nf_conntrack_init_net(struct net *net)
{
+ static atomic64_t unique_id;
int ret = -ENOMEM;
int cpu;
@@ -1800,7 +1801,8 @@
if (!net->ct.stat)
goto err_pcpu_lists;
- net->ct.slabname = kasprintf(GFP_KERNEL, "nf_conntrack_%pK", net);
+ net->ct.slabname = kasprintf(GFP_KERNEL, "nf_conntrack_%llu",
+ (u64)atomic64_inc_return(&unique_id));
if (!net->ct.slabname)
goto err_slabname;
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index ff6f359..ab24224 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -277,11 +277,11 @@
return;
}
- switch(ret) {
- case true:
+ switch (ret ? 1 : 0) {
+ case 1:
data[NFT_REG_VERDICT].verdict = NFT_CONTINUE;
break;
- case false:
+ case 0:
data[NFT_REG_VERDICT].verdict = NFT_BREAK;
break;
}
diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c
index cc56030..18d520e 100644
--- a/net/netfilter/nft_ct.c
+++ b/net/netfilter/nft_ct.c
@@ -56,6 +56,8 @@
state = NF_CT_STATE_BIT(ctinfo);
dest->data[0] = state;
return;
+ default:
+ break;
}
if (ct == NULL)
@@ -117,6 +119,8 @@
return;
}
#endif
+ default:
+ break;
}
tuple = &ct->tuplehash[priv->dir].tuple;
@@ -141,6 +145,8 @@
case NFT_CT_PROTO_DST:
dest->data[0] = (__force __u16)tuple->dst.u.all;
return;
+ default:
+ break;
}
return;
err:
@@ -172,6 +178,8 @@
}
break;
#endif
+ default:
+ break;
}
}
diff --git a/net/netfilter/nft_reject.c b/net/netfilter/nft_reject.c
index 57d3e1a..0522fc9 100644
--- a/net/netfilter/nft_reject.c
+++ b/net/netfilter/nft_reject.c
@@ -63,6 +63,8 @@
if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
goto nla_put_failure;
break;
+ default:
+ break;
}
return 0;
diff --git a/net/netfilter/nft_reject_inet.c b/net/netfilter/nft_reject_inet.c
index 7b5f9d5..1fb065a 100644
--- a/net/netfilter/nft_reject_inet.c
+++ b/net/netfilter/nft_reject_inet.c
@@ -105,6 +105,8 @@
if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
goto nla_put_failure;
break;
+ default:
+ break;
}
return 0;
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index 133eb47..4898993 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -418,6 +418,47 @@
}
EXPORT_SYMBOL_GPL(xt_check_match);
+/** xt_check_entry_match - check that matches end before start of target
+ *
+ * @match: beginning of xt_entry_match
+ * @target: beginning of this rules target (alleged end of matches)
+ * @alignment: alignment requirement of match structures
+ *
+ * Validates that all matches add up to the beginning of the target,
+ * and that each match covers at least the base structure size.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+static int xt_check_entry_match(const char *match, const char *target,
+ const size_t alignment)
+{
+ const struct xt_entry_match *pos;
+ int length = target - match;
+
+ if (length == 0) /* no matches */
+ return 0;
+
+ pos = (struct xt_entry_match *)match;
+ do {
+ if ((unsigned long)pos % alignment)
+ return -EINVAL;
+
+ if (length < (int)sizeof(struct xt_entry_match))
+ return -EINVAL;
+
+ if (pos->u.match_size < sizeof(struct xt_entry_match))
+ return -EINVAL;
+
+ if (pos->u.match_size > length)
+ return -EINVAL;
+
+ length -= pos->u.match_size;
+ pos = ((void *)((char *)(pos) + (pos)->u.match_size));
+ } while (length > 0);
+
+ return 0;
+}
+
#ifdef CONFIG_COMPAT
int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta)
{
@@ -487,13 +528,14 @@
}
EXPORT_SYMBOL_GPL(xt_compat_match_offset);
-int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
- unsigned int *size)
+void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr,
+ unsigned int *size)
{
const struct xt_match *match = m->u.kernel.match;
struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m;
int pad, off = xt_compat_match_offset(match);
u_int16_t msize = cm->u.user.match_size;
+ char name[sizeof(m->u.user.name)];
m = *dstptr;
memcpy(m, cm, sizeof(*cm));
@@ -507,10 +549,12 @@
msize += off;
m->u.user.match_size = msize;
+ strlcpy(name, match->name, sizeof(name));
+ module_put(match->me);
+ strncpy(m->u.user.name, name, sizeof(m->u.user.name));
*size += off;
*dstptr += msize;
- return 0;
}
EXPORT_SYMBOL_GPL(xt_compat_match_from_user);
@@ -541,8 +585,175 @@
return 0;
}
EXPORT_SYMBOL_GPL(xt_compat_match_to_user);
+
+/* non-compat version may have padding after verdict */
+struct compat_xt_standard_target {
+ struct compat_xt_entry_target t;
+ compat_uint_t verdict;
+};
+
+int xt_compat_check_entry_offsets(const void *base, const char *elems,
+ unsigned int target_offset,
+ unsigned int next_offset)
+{
+ long size_of_base_struct = elems - (const char *)base;
+ const struct compat_xt_entry_target *t;
+ const char *e = base;
+
+ if (target_offset < size_of_base_struct)
+ return -EINVAL;
+
+ if (target_offset + sizeof(*t) > next_offset)
+ return -EINVAL;
+
+ t = (void *)(e + target_offset);
+ if (t->u.target_size < sizeof(*t))
+ return -EINVAL;
+
+ if (target_offset + t->u.target_size > next_offset)
+ return -EINVAL;
+
+ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
+ COMPAT_XT_ALIGN(target_offset + sizeof(struct compat_xt_standard_target)) != next_offset)
+ return -EINVAL;
+
+ /* compat_xt_entry match has less strict aligment requirements,
+ * otherwise they are identical. In case of padding differences
+ * we need to add compat version of xt_check_entry_match.
+ */
+ BUILD_BUG_ON(sizeof(struct compat_xt_entry_match) != sizeof(struct xt_entry_match));
+
+ return xt_check_entry_match(elems, base + target_offset,
+ __alignof__(struct compat_xt_entry_match));
+}
+EXPORT_SYMBOL(xt_compat_check_entry_offsets);
#endif /* CONFIG_COMPAT */
+/**
+ * xt_check_entry_offsets - validate arp/ip/ip6t_entry
+ *
+ * @base: pointer to arp/ip/ip6t_entry
+ * @elems: pointer to first xt_entry_match, i.e. ip(6)t_entry->elems
+ * @target_offset: the arp/ip/ip6_t->target_offset
+ * @next_offset: the arp/ip/ip6_t->next_offset
+ *
+ * validates that target_offset and next_offset are sane and that all
+ * match sizes (if any) align with the target offset.
+ *
+ * This function does not validate the targets or matches themselves, it
+ * only tests that all the offsets and sizes are correct, that all
+ * match structures are aligned, and that the last structure ends where
+ * the target structure begins.
+ *
+ * Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
+ *
+ * The arp/ip/ip6t_entry structure @base must have passed following tests:
+ * - it must point to a valid memory location
+ * - base to base + next_offset must be accessible, i.e. not exceed allocated
+ * length.
+ *
+ * A well-formed entry looks like this:
+ *
+ * ip(6)t_entry match [mtdata] match [mtdata] target [tgdata] ip(6)t_entry
+ * e->elems[]-----' | |
+ * matchsize | |
+ * matchsize | |
+ * | |
+ * target_offset---------------------------------' |
+ * next_offset---------------------------------------------------'
+ *
+ * elems[]: flexible array member at end of ip(6)/arpt_entry struct.
+ * This is where matches (if any) and the target reside.
+ * target_offset: beginning of target.
+ * next_offset: start of the next rule; also: size of this rule.
+ * Since targets have a minimum size, target_offset + minlen <= next_offset.
+ *
+ * Every match stores its size, sum of sizes must not exceed target_offset.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int xt_check_entry_offsets(const void *base,
+ const char *elems,
+ unsigned int target_offset,
+ unsigned int next_offset)
+{
+ long size_of_base_struct = elems - (const char *)base;
+ const struct xt_entry_target *t;
+ const char *e = base;
+
+ /* target start is within the ip/ip6/arpt_entry struct */
+ if (target_offset < size_of_base_struct)
+ return -EINVAL;
+
+ if (target_offset + sizeof(*t) > next_offset)
+ return -EINVAL;
+
+ t = (void *)(e + target_offset);
+ if (t->u.target_size < sizeof(*t))
+ return -EINVAL;
+
+ if (target_offset + t->u.target_size > next_offset)
+ return -EINVAL;
+
+ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
+ XT_ALIGN(target_offset + sizeof(struct xt_standard_target)) != next_offset)
+ return -EINVAL;
+
+ return xt_check_entry_match(elems, base + target_offset,
+ __alignof__(struct xt_entry_match));
+}
+EXPORT_SYMBOL(xt_check_entry_offsets);
+
+/**
+ * xt_alloc_entry_offsets - allocate array to store rule head offsets
+ *
+ * @size: number of entries
+ *
+ * Return: NULL or kmalloc'd or vmalloc'd array
+ */
+unsigned int *xt_alloc_entry_offsets(unsigned int size)
+{
+ unsigned int *off;
+
+ off = kcalloc(size, sizeof(unsigned int), GFP_KERNEL | __GFP_NOWARN);
+
+ if (off)
+ return off;
+
+ if (size < (SIZE_MAX / sizeof(unsigned int)))
+ off = vmalloc(size * sizeof(unsigned int));
+
+ return off;
+}
+EXPORT_SYMBOL(xt_alloc_entry_offsets);
+
+/**
+ * xt_find_jump_offset - check if target is a valid jump offset
+ *
+ * @offsets: array containing all valid rule start offsets of a rule blob
+ * @target: the jump target to search for
+ * @size: entries in @offset
+ */
+bool xt_find_jump_offset(const unsigned int *offsets,
+ unsigned int target, unsigned int size)
+{
+ int m, low = 0, hi = size;
+
+ while (hi > low) {
+ m = (low + hi) / 2u;
+
+ if (offsets[m] > target)
+ hi = m;
+ else if (offsets[m] < target)
+ low = m + 1;
+ else
+ return true;
+ }
+
+ return false;
+}
+EXPORT_SYMBOL(xt_find_jump_offset);
+
int xt_check_target(struct xt_tgchk_param *par,
unsigned int size, u_int8_t proto, bool inv_proto)
{
@@ -593,6 +804,80 @@
}
EXPORT_SYMBOL_GPL(xt_check_target);
+/**
+ * xt_copy_counters_from_user - copy counters and metadata from userspace
+ *
+ * @user: src pointer to userspace memory
+ * @len: alleged size of userspace memory
+ * @info: where to store the xt_counters_info metadata
+ * @compat: true if we setsockopt call is done by 32bit task on 64bit kernel
+ *
+ * Copies counter meta data from @user and stores it in @info.
+ *
+ * vmallocs memory to hold the counters, then copies the counter data
+ * from @user to the new memory and returns a pointer to it.
+ *
+ * If @compat is true, @info gets converted automatically to the 64bit
+ * representation.
+ *
+ * The metadata associated with the counters is stored in @info.
+ *
+ * Return: returns pointer that caller has to test via IS_ERR().
+ * If IS_ERR is false, caller has to vfree the pointer.
+ */
+void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
+ struct xt_counters_info *info, bool compat)
+{
+ void *mem;
+ u64 size;
+
+#ifdef CONFIG_COMPAT
+ if (compat) {
+ /* structures only differ in size due to alignment */
+ struct compat_xt_counters_info compat_tmp;
+
+ if (len <= sizeof(compat_tmp))
+ return ERR_PTR(-EINVAL);
+
+ len -= sizeof(compat_tmp);
+ if (copy_from_user(&compat_tmp, user, sizeof(compat_tmp)) != 0)
+ return ERR_PTR(-EFAULT);
+
+ strlcpy(info->name, compat_tmp.name, sizeof(info->name));
+ info->num_counters = compat_tmp.num_counters;
+ user += sizeof(compat_tmp);
+ } else
+#endif
+ {
+ if (len <= sizeof(*info))
+ return ERR_PTR(-EINVAL);
+
+ len -= sizeof(*info);
+ if (copy_from_user(info, user, sizeof(*info)) != 0)
+ return ERR_PTR(-EFAULT);
+
+ info->name[sizeof(info->name) - 1] = '\0';
+ user += sizeof(*info);
+ }
+
+ size = sizeof(struct xt_counters);
+ size *= info->num_counters;
+
+ if (size != (u64)len)
+ return ERR_PTR(-EINVAL);
+
+ mem = vmalloc(len);
+ if (!mem)
+ return ERR_PTR(-ENOMEM);
+
+ if (copy_from_user(mem, user, len) == 0)
+ return mem;
+
+ vfree(mem);
+ return ERR_PTR(-EFAULT);
+}
+EXPORT_SYMBOL_GPL(xt_copy_counters_from_user);
+
#ifdef CONFIG_COMPAT
int xt_compat_target_offset(const struct xt_target *target)
{
@@ -608,6 +893,7 @@
struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t;
int pad, off = xt_compat_target_offset(target);
u_int16_t tsize = ct->u.user.target_size;
+ char name[sizeof(t->u.user.name)];
t = *dstptr;
memcpy(t, ct, sizeof(*ct));
@@ -621,6 +907,9 @@
tsize += off;
t->u.user.target_size = tsize;
+ strlcpy(name, target->name, sizeof(name));
+ module_put(target->me);
+ strncpy(t->u.user.name, name, sizeof(t->u.user.name));
*size += off;
*dstptr += tsize;
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index 8cc1d3b..e1b6112 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -320,7 +320,7 @@
st_entry->tag,
get_uid_from_tag(st_entry->tag));
rb_erase(&st_entry->sock_node, st_to_free_tree);
- sockfd_put(st_entry->socket);
+ sock_put(st_entry->sk);
kfree(st_entry);
}
}
@@ -1802,8 +1802,11 @@
}
#ifdef DDEBUG
-/* This function is not in xt_qtaguid_print.c because of locks visibility */
-static void prdebug_full_state(int indent_level, const char *fmt, ...)
+/*
+ * This function is not in xt_qtaguid_print.c because of locks visibility.
+ * The lock of sock_tag_list must be aquired before calling this function
+ */
+static void prdebug_full_state_locked(int indent_level, const char *fmt, ...)
{
va_list args;
char *fmt_buff;
@@ -1824,16 +1827,12 @@
kfree(buff);
va_end(args);
- spin_lock_bh(&sock_tag_list_lock);
prdebug_sock_tag_tree(indent_level, &sock_tag_tree);
- spin_unlock_bh(&sock_tag_list_lock);
- spin_lock_bh(&sock_tag_list_lock);
spin_lock_bh(&uid_tag_data_tree_lock);
prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree);
prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree);
spin_unlock_bh(&uid_tag_data_tree_lock);
- spin_unlock_bh(&sock_tag_list_lock);
spin_lock_bh(&iface_stat_list_lock);
prdebug_iface_stat_list(indent_level, &iface_stat_list);
@@ -1842,7 +1841,7 @@
pr_debug("qtaguid: %s(): }\n", __func__);
}
#else
-static void prdebug_full_state(int indent_level, const char *fmt, ...) {}
+static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) {}
#endif
struct proc_ctrl_print_info {
@@ -1918,12 +1917,12 @@
{
struct sock_tag *sock_tag_entry = v;
uid_t uid;
- long f_count;
CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n",
current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
if (sock_tag_entry != SEQ_START_TOKEN) {
+ int sk_ref_count;
uid = get_uid_from_tag(sock_tag_entry->tag);
CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) "
"pid=%u\n",
@@ -1932,13 +1931,13 @@
uid,
sock_tag_entry->pid
);
- f_count = atomic_long_read(
- &sock_tag_entry->socket->file->f_count);
+ sk_ref_count = atomic_read(
+ &sock_tag_entry->sk->sk_refcnt);
seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u "
- "f_count=%lu\n",
+ "f_count=%d\n",
sock_tag_entry->sk,
sock_tag_entry->tag, uid,
- sock_tag_entry->pid, f_count);
+ sock_tag_entry->pid, sk_ref_count);
} else {
seq_printf(m, "events: sockets_tagged=%llu "
"sockets_untagged=%llu "
@@ -1965,8 +1964,11 @@
(u64)atomic64_read(&qtu_events.match_no_sk),
(u64)atomic64_read(&qtu_events.match_no_sk_file));
- /* Count the following as part of the last item_index */
- prdebug_full_state(0, "proc ctrl");
+ /* Count the following as part of the last item_index. No need
+ * to lock the sock_tag_list here since it is already locked when
+ * starting the seq_file operation
+ */
+ prdebug_full_state_locked(0, "proc ctrl");
}
return 0;
@@ -2231,8 +2233,8 @@
from_kuid(&init_user_ns, current_fsuid()));
goto err;
}
- CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n",
- input, atomic_long_read(&el_socket->file->f_count),
+ CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->sk_refcnt=%d ->sk=%p\n",
+ input, atomic_read(&el_socket->sk->sk_refcnt),
el_socket->sk);
if (argc < 3) {
acct_tag = make_atag_from_value(0);
@@ -2276,16 +2278,9 @@
struct tag_ref *prev_tag_ref_entry;
CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p "
- "st@%p ...->f_count=%ld\n",
+ "st@%p ...->sk_refcnt=%d\n",
input, el_socket->sk, sock_tag_entry,
- atomic_long_read(&el_socket->file->f_count));
- /*
- * This is a re-tagging, so release the sock_fd that was
- * locked at the time of the 1st tagging.
- * There is still the ref from this call's sockfd_lookup() so
- * it can be done within the spinlock.
- */
- sockfd_put(sock_tag_entry->socket);
+ atomic_read(&el_socket->sk->sk_refcnt));
prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag,
&uid_tag_data_entry);
BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry));
@@ -2305,8 +2300,11 @@
res = -ENOMEM;
goto err_tag_unref_put;
}
+ /* Hold the sk refcount here to make sure the sk pointer cannot
+ * be freed and reused
+ */
+ sock_hold(el_socket->sk);
sock_tag_entry->sk = el_socket->sk;
- sock_tag_entry->socket = el_socket;
sock_tag_entry->pid = current->tgid;
sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int);
spin_lock_bh(&uid_tag_data_tree_lock);
@@ -2333,10 +2331,11 @@
atomic64_inc(&qtu_events.sockets_tagged);
}
spin_unlock_bh(&sock_tag_list_lock);
- /* We keep the ref to the socket (file) until it is untagged */
- CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n",
+ /* We keep the ref to the sk until it is untagged */
+ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n",
input, sock_tag_entry,
- atomic_long_read(&el_socket->file->f_count));
+ atomic_read(&el_socket->sk->sk_refcnt));
+ sockfd_put(el_socket);
return 0;
err_tag_unref_put:
@@ -2344,8 +2343,8 @@
tag_ref_entry->num_sock_tags--;
free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry);
err_put:
- CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n",
- input, atomic_long_read(&el_socket->file->f_count) - 1);
+ CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n",
+ input, atomic_read(&el_socket->sk->sk_refcnt) - 1);
/* Release the sock_fd that was grabbed by sockfd_lookup(). */
sockfd_put(el_socket);
return res;
@@ -2361,17 +2360,13 @@
int sock_fd = 0;
struct socket *el_socket;
int res, argc;
- struct sock_tag *sock_tag_entry;
- struct tag_ref *tag_ref_entry;
- struct uid_tag_data *utd_entry;
- struct proc_qtu_data *pqd_entry;
argc = sscanf(input, "%c %d", &cmd, &sock_fd);
CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n",
input, argc, cmd, sock_fd);
if (argc < 2) {
res = -EINVAL;
- goto err;
+ return res;
}
el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */
if (!el_socket) {
@@ -2379,17 +2374,31 @@
" sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
input, sock_fd, res, current->pid, current->tgid,
from_kuid(&init_user_ns, current_fsuid()));
- goto err;
+ return res;
}
CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n",
input, atomic_long_read(&el_socket->file->f_count),
el_socket->sk);
+ res = qtaguid_untag(el_socket, false);
+ sockfd_put(el_socket);
+ return res;
+}
+
+int qtaguid_untag(struct socket *el_socket, bool kernel)
+{
+ int res;
+ pid_t pid;
+ struct sock_tag *sock_tag_entry;
+ struct tag_ref *tag_ref_entry;
+ struct uid_tag_data *utd_entry;
+ struct proc_qtu_data *pqd_entry;
+
spin_lock_bh(&sock_tag_list_lock);
sock_tag_entry = get_sock_stat_nl(el_socket->sk);
if (!sock_tag_entry) {
spin_unlock_bh(&sock_tag_list_lock);
res = -EINVAL;
- goto err_put;
+ return res;
}
/*
* The socket already belongs to the current process
@@ -2401,20 +2410,26 @@
BUG_ON(!tag_ref_entry);
BUG_ON(tag_ref_entry->num_sock_tags <= 0);
spin_lock_bh(&uid_tag_data_tree_lock);
+ if (kernel)
+ pid = sock_tag_entry->pid;
+ else
+ pid = current->tgid;
pqd_entry = proc_qtu_data_tree_search(
- &proc_qtu_data_tree, current->tgid);
+ &proc_qtu_data_tree, pid);
/*
* TODO: remove if, and start failing.
* At first, we want to catch user-space code that is not
* opening the /dev/xt_qtaguid.
*/
- if (IS_ERR_OR_NULL(pqd_entry))
+ if (IS_ERR_OR_NULL(pqd_entry) || !sock_tag_entry->list.next) {
pr_warn_once("qtaguid: %s(): "
"User space forgot to open /dev/xt_qtaguid? "
- "pid=%u tgid=%u uid=%u\n", __func__,
- current->pid, current->tgid, from_kuid(&init_user_ns, current_fsuid()));
- else
+ "pid=%u tgid=%u sk_pid=%u, uid=%u\n", __func__,
+ current->pid, current->tgid, sock_tag_entry->pid,
+ from_kuid(&init_user_ns, current_fsuid()));
+ } else {
list_del(&sock_tag_entry->list);
+ }
spin_unlock_bh(&uid_tag_data_tree_lock);
/*
* We don't free tag_ref from the utd_entry here,
@@ -2423,30 +2438,17 @@
tag_ref_entry->num_sock_tags--;
spin_unlock_bh(&sock_tag_list_lock);
/*
- * Release the sock_fd that was grabbed at tag time,
- * and once more for the sockfd_lookup() here.
+ * Release the sock_fd that was grabbed at tag time.
*/
- sockfd_put(sock_tag_entry->socket);
- CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n",
- input, sock_tag_entry,
- atomic_long_read(&el_socket->file->f_count) - 1);
- sockfd_put(el_socket);
+ sock_put(sock_tag_entry->sk);
+ CT_DEBUG("qtaguid: done. st@%p ...->sk_refcnt=%d\n",
+ sock_tag_entry,
+ atomic_read(&el_socket->sk->sk_refcnt));
kfree(sock_tag_entry);
atomic64_inc(&qtu_events.sockets_untagged);
return 0;
-
-err_put:
- CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n",
- input, atomic_long_read(&el_socket->file->f_count) - 1);
- /* Release the sock_fd that was grabbed by sockfd_lookup(). */
- sockfd_put(el_socket);
- return res;
-
-err:
- CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input);
- return res;
}
static ssize_t qtaguid_ctrl_parse(const char *input, size_t count)
@@ -2876,8 +2878,10 @@
sock_tag_tree_erase(&st_to_free_tree);
- prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__,
+ spin_lock_bh(&sock_tag_list_lock);
+ prdebug_full_state_locked(0, "%s(): pid=%u tgid=%u", __func__,
current->pid, current->tgid);
+ spin_unlock_bh(&sock_tag_list_lock);
return 0;
}
diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h
index 6dc14a9..8178fbd 100644
--- a/net/netfilter/xt_qtaguid_internal.h
+++ b/net/netfilter/xt_qtaguid_internal.h
@@ -256,8 +256,6 @@
struct sock_tag {
struct rb_node sock_node;
struct sock *sk; /* Only used as a number, never dereferenced */
- /* The socket is needed for sockfd_put() */
- struct socket *socket;
/* Used to associate with a given pid */
struct list_head list; /* in proc_qtu_data.sock_tag_list */
pid_t pid;
diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c
index f6a00a3..2a7190d 100644
--- a/net/netfilter/xt_qtaguid_print.c
+++ b/net/netfilter/xt_qtaguid_print.c
@@ -24,7 +24,7 @@
#include <linux/rbtree.h>
#include <linux/slab.h>
#include <linux/spinlock_types.h>
-
+#include <net/sock.h>
#include "xt_qtaguid_internal.h"
#include "xt_qtaguid_print.h"
@@ -237,10 +237,10 @@
tag_str = pp_tag_t(&st->tag);
res = kasprintf(GFP_ATOMIC, "sock_tag@%p{"
"sock_node=rb_node{...}, "
- "sk=%p socket=%p (f_count=%lu), list=list_head{...}, "
+ "sk=%p (f_count=%d), list=list_head{...}, "
"pid=%u, tag=%s}",
- st, st->sk, st->socket, atomic_long_read(
- &st->socket->file->f_count),
+ st, st->sk, atomic_read(
+ &st->sk->sk_refcnt),
st->pid, tag_str);
_bug_on_err_or_null(res);
kfree(tag_str);
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index 28cddc8..bfa2b6d 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -824,7 +824,11 @@
*/
void netlbl_sock_delattr(struct sock *sk)
{
- cipso_v4_sock_delattr(sk);
+ switch (sk->sk_family) {
+ case AF_INET:
+ cipso_v4_sock_delattr(sk);
+ break;
+ }
}
/**
@@ -987,7 +991,11 @@
*/
void netlbl_req_delattr(struct request_sock *req)
{
- cipso_v4_req_delattr(req);
+ switch (req->rsk_ops->family) {
+ case AF_INET:
+ cipso_v4_req_delattr(req);
+ break;
+ }
}
/**
diff --git a/net/netlink/Kconfig b/net/netlink/Kconfig
index 2c5e95e..5d6e8c0 100644
--- a/net/netlink/Kconfig
+++ b/net/netlink/Kconfig
@@ -2,15 +2,6 @@
# Netlink Sockets
#
-config NETLINK_MMAP
- bool "NETLINK: mmaped IO"
- ---help---
- This option enables support for memory mapped netlink IO. This
- reduces overhead by avoiding copying data between kernel- and
- userspace.
-
- If unsure, say N.
-
config NETLINK_DIAG
tristate "NETLINK: socket monitoring interface"
default n
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 5799eff..4f802a5 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -234,7 +234,7 @@
dev_hold(dev);
- if (netlink_skb_is_mmaped(skb) || is_vmalloc_addr(skb->head))
+ if (is_vmalloc_addr(skb->head))
nskb = netlink_to_full_skb(skb, GFP_ATOMIC);
else
nskb = skb_clone(skb, GFP_ATOMIC);
@@ -308,599 +308,8 @@
wake_up_interruptible(&nlk->wait);
}
-#ifdef CONFIG_NETLINK_MMAP
-static bool netlink_rx_is_mmaped(struct sock *sk)
-{
- return nlk_sk(sk)->rx_ring.pg_vec != NULL;
-}
-
-static bool netlink_tx_is_mmaped(struct sock *sk)
-{
- return nlk_sk(sk)->tx_ring.pg_vec != NULL;
-}
-
-static __pure struct page *pgvec_to_page(const void *addr)
-{
- if (is_vmalloc_addr(addr))
- return vmalloc_to_page(addr);
- else
- return virt_to_page(addr);
-}
-
-static void free_pg_vec(void **pg_vec, unsigned int order, unsigned int len)
-{
- unsigned int i;
-
- for (i = 0; i < len; i++) {
- if (pg_vec[i] != NULL) {
- if (is_vmalloc_addr(pg_vec[i]))
- vfree(pg_vec[i]);
- else
- free_pages((unsigned long)pg_vec[i], order);
- }
- }
- kfree(pg_vec);
-}
-
-static void *alloc_one_pg_vec_page(unsigned long order)
-{
- void *buffer;
- gfp_t gfp_flags = GFP_KERNEL | __GFP_COMP | __GFP_ZERO |
- __GFP_NOWARN | __GFP_NORETRY;
-
- buffer = (void *)__get_free_pages(gfp_flags, order);
- if (buffer != NULL)
- return buffer;
-
- buffer = vzalloc((1 << order) * PAGE_SIZE);
- if (buffer != NULL)
- return buffer;
-
- gfp_flags &= ~__GFP_NORETRY;
- return (void *)__get_free_pages(gfp_flags, order);
-}
-
-static void **alloc_pg_vec(struct netlink_sock *nlk,
- struct nl_mmap_req *req, unsigned int order)
-{
- unsigned int block_nr = req->nm_block_nr;
- unsigned int i;
- void **pg_vec;
-
- pg_vec = kcalloc(block_nr, sizeof(void *), GFP_KERNEL);
- if (pg_vec == NULL)
- return NULL;
-
- for (i = 0; i < block_nr; i++) {
- pg_vec[i] = alloc_one_pg_vec_page(order);
- if (pg_vec[i] == NULL)
- goto err1;
- }
-
- return pg_vec;
-err1:
- free_pg_vec(pg_vec, order, block_nr);
- return NULL;
-}
-
-
-static void
-__netlink_set_ring(struct sock *sk, struct nl_mmap_req *req, bool tx_ring, void **pg_vec,
- unsigned int order)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
- struct sk_buff_head *queue;
- struct netlink_ring *ring;
-
- queue = tx_ring ? &sk->sk_write_queue : &sk->sk_receive_queue;
- ring = tx_ring ? &nlk->tx_ring : &nlk->rx_ring;
-
- spin_lock_bh(&queue->lock);
-
- ring->frame_max = req->nm_frame_nr - 1;
- ring->head = 0;
- ring->frame_size = req->nm_frame_size;
- ring->pg_vec_pages = req->nm_block_size / PAGE_SIZE;
-
- swap(ring->pg_vec_len, req->nm_block_nr);
- swap(ring->pg_vec_order, order);
- swap(ring->pg_vec, pg_vec);
-
- __skb_queue_purge(queue);
- spin_unlock_bh(&queue->lock);
-
- WARN_ON(atomic_read(&nlk->mapped));
-
- if (pg_vec)
- free_pg_vec(pg_vec, order, req->nm_block_nr);
-}
-
-static int netlink_set_ring(struct sock *sk, struct nl_mmap_req *req,
- bool tx_ring)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
- struct netlink_ring *ring;
- void **pg_vec = NULL;
- unsigned int order = 0;
-
- ring = tx_ring ? &nlk->tx_ring : &nlk->rx_ring;
-
- if (atomic_read(&nlk->mapped))
- return -EBUSY;
- if (atomic_read(&ring->pending))
- return -EBUSY;
-
- if (req->nm_block_nr) {
- if (ring->pg_vec != NULL)
- return -EBUSY;
-
- if ((int)req->nm_block_size <= 0)
- return -EINVAL;
- if (!PAGE_ALIGNED(req->nm_block_size))
- return -EINVAL;
- if (req->nm_frame_size < NL_MMAP_HDRLEN)
- return -EINVAL;
- if (!IS_ALIGNED(req->nm_frame_size, NL_MMAP_MSG_ALIGNMENT))
- return -EINVAL;
-
- ring->frames_per_block = req->nm_block_size /
- req->nm_frame_size;
- if (ring->frames_per_block == 0)
- return -EINVAL;
- if (ring->frames_per_block * req->nm_block_nr !=
- req->nm_frame_nr)
- return -EINVAL;
-
- order = get_order(req->nm_block_size);
- pg_vec = alloc_pg_vec(nlk, req, order);
- if (pg_vec == NULL)
- return -ENOMEM;
- } else {
- if (req->nm_frame_nr)
- return -EINVAL;
- }
-
- mutex_lock(&nlk->pg_vec_lock);
- if (atomic_read(&nlk->mapped) == 0) {
- __netlink_set_ring(sk, req, tx_ring, pg_vec, order);
- mutex_unlock(&nlk->pg_vec_lock);
- return 0;
- }
-
- mutex_unlock(&nlk->pg_vec_lock);
-
- if (pg_vec)
- free_pg_vec(pg_vec, order, req->nm_block_nr);
-
- return -EBUSY;
-}
-
-static void netlink_mm_open(struct vm_area_struct *vma)
-{
- struct file *file = vma->vm_file;
- struct socket *sock = file->private_data;
- struct sock *sk = sock->sk;
-
- if (sk)
- atomic_inc(&nlk_sk(sk)->mapped);
-}
-
-static void netlink_mm_close(struct vm_area_struct *vma)
-{
- struct file *file = vma->vm_file;
- struct socket *sock = file->private_data;
- struct sock *sk = sock->sk;
-
- if (sk)
- atomic_dec(&nlk_sk(sk)->mapped);
-}
-
-static const struct vm_operations_struct netlink_mmap_ops = {
- .open = netlink_mm_open,
- .close = netlink_mm_close,
-};
-
-static int netlink_mmap(struct file *file, struct socket *sock,
- struct vm_area_struct *vma)
-{
- struct sock *sk = sock->sk;
- struct netlink_sock *nlk = nlk_sk(sk);
- struct netlink_ring *ring;
- unsigned long start, size, expected;
- unsigned int i;
- int err = -EINVAL;
-
- if (vma->vm_pgoff)
- return -EINVAL;
-
- mutex_lock(&nlk->pg_vec_lock);
-
- expected = 0;
- for (ring = &nlk->rx_ring; ring <= &nlk->tx_ring; ring++) {
- if (ring->pg_vec == NULL)
- continue;
- expected += ring->pg_vec_len * ring->pg_vec_pages * PAGE_SIZE;
- }
-
- if (expected == 0)
- goto out;
-
- size = vma->vm_end - vma->vm_start;
- if (size != expected)
- goto out;
-
- start = vma->vm_start;
- for (ring = &nlk->rx_ring; ring <= &nlk->tx_ring; ring++) {
- if (ring->pg_vec == NULL)
- continue;
-
- for (i = 0; i < ring->pg_vec_len; i++) {
- struct page *page;
- void *kaddr = ring->pg_vec[i];
- unsigned int pg_num;
-
- for (pg_num = 0; pg_num < ring->pg_vec_pages; pg_num++) {
- page = pgvec_to_page(kaddr);
- err = vm_insert_page(vma, start, page);
- if (err < 0)
- goto out;
- start += PAGE_SIZE;
- kaddr += PAGE_SIZE;
- }
- }
- }
-
- atomic_inc(&nlk->mapped);
- vma->vm_ops = &netlink_mmap_ops;
- err = 0;
-out:
- mutex_unlock(&nlk->pg_vec_lock);
- return err;
-}
-
-static void netlink_frame_flush_dcache(const struct nl_mmap_hdr *hdr, unsigned int nm_len)
-{
-#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE == 1
- struct page *p_start, *p_end;
-
- /* First page is flushed through netlink_{get,set}_status */
- p_start = pgvec_to_page(hdr + PAGE_SIZE);
- p_end = pgvec_to_page((void *)hdr + NL_MMAP_HDRLEN + nm_len - 1);
- while (p_start <= p_end) {
- flush_dcache_page(p_start);
- p_start++;
- }
-#endif
-}
-
-static enum nl_mmap_status netlink_get_status(const struct nl_mmap_hdr *hdr)
-{
- smp_rmb();
- flush_dcache_page(pgvec_to_page(hdr));
- return hdr->nm_status;
-}
-
-static void netlink_set_status(struct nl_mmap_hdr *hdr,
- enum nl_mmap_status status)
-{
- smp_mb();
- hdr->nm_status = status;
- flush_dcache_page(pgvec_to_page(hdr));
-}
-
-static struct nl_mmap_hdr *
-__netlink_lookup_frame(const struct netlink_ring *ring, unsigned int pos)
-{
- unsigned int pg_vec_pos, frame_off;
-
- pg_vec_pos = pos / ring->frames_per_block;
- frame_off = pos % ring->frames_per_block;
-
- return ring->pg_vec[pg_vec_pos] + (frame_off * ring->frame_size);
-}
-
-static struct nl_mmap_hdr *
-netlink_lookup_frame(const struct netlink_ring *ring, unsigned int pos,
- enum nl_mmap_status status)
-{
- struct nl_mmap_hdr *hdr;
-
- hdr = __netlink_lookup_frame(ring, pos);
- if (netlink_get_status(hdr) != status)
- return NULL;
-
- return hdr;
-}
-
-static struct nl_mmap_hdr *
-netlink_current_frame(const struct netlink_ring *ring,
- enum nl_mmap_status status)
-{
- return netlink_lookup_frame(ring, ring->head, status);
-}
-
-static struct nl_mmap_hdr *
-netlink_previous_frame(const struct netlink_ring *ring,
- enum nl_mmap_status status)
-{
- unsigned int prev;
-
- prev = ring->head ? ring->head - 1 : ring->frame_max;
- return netlink_lookup_frame(ring, prev, status);
-}
-
-static void netlink_increment_head(struct netlink_ring *ring)
-{
- ring->head = ring->head != ring->frame_max ? ring->head + 1 : 0;
-}
-
-static void netlink_forward_ring(struct netlink_ring *ring)
-{
- unsigned int head = ring->head, pos = head;
- const struct nl_mmap_hdr *hdr;
-
- do {
- hdr = __netlink_lookup_frame(ring, pos);
- if (hdr->nm_status == NL_MMAP_STATUS_UNUSED)
- break;
- if (hdr->nm_status != NL_MMAP_STATUS_SKIP)
- break;
- netlink_increment_head(ring);
- } while (ring->head != head);
-}
-
-static bool netlink_dump_space(struct netlink_sock *nlk)
-{
- struct netlink_ring *ring = &nlk->rx_ring;
- struct nl_mmap_hdr *hdr;
- unsigned int n;
-
- hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED);
- if (hdr == NULL)
- return false;
-
- n = ring->head + ring->frame_max / 2;
- if (n > ring->frame_max)
- n -= ring->frame_max;
-
- hdr = __netlink_lookup_frame(ring, n);
-
- return hdr->nm_status == NL_MMAP_STATUS_UNUSED;
-}
-
-static unsigned int netlink_poll(struct file *file, struct socket *sock,
- poll_table *wait)
-{
- struct sock *sk = sock->sk;
- struct netlink_sock *nlk = nlk_sk(sk);
- unsigned int mask;
- int err;
-
- if (nlk->rx_ring.pg_vec != NULL) {
- /* Memory mapped sockets don't call recvmsg(), so flow control
- * for dumps is performed here. A dump is allowed to continue
- * if at least half the ring is unused.
- */
- while (nlk->cb_running && netlink_dump_space(nlk)) {
- err = netlink_dump(sk);
- if (err < 0) {
- sk->sk_err = -err;
- sk->sk_error_report(sk);
- break;
- }
- }
- netlink_rcv_wake(sk);
- }
-
- mask = datagram_poll(file, sock, wait);
-
- spin_lock_bh(&sk->sk_receive_queue.lock);
- if (nlk->rx_ring.pg_vec) {
- netlink_forward_ring(&nlk->rx_ring);
- if (!netlink_previous_frame(&nlk->rx_ring, NL_MMAP_STATUS_UNUSED))
- mask |= POLLIN | POLLRDNORM;
- }
- spin_unlock_bh(&sk->sk_receive_queue.lock);
-
- spin_lock_bh(&sk->sk_write_queue.lock);
- if (nlk->tx_ring.pg_vec) {
- if (netlink_current_frame(&nlk->tx_ring, NL_MMAP_STATUS_UNUSED))
- mask |= POLLOUT | POLLWRNORM;
- }
- spin_unlock_bh(&sk->sk_write_queue.lock);
-
- return mask;
-}
-
-static struct nl_mmap_hdr *netlink_mmap_hdr(struct sk_buff *skb)
-{
- return (struct nl_mmap_hdr *)(skb->head - NL_MMAP_HDRLEN);
-}
-
-static void netlink_ring_setup_skb(struct sk_buff *skb, struct sock *sk,
- struct netlink_ring *ring,
- struct nl_mmap_hdr *hdr)
-{
- unsigned int size;
- void *data;
-
- size = ring->frame_size - NL_MMAP_HDRLEN;
- data = (void *)hdr + NL_MMAP_HDRLEN;
-
- skb->head = data;
- skb->data = data;
- skb_reset_tail_pointer(skb);
- skb->end = skb->tail + size;
- skb->len = 0;
-
- skb->destructor = netlink_skb_destructor;
- NETLINK_CB(skb).flags |= NETLINK_SKB_MMAPED;
- NETLINK_CB(skb).sk = sk;
-}
-
-static int netlink_mmap_sendmsg(struct sock *sk, struct msghdr *msg,
- u32 dst_portid, u32 dst_group,
- struct sock_iocb *siocb)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
- struct netlink_ring *ring;
- struct nl_mmap_hdr *hdr;
- struct sk_buff *skb;
- unsigned int maxlen;
- int err = 0, len = 0;
-
- mutex_lock(&nlk->pg_vec_lock);
-
- ring = &nlk->tx_ring;
- maxlen = ring->frame_size - NL_MMAP_HDRLEN;
-
- do {
- unsigned int nm_len;
-
- hdr = netlink_current_frame(ring, NL_MMAP_STATUS_VALID);
- if (hdr == NULL) {
- if (!(msg->msg_flags & MSG_DONTWAIT) &&
- atomic_read(&nlk->tx_ring.pending))
- schedule();
- continue;
- }
-
- nm_len = ACCESS_ONCE(hdr->nm_len);
- if (nm_len > maxlen) {
- err = -EINVAL;
- goto out;
- }
-
- netlink_frame_flush_dcache(hdr, nm_len);
-
- skb = alloc_skb(nm_len, GFP_KERNEL);
- if (skb == NULL) {
- err = -ENOBUFS;
- goto out;
- }
- __skb_put(skb, nm_len);
- memcpy(skb->data, (void *)hdr + NL_MMAP_HDRLEN, nm_len);
- netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED);
-
- netlink_increment_head(ring);
-
- NETLINK_CB(skb).portid = nlk->portid;
- NETLINK_CB(skb).dst_group = dst_group;
- NETLINK_CB(skb).creds = siocb->scm->creds;
-
- err = security_netlink_send(sk, skb);
- if (err) {
- kfree_skb(skb);
- goto out;
- }
-
- if (unlikely(dst_group)) {
- atomic_inc(&skb->users);
- netlink_broadcast(sk, skb, dst_portid, dst_group,
- GFP_KERNEL);
- }
- err = netlink_unicast(sk, skb, dst_portid,
- msg->msg_flags & MSG_DONTWAIT);
- if (err < 0)
- goto out;
- len += err;
-
- } while (hdr != NULL ||
- (!(msg->msg_flags & MSG_DONTWAIT) &&
- atomic_read(&nlk->tx_ring.pending)));
-
- if (len > 0)
- err = len;
-out:
- mutex_unlock(&nlk->pg_vec_lock);
- return err;
-}
-
-static void netlink_queue_mmaped_skb(struct sock *sk, struct sk_buff *skb)
-{
- struct nl_mmap_hdr *hdr;
-
- hdr = netlink_mmap_hdr(skb);
- hdr->nm_len = skb->len;
- hdr->nm_group = NETLINK_CB(skb).dst_group;
- hdr->nm_pid = NETLINK_CB(skb).creds.pid;
- hdr->nm_uid = from_kuid(sk_user_ns(sk), NETLINK_CB(skb).creds.uid);
- hdr->nm_gid = from_kgid(sk_user_ns(sk), NETLINK_CB(skb).creds.gid);
- netlink_frame_flush_dcache(hdr, hdr->nm_len);
- netlink_set_status(hdr, NL_MMAP_STATUS_VALID);
-
- NETLINK_CB(skb).flags |= NETLINK_SKB_DELIVERED;
- kfree_skb(skb);
-}
-
-static void netlink_ring_set_copied(struct sock *sk, struct sk_buff *skb)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
- struct netlink_ring *ring = &nlk->rx_ring;
- struct nl_mmap_hdr *hdr;
-
- spin_lock_bh(&sk->sk_receive_queue.lock);
- hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED);
- if (hdr == NULL) {
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- kfree_skb(skb);
- netlink_overrun(sk);
- return;
- }
- netlink_increment_head(ring);
- __skb_queue_tail(&sk->sk_receive_queue, skb);
- spin_unlock_bh(&sk->sk_receive_queue.lock);
-
- hdr->nm_len = skb->len;
- hdr->nm_group = NETLINK_CB(skb).dst_group;
- hdr->nm_pid = NETLINK_CB(skb).creds.pid;
- hdr->nm_uid = from_kuid(sk_user_ns(sk), NETLINK_CB(skb).creds.uid);
- hdr->nm_gid = from_kgid(sk_user_ns(sk), NETLINK_CB(skb).creds.gid);
- netlink_set_status(hdr, NL_MMAP_STATUS_COPY);
-}
-
-#else /* CONFIG_NETLINK_MMAP */
-#define netlink_rx_is_mmaped(sk) false
-#define netlink_tx_is_mmaped(sk) false
-#define netlink_mmap sock_no_mmap
-#define netlink_poll datagram_poll
-#define netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, siocb) 0
-#endif /* CONFIG_NETLINK_MMAP */
-
static void netlink_skb_destructor(struct sk_buff *skb)
{
-#ifdef CONFIG_NETLINK_MMAP
- struct nl_mmap_hdr *hdr;
- struct netlink_ring *ring;
- struct sock *sk;
-
- /* If a packet from the kernel to userspace was freed because of an
- * error without being delivered to userspace, the kernel must reset
- * the status. In the direction userspace to kernel, the status is
- * always reset here after the packet was processed and freed.
- */
- if (netlink_skb_is_mmaped(skb)) {
- hdr = netlink_mmap_hdr(skb);
- sk = NETLINK_CB(skb).sk;
-
- if (NETLINK_CB(skb).flags & NETLINK_SKB_TX) {
- netlink_set_status(hdr, NL_MMAP_STATUS_UNUSED);
- ring = &nlk_sk(sk)->tx_ring;
- } else {
- if (!(NETLINK_CB(skb).flags & NETLINK_SKB_DELIVERED)) {
- hdr->nm_len = 0;
- netlink_set_status(hdr, NL_MMAP_STATUS_VALID);
- }
- ring = &nlk_sk(sk)->rx_ring;
- }
-
- WARN_ON(atomic_read(&ring->pending) == 0);
- atomic_dec(&ring->pending);
- sock_put(sk);
-
- skb->head = NULL;
- }
-#endif
if (is_vmalloc_addr(skb->head)) {
if (!skb->cloned ||
!atomic_dec_return(&(skb_shinfo(skb)->dataref)))
@@ -934,18 +343,6 @@
}
skb_queue_purge(&sk->sk_receive_queue);
-#ifdef CONFIG_NETLINK_MMAP
- if (1) {
- struct nl_mmap_req req;
-
- memset(&req, 0, sizeof(req));
- if (nlk->rx_ring.pg_vec)
- __netlink_set_ring(sk, &req, false, NULL, 0);
- memset(&req, 0, sizeof(req));
- if (nlk->tx_ring.pg_vec)
- __netlink_set_ring(sk, &req, true, NULL, 0);
- }
-#endif /* CONFIG_NETLINK_MMAP */
if (!sock_flag(sk, SOCK_DEAD)) {
printk(KERN_ERR "Freeing alive netlink socket %p\n", sk);
@@ -1153,9 +550,6 @@
mutex_init(nlk->cb_mutex);
}
init_waitqueue_head(&nlk->wait);
-#ifdef CONFIG_NETLINK_MMAP
- mutex_init(&nlk->pg_vec_lock);
-#endif
sk->sk_destruct = netlink_sock_destruct;
sk->sk_protocol = protocol;
@@ -1653,9 +1047,8 @@
nlk = nlk_sk(sk);
- if ((atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
- test_bit(NETLINK_CONGESTED, &nlk->state)) &&
- !netlink_skb_is_mmaped(skb)) {
+ if (atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf ||
+ test_bit(NETLINK_CONGESTED, &nlk->state)) {
DECLARE_WAITQUEUE(wait, current);
if (!*timeo) {
if (!ssk || netlink_is_kernel(ssk))
@@ -1693,14 +1086,7 @@
netlink_deliver_tap(skb);
-#ifdef CONFIG_NETLINK_MMAP
- if (netlink_skb_is_mmaped(skb))
- netlink_queue_mmaped_skb(sk, skb);
- else if (netlink_rx_is_mmaped(sk))
- netlink_ring_set_copied(sk, skb);
- else
-#endif /* CONFIG_NETLINK_MMAP */
- skb_queue_tail(&sk->sk_receive_queue, skb);
+ skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_data_ready(sk);
return len;
}
@@ -1724,9 +1110,6 @@
int delta;
WARN_ON(skb->sk != NULL);
- if (netlink_skb_is_mmaped(skb))
- return skb;
-
delta = skb->end - skb->tail;
if (is_vmalloc_addr(skb->head) || delta * 2 < skb->truesize)
return skb;
@@ -1805,66 +1188,6 @@
struct sk_buff *netlink_alloc_skb(struct sock *ssk, unsigned int size,
u32 dst_portid, gfp_t gfp_mask)
{
-#ifdef CONFIG_NETLINK_MMAP
- struct sock *sk = NULL;
- struct sk_buff *skb;
- struct netlink_ring *ring;
- struct nl_mmap_hdr *hdr;
- unsigned int maxlen;
-
- sk = netlink_getsockbyportid(ssk, dst_portid);
- if (IS_ERR(sk))
- goto out;
-
- ring = &nlk_sk(sk)->rx_ring;
- /* fast-path without atomic ops for common case: non-mmaped receiver */
- if (ring->pg_vec == NULL)
- goto out_put;
-
- if (ring->frame_size - NL_MMAP_HDRLEN < size)
- goto out_put;
-
- skb = alloc_skb_head(gfp_mask);
- if (skb == NULL)
- goto err1;
-
- spin_lock_bh(&sk->sk_receive_queue.lock);
- /* check again under lock */
- if (ring->pg_vec == NULL)
- goto out_free;
-
- /* check again under lock */
- maxlen = ring->frame_size - NL_MMAP_HDRLEN;
- if (maxlen < size)
- goto out_free;
-
- netlink_forward_ring(ring);
- hdr = netlink_current_frame(ring, NL_MMAP_STATUS_UNUSED);
- if (hdr == NULL)
- goto err2;
- netlink_ring_setup_skb(skb, sk, ring, hdr);
- netlink_set_status(hdr, NL_MMAP_STATUS_RESERVED);
- atomic_inc(&ring->pending);
- netlink_increment_head(ring);
-
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- return skb;
-
-err2:
- kfree_skb(skb);
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- netlink_overrun(sk);
-err1:
- sock_put(sk);
- return NULL;
-
-out_free:
- kfree_skb(skb);
- spin_unlock_bh(&sk->sk_receive_queue.lock);
-out_put:
- sock_put(sk);
-out:
-#endif
return alloc_skb(size, gfp_mask);
}
EXPORT_SYMBOL_GPL(netlink_alloc_skb);
@@ -2126,8 +1449,7 @@
if (level != SOL_NETLINK)
return -ENOPROTOOPT;
- if (optname != NETLINK_RX_RING && optname != NETLINK_TX_RING &&
- optlen >= sizeof(int) &&
+ if (optlen >= sizeof(int) &&
get_user(val, (unsigned int __user *)optval))
return -EFAULT;
@@ -2180,25 +1502,6 @@
}
err = 0;
break;
-#ifdef CONFIG_NETLINK_MMAP
- case NETLINK_RX_RING:
- case NETLINK_TX_RING: {
- struct nl_mmap_req req;
-
- /* Rings might consume more memory than queue limits, require
- * CAP_NET_ADMIN.
- */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- if (optlen < sizeof(req))
- return -EINVAL;
- if (copy_from_user(&req, optval, sizeof(req)))
- return -EFAULT;
- err = netlink_set_ring(sk, &req,
- optname == NETLINK_TX_RING);
- break;
- }
-#endif /* CONFIG_NETLINK_MMAP */
default:
err = -ENOPROTOOPT;
}
@@ -2311,13 +1614,6 @@
goto out;
}
- if (netlink_tx_is_mmaped(sk) &&
- msg->msg_iov->iov_base == NULL) {
- err = netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group,
- siocb);
- goto out;
- }
-
err = -EMSGSIZE;
if (len > sk->sk_sndbuf - 32)
goto out;
@@ -2398,7 +1694,7 @@
/* Record the max length of recvmsg() calls for future allocations */
nlk->max_recvmsg_len = max(nlk->max_recvmsg_len, len);
nlk->max_recvmsg_len = min_t(size_t, nlk->max_recvmsg_len,
- 16384);
+ SKB_WITH_OVERHEAD(32768));
copied = data_skb->len;
if (len < copied) {
@@ -2643,8 +1939,7 @@
cb = &nlk->cb;
alloc_size = max_t(int, cb->min_dump_alloc, NLMSG_GOODSIZE);
- if (!netlink_rx_is_mmaped(sk) &&
- atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
+ if (atomic_read(&sk->sk_rmem_alloc) >= sk->sk_rcvbuf)
goto errout_skb;
/* NLMSG_GOODSIZE is small to avoid high order allocations being
@@ -2656,9 +1951,8 @@
skb = netlink_alloc_skb(sk,
nlk->max_recvmsg_len,
nlk->portid,
- GFP_KERNEL |
- __GFP_NOWARN |
- __GFP_NORETRY);
+ (GFP_KERNEL & ~__GFP_WAIT) |
+ __GFP_NOWARN | __GFP_NORETRY);
/* available room should be exact amount to avoid MSG_TRUNC */
if (skb)
skb_reserve(skb, skb_tailroom(skb) -
@@ -2666,7 +1960,7 @@
}
if (!skb)
skb = netlink_alloc_skb(sk, alloc_size, nlk->portid,
- GFP_KERNEL);
+ (GFP_KERNEL & ~__GFP_WAIT));
if (!skb)
goto errout_skb;
netlink_skb_set_owner_r(skb, sk);
@@ -2722,16 +2016,7 @@
struct netlink_sock *nlk;
int ret;
- /* Memory mapped dump requests need to be copied to avoid looping
- * on the pending state in netlink_mmap_sendmsg() while the CB hold
- * a reference to the skb.
- */
- if (netlink_skb_is_mmaped(skb)) {
- skb = skb_copy(skb, GFP_KERNEL);
- if (skb == NULL)
- return -ENOBUFS;
- } else
- atomic_inc(&skb->users);
+ atomic_inc(&skb->users);
sk = netlink_lookup(sock_net(ssk), ssk->sk_protocol, NETLINK_CB(skb).portid);
if (sk == NULL) {
@@ -3072,7 +2357,7 @@
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = netlink_getname,
- .poll = netlink_poll,
+ .poll = datagram_poll,
.ioctl = sock_no_ioctl,
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
@@ -3080,7 +2365,7 @@
.getsockopt = netlink_getsockopt,
.sendmsg = netlink_sendmsg,
.recvmsg = netlink_recvmsg,
- .mmap = netlink_mmap,
+ .mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
diff --git a/net/netlink/af_netlink.h b/net/netlink/af_netlink.h
index 3951874..c6bd3dd 100644
--- a/net/netlink/af_netlink.h
+++ b/net/netlink/af_netlink.h
@@ -42,12 +42,6 @@
int (*netlink_bind)(int group);
void (*netlink_unbind)(int group);
struct module *module;
-#ifdef CONFIG_NETLINK_MMAP
- struct mutex pg_vec_lock;
- struct netlink_ring rx_ring;
- struct netlink_ring tx_ring;
- atomic_t mapped;
-#endif /* CONFIG_NETLINK_MMAP */
struct rhash_head node;
};
@@ -57,15 +51,6 @@
return container_of(sk, struct netlink_sock, sk);
}
-static inline bool netlink_skb_is_mmaped(const struct sk_buff *skb)
-{
-#ifdef CONFIG_NETLINK_MMAP
- return NETLINK_CB(skb).flags & NETLINK_SKB_MMAPED;
-#else
- return false;
-#endif /* CONFIG_NETLINK_MMAP */
-}
-
struct netlink_table {
struct rhashtable hash;
struct hlist_head mc_list;
diff --git a/net/netlink/diag.c b/net/netlink/diag.c
index de8c74a..dd5f2ff 100644
--- a/net/netlink/diag.c
+++ b/net/netlink/diag.c
@@ -8,41 +8,6 @@
#include "af_netlink.h"
-#ifdef CONFIG_NETLINK_MMAP
-static int sk_diag_put_ring(struct netlink_ring *ring, int nl_type,
- struct sk_buff *nlskb)
-{
- struct netlink_diag_ring ndr;
-
- ndr.ndr_block_size = ring->pg_vec_pages << PAGE_SHIFT;
- ndr.ndr_block_nr = ring->pg_vec_len;
- ndr.ndr_frame_size = ring->frame_size;
- ndr.ndr_frame_nr = ring->frame_max + 1;
-
- return nla_put(nlskb, nl_type, sizeof(ndr), &ndr);
-}
-
-static int sk_diag_put_rings_cfg(struct sock *sk, struct sk_buff *nlskb)
-{
- struct netlink_sock *nlk = nlk_sk(sk);
- int ret;
-
- mutex_lock(&nlk->pg_vec_lock);
- ret = sk_diag_put_ring(&nlk->rx_ring, NETLINK_DIAG_RX_RING, nlskb);
- if (!ret)
- ret = sk_diag_put_ring(&nlk->tx_ring, NETLINK_DIAG_TX_RING,
- nlskb);
- mutex_unlock(&nlk->pg_vec_lock);
-
- return ret;
-}
-#else
-static int sk_diag_put_rings_cfg(struct sock *sk, struct sk_buff *nlskb)
-{
- return 0;
-}
-#endif
-
static int sk_diag_dump_groups(struct sock *sk, struct sk_buff *nlskb)
{
struct netlink_sock *nlk = nlk_sk(sk);
@@ -87,10 +52,6 @@
sock_diag_put_meminfo(sk, skb, NETLINK_DIAG_MEMINFO))
goto out_nlmsg_trim;
- if ((req->ndiag_show & NDIAG_SHOW_RING_CFG) &&
- sk_diag_put_rings_cfg(sk, skb))
- goto out_nlmsg_trim;
-
return nlmsg_end(skb, nlh);
out_nlmsg_trim:
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 1f2b481..6e078e8 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -240,7 +240,7 @@
static int packet_direct_xmit(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
- netdev_features_t features;
+ struct sk_buff *orig_skb = skb;
struct netdev_queue *txq;
int ret = NETDEV_TX_BUSY;
@@ -248,9 +248,8 @@
!netif_carrier_ok(dev)))
goto drop;
- features = netif_skb_features(skb);
- if (skb_needs_linearize(skb, features) &&
- __skb_linearize(skb))
+ skb = validate_xmit_skb_list(skb, dev);
+ if (skb != orig_skb)
goto drop;
txq = skb_get_tx_queue(dev, skb);
@@ -270,7 +269,7 @@
return ret;
drop:
atomic_long_inc(&dev->tx_dropped);
- kfree_skb(skb);
+ kfree_skb_list(skb);
return NET_XMIT_DROP;
}
@@ -2744,7 +2743,7 @@
int addr_len)
{
struct sock *sk = sock->sk;
- char name[15];
+ char name[sizeof(uaddr->sa_data) + 1];
/*
* Check legality
@@ -2752,7 +2751,11 @@
if (addr_len != sizeof(struct sockaddr))
return -EINVAL;
- strlcpy(name, uaddr->sa_data, sizeof(name));
+ /* uaddr->sa_data comes from the userspace, it's not guaranteed to be
+ * zero-terminated.
+ */
+ memcpy(name, uaddr->sa_data, sizeof(uaddr->sa_data));
+ name[sizeof(uaddr->sa_data)] = 0;
return packet_do_bind(sk, name, 0, pkt_sk(sk)->num);
}
@@ -3148,6 +3151,7 @@
i->ifindex = mreq->mr_ifindex;
i->alen = mreq->mr_alen;
memcpy(i->addr, mreq->mr_address, i->alen);
+ memset(i->addr + i->alen, 0, sizeof(i->addr) - i->alen);
i->count = 1;
i->next = po->mclist;
po->mclist = i;
@@ -3558,6 +3562,7 @@
}
if (msg == NETDEV_UNREGISTER) {
packet_cached_dev_reset(po);
+ fanout_release(sk);
po->ifindex = -1;
if (po->prot_hook.dev)
dev_put(po->prot_hook.dev);
diff --git a/net/rds/cong.c b/net/rds/cong.c
index e5b65ac..cec4c4e 100644
--- a/net/rds/cong.c
+++ b/net/rds/cong.c
@@ -285,7 +285,7 @@
i = be16_to_cpu(port) / RDS_CONG_MAP_PAGE_BITS;
off = be16_to_cpu(port) % RDS_CONG_MAP_PAGE_BITS;
- __set_bit_le(off, (void *)map->m_page_addrs[i]);
+ set_bit_le(off, (void *)map->m_page_addrs[i]);
}
void rds_cong_clear_bit(struct rds_cong_map *map, __be16 port)
@@ -299,7 +299,7 @@
i = be16_to_cpu(port) / RDS_CONG_MAP_PAGE_BITS;
off = be16_to_cpu(port) % RDS_CONG_MAP_PAGE_BITS;
- __clear_bit_le(off, (void *)map->m_page_addrs[i]);
+ clear_bit_le(off, (void *)map->m_page_addrs[i]);
}
static int rds_cong_test_bit(struct rds_cong_map *map, __be16 port)
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 3d43e49..df763d5 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -801,10 +801,8 @@
goto out_module_put;
err = a.ops->walk(skb, &dcb, RTM_DELACTION, &a);
- if (err < 0)
+ if (err <= 0)
goto out_module_put;
- if (err == 0)
- goto noflush_out;
nla_nest_end(skb, nest);
@@ -821,7 +819,6 @@
out_module_put:
module_put(a.ops->owner);
err_out:
-noflush_out:
kfree_skb(skb);
return err;
}
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 8884b26..3e21cf1 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -740,14 +740,15 @@
return 0;
}
-void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n)
+void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n,
+ unsigned int len)
{
const struct Qdisc_class_ops *cops;
unsigned long cl;
u32 parentid;
int drops;
- if (n == 0)
+ if (n == 0 && len == 0)
return;
drops = max_t(int, n, 0);
while ((parentid = sch->parent)) {
@@ -766,10 +767,11 @@
cops->put(sch, cl);
}
sch->q.qlen -= n;
+ sch->qstats.backlog -= len;
__qdisc_qstats_drop(sch, drops);
}
}
-EXPORT_SYMBOL(qdisc_tree_decrease_qlen);
+EXPORT_SYMBOL(qdisc_tree_reduce_backlog);
static void notify_and_destroy(struct net *net, struct sk_buff *skb,
struct nlmsghdr *n, u32 clid,
@@ -1189,12 +1191,7 @@
qdisc_len = q->q.qlen;
if (q->ops->change(q, &req.attr) != 0)
pr_err("%s(): qdisc change failed", __func__);
- } else {
- WARN_ONCE(1, "%s(): called on queue which does %s",
- __func__, "not support change() operation");
}
- } else {
- WARN_ONCE(1, "%s(): called on bad queue", __func__);
}
return qdisc_len;
}
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index beeb75f..f6e7a60 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -1624,13 +1624,8 @@
new->reshape_fail = cbq_reshape_fail;
#endif
}
- sch_tree_lock(sch);
- *old = cl->q;
- cl->q = new;
- qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
- qdisc_reset(*old);
- sch_tree_unlock(sch);
+ *old = qdisc_replace(sch, new, &cl->q);
return 0;
}
@@ -1914,7 +1909,7 @@
{
struct cbq_sched_data *q = qdisc_priv(sch);
struct cbq_class *cl = (struct cbq_class *)arg;
- unsigned int qlen;
+ unsigned int qlen, backlog;
if (cl->filters || cl->children || cl == &q->link)
return -EBUSY;
@@ -1922,8 +1917,9 @@
sch_tree_lock(sch);
qlen = cl->q->q.qlen;
+ backlog = cl->q->qstats.backlog;
qdisc_reset(cl->q);
- qdisc_tree_decrease_qlen(cl->q, qlen);
+ qdisc_tree_reduce_backlog(cl->q, qlen, backlog);
if (cl->next_alive)
cbq_deactivate_class(cl);
diff --git a/net/sched/sch_choke.c b/net/sched/sch_choke.c
index c009eb9..3f6437db 100644
--- a/net/sched/sch_choke.c
+++ b/net/sched/sch_choke.c
@@ -128,8 +128,8 @@
choke_zap_tail_holes(q);
qdisc_qstats_backlog_dec(sch, skb);
+ qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(skb));
qdisc_drop(skb, sch);
- qdisc_tree_decrease_qlen(sch, 1);
--sch->q.qlen;
}
@@ -449,6 +449,7 @@
old = q->tab;
if (old) {
unsigned int oqlen = sch->q.qlen, tail = 0;
+ unsigned dropped = 0;
while (q->head != q->tail) {
struct sk_buff *skb = q->tab[q->head];
@@ -460,11 +461,12 @@
ntab[tail++] = skb;
continue;
}
+ dropped += qdisc_pkt_len(skb);
qdisc_qstats_backlog_dec(sch, skb);
--sch->q.qlen;
qdisc_drop(skb, sch);
}
- qdisc_tree_decrease_qlen(sch, oqlen - sch->q.qlen);
+ qdisc_tree_reduce_backlog(sch, oqlen - sch->q.qlen, dropped);
q->head = 0;
q->tail = tail;
}
diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c
index de28f8e..0d60ea5 100644
--- a/net/sched/sch_codel.c
+++ b/net/sched/sch_codel.c
@@ -79,12 +79,13 @@
skb = codel_dequeue(sch, &q->params, &q->vars, &q->stats, dequeue);
- /* We cant call qdisc_tree_decrease_qlen() if our qlen is 0,
+ /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
* or HTB crashes. Defer it for next round.
*/
if (q->stats.drop_count && sch->q.qlen) {
- qdisc_tree_decrease_qlen(sch, q->stats.drop_count);
+ qdisc_tree_reduce_backlog(sch, q->stats.drop_count, q->stats.drop_len);
q->stats.drop_count = 0;
+ q->stats.drop_len = 0;
}
if (skb)
qdisc_bstats_update(sch, skb);
@@ -115,7 +116,7 @@
{
struct codel_sched_data *q = qdisc_priv(sch);
struct nlattr *tb[TCA_CODEL_MAX + 1];
- unsigned int qlen;
+ unsigned int qlen, dropped = 0;
int err;
if (!opt)
@@ -149,10 +150,11 @@
while (sch->q.qlen > sch->limit) {
struct sk_buff *skb = __skb_dequeue(&sch->q);
+ dropped += qdisc_pkt_len(skb);
qdisc_qstats_backlog_dec(sch, skb);
qdisc_drop(skb, sch);
}
- qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen);
+ qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped);
sch_tree_unlock(sch);
return 0;
diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c
index 3387060..e599803 100644
--- a/net/sched/sch_drr.c
+++ b/net/sched/sch_drr.c
@@ -53,9 +53,10 @@
static void drr_purge_queue(struct drr_class *cl)
{
unsigned int len = cl->qdisc->q.qlen;
+ unsigned int backlog = cl->qdisc->qstats.backlog;
qdisc_reset(cl->qdisc);
- qdisc_tree_decrease_qlen(cl->qdisc, len);
+ qdisc_tree_reduce_backlog(cl->qdisc, len, backlog);
}
static const struct nla_policy drr_policy[TCA_DRR_MAX + 1] = {
@@ -226,11 +227,7 @@
new = &noop_qdisc;
}
- sch_tree_lock(sch);
- drr_purge_queue(cl);
- *old = cl->qdisc;
- cl->qdisc = new;
- sch_tree_unlock(sch);
+ *old = qdisc_replace(sch, new, &cl->qdisc);
return 0;
}
diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c
index 227114f..eb87a2a 100644
--- a/net/sched/sch_dsmark.c
+++ b/net/sched/sch_dsmark.c
@@ -67,13 +67,7 @@
new = &noop_qdisc;
}
- sch_tree_lock(sch);
- *old = p->q;
- p->q = new;
- qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
- qdisc_reset(*old);
- sch_tree_unlock(sch);
-
+ *old = qdisc_replace(sch, new, &p->q);
return 0;
}
@@ -262,6 +256,7 @@
return err;
}
+ qdisc_qstats_backlog_inc(sch, skb);
sch->q.qlen++;
return NET_XMIT_SUCCESS;
@@ -284,6 +279,7 @@
return NULL;
qdisc_bstats_update(sch, skb);
+ qdisc_qstats_backlog_dec(sch, skb);
sch->q.qlen--;
index = skb->tc_index & (p->indices - 1);
@@ -399,6 +395,7 @@
pr_debug("%s(sch %p,[qdisc %p])\n", __func__, sch, p);
qdisc_reset(p->q);
+ sch->qstats.backlog = 0;
sch->q.qlen = 0;
}
diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c
index cbd7e1f..01fe9d5 100644
--- a/net/sched/sch_fq.c
+++ b/net/sched/sch_fq.c
@@ -644,6 +644,7 @@
struct fq_sched_data *q = qdisc_priv(sch);
struct nlattr *tb[TCA_FQ_MAX + 1];
int err, drop_count = 0;
+ unsigned drop_len = 0;
u32 fq_log;
if (!opt)
@@ -709,10 +710,11 @@
if (!skb)
break;
+ drop_len += qdisc_pkt_len(skb);
kfree_skb(skb);
drop_count++;
}
- qdisc_tree_decrease_qlen(sch, drop_count);
+ qdisc_tree_reduce_backlog(sch, drop_count, drop_len);
sch_tree_unlock(sch);
return err;
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index 1e52dec..8d21f1b 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -173,7 +173,7 @@
static int fq_codel_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
- unsigned int idx;
+ unsigned int idx, prev_backlog;
struct fq_codel_flow *flow;
int uninitialized_var(ret);
@@ -201,6 +201,7 @@
if (++sch->q.qlen <= sch->limit)
return NET_XMIT_SUCCESS;
+ prev_backlog = sch->qstats.backlog;
q->drop_overlimit++;
/* Return Congestion Notification only if we dropped a packet
* from this flow.
@@ -209,7 +210,7 @@
return NET_XMIT_CN;
/* As we dropped a packet, better let upper stack know this */
- qdisc_tree_decrease_qlen(sch, 1);
+ qdisc_tree_reduce_backlog(sch, 1, prev_backlog - sch->qstats.backlog);
return NET_XMIT_SUCCESS;
}
@@ -239,6 +240,7 @@
struct fq_codel_flow *flow;
struct list_head *head;
u32 prev_drop_count, prev_ecn_mark;
+ unsigned int prev_backlog;
begin:
head = &q->new_flows;
@@ -257,6 +259,7 @@
prev_drop_count = q->cstats.drop_count;
prev_ecn_mark = q->cstats.ecn_mark;
+ prev_backlog = sch->qstats.backlog;
skb = codel_dequeue(sch, &q->cparams, &flow->cvars, &q->cstats,
dequeue);
@@ -274,12 +277,14 @@
}
qdisc_bstats_update(sch, skb);
flow->deficit -= qdisc_pkt_len(skb);
- /* We cant call qdisc_tree_decrease_qlen() if our qlen is 0,
+ /* We cant call qdisc_tree_reduce_backlog() if our qlen is 0,
* or HTB crashes. Defer it for next round.
*/
if (q->cstats.drop_count && sch->q.qlen) {
- qdisc_tree_decrease_qlen(sch, q->cstats.drop_count);
+ qdisc_tree_reduce_backlog(sch, q->cstats.drop_count,
+ q->cstats.drop_len);
q->cstats.drop_count = 0;
+ q->cstats.drop_len = 0;
}
return skb;
}
@@ -347,11 +352,13 @@
while (sch->q.qlen > sch->limit) {
struct sk_buff *skb = fq_codel_dequeue(sch);
+ q->cstats.drop_len += qdisc_pkt_len(skb);
kfree_skb(skb);
q->cstats.drop_count++;
}
- qdisc_tree_decrease_qlen(sch, q->cstats.drop_count);
+ qdisc_tree_reduce_backlog(sch, q->cstats.drop_count, q->cstats.drop_len);
q->cstats.drop_count = 0;
+ q->cstats.drop_len = 0;
sch_tree_unlock(sch);
return 0;
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 9598ef2..62a9bc7 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -159,7 +159,7 @@
if (validate)
skb = validate_xmit_skb_list(skb, dev);
- if (skb) {
+ if (likely(skb)) {
HARD_TX_LOCK(dev, txq, smp_processor_id());
if (!netif_xmit_frozen_or_stopped(txq)) {
if (unlikely(skb->fast_forwarded))
@@ -170,6 +170,9 @@
}
HARD_TX_UNLOCK(dev, txq);
+ } else {
+ spin_lock(root_lock);
+ return qdisc_qlen(q);
}
spin_lock(root_lock);
diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
index e6c7416..d3e21da 100644
--- a/net/sched/sch_hfsc.c
+++ b/net/sched/sch_hfsc.c
@@ -895,9 +895,10 @@
hfsc_purge_queue(struct Qdisc *sch, struct hfsc_class *cl)
{
unsigned int len = cl->qdisc->q.qlen;
+ unsigned int backlog = cl->qdisc->qstats.backlog;
qdisc_reset(cl->qdisc);
- qdisc_tree_decrease_qlen(cl->qdisc, len);
+ qdisc_tree_reduce_backlog(cl->qdisc, len, backlog);
}
static void
@@ -1215,11 +1216,7 @@
new = &noop_qdisc;
}
- sch_tree_lock(sch);
- hfsc_purge_queue(sch, cl);
- *old = cl->qdisc;
- cl->qdisc = new;
- sch_tree_unlock(sch);
+ *old = qdisc_replace(sch, new, &cl->qdisc);
return 0;
}
diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c
index 15d3aab..792c6f3 100644
--- a/net/sched/sch_hhf.c
+++ b/net/sched/sch_hhf.c
@@ -390,6 +390,7 @@
struct hhf_sched_data *q = qdisc_priv(sch);
enum wdrr_bucket_idx idx;
struct wdrr_bucket *bucket;
+ unsigned int prev_backlog;
idx = hhf_classify(skb, sch);
@@ -417,6 +418,7 @@
if (++sch->q.qlen <= sch->limit)
return NET_XMIT_SUCCESS;
+ prev_backlog = sch->qstats.backlog;
q->drop_overlimit++;
/* Return Congestion Notification only if we dropped a packet from this
* bucket.
@@ -425,7 +427,7 @@
return NET_XMIT_CN;
/* As we dropped a packet, better let upper stack know this. */
- qdisc_tree_decrease_qlen(sch, 1);
+ qdisc_tree_reduce_backlog(sch, 1, prev_backlog - sch->qstats.backlog);
return NET_XMIT_SUCCESS;
}
@@ -535,7 +537,7 @@
{
struct hhf_sched_data *q = qdisc_priv(sch);
struct nlattr *tb[TCA_HHF_MAX + 1];
- unsigned int qlen;
+ unsigned int qlen, prev_backlog;
int err;
u64 non_hh_quantum;
u32 new_quantum = q->quantum;
@@ -585,12 +587,14 @@
}
qlen = sch->q.qlen;
+ prev_backlog = sch->qstats.backlog;
while (sch->q.qlen > sch->limit) {
struct sk_buff *skb = hhf_dequeue(sch);
kfree_skb(skb);
}
- qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen);
+ qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen,
+ prev_backlog - sch->qstats.backlog);
sch_tree_unlock(sch);
return 0;
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index f1acb0f..ccff00640 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -600,6 +600,7 @@
htb_activate(q, cl);
}
+ qdisc_qstats_backlog_inc(sch, skb);
sch->q.qlen++;
return NET_XMIT_SUCCESS;
}
@@ -889,6 +890,7 @@
ok:
qdisc_bstats_update(sch, skb);
qdisc_unthrottled(sch);
+ qdisc_qstats_backlog_dec(sch, skb);
sch->q.qlen--;
return skb;
}
@@ -955,6 +957,7 @@
unsigned int len;
if (cl->un.leaf.q->ops->drop &&
(len = cl->un.leaf.q->ops->drop(cl->un.leaf.q))) {
+ sch->qstats.backlog -= len;
sch->q.qlen--;
if (!cl->un.leaf.q->q.qlen)
htb_deactivate(q, cl);
@@ -984,12 +987,12 @@
}
cl->prio_activity = 0;
cl->cmode = HTB_CAN_SEND;
-
}
}
qdisc_watchdog_cancel(&q->watchdog);
__skb_queue_purge(&q->direct_queue);
sch->q.qlen = 0;
+ sch->qstats.backlog = 0;
memset(q->hlevel, 0, sizeof(q->hlevel));
memset(q->row_mask, 0, sizeof(q->row_mask));
for (i = 0; i < TC_HTB_NUMPRIO; i++)
@@ -1165,14 +1168,7 @@
cl->common.classid)) == NULL)
return -ENOBUFS;
- sch_tree_lock(sch);
- *old = cl->un.leaf.q;
- cl->un.leaf.q = new;
- if (*old != NULL) {
- qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
- qdisc_reset(*old);
- }
- sch_tree_unlock(sch);
+ *old = qdisc_replace(sch, new, &cl->un.leaf.q);
return 0;
}
@@ -1274,7 +1270,6 @@
{
struct htb_sched *q = qdisc_priv(sch);
struct htb_class *cl = (struct htb_class *)arg;
- unsigned int qlen;
struct Qdisc *new_q = NULL;
int last_child = 0;
@@ -1294,9 +1289,11 @@
sch_tree_lock(sch);
if (!cl->level) {
- qlen = cl->un.leaf.q->q.qlen;
+ unsigned int qlen = cl->un.leaf.q->q.qlen;
+ unsigned int backlog = cl->un.leaf.q->qstats.backlog;
+
qdisc_reset(cl->un.leaf.q);
- qdisc_tree_decrease_qlen(cl->un.leaf.q, qlen);
+ qdisc_tree_reduce_backlog(cl->un.leaf.q, qlen, backlog);
}
/* delete from hash and active; remainder in destroy_class */
@@ -1430,10 +1427,11 @@
sch_tree_lock(sch);
if (parent && !parent->level) {
unsigned int qlen = parent->un.leaf.q->q.qlen;
+ unsigned int backlog = parent->un.leaf.q->qstats.backlog;
/* turn parent into inner node */
qdisc_reset(parent->un.leaf.q);
- qdisc_tree_decrease_qlen(parent->un.leaf.q, qlen);
+ qdisc_tree_reduce_backlog(parent->un.leaf.q, qlen, backlog);
qdisc_destroy(parent->un.leaf.q);
if (parent->prio_activity)
htb_deactivate(q, parent);
diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
index 42dd218..23437d6 100644
--- a/net/sched/sch_multiq.c
+++ b/net/sched/sch_multiq.c
@@ -218,7 +218,8 @@
if (q->queues[i] != &noop_qdisc) {
struct Qdisc *child = q->queues[i];
q->queues[i] = &noop_qdisc;
- qdisc_tree_decrease_qlen(child, child->q.qlen);
+ qdisc_tree_reduce_backlog(child, child->q.qlen,
+ child->qstats.backlog);
qdisc_destroy(child);
}
}
@@ -238,8 +239,9 @@
q->queues[i] = child;
if (old != &noop_qdisc) {
- qdisc_tree_decrease_qlen(old,
- old->q.qlen);
+ qdisc_tree_reduce_backlog(old,
+ old->q.qlen,
+ old->qstats.backlog);
qdisc_destroy(old);
}
sch_tree_unlock(sch);
@@ -303,13 +305,7 @@
if (new == NULL)
new = &noop_qdisc;
- sch_tree_lock(sch);
- *old = q->queues[band];
- q->queues[band] = new;
- qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
- qdisc_reset(*old);
- sch_tree_unlock(sch);
-
+ *old = qdisc_replace(sch, new, &q->queues[band]);
return 0;
}
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index b343319..f60db2b 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -408,6 +408,25 @@
sch->q.qlen++;
}
+/* netem can't properly corrupt a megapacket (like we get from GSO), so instead
+ * when we statistically choose to corrupt one, we instead segment it, returning
+ * the first packet to be corrupted, and re-enqueue the remaining frames
+ */
+static struct sk_buff *netem_segment(struct sk_buff *skb, struct Qdisc *sch)
+{
+ struct sk_buff *segs;
+ netdev_features_t features = netif_skb_features(skb);
+
+ segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
+
+ if (IS_ERR_OR_NULL(segs)) {
+ qdisc_reshape_fail(skb, sch);
+ return NULL;
+ }
+ consume_skb(skb);
+ return segs;
+}
+
/*
* Insert one skb into qdisc.
* Note: parent depends on return value to account for queue length.
@@ -420,7 +439,11 @@
/* We don't fill cb now as skb_unshare() may invalidate it */
struct netem_skb_cb *cb;
struct sk_buff *skb2;
+ struct sk_buff *segs = NULL;
+ unsigned int len = 0, last_len, prev_len = qdisc_pkt_len(skb);
+ int nb = 0;
int count = 1;
+ int rc = NET_XMIT_SUCCESS;
/* Random duplication */
if (q->duplicate && q->duplicate >= get_crandom(&q->dup_cor))
@@ -466,10 +489,23 @@
* do it now in software before we mangle it.
*/
if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) {
+ if (skb_is_gso(skb)) {
+ segs = netem_segment(skb, sch);
+ if (!segs)
+ return NET_XMIT_DROP;
+ } else {
+ segs = skb;
+ }
+
+ skb = segs;
+ segs = segs->next;
+
if (!(skb = skb_unshare(skb, GFP_ATOMIC)) ||
(skb->ip_summed == CHECKSUM_PARTIAL &&
- skb_checksum_help(skb)))
- return qdisc_drop(skb, sch);
+ skb_checksum_help(skb))) {
+ rc = qdisc_drop(skb, sch);
+ goto finish_segs;
+ }
skb->data[prandom_u32() % skb_headlen(skb)] ^=
1<<(prandom_u32() % 8);
@@ -529,6 +565,27 @@
sch->qstats.requeues++;
}
+finish_segs:
+ if (segs) {
+ while (segs) {
+ skb2 = segs->next;
+ segs->next = NULL;
+ qdisc_skb_cb(segs)->pkt_len = segs->len;
+ last_len = segs->len;
+ rc = qdisc_enqueue(segs, sch);
+ if (rc != NET_XMIT_SUCCESS) {
+ if (net_xmit_drop_count(rc))
+ qdisc_qstats_drop(sch);
+ } else {
+ nb++;
+ len += last_len;
+ }
+ segs = skb2;
+ }
+ sch->q.qlen += nb;
+ if (nb > 1)
+ qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len);
+ }
return NET_XMIT_SUCCESS;
}
@@ -610,7 +667,8 @@
if (unlikely(err != NET_XMIT_SUCCESS)) {
if (net_xmit_drop_count(err)) {
qdisc_qstats_drop(sch);
- qdisc_tree_decrease_qlen(sch, 1);
+ qdisc_tree_reduce_backlog(sch, 1,
+ qdisc_pkt_len(skb));
}
}
goto tfifo_dequeue;
@@ -1049,15 +1107,7 @@
{
struct netem_sched_data *q = qdisc_priv(sch);
- sch_tree_lock(sch);
- *old = q->qdisc;
- q->qdisc = new;
- if (*old) {
- qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
- qdisc_reset(*old);
- }
- sch_tree_unlock(sch);
-
+ *old = qdisc_replace(sch, new, &q->qdisc);
return 0;
}
diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c
index b783a44..71ae3b9 100644
--- a/net/sched/sch_pie.c
+++ b/net/sched/sch_pie.c
@@ -183,7 +183,7 @@
{
struct pie_sched_data *q = qdisc_priv(sch);
struct nlattr *tb[TCA_PIE_MAX + 1];
- unsigned int qlen;
+ unsigned int qlen, dropped = 0;
int err;
if (!opt)
@@ -232,10 +232,11 @@
while (sch->q.qlen > sch->limit) {
struct sk_buff *skb = __skb_dequeue(&sch->q);
+ dropped += qdisc_pkt_len(skb);
qdisc_qstats_backlog_dec(sch, skb);
qdisc_drop(skb, sch);
}
- qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen);
+ qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped);
sch_tree_unlock(sch);
return 0;
diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
index 36384e8..9e977a7 100644
--- a/net/sched/sch_prio.c
+++ b/net/sched/sch_prio.c
@@ -205,7 +205,7 @@
struct Qdisc *child = q->queues[i];
q->queues[i] = &noop_qdisc;
if (child != &noop_qdisc) {
- qdisc_tree_decrease_qlen(child, child->q.qlen);
+ qdisc_tree_reduce_backlog(child, child->q.qlen, child->qstats.backlog);
qdisc_destroy(child);
}
}
@@ -224,8 +224,9 @@
q->queues[i] = child;
if (old != &noop_qdisc) {
- qdisc_tree_decrease_qlen(old,
- old->q.qlen);
+ qdisc_tree_reduce_backlog(old,
+ old->q.qlen,
+ old->qstats.backlog);
qdisc_destroy(old);
}
sch_tree_unlock(sch);
@@ -290,13 +291,7 @@
if (new == NULL)
new = &noop_qdisc;
- sch_tree_lock(sch);
- *old = q->queues[band];
- q->queues[band] = new;
- qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
- qdisc_reset(*old);
- sch_tree_unlock(sch);
-
+ *old = qdisc_replace(sch, new, &q->queues[band]);
return 0;
}
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index 3ec7e88..e2b8fd4 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -221,9 +221,10 @@
static void qfq_purge_queue(struct qfq_class *cl)
{
unsigned int len = cl->qdisc->q.qlen;
+ unsigned int backlog = cl->qdisc->qstats.backlog;
qdisc_reset(cl->qdisc);
- qdisc_tree_decrease_qlen(cl->qdisc, len);
+ qdisc_tree_reduce_backlog(cl->qdisc, len, backlog);
}
static const struct nla_policy qfq_policy[TCA_QFQ_MAX + 1] = {
@@ -619,11 +620,7 @@
new = &noop_qdisc;
}
- sch_tree_lock(sch);
- qfq_purge_queue(cl);
- *old = cl->qdisc;
- cl->qdisc = new;
- sch_tree_unlock(sch);
+ *old = qdisc_replace(sch, new, &cl->qdisc);
return 0;
}
diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c
index 6c0534cc..8c0508c 100644
--- a/net/sched/sch_red.c
+++ b/net/sched/sch_red.c
@@ -210,7 +210,8 @@
q->flags = ctl->flags;
q->limit = ctl->limit;
if (child) {
- qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen);
+ qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
+ q->qdisc->qstats.backlog);
qdisc_destroy(q->qdisc);
q->qdisc = child;
}
@@ -313,12 +314,7 @@
if (new == NULL)
new = &noop_qdisc;
- sch_tree_lock(sch);
- *old = q->qdisc;
- q->qdisc = new;
- qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
- qdisc_reset(*old);
- sch_tree_unlock(sch);
+ *old = qdisc_replace(sch, new, &q->qdisc);
return 0;
}
diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c
index 5819dd8..e1d634e 100644
--- a/net/sched/sch_sfb.c
+++ b/net/sched/sch_sfb.c
@@ -518,7 +518,8 @@
sch_tree_lock(sch);
- qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen);
+ qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
+ q->qdisc->qstats.backlog);
qdisc_destroy(q->qdisc);
q->qdisc = child;
@@ -614,12 +615,7 @@
if (new == NULL)
new = &noop_qdisc;
- sch_tree_lock(sch);
- *old = q->qdisc;
- q->qdisc = new;
- qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
- qdisc_reset(*old);
- sch_tree_unlock(sch);
+ *old = qdisc_replace(sch, new, &q->qdisc);
return 0;
}
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index b877140..4417fb2 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -369,7 +369,7 @@
sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
struct sfq_sched_data *q = qdisc_priv(sch);
- unsigned int hash;
+ unsigned int hash, dropped;
sfq_index x, qlen;
struct sfq_slot *slot;
int uninitialized_var(ret);
@@ -484,7 +484,7 @@
return NET_XMIT_SUCCESS;
qlen = slot->qlen;
- sfq_drop(sch);
+ dropped = sfq_drop(sch);
/* Return Congestion Notification only if we dropped a packet
* from this flow.
*/
@@ -492,7 +492,7 @@
return NET_XMIT_CN;
/* As we dropped a packet, better let upper stack know this */
- qdisc_tree_decrease_qlen(sch, 1);
+ qdisc_tree_reduce_backlog(sch, 1, dropped);
return NET_XMIT_SUCCESS;
}
@@ -560,6 +560,7 @@
struct sfq_slot *slot;
struct sk_buff_head list;
int dropped = 0;
+ unsigned int drop_len = 0;
__skb_queue_head_init(&list);
@@ -588,6 +589,7 @@
if (x >= SFQ_MAX_FLOWS) {
drop:
qdisc_qstats_backlog_dec(sch, skb);
+ drop_len += qdisc_pkt_len(skb);
kfree_skb(skb);
dropped++;
continue;
@@ -617,7 +619,7 @@
}
}
sch->q.qlen -= dropped;
- qdisc_tree_decrease_qlen(sch, dropped);
+ qdisc_tree_reduce_backlog(sch, dropped, drop_len);
}
static void sfq_perturbation(unsigned long arg)
@@ -641,7 +643,7 @@
struct sfq_sched_data *q = qdisc_priv(sch);
struct tc_sfq_qopt *ctl = nla_data(opt);
struct tc_sfq_qopt_v1 *ctl_v1 = NULL;
- unsigned int qlen;
+ unsigned int qlen, dropped = 0;
struct red_parms *p = NULL;
if (opt->nla_len < nla_attr_size(sizeof(*ctl)))
@@ -690,8 +692,8 @@
qlen = sch->q.qlen;
while (sch->q.qlen > q->limit)
- sfq_drop(sch);
- qdisc_tree_decrease_qlen(sch, qlen - sch->q.qlen);
+ dropped += sfq_drop(sch);
+ qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped);
del_timer(&q->perturb_timer);
if (q->perturb_period) {
diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index a4afde1..c2fbde7 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -160,6 +160,7 @@
struct tbf_sched_data *q = qdisc_priv(sch);
struct sk_buff *segs, *nskb;
netdev_features_t features = netif_skb_features(skb);
+ unsigned int len = 0, prev_len = qdisc_pkt_len(skb);
int ret, nb;
segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
@@ -172,6 +173,7 @@
nskb = segs->next;
segs->next = NULL;
qdisc_skb_cb(segs)->pkt_len = segs->len;
+ len += segs->len;
ret = qdisc_enqueue(segs, q->qdisc);
if (ret != NET_XMIT_SUCCESS) {
if (net_xmit_drop_count(ret))
@@ -183,7 +185,7 @@
}
sch->q.qlen += nb;
if (nb > 1)
- qdisc_tree_decrease_qlen(sch, 1 - nb);
+ qdisc_tree_reduce_backlog(sch, 1 - nb, prev_len - len);
consume_skb(skb);
return nb > 0 ? NET_XMIT_SUCCESS : NET_XMIT_DROP;
}
@@ -399,7 +401,8 @@
sch_tree_lock(sch);
if (child) {
- qdisc_tree_decrease_qlen(q->qdisc, q->qdisc->q.qlen);
+ qdisc_tree_reduce_backlog(q->qdisc, q->qdisc->q.qlen,
+ q->qdisc->qstats.backlog);
qdisc_destroy(q->qdisc);
q->qdisc = child;
}
@@ -502,13 +505,7 @@
if (new == NULL)
new = &noop_qdisc;
- sch_tree_lock(sch);
- *old = q->qdisc;
- q->qdisc = new;
- qdisc_tree_decrease_qlen(*old, (*old)->q.qlen);
- qdisc_reset(*old);
- sch_tree_unlock(sch);
-
+ *old = qdisc_replace(sch, new, &q->qdisc);
return 0;
}
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 497b6c3..29fa707 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -519,6 +519,8 @@
}
return 0;
}
+ if (addr1->v6.sin6_port != addr2->v6.sin6_port)
+ return 0;
if (!ipv6_addr_equal(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr))
return 0;
/* If this is a linklocal address, compare the scope_id. */
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index e6bb98e..690a973b 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -3426,6 +3426,12 @@
return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
commands);
+ /* Report violation if chunk len overflows */
+ ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
+ if (ch_end > skb_tail_pointer(skb))
+ return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
+ commands);
+
/* Now that we know we at least have a chunk header,
* do things that are type appropriate.
*/
@@ -3457,12 +3463,6 @@
}
}
- /* Report violation if chunk len overflows */
- ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
- if (ch_end > skb_tail_pointer(skb))
- return sctp_sf_violation_chunklen(net, ep, asoc, type, arg,
- commands);
-
ch = (sctp_chunkhdr_t *) ch_end;
} while (ch_end < skb_tail_pointer(skb));
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 72da7d5..92c920c9 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4385,7 +4385,7 @@
static int sctp_getsockopt_events(struct sock *sk, int len, char __user *optval,
int __user *optlen)
{
- if (len <= 0)
+ if (len == 0)
return -EINVAL;
if (len > sizeof(struct sctp_event_subscribe))
len = sizeof(struct sctp_event_subscribe);
@@ -4433,6 +4433,12 @@
if (!asoc)
return -EINVAL;
+ /* If there is a thread waiting on more sndbuf space for
+ * sending on this asoc, it cannot be peeled.
+ */
+ if (waitqueue_active(&asoc->wait))
+ return -EBUSY;
+
/* An association cannot be branched off from an already peeled-off
* socket, nor is this supported for tcp style sockets.
*/
@@ -5981,6 +5987,9 @@
if (get_user(len, optlen))
return -EFAULT;
+ if (len < 0)
+ return -EINVAL;
+
lock_sock(sk);
switch (optname) {
@@ -6391,6 +6400,9 @@
if (sock->state != SS_UNCONNECTED)
goto out;
+ if (!sctp_sstate(sk, LISTENING) && !sctp_sstate(sk, CLOSED))
+ goto out;
+
/* If backlog is zero, disable listening. */
if (!backlog) {
if (sctp_sstate(sk, CLOSED))
@@ -6962,7 +6974,6 @@
*/
release_sock(sk);
current_timeo = schedule_timeout(current_timeo);
- BUG_ON(sk != asoc->base.sk);
lock_sock(sk);
*timeo_p = current_timeo;
diff --git a/net/socket.c b/net/socket.c
index 6f886c4..2288281 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -536,9 +536,23 @@
return used;
}
+int sockfs_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ int err = simple_setattr(dentry, iattr);
+
+ if (!err && (iattr->ia_valid & ATTR_UID)) {
+ struct socket *sock = SOCKET_I(dentry->d_inode);
+
+ sock->sk->sk_uid = iattr->ia_uid;
+ }
+
+ return err;
+}
+
static const struct inode_operations sockfs_inode_ops = {
.getxattr = sockfs_getxattr,
.listxattr = sockfs_listxattr,
+ .setattr = sockfs_setattr,
};
/**
@@ -2394,8 +2408,10 @@
return err;
err = sock_error(sock->sk);
- if (err)
+ if (err) {
+ datagrams = err;
goto out_put;
+ }
entry = mmsg;
compat_entry = (struct compat_mmsghdr __user *)mmsg;
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 53ed8d3..81e29f0 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -539,9 +539,13 @@
return gss_new;
gss_msg = gss_add_msg(gss_new);
if (gss_msg == gss_new) {
- int res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
+ int res;
+ atomic_inc(&gss_msg->count);
+ res = rpc_queue_upcall(gss_new->pipe, &gss_new->msg);
if (res) {
gss_unhash_msg(gss_new);
+ atomic_dec(&gss_msg->count);
+ gss_release_msg(gss_new);
gss_msg = ERR_PTR(res);
}
} else
@@ -834,6 +838,7 @@
warn_gssd();
gss_release_msg(gss_msg);
}
+ gss_release_msg(gss_msg);
}
static void gss_pipe_dentry_destroy(struct dentry *dir,
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index c548ab2..d08f759 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -855,8 +855,8 @@
goto out;
if (svc_getnl(&buf->head[0]) != seq)
goto out;
- /* trim off the mic at the end before returning */
- xdr_buf_trim(buf, mic.len + 4);
+ /* trim off the mic and padding at the end before returning */
+ xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4);
stat = 0;
out:
kfree(mic.data);
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 14d38ec..11e7b55 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -1187,14 +1187,14 @@
}
crq->q.reader = 0;
- crq->item = cache_get(h);
crq->buf = buf;
crq->len = 0;
crq->readers = 0;
spin_lock(&queue_lock);
- if (test_bit(CACHE_PENDING, &h->flags))
+ if (test_bit(CACHE_PENDING, &h->flags)) {
+ crq->item = cache_get(h);
list_add_tail(&crq->q.list, &detail->queue);
- else
+ } else
/* Lost a race, no longer PENDING, so don't enqueue */
ret = -EAGAIN;
spin_unlock(&queue_lock);
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index ae46f01..7b738b53 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -439,7 +439,7 @@
return ERR_PTR(err);
}
-struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
+static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
struct rpc_xprt *xprt)
{
struct rpc_clnt *clnt = NULL;
@@ -471,7 +471,6 @@
return clnt;
}
-EXPORT_SYMBOL_GPL(rpc_create_xprt);
/**
* rpc_create - create an RPC client and transport with one call
@@ -497,6 +496,15 @@
};
char servername[48];
+ if (args->bc_xprt) {
+ WARN_ON(args->protocol != XPRT_TRANSPORT_BC_TCP);
+ xprt = args->bc_xprt->xpt_bc_xprt;
+ if (xprt) {
+ xprt_get(xprt);
+ return rpc_create_xprt(args, xprt);
+ }
+ }
+
if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS)
xprtargs.flags |= XPRT_CREATE_INFINITE_SLOTS;
if (args->flags & RPC_CLNT_CREATE_NO_IDLE_TIMEOUT)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 3e2eec6..29b1f4d 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -305,7 +305,7 @@
&unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) {
struct dentry *dentry = unix_sk(s)->path.dentry;
- if (dentry && dentry->d_inode == i) {
+ if (dentry && d_backing_inode(dentry) == i) {
sock_hold(s);
goto found;
}
@@ -898,7 +898,7 @@
err = kern_path(sunname->sun_path, LOOKUP_FOLLOW, &path);
if (err)
goto fail;
- inode = path.dentry->d_inode;
+ inode = d_backing_inode(path.dentry);
err = inode_permission(inode, MAY_WRITE);
if (err)
goto put_fail;
@@ -959,7 +959,7 @@
*/
err = security_path_mknod(&path, dentry, mode, 0);
if (!err) {
- err = vfs_mknod(path.dentry->d_inode, dentry, mode, 0);
+ err = vfs_mknod(d_inode(path.dentry), dentry, mode, 0);
if (!err) {
res->mnt = mntget(path.mnt);
res->dentry = dget(dentry);
@@ -1017,8 +1017,6 @@
struct path path;
umode_t mode = S_IFSOCK |
(SOCK_INODE(sock)->i_mode & ~current_umask());
- path.dentry = NULL;
- path.mnt = NULL;
err = unix_mknod(sun_path, mode, &path);
if (err) {
if (err == -EEXIST)
@@ -1027,7 +1025,7 @@
goto out_up;
}
addr->hash = UNIX_HASH_SIZE;
- hash = path.dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1);
+ hash = d_backing_inode(path.dentry)->i_ino & (UNIX_HASH_SIZE-1);
spin_lock(&unix_table_lock);
u->path = path;
list = &unix_socket_table[hash];
diff --git a/net/unix/diag.c b/net/unix/diag.c
index 86fa0f3..9d4218f 100644
--- a/net/unix/diag.c
+++ b/net/unix/diag.c
@@ -25,7 +25,7 @@
if (dentry) {
struct unix_diag_vfs uv = {
- .udiag_vfs_ino = dentry->d_inode->i_ino,
+ .udiag_vfs_ino = d_backing_inode(dentry)->i_ino,
.udiag_vfs_dev = dentry->d_sb->s_dev,
};
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index 6247787..58783dc 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -152,6 +152,7 @@
if (s) {
struct unix_sock *u = unix_sk(s);
+ BUG_ON(!atomic_long_read(&u->inflight));
BUG_ON(list_empty(&u->link));
if (atomic_long_dec_and_test(&u->inflight))
list_del_init(&u->link);
@@ -358,6 +359,14 @@
}
list_del(&cursor);
+ /* Now gc_candidates contains only garbage. Restore original
+ * inflight counters for these as well, and remove the skbuffs
+ * which are creating the cycle(s).
+ */
+ skb_queue_head_init(&hitlist);
+ list_for_each_entry(u, &gc_candidates, link)
+ scan_children(&u->sk, inc_inflight, &hitlist);
+
/*
* not_cycle_list contains those sockets which do not make up a
* cycle. Restore these to the inflight list.
@@ -368,15 +377,6 @@
list_move_tail(&u->link, &gc_inflight_list);
}
- /*
- * Now gc_candidates contains only garbage. Restore original
- * inflight counters for these as well, and remove the skbuffs
- * which are creating the cycle(s).
- */
- skb_queue_head_init(&hitlist);
- list_for_each_entry(u, &gc_candidates, link)
- scan_children(&u->sk, inc_inflight, &hitlist);
-
spin_unlock(&unix_gc_lock);
/* Here we are. Hitlist is filled. Die. */
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 85d232b..e8d3313 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -1796,27 +1796,8 @@
else if (sk->sk_shutdown & RCV_SHUTDOWN)
err = 0;
- if (copied > 0) {
- /* We only do these additional bookkeeping/notification steps
- * if we actually copied something out of the queue pair
- * instead of just peeking ahead.
- */
-
- if (!(flags & MSG_PEEK)) {
- /* If the other side has shutdown for sending and there
- * is nothing more to read, then modify the socket
- * state.
- */
- if (vsk->peer_shutdown & SEND_SHUTDOWN) {
- if (vsock_stream_has_data(vsk) <= 0) {
- sk->sk_state = SS_UNCONNECTED;
- sock_set_flag(sk, SOCK_DONE);
- sk->sk_state_change(sk);
- }
- }
- }
+ if (copied > 0)
err = copied;
- }
out_wait:
finish_wait(sk_sleep(sk), &wait);
diff --git a/net/wireless/core.c b/net/wireless/core.c
index e22f1de..335a924 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -419,6 +419,9 @@
rdev->wiphy.max_num_csa_counters = 1;
+ rdev->wiphy.max_sched_scan_plans = 1;
+ rdev->wiphy.max_sched_scan_plan_interval = U32_MAX;
+
return &rdev->wiphy;
}
EXPORT_SYMBOL(wiphy_new);
@@ -545,6 +548,13 @@
!rdev->ops->set_mac_acl)))
return -EINVAL;
+ /* assure only valid behaviours are flagged by driver
+ * hence subtract 2 as bit 0 is invalid.
+ */
+ if (WARN_ON(wiphy->bss_select_support &&
+ (wiphy->bss_select_support & ~(BIT(__NL80211_BSS_SELECT_ATTR_AFTER_LAST) - 2))))
+ return -EINVAL;
+
if (wiphy->addresses)
memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
@@ -659,6 +669,36 @@
nl80211_send_reg_change_event(&request);
}
+ /* Check that nobody globally advertises any capabilities they do not
+ * advertise on all possible interface types.
+ */
+ if (wiphy->extended_capabilities_len &&
+ wiphy->num_iftype_ext_capab &&
+ wiphy->iftype_ext_capab) {
+ u8 supported_on_all, j;
+ const struct wiphy_iftype_ext_capab *capab;
+
+ capab = wiphy->iftype_ext_capab;
+ for (j = 0; j < wiphy->extended_capabilities_len; j++) {
+ if (capab[0].extended_capabilities_len > j)
+ supported_on_all =
+ capab[0].extended_capabilities[j];
+ else
+ supported_on_all = 0x00;
+ for (i = 1; i < wiphy->num_iftype_ext_capab; i++) {
+ if (j >= capab[i].extended_capabilities_len) {
+ supported_on_all = 0x00;
+ break;
+ }
+ supported_on_all &=
+ capab[i].extended_capabilities[j];
+ }
+ if (WARN_ON(wiphy->extended_capabilities[j] &
+ ~supported_on_all))
+ break;
+ }
+ }
+
rdev->wiphy.registered = true;
rtnl_unlock();
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 5658033..2c0d3d3 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -125,6 +125,7 @@
struct list_head list;
struct list_head hidden_list;
struct rb_node rbn;
+ u64 ts_boottime;
unsigned long ts;
unsigned long refcount;
atomic_t hold;
@@ -202,7 +203,7 @@
size_t req_ie_len;
size_t resp_ie_len;
struct cfg80211_bss *bss;
- u16 status;
+ int status; /* -1 = failed; 0..65535 = status code */
} cr;
struct {
const u8 *req_ie;
@@ -215,6 +216,7 @@
const u8 *ie;
size_t ie_len;
u16 reason;
+ bool locally_generated;
} dc;
struct {
u8 bssid[ETH_ALEN];
@@ -348,7 +350,7 @@
void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len,
- u16 status, bool wextev,
+ int status, bool wextev,
struct cfg80211_bss *bss);
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
size_t ie_len, u16 reason, bool from_ap);
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 7aae329..72be2be 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -599,8 +599,25 @@
return err;
}
- if (!ether_addr_equal(mgmt->sa, wdev_address(wdev)))
- return -EINVAL;
+ if (!ether_addr_equal(mgmt->sa, wdev_address(wdev))) {
+ /* Allow random TA to be used with Public Action frames if the
+ * driver has indicated support for this. Otherwise, only allow
+ * the local address to be used.
+ */
+ if (!ieee80211_is_action(mgmt->frame_control) ||
+ mgmt->u.action.category != WLAN_CATEGORY_PUBLIC)
+ return -EINVAL;
+ if (!wdev->current_bss &&
+ !wiphy_ext_feature_isset(
+ &rdev->wiphy,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA))
+ return -EINVAL;
+ if (wdev->current_bss &&
+ !wiphy_ext_feature_isset(
+ &rdev->wiphy,
+ NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
+ return -EINVAL;
+ }
/* Transmit the Action frame as requested by user space */
return rdev_mgmt_tx(rdev, wdev, params, cookie);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index a23e372..86e8031 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -395,7 +395,14 @@
[NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 },
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
+ [NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
[NL80211_ATTR_PBSS] = { .type = NLA_FLAG },
+ [NL80211_ATTR_BSS_SELECT] = { .type = NLA_NESTED },
+ [NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
+ [NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] = { .type = NLA_S8 },
+ [NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = {
+ .len = sizeof(struct nl80211_bss_select_rssi_adjust)
+ },
};
/* policy for the key attributes */
@@ -473,6 +480,21 @@
[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
};
+static const struct nla_policy
+nl80211_plan_policy[NL80211_SCHED_SCAN_PLAN_MAX + 1] = {
+ [NL80211_SCHED_SCAN_PLAN_INTERVAL] = { .type = NLA_U32 },
+ [NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy
+nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = {
+ [NL80211_BSS_SELECT_ATTR_RSSI] = { .type = NLA_FLAG },
+ [NL80211_BSS_SELECT_ATTR_BAND_PREF] = { .type = NLA_U32 },
+ [NL80211_BSS_SELECT_ATTR_RSSI_ADJUST] = {
+ .len = sizeof(struct nl80211_bss_select_rssi_adjust)
+ },
+};
+
static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct cfg80211_registered_device **rdev,
@@ -1230,7 +1252,7 @@
struct nl80211_dump_wiphy_state {
s64 filter_wiphy;
long start;
- long split_start, band_start, chan_start;
+ long split_start, band_start, chan_start, capa_start;
bool split;
};
@@ -1288,7 +1310,14 @@
nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
rdev->wiphy.max_sched_scan_ie_len) ||
nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
- rdev->wiphy.max_match_sets))
+ rdev->wiphy.max_match_sets) ||
+ nla_put_u32(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+ rdev->wiphy.max_sched_scan_plans) ||
+ nla_put_u32(msg, NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+ rdev->wiphy.max_sched_scan_plan_interval) ||
+ nla_put_u32(msg, NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+ rdev->wiphy.max_sched_scan_plan_iterations))
+
goto nla_put_failure;
if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
@@ -1518,6 +1547,7 @@
if (rdev->wiphy.flags &
WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)
CMD(add_tx_ts, ADD_TX_TS);
+ CMD(update_connect_params, UPDATE_CONNECT_PARAMS);
}
/* add into the if now */
#undef CMD
@@ -1693,6 +1723,71 @@
rdev->wiphy.max_num_csa_counters))
goto nla_put_failure;
+ state->split_start++;
+ break;
+ case 13:
+ if (rdev->wiphy.num_iftype_ext_capab &&
+ rdev->wiphy.iftype_ext_capab) {
+ struct nlattr *nested_ext_capab, *nested;
+
+ nested = nla_nest_start(msg,
+ NL80211_ATTR_IFTYPE_EXT_CAPA);
+ if (!nested)
+ goto nla_put_failure;
+
+ for (i = state->capa_start;
+ i < rdev->wiphy.num_iftype_ext_capab; i++) {
+ const struct wiphy_iftype_ext_capab *capab;
+
+ capab = &rdev->wiphy.iftype_ext_capab[i];
+
+ nested_ext_capab = nla_nest_start(msg, i);
+ if (!nested_ext_capab ||
+ nla_put_u32(msg, NL80211_ATTR_IFTYPE,
+ capab->iftype) ||
+ nla_put(msg, NL80211_ATTR_EXT_CAPA,
+ capab->extended_capabilities_len,
+ capab->extended_capabilities) ||
+ nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
+ capab->extended_capabilities_len,
+ capab->extended_capabilities_mask))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nested_ext_capab);
+ if (state->split)
+ break;
+ }
+ nla_nest_end(msg, nested);
+ if (i < rdev->wiphy.num_iftype_ext_capab) {
+ state->capa_start = i + 1;
+ break;
+ }
+ }
+
+ if (nla_put(msg, NL80211_ATTR_EXT_FEATURES,
+ sizeof(rdev->wiphy.ext_features),
+ rdev->wiphy.ext_features))
+ goto nla_put_failure;
+
+ if (rdev->wiphy.bss_select_support) {
+ struct nlattr *nested;
+ u32 bss_select_support = rdev->wiphy.bss_select_support;
+
+ nested = nla_nest_start(msg, NL80211_ATTR_BSS_SELECT);
+ if (!nested)
+ goto nla_put_failure;
+
+ i = 0;
+ while (bss_select_support) {
+ if ((bss_select_support & 1) &&
+ nla_put_flag(msg, i))
+ goto nla_put_failure;
+ i++;
+ bss_select_support >>= 1;
+ }
+ nla_nest_end(msg, nested);
+ }
+
/* done */
state->split_start = 0;
break;
@@ -3111,6 +3206,291 @@
return err;
}
+static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
+ u8 *rates, u8 rates_len)
+{
+ u8 i;
+ u32 mask = 0;
+
+ for (i = 0; i < rates_len; i++) {
+ int rate = (rates[i] & 0x7f) * 5;
+ int ridx;
+
+ for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
+ struct ieee80211_rate *srate =
+ &sband->bitrates[ridx];
+ if (rate == srate->bitrate) {
+ mask |= 1 << ridx;
+ break;
+ }
+ }
+ if (ridx == sband->n_bitrates)
+ return 0; /* rate not found */
+ }
+
+ return mask;
+}
+
+static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
+ u8 *rates, u8 rates_len,
+ u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
+{
+ u8 i;
+
+ memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
+
+ for (i = 0; i < rates_len; i++) {
+ int ridx, rbit;
+
+ ridx = rates[i] / 8;
+ rbit = BIT(rates[i] % 8);
+
+ /* check validity */
+ if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
+ return false;
+
+ /* check availability */
+ if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
+ mcs[ridx] |= rbit;
+ else
+ return false;
+ }
+
+ return true;
+}
+
+static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
+{
+ u16 mcs_mask = 0;
+
+ switch (vht_mcs_map) {
+ case IEEE80211_VHT_MCS_NOT_SUPPORTED:
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_7:
+ mcs_mask = 0x00FF;
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_8:
+ mcs_mask = 0x01FF;
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_9:
+ mcs_mask = 0x03FF;
+ break;
+ default:
+ break;
+ }
+
+ return mcs_mask;
+}
+
+static void vht_build_mcs_mask(u16 vht_mcs_map,
+ u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
+{
+ u8 nss;
+
+ for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
+ vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
+ vht_mcs_map >>= 2;
+ }
+}
+
+static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
+ struct nl80211_txrate_vht *txrate,
+ u16 mcs[NL80211_VHT_NSS_MAX])
+{
+ u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+ u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
+ u8 i;
+
+ if (!sband->vht_cap.vht_supported)
+ return false;
+
+ memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
+
+ /* Build vht_mcs_mask from VHT capabilities */
+ vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
+
+ for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+ if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
+ mcs[i] = txrate->mcs[i];
+ else
+ return false;
+ }
+
+ return true;
+}
+
+static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
+ [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_RATES },
+ [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
+ .len = NL80211_MAX_SUPP_HT_RATES },
+ [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
+ [NL80211_TXRATE_GI] = { .type = NLA_U8 },
+};
+
+static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
+ struct cfg80211_bitrate_mask *mask)
+{
+ struct nlattr *tb[NL80211_TXRATE_MAX + 1];
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int rem, i;
+ struct nlattr *tx_rates;
+ struct ieee80211_supported_band *sband;
+ u16 vht_tx_mcs_map;
+
+ memset(mask, 0, sizeof(*mask));
+ /* Default to all rates enabled */
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
+ sband = rdev->wiphy.bands[i];
+
+ if (!sband)
+ continue;
+
+ mask->control[i].legacy = (1 << sband->n_bitrates) - 1;
+ memcpy(mask->control[i].ht_mcs,
+ sband->ht_cap.mcs.rx_mask,
+ sizeof(mask->control[i].ht_mcs));
+
+ if (!sband->vht_cap.vht_supported)
+ continue;
+
+ vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+ vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs);
+ }
+
+ /* if no rates are given set it back to the defaults */
+ if (!info->attrs[NL80211_ATTR_TX_RATES])
+ goto out;
+
+ /* The nested attribute uses enum nl80211_band as the index. This maps
+ * directly to the enum nl80211_band values used in cfg80211.
+ */
+ BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
+ nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
+ enum ieee80211_band band = nla_type(tx_rates);
+ int err;
+
+ if (band < 0 || band >= IEEE80211_NUM_BANDS)
+ return -EINVAL;
+ sband = rdev->wiphy.bands[band];
+ if (sband == NULL)
+ return -EINVAL;
+ err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
+ nla_len(tx_rates), nl80211_txattr_policy);
+ if (err)
+ return err;
+ if (tb[NL80211_TXRATE_LEGACY]) {
+ mask->control[band].legacy = rateset_to_mask(
+ sband,
+ nla_data(tb[NL80211_TXRATE_LEGACY]),
+ nla_len(tb[NL80211_TXRATE_LEGACY]));
+ if ((mask->control[band].legacy == 0) &&
+ nla_len(tb[NL80211_TXRATE_LEGACY]))
+ return -EINVAL;
+ }
+ if (tb[NL80211_TXRATE_HT]) {
+ if (!ht_rateset_to_mask(
+ sband,
+ nla_data(tb[NL80211_TXRATE_HT]),
+ nla_len(tb[NL80211_TXRATE_HT]),
+ mask->control[band].ht_mcs))
+ return -EINVAL;
+ }
+ if (tb[NL80211_TXRATE_VHT]) {
+ if (!vht_set_mcs_mask(
+ sband,
+ nla_data(tb[NL80211_TXRATE_VHT]),
+ mask->control[band].vht_mcs))
+ return -EINVAL;
+ }
+ if (tb[NL80211_TXRATE_GI]) {
+ mask->control[band].gi =
+ nla_get_u8(tb[NL80211_TXRATE_GI]);
+ if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI)
+ return -EINVAL;
+ }
+
+ if (mask->control[band].legacy == 0) {
+ /* don't allow empty legacy rates if HT or VHT
+ * are not even supported.
+ */
+ if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
+ rdev->wiphy.bands[band]->vht_cap.vht_supported))
+ return -EINVAL;
+
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+ if (mask->control[band].ht_mcs[i])
+ goto out;
+
+ for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+ if (mask->control[band].vht_mcs[i])
+ goto out;
+
+ /* legacy and mcs rates may not be both empty */
+ return -EINVAL;
+ }
+ }
+
+out:
+ return 0;
+}
+
+static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
+ enum nl80211_band band,
+ struct cfg80211_bitrate_mask *beacon_rate)
+{
+ u32 count_ht, count_vht, i;
+ u32 rate = beacon_rate->control[band].legacy;
+
+ /* Allow only one rate */
+ if (hweight32(rate) > 1)
+ return -EINVAL;
+
+ count_ht = 0;
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+ if (hweight8(beacon_rate->control[band].ht_mcs[i]) > 1) {
+ return -EINVAL;
+ } else if (beacon_rate->control[band].ht_mcs[i]) {
+ count_ht++;
+ if (count_ht > 1)
+ return -EINVAL;
+ }
+ if (count_ht && rate)
+ return -EINVAL;
+ }
+
+ count_vht = 0;
+ for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+ if (hweight16(beacon_rate->control[band].vht_mcs[i]) > 1) {
+ return -EINVAL;
+ } else if (beacon_rate->control[band].vht_mcs[i]) {
+ count_vht++;
+ if (count_vht > 1)
+ return -EINVAL;
+ }
+ if (count_vht && rate)
+ return -EINVAL;
+ }
+
+ if ((count_ht && count_vht) || (!rate && !count_ht && !count_vht))
+ return -EINVAL;
+
+ if (rate &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_BEACON_RATE_LEGACY))
+ return -EINVAL;
+ if (count_ht &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_BEACON_RATE_HT))
+ return -EINVAL;
+ if (count_vht &&
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_BEACON_RATE_VHT))
+ return -EINVAL;
+
+ return 0;
+}
+
static int nl80211_parse_beacon(struct nlattr *attrs[],
struct cfg80211_beacon_data *bcn)
{
@@ -3340,6 +3720,17 @@
wdev->iftype))
return -EINVAL;
+ if (info->attrs[NL80211_ATTR_TX_RATES]) {
+ err = nl80211_parse_tx_bitrate_mask(info, ¶ms.beacon_rate);
+ if (err)
+ return err;
+
+ err = validate_beacon_tx_rate(rdev, params.chandef.chan->band,
+ ¶ms.beacon_rate);
+ if (err)
+ return err;
+ }
+
if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
params.acl = parse_acl_data(&rdev->wiphy, info);
if (IS_ERR(params.acl))
@@ -5395,6 +5786,110 @@
return n_channels;
}
+static bool is_band_valid(struct wiphy *wiphy, enum ieee80211_band b)
+{
+ return b < IEEE80211_NUM_BANDS && wiphy->bands[b];
+}
+
+static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy,
+ struct cfg80211_bss_selection *bss_select)
+{
+ struct nlattr *attr[NL80211_BSS_SELECT_ATTR_MAX + 1];
+ struct nlattr *nest;
+ int err;
+ bool found = false;
+ int i;
+
+ /* only process one nested attribute */
+ nest = nla_data(nla);
+ if (!nla_ok(nest, nla_len(nest)))
+ return -EINVAL;
+
+ err = nla_parse(attr, NL80211_BSS_SELECT_ATTR_MAX, nla_data(nest),
+ nla_len(nest), nl80211_bss_select_policy);
+ if (err)
+ return err;
+
+ /* only one attribute may be given */
+ for (i = 0; i <= NL80211_BSS_SELECT_ATTR_MAX; i++) {
+ if (attr[i]) {
+ if (found)
+ return -EINVAL;
+ found = true;
+ }
+ }
+
+ bss_select->behaviour = __NL80211_BSS_SELECT_ATTR_INVALID;
+
+ if (attr[NL80211_BSS_SELECT_ATTR_RSSI])
+ bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI;
+
+ if (attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]) {
+ bss_select->behaviour = NL80211_BSS_SELECT_ATTR_BAND_PREF;
+ bss_select->param.band_pref =
+ nla_get_u32(attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]);
+ if (!is_band_valid(wiphy, bss_select->param.band_pref))
+ return -EINVAL;
+ }
+
+ if (attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]) {
+ struct nl80211_bss_select_rssi_adjust *adj_param;
+
+ adj_param = nla_data(attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]);
+ bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI_ADJUST;
+ bss_select->param.adjust.band = adj_param->band;
+ bss_select->param.adjust.delta = adj_param->delta;
+ if (!is_band_valid(wiphy, bss_select->param.adjust.band))
+ return -EINVAL;
+ }
+
+ /* user-space did not provide behaviour attribute */
+ if (bss_select->behaviour == __NL80211_BSS_SELECT_ATTR_INVALID)
+ return -EINVAL;
+
+ if (!(wiphy->bss_select_support & BIT(bss_select->behaviour)))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int nl80211_parse_random_mac(struct nlattr **attrs,
+ u8 *mac_addr, u8 *mac_addr_mask)
+{
+ int i;
+
+ if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) {
+ memset(mac_addr, 0, ETH_ALEN);
+ memset(mac_addr_mask, 0, ETH_ALEN);
+ mac_addr[0] = 0x2;
+ mac_addr_mask[0] = 0x3;
+
+ return 0;
+ }
+
+ /* need both or none */
+ if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_MAC_MASK])
+ return -EINVAL;
+
+ memcpy(mac_addr, nla_data(attrs[NL80211_ATTR_MAC]), ETH_ALEN);
+ memcpy(mac_addr_mask, nla_data(attrs[NL80211_ATTR_MAC_MASK]), ETH_ALEN);
+
+ /* don't allow or configure an mcast address */
+ if (!is_multicast_ether_addr(mac_addr_mask) ||
+ is_multicast_ether_addr(mac_addr))
+ return -EINVAL;
+
+ /*
+ * allow users to pass a MAC address that has bits set outside
+ * of the mask, but don't bother drivers with having to deal
+ * with such bits
+ */
+ for (i = 0; i < ETH_ALEN; i++)
+ mac_addr[i] &= mac_addr_mask[i];
+
+ return 0;
+}
+
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5572,12 +6067,44 @@
err = -EOPNOTSUPP;
goto out_free;
}
+
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ if (!(wiphy->features &
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ if (wdev->current_bss) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ err = nl80211_parse_random_mac(info->attrs,
+ request->mac_addr,
+ request->mac_addr_mask);
+ if (err)
+ goto out_free;
+ }
}
request->no_cck =
nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
- if (info->attrs[NL80211_ATTR_MAC])
+ /* Initial implementation used NL80211_ATTR_MAC to set the specific
+ * BSSID to scan for. This was problematic because that same attribute
+ * was already used for another purpose (local random MAC address). The
+ * NL80211_ATTR_BSSID attribute was added to fix this. For backwards
+ * compatibility with older userspace components, also use the
+ * NL80211_ATTR_MAC value here if it can be determined to be used for
+ * the specific BSSID use case instead of the random MAC address
+ * (NL80211_ATTR_SCAN_FLAGS is used to enable random MAC address use).
+ */
+ if (info->attrs[NL80211_ATTR_BSSID])
+ memcpy(request->bssid,
+ nla_data(info->attrs[NL80211_ATTR_BSSID]), ETH_ALEN);
+ else if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) &&
+ info->attrs[NL80211_ATTR_MAC])
memcpy(request->bssid, nla_data(info->attrs[NL80211_ATTR_MAC]),
ETH_ALEN);
else
@@ -5604,6 +6131,94 @@
return err;
}
+static int
+nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans,
+ struct cfg80211_sched_scan_request *request,
+ struct nlattr **attrs)
+{
+ int tmp, err, i = 0;
+ struct nlattr *attr;
+
+ if (!attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) {
+ u32 interval;
+
+ /*
+ * If scan plans are not specified,
+ * %NL80211_ATTR_SCHED_SCAN_INTERVAL must be specified. In this
+ * case one scan plan will be set with the specified scan
+ * interval and infinite number of iterations.
+ */
+ if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+ return -EINVAL;
+
+ interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
+ if (!interval)
+ return -EINVAL;
+
+ request->scan_plans[0].interval =
+ DIV_ROUND_UP(interval, MSEC_PER_SEC);
+ if (!request->scan_plans[0].interval)
+ return -EINVAL;
+
+ if (request->scan_plans[0].interval >
+ wiphy->max_sched_scan_plan_interval)
+ request->scan_plans[0].interval =
+ wiphy->max_sched_scan_plan_interval;
+
+ return 0;
+ }
+
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp) {
+ struct nlattr *plan[NL80211_SCHED_SCAN_PLAN_MAX + 1];
+
+ if (WARN_ON(i >= n_plans))
+ return -EINVAL;
+
+ err = nla_parse(plan, NL80211_SCHED_SCAN_PLAN_MAX,
+ nla_data(attr), nla_len(attr),
+ nl80211_plan_policy);
+ if (err)
+ return err;
+
+ if (!plan[NL80211_SCHED_SCAN_PLAN_INTERVAL])
+ return -EINVAL;
+
+ request->scan_plans[i].interval =
+ nla_get_u32(plan[NL80211_SCHED_SCAN_PLAN_INTERVAL]);
+ if (!request->scan_plans[i].interval ||
+ request->scan_plans[i].interval >
+ wiphy->max_sched_scan_plan_interval)
+ return -EINVAL;
+
+ if (plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]) {
+ request->scan_plans[i].iterations =
+ nla_get_u32(plan[
+ NL80211_SCHED_SCAN_PLAN_ITERATIONS]);
+ if (!request->scan_plans[i].iterations ||
+ (request->scan_plans[i].iterations >
+ wiphy->max_sched_scan_plan_iterations))
+ return -EINVAL;
+ } else if (i < n_plans - 1) {
+ /*
+ * All scan plans but the last one must specify
+ * a finite number of iterations
+ */
+ return -EINVAL;
+ }
+
+ i++;
+ }
+
+ /*
+ * The last scan plan must not specify the number of
+ * iterations, it is supposed to run infinitely
+ */
+ if (request->scan_plans[n_plans - 1].iterations)
+ return -EINVAL;
+
+ return 0;
+}
+
static int nl80211_abort_scan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5622,53 +6237,37 @@
return 0;
}
-static int nl80211_start_sched_scan(struct sk_buff *skb,
- struct genl_info *info)
+static struct cfg80211_sched_scan_request *
+nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct nlattr **attrs)
{
struct cfg80211_sched_scan_request *request;
- struct cfg80211_registered_device *rdev = info->user_ptr[0];
- struct net_device *dev = info->user_ptr[1];
struct nlattr *attr;
- struct wiphy *wiphy;
- int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
- u32 interval;
+ int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i, n_plans = 0;
enum ieee80211_band band;
size_t ie_len;
struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
- if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
- !rdev->ops->sched_scan_start)
- return -EOPNOTSUPP;
+ if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
+ return ERR_PTR(-EINVAL);
- if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
- return -EINVAL;
-
- if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
- return -EINVAL;
-
- interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
- if (interval == 0)
- return -EINVAL;
-
- wiphy = &rdev->wiphy;
-
- if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
n_channels = validate_scan_freqs(
- info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
+ attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
if (!n_channels)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
} else {
n_channels = ieee80211_get_num_supported_channels(wiphy);
}
- if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
- nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
+ if (attrs[NL80211_ATTR_SCAN_SSIDS])
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
tmp)
n_ssids++;
if (n_ssids > wiphy->max_sched_scan_ssids)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
/*
* First, count the number of 'real' matchsets. Due to an issue with
@@ -5679,9 +6278,9 @@
* older userspace that treated a matchset with only the RSSI as the
* global RSSI for all other matchsets - if there are other matchsets.
*/
- if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+ if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
nla_for_each_nested(attr,
- info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+ attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
tmp) {
struct nlattr *rssi;
@@ -5689,7 +6288,7 @@
nla_data(attr), nla_len(attr),
nl80211_match_policy);
if (err)
- return err;
+ return ERR_PTR(err);
/* add other standalone attributes here */
if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
n_match_sets++;
@@ -5706,30 +6305,57 @@
n_match_sets = 1;
if (n_match_sets > wiphy->max_match_sets)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- if (info->attrs[NL80211_ATTR_IE])
- ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ if (attrs[NL80211_ATTR_IE])
+ ie_len = nla_len(attrs[NL80211_ATTR_IE]);
else
ie_len = 0;
if (ie_len > wiphy->max_sched_scan_ie_len)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- if (rdev->sched_scan_req) {
- err = -EINPROGRESS;
- goto out;
+ if (attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) {
+ /*
+ * NL80211_ATTR_SCHED_SCAN_INTERVAL must not be specified since
+ * each scan plan already specifies its own interval
+ */
+ if (attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+ return ERR_PTR(-EINVAL);
+
+ nla_for_each_nested(attr,
+ attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp)
+ n_plans++;
+ } else {
+ /*
+ * The scan interval attribute is kept for backward
+ * compatibility. If no scan plans are specified and sched scan
+ * interval is specified, one scan plan will be set with this
+ * scan interval and infinite number of iterations.
+ */
+ if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+ return ERR_PTR(-EINVAL);
+
+ n_plans = 1;
}
+ if (!n_plans || n_plans > wiphy->max_sched_scan_plans)
+ return ERR_PTR(-EINVAL);
+
+ if (!wiphy_ext_feature_isset(
+ wiphy, NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI) &&
+ (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] ||
+ attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]))
+ return ERR_PTR(-EINVAL);
+
request = kzalloc(sizeof(*request)
+ sizeof(*request->ssids) * n_ssids
+ sizeof(*request->match_sets) * n_match_sets
+ + sizeof(*request->scan_plans) * n_plans
+ sizeof(*request->channels) * n_channels
+ ie_len, GFP_KERNEL);
- if (!request) {
- err = -ENOMEM;
- goto out;
- }
+ if (!request)
+ return ERR_PTR(-ENOMEM);
if (n_ssids)
request->ssids = (void *)&request->channels[n_channels];
@@ -5752,12 +6378,23 @@
(void *)(request->channels + n_channels);
}
request->n_match_sets = n_match_sets;
+ if (n_match_sets)
+ request->scan_plans = (void *)(request->match_sets +
+ n_match_sets);
+ else if (request->ie)
+ request->scan_plans = (void *)(request->ie + ie_len);
+ else if (n_ssids)
+ request->scan_plans = (void *)(request->ssids + n_ssids);
+ else
+ request->scan_plans = (void *)(request->channels + n_channels);
+
+ request->n_scan_plans = n_plans;
i = 0;
- if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
/* user specified, bail out if channel not found */
nla_for_each_nested(attr,
- info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
+ attrs[NL80211_ATTR_SCAN_FREQUENCIES],
tmp) {
struct ieee80211_channel *chan;
@@ -5803,8 +6440,8 @@
request->n_channels = i;
i = 0;
- if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
- nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
+ if (attrs[NL80211_ATTR_SCAN_SSIDS]) {
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
tmp) {
if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
err = -EINVAL;
@@ -5818,9 +6455,9 @@
}
i = 0;
- if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+ if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
nla_for_each_nested(attr,
- info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+ attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
tmp) {
struct nlattr *ssid, *rssi;
@@ -5875,39 +6512,115 @@
if (ie_len) {
request->ie_len = ie_len;
memcpy((void *)request->ie,
- nla_data(info->attrs[NL80211_ATTR_IE]),
+ nla_data(attrs[NL80211_ATTR_IE]),
request->ie_len);
}
- if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
+ if (attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32(
- info->attrs[NL80211_ATTR_SCAN_FLAGS]);
+ attrs[NL80211_ATTR_SCAN_FLAGS]);
if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
!(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
err = -EOPNOTSUPP;
goto out_free;
}
+
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ u32 flg = NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+
+ if (!wdev) /* must be net-detect */
+ flg = NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+
+ if (!(wiphy->features & flg)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ if (wdev && wdev->current_bss) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ err = nl80211_parse_random_mac(attrs, request->mac_addr,
+ request->mac_addr_mask);
+ if (err)
+ goto out_free;
+ }
}
- request->dev = dev;
- request->wiphy = &rdev->wiphy;
- request->interval = interval;
+ if (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]) {
+ request->relative_rssi = nla_get_s8(
+ attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]);
+ request->relative_rssi_set = true;
+ }
+
+ if (request->relative_rssi_set &&
+ attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]) {
+ struct nl80211_bss_select_rssi_adjust *rssi_adjust;
+
+ rssi_adjust = nla_data(
+ attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]);
+ request->rssi_adjust.band = rssi_adjust->band;
+ request->rssi_adjust.delta = rssi_adjust->delta;
+ if (!is_band_valid(wiphy, request->rssi_adjust.band)) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ }
+
+ err = nl80211_parse_sched_scan_plans(wiphy, n_plans, request,
+ attrs);
+ if (err)
+ goto out_free;
+
request->scan_start = jiffies;
- err = rdev_sched_scan_start(rdev, dev, request);
- if (!err) {
- if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
- request->owner_nlportid = info->snd_portid;
-
- rdev->sched_scan_req = request;
- nl80211_send_sched_scan(rdev, dev,
- NL80211_CMD_START_SCHED_SCAN);
- goto out;
- }
+ return request;
out_free:
kfree(request);
-out:
+ return ERR_PTR(err);
+}
+
+static int nl80211_start_sched_scan(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
+ !rdev->ops->sched_scan_start)
+ return -EOPNOTSUPP;
+
+ if (rdev->sched_scan_req)
+ return -EINPROGRESS;
+
+ rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
+ info->attrs);
+ err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
+ if (err)
+ goto out_err;
+
+ err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
+ if (err)
+ goto out_free;
+
+ rdev->sched_scan_req->dev = dev;
+ rdev->sched_scan_req->wiphy = &rdev->wiphy;
+
+ if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
+ rdev->sched_scan_req->owner_nlportid = info->snd_portid;
+
+ nl80211_send_sched_scan(rdev, dev,
+ NL80211_CMD_START_SCHED_SCAN);
+ return 0;
+
+out_free:
+ kfree(rdev->sched_scan_req);
+out_err:
+ rdev->sched_scan_req = NULL;
return err;
}
@@ -6091,7 +6804,7 @@
params.n_counter_offsets_presp = len / sizeof(u16);
if (rdev->wiphy.max_num_csa_counters &&
- (params.n_counter_offsets_beacon >
+ (params.n_counter_offsets_presp >
rdev->wiphy.max_num_csa_counters))
return -EINVAL;
@@ -6215,6 +6928,11 @@
jiffies_to_msecs(jiffies - intbss->ts)))
goto nla_put_failure;
+ if (intbss->ts_boottime &&
+ nla_put_u64(msg, NL80211_BSS_LAST_SEEN_BOOTTIME,
+ intbss->ts_boottime))
+ goto nla_put_failure;
+
switch (rdev->wiphy.signal_type) {
case CFG80211_SIGNAL_TYPE_MBM:
if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
@@ -7398,6 +8116,21 @@
return -EOPNOTSUPP;
}
+ if (info->attrs[NL80211_ATTR_BSS_SELECT]) {
+ /* bss selection makes no sense if bssid is set */
+ if (connect.bssid) {
+ kzfree(connkeys);
+ return -EINVAL;
+ }
+
+ err = parse_bss_select(info->attrs[NL80211_ATTR_BSS_SELECT],
+ wiphy, &connect.bss_select);
+ if (err) {
+ kzfree(connkeys);
+ return err;
+ }
+ }
+
wdev_lock(dev->ieee80211_ptr);
err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
wdev_unlock(dev->ieee80211_ptr);
@@ -7406,6 +8139,37 @@
return err;
}
+static int nl80211_update_connect_params(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_connect_params connect = {};
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ u32 changed = 0;
+ int ret;
+
+ if (!rdev->ops->update_connect_params)
+ return -EOPNOTSUPP;
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+ return -EINVAL;
+ connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ changed |= UPDATE_ASSOC_IES;
+ }
+
+ wdev_lock(dev->ieee80211_ptr);
+ if (!wdev->current_bss)
+ ret = -ENOLINK;
+ else
+ ret = rdev_update_connect_params(rdev, dev, &connect, changed);
+ wdev_unlock(dev->ieee80211_ptr);
+
+ return ret;
+}
+
static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -7654,237 +8418,21 @@
return rdev_cancel_remain_on_channel(rdev, wdev, cookie);
}
-static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
- u8 *rates, u8 rates_len)
-{
- u8 i;
- u32 mask = 0;
-
- for (i = 0; i < rates_len; i++) {
- int rate = (rates[i] & 0x7f) * 5;
- int ridx;
- for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
- struct ieee80211_rate *srate =
- &sband->bitrates[ridx];
- if (rate == srate->bitrate) {
- mask |= 1 << ridx;
- break;
- }
- }
- if (ridx == sband->n_bitrates)
- return 0; /* rate not found */
- }
-
- return mask;
-}
-
-static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
- u8 *rates, u8 rates_len,
- u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
-{
- u8 i;
-
- memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
-
- for (i = 0; i < rates_len; i++) {
- int ridx, rbit;
-
- ridx = rates[i] / 8;
- rbit = BIT(rates[i] % 8);
-
- /* check validity */
- if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
- return false;
-
- /* check availability */
- if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
- mcs[ridx] |= rbit;
- else
- return false;
- }
-
- return true;
-}
-
-static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
-{
- u16 mcs_mask = 0;
-
- switch (vht_mcs_map) {
- case IEEE80211_VHT_MCS_NOT_SUPPORTED:
- break;
- case IEEE80211_VHT_MCS_SUPPORT_0_7:
- mcs_mask = 0x00FF;
- break;
- case IEEE80211_VHT_MCS_SUPPORT_0_8:
- mcs_mask = 0x01FF;
- break;
- case IEEE80211_VHT_MCS_SUPPORT_0_9:
- mcs_mask = 0x03FF;
- break;
- default:
- break;
- }
-
- return mcs_mask;
-}
-
-static void vht_build_mcs_mask(u16 vht_mcs_map,
- u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
-{
- u8 nss;
-
- for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
- vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
- vht_mcs_map >>= 2;
- }
-}
-
-static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
- struct nl80211_txrate_vht *txrate,
- u16 mcs[NL80211_VHT_NSS_MAX])
-{
- u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
- u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
- u8 i;
-
- if (!sband->vht_cap.vht_supported)
- return false;
-
- memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
-
- /* Build vht_mcs_mask from VHT capabilities */
- vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
-
- for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
- if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
- mcs[i] = txrate->mcs[i];
- else
- return false;
- }
-
- return true;
-}
-
-static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
- [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
- .len = NL80211_MAX_SUPP_RATES },
- [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
- .len = NL80211_MAX_SUPP_HT_RATES },
- [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
- [NL80211_TXRATE_GI] = { .type = NLA_U8 },
-};
-
static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
struct genl_info *info)
{
- struct nlattr *tb[NL80211_TXRATE_MAX + 1];
- struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct cfg80211_bitrate_mask mask;
- int rem, i;
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
- struct nlattr *tx_rates;
- struct ieee80211_supported_band *sband;
- u16 vht_tx_mcs_map;
+ int err;
if (!rdev->ops->set_bitrate_mask)
return -EOPNOTSUPP;
- memset(&mask, 0, sizeof(mask));
- /* Default to all rates enabled */
- for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
- sband = rdev->wiphy.bands[i];
+ err = nl80211_parse_tx_bitrate_mask(info, &mask);
+ if (err)
+ return err;
- if (!sband)
- continue;
-
- mask.control[i].legacy = (1 << sband->n_bitrates) - 1;
- memcpy(mask.control[i].ht_mcs,
- sband->ht_cap.mcs.rx_mask,
- sizeof(mask.control[i].ht_mcs));
-
- if (!sband->vht_cap.vht_supported)
- continue;
-
- vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
- vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs);
- }
-
- /* if no rates are given set it back to the defaults */
- if (!info->attrs[NL80211_ATTR_TX_RATES])
- goto out;
-
- /*
- * The nested attribute uses enum nl80211_band as the index. This maps
- * directly to the enum ieee80211_band values used in cfg80211.
- */
- BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
- nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
- enum ieee80211_band band = nla_type(tx_rates);
- int err;
-
- if (band < 0 || band >= IEEE80211_NUM_BANDS)
- return -EINVAL;
- sband = rdev->wiphy.bands[band];
- if (sband == NULL)
- return -EINVAL;
- err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
- nla_len(tx_rates), nl80211_txattr_policy);
- if (err)
- return err;
- if (tb[NL80211_TXRATE_LEGACY]) {
- mask.control[band].legacy = rateset_to_mask(
- sband,
- nla_data(tb[NL80211_TXRATE_LEGACY]),
- nla_len(tb[NL80211_TXRATE_LEGACY]));
- if ((mask.control[band].legacy == 0) &&
- nla_len(tb[NL80211_TXRATE_LEGACY]))
- return -EINVAL;
- }
- if (tb[NL80211_TXRATE_HT]) {
- if (!ht_rateset_to_mask(
- sband,
- nla_data(tb[NL80211_TXRATE_HT]),
- nla_len(tb[NL80211_TXRATE_HT]),
- mask.control[band].ht_mcs))
- return -EINVAL;
- }
- if (tb[NL80211_TXRATE_VHT]) {
- if (!vht_set_mcs_mask(
- sband,
- nla_data(tb[NL80211_TXRATE_VHT]),
- mask.control[band].vht_mcs))
- return -EINVAL;
- }
- if (tb[NL80211_TXRATE_GI]) {
- mask.control[band].gi =
- nla_get_u8(tb[NL80211_TXRATE_GI]);
- if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI)
- return -EINVAL;
- }
-
- if (mask.control[band].legacy == 0) {
- /* don't allow empty legacy rates if HT or VHT
- * are not even supported.
- */
- if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
- rdev->wiphy.bands[band]->vht_cap.vht_supported))
- return -EINVAL;
-
- for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
- if (mask.control[band].ht_mcs[i])
- goto out;
-
- for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
- if (mask.control[band].vht_mcs[i])
- goto out;
-
- /* legacy and mcs rates may not be both empty */
- return -EINVAL;
- }
- }
-
-out:
return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
}
@@ -8329,6 +8877,17 @@
return err;
}
+ if (info->attrs[NL80211_ATTR_TX_RATES] && setup.chandef.chan != NULL) {
+ err = nl80211_parse_tx_bitrate_mask(info, &setup.beacon_rate);
+ if (err)
+ return err;
+
+ err = validate_beacon_tx_rate(rdev, setup.chandef.chan->band,
+ &setup.beacon_rate);
+ if (err)
+ return err;
+ }
+
return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
}
@@ -10043,6 +10602,14 @@
NL80211_FLAG_NEED_RTNL,
},
{
+ .cmd = NL80211_CMD_UPDATE_CONNECT_PARAMS,
+ .doit = nl80211_update_connect_params,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
.cmd = NL80211_CMD_DISCONNECT,
.doit = nl80211_disconnect,
.policy = nl80211_policy,
@@ -10811,7 +11378,7 @@
struct net_device *netdev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len,
- u16 status, gfp_t gfp)
+ int status, gfp_t gfp)
{
struct sk_buff *msg;
void *hdr;
@@ -10829,7 +11396,10 @@
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
(bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) ||
- nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status) ||
+ nla_put_u16(msg, NL80211_ATTR_STATUS_CODE,
+ status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE :
+ status) ||
+ (status < 0 && nla_put_flag(msg, NL80211_ATTR_TIMED_OUT)) ||
(req_ie &&
nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
(resp_ie &&
@@ -12038,7 +12608,7 @@
struct wireless_dev *wdev;
struct cfg80211_beacon_registration *reg, *tmp;
- if (state != NETLINK_URELEASE)
+ if (state != NETLINK_URELEASE || notify->protocol != NETLINK_GENERIC)
return NOTIFY_DONE;
rcu_read_lock();
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 7ad70d6..57d42fd 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -41,7 +41,7 @@
struct net_device *netdev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len,
- u16 status, gfp_t gfp);
+ int status, gfp_t gfp);
void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 9153f3e..74e974e 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -442,6 +442,18 @@
return ret;
}
+static inline int
+rdev_update_connect_params(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_connect_params *sme, u32 changed)
+{
+ int ret;
+ trace_rdev_update_connect_params(&rdev->wiphy, dev, sme, changed);
+ ret = rdev->ops->update_connect_params(&rdev->wiphy, dev, sme, changed);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
static inline int rdev_disconnect(struct cfg80211_registered_device *rdev,
struct net_device *dev, u16 reason_code)
{
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 9125797..0e88483 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -266,8 +266,7 @@
spin_lock_bh(&rdev->bss_lock);
__cfg80211_bss_expire(rdev, request->scan_start);
spin_unlock_bh(&rdev->bss_lock);
- request->scan_start =
- jiffies + msecs_to_jiffies(request->interval);
+ request->scan_start = jiffies;
}
nl80211_send_sched_scan_results(rdev, request->dev);
}
@@ -836,6 +835,7 @@
found->pub.signal = tmp->pub.signal;
found->pub.capability = tmp->pub.capability;
found->ts = tmp->ts;
+ found->ts_boottime = tmp->ts_boottime;
} else {
struct cfg80211_internal_bss *new;
struct cfg80211_internal_bss *hidden;
@@ -935,14 +935,13 @@
}
/* Returned bss is reference counted and must be cleaned up appropriately. */
-struct cfg80211_bss*
-cfg80211_inform_bss_width(struct wiphy *wiphy,
- struct ieee80211_channel *rx_channel,
- enum nl80211_bss_scan_width scan_width,
- enum cfg80211_bss_frame_type ftype,
- const u8 *bssid, u64 tsf, u16 capability,
- u16 beacon_interval, const u8 *ie, size_t ielen,
- s32 signal, gfp_t gfp)
+struct cfg80211_bss *
+cfg80211_inform_bss_data(struct wiphy *wiphy,
+ struct cfg80211_inform_bss *data,
+ enum cfg80211_bss_frame_type ftype,
+ const u8 *bssid, u64 tsf, u16 capability,
+ u16 beacon_interval, const u8 *ie, size_t ielen,
+ gfp_t gfp)
{
struct cfg80211_bss_ies *ies;
struct ieee80211_channel *channel;
@@ -954,19 +953,21 @@
return NULL;
if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
- (signal < 0 || signal > 100)))
+ (data->signal < 0 || data->signal > 100)))
return NULL;
- channel = cfg80211_get_bss_channel(wiphy, ie, ielen, rx_channel);
+ channel = cfg80211_get_bss_channel(wiphy, ie, ielen, data->chan);
if (!channel)
return NULL;
memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
tmp.pub.channel = channel;
- tmp.pub.scan_width = scan_width;
- tmp.pub.signal = signal;
+ tmp.pub.scan_width = data->scan_width;
+ tmp.pub.signal = data->signal;
tmp.pub.beacon_interval = beacon_interval;
tmp.pub.capability = capability;
+ tmp.ts_boottime = data->boottime_ns;
+
/*
* If we do not know here whether the IEs are from a Beacon or Probe
* Response frame, we need to pick one of the options and only use it
@@ -996,7 +997,7 @@
}
rcu_assign_pointer(tmp.pub.ies, ies);
- signal_valid = abs(rx_channel->center_freq - channel->center_freq) <=
+ signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
wiphy->max_adj_channel_rssi_comp;
res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
if (!res)
@@ -1016,15 +1017,15 @@
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
-EXPORT_SYMBOL(cfg80211_inform_bss_width);
+EXPORT_SYMBOL(cfg80211_inform_bss_data);
-/* Returned bss is reference counted and must be cleaned up appropriately. */
+/* cfg80211_inform_bss_width_frame helper */
struct cfg80211_bss *
-cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
- struct ieee80211_channel *rx_channel,
- enum nl80211_bss_scan_width scan_width,
- struct ieee80211_mgmt *mgmt, size_t len,
- s32 signal, gfp_t gfp)
+cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
+ struct cfg80211_inform_bss *data,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ gfp_t gfp)
+
{
struct cfg80211_internal_bss tmp = {}, *res;
struct cfg80211_bss_ies *ies;
@@ -1037,8 +1038,7 @@
BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
offsetof(struct ieee80211_mgmt, u.beacon.variable));
- trace_cfg80211_inform_bss_width_frame(wiphy, rx_channel, scan_width, mgmt,
- len, signal);
+ trace_cfg80211_inform_bss_frame(wiphy, data, mgmt, len);
if (WARN_ON(!mgmt))
return NULL;
@@ -1047,14 +1047,14 @@
return NULL;
if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
- (signal < 0 || signal > 100)))
+ (data->signal < 0 || data->signal > 100)))
return NULL;
if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
return NULL;
channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
- ielen, rx_channel);
+ ielen, data->chan);
if (!channel)
return NULL;
@@ -1074,12 +1074,13 @@
memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
tmp.pub.channel = channel;
- tmp.pub.scan_width = scan_width;
- tmp.pub.signal = signal;
+ tmp.pub.scan_width = data->scan_width;
+ tmp.pub.signal = data->signal;
tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
+ tmp.ts_boottime = data->boottime_ns;
- signal_valid = abs(rx_channel->center_freq - channel->center_freq) <=
+ signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
wiphy->max_adj_channel_rssi_comp;
res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid);
if (!res)
@@ -1099,7 +1100,7 @@
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
-EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
+EXPORT_SYMBOL(cfg80211_inform_bss_frame_data);
void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
{
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index c6fe7e2..f189f47 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -258,9 +258,7 @@
if (cfg80211_conn_do_work(wdev)) {
__cfg80211_connect_result(
wdev->netdev, bssid,
- NULL, 0, NULL, 0,
- WLAN_STATUS_UNSPECIFIED_FAILURE,
- false, NULL);
+ NULL, 0, NULL, 0, -1, false, NULL);
}
wdev_unlock(wdev);
}
@@ -599,7 +597,7 @@
void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
const u8 *req_ie, size_t req_ie_len,
const u8 *resp_ie, size_t resp_ie_len,
- u16 status, bool wextev,
+ int status, bool wextev,
struct cfg80211_bss *bss)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
@@ -708,7 +706,7 @@
void cfg80211_connect_bss(struct net_device *dev, const u8 *bssid,
struct cfg80211_bss *bss, const u8 *req_ie,
size_t req_ie_len, const u8 *resp_ie,
- size_t resp_ie_len, u16 status, gfp_t gfp)
+ size_t resp_ie_len, int status, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
@@ -917,7 +915,8 @@
}
void cfg80211_disconnected(struct net_device *dev, u16 reason,
- const u8 *ie, size_t ie_len, gfp_t gfp)
+ const u8 *ie, size_t ie_len,
+ bool locally_generated, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
@@ -933,6 +932,7 @@
ev->dc.ie_len = ie_len;
memcpy((void *)ev->dc.ie, ie, ie_len);
ev->dc.reason = reason;
+ ev->dc.locally_generated = locally_generated;
spin_lock_irqsave(&wdev->event_lock, flags);
list_add_tail(&ev->list, &wdev->event_list);
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 5d69886..c7f91f6 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1188,6 +1188,24 @@
__entry->wpa_versions, __entry->flags, MAC_PR_ARG(prev_bssid))
);
+TRACE_EVENT(rdev_update_connect_params,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_connect_params *sme, u32 changed),
+ TP_ARGS(wiphy, netdev, sme, changed),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u32, changed)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->changed = changed;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", parameters changed: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->changed)
+);
+
TRACE_EVENT(rdev_set_cqm_rssi_config,
TP_PROTO(struct wiphy *wiphy,
struct net_device *netdev, s32 rssi_thold,
@@ -2537,30 +2555,30 @@
__entry->privacy)
);
-TRACE_EVENT(cfg80211_inform_bss_width_frame,
- TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
- enum nl80211_bss_scan_width scan_width,
- struct ieee80211_mgmt *mgmt, size_t len,
- s32 signal),
- TP_ARGS(wiphy, channel, scan_width, mgmt, len, signal),
+TRACE_EVENT(cfg80211_inform_bss_frame,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_inform_bss *data,
+ struct ieee80211_mgmt *mgmt, size_t len),
+ TP_ARGS(wiphy, data, mgmt, len),
TP_STRUCT__entry(
WIPHY_ENTRY
CHAN_ENTRY
__field(enum nl80211_bss_scan_width, scan_width)
__dynamic_array(u8, mgmt, len)
__field(s32, signal)
+ __field(u64, ts_boottime)
),
TP_fast_assign(
WIPHY_ASSIGN;
- CHAN_ASSIGN(channel);
- __entry->scan_width = scan_width;
+ CHAN_ASSIGN(data->chan);
+ __entry->scan_width = data->scan_width;
if (mgmt)
memcpy(__get_dynamic_array(mgmt), mgmt, len);
- __entry->signal = signal;
+ __entry->signal = data->signal;
+ __entry->ts_boottime = data->boottime_ns;
),
- TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d",
+ TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d, tsb:%llu",
WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width,
- __entry->signal)
+ __entry->signal, (unsigned long long)__entry->ts_boottime)
);
DECLARE_EVENT_CLASS(cfg80211_bss_evt,
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 730ab84..f8ccd8b 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -837,7 +837,8 @@
case EVENT_DISCONNECTED:
__cfg80211_disconnected(wdev->netdev,
ev->dc.ie, ev->dc.ie_len,
- ev->dc.reason, true);
+ ev->dc.reason,
+ !ev->dc.locally_generated);
break;
case EVENT_IBSS_JOINED:
__cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid,
diff --git a/net/x25/x25_facilities.c b/net/x25/x25_facilities.c
index 7ecd04c..997ff7b 100644
--- a/net/x25/x25_facilities.c
+++ b/net/x25/x25_facilities.c
@@ -277,6 +277,7 @@
memset(&theirs, 0, sizeof(theirs));
memcpy(new, ours, sizeof(*new));
+ memset(dte, 0, sizeof(*dte));
len = x25_parse_facilities(skb, &theirs, dte, &x25->vc_facil_mask);
if (len < 0)
diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c
index debe733..ca21ba7 100644
--- a/net/xfrm/xfrm_algo.c
+++ b/net/xfrm/xfrm_algo.c
@@ -220,7 +220,7 @@
.uinfo = {
.auth = {
- .icv_truncbits = 96,
+ .icv_truncbits = 128,
.icv_fullbits = 256,
}
},
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 4769382..64faba06 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -3004,6 +3004,11 @@
{
int rv;
+ /* Initialize the per-net locks here */
+ spin_lock_init(&net->xfrm.xfrm_state_lock);
+ rwlock_init(&net->xfrm.xfrm_policy_lock);
+ mutex_init(&net->xfrm.xfrm_cfg_mutex);
+
rv = xfrm_statistics_init(net);
if (rv < 0)
goto out_statistics;
@@ -3020,11 +3025,6 @@
if (rv < 0)
goto out;
- /* Initialize the per-net locks here */
- spin_lock_init(&net->xfrm.xfrm_state_lock);
- rwlock_init(&net->xfrm.xfrm_policy_lock);
- mutex_init(&net->xfrm.xfrm_cfg_mutex);
-
return 0;
out:
diff --git a/Documentation/mic/mpssd/.gitignore b/samples/mic/mpssd/.gitignore
similarity index 100%
rename from Documentation/mic/mpssd/.gitignore
rename to samples/mic/mpssd/.gitignore
diff --git a/samples/mic/mpssd/Makefile b/samples/mic/mpssd/Makefile
new file mode 100644
index 0000000..3e3ef91
--- /dev/null
+++ b/samples/mic/mpssd/Makefile
@@ -0,0 +1,27 @@
+ifndef CROSS_COMPILE
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
+
+ifeq ($(ARCH),x86)
+
+PROGS := mpssd
+CC = $(CROSS_COMPILE)gcc
+CFLAGS := -I../../../usr/include -I../../../tools/include
+
+ifdef DEBUG
+CFLAGS += -DDEBUG=$(DEBUG)
+endif
+
+all: $(PROGS)
+mpssd: mpssd.c sysfs.c
+ $(CC) $(CFLAGS) mpssd.c sysfs.c -o mpssd -lpthread
+
+install:
+ install mpssd /usr/sbin/mpssd
+ install micctrl /usr/sbin/micctrl
+
+clean:
+ rm -fr $(PROGS)
+
+endif
+endif
diff --git a/Documentation/mic/mpssd/micctrl b/samples/mic/mpssd/micctrl
old mode 100755
new mode 100644
similarity index 100%
rename from Documentation/mic/mpssd/micctrl
rename to samples/mic/mpssd/micctrl
diff --git a/Documentation/mic/mpssd/mpss b/samples/mic/mpssd/mpss
old mode 100755
new mode 100644
similarity index 100%
rename from Documentation/mic/mpssd/mpss
rename to samples/mic/mpssd/mpss
diff --git a/Documentation/mic/mpssd/mpssd.c b/samples/mic/mpssd/mpssd.c
similarity index 100%
rename from Documentation/mic/mpssd/mpssd.c
rename to samples/mic/mpssd/mpssd.c
diff --git a/Documentation/mic/mpssd/mpssd.h b/samples/mic/mpssd/mpssd.h
similarity index 100%
rename from Documentation/mic/mpssd/mpssd.h
rename to samples/mic/mpssd/mpssd.h
diff --git a/Documentation/mic/mpssd/sysfs.c b/samples/mic/mpssd/sysfs.c
similarity index 100%
rename from Documentation/mic/mpssd/sysfs.c
rename to samples/mic/mpssd/sysfs.c
diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn
index f734033..5c17a5c 100644
--- a/scripts/Makefile.extrawarn
+++ b/scripts/Makefile.extrawarn
@@ -24,6 +24,7 @@
warning-1 += -Wold-style-definition
warning-1 += $(call cc-option, -Wmissing-include-dirs)
warning-1 += $(call cc-option, -Wunused-but-set-variable)
+warning-1 += $(call cc-option, -Wunused-const-variable)
warning-1 += $(call cc-disable-warning, missing-field-initializers)
warning-2 := -Waggregate-return
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 0f1d004..c2b437e 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -351,7 +351,7 @@
quiet_cmd_lz4 = LZ4 $@
cmd_lz4 = (cat $(filter-out FORCE,$^) | \
- lz4c -l -c1 stdin stdout && $(call size_append, $(filter-out FORCE,$^))) > $@ || \
+ lz4c -9) > $@ || \
(rm -f $@ ; false)
# U-Boot mkimage
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 77c4b69..0c6e433 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -2306,6 +2306,7 @@
# Check for improperly formed commit descriptions
if ($in_commit_log &&
+ $line !~ /^This reverts commit [0-9a-f]{12,40}\./ &&
$line =~ /\bcommit\s+[0-9a-f]{5,}/i &&
!($line =~ /\b[Cc]ommit [0-9a-f]{12,40} \("/ ||
($line =~ /\b[Cc]ommit [0-9a-f]{12,40}\s*$/ &&
diff --git a/scripts/gcc-x86_64-has-stack-protector.sh b/scripts/gcc-x86_64-has-stack-protector.sh
index 973e8c1..17867e7 100755
--- a/scripts/gcc-x86_64-has-stack-protector.sh
+++ b/scripts/gcc-x86_64-has-stack-protector.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-echo "int foo(void) { char X[200]; return 3; }" | $* -S -x c -c -O0 -mcmodel=kernel -fstack-protector - -o - 2> /dev/null | grep -q "%gs"
+echo "int foo(void) { char X[200]; return 3; }" | $* -S -x c -c -O0 -mcmodel=kernel -fno-PIE -fstack-protector - -o - 2> /dev/null | grep -q "%gs"
if [ "$?" -eq "0" ] ; then
echo y
else
diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c
index 8275f0e5..4b2f44c 100644
--- a/scripts/kconfig/nconf.gui.c
+++ b/scripts/kconfig/nconf.gui.c
@@ -364,12 +364,14 @@
WINDOW *prompt_win;
WINDOW *form_win;
PANEL *panel;
- int i, x, y;
+ int i, x, y, lines, columns, win_lines, win_cols;
int res = -1;
int cursor_position = strlen(init);
int cursor_form_win;
char *result = *resultp;
+ getmaxyx(stdscr, lines, columns);
+
if (strlen(init)+1 > *result_len) {
*result_len = strlen(init)+1;
*resultp = result = realloc(result, *result_len);
@@ -386,14 +388,19 @@
if (title)
prompt_width = max(prompt_width, strlen(title));
+ win_lines = min(prompt_lines+6, lines-2);
+ win_cols = min(prompt_width+7, columns-2);
+ prompt_lines = max(win_lines-6, 0);
+ prompt_width = max(win_cols-7, 0);
+
/* place dialog in middle of screen */
- y = (getmaxy(stdscr)-(prompt_lines+4))/2;
- x = (getmaxx(stdscr)-(prompt_width+4))/2;
+ y = (lines-win_lines)/2;
+ x = (columns-win_cols)/2;
strncpy(result, init, *result_len);
/* create the windows */
- win = newwin(prompt_lines+6, prompt_width+7, y, x);
+ win = newwin(win_lines, win_cols, y, x);
prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2);
form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2);
keypad(form_win, TRUE);
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index e614ef6..268acec 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -653,7 +653,7 @@
len = sprintf(alias, "of:N%sT%s", (*name)[0] ? *name : "*",
(*type)[0] ? *type : "*");
- if (compatible[0])
+ if ((*compatible)[0])
sprintf(&alias[len], "%sC%s", (*type)[0] ? "*" : "",
*compatible);
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index e268d83..d24e239 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -778,6 +778,7 @@
* "foo" will match an exact string equal to "foo"
* "*foo" will match a string that ends with "foo"
* "foo*" will match a string that begins with "foo"
+ * "*foo*" will match a string that contains "foo"
*/
static int match(const char *sym, const char * const pat[])
{
@@ -786,8 +787,17 @@
p = *pat++;
const char *endp = p + strlen(p) - 1;
+ /* "*foo*" */
+ if (*p == '*' && *endp == '*') {
+ char *here, *bare = strndup(p + 1, strlen(p) - 2);
+
+ here = strstr(sym, bare);
+ free(bare);
+ if (here != NULL)
+ return 1;
+ }
/* "*foo" */
- if (*p == '*') {
+ else if (*p == '*') {
if (strrcmp(sym, p + 1) == 0)
return 1;
}
@@ -894,6 +904,10 @@
static const char *const init_exit_sections[] =
{ALL_INIT_SECTIONS, ALL_EXIT_SECTIONS, NULL };
+/* all text sections */
+static const char *const text_sections[] = { ALL_INIT_TEXT_SECTIONS,
+ ALL_EXIT_TEXT_SECTIONS, TEXT_SECTIONS, NULL };
+
/* data section */
static const char *const data_sections[] = { DATA_SECTIONS, NULL };
@@ -912,6 +926,7 @@
static const char *const head_sections[] = { ".head.text*", NULL };
static const char *const linker_symbols[] =
{ "__init_begin", "_sinittext", "_einittext", NULL };
+static const char *const optim_symbols[] = { "*.constprop.*", NULL };
enum mismatch {
TEXT_TO_ANY_INIT,
@@ -1069,6 +1084,17 @@
* This pattern is identified by
* refsymname = __init_begin, _sinittext, _einittext
*
+ * Pattern 5:
+ * GCC may optimize static inlines when fed constant arg(s) resulting
+ * in functions like cpumask_empty() -- generating an associated symbol
+ * cpumask_empty.constprop.3 that appears in the audit. If the const that
+ * is passed in comes from __init, like say nmi_ipi_mask, we get a
+ * meaningless section warning. May need to add isra symbols too...
+ * This pattern is identified by
+ * tosec = init section
+ * fromsec = text section
+ * refsymname = *.constprop.*
+ *
**/
static int secref_whitelist(const struct sectioncheck *mismatch,
const char *fromsec, const char *fromsym,
@@ -1101,6 +1127,12 @@
if (match(tosym, linker_symbols))
return 0;
+ /* Check for pattern 5 */
+ if (match(fromsec, text_sections) &&
+ match(tosec, init_sections) &&
+ match(fromsym, optim_symbols))
+ return 0;
+
return 1;
}
diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c
index 001facf..d96d8a2 100644
--- a/scripts/recordmcount.c
+++ b/scripts/recordmcount.c
@@ -33,10 +33,17 @@
#include <string.h>
#include <unistd.h>
+/*
+ * glibc synced up and added the metag number but didn't add the relocations.
+ * Work around this in a crude manner for now.
+ */
#ifndef EM_METAG
-/* Remove this when these make it to the standard system elf.h. */
#define EM_METAG 174
+#endif
+#ifndef R_METAG_ADDR32
#define R_METAG_ADDR32 2
+#endif
+#ifndef R_METAG_NONE
#define R_METAG_NONE 3
#endif
diff --git a/security/Kconfig b/security/Kconfig
index 263e25b..47bbc9f 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -131,6 +131,46 @@
this low address space will need the permission specific to the
systems running LSM.
+config HAVE_HARDENED_USERCOPY_ALLOCATOR
+ bool
+ help
+ The heap allocator implements __check_heap_object() for
+ validating memory ranges against heap object sizes in
+ support of CONFIG_HARDENED_USERCOPY.
+
+config HAVE_ARCH_HARDENED_USERCOPY
+ bool
+ help
+ The architecture supports CONFIG_HARDENED_USERCOPY by
+ calling check_object_size() just before performing the
+ userspace copies in the low level implementation of
+ copy_to_user() and copy_from_user().
+
+config HARDENED_USERCOPY
+ bool "Harden memory copies between kernel and userspace"
+ depends on HAVE_ARCH_HARDENED_USERCOPY
+ depends on HAVE_HARDENED_USERCOPY_ALLOCATOR
+ select BUG
+ help
+ This option checks for obviously wrong memory regions when
+ copying memory to/from the kernel (via copy_to_user() and
+ copy_from_user() functions) by rejecting memory ranges that
+ are larger than the specified heap object, span multiple
+ separately allocates pages, are not on the process stack,
+ or are part of the kernel text. This kills entire classes
+ of heap overflow exploits and similar kernel memory exposures.
+
+config HARDENED_USERCOPY_PAGESPAN
+ bool "Refuse to copy allocations that span multiple pages"
+ depends on HARDENED_USERCOPY
+ depends on !COMPILE_TEST
+ help
+ When a multi-page allocation is done without __GFP_COMP,
+ hardened usercopy will reject attempts to copy it. There are,
+ however, several cases of this in the kernel that have not all
+ been removed. This config is intended to be used only while
+ trying to find such users.
+
source security/selinux/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
diff --git a/security/commoncap.c b/security/commoncap.c
index af87133..740966a 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -288,6 +288,16 @@
new->cap_effective = *effective;
new->cap_inheritable = *inheritable;
new->cap_permitted = *permitted;
+
+ /*
+ * Mask off ambient bits that are no longer both permitted and
+ * inheritable.
+ */
+ new->cap_ambient = cap_intersect(new->cap_ambient,
+ cap_intersect(*permitted,
+ *inheritable));
+ if (WARN_ON(!cap_ambient_invariant_ok(new)))
+ return -EINVAL;
return 0;
}
@@ -368,6 +378,7 @@
/*
* pP' = (X & fP) | (pI & fI)
+ * The addition of pA' is handled later.
*/
new->cap_permitted.cap[i] =
(new->cap_bset.cap[i] & permitted) |
@@ -499,10 +510,13 @@
{
const struct cred *old = current_cred();
struct cred *new = bprm->cred;
- bool effective, has_cap = false;
+ bool effective, has_cap = false, is_setid;
int ret;
kuid_t root_uid;
+ if (WARN_ON(!cap_ambient_invariant_ok(old)))
+ return -EPERM;
+
effective = false;
ret = get_file_caps(bprm, &effective, &has_cap);
if (ret < 0)
@@ -547,8 +561,9 @@
*
* In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
*/
- if ((!uid_eq(new->euid, old->uid) ||
- !gid_eq(new->egid, old->gid) ||
+ is_setid = !uid_eq(new->euid, old->uid) || !gid_eq(new->egid, old->gid);
+
+ if ((is_setid ||
!cap_issubset(new->cap_permitted, old->cap_permitted)) &&
bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
/* downgrade; they get no more than they had, and maybe less */
@@ -564,10 +579,28 @@
new->suid = new->fsuid = new->euid;
new->sgid = new->fsgid = new->egid;
+ /* File caps or setid cancels ambient. */
+ if (has_cap || is_setid)
+ cap_clear(new->cap_ambient);
+
+ /*
+ * Now that we've computed pA', update pP' to give:
+ * pP' = (X & fP) | (pI & fI) | pA'
+ */
+ new->cap_permitted = cap_combine(new->cap_permitted, new->cap_ambient);
+
+ /*
+ * Set pE' = (fE ? pP' : pA'). Because pA' is zero if fE is set,
+ * this is the same as pE' = (fE ? pP' : 0) | pA'.
+ */
if (effective)
new->cap_effective = new->cap_permitted;
else
- cap_clear(new->cap_effective);
+ new->cap_effective = new->cap_ambient;
+
+ if (WARN_ON(!cap_ambient_invariant_ok(new)))
+ return -EPERM;
+
bprm->cap_effective = effective;
/*
@@ -582,7 +615,7 @@
* Number 1 above might fail if you don't have a full bset, but I think
* that is interesting information to audit.
*/
- if (!cap_isclear(new->cap_effective)) {
+ if (!cap_issubset(new->cap_effective, new->cap_ambient)) {
if (!cap_issubset(CAP_FULL_SET, new->cap_effective) ||
!uid_eq(new->euid, root_uid) || !uid_eq(new->uid, root_uid) ||
issecure(SECURE_NOROOT)) {
@@ -593,6 +626,10 @@
}
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+
+ if (WARN_ON(!cap_ambient_invariant_ok(new)))
+ return -EPERM;
+
return 0;
}
@@ -614,7 +651,7 @@
if (!uid_eq(cred->uid, root_uid)) {
if (bprm->cap_effective)
return 1;
- if (!cap_isclear(cred->cap_permitted))
+ if (!cap_issubset(cred->cap_permitted, cred->cap_ambient))
return 1;
}
@@ -716,10 +753,18 @@
uid_eq(old->suid, root_uid)) &&
(!uid_eq(new->uid, root_uid) &&
!uid_eq(new->euid, root_uid) &&
- !uid_eq(new->suid, root_uid)) &&
- !issecure(SECURE_KEEP_CAPS)) {
- cap_clear(new->cap_permitted);
- cap_clear(new->cap_effective);
+ !uid_eq(new->suid, root_uid))) {
+ if (!issecure(SECURE_KEEP_CAPS)) {
+ cap_clear(new->cap_permitted);
+ cap_clear(new->cap_effective);
+ }
+
+ /*
+ * Pre-ambient programs expect setresuid to nonroot followed
+ * by exec to drop capabilities. We should make sure that
+ * this remains the case.
+ */
+ cap_clear(new->cap_ambient);
}
if (uid_eq(old->euid, root_uid) && !uid_eq(new->euid, root_uid))
cap_clear(new->cap_effective);
@@ -949,6 +994,43 @@
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
return commit_creds(new);
+ case PR_CAP_AMBIENT:
+ if (arg2 == PR_CAP_AMBIENT_CLEAR_ALL) {
+ if (arg3 | arg4 | arg5)
+ return -EINVAL;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ cap_clear(new->cap_ambient);
+ return commit_creds(new);
+ }
+
+ if (((!cap_valid(arg3)) | arg4 | arg5))
+ return -EINVAL;
+
+ if (arg2 == PR_CAP_AMBIENT_IS_SET) {
+ return !!cap_raised(current_cred()->cap_ambient, arg3);
+ } else if (arg2 != PR_CAP_AMBIENT_RAISE &&
+ arg2 != PR_CAP_AMBIENT_LOWER) {
+ return -EINVAL;
+ } else {
+ if (arg2 == PR_CAP_AMBIENT_RAISE &&
+ (!cap_raised(current_cred()->cap_permitted, arg3) ||
+ !cap_raised(current_cred()->cap_inheritable,
+ arg3)))
+ return -EPERM;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ if (arg2 == PR_CAP_AMBIENT_RAISE)
+ cap_raise(new->cap_ambient, arg3);
+ else
+ cap_lower(new->cap_ambient, arg3);
+ return commit_creds(new);
+ }
+
default:
/* No functionality available - continue with default */
return -ENOSYS;
diff --git a/security/inode.c b/security/inode.c
index 8e7ca62..b5f9474 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -105,7 +105,7 @@
dir = parent->d_inode;
mutex_lock(&dir->i_mutex);
- dentry = lookup_one_len(name, parent, strlen(name));
+ dentry = lookup_one_len2(name, mount, parent, strlen(name));
if (IS_ERR(dentry))
goto out;
diff --git a/security/keys/gc.c b/security/keys/gc.c
index addf060..9cb4fe4 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -46,7 +46,7 @@
* immediately unlinked.
*/
struct key_type key_type_dead = {
- .name = "dead",
+ .name = ".dead",
};
/*
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index fee27fe..1187d2f 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -277,7 +277,8 @@
* Create and join an anonymous session keyring or join a named session
* keyring, creating it if necessary. A named session keyring must have Search
* permission for it to be joined. Session keyrings without this permit will
- * be skipped over.
+ * be skipped over. It is not permitted for userspace to create or join
+ * keyrings whose name begin with a dot.
*
* If successful, the ID of the joined session keyring will be returned.
*/
@@ -294,12 +295,16 @@
ret = PTR_ERR(name);
goto error;
}
+
+ ret = -EPERM;
+ if (name[0] == '.')
+ goto error_name;
}
/* join the session */
ret = join_session_keyring(name);
+error_name:
kfree(name);
-
error:
return ret;
}
@@ -1253,8 +1258,8 @@
* Read or set the default keyring in which request_key() will cache keys and
* return the old setting.
*
- * If a process keyring is specified then this will be created if it doesn't
- * yet exist. The old setting will be returned if successful.
+ * If a thread or process keyring is specified then it will be created if it
+ * doesn't yet exist. The old setting will be returned if successful.
*/
long keyctl_set_reqkey_keyring(int reqkey_defl)
{
@@ -1279,11 +1284,8 @@
case KEY_REQKEY_DEFL_PROCESS_KEYRING:
ret = install_process_keyring_to_cred(new);
- if (ret < 0) {
- if (ret != -EEXIST)
- goto error;
- ret = 0;
- }
+ if (ret < 0)
+ goto error;
goto set;
case KEY_REQKEY_DEFL_DEFAULT:
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 972eeb3..9962535 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -187,7 +187,7 @@
struct timespec now;
unsigned long timo;
key_ref_t key_ref, skey_ref;
- char xbuf[12];
+ char xbuf[16];
int rc;
struct keyring_search_context ctx = {
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index db91639..01507ea 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -125,13 +125,18 @@
}
/*
- * Install a fresh thread keyring directly to new credentials. This keyring is
- * allowed to overrun the quota.
+ * Install a thread keyring to the given credentials struct if it didn't have
+ * one already. This is allowed to overrun the quota.
+ *
+ * Return: 0 if a thread keyring is now present; -errno on failure.
*/
int install_thread_keyring_to_cred(struct cred *new)
{
struct key *keyring;
+ if (new->thread_keyring)
+ return 0;
+
keyring = keyring_alloc("_tid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
KEY_ALLOC_QUOTA_OVERRUN, NULL);
@@ -143,7 +148,9 @@
}
/*
- * Install a fresh thread keyring, discarding the old one.
+ * Install a thread keyring to the current task if it didn't have one already.
+ *
+ * Return: 0 if a thread keyring is now present; -errno on failure.
*/
static int install_thread_keyring(void)
{
@@ -154,8 +161,6 @@
if (!new)
return -ENOMEM;
- BUG_ON(new->thread_keyring);
-
ret = install_thread_keyring_to_cred(new);
if (ret < 0) {
abort_creds(new);
@@ -166,17 +171,17 @@
}
/*
- * Install a process keyring directly to a credentials struct.
+ * Install a process keyring to the given credentials struct if it didn't have
+ * one already. This is allowed to overrun the quota.
*
- * Returns -EEXIST if there was already a process keyring, 0 if one installed,
- * and other value on any other error
+ * Return: 0 if a process keyring is now present; -errno on failure.
*/
int install_process_keyring_to_cred(struct cred *new)
{
struct key *keyring;
if (new->process_keyring)
- return -EEXIST;
+ return 0;
keyring = keyring_alloc("_pid", new->uid, new->gid, new,
KEY_POS_ALL | KEY_USR_VIEW,
@@ -189,11 +194,9 @@
}
/*
- * Make sure a process keyring is installed for the current process. The
- * existing process keyring is not replaced.
+ * Install a process keyring to the current task if it didn't have one already.
*
- * Returns 0 if there is a process keyring by the end of this function, some
- * error otherwise.
+ * Return: 0 if a process keyring is now present; -errno on failure.
*/
static int install_process_keyring(void)
{
@@ -207,14 +210,18 @@
ret = install_process_keyring_to_cred(new);
if (ret < 0) {
abort_creds(new);
- return ret != -EEXIST ? ret : 0;
+ return ret;
}
return commit_creds(new);
}
/*
- * Install a session keyring directly to a credentials struct.
+ * Install the given keyring as the session keyring of the given credentials
+ * struct, replacing the existing one if any. If the given keyring is NULL,
+ * then install a new anonymous session keyring.
+ *
+ * Return: 0 on success; -errno on failure.
*/
int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
{
@@ -249,8 +256,11 @@
}
/*
- * Install a session keyring, discarding the old one. If a keyring is not
- * supplied, an empty one is invented.
+ * Install the given keyring as the session keyring of the current task,
+ * replacing the existing one if any. If the given keyring is NULL, then
+ * install a new anonymous session keyring.
+ *
+ * Return: 0 on success; -errno on failure.
*/
static int install_session_keyring(struct key *keyring)
{
@@ -849,6 +859,7 @@
new->cap_inheritable = old->cap_inheritable;
new->cap_permitted = old->cap_permitted;
new->cap_effective = old->cap_effective;
+ new->cap_ambient = old->cap_ambient;
new->cap_bset = old->cap_bset;
new->jit_keyring = old->jit_keyring;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d2ae32d..e447b01 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -413,6 +413,7 @@
!strcmp(sb->s_type->name, "sysfs") ||
!strcmp(sb->s_type->name, "pstore") ||
!strcmp(sb->s_type->name, "debugfs") ||
+ !strcmp(sb->s_type->name, "tracefs") ||
!strcmp(sb->s_type->name, "rootfs");
}
@@ -737,6 +738,7 @@
sbsec->flags |= SE_SBPROC | SE_SBGENFS;
if (!strcmp(sb->s_type->name, "debugfs") ||
+ !strcmp(sb->s_type->name, "tracefs") ||
!strcmp(sb->s_type->name, "sysfs") ||
!strcmp(sb->s_type->name, "pstore"))
sbsec->flags |= SE_SBGENFS;
@@ -5780,7 +5782,7 @@
return error;
/* Obtain a SID for the context, if one was specified. */
- if (size && str[1] && str[1] != '\n') {
+ if (size && str[0] && str[0] != '\n') {
if (str[size-1] == '\n') {
str[size-1] = 0;
size--;
diff --git a/sound/core/control.c b/sound/core/control.c
index 4de738b..d1079d4 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -164,6 +164,8 @@
if (snd_BUG_ON(!card || !id))
return;
+ if (card->shutdown)
+ return;
read_lock(&card->ctl_files_rwlock);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
card->mixer_oss_change_count++;
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index a4dabd5..4a24041 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -1921,6 +1921,7 @@
info.output_pool != client->pool->size)) {
if (snd_seq_write_pool_allocated(client)) {
/* remove all existing cells */
+ snd_seq_pool_mark_closing(client->pool);
snd_seq_queue_client_leave_cells(client->number);
snd_seq_pool_done(client->pool);
}
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index 1d5acbe..a2ff0d4 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -70,6 +70,9 @@
return;
*fifo = NULL;
+ if (f->pool)
+ snd_seq_pool_mark_closing(f->pool);
+
snd_seq_fifo_clear(f);
/* wake up clients if any */
@@ -259,6 +262,10 @@
/* NOTE: overflow flag is not cleared */
spin_unlock_irqrestore(&f->lock, flags);
+ /* close the old pool and wait until all users are gone */
+ snd_seq_pool_mark_closing(oldpool);
+ snd_use_lock_sync(&f->use_lock);
+
/* release cells in old pool */
for (cell = oldhead; cell; cell = next) {
next = cell->next;
diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
index 3b693e9..12ba833 100644
--- a/sound/core/seq/seq_lock.c
+++ b/sound/core/seq/seq_lock.c
@@ -28,19 +28,16 @@
/* wait until all locks are released */
void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
{
- int max_count = 5 * HZ;
+ int warn_count = 5 * HZ;
if (atomic_read(lockp) < 0) {
pr_warn("ALSA: seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
return;
}
while (atomic_read(lockp) > 0) {
- if (max_count == 0) {
- pr_warn("ALSA: seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line);
- break;
- }
+ if (warn_count-- == 0)
+ pr_warn("ALSA: seq_lock: waiting [%d left] in %s:%d\n", atomic_read(lockp), file, line);
schedule_timeout_uninterruptible(1);
- max_count--;
}
}
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index c850345..b855c08 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -414,6 +414,18 @@
return 0;
}
+/* refuse the further insertion to the pool */
+void snd_seq_pool_mark_closing(struct snd_seq_pool *pool)
+{
+ unsigned long flags;
+
+ if (snd_BUG_ON(!pool))
+ return;
+ spin_lock_irqsave(&pool->lock, flags);
+ pool->closing = 1;
+ spin_unlock_irqrestore(&pool->lock, flags);
+}
+
/* remove events */
int snd_seq_pool_done(struct snd_seq_pool *pool)
{
@@ -425,10 +437,6 @@
return -EINVAL;
/* wait for closing all threads */
- spin_lock_irqsave(&pool->lock, flags);
- pool->closing = 1;
- spin_unlock_irqrestore(&pool->lock, flags);
-
if (waitqueue_active(&pool->output_sleep))
wake_up(&pool->output_sleep);
@@ -491,6 +499,7 @@
*ppool = NULL;
if (pool == NULL)
return 0;
+ snd_seq_pool_mark_closing(pool);
snd_seq_pool_done(pool);
kfree(pool);
return 0;
diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h
index 4a2ec77..32f959c 100644
--- a/sound/core/seq/seq_memory.h
+++ b/sound/core/seq/seq_memory.h
@@ -84,6 +84,7 @@
int snd_seq_pool_init(struct snd_seq_pool *pool);
/* done pool - free events */
+void snd_seq_pool_mark_closing(struct snd_seq_pool *pool);
int snd_seq_pool_done(struct snd_seq_pool *pool);
/* create pool */
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 9d543fa..d448437 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -35,6 +35,9 @@
#include <sound/initval.h>
#include <linux/kmod.h>
+/* internal flags */
+#define SNDRV_TIMER_IFLG_PAUSED 0x00010000
+
#if IS_ENABLED(CONFIG_SND_HRTIMER)
#define DEFAULT_TIMER_LIMIT 4
#elif IS_ENABLED(CONFIG_SND_RTCTIMER)
@@ -296,8 +299,21 @@
get_device(&timer->card->card_dev);
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = slave_id;
- if (list_empty(&timer->open_list_head) && timer->hw.open)
- timer->hw.open(timer);
+
+ if (list_empty(&timer->open_list_head) && timer->hw.open) {
+ int err = timer->hw.open(timer);
+ if (err) {
+ kfree(timeri->owner);
+ kfree(timeri);
+
+ if (timer->card)
+ put_device(&timer->card->card_dev);
+ module_put(timer->module);
+ mutex_unlock(®ister_mutex);
+ return err;
+ }
+ }
+
list_add_tail(&timeri->open_list, &timer->open_list_head);
snd_timer_check_master(timeri);
mutex_unlock(®ister_mutex);
@@ -305,8 +321,6 @@
return 0;
}
-static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
-
/*
* close a timer instance
*/
@@ -395,7 +409,6 @@
static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
{
struct snd_timer *timer;
- unsigned long flags;
unsigned long resolution = 0;
struct snd_timer_instance *ts;
struct timespec tstamp;
@@ -419,34 +432,66 @@
return;
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
return;
- spin_lock_irqsave(&timer->lock, flags);
list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
ts->ccallback(ts, event + 100, &tstamp, resolution);
- spin_unlock_irqrestore(&timer->lock, flags);
}
-static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri,
- unsigned long sticks)
+/* start/continue a master timer */
+static int snd_timer_start1(struct snd_timer_instance *timeri,
+ bool start, unsigned long ticks)
{
+ struct snd_timer *timer;
+ int result;
+ unsigned long flags;
+
+ timer = timeri->timer;
+ if (!timer)
+ return -EINVAL;
+
+ spin_lock_irqsave(&timer->lock, flags);
+ if (timer->card && timer->card->shutdown) {
+ result = -ENODEV;
+ goto unlock;
+ }
+ if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+ SNDRV_TIMER_IFLG_START)) {
+ result = -EBUSY;
+ goto unlock;
+ }
+
+ if (start)
+ timeri->ticks = timeri->cticks = ticks;
+ else if (!timeri->cticks)
+ timeri->cticks = 1;
+ timeri->pticks = 0;
+
list_move_tail(&timeri->active_list, &timer->active_list_head);
if (timer->running) {
if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
goto __start_now;
timer->flags |= SNDRV_TIMER_FLG_RESCHED;
timeri->flags |= SNDRV_TIMER_IFLG_START;
- return 1; /* delayed start */
+ result = 1; /* delayed start */
} else {
- timer->sticks = sticks;
+ if (start)
+ timer->sticks = ticks;
timer->hw.start(timer);
__start_now:
timer->running++;
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
- return 0;
+ result = 0;
}
+ snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+ SNDRV_TIMER_EVENT_CONTINUE);
+ unlock:
+ spin_unlock_irqrestore(&timer->lock, flags);
+ return result;
}
-static int snd_timer_start_slave(struct snd_timer_instance *timeri)
+/* start/continue a slave timer */
+static int snd_timer_start_slave(struct snd_timer_instance *timeri,
+ bool start)
{
unsigned long flags;
@@ -460,88 +505,37 @@
spin_lock(&timeri->timer->lock);
list_add_tail(&timeri->active_list,
&timeri->master->slave_active_head);
+ snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+ SNDRV_TIMER_EVENT_CONTINUE);
spin_unlock(&timeri->timer->lock);
}
spin_unlock_irqrestore(&slave_active_lock, flags);
return 1; /* delayed start */
}
-/*
- * start the timer instance
- */
-int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+/* stop/pause a master timer */
+static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
{
struct snd_timer *timer;
- int result = -EINVAL;
+ int result = 0;
unsigned long flags;
- if (timeri == NULL || ticks < 1)
- return -EINVAL;
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- result = snd_timer_start_slave(timeri);
- if (result >= 0)
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
- return result;
- }
- timer = timeri->timer;
- if (timer == NULL)
- return -EINVAL;
- if (timer->card && timer->card->shutdown)
- return -ENODEV;
- spin_lock_irqsave(&timer->lock, flags);
- if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
- SNDRV_TIMER_IFLG_START)) {
- result = -EBUSY;
- goto unlock;
- }
- timeri->ticks = timeri->cticks = ticks;
- timeri->pticks = 0;
- result = snd_timer_start1(timer, timeri, ticks);
- unlock:
- spin_unlock_irqrestore(&timer->lock, flags);
- if (result >= 0)
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
- return result;
-}
-
-static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
-{
- struct snd_timer *timer;
- unsigned long flags;
-
- if (snd_BUG_ON(!timeri))
- return -ENXIO;
-
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- spin_lock_irqsave(&slave_active_lock, flags);
- if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
- spin_unlock_irqrestore(&slave_active_lock, flags);
- return -EBUSY;
- }
- if (timeri->timer)
- spin_lock(&timeri->timer->lock);
- timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
- list_del_init(&timeri->ack_list);
- list_del_init(&timeri->active_list);
- if (timeri->timer)
- spin_unlock(&timeri->timer->lock);
- spin_unlock_irqrestore(&slave_active_lock, flags);
- goto __end;
- }
timer = timeri->timer;
if (!timer)
return -EINVAL;
spin_lock_irqsave(&timer->lock, flags);
if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
SNDRV_TIMER_IFLG_START))) {
- spin_unlock_irqrestore(&timer->lock, flags);
- return -EBUSY;
+ result = -EBUSY;
+ goto unlock;
}
list_del_init(&timeri->ack_list);
list_del_init(&timeri->active_list);
- if (timer->card && timer->card->shutdown) {
- spin_unlock_irqrestore(&timer->lock, flags);
- return 0;
+ if (timer->card && timer->card->shutdown)
+ goto unlock;
+ if (stop) {
+ timeri->cticks = timeri->ticks;
+ timeri->pticks = 0;
}
if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
!(--timer->running)) {
@@ -556,35 +550,64 @@
}
}
timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
+ if (stop)
+ timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED;
+ else
+ timeri->flags |= SNDRV_TIMER_IFLG_PAUSED;
+ snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+ SNDRV_TIMER_EVENT_CONTINUE);
+ unlock:
spin_unlock_irqrestore(&timer->lock, flags);
- __end:
- if (event != SNDRV_TIMER_EVENT_RESOLUTION)
- snd_timer_notify1(timeri, event);
+ return result;
+}
+
+/* stop/pause a slave timer */
+static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&slave_active_lock, flags);
+ if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
+ spin_unlock_irqrestore(&slave_active_lock, flags);
+ return -EBUSY;
+ }
+ timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+ if (timeri->timer) {
+ spin_lock(&timeri->timer->lock);
+ list_del_init(&timeri->ack_list);
+ list_del_init(&timeri->active_list);
+ snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+ SNDRV_TIMER_EVENT_CONTINUE);
+ spin_unlock(&timeri->timer->lock);
+ }
+ spin_unlock_irqrestore(&slave_active_lock, flags);
return 0;
}
/*
+ * start the timer instance
+ */
+int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+{
+ if (timeri == NULL || ticks < 1)
+ return -EINVAL;
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_start_slave(timeri, true);
+ else
+ return snd_timer_start1(timeri, true, ticks);
+}
+
+/*
* stop the timer instance.
*
* do not call this from the timer callback!
*/
int snd_timer_stop(struct snd_timer_instance *timeri)
{
- struct snd_timer *timer;
- unsigned long flags;
- int err;
-
- err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP);
- if (err < 0)
- return err;
- timer = timeri->timer;
- if (!timer)
- return -EINVAL;
- spin_lock_irqsave(&timer->lock, flags);
- timeri->cticks = timeri->ticks;
- timeri->pticks = 0;
- spin_unlock_irqrestore(&timer->lock, flags);
- return 0;
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_stop_slave(timeri, true);
+ else
+ return snd_timer_stop1(timeri, true);
}
/*
@@ -592,32 +615,14 @@
*/
int snd_timer_continue(struct snd_timer_instance *timeri)
{
- struct snd_timer *timer;
- int result = -EINVAL;
- unsigned long flags;
-
- if (timeri == NULL)
- return result;
- if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
- return snd_timer_start_slave(timeri);
- timer = timeri->timer;
- if (! timer)
+ /* timer can continue only after pause */
+ if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
return -EINVAL;
- if (timer->card && timer->card->shutdown)
- return -ENODEV;
- spin_lock_irqsave(&timer->lock, flags);
- if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
- result = -EBUSY;
- goto unlock;
- }
- if (!timeri->cticks)
- timeri->cticks = 1;
- timeri->pticks = 0;
- result = snd_timer_start1(timer, timeri, timer->sticks);
- unlock:
- spin_unlock_irqrestore(&timer->lock, flags);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
- return result;
+
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_start_slave(timeri, false);
+ else
+ return snd_timer_start1(timeri, false, 0);
}
/*
@@ -625,7 +630,10 @@
*/
int snd_timer_pause(struct snd_timer_instance * timeri)
{
- return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE);
+ if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+ return snd_timer_stop_slave(timeri, false);
+ else
+ return snd_timer_stop1(timeri, false);
}
/*
@@ -839,6 +847,7 @@
timer->tmr_subdevice = tid->subdevice;
if (id)
strlcpy(timer->id, id, sizeof(timer->id));
+ timer->sticks = 1;
INIT_LIST_HEAD(&timer->device_list);
INIT_LIST_HEAD(&timer->open_list_head);
INIT_LIST_HEAD(&timer->active_list_head);
@@ -1053,8 +1062,8 @@
njiff += timer->sticks - priv->correction;
priv->correction = 0;
}
- priv->last_expires = priv->tlist.expires = njiff;
- add_timer(&priv->tlist);
+ priv->last_expires = njiff;
+ mod_timer(&priv->tlist, njiff);
return 0;
}
@@ -1830,6 +1839,9 @@
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
+ /* start timer instead of continue if it's not used before */
+ if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
+ return snd_timer_user_start(file);
tu->timeri->lost = 0;
return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
}
@@ -1968,6 +1980,7 @@
qhead = tu->qhead++;
tu->qhead %= tu->queue_size;
+ tu->qused--;
spin_unlock_irq(&tu->qlock);
if (tu->tread) {
@@ -1981,7 +1994,6 @@
}
spin_lock_irq(&tu->qlock);
- tu->qused--;
if (err < 0)
goto _error;
result += unit;
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 387bb8f..35cc884 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -422,6 +422,7 @@
static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
{
+ hrtimer_cancel(&dpcm->timer);
tasklet_kill(&dpcm->tasklet);
}
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 084d414..b431c34b 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -106,7 +106,6 @@
u8 *resp_buf;
u8 *pull_ptr;
u8 *push_ptr;
- unsigned int resp_queues;
};
int snd_efw_transaction_cmd(struct fw_unit *unit,
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
index 33df865..2e1d9a2 100644
--- a/sound/firewire/fireworks/fireworks_hwdep.c
+++ b/sound/firewire/fireworks/fireworks_hwdep.c
@@ -25,6 +25,7 @@
{
unsigned int length, till_end, type;
struct snd_efw_transaction *t;
+ u8 *pull_ptr;
long count = 0;
if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
@@ -38,8 +39,17 @@
buf += sizeof(type);
/* write into buffer as many responses as possible */
- while (efw->resp_queues > 0) {
- t = (struct snd_efw_transaction *)(efw->pull_ptr);
+ spin_lock_irq(&efw->lock);
+
+ /*
+ * When another task reaches here during this task's access to user
+ * space, it picks up current position in buffer and can read the same
+ * series of responses.
+ */
+ pull_ptr = efw->pull_ptr;
+
+ while (efw->push_ptr != pull_ptr) {
+ t = (struct snd_efw_transaction *)(pull_ptr);
length = be32_to_cpu(t->length) * sizeof(__be32);
/* confirm enough space for this response */
@@ -49,26 +59,39 @@
/* copy from ring buffer to user buffer */
while (length > 0) {
till_end = snd_efw_resp_buf_size -
- (unsigned int)(efw->pull_ptr - efw->resp_buf);
+ (unsigned int)(pull_ptr - efw->resp_buf);
till_end = min_t(unsigned int, length, till_end);
- if (copy_to_user(buf, efw->pull_ptr, till_end))
+ spin_unlock_irq(&efw->lock);
+
+ if (copy_to_user(buf, pull_ptr, till_end))
return -EFAULT;
- efw->pull_ptr += till_end;
- if (efw->pull_ptr >= efw->resp_buf +
- snd_efw_resp_buf_size)
- efw->pull_ptr -= snd_efw_resp_buf_size;
+ spin_lock_irq(&efw->lock);
+
+ pull_ptr += till_end;
+ if (pull_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
+ pull_ptr -= snd_efw_resp_buf_size;
length -= till_end;
buf += till_end;
count += till_end;
remained -= till_end;
}
-
- efw->resp_queues--;
}
+ /*
+ * All of tasks can read from the buffer nearly simultaneously, but the
+ * last position for each task is different depending on the length of
+ * given buffer. Here, for simplicity, a position of buffer is set by
+ * the latest task. It's better for a listening application to allow one
+ * thread to read from the buffer. Unless, each task can read different
+ * sequence of responses depending on variation of buffer length.
+ */
+ efw->pull_ptr = pull_ptr;
+
+ spin_unlock_irq(&efw->lock);
+
return count;
}
@@ -76,14 +99,17 @@
hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
loff_t *offset)
{
- union snd_firewire_event event;
+ union snd_firewire_event event = {
+ .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+ };
- memset(&event, 0, sizeof(event));
+ spin_lock_irq(&efw->lock);
- event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
event.lock_status.status = (efw->dev_lock_count > 0);
efw->dev_lock_changed = false;
+ spin_unlock_irq(&efw->lock);
+
count = min_t(long, count, sizeof(event.lock_status));
if (copy_to_user(buf, &event, count))
@@ -98,10 +124,15 @@
{
struct snd_efw *efw = hwdep->private_data;
DEFINE_WAIT(wait);
+ bool dev_lock_changed;
+ bool queued;
spin_lock_irq(&efw->lock);
- while ((!efw->dev_lock_changed) && (efw->resp_queues == 0)) {
+ dev_lock_changed = efw->dev_lock_changed;
+ queued = efw->push_ptr != efw->pull_ptr;
+
+ while (!dev_lock_changed && !queued) {
prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&efw->lock);
schedule();
@@ -109,15 +140,17 @@
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&efw->lock);
+ dev_lock_changed = efw->dev_lock_changed;
+ queued = efw->push_ptr != efw->pull_ptr;
}
- if (efw->dev_lock_changed)
- count = hwdep_read_locked(efw, buf, count, offset);
- else if (efw->resp_queues > 0)
- count = hwdep_read_resp_buf(efw, buf, count, offset);
-
spin_unlock_irq(&efw->lock);
+ if (dev_lock_changed)
+ count = hwdep_read_locked(efw, buf, count, offset);
+ else if (queued)
+ count = hwdep_read_resp_buf(efw, buf, count, offset);
+
return count;
}
@@ -160,7 +193,7 @@
poll_wait(file, &efw->hwdep_wait, wait);
spin_lock_irq(&efw->lock);
- if (efw->dev_lock_changed || (efw->resp_queues > 0))
+ if (efw->dev_lock_changed || efw->pull_ptr != efw->push_ptr)
events = POLLIN | POLLRDNORM;
else
events = 0;
diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c
index 0639dcb..beb0a0f 100644
--- a/sound/firewire/fireworks/fireworks_proc.c
+++ b/sound/firewire/fireworks/fireworks_proc.c
@@ -188,8 +188,8 @@
else
consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr);
- snd_iprintf(buffer, "%d %d/%d\n",
- efw->resp_queues, consumed, snd_efw_resp_buf_size);
+ snd_iprintf(buffer, "%d/%d\n",
+ consumed, snd_efw_resp_buf_size);
}
static void
diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c
index 2a85e42..8e9abbb 100644
--- a/sound/firewire/fireworks/fireworks_transaction.c
+++ b/sound/firewire/fireworks/fireworks_transaction.c
@@ -121,11 +121,11 @@
size_t capacity, till_end;
struct snd_efw_transaction *t;
- spin_lock_irq(&efw->lock);
-
t = (struct snd_efw_transaction *)data;
length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length);
+ spin_lock_irq(&efw->lock);
+
if (efw->push_ptr < efw->pull_ptr)
capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
else
@@ -155,7 +155,6 @@
}
/* for hwdep */
- efw->resp_queues++;
wake_up(&efw->hwdep_wait);
*rcode = RCODE_COMPLETE;
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 72e8128..8eae95a 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -1442,9 +1442,8 @@
int page, p, pp, delta, i;
page =
- (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2)) &
- WT_SUBBUF_MASK)
- >> WT_SUBBUF_SHIFT;
+ (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2))
+ >> WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK;
if (dma->nr_periods >= 4)
delta = (page - dma->period_real) & 3;
else {
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 631aaa4..3a1fe2c 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -2247,11 +2247,11 @@
DE_INIT(("resume start\n"));
pci_restore_state(pci);
- commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL);
+ commpage_bak = kmalloc(sizeof(*commpage), GFP_KERNEL);
if (commpage_bak == NULL)
return -ENOMEM;
commpage = chip->comm_page;
- memcpy(commpage_bak, commpage, sizeof(struct comm_page));
+ memcpy(commpage_bak, commpage, sizeof(*commpage));
err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
if (err < 0) {
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 58f8273..c1115b3 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -1119,8 +1119,10 @@
if (use_vga_switcheroo(hda)) {
if (chip->disabled && chip->bus)
snd_hda_unlock_devices(chip->bus);
- if (hda->vga_switcheroo_registered)
+ if (hda->vga_switcheroo_registered) {
vga_switcheroo_unregister_client(chip->pci);
+ vga_switcheroo_fini_domain_pm_ops(chip->card->dev);
+ }
}
if (chip->initialized) {
@@ -2091,8 +2093,14 @@
{ PCI_DEVICE(0x1022, 0x780d),
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB },
/* ATI HDMI */
+ { PCI_DEVICE(0x1002, 0x0002),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
{ PCI_DEVICE(0x1002, 0x1308),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0x157a),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0x15b3),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
{ PCI_DEVICE(0x1002, 0x793b),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0x7919),
@@ -2147,8 +2155,14 @@
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
{ PCI_DEVICE(0x1002, 0xaab0),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0xaac0),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
{ PCI_DEVICE(0x1002, 0xaac8),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0xaad8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0xaae8),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
/* VIA VT8251/VT8237A */
{ PCI_DEVICE(0x1106, 0x3288),
.driver_data = AZX_DRIVER_VIA | AZX_DCAPS_POSFIX_VIA },
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 1bc0be9..0e4908b 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -4552,6 +4552,55 @@
}
}
+/* Hook to update amp GPIO4 for automute */
+static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+
+ snd_hda_gen_hp_automute(codec, jack);
+ /* mute_led_polarity is set to 0, so we pass inverted value here */
+ alc_update_gpio_led(codec, 0x10, !spec->gen.hp_jack_present);
+}
+
+/* Manage GPIOs for HP EliteBook Folio 9480m.
+ *
+ * GPIO4 is the headphone amplifier power control
+ * GPIO3 is the audio output mute indicator LED
+ */
+
+static void alc280_fixup_hp_9480m(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
+ {}
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* Set the hooks to turn the headphone amp on/off
+ * as needed
+ */
+ spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
+ spec->gen.hp_automute_hook = alc280_hp_gpio4_automute_hook;
+
+ /* The GPIOs are currently off */
+ spec->gpio_led = 0;
+
+ /* GPIO3 is connected to the output mute LED,
+ * high is on, low is off
+ */
+ spec->mute_led_polarity = 0;
+ spec->gpio_mute_led_mask = 0x08;
+
+ /* Initialize GPIO configuration */
+ snd_hda_add_verbs(codec, gpio_init);
+ }
+}
+
/* for hda_fixup_thinkpad_acpi() */
#include "thinkpad_helper.c"
@@ -4633,6 +4682,7 @@
ALC286_FIXUP_HP_GPIO_LED,
ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY,
ALC280_FIXUP_HP_DOCK_PINS,
+ ALC280_FIXUP_HP_9480M,
ALC288_FIXUP_DELL_HEADSET_MODE,
ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC288_FIXUP_DELL_XPS_13_GPIO6,
@@ -4644,6 +4694,7 @@
ALC255_FIXUP_DELL_SPK_NOISE,
ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC280_FIXUP_HP_HEADSET_MIC,
+ ALC221_FIXUP_HP_FRONT_MIC,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -5164,6 +5215,10 @@
.chained = true,
.chain_id = ALC280_FIXUP_HP_GPIO4
},
+ [ALC280_FIXUP_HP_9480M] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_9480m,
+ },
[ALC288_FIXUP_DELL_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_dell_alc288,
@@ -5256,6 +5311,13 @@
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MIC,
},
+ [ALC221_FIXUP_HP_FRONT_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x02a19020 }, /* Front Mic */
+ { }
+ },
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -5312,6 +5374,7 @@
SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x22bf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x22db, "HP", ALC280_FIXUP_HP_9480M),
SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
/* ALC290 */
@@ -5345,6 +5408,7 @@
SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -5404,6 +5468,7 @@
SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x504a, "ThinkPad X260", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
@@ -5486,7 +5551,6 @@
{}
};
#define ALC225_STANDARD_PINS \
- {0x12, 0xb7a60130}, \
{0x21, 0x04211020}
#define ALC255_STANDARD_PINS \
@@ -5534,8 +5598,7 @@
{0x15, 0x0221401f}, \
{0x1a, 0x411111f0}, \
{0x1b, 0x411111f0}, \
- {0x1d, 0x40700001}, \
- {0x1e, 0x411111f0}
+ {0x1d, 0x40700001}
#define ALC298_STANDARD_PINS \
{0x18, 0x411111f0}, \
@@ -5553,10 +5616,24 @@
{0x14, 0x901701b0}),
SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
{0x14, 0x901701a0}),
SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
{0x14, 0x901701b0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60150},
+ {0x14, 0x901701a0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60150},
+ {0x14, 0x901701b0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x1b, 0x90170110}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC255_STANDARD_PINS,
{0x12, 0x40300000},
@@ -5631,6 +5708,13 @@
{0x17, 0x40000000},
{0x1d, 0x40700001},
{0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5565", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60180},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x1b, 0x01011020},
+ {0x21, 0x02211010}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC256_STANDARD_PINS,
{0x13, 0x40000000}),
@@ -5799,35 +5883,48 @@
{0x13, 0x411111f0},
{0x16, 0x01014020},
{0x18, 0x411111f0},
- {0x19, 0x01a19030}),
+ {0x19, 0x01a19030},
+ {0x1e, 0x411111f0}),
SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
{0x12, 0x90a60140},
{0x13, 0x411111f0},
{0x16, 0x01014020},
{0x18, 0x02a19031},
- {0x19, 0x01a1903e}),
+ {0x19, 0x01a1903e},
+ {0x1e, 0x411111f0}),
SND_HDA_PIN_QUIRK(0x10ec0292, 0x1028, "Dell", ALC269_FIXUP_DELL3_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
{0x12, 0x90a60140},
{0x13, 0x411111f0},
{0x16, 0x411111f0},
{0x18, 0x411111f0},
- {0x19, 0x411111f0}),
+ {0x19, 0x411111f0},
+ {0x1e, 0x411111f0}),
SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
{0x12, 0x40000000},
{0x13, 0x90a60140},
{0x16, 0x21014020},
{0x18, 0x411111f0},
- {0x19, 0x21a19030}),
+ {0x19, 0x21a19030},
+ {0x1e, 0x411111f0}),
SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
{0x12, 0x40000000},
{0x13, 0x90a60140},
{0x16, 0x411111f0},
{0x18, 0x411111f0},
- {0x19, 0x411111f0}),
+ {0x19, 0x411111f0},
+ {0x1e, 0x411111f0}),
+ SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC292_STANDARD_PINS,
+ {0x12, 0x40000000},
+ {0x13, 0x90a60140},
+ {0x16, 0x21014020},
+ {0x18, 0x411111f0},
+ {0x19, 0x21a19030},
+ {0x1e, 0x411111ff}),
SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC298_STANDARD_PINS,
{0x12, 0x90a60130},
@@ -6382,6 +6479,8 @@
ALC668_FIXUP_AUTO_MUTE,
ALC668_FIXUP_DELL_DISABLE_AAMIX,
ALC668_FIXUP_DELL_XPS13,
+ ALC662_FIXUP_ASUS_Nx50,
+ ALC668_FIXUP_ASUS_Nx51,
};
static const struct hda_fixup alc662_fixups[] = {
@@ -6622,6 +6721,21 @@
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_bass_chmap,
},
+ [ALC662_FIXUP_ASUS_Nx50] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_auto_mute_via_amp,
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_1A
+ },
+ [ALC668_FIXUP_ASUS_Nx51] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ {0x1a, 0x90170151}, /* bass speaker */
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -6642,9 +6756,14 @@
SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
- SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A),
+ SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE),
+ SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50),
+ SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A),
+ SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50),
SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16),
+ SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51),
+ SND_PCI_QUIRK(0x1043, 0x17bd, "ASUS N751", ALC668_FIXUP_ASUS_Nx51),
SND_PCI_QUIRK(0x1043, 0x1b73, "ASUS N55SF", ALC662_FIXUP_BASS_16),
SND_PCI_QUIRK(0x1043, 0x1bf3, "ASUS N76VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),
diff --git a/sound/pci/oxygen/oxygen_mixer.c b/sound/pci/oxygen/oxygen_mixer.c
index 5988e04..259bf54a 100644
--- a/sound/pci/oxygen/oxygen_mixer.c
+++ b/sound/pci/oxygen/oxygen_mixer.c
@@ -88,7 +88,7 @@
int changed;
mutex_lock(&chip->mutex);
- changed = !value->value.integer.value[0] != chip->dac_mute;
+ changed = (!value->value.integer.value[0]) != chip->dac_mute;
if (changed) {
chip->dac_mute = !value->value.integer.value[0];
chip->model.update_dac_mute(chip);
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index a584acb..874591e 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -1339,5 +1339,6 @@
}
pcxhr_msg_thread(mgr);
+ mutex_unlock(&mgr->lock);
return IRQ_HANDLED;
}
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 5fbf5db..8e9c71a 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -991,6 +991,7 @@
if (err < 0)
return err;
}
+ master_vol = NULL;
if (pm7500)
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500),
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 0417125..f449d87 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -64,12 +64,15 @@
#define FIL1_0 0x1c
#define FIL1_1 0x1d
#define FIL1_2 0x1e
-#define FIL1_3 0x1f
+#define FIL1_3 0x1f /* The maximum valid register for ak4642 */
#define PW_MGMT4 0x20
#define MD_CTL5 0x21
#define LO_MS 0x22
#define HP_MS 0x23
-#define SPK_MS 0x24
+#define SPK_MS 0x24 /* The maximum valid register for ak4643 */
+#define EQ_FBEQAB 0x25
+#define EQ_FBEQCD 0x26
+#define EQ_FBEQE 0x27 /* The maximum valid register for ak4648 */
/* PW_MGMT1*/
#define PMVCM (1 << 6) /* VCOM Power Management */
@@ -210,7 +213,7 @@
/*
* ak4642 register cache
*/
-static const struct reg_default ak4642_reg[] = {
+static const struct reg_default ak4643_reg[] = {
{ 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
{ 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
{ 8, 0xe1 }, { 9, 0xe1 }, { 10, 0x18 }, { 11, 0x00 },
@@ -223,6 +226,14 @@
{ 36, 0x00 },
};
+/* The default settings for 0x0 ~ 0x1f registers are the same for ak4642
+ and ak4643. So we reuse the ak4643 reg_default for ak4642.
+ The valid registers for ak4642 are 0x0 ~ 0x1f which is a subset of ak4643,
+ so define NUM_AK4642_REG_DEFAULTS for ak4642.
+*/
+#define ak4642_reg ak4643_reg
+#define NUM_AK4642_REG_DEFAULTS (FIL1_3 + 1)
+
static const struct reg_default ak4648_reg[] = {
{ 0, 0x00 }, { 1, 0x00 }, { 2, 0x01 }, { 3, 0x00 },
{ 4, 0x02 }, { 5, 0x00 }, { 6, 0x00 }, { 7, 0x00 },
@@ -521,17 +532,28 @@
static const struct regmap_config ak4642_regmap = {
.reg_bits = 8,
.val_bits = 8,
- .max_register = ARRAY_SIZE(ak4642_reg) + 1,
+ .max_register = FIL1_3,
.reg_defaults = ak4642_reg,
- .num_reg_defaults = ARRAY_SIZE(ak4642_reg),
+ .num_reg_defaults = NUM_AK4642_REG_DEFAULTS,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config ak4643_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = SPK_MS,
+ .reg_defaults = ak4643_reg,
+ .num_reg_defaults = ARRAY_SIZE(ak4643_reg),
+ .cache_type = REGCACHE_RBTREE,
};
static const struct regmap_config ak4648_regmap = {
.reg_bits = 8,
.val_bits = 8,
- .max_register = ARRAY_SIZE(ak4648_reg) + 1,
+ .max_register = EQ_FBEQE,
.reg_defaults = ak4648_reg,
.num_reg_defaults = ARRAY_SIZE(ak4648_reg),
+ .cache_type = REGCACHE_RBTREE,
};
static const struct ak4642_drvdata ak4642_drvdata = {
@@ -539,7 +561,7 @@
};
static const struct ak4642_drvdata ak4643_drvdata = {
- .regmap_config = &ak4642_regmap,
+ .regmap_config = &ak4643_regmap,
};
static const struct ak4642_drvdata ak4648_drvdata = {
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index e6d4ff9..736c1ea 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -148,11 +148,11 @@
};
static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
- { "Capture", NULL, "AINA" },
- { "Capture", NULL, "AINB" },
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
- { "AOUTA", NULL, "Playback" },
- { "AOUTB", NULL, "Playback" },
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
};
/**
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index c3f2decd..607c758 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -361,7 +361,7 @@
/* Interface data select */
static const char * const rt5640_data_select[] = {
- "Normal", "left copy to right", "right copy to left", "Swap"};
+ "Normal", "Swap", "left copy to right", "right copy to left"};
static SOC_ENUM_SINGLE_DECL(rt5640_if1_dac_enum, RT5640_DIG_INF_DATA,
RT5640_IF1_DAC_SEL_SFT, rt5640_data_select);
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index 3deb8ba..243f426 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -442,39 +442,39 @@
#define RT5640_IF1_DAC_SEL_MASK (0x3 << 14)
#define RT5640_IF1_DAC_SEL_SFT 14
#define RT5640_IF1_DAC_SEL_NOR (0x0 << 14)
-#define RT5640_IF1_DAC_SEL_L2R (0x1 << 14)
-#define RT5640_IF1_DAC_SEL_R2L (0x2 << 14)
-#define RT5640_IF1_DAC_SEL_SWAP (0x3 << 14)
+#define RT5640_IF1_DAC_SEL_SWAP (0x1 << 14)
+#define RT5640_IF1_DAC_SEL_L2R (0x2 << 14)
+#define RT5640_IF1_DAC_SEL_R2L (0x3 << 14)
#define RT5640_IF1_ADC_SEL_MASK (0x3 << 12)
#define RT5640_IF1_ADC_SEL_SFT 12
#define RT5640_IF1_ADC_SEL_NOR (0x0 << 12)
-#define RT5640_IF1_ADC_SEL_L2R (0x1 << 12)
-#define RT5640_IF1_ADC_SEL_R2L (0x2 << 12)
-#define RT5640_IF1_ADC_SEL_SWAP (0x3 << 12)
+#define RT5640_IF1_ADC_SEL_SWAP (0x1 << 12)
+#define RT5640_IF1_ADC_SEL_L2R (0x2 << 12)
+#define RT5640_IF1_ADC_SEL_R2L (0x3 << 12)
#define RT5640_IF2_DAC_SEL_MASK (0x3 << 10)
#define RT5640_IF2_DAC_SEL_SFT 10
#define RT5640_IF2_DAC_SEL_NOR (0x0 << 10)
-#define RT5640_IF2_DAC_SEL_L2R (0x1 << 10)
-#define RT5640_IF2_DAC_SEL_R2L (0x2 << 10)
-#define RT5640_IF2_DAC_SEL_SWAP (0x3 << 10)
+#define RT5640_IF2_DAC_SEL_SWAP (0x1 << 10)
+#define RT5640_IF2_DAC_SEL_L2R (0x2 << 10)
+#define RT5640_IF2_DAC_SEL_R2L (0x3 << 10)
#define RT5640_IF2_ADC_SEL_MASK (0x3 << 8)
#define RT5640_IF2_ADC_SEL_SFT 8
#define RT5640_IF2_ADC_SEL_NOR (0x0 << 8)
-#define RT5640_IF2_ADC_SEL_L2R (0x1 << 8)
-#define RT5640_IF2_ADC_SEL_R2L (0x2 << 8)
-#define RT5640_IF2_ADC_SEL_SWAP (0x3 << 8)
+#define RT5640_IF2_ADC_SEL_SWAP (0x1 << 8)
+#define RT5640_IF2_ADC_SEL_L2R (0x2 << 8)
+#define RT5640_IF2_ADC_SEL_R2L (0x3 << 8)
#define RT5640_IF3_DAC_SEL_MASK (0x3 << 6)
#define RT5640_IF3_DAC_SEL_SFT 6
#define RT5640_IF3_DAC_SEL_NOR (0x0 << 6)
-#define RT5640_IF3_DAC_SEL_L2R (0x1 << 6)
-#define RT5640_IF3_DAC_SEL_R2L (0x2 << 6)
-#define RT5640_IF3_DAC_SEL_SWAP (0x3 << 6)
+#define RT5640_IF3_DAC_SEL_SWAP (0x1 << 6)
+#define RT5640_IF3_DAC_SEL_L2R (0x2 << 6)
+#define RT5640_IF3_DAC_SEL_R2L (0x3 << 6)
#define RT5640_IF3_ADC_SEL_MASK (0x3 << 4)
#define RT5640_IF3_ADC_SEL_SFT 4
#define RT5640_IF3_ADC_SEL_NOR (0x0 << 4)
-#define RT5640_IF3_ADC_SEL_L2R (0x1 << 4)
-#define RT5640_IF3_ADC_SEL_R2L (0x2 << 4)
-#define RT5640_IF3_ADC_SEL_SWAP (0x3 << 4)
+#define RT5640_IF3_ADC_SEL_SWAP (0x1 << 4)
+#define RT5640_IF3_ADC_SEL_L2R (0x2 << 4)
+#define RT5640_IF3_ADC_SEL_R2L (0x3 << 4)
/* REC Left Mixer Control 1 (0x3b) */
#define RT5640_G_HP_L_RM_L_MASK (0x7 << 13)
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index 4b5c17f..9bb976e 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -206,6 +206,11 @@
regcache_cache_only(ssm4567->regmap, !enable);
if (enable) {
+ ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET,
+ 0x00);
+ if (ret)
+ return ret;
+
ret = regmap_update_bits(ssm4567->regmap,
SSM4567_REG_POWER_CTRL,
SSM4567_POWER_SPWDN, 0x00);
diff --git a/sound/soc/codecs/tfa9897_init.c b/sound/soc/codecs/tfa9897_init.c
index 901285d..bdb0c6d 100644
--- a/sound/soc/codecs/tfa9897_init.c
+++ b/sound/soc/codecs/tfa9897_init.c
@@ -103,9 +103,6 @@
{
enum Tfa98xx_Error error;
- /* Not used for max1! */
- sample_rate = sample_rate;
-
error = tfa9897_dsp_write_vsfwdelay_table(dev_idx);
if (error == Tfa98xx_Error_Ok) {
error = tfa9897_dsp_write_cvfracdelay_table(dev_idx);
diff --git a/sound/soc/codecs/tfa_dsp.c b/sound/soc/codecs/tfa_dsp.c
index 310da2c..df25d82 100644
--- a/sound/soc/codecs/tfa_dsp.c
+++ b/sound/soc/codecs/tfa_dsp.c
@@ -2594,8 +2594,6 @@
{
enum Tfa98xx_Error err = Tfa98xx_Error_Ok;
int calibrateDone, spkr_count = 0;
- /* Avoid warning in user-space */
- profile = profile;
#ifdef __KERNEL__ /* Necessary otherwise we are thrown out of operating mode in kernel (because of internal clock) */
if ((strnstr(tfaContProfileName(handle, profile), ".cal", strlen(tfaContProfileName(handle, profile))) == NULL) && (tfa98xx_dev_family(handle) == 2)) {
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index e314097..c0fd42c6 100755
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -1028,7 +1028,7 @@
static bool wcd_mbhc_detect_anc_plug_type(struct wcd_mbhc *mbhc)
{
bool anc_mic_found = false;
- u16 val, hs_comp_res, btn_status = 0;
+ u16 val = 0, hs_comp_res = 0, btn_status = 0;
unsigned long retry = 0;
int valid_plug_cnt = 0, invalid_plug_cnt = 0;
int btn_status_cnt = 0;
@@ -1042,6 +1042,10 @@
if (!mbhc->mbhc_cb->mbhc_micbias_control)
return false;
+ // Fail when reg WCD_MBHC_FSM_EN is not valid
+ if (!mbhc->wcd_mbhc_regs[WCD_MBHC_FSM_EN].reg)
+ return false;
+
WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, val);
if (val)
@@ -1074,7 +1078,8 @@
pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1);
WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
- if (!hs_comp_res) {
+ if (!hs_comp_res &&
+ mbhc->wcd_mbhc_regs[WCD_MBHC_HS_COMP_RESULT].reg) {
valid_plug_cnt++;
is_check_btn_press = true;
} else
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index c480277..b2e1c23 100755
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -4643,9 +4643,12 @@
{
struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
u16 prim_int_reg;
- u16 ind;
+ u16 ind = 0;
prim_int_reg = tasha_interp_get_primary_reg(reg, &ind);
+ if (!prim_int_reg) {
+ return -EINVAL;
+ }
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -5115,7 +5118,7 @@
struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
u16 gain_reg;
u16 reg;
- int val;
+ int val, ret;
int offset_val = 0;
dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
@@ -5160,7 +5163,12 @@
set_bit(SB_CLK_GEAR, &tasha->status_mask);
}
/* Reset if needed */
- tasha_codec_enable_prim_interpolator(codec, reg, event);
+ ret = tasha_codec_enable_prim_interpolator(codec, reg, event);
+ if (ret) {
+ dev_err(codec->dev, "%s: enable_prim_interpolator fail\n",
+ __func__);
+ return ret;
+ }
break;
case SND_SOC_DAPM_POST_PMU:
tasha_config_compander(codec, w->shift, event);
@@ -5188,7 +5196,12 @@
break;
case SND_SOC_DAPM_POST_PMD:
tasha_config_compander(codec, w->shift, event);
- tasha_codec_enable_prim_interpolator(codec, reg, event);
+ ret = tasha_codec_enable_prim_interpolator(codec, reg, event);
+ if (ret) {
+ dev_err(codec->dev, "%s: enable_prim_interpolator fail\n",
+ __func__);
+ return ret;
+ }
if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
(tasha->comp_enabled[COMPANDER_7] ||
tasha->comp_enabled[COMPANDER_8]) &&
@@ -10803,18 +10816,13 @@
u32 tx_port;
u8 shift, shift_val, tx_mux_sel;
int decimator = -1;
- u16 tx_port_reg, tx_fs_reg;
+ u16 tx_port_reg = 0, tx_fs_reg;
list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) {
tx_port = ch->port;
dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d",
__func__, dai->id, tx_port);
- if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) {
- dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n",
- __func__, tx_port, dai->id);
- return -EINVAL;
- }
/* Find the SB TX MUX input - which decimator is connected */
if (tx_port < 4) {
tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0;
@@ -10836,6 +10844,10 @@
tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3;
shift = 4;
shift_val = 0x03;
+ } else { // (tx_port == 12) || (tx_port >= 14)
+ dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n",
+ __func__, tx_port, dai->id);
+ return -EINVAL;
}
tx_mux_sel = snd_soc_read(codec, tx_port_reg) &
(shift_val << shift);
@@ -12681,7 +12693,7 @@
{
struct snd_soc_codec *codec = data;
struct tasha_priv *tasha;
- u32 cpe_clk_khz, req_freq;
+ u32 cpe_clk_khz, req_freq = 0;
if (!codec) {
pr_err("%s: Invalid codec handle\n",
diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c
index 97a492c..5f7809e 100644
--- a/sound/soc/codecs/wcd_cpe_core.c
+++ b/sound/soc/codecs/wcd_cpe_core.c
@@ -3588,6 +3588,7 @@
struct cpe_param_data *param_d = &lab_enable->param;
struct cpe_lsm_ids ids;
+ memset(&cpe_lab_enable, 0, sizeof (cpe_lab_enable));
pr_debug("%s: enter payload_size = %d Enable %d\n",
__func__, pld_size, enable);
diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c
index 2fa25c0..1af8da3 100644
--- a/sound/soc/msm/msm-cpe-lsm.c
+++ b/sound/soc/msm/msm-cpe-lsm.c
@@ -2581,7 +2581,7 @@
event_status->payload_size;
memcpy(udata_32->payload,
event_status->payload,
- u_pld_size);
+ udata_32->payload_size);
}
}
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 3581312..2819212 100755
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -2211,8 +2211,20 @@
.rate_min = 8000,
.rate_max = 192000,
},
+ .capture = {
+ .stream_name = "MultiMedia10 Capture",
+ .aif_name = "MM_UL10",
+ .rates = (SNDRV_PCM_RATE_8000_48000|
+ SNDRV_PCM_RATE_KNOT),
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE),
+ .channels_min = 1,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
.ops = &msm_fe_Multimedia_dai_ops,
- .compress_dai = 1,
.name = "MultiMedia10",
.probe = fe_dai_probe,
},
diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c
index 0311b94..bc2f1c5 100644
--- a/sound/soc/msm/msm8996.c
+++ b/sound/soc/msm/msm8996.c
@@ -3336,7 +3336,7 @@
* Send speaker configuration only for WSA8810.
* Defalut configuration is for WSA8815.
*/
- if (rtd_aux && rtd_aux->component)
+ if (rtd->card->num_aux_devs > 0 && rtd_aux && rtd_aux->component)
if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
!strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1);
@@ -3923,12 +3923,13 @@
.be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
},
{
- .name = "MSM8996 Compress3",
- .stream_name = "Compress3",
+ .name = "MSM8996 ULL NOIRQ_2",
+ .stream_name = "MM_NOIRQ_2",
.cpu_dai_name = "MultiMedia10",
- .platform_name = "msm-compress-dsp",
+ .platform_name = "msm-pcm-dsp-noirq",
.dynamic = 1,
.dpcm_playback = 1,
+ .dpcm_capture = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST,
SND_SOC_DPCM_TRIGGER_POST},
.codec_dai_name = "snd-soc-dummy-dai",
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index ab7e30b..9ab6a27 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -2220,6 +2220,11 @@
msm_route_ec_ref_rx_enum[0],
msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul10 =
+ SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL10 MUX Mux",
+ msm_route_ec_ref_rx_enum[0],
+ msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
static int msm_routing_ext_ec_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -4193,6 +4198,66 @@
msm_routing_put_audio_mixer),
};
+static const struct snd_kcontrol_new mmul10_mixer_controls[] = {
+ SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("SLIM_6_TX", MSM_BACKEND_DAI_SLIMBUS_6_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+ MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
+
static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
@@ -6874,6 +6939,7 @@
SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("MM_UL9", "MultiMedia9 Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MM_UL10", "MultiMedia10 Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VOICE2_DL", "Voice2 Playback", 0, 0, 0, 0),
@@ -7504,6 +7570,8 @@
mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia9 Mixer", SND_SOC_NOPM, 0, 0,
mmul9_mixer_controls, ARRAY_SIZE(mmul9_mixer_controls)),
+ SND_SOC_DAPM_MIXER("MultiMedia10 Mixer", SND_SOC_NOPM, 0, 0,
+ mmul10_mixer_controls, ARRAY_SIZE(mmul10_mixer_controls)),
SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
@@ -7727,6 +7795,8 @@
&ext_ec_ref_mux_ul8),
SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL9 MUX", SND_SOC_NOPM, 0, 0,
&ext_ec_ref_mux_ul9),
+ SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL10 MUX", SND_SOC_NOPM, 0, 0,
+ &ext_ec_ref_mux_ul10),
};
static const struct snd_soc_dapm_route intercon[] = {
@@ -8340,6 +8410,30 @@
{"MM_UL6", NULL, "MultiMedia6 Mixer"},
{"MM_UL8", NULL, "MultiMedia8 Mixer"},
{"MM_UL9", NULL, "MultiMedia9 Mixer"},
+ {"MM_UL10", NULL, "MultiMedia10 Mixer"},
+
+ {"MultiMedia10 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia10 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"},
+ {"MultiMedia10 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+ {"MultiMedia10 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+ {"MultiMedia10 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"},
+ {"MultiMedia10 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+ {"MultiMedia10 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+ {"MultiMedia10 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+
+ {"MultiMedia10 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+ {"MultiMedia10 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+ {"MultiMedia10 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+ {"MultiMedia10 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+ {"MultiMedia10 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+ {"MultiMedia10 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+ {"MultiMedia10 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+ {"MultiMedia10 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+ {"MultiMedia10 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+ {"MultiMedia10 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+
+ {"MultiMedia10 Mixer", "AFE_PCM_TX", "PCM_TX"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
{"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
@@ -8629,6 +8723,11 @@
{"AUDIO_REF_EC_UL9 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
{"AUDIO_REF_EC_UL9 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL10 MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
+ {"AUDIO_REF_EC_UL10 MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
+ {"AUDIO_REF_EC_UL10 MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
+ {"AUDIO_REF_EC_UL10 MUX", "QUAT_MI2S_TX" , "QUAT_MI2S_TX"},
+
{"MM_UL1", NULL, "AUDIO_REF_EC_UL1 MUX"},
{"MM_UL2", NULL, "AUDIO_REF_EC_UL2 MUX"},
{"MM_UL3", NULL, "AUDIO_REF_EC_UL3 MUX"},
@@ -8637,6 +8736,7 @@
{"MM_UL6", NULL, "AUDIO_REF_EC_UL6 MUX"},
{"MM_UL8", NULL, "AUDIO_REF_EC_UL8 MUX"},
{"MM_UL9", NULL, "AUDIO_REF_EC_UL9 MUX"},
+ {"MM_UL10", NULL, "AUDIO_REF_EC_UL10 MUX"},
{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
{"Voice_Tx Mixer", "PRI_MI2S_TX_Voice", "PRI_MI2S_TX"},
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
index 8292f47..9dcf6f9 100644
--- a/sound/soc/msm/qdsp6v2/q6voice.c
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -7624,8 +7624,13 @@
{
int ret = 0;
int cal_index;
+ int cal_version = cal_utils_get_cal_type_version(data);
+
pr_debug("%s\n", __func__);
+ common.is_per_vocoder_cal_enabled =
+ (cal_version & PER_VOCODER_CAL_BIT_MASK) ? true : false;
+
cal_index = get_cal_type_index(cal_type);
if (cal_index < 0) {
pr_err("%s: Could not get cal index %d!\n",
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index e161511..98e3b93 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -324,7 +324,7 @@
static int s3c_ac97_probe(struct platform_device *pdev)
{
- struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
+ struct resource *mem_res, *irq_res;
struct s3c_audio_pdata *ac97_pdata;
int ret;
@@ -335,24 +335,6 @@
}
/* Check for availability of necessary resource */
- dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dmatx_res) {
- dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
- return -ENXIO;
- }
-
- dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!dmarx_res) {
- dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
- return -ENXIO;
- }
-
- dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
- if (!dmamic_res) {
- dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
- return -ENXIO;
- }
-
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq_res) {
dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
@@ -364,11 +346,11 @@
if (IS_ERR(s3c_ac97.regs))
return PTR_ERR(s3c_ac97.regs);
- s3c_ac97_pcm_out.channel = dmatx_res->start;
+ s3c_ac97_pcm_out.slave = ac97_pdata->dma_playback;
s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
- s3c_ac97_pcm_in.channel = dmarx_res->start;
+ s3c_ac97_pcm_in.slave = ac97_pdata->dma_capture;
s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
- s3c_ac97_mic_in.channel = dmamic_res->start;
+ s3c_ac97_mic_in.slave = ac97_pdata->dma_capture_mic;
s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
init_completion(&s3c_ac97.done);
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index 0e85dcf..085ef30 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -15,7 +15,7 @@
#include <sound/dmaengine_pcm.h>
struct s3c_dma_params {
- int channel; /* Channel ID */
+ void *slave; /* Channel ID */
dma_addr_t dma_addr;
int dma_size; /* Size of the DMA transfer */
char *ch_name;
diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c
index 506f5bf..727008d 100644
--- a/sound/soc/samsung/dmaengine.c
+++ b/sound/soc/samsung/dmaengine.c
@@ -50,14 +50,14 @@
if (playback) {
playback_data = &playback->dma_data;
- playback_data->filter_data = (void *)playback->channel;
+ playback_data->filter_data = playback->slave;
playback_data->chan_name = playback->ch_name;
playback_data->addr = playback->dma_addr;
playback_data->addr_width = playback->dma_size;
}
if (capture) {
capture_data = &capture->dma_data;
- capture_data->filter_data = (void *)capture->channel;
+ capture_data->filter_data = capture->slave;
capture_data->chan_name = capture->ch_name;
capture_data->addr = capture->dma_addr;
capture_data->addr_width = capture->dma_size;
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 9d51347..f2ce03d 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1167,27 +1167,14 @@
}
if (!np) {
- res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get I2S-TX dma resource\n");
- return -ENXIO;
- }
- pri_dai->dma_playback.channel = res->start;
-
- res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!res) {
- dev_err(&pdev->dev,
- "Unable to get I2S-RX dma resource\n");
- return -ENXIO;
- }
- pri_dai->dma_capture.channel = res->start;
-
if (i2s_pdata == NULL) {
dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
return -EINVAL;
}
+ pri_dai->dma_playback.slave = i2s_pdata->dma_playback;
+ pri_dai->dma_capture.slave = i2s_pdata->dma_capture;
+
if (&i2s_pdata->type)
i2s_cfg = &i2s_pdata->type.i2s;
@@ -1242,11 +1229,8 @@
sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
sec_dai->dma_playback.ch_name = "tx-sec";
- if (!np) {
- res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
- if (res)
- sec_dai->dma_playback.channel = res->start;
- }
+ if (!np)
+ sec_dai->dma_playback.slave = i2s_pdata->dma_play_sec;
sec_dai->dma_playback.dma_size = 4;
sec_dai->base = regs_base;
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index bac034b..9e6e33b 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -486,7 +486,7 @@
static int s3c_pcm_dev_probe(struct platform_device *pdev)
{
struct s3c_pcm_info *pcm;
- struct resource *mem_res, *dmatx_res, *dmarx_res;
+ struct resource *mem_res;
struct s3c_audio_pdata *pcm_pdata;
int ret;
@@ -499,18 +499,6 @@
pcm_pdata = pdev->dev.platform_data;
/* Check for availability of necessary resource */
- dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dmatx_res) {
- dev_err(&pdev->dev, "Unable to get PCM-TX dma resource\n");
- return -ENXIO;
- }
-
- dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (!dmarx_res) {
- dev_err(&pdev->dev, "Unable to get PCM-RX dma resource\n");
- return -ENXIO;
- }
-
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
dev_err(&pdev->dev, "Unable to get register resource\n");
@@ -568,8 +556,10 @@
s3c_pcm_stereo_out[pdev->id].dma_addr = mem_res->start
+ S3C_PCM_TXFIFO;
- s3c_pcm_stereo_in[pdev->id].channel = dmarx_res->start;
- s3c_pcm_stereo_out[pdev->id].channel = dmatx_res->start;
+ if (pcm_pdata) {
+ s3c_pcm_stereo_in[pdev->id].slave = pcm_pdata->dma_capture;
+ s3c_pcm_stereo_out[pdev->id].slave = pcm_pdata->dma_playback;
+ }
pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
index df65c5b..b6ab3fc 100644
--- a/sound/soc/samsung/s3c-i2s-v2.c
+++ b/sound/soc/samsung/s3c-i2s-v2.c
@@ -709,7 +709,7 @@
#endif
int s3c_i2sv2_register_component(struct device *dev, int id,
- struct snd_soc_component_driver *cmp_drv,
+ const struct snd_soc_component_driver *cmp_drv,
struct snd_soc_dai_driver *dai_drv)
{
struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops;
diff --git a/sound/soc/samsung/s3c-i2s-v2.h b/sound/soc/samsung/s3c-i2s-v2.h
index 90abab3..d068414 100644
--- a/sound/soc/samsung/s3c-i2s-v2.h
+++ b/sound/soc/samsung/s3c-i2s-v2.h
@@ -101,7 +101,7 @@
* soc core.
*/
extern int s3c_i2sv2_register_component(struct device *dev, int id,
- struct snd_soc_component_driver *cmp_drv,
+ const struct snd_soc_component_driver *cmp_drv,
struct snd_soc_dai_driver *dai_drv);
#endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */
diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c
index 27b339c..e3650d1 100644
--- a/sound/soc/samsung/s3c2412-i2s.c
+++ b/sound/soc/samsung/s3c2412-i2s.c
@@ -34,13 +34,13 @@
#include "s3c2412-i2s.h"
static struct s3c_dma_params s3c2412_i2s_pcm_stereo_out = {
- .channel = DMACH_I2S_OUT,
+ .slave = (void *)(uintptr_t)DMACH_I2S_OUT,
.ch_name = "tx",
.dma_size = 4,
};
static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = {
- .channel = DMACH_I2S_IN,
+ .slave = (void *)(uintptr_t)DMACH_I2S_IN,
.ch_name = "rx",
.dma_size = 4,
};
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
index fb1d393..60be398 100644
--- a/sound/soc/samsung/s3c24xx-i2s.c
+++ b/sound/soc/samsung/s3c24xx-i2s.c
@@ -32,13 +32,13 @@
#include "s3c24xx-i2s.h"
static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_out = {
- .channel = DMACH_I2S_OUT,
+ .slave = (void *)(uintptr_t)DMACH_I2S_OUT,
.ch_name = "tx",
.dma_size = 2,
};
static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = {
- .channel = DMACH_I2S_IN,
+ .slave = (void *)(uintptr_t)DMACH_I2S_IN,
.ch_name = "rx",
.dma_size = 2,
};
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index d7d2e20..1de2686 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -359,7 +359,7 @@
static int spdif_probe(struct platform_device *pdev)
{
struct s3c_audio_pdata *spdif_pdata;
- struct resource *mem_res, *dma_res;
+ struct resource *mem_res;
struct samsung_spdif_info *spdif;
int ret;
@@ -367,12 +367,6 @@
dev_dbg(&pdev->dev, "Entered %s\n", __func__);
- dma_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dma_res) {
- dev_err(&pdev->dev, "Unable to get dma resource.\n");
- return -ENXIO;
- }
-
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem_res) {
dev_err(&pdev->dev, "Unable to get register resource.\n");
@@ -432,7 +426,7 @@
spdif_stereo_out.dma_size = 2;
spdif_stereo_out.dma_addr = mem_res->start + DATA_OUTBUF;
- spdif_stereo_out.channel = dma_res->start;
+ spdif_stereo_out.slave = spdif_pdata ? spdif_pdata->dma_playback : NULL;
spdif->dma_playback = &spdif_stereo_out;
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index 58785e5..6c2c833 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -118,11 +118,14 @@
return -EINVAL;
mutex_lock(&codec->cache_rw_mutex);
- if (!ZERO_OR_NULL_PTR(codec->reg_cache))
+ if (!ZERO_OR_NULL_PTR(codec->reg_cache)) {
*value = snd_soc_get_cache_val(codec->reg_cache, reg,
codec->driver->reg_word_size);
+ } else {
+ mutex_unlock(&codec->cache_rw_mutex);
+ return -EINVAL;
+ }
mutex_unlock(&codec->cache_rw_mutex);
-
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_cache_read);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index c3e6484..a06c41d 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -741,7 +741,7 @@
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai;
+ struct snd_soc_dai *codec_dai = NULL;
int i, ret = 0;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
@@ -798,7 +798,7 @@
cancel_delayed_work(&rtd->delayed_work);
}
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ if (codec_dai && substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
if (codec_dai->capture_active == 1)
snd_soc_dapm_stream_event(rtd,
SNDRV_PCM_STREAM_CAPTURE,
diff --git a/sound/usb/hiface/pcm.c b/sound/usb/hiface/pcm.c
index 2c44139..33db205 100644
--- a/sound/usb/hiface/pcm.c
+++ b/sound/usb/hiface/pcm.c
@@ -445,6 +445,8 @@
mutex_lock(&rt->stream_mutex);
+ hiface_pcm_stream_stop(rt);
+
sub->dma_off = 0;
sub->period_off = 0;
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 8d541a1..1f1dc5c 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -922,9 +922,10 @@
case USB_ID(0x046d, 0x0826): /* HD Webcam c525 */
case USB_ID(0x046d, 0x08ca): /* Logitech Quickcam Fusion */
case USB_ID(0x046d, 0x0991):
+ case USB_ID(0x046d, 0x09a2): /* QuickCam Communicate Deluxe/S7500 */
/* Most audio usb devices lie about volume resolution.
* Most Logitech webcams have res = 384.
- * Proboly there is some logitech magic behind this number --fishor
+ * Probably there is some logitech magic behind this number --fishor
*/
if (!strcmp(kctl->id.name, "Mic Capture Volume")) {
usb_audio_info(chip,
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 9a3e107..c758638 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -351,6 +351,16 @@
};
/*
+ * Dell usb dock with ALC4020 codec had a firmware problem where it got
+ * screwed up when zero volume is passed; just skip it as a workaround
+ */
+static const struct usbmix_name_map dell_alc4020_map[] = {
+ { 16, NULL },
+ { 19, NULL },
+ { 0 }
+};
+
+/*
* Control map entries
*/
@@ -433,6 +443,10 @@
.map = aureon_51_2_map,
},
{
+ .id = USB_ID(0x0bda, 0x4014),
+ .map = dell_alc4020_map,
+ },
+ {
.id = USB_ID(0x13e5, 0x0001),
.map = scratch_live_map,
.ignore_ctl_error = 1,
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 17e1fda..814a118 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2942,6 +2942,40 @@
AU0828_DEVICE(0x2040, 0x7213, "Hauppauge", "HVR-950Q"),
AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
+/* Syntek STK1160 */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ .idVendor = 0x05e1,
+ .idProduct = 0x0408,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Syntek",
+ .product_name = "STK1160",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_AUDIO_ALIGN_TRANSFER
+ }
+},
+
+/* Syntek STK1160 */
+{
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ .idVendor = 0x05e1,
+ .idProduct = 0x0408,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+ .vendor_name = "Syntek",
+ .product_name = "STK1160",
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_AUDIO_ALIGN_TRANSFER
+ }
+},
+
/* Digidesign Mbox */
{
/* Thanks to Clemens Ladisch <clemens@ladisch.de> */
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 64789dd..5ace1fb 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -166,9 +166,8 @@
alts = &iface->altsetting[fp->altset_idx];
altsd = get_iface_desc(alts);
if (altsd->bNumEndpoints < 1) {
- kfree(fp);
- kfree(rate_table);
- return -EINVAL;
+ err = -EINVAL;
+ goto error;
}
fp->protocol = altsd->bInterfaceProtocol;
@@ -1123,8 +1122,16 @@
case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */
case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
+ case USB_ID(0x047F, 0x0415): /* Plantronics BT-300 */
case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
+ case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */
+ case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
+ case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */
+ case USB_ID(0x1de7, 0x0013): /* Phoenix Audio MT202exe */
+ case USB_ID(0x1de7, 0x0014): /* Phoenix Audio TMX320 */
+ case USB_ID(0x1de7, 0x0114): /* Phoenix Audio MT202pcs */
+ case USB_ID(0x21B4, 0x0081): /* AudioQuest DragonFly */
return true;
}
return false;
diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c
index b502344..88cccea 100644
--- a/tools/lib/traceevent/parse-filter.c
+++ b/tools/lib/traceevent/parse-filter.c
@@ -1058,6 +1058,7 @@
*parg = current_op;
else
*parg = current_exp;
+ free(token);
return PEVENT_ERRNO__UNBALANCED_PAREN;
}
break;
@@ -1163,11 +1164,12 @@
current_op = current_exp;
ret = collapse_tree(current_op, parg, error_str);
+ /* collapse_tree() may free current_op, and updates parg accordingly */
+ current_op = NULL;
if (ret < 0)
goto fail;
- *parg = current_op;
-
+ free(token);
return 0;
fail_alloc:
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt
index 29ee857..6f9fbb4 100644
--- a/tools/perf/Documentation/perf-stat.txt
+++ b/tools/perf/Documentation/perf-stat.txt
@@ -50,6 +50,14 @@
--scale::
scale/normalize counter values
+-d::
+--detailed::
+ print more detailed statistics, can be specified up to 3 times
+
+ -d: detailed events, L1 and LLC data cache
+ -d -d: more detailed events, dTLB and iTLB events
+ -d -d -d: very detailed events, adding prefetch events
+
-r::
--repeat=<n>::
repeat command and print average + stddev (max: 100). 0 means forever.
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 4af6b27..da4c17c 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -197,7 +197,7 @@
strcpy(execname, "");
/* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */
- n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %s\n",
+ n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %x:%x %u %[^\n]\n",
&event->mmap2.start, &event->mmap2.len, prot,
&event->mmap2.pgoff, &event->mmap2.maj,
&event->mmap2.min,
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
index dcb1e9a..d9d10f3 100755
--- a/tools/testing/ktest/ktest.pl
+++ b/tools/testing/ktest/ktest.pl
@@ -2450,7 +2450,7 @@
}
waitpid $child_pid, 0;
- $child_exit = $?;
+ $child_exit = $? >> 8;
if (!$bug && $in_bisect) {
if (defined($bisect_ret_good)) {
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE03 b/tools/testing/selftests/rcutorture/configs/rcu/TREE03
index f4567fb..b95f62e 100644
--- a/tools/testing/selftests/rcutorture/configs/rcu/TREE03
+++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE03
@@ -17,5 +17,5 @@
CONFIG_RCU_CPU_STALL_INFO=n
CONFIG_RCU_CPU_STALL_VERBOSE=n
CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_PRIO=2
+CONFIG_RCU_KTHREAD_PRIO=2
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
diff --git a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
index 3e588db..ab6e7b4 100644
--- a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
+++ b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt
@@ -15,8 +15,8 @@
CONFIG_PROVE_LOCKING -- Do all but two, covering CONFIG_PROVE_RCU and not.
CONFIG_PROVE_RCU -- Do all but one under CONFIG_PROVE_LOCKING.
CONFIG_RCU_BOOST -- one of TREE_PREEMPT_RCU.
-CONFIG_RCU_BOOST_PRIO -- set to 2 for _BOOST testing.
-CONFIG_RCU_CPU_STALL_INFO -- do one with and without _VERBOSE.
+CONFIG_RCU_KTHREAD_PRIO -- set to 2 for _BOOST testing.
+CONFIG_RCU_CPU_STALL_INFO -- Do one.
CONFIG_RCU_CPU_STALL_VERBOSE -- do one with and without _INFO.
CONFIG_RCU_FANOUT -- Cover hierarchy as currently, but overlap with others.
CONFIG_RCU_FANOUT_EXACT -- Do one.
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c
index 5a310ca..64238c9 100644
--- a/virt/kvm/eventfd.c
+++ b/virt/kvm/eventfd.c
@@ -866,7 +866,8 @@
continue;
kvm_io_bus_unregister_dev(kvm, bus_idx, &p->dev);
- kvm->buses[bus_idx]->ioeventfd_count--;
+ if (kvm->buses[bus_idx])
+ kvm->buses[bus_idx]->ioeventfd_count--;
ioeventfd_release(p);
ret = 0;
break;
diff --git a/virt/kvm/irqchip.c b/virt/kvm/irqchip.c
index 7f256f3..da39ee3 100644
--- a/virt/kvm/irqchip.c
+++ b/virt/kvm/irqchip.c
@@ -51,7 +51,7 @@
irq_rt = srcu_dereference_check(kvm->irq_routing, &kvm->irq_srcu,
lockdep_is_held(&kvm->irq_lock));
- if (gsi < irq_rt->nr_rt_entries) {
+ if (irq_rt && gsi < irq_rt->nr_rt_entries) {
hlist_for_each_entry(e, &irq_rt->map[gsi], link) {
entries[n] = *e;
++n;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 2c9d47f..3d5ae6f 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -139,6 +139,7 @@
put_cpu();
return 0;
}
+EXPORT_SYMBOL_GPL(vcpu_load);
void vcpu_put(struct kvm_vcpu *vcpu)
{
@@ -148,6 +149,7 @@
preempt_enable();
mutex_unlock(&vcpu->mutex);
}
+EXPORT_SYMBOL_GPL(vcpu_put);
static void ack_flush(void *_completed)
{
@@ -612,8 +614,11 @@
list_del(&kvm->vm_list);
spin_unlock(&kvm_lock);
kvm_free_irq_routing(kvm);
- for (i = 0; i < KVM_NR_BUSES; i++)
- kvm_io_bus_destroy(kvm->buses[i]);
+ for (i = 0; i < KVM_NR_BUSES; i++) {
+ if (kvm->buses[i])
+ kvm_io_bus_destroy(kvm->buses[i]);
+ kvm->buses[i] = NULL;
+ }
kvm_coalesced_mmio_free(kvm);
#if defined(CONFIG_MMU_NOTIFIER) && defined(KVM_ARCH_WANT_MMU_NOTIFIER)
mmu_notifier_unregister(&kvm->mmu_notifier, kvm->mm);
@@ -2559,7 +2564,7 @@
if (copy_from_user(&routing, argp, sizeof(routing)))
goto out;
r = -EINVAL;
- if (routing.nr >= KVM_MAX_IRQ_ROUTES)
+ if (routing.nr > KVM_MAX_IRQ_ROUTES)
goto out;
if (routing.flags)
goto out;
@@ -2976,6 +2981,8 @@
};
bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu);
+ if (!bus)
+ return -ENOMEM;
r = __kvm_io_bus_write(bus, &range, val);
return r < 0 ? r : 0;
}
@@ -2993,6 +3000,8 @@
};
bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu);
+ if (!bus)
+ return -ENOMEM;
/* First try the device referenced by cookie. */
if ((cookie >= 0) && (cookie < bus->dev_count) &&
@@ -3043,6 +3052,8 @@
};
bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu);
+ if (!bus)
+ return -ENOMEM;
r = __kvm_io_bus_read(bus, &range, val);
return r < 0 ? r : 0;
}
@@ -3055,6 +3066,9 @@
struct kvm_io_bus *new_bus, *bus;
bus = kvm->buses[bus_idx];
+ if (!bus)
+ return -ENOMEM;
+
/* exclude ioeventfd which is limited by maximum fd */
if (bus->dev_count - bus->ioeventfd_count > NR_IOBUS_DEVS - 1)
return -ENOSPC;
@@ -3074,37 +3088,41 @@
}
/* Caller must hold slots_lock. */
-int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
- struct kvm_io_device *dev)
+void kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx,
+ struct kvm_io_device *dev)
{
- int i, r;
+ int i;
struct kvm_io_bus *new_bus, *bus;
bus = kvm->buses[bus_idx];
- r = -ENOENT;
+ if (!bus)
+ return;
+
for (i = 0; i < bus->dev_count; i++)
if (bus->range[i].dev == dev) {
- r = 0;
break;
}
- if (r)
- return r;
+ if (i == bus->dev_count)
+ return;
new_bus = kzalloc(sizeof(*bus) + ((bus->dev_count - 1) *
sizeof(struct kvm_io_range)), GFP_KERNEL);
- if (!new_bus)
- return -ENOMEM;
+ if (!new_bus) {
+ pr_err("kvm: failed to shrink bus, removing it completely\n");
+ goto broken;
+ }
memcpy(new_bus, bus, sizeof(*bus) + i * sizeof(struct kvm_io_range));
new_bus->dev_count--;
memcpy(new_bus->range + i, bus->range + i + 1,
(new_bus->dev_count - i) * sizeof(struct kvm_io_range));
+broken:
rcu_assign_pointer(kvm->buses[bus_idx], new_bus);
synchronize_srcu_expedited(&kvm->srcu);
kfree(bus);
- return r;
+ return;
}
static struct notifier_block kvm_cpu_notifier = {