diff --git a/.travis.yml b/.travis.yml index 23e81f65..56592e40 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ addons: apt: - gcc-aarch64-linux-gnu script: -- git config --global user.name "홍길동" +- git config --global user.name "abc" - git config --global user.email "support@webisfree.com" - yes | git clone https://github.com/roynatech2544/Samsung_Device_A20e_Kernel.git -b Kernel_4.4.177 - cd Samsung_Device_A20e_Kernel @@ -18,5 +18,5 @@ script: - cp arch/arm64/boot/Image out - cp arch/arm64/boot/dts/exynos/dtbo/exynos7884B-a20e_kor_ktt_00.dtbo out - cp arch/arm64/boot/dts/exynos/dtbo/exynos7884B-a20e_kor_ktt_00.dts out -- zip -r Kernel out -- curl --upload-file Kernel.zip https://transfer.sh/Kernel.zip +- zip -r Kernel_$TRAVIS_BUILD_NUMBER out +- curl --upload-file Kernel_$TRAVIS_BUILD_NUMBER.zip https://transfer.sh/Kernel.zip diff --git a/README b/README deleted file mode 100755 index 70935dd3..00000000 --- a/README +++ /dev/null @@ -1,406 +0,0 @@ -[![Build Status](https://travis-ci.com/roynatech2544/Samsung_Device_A20e_Kernel.svg?branch=Kernel_4.4.177)](https://travis-ci.com/roynatech2544/Samsung_Device_A20e_Kernel) - -Linux kernel release 4.x - -These are the release notes for Linux version 4. Read them carefully, -as they tell you what this is all about, explain how to install the -kernel, and what to do if something goes wrong. - -WHAT IS LINUX? - - Linux is a clone of the operating system Unix, written from scratch by - Linus Torvalds with assistance from a loosely-knit team of hackers across - the Net. It aims towards POSIX and Single UNIX Specification compliance. - - It has all the features you would expect in a modern fully-fledged Unix, - including true multitasking, virtual memory, shared libraries, demand - loading, shared copy-on-write executables, proper memory management, - and multistack networking including IPv4 and IPv6. - - It is distributed under the GNU General Public License - see the - accompanying COPYING file for more details. - -ON WHAT HARDWARE DOES IT RUN? - - Although originally developed first for 32-bit x86-based PCs (386 or higher), - today Linux also runs on (at least) the Compaq Alpha AXP, Sun SPARC and - UltraSPARC, Motorola 68000, PowerPC, PowerPC64, ARM, Hitachi SuperH, Cell, - IBM S/390, MIPS, HP PA-RISC, Intel IA-64, DEC VAX, AMD x86-64, AXIS CRIS, - Xtensa, Tilera TILE, AVR32, ARC and Renesas M32R architectures. - - Linux is easily portable to most general-purpose 32- or 64-bit architectures - as long as they have a paged memory management unit (PMMU) and a port of the - GNU C compiler (gcc) (part of The GNU Compiler Collection, GCC). Linux has - also been ported to a number of architectures without a PMMU, although - functionality is then obviously somewhat limited. - Linux has also been ported to itself. You can now run the kernel as a - userspace application - this is called UserMode Linux (UML). - -DOCUMENTATION: - - - There is a lot of documentation available both in electronic form on - the Internet and in books, both Linux-specific and pertaining to - general UNIX questions. I'd recommend looking into the documentation - subdirectories on any Linux FTP site for the LDP (Linux Documentation - Project) books. This README is not meant to be documentation on the - system: there are much better sources available. - - - There are various README files in the Documentation/ subdirectory: - these typically contain kernel-specific installation notes for some - drivers for example. See Documentation/00-INDEX for a list of what - is contained in each file. Please read the Changes file, as it - contains information about the problems, which may result by upgrading - your kernel. - - - The Documentation/DocBook/ subdirectory contains several guides for - kernel developers and users. These guides can be rendered in a - number of formats: PostScript (.ps), PDF, HTML, & man-pages, among others. - After installation, "make psdocs", "make pdfdocs", "make htmldocs", - or "make mandocs" will render the documentation in the requested format. - -INSTALLING the kernel source: - - - If you install the full sources, put the kernel tarball in a - directory where you have permissions (eg. your home directory) and - unpack it: - - xz -cd linux-4.X.tar.xz | tar xvf - - - Replace "X" with the version number of the latest kernel. - - Do NOT use the /usr/src/linux area! This area has a (usually - incomplete) set of kernel headers that are used by the library header - files. They should match the library, and not get messed up by - whatever the kernel-du-jour happens to be. - - - You can also upgrade between 4.x releases by patching. Patches are - distributed in the xz format. To install by patching, get all the - newer patch files, enter the top level directory of the kernel source - (linux-4.X) and execute: - - xz -cd ../patch-4.x.xz | patch -p1 - - Replace "x" for all versions bigger than the version "X" of your current - source tree, _in_order_, and you should be ok. You may want to remove - the backup files (some-file-name~ or some-file-name.orig), and make sure - that there are no failed patches (some-file-name# or some-file-name.rej). - If there are, either you or I have made a mistake. - - Unlike patches for the 4.x kernels, patches for the 4.x.y kernels - (also known as the -stable kernels) are not incremental but instead apply - directly to the base 4.x kernel. For example, if your base kernel is 4.0 - and you want to apply the 4.0.3 patch, you must not first apply the 4.0.1 - and 4.0.2 patches. Similarly, if you are running kernel version 4.0.2 and - want to jump to 4.0.3, you must first reverse the 4.0.2 patch (that is, - patch -R) _before_ applying the 4.0.3 patch. You can read more on this in - Documentation/applying-patches.txt - - Alternatively, the script patch-kernel can be used to automate this - process. It determines the current kernel version and applies any - patches found. - - linux/scripts/patch-kernel linux - - The first argument in the command above is the location of the - kernel source. Patches are applied from the current directory, but - an alternative directory can be specified as the second argument. - - - Make sure you have no stale .o files and dependencies lying around: - - cd linux - make mrproper - - You should now have the sources correctly installed. - -SOFTWARE REQUIREMENTS - - Compiling and running the 4.x kernels requires up-to-date - versions of various software packages. Consult - Documentation/Changes for the minimum version numbers required - and how to get updates for these packages. Beware that using - excessively old versions of these packages can cause indirect - errors that are very difficult to track down, so don't assume that - you can just update packages when obvious problems arise during - build or operation. - -BUILD directory for the kernel: - - When compiling the kernel, all output files will per default be - stored together with the kernel source code. - Using the option "make O=output/dir" allow you to specify an alternate - place for the output files (including .config). - Example: - - kernel source code: /usr/src/linux-4.X - build directory: /home/name/build/kernel - - To configure and build the kernel, use: - - cd /usr/src/linux-4.X - make O=/home/name/build/kernel menuconfig - make O=/home/name/build/kernel - sudo make O=/home/name/build/kernel modules_install install - - Please note: If the 'O=output/dir' option is used, then it must be - used for all invocations of make. - -CONFIGURING the kernel: - - Do not skip this step even if you are only upgrading one minor - version. New configuration options are added in each release, and - odd problems will turn up if the configuration files are not set up - as expected. If you want to carry your existing configuration to a - new version with minimal work, use "make oldconfig", which will - only ask you for the answers to new questions. - - - Alternative configuration commands are: - - "make config" Plain text interface. - - "make menuconfig" Text based color menus, radiolists & dialogs. - - "make nconfig" Enhanced text based color menus. - - "make xconfig" X windows (Qt) based configuration tool. - - "make gconfig" X windows (GTK+) based configuration tool. - - "make oldconfig" Default all questions based on the contents of - your existing ./.config file and asking about - new config symbols. - - "make silentoldconfig" - Like above, but avoids cluttering the screen - with questions already answered. - Additionally updates the dependencies. - - "make olddefconfig" - Like above, but sets new symbols to their default - values without prompting. - - "make defconfig" Create a ./.config file by using the default - symbol values from either arch/$ARCH/defconfig - or arch/$ARCH/configs/${PLATFORM}_defconfig, - depending on the architecture. - - "make ${PLATFORM}_defconfig" - Create a ./.config file by using the default - symbol values from - arch/$ARCH/configs/${PLATFORM}_defconfig. - Use "make help" to get a list of all available - platforms of your architecture. - - "make allyesconfig" - Create a ./.config file by setting symbol - values to 'y' as much as possible. - - "make allmodconfig" - Create a ./.config file by setting symbol - values to 'm' as much as possible. - - "make allnoconfig" Create a ./.config file by setting symbol - values to 'n' as much as possible. - - "make randconfig" Create a ./.config file by setting symbol - values to random values. - - "make localmodconfig" Create a config based on current config and - loaded modules (lsmod). Disables any module - option that is not needed for the loaded modules. - - To create a localmodconfig for another machine, - store the lsmod of that machine into a file - and pass it in as a LSMOD parameter. - - target$ lsmod > /tmp/mylsmod - target$ scp /tmp/mylsmod host:/tmp - - host$ make LSMOD=/tmp/mylsmod localmodconfig - - The above also works when cross compiling. - - "make localyesconfig" Similar to localmodconfig, except it will convert - all module options to built in (=y) options. - - You can find more information on using the Linux kernel config tools - in Documentation/kbuild/kconfig.txt. - - - NOTES on "make config": - - - Having unnecessary drivers will make the kernel bigger, and can - under some circumstances lead to problems: probing for a - nonexistent controller card may confuse your other controllers - - - Compiling the kernel with "Processor type" set higher than 386 - will result in a kernel that does NOT work on a 386. The - kernel will detect this on bootup, and give up. - - - A kernel with math-emulation compiled in will still use the - coprocessor if one is present: the math emulation will just - never get used in that case. The kernel will be slightly larger, - but will work on different machines regardless of whether they - have a math coprocessor or not. - - - The "kernel hacking" configuration details usually result in a - bigger or slower kernel (or both), and can even make the kernel - less stable by configuring some routines to actively try to - break bad code to find kernel problems (kmalloc()). Thus you - should probably answer 'n' to the questions for "development", - "experimental", or "debugging" features. - -COMPILING the kernel: - - - Make sure you have at least gcc 3.2 available. - For more information, refer to Documentation/Changes. - - Please note that you can still run a.out user programs with this kernel. - - - Do a "make" to create a compressed kernel image. It is also - possible to do "make install" if you have lilo installed to suit the - kernel makefiles, but you may want to check your particular lilo setup first. - - To do the actual install, you have to be root, but none of the normal - build should require that. Don't take the name of root in vain. - - - If you configured any of the parts of the kernel as `modules', you - will also have to do "make modules_install". - - - Verbose kernel compile/build output: - - Normally, the kernel build system runs in a fairly quiet mode (but not - totally silent). However, sometimes you or other kernel developers need - to see compile, link, or other commands exactly as they are executed. - For this, use "verbose" build mode. This is done by inserting - "V=1" in the "make" command. E.g.: - - make V=1 all - - To have the build system also tell the reason for the rebuild of each - target, use "V=2". The default is "V=0". - - - Keep a backup kernel handy in case something goes wrong. This is - especially true for the development releases, since each new release - contains new code which has not been debugged. Make sure you keep a - backup of the modules corresponding to that kernel, as well. If you - are installing a new kernel with the same version number as your - working kernel, make a backup of your modules directory before you - do a "make modules_install". - - Alternatively, before compiling, use the kernel config option - "LOCALVERSION" to append a unique suffix to the regular kernel version. - LOCALVERSION can be set in the "General Setup" menu. - - - In order to boot your new kernel, you'll need to copy the kernel - image (e.g. .../linux/arch/i386/boot/bzImage after compilation) - to the place where your regular bootable kernel is found. - - - Booting a kernel directly from a floppy without the assistance of a - bootloader such as LILO, is no longer supported. - - If you boot Linux from the hard drive, chances are you use LILO, which - uses the kernel image as specified in the file /etc/lilo.conf. The - kernel image file is usually /vmlinuz, /boot/vmlinuz, /bzImage or - /boot/bzImage. To use the new kernel, save a copy of the old image - and copy the new image over the old one. Then, you MUST RERUN LILO - to update the loading map!! If you don't, you won't be able to boot - the new kernel image. - - Reinstalling LILO is usually a matter of running /sbin/lilo. - You may wish to edit /etc/lilo.conf to specify an entry for your - old kernel image (say, /vmlinux.old) in case the new one does not - work. See the LILO docs for more information. - - After reinstalling LILO, you should be all set. Shutdown the system, - reboot, and enjoy! - - If you ever need to change the default root device, video mode, - ramdisk size, etc. in the kernel image, use the 'rdev' program (or - alternatively the LILO boot options when appropriate). No need to - recompile the kernel to change these parameters. - - - Reboot with the new kernel and enjoy. - -IF SOMETHING GOES WRONG: - - - If you have problems that seem to be due to kernel bugs, please check - the file MAINTAINERS to see if there is a particular person associated - with the part of the kernel that you are having trouble with. If there - isn't anyone listed there, then the second best thing is to mail - them to me (torvalds@linux-foundation.org), and possibly to any other - relevant mailing-list or to the newsgroup. - - - In all bug-reports, *please* tell what kernel you are talking about, - how to duplicate the problem, and what your setup is (use your common - sense). If the problem is new, tell me so, and if the problem is - old, please try to tell me when you first noticed it. - - - If the bug results in a message like - - unable to handle kernel paging request at address C0000010 - Oops: 0002 - EIP: 0010:XXXXXXXX - eax: xxxxxxxx ebx: xxxxxxxx ecx: xxxxxxxx edx: xxxxxxxx - esi: xxxxxxxx edi: xxxxxxxx ebp: xxxxxxxx - ds: xxxx es: xxxx fs: xxxx gs: xxxx - Pid: xx, process nr: xx - xx xx xx xx xx xx xx xx xx xx - - or similar kernel debugging information on your screen or in your - system log, please duplicate it *exactly*. The dump may look - incomprehensible to you, but it does contain information that may - help debugging the problem. The text above the dump is also - important: it tells something about why the kernel dumped code (in - the above example, it's due to a bad kernel pointer). More information - on making sense of the dump is in Documentation/oops-tracing.txt - - - If you compiled the kernel with CONFIG_KALLSYMS you can send the dump - as is, otherwise you will have to use the "ksymoops" program to make - sense of the dump (but compiling with CONFIG_KALLSYMS is usually preferred). - This utility can be downloaded from - ftp://ftp..kernel.org/pub/linux/utils/kernel/ksymoops/ . - Alternatively, you can do the dump lookup by hand: - - - In debugging dumps like the above, it helps enormously if you can - look up what the EIP value means. The hex value as such doesn't help - me or anybody else very much: it will depend on your particular - kernel setup. What you should do is take the hex value from the EIP - line (ignore the "0010:"), and look it up in the kernel namelist to - see which kernel function contains the offending address. - - To find out the kernel function name, you'll need to find the system - binary associated with the kernel that exhibited the symptom. This is - the file 'linux/vmlinux'. To extract the namelist and match it against - the EIP from the kernel crash, do: - - nm vmlinux | sort | less - - This will give you a list of kernel addresses sorted in ascending - order, from which it is simple to find the function that contains the - offending address. Note that the address given by the kernel - debugging messages will not necessarily match exactly with the - function addresses (in fact, that is very unlikely), so you can't - just 'grep' the list: the list will, however, give you the starting - point of each kernel function, so by looking for the function that - has a starting address lower than the one you are searching for but - is followed by a function with a higher address you will find the one - you want. In fact, it may be a good idea to include a bit of - "context" in your problem report, giving a few lines around the - interesting one. - - If you for some reason cannot do the above (you have a pre-compiled - kernel image or similar), telling me as much about your setup as - possible will help. Please read the REPORTING-BUGS document for details. - - - Alternatively, you can use gdb on a running kernel. (read-only; i.e. you - cannot change values or set break points.) To do this, first compile the - kernel with -g; edit arch/i386/Makefile appropriately, then do a "make - clean". You'll also need to enable CONFIG_PROC_FS (via "make config"). - - After you've rebooted with the new kernel, do "gdb vmlinux /proc/kcore". - You can now use all the usual gdb commands. The command to look up the - point where your system crashed is "l *0xXXXXXXXX". (Replace the XXXes - with the EIP value.) - - gdb'ing a non-running kernel currently fails because gdb (wrongly) - disregards the starting offset for which the kernel is compiled. - diff --git a/README.md b/README.md new file mode 100755 index 00000000..ee8a56aa --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# SM-A202K/F Kernel Source +[![Build Status](https://travis-ci.com/roynatech2544/Samsung_Device_A20e_Kernel.svg?branch=Kernel_4.4.177)](https://travis-ci.com/roynatech2544/Samsung_Device_A20e_Kernel) +Modified Kernel Source for A20e running exynos7885 CPU chipset diff --git a/arch/arm64/boot/Image b/arch/arm64/boot/Image deleted file mode 100755 index 3dac7ed1..00000000 Binary files a/arch/arm64/boot/Image and /dev/null differ diff --git a/arch/arm64/boot/Image.gz b/arch/arm64/boot/Image.gz deleted file mode 100644 index 36d48eac..00000000 Binary files a/arch/arm64/boot/Image.gz and /dev/null differ diff --git a/arch/arm64/boot/dts/exynos/dtbo/exynos7884B-a20e_kor_ktt_00.dtbo b/arch/arm64/boot/dts/exynos/dtbo/exynos7884B-a20e_kor_ktt_00.dtbo deleted file mode 100644 index ffacea1c..00000000 Binary files a/arch/arm64/boot/dts/exynos/dtbo/exynos7884B-a20e_kor_ktt_00.dtbo and /dev/null differ diff --git a/arch/arm64/boot/dts/exynos/dtbo/exynos7884B-a20e_kor_ktt_00.dts b/arch/arm64/boot/dts/exynos/dtbo/exynos7884B-a20e_kor_ktt_00.dts deleted file mode 100755 index f4406fb1..00000000 --- a/arch/arm64/boot/dts/exynos/dtbo/exynos7884B-a20e_kor_ktt_00.dts +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SAMSUNG EXYNOS7884B board device tree source - - * - * Copyright (c) 2018 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -/dts-v1/; -/plugin/; - -#include "exynos7884B-a20e_common.dtsi" -#include "exynos7884B-a20e_kor_ktt_gpio_00.dtsi" -#include "exynos7884B-a20e_fingerprint_00.dtsi" -#include "exynos7884B-a20e_kor-tdmb-00.dtsi" - -/ { - compatible = "samsung,A20E KOR KTT 00", "samsung,EXYNOS7884B"; - - dtbo-hw_rev = <0>; - dtbo-hw_rev_end = <255>; - - - fragment@model { - target-path = "/"; - __overlay__ { - #address-cells = <2>; - #size-cells = <1>; - - model = "Samsung A20E KOR KTT 00 board based on EXYNOS7884B"; - - fm@14840000 { - /delete-property/ elna_gpio; - elna_gpio = <&gpg0 2 0x1>; /* FM_LNA_EN */ - num-trfon-freq = <56>; - val-trfon-freq = <87500 87600 88300 88400 89000 89100 89800 89900 - 90600 90700 91300 91400 92100 92200 92900 93000 93600 93700 - 94400 94500 95200 95300 96700 96800 97500 97600 98300 98400 99000 99800 99900 99100 - 100000 100600 100700 101300 101400 101500 101600 102100 102200 102900 103000 103600 - 103700 104000 104400 104500 105200 105300 105900 106000 106700 106800 107500 107600 >; - spur-trfon-freq = <1536>; - }; - - }; /* end of __overlay__ */ - }; /* end of fragment */ -}; /* end of root */ - -&pinctrl_0 { //11CB0000 - nfc_clk: nfc-clk { - samsung,pins = "gpq0-0"; - samsung,pin-function = <2>; - samsung,pin-pud = <0>; - }; - - nfc_int: nfc-int { - samsung,pins = "gpa1-0"; - samsung,pin-function = <0>; - samsung,pin-pud = <1>; - }; -}; - -&pinctrl_2 { //13430000 - sd2_bus1: sd2-bus-width1 { - samsung,pins = "gpf4-2"; - samsung,pin-function = <2>; - samsung,pin-pud = <3>; - samsung,pin-drv = <1>; - }; - - sd2_bus4: sd2-bus-width4 { - samsung,pins = "gpf4-3", "gpf4-4", "gpf4-5"; - samsung,pin-function = <2>; - samsung,pin-pud = <3>; - samsung,pin-drv = <1>; - }; -}; - -&pinctrl_3 { //139B0000 - nfc_pvdd_en: nfc_pvdd_en { - samsung,pins = "gpp6-4"; - samsung,pin-function = <1>; - samsung,pin-pud = <0>; - samsung,pin-val = <0>; - samsung,pin-con-pdn = <3>; - samsung,pin-pud-pdn = <0>; - }; - - nfc_firm: nfc_firm { - samsung,pins = "gpp3-2"; - samsung,pin-function = <1>; - samsung,pin-pud = <0>; - samsung,pin-val = <0>; - samsung,pin-con-pdn = <3>; - samsung,pin-pud-pdn = <0>; - }; - - nfc_pd: nfc_pd { - samsung,pins = "gpp2-2"; - samsung,pin-function = <2>; - samsung,pin-pud = <0>; - samsung,pin-drv = <0>; - samsung,pin-con-pdn = <3>; - samsung,pin-pud-pdn = <0>; - }; - - nfc_clk_req: nfc_clk_req { - samsung,pins = "gpp2-3"; - samsung,pin-function = <2>; - samsung,pin-pud = <1>; - samsung,pin-drv = <0>; - samsung,pin-con-pdn = <3>; - samsung,pin-pud-pdn = <0>; - }; -}; - -&hsi2c_5 { - status = "okay"; - clock-frequency = <400000>; /* 400 kHz */ - sec-nfc@27{ - compatible = "sec-nfc"; - reg = <0x27>; - - interrupt-parent = <&gpa1>; - interrupts = <0 0 0>; - - sec-nfc,nfc_pd = <&gpp2 2 0x2>; - sec-nfc,firm-gpio = <&gpp3 2 1>; - sec-nfc,irq-gpio = <&gpa1 0 0>; - sec-nfc,nfc_clkreq = <&gpp2 3 0x2>; - sec-nfc,pvdd_en = <&gpp6 4 1>; - clkctrl-reg = <0x11C8600C>; - - pinctrl-names = "default"; - pinctrl-0 = <&nfc_pd &nfc_firm &nfc_int &nfc_clk &nfc_pvdd_en &nfc_clk_req>; - }; -}; diff --git a/arch/arm64/configs/exynos7885-a20ektt_defconfig b/arch/arm64/configs/exynos7885-a20ektt_defconfig index f05c6757..3646a35f 100755 --- a/arch/arm64/configs/exynos7885-a20ektt_defconfig +++ b/arch/arm64/configs/exynos7885-a20ektt_defconfig @@ -44,11 +44,13 @@ CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_CROSS_COMPILE="aarch64-linux-gnu-" # CONFIG_COMPILE_TEST is not set CONFIG_LOCALVERSION="" -CONFIG_LOCALVERSION_AUTO=y +# CONFIG_LOCALVERSION_AUTO is not set CONFIG_DEFAULT_HOSTNAME="(none)" CONFIG_SWAP=y CONFIG_SYSVIPC=y -# CONFIG_POSIX_MQUEUE is not set +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_POSIX_MQUEUE=y +CONFIG_POSIX_MQUEUE_SYSCTL=y CONFIG_CROSS_MEMORY_ATTACH=y CONFIG_FHANDLE=y # CONFIG_USELIB is not set @@ -58,7 +60,6 @@ CONFIG_AUDITSYSCALL=y CONFIG_AUDIT_WATCH=y CONFIG_AUDIT_TREE=y -# # IRQ subsystem # CONFIG_GENERIC_IRQ_PROBE=y @@ -118,35 +119,43 @@ CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=19 CONFIG_LOG_CPU_MAX_BUF_SHIFT=12 CONFIG_GENERIC_SCHED_CLOCK=y +CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y CONFIG_CGROUPS=y -CONFIG_CGROUP_DEBUG=y +# CONFIG_CGROUP_DEBUG is not set CONFIG_CGROUP_FREEZER=y -# CONFIG_CGROUP_PIDS is not set +CONFIG_CGROUP_PIDS=y CONFIG_CGROUP_DEVICE=y CONFIG_CPUSETS=y CONFIG_PROC_PID_CPUSET=y CONFIG_CGROUP_CPUACCT=y +# CONFIG_CGROUP_SCHEDTUNE is not set CONFIG_PAGE_COUNTER=y CONFIG_MEMCG=y CONFIG_MEMCG_SWAP=y CONFIG_MEMCG_SWAP_ENABLED=y CONFIG_MEMCG_FORCE_USE_VM_SWAPPINESS=y -# CONFIG_MEMCG_KMEM is not set -CONFIG_CGROUP_PERF=y +CONFIG_BLK_CGROUP=y +# CONFIG_DEBUG_BLK_CGROUP is not set +CONFIG_CGROUP_WRITEBACK=y CONFIG_CGROUP_SCHED=y CONFIG_FAIR_GROUP_SCHED=y CONFIG_CFS_BANDWIDTH=y CONFIG_RT_GROUP_SCHED=y -CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PERF=y +CONFIG_CGROUP_BPF=y +CONFIG_SOCK_CGROUP_DATA=y # CONFIG_CHECKPOINT_RESTORE is not set CONFIG_NAMESPACES=y CONFIG_UTS_NS=y +CONFIG_IPC_NS=y CONFIG_USER_NS=y CONFIG_PID_NS=y CONFIG_NET_NS=y # CONFIG_SCHED_AUTOGROUP is not set -# CONFIG_SCHED_TUNE is not set -# CONFIG_DEFAULT_USE_ENERGY_AWARE is not set +CONFIG_SCHED_TUNE=y +CONFIG_SCHED_EHMP=y +CONFIG_DEFAULT_USE_ENERGY_AWARE=y +CONFIG_SCHED_USE_FLUID_RT=y # CONFIG_SYSFS_DEPRECATED is not set # CONFIG_RELAY is not set CONFIG_BLK_DEV_INITRD=y @@ -157,8 +166,8 @@ CONFIG_RD_LZMA=y CONFIG_RD_XZ=y CONFIG_RD_LZO=y CONFIG_RD_LZ4=y -# CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SYSCTL=y CONFIG_ANON_INODES=y CONFIG_HAVE_UID16=y @@ -171,7 +180,9 @@ CONFIG_MULTIUSER=y CONFIG_SYSFS_SYSCALL=y # CONFIG_SYSCTL_SYSCALL is not set CONFIG_KALLSYMS=y -CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_ABSOLUTE_PERCPU is not set +CONFIG_KALLSYMS_BASE_RELATIVE=y CONFIG_PRINTK=y CONFIG_BUG=y CONFIG_ELF_CORE=y @@ -181,73 +192,16 @@ CONFIG_EPOLL=y CONFIG_SIGNALFD=y CONFIG_TIMERFD=y CONFIG_EVENTFD=y -# CONFIG_BPF_SYSCALL is not set +CONFIG_BPF_SYSCALL=y CONFIG_SHMEM=y CONFIG_AIO=y CONFIG_ADVISE_SYSCALLS=y # CONFIG_USERFAULTFD is not set +CONFIG_PCI_QUIRKS=y CONFIG_MEMBARRIER=y CONFIG_EMBEDDED=y CONFIG_HAVE_PERF_EVENTS=y -CONFIG_PERF_USE_VMALLOC=y - -# -# Kernel Performance Events And Counters -# -CONFIG_PERF_EVENTS=y -# CONFIG_DEBUG_PERF_USE_VMALLOC is not set -CONFIG_VM_EVENT_COUNTERS=y -CONFIG_SLUB_DEBUG=y -# CONFIG_COMPAT_BRK is not set -# CONFIG_SLAB is not set -CONFIG_SLUB=y -# CONFIG_SLOB is not set -CONFIG_SLUB_CPU_PARTIAL=y -# CONFIG_SYSTEM_DATA_VERIFICATION is not set -CONFIG_PROFILING=y -CONFIG_TRACEPOINTS=y -# CONFIG_JUMP_LABEL is not set -# CONFIG_UPROBES is not set -# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set -CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y -CONFIG_HAVE_ARCH_TRACEHOOK=y -CONFIG_HAVE_DMA_ATTRS=y -CONFIG_HAVE_DMA_CONTIGUOUS=y -CONFIG_GENERIC_SMP_IDLE_THREAD=y -CONFIG_GENERIC_IDLE_POLL_SETUP=y -CONFIG_HAVE_CLK=y -CONFIG_HAVE_DMA_API_DEBUG=y -CONFIG_HAVE_HW_BREAKPOINT=y -CONFIG_HAVE_PERF_REGS=y -CONFIG_HAVE_PERF_USER_STACK_DUMP=y -CONFIG_HAVE_ARCH_JUMP_LABEL=y -CONFIG_HAVE_RCU_TABLE_FREE=y -CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y -CONFIG_HAVE_CMPXCHG_LOCAL=y -CONFIG_HAVE_CMPXCHG_DOUBLE=y -CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y -CONFIG_HAVE_ARCH_SECCOMP_FILTER=y -CONFIG_SECCOMP_FILTER=y -CONFIG_HAVE_CC_STACKPROTECTOR=y -CONFIG_CC_STACKPROTECTOR=y -CONFIG_CC_STACKPROTECTOR_NONE=y -# CONFIG_CC_STACKPROTECTOR_REGULAR is not set -CONFIG_CC_STACKPROTECTOR_STRONG=y -CONFIG_HAVE_CONTEXT_TRACKING=y -CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y -CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y -CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y -CONFIG_HAVE_ARCH_HUGE_VMAP=y -CONFIG_HAVE_MOD_ARCH_SPECIFIC=y -CONFIG_MODULES_USE_ELF_RELA=y -CONFIG_ARCH_HAS_ELF_RANDOMIZE=y -CONFIG_HAVE_ARCH_MMAP_RND_BITS=y -CONFIG_ARCH_MMAP_RND_BITS=18 -CONFIG_HAVE_ARCH_MMAP_RND_COMPAT_BITS=y -CONFIG_ARCH_MMAP_RND_COMPAT_BITS=11 -CONFIG_CLONE_BACKWARDS=y -CONFIG_OLD_SIGSUSPEND3=y -CONFIG_COMPAT_OLD_SIGACTION=y + # # GCOV-based kernel profiling @@ -309,7 +263,7 @@ CONFIG_IOSCHED_CFQ=y CONFIG_DEFAULT_CFQ=y # CONFIG_DEFAULT_NOOP is not set # CONFIG_DEFAULT_IOSCHED was "cfq" -CONFIG_DEFAULT_IOSCHED="deadline" +CONFIG_DEFAULT_IOSCHED="cfq" CONFIG_ASN1=y CONFIG_UNINLINE_SPIN_UNLOCK=y CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y @@ -341,32 +295,47 @@ CONFIG_OLAF_SUPPORT=y # # Samsung Exynos # -CONFIG_ARCH_EXYNOS=y -# CONFIG_SOC_EXYNOS7872 is not set -# CONFIG_SOC_EMULATOR7872 is not set CONFIG_SOC_EXYNOS7885=y -# CONFIG_SOC_EXYNOS7885_ANDROID_VERSION_O is not set -CONFIG_SOC_EXYNOS7885_ANDROID_VERSION_P=y -# CONFIG_SOC_EXYNOS7885_ANDROID_VERSION_Q is not set -# CONFIG_SOC_EXYNOS7885_ANDROID_VERSION_Q_MR is not set -# CONFIG_SOC_EXYNOS7885_ANDROID_VERSION_P_MR is not set -# CONFIG_SOC_EXYNOS7884 is not set -# CONFIG_SOC_EXYNOS7883 is not set CONFIG_SOC_EXYNOS7884A=y CONFIG_ARCH_EXYNOS7=y -# CONFIG_SOC_EXYNOS8890 is not set -# CONFIG_SOC_EXYNOS8895 is not set -# CONFIG_SOC_EMULATOR8895 is not set -# CONFIG_ARCH_EXYNOS8 is not set +CONFIG_SOC_EXYNOS7885_ANDROID_VERSION_P=y +# CONFIG_EXYNOS_DTBTOOL +# CONFIG_EXYNOS_DTBH_PLATFORM_CODE +# CONFIG_EXYNOS_DTBH_SUBTYPE_CODE +# CONFIG_EXYNOS_DTBH_PAGE_SIZE=2048 # CONFIG_ZONE_MOVABLE is not set + # # Bus support # -# CONFIG_PCI is not set -# CONFIG_PCI_DOMAINS is not set -# CONFIG_PCI_DOMAINS_GENERIC is not set -# CONFIG_PCI_SYSCALL is not set +CONFIG_PCI=y +CONFIG_PCI_DOMAINS=y +CONFIG_PCI_DOMAINS_GENERIC=y +CONFIG_PCI_SYSCALL=y +CONFIG_PCIEPORTBUS=y +CONFIG_PCIEAER=y +# CONFIG_PCIE_ECRC is not set +# CONFIG_PCIEAER_INJECT is not set +CONFIG_PCIEASPM=y +# CONFIG_PCIEASPM_DEBUG is not set +CONFIG_PCIEASPM_DEFAULT=y +# CONFIG_PCIEASPM_POWERSAVE is not set +# CONFIG_PCIEASPM_PERFORMANCE is not set +CONFIG_PCIE_PME=y +# CONFIG_PCIE_DPC is not set +# CONFIG_PCIE_PTM is not set +CONFIG_PCI_BUS_ADDR_T_64BIT=y +CONFIG_PCI_MSI=y +CONFIG_PCI_MSI_IRQ_DOMAIN=y +# CONFIG_PCI_DEBUG is not set +# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set +# CONFIG_PCI_STUB is not set +# CONFIG_PCI_IOV is not set +# CONFIG_PCI_PRI is not set +# CONFIG_PCI_PASID is not set +# CONFIG_HOTPLUG_PCI is not set + # # Kernel Features @@ -495,30 +464,27 @@ CONFIG_SETEND_EMULATION=y # ARMv8.1 architectural features # # CONFIG_ARM64_HW_AFDBM is not set -CONFIG_ARM64_PAN=y +# CONFIG_ARM64_PAN is not set # CONFIG_ARM64_LSE_ATOMICS is not set -CONFIG_ARM64_UAO=y -CONFIG_ARM64_MODULE_CMODEL_LARGE=y -CONFIG_ARM64_MODULE_PLTS=y -CONFIG_RELOCATABLE=y -CONFIG_RANDOMIZE_BASE=y -CONFIG_RANDOMIZE_MODULE_REGION_FULL=y +# CONFIG_ARM64_VHE is not set + # # Boot options # CONFIG_CMDLINE="" # CONFIG_EFI is not set -CONFIG_TIMA=y -CONFIG_TIMA_LKMAUTH=y -CONFIG_TIMA_LKM_BLOCK=y +# CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE is not set +CONFIG_LOD_SEC=y +# CONFIG_TIMA is not set +# CONFIG_TIMA_LKMAUTH is not set +# CONFIG_TIMA_LKM_BLOCK is not set # CONFIG_TIMA_LKMAUTH_CODE_PROT is not set CONFIG_UH=y -CONFIG_UH_RKP=y -CONFIG_RKP_KDP=y -CONFIG_RKP_NS_PROT=y -CONFIG_RKP_DMAP_PROT=y -# CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE is not set +CONFIG_UH_DEBUG=y +# CONFIG_UH_RKP is not set +# CONFIG_UH_INFORM is not set + # # Userspace binary formats @@ -539,6 +505,7 @@ CONFIG_SUSPEND=y CONFIG_SUSPEND_FREEZER=y # CONFIG_SUSPEND_SKIP_SYNC is not set CONFIG_WAKELOCK=y +# CONFIG_HIBERNATION is not set CONFIG_PM_SLEEP=y CONFIG_PM_SLEEP_SMP=y CONFIG_PM_AUTOSLEEP=y @@ -558,8 +525,12 @@ CONFIG_PM_GENERIC_DOMAINS=y CONFIG_PM_GENERIC_DOMAINS_SLEEP=y CONFIG_PM_GENERIC_DOMAINS_OF=y CONFIG_CPU_PM=y +# CONFIG_FOTA_LIMIT is not set +CONFIG_ARCH_HIBERNATION_POSSIBLE=y CONFIG_ARCH_SUSPEND_POSSIBLE=y + + # # CPU Power Management # @@ -584,26 +555,27 @@ CONFIG_ARM64_EXYNOS_CPUIDLE=y # CPU Frequency scaling # CONFIG_CPU_FREQ=y -# CONFIG_CPU_FREQ_SCHEDUTIL_PERFSTAT_TRIGGER is not set +CONFIG_CPU_FREQ_GOV_ATTR_SET=y CONFIG_CPU_FREQ_STAT=y # CONFIG_CPU_FREQ_STAT_DETAILS is not set -CONFIG_CPU_FREQ_TIMES=y # CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y -# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHED is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL=y +# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set # CONFIG_CPU_FREQ_GOV_POWERSAVE is not set -CONFIG_CPU_FREQ_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_GOV_USERSPACE is not set # CONFIG_CPU_FREQ_GOV_ONDEMAND is not set -CONFIG_CPU_FREQ_GOV_INTERACTIVE=y -CONFIG_CPU_INDEX_QOS_CLUSTER0=0 -CONFIG_CPU_INDEX_QOS_CLUSTER1=6 # CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set -# CONFIG_CPU_FREQ_GOV_SCHEDUTIL is not set +# CONFIG_CPU_FREQ_GOV_SCHED is not set +# CONFIG_CPU_FREQ_GOV_INTERACTIVE is not set +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y +CONFIG_FREQVAR_TUNE=y + # # CPU frequency scaling drivers @@ -3913,32 +3885,37 @@ CONFIG_STAGING=y # Android # CONFIG_ASHMEM=y -CONFIG_ANDROID_TIMED_OUTPUT=y -CONFIG_ANDROID_TIMED_GPIO=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y CONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES=y -CONFIG_SYNC=y -CONFIG_SW_SYNC=y -CONFIG_SW_SYNC_USER=y +# CONFIG_ANDROID_VSOC is not set CONFIG_ANDROID_INTF_ALARM_DEV=y -# CONFIG_SAMSUNG_FREECESS is not set +CONFIG_SAMSUNG_FREECESS=y CONFIG_ION=y -# CONFIG_ION_TEST is not set +CONFIG_ION_TEST=y # CONFIG_ION_DUMMY is not set +# CONFIG_ION_OF is not set CONFIG_ION_EXYNOS=y -CONFIG_ION_EXYNOS_STAT_LOG=y +# CONFIG_ION_EXYNOS_STAT_LOG is not set +CONFIG_ION_RBIN_HEAP=y # CONFIG_ION_EXYNOS_OF is not set +CONFIG_ANDROID_SWITCH=y +# CONFIG_FIQ_DEBUGGER is not set +# CONFIG_FIQ_WATCHDOG is not set # CONFIG_STAGING_BOARD is not set -# CONFIG_WIMAX_GDM72XX is not set # CONFIG_LTE_GDM724X is not set -# CONFIG_LUSTRE_FS is not set -# CONFIG_DGAP is not set +# CONFIG_LNET is not set +# CONFIG_DGNC is not set # CONFIG_GS_FPGABOOT is not set # CONFIG_COMMON_CLK_XLNX_CLKWZRD is not set # CONFIG_FB_TFT is not set # CONFIG_FSL_MC_BUS is not set -# CONFIG_WILC1000_DRIVER is not set +# CONFIG_WILC1000_SDIO is not set +# CONFIG_WILC1000_SPI is not set # CONFIG_MOST is not set +# CONFIG_KS7010 is not set +# CONFIG_GREYBUS is not set +CONFIG_SEC_EXT=y + # # Samsung TN Features @@ -4022,7 +3999,7 @@ CONFIG_SEC_ABC=y # # Samsung ABC Hub Options # -# CONFIG_SEC_ABC_HUB is not set +CONFIG_SEC_ABC_HUB=y # # Samsung ABC Hub Connect Detect Options @@ -4488,17 +4465,15 @@ CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder,vndbinder" # FPGA Configuration Support # # CONFIG_FPGA is not set -CONFIG_EXYNOS_BTS=y -CONFIG_EXYNOS7885_BTS=y -# CONFIG_EXYNOS7885_BTS_VM is not set +# CONFIG_TEE is not set CONFIG_TRACE=y CONFIG_EXYNOS_ITMON=y -CONFIG_EXYNOS_ITMON_THRESHOLD_CPU=3 CONFIG_EXYNOS_SNAPSHOT=y CONFIG_EXYNOS_SNAPSHOT_CALLSTACK=4 CONFIG_EXYNOS_SNAPSHOT_IRQ_EXIT=y CONFIG_EXYNOS_SNAPSHOT_IRQ_EXIT_THRESHOLD=0 # CONFIG_EXYNOS_SNAPSHOT_IRQ_DISABLED is not set +# CONFIG_EXYNOS_SNAPSHOT_SPINLOCK is not set CONFIG_EXYNOS_SNAPSHOT_CLK=y CONFIG_EXYNOS_SNAPSHOT_PMU=y CONFIG_EXYNOS_SNAPSHOT_FREQ=y @@ -4508,21 +4483,27 @@ CONFIG_EXYNOS_SNAPSHOT_HRTIMER=y CONFIG_EXYNOS_SNAPSHOT_REGULATOR=y CONFIG_EXYNOS_SNAPSHOT_ACPM=y CONFIG_EXYNOS_SNAPSHOT_THERMAL=y -# CONFIG_EXYNOS_SNAPSHOT_I2C is not set -# CONFIG_EXYNOS_SNAPSHOT_SPI is not set -CONFIG_EXYNOS_SNAPSHOT_PSTORE=y -CONFIG_EXYNOS_SNAPSHOT_HOOK_LOGGER=y +CONFIG_EXYNOS_SNAPSHOT_UART=y +CONFIG_EXYNOS_SNAPSHOT_I2C=y +CONFIG_EXYNOS_SNAPSHOT_SPI=y +# CONFIG_EXYNOS_SNAPSHOT_LOGGING_HVC_CALL is not set CONFIG_EXYNOS_SNAPSHOT_PANIC_REBOOT=y CONFIG_EXYNOS_SNAPSHOT_WATCHDOG_RESET=y CONFIG_EXYNOS_SNAPSHOT_CRASH_KEY=y -CONFIG_EXYNOS_SNAPSHOT_SFRDUMP=y # CONFIG_EXYNOS_SNAPSHOT_MINIMIZED_MODE is not set -# CONFIG_EXYNOS_CORESIGHT is not set +CONFIG_EXYNOS_CORESIGHT=y +CONFIG_EXYNOS_CORESIGHT_PC_INFO=y +CONFIG_PC_ITERATION=5 +# CONFIG_EXYNOS_CORESIGHT_MAINTAIN_DBG_REG is not set +CONFIG_EXYNOS_CONSOLE_DEBUGGER=y +CONFIG_EXYNOS_CONSOLE_DEBUGGER_INTERFACE=y # CONFIG_EXYNOS_CORESIGHT_ETM is not set -# CONFIG_EXYNOS_CORESIGHT_STM is not set -# CONFIG_EXYNOS_CONSOLE_DEBUGGER is not set -# CONFIG_TRUSTONIC_TEE is not set -# CONFIG_VISION_SUPPORT is not set +CONFIG_VISION_SUPPORT=y +CONFIG_EXYNOS_IVA=y +CONFIG_IVA_VER=20 +CONFIG_EXYNOS_SCORE=y +CONFIG_EXYNOS_SCORE_V2=y + # # USB PD configs @@ -4647,8 +4628,6 @@ CONFIG_SEC_VIB=y # CONFIG_MOTOR_S2MU004 is not set # CONFIG_MOTOR_S2MU106 is not set # CONFIG_ISA1000 is not set -CONFIG_FIVE_TEE_DRIVER=y -CONFIG_FIVE_USE_TZDEV=y # CONFIG_TEE_DRIVER_DEBUG is not set # CONFIG_FIVE_EARLY_LOAD_TRUSTED_APP is not set CONFIG_ICD=y @@ -4680,9 +4659,7 @@ CONFIG_EXT4_FS_POSIX_ACL=y CONFIG_EXT4_FS_SECURITY=y CONFIG_EXT4_ENCRYPTION=y CONFIG_EXT4_FS_ENCRYPTION=y -CONFIG_EXT4_PRIVATE_ENCRYPTION=y # CONFIG_EXT4_DEBUG is not set -CONFIG_EXT4CRYPT_SDP=y CONFIG_JBD2=y # CONFIG_JBD2_DEBUG is not set CONFIG_FS_MBCACHE=y @@ -4693,22 +4670,19 @@ CONFIG_FS_MBCACHE=y # CONFIG_OCFS2_FS is not set # CONFIG_BTRFS_FS is not set # CONFIG_NILFS2_FS is not set -CONFIG_F2FS_FS=y -CONFIG_F2FS_STAT_FS=y -CONFIG_F2FS_FS_XATTR=y -CONFIG_F2FS_FS_POSIX_ACL=y -CONFIG_F2FS_FS_SECURITY=y -CONFIG_F2FS_STRICT_BUG_ON=y -CONFIG_F2FS_FS_ENCRYPTION=y -# CONFIG_F2FS_FAULT_INJECTION is not set +# CONFIG_F2FS_FS is not set # CONFIG_FS_DAX is not set CONFIG_FS_POSIX_ACL=y +CONFIG_EXPORTFS=y +# CONFIG_EXPORTFS_BLOCK_OPS is not set CONFIG_FILE_LOCKING=y +CONFIG_MANDATORY_FILE_LOCKING=y CONFIG_FS_ENCRYPTION=y CONFIG_FSNOTIFY=y CONFIG_DNOTIFY=y CONFIG_INOTIFY_USER=y CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y CONFIG_QUOTA=y CONFIG_QUOTA_NETLINK_INTERFACE=y CONFIG_PRINT_QUOTA_WARNING=y @@ -4940,7 +4914,6 @@ CONFIG_HAVE_ARCH_KASAN=y # CONFIG_LOCKUP_DETECTOR=y CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU=y -CONFIG_HARDLOCKUP_DETECTOR=y CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=1 CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y @@ -4949,10 +4922,11 @@ CONFIG_DETECT_HUNG_TASK=y CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120 # CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +# CONFIG_WQ_WATCHDOG is not set # CONFIG_PANIC_ON_OOPS is not set CONFIG_PANIC_ON_OOPS_VALUE=0 CONFIG_PANIC_TIMEOUT=5 -CONFIG_SCHED_DEBUG=y +# CONFIG_SCHED_DEBUG is not set CONFIG_SCHED_INFO=y # CONFIG_PANIC_ON_RT_THROTTLING is not set CONFIG_SCHEDSTATS=y @@ -4990,13 +4964,17 @@ CONFIG_DEBUG_BUGVERBOSE=y # CONFIG_PROVE_RCU is not set # CONFIG_SPARSE_RCU_POINTER is not set # CONFIG_TORTURE_TEST is not set +# CONFIG_RCU_PERF_TEST is not set # CONFIG_RCU_TORTURE_TEST is not set CONFIG_RCU_CPU_STALL_TIMEOUT=21 # CONFIG_RCU_TRACE is not set # CONFIG_RCU_EQS_DEBUG is not set +# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set # CONFIG_NOTIFIER_ERROR_INJECTION is not set # CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set CONFIG_NOP_TRACER=y CONFIG_HAVE_FUNCTION_TRACER=y CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y @@ -5011,26 +4989,8 @@ CONFIG_GPU_TRACEPOINTS=y CONFIG_CONTEXT_SWITCH_TRACER=y CONFIG_TRACING=y CONFIG_TRACING_SUPPORT=y -CONFIG_FTRACE=y -# CONFIG_FUNCTION_TRACER is not set -# CONFIG_PREEMPTIRQ_EVENTS is not set -# CONFIG_IRQSOFF_TRACER is not set -# CONFIG_PREEMPT_TRACER is not set -# CONFIG_SCHED_TRACER is not set -CONFIG_ENABLE_DEFAULT_TRACERS=y -# CONFIG_FTRACE_SYSCALLS is not set -# CONFIG_TRACER_SNAPSHOT is not set -CONFIG_BRANCH_PROFILE_NONE=y -# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set -# CONFIG_PROFILE_ALL_BRANCHES is not set -# CONFIG_STACK_TRACER is not set -# CONFIG_BLK_DEV_IO_TRACE is not set -# CONFIG_PROBE_EVENTS is not set -# CONFIG_TRACEPOINT_BENCHMARK is not set -# CONFIG_RING_BUFFER_BENCHMARK is not set -# CONFIG_RING_BUFFER_STARTUP_TEST is not set -# CONFIG_TRACE_ENUM_MAP_FILE is not set -CONFIG_TRACING_EVENTS_GPIO=y +# CONFIG_FTRACE is not set + # # Runtime Testing @@ -5041,7 +5001,7 @@ CONFIG_TRACING_EVENTS_GPIO=y # CONFIG_RBTREE_TEST is not set # CONFIG_INTERVAL_TREE_TEST is not set # CONFIG_PERCPU_TEST is not set -# CONFIG_ATOMIC64_SELFTEST is not set +CONFIG_ATOMIC64_SELFTEST=y # CONFIG_TEST_HEXDUMP is not set # CONFIG_TEST_STRING_HELPERS is not set # CONFIG_TEST_KSTRTOX is not set @@ -5068,21 +5028,18 @@ CONFIG_STRICT_DEVMEM=y # # Samsung Rooting Restriction Feature # -CONFIG_SEC_RESTRICT_ROOTING=y -CONFIG_SEC_RESTRICT_SETUID=y -CONFIG_SEC_RESTRICT_FORK=y -CONFIG_SEC_RESTRICT_ROOTING_LOG=y +# CONFIG_SEC_RESTRICT_ROOTING is not set # CONFIG_CORESIGHT is not set + # # Security options # CONFIG_KEYS=y -CONFIG_KEYS_COMPAT=y # CONFIG_PERSISTENT_KEYRINGS is not set # CONFIG_BIG_KEYS is not set CONFIG_ENCRYPTED_KEYS=y -CONFIG_KEYS_SUPPORT_STLOG=y +# CONFIG_KEY_DH_OPERATIONS is not set # CONFIG_SECURITY_DMESG_RESTRICT is not set CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y CONFIG_SECURITY=y @@ -5095,55 +5052,55 @@ CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y CONFIG_HAVE_ARCH_HARDENED_USERCOPY=y CONFIG_HARDENED_USERCOPY=y # CONFIG_HARDENED_USERCOPY_PAGESPAN is not set +# CONFIG_NO_SDP_BASE is not set CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y -# CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE was 1 CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=0 CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_SECURITY_SELINUX_DEVELOP=y CONFIG_SECURITY_SELINUX_AVC_STATS=y -# CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE was 0 CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1 -# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set CONFIG_SECURITY_SMACK=y +# CONFIG_SECURITY_SMACK_BRINGUP is not set +# CONFIG_SECURITY_SMACK_NETFILTER is not set +# CONFIG_SECURITY_SMACK_APPEND_SIGNALS is not set CONFIG_SECURITY_TOMOYO=y +CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY=2048 +CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG=1024 +# CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER is not set +CONFIG_SECURITY_TOMOYO_POLICY_LOADER="/sbin/tomoyo-init" +CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER="/sbin/init" CONFIG_SECURITY_APPARMOR=y +CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 +# CONFIG_SECURITY_APPARMOR_STATS is not set +CONFIG_SECURITY_APPARMOR_UNCONFINED_INIT=y +CONFIG_SECURITY_APPARMOR_HASH=y +CONFIG_SECURITY_APPARMOR_HASH_DEFAULT=y +# CONFIG_SECURITY_LOADPIN is not set CONFIG_SECURITY_YAMA=y -# CONFIG_MST_LDO is not set -# CONFIG_MST_SUPPORT_GPIO is not set -# CONFIG_MST_NONSECURE is not set -# CONFIG_MST_IF_PMIC is not set -# CONFIG_MFC_CHARGER is not set -# CONFIG_MST_TEEGRIS is not set -# CONFIG_MST_LPM_CONTROL is not set +CONFIG_MST_LDO=y +CONFIG_MFC_CHARGER=y CONFIG_INTEGRITY=y CONFIG_INTEGRITY_SIGNATURE=y CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y CONFIG_INTEGRITY_AUDIT=y # CONFIG_IMA is not set CONFIG_EVM=y -CONFIG_TZ_ICCC=y -CONFIG_FIVE=y -# CONFIG_FIVE_DEBUG is not set -CONFIG_FIVE_CERT_USER="x509_five_user.der" -CONFIG_FIVE_DEFAULT_HASH_SHA1=y -# CONFIG_FIVE_DEFAULT_HASH_SHA256 is not set -# CONFIG_FIVE_DEFAULT_HASH_SHA512 is not set -CONFIG_FIVE_DEFAULT_HASH="sha1" -CONFIG_SECURITY_DEFEX=y +CONFIG_EVM_ATTR_FSUUID=y +# CONFIG_EVM_EXTRA_SMACK_XATTRS is not set +# CONFIG_TZ_ICCC is not set +# CONFIG_SECURITY_DEFEX is not set # CONFIG_DEFEX_KERNEL_ONLY is not set CONFIG_SECURITY_DSMS=y -CONFIG_PROCA=y -# CONFIG_GAF_V3 is not set -# CONFIG_GAF_V4 is not set -# CONFIG_GAF_V5 is not set -CONFIG_GAF_V6=y # CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_APPARMOR=y # CONFIG_DEFAULT_SECURITY_DAC is not set -CONFIG_DEFAULT_SECURITY="selinux" -# CONFIG_SDP_ENHANCED is not set +CONFIG_DEFAULT_SECURITY="apparmor" CONFIG_CRYPTO=y + # # Crypto core or helper # @@ -5275,18 +5232,23 @@ CONFIG_CRYPTO_JITTERENTROPY=y # CONFIG_CRYPTO_USER_API_RNG is not set # CONFIG_CRYPTO_USER_API_AEAD is not set CONFIG_CRYPTO_HASH_INFO=y +# CONFIG_CRYPTO_POST_DEFERRED_INIT is not set +CONFIG_CRYPTO_POST_LATE_INIT_SYNC=y +# CONFIG_CRYPTO_POST_LATE_INIT is not set CONFIG_CRYPTO_HW=y # CONFIG_CRYPTO_DEV_S5P is not set # CONFIG_CRYPTO_DEV_CCP is not set CONFIG_EXYNOS_SMU=y CONFIG_EXYNOS_FMP=y -# CONFIG_EXYNOS_FMP_FIPS is not set +CONFIG_EXYNOS_FMP_FIPS=y +CONFIG_NODE_FOR_SELFTEST_FAIL=y +# CONFIG_PANIC_FOR_SELFTEST_FAIL is not set CONFIG_ASYMMETRIC_KEY_TYPE=y CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y -CONFIG_PUBLIC_KEY_ALGO_RSA=y CONFIG_X509_CERTIFICATE_PARSER=y # CONFIG_PKCS7_MESSAGE_PARSER is not set + # # Certificates for signature checking # @@ -5300,9 +5262,10 @@ CONFIG_CRYPTO_AES_ARM64_CE=y # CONFIG_CRYPTO_AES_ARM64_CE_CCM is not set CONFIG_CRYPTO_AES_ARM64_CE_BLK=y # CONFIG_CRYPTO_AES_ARM64_NEON_BLK is not set -# CONFIG_CRYPTO_CRC32_ARM64 is not set +CONFIG_CRYPTO_CRC32_ARM64=y CONFIG_BINARY_PRINTF=y + # # Library routines # @@ -5315,7 +5278,7 @@ CONFIG_GENERIC_NET_UTILS=y CONFIG_GENERIC_PCI_IOMAP=y CONFIG_GENERIC_IO=y CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y -# CONFIG_CRC_CCITT is not set +CONFIG_CRC_CCITT=y CONFIG_CRC16=y # CONFIG_CRC_T10DIF is not set # CONFIG_CRC_ITU_T is not set @@ -5328,6 +5291,7 @@ CONFIG_CRC32_SLICEBY8=y # CONFIG_CRC7 is not set CONFIG_LIBCRC32C=y # CONFIG_CRC8 is not set +CONFIG_XXHASH=y CONFIG_AUDIT_GENERIC=y CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y CONFIG_AUDIT_COMPAT_GENERIC=y @@ -5338,6 +5302,8 @@ CONFIG_LZO_COMPRESS=y CONFIG_LZO_DECOMPRESS=y CONFIG_LZ4_COMPRESS=y CONFIG_LZ4_DECOMPRESS=y +CONFIG_ZSTD_COMPRESS=y +CONFIG_ZSTD_DECOMPRESS=y CONFIG_XZ_DEC=y CONFIG_XZ_DEC_X86=y CONFIG_XZ_DEC_POWERPC=y @@ -5363,72 +5329,20 @@ CONFIG_TEXTSEARCH_BM=y CONFIG_TEXTSEARCH_FSM=y CONFIG_ASSOCIATIVE_ARRAY=y CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT_MAP=y CONFIG_HAS_DMA=y CONFIG_CPU_RMAP=y CONFIG_DQL=y CONFIG_NLATTR=y -CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y CONFIG_CLZ_TAB=y # CONFIG_CORDIC is not set # CONFIG_DDR is not set +# CONFIG_IRQ_POLL is not set CONFIG_MPILIB=y CONFIG_SIGNATURE=y CONFIG_LIBFDT=y CONFIG_OID_REGISTRY=y # CONFIG_SG_SPLIT is not set +CONFIG_SG_POOL=y CONFIG_ARCH_HAS_SG_CHAIN=y -# CONFIG_FPSIMD_CORRUPTION_DETECT is not set -CONFIG_CGROUP_MEM_RES_CTLR=y -CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y -CONFIG_CGROUP_MEM_RES_CTLR_KMEM=y -CONFIG_RTC_DRV_CMOS=y -CONFIG_IPC_NS=y -CONFIG_DEBUG_RODATA=y -CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y -CONFIG_VT_CONSOLE=y -CONFIG_DEFAULT_SECURITY_APPARMOR=y -CONFIG_SECURITY_APPARMOR_HASH=y -CONFIG_SECURITY_APPARMOR_UNCONFINED_INIT=y -CONFIG_SECURITY_YAMA_STACKED=y -CONFIG_BT_RFCOMM=y -CONFIG_BT_RFCOMM_TTY=y -CONFIG_BT_BNEP=y -CONFIG_BT_BNEP_MC_FILTER=y -CONFIG_BT_BNEP_PROTO_FILTER=y -CONFIG_BT_HIDP=y -CONFIG_NETFILTER_TPROXY=y -CONFIG_NETFILTER_XT_TARGET_LED=y -CONFIG_IP6_NF_QUEUE=y -CONFIG_IP6_NF_TARGET_REJECT_SKERR=y -CONFIG_SUSPEND_TIME=y -CONFIG_CONSOLE_TRANSLATIONS=y -# CONFIG_CGROUP_SCHEDTUNE is not set -# CONFIG_NETPRIO_CGROUP is not set -# CONFIG_USB_CONFIGFS_QCRNDIS is not set -# CONFIG_USB_CONFIGFS_F_GSI is not set -# CONFIG_DEFAULT_SECURITY_TOMOYO is not set -# CONFIG_DEFAULT_SECURITY_YAMA is not set -# CONFIG_DEFAULT_SECURITY_SMACK is not set -# CONFIG_SECURITY_APPARMOR_STATS is not set -# CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER is not set -# CONFIG_BT_HCIBTUSB is not set -# CONFIG_BT_HCIBTSDIO is not set -# CONFIG_BT_HCIUART is not set -# CONFIG_BT_HCIBCM203X is not set -# CONFIG_BT_HCIBPA10X is not set -# CONFIG_BT_HCIBFUSB is not set -# CONFIG_BT_HCIVHCI is not set -# CONFIG_BT_MRVL is not set -# CONFIG_KEYS_DEBUG_PROC_KEYS is not set -# CONFIG_ARM_UNWIND is not set -# CONFIG_VT_HW_CONSOLE_BINDING is not set -# CONFIG_FRAMEBUFFER_CONSOLE is not set -# CONFIG_SPEAKUP is not set -# CONFIG_CIFS_UPCALL is not set -# CONFIG_CIFS_DFS_UPCALL is not set -CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 -CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY=2048 -CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG=1024 -CONFIG_SECURITY_TOMOYO_POLICY_LOADER="/sbin/tomoyo-init" -CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER="/sbin/init" -CONFIG_EVM_HMAC_VERSION=2 +CONFIG_SBITMAP=y diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c old mode 100755 new mode 100644 index d4e96cc1..499c8362 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -1,369 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * * drivers/staging/android/ion/ion.c * * Copyright (C) 2011 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. + * Copyright (c) 2011-2019, The Linux Foundation. All rights reserved. * */ -#include +#include +#include #include +#include #include +#include #include #include #include -#include +#include #include #include #include #include -#include #include #include +#include #include -#include +#include #include +#include #include #include -#include -#include -#include -#include -#include -#include - -#include "ion.h" -#include -#include - +#include +#include +#include +#include #define CREATE_TRACE_POINTS -#include "ion_priv.h" -#include "compat_ion.h" - -/** - * struct ion_device - the metadata of the ion device node - * @dev: the actual misc device - * @buffers: an rb tree of all the existing buffers - * @buffer_lock: lock protecting the tree of buffers - * @lock: rwsem protecting the tree of heaps and clients - * @heaps: list of all the heaps in the system - * @user_clients: list of all the clients created from userspace - */ -struct ion_device { - struct miscdevice dev; - struct rb_root buffers; - struct mutex buffer_lock; - struct rw_semaphore lock; - struct plist_head heaps; - long (*custom_ioctl)(struct ion_client *client, unsigned int cmd, - unsigned long arg); - struct rb_root clients; - struct dentry *debug_root; - struct dentry *heaps_debug_root; - struct dentry *clients_debug_root; - -#ifdef CONFIG_ION_EXYNOS_STAT_LOG - /* event log */ - struct dentry *buffer_debug_file; - struct dentry *event_debug_file; - struct ion_eventlog eventlog[ION_EVENT_LOG_MAX]; - atomic_t event_idx; -#endif -}; - -/** - * struct ion_client - a process/hw block local address space - * @node: node in the tree of all clients - * @dev: backpointer to ion device - * @handles: an rb tree of all the handles in this client - * @idr: an idr space for allocating handle ids - * @lock: lock protecting the tree of handles - * @name: used for debugging - * @display_name: used for debugging (unique version of @name) - * @display_serial: used for debugging (to make display_name unique) - * @task: used for debugging - * - * A client represents a list of buffers this client may access. - * The mutex stored here is used to protect both handles tree - * as well as the handles themselves, and should be held while modifying either. - */ -struct ion_client { - struct rb_node node; - struct ion_device *dev; - struct rb_root handles; - struct idr idr; - struct mutex lock; - const char *name; - char *display_name; - int display_serial; - struct task_struct *task; - pid_t pid; - struct dentry *debug_root; -}; - -/** - * ion_handle - a client local reference to a buffer - * @ref: reference count - * @client: back pointer to the client the buffer resides in - * @buffer: pointer to the buffer - * @node: node in the client's handle rbtree - * @kmap_cnt: count of times this client has mapped to kernel - * @id: client-unique id allocated by client->idr - * - * Modifications to node, map_cnt or mapping should be protected by the - * lock in the client. Other fields are never changed after initialization. - */ -struct ion_handle { - struct kref ref; - struct ion_client *client; - struct ion_buffer *buffer; - struct rb_node node; - unsigned int kmap_cnt; - int id; -}; - -struct ion_device *g_idev; +#include +#include -static inline struct page *ion_buffer_page(struct page *page) -{ - return (struct page *)((unsigned long)page & ~(1UL)); -} - -static inline bool ion_buffer_page_is_dirty(struct page *page) -{ - return !!((unsigned long)page & 1UL); -} - -static inline void ion_buffer_page_dirty(struct page **page) -{ - *page = (struct page *)((unsigned long)(*page) | 1UL); -} +#include "ion.h" +#include "ion_secure_util.h" -static inline void ion_buffer_page_clean(struct page **page) -{ - *page = (struct page *)((unsigned long)(*page) & ~(1UL)); -} +static struct ion_device *internal_dev; -void ion_debug_heap_usage_show(struct ion_heap *heap) +int ion_walk_heaps(int heap_id, enum ion_heap_type type, void *data, + int (*f)(struct ion_heap *heap, void *data)) { - struct scatterlist *sg; - struct sg_table *table; - struct rb_node *n; - struct page *page; - struct ion_device *dev = heap->dev; - int i; - ion_phys_addr_t paddr; - - /* show the usage for only contiguous buffer */ - if ((heap->type != ION_HEAP_TYPE_CARVEOUT) - && (heap->type != ION_HEAP_TYPE_DMA)) - return; - - pr_err("[HEAP %16s (id %4d) DETAIL USAGE]\n", heap->name, heap->id); - - mutex_lock(&dev->buffer_lock); - for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { - struct ion_buffer *buffer = rb_entry(n, struct ion_buffer, - node); - if (buffer->heap->id != heap->id) + int ret_val = 0; + struct ion_heap *heap; + struct ion_device *dev = internal_dev; + /* + * traverse the list of heaps available in this system + * and find the heap that is specified. + */ + down_write(&dev->lock); + plist_for_each_entry(heap, &dev->heaps, node) { + if (ION_HEAP(heap->id) != heap_id || + type != heap->type) continue; - table = buffer->sg_table; - for_each_sg(table->sgl, sg, table->nents, i) { - page = sg_page(sg); - paddr = PFN_PHYS(page_to_pfn(page)); - pr_err("[%16lx--%16lx] %16zu\n", - paddr, paddr + sg->length, buffer->size); - } - } - mutex_unlock(&dev->buffer_lock); -} - -#ifdef CONFIG_ION_EXYNOS_STAT_LOG -static inline void ION_EVENT_ALLOC(struct ion_buffer *buffer, ktime_t begin) -{ - struct ion_device *dev = buffer->dev; - int idx = atomic_inc_return(&dev->event_idx); - struct ion_eventlog *log = &dev->eventlog[idx % ION_EVENT_LOG_MAX]; - struct ion_event_alloc *data = &log->data.alloc; - - log->type = ION_EVENT_TYPE_ALLOC; - log->begin = begin; - log->done = ktime_get(); - data->id = buffer; - data->size = buffer->size; - data->flags = buffer->flags; - strlcpy(data->heapname, buffer->heap->name, sizeof(data->heapname)); -} - -static inline void ION_EVENT_FREE(struct ion_buffer *buffer, ktime_t begin) -{ - struct ion_device *dev = buffer->dev; - int idx = atomic_inc_return(&dev->event_idx) % ION_EVENT_LOG_MAX; - struct ion_eventlog *log = &dev->eventlog[idx]; - struct ion_event_free *data = &log->data.free; - - log->type = ION_EVENT_TYPE_FREE; - log->begin = begin; - log->done = ktime_get(); - data->id = buffer; - data->size = buffer->size; - data->shrinker = (buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE); - strlcpy(data->heapname, buffer->heap->name, sizeof(data->heapname)); -} - -static inline void ION_EVENT_MMAP(struct ion_buffer *buffer, ktime_t begin) -{ - struct ion_device *dev = buffer->dev; - int idx = atomic_inc_return(&dev->event_idx) % ION_EVENT_LOG_MAX; - struct ion_eventlog *log = &dev->eventlog[idx]; - struct ion_event_mmap *data = &log->data.mmap; - - log->type = ION_EVENT_TYPE_MMAP; - log->begin = begin; - log->done = ktime_get(); - data->id = buffer; - data->size = buffer->size; - strlcpy(data->heapname, buffer->heap->name, sizeof(data->heapname)); -} - -void ION_EVENT_SHRINK(struct ion_device *dev, size_t size) -{ - int idx = atomic_inc_return(&dev->event_idx) % ION_EVENT_LOG_MAX; - struct ion_eventlog *log = &dev->eventlog[idx]; - - log->type = ION_EVENT_TYPE_SHRINK; - log->begin = ktime_get(); - log->done = ktime_set(0, 0); - log->data.shrink.size = size; -} - -void ION_EVENT_CLEAR(struct ion_buffer *buffer, ktime_t begin) -{ - struct ion_device *dev = buffer->dev; - int idx = atomic_inc_return(&dev->event_idx) % ION_EVENT_LOG_MAX; - struct ion_eventlog *log = &dev->eventlog[idx]; - struct ion_event_clear *data = &log->data.clear; - - log->type = ION_EVENT_TYPE_CLEAR; - log->begin = begin; - log->done = ktime_get(); - data->id = buffer; - data->size = buffer->size; - data->flags = buffer->flags; - strlcpy(data->heapname, buffer->heap->name, sizeof(data->heapname)); -} - -static struct ion_task *ion_buffer_task_lookup(struct ion_buffer *buffer, - struct device *master) -{ - bool found = false; - struct ion_task *task; - - list_for_each_entry(task, &buffer->master_list, list) { - if (task->master == master) { - found = true; - break; - } - } - - return found ? task : NULL; -} - -static void ion_buffer_set_task_info(struct ion_buffer *buffer) -{ - INIT_LIST_HEAD(&buffer->master_list); - get_task_comm(buffer->task_comm, current->group_leader); - get_task_comm(buffer->thread_comm, current); - buffer->pid = task_pid_nr(current->group_leader); - buffer->tid = task_pid_nr(current); -} - -static void ion_buffer_task_add(struct ion_buffer *buffer, - struct device *master) -{ - struct ion_task *task; - - task = ion_buffer_task_lookup(buffer, master); - if (!task) { - task = kzalloc(sizeof(*task), GFP_KERNEL); - if (task) { - task->master = master; - kref_init(&task->ref); - list_add_tail(&task->list, &buffer->master_list); - } - } else { - kref_get(&task->ref); - } -} - -static void ion_buffer_task_add_lock(struct ion_buffer *buffer, - struct device *master) -{ - mutex_lock(&buffer->lock); - ion_buffer_task_add(buffer, master); - mutex_unlock(&buffer->lock); -} - -static void __ion_buffer_task_remove(struct kref *kref) -{ - struct ion_task *task = container_of(kref, struct ion_task, ref); - - list_del(&task->list); - kfree(task); -} - -static void ion_buffer_task_remove(struct ion_buffer *buffer, - struct device *master) -{ - struct ion_task *task, *tmp; - - list_for_each_entry_safe(task, tmp, &buffer->master_list, list) { - if (task->master == master) { - kref_put(&task->ref, __ion_buffer_task_remove); - break; - } + ret_val = f(heap, data); + break; } + up_write(&dev->lock); + return ret_val; } +EXPORT_SYMBOL(ion_walk_heaps); -static void ion_buffer_task_remove_lock(struct ion_buffer *buffer, - struct device *master) -{ - mutex_lock(&buffer->lock); - ion_buffer_task_remove(buffer, master); - mutex_unlock(&buffer->lock); -} - -static void ion_buffer_task_remove_all(struct ion_buffer *buffer) +bool ion_buffer_cached(struct ion_buffer *buffer) { - struct ion_task *task, *tmp; - - mutex_lock(&buffer->lock); - list_for_each_entry_safe(task, tmp, &buffer->master_list, list) { - list_del(&task->list); - kfree(task); - } - mutex_unlock(&buffer->lock); + return !!(buffer->flags & ION_FLAG_CACHED); } -#else -#define ION_EVENT_ALLOC(buffer, begin) do { } while (0) -#define ION_EVENT_FREE(buffer, begin) do { } while (0) -#define ION_EVENT_MMAP(buffer, begin) do { } while (0) -#define ion_buffer_set_task_info(buffer) do { } while (0) -#define ion_buffer_task_add(buffer, master) do { } while (0) -#define ion_buffer_task_add_lock(buffer, master) do { } while (0) -#define ion_buffer_task_remove(buffer, master) do { } while (0) -#define ion_buffer_task_remove_lock(buffer, master) do { } while (0) -#define ion_buffer_task_remove_all(buffer) do { } while (0) -#endif /* this function should only be called while dev->lock is held */ static void ion_buffer_add(struct ion_device *dev, @@ -372,6 +78,7 @@ static void ion_buffer_add(struct ion_device *dev, struct rb_node **p = &dev->buffers.rb_node; struct rb_node *parent = NULL; struct ion_buffer *entry; + struct task_struct *task; while (*p) { parent = *p; @@ -386,99 +93,81 @@ static void ion_buffer_add(struct ion_device *dev, BUG(); } } + task = current; + get_task_comm(buffer->task_comm, task->group_leader); + get_task_comm(buffer->thread_comm, task); + buffer->pid = task_pid_nr(task->group_leader); + buffer->tid = task_pid_nr(task); rb_link_node(&buffer->node, parent, p); rb_insert_color(&buffer->node, &dev->buffers); - - ion_buffer_set_task_info(buffer); - ion_buffer_task_add(buffer, dev->dev.this_device); } +static void ion_debug_heap_usage_show(struct ion_heap *heap); + /* this function should only be called while dev->lock is held */ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, - struct ion_device *dev, - unsigned long len, - unsigned long align, - unsigned long flags) + struct ion_device *dev, + unsigned long len, + unsigned long flags) { struct ion_buffer *buffer; struct sg_table *table; - struct scatterlist *sg; - int i, ret; + int ret; long nr_alloc_cur, nr_alloc_peak; - buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL); + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); if (!buffer) return ERR_PTR(-ENOMEM); buffer->heap = heap; buffer->flags = flags; + buffer->dev = dev; buffer->size = len; - kref_init(&buffer->ref); - ret = heap->ops->allocate(heap, buffer, len, align, flags); + ret = heap->ops->allocate(heap, buffer, len, flags); if (ret) { if (!(heap->flags & ION_HEAP_FLAG_DEFER_FREE)) goto err2; + if (ret == -EINTR) + goto err2; + ion_heap_freelist_drain(heap, 0); - ret = heap->ops->allocate(heap, buffer, len, align, - flags); + ret = heap->ops->allocate(heap, buffer, len, flags); if (ret) goto err2; } - buffer->dev = dev; - - table = heap->ops->map_dma(heap, buffer); - if (WARN_ONCE(table == NULL, - "heap->ops->map_dma should return ERR_PTR on error")) - table = ERR_PTR(-EINVAL); - if (IS_ERR(table)) { + if (!buffer->sg_table) { + WARN_ONCE(1, "This heap needs to set the sgtable"); ret = -EINVAL; goto err1; } - buffer->sg_table = table; - if (ion_buffer_fault_user_mappings(buffer)) { - int num_pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; - struct scatterlist *sg; - int i, j, k = 0; + table = buffer->sg_table; + INIT_LIST_HEAD(&buffer->attachments); + INIT_LIST_HEAD(&buffer->vmas); + mutex_init(&buffer->lock); - buffer->pages = vmalloc(sizeof(struct page *) * num_pages); - if (!buffer->pages) { - ret = -ENOMEM; - goto err; - } + if (IS_ENABLED(CONFIG_ION_FORCE_DMA_SYNC)) { + int i; + struct scatterlist *sg; + /* + * this will set up dma addresses for the sglist -- it is not + * technically correct as per the dma api -- a specific + * device isn't really taking ownership here. However, in + * practice on our systems the only dma_address space is + * physical addresses. + */ for_each_sg(table->sgl, sg, table->nents, i) { - struct page *page = sg_page(sg); - - for (j = 0; j < sg->length / PAGE_SIZE; j++) - buffer->pages[k++] = page++; + sg_dma_address(sg) = sg_phys(sg); + sg_dma_len(sg) = sg->length; } } - buffer->dev = dev; - buffer->size = len; - INIT_LIST_HEAD(&buffer->vmas); - INIT_LIST_HEAD(&buffer->iovas); - mutex_init(&buffer->lock); - /* - * this will set up dma addresses for the sglist -- it is not - * technically correct as per the dma api -- a specific - * device isn't really taking ownership here. However, in practice on - * our systems the only dma_address space is physical addresses. - * Additionally, we can't afford the overhead of invalidating every - * allocation via dma_map_sg. The implicit contract here is that - * memory coming from the heaps is ready for dma, ie if it has a - * cached mapping that mapping has been invalidated - */ - for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) { - sg_dma_address(sg) = sg_phys(sg); - sg_dma_len(sg) = sg->length; - } mutex_lock(&dev->buffer_lock); ion_buffer_add(dev, buffer); mutex_unlock(&dev->buffer_lock); @@ -488,776 +177,257 @@ static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, atomic_long_set(&heap->total_allocated_peak, nr_alloc_cur); return buffer; -err: - heap->ops->unmap_dma(heap, buffer); err1: heap->ops->free(buffer); err2: kfree(buffer); + ion_debug_heap_usage_show(heap); return ERR_PTR(ret); } void ion_buffer_destroy(struct ion_buffer *buffer) { - struct ion_iovm_map *iovm_map; - struct ion_iovm_map *tmp; - - ION_EVENT_BEGIN(); - trace_ion_free_start((unsigned long) buffer, buffer->size, - buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE); - - if (WARN_ON(buffer->kmap_cnt > 0)) + if (buffer->kmap_cnt > 0) { + pr_warn_ratelimited("ION client likely missing a call to dma_buf_kunmap or dma_buf_vunmap\n"); buffer->heap->ops->unmap_kernel(buffer->heap, buffer); - - list_for_each_entry_safe(iovm_map, tmp, &buffer->iovas, list) { - iovmm_unmap(iovm_map->dev, iovm_map->iova); - list_del(&iovm_map->list); - kfree(iovm_map); } - - atomic_long_sub(buffer->size, &buffer->heap->total_allocated); - buffer->heap->ops->unmap_dma(buffer->heap, buffer); buffer->heap->ops->free(buffer); - vfree(buffer->pages); - - ion_buffer_task_remove_all(buffer); - ION_EVENT_FREE(buffer, ION_EVENT_DONE()); - trace_ion_free_end((unsigned long) buffer, buffer->size, - buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE); kfree(buffer); } -static void _ion_buffer_destroy(struct kref *kref) +static void _ion_buffer_destroy(struct ion_buffer *buffer) { - struct ion_buffer *buffer = container_of(kref, struct ion_buffer, ref); struct ion_heap *heap = buffer->heap; struct ion_device *dev = buffer->dev; + msm_dma_buf_freed(buffer); + mutex_lock(&dev->buffer_lock); rb_erase(&buffer->node, &dev->buffers); mutex_unlock(&dev->buffer_lock); + atomic_long_sub(buffer->size, &buffer->heap->total_allocated); if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) ion_heap_freelist_add(heap, buffer); else ion_buffer_destroy(buffer); } -static void ion_buffer_get(struct ion_buffer *buffer) +static void *ion_buffer_kmap_get(struct ion_buffer *buffer) { - kref_get(&buffer->ref); -} + void *vaddr; -static int ion_buffer_put(struct ion_buffer *buffer) -{ - return kref_put(&buffer->ref, _ion_buffer_destroy); + if (buffer->kmap_cnt) { + buffer->kmap_cnt++; + return buffer->vaddr; + } + vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer); + if (WARN_ONCE(!vaddr, + "heap->ops->map_kernel should return ERR_PTR on error")) + return ERR_PTR(-EINVAL); + if (IS_ERR(vaddr)) + return vaddr; + buffer->vaddr = vaddr; + buffer->kmap_cnt++; + return vaddr; } -static void ion_buffer_add_to_handle(struct ion_buffer *buffer) +static void ion_buffer_kmap_put(struct ion_buffer *buffer) { - mutex_lock(&buffer->lock); - if (buffer->handle_count == 0) - atomic_long_add(buffer->size, &buffer->heap->total_handles); + if (buffer->kmap_cnt == 0) { + pr_warn_ratelimited("ION client likely missing a call to dma_buf_kmap or dma_buf_vmap, pid:%d\n", + current->pid); + return; + } - buffer->handle_count++; - mutex_unlock(&buffer->lock); + buffer->kmap_cnt--; + if (!buffer->kmap_cnt) { + buffer->heap->ops->unmap_kernel(buffer->heap, buffer); + buffer->vaddr = NULL; + } } -static void ion_buffer_remove_from_handle(struct ion_buffer *buffer) +static struct sg_table *dup_sg_table(struct sg_table *table) { - /* - * when a buffer is removed from a handle, if it is not in - * any other handles, copy the taskcomm and the pid of the - * process it's being removed from into the buffer. At this - * point there will be no way to track what processes this buffer is - * being used by, it only exists as a dma_buf file descriptor. - * The taskcomm and pid can provide a debug hint as to where this fd - * is in the system - */ - mutex_lock(&buffer->lock); - buffer->handle_count--; - BUG_ON(buffer->handle_count < 0); - if (!buffer->handle_count) { - struct task_struct *task; - - task = current->group_leader; - get_task_comm(buffer->task_comm, task); - buffer->pid = task_pid_nr(task); + struct sg_table *new_table; + int ret, i; + struct scatterlist *sg, *new_sg; + + new_table = kzalloc(sizeof(*new_table), GFP_KERNEL); + if (!new_table) + return ERR_PTR(-ENOMEM); + + ret = sg_alloc_table(new_table, table->nents, GFP_KERNEL); + if (ret) { + kfree(new_table); + return ERR_PTR(-ENOMEM); } - atomic_long_sub(buffer->size, &buffer->heap->total_handles); - mutex_unlock(&buffer->lock); + + new_sg = new_table->sgl; + for_each_sg(table->sgl, sg, table->nents, i) { + memcpy(new_sg, sg, sizeof(*sg)); + sg_dma_address(new_sg) = 0; + sg_dma_len(new_sg) = 0; + new_sg = sg_next(new_sg); + } + + return new_table; } -static bool ion_handle_validate(struct ion_client *client, - struct ion_handle *handle) +static void free_duped_table(struct sg_table *table) { - WARN_ON(!mutex_is_locked(&client->lock)); - return idr_find(&client->idr, handle->id) == handle; + sg_free_table(table); + kfree(table); } -static struct ion_handle *ion_handle_create(struct ion_client *client, - struct ion_buffer *buffer) +struct ion_dma_buf_attachment { + struct device *dev; + struct sg_table *table; + struct list_head list; + bool dma_mapped; +}; + +static int ion_dma_buf_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) { - struct ion_handle *handle; + struct ion_dma_buf_attachment *a; + struct sg_table *table; + struct ion_buffer *buffer = dmabuf->priv; - handle = kzalloc(sizeof(struct ion_handle), GFP_KERNEL); - if (!handle) - return ERR_PTR(-ENOMEM); - kref_init(&handle->ref); - RB_CLEAR_NODE(&handle->node); - handle->client = client; - ion_buffer_get(buffer); - ion_buffer_add_to_handle(buffer); - handle->buffer = buffer; - - return handle; -} + a = kzalloc(sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + table = dup_sg_table(buffer->sg_table); + if (IS_ERR(table)) { + kfree(a); + return -ENOMEM; + } -static void ion_handle_kmap_put(struct ion_handle *); + a->table = table; + a->dev = attachment->dev; + a->dma_mapped = false; + INIT_LIST_HEAD(&a->list); -static void ion_handle_destroy(struct kref *kref) -{ - struct ion_handle *handle = container_of(kref, struct ion_handle, ref); - struct ion_client *client = handle->client; - struct ion_buffer *buffer = handle->buffer; + attachment->priv = a; mutex_lock(&buffer->lock); - while (handle->kmap_cnt) - ion_handle_kmap_put(handle); + list_add(&a->list, &buffer->attachments); mutex_unlock(&buffer->lock); - idr_remove(&client->idr, handle->id); - if (!RB_EMPTY_NODE(&handle->node)) - rb_erase(&handle->node, &client->handles); - - ion_buffer_remove_from_handle(buffer); - ion_buffer_put(buffer); - - kfree(handle); + return 0; } -struct ion_buffer *ion_handle_buffer(struct ion_handle *handle) +static void ion_dma_buf_detatch(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) { - return handle->buffer; -} + struct ion_dma_buf_attachment *a = attachment->priv; + struct ion_buffer *buffer = dmabuf->priv; -static void ion_handle_get(struct ion_handle *handle) -{ - kref_get(&handle->ref); -} + mutex_lock(&buffer->lock); + list_del(&a->list); + mutex_unlock(&buffer->lock); + free_duped_table(a->table); -/* Must hold the client lock */ -static struct ion_handle *ion_handle_get_check_overflow( - struct ion_handle *handle) -{ - if (atomic_read(&handle->ref.refcount) + 1 == 0) - return ERR_PTR(-EOVERFLOW); - ion_handle_get(handle); - return handle; + kfree(a); } -static int ion_handle_put_nolock(struct ion_handle *handle) +static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) { - int ret; - - ret = kref_put(&handle->ref, ion_handle_destroy); + struct ion_dma_buf_attachment *a = attachment->priv; + struct sg_table *table; + int count, map_attrs; + struct ion_buffer *buffer = attachment->dmabuf->priv; - return ret; -} + table = a->table; -int ion_handle_put(struct ion_handle *handle) -{ - struct ion_client *client = handle->client; - bool valid_handle; - int ret; + map_attrs = attachment->dma_map_attrs; + if (!(buffer->flags & ION_FLAG_CACHED) || + !hlos_accessible_buffer(buffer)) + map_attrs |= DMA_ATTR_SKIP_CPU_SYNC; - mutex_lock(&client->lock); - valid_handle = ion_handle_validate(client, handle); + mutex_lock(&buffer->lock); + if (map_attrs & DMA_ATTR_SKIP_CPU_SYNC) + trace_ion_dma_map_cmo_skip(attachment->dev, + attachment->dmabuf->name, + ion_buffer_cached(buffer), + hlos_accessible_buffer(buffer), + attachment->dma_map_attrs, + direction); + else + trace_ion_dma_map_cmo_apply(attachment->dev, + attachment->dmabuf->name, + ion_buffer_cached(buffer), + hlos_accessible_buffer(buffer), + attachment->dma_map_attrs, + direction); + + if (map_attrs & DMA_ATTR_DELAYED_UNMAP) { + count = msm_dma_map_sg_attrs(attachment->dev, table->sgl, + table->nents, direction, + attachment->dmabuf, map_attrs); + } else { + count = dma_map_sg_attrs(attachment->dev, table->sgl, + table->nents, direction, + map_attrs); + } - if (!valid_handle) { - WARN(1, "%s: invalid handle passed to free.\n", __func__); - mutex_unlock(&client->lock); - return -EINVAL; + if (count <= 0) { + mutex_unlock(&buffer->lock); + return ERR_PTR(-ENOMEM); } - ret = ion_handle_put_nolock(handle); - mutex_unlock(&client->lock); - return ret; + a->dma_mapped = true; + mutex_unlock(&buffer->lock); + return table; } -static struct ion_handle *ion_handle_lookup(struct ion_client *client, - struct ion_buffer *buffer) +static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) { - struct rb_node *n = client->handles.rb_node; + int map_attrs; + struct ion_buffer *buffer = attachment->dmabuf->priv; + struct ion_dma_buf_attachment *a = attachment->priv; - while (n) { - struct ion_handle *entry = rb_entry(n, struct ion_handle, node); + map_attrs = attachment->dma_map_attrs; + if (!(buffer->flags & ION_FLAG_CACHED) || + !hlos_accessible_buffer(buffer)) + map_attrs |= DMA_ATTR_SKIP_CPU_SYNC; - if (buffer < entry->buffer) - n = n->rb_left; - else if (buffer > entry->buffer) - n = n->rb_right; - else - return entry; - } - return ERR_PTR(-EINVAL); + mutex_lock(&buffer->lock); + if (map_attrs & DMA_ATTR_SKIP_CPU_SYNC) + trace_ion_dma_unmap_cmo_skip(attachment->dev, + attachment->dmabuf->name, + ion_buffer_cached(buffer), + hlos_accessible_buffer(buffer), + attachment->dma_map_attrs, + direction); + else + trace_ion_dma_unmap_cmo_apply(attachment->dev, + attachment->dmabuf->name, + ion_buffer_cached(buffer), + hlos_accessible_buffer(buffer), + attachment->dma_map_attrs, + direction); + + if (map_attrs & DMA_ATTR_DELAYED_UNMAP) + msm_dma_unmap_sg_attrs(attachment->dev, table->sgl, + table->nents, direction, + attachment->dmabuf, + map_attrs); + else + dma_unmap_sg_attrs(attachment->dev, table->sgl, table->nents, + direction, map_attrs); + a->dma_mapped = false; + mutex_unlock(&buffer->lock); } -static struct ion_handle *ion_handle_get_by_id_nolock(struct ion_client *client, - int id) +void ion_pages_sync_for_device(struct device *dev, struct page *page, + size_t size, enum dma_data_direction dir) { - struct ion_handle *handle; - - handle = idr_find(&client->idr, id); - if (handle) - return ion_handle_get_check_overflow(handle); - - return ERR_PTR(-EINVAL); -} - -static int ion_handle_add(struct ion_client *client, struct ion_handle *handle) -{ - int id; - struct rb_node **p = &client->handles.rb_node; - struct rb_node *parent = NULL; - struct ion_handle *entry; - - id = idr_alloc(&client->idr, handle, 1, 0, GFP_KERNEL); - if (id < 0) { - pr_err("%s: Fail to get bad id (ret %d)\n", __func__, id); - return id; - } - - handle->id = id; - - while (*p) { - parent = *p; - entry = rb_entry(parent, struct ion_handle, node); - - if (handle->buffer < entry->buffer) - p = &(*p)->rb_left; - else if (handle->buffer > entry->buffer) - p = &(*p)->rb_right; - else - WARN(1, "%s: buffer already found.", __func__); - } - - rb_link_node(&handle->node, parent, p); - rb_insert_color(&handle->node, &client->handles); - - return 0; -} - -unsigned int ion_parse_heap_id(unsigned int heap_id_mask, unsigned int flags); - -static struct ion_handle *__ion_alloc(struct ion_client *client, size_t len, - size_t align, unsigned int heap_id_mask, - unsigned int flags, bool grab_handle) -{ - struct ion_handle *handle; - struct ion_device *dev = client->dev; - struct ion_buffer *buffer = NULL; - struct ion_heap *heap; - int ret; - - ION_EVENT_BEGIN(); - trace_ion_alloc_start(client->name, 0, len, align, heap_id_mask, flags); - - pr_debug("%s: len %zu align %zu heap_id_mask %u flags %x\n", __func__, - len, align, heap_id_mask, flags); - /* - * traverse the list of heaps available in this system in priority - * order. If the heap type is supported by the client, and matches the - * request of the caller allocate from it. Repeat until allocate has - * succeeded or all heaps have been tried - */ - len = PAGE_ALIGN(len); - if (WARN_ON(!len)) { - trace_ion_alloc_fail(client->name, EINVAL, len, - align, heap_id_mask, flags); - return ERR_PTR(-EINVAL); - } - - heap_id_mask = ion_parse_heap_id(heap_id_mask, flags); - if (heap_id_mask == 0) { - trace_ion_alloc_fail(client->name, EINVAL, len, - align, heap_id_mask, flags); - return ERR_PTR(-EINVAL); - } - - down_read(&dev->lock); - plist_for_each_entry(heap, &dev->heaps, node) { - /* if the caller didn't specify this heap id */ - if (!((1 << heap->id) & heap_id_mask)) - continue; - buffer = ion_buffer_create(heap, dev, len, align, flags); - if (!IS_ERR(buffer)) - break; - } - up_read(&dev->lock); - - if (buffer == NULL) { - trace_ion_alloc_fail(client->name, ENODEV, len, - align, heap_id_mask, flags); - return ERR_PTR(-ENODEV); - } - - if (IS_ERR(buffer)) { - trace_ion_alloc_fail(client->name, PTR_ERR(buffer), - len, align, heap_id_mask, flags); - return ERR_CAST(buffer); - } - - handle = ion_handle_create(client, buffer); - - /* - * ion_buffer_create will create a buffer with a ref_cnt of 1, - * and ion_handle_create will take a second reference, drop one here - */ - ion_buffer_put(buffer); - - if (IS_ERR(handle)) { - trace_ion_alloc_fail(client->name, (unsigned long) buffer, - len, align, heap_id_mask, flags); - return handle; - } - - mutex_lock(&client->lock); - if (grab_handle) - ion_handle_get(handle); - ret = ion_handle_add(client, handle); - mutex_unlock(&client->lock); - if (ret) { - ion_handle_put(handle); - handle = ERR_PTR(ret); - trace_ion_alloc_fail(client->name, (unsigned long ) buffer, - len, align, heap_id_mask, flags); - } - - ION_EVENT_ALLOC(buffer, ION_EVENT_DONE()); - trace_ion_alloc_end(client->name, (unsigned long) buffer, - len, align, heap_id_mask, flags); - - return handle; -} - -struct ion_handle *ion_alloc(struct ion_client *client, size_t len, - size_t align, unsigned int heap_id_mask, - unsigned int flags) -{ - return __ion_alloc(client, len, align, heap_id_mask, flags, false); -} -EXPORT_SYMBOL(ion_alloc); - -static void ion_free_nolock(struct ion_client *client, struct ion_handle *handle) -{ - bool valid_handle; - - BUG_ON(client != handle->client); - - valid_handle = ion_handle_validate(client, handle); - - if (!valid_handle) { - WARN(1, "%s: invalid handle passed to free.\n", __func__); - return; - } - ion_handle_put_nolock(handle); -} - -void ion_free(struct ion_client *client, struct ion_handle *handle) -{ - BUG_ON(client != handle->client); - - mutex_lock(&client->lock); - ion_free_nolock(client, handle); - mutex_unlock(&client->lock); -} -EXPORT_SYMBOL(ion_free); - -int ion_phys(struct ion_client *client, struct ion_handle *handle, - ion_phys_addr_t *addr, size_t *len) -{ - struct ion_buffer *buffer; - int ret; - - mutex_lock(&client->lock); - if (!ion_handle_validate(client, handle)) { - mutex_unlock(&client->lock); - return -EINVAL; - } - - buffer = handle->buffer; - - if (!buffer->heap->ops->phys) { - pr_err("%s: ion_phys is not implemented by this heap (name=%s, type=%d).\n", - __func__, buffer->heap->name, buffer->heap->type); - mutex_unlock(&client->lock); - return -ENODEV; - } - mutex_unlock(&client->lock); - ret = buffer->heap->ops->phys(buffer->heap, buffer, addr, len); - return ret; -} -EXPORT_SYMBOL(ion_phys); - -static void *ion_buffer_kmap_get(struct ion_buffer *buffer) -{ - void *vaddr; - - if (buffer->kmap_cnt) { - buffer->kmap_cnt++; - return buffer->vaddr; - } - vaddr = buffer->heap->ops->map_kernel(buffer->heap, buffer); - if (WARN_ONCE(vaddr == NULL, - "heap->ops->map_kernel should return ERR_PTR on error")) - return ERR_PTR(-EINVAL); - if (IS_ERR(vaddr)) - return vaddr; - buffer->vaddr = vaddr; - buffer->kmap_cnt++; - - return vaddr; -} - -static void *ion_handle_kmap_get(struct ion_handle *handle) -{ - struct ion_buffer *buffer = handle->buffer; - void *vaddr; - - if (handle->kmap_cnt) { - handle->kmap_cnt++; - return buffer->vaddr; - } - vaddr = ion_buffer_kmap_get(buffer); - if (IS_ERR(vaddr)) - return vaddr; - handle->kmap_cnt++; - return vaddr; -} - -static void ion_buffer_kmap_put(struct ion_buffer *buffer) -{ - buffer->kmap_cnt--; - if (!buffer->kmap_cnt) { - buffer->heap->ops->unmap_kernel(buffer->heap, buffer); - buffer->vaddr = NULL; - } -} - -static void ion_handle_kmap_put(struct ion_handle *handle) -{ - struct ion_buffer *buffer = handle->buffer; - - if (!handle->kmap_cnt) { - WARN(1, "%s: Double unmap detected! bailing...\n", __func__); - return; - } - handle->kmap_cnt--; - if (!handle->kmap_cnt) - ion_buffer_kmap_put(buffer); -} - -void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle) -{ - struct ion_buffer *buffer; - void *vaddr; - - mutex_lock(&client->lock); - if (!ion_handle_validate(client, handle)) { - pr_err("%s: invalid handle passed to map_kernel.\n", - __func__); - mutex_unlock(&client->lock); - return ERR_PTR(-EINVAL); - } - - buffer = handle->buffer; - - if (!handle->buffer->heap->ops->map_kernel) { - pr_err("%s: map_kernel is not implemented by this heap.\n", - __func__); - mutex_unlock(&client->lock); - return ERR_PTR(-ENODEV); - } - - mutex_lock(&buffer->lock); - vaddr = ion_handle_kmap_get(handle); - mutex_unlock(&buffer->lock); - mutex_unlock(&client->lock); - return vaddr; -} -EXPORT_SYMBOL(ion_map_kernel); - -void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle) -{ - struct ion_buffer *buffer; - - mutex_lock(&client->lock); - buffer = handle->buffer; - mutex_lock(&buffer->lock); - ion_handle_kmap_put(handle); - mutex_unlock(&buffer->lock); - mutex_unlock(&client->lock); -} -EXPORT_SYMBOL(ion_unmap_kernel); - -static int ion_debug_client_show(struct seq_file *s, void *unused) -{ - struct ion_client *client = s->private; - struct rb_node *n; - size_t sizes[ION_NUM_HEAP_IDS] = {0}; - size_t sizes_pss[ION_NUM_HEAP_IDS] = {0}; - const char *names[ION_NUM_HEAP_IDS] = {NULL}; - int i; - - down_read(&g_idev->lock); - - /* check validity of the client */ - for (n = rb_first(&g_idev->clients); n; n = rb_next(n)) { - struct ion_client *c = rb_entry(n, struct ion_client, node); - if (client == c) - break; - } - - if (IS_ERR_OR_NULL(n)) { - pr_err("%s: invalid client %p\n", __func__, client); - up_read(&g_idev->lock); - return -EINVAL; - } - - seq_printf(s, "%16.s %4.s %16.s %4.s %10.s %8.s %9.s\n", - "task", "pid", "thread", "tid", "size", "# procs", "flag"); - seq_printf(s, "----------------------------------------------" - "--------------------------------------------\n"); - - mutex_lock(&client->lock); - for (n = rb_first(&client->handles); n; n = rb_next(n)) { - struct ion_handle *handle = rb_entry(n, struct ion_handle, - node); - struct ion_buffer *buffer = handle->buffer; - unsigned int id = buffer->heap->id; - - if (!names[id]) - names[id] = buffer->heap->name; - sizes[id] += buffer->size; - sizes_pss[id] += (buffer->size / buffer->handle_count); - seq_printf(s, "%16.s %4u %16.s %4u %10zu %8d %9lx\n", - buffer->task_comm, buffer->pid, - buffer->thread_comm, buffer->tid, buffer->size, - buffer->handle_count, buffer->flags); - } - mutex_unlock(&client->lock); - up_read(&g_idev->lock); - - seq_printf(s, "----------------------------------------------" - "--------------------------------------------\n"); - seq_printf(s, "%16.16s: %16.16s %18.18s\n", "heap_name", - "size_in_bytes", "size_in_bytes(pss)"); - for (i = 0; i < ION_NUM_HEAP_IDS; i++) { - if (!names[i]) - continue; - seq_printf(s, "%16.16s: %16zu %18zu\n", - names[i], sizes[i], sizes_pss[i]); - } - return 0; -} - -static int ion_debug_client_open(struct inode *inode, struct file *file) -{ - return single_open(file, ion_debug_client_show, inode->i_private); -} - -static const struct file_operations debug_client_fops = { - .open = ion_debug_client_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int ion_get_client_serial(const struct rb_root *root, - const unsigned char *name) -{ - int serial = -1; - struct rb_node *node; - - for (node = rb_first(root); node; node = rb_next(node)) { - struct ion_client *client = rb_entry(node, struct ion_client, - node); - - if (strcmp(client->name, name)) - continue; - serial = max(serial, client->display_serial); - } - return serial + 1; -} - -struct ion_client *ion_client_create(struct ion_device *dev, - const char *name) -{ - struct ion_client *client; - struct task_struct *task; - struct rb_node **p; - struct rb_node *parent = NULL; - struct ion_client *entry; - pid_t pid; - - if (!name) { - pr_err("%s: Name cannot be null\n", __func__); - return ERR_PTR(-EINVAL); - } - - get_task_struct(current->group_leader); - task_lock(current->group_leader); - pid = task_pid_nr(current->group_leader); - /* - * don't bother to store task struct for kernel threads, - * they can't be killed anyway - */ - if (current->group_leader->flags & PF_KTHREAD) { - put_task_struct(current->group_leader); - task = NULL; - } else { - task = current->group_leader; - } - task_unlock(current->group_leader); - - client = kzalloc(sizeof(struct ion_client), GFP_KERNEL); - if (!client) - goto err_put_task_struct; - - client->dev = dev; - client->handles = RB_ROOT; - idr_init(&client->idr); - mutex_init(&client->lock); - client->task = task; - client->pid = pid; - client->name = kstrdup(name, GFP_KERNEL); - if (!client->name) - goto err_free_client; - - down_write(&dev->lock); - client->display_serial = ion_get_client_serial(&dev->clients, name); - client->display_name = kasprintf( - GFP_KERNEL, "%s-%d", name, client->display_serial); - if (!client->display_name) { - up_write(&dev->lock); - goto err_free_client_name; - } - p = &dev->clients.rb_node; - while (*p) { - parent = *p; - entry = rb_entry(parent, struct ion_client, node); - - if (client < entry) - p = &(*p)->rb_left; - else if (client > entry) - p = &(*p)->rb_right; - } - rb_link_node(&client->node, parent, p); - rb_insert_color(&client->node, &dev->clients); - - client->debug_root = debugfs_create_file(client->display_name, 0664, - dev->clients_debug_root, - client, &debug_client_fops); - if (!client->debug_root) { - char buf[256], *path; - - path = dentry_path(dev->clients_debug_root, buf, 256); - pr_err("Failed to create client debugfs at %s/%s\n", - path, client->display_name); - } - - up_write(&dev->lock); - - return client; - -err_free_client_name: - kfree(client->name); -err_free_client: - kfree(client); -err_put_task_struct: - if (task) - put_task_struct(current->group_leader); - return ERR_PTR(-ENOMEM); -} -EXPORT_SYMBOL(ion_client_create); - -void ion_client_destroy(struct ion_client *client) -{ - struct ion_device *dev = client->dev; - struct rb_node *n; - - mutex_lock(&client->lock); - while ((n = rb_first(&client->handles))) { - struct ion_handle *handle = rb_entry(n, struct ion_handle, - node); - ion_handle_destroy(&handle->ref); - } - - mutex_unlock(&client->lock); - idr_destroy(&client->idr); - - down_write(&dev->lock); - if (client->task) - put_task_struct(client->task); - rb_erase(&client->node, &dev->clients); - debugfs_remove_recursive(client->debug_root); - up_write(&dev->lock); - - kfree(client->display_name); - kfree(client->name); - kfree(client); -} -EXPORT_SYMBOL(ion_client_destroy); - -struct sg_table *ion_sg_table(struct ion_client *client, - struct ion_handle *handle) -{ - struct ion_buffer *buffer; - struct sg_table *table; - - mutex_lock(&client->lock); - if (!ion_handle_validate(client, handle)) { - pr_err("%s: invalid handle passed to map_dma.\n", - __func__); - mutex_unlock(&client->lock); - return ERR_PTR(-EINVAL); - } - buffer = handle->buffer; - table = buffer->sg_table; - mutex_unlock(&client->lock); - return table; -} -EXPORT_SYMBOL(ion_sg_table); - -static void ion_buffer_sync_for_device(struct ion_buffer *buffer, - struct device *dev, - enum dma_data_direction direction); - -static struct sg_table *ion_map_dma_buf(struct dma_buf_attachment *attachment, - enum dma_data_direction direction) -{ - struct dma_buf *dmabuf = attachment->dmabuf; - struct ion_buffer *buffer = dmabuf->priv; - - ion_buffer_sync_for_device(buffer, attachment->dev, direction); - - ion_buffer_task_add_lock(buffer, attachment->dev); - - return buffer->sg_table; -} - -static void ion_unmap_dma_buf(struct dma_buf_attachment *attachment, - struct sg_table *table, - enum dma_data_direction direction) -{ - ion_buffer_task_remove_lock(attachment->dmabuf->priv, attachment->dev); -} - -void ion_pages_sync_for_device(struct device *dev, struct page *page, - size_t size, enum dma_data_direction dir) -{ - struct scatterlist sg; + struct scatterlist sg; sg_init_table(&sg, 1); sg_set_page(&sg, page, size, 0); @@ -1270,69 +440,12 @@ void ion_pages_sync_for_device(struct device *dev, struct page *page, dma_sync_sg_for_device(dev, &sg, 1, dir); } -struct ion_vma_list { - struct list_head list; - struct vm_area_struct *vma; -}; - -static void ion_buffer_sync_for_device(struct ion_buffer *buffer, - struct device *dev, - enum dma_data_direction dir) -{ - struct ion_vma_list *vma_list; - int pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE; - int i; - - if (!ion_buffer_cached(buffer)) - return; - - if (!ion_buffer_fault_user_mappings(buffer)) - return; - - mutex_lock(&buffer->lock); - for (i = 0; i < pages; i++) { - struct page *page = buffer->pages[i]; - - if (ion_buffer_page_is_dirty(page)) - ion_pages_sync_for_device(dev, ion_buffer_page(page), - PAGE_SIZE, dir); - - ion_buffer_page_clean(buffer->pages + i); - } - list_for_each_entry(vma_list, &buffer->vmas, list) { - struct vm_area_struct *vma = vma_list->vma; - - zap_page_range(vma, vma->vm_start, vma->vm_end - vma->vm_start, - NULL); - } - mutex_unlock(&buffer->lock); -} - -static int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct ion_buffer *buffer = vma->vm_private_data; - unsigned long pfn; - int ret; - - mutex_lock(&buffer->lock); - ion_buffer_page_dirty(buffer->pages + vmf->pgoff); - BUG_ON(!buffer->pages || !buffer->pages[vmf->pgoff]); - - pfn = page_to_pfn(ion_buffer_page(buffer->pages[vmf->pgoff])); - ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); - mutex_unlock(&buffer->lock); - if (ret) - return VM_FAULT_ERROR; - - return VM_FAULT_NOPAGE; -} - static void ion_vm_open(struct vm_area_struct *vma) { struct ion_buffer *buffer = vma->vm_private_data; struct ion_vma_list *vma_list; - vma_list = kmalloc(sizeof(struct ion_vma_list), GFP_KERNEL); + vma_list = kmalloc(sizeof(*vma_list), GFP_KERNEL); if (!vma_list) return; vma_list->vma = vma; @@ -1360,7 +473,6 @@ static void ion_vm_close(struct vm_area_struct *vma) static const struct vm_operations_struct ion_vma_ops = { .open = ion_vm_open, .close = ion_vm_close, - .fault = ion_vm_fault, }; static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) @@ -1368,51 +480,19 @@ static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) struct ion_buffer *buffer = dmabuf->priv; int ret = 0; - ION_EVENT_BEGIN(); - - if (buffer->flags & ION_FLAG_NOZEROED) { - pr_err("%s: mmap non-zeroed buffer to user is prohibited!\n", - __func__); - return -EINVAL; - } - - if (buffer->flags & ION_FLAG_PROTECTED) { - pr_err("%s: mmap protected buffer to user is prohibited!\n", - __func__); - return -EPERM; - } - - if ((((vma->vm_pgoff << PAGE_SHIFT) >= buffer->size)) || - ((vma->vm_end - vma->vm_start) > - (buffer->size - (vma->vm_pgoff << PAGE_SHIFT)))) { - pr_err("%s: trying to map outside of buffer.\n", __func__); - return -EINVAL; - } - if (!buffer->heap->ops->map_user) { pr_err("%s: this heap does not define a method for mapping to userspace\n", - __func__); + __func__); return -EINVAL; } - trace_ion_mmap_start((unsigned long) buffer, buffer->size, - !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC)); - - if (ion_buffer_fault_user_mappings(buffer)) { - vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | - VM_DONTDUMP; - vma->vm_private_data = buffer; - vma->vm_ops = &ion_vma_ops; - ion_vm_open(vma); - ION_EVENT_MMAP(buffer, ION_EVENT_DONE()); - trace_ion_mmap_end((unsigned long) buffer, buffer->size, - !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC)); - return 0; - } - if (!(buffer->flags & ION_FLAG_CACHED)) vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_private_data = buffer; + vma->vm_ops = &ion_vma_ops; + ion_vm_open(vma); + mutex_lock(&buffer->lock); /* now map it to userspace */ ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma); @@ -1422,10 +502,6 @@ static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) pr_err("%s: failure mapping buffer to userspace\n", __func__); - ION_EVENT_MMAP(buffer, ION_EVENT_DONE()); - trace_ion_mmap_end((unsigned long) buffer, buffer->size, - !(buffer->flags & ION_FLAG_CACHED_NEEDS_SYNC)); - return ret; } @@ -1433,625 +509,806 @@ static void ion_dma_buf_release(struct dma_buf *dmabuf) { struct ion_buffer *buffer = dmabuf->priv; - ion_buffer_put(buffer); + _ion_buffer_destroy(buffer); + kfree(dmabuf->exp_name); } static void *ion_dma_buf_vmap(struct dma_buf *dmabuf) { struct ion_buffer *buffer = dmabuf->priv; - void *vaddr; + void *vaddr = ERR_PTR(-EINVAL); - if (!buffer->heap->ops->map_kernel) { - pr_err("%s: map kernel is not implemented by this heap.\n", - __func__); - return ERR_PTR(-ENODEV); + if (buffer->heap->ops->map_kernel) { + mutex_lock(&buffer->lock); + vaddr = ion_buffer_kmap_get(buffer); + mutex_unlock(&buffer->lock); + } else { + pr_warn_ratelimited("heap %s doesn't support map_kernel\n", + buffer->heap->name); } - mutex_lock(&buffer->lock); - vaddr = ion_buffer_kmap_get(buffer); - mutex_unlock(&buffer->lock); - return vaddr; } -static void ion_dma_buf_vunmap(struct dma_buf *dmabuf, void *ptr) +static void ion_dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr) { struct ion_buffer *buffer = dmabuf->priv; - mutex_lock(&buffer->lock); - ion_buffer_kmap_put(buffer); - mutex_unlock(&buffer->lock); + if (buffer->heap->ops->map_kernel) { + mutex_lock(&buffer->lock); + ion_buffer_kmap_put(buffer); + mutex_unlock(&buffer->lock); + } +} + +static void *ion_dma_buf_kmap(struct dma_buf *dmabuf, unsigned long offset) +{ + /* + * TODO: Once clients remove their hacks where they assume kmap(ed) + * addresses are virtually contiguous implement this properly + */ + void *vaddr = ion_dma_buf_vmap(dmabuf); + + if (IS_ERR(vaddr)) + return vaddr; + + return vaddr + offset * PAGE_SIZE; +} + +static void ion_dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long offset, + void *ptr) +{ + /* + * TODO: Once clients remove their hacks where they assume kmap(ed) + * addresses are virtually contiguous implement this properly + */ + ion_dma_buf_vunmap(dmabuf, ptr); +} + +static int ion_sgl_sync_range(struct device *dev, struct scatterlist *sgl, + unsigned int nents, unsigned long offset, + unsigned long length, + enum dma_data_direction dir, bool for_cpu) +{ + int i; + struct scatterlist *sg; + unsigned int len = 0; + dma_addr_t sg_dma_addr; + + for_each_sg(sgl, sg, nents, i) { + if (sg_dma_len(sg) == 0) + break; + + if (i > 0) { + pr_warn_ratelimited("Partial cmo only supported with 1 segment\n" + "is dma_set_max_seg_size being set on dev:%s\n", + dev_name(dev)); + return -EINVAL; + } + } + + for_each_sg(sgl, sg, nents, i) { + unsigned int sg_offset, sg_left, size = 0; + + if (i == 0) + sg_dma_addr = sg_dma_address(sg); + + len += sg->length; + if (len <= offset) { + sg_dma_addr += sg->length; + continue; + } + + sg_left = len - offset; + sg_offset = sg->length - sg_left; + + size = (length < sg_left) ? length : sg_left; + if (for_cpu) + dma_sync_single_range_for_cpu(dev, sg_dma_addr, + sg_offset, size, dir); + else + dma_sync_single_range_for_device(dev, sg_dma_addr, + sg_offset, size, dir); + + offset += size; + length -= size; + sg_dma_addr += sg->length; + + if (length == 0) + break; + } + + return 0; } -static void *ion_dma_buf_kmap(struct dma_buf *dmabuf, unsigned long offset) +static int ion_sgl_sync_mapped(struct device *dev, struct scatterlist *sgl, + unsigned int nents, struct list_head *vmas, + enum dma_data_direction dir, bool for_cpu) { - struct ion_buffer *buffer = dmabuf->priv; + struct ion_vma_list *vma_list; + int ret = 0; - return buffer->vaddr + offset * PAGE_SIZE; -} + list_for_each_entry(vma_list, vmas, list) { + struct vm_area_struct *vma = vma_list->vma; -static void ion_dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long offset, - void *ptr) -{ + ret = ion_sgl_sync_range(dev, sgl, nents, + vma->vm_pgoff * PAGE_SIZE, + vma->vm_end - vma->vm_start, dir, + for_cpu); + if (ret) + break; + } + + return ret; } -static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, size_t start, - size_t len, - enum dma_data_direction direction) +static int __ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction direction, + bool sync_only_mapped) { struct ion_buffer *buffer = dmabuf->priv; - void *vaddr; + struct ion_dma_buf_attachment *a; + int ret = 0; - if (!buffer->heap->ops->map_kernel) { - pr_err("%s: map kernel is not implemented by this heap.\n", - __func__); - return -ENODEV; + if (!hlos_accessible_buffer(buffer)) { + trace_ion_begin_cpu_access_cmo_skip(NULL, dmabuf->name, + ion_buffer_cached(buffer), + false, direction, + sync_only_mapped); + ret = -EPERM; + goto out; + } + + if (!(buffer->flags & ION_FLAG_CACHED)) { + trace_ion_begin_cpu_access_cmo_skip(NULL, dmabuf->name, false, + true, direction, + sync_only_mapped); + goto out; } mutex_lock(&buffer->lock); - vaddr = ion_buffer_kmap_get(buffer); - mutex_unlock(&buffer->lock); - return PTR_ERR_OR_ZERO(vaddr); -} -static void ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, size_t start, - size_t len, - enum dma_data_direction direction) -{ - struct ion_buffer *buffer = dmabuf->priv; + if (IS_ENABLED(CONFIG_ION_FORCE_DMA_SYNC)) { + struct device *dev = buffer->heap->priv; + struct sg_table *table = buffer->sg_table; - mutex_lock(&buffer->lock); - ion_buffer_kmap_put(buffer); - mutex_unlock(&buffer->lock); -} + if (sync_only_mapped) + ret = ion_sgl_sync_mapped(dev, table->sgl, + table->nents, &buffer->vmas, + direction, true); + else + dma_sync_sg_for_cpu(dev, table->sgl, + table->nents, direction); + + if (!ret) + trace_ion_begin_cpu_access_cmo_apply(dev, dmabuf->name, + true, true, + direction, + sync_only_mapped); + else + trace_ion_begin_cpu_access_cmo_skip(dev, dmabuf->name, + true, true, + direction, + sync_only_mapped); + mutex_unlock(&buffer->lock); + goto out; + } -static void ion_dma_buf_set_privflag(struct dma_buf *dmabuf) -{ - struct ion_buffer *buffer = dmabuf->priv; + list_for_each_entry(a, &buffer->attachments, list) { + int tmp = 0; - mutex_lock(&buffer->lock); - buffer->private_flags |= ION_PRIV_FLAG_NEED_TO_FLUSH; + if (!a->dma_mapped) { + trace_ion_begin_cpu_access_notmapped(a->dev, + dmabuf->name, + true, true, + direction, + sync_only_mapped); + continue; + } + + if (sync_only_mapped) + tmp = ion_sgl_sync_mapped(a->dev, a->table->sgl, + a->table->nents, + &buffer->vmas, + direction, true); + else + dma_sync_sg_for_cpu(a->dev, a->table->sgl, + a->table->nents, direction); + + if (!tmp) { + trace_ion_begin_cpu_access_cmo_apply(a->dev, + dmabuf->name, + true, true, + direction, + sync_only_mapped); + } else { + trace_ion_begin_cpu_access_cmo_skip(a->dev, + dmabuf->name, true, + true, direction, + sync_only_mapped); + ret = tmp; + } + + } mutex_unlock(&buffer->lock); +out: + return ret; } -static bool ion_dma_buf_get_privflag(struct dma_buf *dmabuf, bool clear) +static int __ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction direction, + bool sync_only_mapped) { struct ion_buffer *buffer = dmabuf->priv; - bool ret; - - mutex_lock(&buffer->lock); - ret = !!(buffer->private_flags & ION_PRIV_FLAG_NEED_TO_FLUSH); - if (clear) - buffer->private_flags &= ~ION_PRIV_FLAG_NEED_TO_FLUSH; - mutex_unlock(&buffer->lock); + struct ion_dma_buf_attachment *a; + int ret = 0; - return ret; -} + if (!hlos_accessible_buffer(buffer)) { + trace_ion_end_cpu_access_cmo_skip(NULL, dmabuf->name, + ion_buffer_cached(buffer), + false, direction, + sync_only_mapped); + ret = -EPERM; + goto out; + } -static struct dma_buf_ops dma_buf_ops = { - .map_dma_buf = ion_map_dma_buf, - .unmap_dma_buf = ion_unmap_dma_buf, - .mmap = ion_mmap, - .release = ion_dma_buf_release, - .begin_cpu_access = ion_dma_buf_begin_cpu_access, - .end_cpu_access = ion_dma_buf_end_cpu_access, - .kmap_atomic = ion_dma_buf_kmap, - .kunmap_atomic = ion_dma_buf_kunmap, - .kmap = ion_dma_buf_kmap, - .kunmap = ion_dma_buf_kunmap, - .vmap = ion_dma_buf_vmap, - .vunmap = ion_dma_buf_vunmap, - .set_privflag = ion_dma_buf_set_privflag, - .get_privflag = ion_dma_buf_get_privflag, -}; + if (!(buffer->flags & ION_FLAG_CACHED)) { + trace_ion_end_cpu_access_cmo_skip(NULL, dmabuf->name, false, + true, direction, + sync_only_mapped); + goto out; + } -static struct dma_buf *__ion_share_dma_buf(struct ion_client *client, - struct ion_handle *handle, - bool lock_client) -{ - DEFINE_DMA_BUF_EXPORT_INFO(exp_info); - struct ion_buffer *buffer; - struct dma_buf *dmabuf; - bool valid_handle; - - if (lock_client) - mutex_lock(&client->lock); - valid_handle = ion_handle_validate(client, handle); - if (!valid_handle) { - WARN(1, "%s: invalid handle passed to share.\n", __func__); - if (lock_client) - mutex_unlock(&client->lock); - return ERR_PTR(-EINVAL); + mutex_lock(&buffer->lock); + if (IS_ENABLED(CONFIG_ION_FORCE_DMA_SYNC)) { + struct device *dev = buffer->heap->priv; + struct sg_table *table = buffer->sg_table; + + if (sync_only_mapped) + ret = ion_sgl_sync_mapped(dev, table->sgl, + table->nents, &buffer->vmas, + direction, false); + else + dma_sync_sg_for_device(dev, table->sgl, + table->nents, direction); + + if (!ret) + trace_ion_end_cpu_access_cmo_apply(dev, dmabuf->name, + true, true, + direction, + sync_only_mapped); + else + trace_ion_end_cpu_access_cmo_skip(dev, dmabuf->name, + true, true, direction, + sync_only_mapped); + mutex_unlock(&buffer->lock); + goto out; } - buffer = handle->buffer; - ion_buffer_get(buffer); - if (lock_client) - mutex_unlock(&client->lock); - exp_info.ops = &dma_buf_ops; - exp_info.size = buffer->size; - exp_info.flags = O_RDWR; - exp_info.priv = buffer; + list_for_each_entry(a, &buffer->attachments, list) { + int tmp = 0; - dmabuf = dma_buf_export(&exp_info); - if (IS_ERR(dmabuf)) { - ion_buffer_put(buffer); - return dmabuf; + if (!a->dma_mapped) { + trace_ion_end_cpu_access_notmapped(a->dev, + dmabuf->name, + true, true, + direction, + sync_only_mapped); + continue; + } + + if (sync_only_mapped) + tmp = ion_sgl_sync_mapped(a->dev, a->table->sgl, + a->table->nents, + &buffer->vmas, direction, + false); + else + dma_sync_sg_for_device(a->dev, a->table->sgl, + a->table->nents, direction); + + if (!tmp) { + trace_ion_end_cpu_access_cmo_apply(a->dev, dmabuf->name, + true, true, + direction, + sync_only_mapped); + } else { + trace_ion_end_cpu_access_cmo_skip(a->dev, dmabuf->name, + true, true, direction, + sync_only_mapped); + ret = tmp; + } } + mutex_unlock(&buffer->lock); - return dmabuf; +out: + return ret; } -struct dma_buf *ion_share_dma_buf(struct ion_client *client, - struct ion_handle *handle) +static int ion_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction direction) { - return __ion_share_dma_buf(client, handle, true); + return __ion_dma_buf_begin_cpu_access(dmabuf, direction, false); } -EXPORT_SYMBOL(ion_share_dma_buf); -static int __ion_share_dma_buf_fd(struct ion_client *client, - struct ion_handle *handle, bool lock_client) +static int ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction direction) { - struct dma_buf *dmabuf; - int fd; - - dmabuf = __ion_share_dma_buf(client, handle, lock_client); - if (IS_ERR(dmabuf)) - return PTR_ERR(dmabuf); - - fd = dma_buf_fd(dmabuf, O_CLOEXEC); - if (fd < 0) - dma_buf_put(dmabuf); - - return fd; + return __ion_dma_buf_end_cpu_access(dmabuf, direction, false); } -int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle) +static int ion_dma_buf_begin_cpu_access_umapped(struct dma_buf *dmabuf, + enum dma_data_direction dir) { - return __ion_share_dma_buf_fd(client, handle, true); + return __ion_dma_buf_begin_cpu_access(dmabuf, dir, true); } -EXPORT_SYMBOL(ion_share_dma_buf_fd); -static int ion_share_dma_buf_fd_nolock(struct ion_client *client, - struct ion_handle *handle) +static int ion_dma_buf_end_cpu_access_umapped(struct dma_buf *dmabuf, + enum dma_data_direction dir) { - return __ion_share_dma_buf_fd(client, handle, false); + return __ion_dma_buf_end_cpu_access(dmabuf, dir, true); } -struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd) +static int ion_dma_buf_begin_cpu_access_partial(struct dma_buf *dmabuf, + enum dma_data_direction dir, + unsigned int offset, + unsigned int len) { - struct dma_buf *dmabuf; - struct ion_buffer *buffer; - struct ion_handle *handle; - int ret; - - dmabuf = dma_buf_get(fd); - if (IS_ERR(dmabuf)) - return ERR_CAST(dmabuf); - /* if this memory came from ion */ + struct ion_buffer *buffer = dmabuf->priv; + struct ion_dma_buf_attachment *a; + int ret = 0; - if (dmabuf->ops != &dma_buf_ops) { - pr_err("%s: can not import dmabuf from another exporter\n", - __func__); - dma_buf_put(dmabuf); - return ERR_PTR(-EINVAL); - } - buffer = dmabuf->priv; - - mutex_lock(&client->lock); - /* if a handle exists for this buffer just take a reference to it */ - handle = ion_handle_lookup(client, buffer); - if (!IS_ERR(handle)) { - handle = ion_handle_get_check_overflow(handle); - mutex_unlock(&client->lock); - goto end; + if (!hlos_accessible_buffer(buffer)) { + trace_ion_begin_cpu_access_cmo_skip(NULL, dmabuf->name, + ion_buffer_cached(buffer), + false, dir, + false); + ret = -EPERM; + goto out; } - handle = ion_handle_create(client, buffer); - if (IS_ERR(handle)) { - mutex_unlock(&client->lock); - goto end; + if (!(buffer->flags & ION_FLAG_CACHED)) { + trace_ion_begin_cpu_access_cmo_skip(NULL, dmabuf->name, false, + true, dir, + false); + goto out; } - ret = ion_handle_add(client, handle); - mutex_unlock(&client->lock); - if (ret) { - ion_handle_put(handle); - handle = ERR_PTR(ret); - } + mutex_lock(&buffer->lock); + if (IS_ENABLED(CONFIG_ION_FORCE_DMA_SYNC)) { + struct device *dev = buffer->heap->priv; + struct sg_table *table = buffer->sg_table; -end: - dma_buf_put(dmabuf); - return handle; -} -EXPORT_SYMBOL(ion_import_dma_buf); + ret = ion_sgl_sync_range(dev, table->sgl, table->nents, + offset, len, dir, true); -int ion_cached_needsync_dmabuf(struct dma_buf *dmabuf) -{ - struct ion_buffer *buffer = dmabuf->priv; - unsigned long cacheflag = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC; + if (!ret) + trace_ion_begin_cpu_access_cmo_apply(dev, dmabuf->name, + true, true, dir, + false); + else + trace_ion_begin_cpu_access_cmo_skip(dev, dmabuf->name, + true, true, dir, + false); + mutex_unlock(&buffer->lock); + goto out; + } - if (dmabuf->ops != &dma_buf_ops) - return -EINVAL; + list_for_each_entry(a, &buffer->attachments, list) { + int tmp = 0; - return ((buffer->flags & cacheflag) == cacheflag) ? 1 : 0; -} -EXPORT_SYMBOL(ion_cached_needsync_dmabuf); + if (!a->dma_mapped) { + trace_ion_begin_cpu_access_notmapped(a->dev, + dmabuf->name, + true, true, + dir, + false); + continue; + } -bool ion_may_hwrender_dmabuf(struct dma_buf *dmabuf) -{ - struct ion_buffer *buffer = dmabuf->priv; + tmp = ion_sgl_sync_range(a->dev, a->table->sgl, a->table->nents, + offset, len, dir, true); - if (dmabuf->ops != &dma_buf_ops) { - WARN(1, "%s: given dmabuf is not exported by ION\n", __func__); - return false; + if (!tmp) { + trace_ion_begin_cpu_access_cmo_apply(a->dev, + dmabuf->name, + true, true, dir, + false); + } else { + trace_ion_begin_cpu_access_cmo_skip(a->dev, + dmabuf->name, + true, true, dir, + false); + ret = tmp; + } } + mutex_unlock(&buffer->lock); - return !!(buffer->flags & ION_FLAG_MAY_HWRENDER); +out: + return ret; } -EXPORT_SYMBOL(ion_may_hwrender_dmabuf); -bool ion_may_hwrender_handle(struct ion_client *client, struct ion_handle *handle) +static int ion_dma_buf_end_cpu_access_partial(struct dma_buf *dmabuf, + enum dma_data_direction direction, + unsigned int offset, + unsigned int len) { - struct ion_buffer *buffer = handle->buffer; - bool valid_handle; + struct ion_buffer *buffer = dmabuf->priv; + struct ion_dma_buf_attachment *a; + int ret = 0; - mutex_lock(&client->lock); - valid_handle = ion_handle_validate(client, handle); + if (!hlos_accessible_buffer(buffer)) { + trace_ion_end_cpu_access_cmo_skip(NULL, dmabuf->name, + ion_buffer_cached(buffer), + false, direction, + false); + ret = -EPERM; + goto out; + } - if (!valid_handle) { - WARN(1, "%s: invalid handle passed\n", __func__); - mutex_unlock(&client->lock); - return false; + if (!(buffer->flags & ION_FLAG_CACHED)) { + trace_ion_end_cpu_access_cmo_skip(NULL, dmabuf->name, false, + true, direction, + false); + goto out; } - mutex_unlock(&client->lock); - return !!(buffer->flags & ION_FLAG_MAY_HWRENDER); -} -EXPORT_SYMBOL(ion_may_hwrender_handle); + mutex_lock(&buffer->lock); + if (IS_ENABLED(CONFIG_ION_FORCE_DMA_SYNC)) { + struct device *dev = buffer->heap->priv; + struct sg_table *table = buffer->sg_table; -static int ion_sync_for_device(struct ion_client *client, int fd) -{ - struct dma_buf *dmabuf; - struct ion_buffer *buffer; - struct scatterlist *sg, *sgl; - int nelems; - void *vaddr; - int i = 0; + ret = ion_sgl_sync_range(dev, table->sgl, table->nents, + offset, len, direction, false); - dmabuf = dma_buf_get(fd); - if (IS_ERR(dmabuf)) - return PTR_ERR(dmabuf); + if (!ret) + trace_ion_end_cpu_access_cmo_apply(dev, dmabuf->name, + true, true, + direction, false); + else + trace_ion_end_cpu_access_cmo_skip(dev, dmabuf->name, + true, true, + direction, false); - /* if this memory came from ion */ - if (dmabuf->ops != &dma_buf_ops) { - pr_err("%s: can not sync dmabuf from another exporter\n", - __func__); - dma_buf_put(dmabuf); - return -EINVAL; + mutex_unlock(&buffer->lock); + goto out; } - buffer = dmabuf->priv; - if (!ion_buffer_cached(buffer) || - ion_buffer_fault_user_mappings(buffer)) { - dma_buf_put(dmabuf); - return 0; - } + list_for_each_entry(a, &buffer->attachments, list) { + int tmp = 0; + + if (!a->dma_mapped) { + trace_ion_end_cpu_access_notmapped(a->dev, + dmabuf->name, + true, true, + direction, + false); + continue; + } - trace_ion_sync_start(_RET_IP_, buffer->dev->dev.this_device, - DMA_BIDIRECTIONAL, buffer->size, - buffer->vaddr, 0, false); + tmp = ion_sgl_sync_range(a->dev, a->table->sgl, a->table->nents, + offset, len, direction, false); - sgl = buffer->sg_table->sgl; - nelems = buffer->sg_table->nents; + if (!tmp) { + trace_ion_end_cpu_access_cmo_apply(a->dev, dmabuf->name, + true, true, + direction, false); - for_each_sg(sgl, sg, nelems, i) { - vaddr = phys_to_virt(sg_phys(sg)); - __dma_flush_range(vaddr, vaddr + sg->length); + } else { + trace_ion_end_cpu_access_cmo_skip(a->dev, dmabuf->name, + true, true, direction, + false); + ret = tmp; + } } + mutex_unlock(&buffer->lock); + +out: + return ret; +} - trace_ion_sync_end(_RET_IP_, buffer->dev->dev.this_device, - DMA_BIDIRECTIONAL, buffer->size, - buffer->vaddr, 0, false); +static int ion_dma_buf_get_flags(struct dma_buf *dmabuf, + unsigned long *flags) +{ + struct ion_buffer *buffer = dmabuf->priv; + *flags = buffer->flags; - dma_buf_put(dmabuf); return 0; } -static int ion_sync_partial_for_device(struct ion_client *client, int fd, - off_t offset, size_t len) +static const struct dma_buf_ops dma_buf_ops = { + .map_dma_buf = ion_map_dma_buf, + .unmap_dma_buf = ion_unmap_dma_buf, + .mmap = ion_mmap, + .release = ion_dma_buf_release, + .attach = ion_dma_buf_attach, + .detach = ion_dma_buf_detatch, + .begin_cpu_access = ion_dma_buf_begin_cpu_access, + .end_cpu_access = ion_dma_buf_end_cpu_access, + .begin_cpu_access_umapped = ion_dma_buf_begin_cpu_access_umapped, + .end_cpu_access_umapped = ion_dma_buf_end_cpu_access_umapped, + .begin_cpu_access_partial = ion_dma_buf_begin_cpu_access_partial, + .end_cpu_access_partial = ion_dma_buf_end_cpu_access_partial, + .map = ion_dma_buf_kmap, + .unmap = ion_dma_buf_kunmap, + .vmap = ion_dma_buf_vmap, + .vunmap = ion_dma_buf_vunmap, + .get_flags = ion_dma_buf_get_flags, +}; + +struct dma_buf *ion_alloc_dmabuf(size_t len, unsigned int heap_id_mask, + unsigned int flags) { + struct ion_device *dev = internal_dev; + struct ion_buffer *buffer = NULL; + struct ion_heap *heap; + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); struct dma_buf *dmabuf; - struct ion_buffer *buffer; - struct scatterlist *sg, *sgl; - size_t remained = len; - int nelems; - int i; - - dmabuf = dma_buf_get(fd); - if (IS_ERR(dmabuf)) - return PTR_ERR(dmabuf); + char task_comm[TASK_COMM_LEN]; + unsigned long jiffies_s = jiffies; + u64 utime, stime_s, stime_e, stime_d; + static DEFINE_RATELIMIT_STATE(show_mem_ratelimit, HZ * 10, 1); + + task_cputime(current, &utime, &stime_s); + pr_debug("%s: len %zu heap_id_mask %u flags %x\n", __func__, + len, heap_id_mask, flags); + /* + * traverse the list of heaps available in this system in priority + * order. If the heap type is supported by the client, and matches the + * request of the caller allocate from it. Repeat until allocate has + * succeeded or all heaps have been tried + */ + len = PAGE_ALIGN(len); - /* if this memory came from ion */ - if (dmabuf->ops != &dma_buf_ops) { - pr_err("%s: can not sync dmabuf from another exporter\n", - __func__); - dma_buf_put(dmabuf); - return -EINVAL; - } - buffer = dmabuf->priv; + if (!len) + return ERR_PTR(-EINVAL); - if (!ion_buffer_cached(buffer) || - ion_buffer_fault_user_mappings(buffer)) { - dma_buf_put(dmabuf); - return 0; + if (heap_id_mask == 0xFFFFFFFF) { + heap_id_mask = get_ion_system_heap_id(); + if (IS_ERR(ERR_PTR(heap_id_mask))) + return ERR_PTR(heap_id_mask); + heap_id_mask = (1 << heap_id_mask); } - trace_ion_sync_start(_RET_IP_, buffer->dev->dev.this_device, - DMA_BIDIRECTIONAL, buffer->size, - buffer->vaddr, 0, false); - - sgl = buffer->sg_table->sgl; - nelems = buffer->sg_table->nents; - - for_each_sg(sgl, sg, nelems, i) { - size_t len_to_flush; - if (offset >= sg->length) { - offset -= sg->length; + down_read(&dev->lock); + plist_for_each_entry(heap, &dev->heaps, node) { + /* if the caller didn't specify this heap id */ + if (!((1 << heap->id) & heap_id_mask)) continue; - } - - len_to_flush = sg->length - offset; - if (remained < len_to_flush) { - len_to_flush = remained; - remained = 0; - } else { - remained -= len_to_flush; - } + buffer = ion_buffer_create(heap, dev, len, flags); + if (!IS_ERR(buffer) || PTR_ERR(buffer) == -EINTR) + break; + } + up_read(&dev->lock); - __dma_map_area(phys_to_virt(sg_phys(sg)) + offset, - len_to_flush, DMA_TO_DEVICE); + if (!buffer) + return ERR_PTR(-ENODEV); - if (remained == 0) - break; - offset = 0; + if (IS_ERR(buffer)) { + pr_err("%s ion alloc failed len: 0x%zx mask=0x%x flags=0x%x error%ld\n", + __func__, len, heap_id_mask, flags, PTR_ERR(buffer)); + return ERR_CAST(buffer); } - trace_ion_sync_end(_RET_IP_, buffer->dev->dev.this_device, - DMA_BIDIRECTIONAL, buffer->size, - buffer->vaddr, 0, false); + get_task_comm(task_comm, current->group_leader); - dma_buf_put(dmabuf); + exp_info.ops = &dma_buf_ops; + exp_info.size = buffer->size; + exp_info.flags = O_RDWR; + exp_info.priv = buffer; + exp_info.exp_name = kasprintf(GFP_KERNEL, "%s-%s-%d-%s", KBUILD_MODNAME, + heap->name, current->tgid, task_comm); - return 0; -} + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) { + _ion_buffer_destroy(buffer); + kfree(exp_info.exp_name); + } -/* fix up the cases where the ioctl direction bits are incorrect */ -static unsigned int ion_ioctl_dir(unsigned int cmd) -{ - switch (cmd) { - case ION_IOC_SYNC: - case ION_IOC_SYNC_PARTIAL: - case ION_IOC_FREE: - case ION_IOC_CUSTOM: - return _IOC_WRITE; - default: - return _IOC_DIR(cmd); + task_cputime(current, &utime, &stime_e); + stime_d = stime_e - stime_s; + if (!IS_ERR(dmabuf) && stime_d / NSEC_PER_MSEC > 100) { + pr_info("%s ion_heap_id: %d mask=0x%x timeJS(ms):%u/%llu len:0x%zx", + __func__, heap->id, heap_id_mask, + jiffies_to_msecs(jiffies - jiffies_s), + stime_d / NSEC_PER_MSEC, len); + if (__ratelimit(&show_mem_ratelimit)) + show_mem(0, NULL); } + return dmabuf; } -static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, + unsigned int flags) { - struct ion_client *client = filp->private_data; - struct ion_device *dev = client->dev; - struct ion_handle *cleanup_handle = NULL; - int ret = 0; - unsigned int dir; - - union { - struct ion_fd_data fd; - struct ion_fd_partial_data fd_partial; - struct ion_allocation_data allocation; - struct ion_handle_data handle; - struct ion_custom_data custom; - } data; - - dir = ion_ioctl_dir(cmd); - - if (_IOC_SIZE(cmd) > sizeof(data)) - return -EINVAL; - - if (dir & _IOC_WRITE) - if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd))) - return -EFAULT; - - switch (cmd) { - case ION_IOC_ALLOC: - { - struct ion_handle *handle; - - handle = __ion_alloc(client, data.allocation.len, - data.allocation.align, - data.allocation.heap_id_mask, - data.allocation.flags, true); - if (IS_ERR(handle)) { - pr_err("%s: len %zu align %zu heap_id_mask %u flags %x (ret %ld)\n", - __func__, data.allocation.len, - data.allocation.align, - data.allocation.heap_id_mask, - data.allocation.flags, PTR_ERR(handle)); - return PTR_ERR(handle); - } + struct ion_device *dev = internal_dev; + struct ion_heap *heap; + bool type_valid = false; - data.allocation.handle = handle->id; + pr_debug("%s: len %zu heap_id_mask %u flags %x\n", __func__, + len, heap_id_mask, flags); + /* + * traverse the list of heaps available in this system in priority + * order. Check the heap type is supported. + */ - cleanup_handle = handle; - break; - } - case ION_IOC_FREE: - { - struct ion_handle *handle; - - mutex_lock(&client->lock); - handle = ion_handle_get_by_id_nolock(client, data.handle.handle); - if (IS_ERR(handle)) { - mutex_unlock(&client->lock); - return PTR_ERR(handle); - } - ion_free_nolock(client, handle); - ion_handle_put_nolock(handle); - mutex_unlock(&client->lock); - break; - } - case ION_IOC_SHARE: - case ION_IOC_MAP: - { - struct ion_handle *handle; - - mutex_lock(&client->lock); - handle = ion_handle_get_by_id_nolock(client, data.handle.handle); - if (IS_ERR(handle)) { - mutex_unlock(&client->lock); - return PTR_ERR(handle); + down_read(&dev->lock); + plist_for_each_entry(heap, &dev->heaps, node) { + /* if the caller didn't specify this heap id */ + if (!((1 << heap->id) & heap_id_mask)) + continue; + if (heap->type == ION_HEAP_TYPE_SYSTEM || + heap->type == ION_HEAP_TYPE_CARVEOUT || + heap->type == (enum ion_heap_type)ION_HEAP_TYPE_HYP_CMA || + heap->type == (enum ion_heap_type)ION_HEAP_TYPE_RBIN || + heap->type == + (enum ion_heap_type)ION_HEAP_TYPE_SYSTEM_SECURE) { + type_valid = true; + } else { + pr_warn("%s: heap type not supported, type:%d\n", + __func__, heap->type); } - data.fd.fd = ion_share_dma_buf_fd_nolock(client, handle); - ion_handle_put_nolock(handle); - mutex_unlock(&client->lock); - if (data.fd.fd < 0) - ret = data.fd.fd; break; } - case ION_IOC_IMPORT: - { - struct ion_handle *handle; - - handle = ion_import_dma_buf(client, data.fd.fd); - if (IS_ERR(handle)) - ret = PTR_ERR(handle); - else - data.handle.handle = handle->id; - break; - } - case ION_IOC_SYNC: - { - ret = ion_sync_for_device(client, data.fd.fd); - break; - } - case ION_IOC_SYNC_PARTIAL: - { - ret = ion_sync_partial_for_device(client, data.fd_partial.fd, - data.fd_partial.offset, data.fd_partial.len); - break; - } - case ION_IOC_CUSTOM: - { - if (!dev->custom_ioctl) - return -ENOTTY; - ret = dev->custom_ioctl(client, data.custom.cmd, - data.custom.arg); - break; - } - default: - return -ENOTTY; - } + up_read(&dev->lock); - if (dir & _IOC_READ) { - if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) { - if (cleanup_handle) { - ion_free(client, cleanup_handle); - ion_handle_put(cleanup_handle); - } - return -EFAULT; - } - } - if (cleanup_handle) - ion_handle_put(cleanup_handle); - return ret; + if (!type_valid) + return ERR_PTR(-EINVAL); + + return ion_alloc_dmabuf(len, heap_id_mask, flags); } +EXPORT_SYMBOL(ion_alloc); -static int ion_release(struct inode *inode, struct file *file) +int ion_alloc_fd(size_t len, unsigned int heap_id_mask, unsigned int flags) { - struct ion_client *client = file->private_data; + int fd; + struct dma_buf *dmabuf; - ion_client_destroy(client); - return 0; + dmabuf = ion_alloc_dmabuf(len, heap_id_mask, flags); + if (IS_ERR(dmabuf)) + return PTR_ERR(dmabuf); + + fd = dma_buf_fd(dmabuf, O_CLOEXEC); + if (fd < 0) + dma_buf_put(dmabuf); + + return fd; } -static int ion_open(struct inode *inode, struct file *file) +int ion_query_heaps(struct ion_heap_query *query) { - struct miscdevice *miscdev = file->private_data; - struct ion_device *dev = container_of(miscdev, struct ion_device, dev); - struct ion_client *client; - char debug_name[64]; + struct ion_device *dev = internal_dev; + struct ion_heap_data __user *buffer = u64_to_user_ptr(query->heaps); + int ret = -EINVAL, cnt = 0, max_cnt; + struct ion_heap *heap; + struct ion_heap_data hdata; - snprintf(debug_name, 64, "%u", task_pid_nr(current->group_leader)); - client = ion_client_create(dev, debug_name); - if (IS_ERR(client)) - return PTR_ERR(client); - file->private_data = client; + memset(&hdata, 0, sizeof(hdata)); - return 0; + down_read(&dev->lock); + if (!buffer) { + query->cnt = dev->heap_cnt; + ret = 0; + goto out; + } + + if (query->cnt <= 0) + goto out; + + max_cnt = query->cnt; + + plist_for_each_entry(heap, &dev->heaps, node) { + strlcpy(hdata.name, heap->name, sizeof(hdata.name)); + hdata.name[sizeof(hdata.name) - 1] = '\0'; + hdata.type = heap->type; + hdata.heap_id = heap->id; + + if (copy_to_user(&buffer[cnt], &hdata, sizeof(hdata))) { + ret = -EFAULT; + goto out; + } + + cnt++; + if (cnt >= max_cnt) + break; + } + + query->cnt = cnt; + ret = 0; +out: + up_read(&dev->lock); + return ret; } static const struct file_operations ion_fops = { .owner = THIS_MODULE, - .open = ion_open, - .release = ion_release, .unlocked_ioctl = ion_ioctl, - .compat_ioctl = compat_ion_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ion_ioctl, +#endif }; -static size_t ion_debug_heap_total(struct ion_client *client, - unsigned int id) +static void __ion_debug_heap_usage_show(struct ion_heap *heap) { - size_t size = 0; + struct ion_device *dev = heap->dev; struct rb_node *n; + size_t total_size = 0; - mutex_lock(&client->lock); - for (n = rb_first(&client->handles); n; n = rb_next(n)) { - struct ion_handle *handle = rb_entry(n, - struct ion_handle, + pr_info("ion heap: %s %u\n", heap->name, heap->id); + pr_info("%16s %16s %16s\n", "task", "pid", "size"); + mutex_lock(&dev->buffer_lock); + for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { + struct ion_buffer *buffer = rb_entry(n, struct ion_buffer, node); - if (handle->buffer->heap->id == id) - size += handle->buffer->size; + if (buffer->heap->id != heap->id) + continue; + total_size += buffer->size; + pr_info("%16s %16u (%16s %16u) %16zu\n", buffer->task_comm, + buffer->pid, buffer->thread_comm, buffer->tid, + buffer->size); } - mutex_unlock(&client->lock); - return size; + mutex_unlock(&dev->buffer_lock); + pr_info("%16s %16zu\n", "total ", total_size); + pr_info("%16.s %16lu\n", "peak allocated", + atomic_long_read(&heap->total_allocated_peak)); +} + +static void ion_debug_heap_usage_show(struct ion_heap *heap) +{ + static DEFINE_RATELIMIT_STATE(show_heap_usage, HZ * 10, 1); + + /* supports only for some heaps */ + if (heap->type != ION_HEAP_TYPE_CARVEOUT && + heap->type != ION_HEAP_TYPE_DMA && + heap->type != ION_HEAP_TYPE_SECURE_DMA && + heap->type != ION_HEAP_TYPE_HYP_CMA && + heap->type != ION_HEAP_TYPE_SECURE_CARVEOUT) + return; + + if (heap->id == ION_CAMERA_HEAP_ID) + return; + + if (!__ratelimit(&show_heap_usage)) + return; + + __ion_debug_heap_usage_show(heap); } +static void ion_debug_heap_usage_show_force(struct ion_heap *heap) +{ + static DEFINE_RATELIMIT_STATE(show_heap_usage_force, HZ * 10, 1); + + if (!__ratelimit(&show_heap_usage_force)) + return; + + __ion_debug_heap_usage_show(heap); +} + +static int ion_oom_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct ion_heap *heap; + + /* print ion system_heap */ + heap = get_ion_heap(ION_SYSTEM_HEAP_ID); + ion_debug_heap_usage_show_force(heap); + + return NOTIFY_DONE; +} + +static struct notifier_block ion_oom_notifier = { + .notifier_call = ion_oom_notify, +}; + static int ion_debug_heap_show(struct seq_file *s, void *unused) { struct ion_heap *heap = s->private; struct ion_device *dev = heap->dev; struct rb_node *n; size_t total_size = 0; - size_t total_orphaned_size = 0; seq_printf(s, "%16s %16s %16s\n", "client", "pid", "size"); - seq_puts(s, "----------------------------------------------------\n"); - - down_read(&dev->lock); - - for (n = rb_first(&dev->clients); n; n = rb_next(n)) { - struct ion_client *client = rb_entry(n, struct ion_client, - node); - size_t size = ion_debug_heap_total(client, heap->id); - - if (!size) - continue; - if (client->task) { - char task_comm[TASK_COMM_LEN]; - get_task_comm(task_comm, client->task); - seq_printf(s, "%16s %16u %16zu\n", task_comm, - client->pid, size); - } else { - seq_printf(s, "%16s %16u %16zu\n", client->name, - client->pid, size); - } - } seq_puts(s, "----------------------------------------------------\n"); - seq_puts(s, "orphaned allocations (info is from last known client):\n"); mutex_lock(&dev->buffer_lock); for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { struct ion_buffer *buffer = rb_entry(n, struct ion_buffer, @@ -2059,31 +1316,24 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused) if (buffer->heap->id != heap->id) continue; total_size += buffer->size; - if (!buffer->handle_count) { - seq_printf(s, "%16s %16u %16zu %d %d\n", - buffer->task_comm, buffer->pid, - buffer->size, buffer->kmap_cnt, - atomic_read(&buffer->ref.refcount)); - total_orphaned_size += buffer->size; - } + seq_printf(s, "%16s %16u (%16s %16u) %16zu\n", + buffer->task_comm, buffer->pid, + buffer->thread_comm, buffer->tid, + buffer->size); } mutex_unlock(&dev->buffer_lock); seq_puts(s, "----------------------------------------------------\n"); - seq_printf(s, "%16s %16zu\n", "total orphaned", - total_orphaned_size); seq_printf(s, "%16s %16zu\n", "total ", total_size); seq_printf(s, "%16.s %16lu\n", "peak allocated", atomic_long_read(&heap->total_allocated_peak)); if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) seq_printf(s, "%16s %16zu\n", "deferred free", - heap->free_list_size); + heap->free_list_size); seq_puts(s, "----------------------------------------------------\n"); if (heap->debug_show) heap->debug_show(heap, s, unused); - up_read(&dev->lock); - return 0; } @@ -2105,7 +1355,7 @@ static int debug_shrink_set(void *data, u64 val) struct shrink_control sc; int objs; - sc.gfp_mask = -1; + sc.gfp_mask = GFP_HIGHUSER; sc.nr_to_scan = val; if (!val) { @@ -2123,7 +1373,7 @@ static int debug_shrink_get(void *data, u64 *val) struct shrink_control sc; int objs; - sc.gfp_mask = -1; + sc.gfp_mask = GFP_HIGHUSER; sc.nr_to_scan = 0; objs = heap->shrinker.count_objects(&heap->shrinker, &sc); @@ -2136,10 +1386,10 @@ DEFINE_SIMPLE_ATTRIBUTE(debug_shrink_fops, debug_shrink_get, void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap) { - struct dentry *debug_file; + char debug_name[64], buf[256]; + int ret; - if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma || - !heap->ops->unmap_dma) + if (!heap->ops->allocate || !heap->ops->free) pr_err("%s: can not add heap with invalid ops struct.\n", __func__); @@ -2149,8 +1399,11 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap) if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) ion_heap_init_deferred_free(heap); - if ((heap->flags & ION_HEAP_FLAG_DEFER_FREE) || heap->ops->shrink) - ion_heap_init_shrinker(heap); + if ((heap->flags & ION_HEAP_FLAG_DEFER_FREE) || heap->ops->shrink) { + ret = ion_heap_init_shrinker(heap); + if (ret) + pr_err("%s: Failed to register shrinker\n", __func__); + } heap->dev = dev; down_write(&dev->lock); @@ -2160,278 +1413,35 @@ void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap) */ plist_node_init(&heap->node, -heap->id); plist_add(&heap->node, &dev->heaps); - debug_file = debugfs_create_file(heap->name, 0664, - dev->heaps_debug_root, heap, - &debug_heap_fops); - - if (!debug_file) { - char buf[256], *path; - path = dentry_path(dev->heaps_debug_root, buf, 256); + snprintf(debug_name, 64, "%s", heap->name); + if (!debugfs_create_file(heap->name, 0664, dev->heaps_debug_root, + heap, &debug_heap_fops)) pr_err("Failed to create heap debugfs at %s/%s\n", - path, heap->name); - } + dentry_path(dev->heaps_debug_root, buf, 256), + debug_name); - if (heap->shrinker.count_objects && heap->shrinker.scan_objects) { - char debug_name[64]; + if (heap->shrinker.count_objects && heap->shrinker.scan_objects) { snprintf(debug_name, 64, "%s_shrink", heap->name); - debug_file = debugfs_create_file( - debug_name, 0644, dev->heaps_debug_root, heap, - &debug_shrink_fops); - if (!debug_file) { - char buf[256], *path; - - path = dentry_path(dev->heaps_debug_root, buf, 256); - pr_err("Failed to create heap shrinker debugfs at %s/%s\n", - path, debug_name); - } + if (!debugfs_create_file(debug_name, 0644, dev->heaps_debug_root, + heap, &debug_shrink_fops)) + pr_err("Failed to create heap debugfs at %s/%s\n", + dentry_path(dev->heaps_debug_root, buf, 256), + debug_name); } + dev->heap_cnt++; up_write(&dev->lock); } EXPORT_SYMBOL(ion_device_add_heap); -#ifdef CONFIG_ION_EXYNOS_STAT_LOG - -#define MAX_DUMP_TASKS 8 -#define MAX_DUMP_NAME_LEN 32 -#define MAX_DUMP_BUFF_LEN 512 - -static void ion_buffer_dump_flags(struct seq_file *s, unsigned long flags) -{ - if ((flags & ION_FLAG_CACHED) && !(flags & ION_FLAG_CACHED_NEEDS_SYNC)) - seq_printf(s, "cached|faultmap"); - else if (flags & ION_FLAG_CACHED) - seq_printf(s, "cached|needsync"); - else - seq_printf(s, "noncached"); - - if (flags & ION_FLAG_NOZEROED) - seq_printf(s, "|nozeroed"); - - if (flags & ION_FLAG_PROTECTED) - seq_printf(s, "|protected"); -} - -static void ion_buffer_dump_tasks(struct ion_buffer *buffer, char *str) -{ - struct ion_task *task, *tmp; - const char *delim = "|"; - size_t total_len = 0; - int count = 0; - - list_for_each_entry_safe(task, tmp, &buffer->master_list, list) { - const char *name; - size_t len = strlen(dev_name(task->master)); - - if (len > MAX_DUMP_NAME_LEN) - len = MAX_DUMP_NAME_LEN; - if (!strncmp(dev_name(task->master), "ion", len)) { - continue; - } else { - name = dev_name(task->master) + 9; - len -= 9; - } - if (total_len + len + 1 > MAX_DUMP_BUFF_LEN) - break; - - strncat((char *)(str + total_len), name, len); - total_len += len; - if (!list_is_last(&task->list, &buffer->master_list)) - str[total_len++] = *delim; - - if (++count > MAX_DUMP_TASKS) - break; - } -} - -static int ion_debug_buffer_show(struct seq_file *s, void *unused) -{ - struct ion_device *dev = s->private; - struct rb_node *n; - char *master_name; - size_t total_size = 0; - - master_name = kzalloc(MAX_DUMP_BUFF_LEN, GFP_KERNEL); - if (!master_name) { - pr_err("%s: no memory for client string buffer\n", __func__); - return -ENOMEM; - } - - seq_printf(s, "%20.s %16.s %4.s %16.s %4.s %10.s %4.s %3.s %6.s " - "%24.s %9.s\n", - "heap", "task", "pid", "thread", "tid", - "size", "kmap", "ref", "handle", - "master", "flag"); - seq_printf(s, "------------------------------------------" - "----------------------------------------" - "----------------------------------------" - "--------------------------------------\n"); - - mutex_lock(&dev->buffer_lock); - for (n = rb_first(&dev->buffers); n; n = rb_next(n)) { - struct ion_buffer *buffer = rb_entry(n, struct ion_buffer, - node); - mutex_lock(&buffer->lock); - ion_buffer_dump_tasks(buffer, master_name); - total_size += buffer->size; - seq_printf(s, "%20.s %16.s %4u %16.s %4u %10zu %4d %3d %6d " - "%24.s %9lx", buffer->heap->name, - buffer->task_comm, buffer->pid, - buffer->thread_comm, - buffer->tid, buffer->size, buffer->kmap_cnt, - atomic_read(&buffer->ref.refcount), - buffer->handle_count, master_name, - buffer->flags); - seq_printf(s, "("); - ion_buffer_dump_flags(s, buffer->flags); - seq_printf(s, ")\n"); - mutex_unlock(&buffer->lock); - - memset(master_name, 0, MAX_DUMP_BUFF_LEN); - } - mutex_unlock(&dev->buffer_lock); - - seq_printf(s, "------------------------------------------" - "----------------------------------------" - "----------------------------------------" - "--------------------------------------\n"); - seq_printf(s, "%16.s %16zu\n", "total ", total_size); - seq_printf(s, "------------------------------------------" - "----------------------------------------" - "----------------------------------------" - "--------------------------------------\n"); - - kfree(master_name); - - return 0; -} - -static int ion_debug_buffer_open(struct inode *inode, struct file *file) -{ - return single_open(file, ion_debug_buffer_show, inode->i_private); -} - -static const struct file_operations debug_buffer_fops = { - .open = ion_debug_buffer_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void ion_debug_event_show_one(struct seq_file *s, - struct ion_eventlog *log) -{ - struct timeval tv = ktime_to_timeval(log->begin); - long elapsed = ktime_us_delta(log->done, log->begin); - - if (elapsed == 0) - return; - - seq_printf(s, "[%06ld.%06ld] ", tv.tv_sec, tv.tv_usec); - - switch (log->type) { - case ION_EVENT_TYPE_ALLOC: - { - struct ion_event_alloc *data = &log->data.alloc; - seq_printf(s, "%8s %pK %18s %11zd ", "alloc", - data->id, data->heapname, data->size); - break; - } - case ION_EVENT_TYPE_FREE: - { - struct ion_event_free *data = &log->data.free; - seq_printf(s, "%8s %pK %18s %11zd ", "free", - data->id, data->heapname, data->size); - break; - } - case ION_EVENT_TYPE_MMAP: - { - struct ion_event_mmap *data = &log->data.mmap; - seq_printf(s, "%8s %pK %18s %11zd ", "mmap", - data->id, data->heapname, data->size); - break; - } - case ION_EVENT_TYPE_SHRINK: - { - struct ion_event_shrink *data = &log->data.shrink; - seq_printf(s, "%8s %16lx %18s %11zd ", "shrink", - 0l, "ion_noncontig_heap", data->size); - elapsed = 0; - break; - } - case ION_EVENT_TYPE_CLEAR: - { - struct ion_event_clear *data = &log->data.clear; - seq_printf(s, "%8s %pK %18s %11zd ", "clear", - data->id, data->heapname, data->size); - break; - } - } - - seq_printf(s, "%9ld", elapsed); - - if (elapsed > 100 * USEC_PER_MSEC) - seq_printf(s, " *"); - - if (log->type == ION_EVENT_TYPE_ALLOC) { - seq_printf(s, " "); - ion_buffer_dump_flags(s, log->data.alloc.flags); - } else if (log->type == ION_EVENT_TYPE_CLEAR) { - seq_printf(s, " "); - ion_buffer_dump_flags(s, log->data.clear.flags); - } - - if (log->type == ION_EVENT_TYPE_FREE && log->data.free.shrinker) - seq_printf(s, " shrinker"); - - seq_printf(s, "\n"); -} - -static int ion_debug_event_show(struct seq_file *s, void *unused) -{ - struct ion_device *dev = s->private; - int index = atomic_read(&dev->event_idx) % ION_EVENT_LOG_MAX; - int last = index; - - seq_printf(s, "%13s %10s %8s %18s %11s %10s %24s\n", "timestamp", - "type", "id", "heap", "size", "time (us)", "remarks"); - seq_printf(s, "-------------------------------------------"); - seq_printf(s, "-------------------------------------------"); - seq_printf(s, "-----------------------------------------\n"); - - do { - if (++index >= ION_EVENT_LOG_MAX) - index = 0; - ion_debug_event_show_one(s, &dev->eventlog[index]); - } while (index != last); - - return 0; -} - -static int ion_debug_event_open(struct inode *inode, struct file *file) -{ - return single_open(file, ion_debug_event_show, inode->i_private); -} - -static const struct file_operations debug_event_fops = { - .open = ion_debug_event_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif - -struct ion_device *ion_device_create(long (*custom_ioctl) - (struct ion_client *client, - unsigned int cmd, - unsigned long arg)) +struct ion_device *ion_device_create(void) { struct ion_device *idev; int ret; - idev = kzalloc(sizeof(struct ion_device), GFP_KERNEL); + idev = kzalloc(sizeof(*idev), GFP_KERNEL); if (!idev) return ERR_PTR(-ENOMEM); @@ -2447,236 +1457,13 @@ struct ion_device *ion_device_create(long (*custom_ioctl) } idev->debug_root = debugfs_create_dir("ion", NULL); - if (!idev->debug_root) { - pr_err("ion: failed to create debugfs root directory.\n"); - goto debugfs_done; - } idev->heaps_debug_root = debugfs_create_dir("heaps", idev->debug_root); - if (!idev->heaps_debug_root) { - pr_err("ion: failed to create debugfs heaps directory.\n"); - goto debugfs_done; - } - idev->clients_debug_root = debugfs_create_dir("clients", - idev->debug_root); - if (!idev->clients_debug_root) { - pr_err("ion: failed to create debugfs clients directory.\n"); - goto debugfs_done; - } - -#ifdef CONFIG_ION_EXYNOS_STAT_LOG - atomic_set(&idev->event_idx, -1); - idev->buffer_debug_file = debugfs_create_file("buffer", 0444, - idev->debug_root, idev, - &debug_buffer_fops); - if (!idev->buffer_debug_file) { - pr_err("%s: failed to create buffer debug file\n", __func__); - goto debugfs_done; - } - - idev->event_debug_file = debugfs_create_file("event", 0444, - idev->debug_root, idev, - &debug_event_fops); - if (!idev->event_debug_file) - pr_err("%s: failed to create event debug file\n", __func__); -#endif - -debugfs_done: - - idev->custom_ioctl = custom_ioctl; + WARN_ON(register_oom_notifier(&ion_oom_notifier)); idev->buffers = RB_ROOT; mutex_init(&idev->buffer_lock); init_rwsem(&idev->lock); plist_head_init(&idev->heaps); - idev->clients = RB_ROOT; - - /* backup of ion device: assumes there is only one ion device */ - g_idev = idev; - + internal_dev = idev; return idev; } EXPORT_SYMBOL(ion_device_create); - -void ion_device_destroy(struct ion_device *dev) -{ - misc_deregister(&dev->dev); - debugfs_remove_recursive(dev->debug_root); - kfree(dev); -} -EXPORT_SYMBOL(ion_device_destroy); - -void __init ion_reserve(struct ion_platform_data *data) -{ - int i; - - for (i = 0; i < data->nr; i++) { - if (data->heaps[i].size == 0) - continue; - - if (data->heaps[i].base == 0) { - phys_addr_t paddr; - - paddr = memblock_alloc_base(data->heaps[i].size, - data->heaps[i].align, - MEMBLOCK_ALLOC_ANYWHERE); - if (!paddr) { - pr_err("%s: error allocating memblock for heap %d\n", - __func__, i); - continue; - } - data->heaps[i].base = paddr; - } else { - int ret = memblock_reserve(data->heaps[i].base, - data->heaps[i].size); - if (ret) - pr_err("memblock reserve of %zx@%lx failed\n", - data->heaps[i].size, - data->heaps[i].base); - } - pr_info("%s: %s reserved base %lx size %zu\n", __func__, - data->heaps[i].name, - data->heaps[i].base, - data->heaps[i].size); - } -} - -static struct ion_iovm_map *ion_buffer_iova_create(struct ion_buffer *buffer, - struct device *dev, enum dma_data_direction dir, int prop) -{ - /* Must be called under buffer->lock held */ - struct ion_iovm_map *iovm_map; - int ret = 0; - - iovm_map = kzalloc(sizeof(struct ion_iovm_map), GFP_KERNEL); - if (!iovm_map) { - pr_err("%s: Failed to allocate ion_iovm_map for %s\n", - __func__, dev_name(dev)); - return ERR_PTR(-ENOMEM); - } - - iovm_map->iova = iovmm_map(dev, buffer->sg_table->sgl, - 0, buffer->size, dir, prop); - - if (iovm_map->iova == (dma_addr_t)-ENOSYS) { - size_t len; - ion_phys_addr_t addr; - - BUG_ON(!buffer->heap->ops->phys); - ret = buffer->heap->ops->phys(buffer->heap, buffer, - &addr, &len); - if (ret) - pr_err("%s: Unable to get PA for %s\n", - __func__, dev_name(dev)); - } else if (IS_ERR_VALUE(iovm_map->iova)) { - ret = iovm_map->iova; - pr_err("%s: Unable to allocate IOVA for %s\n", - __func__, dev_name(dev)); - } - - if (ret) { - kfree(iovm_map); - return ERR_PTR(ret); - } - - iovm_map->dev = dev; - iovm_map->domain = get_domain_from_dev(dev); - iovm_map->map_cnt = 1; - - pr_debug("%s: new map added for dev %s, iova %pa, prop %d\n", __func__, - dev_name(dev), &iovm_map->iova, prop); - - return iovm_map; -} - -dma_addr_t ion_iovmm_map(struct dma_buf_attachment *attachment, - off_t offset, size_t size, - enum dma_data_direction direction, int prop) -{ - struct dma_buf *dmabuf = attachment->dmabuf; - struct ion_buffer *buffer = dmabuf->priv; - struct ion_iovm_map *iovm_map; - struct iommu_domain *domain; - - BUG_ON(dmabuf->ops != &dma_buf_ops); - - if (IS_ENABLED(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION) && - buffer->flags & ION_FLAG_PROTECTED) { - struct ion_buffer_info *info = buffer->priv_virt; - - if (info->prot_desc.dma_addr) - return info->prot_desc.dma_addr; - pr_err("%s: protected buffer but no secure iova\n", __func__); - return -EINVAL; - } - - domain = get_domain_from_dev(attachment->dev); - if (!domain) { - pr_err("%s: invalid iommu device\n", __func__); - return -EINVAL; - } - - mutex_lock(&buffer->lock); - list_for_each_entry(iovm_map, &buffer->iovas, list) { - if (domain == iovm_map->domain) { - iovm_map->map_cnt++; - mutex_unlock(&buffer->lock); - return iovm_map->iova; - } - } - - if (!ion_buffer_cached(buffer)) - prop &= ~IOMMU_CACHE; - - iovm_map = ion_buffer_iova_create(buffer, attachment->dev, - direction, prop); - if (IS_ERR(iovm_map)) { - mutex_unlock(&buffer->lock); - return PTR_ERR(iovm_map); - } - - list_add_tail(&iovm_map->list, &buffer->iovas); - mutex_unlock(&buffer->lock); - - return iovm_map->iova; -} - -void ion_iovmm_unmap(struct dma_buf_attachment *attachment, dma_addr_t iova) -{ - struct ion_iovm_map *iovm_map; - struct dma_buf * dmabuf = attachment->dmabuf; - struct device *dev = attachment->dev; - struct ion_buffer *buffer = attachment->dmabuf->priv; - struct iommu_domain *domain; - - BUG_ON(dmabuf->ops != &dma_buf_ops); - - if (IS_ENABLED(CONFIG_EXYNOS_CONTENT_PATH_PROTECTION) && - buffer->flags & ION_FLAG_PROTECTED) - return; - - domain = get_domain_from_dev(attachment->dev); - if (!domain) { - pr_err("%s: invalid iommu device\n", __func__); - return; - } - - mutex_lock(&buffer->lock); - list_for_each_entry(iovm_map, &buffer->iovas, list) { - if ((domain == iovm_map->domain) && (iova == iovm_map->iova)) { - if (--iovm_map->map_cnt == 0) { - list_del(&iovm_map->list); - pr_debug("%s: unmap previous %pa for dev %s\n", - __func__, &iovm_map->iova, - dev_name(iovm_map->dev)); - iovmm_unmap(iovm_map->dev, iovm_map->iova); - kfree(iovm_map); - } - - mutex_unlock(&buffer->lock); - return; - } - } - - mutex_unlock(&buffer->lock); - - WARN(1, "IOVA %pa is not found for %s\n", &iova, dev_name(dev)); -} diff --git a/drivers/staging/android/ion/ion.h b/drivers/staging/android/ion/ion.h old mode 100755 new mode 100644 index 6fa9dbe3..b1236ed4 --- a/drivers/staging/android/ion/ion.h +++ b/drivers/staging/android/ion/ion.h @@ -1,51 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * drivers/staging/android/ion/ion.h * * Copyright (C) 2011 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. + * Copyright (c) 2011-2019, The Linux Foundation. All rights reserved. * */ -#ifndef _LINUX_ION_H -#define _LINUX_ION_H +#ifndef _ION_H +#define _ION_H +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include "ion_kernel.h" +#include "../uapi/ion.h" +#include "../uapi/msm_ion.h" -#include +#define ION_ADSP_HEAP_NAME "adsp" +#define ION_SYSTEM_HEAP_NAME "system" +#define ION_MM_HEAP_NAME "mm" +#define ION_CAMERA_HEAP_NAME "camera_preview" +#define ION_SPSS_HEAP_NAME "spss" +#define ION_SECURE_CARVEOUT_HEAP_NAME "secure_carveout" +#define ION_USER_CONTIG_HEAP_NAME "user_contig" +#define ION_QSECOM_HEAP_NAME "qsecom" +#define ION_QSECOM_TA_HEAP_NAME "qsecom_ta" +#define ION_SECURE_HEAP_NAME "secure_heap" +#define ION_SECURE_DISPLAY_HEAP_NAME "secure_display" +#define ION_AUDIO_HEAP_NAME "audio" -struct ion_handle; -struct ion_device; -struct ion_heap; -struct ion_mapper; -struct ion_client; -struct ion_buffer; +#define ION_IS_CACHED(__flags) ((__flags) & ION_FLAG_CACHED) -/* - * This should be removed some day when phys_addr_t's are fully - * plumbed in the kernel, and all instances of ion_phys_addr_t should - * be converted to phys_addr_t. For the time being many kernel interfaces - * do not accept phys_addr_t's that would have to +/** + * Debug feature. Make ION allocations DMA + * ready to help identify clients who are wrongly + * dependending on ION allocations being DMA + * ready. + * + * As default set to 'false' since ION allocations + * are no longer required to be DMA ready */ -#define ion_phys_addr_t unsigned long +#ifdef CONFIG_ION_FORCE_DMA_SYNC +#define MAKE_ION_ALLOC_DMA_READY 1 +#else +#define MAKE_ION_ALLOC_DMA_READY 0 +#endif + +/* ION page pool marks in bytes */ +#ifdef CONFIG_ION_POOL_AUTO_REFILL +#define ION_POOL_FILL_MARK (CONFIG_ION_POOL_FILL_MARK * SZ_1M) +#define POOL_LOW_MARK_PERCENT 40UL +#define ION_POOL_LOW_MARK ((ION_POOL_FILL_MARK * POOL_LOW_MARK_PERCENT) / 100) +#else +#define ION_POOL_FILL_MARK 0UL +#define ION_POOL_LOW_MARK 0UL +#endif + +/* if low watermark of zones have reached, defer the refill in this window */ +#define ION_POOL_REFILL_DEFER_WINDOW_MS 10 /** * struct ion_platform_heap - defines a heap in the given platform * @type: type of the heap from ion_heap_type enum - * @id: unique identifier for heap. When allocating higher numbers + * @id: unique identifier for heap. When allocating higher numb ers * will be allocated from first. At allocation these are passed * as a bit mask and therefore can not exceed ION_NUM_HEAP_IDS. * @name: used for debug purposes * @base: base address of heap in physical memory if applicable * @size: size of the heap in bytes if applicable - * @align: required alignment in physical memory if applicable * @priv: private info passed from the board file * * Provided by the board file. @@ -54,16 +87,16 @@ struct ion_platform_heap { enum ion_heap_type type; unsigned int id; const char *name; - ion_phys_addr_t base; + phys_addr_t base; size_t size; - ion_phys_addr_t align; + phys_addr_t align; void *priv; }; /** * struct ion_platform_data - array of platform heaps passed from board file - * @nr: number of structures in the array - * @heaps: array of platform_heap structions + * @nr: number of structures in the array + * @heaps: array of platform_heap structions * * Provided by the board file in the form of platform data to a platform device. */ @@ -72,182 +105,420 @@ struct ion_platform_data { struct ion_platform_heap *heaps; }; +struct ion_vma_list { + struct list_head list; + struct vm_area_struct *vma; +}; + +/** + * struct ion_buffer - metadata for a particular buffer + * @ref: reference count + * @node: node in the ion_device buffers tree + * @dev: back pointer to the ion_device + * @heap: back pointer to the heap the buffer came from + * @flags: buffer specific flags + * @private_flags: internal buffer specific flags + * @size: size of the buffer + * @priv_virt: private data to the buffer representable as + * a void * + * @lock: protects the buffers cnt fields + * @kmap_cnt: number of times the buffer is mapped to the kernel + * @vaddr: the kernel mapping if kmap_cnt is not zero + * @sg_table: the sg table for the buffer if dmap_cnt is not zero + * @vmas: list of vma's mapping this buffer + */ +struct ion_buffer { + union { + struct rb_node node; + struct list_head list; + }; + struct ion_device *dev; + struct ion_heap *heap; + unsigned long flags; + unsigned long private_flags; + size_t size; + void *priv_virt; + /* Protect ion buffer */ + struct mutex lock; + int kmap_cnt; + void *vaddr; + struct sg_table *sg_table; + struct list_head attachments; + struct list_head vmas; + char task_comm[TASK_COMM_LEN]; + pid_t pid; + char thread_comm[TASK_COMM_LEN]; + pid_t tid; +}; + +void ion_buffer_destroy(struct ion_buffer *buffer); + +/** + * struct ion_device - the metadata of the ion device node + * @dev: the actual misc device + * @buffers: an rb tree of all the existing buffers + * @buffer_lock: lock protecting the tree of buffers + * @lock: rwsem protecting the tree of heaps and clients + */ +struct ion_device { + struct miscdevice dev; + struct rb_root buffers; + /* buffer_lock used for adding and removing buffers */ + struct mutex buffer_lock; + struct rw_semaphore lock; + struct plist_head heaps; + struct dentry *debug_root; + struct dentry *heaps_debug_root; + int heap_cnt; +}; + /** - * ion_reserve() - reserve memory for ion heaps if applicable - * @data: platform data specifying starting physical address and - * size + * struct ion_heap_ops - ops to operate on a given heap + * @allocate: allocate memory + * @free: free memory + * @map_kernel map memory to the kernel + * @unmap_kernel unmap memory to the kernel + * @map_user map memory to userspace * - * Calls memblock reserve to set aside memory for heaps that are - * located at specific memory addresses or of specific sizes not - * managed by the kernel + * allocate, phys, and map_user return 0 on success, -errno on error. + * map_dma and map_kernel return pointer on success, ERR_PTR on + * error. @free will be called with ION_PRIV_FLAG_SHRINKER_FREE set in + * the buffer's private_flags when called from a shrinker. In that + * case, the pages being free'd must be truly free'd back to the + * system, not put in a page pool or otherwise cached. */ -void ion_reserve(struct ion_platform_data *data); +struct ion_heap_ops { + int (*allocate)(struct ion_heap *heap, + struct ion_buffer *buffer, unsigned long len, + unsigned long flags); + void (*free)(struct ion_buffer *buffer); + void * (*map_kernel)(struct ion_heap *heap, struct ion_buffer *buffer); + void (*unmap_kernel)(struct ion_heap *heap, struct ion_buffer *buffer); + int (*map_user)(struct ion_heap *mapper, struct ion_buffer *buffer, + struct vm_area_struct *vma); + int (*shrink)(struct ion_heap *heap, gfp_t gfp_mask, int nr_to_scan); +}; /** - * ion_client_create() - allocate a client and returns it - * @dev: the global ion device - * @name: used for debugging + * heap flags - flags between the heaps and core ion code */ -struct ion_client *ion_client_create(struct ion_device *dev, - const char *name); +#define ION_HEAP_FLAG_DEFER_FREE BIT(0) /** - * ion_client_create() - allocate a client and returns it + * private flags - flags internal to ion + */ +/* + * Buffer is being freed from a shrinker function. Skip any possible + * heap-specific caching mechanism (e.g. page pools). Guarantees that + * any buffer storage that came from the system allocator will be + * returned to the system allocator. + */ +#define ION_PRIV_FLAG_SHRINKER_FREE BIT(0) + +/** + * struct ion_heap - represents a heap in the system + * @node: rb node to put the heap on the device's tree of heaps + * @dev: back pointer to the ion_device + * @type: type of heap + * @ops: ops struct as above + * @flags: flags + * @id: id of heap, also indicates priority of this heap when + * allocating. These are specified by platform data and + * MUST be unique * @name: used for debugging + * @shrinker: a shrinker for the heap + * @priv: private heap data + * @free_list: free list head if deferred free is used + * @free_list_size size of the deferred free list in bytes + * @lock: protects the free list + * @waitqueue: queue to wait on from deferred free thread + * @task: task struct of deferred free thread + * @debug_show: called when heap debug file is read to add any + * heap specific debug info to output + * + * Represents a pool of memory from which buffers can be made. In some + * systems the only heap is regular system memory allocated via vmalloc. + * On others, some blocks might require large physically contiguous buffers + * that are allocated from a specially reserved heap. */ -struct ion_client *exynos_ion_client_create(const char *name); +struct ion_heap { + struct plist_node node; + struct ion_device *dev; + enum ion_heap_type type; + struct ion_heap_ops *ops; + unsigned long flags; + unsigned int id; + const char *name; + struct shrinker shrinker; + void *priv; + struct list_head free_list; + size_t free_list_size; + /* Protect the free list */ + spinlock_t free_lock; + wait_queue_head_t waitqueue; + struct task_struct *task; + atomic_long_t total_allocated; + atomic_long_t total_allocated_peak; + + int (*debug_show)(struct ion_heap *heap, struct seq_file *s, + void *unused); +}; /** - * ion_client_destroy() - free's a client and all it's handles - * @client: the client + * ion_buffer_cached - this ion buffer is cached + * @buffer: buffer * - * Free the provided client and all it's resources including - * any handles it is holding. + * indicates whether this ion buffer is cached */ -void ion_client_destroy(struct ion_client *client); +bool ion_buffer_cached(struct ion_buffer *buffer); /** - * ion_alloc - allocate ion memory - * @client: the client - * @len: size of the allocation - * @align: requested allocation alignment, lots of hardware blocks - * have alignment requirements of some kind - * @heap_id_mask: mask of heaps to allocate from, if multiple bits are set - * heaps will be tried in order from highest to lowest - * id - * @flags: heap flags, the low 16 bits are consumed by ion, the - * high 16 bits are passed on to the respective heap and - * can be heap custom + * ion_device_create - allocates and returns an ion device * - * Allocate memory in one of the heaps provided in heap mask and return - * an opaque handle to it. + * returns a valid device or -PTR_ERR */ -struct ion_handle *ion_alloc(struct ion_client *client, size_t len, - size_t align, unsigned int heap_id_mask, - unsigned int flags); +struct ion_device *ion_device_create(void); /** - * ion_free - free a handle - * @client: the client - * @handle: the handle to free + * ion_device_add_heap - adds a heap to the ion device + * @dev: the device + * @heap: the heap to add + */ +void ion_device_add_heap(struct ion_device *dev, struct ion_heap *heap); + +/** + * some helpers for common operations on buffers using the sg_table + * and vaddr fields + */ +void *ion_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer); +void ion_heap_unmap_kernel(struct ion_heap *heap, struct ion_buffer *buffer); +int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, + struct vm_area_struct *vma); +int ion_heap_buffer_zero(struct ion_buffer *buffer); +int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot); + +int ion_alloc_fd(size_t len, unsigned int heap_id_mask, unsigned int flags); + +/** + * ion_heap_init_shrinker + * @heap: the heap * - * Free the provided handle. + * If a heap sets the ION_HEAP_FLAG_DEFER_FREE flag or defines the shrink op + * this function will be called to setup a shrinker to shrink the freelists + * and call the heap's shrink op. */ -void ion_free(struct ion_client *client, struct ion_handle *handle); +int ion_heap_init_shrinker(struct ion_heap *heap); /** - * ion_phys - returns the physical address and len of a handle - * @client: the client - * @handle: the handle - * @addr: a pointer to put the address in - * @len: a pointer to put the length in + * ion_heap_init_deferred_free -- initialize deferred free functionality + * @heap: the heap * - * This function queries the heap for a particular handle to get the - * handle's physical address. It't output is only correct if - * a heap returns physically contiguous memory -- in other cases - * this api should not be implemented -- ion_sg_table should be used - * instead. Returns -EINVAL if the handle is invalid. This has - * no implications on the reference counting of the handle -- - * the returned value may not be valid if the caller is not - * holding a reference. + * If a heap sets the ION_HEAP_FLAG_DEFER_FREE flag this function will + * be called to setup deferred frees. Calls to free the buffer will + * return immediately and the actual free will occur some time later */ -int ion_phys(struct ion_client *client, struct ion_handle *handle, - ion_phys_addr_t *addr, size_t *len); +int ion_heap_init_deferred_free(struct ion_heap *heap); /** - * ion_map_dma - return an sg_table describing a handle - * @client: the client - * @handle: the handle + * ion_heap_freelist_add - add a buffer to the deferred free list + * @heap: the heap + * @buffer: the buffer * - * This function returns the sg_table describing - * a particular ion handle. + * Adds an item to the deferred freelist. */ -struct sg_table *ion_sg_table(struct ion_client *client, - struct ion_handle *handle); +void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer); /** - * ion_map_kernel - create mapping for the given handle - * @client: the client - * @handle: handle to map + * ion_heap_freelist_drain - drain the deferred free list + * @heap: the heap + * @size: amount of memory to drain in bytes * - * Map the given handle into the kernel and return a kernel address that - * can be used to access this address. + * Drains the indicated amount of memory from the deferred freelist immediately. + * Returns the total amount freed. The total freed may be higher depending + * on the size of the items in the list, or lower if there is insufficient + * total memory on the freelist. */ -void *ion_map_kernel(struct ion_client *client, struct ion_handle *handle); +size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size); /** - * ion_unmap_kernel() - destroy a kernel mapping for a handle - * @client: the client - * @handle: handle to unmap + * ion_heap_freelist_shrink - drain the deferred free + * list, skipping any heap-specific + * pooling or caching mechanisms + * + * @heap: the heap + * @size: amount of memory to drain in bytes + * + * Drains the indicated amount of memory from the deferred freelist immediately. + * Returns the total amount freed. The total freed may be higher depending + * on the size of the items in the list, or lower if there is insufficient + * total memory on the freelist. + * + * Unlike with @ion_heap_freelist_drain, don't put any pages back into + * page pools or otherwise cache the pages. Everything must be + * genuinely free'd back to the system. If you're free'ing from a + * shrinker you probably want to use this. Note that this relies on + * the heap.ops.free callback honoring the ION_PRIV_FLAG_SHRINKER_FREE + * flag. */ -void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle); +size_t ion_heap_freelist_shrink(struct ion_heap *heap, size_t size); /** - * ion_share_dma_buf() - share buffer as dma-buf - * @client: the client - * @handle: the handle + * ion_heap_freelist_size - returns the size of the freelist in bytes + * @heap: the heap */ -struct dma_buf *ion_share_dma_buf(struct ion_client *client, - struct ion_handle *handle); +size_t ion_heap_freelist_size(struct ion_heap *heap); /** - * ion_share_dma_buf_fd() - given an ion client, create a dma-buf fd - * @client: the client - * @handle: the handle + * functions for creating and destroying the built in ion heaps. + * architectures can add their own custom architecture specific + * heaps as appropriate. */ -int ion_share_dma_buf_fd(struct ion_client *client, struct ion_handle *handle); + +struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data); + +struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused); +#ifdef CONFIG_ION_RBIN_HEAP +struct ion_heap *ion_rbin_heap_create(struct ion_platform_heap *unused); +#endif +struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *heap); + +struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data); + +struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data); + +#ifdef CONFIG_CMA +struct ion_heap *ion_secure_cma_heap_create(struct ion_platform_heap *data); +void ion_secure_cma_heap_destroy(struct ion_heap *heap); + +struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data); +#else +static inline struct ion_heap + *ion_secure_cma_heap_create(struct ion_platform_heap *h) +{ + return NULL; +} + +static inline void ion_cma_heap_destroy(struct ion_heap *h) {} + +static inline struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *h) +{ + return NULL; +} +#endif + +struct ion_heap *ion_system_secure_heap_create(struct ion_platform_heap *heap); + +struct ion_heap *ion_cma_secure_heap_create(struct ion_platform_heap *heap); + +struct ion_heap * +ion_secure_carveout_heap_create(struct ion_platform_heap *heap); /** - * ion_import_dma_buf() - given an dma-buf fd from the ion exporter get handle - * @client: the client - * @fd: the dma-buf fd - * - * Given an dma-buf fd that was allocated through ion via ion_share_dma_buf, - * import that fd and return a handle representing it. If a dma-buf from - * another exporter is passed in this function will return ERR_PTR(-EINVAL) + * functions for creating and destroying a heap pool -- allows you + * to keep a pool of pre allocated memory to use from your heap. Keeping + * a pool of memory that is ready for dma, ie any cached mapping have been + * invalidated from the cache, provides a significant performance benefit on + * many systems */ -struct ion_handle *ion_import_dma_buf(struct ion_client *client, int fd); /** - * ion_cached_needsync_dmabuf() - check if a dmabuf is cacheable - * @dmabuf: a pointer to dma_buf + * struct ion_page_pool - pagepool struct + * @high_count: number of highmem items in the pool + * @low_count: number of lowmem items in the pool + * @count: total number of pages/items in the pool + * @high_items: list of highmem items + * @low_items: list of lowmem items + * @mutex: lock protecting this struct and especially the count + * item list + * @gfp_mask: gfp_mask to use from alloc + * @order: order of pages in the pool + * @list: plist node for list of pools + * @cached: it's cached pool or not + * @heap: ion heap associated to this pool * - * Given a dma-buf that is exported by ION, check if the buffer is allocated - * with ION_FLAG_CACHED and ION_FLAG_CACHED_NEED_SYNC. If the flags are set - * the function returns 1. If it is unset, 0. If the given dmabuf is not - * exported by ION, -error is returned. + * Allows you to keep a pool of pre allocated pages to use from your heap. + * Keeping a pool of pages that is ready for dma, ie any cached mapping have + * been invalidated from the cache, provides a significant performance benefit + * on many systems */ -int ion_cached_needsync_dmabuf(struct dma_buf *dmabuf); +struct ion_page_pool { + int high_count; + int low_count; + atomic_t count; + bool cached; + struct list_head high_items; + struct list_head low_items; + ktime_t last_low_watermark_ktime; + /* Protect the pool */ + struct mutex mutex; + gfp_t gfp_mask; + unsigned int order; + struct plist_node list; + struct device *dev; +}; -/** - * ion_may_hwrender_dmabuf() - check if a dmabuf set ION_FLAG_MAY_HWRENDER - * @dmabuf: a pointer to dma_buf +struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order, + bool cached); +void ion_page_pool_refill(struct ion_page_pool *pool); +void ion_page_pool_destroy(struct ion_page_pool *pool); +struct page *ion_page_pool_only_alloc(struct ion_page_pool *pool); +struct page *ion_page_pool_alloc(struct ion_page_pool *a, bool *from_pool); +void ion_page_pool_free(struct ion_page_pool *pool, struct page *page); + +struct ion_heap *get_ion_heap(int heap_id); +struct page *ion_page_pool_alloc_pool_only(struct ion_page_pool *a); +void ion_page_pool_free_immediate(struct ion_page_pool *pool, + struct page *page); +int ion_page_pool_total(struct ion_page_pool *pool, bool high); +size_t ion_system_heap_secure_page_pool_total(struct ion_heap *heap, int vmid); + +/** ion_page_pool_shrink - shrinks the size of the memory cached in the pool + * @pool: the pool + * @gfp_mask: the memory type to reclaim + * @nr_to_scan: number of items to shrink in pages * - * Given a dma-buf that is exported by ION, check if the buffer is allocated - * with ION_FLAG_MAY_HWRENDER. If the flags are set the function returns true. - * If it is unset, false. If the given dmabuf is not exported by ION, - * false is returned. + * returns the number of items freed in pages */ -bool ion_may_hwrender_dmabuf(struct dma_buf *dmabuf); +int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, + int nr_to_scan); /** - * ion_may_hwrender_handle() - check if a handle set ION_FLAG_MAY_HWRENDER - * @client: the client - * @handle: the handle - * - * Given a handle, check if the buffer is allocated with ION_FLAG_MAY_HWRENDER. - * If the flags are set the function returns true. If it is unset, false. - * If the given handle is not valid, false is returned. + * ion_pages_sync_for_device - cache flush pages for use with the specified + * device + * @dev: the device the pages will be used with + * @page: the first page to be flushed + * @size: size in bytes of region to be flushed + * @dir: direction of dma transfer */ -bool ion_may_hwrender_handle(struct ion_client *client, struct ion_handle *handle); +void ion_pages_sync_for_device(struct device *dev, struct page *page, + size_t size, enum dma_data_direction dir); -#include -#include +int ion_walk_heaps(int heap_id, enum ion_heap_type type, void *data, + int (*f)(struct ion_heap *heap, void *data)); + +long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); + +int ion_query_heaps(struct ion_heap_query *query); + +unsigned int get_ion_system_heap_id(void); + +static __always_inline int get_pool_fillmark(struct ion_page_pool *pool) +{ + return ION_POOL_FILL_MARK / (PAGE_SIZE << pool->order); +} + +static __always_inline int get_pool_lowmark(struct ion_page_pool *pool) +{ + return ION_POOL_LOW_MARK / (PAGE_SIZE << pool->order); +} -dma_addr_t ion_iovmm_map(struct dma_buf_attachment *attachment, - off_t offset, size_t size, - enum dma_data_direction direction, int prop); -void ion_iovmm_unmap(struct dma_buf_attachment *attachment, dma_addr_t iova); -bool ion_is_heap_available(struct ion_heap *heap, unsigned long flags, void *data); +static __always_inline bool pool_count_below_lowmark(struct ion_page_pool *pool) +{ + return atomic_read(&pool->count) < get_pool_lowmark(pool); +} -#endif /* _LINUX_ION_H */ +static __always_inline bool pool_fillmark_reached(struct ion_page_pool *pool) +{ + return atomic_read(&pool->count) >= get_pool_fillmark(pool); +} +#endif /* _ION_H */ diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c old mode 100755 new mode 100644 index ae46bf6b..78075eef --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1,26 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * thermal.c - Generic Thermal Management Sysfs support. * * Copyright (C) 2008 Intel Corp * Copyright (C) 2008 Zhang Rui * Copyright (C) 2008 Sujith Thomas - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -45,13 +29,21 @@ #include "thermal_core.h" #include "thermal_hwmon.h" +#if defined(CONFIG_SEC_PM) +void *thermal_ipc_log; + +/* cooling device state */ +static struct delayed_work cdev_print_work; +#endif + MODULE_AUTHOR("Zhang Rui"); MODULE_DESCRIPTION("Generic thermal management sysfs support"); MODULE_LICENSE("GPL v2"); -static DEFINE_IDR(thermal_tz_idr); -static DEFINE_IDR(thermal_cdev_idr); -static DEFINE_MUTEX(thermal_idr_lock); +#define THERMAL_MAX_ACTIVE 16 + +static DEFINE_IDA(thermal_tz_ida); +static DEFINE_IDA(thermal_cdev_ida); static LIST_HEAD(thermal_tz_list); static LIST_HEAD(thermal_cdev_list); @@ -59,20 +51,22 @@ static LIST_HEAD(thermal_governor_list); static DEFINE_MUTEX(thermal_list_lock); static DEFINE_MUTEX(thermal_governor_lock); +static DEFINE_MUTEX(poweroff_lock); static atomic_t in_suspend; - -#ifdef CONFIG_SCHED_HMP -#define BOUNDED_CPU 1 -static void start_poll_queue(struct thermal_zone_device *tz, int delay) -{ - mod_delayed_work_on(tz->poll_queue_cpu, system_freezable_wq, &tz->poll_queue, - msecs_to_jiffies(delay)); -} -#endif +static bool power_off_triggered; static struct thermal_governor *def_governor; +static struct workqueue_struct *thermal_passive_wq; + +/* + * Governor section: set of functions to handle thermal governors + * + * Functions to help in the life cycle of thermal governors within + * the thermal core and by the thermal governor code. + */ + static struct thermal_governor *__find_governor(const char *name) { struct thermal_governor *pos; @@ -151,11 +145,16 @@ int thermal_register_governor(struct thermal_governor *governor) mutex_lock(&thermal_governor_lock); err = -EBUSY; - if (__find_governor(governor->name) == NULL) { + if (!__find_governor(governor->name)) { + bool match_default; + err = 0; list_add(&governor->governor_list, &thermal_governor_list); - if (!def_governor && !strncmp(governor->name, - DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH)) + match_default = !strncmp(governor->name, + DEFAULT_THERMAL_GOVERNOR, + THERMAL_NAME_LENGTH); + + if (!def_governor && match_default) def_governor = governor; } @@ -197,14 +196,14 @@ void thermal_unregister_governor(struct thermal_governor *governor) mutex_lock(&thermal_governor_lock); - if (__find_governor(governor->name) == NULL) + if (!__find_governor(governor->name)) goto exit; mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) { if (!strncasecmp(pos->governor->name, governor->name, - THERMAL_NAME_LENGTH)) + THERMAL_NAME_LENGTH)) thermal_set_governor(pos, NULL); } @@ -212,212 +211,107 @@ void thermal_unregister_governor(struct thermal_governor *governor) list_del(&governor->governor_list); exit: mutex_unlock(&thermal_governor_lock); - return; -} - -static int get_idr(struct idr *idr, struct mutex *lock, int *id) -{ - int ret; - - if (lock) - mutex_lock(lock); - ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); - if (lock) - mutex_unlock(lock); - if (unlikely(ret < 0)) - return ret; - *id = ret; - return 0; -} - -static void release_idr(struct idr *idr, struct mutex *lock, int id) -{ - if (lock) - mutex_lock(lock); - idr_remove(idr, id); - if (lock) - mutex_unlock(lock); -} - -int get_tz_trend(struct thermal_zone_device *tz, int trip) -{ - enum thermal_trend trend; - - if (tz->emul_temperature || !tz->ops->get_trend || - tz->ops->get_trend(tz, trip, &trend)) { - if (tz->temperature > tz->last_temperature) - trend = THERMAL_TREND_RAISING; - else if (tz->temperature < tz->last_temperature) - trend = THERMAL_TREND_DROPPING; - else - trend = THERMAL_TREND_STABLE; - } - - return trend; } -EXPORT_SYMBOL(get_tz_trend); -struct thermal_instance *get_thermal_instance(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int trip) +int thermal_zone_device_set_policy(struct thermal_zone_device *tz, + char *policy) { - struct thermal_instance *pos = NULL; - struct thermal_instance *target_instance = NULL; + struct thermal_governor *gov; + int ret = -EINVAL; + mutex_lock(&thermal_governor_lock); mutex_lock(&tz->lock); - mutex_lock(&cdev->lock); - list_for_each_entry(pos, &tz->thermal_instances, tz_node) { - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { - target_instance = pos; - break; - } - } + gov = __find_governor(strim(policy)); + if (!gov) + goto exit; - mutex_unlock(&cdev->lock); - mutex_unlock(&tz->lock); + ret = thermal_set_governor(tz, gov); - return target_instance; -} -EXPORT_SYMBOL(get_thermal_instance); +exit: + mutex_unlock(&tz->lock); + mutex_unlock(&thermal_governor_lock); -static void print_bind_err_msg(struct thermal_zone_device *tz, - struct thermal_cooling_device *cdev, int ret) -{ - dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", - tz->type, cdev->type, ret); + return ret; } -static void __bind(struct thermal_zone_device *tz, int mask, - struct thermal_cooling_device *cdev, - unsigned long *limits, - unsigned int weight) +int thermal_build_list_of_policies(char *buf) { - int i, ret; + struct thermal_governor *pos; + ssize_t count = 0; + ssize_t size = PAGE_SIZE; - for (i = 0; i < tz->trips; i++) { - if (mask & (1 << i)) { - unsigned long upper, lower; + mutex_lock(&thermal_governor_lock); - upper = THERMAL_NO_LIMIT; - lower = THERMAL_NO_LIMIT; - if (limits) { - lower = limits[i * 2]; - upper = limits[i * 2 + 1]; - } - ret = thermal_zone_bind_cooling_device(tz, i, cdev, - upper, lower, - weight); - if (ret) - print_bind_err_msg(tz, cdev, ret); - } + list_for_each_entry(pos, &thermal_governor_list, governor_list) { + size = PAGE_SIZE - count; + count += scnprintf(buf + count, size, "%s ", pos->name); } -} + count += scnprintf(buf + count, size, "\n"); -static void __unbind(struct thermal_zone_device *tz, int mask, - struct thermal_cooling_device *cdev) -{ - int i; + mutex_unlock(&thermal_governor_lock); - for (i = 0; i < tz->trips; i++) - if (mask & (1 << i)) - thermal_zone_unbind_cooling_device(tz, i, cdev); + return count; } -static void bind_cdev(struct thermal_cooling_device *cdev) +static int __init thermal_register_governors(void) { - int i, ret; - const struct thermal_zone_params *tzp; - struct thermal_zone_device *pos = NULL; + int result; - mutex_lock(&thermal_list_lock); + result = thermal_gov_step_wise_register(); + if (result) + return result; - list_for_each_entry(pos, &thermal_tz_list, node) { - if (!pos->tzp && !pos->ops->bind) - continue; + result = thermal_gov_fair_share_register(); + if (result) + return result; - if (pos->ops->bind) { - ret = pos->ops->bind(pos, cdev); - if (ret) - print_bind_err_msg(pos, cdev, ret); - continue; - } + result = thermal_gov_bang_bang_register(); + if (result) + return result; - tzp = pos->tzp; - if (!tzp || !tzp->tbp) - continue; + result = thermal_gov_user_space_register(); + if (result) + return result; - for (i = 0; i < tzp->num_tbps; i++) { - if (tzp->tbp[i].cdev || !tzp->tbp[i].match) - continue; - if (tzp->tbp[i].match(pos, cdev)) - continue; - tzp->tbp[i].cdev = cdev; - __bind(pos, tzp->tbp[i].trip_mask, cdev, - tzp->tbp[i].binding_limits, - tzp->tbp[i].weight); - } - } + result = thermal_gov_low_limits_register(); + if (result) + return result; - mutex_unlock(&thermal_list_lock); + return thermal_gov_power_allocator_register(); } -static void bind_tz(struct thermal_zone_device *tz) +static void thermal_unregister_governors(void) { - int i, ret; - struct thermal_cooling_device *pos = NULL; - const struct thermal_zone_params *tzp = tz->tzp; - - if (!tzp && !tz->ops->bind) - return; - - mutex_lock(&thermal_list_lock); - - /* If there is ops->bind, try to use ops->bind */ - if (tz->ops->bind) { - list_for_each_entry(pos, &thermal_cdev_list, node) { - ret = tz->ops->bind(tz, pos); - if (ret) - print_bind_err_msg(tz, pos, ret); - } - goto exit; - } - - if (!tzp || !tzp->tbp) - goto exit; - - list_for_each_entry(pos, &thermal_cdev_list, node) { - for (i = 0; i < tzp->num_tbps; i++) { - if (tzp->tbp[i].cdev || !tzp->tbp[i].match) - continue; - if (tzp->tbp[i].match(tz, pos)) - continue; - tzp->tbp[i].cdev = pos; - __bind(tz, tzp->tbp[i].trip_mask, pos, - tzp->tbp[i].binding_limits, - tzp->tbp[i].weight); - } - } -exit: - mutex_unlock(&thermal_list_lock); + thermal_gov_step_wise_unregister(); + thermal_gov_fair_share_unregister(); + thermal_gov_bang_bang_unregister(); + thermal_gov_user_space_unregister(); + thermal_gov_low_limits_unregister(); + thermal_gov_power_allocator_unregister(); } -static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, +/* + * Zone update section: main control loop applied to each zone while monitoring + * + * in polling mode. The monitoring is done using a workqueue. + * Same update may be done on a zone by calling thermal_zone_device_update(). + * + * An update means: + * - Non-critical trips will invoke the governor responsible for that zone; + * - Hot trips will produce a notification to userspace; + * - Critical trip point will cause a system shutdown. + */ +static void thermal_zone_device_set_polling(struct workqueue_struct *queue, + struct thermal_zone_device *tz, int delay) { if (delay > 1000) -#ifdef CONFIG_SCHED_HMP - start_poll_queue(tz, delay); -#else - mod_delayed_work(system_freezable_wq, &tz->poll_queue, + mod_delayed_work(queue, &tz->poll_queue, round_jiffies(msecs_to_jiffies(delay))); -#endif else if (delay) -#ifdef CONFIG_SCHED_HMP - start_poll_queue(tz, delay); -#else - mod_delayed_work(system_freezable_wq, &tz->poll_queue, + mod_delayed_work(queue, &tz->poll_queue, msecs_to_jiffies(delay)); -#endif else cancel_delayed_work(&tz->poll_queue); } @@ -427,24 +321,76 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz) mutex_lock(&tz->lock); if (tz->passive) - thermal_zone_device_set_polling(tz, tz->passive_delay); + thermal_zone_device_set_polling(thermal_passive_wq, + tz, tz->passive_delay); else if (tz->polling_delay) - thermal_zone_device_set_polling(tz, tz->polling_delay); + thermal_zone_device_set_polling( + system_freezable_power_efficient_wq, + tz, tz->polling_delay); else - thermal_zone_device_set_polling(tz, 0); + thermal_zone_device_set_polling(NULL, tz, 0); mutex_unlock(&tz->lock); } static void handle_non_critical_trips(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type) + int trip, + enum thermal_trip_type trip_type) { tz->governor ? tz->governor->throttle(tz, trip) : def_governor->throttle(tz, trip); } +/** + * thermal_emergency_poweroff_func - emergency poweroff work after a known delay + * @work: work_struct associated with the emergency poweroff function + * + * This function is called in very critical situations to force + * a kernel poweroff after a configurable timeout value. + */ +static void thermal_emergency_poweroff_func(struct work_struct *work) +{ + /* + * We have reached here after the emergency thermal shutdown + * Waiting period has expired. This means orderly_poweroff has + * not been able to shut off the system for some reason. + * Try to shut down the system immediately using kernel_power_off + * if populated + */ + WARN(1, "Attempting kernel_power_off: Temperature too high\n"); + kernel_power_off(); + + /* + * Worst of the worst case trigger emergency restart + */ + WARN(1, "Attempting emergency_restart: Temperature too high\n"); + emergency_restart(); +} + +static DECLARE_DELAYED_WORK(thermal_emergency_poweroff_work, + thermal_emergency_poweroff_func); + +/** + * thermal_emergency_poweroff - Trigger an emergency system poweroff + * + * This may be called from any critical situation to trigger a system shutdown + * after a known period of time. By default this is not scheduled. + */ +static void thermal_emergency_poweroff(void) +{ + int poweroff_delay_ms = CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS; + /* + * poweroff_delay_ms must be a carefully profiled positive value. + * Its a must for thermal_emergency_poweroff_work to be scheduled + */ + if (poweroff_delay_ms <= 0) + return; + schedule_delayed_work(&thermal_emergency_poweroff_work, + msecs_to_jiffies(poweroff_delay_ms)); +} + static void handle_critical_trips(struct thermal_zone_device *tz, - int trip, enum thermal_trip_type trip_type) + int trip, enum thermal_trip_type trip_type) { int trip_temp; @@ -454,93 +400,37 @@ static void handle_critical_trips(struct thermal_zone_device *tz, if (trip_temp <= 0 || tz->temperature < trip_temp) return; - trace_thermal_zone_trip(tz, trip, trip_type); + trace_thermal_zone_trip(tz, trip, trip_type, true); if (tz->ops->notify) tz->ops->notify(tz, trip, trip_type); if (trip_type == THERMAL_TRIP_CRITICAL) { dev_emerg(&tz->device, - "critical temperature reached(%d C),shutting down\n", + "critical temperature reached (%d C), shutting down\n", tz->temperature / 1000); - orderly_poweroff(true); + mutex_lock(&poweroff_lock); + if (!power_off_triggered) { + /* + * Queue a backup emergency shutdown in the event of + * orderly_poweroff failure + */ + thermal_emergency_poweroff(); + orderly_poweroff(true); + power_off_triggered = true; + } + mutex_unlock(&poweroff_lock); } } -#ifdef CONFIG_SEC_DEBUG_HW_PARAM -#define APO_THROTTLE_TEMP 81000 -static bool period_check; -static bool is_apo_check; -static int trip_temp; -static u64 last_time[THERMAL_ZONE_MAX], curr_time[THERMAL_ZONE_MAX]; -static u64 last_apo, curr_apo; -static enum thermal_trip_type result_type; -struct thermal_data_devices thermal_data_info[THERMAL_ZONE_MAX]; -#endif - static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) { enum thermal_trip_type type; -#ifdef CONFIG_SEC_DEBUG_HW_PARAM - int tid = tz->id; -#endif /* Ignore disabled trip points */ if (test_bit(trip, &tz->trips_disabled)) return; -#ifdef CONFIG_SEC_DEBUG_HW_PARAM - if (trip == 0) { - period_check = false; - result_type = THERMAL_TRIP_ACTIVE; - } - - if (tz->temperature > thermal_data_info[tid].max_temp) - thermal_data_info[tid].max_temp = tz->temperature; - - tz->ops->get_trip_temp(tz, trip, &trip_temp); - if (tz->temperature > trip_temp) { - tz->ops->get_trip_type(tz, trip + 1, &result_type); - } - else { - if (!period_check) { - curr_time[tid] = ktime_to_ns(ktime_get()) / 1000000; - if(last_time[tid]) { - if(result_type == THERMAL_TRIP_ACTIVE) - thermal_data_info[tid].times[ACTIVE_TIMES] += - (curr_time[tid] - last_time[tid]); - else if (result_type == THERMAL_TRIP_PASSIVE) - thermal_data_info[tid].times[PASSIVE_TIMES] += - (curr_time[tid] - last_time[tid]); - else if (result_type == THERMAL_TRIP_HOT) - thermal_data_info[tid].times[HOT_TIMES] += - (curr_time[tid] - last_time[tid]); - - period_check = true; - } - - if (tid == THERMAL_ZONE_APOLLO) { - if (tz->temperature >= APO_THROTTLE_TEMP) { - if (!is_apo_check) { - is_apo_check = true; - last_apo = ktime_to_ns(ktime_get()) / 1000000; - } - } - else { - if (is_apo_check) { - curr_apo = ktime_to_ns(ktime_get()) / 1000000; - thermal_data_info[tid].hotplug_out += - (curr_apo - last_apo); - is_apo_check = false; - } - } - } - - last_time[tid] = curr_time[tid]; - } - } -#endif - tz->ops->get_trip_type(tz, trip, &type); if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT) @@ -552,70 +442,29 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) * So, start monitoring again. */ monitor_thermal_zone(tz); + trace_thermal_handle_trip(tz, trip); } -/** - * thermal_zone_get_temp() - returns the temperature of a thermal zone - * @tz: a valid pointer to a struct thermal_zone_device - * @temp: a valid pointer to where to store the resulting temperature. - * - * When a valid thermal zone reference is passed, it will fetch its - * temperature and fill @temp. - * - * Return: On success returns 0, an error code otherwise - */ -int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp) +static void store_temperature(struct thermal_zone_device *tz, int temp) { - int ret = -EINVAL; - int count; - int crit_temp = INT_MAX; - enum thermal_trip_type type; - - if (!tz || IS_ERR(tz) || !tz->ops->get_temp) - goto exit; - mutex_lock(&tz->lock); - - ret = tz->ops->get_temp(tz, temp); - - if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) { - for (count = 0; count < tz->trips; count++) { - ret = tz->ops->get_trip_type(tz, count, &type); - if (!ret && type == THERMAL_TRIP_CRITICAL) { - ret = tz->ops->get_trip_temp(tz, count, - &crit_temp); - break; - } - } - - /* - * Only allow emulating a temperature when the real temperature - * is below the critical temperature so that the emulation code - * cannot hide critical conditions. - */ - if (!ret && *temp < crit_temp) - *temp = tz->emul_temperature; - } - + tz->last_temperature = tz->temperature; + tz->temperature = temp; mutex_unlock(&tz->lock); -exit: - return ret; -} -EXPORT_SYMBOL_GPL(thermal_zone_get_temp); -#ifdef CONFIG_SEC_PM_DEBUG -#define TEMP_NORMAL_COUNT 500 -#define TEMP_HOT_COUNT 100 -#define TEMP_THRESHOLD 76000 -#endif + trace_thermal_temperature(tz); + if (tz->last_temperature == THERMAL_TEMP_INVALID || + tz->last_temperature == THERMAL_TEMP_INVALID_LOW) + dev_dbg(&tz->device, "last_temperature N/A, current_temperature=%d\n", + tz->temperature); + else + dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", + tz->last_temperature, tz->temperature); +} static void update_temperature(struct thermal_zone_device *tz) { int temp, ret; -#ifdef CONFIG_SEC_PM_DEBUG - static int count = 1; - int count_limit; -#endif ret = thermal_zone_get_temp(tz, &temp); if (ret) { @@ -625,511 +474,100 @@ static void update_temperature(struct thermal_zone_device *tz) ret); return; } - - mutex_lock(&tz->lock); - tz->last_temperature = tz->temperature; - tz->temperature = temp; - mutex_unlock(&tz->lock); - - trace_thermal_temperature(tz); - if (tz->last_temperature == THERMAL_TEMP_INVALID) - dev_dbg(&tz->device, "last_temperature N/A, current_temperature=%d\n", - tz->temperature); - else - dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", - tz->last_temperature, tz->temperature); - -#ifdef CONFIG_SEC_PM_DEBUG - if (tz->temperature >= TEMP_THRESHOLD) - count_limit = TEMP_HOT_COUNT; - else - count_limit = TEMP_NORMAL_COUNT; - - if (count++ >= count_limit) { - count = 1; - dev_info(&tz->device, "[TMU] last_temperature=%d, current_temperature=%d\n", - tz->last_temperature, tz->temperature); - } -#endif + store_temperature(tz, temp); } -static void thermal_zone_device_reset(struct thermal_zone_device *tz) +static void thermal_zone_device_init(struct thermal_zone_device *tz) { struct thermal_instance *pos; - tz->temperature = THERMAL_TEMP_INVALID; - tz->passive = 0; list_for_each_entry(pos, &tz->thermal_instances, tz_node) pos->initialized = false; } -void thermal_zone_device_update(struct thermal_zone_device *tz) -{ - int count; - enum thermal_device_mode mode; - - if (atomic_read(&in_suspend)) - return; - - if (!tz->ops->get_temp || !tz->ops->get_mode) - return; - - tz->ops->get_mode(tz, &mode); - - if (mode == THERMAL_DEVICE_ENABLED) { - update_temperature(tz); - - for (count = 0; count < tz->trips; count++) - handle_thermal_trip(tz, count); - - if (tz->ops->throttle_hotplug) - tz->ops->throttle_hotplug(tz); - } -} -EXPORT_SYMBOL_GPL(thermal_zone_device_update); - -static void thermal_zone_device_check(struct work_struct *work) -{ - struct thermal_zone_device *tz = container_of(work, struct - thermal_zone_device, - poll_queue.work); - thermal_zone_device_update(tz); -} - -/* sys I/F for thermal zone */ - -#define to_thermal_zone(_dev) \ - container_of(_dev, struct thermal_zone_device, device) - -static ssize_t -type_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - - return sprintf(buf, "%s\n", tz->type); -} - -static ssize_t -temp_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int temperature, ret; - - ret = thermal_zone_get_temp(tz, &temperature); - - if (ret) - return ret; - - return sprintf(buf, "%d\n", temperature); -} - -static ssize_t -mode_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - enum thermal_device_mode mode; - int result; - - if (!tz->ops->get_mode) - return -EPERM; - - result = tz->ops->get_mode(tz, &mode); - if (result) - return result; - - return sprintf(buf, "%s\n", mode == THERMAL_DEVICE_ENABLED ? "enabled" - : "disabled"); -} - -static ssize_t -mode_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int result; - - if (!tz->ops->set_mode) - return -EPERM; - - if (!strncmp(buf, "enabled", sizeof("enabled") - 1)) - result = tz->ops->set_mode(tz, THERMAL_DEVICE_ENABLED); - else if (!strncmp(buf, "disabled", sizeof("disabled") - 1)) - result = tz->ops->set_mode(tz, THERMAL_DEVICE_DISABLED); - else - result = -EINVAL; - - if (result) - return result; - - return count; -} - -static ssize_t -trip_point_type_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - enum thermal_trip_type type; - int trip, result; - - if (!tz->ops->get_trip_type) - return -EPERM; - - if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) - return -EINVAL; - - result = tz->ops->get_trip_type(tz, trip, &type); - if (result) - return result; - - switch (type) { - case THERMAL_TRIP_CRITICAL: - return sprintf(buf, "critical\n"); - case THERMAL_TRIP_HOT: - return sprintf(buf, "hot\n"); - case THERMAL_TRIP_PASSIVE: - return sprintf(buf, "passive\n"); - case THERMAL_TRIP_ACTIVE: - return sprintf(buf, "active\n"); - default: - return sprintf(buf, "unknown\n"); - } -} - -static ssize_t -trip_point_temp_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip, ret; - unsigned long temperature; - - if (!tz->ops->set_trip_temp) - return -EPERM; - - if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip)) - return -EINVAL; - - if (kstrtoul(buf, 10, &temperature)) - return -EINVAL; - - ret = tz->ops->set_trip_temp(tz, trip, temperature); - - return ret ? ret : count; -} - -static ssize_t -trip_point_temp_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip, ret; - int temperature; - - if (!tz->ops->get_trip_temp) - return -EPERM; - - if (!sscanf(attr->attr.name, "trip_point_%d_temp", &trip)) - return -EINVAL; - - ret = tz->ops->get_trip_temp(tz, trip, &temperature); - - if (ret) - return ret; - - return sprintf(buf, "%d\n", temperature); -} - -static ssize_t -trip_point_hyst_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip, ret; - int temperature; - - if (!tz->ops->set_trip_hyst) - return -EPERM; - - if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip)) - return -EINVAL; - - if (kstrtoint(buf, 10, &temperature)) - return -EINVAL; - - /* - * We are not doing any check on the 'temperature' value - * here. The driver implementing 'set_trip_hyst' has to - * take care of this. - */ - ret = tz->ops->set_trip_hyst(tz, trip, temperature); - - return ret ? ret : count; -} - -static ssize_t -trip_point_hyst_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int trip, ret; - int temperature; - - if (!tz->ops->get_trip_hyst) - return -EPERM; - - if (!sscanf(attr->attr.name, "trip_point_%d_hyst", &trip)) - return -EINVAL; - - ret = tz->ops->get_trip_hyst(tz, trip, &temperature); - - return ret ? ret : sprintf(buf, "%d\n", temperature); -} - -static ssize_t -passive_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - struct thermal_cooling_device *cdev = NULL; - int state; - - if (!sscanf(buf, "%d\n", &state)) - return -EINVAL; - - /* sanity check: values below 1000 millicelcius don't make sense - * and can cause the system to go into a thermal heart attack - */ - if (state && state < 1000) - return -EINVAL; - - if (state && !tz->forced_passive) { - mutex_lock(&thermal_list_lock); - list_for_each_entry(cdev, &thermal_cdev_list, node) { - if (!strncmp("Processor", cdev->type, - sizeof("Processor"))) - thermal_zone_bind_cooling_device(tz, - THERMAL_TRIPS_NONE, cdev, - THERMAL_NO_LIMIT, - THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); - } - mutex_unlock(&thermal_list_lock); - if (!tz->passive_delay) - tz->passive_delay = 1000; - } else if (!state && tz->forced_passive) { - mutex_lock(&thermal_list_lock); - list_for_each_entry(cdev, &thermal_cdev_list, node) { - if (!strncmp("Processor", cdev->type, - sizeof("Processor"))) - thermal_zone_unbind_cooling_device(tz, - THERMAL_TRIPS_NONE, - cdev); - } - mutex_unlock(&thermal_list_lock); - tz->passive_delay = 0; - } - - tz->forced_passive = state; - - thermal_zone_device_update(tz); - - return count; -} - -static ssize_t -passive_show(struct device *dev, struct device_attribute *attr, - char *buf) +static void thermal_zone_device_reset(struct thermal_zone_device *tz) { - struct thermal_zone_device *tz = to_thermal_zone(dev); - - return sprintf(buf, "%d\n", tz->forced_passive); + tz->passive = 0; + thermal_zone_device_init(tz); } -static ssize_t -policy_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +void thermal_zone_device_update_temp(struct thermal_zone_device *tz, + enum thermal_notify_event event, int temp) { - int ret = -EINVAL; - struct thermal_zone_device *tz = to_thermal_zone(dev); - struct thermal_governor *gov; - char name[THERMAL_NAME_LENGTH]; + int count; - snprintf(name, sizeof(name), "%s", buf); + if (atomic_read(&in_suspend) && (!tz->ops->is_wakeable || + !(tz->ops->is_wakeable(tz)))) + return; - mutex_lock(&thermal_governor_lock); - mutex_lock(&tz->lock); + trace_thermal_device_update(tz, event); + store_temperature(tz, temp); - gov = __find_governor(strim(name)); - if (!gov) - goto exit; + thermal_zone_set_trips(tz); - ret = thermal_set_governor(tz, gov); - if (!ret) - ret = count; + tz->notify_event = event; -exit: - mutex_unlock(&tz->lock); - mutex_unlock(&thermal_governor_lock); - return ret; -} - -static ssize_t -policy_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - - return sprintf(buf, "%s\n", tz->governor->name); + for (count = 0; count < tz->trips; count++) + handle_thermal_trip(tz, count); } +EXPORT_SYMBOL(thermal_zone_device_update_temp); -static ssize_t -available_policies_show(struct device *dev, struct device_attribute *devattr, - char *buf) +void thermal_zone_device_update(struct thermal_zone_device *tz, + enum thermal_notify_event event) { - struct thermal_governor *pos; - ssize_t count = 0; - ssize_t size = PAGE_SIZE; - - mutex_lock(&thermal_governor_lock); - - list_for_each_entry(pos, &thermal_governor_list, governor_list) { - size = PAGE_SIZE - count; - count += scnprintf(buf + count, size, "%s ", pos->name); - } - count += scnprintf(buf + count, size, "\n"); - - mutex_unlock(&thermal_governor_lock); + int count; - return count; -} + if (atomic_read(&in_suspend) && (!tz->ops->is_wakeable || + !(tz->ops->is_wakeable(tz)))) + return; -static ssize_t -emul_temp_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_zone_device *tz = to_thermal_zone(dev); - int ret = 0; - unsigned long temperature; + if (!tz->ops->get_temp) + return; - if (kstrtoul(buf, 10, &temperature)) - return -EINVAL; + trace_thermal_device_update(tz, event); + update_temperature(tz); - if (!tz->ops->set_emul_temp) { - mutex_lock(&tz->lock); - tz->emul_temperature = temperature; - mutex_unlock(&tz->lock); - } else { - ret = tz->ops->set_emul_temp(tz, temperature); - } + thermal_zone_set_trips(tz); - if (!ret) - thermal_zone_device_update(tz); + tz->notify_event = event; - return ret ? ret : count; + for (count = 0; count < tz->trips; count++) + handle_thermal_trip(tz, count); } -static DEVICE_ATTR(emul_temp, S_IWUSR, NULL, emul_temp_store); +EXPORT_SYMBOL_GPL(thermal_zone_device_update); -static ssize_t -sustainable_power_show(struct device *dev, struct device_attribute *devattr, - char *buf) +/** + * thermal_notify_framework - Sensor drivers use this API to notify framework + * @tz: thermal zone device + * @trip: indicates which trip point has been crossed + * + * This function handles the trip events from sensor drivers. It starts + * throttling the cooling devices according to the policy configured. + * For CRITICAL and HOT trip points, this notifies the respective drivers, + * and does actual throttling for other trip points i.e ACTIVE and PASSIVE. + * The throttling policy is based on the configured platform data; if no + * platform data is provided, this uses the step_wise throttling policy. + */ +void thermal_notify_framework(struct thermal_zone_device *tz, int trip) { - struct thermal_zone_device *tz = to_thermal_zone(dev); - - if (tz->tzp) - return sprintf(buf, "%u\n", tz->tzp->sustainable_power); - else - return -EIO; + handle_thermal_trip(tz, trip); } +EXPORT_SYMBOL_GPL(thermal_notify_framework); -static ssize_t -sustainable_power_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static void thermal_zone_device_check(struct work_struct *work) { - struct thermal_zone_device *tz = to_thermal_zone(dev); - u32 sustainable_power; - - if (!tz->tzp) - return -EIO; - - if (kstrtou32(buf, 10, &sustainable_power)) - return -EINVAL; - - tz->tzp->sustainable_power = sustainable_power; - - return count; + struct thermal_zone_device *tz = container_of(work, struct + thermal_zone_device, + poll_queue.work); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } -static DEVICE_ATTR(sustainable_power, S_IWUSR | S_IRUGO, sustainable_power_show, - sustainable_power_store); - -#define create_s32_tzp_attr(name) \ - static ssize_t \ - name##_show(struct device *dev, struct device_attribute *devattr, \ - char *buf) \ - { \ - struct thermal_zone_device *tz = to_thermal_zone(dev); \ - \ - if (tz->tzp) \ - return sprintf(buf, "%u\n", tz->tzp->name); \ - else \ - return -EIO; \ - } \ - \ - static ssize_t \ - name##_store(struct device *dev, struct device_attribute *devattr, \ - const char *buf, size_t count) \ - { \ - struct thermal_zone_device *tz = to_thermal_zone(dev); \ - s32 value; \ - \ - if (!tz->tzp) \ - return -EIO; \ - \ - if (kstrtos32(buf, 10, &value)) \ - return -EINVAL; \ - \ - tz->tzp->name = value; \ - \ - return count; \ - } \ - static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, name##_show, name##_store) - -create_s32_tzp_attr(k_po); -create_s32_tzp_attr(k_pu); -create_s32_tzp_attr(k_i); -create_s32_tzp_attr(k_d); -create_s32_tzp_attr(integral_cutoff); -create_s32_tzp_attr(slope); -create_s32_tzp_attr(offset); -create_s32_tzp_attr(integral_max); -#undef create_s32_tzp_attr - -static struct device_attribute *dev_tzp_attrs[] = { - &dev_attr_sustainable_power, - &dev_attr_k_po, - &dev_attr_k_pu, - &dev_attr_k_i, - &dev_attr_k_d, - &dev_attr_integral_cutoff, - &dev_attr_slope, - &dev_attr_offset, - &dev_attr_integral_max, -}; - -static int create_tzp_attrs(struct device *dev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dev_tzp_attrs); i++) { - int ret; - struct device_attribute *dev_attr = dev_tzp_attrs[i]; - - ret = device_create_file(dev, dev_attr); - if (ret) - return ret; - } - return 0; -} +/* + * Power actor section: interface to power actors to estimate power + * + * Set of functions used to interact to cooling devices that know + * how to estimate their devices power consumption. + */ /** * power_actor_get_max_power() - get the maximum power that a cdev can consume @@ -1181,12 +619,13 @@ int power_actor_get_min_power(struct thermal_cooling_device *cdev, } /** - * power_actor_set_power() - limit the maximum power that a cooling device can consume + * power_actor_set_power() - limit the maximum power a cooling device consumes * @cdev: pointer to &thermal_cooling_device * @instance: thermal instance to update * @power: the power in milliwatts * - * Set the cooling device to consume at most @power milliwatts. + * Set the cooling device to consume at most @power milliwatts. The limit is + * expected to be a cap at the maximum power consumption. * * Return: 0 on success, -EINVAL if the cooling device does not * implement the power actor API or -E* for other failures. @@ -1205,149 +644,60 @@ int power_actor_set_power(struct thermal_cooling_device *cdev, return ret; instance->target = state; + mutex_lock(&cdev->lock); cdev->updated = false; + mutex_unlock(&cdev->lock); thermal_cdev_update(cdev); return 0; } -static DEVICE_ATTR(type, 0444, type_show, NULL); -static DEVICE_ATTR(temp, 0444, temp_show, NULL); -static DEVICE_ATTR(mode, 0644, mode_show, mode_store); -static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); -static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); -static DEVICE_ATTR(available_policies, S_IRUGO, available_policies_show, NULL); - -/* sys I/F for cooling device */ -#define to_cooling_device(_dev) \ - container_of(_dev, struct thermal_cooling_device, device) - -static ssize_t -thermal_cooling_device_type_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct thermal_cooling_device *cdev = to_cooling_device(dev); - - return sprintf(buf, "%s\n", cdev->type); -} - -static ssize_t -thermal_cooling_device_max_state_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct thermal_cooling_device *cdev = to_cooling_device(dev); - unsigned long state; - int ret; - - ret = cdev->ops->get_max_state(cdev, &state); - if (ret) - return ret; - return sprintf(buf, "%ld\n", state); -} - -static ssize_t -thermal_cooling_device_cur_state_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct thermal_cooling_device *cdev = to_cooling_device(dev); - unsigned long state; - int ret; - - ret = cdev->ops->get_cur_state(cdev, &state); - if (ret) - return ret; - return sprintf(buf, "%ld\n", state); -} - -static ssize_t -thermal_cooling_device_cur_state_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +void thermal_zone_device_rebind_exception(struct thermal_zone_device *tz, + const char *cdev_type, size_t size) { - struct thermal_cooling_device *cdev = to_cooling_device(dev); - unsigned long state; - int result; - - if (!sscanf(buf, "%ld\n", &state)) - return -EINVAL; + struct thermal_cooling_device *cdev = NULL; - if ((long)state < 0) - return -EINVAL; + mutex_lock(&thermal_list_lock); + list_for_each_entry(cdev, &thermal_cdev_list, node) { + /* skip non matching cdevs */ + if (strncmp(cdev_type, cdev->type, size)) + continue; - result = cdev->ops->set_cur_state(cdev, state); - if (result) - return result; - return count; + /* re binding the exception matching the type pattern */ + thermal_zone_bind_cooling_device(tz, THERMAL_TRIPS_NONE, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT, + THERMAL_WEIGHT_DEFAULT); + } + mutex_unlock(&thermal_list_lock); } -static struct device_attribute dev_attr_cdev_type = -__ATTR(type, 0444, thermal_cooling_device_type_show, NULL); -static DEVICE_ATTR(max_state, 0444, - thermal_cooling_device_max_state_show, NULL); -static DEVICE_ATTR(cur_state, 0644, - thermal_cooling_device_cur_state_show, - thermal_cooling_device_cur_state_store); - -static ssize_t -thermal_cooling_device_trip_point_show(struct device *dev, - struct device_attribute *attr, char *buf) +void thermal_zone_device_unbind_exception(struct thermal_zone_device *tz, + const char *cdev_type, size_t size) { - struct thermal_instance *instance; - - instance = - container_of(attr, struct thermal_instance, attr); - - if (instance->trip == THERMAL_TRIPS_NONE) - return sprintf(buf, "-1\n"); - else - return sprintf(buf, "%d\n", instance->trip); -} - -static struct attribute *cooling_device_attrs[] = { - &dev_attr_cdev_type.attr, - &dev_attr_max_state.attr, - &dev_attr_cur_state.attr, - NULL, -}; - -static const struct attribute_group cooling_device_attr_group = { - .attrs = cooling_device_attrs, -}; - -static const struct attribute_group *cooling_device_attr_groups[] = { - &cooling_device_attr_group, - NULL, -}; - -static ssize_t -thermal_cooling_device_weight_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct thermal_instance *instance; - - instance = container_of(attr, struct thermal_instance, weight_attr); + struct thermal_cooling_device *cdev = NULL; - return sprintf(buf, "%d\n", instance->weight); + mutex_lock(&thermal_list_lock); + list_for_each_entry(cdev, &thermal_cdev_list, node) { + /* skip non matching cdevs */ + if (strncmp(cdev_type, cdev->type, size)) + continue; + /* unbinding the exception matching the type pattern */ + thermal_zone_unbind_cooling_device(tz, THERMAL_TRIPS_NONE, + cdev); + } + mutex_unlock(&thermal_list_lock); } -static ssize_t -thermal_cooling_device_weight_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct thermal_instance *instance; - int ret, weight; - - ret = kstrtoint(buf, 0, &weight); - if (ret) - return ret; - - instance = container_of(attr, struct thermal_instance, weight_attr); - instance->weight = weight; - - return count; -} -/* Device management */ +/* + * Device management section: cooling devices, zones devices, and binding + * + * Set of functions provided by the thermal core for: + * - cooling devices lifecycle: registration, unregistration, + * binding, and unbinding. + * - thermal zone devices lifecycle: registration, unregistration, + * binding, and unbinding. + */ /** * thermal_zone_bind_cooling_device() - bind a cooling device to a thermal zone @@ -1403,15 +753,31 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, if (ret) return ret; - /* lower default 0, upper default max_state */ - lower = lower == THERMAL_NO_LIMIT ? 0 : lower; - upper = upper == THERMAL_NO_LIMIT ? max_state : upper; + /* + * If upper or lower has a MACRO to define the mitigation state, + * based on the MACRO determine the default state to use or the + * offset from the max_state. + */ + if (upper >= (THERMAL_MAX_LIMIT - max_state)) { + /* upper default max_state */ + if (upper == THERMAL_NO_LIMIT) + upper = max_state; + else + upper = max_state - (THERMAL_MAX_LIMIT - upper); + } + + if (lower >= (THERMAL_MAX_LIMIT - max_state)) { + /* lower default 0 */ + if (lower == THERMAL_NO_LIMIT) + lower = 0; + else + lower = max_state - (THERMAL_MAX_LIMIT - lower); + } if (lower > upper || upper > max_state) return -EINVAL; - dev = - kzalloc(sizeof(struct thermal_instance), GFP_KERNEL); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; dev->tz = tz; @@ -1422,42 +788,65 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, dev->target = THERMAL_NO_TARGET; dev->weight = weight; - result = get_idr(&tz->idr, &tz->lock, &dev->id); - if (result) + result = ida_simple_get(&tz->ida, 0, 0, GFP_KERNEL); + if (result < 0) goto free_mem; + dev->id = result; sprintf(dev->name, "cdev%d", dev->id); result = sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name); if (result) - goto release_idr; + goto release_ida; sprintf(dev->attr_name, "cdev%d_trip_point", dev->id); sysfs_attr_init(&dev->attr.attr); dev->attr.attr.name = dev->attr_name; dev->attr.attr.mode = 0444; - dev->attr.show = thermal_cooling_device_trip_point_show; + dev->attr.show = trip_point_show; result = device_create_file(&tz->device, &dev->attr); if (result) goto remove_symbol_link; + snprintf(dev->upper_attr_name, THERMAL_NAME_LENGTH, + "cdev%d_upper_limit", dev->id); + sysfs_attr_init(&dev->upper_attr.attr); + dev->upper_attr.attr.name = dev->upper_attr_name; + dev->upper_attr.attr.mode = 0644; + dev->upper_attr.show = upper_limit_show; + dev->upper_attr.store = upper_limit_store; + result = device_create_file(&tz->device, &dev->upper_attr); + if (result) + goto remove_trip_file; + + snprintf(dev->lower_attr_name, THERMAL_NAME_LENGTH, + "cdev%d_lower_limit", dev->id); + sysfs_attr_init(&dev->lower_attr.attr); + dev->lower_attr.attr.name = dev->lower_attr_name; + dev->lower_attr.attr.mode = 0644; + dev->lower_attr.show = lower_limit_show; + dev->lower_attr.store = lower_limit_store; + result = device_create_file(&tz->device, &dev->lower_attr); + if (result) + goto remove_upper_file; + sprintf(dev->weight_attr_name, "cdev%d_weight", dev->id); sysfs_attr_init(&dev->weight_attr.attr); dev->weight_attr.attr.name = dev->weight_attr_name; dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO; - dev->weight_attr.show = thermal_cooling_device_weight_show; - dev->weight_attr.store = thermal_cooling_device_weight_store; + dev->weight_attr.show = weight_show; + dev->weight_attr.store = weight_store; result = device_create_file(&tz->device, &dev->weight_attr); if (result) - goto remove_trip_file; + goto remove_lower_file; mutex_lock(&tz->lock); mutex_lock(&cdev->lock); list_for_each_entry(pos, &tz->thermal_instances, tz_node) - if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { - result = -EEXIST; - break; - } + if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { + result = -EEXIST; + break; + } if (!result) { list_add_tail(&dev->tz_node, &tz->thermal_instances); list_add_tail(&dev->cdev_node, &cdev->thermal_instances); @@ -1470,12 +859,16 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, return 0; device_remove_file(&tz->device, &dev->weight_attr); +remove_lower_file: + device_remove_file(&tz->device, &dev->lower_attr); +remove_upper_file: + device_remove_file(&tz->device, &dev->upper_attr); remove_trip_file: device_remove_file(&tz->device, &dev->attr); remove_symbol_link: sysfs_remove_link(&tz->device.kobj, dev->name); -release_idr: - release_idr(&tz->idr, &tz->lock, dev->id); +release_ida: + ida_simple_remove(&tz->ida, dev->id); free_mem: kfree(dev); return result; @@ -1519,35 +912,110 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, return -ENODEV; unbind: + device_remove_file(&tz->device, &pos->lower_attr); + device_remove_file(&tz->device, &pos->upper_attr); device_remove_file(&tz->device, &pos->weight_attr); device_remove_file(&tz->device, &pos->attr); sysfs_remove_link(&tz->device.kobj, pos->name); - release_idr(&tz->idr, &tz->lock, pos->id); + ida_simple_remove(&tz->ida, pos->id); kfree(pos); return 0; } EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device); -static void thermal_release(struct device *dev) -{ - struct thermal_zone_device *tz; - struct thermal_cooling_device *cdev; +static void thermal_release(struct device *dev) +{ + struct thermal_zone_device *tz; + struct thermal_cooling_device *cdev; + + if (!strncmp(dev_name(dev), "thermal_zone", + sizeof("thermal_zone") - 1)) { + tz = to_thermal_zone(dev); + thermal_zone_destroy_device_groups(tz); + kfree(tz); + } else if (!strncmp(dev_name(dev), "cooling_device", + sizeof("cooling_device") - 1)) { + cdev = to_cooling_device(dev); + kfree(cdev); + } +} + +static struct class thermal_class = { + .name = "thermal", + .dev_release = thermal_release, +}; + +static inline +void print_bind_err_msg(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int ret) +{ + dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", + tz->type, cdev->type, ret); +} + +static void __bind(struct thermal_zone_device *tz, int mask, + struct thermal_cooling_device *cdev, + unsigned long *limits, + unsigned int weight) +{ + int i, ret; + + for (i = 0; i < tz->trips; i++) { + if (mask & (1 << i)) { + unsigned long upper, lower; + + upper = THERMAL_NO_LIMIT; + lower = THERMAL_NO_LIMIT; + if (limits) { + lower = limits[i * 2]; + upper = limits[i * 2 + 1]; + } + ret = thermal_zone_bind_cooling_device(tz, i, cdev, + upper, lower, + weight); + if (ret) + print_bind_err_msg(tz, cdev, ret); + } + } +} + +static void bind_cdev(struct thermal_cooling_device *cdev) +{ + int i, ret; + const struct thermal_zone_params *tzp; + struct thermal_zone_device *pos = NULL; + + mutex_lock(&thermal_list_lock); + + list_for_each_entry(pos, &thermal_tz_list, node) { + if (!pos->tzp && !pos->ops->bind) + continue; - if (!strncmp(dev_name(dev), "thermal_zone", - sizeof("thermal_zone") - 1)) { - tz = to_thermal_zone(dev); - kfree(tz); - } else if(!strncmp(dev_name(dev), "cooling_device", - sizeof("cooling_device") - 1)){ - cdev = to_cooling_device(dev); - kfree(cdev); + if (pos->ops->bind) { + ret = pos->ops->bind(pos, cdev); + if (ret) + print_bind_err_msg(pos, cdev, ret); + continue; + } + + tzp = pos->tzp; + if (!tzp || !tzp->tbp) + continue; + + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev || !tzp->tbp[i].match) + continue; + if (tzp->tbp[i].match(pos, cdev)) + continue; + tzp->tbp[i].cdev = cdev; + __bind(pos, tzp->tbp[i].trip_mask, cdev, + tzp->tbp[i].binding_limits, + tzp->tbp[i].weight); + } } -} -static struct class thermal_class = { - .name = "thermal", - .dev_release = thermal_release, -}; + mutex_unlock(&thermal_list_lock); +} /** * __thermal_cooling_device_register() - register a new thermal cooling device @@ -1581,16 +1049,17 @@ __thermal_cooling_device_register(struct device_node *np, !ops->set_cur_state) return ERR_PTR(-EINVAL); - cdev = kzalloc(sizeof(struct thermal_cooling_device), GFP_KERNEL); + cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); if (!cdev) return ERR_PTR(-ENOMEM); - result = get_idr(&thermal_cdev_idr, &thermal_idr_lock, &cdev->id); - if (result) { + result = ida_simple_get(&thermal_cdev_ida, 0, 0, GFP_KERNEL); + if (result < 0) { kfree(cdev); return ERR_PTR(result); } + cdev->id = result; strlcpy(cdev->type, type ? : "", sizeof(cdev->type)); mutex_init(&cdev->lock); INIT_LIST_HEAD(&cdev->thermal_instances); @@ -1598,15 +1067,18 @@ __thermal_cooling_device_register(struct device_node *np, cdev->ops = ops; cdev->updated = false; cdev->device.class = &thermal_class; - cdev->device.groups = cooling_device_attr_groups; cdev->devdata = devdata; + cdev->sysfs_cur_state_req = 0; + cdev->sysfs_min_state_req = ULONG_MAX; + thermal_cooling_device_setup_sysfs(cdev); dev_set_name(&cdev->device, "cooling_device%d", cdev->id); result = device_register(&cdev->device); if (result) { - release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); + ida_simple_remove(&thermal_cdev_ida, cdev->id); kfree(cdev); return ERR_PTR(result); } + pr_info("register cooling_device%d-%s\n", cdev->id, cdev->type); /* Add 'this' new cdev to the global cdev list */ mutex_lock(&thermal_list_lock); @@ -1619,7 +1091,8 @@ __thermal_cooling_device_register(struct device_node *np, mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) if (atomic_cmpxchg(&pos->need_update, 1, 0)) - thermal_zone_device_update(pos); + thermal_zone_device_update(pos, + THERMAL_EVENT_UNSPECIFIED); mutex_unlock(&thermal_list_lock); return cdev; @@ -1670,12 +1143,22 @@ thermal_of_cooling_device_register(struct device_node *np, } EXPORT_SYMBOL_GPL(thermal_of_cooling_device_register); +static void __unbind(struct thermal_zone_device *tz, int mask, + struct thermal_cooling_device *cdev) +{ + int i; + + for (i = 0; i < tz->trips; i++) + if (mask & (1 << i)) + thermal_zone_unbind_cooling_device(tz, i, cdev); +} + /** - * thermal_cooling_device_unregister - removes the registered thermal cooling device + * thermal_cooling_device_unregister - removes a thermal cooling device * @cdev: the thermal cooling device to remove. * - * thermal_cooling_device_unregister() must be called when the device is no - * longer needed. + * thermal_cooling_device_unregister() must be called when a registered + * thermal cooling device is no longer needed. */ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) { @@ -1689,8 +1172,8 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_cdev_list, node) - if (pos == cdev) - break; + if (pos == cdev) + break; if (pos != cdev) { /* thermal cooling device not found */ mutex_unlock(&thermal_list_lock); @@ -1719,172 +1202,51 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) mutex_unlock(&thermal_list_lock); - if (cdev->type[0]) - device_remove_file(&cdev->device, &dev_attr_cdev_type); - device_remove_file(&cdev->device, &dev_attr_max_state); - device_remove_file(&cdev->device, &dev_attr_cur_state); - - release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); - device_unregister(&cdev->device); - return; + ida_simple_remove(&thermal_cdev_ida, cdev->id); + device_del(&cdev->device); + thermal_cooling_device_destroy_sysfs(cdev); + put_device(&cdev->device); } EXPORT_SYMBOL_GPL(thermal_cooling_device_unregister); -void thermal_cdev_update(struct thermal_cooling_device *cdev) +static void bind_tz(struct thermal_zone_device *tz) { - struct thermal_instance *instance; - unsigned long target = 0; - - /* cooling device is updated*/ - if (cdev->updated) - return; - - mutex_lock(&cdev->lock); - /* Make sure cdev enters the deepest cooling state */ - list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { - dev_dbg(&cdev->device, "zone%d->target=%lu\n", - instance->tz->id, instance->target); - if (instance->target == THERMAL_NO_TARGET) - continue; - if (instance->target > target) - target = instance->target; - } - mutex_unlock(&cdev->lock); - cdev->ops->set_cur_state(cdev, target); - cdev->updated = true; - trace_cdev_update(cdev, target); - dev_dbg(&cdev->device, "set to state %lu\n", target); -} -EXPORT_SYMBOL(thermal_cdev_update); + int i, ret; + struct thermal_cooling_device *pos = NULL; + const struct thermal_zone_params *tzp = tz->tzp; -/** - * thermal_notify_framework - Sensor drivers use this API to notify framework - * @tz: thermal zone device - * @trip: indicates which trip point has been crossed - * - * This function handles the trip events from sensor drivers. It starts - * throttling the cooling devices according to the policy configured. - * For CRITICAL and HOT trip points, this notifies the respective drivers, - * and does actual throttling for other trip points i.e ACTIVE and PASSIVE. - * The throttling policy is based on the configured platform data; if no - * platform data is provided, this uses the step_wise throttling policy. - */ -void thermal_notify_framework(struct thermal_zone_device *tz, int trip) -{ - if (atomic_read(&in_suspend)) + if (!tzp && !tz->ops->bind) return; - handle_thermal_trip(tz, trip); -} -EXPORT_SYMBOL_GPL(thermal_notify_framework); - -/** - * create_trip_attrs() - create attributes for trip points - * @tz: the thermal zone device - * @mask: Writeable trip point bitmap. - * - * helper function to instantiate sysfs entries for every trip - * point and its properties of a struct thermal_zone_device. - * - * Return: 0 on success, the proper error value otherwise. - */ -static int create_trip_attrs(struct thermal_zone_device *tz, int mask) -{ - int indx; - int size = sizeof(struct thermal_attr) * tz->trips; - - tz->trip_type_attrs = kzalloc(size, GFP_KERNEL); - if (!tz->trip_type_attrs) - return -ENOMEM; - - tz->trip_temp_attrs = kzalloc(size, GFP_KERNEL); - if (!tz->trip_temp_attrs) { - kfree(tz->trip_type_attrs); - return -ENOMEM; - } + mutex_lock(&thermal_list_lock); - if (tz->ops->get_trip_hyst) { - tz->trip_hyst_attrs = kzalloc(size, GFP_KERNEL); - if (!tz->trip_hyst_attrs) { - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - return -ENOMEM; + /* If there is ops->bind, try to use ops->bind */ + if (tz->ops->bind) { + list_for_each_entry(pos, &thermal_cdev_list, node) { + ret = tz->ops->bind(tz, pos); + if (ret) + print_bind_err_msg(tz, pos, ret); } + goto exit; } + if (!tzp || !tzp->tbp) + goto exit; - for (indx = 0; indx < tz->trips; indx++) { - /* create trip type attribute */ - snprintf(tz->trip_type_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_type", indx); - - sysfs_attr_init(&tz->trip_type_attrs[indx].attr.attr); - tz->trip_type_attrs[indx].attr.attr.name = - tz->trip_type_attrs[indx].name; - tz->trip_type_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_type_attrs[indx].attr.show = trip_point_type_show; - - device_create_file(&tz->device, - &tz->trip_type_attrs[indx].attr); - - /* create trip temp attribute */ - snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_temp", indx); - - sysfs_attr_init(&tz->trip_temp_attrs[indx].attr.attr); - tz->trip_temp_attrs[indx].attr.attr.name = - tz->trip_temp_attrs[indx].name; - tz->trip_temp_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_temp_attrs[indx].attr.show = trip_point_temp_show; - if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS) && - mask & (1 << indx)) { - tz->trip_temp_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_temp_attrs[indx].attr.store = - trip_point_temp_store; - } - - device_create_file(&tz->device, - &tz->trip_temp_attrs[indx].attr); - - /* create Optional trip hyst attribute */ - if (!tz->ops->get_trip_hyst) - continue; - snprintf(tz->trip_hyst_attrs[indx].name, THERMAL_NAME_LENGTH, - "trip_point_%d_hyst", indx); - - sysfs_attr_init(&tz->trip_hyst_attrs[indx].attr.attr); - tz->trip_hyst_attrs[indx].attr.attr.name = - tz->trip_hyst_attrs[indx].name; - tz->trip_hyst_attrs[indx].attr.attr.mode = S_IRUGO; - tz->trip_hyst_attrs[indx].attr.show = trip_point_hyst_show; - if (tz->ops->set_trip_hyst) { - tz->trip_hyst_attrs[indx].attr.attr.mode |= S_IWUSR; - tz->trip_hyst_attrs[indx].attr.store = - trip_point_hyst_store; + list_for_each_entry(pos, &thermal_cdev_list, node) { + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev || !tzp->tbp[i].match) + continue; + if (tzp->tbp[i].match(tz, pos)) + continue; + tzp->tbp[i].cdev = pos; + __bind(tz, tzp->tbp[i].trip_mask, pos, + tzp->tbp[i].binding_limits, + tzp->tbp[i].weight); } - - device_create_file(&tz->device, - &tz->trip_hyst_attrs[indx].attr); - } - return 0; -} - -static void remove_trip_attrs(struct thermal_zone_device *tz) -{ - int indx; - - for (indx = 0; indx < tz->trips; indx++) { - device_remove_file(&tz->device, - &tz->trip_type_attrs[indx].attr); - device_remove_file(&tz->device, - &tz->trip_temp_attrs[indx].attr); - if (tz->ops->get_trip_hyst) - device_remove_file(&tz->device, - &tz->trip_hyst_attrs[indx].attr); } - kfree(tz->trip_type_attrs); - kfree(tz->trip_temp_attrs); - kfree(tz->trip_hyst_attrs); +exit: + mutex_unlock(&thermal_list_lock); } /** @@ -1911,20 +1273,22 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) * in case of error, an ERR_PTR. Caller must check return value with * IS_ERR*() helpers. */ -struct thermal_zone_device *thermal_zone_device_register(const char *type, - int trips, int mask, void *devdata, - struct thermal_zone_device_ops *ops, - struct thermal_zone_params *tzp, - int passive_delay, int polling_delay) +struct thermal_zone_device * +thermal_zone_device_register(const char *type, int trips, int mask, + void *devdata, struct thermal_zone_device_ops *ops, + struct thermal_zone_params *tzp, int passive_delay, + int polling_delay) { struct thermal_zone_device *tz; enum thermal_trip_type trip_type; int trip_temp; int result; int count; - int passive = 0; struct thermal_governor *governor; + if (!type || strlen(type) == 0) + return ERR_PTR(-EINVAL); + if (type && strlen(type) >= THERMAL_NAME_LENGTH) return ERR_PTR(-EINVAL); @@ -1937,20 +1301,19 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp)) return ERR_PTR(-EINVAL); - tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL); + tz = kzalloc(sizeof(*tz), GFP_KERNEL); if (!tz) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&tz->thermal_instances); - idr_init(&tz->idr); + ida_init(&tz->ida); mutex_init(&tz->lock); - result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id); - if (result) { - kfree(tz); - return ERR_PTR(result); - } + result = ida_simple_get(&thermal_tz_ida, 0, 0, GFP_KERNEL); + if (result < 0) + goto free_tz; - strlcpy(tz->type, type ? : "", sizeof(tz->type)); + tz->id = result; + strlcpy(tz->type, type, sizeof(tz->type)); tz->ops = ops; tz->tzp = tzp; tz->device.class = &thermal_class; @@ -1958,46 +1321,24 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, tz->trips = trips; tz->passive_delay = passive_delay; tz->polling_delay = polling_delay; -#ifdef CONFIG_SCHED_HMP - tz->poll_queue_cpu = BOUNDED_CPU; -#endif - /* A new thermal zone needs to be updated anyway. */ - atomic_set(&tz->need_update, 1); - - dev_set_name(&tz->device, "thermal_zone%d", tz->id); - result = device_register(&tz->device); - if (result) { - release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); - kfree(tz); - return ERR_PTR(result); - } /* sys I/F */ - if (type) { - result = device_create_file(&tz->device, &dev_attr_type); - if (result) - goto unregister; - } - - result = device_create_file(&tz->device, &dev_attr_temp); + /* Add nodes that are always present via .groups */ + result = thermal_zone_create_device_groups(tz, mask); if (result) - goto unregister; + goto remove_id; - if (ops->get_mode) { - result = device_create_file(&tz->device, &dev_attr_mode); - if (result) - goto unregister; - } + /* A new thermal zone needs to be updated anyway. */ + atomic_set(&tz->need_update, 1); - result = create_trip_attrs(tz, mask); + dev_set_name(&tz->device, "thermal_zone%d", tz->id); + result = device_register(&tz->device); if (result) - goto unregister; + goto remove_device_groups; for (count = 0; count < trips; count++) { if (tz->ops->get_trip_type(tz, count, &trip_type)) set_bit(count, &tz->trips_disabled); - if (trip_type == THERMAL_TRIP_PASSIVE) - passive = 1; if (tz->ops->get_trip_temp(tz, count, &trip_temp)) set_bit(count, &tz->trips_disabled); /* Check for bogus trip points */ @@ -2005,33 +1346,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, set_bit(count, &tz->trips_disabled); } - if (!passive) { - result = device_create_file(&tz->device, &dev_attr_passive); - if (result) - goto unregister; - } - - if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) { - result = device_create_file(&tz->device, &dev_attr_emul_temp); - if (result) - goto unregister; - } - - /* Create policy attribute */ - result = device_create_file(&tz->device, &dev_attr_policy); - if (result) - goto unregister; - - /* Add thermal zone params */ - result = create_tzp_attrs(&tz->device); - if (result) - goto unregister; - - /* Create available_policies attribute */ - result = device_create_file(&tz->device, &dev_attr_available_policies); - if (result) - goto unregister; - /* Update 'this' zone's governor information */ mutex_lock(&thermal_governor_lock); @@ -2061,19 +1375,27 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, /* Bind cooling devices for this zone */ bind_tz(tz); - INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check); + INIT_DEFERRABLE_WORK(&(tz->poll_queue), thermal_zone_device_check); thermal_zone_device_reset(tz); /* Update the new thermal zone and mark it as already updated. */ if (atomic_cmpxchg(&tz->need_update, 1, 0)) - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); return tz; unregister: - release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); + ida_simple_remove(&thermal_tz_ida, tz->id); device_unregister(&tz->device); return ERR_PTR(result); + +remove_device_groups: + thermal_zone_destroy_device_groups(tz); +remove_id: + ida_simple_remove(&thermal_tz_ida, tz->id); +free_tz: + kfree(tz); + return ERR_PTR(result); } EXPORT_SYMBOL_GPL(thermal_zone_device_register); @@ -2095,8 +1417,8 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) - if (pos == tz) - break; + if (pos == tz) + break; if (pos != tz) { /* thermal zone device not found */ mutex_unlock(&thermal_list_lock); @@ -2124,24 +1446,15 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) mutex_unlock(&thermal_list_lock); - thermal_zone_device_set_polling(tz, 0); + cancel_delayed_work_sync(&tz->poll_queue); - if (tz->type[0]) - device_remove_file(&tz->device, &dev_attr_type); - device_remove_file(&tz->device, &dev_attr_temp); - if (tz->ops->get_mode) - device_remove_file(&tz->device, &dev_attr_mode); - device_remove_file(&tz->device, &dev_attr_policy); - device_remove_file(&tz->device, &dev_attr_available_policies); - remove_trip_attrs(tz); thermal_set_governor(tz, NULL); thermal_remove_hwmon_sysfs(tz); - release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); - idr_destroy(&tz->idr); + ida_simple_remove(&thermal_tz_ida, tz->id); + ida_destroy(&tz->ida); mutex_destroy(&tz->lock); device_unregister(&tz->device); - return; } EXPORT_SYMBOL_GPL(thermal_zone_device_unregister); @@ -2188,8 +1501,8 @@ static const struct genl_multicast_group thermal_event_mcgrps[] = { { .name = THERMAL_GENL_MCAST_GROUP_NAME, }, }; -static struct genl_family thermal_event_genl_family = { - .id = GENL_ID_GENERATE, +static struct genl_family thermal_event_genl_family __ro_after_init = { + .module = THIS_MODULE, .name = THERMAL_GENL_FAMILY_NAME, .version = THERMAL_GENL_VERSION, .maxattr = THERMAL_GENL_ATTR_MAX, @@ -2197,8 +1510,10 @@ static struct genl_family thermal_event_genl_family = { .n_mcgrps = ARRAY_SIZE(thermal_event_mcgrps), }; +static int allow_netlink_events; + int thermal_generate_netlink_event(struct thermal_zone_device *tz, - enum events event) + enum events event) { struct sk_buff *skb; struct nlattr *attr; @@ -2211,6 +1526,9 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz, if (!tz) return -EINVAL; + if (!allow_netlink_events) + return -ENODEV; + /* allocate memory */ size = nla_total_size(sizeof(struct thermal_genl_event)) + nla_total_size(0); @@ -2260,7 +1578,7 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz, } EXPORT_SYMBOL_GPL(thermal_generate_netlink_event); -static int genetlink_init(void) +static int __init genetlink_init(void) { return genl_register_family(&thermal_event_genl_family); } @@ -2272,79 +1590,42 @@ static void genetlink_exit(void) #else /* !CONFIG_NET */ static inline int genetlink_init(void) { return 0; } static inline void genetlink_exit(void) {} +static inline int thermal_generate_netlink_event(struct thermal_zone_device *tz, + enum events event) { return -ENODEV; } #endif /* !CONFIG_NET */ -static int __init thermal_register_governors(void) -{ - int result; - - result = thermal_gov_step_wise_register(); - if (result) - return result; - - result = thermal_gov_fair_share_register(); - if (result) - return result; - - result = thermal_gov_bang_bang_register(); - if (result) - return result; - - result = thermal_gov_user_space_register(); - if (result) - return result; - - return thermal_gov_power_allocator_register(); -} - -static void thermal_unregister_governors(void) -{ - thermal_gov_step_wise_unregister(); - thermal_gov_fair_share_unregister(); - thermal_gov_bang_bang_unregister(); - thermal_gov_user_space_unregister(); - thermal_gov_power_allocator_unregister(); -} - -#ifdef CONFIG_SCHED_HMP -static int thermal_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) +#if defined(CONFIG_SEC_PM) +static void __ref cdev_print(struct work_struct *work) { - unsigned long cpu = (unsigned long)hcpu; - struct thermal_zone_device *pos; + struct thermal_cooling_device *cdev; + unsigned long cur_state = 0; + int added = 0, ret = 0; + char buffer[500] = { 0, }; + bool is_state = false; - switch (action) { - case CPU_ONLINE: - if (cpu == BOUNDED_CPU) { - list_for_each_entry(pos, &thermal_tz_list, node) { - pos->poll_queue_cpu = BOUNDED_CPU; - if (pos->polling_delay) { - start_poll_queue(pos, pos->polling_delay); - } - } - } - break; - case CPU_DOWN_PREPARE: - list_for_each_entry(pos, &thermal_tz_list, node) { - if (pos->poll_queue_cpu == cpu) { - pos->poll_queue_cpu = 0; - if (pos->polling_delay) - start_poll_queue(pos, pos->polling_delay); - } + mutex_lock(&thermal_list_lock); + list_for_each_entry(cdev, &thermal_cdev_list, node) { + if (cdev->ops->get_cur_state) + cdev->ops->get_cur_state(cdev, &cur_state);; + + if (cur_state) { + is_state = true; + ret = snprintf(buffer + added, sizeof(buffer) - added, + "[%s:%ld]", cdev->type, cur_state); + added += ret; } - break; } - return NOTIFY_OK; -} + mutex_unlock(&thermal_list_lock); -static struct notifier_block thermal_cpu_notifier = -{ - .notifier_call = thermal_cpu_callback, -}; + if (is_state) + printk("thermal: cdev%s\n", buffer); + + schedule_delayed_work(&cdev_print_work, HZ * 5); +} #endif static int thermal_pm_notify(struct notifier_block *nb, - unsigned long mode, void *_unused) + unsigned long mode, void *_unused) { struct thermal_zone_device *tz; @@ -2352,6 +1633,9 @@ static int thermal_pm_notify(struct notifier_block *nb, case PM_HIBERNATION_PREPARE: case PM_RESTORE_PREPARE: case PM_SUSPEND_PREPARE: +#if defined(CONFIG_SEC_PM) + cancel_delayed_work(&cdev_print_work); +#endif atomic_set(&in_suspend, 1); break; case PM_POST_HIBERNATION: @@ -2359,9 +1643,16 @@ static int thermal_pm_notify(struct notifier_block *nb, case PM_POST_SUSPEND: atomic_set(&in_suspend, 0); list_for_each_entry(tz, &thermal_tz_list, node) { - thermal_zone_device_reset(tz); - thermal_zone_device_update(tz); + if (tz->ops->is_wakeable && + tz->ops->is_wakeable(tz)) + continue; + thermal_zone_device_init(tz); + thermal_zone_device_update(tz, + THERMAL_EVENT_UNSPECIFIED); } +#if defined(CONFIG_SEC_PM) + schedule_delayed_work(&cdev_print_work, 0); +#endif break; default: break; @@ -2377,63 +1668,91 @@ static int __init thermal_init(void) { int result; + mutex_init(&poweroff_lock); + thermal_passive_wq = alloc_workqueue("thermal_passive_wq", + WQ_HIGHPRI | WQ_UNBOUND + | WQ_FREEZABLE, + THERMAL_MAX_ACTIVE); + if (!thermal_passive_wq) { + result = -ENOMEM; + goto error; + } + result = thermal_register_governors(); if (result) - goto error; + goto destroy_wq; result = class_register(&thermal_class); if (result) goto unregister_governors; - result = genetlink_init(); - if (result) - goto unregister_class; - result = of_parse_thermal_zones(); if (result) - goto exit_netlink; + goto exit_zone_parse; -#ifdef CONFIG_SCHED_HMP - register_hotcpu_notifier(&thermal_cpu_notifier); -#endif result = register_pm_notifier(&thermal_pm_nb); if (result) pr_warn("Thermal: Can not register suspend notifier, return %d\n", result); +#if defined(CONFIG_SEC_PM) + INIT_DELAYED_WORK(&cdev_print_work, cdev_print); + schedule_delayed_work(&cdev_print_work, 0); + + if (!thermal_ipc_log) + thermal_ipc_log = ipc_log_context_create(10, "lmh_dcvs", 0); + + if (!thermal_ipc_log) + pr_err("%s: Failed to create thermal logging context\n", __func__); +#endif + return 0; -exit_netlink: - genetlink_exit(); -unregister_class: +exit_zone_parse: class_unregister(&thermal_class); unregister_governors: thermal_unregister_governors(); +destroy_wq: + destroy_workqueue(thermal_passive_wq); error: - idr_destroy(&thermal_tz_idr); - idr_destroy(&thermal_cdev_idr); - mutex_destroy(&thermal_idr_lock); + ida_destroy(&thermal_tz_ida); + ida_destroy(&thermal_cdev_ida); mutex_destroy(&thermal_list_lock); mutex_destroy(&thermal_governor_lock); + mutex_destroy(&poweroff_lock); return result; } -static void __exit thermal_exit(void) +static void thermal_exit(void) { - unregister_pm_notifier(&thermal_pm_nb); -#ifdef CONFIG_SCHED_HMP - unregister_hotcpu_notifier(&thermal_cpu_notifier); +#if defined(CONFIG_SEC_PM) + cancel_delayed_work_sync(&cdev_print_work); #endif + unregister_pm_notifier(&thermal_pm_nb); of_thermal_destroy_zones(); + destroy_workqueue(thermal_passive_wq); genetlink_exit(); class_unregister(&thermal_class); thermal_unregister_governors(); - idr_destroy(&thermal_tz_idr); - idr_destroy(&thermal_cdev_idr); - mutex_destroy(&thermal_idr_lock); + ida_destroy(&thermal_tz_ida); + ida_destroy(&thermal_cdev_ida); mutex_destroy(&thermal_list_lock); mutex_destroy(&thermal_governor_lock); } -fs_initcall(thermal_init); +static int __init thermal_netlink_init(void) +{ + int ret = 0; + + ret = genetlink_init(); + if (!ret) + goto exit_netlink; + + thermal_exit(); +exit_netlink: + return ret; +} + +subsys_initcall(thermal_init); +fs_initcall(thermal_netlink_init); module_exit(thermal_exit); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h old mode 100755 new mode 100644 index 749d41ab..3904a52f --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -1,24 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * thermal_core.h * * Copyright (C) 2012 Intel Corp * Author: Durgadoss R - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * 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; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ #ifndef __THERMAL_CORE_H__ @@ -49,13 +34,57 @@ struct thermal_instance { struct device_attribute attr; char weight_attr_name[THERMAL_NAME_LENGTH]; struct device_attribute weight_attr; + char upper_attr_name[THERMAL_NAME_LENGTH]; + struct device_attribute upper_attr; + char lower_attr_name[THERMAL_NAME_LENGTH]; + struct device_attribute lower_attr; struct list_head tz_node; /* node in tz->thermal_instances */ struct list_head cdev_node; /* node in cdev->thermal_instances */ unsigned int weight; /* The weight of the cooling device */ }; +#define to_thermal_zone(_dev) \ + container_of(_dev, struct thermal_zone_device, device) + +#define to_cooling_device(_dev) \ + container_of(_dev, struct thermal_cooling_device, device) + int thermal_register_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *); +void thermal_zone_device_rebind_exception(struct thermal_zone_device *, + const char *, size_t); +void thermal_zone_device_unbind_exception(struct thermal_zone_device *, + const char *, size_t); +int thermal_zone_device_set_policy(struct thermal_zone_device *, char *); +int thermal_build_list_of_policies(char *buf); + +/* sysfs I/F */ +int thermal_zone_create_device_groups(struct thermal_zone_device *, int); +void thermal_zone_destroy_device_groups(struct thermal_zone_device *); +void thermal_cooling_device_setup_sysfs(struct thermal_cooling_device *); +void thermal_cooling_device_destroy_sysfs(struct thermal_cooling_device *cdev); +/* used only at binding time */ +ssize_t trip_point_show(struct device *, struct device_attribute *, char *); +ssize_t weight_show(struct device *, struct device_attribute *, char *); +ssize_t lower_limit_show(struct device *dev, struct device_attribute *attr, + char *buf); +ssize_t upper_limit_show(struct device *dev, struct device_attribute *attr, + char *buf); +ssize_t weight_store(struct device *, struct device_attribute *, const char *, + size_t); +ssize_t lower_limit_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); +ssize_t upper_limit_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); + +#ifdef CONFIG_THERMAL_STATISTICS +void thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, + unsigned long new_state); +#else +static inline void +thermal_cooling_device_stats_update(struct thermal_cooling_device *cdev, + unsigned long new_state) {} +#endif /* CONFIG_THERMAL_STATISTICS */ #ifdef CONFIG_THERMAL_GOV_STEP_WISE int thermal_gov_step_wise_register(void); @@ -97,6 +126,14 @@ static inline int thermal_gov_power_allocator_register(void) { return 0; } static inline void thermal_gov_power_allocator_unregister(void) {} #endif /* CONFIG_THERMAL_GOV_POWER_ALLOCATOR */ +#ifdef CONFIG_THERMAL_GOV_LOW_LIMITS +int thermal_gov_low_limits_register(void); +void thermal_gov_low_limits_unregister(void); +#else +static inline int thermal_gov_low_limits_register(void) { return 0; } +static inline void thermal_gov_low_limits_unregister(void) {} +#endif /* CONFIG_THERMAL_GOV_LOW_LIMITS */ + /* device tree support */ #ifdef CONFIG_THERMAL_OF int of_parse_thermal_zones(void); @@ -105,6 +142,12 @@ int of_thermal_get_ntrips(struct thermal_zone_device *); bool of_thermal_is_trip_valid(struct thermal_zone_device *, int); const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *); +int of_thermal_aggregate_trip(struct thermal_zone_device *tz, + enum thermal_trip_type type, + int *low, int *high); +void of_thermal_handle_trip(struct thermal_zone_device *tz); +void of_thermal_handle_trip_temp(struct thermal_zone_device *tz, + int trip_temp); #else static inline int of_parse_thermal_zones(void) { return 0; } static inline void of_thermal_destroy_zones(void) { } @@ -122,6 +165,19 @@ of_thermal_get_trip_points(struct thermal_zone_device *tz) { return NULL; } +static inline int of_thermal_aggregate_trip(struct thermal_zone_device *tz, + enum thermal_trip_type type, + int *low, int *high) +{ + return -ENODEV; +} +static inline +void of_thermal_handle_trip(struct thermal_zone_device *tz) +{ } +static inline +void of_thermal_handle_trip_temp(struct thermal_zone_device *tz, + int trip_temp) +{ } #endif #endif /* __THERMAL_CORE_H__ */