mirror of
https://github.com/torvalds/linux.git
synced 2026-06-08 14:42:37 +02:00
Merge branch 'linaro-android-3.10-lsk' of git://git.linaro.org/people/john.stultz/android into lsk-v3.10-aosp
This commit is contained in:
commit
95b9600d1e
34
Documentation/arm64/tagged-pointers.txt
Normal file
34
Documentation/arm64/tagged-pointers.txt
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
Tagged virtual addresses in AArch64 Linux
|
||||
=========================================
|
||||
|
||||
Author: Will Deacon <will.deacon@arm.com>
|
||||
Date : 12 June 2013
|
||||
|
||||
This document briefly describes the provision of tagged virtual
|
||||
addresses in the AArch64 translation system and their potential uses
|
||||
in AArch64 Linux.
|
||||
|
||||
The kernel configures the translation tables so that translations made
|
||||
via TTBR0 (i.e. userspace mappings) have the top byte (bits 63:56) of
|
||||
the virtual address ignored by the translation hardware. This frees up
|
||||
this byte for application use, with the following caveats:
|
||||
|
||||
(1) The kernel requires that all user addresses passed to EL1
|
||||
are tagged with tag 0x00. This means that any syscall
|
||||
parameters containing user virtual addresses *must* have
|
||||
their top byte cleared before trapping to the kernel.
|
||||
|
||||
(2) Non-zero tags are not preserved when delivering signals.
|
||||
This means that signal handlers in applications making use
|
||||
of tags cannot rely on the tag information for user virtual
|
||||
addresses being maintained for fields inside siginfo_t.
|
||||
One exception to this rule is for signals raised in response
|
||||
to watchpoint debug exceptions, where the tag information
|
||||
will be preserved.
|
||||
|
||||
(3) Special care should be taken when using tagged pointers,
|
||||
since it is likely that C compilers will not hazard two
|
||||
virtual addresses differing only in the upper byte.
|
||||
|
||||
The architecture prevents the use of a tagged PC, so the upper byte will
|
||||
be set to a sign-extension of bit 55 on exception return.
|
||||
|
|
@ -16,8 +16,8 @@ CONFIG_CGROUP_DEBUG=y
|
|||
CONFIG_CGROUP_FREEZER=y
|
||||
CONFIG_CGROUP_SCHED=y
|
||||
CONFIG_DM_CRYPT=y
|
||||
CONFIG_DM_VERITY=y
|
||||
CONFIG_EMBEDDED=y
|
||||
CONFIG_EXPERIMENTAL=y
|
||||
CONFIG_FB=y
|
||||
CONFIG_HIGH_RES_TIMERS=y
|
||||
CONFIG_INET6_AH=y
|
||||
|
|
@ -36,6 +36,7 @@ CONFIG_IPV6_MULTIPLE_TABLES=y
|
|||
CONFIG_IPV6_OPTIMISTIC_DAD=y
|
||||
CONFIG_IPV6_PRIVACY=y
|
||||
CONFIG_IPV6_ROUTER_PREF=y
|
||||
CONFIG_IPV6_ROUTE_INFO=y
|
||||
CONFIG_IP_ADVANCED_ROUTER=y
|
||||
CONFIG_IP_MULTIPLE_TABLES=y
|
||||
CONFIG_IP_NF_ARPFILTER=y
|
||||
|
|
@ -82,9 +83,11 @@ CONFIG_NETFILTER_XT_MATCH_TIME=y
|
|||
CONFIG_NETFILTER_XT_MATCH_U32=y
|
||||
CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
|
||||
CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
|
||||
CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
|
||||
CONFIG_NETFILTER_XT_TARGET_MARK=y
|
||||
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
|
||||
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
|
||||
CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
|
||||
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
|
||||
CONFIG_NETFILTER_XT_TARGET_TRACE=y
|
||||
CONFIG_NET_CLS_ACT=y
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
# CONFIG_NF_CONNTRACK_SIP is not set
|
||||
# CONFIG_PM_WAKELOCKS_GC is not set
|
||||
# CONFIG_VT is not set
|
||||
CONFIG_ANDROID_RAM_CONSOLE=y
|
||||
CONFIG_ANDROID_TIMED_GPIO=y
|
||||
CONFIG_BACKLIGHT_LCD_SUPPORT=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
|
|
@ -14,10 +13,12 @@ CONFIG_BLK_DEV_RAM_SIZE=8192
|
|||
CONFIG_COMPACTION=y
|
||||
CONFIG_DM_UEVENT=y
|
||||
CONFIG_DRAGONRISE_FF=y
|
||||
CONFIG_ENABLE_DEFAULT_TRACERS=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_EXT4_FS_SECURITY=y
|
||||
CONFIG_FUSE_FS=y
|
||||
CONFIG_GREENASIA_FF=y
|
||||
CONFIG_HIDRAW=y
|
||||
CONFIG_HID_A4TECH=y
|
||||
CONFIG_HID_ACRUX=y
|
||||
CONFIG_HID_ACRUX_FF=y
|
||||
|
|
@ -94,8 +95,10 @@ CONFIG_PM_DEBUG=y
|
|||
CONFIG_PM_RUNTIME=y
|
||||
CONFIG_PM_WAKELOCKS_LIMIT=0
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
CONFIG_PSTORE=y
|
||||
CONFIG_PSTORE_CONSOLE=y
|
||||
CONFIG_PSTORE_RAM=y
|
||||
CONFIG_SCHEDSTATS=y
|
||||
CONFIG_SCHED_TRACER=y
|
||||
CONFIG_SMARTJOYPLUS_FF=y
|
||||
CONFIG_SND=y
|
||||
CONFIG_SOUND=y
|
||||
|
|
|
|||
|
|
@ -1861,16 +1861,16 @@ config BUILD_ARM_APPENDED_DTB_IMAGE
|
|||
bool "Build a concatenated zImage/dtb by default"
|
||||
depends on OF
|
||||
help
|
||||
Enabling this option will cause a concatenated zImage and list of
|
||||
DTBs to be built by default (instead of a standalone zImage.)
|
||||
The image will built in arch/arm/boot/zImage-dtb
|
||||
Enabling this option will cause a concatenated zImage and DTB to
|
||||
be built by default (instead of a standalone zImage.) The image
|
||||
will built in arch/arm/boot/zImage-dtb.<dtb name>
|
||||
|
||||
config BUILD_ARM_APPENDED_DTB_IMAGE_NAMES
|
||||
string "Default dtb names"
|
||||
config BUILD_ARM_APPENDED_DTB_IMAGE_NAME
|
||||
string "Default dtb name"
|
||||
depends on BUILD_ARM_APPENDED_DTB_IMAGE
|
||||
help
|
||||
Space separated list of names of dtbs to append when
|
||||
building a concatenated zImage-dtb.
|
||||
name of the dtb to append when building a concatenated
|
||||
zImage/dtb.
|
||||
|
||||
# Compressed boot loader in ROM. Yes, we really want to ask about
|
||||
# TEXT and BSS so we preserve their values in the config files.
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ libs-y := arch/arm/lib/ $(libs-y)
|
|||
ifeq ($(CONFIG_XIP_KERNEL),y)
|
||||
KBUILD_IMAGE := xipImage
|
||||
else ifeq ($(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE),y)
|
||||
KBUILD_IMAGE := zImage-dtb
|
||||
KBUILD_IMAGE := zImage-dtb.$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAME)
|
||||
else
|
||||
KBUILD_IMAGE := zImage
|
||||
endif
|
||||
|
|
@ -297,9 +297,6 @@ zinstall uinstall install: vmlinux
|
|||
dtbs: scripts
|
||||
$(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) dtbs
|
||||
|
||||
zImage-dtb: vmlinux scripts dtbs
|
||||
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
|
||||
|
||||
# We use MRPROPER_FILES and CLEAN_FILES now
|
||||
archclean:
|
||||
$(Q)$(MAKE) $(clean)=$(boot)
|
||||
|
|
|
|||
1
arch/arm/boot/.gitignore
vendored
1
arch/arm/boot/.gitignore
vendored
|
|
@ -4,4 +4,3 @@ xipImage
|
|||
bootpImage
|
||||
uImage
|
||||
*.dtb
|
||||
zImage-dtb
|
||||
|
|
@ -28,14 +28,6 @@ export ZRELADDR INITRD_PHYS PARAMS_PHYS
|
|||
|
||||
targets := Image zImage xipImage bootpImage uImage
|
||||
|
||||
DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES))
|
||||
ifneq ($(DTB_NAMES),)
|
||||
DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES))
|
||||
else
|
||||
DTB_LIST := $(dtb-y)
|
||||
endif
|
||||
DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST))
|
||||
|
||||
ifeq ($(CONFIG_XIP_KERNEL),y)
|
||||
|
||||
$(obj)/xipImage: vmlinux FORCE
|
||||
|
|
@ -64,10 +56,6 @@ $(obj)/zImage: $(obj)/compressed/vmlinux FORCE
|
|||
$(call if_changed,objcopy)
|
||||
@$(kecho) ' Kernel: $@ is ready'
|
||||
|
||||
$(obj)/zImage-dtb: $(obj)/zImage $(DTB_OBJS) FORCE
|
||||
$(call if_changed,cat)
|
||||
@echo ' Kernel: $@ is ready'
|
||||
|
||||
endif
|
||||
|
||||
ifneq ($(LOADADDR),)
|
||||
|
|
|
|||
|
|
@ -210,20 +210,13 @@ dtb-$(CONFIG_ARCH_VT8500) += vt8500-bv07.dtb \
|
|||
wm8850-w70v2.dtb
|
||||
dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb
|
||||
|
||||
DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE_NAMES))
|
||||
ifneq ($(DTB_NAMES),)
|
||||
DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES))
|
||||
else
|
||||
DTB_LIST := $(dtb-y)
|
||||
endif
|
||||
|
||||
targets += dtbs
|
||||
targets += $(DTB_LIST)
|
||||
targets += $(dtb-y)
|
||||
endif
|
||||
|
||||
# *.dtb used to be generated in the directory above. Clean out the
|
||||
# old build results so people don't accidentally use them.
|
||||
dtbs: $(addprefix $(obj)/, $(DTB_LIST))
|
||||
dtbs: $(addprefix $(obj)/, $(dtb-y))
|
||||
$(Q)rm -f $(obj)/../*.dtb
|
||||
|
||||
clean-files := *.dtb
|
||||
|
|
|
|||
|
|
@ -21,49 +21,3 @@ config SHARP_SCOOP
|
|||
config FIQ_GLUE
|
||||
bool
|
||||
select FIQ
|
||||
|
||||
config FIQ_DEBUGGER
|
||||
bool "FIQ Mode Serial Debugger"
|
||||
select FIQ
|
||||
select FIQ_GLUE
|
||||
default n
|
||||
help
|
||||
The FIQ serial debugger can accept commands even when the
|
||||
kernel is unresponsive due to being stuck with interrupts
|
||||
disabled.
|
||||
|
||||
|
||||
config FIQ_DEBUGGER_NO_SLEEP
|
||||
bool "Keep serial debugger active"
|
||||
depends on FIQ_DEBUGGER
|
||||
default n
|
||||
help
|
||||
Enables the serial debugger at boot. Passing
|
||||
fiq_debugger.no_sleep on the kernel commandline will
|
||||
override this config option.
|
||||
|
||||
config FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
|
||||
bool "Don't disable wakeup IRQ when debugger is active"
|
||||
depends on FIQ_DEBUGGER
|
||||
default n
|
||||
help
|
||||
Don't disable the wakeup irq when enabling the uart clock. This will
|
||||
cause extra interrupts, but it makes the serial debugger usable with
|
||||
on some MSM radio builds that ignore the uart clock request in power
|
||||
collapse.
|
||||
|
||||
config FIQ_DEBUGGER_CONSOLE
|
||||
bool "Console on FIQ Serial Debugger port"
|
||||
depends on FIQ_DEBUGGER
|
||||
default n
|
||||
help
|
||||
Enables a console so that printk messages are displayed on
|
||||
the debugger serial port as the occur.
|
||||
|
||||
config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE
|
||||
bool "Put the FIQ debugger into console mode by default"
|
||||
depends on FIQ_DEBUGGER_CONSOLE
|
||||
default n
|
||||
help
|
||||
If enabled, this puts the fiq debugger into console mode by default.
|
||||
Otherwise, the fiq debugger will start out in debug mode.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
obj-y += firmware.o
|
||||
|
||||
obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger.o
|
||||
obj-$(CONFIG_FIQ_GLUE) += fiq_glue.o fiq_glue_setup.o
|
||||
obj-$(CONFIG_ICST) += icst.o
|
||||
obj-$(CONFIG_SA1111) += sa1111.o
|
||||
|
|
|
|||
|
|
@ -194,6 +194,23 @@ config CMDLINE
|
|||
entering them here. As a minimum, you should specify the the
|
||||
root device (e.g. root=/dev/nfs).
|
||||
|
||||
choice
|
||||
prompt "Kernel command line type" if CMDLINE != ""
|
||||
default CMDLINE_FROM_BOOTLOADER
|
||||
|
||||
config CMDLINE_FROM_BOOTLOADER
|
||||
bool "Use bootloader kernel arguments if available"
|
||||
help
|
||||
Uses the command-line options passed by the boot loader. If
|
||||
the boot loader doesn't provide any, the default kernel command
|
||||
string provided in CMDLINE will be used.
|
||||
|
||||
config CMDLINE_EXTEND
|
||||
bool "Extend bootloader kernel arguments"
|
||||
help
|
||||
The command-line arguments provided by the boot loader will be
|
||||
appended to the default kernel command string.
|
||||
|
||||
config CMDLINE_FORCE
|
||||
bool "Always use the default kernel command string"
|
||||
help
|
||||
|
|
@ -201,6 +218,22 @@ config CMDLINE_FORCE
|
|||
loader passes other arguments to the kernel.
|
||||
This is useful if you cannot or don't want to change the
|
||||
command-line options your boot loader passes to the kernel.
|
||||
endchoice
|
||||
|
||||
config BUILD_ARM64_APPENDED_DTB_IMAGE
|
||||
bool "Build a concatenated Image.gz/dtb by default"
|
||||
depends on OF
|
||||
help
|
||||
Enabling this option will cause a concatenated Image.gz and list of
|
||||
DTBs to be built by default (instead of a standalone Image.gz.)
|
||||
The image will built in arch/arm64/boot/Image.gz-dtb
|
||||
|
||||
config BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES
|
||||
string "Default dtb names"
|
||||
depends on BUILD_ARM64_APPENDED_DTB_IMAGE
|
||||
help
|
||||
Space separated list of names of dtbs to append when
|
||||
building a concatenated Image.gz-dtb.
|
||||
|
||||
endmenu
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,12 @@ libs-y := arch/arm64/lib/ $(libs-y)
|
|||
libs-y += $(LIBGCC)
|
||||
|
||||
# Default target when executing plain make
|
||||
ifeq ($(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE),y)
|
||||
KBUILD_IMAGE := Image.gz-dtb
|
||||
else
|
||||
KBUILD_IMAGE := Image.gz
|
||||
endif
|
||||
|
||||
KBUILD_DTBS := dtbs
|
||||
|
||||
all: $(KBUILD_IMAGE) $(KBUILD_DTBS)
|
||||
|
|
@ -60,6 +65,9 @@ zinstall install: vmlinux
|
|||
dtbs: scripts
|
||||
$(Q)$(MAKE) $(build)=$(boot)/dts dtbs
|
||||
|
||||
Image.gz-dtb: vmlinux scripts dtbs
|
||||
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
|
||||
|
||||
# We use MRPROPER_FILES and CLEAN_FILES now
|
||||
archclean:
|
||||
$(Q)$(MAKE) $(clean)=$(boot)
|
||||
|
|
|
|||
1
arch/arm64/boot/.gitignore
vendored
1
arch/arm64/boot/.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
Image
|
||||
Image.gz
|
||||
Image.gz-dtb
|
||||
|
|
|
|||
|
|
@ -14,14 +14,27 @@
|
|||
# Based on the ia64 boot/Makefile.
|
||||
#
|
||||
|
||||
include $(srctree)/arch/arm64/boot/dts/Makefile
|
||||
|
||||
targets := Image Image.gz
|
||||
|
||||
DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES))
|
||||
ifneq ($(DTB_NAMES),)
|
||||
DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES))
|
||||
else
|
||||
DTB_LIST := $(dtb-y)
|
||||
endif
|
||||
DTB_OBJS := $(addprefix $(obj)/dts/,$(DTB_LIST))
|
||||
|
||||
$(obj)/Image: vmlinux FORCE
|
||||
$(call if_changed,objcopy)
|
||||
|
||||
$(obj)/Image.gz: $(obj)/Image FORCE
|
||||
$(call if_changed,gzip)
|
||||
|
||||
$(obj)/Image.gz-dtb: $(obj)/Image.gz $(DTB_OBJS) FORCE
|
||||
$(call if_changed,cat)
|
||||
|
||||
install: $(obj)/Image
|
||||
$(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
|
||||
$(obj)/Image System.map "$(INSTALL_PATH)"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb foundation-v8.dtb
|
||||
|
||||
targets += dtbs
|
||||
targets += $(dtb-y)
|
||||
|
||||
dtbs: $(addprefix $(obj)/, $(dtb-y))
|
||||
DTB_NAMES := $(subst $\",,$(CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE_NAMES))
|
||||
ifneq ($(DTB_NAMES),)
|
||||
DTB_LIST := $(addsuffix .dtb,$(DTB_NAMES))
|
||||
else
|
||||
DTB_LIST := $(dtb-y)
|
||||
endif
|
||||
targets += $(DTB_LIST)
|
||||
|
||||
dtbs: $(addprefix $(obj)/, $(DTB_LIST))
|
||||
|
||||
clean-files := *.dtb
|
||||
|
|
|
|||
|
|
@ -158,17 +158,23 @@ static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old,
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define cmpxchg(ptr,o,n) \
|
||||
((__typeof__(*(ptr)))__cmpxchg_mb((ptr), \
|
||||
(unsigned long)(o), \
|
||||
(unsigned long)(n), \
|
||||
sizeof(*(ptr))))
|
||||
#define cmpxchg(ptr, o, n) \
|
||||
({ \
|
||||
__typeof__(*(ptr)) __ret; \
|
||||
__ret = (__typeof__(*(ptr))) \
|
||||
__cmpxchg_mb((ptr), (unsigned long)(o), (unsigned long)(n), \
|
||||
sizeof(*(ptr))); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#define cmpxchg_local(ptr,o,n) \
|
||||
((__typeof__(*(ptr)))__cmpxchg((ptr), \
|
||||
(unsigned long)(o), \
|
||||
(unsigned long)(n), \
|
||||
sizeof(*(ptr))))
|
||||
#define cmpxchg_local(ptr, o, n) \
|
||||
({ \
|
||||
__typeof__(*(ptr)) __ret; \
|
||||
__ret = (__typeof__(*(ptr))) \
|
||||
__cmpxchg((ptr), (unsigned long)(o), \
|
||||
(unsigned long)(n), sizeof(*(ptr))); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#define cmpxchg64(ptr,o,n) cmpxchg((ptr),(o),(n))
|
||||
#define cmpxchg64_local(ptr,o,n) cmpxchg_local((ptr),(o),(n))
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
#ifndef __ASM_PGTABLE_3LEVEL_TYPES_H
|
||||
#define __ASM_PGTABLE_3LEVEL_TYPES_H
|
||||
|
||||
#include <asm/types.h>
|
||||
|
||||
typedef u64 pteval_t;
|
||||
typedef u64 pmdval_t;
|
||||
typedef u64 pgdval_t;
|
||||
|
|
|
|||
|
|
@ -92,5 +92,6 @@
|
|||
#define TCR_TG1_64K (UL(1) << 30)
|
||||
#define TCR_IPS_40BIT (UL(2) << 32)
|
||||
#define TCR_ASID16 (UL(1) << 36)
|
||||
#define TCR_TBI0 (UL(1) << 37)
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -423,6 +423,7 @@ el0_da:
|
|||
* Data abort handling
|
||||
*/
|
||||
mrs x0, far_el1
|
||||
bic x0, x0, #(0xff << 56)
|
||||
disable_step x1
|
||||
isb
|
||||
enable_dbg
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ ENTRY(__cpu_setup)
|
|||
* both user and kernel.
|
||||
*/
|
||||
ldr x10, =TCR_TxSZ(VA_BITS) | TCR_FLAGS | TCR_IPS_40BIT | \
|
||||
TCR_ASID16 | (1 << 31)
|
||||
TCR_ASID16 | TCR_TBI0 | (1 << 31)
|
||||
#ifdef CONFIG_ARM64_64K_PAGES
|
||||
orr x10, x10, TCR_TG0_64K
|
||||
orr x10, x10, TCR_TG1_64K
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ MODULE_DESCRIPTION("DCC TTY Driver");
|
|||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("1.0");
|
||||
|
||||
static spinlock_t g_dcc_tty_lock = SPIN_LOCK_UNLOCKED;
|
||||
DEFINE_SPINLOCK(g_dcc_tty_lock);
|
||||
static struct hrtimer g_dcc_timer;
|
||||
static char g_dcc_buffer[16];
|
||||
static int g_dcc_buffer_head;
|
||||
|
|
@ -80,8 +80,8 @@ static void dcc_poll_locked(void)
|
|||
);
|
||||
if (rch >= 0) {
|
||||
ch = rch;
|
||||
tty_insert_flip_string(g_dcc_tty, &ch, 1);
|
||||
tty_flip_buffer_push(g_dcc_tty);
|
||||
tty_insert_flip_string(g_dcc_tty->port, &ch, 1);
|
||||
tty_flip_buffer_push(g_dcc_tty->port);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ config CPU_FREQ_GOV_ONDEMAND
|
|||
|
||||
config CPU_FREQ_GOV_INTERACTIVE
|
||||
tristate "'interactive' cpufreq policy governor"
|
||||
default n
|
||||
help
|
||||
'interactive' - This driver adds a dynamic cpufreq policy governor
|
||||
designed for latency-sensitive workloads.
|
||||
|
|
|
|||
|
|
@ -132,6 +132,16 @@ bool have_governor_per_policy(void)
|
|||
{
|
||||
return cpufreq_driver->have_governor_per_policy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(have_governor_per_policy);
|
||||
|
||||
struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy)
|
||||
{
|
||||
if (have_governor_per_policy())
|
||||
return &policy->kobj;
|
||||
else
|
||||
return cpufreq_global_kobject;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_governor_parent_kobj);
|
||||
|
||||
static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,14 +30,6 @@
|
|||
|
||||
#include "cpufreq_governor.h"
|
||||
|
||||
static struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy)
|
||||
{
|
||||
if (have_governor_per_policy())
|
||||
return &policy->kobj;
|
||||
else
|
||||
return cpufreq_global_kobject;
|
||||
}
|
||||
|
||||
static struct attribute_group *get_sysfs_attr(struct dbs_data *dbs_data)
|
||||
{
|
||||
if (have_governor_per_policy())
|
||||
|
|
@ -404,6 +396,7 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
|
|||
|
||||
mutex_lock(&dbs_data->mutex);
|
||||
mutex_destroy(&cpu_cdbs->timer_mutex);
|
||||
cpu_cdbs->cur_policy = NULL;
|
||||
|
||||
mutex_unlock(&dbs_data->mutex);
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,10 @@ struct cpufreq_interactive_cpuinfo {
|
|||
u64 cputime_speedadj_timestamp;
|
||||
struct cpufreq_policy *policy;
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
spinlock_t target_freq_lock; /*protects target freq */
|
||||
unsigned int target_freq;
|
||||
unsigned int floor_freq;
|
||||
unsigned int max_freq;
|
||||
u64 floor_validate_time;
|
||||
u64 hispeed_validate_time;
|
||||
struct rw_semaphore enable_sem;
|
||||
|
|
@ -398,6 +400,7 @@ static void cpufreq_interactive_timer(unsigned long data)
|
|||
if (WARN_ON_ONCE(!delta_time))
|
||||
goto rearm;
|
||||
|
||||
spin_lock_irqsave(&pcpu->target_freq_lock, flags);
|
||||
do_div(cputime_speedadj, delta_time);
|
||||
loadadjfreq = (unsigned int)cputime_speedadj * 100;
|
||||
cpu_load = loadadjfreq / pcpu->target_freq;
|
||||
|
|
@ -423,6 +426,7 @@ static void cpufreq_interactive_timer(unsigned long data)
|
|||
trace_cpufreq_interactive_notyet(
|
||||
data, cpu_load, pcpu->target_freq,
|
||||
pcpu->policy->cur, new_freq);
|
||||
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
|
||||
goto rearm;
|
||||
}
|
||||
|
||||
|
|
@ -430,8 +434,10 @@ static void cpufreq_interactive_timer(unsigned long data)
|
|||
|
||||
if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table,
|
||||
new_freq, CPUFREQ_RELATION_L,
|
||||
&index))
|
||||
&index)) {
|
||||
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
|
||||
goto rearm;
|
||||
}
|
||||
|
||||
new_freq = pcpu->freq_table[index].frequency;
|
||||
|
||||
|
|
@ -445,6 +451,7 @@ static void cpufreq_interactive_timer(unsigned long data)
|
|||
trace_cpufreq_interactive_notyet(
|
||||
data, cpu_load, pcpu->target_freq,
|
||||
pcpu->policy->cur, new_freq);
|
||||
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
|
||||
goto rearm;
|
||||
}
|
||||
}
|
||||
|
|
@ -466,6 +473,7 @@ static void cpufreq_interactive_timer(unsigned long data)
|
|||
trace_cpufreq_interactive_already(
|
||||
data, cpu_load, pcpu->target_freq,
|
||||
pcpu->policy->cur, new_freq);
|
||||
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
|
||||
goto rearm_if_notmax;
|
||||
}
|
||||
|
||||
|
|
@ -473,6 +481,7 @@ static void cpufreq_interactive_timer(unsigned long data)
|
|||
pcpu->policy->cur, new_freq);
|
||||
|
||||
pcpu->target_freq = new_freq;
|
||||
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
|
||||
spin_lock_irqsave(&speedchange_cpumask_lock, flags);
|
||||
cpumask_set_cpu(data, &speedchange_cpumask);
|
||||
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
|
||||
|
|
@ -616,16 +625,17 @@ static void cpufreq_interactive_boost(void)
|
|||
{
|
||||
int i;
|
||||
int anyboost = 0;
|
||||
unsigned long flags;
|
||||
unsigned long flags[2];
|
||||
struct cpufreq_interactive_cpuinfo *pcpu;
|
||||
struct cpufreq_interactive_tunables *tunables;
|
||||
|
||||
spin_lock_irqsave(&speedchange_cpumask_lock, flags);
|
||||
spin_lock_irqsave(&speedchange_cpumask_lock, flags[0]);
|
||||
|
||||
for_each_online_cpu(i) {
|
||||
pcpu = &per_cpu(cpuinfo, i);
|
||||
tunables = pcpu->policy->governor_data;
|
||||
|
||||
spin_lock_irqsave(&pcpu->target_freq_lock, flags[1]);
|
||||
if (pcpu->target_freq < tunables->hispeed_freq) {
|
||||
pcpu->target_freq = tunables->hispeed_freq;
|
||||
cpumask_set_cpu(i, &speedchange_cpumask);
|
||||
|
|
@ -641,9 +651,10 @@ static void cpufreq_interactive_boost(void)
|
|||
|
||||
pcpu->floor_freq = tunables->hispeed_freq;
|
||||
pcpu->floor_validate_time = ktime_to_us(ktime_get());
|
||||
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags[1]);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags);
|
||||
spin_unlock_irqrestore(&speedchange_cpumask_lock, flags[0]);
|
||||
|
||||
if (anyboost)
|
||||
wake_up_process(speedchange_task);
|
||||
|
|
@ -752,7 +763,7 @@ static ssize_t show_target_loads(
|
|||
ret += sprintf(buf + ret, "%u%s", tunables->target_loads[i],
|
||||
i & 0x1 ? ":" : " ");
|
||||
|
||||
ret += sprintf(buf + --ret, "\n");
|
||||
sprintf(buf + ret - 1, "\n");
|
||||
spin_unlock_irqrestore(&tunables->target_loads_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -792,7 +803,7 @@ static ssize_t show_above_hispeed_delay(
|
|||
tunables->above_hispeed_delay[i],
|
||||
i & 0x1 ? ":" : " ");
|
||||
|
||||
ret += sprintf(buf + --ret, "\n");
|
||||
sprintf(buf + ret - 1, "\n");
|
||||
spin_unlock_irqrestore(&tunables->above_hispeed_delay_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -937,6 +948,7 @@ static ssize_t store_boost(struct cpufreq_interactive_tunables *tunables,
|
|||
trace_cpufreq_interactive_boost("on");
|
||||
cpufreq_interactive_boost();
|
||||
} else {
|
||||
tunables->boostpulse_endtime = ktime_to_us(ktime_get());
|
||||
trace_cpufreq_interactive_unboost("off");
|
||||
}
|
||||
|
||||
|
|
@ -1154,6 +1166,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
|
|||
struct cpufreq_interactive_cpuinfo *pcpu;
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
struct cpufreq_interactive_tunables *tunables;
|
||||
unsigned long flags;
|
||||
|
||||
if (have_governor_per_policy())
|
||||
tunables = policy->governor_data;
|
||||
|
|
@ -1178,13 +1191,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rc = sysfs_create_group(get_governor_parent_kobj(policy),
|
||||
get_sysfs_attr());
|
||||
if (rc) {
|
||||
kfree(tunables);
|
||||
return rc;
|
||||
}
|
||||
|
||||
tunables->usage_count = 1;
|
||||
tunables->above_hispeed_delay = default_above_hispeed_delay;
|
||||
tunables->nabove_hispeed_delay =
|
||||
|
|
@ -1200,16 +1206,26 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
|
|||
spin_lock_init(&tunables->target_loads_lock);
|
||||
spin_lock_init(&tunables->above_hispeed_delay_lock);
|
||||
|
||||
policy->governor_data = tunables;
|
||||
if (!have_governor_per_policy())
|
||||
common_tunables = tunables;
|
||||
|
||||
rc = sysfs_create_group(get_governor_parent_kobj(policy),
|
||||
get_sysfs_attr());
|
||||
if (rc) {
|
||||
kfree(tunables);
|
||||
policy->governor_data = NULL;
|
||||
if (!have_governor_per_policy())
|
||||
common_tunables = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!policy->governor->initialized) {
|
||||
idle_notifier_register(&cpufreq_interactive_idle_nb);
|
||||
cpufreq_register_notifier(&cpufreq_notifier_block,
|
||||
CPUFREQ_TRANSITION_NOTIFIER);
|
||||
}
|
||||
|
||||
policy->governor_data = tunables;
|
||||
if (!have_governor_per_policy())
|
||||
common_tunables = tunables;
|
||||
|
||||
break;
|
||||
|
||||
case CPUFREQ_GOV_POLICY_EXIT:
|
||||
|
|
@ -1246,6 +1262,7 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
|
|||
ktime_to_us(ktime_get());
|
||||
pcpu->hispeed_validate_time =
|
||||
pcpu->floor_validate_time;
|
||||
pcpu->max_freq = policy->max;
|
||||
down_write(&pcpu->enable_sem);
|
||||
del_timer_sync(&pcpu->cpu_timer);
|
||||
del_timer_sync(&pcpu->cpu_slack_timer);
|
||||
|
|
@ -1281,29 +1298,37 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
|
|||
for_each_cpu(j, policy->cpus) {
|
||||
pcpu = &per_cpu(cpuinfo, j);
|
||||
|
||||
/* hold write semaphore to avoid race */
|
||||
down_write(&pcpu->enable_sem);
|
||||
down_read(&pcpu->enable_sem);
|
||||
if (pcpu->governor_enabled == 0) {
|
||||
up_write(&pcpu->enable_sem);
|
||||
up_read(&pcpu->enable_sem);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* update target_freq firstly */
|
||||
spin_lock_irqsave(&pcpu->target_freq_lock, flags);
|
||||
if (policy->max < pcpu->target_freq)
|
||||
pcpu->target_freq = policy->max;
|
||||
else if (policy->min > pcpu->target_freq)
|
||||
pcpu->target_freq = policy->min;
|
||||
|
||||
/* Reschedule timer.
|
||||
spin_unlock_irqrestore(&pcpu->target_freq_lock, flags);
|
||||
up_read(&pcpu->enable_sem);
|
||||
|
||||
/* Reschedule timer only if policy->max is raised.
|
||||
* Delete the timers, else the timer callback may
|
||||
* return without re-arm the timer when failed
|
||||
* acquire the semaphore. This race may cause timer
|
||||
* stopped unexpectedly.
|
||||
*/
|
||||
del_timer_sync(&pcpu->cpu_timer);
|
||||
del_timer_sync(&pcpu->cpu_slack_timer);
|
||||
cpufreq_interactive_timer_start(tunables, j);
|
||||
up_write(&pcpu->enable_sem);
|
||||
|
||||
if (policy->max > pcpu->max_freq) {
|
||||
down_write(&pcpu->enable_sem);
|
||||
del_timer_sync(&pcpu->cpu_timer);
|
||||
del_timer_sync(&pcpu->cpu_slack_timer);
|
||||
cpufreq_interactive_timer_start(tunables, j);
|
||||
up_write(&pcpu->enable_sem);
|
||||
}
|
||||
|
||||
pcpu->max_freq = policy->max;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1339,6 +1364,7 @@ static int __init cpufreq_interactive_init(void)
|
|||
init_timer(&pcpu->cpu_slack_timer);
|
||||
pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer;
|
||||
spin_lock_init(&pcpu->load_lock);
|
||||
spin_lock_init(&pcpu->target_freq_lock);
|
||||
init_rwsem(&pcpu->enable_sem);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ static struct gpio_desc *gpio_to_desc(unsigned gpio)
|
|||
*/
|
||||
static int desc_to_gpio(const struct gpio_desc *desc)
|
||||
{
|
||||
return desc->chip->base + gpio_chip_hwgpio(desc);
|
||||
return desc - &gpio_desc[0];
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1214,15 +1214,14 @@ int gpiochip_add(struct gpio_chip *chip)
|
|||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
INIT_LIST_HEAD(&chip->pin_ranges);
|
||||
#endif
|
||||
|
||||
of_gpiochip_add(chip);
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
|
|
@ -1235,6 +1234,9 @@ int gpiochip_add(struct gpio_chip *chip)
|
|||
chip->label ? : "generic");
|
||||
|
||||
return 0;
|
||||
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&gpio_lock, flags);
|
||||
fail:
|
||||
/* failures here can mean systems won't boot... */
|
||||
pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",
|
||||
|
|
|
|||
|
|
@ -768,6 +768,8 @@ static const char *keys[KEY_MAX + 1] = {
|
|||
[KEY_ALTERASE] = "AlternateErase", [KEY_CANCEL] = "Cancel",
|
||||
[KEY_BRIGHTNESSDOWN] = "BrightnessDown", [KEY_BRIGHTNESSUP] = "BrightnessUp",
|
||||
[KEY_MEDIA] = "Media", [KEY_UNKNOWN] = "Unknown",
|
||||
[BTN_DPAD_UP] = "BtnDPadUp", [BTN_DPAD_DOWN] = "BtnDPadDown",
|
||||
[BTN_DPAD_LEFT] = "BtnDPadLeft", [BTN_DPAD_RIGHT] = "BtnDPadRight",
|
||||
[BTN_0] = "Btn0", [BTN_1] = "Btn1",
|
||||
[BTN_2] = "Btn2", [BTN_3] = "Btn3",
|
||||
[BTN_4] = "Btn4", [BTN_5] = "Btn5",
|
||||
|
|
@ -797,7 +799,8 @@ static const char *keys[KEY_MAX + 1] = {
|
|||
[BTN_TOOL_MOUSE] = "ToolMouse", [BTN_TOOL_LENS] = "ToolLens",
|
||||
[BTN_TOUCH] = "Touch", [BTN_STYLUS] = "Stylus",
|
||||
[BTN_STYLUS2] = "Stylus2", [BTN_TOOL_DOUBLETAP] = "ToolDoubleTap",
|
||||
[BTN_TOOL_TRIPLETAP] = "ToolTripleTap", [BTN_GEAR_DOWN] = "WheelBtn",
|
||||
[BTN_TOOL_TRIPLETAP] = "ToolTripleTap", [BTN_TOOL_QUADTAP] = "ToolQuadrupleTap",
|
||||
[BTN_GEAR_DOWN] = "WheelBtn",
|
||||
[BTN_GEAR_UP] = "Gear up", [KEY_OK] = "Ok",
|
||||
[KEY_SELECT] = "Select", [KEY_GOTO] = "Goto",
|
||||
[KEY_CLEAR] = "Clear", [KEY_POWER2] = "Power2",
|
||||
|
|
@ -852,6 +855,16 @@ static const char *keys[KEY_MAX + 1] = {
|
|||
[KEY_KBDILLUMDOWN] = "KbdIlluminationDown",
|
||||
[KEY_KBDILLUMUP] = "KbdIlluminationUp",
|
||||
[KEY_SWITCHVIDEOMODE] = "SwitchVideoMode",
|
||||
[KEY_BUTTONCONFIG] = "ButtonConfig",
|
||||
[KEY_TASKMANAGER] = "TaskManager",
|
||||
[KEY_JOURNAL] = "Journal",
|
||||
[KEY_CONTROLPANEL] = "ControlPanel",
|
||||
[KEY_APPSELECT] = "AppSelect",
|
||||
[KEY_SCREENSAVER] = "ScreenSaver",
|
||||
[KEY_VOICECOMMAND] = "VoiceCommand",
|
||||
[KEY_BRIGHTNESS_MIN] = "BrightnessMin",
|
||||
[KEY_BRIGHTNESS_MAX] = "BrightnessMax",
|
||||
[KEY_BRIGHTNESS_AUTO] = "BrightnessAuto",
|
||||
};
|
||||
|
||||
static const char *relatives[REL_MAX + 1] = {
|
||||
|
|
|
|||
|
|
@ -714,6 +714,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0x06c: map_key_clear(KEY_YELLOW); break;
|
||||
case 0x06d: map_key_clear(KEY_ZOOM); break;
|
||||
|
||||
case 0x06f: map_key_clear(KEY_BRIGHTNESSUP); break;
|
||||
case 0x070: map_key_clear(KEY_BRIGHTNESSDOWN); break;
|
||||
case 0x072: map_key_clear(KEY_BRIGHTNESS_TOGGLE); break;
|
||||
case 0x073: map_key_clear(KEY_BRIGHTNESS_MIN); break;
|
||||
case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX); break;
|
||||
case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO); break;
|
||||
|
||||
case 0x082: map_key_clear(KEY_VIDEO_NEXT); break;
|
||||
case 0x083: map_key_clear(KEY_LAST); break;
|
||||
case 0x084: map_key_clear(KEY_ENTER); break;
|
||||
|
|
@ -754,6 +761,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0x0bf: map_key_clear(KEY_SLOW); break;
|
||||
|
||||
case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break;
|
||||
case 0x0cf: map_key_clear(KEY_VOICECOMMAND); break;
|
||||
case 0x0e0: map_abs_clear(ABS_VOLUME); break;
|
||||
case 0x0e2: map_key_clear(KEY_MUTE); break;
|
||||
case 0x0e5: map_key_clear(KEY_BASSBOOST); break;
|
||||
|
|
@ -761,6 +769,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break;
|
||||
case 0x0f5: map_key_clear(KEY_SLOW); break;
|
||||
|
||||
case 0x181: map_key_clear(KEY_BUTTONCONFIG); break;
|
||||
case 0x182: map_key_clear(KEY_BOOKMARKS); break;
|
||||
case 0x183: map_key_clear(KEY_CONFIG); break;
|
||||
case 0x184: map_key_clear(KEY_WORDPROCESSOR); break;
|
||||
|
|
@ -774,6 +783,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0x18c: map_key_clear(KEY_VOICEMAIL); break;
|
||||
case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break;
|
||||
case 0x18e: map_key_clear(KEY_CALENDAR); break;
|
||||
case 0x18f: map_key_clear(KEY_TASKMANAGER); break;
|
||||
case 0x190: map_key_clear(KEY_JOURNAL); break;
|
||||
case 0x191: map_key_clear(KEY_FINANCE); break;
|
||||
case 0x192: map_key_clear(KEY_CALC); break;
|
||||
case 0x193: map_key_clear(KEY_PLAYER); break;
|
||||
|
|
@ -782,10 +793,16 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0x199: map_key_clear(KEY_CHAT); break;
|
||||
case 0x19c: map_key_clear(KEY_LOGOFF); break;
|
||||
case 0x19e: map_key_clear(KEY_COFFEE); break;
|
||||
case 0x19f: map_key_clear(KEY_CONTROLPANEL); break;
|
||||
case 0x1a2: map_key_clear(KEY_APPSELECT); break;
|
||||
case 0x1a3: map_key_clear(KEY_NEXT); break;
|
||||
case 0x1a4: map_key_clear(KEY_PREVIOUS); break;
|
||||
case 0x1a6: map_key_clear(KEY_HELP); break;
|
||||
case 0x1a7: map_key_clear(KEY_DOCUMENTS); break;
|
||||
case 0x1ab: map_key_clear(KEY_SPELLCHECK); break;
|
||||
case 0x1ae: map_key_clear(KEY_KEYBOARD); break;
|
||||
case 0x1b1: map_key_clear(KEY_SCREENSAVER); break;
|
||||
case 0x1b4: map_key_clear(KEY_FILE); break;
|
||||
case 0x1b6: map_key_clear(KEY_IMAGES); break;
|
||||
case 0x1b7: map_key_clear(KEY_AUDIO); break;
|
||||
case 0x1b8: map_key_clear(KEY_VIDEO); break;
|
||||
|
|
|
|||
|
|
@ -659,36 +659,66 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert configs to something easy to use in C code
|
||||
*/
|
||||
#if defined(CONFIG_CMDLINE_FORCE)
|
||||
static const int overwrite_incoming_cmdline = 1;
|
||||
static const int read_dt_cmdline;
|
||||
static const int concat_cmdline;
|
||||
#elif defined(CONFIG_CMDLINE_EXTEND)
|
||||
static const int overwrite_incoming_cmdline;
|
||||
static const int read_dt_cmdline = 1;
|
||||
static const int concat_cmdline = 1;
|
||||
#else /* CMDLINE_FROM_BOOTLOADER */
|
||||
static const int overwrite_incoming_cmdline;
|
||||
static const int read_dt_cmdline = 1;
|
||||
static const int concat_cmdline;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CMDLINE
|
||||
static const char *config_cmdline = CONFIG_CMDLINE;
|
||||
#else
|
||||
static const char *config_cmdline = "";
|
||||
#endif
|
||||
|
||||
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
unsigned long l;
|
||||
char *p;
|
||||
unsigned long l = 0;
|
||||
char *p = NULL;
|
||||
char *cmdline = data;
|
||||
|
||||
pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
|
||||
|
||||
if (depth != 1 || !data ||
|
||||
if (depth != 1 || !cmdline ||
|
||||
(strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
|
||||
return 0;
|
||||
|
||||
early_init_dt_check_for_initrd(node);
|
||||
|
||||
/* Retrieve command line */
|
||||
p = of_get_flat_dt_prop(node, "bootargs", &l);
|
||||
if (p != NULL && l > 0)
|
||||
strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE));
|
||||
/* Put CONFIG_CMDLINE in if forced or if data had nothing in it to start */
|
||||
if (overwrite_incoming_cmdline || !cmdline[0])
|
||||
strlcpy(cmdline, config_cmdline, COMMAND_LINE_SIZE);
|
||||
|
||||
/*
|
||||
* CONFIG_CMDLINE is meant to be a default in case nothing else
|
||||
* managed to set the command line, unless CONFIG_CMDLINE_FORCE
|
||||
* is set in which case we override whatever was found earlier.
|
||||
*/
|
||||
#ifdef CONFIG_CMDLINE
|
||||
#ifndef CONFIG_CMDLINE_FORCE
|
||||
if (!((char *)data)[0])
|
||||
#endif
|
||||
strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
|
||||
#endif /* CONFIG_CMDLINE */
|
||||
/* Retrieve command line unless forcing */
|
||||
if (read_dt_cmdline)
|
||||
p = of_get_flat_dt_prop(node, "bootargs", &l);
|
||||
|
||||
if (p != NULL && l > 0) {
|
||||
if (concat_cmdline) {
|
||||
int cmdline_len;
|
||||
int copy_len;
|
||||
strlcat(cmdline, " ", COMMAND_LINE_SIZE);
|
||||
cmdline_len = strlen(cmdline);
|
||||
copy_len = COMMAND_LINE_SIZE - cmdline_len - 1;
|
||||
copy_len = min((int)l, copy_len);
|
||||
strncpy(cmdline + cmdline_len, p, copy_len);
|
||||
cmdline[cmdline_len + copy_len] = '\0';
|
||||
} else {
|
||||
strlcpy(cmdline, p, min((int)l, COMMAND_LINE_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
pr_debug("Command line is: %s\n", (char*)data);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,14 @@ config ANDROID_BINDER_IPC
|
|||
Android process, using Binder to identify, invoke and pass arguments
|
||||
between said processes.
|
||||
|
||||
config ANDROID_BINDER_IPC_32BIT
|
||||
bool
|
||||
default y
|
||||
depends on !64BIT && ANDROID_BINDER_IPC
|
||||
---help---
|
||||
Enable to support an old 32-bit Android user-space. Breaks the new
|
||||
Android user-space.
|
||||
|
||||
config ASHMEM
|
||||
bool "Enable the Anonymous Shared Memory Subsystem"
|
||||
default n
|
||||
|
|
@ -110,6 +118,8 @@ config SW_SYNC_USER
|
|||
|
||||
source "drivers/staging/android/ion/Kconfig"
|
||||
|
||||
source "drivers/staging/android/fiq_debugger/Kconfig"
|
||||
|
||||
endif # if ANDROID
|
||||
|
||||
endmenu
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
ccflags-y += -I$(src) # needed for trace events
|
||||
|
||||
obj-y += ion/
|
||||
obj-$(CONFIG_FIQ_DEBUGGER) += fiq_debugger/
|
||||
|
||||
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
|
||||
obj-$(CONFIG_ASHMEM) += ashmem.o
|
||||
|
|
|
|||
|
|
@ -229,8 +229,8 @@ struct binder_node {
|
|||
int internal_strong_refs;
|
||||
int local_weak_refs;
|
||||
int local_strong_refs;
|
||||
void __user *ptr;
|
||||
void __user *cookie;
|
||||
binder_uintptr_t ptr;
|
||||
binder_uintptr_t cookie;
|
||||
unsigned has_strong_ref:1;
|
||||
unsigned pending_strong_ref:1;
|
||||
unsigned has_weak_ref:1;
|
||||
|
|
@ -243,7 +243,7 @@ struct binder_node {
|
|||
|
||||
struct binder_ref_death {
|
||||
struct binder_work work;
|
||||
void __user *cookie;
|
||||
binder_uintptr_t cookie;
|
||||
};
|
||||
|
||||
struct binder_ref {
|
||||
|
|
@ -516,14 +516,14 @@ static void binder_insert_allocated_buffer(struct binder_proc *proc,
|
|||
}
|
||||
|
||||
static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc,
|
||||
void __user *user_ptr)
|
||||
uintptr_t user_ptr)
|
||||
{
|
||||
struct rb_node *n = proc->allocated_buffers.rb_node;
|
||||
struct binder_buffer *buffer;
|
||||
struct binder_buffer *kern_ptr;
|
||||
|
||||
kern_ptr = user_ptr - proc->user_buffer_offset
|
||||
- offsetof(struct binder_buffer, data);
|
||||
kern_ptr = (struct binder_buffer *)(user_ptr - proc->user_buffer_offset
|
||||
- offsetof(struct binder_buffer, data));
|
||||
|
||||
while (n) {
|
||||
buffer = rb_entry(n, struct binder_buffer, rb_node);
|
||||
|
|
@ -792,7 +792,7 @@ static void binder_delete_free_buffer(struct binder_proc *proc,
|
|||
list_del(&buffer->entry);
|
||||
if (free_page_start || free_page_end) {
|
||||
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
|
||||
"%d: merge free, buffer %p do not share page%s%s with with %p or %p\n",
|
||||
"%d: merge free, buffer %p do not share page%s%s with %p or %p\n",
|
||||
proc->pid, buffer, free_page_start ? "" : " end",
|
||||
free_page_end ? "" : " start", prev, next);
|
||||
binder_update_page_range(proc, 0, free_page_start ?
|
||||
|
|
@ -857,7 +857,7 @@ static void binder_free_buf(struct binder_proc *proc,
|
|||
}
|
||||
|
||||
static struct binder_node *binder_get_node(struct binder_proc *proc,
|
||||
void __user *ptr)
|
||||
binder_uintptr_t ptr)
|
||||
{
|
||||
struct rb_node *n = proc->nodes.rb_node;
|
||||
struct binder_node *node;
|
||||
|
|
@ -876,8 +876,8 @@ static struct binder_node *binder_get_node(struct binder_proc *proc,
|
|||
}
|
||||
|
||||
static struct binder_node *binder_new_node(struct binder_proc *proc,
|
||||
void __user *ptr,
|
||||
void __user *cookie)
|
||||
binder_uintptr_t ptr,
|
||||
binder_uintptr_t cookie)
|
||||
{
|
||||
struct rb_node **p = &proc->nodes.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
|
|
@ -909,9 +909,9 @@ static struct binder_node *binder_new_node(struct binder_proc *proc,
|
|||
INIT_LIST_HEAD(&node->work.entry);
|
||||
INIT_LIST_HEAD(&node->async_todo);
|
||||
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
||||
"%d:%d node %d u%p c%p created\n",
|
||||
"%d:%d node %d u%016llx c%016llx created\n",
|
||||
proc->pid, current->pid, node->debug_id,
|
||||
node->ptr, node->cookie);
|
||||
(u64)node->ptr, (u64)node->cookie);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
|
@ -1227,9 +1227,9 @@ static void binder_send_failed_reply(struct binder_transaction *t,
|
|||
|
||||
static void binder_transaction_buffer_release(struct binder_proc *proc,
|
||||
struct binder_buffer *buffer,
|
||||
size_t *failed_at)
|
||||
binder_size_t *failed_at)
|
||||
{
|
||||
size_t *offp, *off_end;
|
||||
binder_size_t *offp, *off_end;
|
||||
int debug_id = buffer->debug_id;
|
||||
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
|
|
@ -1240,7 +1240,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|||
if (buffer->target_node)
|
||||
binder_dec_node(buffer->target_node, 1, 0);
|
||||
|
||||
offp = (size_t *)(buffer->data + ALIGN(buffer->data_size, sizeof(void *)));
|
||||
offp = (binder_size_t *)(buffer->data +
|
||||
ALIGN(buffer->data_size, sizeof(void *)));
|
||||
if (failed_at)
|
||||
off_end = failed_at;
|
||||
else
|
||||
|
|
@ -1249,9 +1250,9 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|||
struct flat_binder_object *fp;
|
||||
if (*offp > buffer->data_size - sizeof(*fp) ||
|
||||
buffer->data_size < sizeof(*fp) ||
|
||||
!IS_ALIGNED(*offp, sizeof(void *))) {
|
||||
pr_err("transaction release %d bad offset %zd, size %zd\n",
|
||||
debug_id, *offp, buffer->data_size);
|
||||
!IS_ALIGNED(*offp, sizeof(u32))) {
|
||||
pr_err("transaction release %d bad offset %lld, size %zd\n",
|
||||
debug_id, (u64)*offp, buffer->data_size);
|
||||
continue;
|
||||
}
|
||||
fp = (struct flat_binder_object *)(buffer->data + *offp);
|
||||
|
|
@ -1260,20 +1261,20 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|||
case BINDER_TYPE_WEAK_BINDER: {
|
||||
struct binder_node *node = binder_get_node(proc, fp->binder);
|
||||
if (node == NULL) {
|
||||
pr_err("transaction release %d bad node %p\n",
|
||||
debug_id, fp->binder);
|
||||
pr_err("transaction release %d bad node %016llx\n",
|
||||
debug_id, (u64)fp->binder);
|
||||
break;
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
" node %d u%p\n",
|
||||
node->debug_id, node->ptr);
|
||||
" node %d u%016llx\n",
|
||||
node->debug_id, (u64)node->ptr);
|
||||
binder_dec_node(node, fp->type == BINDER_TYPE_BINDER, 0);
|
||||
} break;
|
||||
case BINDER_TYPE_HANDLE:
|
||||
case BINDER_TYPE_WEAK_HANDLE: {
|
||||
struct binder_ref *ref = binder_get_ref(proc, fp->handle);
|
||||
if (ref == NULL) {
|
||||
pr_err("transaction release %d bad handle %ld\n",
|
||||
pr_err("transaction release %d bad handle %d\n",
|
||||
debug_id, fp->handle);
|
||||
break;
|
||||
}
|
||||
|
|
@ -1285,13 +1286,13 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
|
|||
|
||||
case BINDER_TYPE_FD:
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
" fd %ld\n", fp->handle);
|
||||
" fd %d\n", fp->handle);
|
||||
if (failed_at)
|
||||
task_close_fd(proc, fp->handle);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("transaction release %d bad object type %lx\n",
|
||||
pr_err("transaction release %d bad object type %x\n",
|
||||
debug_id, fp->type);
|
||||
break;
|
||||
}
|
||||
|
|
@ -1304,7 +1305,8 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
{
|
||||
struct binder_transaction *t;
|
||||
struct binder_work *tcomplete;
|
||||
size_t *offp, *off_end;
|
||||
binder_size_t *offp, *off_end;
|
||||
binder_size_t off_min;
|
||||
struct binder_proc *target_proc;
|
||||
struct binder_thread *target_thread = NULL;
|
||||
struct binder_node *target_node = NULL;
|
||||
|
|
@ -1437,18 +1439,20 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
|
||||
if (reply)
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
"%d:%d BC_REPLY %d -> %d:%d, data %p-%p size %zd-%zd\n",
|
||||
"%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld\n",
|
||||
proc->pid, thread->pid, t->debug_id,
|
||||
target_proc->pid, target_thread->pid,
|
||||
tr->data.ptr.buffer, tr->data.ptr.offsets,
|
||||
tr->data_size, tr->offsets_size);
|
||||
(u64)tr->data.ptr.buffer,
|
||||
(u64)tr->data.ptr.offsets,
|
||||
(u64)tr->data_size, (u64)tr->offsets_size);
|
||||
else
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
"%d:%d BC_TRANSACTION %d -> %d - node %d, data %p-%p size %zd-%zd\n",
|
||||
"%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld\n",
|
||||
proc->pid, thread->pid, t->debug_id,
|
||||
target_proc->pid, target_node->debug_id,
|
||||
tr->data.ptr.buffer, tr->data.ptr.offsets,
|
||||
tr->data_size, tr->offsets_size);
|
||||
(u64)tr->data.ptr.buffer,
|
||||
(u64)tr->data.ptr.offsets,
|
||||
(u64)tr->data_size, (u64)tr->offsets_size);
|
||||
|
||||
if (!reply && !(tr->flags & TF_ONE_WAY))
|
||||
t->from = thread;
|
||||
|
|
@ -1477,38 +1481,47 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
if (target_node)
|
||||
binder_inc_node(target_node, 1, 0, NULL);
|
||||
|
||||
offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
|
||||
offp = (binder_size_t *)(t->buffer->data +
|
||||
ALIGN(tr->data_size, sizeof(void *)));
|
||||
|
||||
if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
|
||||
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
|
||||
tr->data.ptr.buffer, tr->data_size)) {
|
||||
binder_user_error("%d:%d got transaction with invalid data ptr\n",
|
||||
proc->pid, thread->pid);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_copy_data_failed;
|
||||
}
|
||||
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
|
||||
if (copy_from_user(offp, (const void __user *)(uintptr_t)
|
||||
tr->data.ptr.offsets, tr->offsets_size)) {
|
||||
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
|
||||
proc->pid, thread->pid);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_copy_data_failed;
|
||||
}
|
||||
if (!IS_ALIGNED(tr->offsets_size, sizeof(size_t))) {
|
||||
binder_user_error("%d:%d got transaction with invalid offsets size, %zd\n",
|
||||
proc->pid, thread->pid, tr->offsets_size);
|
||||
if (!IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))) {
|
||||
binder_user_error("%d:%d got transaction with invalid offsets size, %lld\n",
|
||||
proc->pid, thread->pid, (u64)tr->offsets_size);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_bad_offset;
|
||||
}
|
||||
off_end = (void *)offp + tr->offsets_size;
|
||||
off_min = 0;
|
||||
for (; offp < off_end; offp++) {
|
||||
struct flat_binder_object *fp;
|
||||
if (*offp > t->buffer->data_size - sizeof(*fp) ||
|
||||
*offp < off_min ||
|
||||
t->buffer->data_size < sizeof(*fp) ||
|
||||
!IS_ALIGNED(*offp, sizeof(void *))) {
|
||||
binder_user_error("%d:%d got transaction with invalid offset, %zd\n",
|
||||
proc->pid, thread->pid, *offp);
|
||||
!IS_ALIGNED(*offp, sizeof(u32))) {
|
||||
binder_user_error("%d:%d got transaction with invalid offset, %lld (min %lld, max %lld)\n",
|
||||
proc->pid, thread->pid, (u64)*offp,
|
||||
(u64)off_min,
|
||||
(u64)(t->buffer->data_size -
|
||||
sizeof(*fp)));
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_bad_offset;
|
||||
}
|
||||
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
|
||||
off_min = *offp + sizeof(struct flat_binder_object);
|
||||
switch (fp->type) {
|
||||
case BINDER_TYPE_BINDER:
|
||||
case BINDER_TYPE_WEAK_BINDER: {
|
||||
|
|
@ -1524,10 +1537,10 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
|
||||
}
|
||||
if (fp->cookie != node->cookie) {
|
||||
binder_user_error("%d:%d sending u%p node %d, cookie mismatch %p != %p\n",
|
||||
binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
fp->binder, node->debug_id,
|
||||
fp->cookie, node->cookie);
|
||||
(u64)fp->binder, node->debug_id,
|
||||
(u64)fp->cookie, (u64)node->cookie);
|
||||
goto err_binder_get_ref_for_node_failed;
|
||||
}
|
||||
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
|
||||
|
|
@ -1549,15 +1562,15 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
|
||||
trace_binder_transaction_node_to_ref(t, node, ref);
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
" node %d u%p -> ref %d desc %d\n",
|
||||
node->debug_id, node->ptr, ref->debug_id,
|
||||
ref->desc);
|
||||
" node %d u%016llx -> ref %d desc %d\n",
|
||||
node->debug_id, (u64)node->ptr,
|
||||
ref->debug_id, ref->desc);
|
||||
} break;
|
||||
case BINDER_TYPE_HANDLE:
|
||||
case BINDER_TYPE_WEAK_HANDLE: {
|
||||
struct binder_ref *ref = binder_get_ref(proc, fp->handle);
|
||||
if (ref == NULL) {
|
||||
binder_user_error("%d:%d got transaction with invalid handle, %ld\n",
|
||||
binder_user_error("%d:%d got transaction with invalid handle, %d\n",
|
||||
proc->pid,
|
||||
thread->pid, fp->handle);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
|
|
@ -1577,9 +1590,9 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
|
||||
trace_binder_transaction_ref_to_node(t, ref);
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
" ref %d desc %d -> node %d u%p\n",
|
||||
" ref %d desc %d -> node %d u%016llx\n",
|
||||
ref->debug_id, ref->desc, ref->node->debug_id,
|
||||
ref->node->ptr);
|
||||
(u64)ref->node->ptr);
|
||||
} else {
|
||||
struct binder_ref *new_ref;
|
||||
new_ref = binder_get_ref_for_node(target_proc, ref->node);
|
||||
|
|
@ -1604,13 +1617,13 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
|
||||
if (reply) {
|
||||
if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {
|
||||
binder_user_error("%d:%d got reply with fd, %ld, but target does not allow fds\n",
|
||||
binder_user_error("%d:%d got reply with fd, %d, but target does not allow fds\n",
|
||||
proc->pid, thread->pid, fp->handle);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_fd_not_allowed;
|
||||
}
|
||||
} else if (!target_node->accept_fds) {
|
||||
binder_user_error("%d:%d got transaction with fd, %ld, but target does not allow fds\n",
|
||||
binder_user_error("%d:%d got transaction with fd, %d, but target does not allow fds\n",
|
||||
proc->pid, thread->pid, fp->handle);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_fd_not_allowed;
|
||||
|
|
@ -1618,7 +1631,7 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
|
||||
file = fget(fp->handle);
|
||||
if (file == NULL) {
|
||||
binder_user_error("%d:%d got transaction with invalid fd, %ld\n",
|
||||
binder_user_error("%d:%d got transaction with invalid fd, %d\n",
|
||||
proc->pid, thread->pid, fp->handle);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_fget_failed;
|
||||
|
|
@ -1637,13 +1650,13 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
task_fd_install(target_proc, target_fd, file);
|
||||
trace_binder_transaction_fd(t, fp->handle, target_fd);
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
" fd %ld -> %d\n", fp->handle, target_fd);
|
||||
" fd %d -> %d\n", fp->handle, target_fd);
|
||||
/* TODO: fput? */
|
||||
fp->handle = target_fd;
|
||||
} break;
|
||||
|
||||
default:
|
||||
binder_user_error("%d:%d got transaction with invalid object type, %lx\n",
|
||||
binder_user_error("%d:%d got transaction with invalid object type, %x\n",
|
||||
proc->pid, thread->pid, fp->type);
|
||||
return_error = BR_FAILED_REPLY;
|
||||
goto err_bad_object_type;
|
||||
|
|
@ -1700,9 +1713,9 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
err_invalid_target_handle:
|
||||
err_no_context_mgr_node:
|
||||
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
|
||||
"%d:%d transaction failed %d, size %zd-%zd\n",
|
||||
"%d:%d transaction failed %d, size %lld-%lld\n",
|
||||
proc->pid, thread->pid, return_error,
|
||||
tr->data_size, tr->offsets_size);
|
||||
(u64)tr->data_size, (u64)tr->offsets_size);
|
||||
|
||||
{
|
||||
struct binder_transaction_log_entry *fe;
|
||||
|
|
@ -1719,9 +1732,11 @@ static void binder_transaction(struct binder_proc *proc,
|
|||
}
|
||||
|
||||
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
|
||||
void __user *buffer, int size, signed long *consumed)
|
||||
binder_uintptr_t binder_buffer, size_t size,
|
||||
binder_size_t *consumed)
|
||||
{
|
||||
uint32_t cmd;
|
||||
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
|
||||
void __user *ptr = buffer + *consumed;
|
||||
void __user *end = buffer + size;
|
||||
|
||||
|
|
@ -1790,33 +1805,33 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
|
|||
}
|
||||
case BC_INCREFS_DONE:
|
||||
case BC_ACQUIRE_DONE: {
|
||||
void __user *node_ptr;
|
||||
void *cookie;
|
||||
binder_uintptr_t node_ptr;
|
||||
binder_uintptr_t cookie;
|
||||
struct binder_node *node;
|
||||
|
||||
if (get_user(node_ptr, (void * __user *)ptr))
|
||||
if (get_user(node_ptr, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
if (get_user(cookie, (void * __user *)ptr))
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
node = binder_get_node(proc, node_ptr);
|
||||
if (node == NULL) {
|
||||
binder_user_error("%d:%d %s u%p no match\n",
|
||||
binder_user_error("%d:%d %s u%016llx no match\n",
|
||||
proc->pid, thread->pid,
|
||||
cmd == BC_INCREFS_DONE ?
|
||||
"BC_INCREFS_DONE" :
|
||||
"BC_ACQUIRE_DONE",
|
||||
node_ptr);
|
||||
(u64)node_ptr);
|
||||
break;
|
||||
}
|
||||
if (cookie != node->cookie) {
|
||||
binder_user_error("%d:%d %s u%p node %d cookie mismatch %p != %p\n",
|
||||
binder_user_error("%d:%d %s u%016llx node %d cookie mismatch %016llx != %016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
cmd == BC_INCREFS_DONE ?
|
||||
"BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
|
||||
node_ptr, node->debug_id,
|
||||
cookie, node->cookie);
|
||||
(u64)node_ptr, node->debug_id,
|
||||
(u64)cookie, (u64)node->cookie);
|
||||
break;
|
||||
}
|
||||
if (cmd == BC_ACQUIRE_DONE) {
|
||||
|
|
@ -1852,27 +1867,27 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
|
|||
return -EINVAL;
|
||||
|
||||
case BC_FREE_BUFFER: {
|
||||
void __user *data_ptr;
|
||||
binder_uintptr_t data_ptr;
|
||||
struct binder_buffer *buffer;
|
||||
|
||||
if (get_user(data_ptr, (void * __user *)ptr))
|
||||
if (get_user(data_ptr, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
|
||||
buffer = binder_buffer_lookup(proc, data_ptr);
|
||||
if (buffer == NULL) {
|
||||
binder_user_error("%d:%d BC_FREE_BUFFER u%p no match\n",
|
||||
proc->pid, thread->pid, data_ptr);
|
||||
binder_user_error("%d:%d BC_FREE_BUFFER u%016llx no match\n",
|
||||
proc->pid, thread->pid, (u64)data_ptr);
|
||||
break;
|
||||
}
|
||||
if (!buffer->allow_user_free) {
|
||||
binder_user_error("%d:%d BC_FREE_BUFFER u%p matched unreturned buffer\n",
|
||||
proc->pid, thread->pid, data_ptr);
|
||||
binder_user_error("%d:%d BC_FREE_BUFFER u%016llx matched unreturned buffer\n",
|
||||
proc->pid, thread->pid, (u64)data_ptr);
|
||||
break;
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_FREE_BUFFER,
|
||||
"%d:%d BC_FREE_BUFFER u%p found buffer %d for %s transaction\n",
|
||||
proc->pid, thread->pid, data_ptr, buffer->debug_id,
|
||||
"%d:%d BC_FREE_BUFFER u%016llx found buffer %d for %s transaction\n",
|
||||
proc->pid, thread->pid, (u64)data_ptr, buffer->debug_id,
|
||||
buffer->transaction ? "active" : "finished");
|
||||
|
||||
if (buffer->transaction) {
|
||||
|
|
@ -1942,16 +1957,16 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
|
|||
case BC_REQUEST_DEATH_NOTIFICATION:
|
||||
case BC_CLEAR_DEATH_NOTIFICATION: {
|
||||
uint32_t target;
|
||||
void __user *cookie;
|
||||
binder_uintptr_t cookie;
|
||||
struct binder_ref *ref;
|
||||
struct binder_ref_death *death;
|
||||
|
||||
if (get_user(target, (uint32_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(uint32_t);
|
||||
if (get_user(cookie, (void __user * __user *)ptr))
|
||||
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
ref = binder_get_ref(proc, target);
|
||||
if (ref == NULL) {
|
||||
binder_user_error("%d:%d %s invalid ref %d\n",
|
||||
|
|
@ -1964,12 +1979,12 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
|
|||
}
|
||||
|
||||
binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
|
||||
"%d:%d %s %p ref %d desc %d s %d w %d for node %d\n",
|
||||
"%d:%d %s %016llx ref %d desc %d s %d w %d for node %d\n",
|
||||
proc->pid, thread->pid,
|
||||
cmd == BC_REQUEST_DEATH_NOTIFICATION ?
|
||||
"BC_REQUEST_DEATH_NOTIFICATION" :
|
||||
"BC_CLEAR_DEATH_NOTIFICATION",
|
||||
cookie, ref->debug_id, ref->desc,
|
||||
(u64)cookie, ref->debug_id, ref->desc,
|
||||
ref->strong, ref->weak, ref->node->debug_id);
|
||||
|
||||
if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
|
||||
|
|
@ -2007,9 +2022,9 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
|
|||
}
|
||||
death = ref->death;
|
||||
if (death->cookie != cookie) {
|
||||
binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch %p != %p\n",
|
||||
binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch %016llx != %016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
death->cookie, cookie);
|
||||
(u64)death->cookie, (u64)cookie);
|
||||
break;
|
||||
}
|
||||
ref->death = NULL;
|
||||
|
|
@ -2029,9 +2044,9 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
|
|||
} break;
|
||||
case BC_DEAD_BINDER_DONE: {
|
||||
struct binder_work *w;
|
||||
void __user *cookie;
|
||||
binder_uintptr_t cookie;
|
||||
struct binder_ref_death *death = NULL;
|
||||
if (get_user(cookie, (void __user * __user *)ptr))
|
||||
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
|
||||
ptr += sizeof(void *);
|
||||
|
|
@ -2043,11 +2058,11 @@ int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
|
|||
}
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_DEAD_BINDER,
|
||||
"%d:%d BC_DEAD_BINDER_DONE %p found %p\n",
|
||||
proc->pid, thread->pid, cookie, death);
|
||||
"%d:%d BC_DEAD_BINDER_DONE %016llx found %p\n",
|
||||
proc->pid, thread->pid, (u64)cookie, death);
|
||||
if (death == NULL) {
|
||||
binder_user_error("%d:%d BC_DEAD_BINDER_DONE %p not found\n",
|
||||
proc->pid, thread->pid, cookie);
|
||||
binder_user_error("%d:%d BC_DEAD_BINDER_DONE %016llx not found\n",
|
||||
proc->pid, thread->pid, (u64)cookie);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -2099,9 +2114,10 @@ static int binder_has_thread_work(struct binder_thread *thread)
|
|||
|
||||
static int binder_thread_read(struct binder_proc *proc,
|
||||
struct binder_thread *thread,
|
||||
void __user *buffer, int size,
|
||||
signed long *consumed, int non_block)
|
||||
binder_uintptr_t binder_buffer, size_t size,
|
||||
binder_size_t *consumed, int non_block)
|
||||
{
|
||||
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
|
||||
void __user *ptr = buffer + *consumed;
|
||||
void __user *end = buffer + size;
|
||||
|
||||
|
|
@ -2246,32 +2262,36 @@ static int binder_thread_read(struct binder_proc *proc,
|
|||
if (put_user(cmd, (uint32_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(uint32_t);
|
||||
if (put_user(node->ptr, (void * __user *)ptr))
|
||||
if (put_user(node->ptr,
|
||||
(binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
if (put_user(node->cookie, (void * __user *)ptr))
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
if (put_user(node->cookie,
|
||||
(binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
|
||||
binder_stat_br(proc, thread, cmd);
|
||||
binder_debug(BINDER_DEBUG_USER_REFS,
|
||||
"%d:%d %s %d u%p c%p\n",
|
||||
proc->pid, thread->pid, cmd_name, node->debug_id, node->ptr, node->cookie);
|
||||
"%d:%d %s %d u%016llx c%016llx\n",
|
||||
proc->pid, thread->pid, cmd_name,
|
||||
node->debug_id,
|
||||
(u64)node->ptr, (u64)node->cookie);
|
||||
} else {
|
||||
list_del_init(&w->entry);
|
||||
if (!weak && !strong) {
|
||||
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
||||
"%d:%d node %d u%p c%p deleted\n",
|
||||
"%d:%d node %d u%016llx c%016llx deleted\n",
|
||||
proc->pid, thread->pid, node->debug_id,
|
||||
node->ptr, node->cookie);
|
||||
(u64)node->ptr, (u64)node->cookie);
|
||||
rb_erase(&node->rb_node, &proc->nodes);
|
||||
kfree(node);
|
||||
binder_stats_deleted(BINDER_STAT_NODE);
|
||||
} else {
|
||||
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
|
||||
"%d:%d node %d u%p c%p state unchanged\n",
|
||||
proc->pid, thread->pid, node->debug_id, node->ptr,
|
||||
node->cookie);
|
||||
"%d:%d node %d u%016llx c%016llx state unchanged\n",
|
||||
proc->pid, thread->pid, node->debug_id,
|
||||
(u64)node->ptr, (u64)node->cookie);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
|
@ -2289,17 +2309,18 @@ static int binder_thread_read(struct binder_proc *proc,
|
|||
if (put_user(cmd, (uint32_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(uint32_t);
|
||||
if (put_user(death->cookie, (void * __user *)ptr))
|
||||
if (put_user(death->cookie,
|
||||
(binder_uintptr_t __user *)ptr))
|
||||
return -EFAULT;
|
||||
ptr += sizeof(void *);
|
||||
ptr += sizeof(binder_uintptr_t);
|
||||
binder_stat_br(proc, thread, cmd);
|
||||
binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
|
||||
"%d:%d %s %p\n",
|
||||
"%d:%d %s %016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
cmd == BR_DEAD_BINDER ?
|
||||
"BR_DEAD_BINDER" :
|
||||
"BR_CLEAR_DEATH_NOTIFICATION_DONE",
|
||||
death->cookie);
|
||||
(u64)death->cookie);
|
||||
|
||||
if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
|
||||
list_del(&w->entry);
|
||||
|
|
@ -2329,8 +2350,8 @@ static int binder_thread_read(struct binder_proc *proc,
|
|||
binder_set_nice(target_node->min_priority);
|
||||
cmd = BR_TRANSACTION;
|
||||
} else {
|
||||
tr.target.ptr = NULL;
|
||||
tr.cookie = NULL;
|
||||
tr.target.ptr = 0;
|
||||
tr.cookie = 0;
|
||||
cmd = BR_REPLY;
|
||||
}
|
||||
tr.code = t->code;
|
||||
|
|
@ -2347,8 +2368,9 @@ static int binder_thread_read(struct binder_proc *proc,
|
|||
|
||||
tr.data_size = t->buffer->data_size;
|
||||
tr.offsets_size = t->buffer->offsets_size;
|
||||
tr.data.ptr.buffer = (void *)t->buffer->data +
|
||||
proc->user_buffer_offset;
|
||||
tr.data.ptr.buffer = (binder_uintptr_t)(
|
||||
(uintptr_t)t->buffer->data +
|
||||
proc->user_buffer_offset);
|
||||
tr.data.ptr.offsets = tr.data.ptr.buffer +
|
||||
ALIGN(t->buffer->data_size,
|
||||
sizeof(void *));
|
||||
|
|
@ -2363,14 +2385,14 @@ static int binder_thread_read(struct binder_proc *proc,
|
|||
trace_binder_transaction_received(t);
|
||||
binder_stat_br(proc, thread, cmd);
|
||||
binder_debug(BINDER_DEBUG_TRANSACTION,
|
||||
"%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %p-%p\n",
|
||||
"%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %016llx-%016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
(cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :
|
||||
"BR_REPLY",
|
||||
t->debug_id, t->from ? t->from->proc->pid : 0,
|
||||
t->from ? t->from->pid : 0, cmd,
|
||||
t->buffer->data_size, t->buffer->offsets_size,
|
||||
tr.data.ptr.buffer, tr.data.ptr.offsets);
|
||||
(u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets);
|
||||
|
||||
list_del(&t->work.entry);
|
||||
t->buffer->allow_user_free = 1;
|
||||
|
|
@ -2440,8 +2462,8 @@ static void binder_release_work(struct list_head *list)
|
|||
|
||||
death = container_of(w, struct binder_ref_death, work);
|
||||
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
|
||||
"undelivered death notification, %p\n",
|
||||
death->cookie);
|
||||
"undelivered death notification, %016llx\n",
|
||||
(u64)death->cookie);
|
||||
kfree(death);
|
||||
binder_stats_deleted(BINDER_STAT_DEATH);
|
||||
} break;
|
||||
|
|
@ -2597,12 +2619,13 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
goto err;
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_READ_WRITE,
|
||||
"%d:%d write %ld at %08lx, read %ld at %08lx\n",
|
||||
proc->pid, thread->pid, bwr.write_size,
|
||||
bwr.write_buffer, bwr.read_size, bwr.read_buffer);
|
||||
"%d:%d write %lld at %016llx, read %lld at %016llx\n",
|
||||
proc->pid, thread->pid,
|
||||
(u64)bwr.write_size, (u64)bwr.write_buffer,
|
||||
(u64)bwr.read_size, (u64)bwr.read_buffer);
|
||||
|
||||
if (bwr.write_size > 0) {
|
||||
ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
|
||||
ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
|
||||
trace_binder_write_done(ret);
|
||||
if (ret < 0) {
|
||||
bwr.read_consumed = 0;
|
||||
|
|
@ -2612,7 +2635,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
}
|
||||
}
|
||||
if (bwr.read_size > 0) {
|
||||
ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
|
||||
ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
|
||||
trace_binder_read_done(ret);
|
||||
if (!list_empty(&proc->todo))
|
||||
wake_up_interruptible(&proc->wait);
|
||||
|
|
@ -2623,9 +2646,10 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
}
|
||||
}
|
||||
binder_debug(BINDER_DEBUG_READ_WRITE,
|
||||
"%d:%d wrote %ld of %ld, read return %ld of %ld\n",
|
||||
proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,
|
||||
bwr.read_consumed, bwr.read_size);
|
||||
"%d:%d wrote %lld of %lld, read return %lld of %lld\n",
|
||||
proc->pid, thread->pid,
|
||||
(u64)bwr.write_consumed, (u64)bwr.write_size,
|
||||
(u64)bwr.read_consumed, (u64)bwr.read_size);
|
||||
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
|
||||
ret = -EFAULT;
|
||||
goto err;
|
||||
|
|
@ -2657,7 +2681,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
}
|
||||
} else
|
||||
binder_context_mgr_uid = current->cred->euid;
|
||||
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
|
||||
binder_context_mgr_node = binder_new_node(proc, 0, 0);
|
||||
if (binder_context_mgr_node == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
|
|
@ -2924,7 +2948,7 @@ static int binder_node_release(struct binder_node *node, int refs)
|
|||
refs++;
|
||||
|
||||
if (!ref->death)
|
||||
goto out;
|
||||
continue;
|
||||
|
||||
death++;
|
||||
|
||||
|
|
@ -2937,7 +2961,6 @@ static int binder_node_release(struct binder_node *node, int refs)
|
|||
BUG();
|
||||
}
|
||||
|
||||
out:
|
||||
binder_debug(BINDER_DEBUG_DEAD_BINDER,
|
||||
"node %d now dead, refs %d, death %d\n",
|
||||
node->debug_id, refs, death);
|
||||
|
|
@ -3153,8 +3176,9 @@ static void print_binder_work(struct seq_file *m, const char *prefix,
|
|||
break;
|
||||
case BINDER_WORK_NODE:
|
||||
node = container_of(w, struct binder_node, work);
|
||||
seq_printf(m, "%snode work %d: u%p c%p\n",
|
||||
prefix, node->debug_id, node->ptr, node->cookie);
|
||||
seq_printf(m, "%snode work %d: u%016llx c%016llx\n",
|
||||
prefix, node->debug_id,
|
||||
(u64)node->ptr, (u64)node->cookie);
|
||||
break;
|
||||
case BINDER_WORK_DEAD_BINDER:
|
||||
seq_printf(m, "%shas dead binder\n", prefix);
|
||||
|
|
@ -3214,8 +3238,8 @@ static void print_binder_node(struct seq_file *m, struct binder_node *node)
|
|||
hlist_for_each_entry(ref, &node->refs, node_entry)
|
||||
count++;
|
||||
|
||||
seq_printf(m, " node %d: u%p c%p hs %d hw %d ls %d lw %d is %d iw %d",
|
||||
node->debug_id, node->ptr, node->cookie,
|
||||
seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d",
|
||||
node->debug_id, (u64)node->ptr, (u64)node->cookie,
|
||||
node->has_strong_ref, node->has_weak_ref,
|
||||
node->local_strong_refs, node->local_weak_refs,
|
||||
node->internal_strong_refs, count);
|
||||
|
|
@ -3517,6 +3541,7 @@ static const struct file_operations binder_fops = {
|
|||
.owner = THIS_MODULE,
|
||||
.poll = binder_poll,
|
||||
.unlocked_ioctl = binder_ioctl,
|
||||
.compat_ioctl = binder_ioctl,
|
||||
.mmap = binder_mmap,
|
||||
.open = binder_open,
|
||||
.flush = binder_flush,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@
|
|||
#ifndef _LINUX_BINDER_H
|
||||
#define _LINUX_BINDER_H
|
||||
|
||||
#ifdef CONFIG_ANDROID_BINDER_IPC_32BIT
|
||||
#define BINDER_IPC_32BIT 1
|
||||
#endif
|
||||
|
||||
#include "uapi/binder.h"
|
||||
|
||||
#endif /* _LINUX_BINDER_H */
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ TRACE_EVENT(binder_transaction_node_to_ref,
|
|||
TP_STRUCT__entry(
|
||||
__field(int, debug_id)
|
||||
__field(int, node_debug_id)
|
||||
__field(void __user *, node_ptr)
|
||||
__field(binder_uintptr_t, node_ptr)
|
||||
__field(int, ref_debug_id)
|
||||
__field(uint32_t, ref_desc)
|
||||
),
|
||||
|
|
@ -163,8 +163,9 @@ TRACE_EVENT(binder_transaction_node_to_ref,
|
|||
__entry->ref_debug_id = ref->debug_id;
|
||||
__entry->ref_desc = ref->desc;
|
||||
),
|
||||
TP_printk("transaction=%d node=%d src_ptr=0x%p ==> dest_ref=%d dest_desc=%d",
|
||||
__entry->debug_id, __entry->node_debug_id, __entry->node_ptr,
|
||||
TP_printk("transaction=%d node=%d src_ptr=0x%016llx ==> dest_ref=%d dest_desc=%d",
|
||||
__entry->debug_id, __entry->node_debug_id,
|
||||
(u64)__entry->node_ptr,
|
||||
__entry->ref_debug_id, __entry->ref_desc)
|
||||
);
|
||||
|
||||
|
|
@ -177,7 +178,7 @@ TRACE_EVENT(binder_transaction_ref_to_node,
|
|||
__field(int, ref_debug_id)
|
||||
__field(uint32_t, ref_desc)
|
||||
__field(int, node_debug_id)
|
||||
__field(void __user *, node_ptr)
|
||||
__field(binder_uintptr_t, node_ptr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->debug_id = t->debug_id;
|
||||
|
|
@ -186,9 +187,10 @@ TRACE_EVENT(binder_transaction_ref_to_node,
|
|||
__entry->node_debug_id = ref->node->debug_id;
|
||||
__entry->node_ptr = ref->node->ptr;
|
||||
),
|
||||
TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%p",
|
||||
TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%016llx",
|
||||
__entry->debug_id, __entry->node_debug_id,
|
||||
__entry->ref_debug_id, __entry->ref_desc, __entry->node_ptr)
|
||||
__entry->ref_debug_id, __entry->ref_desc,
|
||||
(u64)__entry->node_ptr)
|
||||
);
|
||||
|
||||
TRACE_EVENT(binder_transaction_ref_to_ref,
|
||||
|
|
|
|||
49
drivers/staging/android/fiq_debugger/Kconfig
Normal file
49
drivers/staging/android/fiq_debugger/Kconfig
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
config FIQ_DEBUGGER
|
||||
bool "FIQ Mode Serial Debugger"
|
||||
default n
|
||||
depends on ARM || ARM64
|
||||
help
|
||||
The FIQ serial debugger can accept commands even when the
|
||||
kernel is unresponsive due to being stuck with interrupts
|
||||
disabled.
|
||||
|
||||
config FIQ_DEBUGGER_NO_SLEEP
|
||||
bool "Keep serial debugger active"
|
||||
depends on FIQ_DEBUGGER
|
||||
default n
|
||||
help
|
||||
Enables the serial debugger at boot. Passing
|
||||
fiq_debugger.no_sleep on the kernel commandline will
|
||||
override this config option.
|
||||
|
||||
config FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON
|
||||
bool "Don't disable wakeup IRQ when debugger is active"
|
||||
depends on FIQ_DEBUGGER
|
||||
default n
|
||||
help
|
||||
Don't disable the wakeup irq when enabling the uart clock. This will
|
||||
cause extra interrupts, but it makes the serial debugger usable with
|
||||
on some MSM radio builds that ignore the uart clock request in power
|
||||
collapse.
|
||||
|
||||
config FIQ_DEBUGGER_CONSOLE
|
||||
bool "Console on FIQ Serial Debugger port"
|
||||
depends on FIQ_DEBUGGER
|
||||
default n
|
||||
help
|
||||
Enables a console so that printk messages are displayed on
|
||||
the debugger serial port as the occur.
|
||||
|
||||
config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE
|
||||
bool "Put the FIQ debugger into console mode by default"
|
||||
depends on FIQ_DEBUGGER_CONSOLE
|
||||
default n
|
||||
help
|
||||
If enabled, this puts the fiq debugger into console mode by default.
|
||||
Otherwise, the fiq debugger will start out in debug mode.
|
||||
|
||||
config FIQ_WATCHDOG
|
||||
bool
|
||||
select FIQ_DEBUGGER
|
||||
select PSTORE_RAM
|
||||
default n
|
||||
4
drivers/staging/android/fiq_debugger/Makefile
Normal file
4
drivers/staging/android/fiq_debugger/Makefile
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
obj-y += fiq_debugger.o
|
||||
obj-$(CONFIG_ARM) += fiq_debugger_arm.o
|
||||
obj-$(CONFIG_ARM64) += fiq_debugger_arm64.o
|
||||
obj-$(CONFIG_FIQ_WATCHDOG) += fiq_watchdog.o
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* arch/arm/include/asm/fiq_debugger.h
|
||||
* drivers/staging/android/fiq_debugger/fiq_debugger.h
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Author: Colin Cross <ccross@android.com>
|
||||
240
drivers/staging/android/fiq_debugger/fiq_debugger_arm.c
Normal file
240
drivers/staging/android/fiq_debugger/fiq_debugger_arm.c
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
* Author: Colin Cross <ccross@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <asm/stacktrace.h>
|
||||
|
||||
#include "fiq_debugger_priv.h"
|
||||
|
||||
static char *mode_name(unsigned cpsr)
|
||||
{
|
||||
switch (cpsr & MODE_MASK) {
|
||||
case USR_MODE: return "USR";
|
||||
case FIQ_MODE: return "FIQ";
|
||||
case IRQ_MODE: return "IRQ";
|
||||
case SVC_MODE: return "SVC";
|
||||
case ABT_MODE: return "ABT";
|
||||
case UND_MODE: return "UND";
|
||||
case SYSTEM_MODE: return "SYS";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
void fiq_debugger_dump_pc(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs)
|
||||
{
|
||||
output->printf(output, " pc %08x cpsr %08x mode %s\n",
|
||||
regs->ARM_pc, regs->ARM_cpsr, mode_name(regs->ARM_cpsr));
|
||||
}
|
||||
|
||||
void fiq_debugger_dump_regs(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs)
|
||||
{
|
||||
output->printf(output,
|
||||
" r0 %08x r1 %08x r2 %08x r3 %08x\n",
|
||||
regs->ARM_r0, regs->ARM_r1, regs->ARM_r2, regs->ARM_r3);
|
||||
output->printf(output,
|
||||
" r4 %08x r5 %08x r6 %08x r7 %08x\n",
|
||||
regs->ARM_r4, regs->ARM_r5, regs->ARM_r6, regs->ARM_r7);
|
||||
output->printf(output,
|
||||
" r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n",
|
||||
regs->ARM_r8, regs->ARM_r9, regs->ARM_r10, regs->ARM_fp,
|
||||
mode_name(regs->ARM_cpsr));
|
||||
output->printf(output,
|
||||
" ip %08x sp %08x lr %08x pc %08x cpsr %08x\n",
|
||||
regs->ARM_ip, regs->ARM_sp, regs->ARM_lr, regs->ARM_pc,
|
||||
regs->ARM_cpsr);
|
||||
}
|
||||
|
||||
struct mode_regs {
|
||||
unsigned long sp_svc;
|
||||
unsigned long lr_svc;
|
||||
unsigned long spsr_svc;
|
||||
|
||||
unsigned long sp_abt;
|
||||
unsigned long lr_abt;
|
||||
unsigned long spsr_abt;
|
||||
|
||||
unsigned long sp_und;
|
||||
unsigned long lr_und;
|
||||
unsigned long spsr_und;
|
||||
|
||||
unsigned long sp_irq;
|
||||
unsigned long lr_irq;
|
||||
unsigned long spsr_irq;
|
||||
|
||||
unsigned long r8_fiq;
|
||||
unsigned long r9_fiq;
|
||||
unsigned long r10_fiq;
|
||||
unsigned long r11_fiq;
|
||||
unsigned long r12_fiq;
|
||||
unsigned long sp_fiq;
|
||||
unsigned long lr_fiq;
|
||||
unsigned long spsr_fiq;
|
||||
};
|
||||
|
||||
static void __naked get_mode_regs(struct mode_regs *regs)
|
||||
{
|
||||
asm volatile (
|
||||
"mrs r1, cpsr\n"
|
||||
"msr cpsr_c, #0xd3 @(SVC_MODE | PSR_I_BIT | PSR_F_BIT)\n"
|
||||
"stmia r0!, {r13 - r14}\n"
|
||||
"mrs r2, spsr\n"
|
||||
"msr cpsr_c, #0xd7 @(ABT_MODE | PSR_I_BIT | PSR_F_BIT)\n"
|
||||
"stmia r0!, {r2, r13 - r14}\n"
|
||||
"mrs r2, spsr\n"
|
||||
"msr cpsr_c, #0xdb @(UND_MODE | PSR_I_BIT | PSR_F_BIT)\n"
|
||||
"stmia r0!, {r2, r13 - r14}\n"
|
||||
"mrs r2, spsr\n"
|
||||
"msr cpsr_c, #0xd2 @(IRQ_MODE | PSR_I_BIT | PSR_F_BIT)\n"
|
||||
"stmia r0!, {r2, r13 - r14}\n"
|
||||
"mrs r2, spsr\n"
|
||||
"msr cpsr_c, #0xd1 @(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)\n"
|
||||
"stmia r0!, {r2, r8 - r14}\n"
|
||||
"mrs r2, spsr\n"
|
||||
"stmia r0!, {r2}\n"
|
||||
"msr cpsr_c, r1\n"
|
||||
"bx lr\n");
|
||||
}
|
||||
|
||||
|
||||
void fiq_debugger_dump_allregs(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs)
|
||||
{
|
||||
struct mode_regs mode_regs;
|
||||
unsigned long mode = regs->ARM_cpsr & MODE_MASK;
|
||||
|
||||
fiq_debugger_dump_regs(output, regs);
|
||||
get_mode_regs(&mode_regs);
|
||||
|
||||
output->printf(output,
|
||||
"%csvc: sp %08x lr %08x spsr %08x\n",
|
||||
mode == SVC_MODE ? '*' : ' ',
|
||||
mode_regs.sp_svc, mode_regs.lr_svc, mode_regs.spsr_svc);
|
||||
output->printf(output,
|
||||
"%cabt: sp %08x lr %08x spsr %08x\n",
|
||||
mode == ABT_MODE ? '*' : ' ',
|
||||
mode_regs.sp_abt, mode_regs.lr_abt, mode_regs.spsr_abt);
|
||||
output->printf(output,
|
||||
"%cund: sp %08x lr %08x spsr %08x\n",
|
||||
mode == UND_MODE ? '*' : ' ',
|
||||
mode_regs.sp_und, mode_regs.lr_und, mode_regs.spsr_und);
|
||||
output->printf(output,
|
||||
"%cirq: sp %08x lr %08x spsr %08x\n",
|
||||
mode == IRQ_MODE ? '*' : ' ',
|
||||
mode_regs.sp_irq, mode_regs.lr_irq, mode_regs.spsr_irq);
|
||||
output->printf(output,
|
||||
"%cfiq: r8 %08x r9 %08x r10 %08x r11 %08x r12 %08x\n",
|
||||
mode == FIQ_MODE ? '*' : ' ',
|
||||
mode_regs.r8_fiq, mode_regs.r9_fiq, mode_regs.r10_fiq,
|
||||
mode_regs.r11_fiq, mode_regs.r12_fiq);
|
||||
output->printf(output,
|
||||
" fiq: sp %08x lr %08x spsr %08x\n",
|
||||
mode_regs.sp_fiq, mode_regs.lr_fiq, mode_regs.spsr_fiq);
|
||||
}
|
||||
|
||||
struct stacktrace_state {
|
||||
struct fiq_debugger_output *output;
|
||||
unsigned int depth;
|
||||
};
|
||||
|
||||
static int report_trace(struct stackframe *frame, void *d)
|
||||
{
|
||||
struct stacktrace_state *sts = d;
|
||||
|
||||
if (sts->depth) {
|
||||
sts->output->printf(sts->output,
|
||||
" pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n",
|
||||
frame->pc, frame->pc, frame->lr, frame->lr,
|
||||
frame->sp, frame->fp);
|
||||
sts->depth--;
|
||||
return 0;
|
||||
}
|
||||
sts->output->printf(sts->output, " ...\n");
|
||||
|
||||
return sts->depth == 0;
|
||||
}
|
||||
|
||||
struct frame_tail {
|
||||
struct frame_tail *fp;
|
||||
unsigned long sp;
|
||||
unsigned long lr;
|
||||
} __attribute__((packed));
|
||||
|
||||
static struct frame_tail *user_backtrace(struct fiq_debugger_output *output,
|
||||
struct frame_tail *tail)
|
||||
{
|
||||
struct frame_tail buftail[2];
|
||||
|
||||
/* Also check accessibility of one struct frame_tail beyond */
|
||||
if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) {
|
||||
output->printf(output, " invalid frame pointer %p\n",
|
||||
tail);
|
||||
return NULL;
|
||||
}
|
||||
if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) {
|
||||
output->printf(output,
|
||||
" failed to copy frame pointer %p\n", tail);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
output->printf(output, " %p\n", buftail[0].lr);
|
||||
|
||||
/* frame pointers should strictly progress back up the stack
|
||||
* (towards higher addresses) */
|
||||
if (tail >= buftail[0].fp)
|
||||
return NULL;
|
||||
|
||||
return buftail[0].fp-1;
|
||||
}
|
||||
|
||||
void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs, unsigned int depth, void *ssp)
|
||||
{
|
||||
struct frame_tail *tail;
|
||||
struct thread_info *real_thread_info = THREAD_INFO(ssp);
|
||||
struct stacktrace_state sts;
|
||||
|
||||
sts.depth = depth;
|
||||
sts.output = output;
|
||||
*current_thread_info() = *real_thread_info;
|
||||
|
||||
if (!current)
|
||||
output->printf(output, "current NULL\n");
|
||||
else
|
||||
output->printf(output, "pid: %d comm: %s\n",
|
||||
current->pid, current->comm);
|
||||
fiq_debugger_dump_regs(output, regs);
|
||||
|
||||
if (!user_mode(regs)) {
|
||||
struct stackframe frame;
|
||||
frame.fp = regs->ARM_fp;
|
||||
frame.sp = regs->ARM_sp;
|
||||
frame.lr = regs->ARM_lr;
|
||||
frame.pc = regs->ARM_pc;
|
||||
output->printf(output,
|
||||
" pc: %p (%pF), lr %p (%pF), sp %p, fp %p\n",
|
||||
regs->ARM_pc, regs->ARM_pc, regs->ARM_lr, regs->ARM_lr,
|
||||
regs->ARM_sp, regs->ARM_fp);
|
||||
walk_stackframe(&frame, report_trace, &sts);
|
||||
return;
|
||||
}
|
||||
|
||||
tail = ((struct frame_tail *) regs->ARM_fp) - 1;
|
||||
while (depth-- && tail && !((unsigned long) tail & 3))
|
||||
tail = user_backtrace(output, tail);
|
||||
}
|
||||
202
drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c
Normal file
202
drivers/staging/android/fiq_debugger/fiq_debugger_arm64.c
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
* Author: Colin Cross <ccross@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/ptrace.h>
|
||||
#include <asm/stacktrace.h>
|
||||
|
||||
#include "fiq_debugger_priv.h"
|
||||
|
||||
static char *mode_name(const struct pt_regs *regs)
|
||||
{
|
||||
if (compat_user_mode(regs)) {
|
||||
return "USR";
|
||||
} else {
|
||||
switch (processor_mode(regs)) {
|
||||
case PSR_MODE_EL0t: return "EL0t";
|
||||
case PSR_MODE_EL1t: return "EL1t";
|
||||
case PSR_MODE_EL1h: return "EL1h";
|
||||
case PSR_MODE_EL2t: return "EL2t";
|
||||
case PSR_MODE_EL2h: return "EL2h";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fiq_debugger_dump_pc(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs)
|
||||
{
|
||||
output->printf(output, " pc %016lx cpsr %08lx mode %s\n",
|
||||
regs->pc, regs->pstate, mode_name(regs));
|
||||
}
|
||||
|
||||
void fiq_debugger_dump_regs_aarch32(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs)
|
||||
{
|
||||
output->printf(output, " r0 %08x r1 %08x r2 %08x r3 %08x\n",
|
||||
regs->compat_usr(0), regs->compat_usr(1),
|
||||
regs->compat_usr(2), regs->compat_usr(3));
|
||||
output->printf(output, " r4 %08x r5 %08x r6 %08x r7 %08x\n",
|
||||
regs->compat_usr(4), regs->compat_usr(5),
|
||||
regs->compat_usr(6), regs->compat_usr(7));
|
||||
output->printf(output, " r8 %08x r9 %08x r10 %08x r11 %08x\n",
|
||||
regs->compat_usr(8), regs->compat_usr(9),
|
||||
regs->compat_usr(10), regs->compat_usr(11));
|
||||
output->printf(output, " ip %08x sp %08x lr %08x pc %08x\n",
|
||||
regs->compat_usr(12), regs->compat_sp,
|
||||
regs->compat_lr, regs->pc);
|
||||
output->printf(output, " cpsr %08x (%s)\n",
|
||||
regs->pstate, mode_name(regs));
|
||||
}
|
||||
|
||||
void fiq_debugger_dump_regs_aarch64(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs)
|
||||
{
|
||||
|
||||
output->printf(output, " x0 %016lx x1 %016lx\n",
|
||||
regs->regs[0], regs->regs[1]);
|
||||
output->printf(output, " x2 %016lx x3 %016lx\n",
|
||||
regs->regs[2], regs->regs[3]);
|
||||
output->printf(output, " x4 %016lx x5 %016lx\n",
|
||||
regs->regs[4], regs->regs[5]);
|
||||
output->printf(output, " x6 %016lx x7 %016lx\n",
|
||||
regs->regs[6], regs->regs[7]);
|
||||
output->printf(output, " x8 %016lx x9 %016lx\n",
|
||||
regs->regs[8], regs->regs[9]);
|
||||
output->printf(output, " x10 %016lx x11 %016lx\n",
|
||||
regs->regs[10], regs->regs[11]);
|
||||
output->printf(output, " x12 %016lx x13 %016lx\n",
|
||||
regs->regs[12], regs->regs[13]);
|
||||
output->printf(output, " x14 %016lx x15 %016lx\n",
|
||||
regs->regs[14], regs->regs[15]);
|
||||
output->printf(output, " x16 %016lx x17 %016lx\n",
|
||||
regs->regs[16], regs->regs[17]);
|
||||
output->printf(output, " x18 %016lx x19 %016lx\n",
|
||||
regs->regs[18], regs->regs[19]);
|
||||
output->printf(output, " x20 %016lx x21 %016lx\n",
|
||||
regs->regs[20], regs->regs[21]);
|
||||
output->printf(output, " x22 %016lx x23 %016lx\n",
|
||||
regs->regs[22], regs->regs[23]);
|
||||
output->printf(output, " x24 %016lx x25 %016lx\n",
|
||||
regs->regs[24], regs->regs[25]);
|
||||
output->printf(output, " x26 %016lx x27 %016lx\n",
|
||||
regs->regs[26], regs->regs[27]);
|
||||
output->printf(output, " x28 %016lx x29 %016lx\n",
|
||||
regs->regs[28], regs->regs[29]);
|
||||
output->printf(output, " x30 %016lx sp %016lx\n",
|
||||
regs->regs[30], regs->sp);
|
||||
output->printf(output, " pc %016lx cpsr %08x (%s)\n",
|
||||
regs->pc, regs->pstate, mode_name(regs));
|
||||
}
|
||||
|
||||
void fiq_debugger_dump_regs(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs)
|
||||
{
|
||||
if (compat_user_mode(regs))
|
||||
fiq_debugger_dump_regs_aarch32(output, regs);
|
||||
else
|
||||
fiq_debugger_dump_regs_aarch64(output, regs);
|
||||
}
|
||||
|
||||
#define READ_SPECIAL_REG(x) ({ \
|
||||
u64 val; \
|
||||
asm volatile ("mrs %0, " # x : "=r"(val)); \
|
||||
val; \
|
||||
})
|
||||
|
||||
void fiq_debugger_dump_allregs(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs)
|
||||
{
|
||||
u32 pstate = READ_SPECIAL_REG(CurrentEl);
|
||||
bool in_el2 = (pstate & PSR_MODE_MASK) >= PSR_MODE_EL2t;
|
||||
|
||||
fiq_debugger_dump_regs(output, regs);
|
||||
|
||||
output->printf(output, " sp_el0 %016lx\n",
|
||||
READ_SPECIAL_REG(sp_el0));
|
||||
|
||||
if (in_el2)
|
||||
output->printf(output, " sp_el1 %016lx\n",
|
||||
READ_SPECIAL_REG(sp_el1));
|
||||
|
||||
output->printf(output, " elr_el1 %016lx\n",
|
||||
READ_SPECIAL_REG(elr_el1));
|
||||
|
||||
output->printf(output, " spsr_el1 %08lx\n",
|
||||
READ_SPECIAL_REG(spsr_el1));
|
||||
|
||||
if (in_el2) {
|
||||
output->printf(output, " spsr_irq %08lx\n",
|
||||
READ_SPECIAL_REG(spsr_irq));
|
||||
output->printf(output, " spsr_abt %08lx\n",
|
||||
READ_SPECIAL_REG(spsr_abt));
|
||||
output->printf(output, " spsr_und %08lx\n",
|
||||
READ_SPECIAL_REG(spsr_und));
|
||||
output->printf(output, " spsr_fiq %08lx\n",
|
||||
READ_SPECIAL_REG(spsr_fiq));
|
||||
output->printf(output, " spsr_el2 %08lx\n",
|
||||
READ_SPECIAL_REG(elr_el2));
|
||||
output->printf(output, " spsr_el2 %08lx\n",
|
||||
READ_SPECIAL_REG(spsr_el2));
|
||||
}
|
||||
}
|
||||
|
||||
struct stacktrace_state {
|
||||
struct fiq_debugger_output *output;
|
||||
unsigned int depth;
|
||||
};
|
||||
|
||||
static int report_trace(struct stackframe *frame, void *d)
|
||||
{
|
||||
struct stacktrace_state *sts = d;
|
||||
|
||||
if (sts->depth) {
|
||||
sts->output->printf(sts->output, "%pF:\n", frame->pc);
|
||||
sts->output->printf(sts->output,
|
||||
" pc %016lx sp %016lx fp %016lx\n",
|
||||
frame->pc, frame->sp, frame->fp);
|
||||
sts->depth--;
|
||||
return 0;
|
||||
}
|
||||
sts->output->printf(sts->output, " ...\n");
|
||||
|
||||
return sts->depth == 0;
|
||||
}
|
||||
|
||||
void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs, unsigned int depth, void *ssp)
|
||||
{
|
||||
struct thread_info *real_thread_info = THREAD_INFO(ssp);
|
||||
struct stacktrace_state sts;
|
||||
|
||||
sts.depth = depth;
|
||||
sts.output = output;
|
||||
*current_thread_info() = *real_thread_info;
|
||||
|
||||
if (!current)
|
||||
output->printf(output, "current NULL\n");
|
||||
else
|
||||
output->printf(output, "pid: %d comm: %s\n",
|
||||
current->pid, current->comm);
|
||||
fiq_debugger_dump_regs(output, regs);
|
||||
|
||||
if (!user_mode(regs)) {
|
||||
struct stackframe frame;
|
||||
frame.fp = regs->regs[29];
|
||||
frame.sp = regs->sp;
|
||||
frame.pc = regs->pc;
|
||||
output->printf(output, "\n");
|
||||
walk_stackframe(&frame, report_trace, &sts);
|
||||
}
|
||||
}
|
||||
37
drivers/staging/android/fiq_debugger/fiq_debugger_priv.h
Normal file
37
drivers/staging/android/fiq_debugger/fiq_debugger_priv.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
* Author: Colin Cross <ccross@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _FIQ_DEBUGGER_PRIV_H_
|
||||
#define _FIQ_DEBUGGER_PRIV_H_
|
||||
|
||||
#define THREAD_INFO(sp) ((struct thread_info *) \
|
||||
((unsigned long)(sp) & ~(THREAD_SIZE - 1)))
|
||||
|
||||
struct fiq_debugger_output {
|
||||
void (*printf)(struct fiq_debugger_output *output, const char *fmt, ...);
|
||||
};
|
||||
|
||||
struct pt_regs;
|
||||
|
||||
void fiq_debugger_dump_pc(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs);
|
||||
void fiq_debugger_dump_regs(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs);
|
||||
void fiq_debugger_dump_allregs(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs);
|
||||
void fiq_debugger_dump_stacktrace(struct fiq_debugger_output *output,
|
||||
const struct pt_regs *regs, unsigned int depth, void *ssp);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* arch/arm/common/fiq_debugger_ringbuf.c
|
||||
* drivers/staging/android/fiq_debugger/fiq_debugger_ringbuf.h
|
||||
*
|
||||
* simple lockless ringbuffer
|
||||
*
|
||||
56
drivers/staging/android/fiq_debugger/fiq_watchdog.c
Normal file
56
drivers/staging/android/fiq_debugger/fiq_watchdog.c
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pstore_ram.h>
|
||||
|
||||
#include "fiq_watchdog.h"
|
||||
#include "fiq_debugger_priv.h"
|
||||
|
||||
static DEFINE_RAW_SPINLOCK(fiq_watchdog_lock);
|
||||
|
||||
static void fiq_watchdog_printf(struct fiq_debugger_output *output,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char buf[256];
|
||||
va_list ap;
|
||||
int len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vscnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
ramoops_console_write_buf(buf, len);
|
||||
}
|
||||
|
||||
struct fiq_debugger_output fiq_watchdog_output = {
|
||||
.printf = fiq_watchdog_printf,
|
||||
};
|
||||
|
||||
void fiq_watchdog_triggered(const struct pt_regs *regs, void *svc_sp)
|
||||
{
|
||||
char msg[24];
|
||||
int len;
|
||||
|
||||
raw_spin_lock(&fiq_watchdog_lock);
|
||||
|
||||
len = scnprintf(msg, sizeof(msg), "watchdog fiq cpu %d\n",
|
||||
THREAD_INFO(svc_sp)->cpu);
|
||||
ramoops_console_write_buf(msg, len);
|
||||
|
||||
fiq_debugger_dump_stacktrace(&fiq_watchdog_output, regs, 100, svc_sp);
|
||||
|
||||
raw_spin_unlock(&fiq_watchdog_lock);
|
||||
}
|
||||
20
drivers/staging/android/fiq_debugger/fiq_watchdog.h
Normal file
20
drivers/staging/android/fiq_debugger/fiq_watchdog.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (C) 2014 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _FIQ_WATCHDOG_H_
|
||||
#define _FIQ_WATCHDOG_H_
|
||||
|
||||
void fiq_watchdog_triggered(const struct pt_regs *regs, void *svc_sp);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
menuconfig ION
|
||||
tristate "Ion Memory Manager"
|
||||
bool "Ion Memory Manager"
|
||||
depends on HAVE_MEMBLOCK
|
||||
select GENERIC_ALLOCATOR
|
||||
select DMA_SHARED_BUFFER
|
||||
---help---
|
||||
|
|
@ -16,6 +17,16 @@ config ION_TEST
|
|||
Choose this option to create a device that can be used to test the
|
||||
kernel and device side ION functions.
|
||||
|
||||
config ION_DUMMY
|
||||
bool "Dummy Ion driver"
|
||||
depends on ION
|
||||
help
|
||||
Provides a dummy ION driver that registers the
|
||||
/dev/ion device and some basic heaps. This can
|
||||
be used for testing the ION infrastructure if
|
||||
one doesn't have access to hardware drivers that
|
||||
use ION.
|
||||
|
||||
config ION_TEGRA
|
||||
tristate "Ion for Tegra"
|
||||
depends on ARCH_TEGRA && ION
|
||||
|
|
|
|||
|
|
@ -4,4 +4,7 @@ obj-$(CONFIG_ION_TEST) += ion_test.o
|
|||
ifdef CONFIG_COMPAT
|
||||
obj-$(CONFIG_ION) += compat_ion.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_ION_DUMMY) += ion_dummy_driver.o
|
||||
obj-$(CONFIG_ION_TEGRA) += tegra/
|
||||
|
||||
|
|
|
|||
|
|
@ -35,9 +35,14 @@ struct compat_ion_custom_data {
|
|||
compat_ulong_t arg;
|
||||
};
|
||||
|
||||
struct compat_ion_handle_data {
|
||||
compat_int_t handle;
|
||||
};
|
||||
|
||||
#define COMPAT_ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \
|
||||
struct compat_ion_allocation_data)
|
||||
#define COMPAT_ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
|
||||
#define COMPAT_ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, \
|
||||
struct compat_ion_handle_data)
|
||||
#define COMPAT_ION_IOC_CUSTOM _IOWR(ION_IOC_MAGIC, 6, \
|
||||
struct compat_ion_custom_data)
|
||||
|
||||
|
|
@ -64,6 +69,19 @@ static int compat_get_ion_allocation_data(
|
|||
return err;
|
||||
}
|
||||
|
||||
static int compat_get_ion_handle_data(
|
||||
struct compat_ion_handle_data __user *data32,
|
||||
struct ion_handle_data __user *data)
|
||||
{
|
||||
compat_int_t i;
|
||||
int err;
|
||||
|
||||
err = get_user(i, &data32->handle);
|
||||
err |= put_user(i, &data->handle);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int compat_put_ion_allocation_data(
|
||||
struct compat_ion_allocation_data __user *data32,
|
||||
struct ion_allocation_data __user *data)
|
||||
|
|
@ -132,8 +150,8 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
}
|
||||
case COMPAT_ION_IOC_FREE:
|
||||
{
|
||||
struct compat_ion_allocation_data __user *data32;
|
||||
struct ion_allocation_data __user *data;
|
||||
struct compat_ion_handle_data __user *data32;
|
||||
struct ion_handle_data __user *data;
|
||||
int err;
|
||||
|
||||
data32 = compat_ptr(arg);
|
||||
|
|
@ -141,7 +159,7 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
if (data == NULL)
|
||||
return -EFAULT;
|
||||
|
||||
err = compat_get_ion_allocation_data(data32, data);
|
||||
err = compat_get_ion_handle_data(data32, data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ struct ion_device {
|
|||
unsigned long arg);
|
||||
struct rb_root clients;
|
||||
struct dentry *debug_root;
|
||||
struct dentry *heaps_debug_root;
|
||||
struct dentry *clients_debug_root;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -69,6 +71,8 @@ struct ion_device {
|
|||
* @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.
|
||||
|
|
@ -82,6 +86,8 @@ struct ion_client {
|
|||
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;
|
||||
|
|
@ -708,6 +714,21 @@ static const struct file_operations debug_client_fops = {
|
|||
.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)
|
||||
{
|
||||
|
|
@ -716,9 +737,13 @@ struct ion_client *ion_client_create(struct ion_device *dev,
|
|||
struct rb_node **p;
|
||||
struct rb_node *parent = NULL;
|
||||
struct ion_client *entry;
|
||||
char debug_name[64];
|
||||
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);
|
||||
|
|
@ -733,21 +758,27 @@ struct ion_client *ion_client_create(struct ion_device *dev,
|
|||
task_unlock(current->group_leader);
|
||||
|
||||
client = kzalloc(sizeof(struct ion_client), GFP_KERNEL);
|
||||
if (!client) {
|
||||
if (task)
|
||||
put_task_struct(current->group_leader);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
if (!client)
|
||||
goto err_put_task_struct;
|
||||
|
||||
client->dev = dev;
|
||||
client->handles = RB_ROOT;
|
||||
idr_init(&client->idr);
|
||||
mutex_init(&client->lock);
|
||||
client->name = name;
|
||||
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;
|
||||
|
|
@ -761,13 +792,28 @@ struct ion_client *ion_client_create(struct ion_device *dev,
|
|||
rb_link_node(&client->node, parent, p);
|
||||
rb_insert_color(&client->node, &dev->clients);
|
||||
|
||||
snprintf(debug_name, 64, "%u", client->pid);
|
||||
client->debug_root = debugfs_create_file(debug_name, 0664,
|
||||
dev->debug_root, client,
|
||||
&debug_client_fops);
|
||||
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);
|
||||
|
||||
|
|
@ -792,6 +838,8 @@ void ion_client_destroy(struct ion_client *client)
|
|||
debugfs_remove_recursive(client->debug_root);
|
||||
up_write(&dev->lock);
|
||||
|
||||
kfree(client->display_name);
|
||||
kfree(client->name);
|
||||
kfree(client);
|
||||
}
|
||||
EXPORT_SYMBOL(ion_client_destroy);
|
||||
|
|
@ -1293,9 +1341,11 @@ static int ion_open(struct inode *inode, struct file *file)
|
|||
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];
|
||||
|
||||
pr_debug("%s: %d\n", __func__, __LINE__);
|
||||
client = ion_client_create(dev, "user");
|
||||
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;
|
||||
|
|
@ -1443,6 +1493,8 @@ 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;
|
||||
|
||||
if (!heap->ops->allocate || !heap->ops->free || !heap->ops->map_dma ||
|
||||
!heap->ops->unmap_dma)
|
||||
pr_err("%s: can not add heap with invalid ops struct.\n",
|
||||
|
|
@ -1451,21 +1503,40 @@ 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);
|
||||
|
||||
heap->dev = dev;
|
||||
down_write(&dev->lock);
|
||||
/* use negative heap->id to reverse the priority -- when traversing
|
||||
the list later attempt higher id numbers first */
|
||||
plist_node_init(&heap->node, -heap->id);
|
||||
plist_add(&heap->node, &dev->heaps);
|
||||
debugfs_create_file(heap->name, 0664, dev->debug_root, heap,
|
||||
&debug_heap_fops);
|
||||
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);
|
||||
pr_err("Failed to create heap debugfs at %s/%s\n",
|
||||
path, heap->name);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_HEAP_SHRINKER
|
||||
if (heap->shrinker.shrink) {
|
||||
char debug_name[64];
|
||||
|
||||
snprintf(debug_name, 64, "%s_shrink", heap->name);
|
||||
debugfs_create_file(debug_name, 0644, dev->debug_root, heap,
|
||||
&debug_shrink_fops);
|
||||
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);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
up_write(&dev->lock);
|
||||
|
|
@ -1494,8 +1565,21 @@ 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 debug files.\n");
|
||||
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");
|
||||
|
||||
debugfs_done:
|
||||
|
||||
idev->custom_ioctl = custom_ioctl;
|
||||
idev->buffers = RB_ROOT;
|
||||
|
|
@ -1509,6 +1593,7 @@ struct ion_device *ion_device_create(long (*custom_ioctl)
|
|||
void ion_device_destroy(struct ion_device *dev)
|
||||
{
|
||||
misc_deregister(&dev->dev);
|
||||
debugfs_remove_recursive(dev->debug_root);
|
||||
/* XXX need to free the heaps and clients ? */
|
||||
kfree(dev);
|
||||
}
|
||||
|
|
|
|||
158
drivers/staging/android/ion/ion_dummy_driver.c
Normal file
158
drivers/staging/android/ion/ion_dummy_driver.c
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* drivers/gpu/ion/ion_dummy_driver.c
|
||||
*
|
||||
* Copyright (C) 2013 Linaro, Inc
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/sizes.h>
|
||||
#include "ion.h"
|
||||
#include "ion_priv.h"
|
||||
|
||||
struct ion_device *idev;
|
||||
struct ion_heap **heaps;
|
||||
|
||||
void *carveout_ptr;
|
||||
void *chunk_ptr;
|
||||
|
||||
struct ion_platform_heap dummy_heaps[] = {
|
||||
{
|
||||
.id = ION_HEAP_TYPE_SYSTEM,
|
||||
.type = ION_HEAP_TYPE_SYSTEM,
|
||||
.name = "system",
|
||||
},
|
||||
{
|
||||
.id = ION_HEAP_TYPE_SYSTEM_CONTIG,
|
||||
.type = ION_HEAP_TYPE_SYSTEM_CONTIG,
|
||||
.name = "system contig",
|
||||
},
|
||||
{
|
||||
.id = ION_HEAP_TYPE_CARVEOUT,
|
||||
.type = ION_HEAP_TYPE_CARVEOUT,
|
||||
.name = "carveout",
|
||||
.size = SZ_4M,
|
||||
},
|
||||
{
|
||||
.id = ION_HEAP_TYPE_CHUNK,
|
||||
.type = ION_HEAP_TYPE_CHUNK,
|
||||
.name = "chunk",
|
||||
.size = SZ_4M,
|
||||
.align = SZ_16K,
|
||||
.priv = (void *)(SZ_16K),
|
||||
},
|
||||
};
|
||||
|
||||
struct ion_platform_data dummy_ion_pdata = {
|
||||
.nr = 4,
|
||||
.heaps = dummy_heaps,
|
||||
};
|
||||
|
||||
static int __init ion_dummy_init(void)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
idev = ion_device_create(NULL);
|
||||
heaps = kzalloc(sizeof(struct ion_heap *) * dummy_ion_pdata.nr,
|
||||
GFP_KERNEL);
|
||||
if (!heaps)
|
||||
return PTR_ERR(heaps);
|
||||
|
||||
|
||||
/* Allocate a dummy carveout heap */
|
||||
carveout_ptr = alloc_pages_exact(
|
||||
dummy_heaps[ION_HEAP_TYPE_CARVEOUT].size,
|
||||
GFP_KERNEL);
|
||||
if (carveout_ptr)
|
||||
dummy_heaps[ION_HEAP_TYPE_CARVEOUT].base =
|
||||
virt_to_phys(carveout_ptr);
|
||||
else
|
||||
pr_err("ion_dummy: Could not allocate carveout\n");
|
||||
|
||||
/* Allocate a dummy chunk heap */
|
||||
chunk_ptr = alloc_pages_exact(
|
||||
dummy_heaps[ION_HEAP_TYPE_CHUNK].size,
|
||||
GFP_KERNEL);
|
||||
if (chunk_ptr)
|
||||
dummy_heaps[ION_HEAP_TYPE_CHUNK].base = virt_to_phys(chunk_ptr);
|
||||
else
|
||||
pr_err("ion_dummy: Could not allocate chunk\n");
|
||||
|
||||
for (i = 0; i < dummy_ion_pdata.nr; i++) {
|
||||
struct ion_platform_heap *heap_data = &dummy_ion_pdata.heaps[i];
|
||||
|
||||
if (heap_data->type == ION_HEAP_TYPE_CARVEOUT &&
|
||||
!heap_data->base)
|
||||
continue;
|
||||
|
||||
if (heap_data->type == ION_HEAP_TYPE_CHUNK && !heap_data->base)
|
||||
continue;
|
||||
|
||||
heaps[i] = ion_heap_create(heap_data);
|
||||
if (IS_ERR_OR_NULL(heaps[i])) {
|
||||
err = PTR_ERR(heaps[i]);
|
||||
goto err;
|
||||
}
|
||||
ion_device_add_heap(idev, heaps[i]);
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
for (i = 0; i < dummy_ion_pdata.nr; i++) {
|
||||
if (heaps[i])
|
||||
ion_heap_destroy(heaps[i]);
|
||||
}
|
||||
kfree(heaps);
|
||||
|
||||
if (carveout_ptr) {
|
||||
free_pages_exact(carveout_ptr,
|
||||
dummy_heaps[ION_HEAP_TYPE_CARVEOUT].size);
|
||||
carveout_ptr = NULL;
|
||||
}
|
||||
if (chunk_ptr) {
|
||||
free_pages_exact(chunk_ptr,
|
||||
dummy_heaps[ION_HEAP_TYPE_CHUNK].size);
|
||||
chunk_ptr = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit ion_dummy_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
ion_device_destroy(idev);
|
||||
|
||||
for (i = 0; i < dummy_ion_pdata.nr; i++)
|
||||
ion_heap_destroy(heaps[i]);
|
||||
kfree(heaps);
|
||||
|
||||
if (carveout_ptr) {
|
||||
free_pages_exact(carveout_ptr,
|
||||
dummy_heaps[ION_HEAP_TYPE_CARVEOUT].size);
|
||||
carveout_ptr = NULL;
|
||||
}
|
||||
if (chunk_ptr) {
|
||||
free_pages_exact(chunk_ptr,
|
||||
dummy_heaps[ION_HEAP_TYPE_CHUNK].size);
|
||||
chunk_ptr = NULL;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(ion_dummy_init);
|
||||
module_exit(ion_dummy_exit);
|
||||
|
||||
|
|
@ -160,10 +160,10 @@ int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot)
|
|||
|
||||
void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer)
|
||||
{
|
||||
rt_mutex_lock(&heap->lock);
|
||||
spin_lock(&heap->free_lock);
|
||||
list_add(&buffer->list, &heap->free_list);
|
||||
heap->free_list_size += buffer->size;
|
||||
rt_mutex_unlock(&heap->lock);
|
||||
spin_unlock(&heap->free_lock);
|
||||
wake_up(&heap->waitqueue);
|
||||
}
|
||||
|
||||
|
|
@ -171,38 +171,55 @@ size_t ion_heap_freelist_size(struct ion_heap *heap)
|
|||
{
|
||||
size_t size;
|
||||
|
||||
rt_mutex_lock(&heap->lock);
|
||||
spin_lock(&heap->free_lock);
|
||||
size = heap->free_list_size;
|
||||
rt_mutex_unlock(&heap->lock);
|
||||
spin_unlock(&heap->free_lock);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size)
|
||||
static size_t _ion_heap_freelist_drain(struct ion_heap *heap, size_t size,
|
||||
bool skip_pools)
|
||||
{
|
||||
struct ion_buffer *buffer, *tmp;
|
||||
struct ion_buffer *buffer;
|
||||
size_t total_drained = 0;
|
||||
|
||||
if (ion_heap_freelist_size(heap) == 0)
|
||||
return 0;
|
||||
|
||||
rt_mutex_lock(&heap->lock);
|
||||
spin_lock(&heap->free_lock);
|
||||
if (size == 0)
|
||||
size = heap->free_list_size;
|
||||
|
||||
list_for_each_entry_safe(buffer, tmp, &heap->free_list, list) {
|
||||
while (!list_empty(&heap->free_list)) {
|
||||
if (total_drained >= size)
|
||||
break;
|
||||
buffer = list_first_entry(&heap->free_list, struct ion_buffer,
|
||||
list);
|
||||
list_del(&buffer->list);
|
||||
heap->free_list_size -= buffer->size;
|
||||
if (skip_pools)
|
||||
buffer->private_flags |= ION_PRIV_FLAG_SHRINKER_FREE;
|
||||
total_drained += buffer->size;
|
||||
spin_unlock(&heap->free_lock);
|
||||
ion_buffer_destroy(buffer);
|
||||
spin_lock(&heap->free_lock);
|
||||
}
|
||||
rt_mutex_unlock(&heap->lock);
|
||||
spin_unlock(&heap->free_lock);
|
||||
|
||||
return total_drained;
|
||||
}
|
||||
|
||||
size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size)
|
||||
{
|
||||
return _ion_heap_freelist_drain(heap, size, false);
|
||||
}
|
||||
|
||||
size_t ion_heap_freelist_shrink(struct ion_heap *heap, size_t size)
|
||||
{
|
||||
return _ion_heap_freelist_drain(heap, size, true);
|
||||
}
|
||||
|
||||
static int ion_heap_deferred_free(void *data)
|
||||
{
|
||||
struct ion_heap *heap = data;
|
||||
|
|
@ -213,16 +230,16 @@ static int ion_heap_deferred_free(void *data)
|
|||
wait_event_freezable(heap->waitqueue,
|
||||
ion_heap_freelist_size(heap) > 0);
|
||||
|
||||
rt_mutex_lock(&heap->lock);
|
||||
spin_lock(&heap->free_lock);
|
||||
if (list_empty(&heap->free_list)) {
|
||||
rt_mutex_unlock(&heap->lock);
|
||||
spin_unlock(&heap->free_lock);
|
||||
continue;
|
||||
}
|
||||
buffer = list_first_entry(&heap->free_list, struct ion_buffer,
|
||||
list);
|
||||
list_del(&buffer->list);
|
||||
heap->free_list_size -= buffer->size;
|
||||
rt_mutex_unlock(&heap->lock);
|
||||
spin_unlock(&heap->free_lock);
|
||||
ion_buffer_destroy(buffer);
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +252,7 @@ int ion_heap_init_deferred_free(struct ion_heap *heap)
|
|||
|
||||
INIT_LIST_HEAD(&heap->free_list);
|
||||
heap->free_list_size = 0;
|
||||
rt_mutex_init(&heap->lock);
|
||||
spin_lock_init(&heap->free_lock);
|
||||
init_waitqueue_head(&heap->waitqueue);
|
||||
heap->task = kthread_run(ion_heap_deferred_free, heap,
|
||||
"%s", heap->name);
|
||||
|
|
@ -248,6 +265,44 @@ int ion_heap_init_deferred_free(struct ion_heap *heap)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ion_heap_shrink(struct shrinker *shrinker, struct shrink_control *sc)
|
||||
{
|
||||
struct ion_heap *heap = container_of(shrinker, struct ion_heap,
|
||||
shrinker);
|
||||
int total = 0;
|
||||
int freed = 0;
|
||||
int to_scan = sc->nr_to_scan;
|
||||
|
||||
if (to_scan == 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* shrink the free list first, no point in zeroing the memory if we're
|
||||
* just going to reclaim it. Also, skip any possible page pooling.
|
||||
*/
|
||||
if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
|
||||
freed = ion_heap_freelist_shrink(heap, to_scan * PAGE_SIZE) /
|
||||
PAGE_SIZE;
|
||||
|
||||
to_scan -= freed;
|
||||
if (to_scan < 0)
|
||||
to_scan = 0;
|
||||
|
||||
out:
|
||||
total = ion_heap_freelist_size(heap) / PAGE_SIZE;
|
||||
if (heap->ops->shrink)
|
||||
total += heap->ops->shrink(heap, sc->gfp_mask, to_scan);
|
||||
return total;
|
||||
}
|
||||
|
||||
void ion_heap_init_shrinker(struct ion_heap *heap)
|
||||
{
|
||||
heap->shrinker.shrink = ion_heap_shrink;
|
||||
heap->shrinker.seeks = DEFAULT_SEEKS;
|
||||
heap->shrinker.batch = 0;
|
||||
register_shrinker(&heap->shrinker);
|
||||
}
|
||||
|
||||
struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
|
||||
{
|
||||
struct ion_heap *heap = NULL;
|
||||
|
|
|
|||
|
|
@ -130,15 +130,11 @@ static int ion_page_pool_total(struct ion_page_pool *pool, bool high)
|
|||
int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
|
||||
int nr_to_scan)
|
||||
{
|
||||
int nr_freed = 0;
|
||||
int i;
|
||||
bool high;
|
||||
|
||||
high = !!(gfp_mask & __GFP_HIGHMEM);
|
||||
|
||||
if (nr_to_scan == 0)
|
||||
return ion_page_pool_total(pool, high);
|
||||
|
||||
for (i = 0; i < nr_to_scan; i++) {
|
||||
struct page *page;
|
||||
|
||||
|
|
@ -153,10 +149,9 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
|
|||
}
|
||||
mutex_unlock(&pool->mutex);
|
||||
ion_page_pool_free_pages(pool, page);
|
||||
nr_freed += (1 << pool->order);
|
||||
}
|
||||
|
||||
return nr_freed;
|
||||
return ion_page_pool_total(pool, high);
|
||||
}
|
||||
|
||||
struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#ifndef _ION_PRIV_H
|
||||
#define _ION_PRIV_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/mm_types.h>
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/shrinker.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "ion.h"
|
||||
|
||||
|
|
@ -37,6 +39,7 @@ struct ion_buffer *ion_handle_buffer(struct ion_handle *handle);
|
|||
* @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 *
|
||||
|
|
@ -65,6 +68,7 @@ struct ion_buffer {
|
|||
struct ion_device *dev;
|
||||
struct ion_heap *heap;
|
||||
unsigned long flags;
|
||||
unsigned long private_flags;
|
||||
size_t size;
|
||||
union {
|
||||
void *priv_virt;
|
||||
|
|
@ -97,7 +101,11 @@ void ion_buffer_destroy(struct ion_buffer *buffer);
|
|||
* @map_user map memory to userspace
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
struct ion_heap_ops {
|
||||
int (*allocate) (struct ion_heap *heap,
|
||||
|
|
@ -113,6 +121,7 @@ struct ion_heap_ops {
|
|||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -120,6 +129,17 @@ struct ion_heap_ops {
|
|||
*/
|
||||
#define ION_HEAP_FLAG_DEFER_FREE (1 << 0)
|
||||
|
||||
/**
|
||||
* 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 (1 << 0)
|
||||
|
||||
/**
|
||||
* struct ion_heap - represents a heap in the system
|
||||
* @node: rb node to put the heap on the device's tree of heaps
|
||||
|
|
@ -131,10 +151,7 @@ struct ion_heap_ops {
|
|||
* allocating. These are specified by platform data and
|
||||
* MUST be unique
|
||||
* @name: used for debugging
|
||||
* @shrinker: a shrinker for the heap, if the heap caches system
|
||||
* memory, it must define a shrinker to return it on low
|
||||
* memory conditions, this includes system memory cached
|
||||
* in the deferred free lists for heaps that support it
|
||||
* @shrinker: a shrinker for the heap
|
||||
* @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
|
||||
|
|
@ -159,7 +176,7 @@ struct ion_heap {
|
|||
struct shrinker shrinker;
|
||||
struct list_head free_list;
|
||||
size_t free_list_size;
|
||||
struct rt_mutex lock;
|
||||
spinlock_t free_lock;
|
||||
wait_queue_head_t waitqueue;
|
||||
struct task_struct *task;
|
||||
int (*debug_show)(struct ion_heap *heap, struct seq_file *, void *);
|
||||
|
|
@ -217,6 +234,16 @@ int ion_heap_map_user(struct ion_heap *, struct ion_buffer *,
|
|||
int ion_heap_buffer_zero(struct ion_buffer *buffer);
|
||||
int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot);
|
||||
|
||||
/**
|
||||
* ion_heap_init_shrinker
|
||||
* @heap: the heap
|
||||
*
|
||||
* 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_heap_init_shrinker(struct ion_heap *heap);
|
||||
|
||||
/**
|
||||
* ion_heap_init_deferred_free -- initialize deferred free functionality
|
||||
* @heap: the heap
|
||||
|
|
@ -248,6 +275,29 @@ void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer *buffer);
|
|||
*/
|
||||
size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
size_t ion_heap_freelist_shrink(struct ion_heap *heap,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* ion_heap_freelist_size - returns the size of the freelist in bytes
|
||||
* @heap: the heap
|
||||
|
|
@ -304,13 +354,8 @@ void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
|
|||
* @low_count: number of lowmem items in the pool
|
||||
* @high_items: list of highmem items
|
||||
* @low_items: list of lowmem items
|
||||
* @shrinker: a shrinker for the items
|
||||
* @mutex: lock protecting this struct and especially the count
|
||||
* item list
|
||||
* @alloc: function to be used to allocate pageory when the pool
|
||||
* is empty
|
||||
* @free: function to be used to free pageory back to the system
|
||||
* when the shrinker fires
|
||||
* @gfp_mask: gfp_mask to use from alloc
|
||||
* @order: order of pages in the pool
|
||||
* @list: plist node for list of pools
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ static void free_buffer_page(struct ion_system_heap *heap,
|
|||
{
|
||||
bool cached = ion_buffer_cached(buffer);
|
||||
|
||||
if (!cached) {
|
||||
if (!cached && !(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE)) {
|
||||
struct ion_page_pool *pool = heap->pools[order_to_index(order)];
|
||||
ion_page_pool_free(pool, page);
|
||||
} else {
|
||||
|
|
@ -108,6 +108,10 @@ static struct page_info *alloc_largest_available(struct ion_system_heap *heap,
|
|||
struct page_info *info;
|
||||
int i;
|
||||
|
||||
info = kmalloc(sizeof(struct page_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
if (size < order_to_size(orders[i]))
|
||||
continue;
|
||||
|
|
@ -118,11 +122,13 @@ static struct page_info *alloc_largest_available(struct ion_system_heap *heap,
|
|||
if (!page)
|
||||
continue;
|
||||
|
||||
info = kmalloc(sizeof(struct page_info), GFP_KERNEL);
|
||||
info->page = page;
|
||||
info->order = orders[i];
|
||||
INIT_LIST_HEAD(&info->list);
|
||||
return info;
|
||||
}
|
||||
kfree(info);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -140,12 +146,15 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
|
|||
struct list_head pages;
|
||||
struct page_info *info, *tmp_info;
|
||||
int i = 0;
|
||||
long size_remaining = PAGE_ALIGN(size);
|
||||
unsigned long size_remaining = PAGE_ALIGN(size);
|
||||
unsigned int max_order = orders[0];
|
||||
|
||||
if (align > PAGE_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (size / PAGE_SIZE > totalram_pages / 2)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&pages);
|
||||
while (size_remaining > 0) {
|
||||
info = alloc_largest_available(sys_heap, buffer, size_remaining,
|
||||
|
|
@ -200,7 +209,7 @@ static void ion_system_heap_free(struct ion_buffer *buffer)
|
|||
|
||||
/* uncached pages come from the page pools, zero them before returning
|
||||
for security purposes (other allocations are zerod at alloc time */
|
||||
if (!cached)
|
||||
if (!cached && !(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE))
|
||||
ion_heap_buffer_zero(buffer);
|
||||
|
||||
for_each_sg(table->sgl, sg, table->nents, i)
|
||||
|
|
@ -222,6 +231,23 @@ static void ion_system_heap_unmap_dma(struct ion_heap *heap,
|
|||
return;
|
||||
}
|
||||
|
||||
static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,
|
||||
int nr_to_scan)
|
||||
{
|
||||
struct ion_system_heap *sys_heap;
|
||||
int nr_total = 0;
|
||||
int i;
|
||||
|
||||
sys_heap = container_of(heap, struct ion_system_heap, heap);
|
||||
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
struct ion_page_pool *pool = sys_heap->pools[i];
|
||||
nr_total += ion_page_pool_shrink(pool, gfp_mask, nr_to_scan);
|
||||
}
|
||||
|
||||
return nr_total;
|
||||
}
|
||||
|
||||
static struct ion_heap_ops system_heap_ops = {
|
||||
.allocate = ion_system_heap_allocate,
|
||||
.free = ion_system_heap_free,
|
||||
|
|
@ -230,52 +256,9 @@ static struct ion_heap_ops system_heap_ops = {
|
|||
.map_kernel = ion_heap_map_kernel,
|
||||
.unmap_kernel = ion_heap_unmap_kernel,
|
||||
.map_user = ion_heap_map_user,
|
||||
.shrink = ion_system_heap_shrink,
|
||||
};
|
||||
|
||||
static int ion_system_heap_shrink(struct shrinker *shrinker,
|
||||
struct shrink_control *sc) {
|
||||
|
||||
struct ion_heap *heap = container_of(shrinker, struct ion_heap,
|
||||
shrinker);
|
||||
struct ion_system_heap *sys_heap = container_of(heap,
|
||||
struct ion_system_heap,
|
||||
heap);
|
||||
int nr_total = 0;
|
||||
int nr_freed = 0;
|
||||
int i;
|
||||
|
||||
if (sc->nr_to_scan == 0)
|
||||
goto end;
|
||||
|
||||
/* shrink the free list first, no point in zeroing the memory if
|
||||
we're just going to reclaim it */
|
||||
nr_freed += ion_heap_freelist_drain(heap, sc->nr_to_scan * PAGE_SIZE) /
|
||||
PAGE_SIZE;
|
||||
|
||||
if (nr_freed >= sc->nr_to_scan)
|
||||
goto end;
|
||||
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
struct ion_page_pool *pool = sys_heap->pools[i];
|
||||
|
||||
nr_freed += ion_page_pool_shrink(pool, sc->gfp_mask,
|
||||
sc->nr_to_scan);
|
||||
if (nr_freed >= sc->nr_to_scan)
|
||||
break;
|
||||
}
|
||||
|
||||
end:
|
||||
/* total number of items is whatever the page pools are holding
|
||||
plus whatever's in the freelist */
|
||||
for (i = 0; i < num_orders; i++) {
|
||||
struct ion_page_pool *pool = sys_heap->pools[i];
|
||||
nr_total += ion_page_pool_shrink(pool, sc->gfp_mask, 0);
|
||||
}
|
||||
nr_total += ion_heap_freelist_size(heap) / PAGE_SIZE;
|
||||
return nr_total;
|
||||
|
||||
}
|
||||
|
||||
static int ion_system_heap_debug_show(struct ion_heap *heap, struct seq_file *s,
|
||||
void *unused)
|
||||
{
|
||||
|
|
@ -323,10 +306,6 @@ struct ion_heap *ion_system_heap_create(struct ion_platform_heap *unused)
|
|||
heap->pools[i] = pool;
|
||||
}
|
||||
|
||||
heap->heap.shrinker.shrink = ion_system_heap_shrink;
|
||||
heap->heap.shrinker.seeks = DEFAULT_SEEKS;
|
||||
heap->heap.shrinker.batch = 0;
|
||||
register_shrinker(&heap->heap.shrinker);
|
||||
heap->heap.debug_show = ion_system_heap_debug_show;
|
||||
return &heap->heap;
|
||||
err_create_pool:
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ static int ion_test_release(struct inode *inode, struct file *file)
|
|||
static const struct file_operations ion_test_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = ion_test_ioctl,
|
||||
.compat_ioctl = ion_test_ioctl,
|
||||
.open = ion_test_open,
|
||||
.release = ion_test_release,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,12 +20,11 @@
|
|||
#include "../ion.h"
|
||||
#include "../ion_priv.h"
|
||||
|
||||
struct ion_device *idev;
|
||||
struct ion_mapper *tegra_user_mapper;
|
||||
int num_heaps;
|
||||
struct ion_heap **heaps;
|
||||
static struct ion_device *idev;
|
||||
static int num_heaps;
|
||||
static struct ion_heap **heaps;
|
||||
|
||||
int tegra_ion_probe(struct platform_device *pdev)
|
||||
static int tegra_ion_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ion_platform_data *pdata = pdev->dev.platform_data;
|
||||
int err;
|
||||
|
|
@ -63,7 +62,7 @@ int tegra_ion_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
int tegra_ion_remove(struct platform_device *pdev)
|
||||
static int tegra_ion_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ion_device *idev = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
|
@ -81,16 +80,5 @@ static struct platform_driver ion_driver = {
|
|||
.driver = { .name = "ion-tegra" }
|
||||
};
|
||||
|
||||
static int __init ion_init(void)
|
||||
{
|
||||
return platform_driver_register(&ion_driver);
|
||||
}
|
||||
|
||||
static void __exit ion_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ion_driver);
|
||||
}
|
||||
|
||||
module_init(ion_init);
|
||||
module_exit(ion_exit);
|
||||
module_platform_driver(ion_driver);
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,14 @@ enum {
|
|||
FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100,
|
||||
};
|
||||
|
||||
#ifdef BINDER_IPC_32BIT
|
||||
typedef __u32 binder_size_t;
|
||||
typedef __u32 binder_uintptr_t;
|
||||
#else
|
||||
typedef __u64 binder_size_t;
|
||||
typedef __u64 binder_uintptr_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the flattened representation of a Binder object for transfer
|
||||
* between processes. The 'offsets' supplied as part of a binder transaction
|
||||
|
|
@ -48,17 +56,17 @@ enum {
|
|||
*/
|
||||
struct flat_binder_object {
|
||||
/* 8 bytes for large_flat_header. */
|
||||
unsigned long type;
|
||||
unsigned long flags;
|
||||
__u32 type;
|
||||
__u32 flags;
|
||||
|
||||
/* 8 bytes of data. */
|
||||
union {
|
||||
void __user *binder; /* local object */
|
||||
signed long handle; /* remote object */
|
||||
binder_uintptr_t binder; /* local object */
|
||||
__u32 handle; /* remote object */
|
||||
};
|
||||
|
||||
/* extra data associated with local object */
|
||||
void __user *cookie;
|
||||
binder_uintptr_t cookie;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -67,26 +75,30 @@ struct flat_binder_object {
|
|||
*/
|
||||
|
||||
struct binder_write_read {
|
||||
signed long write_size; /* bytes to write */
|
||||
signed long write_consumed; /* bytes consumed by driver */
|
||||
unsigned long write_buffer;
|
||||
signed long read_size; /* bytes to read */
|
||||
signed long read_consumed; /* bytes consumed by driver */
|
||||
unsigned long read_buffer;
|
||||
binder_size_t write_size; /* bytes to write */
|
||||
binder_size_t write_consumed; /* bytes consumed by driver */
|
||||
binder_uintptr_t write_buffer;
|
||||
binder_size_t read_size; /* bytes to read */
|
||||
binder_size_t read_consumed; /* bytes consumed by driver */
|
||||
binder_uintptr_t read_buffer;
|
||||
};
|
||||
|
||||
/* Use with BINDER_VERSION, driver fills in fields. */
|
||||
struct binder_version {
|
||||
/* driver protocol version -- increment with incompatible change */
|
||||
signed long protocol_version;
|
||||
__s32 protocol_version;
|
||||
};
|
||||
|
||||
/* This is the current protocol version. */
|
||||
#ifdef BINDER_IPC_32BIT
|
||||
#define BINDER_CURRENT_PROTOCOL_VERSION 7
|
||||
#else
|
||||
#define BINDER_CURRENT_PROTOCOL_VERSION 8
|
||||
#endif
|
||||
|
||||
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
|
||||
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64)
|
||||
#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)
|
||||
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
|
||||
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, __s32)
|
||||
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32)
|
||||
#define BINDER_THREAD_EXIT _IOW('b', 8, __s32)
|
||||
|
|
@ -119,18 +131,18 @@ struct binder_transaction_data {
|
|||
* identifying the target and contents of the transaction.
|
||||
*/
|
||||
union {
|
||||
size_t handle; /* target descriptor of command transaction */
|
||||
void *ptr; /* target descriptor of return transaction */
|
||||
__u32 handle; /* target descriptor of command transaction */
|
||||
binder_uintptr_t ptr; /* target descriptor of return transaction */
|
||||
} target;
|
||||
void *cookie; /* target object cookie */
|
||||
unsigned int code; /* transaction command */
|
||||
binder_uintptr_t cookie; /* target object cookie */
|
||||
__u32 code; /* transaction command */
|
||||
|
||||
/* General information about the transaction. */
|
||||
unsigned int flags;
|
||||
__u32 flags;
|
||||
pid_t sender_pid;
|
||||
uid_t sender_euid;
|
||||
size_t data_size; /* number of bytes of data */
|
||||
size_t offsets_size; /* number of bytes of offsets */
|
||||
binder_size_t data_size; /* number of bytes of data */
|
||||
binder_size_t offsets_size; /* number of bytes of offsets */
|
||||
|
||||
/* If this transaction is inline, the data immediately
|
||||
* follows here; otherwise, it ends with a pointer to
|
||||
|
|
@ -139,32 +151,37 @@ struct binder_transaction_data {
|
|||
union {
|
||||
struct {
|
||||
/* transaction data */
|
||||
const void __user *buffer;
|
||||
binder_uintptr_t buffer;
|
||||
/* offsets from buffer to flat_binder_object structs */
|
||||
const void __user *offsets;
|
||||
binder_uintptr_t offsets;
|
||||
} ptr;
|
||||
uint8_t buf[8];
|
||||
__u8 buf[8];
|
||||
} data;
|
||||
};
|
||||
|
||||
struct binder_ptr_cookie {
|
||||
void *ptr;
|
||||
void *cookie;
|
||||
binder_uintptr_t ptr;
|
||||
binder_uintptr_t cookie;
|
||||
};
|
||||
|
||||
struct binder_handle_cookie {
|
||||
__u32 handle;
|
||||
binder_uintptr_t cookie;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct binder_pri_desc {
|
||||
int priority;
|
||||
int desc;
|
||||
__s32 priority;
|
||||
__u32 desc;
|
||||
};
|
||||
|
||||
struct binder_pri_ptr_cookie {
|
||||
int priority;
|
||||
void *ptr;
|
||||
void *cookie;
|
||||
__s32 priority;
|
||||
binder_uintptr_t ptr;
|
||||
binder_uintptr_t cookie;
|
||||
};
|
||||
|
||||
enum binder_driver_return_protocol {
|
||||
BR_ERROR = _IOR('r', 0, int),
|
||||
BR_ERROR = _IOR('r', 0, __s32),
|
||||
/*
|
||||
* int: error code
|
||||
*/
|
||||
|
|
@ -178,7 +195,7 @@ enum binder_driver_return_protocol {
|
|||
* binder_transaction_data: the received command.
|
||||
*/
|
||||
|
||||
BR_ACQUIRE_RESULT = _IOR('r', 4, int),
|
||||
BR_ACQUIRE_RESULT = _IOR('r', 4, __s32),
|
||||
/*
|
||||
* not currently supported
|
||||
* int: 0 if the last bcATTEMPT_ACQUIRE was not successful.
|
||||
|
|
@ -235,11 +252,11 @@ enum binder_driver_return_protocol {
|
|||
* stop threadpool thread
|
||||
*/
|
||||
|
||||
BR_DEAD_BINDER = _IOR('r', 15, void *),
|
||||
BR_DEAD_BINDER = _IOR('r', 15, binder_uintptr_t),
|
||||
/*
|
||||
* void *: cookie
|
||||
*/
|
||||
BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, void *),
|
||||
BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, binder_uintptr_t),
|
||||
/*
|
||||
* void *: cookie
|
||||
*/
|
||||
|
|
@ -258,22 +275,22 @@ enum binder_driver_command_protocol {
|
|||
* binder_transaction_data: the sent command.
|
||||
*/
|
||||
|
||||
BC_ACQUIRE_RESULT = _IOW('c', 2, int),
|
||||
BC_ACQUIRE_RESULT = _IOW('c', 2, __s32),
|
||||
/*
|
||||
* not currently supported
|
||||
* int: 0 if the last BR_ATTEMPT_ACQUIRE was not successful.
|
||||
* Else you have acquired a primary reference on the object.
|
||||
*/
|
||||
|
||||
BC_FREE_BUFFER = _IOW('c', 3, int),
|
||||
BC_FREE_BUFFER = _IOW('c', 3, binder_uintptr_t),
|
||||
/*
|
||||
* void *: ptr to transaction data received on a read
|
||||
*/
|
||||
|
||||
BC_INCREFS = _IOW('c', 4, int),
|
||||
BC_ACQUIRE = _IOW('c', 5, int),
|
||||
BC_RELEASE = _IOW('c', 6, int),
|
||||
BC_DECREFS = _IOW('c', 7, int),
|
||||
BC_INCREFS = _IOW('c', 4, __u32),
|
||||
BC_ACQUIRE = _IOW('c', 5, __u32),
|
||||
BC_RELEASE = _IOW('c', 6, __u32),
|
||||
BC_DECREFS = _IOW('c', 7, __u32),
|
||||
/*
|
||||
* int: descriptor
|
||||
*/
|
||||
|
|
@ -308,19 +325,19 @@ enum binder_driver_command_protocol {
|
|||
* of looping threads it has available.
|
||||
*/
|
||||
|
||||
BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_ptr_cookie),
|
||||
BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_handle_cookie),
|
||||
/*
|
||||
* void *: ptr to binder
|
||||
* int: handle
|
||||
* void *: cookie
|
||||
*/
|
||||
|
||||
BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_ptr_cookie),
|
||||
BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_handle_cookie),
|
||||
/*
|
||||
* void *: ptr to binder
|
||||
* int: handle
|
||||
* void *: cookie
|
||||
*/
|
||||
|
||||
BC_DEAD_BINDER_DONE = _IOW('c', 16, void *),
|
||||
BC_DEAD_BINDER_DONE = _IOW('c', 16, binder_uintptr_t),
|
||||
/*
|
||||
* void *: cookie
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -20,8 +20,6 @@
|
|||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
typedef int ion_user_handle_t;
|
||||
|
||||
/**
|
||||
* struct ion_test_rw_data - metadata passed to the kernel to read handle
|
||||
* @ptr: a pointer to an area at least as large as size
|
||||
|
|
@ -34,6 +32,7 @@ struct ion_test_rw_data {
|
|||
__u64 offset;
|
||||
__u64 size;
|
||||
int write;
|
||||
int __padding;
|
||||
};
|
||||
|
||||
#define ION_IOC_MAGIC 'I'
|
||||
|
|
|
|||
|
|
@ -261,8 +261,10 @@ static void acc_complete_in(struct usb_ep *ep, struct usb_request *req)
|
|||
{
|
||||
struct acc_dev *dev = _acc_dev;
|
||||
|
||||
if (req->status != 0)
|
||||
if (req->status == -ESHUTDOWN) {
|
||||
pr_debug("acc_complete_in set disconnected");
|
||||
acc_set_disconnected(dev);
|
||||
}
|
||||
|
||||
req_put(dev, &dev->tx_idle, req);
|
||||
|
||||
|
|
@ -274,8 +276,10 @@ static void acc_complete_out(struct usb_ep *ep, struct usb_request *req)
|
|||
struct acc_dev *dev = _acc_dev;
|
||||
|
||||
dev->rx_done = 1;
|
||||
if (req->status != 0)
|
||||
if (req->status == -ESHUTDOWN) {
|
||||
pr_debug("acc_complete_out set disconnected");
|
||||
acc_set_disconnected(dev);
|
||||
}
|
||||
|
||||
wake_up(&dev->read_wq);
|
||||
}
|
||||
|
|
@ -552,13 +556,16 @@ static ssize_t acc_read(struct file *fp, char __user *buf,
|
|||
{
|
||||
struct acc_dev *dev = fp->private_data;
|
||||
struct usb_request *req;
|
||||
int r = count, xfer;
|
||||
ssize_t r = count;
|
||||
unsigned xfer;
|
||||
int ret = 0;
|
||||
|
||||
pr_debug("acc_read(%d)\n", count);
|
||||
pr_debug("acc_read(%zu)\n", count);
|
||||
|
||||
if (dev->disconnected)
|
||||
if (dev->disconnected) {
|
||||
pr_debug("acc_read disconnected");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (count > BULK_BUFFER_SIZE)
|
||||
count = BULK_BUFFER_SIZE;
|
||||
|
|
@ -571,6 +578,12 @@ static ssize_t acc_read(struct file *fp, char __user *buf,
|
|||
goto done;
|
||||
}
|
||||
|
||||
if (dev->rx_done) {
|
||||
// last req cancelled. try to get it.
|
||||
req = dev->rx_req[0];
|
||||
goto copy_data;
|
||||
}
|
||||
|
||||
requeue_req:
|
||||
/* queue a request */
|
||||
req = dev->rx_req[0];
|
||||
|
|
@ -588,15 +601,23 @@ static ssize_t acc_read(struct file *fp, char __user *buf,
|
|||
ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
|
||||
if (ret < 0) {
|
||||
r = ret;
|
||||
usb_ep_dequeue(dev->ep_out, req);
|
||||
ret = usb_ep_dequeue(dev->ep_out, req);
|
||||
if (ret != 0) {
|
||||
// cancel failed. There can be a data already received.
|
||||
// it will be retrieved in the next read.
|
||||
pr_debug("acc_read: cancelling failed %d", ret);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
copy_data:
|
||||
dev->rx_done = 0;
|
||||
if (dev->online) {
|
||||
/* If we got a 0-len packet, throw it back and try again. */
|
||||
if (req->actual == 0)
|
||||
goto requeue_req;
|
||||
|
||||
pr_debug("rx %p %d\n", req, req->actual);
|
||||
pr_debug("rx %p %u\n", req, req->actual);
|
||||
xfer = (req->actual < count) ? req->actual : count;
|
||||
r = xfer;
|
||||
if (copy_to_user(buf, req->buf, xfer))
|
||||
|
|
@ -605,7 +626,7 @@ static ssize_t acc_read(struct file *fp, char __user *buf,
|
|||
r = -EIO;
|
||||
|
||||
done:
|
||||
pr_debug("acc_read returning %d\n", r);
|
||||
pr_debug("acc_read returning %zd\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -614,13 +635,16 @@ static ssize_t acc_write(struct file *fp, const char __user *buf,
|
|||
{
|
||||
struct acc_dev *dev = fp->private_data;
|
||||
struct usb_request *req = 0;
|
||||
int r = count, xfer;
|
||||
ssize_t r = count;
|
||||
unsigned xfer;
|
||||
int ret;
|
||||
|
||||
pr_debug("acc_write(%d)\n", count);
|
||||
pr_debug("acc_write(%zu)\n", count);
|
||||
|
||||
if (!dev->online || dev->disconnected)
|
||||
if (!dev->online || dev->disconnected) {
|
||||
pr_debug("acc_write disconnected or not online");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
while (count > 0) {
|
||||
if (!dev->online) {
|
||||
|
|
@ -665,7 +689,7 @@ static ssize_t acc_write(struct file *fp, const char __user *buf,
|
|||
if (req)
|
||||
req_put(dev, &dev->tx_idle, req);
|
||||
|
||||
pr_debug("acc_write returning %d\n", r);
|
||||
pr_debug("acc_write returning %zd\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -466,10 +466,11 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
|
|||
struct mtp_dev *dev = fp->private_data;
|
||||
struct usb_composite_dev *cdev = dev->cdev;
|
||||
struct usb_request *req;
|
||||
int r = count, xfer;
|
||||
ssize_t r = count;
|
||||
unsigned xfer;
|
||||
int ret = 0;
|
||||
|
||||
DBG(cdev, "mtp_read(%d)\n", count);
|
||||
DBG(cdev, "mtp_read(%zu)\n", count);
|
||||
|
||||
if (count > MTP_BULK_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
|
|
@ -533,7 +534,7 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
|
|||
dev->state = STATE_READY;
|
||||
spin_unlock_irq(&dev->lock);
|
||||
|
||||
DBG(cdev, "mtp_read returning %d\n", r);
|
||||
DBG(cdev, "mtp_read returning %zd\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -543,11 +544,12 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf,
|
|||
struct mtp_dev *dev = fp->private_data;
|
||||
struct usb_composite_dev *cdev = dev->cdev;
|
||||
struct usb_request *req = 0;
|
||||
int r = count, xfer;
|
||||
ssize_t r = count;
|
||||
unsigned xfer;
|
||||
int sendZLP = 0;
|
||||
int ret;
|
||||
|
||||
DBG(cdev, "mtp_write(%d)\n", count);
|
||||
DBG(cdev, "mtp_write(%zu)\n", count);
|
||||
|
||||
spin_lock_irq(&dev->lock);
|
||||
if (dev->state == STATE_CANCELED) {
|
||||
|
|
@ -624,7 +626,7 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf,
|
|||
dev->state = STATE_READY;
|
||||
spin_unlock_irq(&dev->lock);
|
||||
|
||||
DBG(cdev, "mtp_write returning %d\n", r);
|
||||
DBG(cdev, "mtp_write returning %zd\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
@ -823,7 +825,7 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event)
|
|||
int ret;
|
||||
int length = event->length;
|
||||
|
||||
DBG(dev->cdev, "mtp_send_event(%d)\n", event->length);
|
||||
DBG(dev->cdev, "mtp_send_event(%zu)\n", event->length);
|
||||
|
||||
if (length < 0 || length > INTR_BUFFER_SIZE)
|
||||
return -EINVAL;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
* Copyright (C) 2013 Google, Inc.
|
||||
* adf_modeinfo_{set_name,set_vrefresh} modified from
|
||||
* drivers/gpu/drm/drm_modes.c
|
||||
* adf_format_validate_yuv modified from framebuffer_check in
|
||||
* drivers/gpu/drm/drm_crtc.c
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
|
|
@ -492,6 +494,7 @@ static void adf_obj_destroy(struct adf_obj *obj, struct idr *idr)
|
|||
struct adf_event_refcount *refcount =
|
||||
container_of(node, struct adf_event_refcount,
|
||||
node);
|
||||
rb_erase(&refcount->node, &obj->event_refcount);
|
||||
kfree(refcount);
|
||||
node = rb_first(&obj->event_refcount);
|
||||
}
|
||||
|
|
@ -920,6 +923,7 @@ int adf_attachment_allow(struct adf_device *dev,
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(adf_attachment_allow);
|
||||
|
||||
/**
|
||||
* adf_obj_type_str - string representation of an adf_obj_type
|
||||
|
|
@ -1070,6 +1074,7 @@ int adf_format_validate_yuv(struct adf_device *dev, struct adf_buffer *buf,
|
|||
u32 width = buf->w / (i != 0 ? hsub : 1);
|
||||
u32 height = buf->h / (i != 0 ? vsub : 1);
|
||||
u8 cpp = adf_format_plane_cpp(buf->format, i);
|
||||
u32 last_line_size;
|
||||
|
||||
if (buf->pitch[i] < (u64) width * cpp) {
|
||||
dev_err(&dev->base.dev, "plane %u pitch is shorter than buffer width (pitch = %u, width = %u, bpp = %u)\n",
|
||||
|
|
@ -1077,8 +1082,21 @@ int adf_format_validate_yuv(struct adf_device *dev, struct adf_buffer *buf,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((u64) height * buf->pitch[i] + buf->offset[i] >
|
||||
buf->dma_bufs[i]->size) {
|
||||
switch (dev->ops->quirks.buffer_padding) {
|
||||
case ADF_BUFFER_PADDED_TO_PITCH:
|
||||
last_line_size = buf->pitch[i];
|
||||
break;
|
||||
|
||||
case ADF_BUFFER_UNPADDED:
|
||||
last_line_size = width * cpp;
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
if ((u64) (height - 1) * buf->pitch[i] + last_line_size +
|
||||
buf->offset[i] > buf->dma_bufs[i]->size) {
|
||||
dev_err(&dev->base.dev, "plane %u buffer too small (height = %u, pitch = %u, offset = %u, size = %zu)\n",
|
||||
i, height, buf->pitch[i],
|
||||
buf->offset[i], buf->dma_bufs[i]->size);
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ static int adf_buffer_map(struct adf_device *dev, struct adf_buffer *buf,
|
|||
attachment = dma_buf_attach(buf->dma_bufs[i], dev->dev);
|
||||
if (IS_ERR(attachment)) {
|
||||
ret = PTR_ERR(attachment);
|
||||
dev_err(&dev->base.dev, "attaching plane %u failed: %d\n",
|
||||
dev_err(&dev->base.dev, "attaching plane %zu failed: %d\n",
|
||||
i, ret);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -292,12 +292,13 @@ static int adf_buffer_map(struct adf_device *dev, struct adf_buffer *buf,
|
|||
sg_table = dma_buf_map_attachment(attachment, DMA_TO_DEVICE);
|
||||
if (IS_ERR(sg_table)) {
|
||||
ret = PTR_ERR(sg_table);
|
||||
dev_err(&dev->base.dev, "mapping plane %u failed: %d",
|
||||
dev_err(&dev->base.dev, "mapping plane %zu failed: %d",
|
||||
i, ret);
|
||||
goto done;
|
||||
} else if (!sg_table) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&dev->base.dev, "mapping plane %u failed\n", i);
|
||||
dev_err(&dev->base.dev, "mapping plane %zu failed\n",
|
||||
i);
|
||||
goto done;
|
||||
}
|
||||
mapping->sg_tables[i] = sg_table;
|
||||
|
|
|
|||
|
|
@ -356,18 +356,25 @@ int adf_fbdev_open(struct fb_info *info, int user)
|
|||
struct adf_fbdev *fbdev = info->par;
|
||||
int ret;
|
||||
|
||||
if (!fbdev->open) {
|
||||
mutex_lock(&fbdev->refcount_lock);
|
||||
|
||||
if (unlikely(fbdev->refcount == UINT_MAX)) {
|
||||
ret = -EMFILE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!fbdev->refcount) {
|
||||
struct drm_mode_modeinfo mode;
|
||||
struct fb_videomode fbmode;
|
||||
struct adf_device *dev = adf_interface_parent(fbdev->intf);
|
||||
|
||||
ret = adf_device_attach(dev, fbdev->eng, fbdev->intf);
|
||||
if (ret < 0 && ret != -EALREADY)
|
||||
return ret;
|
||||
goto done;
|
||||
|
||||
ret = adf_fb_alloc(fbdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto done;
|
||||
|
||||
adf_interface_current_mode(fbdev->intf, &mode);
|
||||
adf_modeinfo_to_fb_videomode(&mode, &fbmode);
|
||||
|
|
@ -379,13 +386,15 @@ int adf_fbdev_open(struct fb_info *info, int user)
|
|||
|
||||
ret = adf_fbdev_post(fbdev);
|
||||
if (ret < 0) {
|
||||
if (!fbdev->open)
|
||||
if (!fbdev->refcount)
|
||||
adf_fb_destroy(fbdev);
|
||||
return ret;
|
||||
goto done;
|
||||
}
|
||||
|
||||
fbdev->open = true;
|
||||
return 0;
|
||||
fbdev->refcount++;
|
||||
done:
|
||||
mutex_unlock(&fbdev->refcount_lock);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(adf_fbdev_open);
|
||||
|
||||
|
|
@ -395,8 +404,12 @@ EXPORT_SYMBOL(adf_fbdev_open);
|
|||
int adf_fbdev_release(struct fb_info *info, int user)
|
||||
{
|
||||
struct adf_fbdev *fbdev = info->par;
|
||||
adf_fb_destroy(fbdev);
|
||||
fbdev->open = false;
|
||||
mutex_lock(&fbdev->refcount_lock);
|
||||
BUG_ON(!fbdev->refcount);
|
||||
fbdev->refcount--;
|
||||
if (!fbdev->refcount)
|
||||
adf_fb_destroy(fbdev);
|
||||
mutex_unlock(&fbdev->refcount_lock);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(adf_fbdev_release);
|
||||
|
|
@ -601,6 +614,7 @@ int adf_fbdev_init(struct adf_fbdev *fbdev, struct adf_interface *interface,
|
|||
dev_err(dev, "allocating framebuffer device failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
mutex_init(&fbdev->refcount_lock);
|
||||
fbdev->default_xres_virtual = xres_virtual;
|
||||
fbdev->default_yres_virtual = yres_virtual;
|
||||
fbdev->default_format = format;
|
||||
|
|
@ -644,8 +658,8 @@ EXPORT_SYMBOL(adf_fbdev_init);
|
|||
void adf_fbdev_destroy(struct adf_fbdev *fbdev)
|
||||
{
|
||||
unregister_framebuffer(fbdev->info);
|
||||
if (WARN_ON(fbdev->open))
|
||||
adf_fb_destroy(fbdev);
|
||||
BUG_ON(fbdev->refcount);
|
||||
mutex_destroy(&fbdev->refcount_lock);
|
||||
framebuffer_release(fbdev->info);
|
||||
}
|
||||
EXPORT_SYMBOL(adf_fbdev_destroy);
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ static int adf_buffer_import(struct adf_device *dev,
|
|||
buf->dma_bufs[i] = dma_buf_get(user_buf.fd[i]);
|
||||
if (IS_ERR(buf->dma_bufs[i])) {
|
||||
ret = PTR_ERR(buf->dma_bufs[i]);
|
||||
dev_err(&dev->base.dev, "importing dma_buf fd %llu failed: %d\n",
|
||||
dev_err(&dev->base.dev, "importing dma_buf fd %d failed: %d\n",
|
||||
user_buf.fd[i], ret);
|
||||
buf->dma_bufs[i] = NULL;
|
||||
goto done;
|
||||
|
|
@ -200,7 +200,7 @@ static int adf_buffer_import(struct adf_device *dev,
|
|||
if (user_buf.acquire_fence >= 0) {
|
||||
buf->acquire_fence = sync_fence_fdget(user_buf.acquire_fence);
|
||||
if (!buf->acquire_fence) {
|
||||
dev_err(&dev->base.dev, "getting fence fd %lld failed\n",
|
||||
dev_err(&dev->base.dev, "getting fence fd %d failed\n",
|
||||
user_buf.acquire_fence);
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ long adf_compat_get_device_data(struct file *file,
|
|||
&data->custom_data))
|
||||
return -EFAULT;
|
||||
|
||||
ret = adf_file_ioctl(file, ADF_GET_DEVICE_DATA32, (unsigned long)data);
|
||||
ret = adf_file_ioctl(file, ADF_GET_DEVICE_DATA, (unsigned long)data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -122,7 +122,7 @@ long adf_compat_get_interface_data(struct file *file,
|
|||
&data->custom_data))
|
||||
return -EFAULT;
|
||||
|
||||
ret = adf_file_ioctl(file, ADF_GET_DEVICE_DATA32, (unsigned long)data);
|
||||
ret = adf_file_ioctl(file, ADF_GET_INTERFACE_DATA, (unsigned long)data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@
|
|||
#include <video/adf.h>
|
||||
|
||||
#define ADF_POST_CONFIG32 \
|
||||
_IOW('D', 2, struct adf_post_config32)
|
||||
_IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config32)
|
||||
#define ADF_GET_DEVICE_DATA32 \
|
||||
_IOR('D', 4, struct adf_device_data32)
|
||||
_IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data32)
|
||||
#define ADF_GET_INTERFACE_DATA32 \
|
||||
_IOR('D', 5, struct adf_interface_data32)
|
||||
_IOR(ADF_IOCTL_TYPE, 5, struct adf_interface_data32)
|
||||
#define ADF_GET_OVERLAY_ENGINE_DATA32 \
|
||||
_IOR('D', 6, struct adf_overlay_engine_data32)
|
||||
_IOR(ADF_IOCTL_TYPE, 6, struct adf_overlay_engine_data32)
|
||||
|
||||
struct adf_post_config32 {
|
||||
compat_size_t n_interfaces;
|
||||
|
|
@ -25,7 +25,7 @@ struct adf_post_config32 {
|
|||
compat_size_t custom_data_size;
|
||||
compat_uptr_t custom_data;
|
||||
|
||||
__s64 complete_fence;
|
||||
__s32 complete_fence;
|
||||
};
|
||||
|
||||
struct adf_device_data32 {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ static struct sg_table *adf_memblock_map(struct dma_buf_attachment *attach,
|
|||
unsigned long pfn = PFN_DOWN(pdata->base);
|
||||
struct page *page = pfn_to_page(pfn);
|
||||
struct sg_table *table;
|
||||
int ret;
|
||||
int nents, ret;
|
||||
|
||||
table = kzalloc(sizeof(*table), GFP_KERNEL);
|
||||
if (!table)
|
||||
|
|
@ -36,12 +36,21 @@ static struct sg_table *adf_memblock_map(struct dma_buf_attachment *attach,
|
|||
|
||||
ret = sg_alloc_table(table, 1, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
goto err_alloc;
|
||||
|
||||
sg_set_page(table->sgl, page, attach->dmabuf->size, 0);
|
||||
|
||||
nents = dma_map_sg(attach->dev, table->sgl, 1, direction);
|
||||
if (!nents) {
|
||||
ret = -EINVAL;
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
return table;
|
||||
|
||||
err:
|
||||
err_map:
|
||||
sg_free_table(table);
|
||||
err_alloc:
|
||||
kfree(table);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
|
@ -49,6 +58,7 @@ static struct sg_table *adf_memblock_map(struct dma_buf_attachment *attach,
|
|||
static void adf_memblock_unmap(struct dma_buf_attachment *attach,
|
||||
struct sg_table *table, enum dma_data_direction direction)
|
||||
{
|
||||
dma_unmap_sg(attach->dev, table->sgl, 1, direction);
|
||||
sg_free_table(table);
|
||||
}
|
||||
|
||||
|
|
@ -147,3 +157,4 @@ struct dma_buf *adf_memblock_export(phys_addr_t base, size_t size, int flags)
|
|||
|
||||
return buf;
|
||||
}
|
||||
EXPORT_SYMBOL(adf_memblock_export);
|
||||
|
|
|
|||
11
fs/dcache.c
11
fs/dcache.c
|
|
@ -2724,6 +2724,17 @@ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen,
|
|||
return memcpy(buffer, temp, sz);
|
||||
}
|
||||
|
||||
char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
|
||||
{
|
||||
char *end = buffer + buflen;
|
||||
/* these dentries are never renamed, so d_lock is not needed */
|
||||
if (prepend(&end, &buflen, " (deleted)", 11) ||
|
||||
prepend_name(&end, &buflen, &dentry->d_name) ||
|
||||
prepend(&end, &buflen, "/", 1))
|
||||
end = ERR_PTR(-ENAMETOOLONG);
|
||||
return end;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write full pathname from the root of the filesystem into the buffer.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1819,8 +1819,7 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
|
|||
goto error_tgt_fput;
|
||||
|
||||
/* Check if EPOLLWAKEUP is allowed */
|
||||
if ((epds.events & EPOLLWAKEUP) && !capable(CAP_BLOCK_SUSPEND))
|
||||
epds.events &= ~EPOLLWAKEUP;
|
||||
ep_take_care_of_epollwakeup(&epds);
|
||||
|
||||
/*
|
||||
* We have to check that the file structure underneath the file descriptor
|
||||
|
|
|
|||
|
|
@ -916,14 +916,8 @@ static int get_hstate_idx(int page_size_log)
|
|||
return h - hstates;
|
||||
}
|
||||
|
||||
static char *hugetlb_dname(struct dentry *dentry, char *buffer, int buflen)
|
||||
{
|
||||
return dynamic_dname(dentry, buffer, buflen, "/%s (deleted)",
|
||||
dentry->d_name.name);
|
||||
}
|
||||
|
||||
static struct dentry_operations anon_ops = {
|
||||
.d_dname = hugetlb_dname
|
||||
.d_dname = simple_dname
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -377,6 +377,12 @@ static int ramoops_init_prz(struct device *dev, struct ramoops_context *cxt,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void notrace ramoops_console_write_buf(const char *buf, size_t size)
|
||||
{
|
||||
struct ramoops_context *cxt = &oops_cxt;
|
||||
persistent_ram_write(cxt->cprz, buf, size);
|
||||
}
|
||||
|
||||
static int ramoops_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
|
|
|||
|
|
@ -317,6 +317,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
|
|||
(clockid != CLOCK_MONOTONIC &&
|
||||
clockid != CLOCK_REALTIME &&
|
||||
clockid != CLOCK_REALTIME_ALARM &&
|
||||
clockid != CLOCK_BOOTTIME &&
|
||||
clockid != CLOCK_BOOTTIME_ALARM))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
|||
|
|
@ -340,6 +340,7 @@ const char *cpufreq_get_current_driver(void);
|
|||
int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu);
|
||||
int cpufreq_update_policy(unsigned int cpu);
|
||||
bool have_governor_per_policy(void);
|
||||
struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy);
|
||||
|
||||
#ifdef CONFIG_CPU_FREQ
|
||||
/* query the current CPU frequency (in kHz). If zero, cpufreq couldn't detect it */
|
||||
|
|
|
|||
|
|
@ -332,6 +332,7 @@ extern int d_validate(struct dentry *, struct dentry *);
|
|||
* helper function for dentry_operations.d_dname() members
|
||||
*/
|
||||
extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
|
||||
extern char *simple_dname(struct dentry *, char *, int);
|
||||
|
||||
extern char *__d_path(const struct path *, const struct path *, char *, int);
|
||||
extern char *d_absolute_path(const struct path *, char *, int);
|
||||
|
|
|
|||
|
|
@ -91,6 +91,27 @@ extern int of_flat_dt_is_compatible(unsigned long node, const char *name);
|
|||
extern int of_flat_dt_match(unsigned long node, const char *const *matches);
|
||||
extern unsigned long of_get_flat_dt_root(void);
|
||||
|
||||
/*
|
||||
* early_init_dt_scan_chosen - scan the device tree for ramdisk and bootargs
|
||||
*
|
||||
* The boot arguments will be placed into the memory pointed to by @data.
|
||||
* That memory should be COMMAND_LINE_SIZE big and initialized to be a valid
|
||||
* (possibly empty) string. Logic for what will be in @data after this
|
||||
* function finishes:
|
||||
*
|
||||
* - CONFIG_CMDLINE_FORCE=true
|
||||
* CONFIG_CMDLINE
|
||||
* - CONFIG_CMDLINE_EXTEND=true, @data is non-empty string
|
||||
* @data + dt bootargs (even if dt bootargs are empty)
|
||||
* - CONFIG_CMDLINE_EXTEND=true, @data is empty string
|
||||
* CONFIG_CMDLINE + dt bootargs (even if dt bootargs are empty)
|
||||
* - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=non-empty:
|
||||
* dt bootargs
|
||||
* - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is non-empty string
|
||||
* @data is left unchanged
|
||||
* - CMDLINE_FROM_BOOTLOADER=true, dt bootargs=empty, @data is empty string
|
||||
* CONFIG_CMDLINE (or "" if that's not defined)
|
||||
*/
|
||||
extern int early_init_dt_scan_chosen(unsigned long node, const char *uname,
|
||||
int depth, void *data);
|
||||
extern void early_init_dt_check_for_initrd(unsigned long node);
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ void persistent_ram_free_old(struct persistent_ram_zone *prz);
|
|||
ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
|
||||
char *str, size_t len);
|
||||
|
||||
void ramoops_console_write_buf(const char *buf, size_t size);
|
||||
|
||||
/*
|
||||
* Ramoops platform data
|
||||
* @mem_size memory size for ramoops
|
||||
|
|
|
|||
23
include/linux/wakeup_reason.h
Normal file
23
include/linux/wakeup_reason.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* include/linux/wakeup_reason.h
|
||||
*
|
||||
* Logs the reason which caused the kernel to resume
|
||||
* from the suspend mode.
|
||||
*
|
||||
* Copyright (C) 2014 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.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_WAKEUP_REASON_H
|
||||
#define _LINUX_WAKEUP_REASON_H
|
||||
|
||||
void log_wakeup_reason(int irq);
|
||||
|
||||
#endif /* _LINUX_WAKEUP_REASON_H */
|
||||
|
|
@ -260,6 +260,12 @@ static inline void fl6_sock_release(struct ip6_flowlabel *fl)
|
|||
|
||||
extern void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info);
|
||||
|
||||
int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
|
||||
struct icmp6hdr *thdr, int len);
|
||||
|
||||
struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb,
|
||||
struct sock *sk, struct flowi6 *fl6);
|
||||
|
||||
extern int ip6_ra_control(struct sock *sk, int sel);
|
||||
|
||||
extern int ipv6_parse_hopopts(struct sk_buff *skb);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#ifndef _PING_H
|
||||
#define _PING_H
|
||||
|
||||
#include <net/icmp.h>
|
||||
#include <net/netns/hash.h>
|
||||
|
||||
/* PING_HTABLE_SIZE must be power of 2 */
|
||||
|
|
@ -28,6 +29,18 @@
|
|||
*/
|
||||
#define GID_T_MAX (((gid_t)~0U) >> 1)
|
||||
|
||||
/* Compatibility glue so we can support IPv6 when it's compiled as a module */
|
||||
struct pingv6_ops {
|
||||
int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len);
|
||||
int (*ip6_datagram_recv_ctl)(struct sock *sk, struct msghdr *msg,
|
||||
struct sk_buff *skb);
|
||||
int (*icmpv6_err_convert)(u8 type, u8 code, int *err);
|
||||
void (*ipv6_icmp_error)(struct sock *sk, struct sk_buff *skb, int err,
|
||||
__be16 port, u32 info, u8 *payload);
|
||||
int (*ipv6_chk_addr)(struct net *net, const struct in6_addr *addr,
|
||||
const struct net_device *dev, int strict);
|
||||
};
|
||||
|
||||
struct ping_table {
|
||||
struct hlist_nulls_head hash[PING_HTABLE_SIZE];
|
||||
rwlock_t lock;
|
||||
|
|
@ -39,10 +52,39 @@ struct ping_iter_state {
|
|||
};
|
||||
|
||||
extern struct proto ping_prot;
|
||||
extern struct ping_table ping_table;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
extern struct pingv6_ops pingv6_ops;
|
||||
#endif
|
||||
|
||||
struct pingfakehdr {
|
||||
struct icmphdr icmph;
|
||||
struct iovec *iov;
|
||||
sa_family_t family;
|
||||
__wsum wcheck;
|
||||
};
|
||||
|
||||
extern void ping_rcv(struct sk_buff *);
|
||||
extern void ping_err(struct sk_buff *, u32 info);
|
||||
int ping_get_port(struct sock *sk, unsigned short ident);
|
||||
void ping_hash(struct sock *sk);
|
||||
void ping_unhash(struct sock *sk);
|
||||
|
||||
int ping_init_sock(struct sock *sk);
|
||||
void ping_close(struct sock *sk, long timeout);
|
||||
int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len);
|
||||
void ping_err(struct sk_buff *skb, int offset, u32 info);
|
||||
int ping_getfrag(void *from, char *to, int offset, int fraglen, int odd,
|
||||
struct sk_buff *);
|
||||
|
||||
int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
||||
size_t len, int noblock, int flags, int *addr_len);
|
||||
int ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
|
||||
void *user_icmph, size_t icmph_len);
|
||||
int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
||||
size_t len);
|
||||
int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
||||
size_t len);
|
||||
int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
|
||||
void ping_rcv(struct sk_buff *skb);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
extern int __init ping_proc_init(void);
|
||||
|
|
@ -50,6 +92,7 @@ extern void ping_proc_exit(void);
|
|||
#endif
|
||||
|
||||
void __init ping_init(void);
|
||||
|
||||
int __init pingv6_init(void);
|
||||
void pingv6_exit(void);
|
||||
|
||||
#endif /* _PING_H */
|
||||
|
|
|
|||
|
|
@ -287,6 +287,7 @@ extern int sysctl_tcp_thin_dupack;
|
|||
extern int sysctl_tcp_early_retrans;
|
||||
extern int sysctl_tcp_limit_output_bytes;
|
||||
extern int sysctl_tcp_challenge_ack_limit;
|
||||
extern int sysctl_tcp_default_init_rwnd;
|
||||
|
||||
extern atomic_long_t tcp_memory_allocated;
|
||||
extern struct percpu_counter tcp_sockets_allocated;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ extern struct proto rawv6_prot;
|
|||
extern struct proto udpv6_prot;
|
||||
extern struct proto udplitev6_prot;
|
||||
extern struct proto tcpv6_prot;
|
||||
extern struct proto pingv6_prot;
|
||||
|
||||
struct flowi6;
|
||||
|
||||
|
|
@ -21,6 +22,8 @@ extern int ipv6_frag_init(void);
|
|||
extern void ipv6_frag_exit(void);
|
||||
|
||||
/* transport protocols */
|
||||
extern int pingv6_init(void);
|
||||
extern void pingv6_exit(void);
|
||||
extern int rawv6_init(void);
|
||||
extern void rawv6_exit(void);
|
||||
extern int udpv6_init(void);
|
||||
|
|
|
|||
|
|
@ -61,5 +61,16 @@ struct epoll_event {
|
|||
__u64 data;
|
||||
} EPOLL_PACKED;
|
||||
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static inline void ep_take_care_of_epollwakeup(struct epoll_event *epev)
|
||||
{
|
||||
if ((epev->events & EPOLLWAKEUP) && !capable(CAP_BLOCK_SUSPEND))
|
||||
epev->events &= ~EPOLLWAKEUP;
|
||||
}
|
||||
#else
|
||||
static inline void ep_take_care_of_epollwakeup(struct epoll_event *epev)
|
||||
{
|
||||
epev->events &= ~EPOLLWAKEUP;
|
||||
}
|
||||
#endif
|
||||
#endif /* _UAPI_LINUX_EVENTPOLL_H */
|
||||
|
|
|
|||
|
|
@ -461,10 +461,14 @@ struct input_keymap_entry {
|
|||
#define KEY_VIDEO_NEXT 241 /* drive next video source */
|
||||
#define KEY_VIDEO_PREV 242 /* drive previous video source */
|
||||
#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */
|
||||
#define KEY_BRIGHTNESS_ZERO 244 /* brightness off, use ambient */
|
||||
#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual
|
||||
brightness control is off,
|
||||
rely on ambient */
|
||||
#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO
|
||||
#define KEY_DISPLAY_OFF 245 /* display device to off state */
|
||||
|
||||
#define KEY_WIMAX 246
|
||||
#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */
|
||||
#define KEY_WIMAX KEY_WWAN
|
||||
#define KEY_RFKILL 247 /* Key that controls all radios */
|
||||
|
||||
#define KEY_MICMUTE 248 /* Mute / unmute the microphone */
|
||||
|
|
@ -509,11 +513,15 @@ struct input_keymap_entry {
|
|||
#define BTN_DEAD 0x12f
|
||||
|
||||
#define BTN_GAMEPAD 0x130
|
||||
#define BTN_A 0x130
|
||||
#define BTN_B 0x131
|
||||
#define BTN_SOUTH 0x130
|
||||
#define BTN_A BTN_SOUTH
|
||||
#define BTN_EAST 0x131
|
||||
#define BTN_B BTN_EAST
|
||||
#define BTN_C 0x132
|
||||
#define BTN_X 0x133
|
||||
#define BTN_Y 0x134
|
||||
#define BTN_NORTH 0x133
|
||||
#define BTN_X BTN_NORTH
|
||||
#define BTN_WEST 0x134
|
||||
#define BTN_Y BTN_WEST
|
||||
#define BTN_Z 0x135
|
||||
#define BTN_TL 0x136
|
||||
#define BTN_TR 0x137
|
||||
|
|
@ -626,6 +634,7 @@ struct input_keymap_entry {
|
|||
#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */
|
||||
#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */
|
||||
#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */
|
||||
#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE
|
||||
#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */
|
||||
#define KEY_LOGOFF 0x1b1 /* AL Logoff */
|
||||
|
||||
|
|
@ -710,6 +719,24 @@ struct input_keymap_entry {
|
|||
#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */
|
||||
#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */
|
||||
|
||||
#define BTN_DPAD_UP 0x220
|
||||
#define BTN_DPAD_DOWN 0x221
|
||||
#define BTN_DPAD_LEFT 0x222
|
||||
#define BTN_DPAD_RIGHT 0x223
|
||||
|
||||
#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */
|
||||
|
||||
#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */
|
||||
#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */
|
||||
#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */
|
||||
#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */
|
||||
#define KEY_APPSELECT 0x244 /* AL Select Task/Application */
|
||||
#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */
|
||||
#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */
|
||||
|
||||
#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
|
||||
#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
|
||||
|
||||
#define BTN_TRIGGER_HAPPY 0x2c0
|
||||
#define BTN_TRIGGER_HAPPY1 0x2c0
|
||||
#define BTN_TRIGGER_HAPPY2 0x2c1
|
||||
|
|
@ -847,6 +874,7 @@ struct input_keymap_entry {
|
|||
#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */
|
||||
#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */
|
||||
#define SW_LINEIN_INSERT 0x0d /* set = inserted */
|
||||
#define SW_MUTE_DEVICE 0x0e /* set = device disabled */
|
||||
#define SW_MAX 0x0f
|
||||
#define SW_CNT (SW_MAX+1)
|
||||
|
||||
|
|
|
|||
|
|
@ -149,6 +149,12 @@
|
|||
|
||||
#define PR_GET_TID_ADDRESS 40
|
||||
|
||||
/* Sets the timerslack for arbitrary threads
|
||||
* arg2 slack value, 0 means "use default"
|
||||
* arg3 pid of the thread whose timer slack needs to be set
|
||||
*/
|
||||
#define PR_SET_TIMERSLACK_PID 41
|
||||
|
||||
#define PR_SET_VMA 0x53564d41
|
||||
# define PR_SET_VMA_ANON_NAME 0
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ struct adf_event {
|
|||
*/
|
||||
struct adf_vsync_event {
|
||||
struct adf_event base;
|
||||
__u64 timestamp;
|
||||
__aligned_u64 timestamp;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -119,12 +119,12 @@ struct adf_buffer_config {
|
|||
__u32 h;
|
||||
__u32 format;
|
||||
|
||||
__s64 fd[ADF_MAX_PLANES];
|
||||
__s32 fd[ADF_MAX_PLANES];
|
||||
__u32 offset[ADF_MAX_PLANES];
|
||||
__u32 pitch[ADF_MAX_PLANES];
|
||||
__u8 n_planes;
|
||||
|
||||
__s64 acquire_fence;
|
||||
__s32 acquire_fence;
|
||||
};
|
||||
#define ADF_MAX_BUFFERS (4096 / sizeof(struct adf_buffer_config))
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ struct adf_post_config {
|
|||
size_t custom_data_size;
|
||||
void __user *custom_data;
|
||||
|
||||
__s64 complete_fence;
|
||||
__s32 complete_fence;
|
||||
};
|
||||
#define ADF_MAX_INTERFACES (4096 / sizeof(__u32))
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ struct adf_simple_buffer_alloc {
|
|||
__u16 h;
|
||||
__u32 format;
|
||||
|
||||
__s64 fd;
|
||||
__s32 fd;
|
||||
__u32 offset;
|
||||
__u32 pitch;
|
||||
};
|
||||
|
|
@ -195,7 +195,7 @@ struct adf_simple_buffer_alloc {
|
|||
*/
|
||||
struct adf_simple_post_config {
|
||||
struct adf_buffer_config buf;
|
||||
__s64 complete_fence;
|
||||
__s32 complete_fence;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -295,17 +295,27 @@ struct adf_overlay_engine_data {
|
|||
};
|
||||
#define ADF_MAX_SUPPORTED_FORMATS (4096 / sizeof(__u32))
|
||||
|
||||
#define ADF_SET_EVENT _IOW('D', 0, struct adf_set_event)
|
||||
#define ADF_BLANK _IOW('D', 1, __u8)
|
||||
#define ADF_POST_CONFIG _IOW('D', 2, struct adf_post_config)
|
||||
#define ADF_SET_MODE _IOW('D', 3, struct drm_mode_modeinfo)
|
||||
#define ADF_GET_DEVICE_DATA _IOR('D', 4, struct adf_device_data)
|
||||
#define ADF_GET_INTERFACE_DATA _IOR('D', 5, struct adf_interface_data)
|
||||
#define ADF_IOCTL_TYPE 'D'
|
||||
#define ADF_IOCTL_NR_CUSTOM 128
|
||||
|
||||
#define ADF_SET_EVENT _IOW(ADF_IOCTL_TYPE, 0, struct adf_set_event)
|
||||
#define ADF_BLANK _IOW(ADF_IOCTL_TYPE, 1, __u8)
|
||||
#define ADF_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 2, struct adf_post_config)
|
||||
#define ADF_SET_MODE _IOW(ADF_IOCTL_TYPE, 3, \
|
||||
struct drm_mode_modeinfo)
|
||||
#define ADF_GET_DEVICE_DATA _IOR(ADF_IOCTL_TYPE, 4, struct adf_device_data)
|
||||
#define ADF_GET_INTERFACE_DATA _IOR(ADF_IOCTL_TYPE, 5, \
|
||||
struct adf_interface_data)
|
||||
#define ADF_GET_OVERLAY_ENGINE_DATA \
|
||||
_IOR('D', 6, struct adf_overlay_engine_data)
|
||||
#define ADF_SIMPLE_POST_CONFIG _IOW('D', 7, struct adf_simple_post_config)
|
||||
#define ADF_SIMPLE_BUFFER_ALLOC _IOW('D', 8, struct adf_simple_buffer_alloc)
|
||||
#define ADF_ATTACH _IOW('D', 9, struct adf_attachment_config)
|
||||
#define ADF_DETACH _IOW('D', 10, struct adf_attachment_config)
|
||||
_IOR(ADF_IOCTL_TYPE, 6, \
|
||||
struct adf_overlay_engine_data)
|
||||
#define ADF_SIMPLE_POST_CONFIG _IOW(ADF_IOCTL_TYPE, 7, \
|
||||
struct adf_simple_post_config)
|
||||
#define ADF_SIMPLE_BUFFER_ALLOC _IOW(ADF_IOCTL_TYPE, 8, \
|
||||
struct adf_simple_buffer_alloc)
|
||||
#define ADF_ATTACH _IOW(ADF_IOCTL_TYPE, 9, \
|
||||
struct adf_attachment_config)
|
||||
#define ADF_DETACH _IOW(ADF_IOCTL_TYPE, 10, \
|
||||
struct adf_attachment_config)
|
||||
|
||||
#endif /* _UAPI_VIDEO_ADF_H_ */
|
||||
|
|
|
|||
|
|
@ -192,11 +192,27 @@ struct adf_obj {
|
|||
int minor;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adf_device_quirks - common display device quirks
|
||||
*
|
||||
* @buffer_padding: whether the last scanline of a buffer extends to the
|
||||
* buffer's pitch (@ADF_BUFFER_PADDED_TO_PITCH) or just to the visible
|
||||
* width (@ADF_BUFFER_UNPADDED)
|
||||
*/
|
||||
struct adf_device_quirks {
|
||||
/* optional, defaults to ADF_BUFFER_PADDED_TO_PITCH */
|
||||
enum {
|
||||
ADF_BUFFER_PADDED_TO_PITCH = 0,
|
||||
ADF_BUFFER_UNPADDED = 1,
|
||||
} buffer_padding;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adf_device_ops - display device implementation ops
|
||||
*
|
||||
* @owner: device's module
|
||||
* @base: common operations (see &struct adf_obj_ops)
|
||||
* @quirks: device's quirks (see &struct adf_device_quirks)
|
||||
*
|
||||
* @attach: attach overlay engine @eng to interface @intf. Return 0 on success
|
||||
* or error code (<0) on failure.
|
||||
|
|
@ -228,6 +244,8 @@ struct adf_device_ops {
|
|||
/* required */
|
||||
struct module *owner;
|
||||
const struct adf_obj_ops base;
|
||||
/* optional */
|
||||
const struct adf_device_quirks quirks;
|
||||
|
||||
/* optional */
|
||||
int (*attach)(struct adf_device *dev, struct adf_overlay_engine *eng,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#define _VIDEO_ADF_FBDEV_H_
|
||||
|
||||
#include <linux/fb.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <video/adf.h>
|
||||
|
||||
struct adf_fbdev {
|
||||
|
|
@ -24,7 +25,8 @@ struct adf_fbdev {
|
|||
struct fb_info *info;
|
||||
u32 pseudo_palette[16];
|
||||
|
||||
bool open;
|
||||
unsigned int refcount;
|
||||
struct mutex refcount_lock;
|
||||
|
||||
struct dma_buf *dma_buf;
|
||||
u32 offset;
|
||||
|
|
@ -37,6 +39,7 @@ struct adf_fbdev {
|
|||
u32 default_format;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_ADF_FBDEV)
|
||||
void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode,
|
||||
struct fb_videomode *vmode);
|
||||
void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode,
|
||||
|
|
@ -55,5 +58,67 @@ int adf_fbdev_set_par(struct fb_info *info);
|
|||
int adf_fbdev_blank(int blank, struct fb_info *info);
|
||||
int adf_fbdev_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
|
||||
int adf_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma);
|
||||
#else
|
||||
static inline void adf_modeinfo_to_fb_videomode(const struct drm_mode_modeinfo *mode,
|
||||
struct fb_videomode *vmode)
|
||||
{
|
||||
WARN_ONCE(1, "%s: CONFIG_ADF_FBDEV is disabled\n", __func__);
|
||||
}
|
||||
|
||||
static inline void adf_modeinfo_from_fb_videomode(const struct fb_videomode *vmode,
|
||||
struct drm_mode_modeinfo *mode)
|
||||
{
|
||||
WARN_ONCE(1, "%s: CONFIG_ADF_FBDEV is disabled\n", __func__);
|
||||
}
|
||||
|
||||
static inline int adf_fbdev_init(struct adf_fbdev *fbdev,
|
||||
struct adf_interface *interface,
|
||||
struct adf_overlay_engine *eng,
|
||||
u16 xres_virtual, u16 yres_virtual, u32 format,
|
||||
struct fb_ops *fbops, const char *fmt, ...)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline void adf_fbdev_destroy(struct adf_fbdev *fbdev) { }
|
||||
|
||||
static inline int adf_fbdev_open(struct fb_info *info, int user)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int adf_fbdev_release(struct fb_info *info, int user)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int adf_fbdev_check_var(struct fb_var_screeninfo *var,
|
||||
struct fb_info *info)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int adf_fbdev_set_par(struct fb_info *info)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int adf_fbdev_blank(int blank, struct fb_info *info)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int adf_fbdev_pan_display(struct fb_var_screeninfo *var,
|
||||
struct fb_info *info)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int adf_fbdev_mmap(struct fb_info *info,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _VIDEO_ADF_FBDEV_H_ */
|
||||
|
|
|
|||
|
|
@ -14,3 +14,5 @@ obj-$(CONFIG_PM_WAKELOCKS) += wakelock.o
|
|||
obj-$(CONFIG_SUSPEND_TIME) += suspend_time.o
|
||||
|
||||
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
|
||||
|
||||
obj-$(CONFIG_SUSPEND) += wakeup_reason.o
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
* manipulate wakelocks on Android.
|
||||
*/
|
||||
|
||||
#include <linux/capability.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
|
|
@ -189,9 +188,6 @@ int pm_wake_lock(const char *buf)
|
|||
size_t len;
|
||||
int ret = 0;
|
||||
|
||||
if (!capable(CAP_BLOCK_SUSPEND))
|
||||
return -EPERM;
|
||||
|
||||
while (*str && !isspace(*str))
|
||||
str++;
|
||||
|
||||
|
|
@ -235,9 +231,6 @@ int pm_wake_unlock(const char *buf)
|
|||
size_t len;
|
||||
int ret = 0;
|
||||
|
||||
if (!capable(CAP_BLOCK_SUSPEND))
|
||||
return -EPERM;
|
||||
|
||||
len = strlen(buf);
|
||||
if (!len)
|
||||
return -EINVAL;
|
||||
|
|
|
|||
139
kernel/power/wakeup_reason.c
Normal file
139
kernel/power/wakeup_reason.c
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* kernel/power/wakeup_reason.c
|
||||
*
|
||||
* Logs the reasons which caused the kernel to resume from
|
||||
* the suspend mode.
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/wakeup_reason.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
|
||||
#define MAX_WAKEUP_REASON_IRQS 32
|
||||
static int irq_list[MAX_WAKEUP_REASON_IRQS];
|
||||
static int irqcount;
|
||||
static struct kobject *wakeup_reason;
|
||||
static spinlock_t resume_reason_lock;
|
||||
|
||||
static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int irq_no, buf_offset = 0;
|
||||
struct irq_desc *desc;
|
||||
spin_lock(&resume_reason_lock);
|
||||
for (irq_no = 0; irq_no < irqcount; irq_no++) {
|
||||
desc = irq_to_desc(irq_list[irq_no]);
|
||||
if (desc && desc->action && desc->action->name)
|
||||
buf_offset += sprintf(buf + buf_offset, "%d %s\n",
|
||||
irq_list[irq_no], desc->action->name);
|
||||
else
|
||||
buf_offset += sprintf(buf + buf_offset, "%d\n",
|
||||
irq_list[irq_no]);
|
||||
}
|
||||
spin_unlock(&resume_reason_lock);
|
||||
return buf_offset;
|
||||
}
|
||||
|
||||
static struct kobj_attribute resume_reason = __ATTR_RO(last_resume_reason);
|
||||
|
||||
static struct attribute *attrs[] = {
|
||||
&resume_reason.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group attr_group = {
|
||||
.attrs = attrs,
|
||||
};
|
||||
|
||||
/*
|
||||
* logs all the wake up reasons to the kernel
|
||||
* stores the irqs to expose them to the userspace via sysfs
|
||||
*/
|
||||
void log_wakeup_reason(int irq)
|
||||
{
|
||||
struct irq_desc *desc;
|
||||
desc = irq_to_desc(irq);
|
||||
if (desc && desc->action && desc->action->name)
|
||||
printk(KERN_INFO "Resume caused by IRQ %d, %s\n", irq,
|
||||
desc->action->name);
|
||||
else
|
||||
printk(KERN_INFO "Resume caused by IRQ %d\n", irq);
|
||||
|
||||
spin_lock(&resume_reason_lock);
|
||||
if (irqcount == MAX_WAKEUP_REASON_IRQS) {
|
||||
spin_unlock(&resume_reason_lock);
|
||||
printk(KERN_WARNING "Resume caused by more than %d IRQs\n",
|
||||
MAX_WAKEUP_REASON_IRQS);
|
||||
return;
|
||||
}
|
||||
|
||||
irq_list[irqcount++] = irq;
|
||||
spin_unlock(&resume_reason_lock);
|
||||
}
|
||||
|
||||
/* Detects a suspend and clears all the previous wake up reasons*/
|
||||
static int wakeup_reason_pm_event(struct notifier_block *notifier,
|
||||
unsigned long pm_event, void *unused)
|
||||
{
|
||||
switch (pm_event) {
|
||||
case PM_SUSPEND_PREPARE:
|
||||
spin_lock(&resume_reason_lock);
|
||||
irqcount = 0;
|
||||
spin_unlock(&resume_reason_lock);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block wakeup_reason_pm_notifier_block = {
|
||||
.notifier_call = wakeup_reason_pm_event,
|
||||
};
|
||||
|
||||
/* Initializes the sysfs parameter
|
||||
* registers the pm_event notifier
|
||||
*/
|
||||
int __init wakeup_reason_init(void)
|
||||
{
|
||||
int retval;
|
||||
spin_lock_init(&resume_reason_lock);
|
||||
retval = register_pm_notifier(&wakeup_reason_pm_notifier_block);
|
||||
if (retval)
|
||||
printk(KERN_WARNING "[%s] failed to register PM notifier %d\n",
|
||||
__func__, retval);
|
||||
|
||||
wakeup_reason = kobject_create_and_add("wakeup_reasons", kernel_kobj);
|
||||
if (!wakeup_reason) {
|
||||
printk(KERN_WARNING "[%s] failed to create a sysfs kobject\n",
|
||||
__func__);
|
||||
return 1;
|
||||
}
|
||||
retval = sysfs_create_group(wakeup_reason, &attr_group);
|
||||
if (retval) {
|
||||
kobject_put(wakeup_reason);
|
||||
printk(KERN_WARNING "[%s] failed to create a sysfs group %d\n",
|
||||
__func__, retval);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(wakeup_reason_init);
|
||||
|
|
@ -51,10 +51,6 @@
|
|||
#define CREATE_TRACE_POINTS
|
||||
#include <trace/events/printk.h>
|
||||
|
||||
#ifdef CONFIG_EARLY_PRINTK_DIRECT
|
||||
extern void printascii(char *);
|
||||
#endif
|
||||
|
||||
/* printk's without a loglevel use this.. */
|
||||
#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
|
||||
|
||||
|
|
|
|||
19
kernel/sys.c
19
kernel/sys.c
|
|
@ -44,6 +44,7 @@
|
|||
#include <linux/ctype.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mempolicy.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <linux/compat.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
|
@ -2252,6 +2253,7 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
|||
unsigned long, arg4, unsigned long, arg5)
|
||||
{
|
||||
struct task_struct *me = current;
|
||||
struct task_struct *tsk;
|
||||
unsigned char comm[sizeof(me->comm)];
|
||||
long error;
|
||||
|
||||
|
|
@ -2375,6 +2377,23 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
|||
else
|
||||
return -EINVAL;
|
||||
break;
|
||||
case PR_SET_TIMERSLACK_PID:
|
||||
rcu_read_lock();
|
||||
tsk = find_task_by_pid_ns((pid_t)arg3, &init_pid_ns);
|
||||
if (tsk == NULL) {
|
||||
rcu_read_unlock();
|
||||
return -EINVAL;
|
||||
}
|
||||
get_task_struct(tsk);
|
||||
rcu_read_unlock();
|
||||
if (arg2 <= 0)
|
||||
tsk->timer_slack_ns =
|
||||
tsk->default_timer_slack_ns;
|
||||
else
|
||||
tsk->timer_slack_ns = arg2;
|
||||
put_task_struct(tsk);
|
||||
error = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2879,14 +2879,8 @@ EXPORT_SYMBOL_GPL(shmem_truncate_range);
|
|||
|
||||
/* common code */
|
||||
|
||||
static char *shmem_dname(struct dentry *dentry, char *buffer, int buflen)
|
||||
{
|
||||
return dynamic_dname(dentry, buffer, buflen, "/%s (deleted)",
|
||||
dentry->d_name.name);
|
||||
}
|
||||
|
||||
static struct dentry_operations anon_ops = {
|
||||
.d_dname = shmem_dname
|
||||
.d_dname = simple_dname
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -167,7 +167,6 @@ static int debug_shrinker_show(struct seq_file *s, void *unused)
|
|||
|
||||
down_read(&shrinker_rwsem);
|
||||
list_for_each_entry(shrinker, &shrinker_list, list) {
|
||||
char name[64];
|
||||
int num_objs;
|
||||
|
||||
num_objs = shrinker->shrink(shrinker, &sc);
|
||||
|
|
|
|||
|
|
@ -83,13 +83,13 @@ endif # if INET
|
|||
|
||||
config ANDROID_PARANOID_NETWORK
|
||||
bool "Only allow certain groups to create sockets"
|
||||
default y
|
||||
default ANDROID
|
||||
help
|
||||
none
|
||||
|
||||
config NET_ACTIVITY_STATS
|
||||
bool "Network activity statistics tracking"
|
||||
default y
|
||||
default ANDROID
|
||||
help
|
||||
Network activity statistics are useful for tracking wireless
|
||||
modem activity on 2G, 3G, 4G wireless networks. Counts number of
|
||||
|
|
|
|||
|
|
@ -939,7 +939,8 @@ int icmp_rcv(struct sk_buff *skb)
|
|||
void icmp_err(struct sk_buff *skb, u32 info)
|
||||
{
|
||||
struct iphdr *iph = (struct iphdr *)skb->data;
|
||||
struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2));
|
||||
int offset = iph->ihl<<2;
|
||||
struct icmphdr *icmph = (struct icmphdr *)(skb->data + offset);
|
||||
int type = icmp_hdr(skb)->type;
|
||||
int code = icmp_hdr(skb)->code;
|
||||
struct net *net = dev_net(skb->dev);
|
||||
|
|
@ -949,7 +950,7 @@ void icmp_err(struct sk_buff *skb, u32 info)
|
|||
* triggered by ICMP_ECHOREPLY which sent from kernel.
|
||||
*/
|
||||
if (icmph->type != ICMP_ECHOREPLY) {
|
||||
ping_err(skb, info);
|
||||
ping_err(skb, offset, info);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
588
net/ipv4/ping.c
588
net/ipv4/ping.c
|
|
@ -33,7 +33,6 @@
|
|||
#include <linux/netdevice.h>
|
||||
#include <net/snmp.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/icmp.h>
|
||||
#include <net/protocol.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
|
@ -46,8 +45,18 @@
|
|||
#include <net/inet_common.h>
|
||||
#include <net/checksum.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
#include <linux/in6.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <net/addrconf.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/transp_v6.h>
|
||||
#endif
|
||||
|
||||
static struct ping_table ping_table;
|
||||
|
||||
struct ping_table ping_table;
|
||||
struct pingv6_ops pingv6_ops;
|
||||
EXPORT_SYMBOL_GPL(pingv6_ops);
|
||||
|
||||
static u16 ping_port_rover;
|
||||
|
||||
|
|
@ -58,6 +67,7 @@ static inline int ping_hashfn(struct net *net, unsigned int num, unsigned int ma
|
|||
pr_debug("hash(%d) = %d\n", num, res);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_hash);
|
||||
|
||||
static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table,
|
||||
struct net *net, unsigned int num)
|
||||
|
|
@ -65,7 +75,7 @@ static inline struct hlist_nulls_head *ping_hashslot(struct ping_table *table,
|
|||
return &table->hash[ping_hashfn(net, num, PING_HTABLE_MASK)];
|
||||
}
|
||||
|
||||
static int ping_v4_get_port(struct sock *sk, unsigned short ident)
|
||||
int ping_get_port(struct sock *sk, unsigned short ident)
|
||||
{
|
||||
struct hlist_nulls_node *node;
|
||||
struct hlist_nulls_head *hlist;
|
||||
|
|
@ -103,6 +113,10 @@ static int ping_v4_get_port(struct sock *sk, unsigned short ident)
|
|||
ping_portaddr_for_each_entry(sk2, node, hlist) {
|
||||
isk2 = inet_sk(sk2);
|
||||
|
||||
/* BUG? Why is this reuse and not reuseaddr? ping.c
|
||||
* doesn't turn off SO_REUSEADDR, and it doesn't expect
|
||||
* that other ping processes can steal its packets.
|
||||
*/
|
||||
if ((isk2->inet_num == ident) &&
|
||||
(sk2 != sk) &&
|
||||
(!sk2->sk_reuse || !sk->sk_reuse))
|
||||
|
|
@ -125,17 +139,18 @@ static int ping_v4_get_port(struct sock *sk, unsigned short ident)
|
|||
write_unlock_bh(&ping_table.lock);
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_get_port);
|
||||
|
||||
static void ping_v4_hash(struct sock *sk)
|
||||
void ping_hash(struct sock *sk)
|
||||
{
|
||||
pr_debug("ping_v4_hash(sk->port=%u)\n", inet_sk(sk)->inet_num);
|
||||
pr_debug("ping_hash(sk->port=%u)\n", inet_sk(sk)->inet_num);
|
||||
BUG(); /* "Please do not press this button again." */
|
||||
}
|
||||
|
||||
static void ping_v4_unhash(struct sock *sk)
|
||||
void ping_unhash(struct sock *sk)
|
||||
{
|
||||
struct inet_sock *isk = inet_sk(sk);
|
||||
pr_debug("ping_v4_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
|
||||
pr_debug("ping_unhash(isk=%p,isk->num=%u)\n", isk, isk->inet_num);
|
||||
if (sk_hashed(sk)) {
|
||||
write_lock_bh(&ping_table.lock);
|
||||
hlist_nulls_del(&sk->sk_nulls_node);
|
||||
|
|
@ -146,31 +161,61 @@ static void ping_v4_unhash(struct sock *sk)
|
|||
write_unlock_bh(&ping_table.lock);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_unhash);
|
||||
|
||||
static struct sock *ping_v4_lookup(struct net *net, __be32 saddr, __be32 daddr,
|
||||
u16 ident, int dif)
|
||||
static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident)
|
||||
{
|
||||
struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident);
|
||||
struct sock *sk = NULL;
|
||||
struct inet_sock *isk;
|
||||
struct hlist_nulls_node *hnode;
|
||||
int dif = skb->dev->ifindex;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n",
|
||||
(int)ident, &ip_hdr(skb)->daddr, dif);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
} else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
pr_debug("try to find: num = %d, daddr = %pI6c, dif = %d\n",
|
||||
(int)ident, &ipv6_hdr(skb)->daddr, dif);
|
||||
#endif
|
||||
}
|
||||
|
||||
pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n",
|
||||
(int)ident, &daddr, dif);
|
||||
read_lock_bh(&ping_table.lock);
|
||||
|
||||
ping_portaddr_for_each_entry(sk, hnode, hslot) {
|
||||
isk = inet_sk(sk);
|
||||
|
||||
pr_debug("found: %p: num = %d, daddr = %pI4, dif = %d\n", sk,
|
||||
(int)isk->inet_num, &isk->inet_rcv_saddr,
|
||||
sk->sk_bound_dev_if);
|
||||
|
||||
pr_debug("iterate\n");
|
||||
if (isk->inet_num != ident)
|
||||
continue;
|
||||
if (isk->inet_rcv_saddr && isk->inet_rcv_saddr != daddr)
|
||||
continue;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP) &&
|
||||
sk->sk_family == AF_INET) {
|
||||
pr_debug("found: %p: num=%d, daddr=%pI4, dif=%d\n", sk,
|
||||
(int) isk->inet_num, &isk->inet_rcv_saddr,
|
||||
sk->sk_bound_dev_if);
|
||||
|
||||
if (isk->inet_rcv_saddr &&
|
||||
isk->inet_rcv_saddr != ip_hdr(skb)->daddr)
|
||||
continue;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
} else if (skb->protocol == htons(ETH_P_IPV6) &&
|
||||
sk->sk_family == AF_INET6) {
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
|
||||
pr_debug("found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk,
|
||||
(int) isk->inet_num,
|
||||
&inet6_sk(sk)->rcv_saddr,
|
||||
sk->sk_bound_dev_if);
|
||||
|
||||
if (!ipv6_addr_any(&np->rcv_saddr) &&
|
||||
!ipv6_addr_equal(&np->rcv_saddr,
|
||||
&ipv6_hdr(skb)->daddr))
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
|
||||
continue;
|
||||
|
||||
|
|
@ -200,33 +245,41 @@ static void inet_get_ping_group_range_net(struct net *net, kgid_t *low,
|
|||
}
|
||||
|
||||
|
||||
static int ping_init_sock(struct sock *sk)
|
||||
int ping_init_sock(struct sock *sk)
|
||||
{
|
||||
struct net *net = sock_net(sk);
|
||||
kgid_t group = current_egid();
|
||||
struct group_info *group_info = get_current_groups();
|
||||
int i, j, count = group_info->ngroups;
|
||||
struct group_info *group_info;
|
||||
int i, j, count;
|
||||
kgid_t low, high;
|
||||
int ret = 0;
|
||||
|
||||
inet_get_ping_group_range_net(net, &low, &high);
|
||||
if (gid_lte(low, group) && gid_lte(group, high))
|
||||
return 0;
|
||||
|
||||
group_info = get_current_groups();
|
||||
count = group_info->ngroups;
|
||||
for (i = 0; i < group_info->nblocks; i++) {
|
||||
int cp_count = min_t(int, NGROUPS_PER_BLOCK, count);
|
||||
for (j = 0; j < cp_count; j++) {
|
||||
kgid_t gid = group_info->blocks[i][j];
|
||||
if (gid_lte(low, gid) && gid_lte(gid, high))
|
||||
return 0;
|
||||
goto out_release_group;
|
||||
}
|
||||
|
||||
count -= cp_count;
|
||||
}
|
||||
|
||||
return -EACCES;
|
||||
}
|
||||
ret = -EACCES;
|
||||
|
||||
static void ping_close(struct sock *sk, long timeout)
|
||||
out_release_group:
|
||||
put_group_info(group_info);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_init_sock);
|
||||
|
||||
void ping_close(struct sock *sk, long timeout)
|
||||
{
|
||||
pr_debug("ping_close(sk=%p,sk->num=%u)\n",
|
||||
inet_sk(sk), inet_sk(sk)->inet_num);
|
||||
|
|
@ -234,36 +287,122 @@ static void ping_close(struct sock *sk, long timeout)
|
|||
|
||||
sk_common_release(sk);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_close);
|
||||
|
||||
/* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */
|
||||
int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk,
|
||||
struct sockaddr *uaddr, int addr_len) {
|
||||
struct net *net = sock_net(sk);
|
||||
if (sk->sk_family == AF_INET) {
|
||||
struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
|
||||
int chk_addr_ret;
|
||||
|
||||
if (addr_len < sizeof(*addr))
|
||||
return -EINVAL;
|
||||
|
||||
pr_debug("ping_check_bind_addr(sk=%p,addr=%pI4,port=%d)\n",
|
||||
sk, &addr->sin_addr.s_addr, ntohs(addr->sin_port));
|
||||
|
||||
chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr);
|
||||
|
||||
if (addr->sin_addr.s_addr == htonl(INADDR_ANY))
|
||||
chk_addr_ret = RTN_LOCAL;
|
||||
|
||||
if ((sysctl_ip_nonlocal_bind == 0 &&
|
||||
isk->freebind == 0 && isk->transparent == 0 &&
|
||||
chk_addr_ret != RTN_LOCAL) ||
|
||||
chk_addr_ret == RTN_MULTICAST ||
|
||||
chk_addr_ret == RTN_BROADCAST)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
} else if (sk->sk_family == AF_INET6) {
|
||||
struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
|
||||
int addr_type, scoped, has_addr;
|
||||
struct net_device *dev = NULL;
|
||||
|
||||
if (addr_len < sizeof(*addr))
|
||||
return -EINVAL;
|
||||
|
||||
pr_debug("ping_check_bind_addr(sk=%p,addr=%pI6c,port=%d)\n",
|
||||
sk, addr->sin6_addr.s6_addr, ntohs(addr->sin6_port));
|
||||
|
||||
addr_type = ipv6_addr_type(&addr->sin6_addr);
|
||||
scoped = __ipv6_addr_needs_scope_id(addr_type);
|
||||
if ((addr_type != IPV6_ADDR_ANY &&
|
||||
!(addr_type & IPV6_ADDR_UNICAST)) ||
|
||||
(scoped && !addr->sin6_scope_id))
|
||||
return -EINVAL;
|
||||
|
||||
rcu_read_lock();
|
||||
if (addr->sin6_scope_id) {
|
||||
dev = dev_get_by_index_rcu(net, addr->sin6_scope_id);
|
||||
if (!dev) {
|
||||
rcu_read_unlock();
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
has_addr = pingv6_ops.ipv6_chk_addr(net, &addr->sin6_addr, dev,
|
||||
scoped);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!(isk->freebind || isk->transparent || has_addr ||
|
||||
addr_type == IPV6_ADDR_ANY))
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
if (scoped)
|
||||
sk->sk_bound_dev_if = addr->sin6_scope_id;
|
||||
#endif
|
||||
} else {
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ping_set_saddr(struct sock *sk, struct sockaddr *saddr)
|
||||
{
|
||||
if (saddr->sa_family == AF_INET) {
|
||||
struct inet_sock *isk = inet_sk(sk);
|
||||
struct sockaddr_in *addr = (struct sockaddr_in *) saddr;
|
||||
isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
} else if (saddr->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr;
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
np->rcv_saddr = np->saddr = addr->sin6_addr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ping_clear_saddr(struct sock *sk, int dif)
|
||||
{
|
||||
sk->sk_bound_dev_if = dif;
|
||||
if (sk->sk_family == AF_INET) {
|
||||
struct inet_sock *isk = inet_sk(sk);
|
||||
isk->inet_rcv_saddr = isk->inet_saddr = 0;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
} else if (sk->sk_family == AF_INET6) {
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
memset(&np->rcv_saddr, 0, sizeof(np->rcv_saddr));
|
||||
memset(&np->saddr, 0, sizeof(np->saddr));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
/*
|
||||
* We need our own bind because there are no privileged id's == local ports.
|
||||
* Moreover, we don't allow binding to multi- and broadcast addresses.
|
||||
*/
|
||||
|
||||
static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
||||
int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
||||
{
|
||||
struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
|
||||
struct inet_sock *isk = inet_sk(sk);
|
||||
unsigned short snum;
|
||||
int chk_addr_ret;
|
||||
int err;
|
||||
int dif = sk->sk_bound_dev_if;
|
||||
|
||||
if (addr_len < sizeof(struct sockaddr_in))
|
||||
return -EINVAL;
|
||||
|
||||
pr_debug("ping_v4_bind(sk=%p,sa_addr=%08x,sa_port=%d)\n",
|
||||
sk, addr->sin_addr.s_addr, ntohs(addr->sin_port));
|
||||
|
||||
chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
|
||||
if (addr->sin_addr.s_addr == htonl(INADDR_ANY))
|
||||
chk_addr_ret = RTN_LOCAL;
|
||||
|
||||
if ((sysctl_ip_nonlocal_bind == 0 &&
|
||||
isk->freebind == 0 && isk->transparent == 0 &&
|
||||
chk_addr_ret != RTN_LOCAL) ||
|
||||
chk_addr_ret == RTN_MULTICAST ||
|
||||
chk_addr_ret == RTN_BROADCAST)
|
||||
return -EADDRNOTAVAIL;
|
||||
err = ping_check_bind_addr(sk, isk, uaddr, addr_len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
|
|
@ -272,42 +411,50 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
|||
goto out;
|
||||
|
||||
err = -EADDRINUSE;
|
||||
isk->inet_rcv_saddr = isk->inet_saddr = addr->sin_addr.s_addr;
|
||||
snum = ntohs(addr->sin_port);
|
||||
if (ping_v4_get_port(sk, snum) != 0) {
|
||||
isk->inet_saddr = isk->inet_rcv_saddr = 0;
|
||||
ping_set_saddr(sk, uaddr);
|
||||
snum = ntohs(((struct sockaddr_in *)uaddr)->sin_port);
|
||||
if (ping_get_port(sk, snum) != 0) {
|
||||
ping_clear_saddr(sk, dif);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_debug("after bind(): num = %d, daddr = %pI4, dif = %d\n",
|
||||
pr_debug("after bind(): num = %d, dif = %d\n",
|
||||
(int)isk->inet_num,
|
||||
&isk->inet_rcv_saddr,
|
||||
(int)sk->sk_bound_dev_if);
|
||||
|
||||
err = 0;
|
||||
if (isk->inet_rcv_saddr)
|
||||
if ((sk->sk_family == AF_INET && isk->inet_rcv_saddr) ||
|
||||
(sk->sk_family == AF_INET6 &&
|
||||
!ipv6_addr_any(&inet6_sk(sk)->rcv_saddr)))
|
||||
sk->sk_userlocks |= SOCK_BINDADDR_LOCK;
|
||||
|
||||
if (snum)
|
||||
sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
|
||||
isk->inet_sport = htons(isk->inet_num);
|
||||
isk->inet_daddr = 0;
|
||||
isk->inet_dport = 0;
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
if (sk->sk_family == AF_INET6)
|
||||
memset(&inet6_sk(sk)->daddr, 0, sizeof(inet6_sk(sk)->daddr));
|
||||
#endif
|
||||
|
||||
sk_dst_reset(sk);
|
||||
out:
|
||||
release_sock(sk);
|
||||
pr_debug("ping_v4_bind -> %d\n", err);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_bind);
|
||||
|
||||
/*
|
||||
* Is this a supported type of ICMP message?
|
||||
*/
|
||||
|
||||
static inline int ping_supported(int type, int code)
|
||||
static inline int ping_supported(int family, int type, int code)
|
||||
{
|
||||
if (type == ICMP_ECHO && code == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
return (family == AF_INET && type == ICMP_ECHO && code == 0) ||
|
||||
(family == AF_INET6 && type == ICMPV6_ECHO_REQUEST && code == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -315,30 +462,42 @@ static inline int ping_supported(int type, int code)
|
|||
* sort of error condition.
|
||||
*/
|
||||
|
||||
static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
|
||||
|
||||
void ping_err(struct sk_buff *skb, u32 info)
|
||||
void ping_err(struct sk_buff *skb, int offset, u32 info)
|
||||
{
|
||||
struct iphdr *iph = (struct iphdr *)skb->data;
|
||||
struct icmphdr *icmph = (struct icmphdr *)(skb->data+(iph->ihl<<2));
|
||||
int family;
|
||||
struct icmphdr *icmph;
|
||||
struct inet_sock *inet_sock;
|
||||
int type = icmp_hdr(skb)->type;
|
||||
int code = icmp_hdr(skb)->code;
|
||||
int type;
|
||||
int code;
|
||||
struct net *net = dev_net(skb->dev);
|
||||
struct sock *sk;
|
||||
int harderr;
|
||||
int err;
|
||||
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
family = AF_INET;
|
||||
type = icmp_hdr(skb)->type;
|
||||
code = icmp_hdr(skb)->code;
|
||||
icmph = (struct icmphdr *)(skb->data + offset);
|
||||
} else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
family = AF_INET6;
|
||||
type = icmp6_hdr(skb)->icmp6_type;
|
||||
code = icmp6_hdr(skb)->icmp6_code;
|
||||
icmph = (struct icmphdr *) (skb->data + offset);
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* We assume the packet has already been checked by icmp_unreach */
|
||||
|
||||
if (!ping_supported(icmph->type, icmph->code))
|
||||
if (!ping_supported(family, icmph->type, icmph->code))
|
||||
return;
|
||||
|
||||
pr_debug("ping_err(type=%04x,code=%04x,id=%04x,seq=%04x)\n", type,
|
||||
code, ntohs(icmph->un.echo.id), ntohs(icmph->un.echo.sequence));
|
||||
pr_debug("ping_err(proto=0x%x,type=%d,code=%d,id=%04x,seq=%04x)\n",
|
||||
skb->protocol, type, code, ntohs(icmph->un.echo.id),
|
||||
ntohs(icmph->un.echo.sequence));
|
||||
|
||||
sk = ping_v4_lookup(net, iph->daddr, iph->saddr,
|
||||
ntohs(icmph->un.echo.id), skb->dev->ifindex);
|
||||
sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id));
|
||||
if (sk == NULL) {
|
||||
pr_debug("no socket, dropping\n");
|
||||
return; /* No socket for error */
|
||||
|
|
@ -349,72 +508,83 @@ void ping_err(struct sk_buff *skb, u32 info)
|
|||
harderr = 0;
|
||||
inet_sock = inet_sk(sk);
|
||||
|
||||
switch (type) {
|
||||
default:
|
||||
case ICMP_TIME_EXCEEDED:
|
||||
err = EHOSTUNREACH;
|
||||
break;
|
||||
case ICMP_SOURCE_QUENCH:
|
||||
/* This is not a real error but ping wants to see it.
|
||||
* Report it with some fake errno. */
|
||||
err = EREMOTEIO;
|
||||
break;
|
||||
case ICMP_PARAMETERPROB:
|
||||
err = EPROTO;
|
||||
harderr = 1;
|
||||
break;
|
||||
case ICMP_DEST_UNREACH:
|
||||
if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
|
||||
ipv4_sk_update_pmtu(skb, sk, info);
|
||||
if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) {
|
||||
err = EMSGSIZE;
|
||||
harderr = 1;
|
||||
break;
|
||||
if (skb->protocol == htons(ETH_P_IP)) {
|
||||
switch (type) {
|
||||
default:
|
||||
case ICMP_TIME_EXCEEDED:
|
||||
err = EHOSTUNREACH;
|
||||
break;
|
||||
case ICMP_SOURCE_QUENCH:
|
||||
/* This is not a real error but ping wants to see it.
|
||||
* Report it with some fake errno.
|
||||
*/
|
||||
err = EREMOTEIO;
|
||||
break;
|
||||
case ICMP_PARAMETERPROB:
|
||||
err = EPROTO;
|
||||
harderr = 1;
|
||||
break;
|
||||
case ICMP_DEST_UNREACH:
|
||||
if (code == ICMP_FRAG_NEEDED) { /* Path MTU discovery */
|
||||
ipv4_sk_update_pmtu(skb, sk, info);
|
||||
if (inet_sock->pmtudisc != IP_PMTUDISC_DONT) {
|
||||
err = EMSGSIZE;
|
||||
harderr = 1;
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
goto out;
|
||||
err = EHOSTUNREACH;
|
||||
if (code <= NR_ICMP_UNREACH) {
|
||||
harderr = icmp_err_convert[code].fatal;
|
||||
err = icmp_err_convert[code].errno;
|
||||
}
|
||||
break;
|
||||
case ICMP_REDIRECT:
|
||||
/* See ICMP_SOURCE_QUENCH */
|
||||
ipv4_sk_redirect(skb, sk);
|
||||
err = EREMOTEIO;
|
||||
break;
|
||||
}
|
||||
err = EHOSTUNREACH;
|
||||
if (code <= NR_ICMP_UNREACH) {
|
||||
harderr = icmp_err_convert[code].fatal;
|
||||
err = icmp_err_convert[code].errno;
|
||||
}
|
||||
break;
|
||||
case ICMP_REDIRECT:
|
||||
/* See ICMP_SOURCE_QUENCH */
|
||||
ipv4_sk_redirect(skb, sk);
|
||||
err = EREMOTEIO;
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
} else if (skb->protocol == htons(ETH_P_IPV6)) {
|
||||
harderr = pingv6_ops.icmpv6_err_convert(type, code, &err);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC1122: OK. Passes ICMP errors back to application, as per
|
||||
* 4.1.3.3.
|
||||
*/
|
||||
if (!inet_sock->recverr) {
|
||||
if ((family == AF_INET && !inet_sock->recverr) ||
|
||||
(family == AF_INET6 && !inet6_sk(sk)->recverr)) {
|
||||
if (!harderr || sk->sk_state != TCP_ESTABLISHED)
|
||||
goto out;
|
||||
} else {
|
||||
ip_icmp_error(sk, skb, err, 0 /* no remote port */,
|
||||
info, (u8 *)icmph);
|
||||
if (family == AF_INET) {
|
||||
ip_icmp_error(sk, skb, err, 0 /* no remote port */,
|
||||
info, (u8 *)icmph);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
} else if (family == AF_INET6) {
|
||||
pingv6_ops.ipv6_icmp_error(sk, skb, err, 0,
|
||||
info, (u8 *)icmph);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
sk->sk_err = err;
|
||||
sk->sk_error_report(sk);
|
||||
out:
|
||||
sock_put(sk);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_err);
|
||||
|
||||
/*
|
||||
* Copy and checksum an ICMP Echo packet from user space into a buffer.
|
||||
* Copy and checksum an ICMP Echo packet from user space into a buffer
|
||||
* starting from the payload.
|
||||
*/
|
||||
|
||||
struct pingfakehdr {
|
||||
struct icmphdr icmph;
|
||||
struct iovec *iov;
|
||||
__wsum wcheck;
|
||||
};
|
||||
|
||||
static int ping_getfrag(void *from, char *to,
|
||||
int offset, int fraglen, int odd, struct sk_buff *skb)
|
||||
int ping_getfrag(void *from, char *to,
|
||||
int offset, int fraglen, int odd, struct sk_buff *skb)
|
||||
{
|
||||
struct pingfakehdr *pfh = (struct pingfakehdr *)from;
|
||||
|
||||
|
|
@ -425,20 +595,33 @@ static int ping_getfrag(void *from, char *to,
|
|||
pfh->iov, 0, fraglen - sizeof(struct icmphdr),
|
||||
&pfh->wcheck))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
} else if (offset < sizeof(struct icmphdr)) {
|
||||
BUG();
|
||||
} else {
|
||||
if (csum_partial_copy_fromiovecend
|
||||
(to, pfh->iov, offset - sizeof(struct icmphdr),
|
||||
fraglen, &pfh->wcheck))
|
||||
return -EFAULT;
|
||||
}
|
||||
if (offset < sizeof(struct icmphdr))
|
||||
BUG();
|
||||
if (csum_partial_copy_fromiovecend
|
||||
(to, pfh->iov, offset - sizeof(struct icmphdr),
|
||||
fraglen, &pfh->wcheck))
|
||||
return -EFAULT;
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
/* For IPv6, checksum each skb as we go along, as expected by
|
||||
* icmpv6_push_pending_frames. For IPv4, accumulate the checksum in
|
||||
* wcheck, it will be finalized in ping_v4_push_pending_frames.
|
||||
*/
|
||||
if (pfh->family == AF_INET6) {
|
||||
skb->csum = pfh->wcheck;
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
pfh->wcheck = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_getfrag);
|
||||
|
||||
static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh,
|
||||
struct flowi4 *fl4)
|
||||
static int ping_v4_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh,
|
||||
struct flowi4 *fl4)
|
||||
{
|
||||
struct sk_buff *skb = skb_peek(&sk->sk_write_queue);
|
||||
|
||||
|
|
@ -450,24 +633,9 @@ static int ping_push_pending_frames(struct sock *sk, struct pingfakehdr *pfh,
|
|||
return ip_push_pending_frames(sk, fl4);
|
||||
}
|
||||
|
||||
static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
||||
size_t len)
|
||||
{
|
||||
struct net *net = sock_net(sk);
|
||||
struct flowi4 fl4;
|
||||
struct inet_sock *inet = inet_sk(sk);
|
||||
struct ipcm_cookie ipc;
|
||||
struct icmphdr user_icmph;
|
||||
struct pingfakehdr pfh;
|
||||
struct rtable *rt = NULL;
|
||||
struct ip_options_data opt_copy;
|
||||
int free = 0;
|
||||
__be32 saddr, daddr, faddr;
|
||||
u8 tos;
|
||||
int err;
|
||||
|
||||
pr_debug("ping_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
|
||||
|
||||
int ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
|
||||
void *user_icmph, size_t icmph_len) {
|
||||
u8 type, code;
|
||||
|
||||
if (len > 0xFFFF)
|
||||
return -EMSGSIZE;
|
||||
|
|
@ -482,15 +650,53 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
|||
|
||||
/*
|
||||
* Fetch the ICMP header provided by the userland.
|
||||
* iovec is modified!
|
||||
* iovec is modified! The ICMP header is consumed.
|
||||
*/
|
||||
|
||||
if (memcpy_fromiovec((u8 *)&user_icmph, msg->msg_iov,
|
||||
sizeof(struct icmphdr)))
|
||||
if (memcpy_fromiovec(user_icmph, msg->msg_iov, icmph_len))
|
||||
return -EFAULT;
|
||||
if (!ping_supported(user_icmph.type, user_icmph.code))
|
||||
|
||||
if (family == AF_INET) {
|
||||
type = ((struct icmphdr *) user_icmph)->type;
|
||||
code = ((struct icmphdr *) user_icmph)->code;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
} else if (family == AF_INET6) {
|
||||
type = ((struct icmp6hdr *) user_icmph)->icmp6_type;
|
||||
code = ((struct icmp6hdr *) user_icmph)->icmp6_code;
|
||||
#endif
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (!ping_supported(family, type, code))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_common_sendmsg);
|
||||
|
||||
int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
||||
size_t len)
|
||||
{
|
||||
struct net *net = sock_net(sk);
|
||||
struct flowi4 fl4;
|
||||
struct inet_sock *inet = inet_sk(sk);
|
||||
struct ipcm_cookie ipc;
|
||||
struct icmphdr user_icmph;
|
||||
struct pingfakehdr pfh;
|
||||
struct rtable *rt = NULL;
|
||||
struct ip_options_data opt_copy;
|
||||
int free = 0;
|
||||
__be32 saddr, daddr, faddr;
|
||||
u8 tos;
|
||||
int err;
|
||||
|
||||
pr_debug("ping_v4_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
|
||||
|
||||
err = ping_common_sendmsg(AF_INET, msg, len, &user_icmph,
|
||||
sizeof(user_icmph));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Get and verify the address.
|
||||
*/
|
||||
|
|
@ -595,13 +801,14 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
|||
pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence;
|
||||
pfh.iov = msg->msg_iov;
|
||||
pfh.wcheck = 0;
|
||||
pfh.family = AF_INET;
|
||||
|
||||
err = ip_append_data(sk, &fl4, ping_getfrag, &pfh, len,
|
||||
0, &ipc, &rt, msg->msg_flags);
|
||||
if (err)
|
||||
ip_flush_pending_frames(sk);
|
||||
else
|
||||
err = ping_push_pending_frames(sk, &pfh, &fl4);
|
||||
err = ping_v4_push_pending_frames(sk, &pfh, &fl4);
|
||||
release_sock(sk);
|
||||
|
||||
out:
|
||||
|
|
@ -622,11 +829,13 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
|||
goto out;
|
||||
}
|
||||
|
||||
static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
||||
size_t len, int noblock, int flags, int *addr_len)
|
||||
int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
||||
size_t len, int noblock, int flags, int *addr_len)
|
||||
{
|
||||
struct inet_sock *isk = inet_sk(sk);
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
|
||||
int family = sk->sk_family;
|
||||
struct sockaddr_in *sin;
|
||||
struct sockaddr_in6 *sin6;
|
||||
struct sk_buff *skb;
|
||||
int copied, err;
|
||||
|
||||
|
|
@ -636,11 +845,22 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
|||
if (flags & MSG_OOB)
|
||||
goto out;
|
||||
|
||||
if (addr_len)
|
||||
*addr_len = sizeof(*sin);
|
||||
if (addr_len) {
|
||||
if (family == AF_INET)
|
||||
*addr_len = sizeof(*sin);
|
||||
else if (family == AF_INET6 && addr_len)
|
||||
*addr_len = sizeof(*sin6);
|
||||
}
|
||||
|
||||
if (flags & MSG_ERRQUEUE)
|
||||
return ip_recv_error(sk, msg, len);
|
||||
if (flags & MSG_ERRQUEUE) {
|
||||
if (family == AF_INET) {
|
||||
return ip_recv_error(sk, msg, len);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
} else if (family == AF_INET6) {
|
||||
return pingv6_ops.ipv6_recv_error(sk, msg, len);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
skb = skb_recv_datagram(sk, flags, noblock, &err);
|
||||
if (!skb)
|
||||
|
|
@ -659,15 +879,44 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
|||
|
||||
sock_recv_timestamp(msg, sk, skb);
|
||||
|
||||
/* Copy the address. */
|
||||
if (sin) {
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_port = 0 /* skb->h.uh->source */;
|
||||
sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
|
||||
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
|
||||
/* Copy the address and add cmsg data. */
|
||||
if (family == AF_INET) {
|
||||
sin = (struct sockaddr_in *) msg->msg_name;
|
||||
if (sin) {
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_port = 0 /* skb->h.uh->source */;
|
||||
sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
|
||||
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
|
||||
}
|
||||
|
||||
if (isk->cmsg_flags)
|
||||
ip_cmsg_recv(msg, skb);
|
||||
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
} else if (family == AF_INET6) {
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
struct ipv6hdr *ip6 = ipv6_hdr(skb);
|
||||
sin6 = (struct sockaddr_in6 *) msg->msg_name;
|
||||
|
||||
if (sin6) {
|
||||
sin6->sin6_family = AF_INET6;
|
||||
sin6->sin6_port = 0;
|
||||
sin6->sin6_addr = ip6->saddr;
|
||||
sin6->sin6_flowinfo = 0;
|
||||
if (np->sndflow)
|
||||
sin6->sin6_flowinfo = ip6_flowinfo(ip6);
|
||||
sin6->sin6_scope_id =
|
||||
ipv6_iface_scope_id(&sin6->sin6_addr,
|
||||
IP6CB(skb)->iif);
|
||||
}
|
||||
|
||||
if (inet6_sk(sk)->rxopt.all)
|
||||
pingv6_ops.ip6_datagram_recv_ctl(sk, msg, skb);
|
||||
#endif
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
if (isk->cmsg_flags)
|
||||
ip_cmsg_recv(msg, skb);
|
||||
|
||||
err = copied;
|
||||
|
||||
done:
|
||||
|
|
@ -676,8 +925,9 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
|||
pr_debug("ping_recvmsg -> %d\n", err);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_recvmsg);
|
||||
|
||||
static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||||
int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n",
|
||||
inet_sk(sk), inet_sk(sk)->inet_num, skb);
|
||||
|
|
@ -688,6 +938,7 @@ static int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_queue_rcv_skb);
|
||||
|
||||
|
||||
/*
|
||||
|
|
@ -698,10 +949,7 @@ void ping_rcv(struct sk_buff *skb)
|
|||
{
|
||||
struct sock *sk;
|
||||
struct net *net = dev_net(skb->dev);
|
||||
struct iphdr *iph = ip_hdr(skb);
|
||||
struct icmphdr *icmph = icmp_hdr(skb);
|
||||
__be32 saddr = iph->saddr;
|
||||
__be32 daddr = iph->daddr;
|
||||
|
||||
/* We assume the packet has already been checked by icmp_rcv */
|
||||
|
||||
|
|
@ -711,8 +959,7 @@ void ping_rcv(struct sk_buff *skb)
|
|||
/* Push ICMP header back */
|
||||
skb_push(skb, skb->data - (u8 *)icmph);
|
||||
|
||||
sk = ping_v4_lookup(net, saddr, daddr, ntohs(icmph->un.echo.id),
|
||||
skb->dev->ifindex);
|
||||
sk = ping_lookup(net, skb, ntohs(icmph->un.echo.id));
|
||||
if (sk != NULL) {
|
||||
pr_debug("rcv on socket %p\n", sk);
|
||||
ping_queue_rcv_skb(sk, skb_get(skb));
|
||||
|
|
@ -723,6 +970,7 @@ void ping_rcv(struct sk_buff *skb)
|
|||
|
||||
/* We're called from icmp_rcv(). kfree_skb() is done there. */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ping_rcv);
|
||||
|
||||
struct proto ping_prot = {
|
||||
.name = "PING",
|
||||
|
|
@ -733,14 +981,14 @@ struct proto ping_prot = {
|
|||
.disconnect = udp_disconnect,
|
||||
.setsockopt = ip_setsockopt,
|
||||
.getsockopt = ip_getsockopt,
|
||||
.sendmsg = ping_sendmsg,
|
||||
.sendmsg = ping_v4_sendmsg,
|
||||
.recvmsg = ping_recvmsg,
|
||||
.bind = ping_bind,
|
||||
.backlog_rcv = ping_queue_rcv_skb,
|
||||
.release_cb = ip4_datagram_release_cb,
|
||||
.hash = ping_v4_hash,
|
||||
.unhash = ping_v4_unhash,
|
||||
.get_port = ping_v4_get_port,
|
||||
.hash = ping_hash,
|
||||
.unhash = ping_unhash,
|
||||
.get_port = ping_get_port,
|
||||
.obj_size = sizeof(struct inet_sock),
|
||||
};
|
||||
EXPORT_SYMBOL(ping_prot);
|
||||
|
|
|
|||
|
|
@ -135,6 +135,21 @@ static int ipv4_ping_group_range(ctl_table *table, int write,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Validate changes from /proc interface. */
|
||||
static int proc_tcp_default_init_rwnd(ctl_table *ctl, int write,
|
||||
void __user *buffer,
|
||||
size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
int old_value = *(int *)ctl->data;
|
||||
int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
|
||||
int new_value = *(int *)ctl->data;
|
||||
|
||||
if (write && ret == 0 && (new_value < 3 || new_value > 100))
|
||||
*(int *)ctl->data = old_value;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int proc_tcp_congestion_control(ctl_table *ctl, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
|
|
@ -732,7 +747,7 @@ static struct ctl_table ipv4_table[] = {
|
|||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec
|
||||
},
|
||||
{
|
||||
{
|
||||
.procname = "tcp_thin_dupack",
|
||||
.data = &sysctl_tcp_thin_dupack,
|
||||
.maxlen = sizeof(int),
|
||||
|
|
@ -748,6 +763,13 @@ static struct ctl_table ipv4_table[] = {
|
|||
.extra1 = &zero,
|
||||
.extra2 = &four,
|
||||
},
|
||||
{
|
||||
.procname = "tcp_default_init_rwnd",
|
||||
.data = &sysctl_tcp_default_init_rwnd,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_tcp_default_init_rwnd
|
||||
},
|
||||
{
|
||||
.procname = "udp_mem",
|
||||
.data = &sysctl_udp_mem,
|
||||
|
|
|
|||
|
|
@ -3471,7 +3471,7 @@ static int tcp_is_local(struct net *net, __be32 addr) {
|
|||
return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
#if defined(CONFIG_IPV6)
|
||||
static int tcp_is_local6(struct net *net, struct in6_addr *addr) {
|
||||
struct rt6_info *rt6 = rt6_lookup(net, addr, addr, 0, 0);
|
||||
return rt6 && rt6->dst.dev && (rt6->dst.dev->flags & IFF_LOOPBACK);
|
||||
|
|
@ -3528,7 +3528,7 @@ int tcp_nuke_addr(struct net *net, struct sockaddr *addr)
|
|||
continue;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
#if defined(CONFIG_IPV6)
|
||||
if (family == AF_INET6) {
|
||||
struct in6_addr *s6;
|
||||
if (!inet->pinet6)
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ int sysctl_tcp_thin_dupack __read_mostly;
|
|||
|
||||
int sysctl_tcp_moderate_rcvbuf __read_mostly = 1;
|
||||
int sysctl_tcp_early_retrans __read_mostly = 3;
|
||||
int sysctl_tcp_default_init_rwnd __read_mostly = TCP_DEFAULT_INIT_RCVWND;
|
||||
|
||||
#define FLAG_DATA 0x01 /* Incoming frame contained data. */
|
||||
#define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */
|
||||
|
|
@ -351,14 +352,14 @@ static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb)
|
|||
static void tcp_fixup_rcvbuf(struct sock *sk)
|
||||
{
|
||||
u32 mss = tcp_sk(sk)->advmss;
|
||||
u32 icwnd = TCP_DEFAULT_INIT_RCVWND;
|
||||
u32 icwnd = sysctl_tcp_default_init_rwnd;
|
||||
int rcvmem;
|
||||
|
||||
/* Limit to 10 segments if mss <= 1460,
|
||||
* or 14600/mss segments, with a minimum of two segments.
|
||||
*/
|
||||
if (mss > 1460)
|
||||
icwnd = max_t(u32, (1460 * TCP_DEFAULT_INIT_RCVWND) / mss, 2);
|
||||
icwnd = max_t(u32, (1460 * icwnd) / mss, 2);
|
||||
|
||||
rcvmem = SKB_TRUESIZE(mss + MAX_TCP_HEADER);
|
||||
while (tcp_win_from_space(rcvmem) < mss)
|
||||
|
|
|
|||
|
|
@ -231,14 +231,13 @@ void tcp_select_initial_window(int __space, __u32 mss,
|
|||
}
|
||||
|
||||
/* Set initial window to a value enough for senders starting with
|
||||
* initial congestion window of TCP_DEFAULT_INIT_RCVWND. Place
|
||||
* initial congestion window of sysctl_tcp_default_init_rwnd. Place
|
||||
* a limit on the initial window when mss is larger than 1460.
|
||||
*/
|
||||
if (mss > (1 << *rcv_wscale)) {
|
||||
int init_cwnd = TCP_DEFAULT_INIT_RCVWND;
|
||||
int init_cwnd = sysctl_tcp_default_init_rwnd;
|
||||
if (mss > 1460)
|
||||
init_cwnd =
|
||||
max_t(u32, (1460 * TCP_DEFAULT_INIT_RCVWND) / mss, 2);
|
||||
init_cwnd = max_t(u32, (1460 * init_cwnd) / mss, 2);
|
||||
/* when initializing use the value from init_rcv_wnd
|
||||
* rather than the default from above
|
||||
*/
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user